-current-entry)
      (goto-char here)
      (error (_"There is no such entry")))))

(defun po-next-entry-with-regexp (regexp wrap)
  "Display the entry following the current entry which msgstr matches REGEXP.
If WRAP is not nil, the search may wrap around the buffer."
  (po-find-span-of-entry)
  (let ((here (point)))
    (goto-char po-end-of-entry)
    (if (re-search-forward regexp nil t)
        (progn
          (goto-char (match-beginning 0))
          (po-current-entry))
      (if (and wrap
               (progn
                 (goto-char (point-min))
                 (re-search-forward regexp po-start-of-entry t)))
          (progn
            (goto-char (match-beginning 0))
            (po-current-entry)
            (message (_"Wrapping around the buffer")))
        (goto-char here)
        (error (_"There is no such entry"))))))

(defun po-previous-entry-with-regexp (regexp wrap)
  "Redisplay the entry preceding the current entry which msgstr matches REGEXP.
If WRAP is not nil, the search may wrap around the buffer."
  (po-find-span-of-entry)
  (let ((here (point)))
    (goto-char po-start-of-entry)
    (if (re-search-backward regexp nil t)
        (po-current-entry)
      (if (and wrap
               (progn
                 (goto-char (point-max))
                 (re-search-backward regexp po-end-of-entry t)))
          (progn
            (po-current-entry)
            (message (_"Wrapping around the buffer")))
        (goto-char here)
        (error (_"There is no such entry"))))))

;; Any entries.

(defun po-first-entry ()
  "Display the first entry."
  (interactive)
  (po-first-entry-with-regexp po-any-msgstr-block-regexp))

(defun po-last-entry ()
  "Display the last entry."
  (interactive)
  (po-last-entry-with-regexp po-any-msgstr-block-regexp))

(defun po-next-entry ()
  "Display the entry following the current entry."
  (interactive)
  (po-next-entry-with-regexp po-any-msgstr-block-regexp nil))

(defun po-previous-entry ()
  "Display the entry preceding the current entry."
  (interactive)
  (po-previous-entry-with-regexp po-any-msgstr-block-regexp nil))

;; Untranslated entries.

(defvar po-after-entry-regexp
  "\\(\\'\\|\\(#[ \t]*\\)?$\\)"
  "Regexp which should be true after a full msgstr string matched.")

(defvar po-untranslated-regexp
  (concat "^msgstr\\(\\[[0-9]\\]\\)?[ \t]*\"\"\n" po-after-entry-regexp)
  "Regexp matching a whole msgstr field, but only if active and empty.")

(defun po-next-untranslated-entry ()
  "Find the next untranslated entry, wrapping around if necessary."
  (interactive)
  (po-next-entry-with-regexp po-untranslated-regexp t))

(defun po-previous-untranslated-entry ()
  "Find the previous untranslated entry, wrapping around if necessary."
  (interactive)
  (po-previous-entry-with-regexp po-untranslated-regexp t))

(defun po-msgid-to-msgstr ()
  "Use another window to edit msgstr reinitialized with msgid."
  (interactive)
  (po-find-span-of-entry)
  (if (or (eq po-entry-type 'untranslated)
          (eq po-entry-type 'obsolete)
          (prog1 (y-or-n-p (_"Really lose previous translation? "))
                 (message "")))
      ;; In an entry with plural forms, use the msgid_plural string,
      ;; as it is more general than the msgid string.
      (if (po-set-msgstr-form (or (po-get-msgid_plural) (po-get-msgid)))
          (po-maybe-delete-previous-untranslated))))

;; Obsolete entries.

(defvar po-obsolete-msgstr-regexp
  "^#~[ \t]*msgstr.*\n\\(#~[ \t]*\".*\n\\)*"
  "Regexp matching a whole msgstr field of an obsolete entry.")

(defun po-next-obsolete-entry ()
  "Find the next obsolete entry, wrapping around if necessary."
  (interactive)
  (po-next-entry-with-regexp po-obsolete-msgstr-regexp t))

(defun po-previous-obsolete-entry ()
  "Find the previous obsolete entry, wrapping around if necessary."
  (interactive)
  (po-previous-entry-with-regexp po-obsolete-msgstr-regexp t))

;; Fuzzy entries.

(defvar po-fuzzy-regexp "^#, .*fuzzy"
  "Regexp matching the string inserted by msgmerge for translations
which does not match exactly.")

(defun po-next-fuzzy-entry ()
  "Find the next fuzzy entry, wrapping around if necessary."
  (interactive)
  (po-next-entry-with-regexp po-fuzzy-regexp t))

(defun po-previous-fuzzy-entry ()
  "Find the next fuzzy entry, wrapping around if necessary."
  (interactive)
  (po-previous-entry-with-regexp po-fuzzy-regexp t))

(defun po-unfuzzy ()
  "Remove the fuzzy attribute for the current entry."
  (interactive)
  (po-find-span-of-entry)
  (cond ((eq po-entry-type 'fuzzy)
         (po-decrease-type-counter)
         (po-delete-attribute "fuzzy")
         (po-maybe-delete-previous-untranslated)
         (po-current-entry)
         (po-increase-type-counter)))
  (if po-auto-select-on-unfuzzy
      (po-auto-select-entry))
  (po-update-mode-line-string))

;; Translated entries.

(defun po-next-translated-entry ()
  "Find the next translated entry, wrapping around if necessary."
  (interactive)
  (if (= po-translated-counter 0)
      (error (_"There is no such entry"))
    (po-next-entry-with-regexp po-any-msgstr-block-regexp t)
    (po-find-span-of-entry)
    (while (not (eq po-entry-type 'translated))
      (po-next-entry-with-regexp po-any-msgstr-block-regexp t)
      (po-find-span-of-entry))))

(defun po-previous-translated-entry ()
  "Find the previous translated entry, wrapping around if necessary."
  (interactive)
  (if (= po-translated-counter 0)
      (error (_"There is no such entry"))
    (po-previous-entry-with-regexp po-any-msgstr-block-regexp t)
    (po-find-span-of-entry)
    (while (not (eq po-entry-type 'translated))
      (po-previous-entry-with-regexp po-any-msgstr-block-regexp t)
      (po-find-span-of-entry))))

;; Auto-selection feature.

(defun po-auto-select-entry ()
  "Select the next entry having the same type as the current one.
If none, wrap from the beginning of the buffer with another type,
going from untranslated to fuzzy, and from fuzzy to obsolete.
Plain translated entries are always disregarded unless there are
no entries of the other types."
  (interactive)
  (po-find-span-of-entry)
  (goto-char po-end-of-entry)
  (if (and (= po-untranslated-counter 0)
           (= po-fuzzy-counter 0)
           (= po-obsolete-counter 0))
      ;; All entries are plain translated.  Next entry will do, or
      ;; wrap around if there is none.
      (if (re-search-forward po-any-msgstr-block-regexp nil t)
          (goto-char (match-beginning 0))
        (goto-char (point-min)))
    ;; If over a translated entry, look for an untranslated one first.
    ;; Else, look for an entry of the same type first.
    (let ((goal (if (eq po-entry-type 'translated)
                    'untranslated
                  po-entry-type)))
      (while goal
        ;; Find an untranslated entry, or wrap up for a fuzzy entry.
        (if (eq goal 'untranslated)
            (if (and (> po-untranslated-counter 0)
                     (re-search-forward po-untranslated-regexp nil t))
                (progn
                  (goto-char (match-beginning 0))
                  (setq goal nil))
              (goto-char (point-min))
              (setq goal 'fuzzy)))
        ;; Find a fuzzy entry, or wrap up for an obsolete entry.
        (if (eq goal 'fuzzy)
            (if (and (> po-fuzzy-counter 0)
                     (re-search-forward po-fuzzy-regexp nil t))
                (progn
                  (goto-char (match-beginning 0))
                  (setq goal nil))
              (goto-char (point-min))
              (setq goal 'obsolete)))
        ;; Find an obsolete entry, or wrap up for an untranslated entry.
        (if (eq goal 'obsolete)
            (if (and (> po-obsolete-counter 0)
                     (re-search-forward po-obsolete-msgstr-regexp nil t))
                (progn
                  (goto-char (match-beginning 0))
                  (setq goal nil))
              (goto-char (point-min))
              (setq goal 'untranslated))))))
  ;; Display this entry nicely.
  (po-current-entry))
