;; ;; Copyright 2011 Yusuke KAWAKAMI, Akihiro ARISAWA ;; ;; Licensed under the Apache License, Version 2.0 (the “License”); ;; you may not use this file except in compliance with the License. ;; You may obtain a copy of the License at ;; ;; www.apache.org/licenses/LICENSE-2.0 ;; ;; Unless required by applicable law or agreed to in writing, software ;; distributed under the License is distributed on an “AS IS” BASIS, ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;; See the License for the specific language governing permissions and ;; limitations under the License.

;; ;; evernote-mode home page is at: code.google.com/p/emacs-evernote-mode/ ;; Author: Yusuke KAWAKAMI, Akihiro ARISAWA ;; Version: 0.41 ;; Keywords: tools, emacs, evernote, bookmark

;; This emacs lisp offers the interactive functions to open, edit, and update notes of Evernote. ;; The minor mode Evernote-mode is applied to the buffer editing a note of Evernote. ;; ;; Please copy this file into emacs lisp library directory or place it in ;; a directory (for example “~/lisp”) and write $HOME/.emacs like this. ;; ;; (add-to-list 'load-path “~/lisp”) ;; (require 'evernote-mode) ;; (setq evernote-enml-formatter-command '(“w3m” “-dump” “-I” “UTF8” “-O” “UTF8”)) ;; (global-set-key “C-cec” 'evernote-create-note) ;; (global-set-key “C-ceo” 'evernote-open-note) ;; (global-set-key “C-ces” 'evernote-search-notes) ;; (global-set-key “C-ceS” 'evernote-do-saved-search) ;; (global-set-key “C-cew” 'evernote-write-note) ;; (global-set-key “C-cep” 'evernote-post-region) ;; (global-set-key “C-ceb” 'evernote-browser) ;; ;; There is one hooks, evernotes-mode-hook. ;; The usage of the hook is shown as follows. ;; ;; (setq evernote-mode-hook ;; '(lambda () ;; (…)))

;;; Code

(require 'tree-widget)

(defun enh-bookmark-supported ()

(or (> emacs-major-version 23)
    (and (= emacs-major-version 23)
         (>= emacs-minor-version 1))))

(when (enh-bookmark-supported)

(declare-function bookmark-default-handler "bookmark" (bmk-record))
(declare-function bookmark-get-bookmark-record "bookmark" (bookmark))
(declare-function bookmark-make-record-default
                  "bookmark" (&optional no-file no-context posn))
(declare-function bookmark-name-from-full-record
                  "bookmark" (full-record)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Macros ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro enh-command-with-auth (&rest body)

`(let (error-code
       (try-func (lambda () ,@body)))
   (setq error-code
         (catch 'error
           (progn
             (funcall try-func)
             t)))
   (cond
    ((eq error-code t)
     t)
    ((or (eq error-code enh-command-error-not-authed)
         (eq error-code enh-command-error-invalid-auth)
         (eq error-code enh-command-error-auth-expired))
     (let ((error-code
            (catch 'error
              (progn
                (evernote-login)
                t))))
       (if (eq error-code t)
           (progn
             (let (error-code)
               (setq error-code
                     (catch 'error
                       (progn
                         (funcall try-func)
                         t)))
               (unless (eq error-code t)
                 (message enh-command-last-error-message))))
         (message enh-command-last-error-message))))
    (t
     (message enh-command-last-error-message)))))

(defmacro enh-base-create-note-interactive (ask-notebook)

"Common interactive procecure of creating a note"
`(progn
   (if (called-interactively-p) (enh-clear-onmem-cache))
   (enh-command-with-auth
    (switch-to-buffer (enh-base-create-note-common "" ,ask-notebook t t nil)))))

(defmacro enh-base-write-note-interactive (ask-notebook)

"Common interactive procecure of writing a note"
`(progn
   (if (called-interactively-p) (enh-clear-onmem-cache))
   (enh-command-with-auth
    (enh-base-create-note-common (buffer-name) ,ask-notebook ,nil t t))))

(defmacro enh-base-post-region-interactive (begin end arg ask-notebook)

"Common interactive procedure of posting a note"
`(progn
   (if (called-interactively-p) (enh-clear-onmem-cache))
   (enh-command-with-auth
    (save-excursion
      (save-restriction
        (narrow-to-region ,begin ,end)
        (if (and (enutil-neq ,arg nil) (enutil-neq ,arg 1))
            (pop-to-buffer (enh-base-create-note-common (buffer-name) ,ask-notebook t t t))
          (enh-base-create-note-common (buffer-name) ,ask-notebook nil nil t)))))))

(defmacro enutil-neq (&rest exprs)

`(not (eq ,@exprs)))

(defmacro enutil-nequal (&rest exprs)

`(not (equal ,@exprs)))

(defmacro enutil-push (elem list)

`(setq ,list (cons ,elem ,list)))

(defmacro enutil-pop (list)

`(prog1
     (car ,list)
   (setq ,list (cdr ,list))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; User options ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar evernote-username nil

"*An username of your evernote")

(defvar evernote-enml-formatter-command nil

"*Formatter for xhtml")

(defvar evernote-ruby-command “ruby”

"*Path of the ruby command")

(defvar evernote-password-cache nil

"*Non-nil means that password cache is enabled.

It is recommended to encrypt the file with EasyPG.“)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interface for evernote-browsing-mode. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar evernote-browsing-page-list nil

"Buffer list of evernote browsing mode.")

(defvar evernote-browsing-current-page nil

"Current buffer of evernote browsing mode.")

(defvar evernote-browsing-mode-map

(let ((map (make-sparse-keymap)))
  (set-keymap-parent map widget-keymap)
  map)
"Keymap used in evernote browsing mode.")

(define-key evernote-browsing-mode-map “o” 'widget-button-press) (define-key evernote-browsing-mode-map “n” 'evernote-browsing-open-next-note) (define-key evernote-browsing-mode-map “p” 'evernote-browsing-open-previous-note) (define-key evernote-browsing-mode-map “N” 'evernote-browsing-list-notebooks) (define-key evernote-browsing-mode-map “t” 'evernote-browsing-list-tags) (define-key evernote-browsing-mode-map “S” 'evernote-browsing-list-searches) (define-key evernote-browsing-mode-map “s” 'evernote-browsing-search-notes) (define-key evernote-browsing-mode-map “b” 'evernote-browsing-prev-page) (define-key evernote-browsing-mode-map “f” 'evernote-browsing-next-page) (define-key evernote-browsing-mode-map “d” 'evernote-browsing-delete-page) (define-key evernote-browsing-mode-map “l” 'evernote-browsing-reflesh) ;(define-key evernote-browsing-mode-map “e” 'evernote-browsing-change-edit-mode) ;(define-key evernote-browsing-mode-map “r” 'evernote-browsing-rename-note) ;(define-key evernote-browsing-mode-map “d” 'evernote-browsing-delete-note)

(defun evernote-browsing-mode ()

"Major mode for browsing notes."
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(setq evernote-browsing-mode t)
(use-local-map evernote-browsing-mode-map)
(setq truncate-lines t
      major-mode 'evernote-browsing-mode
      mode-name "Evernote-Browsing")
(goto-char (point-min)))

(defun evernote-browser ()

"Open an evernote browser"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(enh-browsing-update-page-list)
(if evernote-browsing-current-page
    (enutil-move-cursor-to-window evernote-browsing-current-page)
  (evernote-browsing-list-tags)))

(defun evernote-browsing-open-next-note ()

(interactive)
(next-line)
(when (eq enh-browsing-page-type 'note-list)
  (condition-case nil
      (widget-button-press (point))
    (error nil))))

(defun evernote-browsing-open-previous-note ()

(interactive)
(previous-line)
(when (eq enh-browsing-page-type 'note-list)
  (condition-case nil
      (widget-button-press (point))
    (error nil))))

(defun evernote-browsing-list-notebooks ()

"List tags"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(enh-browsing-update-page-list)
(let ((page (enh-browsing-get-page-of-type 'notebook-list)))
  (if page
      (progn
        (setq evernote-browsing-current-page page)
        (switch-to-buffer page))
    (enh-browsing-push-page
     (enh-browsing-create-page 'notebook-list "All Notebooks")))))

(defun evernote-browsing-list-tags ()

"List tags"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(enh-browsing-update-page-list)
(let ((page (enh-browsing-get-page-of-type 'tag-list)))
  (if page
      (progn
        (setq evernote-browsing-current-page page)
        (switch-to-buffer page))
    (enh-browsing-push-page
     (enh-browsing-create-page 'tag-list "All Tags")))))

(defun evernote-browsing-list-searches ()

"List saved searches"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(enh-browsing-update-page-list)
(let ((page (enh-browsing-get-page-of-type 'search-list)))
  (if page
      (progn
        (setq evernote-browsing-current-page page)
        (switch-to-buffer page))
    (enh-browsing-push-page
     (enh-browsing-create-page 'search-list "All Saved Searches")))))

(defun evernote-browsing-search-notes ()

"Search notes"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(let (note-attrs (query (read-string "Query:")))
  (enh-command-with-auth
   (setq note-attrs
         (enh-command-get-note-attrs-from-query query)))
  (enh-browsing-update-page-list)
  (enh-browsing-push-page
   (enh-browsing-create-page 'note-list
                             (format "Query Result of: %s" query)
                             note-attrs
                             `(lambda () (enh-command-get-note-attrs-from-query ,query))))))

(defun evernote-browsing-prev-page ()

"Move to the prev page"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when (eq major-mode 'evernote-browsing-mode)
  (enh-browsing-update-page-list)
  (let ((prev-page (enh-browsing-get-prev-page)))
    (if prev-page
        (progn
          (setq evernote-browsing-current-page prev-page)
          (switch-to-buffer prev-page))
      (message "[No more previous page]")))))

(defun evernote-browsing-next-page ()

"Move to the next page"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when (eq major-mode 'evernote-browsing-mode)
  (enh-browsing-update-page-list)
  (let ((next-page (enh-browsing-get-next-page)))
    (if next-page
        (progn
          (setq evernote-browsing-current-page next-page)
          (switch-to-buffer next-page))
      (message "[No more next page]")))))

(defun evernote-browsing-delete-page ()

"Delete current page"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when (eq major-mode 'evernote-browsing-mode)
  (kill-buffer (current-buffer))
  (enh-browsing-update-page-list)
  (switch-to-buffer evernote-browsing-current-page)))

(defun evernote-browsing-reflesh ()

(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when (eq major-mode 'evernote-browsing-mode)
  (when enh-browsing-page-data-refresh-closure
    (setq enh-browsing-page-data nil))
  (funcall enh-browsing-page-setup-func)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interface for evernote-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar evernote-mode nil

"Non-nil if Evernote mode is enabled.")

(make-variable-buffer-local 'evernote-mode)

(defvar evernote-note-guid nil

"Note guid of the buffer")

(make-variable-buffer-local 'evernote-note-guid)

(defvar evernote-note-modified-name nil

"Modified name of the note before saving")

(make-variable-buffer-local 'evernote-note-modified-name)

(defvar evernote-note-modified-notebook-guid nil

"Modified noteobok guid of the note before saving")

(make-variable-buffer-local 'evernote-note-modified-notebook-guid)

(defvar evernote-note-modified-edit-mode nil

"Modified edit-mode of the note before saving")

(make-variable-buffer-local 'evernote-note-modified-edit-mode)

(defvar evernote-note-is-modified-tag-names nil

"Modified tag-names of the note before saving")

(make-variable-buffer-local 'evernote-note-is-modified-tag-names)

(defvar evernote-note-modified-tag-names nil

"Modified tag-names of the note before saving")

(make-variable-buffer-local 'evernote-note-modified-tag-names)

(defvar evernote-note-xhtml-mode-content nil

"Note contents as a string of XHTML")

(make-variable-buffer-local 'evernote-note-xhtml-mode-content)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Menu Settings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar evernote-mode-display-menu t

"display the evernote menu on the menubar if this variable is not nil")

(defvar evernote-mode-map (make-sparse-keymap)

"Keymap used in evernote mode.")

(define-key evernote-mode-map “C-xC-s” 'evernote-save-note) (define-key evernote-mode-map “C-cen” 'evernote-change-notebook) (define-key evernote-mode-map “C-cet” 'evernote-edit-tags) (define-key evernote-mode-map “C-cee” 'evernote-change-edit-mode) (define-key evernote-mode-map “C-cer” 'evernote-rename-note) (define-key evernote-mode-map “C-ced” 'evernote-delete-note) (define-key evernote-mode-map “C-xC-q” 'evernote-toggle-read-only)

(defun enh-menu-is-visible-on-ordinary-mode ()

(not evernote-browsing-mode))

(defun enh-menu-is-visible-on-evernote-mode ()

(and evernote-mode (not evernote-browsing-mode)))

(defun enh-menu-is-visible-on-evernote-browsing-mode ()

evernote-browsing-mode)

(let ((menu-bar-map (make-sparse-keymap “Evernote”)))

(define-key-after global-map [menu-bar evernote]
  `(menu-item "Evernote" ,menu-bar-map
              :visible evernote-mode-display-menu)
  'tools)
(define-key menu-bar-map [browser]
  '(menu-item "Evernote Browser" evernote-browser
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [seperator-0]
  '(menu-item "--" nil
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [toggle-read-only]
  '(menu-item "Toggle Read Only" evernote-toggle-read-only
              :visible (enh-menu-is-visible-on-evernote-mode)))
(define-key menu-bar-map [delete-note]
  '(menu-item "Delete Note" evernote-delete-note
              :visible (enh-menu-is-visible-on-evernote-mode)))
(define-key menu-bar-map [rename-note]
  '(menu-item "Rename Note" evernote-rename-note
              :visible (enh-menu-is-visible-on-evernote-mode)))
(define-key menu-bar-map [change-notebook]
  '(menu-item "Change Notebook" evernote-change-notebook
              :visible (enh-menu-is-visible-on-evernote-mode)))
(define-key menu-bar-map [change-edit-mode]
  '(menu-item "Change Edit Mode" evernote-change-edit-mode
              :visible (enh-menu-is-visible-on-evernote-mode)))
(define-key menu-bar-map [edit-tag]
  '(menu-item "Edit Tag" evernote-edit-tags
              :visible (enh-menu-is-visible-on-evernote-mode)))
(define-key menu-bar-map [save-note]
  '(menu-item "Save Note" evernote-save-note
              :visible (enh-menu-is-visible-on-evernote-mode)))
(define-key menu-bar-map [seperator-1]
  '(menu-item "--" nil
              :visible (enh-menu-is-visible-on-evernote-mode)))
(define-key menu-bar-map [edit-notebook]
  '(menu-item "Edit Notebook" evernote-edit-notebook
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [create-notebook]
  '(menu-item "Create Notebook" evernote-create-notebook
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [seperator-2]
  '(menu-item "--" nil
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [edit-search]
  '(menu-item "Edit Saved Search" evernote-edit-search
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [create-search]
  '(menu-item "Create Saved Search" evernote-create-search
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [do-saved-search]
  '(menu-item "Do Saved Search" evernote-do-saved-search
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [search-note]
  '(menu-item "Search Note" evernote-search-notes
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [seperator-3]
  '(menu-item "--" nil
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [post-region-in-notebook]
  '(menu-item "Post Region (w/ notebook param)" evernote-post-region-in-notebook
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [post-region]
  '(menu-item "Post Region" evernote-post-region
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [write-note-in-notebook]
  '(menu-item "Write Note (w/ notebook param)" evernote-write-note-in-notebook
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [write-note]
  '(menu-item "Write Note" evernote-write-note
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [create-note-in-notebook]
  '(menu-item "Create Note (w/ notebook param)" evernote-create-note-in-notebook
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [create-note]
  '(menu-item "Create Note" evernote-create-note
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [open-note-in-notebook]
  '(menu-item "Open Note (w/ notebook param)" evernote-open-note-in-notebook
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [open-note]
  '(menu-item "Open Note" evernote-open-note
              :visible (enh-menu-is-visible-on-ordinary-mode)))
(define-key menu-bar-map [browsing-prev-page]
  '(menu-item "Prev Page" evernote-browsing-prev-page
              :visible (enh-menu-is-visible-on-evernote-browsing-mode)))
(define-key menu-bar-map [browsing-next-page]
  '(menu-item "Next Page" evernote-browsing-next-page
              :visible (enh-menu-is-visible-on-evernote-browsing-mode)))
(define-key menu-bar-map [browsing-search-notes]
  '(menu-item "Searche Notes" evernote-browsing-search-notes
              :visible (enh-menu-is-visible-on-evernote-browsing-mode)))
(define-key menu-bar-map [browsing-list-searches]
  '(menu-item "List Saved Searches" evernote-browsing-list-searches
              :visible (enh-menu-is-visible-on-evernote-browsing-mode)))
(define-key menu-bar-map [browsing-list-tags]
  '(menu-item "List Tags" evernote-browsing-list-tags
              :visible (enh-menu-is-visible-on-evernote-browsing-mode)))
(define-key menu-bar-map [browsing-list-notebooks]
  '(menu-item "List Notebooks" evernote-browsing-list-notebooks
              :visible (enh-menu-is-visible-on-evernote-browsing-mode))))

(defun evernote-mode (&optional guid)

"Toggle Evernote mode, a minor mode for using evernote functions."
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(or (assq 'evernote-mode minor-mode-alist)
    (setq minor-mode-alist (cons '(evernote-mode " Evernote") minor-mode-alist)))
(or (assq 'evernote-mode minor-mode-map-alist)
    (setq minor-mode-map-alist
          (cons (cons 'evernote-mode evernote-mode-map) minor-mode-map-alist)))
(let ((modified (buffer-modified-p)))
  (set-buffer-file-coding-system 'utf-8)
  (set-buffer-modified-p modified))
(setq evernote-mode (not evernote-mode))
(if evernote-mode
    (progn
      (when guid (setq evernote-note-guid guid))
      (enh-base-update-mode-line
       evernote-note-modified-notebook-guid
       evernote-note-is-modified-tag-names
       evernote-note-modified-tag-names
       evernote-note-modified-edit-mode)
      (add-hook 'after-save-hook
                'evernote-mode-after-save-hook
                nil t)
      (add-hook 'change-major-mode-hook
                'evernote-mode-change-major-mode-hook
                nil t)
      (when (enh-bookmark-supported)
        (enh-bookmark-prepare))
      (run-hooks 'evernote-mode-hook))
  (progn
    (setq evernote-note-guid nil)
    (setq vc-mode nil)
    (remove-hook 'after-save-hook
                 'evernote-mode-after-save-hook)
    (remove-hook 'change-major-mode-hook
                 'evernote-mode-change-major-mode-hook))))

(defun evernote-login ()

"Login"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(unwind-protect
    (let* ((cache (enh-password-cache-load))
           (usernames (mapcar #'car cache))
           (username (or evernote-username
                         (read-string "Evernote user name:"
                                      (car usernames) 'usernames)))
           (cache-passwd (enutil-aget username cache)))
      (unless (and cache-passwd
                   (eq (catch 'error
                         (progn
                           (enh-command-login username cache-passwd)
                           t))
                       t))
        (let* ((passwd (read-passwd "Passwd:")))
          (enh-command-login username passwd)
          (setq evernote-username username)
          (enh-password-cache-save (enutil-aset username cache passwd)))))
  (enh-password-cache-close)))

(defun evernote-open-note (&optional ask-notebook)

"Open a note"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(enh-command-with-auth
 (let* ((notebook-guid (and ask-notebook
                            (enutil-aget 'guid (enh-read-notebook))))
        (tag-guids (enh-read-tag-guids
                    "Tags used for search (comma separated form. default search all tags):"))
        (note-attrs
         (enh-command-get-note-attrs-from-notebook-and-tag-guids notebook-guid tag-guids)))
   (enh-base-open-note-common (enh-base-read-note-attr note-attrs))
   (enh-browsing-update-page-list)
   (enh-browsing-push-page
    (enh-browsing-create-page 'note-list
                              (if tag-guids
                                  (format "Notes with tag: %s"
                                          (enh-tag-guids-to-comma-separated-names tag-guids))
                                "All notes")
                              note-attrs
                              `(lambda () (enh-command-get-note-attrs-from-notebook-and-tag-guids
                                           ,notebook-guid ',tag-guids)))
    t))))

(defun evernote-open-note-in-notebook ()

"Open a note in the specified notebook"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(evernote-open-note t))

(defun evernote-search-notes ()

"Search notes with query and open a note among them"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(let ((query (read-string "Query:")))
  (enh-command-with-auth
   (let ((note-attrs
          (enh-command-get-note-attrs-from-query
           query)))
     (enh-base-open-note-common (enh-base-read-note-attr note-attrs))
     (enh-browsing-update-page-list)
     (enh-browsing-push-page
      (enh-browsing-create-page 'note-list
                                (format "Query Result of: %s" query)
                                note-attrs
                                `(lambda () (enh-command-get-note-attrs-from-query ,query)))
      t)))))

(defun evernote-do-saved-search ()

"Do a saved search and open a note"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(enh-command-with-auth
 (let* ((search-attr (enh-read-saved-search))
        (note-attrs
         (enh-command-get-note-attrs-from-query
          (enutil-aget 'query search-attr))))
   (enh-base-open-note-common (enh-base-read-note-attr note-attrs))
   (enh-browsing-update-page-list)
   (enh-browsing-push-page
    (enh-browsing-create-page 'note-list
                              (format "Query Result of Saved Search: %s"
                                      (enutil-aget 'name search-attr))
                              note-attrs
                              `(lambda () (enh-command-get-note-attrs-from-query
                                           ,(enutil-aget 'query search-attr))))
    t))))

(defun evernote-create-note ()

"Create a note"
(interactive)
(enh-base-create-note-interactive nil))

(defun evernote-create-note-in-notebook ()

"Create a note in the specified notebook"
(interactive)
(enh-base-create-note-interactive t))

(defun evernote-write-note ()

"Write buffer to a note"
(interactive)
(enh-base-write-note-interactive nil))

(defun evernote-write-note-in-notebook ()

"Write buffer to a note in the specified notebook"
(interactive)
(enh-base-write-note-interactive t))

(defun evernote-post-region (begin end arg)

"Post the region as a note"
(interactive "r\np")
(enh-base-post-region-interactive begin end arg nil))

(defun evernote-post-region-in-notebook (begin end arg)

"Post the region as a note in the specified notebook"
(interactive "r\np")
(enh-base-post-region-interactive begin end arg t))

(defun evernote-save-note ()

"Save a note"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(cond
 ((not evernote-mode)
  nil) ; do nothing
 ((not (buffer-modified-p))
  (message "(No changes need to be saved)"))
 (buffer-read-only
  (ding)
  (message "Unset read-only before you save"))
 (t
  (enh-command-with-auth
   (enh-base-update-note-common
    (current-buffer)   ; contents
    evernote-note-guid ; guid
    (if evernote-note-modified-name ; name
        evernote-note-modified-name
      nil)
    evernote-note-modified-notebook-guid ; notebook-guid
    evernote-note-is-modified-tag-names ; is-tag-updated
    (if evernote-note-is-modified-tag-names ; tag-names
        evernote-note-modified-tag-names
      nil)
    (if evernote-note-modified-edit-mode ; edit-mode
        evernote-note-modified-edit-mode
      nil))
   (if (or evernote-note-modified-name
           evernote-note-modified-notebook-guid
           evernote-note-is-modified-tag-names
           evernote-note-modified-edit-mode)
       (enh-browsing-reflesh-page 'note-list))
   (setq evernote-note-modified-name nil
         evernote-note-modified-notebook-guid nil
         evernote-note-is-modified-tag-names nil
         evernote-note-modified-tag-names nil
         evernote-note-modified-edit-mode nil)
   (set-buffer-modified-p nil)))))

(defun evernote-change-notebook ()

"Change notebook to which this note belongs"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when evernote-mode
  (let* (current-notebook-guid current-notebook-name next-notebook-guid)
    (setq current-notebook-guid
          (or evernote-note-modified-notebook-guid
              (enutil-aget 'notebookGuid (enh-get-note-attr evernote-note-guid))))
    (setq current-notebook-name
          (enutil-aget 'name (enh-get-notebook-attr current-notebook-guid)))
    (setq next-notebook-guid
          (enutil-aget 'guid (enh-read-notebook current-notebook-name)))
    (when (not (string= current-notebook-guid next-notebook-guid))
      (setq evernote-note-modified-notebook-guid next-notebook-guid)
      (enh-base-update-mode-line evernote-note-modified-notebook-guid
                                 evernote-note-is-modified-tag-names
                                 evernote-note-modified-tag-names
                                 evernote-note-modified-edit-mode)
      (set-buffer-modified-p t)))))

(defun evernote-edit-tags ()

"Add or remove tags from/to the note"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when evernote-mode
  (setq evernote-note-modified-tag-names
        (enh-read-tag-names
         "Change attached Tags (comma separated form):"
         evernote-note-guid))
  (setq evernote-note-is-modified-tag-names t) ; this must be after enh-read-tag-names
  (enh-base-update-mode-line evernote-note-modified-notebook-guid
                             evernote-note-is-modified-tag-names
                             evernote-note-modified-tag-names
                             evernote-note-modified-edit-mode)
  (set-buffer-modified-p t)))

(defun evernote-change-edit-mode ()

"Change edit mode of the note"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when evernote-mode
  (let* ((current-edit-mode
          (or evernote-note-modified-edit-mode
              (enutil-aget 'editMode (enh-get-note-attr evernote-note-guid))))
         (next-edit-mode (enh-read-edit-mode current-edit-mode))
         (need-change nil))
    (when (not (string= current-edit-mode next-edit-mode))
      (cond
       ;; XHTML mode, Confirm the buffer is saved.
       ((and (string= current-edit-mode "XHTML")
             (buffer-modified-p))
        (ding)
        (message "Save the buffer before you change edit mode"))
       ;; XHTML mode, Formatted xml.
       ((and (string= current-edit-mode "XHTML")
             buffer-read-only)
        (when (y-or-n-p "Changing text mode will remove all format information. Continue? ")
          (setq evernote-note-xhtml-mode-content nil)
          (setq buffer-read-only nil)
          (setq need-change t)))
       ;; XHTML mode, raw xml.
       ((and (string= current-edit-mode "XHTML")
             (not buffer-read-only))
        (setq evernote-note-xhtml-mode-content nil)
        (setq need-change t))
       ;; HTML mode.
       ((string= current-edit-mode "TEXT")
        (setq buffer-read-only nil)
        (setq need-change t))))
    (when need-change
      (setq evernote-note-modified-edit-mode next-edit-mode)
      (enh-base-update-mode-line
       evernote-note-modified-notebook-guid
       evernote-note-is-modified-tag-names
       evernote-note-modified-tag-names
       evernote-note-modified-edit-mode)
      (set-buffer-modified-p t)))))

(defun evernote-rename-note ()

"Rename a note"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when evernote-mode
  (setq evernote-note-modified-name
        (read-string "New note name:"
                     (enutil-aget 'title (enh-get-note-attr evernote-note-guid))))
  (rename-buffer evernote-note-modified-name t)
  (enh-base-change-major-mode-from-note-name evernote-note-modified-name)
  (set-buffer-modified-p t)))

(defun evernote-delete-note ()

"Delete a note"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(if (and evernote-mode
         (y-or-n-p "Do you really want to remove this note? "))
    (enh-command-with-auth
     (enh-command-delete-note evernote-note-guid)
     (kill-buffer (current-buffer)))))

(defun evernote-create-notebook ()

"Create a notebook"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(let ((name (read-string "Notebook Name:")))
  (enh-command-with-auth
   (enh-command-create-notebook name nil))
  (enh-browsing-reflesh-page 'notebook-list)))

(defun evernote-edit-notebook ()

"Create a notebook"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(enh-command-with-auth
 (let* ((notebook-alist (enh-get-notebook-name-attr-alist))
        (notebook-attr
         (enutil-aget
          (completing-read
           "Notebook:"
           notebook-alist
           nil t)
          notebook-alist)))
   (enh-command-update-notebook
    (enutil-aget 'guid notebook-attr)
    (read-string "New notebook name:"
                 (enutil-aget 'name notebook-attr))
    (yes-or-no-p "Use as the default notebook:"))))
(clrhash enh-notebook-info)
(enh-browsing-reflesh-page 'notebook-list))

(defun evernote-create-search ()

"Create a saved search"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(let ((name (read-string "Saved Search Name:"))
      (query (read-string "Query:")))
  (enh-command-with-auth
   (enh-command-create-search name query))
  (enh-browsing-reflesh-page 'search-list)))

(defun evernote-edit-search ()

"Create a saved search"
(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(enh-command-with-auth
 (let* ((search-alist (enh-get-search-name-attr-alist))
        (search-attr
         (enutil-aget
          (completing-read
           "Saved search:"
           search-alist
           nil t)
          search-alist)))
   (enh-command-update-search
    (enutil-aget 'guid search-attr)
    (read-string "New Saved search name:"
                 (enutil-aget 'name search-attr))
    (read-string "New Query:"
                 (enutil-aget 'query search-attr)))))
(clrhash enh-search-info)
(enh-browsing-reflesh-page 'search-list))

(defun evernote-toggle-read-only ()

(interactive)
(if (called-interactively-p) (enh-clear-onmem-cache))
(when evernote-mode
  (if (string= (or evernote-note-modified-edit-mode
                   (enutil-aget 'editMode (enh-get-note-attr evernote-note-guid)))
               "XHTML")
      (if buffer-read-only
          (progn
            (setq buffer-read-only nil)
            (let ((orig-buffer-modified-p (buffer-modified-p)))
              (erase-buffer)
              (insert evernote-note-xhtml-mode-content)
              (goto-char (point-min))
              (set-buffer-modified-p orig-buffer-modified-p)))
        (if (buffer-modified-p)
            (progn
              (ding)
              (message "Save the buffer before you toggle read only"))
          (setq evernote-note-xhtml-mode-content
                (buffer-substring (point-min) (point-max)))
          (erase-buffer)
          (enh-format-enml evernote-note-xhtml-mode-content (current-buffer))
          (goto-char (point-min))
          (set-buffer-modified-p nil)
          (setq buffer-read-only t)))
    (setq buffer-read-only (not buffer-read-only)))
  (force-mode-line-update)))

(defvar evernote-mode-info-for-changing-major-mode nil

"Temporal values used when changing the major mode")

(defun evernote-mode-after-save-hook ()

"After save hook for evernote mode. This invalid evernote-mode"
(if evernote-mode
    (evernote-mode)))

(defun evernote-mode-change-major-mode-hook ()

"Change major mode hook for evernote mode. This records the note info to the global variable to restore them after changing the major mode"
(if evernote-mode
    (setq evernote-mode-info-for-changing-major-mode
          (list
           (cons 'guid  evernote-note-guid)
           (cons 'modified-name evernote-note-modified-name)
           (cons 'modified-notebook-guid evernote-note-modified-notebook-guid)
           (cons 'is-modified-tag-names evernote-note-is-modified-tag-names)
           (cons 'modified-tag-names evernote-note-modified-tag-names)
           (cons 'modified-edit-mode evernote-note-modified-edit-mode)
           (cons 'note-xhtml-mode-content evernote-note-xhtml-mode-content)))))

(defun evernote-mode-after-change-major-mode-hook ()

"After change major mode hook for evernote mode. This restore the note info after changing the major mode"
(if evernote-mode-info-for-changing-major-mode
    (progn
      (setq evernote-note-modified-name
            (enutil-aget 'modified-name evernote-mode-info-for-changing-major-mode))
      (setq evernote-note-modified-notebook-guid
            (enutil-aget 'modified-notebook-guid evernote-mode-info-for-changing-major-mode))
      (setq evernote-note-is-modified-tag-names
            (enutil-aget 'is-modified-tag-names evernote-mode-info-for-changing-major-mode))
      (setq evernote-note-modified-tag-names
            (enutil-aget 'modified-tag-names evernote-mode-info-for-changing-major-mode))
      (setq evernote-note-modified-edit-mode
            (enutil-aget 'modified-edit-mode evernote-mode-info-for-changing-major-mode))
      (evernote-mode ; this must be after setting evernote-note-modified-xxx
       (enutil-aget 'guid evernote-mode-info-for-changing-major-mode))
      (setq evernote-note-xhtml-mode-content
            (enutil-aget 'note-xhtml-mode-content evernote-mode-info-for-changing-major-mode))
      (setq evernote-mode-info-for-changing-major-mode nil))))

(add-hook 'after-change-major-mode-hook

'evernote-mode-after-change-major-mode-hook)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interface for inputing the note name in the minibuffer. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar evernote-read-note-map

(let ((map (make-sparse-keymap)))
  (set-keymap-parent map minibuffer-local-completion-map)
  map))

(define-key evernote-read-note-map [tab] 'evernote-read-note-completion) (define-key evernote-read-note-map “C-i” 'evernote-read-note-completion) (define-key evernote-read-note-map “C-m” 'evernote-read-note-finish) (define-key evernote-read-note-map “ ” 'self-insert-command)

(defun evernote-read-note-completion ()

"Complete note name and display completion list"
(interactive)
(let (word result start)
  (setq word (enutil-get-minibuffer-string))
  (setq result (try-completion word enh-base-displayed-name-attr-alist))
  (cond
   ((eq result t)
    (enutil-minibuffer-tmp-message "[Sole Completion]"))
   ((eq result nil)
    (ding)
    (enutil-minibuffer-tmp-message "[No Match]"))
   ((string= result word)
    (enh-base-display-note-completion-buf
     enh-base-displayed-name-formatted-name-alist
     word))
   (t (enutil-set-minibuffer-string result)
      (end-of-buffer)
      (if (eq t
              (try-completion result
                              enh-base-displayed-name-attr-alist))
          nil
        (enutil-minibuffer-tmp-message "[Complete, but not unique]"))))))

(defun evernote-read-note-finish ()

"Finish input note name"
(interactive)
(if (assoc
     (enutil-get-minibuffer-string)
     enh-base-displayed-name-attr-alist)
    (progn
      (let ((completion-buf (get-buffer "*Evernote-Completions*")))
        (if completion-buf
            (kill-buffer completion-buf)))
      (exit-minibuffer))
  (enutil-minibuffer-tmp-message "[No Match]")))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interface for evernote-search-mode. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar evernote-search-mode-map

(let ((map (make-sparse-keymap)))
  (set-keymap-parent map global-map)
  map)
"Keymap used in evernote search mode.")

(define-key evernote-search-mode-map “C-m” 'evernote-select-note-in-search-mode)

(defvar evernote-search-mode-formatted-name-displayed-name-alist nil

"Alist from formatted names to names used only in evernote-search-mode buffer")

(make-variable-buffer-local 'evernote-search-mode-formatted-name-displayed-name-alist)

(defun evernote-search-mode ()

"Major mode for selecting a note."
(interactive)
(use-local-map evernote-search-mode-map)
(setq buffer-read-only t
      truncate-lines t
      major-mode 'evernote-search-mode
      mode-name "Evernote-Search")
(goto-char (point-min)))

(defun evernote-select-note-in-search-mode ()

"Select a note name on this buffer and input it into the mini buffer"
(interactive)
(if (active-minibuffer-window)
    (save-excursion
      (let (displayed-name)
        (setq displayed-name
              (enutil-aget
               (enutil-get-current-line-string)
               evernote-search-mode-formatted-name-displayed-name-alist))
        (if displayed-name
            (progn
              (kill-buffer (current-buffer))
              (enutil-set-minibuffer-string displayed-name)
              (exit-minibuffer)))))
  (kill-buffer (current-buffer))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; For Anything ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun anything-c-evernote-title-set-candidates ()

(let ((buffer (get-buffer enh-command-output-buffer-name))
      candidates)
  (when buffer ; create candidates only when logined.
    (enh-command-with-auth
     (setq candidates
           (enh-command-get-note-attrs-from-query
             ; Put '*' to match the start of a word.
             ; Note: A wildcard is only permitted at the end of the term,
             ;       not at the beginning or middle for scalability reasons on the service
            (format "intitle:\"%s\"*" anything-input))))
    candidates)))

(defun anything-c-evernote-title-candidate-transformer (candidates)

(mapcar (lambda (cand)
          (cons (enutil-aget 'title cand) cand))
        candidates))

(defun anything-c-evernote-title-action (candidate)

(enh-base-open-note-common candidate))

(defvar anything-c-source-evernote-title

'((name . "Evernote Title")
  (candidates . anything-c-evernote-title-set-candidates)
  (candidate-transformer . anything-c-evernote-title-candidate-transformer)
  (action . (("Open" . anything-c-evernote-title-action)))
  (volatile)
  (requires-pattern . 3)
  (delayed)))

(defun anything-evernote-title ()

"Preconfigured `anything' for searching notes with the note names."
(interactive)
(anything-other-buffer 'anything-c-source-evernote-title "*anything evernote title*"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helper functions for evernote-mode (enh-base-xxx) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defconst enh-base-enml-template

(concat
 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
 "<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">\n"
 "<en-note>\n"
 "</en-note>\n"))

(defun enh-base-open-note-common (note-attr)

"Common procedure of opening a note"
(let* ((note-guid (enutil-aget 'guid note-attr))
       (note-name (enutil-aget 'title note-attr))
       (note-edit-mode (enutil-aget 'editMode note-attr))
       (note-content-file (enutil-aget 'contentFile note-attr))
       (opened-buf (enh-base-find-opened-buffer note-guid)))
  (unless note-content-file
    (setq note-attr (enh-get-note-attr note-guid))
    (setq note-content-file (enutil-aget 'contentFile note-attr)))
  (if opened-buf
      (enutil-move-cursor-to-window opened-buf t)
    (let ((buf (generate-new-buffer note-name)))
      (set-buffer buf)
      (insert-file-contents note-content-file)
      (when (string= note-edit-mode "XHTML")
        (setq evernote-note-xhtml-mode-content (buffer-string))
        (erase-buffer)
        (enh-format-enml evernote-note-xhtml-mode-content (current-buffer))
        (setq buffer-read-only t))
      (evernote-mode note-guid)
      ; this must be after (evernote-mode) that setup the change major mode hooks.
      (enh-base-change-major-mode-from-note-name note-name)
      (goto-char (point-min))
      (set-buffer-modified-p nil)
      (pop-to-buffer buf)))))

(defun enh-base-create-note-common (default-note-name

                                   ask-notebook
                                   create-new-buffer
                                   change-to-evernote-mode
                                   &optional
                                   use-current-buffer-content)
"Common procedure of creating a note"
(let ((notebook-guid (and ask-notebook
                          (enutil-aget 'guid (enh-read-notebook))))
      (tag-names (enh-read-tag-names))
      (name (read-string "Note name:" default-note-name))
      (edit-mode (enh-read-edit-mode "TEXT"))
      content
      note-attr)
  (if use-current-buffer-content
      ;; create a note from a buffer.
      (progn
        (setq note-attr
              (enh-command-create-note (current-buffer)
                                       name
                                       notebook-guid
                                       tag-names
                                       edit-mode))
        (setq content (buffer-substring (point-min) (point-max))))
    ;; create a note from scratch.
    (if (string= edit-mode "TEXT") ;; edit-mode = TEXT
        (with-temp-buffer
          (setq note-attr
                (enh-command-create-note (current-buffer)
                                         name
                                         notebook-guid
                                         tag-names
                                         edit-mode)))
      (with-temp-buffer ;; edit-mode = XHTML
        (insert enh-base-enml-template)
        (setq note-attr
              (enh-command-create-note (current-buffer)
                                       name
                                       notebook-guid
                                       tag-names
                                       edit-mode))
        (setq content (buffer-substring (point-min) (point-max))))))
  (enh-update-note-and-new-tag-attrs note-attr)
  (let ((buf nil))
    (save-excursion
      (if create-new-buffer
          (progn
            (setq buf (generate-new-buffer name))
            (set-buffer buf)
            (when content (insert content)))
        (when change-to-evernote-mode
          (rename-buffer name t)))
      (when change-to-evernote-mode
        (set-visited-file-name nil) ; set-visited-file-name must be before (evernote-mode) because it changes the mode line.
        (enh-base-change-major-mode-from-note-name name)
        (if (not evernote-mode)
            (evernote-mode (enutil-aget 'guid note-attr))
          (setq evernote-note-guid (enutil-aget 'guid note-attr))
          (enh-base-update-mode-line))
        (set-buffer-modified-p nil)))
    buf)))

(defun enh-base-update-note-common (inbuf guid &optional name notebook-guid is-tag-updated tag-names edit-mode)

"Common procedure of opening a note"
(let ((attr (enh-get-note-attr guid)))
  (setq attr
        (enh-command-update-note inbuf guid name notebook-guid is-tag-updated tag-names edit-mode))
  (enh-update-note-and-new-tag-attrs attr)))

(defun enh-base-read-note-attr (note-attrs &optional display-completion)

"Prompts a note name and returns a note attribute"
(let ((name-num-hash (make-hash-table :test #'equal))
      enh-base-displayed-name-attr-alist ; used in evernote-search-mode
      enh-base-displayed-name-formatted-name-alist) ; used in evernote-search-mode
  (mapc
   (lambda (attr)
     (let (name displayed-name)
       (setq name (enutil-aget 'title attr))
       (setq displayed-name
             (enh-base-get-displayed-note-name name name-num-hash))
       (setq enh-base-displayed-name-attr-alist
             (cons (cons displayed-name attr)
                   enh-base-displayed-name-attr-alist))
       (setq enh-base-displayed-name-formatted-name-alist
             (cons (cons displayed-name
                         (format "%-30s   %-20s   %-15s   %s"
                                 (enutil-aget 'updated attr)
                                 (enutil-aget 'name
                                              (enh-get-notebook-attr (enutil-aget 'notebookGuid attr)))
                                 (enh-tag-guids-to-comma-separated-names
                                  (enutil-aget 'tagGuids attr)
                                  15)
                                 displayed-name))
                   enh-base-displayed-name-formatted-name-alist))))
   note-attrs)
  (setq enh-base-displayed-name-attr-alist
        (nreverse enh-base-displayed-name-attr-alist))
  (setq enh-base-displayed-name-formatted-name-alist
        (nreverse enh-base-displayed-name-formatted-name-alist))
  (if display-completion
      (enh-base-display-note-completion-buf
       enh-base-displayed-name-formatted-name-alist))
  (enutil-aget (read-from-minibuffer "Note:"
                                     nil evernote-read-note-map)
               enh-base-displayed-name-attr-alist)))

(defun enh-base-get-displayed-note-name (name name-hash)

"Get displayed note name from the read note name"
(let ((num (gethash name name-num-hash))
      result)
  (if num
      (progn
        (setq num (+ num 1))
        (setq result (format "%s(%d)" name num))
        (puthash name num name-num-hash))
    (setq result (substring name 0))
    (puthash name 1 name-num-hash))
  result))

(defun enh-base-display-note-completion-buf (displayed-name-formatted-name-alist &optional word)

(let (formatted-name-displayed-name-alist completion-buf)
  (setq formatted-name-displayed-name-alist
        (mapcar (lambda (displayed-name)
                  (cons
                   (enutil-aget
                    displayed-name
                    enh-base-displayed-name-formatted-name-alist)
                   displayed-name))
                (all-completions
                 (if word
                     word
                   "")
                 enh-base-displayed-name-formatted-name-alist)))
  (save-excursion
    (setq completion-buf (get-buffer-create "*Evernote-Completions*"))
    (set-buffer completion-buf)
    (enh-base-display-note-completion-list
     formatted-name-displayed-name-alist)
    (setq evernote-search-mode-formatted-name-displayed-name-alist
          formatted-name-displayed-name-alist)
    (evernote-search-mode))
  (display-buffer completion-buf)))

(defun enh-base-display-note-completion-list (formatted-name-displayed-name-alist)

"Display formatted note names on this buffer"
(setq buffer-read-only nil)
(erase-buffer)
(insert (format "total %d\n%-30s   %-20s   %-15s   %s\n\n"
                (length formatted-name-displayed-name-alist)
                "Last Modified"
                "Notebook"
                "Tags"
                "Title"))
(mapc (lambda (elem)
        (insert (car elem) "\n"))
      formatted-name-displayed-name-alist)
(setq buffer-read-only t))

(defun enh-base-change-major-mode-from-note-name (note-name)

(let ((buffer-file-name note-name))
  (normal-mode)))

(defun enh-base-find-opened-buffer (guid)

"Find a buffer associated with guid"
(let ((found_buf nil))
  (save-excursion
    (mapc (lambda (buf)
            (set-buffer buf)
            (if (string= evernote-note-guid guid)
                (setq found_buf buf)))
          (buffer-list)))
  found_buf))

(defun enh-base-update-mode-line (&optional notebook-guid is-set-tag-names tag-names edit-mode)

"Update mode line"
(let ((note-attr (enh-get-note-attr evernote-note-guid)))
  (setq vc-mode
        (concat "[Notebook:"
                (if notebook-guid
                    (enutil-aget 'name (enh-get-notebook-attr notebook-guid))
                  (enutil-aget 'name (enh-get-notebook-attr
                                      (enutil-aget 'notebookGuid note-attr))))
                "] "
                "[Tag:"
                (if is-set-tag-names
                    (mapconcat #'identity tag-names ",")
                  (enh-tag-guids-to-comma-separated-names
                   (enutil-aget 'tagGuids note-attr)))
                "] "
                "[Edit mode:"
                (if edit-mode
                    edit-mode
                  (enutil-aget 'editMode note-attr))
                "]"))
  (force-mode-line-update)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helper functions for evernote-browsing-mode (enh-browsing-xxx) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar evernote-browsing-mode nil

"Non-nil if Evernote Browsing mode is enabled.")

(make-variable-buffer-local 'evernote-browsing-mode)

(defvar enh-browsing-page-type nil) (make-variable-buffer-local 'enh-browsing-page-type)

(defvar enh-browsing-page-description nil) (make-variable-buffer-local 'enh-browsing-page-description)

(defvar enh-browsing-page-data nil) (make-variable-buffer-local 'enh-browsing-page-data)

(defvar enh-browsing-page-data-refresh-closure nil) (make-variable-buffer-local 'enh-browsing-page-data-refresh-closure)

(defvar enh-browsing-page-widget-title nil) (make-variable-buffer-local 'enh-browsing-page-widget-title)

(defvar enh-browsing-page-widget-root nil) (make-variable-buffer-local 'enh-browsing-page-widget-root)

(defvar enh-browsing-page-setup-func nil) (make-variable-buffer-local 'enh-browsing-page-setup-func)

(defun enh-browsing-open-notebook (widget &rest ignored)

"Open a saved search in browsing mode"
(enh-clear-onmem-cache)
(let* ((guid (widget-value widget)) note-attrs)
  (enh-command-with-auth
   (setq note-attrs
         (enh-command-get-note-attrs-from-notebook-and-tag-guids guid nil)))
  (enh-browsing-update-page-list)
  (enh-browsing-push-page
   (enh-browsing-create-page 'note-list
                             (format "Notes in Notebook: %s"
                                     (enutil-aget 'name
                                                  (enh-get-notebook-attr guid)))
                             note-attrs
                             `(lambda () (enh-command-get-note-attrs-from-notebook-and-tag-guids
                                          ,guid nil))))))

(defun enh-browsing-open-tag (widget &rest ignored)

"Open a tag in browsing mode"
(enh-clear-onmem-cache)
(let ((guid (widget-value widget)) note-attrs)
  (enh-command-with-auth
   (setq note-attrs
         (enh-command-get-note-attrs-from-notebook-and-tag-guids
          nil
          (if guid
              (list guid)
            nil))))
  (enh-browsing-update-page-list)
  (enh-browsing-push-page
   (enh-browsing-create-page 'note-list
                             (if guid
                                 (format "Notes with tag %s"
                                         (enutil-aget 'name
                                                      (enh-get-tag-attr guid)))
                               "All notes")
                             note-attrs
                             `(lambda () (enh-command-get-note-attrs-from-notebook-and-tag-guids
                                          nil ',(if guid
                                                    (list guid)
                                                  nil)))))))

(defun enh-browsing-open-search (widget &rest ignored)

"Open a saved search in browsing mode"
(enh-clear-onmem-cache)
(let* ((guid (widget-value widget)) note-attrs)
  (enh-command-with-auth
   (setq note-attrs
         (enh-command-get-note-attrs-from-query
          (enutil-aget 'query
                       (enh-get-search-attr guid)))))
  (enh-browsing-update-page-list)
  (enh-browsing-push-page
   (enh-browsing-create-page 'note-list
                             (format "Query Result of Saved Search: %s"
                                     (enutil-aget 'name
                                                  (enh-get-search-attr guid)))
                             note-attrs
                             `(lambda () (enh-command-get-note-attrs-from-query
                                          ,(enutil-aget 'query
                                                        (enh-get-search-attr guid))))))))

(defun enh-browsing-open-note (widget &rest ignored)

"Open a note in browsing mode"
(enh-clear-onmem-cache)
(enh-command-with-auth
 (let* ((guid (widget-value widget))
        (note-attr (enh-get-note-attr guid))
        (cur-buf (current-buffer)))
   (enh-base-open-note-common note-attr)
   (let ((command-keys (this-command-keys)))
     (if (and (stringp command-keys)
              (or (string= "o" command-keys)
                  (string= "n" command-keys)
                  (string= "p" command-keys)))
         (enutil-move-cursor-to-window cur-buf t))))))

(defun enh-browsing-push-page (page &optional noswitch)

"Push a new page to the browsing mode"
(enutil-push page evernote-browsing-page-list)
(if noswitch
    (save-excursion
      (set-buffer page)
      (funcall enh-browsing-page-setup-func))
  (setq evernote-browsing-current-page page)
  (switch-to-buffer page)
  (enh-command-with-auth
   (funcall enh-browsing-page-setup-func))))

(defun enh-browsing-create-page (type description &optional note-attrs refresh-closure)

"Create a page structure of the attr-list"
(let ((buf (generate-new-buffer (format "*ENB %s* " description))))
  (save-excursion
    (set-buffer buf)
    (setq enh-browsing-page-type type
          enh-browsing-page-description description
          enh-browsing-page-data note-attrs
          enh-browsing-page-data-refresh-closure refresh-closure)
    (cond
     ((eq type 'notebook-list)
      (setq enh-browsing-page-setup-func
            'enh-browsing-setup-notebook-list-page))
     ((eq type 'tag-list)
      (setq enh-browsing-page-setup-func
            'enh-browsing-setup-tag-list-page))
     ((eq type 'search-list)
      (setq enh-browsing-page-setup-func
            'enh-browsing-setup-search-list-page))
     ((eq type 'note-list)
      (setq enh-browsing-page-setup-func
            'enh-browsing-setup-note-list-page)))
    (evernote-browsing-mode))
  buf))

(defun enh-browsing-setup-notebook-list-page ()

"Insert notebook list into the browsing buffer"
(when enh-browsing-page-widget-root
  (widget-delete enh-browsing-page-widget-root)
  (setq enh-browsing-page-widget-root nil))
(let ((notebook-list nil))
  (maphash
   (lambda (guid attr)
     (let ((attr (enh-get-notebook-attr guid)))
       (enutil-push `(push-button
                      :tag ,(format "%-30s   %s"
                                    (enutil-aget 'name attr)
                                    (enutil-aget 'defaultNotebook attr))
                      :format "%[%t%]\n"
                      :notify enh-browsing-open-notebook
                      ,guid)
                    notebook-list)))
   (enh-get-notebook-attrs))
  (setq enh-browsing-page-widget-root
        (apply 'widget-create
               `(group
                 (item ,(format "%s\n\ntotal %d\n%-30s   %s\n"
                                enh-browsing-page-description
                                (hash-table-count (enh-get-notebook-attrs))
                                "Name"
                                "Default"))
                 ,@(nreverse notebook-list)))))
(widget-setup)
(goto-char (point-min)))

(defun enh-browsing-setup-tag-list-page ()

"Create a page structure of the attr-list"
(when enh-browsing-page-widget-root
  (widget-delete enh-browsing-page-widget-title)
  (widget-delete enh-browsing-page-widget-root)
  (setq enh-browsing-page-widget-title nil)
  (setq enh-browsing-page-widget-root nil))
(let ((guid-children-hash (make-hash-table :test #'equal)))
  (maphash
   (lambda (guid attr)
     (let* ((parent (enutil-aget 'parentGuid attr))
            (children (gethash parent guid-children-hash)))
       (if children
           (puthash parent (cons guid children) guid-children-hash)
         (puthash parent (list guid) guid-children-hash))))
   (enh-get-tag-attrs))
  (setq enh-browsing-page-widget-title
        (widget-create 'item (format "Tag List\n\ntotal %d\n" (hash-table-count (enh-get-tag-attrs)))))
  (setq enh-browsing-page-widget-root
        (apply 'widget-create
               (enh-browsing-get-tag-tree nil))))
(widget-setup)
(goto-char (point-min)))

(defun enh-browsing-get-tag-tree (guid) ; root (eq guid nil)

(let* ((children (gethash guid guid-children-hash))
       (attr (enh-get-tag-attr guid))
       (name (if attr (enutil-aget 'name attr) "All tags")))
  (if children
      `(tree-widget :node (push-button :tag ,name
                                       :format "%[%t%]\n"
                                       :notify enh-browsing-open-tag
                                       ,guid)
                    :args ,(mapcar
                            'enh-browsing-get-tag-tree
                            (nreverse children))
                    :open ,(if attr nil t))
    `(push-button :tag ,name
                  :format "%[%t%]\n"
                  :notify enh-browsing-open-tag
                  ,guid))))

(defun enh-browsing-setup-search-list-page ()

"Insert saved search list into the browsing buffer"
(when enh-browsing-page-widget-root
  (widget-delete enh-browsing-page-widget-root)
  (setq enh-browsing-page-widget-root nil))
(let ((search-list nil))
  (maphash
   (lambda (guid attr)
     (let ((attr (enh-get-search-attr guid)))
       (enutil-push `(push-button
                      :tag ,(format "%-30s   %s"
                                    (enutil-aget 'name attr)
                                    (enutil-aget 'query attr))
                      :format "%[%t%]\n"
                      :notify enh-browsing-open-search
                      ,guid)
                    search-list)))
   (enh-get-search-attrs))
  (setq enh-browsing-page-widget-root
        (apply 'widget-create
               `(group
                 (item ,(format "%s\n\ntotal %d\n%-30s   %s\n"
                                enh-browsing-page-description
                                (hash-table-count (enh-get-search-attrs))
                                "Name"
                                "Query"))
                 ,@(nreverse search-list)))))
(widget-setup)
(goto-char (point-min)))

(defun enh-browsing-setup-note-list-page ()

"Insert note list into the browsing buffer"
(when enh-browsing-page-widget-root
  (widget-delete enh-browsing-page-widget-root)
  (setq enh-browsing-page-widget-root nil))
(when (and (null enh-browsing-page-data)
           enh-browsing-page-data-refresh-closure)
  (setq enh-browsing-page-data (funcall enh-browsing-page-data-refresh-closure)))
(let ((note-attrs enh-browsing-page-data)
      (note-list nil))
  (mapc
   (lambda (attr)
     (enutil-push `(push-button
                    :tag ,(format "%-30s   %-20s   %-15s   %4s   %s"
                                  (enutil-aget 'updated attr)
                                  (enutil-aget 'name
                                               (enh-get-notebook-attr (enutil-aget 'notebookGuid attr)))
                                  (enh-tag-guids-to-comma-separated-names
                                   (enutil-aget 'tagGuids attr)
                                   15)
                                  (enutil-aget 'editMode attr)
                                  (enutil-aget 'title attr))
                    :format "%[%t%]\n"
                    :notify enh-browsing-open-note
                    ,(enutil-aget 'guid attr))
                  note-list))
   note-attrs)
  (setq enh-browsing-page-widget-root
        (apply 'widget-create
               `(group
                 (item ,(format "%s\n\ntotal %d\n%-30s   %-20s   %-15s   %4s   %s\n"
                                enh-browsing-page-description
                                (length note-attrs)
                                "Last Modified"
                                "Notebook"
                                "Tags"
                                "Mode"
                                "Name"))
                 ,@(nreverse note-list)))))
(widget-setup)
(goto-char (point-min)))

(defun enh-browsing-update-page-list ()

(setq evernote-browsing-page-list
      (delete nil
              (mapcar (lambda (page)
                        (if (buffer-live-p page) page nil))
                      evernote-browsing-page-list)))
(unless (memq evernote-browsing-current-page evernote-browsing-page-list)
  (setq evernote-browsing-current-page (car evernote-browsing-page-list))))

(defun enh-browsing-get-prev-page ()

(cadr
 (memq evernote-browsing-current-page
       evernote-browsing-page-list)))

(defun enh-browsing-get-next-page ()

(catch 'next-page
  (let (next-page)
    (mapc
     (lambda (page)
       (when (eq page evernote-browsing-current-page)
         (throw 'next-page next-page))
       (setq next-page page))
     evernote-browsing-page-list))))

(defun enh-browsing-get-page-of-type (type)

"Get a page of the type in the browsing mode"
(let ((result nil))
  (save-excursion
    (mapc
     (lambda (page)
       (set-buffer page)
       (when (eq enh-browsing-page-type type)
         (setq result page)))
     evernote-browsing-page-list))
  result))

(defun enh-browsing-reflesh-page (type)

"Reflesh current page"
(enh-browsing-update-page-list)
(save-excursion
  (mapcar (lambda (page)
            (set-buffer page)
            (when (eq enh-browsing-page-type type)
              (when enh-browsing-page-data-refresh-closure
                (setq enh-browsing-page-data nil))
              (funcall enh-browsing-page-setup-func)))
          evernote-browsing-page-list)))

; ; ;(defun evernote-test-list-note () ; “Test” ; (interactive) ; (mapc ; (lambda (tag) ; (insert (format “guid=%s name=%sn” ; (enutil-aget 'guid tag) ; (enutil-aget 'name tag)))) ; (enutil-aget 'tags (enh-command-list-tag))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Functions for executing the external command (enh-command-xxx) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar enh-enclient-command

"/usr/lib/evernote-mode/enclient.rb"
"Name of the enclient.rb command")

(defconst enh-command-process-name “Evernote-Client”) (defconst enh-command-output-buffer-name “Evernote-Client-Output”)

;; ;; Const variables. ;;

(defconst enh-command-error-ok 0) (defconst enh-command-error-not-found 100) (defconst enh-command-error-unexpected 101) (defconst enh-command-error-not-authed 102) (defconst enh-command-error-unknown 1) (defconst enh-command-error-bad-data-format 2) (defconst enh-command-error-permission-denied 3) (defconst enh-command-error-internal-error 4) (defconst enh-command-error-data-required 5) (defconst enh-command-error-limit-reached 6) (defconst enh-command-error-quota-reached 7) (defconst enh-command-error-invalid-auth 8) (defconst enh-command-error-auth-expired 9) (defconst enh-command-error-data-conflict 10) (defconst enh-command-error-enml-validation 11) (defconst enh-command-error-shared-unavailable 12)

(defvar enh-command-last-result-code enh-command-error-ok) (defvar enh-command-last-error-message nil) (defvar enh-command-next-command-id 0)

(defun enh-command-login (user passwd)

"Issue login command"
(enh-command-issue
 (format ":class => %s, :user => %s, :passwd => %s"
         (enutil-to-ruby-string "AuthCommand")
         (enutil-to-ruby-string user)
         (enutil-to-ruby-string passwd))))

(defun enh-command-get-notebook-attrs ()

"Issue listnotebooks command"
(let ((reply (enh-command-issue
              (format ":class => %s"
                      (enutil-to-ruby-string "ListNotebookCommand")))))
  (enutil-aget 'notebooks reply)))

(defun enh-command-get-tag-attrs ()

"Issue listtags command"
(let ((reply (enh-command-issue
              (format ":class => %s"
                      (enutil-to-ruby-string "ListTagCommand")))))
  (enutil-aget 'tags reply)))

(defun enh-command-get-note-attr (guid)

"Issue getnote command from the tag name list."
(let ((reply (enh-command-issue
              (format ":class => %s, :guid => %s"
                      (enutil-to-ruby-string "GetNoteCommand")
                      (enutil-to-ruby-string guid)))))
  (enutil-aget 'note reply)))

(defun enh-command-get-note-attrs-from-notebook-and-tag-guids (notebook-guid tag-guids)

"Issue listnotes command from the notebook guid and the tag guids."
(message "notebook %s" notebook-guid)
(let ((reply (enh-command-issue
              (format ":class => %s, :notebook_guid => %s, :tag_guids => %s"
                      (enutil-to-ruby-string "ListNoteCommand")
                      (enutil-to-ruby-string notebook-guid)
                      (enutil-to-ruby-string-list tag-guids nil)))))
  (enutil-aget 'notes reply)))

(defun enh-command-get-note-attrs-from-query (query)

"Issue listnotes command from the query."
(let ((reply (enh-command-issue
              (format ":class => %s, :query => %s"
                      (enutil-to-ruby-string "SearchNoteCommand")
                      (enutil-to-ruby-string query)))))
  (enutil-aget 'notes reply)))

(defun enh-command-get-note-content (guid edit-mode)

"Issue getnotecontent command specified by the guid and the edit mode."
(let ((reply (enh-command-issue
              (format ":class => %s, :guid => %s, :edit_mode => %s"
                      (enutil-to-ruby-string "GetContentCommand")
                      (enutil-to-ruby-string guid)
                      (enutil-to-ruby-string edit-mode)))))
  (enutil-aget 'content reply)))

(defun enh-command-create-note (inbuf name notebook-guid tag-names edit-mode)

"Issue createnote command specified by the guid, tags and the edit-mode."
(let ((reply (enh-command-issue
              (format ":class => %s, :notebook_guid => %s, :title => %s, :tag_names => %s, :edit_mode => %s, :content => %s"
                      (enutil-to-ruby-string "CreateNoteCommand")
                      (enutil-to-ruby-string notebook-guid)
                      (enutil-to-ruby-string name)
                      (enutil-to-ruby-string-list tag-names nil)
                      (enutil-to-ruby-string edit-mode)
                      (enutil-to-ruby-string (enutil-buffer-string inbuf))))))
  (enutil-aget 'note reply)))

(defun enh-command-update-note (inbuf guid name notebook-guid is-tag-updated tag-names edit-mode)

"Issue updatenote command specified by the guid and the parameters for updating."
(let ((reply (enh-command-issue
              (format ":class => %s, :guid => %s, :notebook_guid => %s, :title => %s, :tag_names => %s, :edit_mode => %s, :content => %s"
                      (enutil-to-ruby-string "UpdateNoteCommand")
                      (enutil-to-ruby-string guid)
                      (enutil-to-ruby-string notebook-guid)
                      (enutil-to-ruby-string name)
                      (enutil-to-ruby-string-list tag-names is-tag-updated)
                      (enutil-to-ruby-string edit-mode)
                      (enutil-to-ruby-string (enutil-buffer-string inbuf))))))
  (enutil-aget 'note reply)))

(defun enh-command-delete-note (guid)

"Issue deletenote command specified by the guid, tags and the edit mode."
(enh-command-issue
 (format ":class => %s, :guid => %s"
         (enutil-to-ruby-string "DeleteNoteCommand")
         (enutil-to-ruby-string guid))))

(defun enh-command-create-notebook (name default-notebook)

"Issue createnotebook command"
(enh-command-issue
 (format ":class => %s, :name => %s, :default_notebook => %s"
         (enutil-to-ruby-string "CreateNotebookCommand")
         (enutil-to-ruby-string name)
         (if default-notebook "true" "false"))))

(defun enh-command-update-notebook (guid name default-notebook)

"Issue updatenotebook command"
(enh-command-issue
 (format ":class => %s, :guid => %s, :name => %s, :default_notebook => %s"
         (enutil-to-ruby-string "UpdateNotebookCommand")
         (enutil-to-ruby-string guid)
         (enutil-to-ruby-string name)
         (if default-notebook "true" "false"))))

(defun enh-command-get-search-attrs ()

"Issue listsearch command"
(let ((reply (enh-command-issue
              (format ":class => %s"
                      (enutil-to-ruby-string "ListSearchCommand")))))
  (enutil-aget 'searches reply)))

(defun enh-command-create-search (name query)

"Issue createsearch command"
(enh-command-issue
 (format ":class => %s, :name => %s, :query => %s"
         (enutil-to-ruby-string "CreateSearchCommand")
         (enutil-to-ruby-string name)
         (enutil-to-ruby-string query))))

(defun enh-command-update-search (guid name query)

"Issue updatesearch command"
(enh-command-issue
 (format ":class => %s, :guid => %s, :name => %s, :query => %s"
         (enutil-to-ruby-string "UpdateSearchCommand")
         (enutil-to-ruby-string guid)
         (enutil-to-ruby-string name)
         (enutil-to-ruby-string query))))

(defun enh-command-issue (command)

(enh-command-setup-process)
(let ((proc (get-process enh-command-process-name))
      (reply nil)
      (buffer (get-buffer enh-command-output-buffer-name)))
  (message "Waiting for the result...")
  (save-excursion
    (set-buffer buffer)
    (erase-buffer)
    ;(delete-region (point-min) (point-max))
    (setq enh-command-next-command-id
          (+ 1 enh-command-next-command-id))
    (process-send-string proc
                         (format "{%s, :command_id => %d}"
                                 command enh-command-next-command-id))
    (process-send-string proc "\x00\n")
    (while (or (not reply)
               (enutil-neq (enutil-aget 'command_id reply)
                           enh-command-next-command-id))
      (accept-process-output proc)
      (setq reply (enutil-get-first-sexp-in-buffer))))
  (message "")
  (if (eq (enutil-aget 'class reply) 'ErrorReply)
      (progn
        (setq enh-command-last-result-code (enutil-aget 'result_code reply))
        (setq enh-command-last-error-message (enutil-aget 'message reply))
        (throw 'error enh-command-last-result-code))
    (setq enh-command-last-result-code enh-command-error-ok)
    reply)))

(defun enh-command-sentinel (process event)

(error "enclient.rb %s%s" event
       (with-current-buffer enh-command-output-buffer-name
         (buffer-string))))

(defun enh-command-setup-process ()

(let ((proc (get-process enh-command-process-name))
      (process-connection-type nil)) ; use pipe instead of pty
  (when (or (not proc)
            (not (eq (process-status proc) 'run)))
    (setq proc (start-process enh-command-process-name
                              enh-command-output-buffer-name
                              evernote-ruby-command "-S" enh-enclient-command))
    (set-process-sentinel proc 'enh-command-sentinel)
    (set-process-coding-system proc 'utf-8 'utf-8)
    (set-process-query-on-exit-flag proc nil))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helper functions for all modes in this file (enh-xxx) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar enh-notebook-info (make-hash-table :test #'equal)

"Notebook info associated with the guid")

(defvar enh-tag-info (make-hash-table :test #'equal)

"Tag info associated with the guid")

(defvar enh-search-info (make-hash-table :test #'equal)

"Saved search info associated with the guid")

(defvar enh-note-attr nil

"Note attr associated with most recently accessed guid")

(defvar enh-password-cache-file “~/.evernote-mode/password.gpg”

"Filename of saving password cache.")

(defvar enh-password-cache-buffer “ evernote-password-cache”)

(defun enh-get-notebook-attrs ()

(when (eq (hash-table-count enh-notebook-info) 0)
  (mapc
   (lambda (attr)
     (puthash (enutil-aget 'guid attr) attr enh-notebook-info))
   (enh-command-get-notebook-attrs)))
enh-notebook-info)

(defun enh-get-tag-attrs ()

(when (eq (hash-table-count enh-tag-info) 0)
  (mapc
   (lambda (attr)
     (puthash (enutil-aget 'guid attr) attr enh-tag-info))
   (enh-command-get-tag-attrs)))
enh-tag-info)

(defun enh-get-search-attrs ()

(when (eq (hash-table-count enh-search-info) 0)
  (mapc
   (lambda (attr)
     (puthash (enutil-aget 'guid attr) attr enh-search-info))
   (enh-command-get-search-attrs)))
enh-search-info)

(defun enh-get-note-attr (guid)

"Get the note attr from the guid"
(if (and enh-note-attr
         (string= guid (enutil-aget 'guid enh-note-attr)))
    enh-note-attr
  (setq enh-note-attr (enh-command-get-note-attr guid))))

(defun enh-get-notebook-attr (guid)

"Get the notebook attr from the guid"
(gethash guid (enh-get-notebook-attrs)))

(defun enh-get-tag-attr (guid)

"Get the tag attr from the guid"
(gethash guid (enh-get-tag-attrs)))

(defun enh-get-search-attr (guid)

"Get the search attr from the guid"
(gethash guid (enh-get-search-attrs)))

(defun enh-read-tag-guids (&optional prompt)

(enh-tag-names-to-guids
 (enutil-completing-read-multiple
  (if prompt
      prompt
    "Tags (comma separated form):")
  (enh-get-tag-name-alist)
  nil
  t)))

(defun enh-read-tag-names (&optional prompt note-guid)

(enutil-completing-read-multiple
 (if prompt
     prompt
   "Tags (comma separated form):")
 (enh-get-tag-name-alist)
 nil
 nil
 (if note-guid
     (enh-tag-guids-to-comma-separated-names
      (enutil-aget 'tagGuids
                   (enh-get-note-attr note-guid)))
   nil)))

(defun enh-format-enml (content outbuf)

(if evernote-enml-formatter-command
    (let ((infile (concat (make-temp-file "evernote-enml") ".html"))
          (command (car evernote-enml-formatter-command))
          (args (cdr evernote-enml-formatter-command)))
      (setq args (append args (list infile)))
      (with-temp-buffer
        (insert content)
        (write-region (point-min) (point-max) infile)
        (message "") ; remove the message notifying writing to tmp file.
        (let ((coding-system-for-read 'utf-8)
              (coding-system-for-write 'utf-8))
          (apply 'call-process
                 command
                 infile
                 outbuf
                 nil
                 args))))
  (save-excursion ; insert the content as is.
    (set-buffer outbuf)
    (insert content))))

(defun enh-clear-onmem-cache ()

(clrhash enh-notebook-info)
(clrhash enh-tag-info)
(clrhash enh-search-info)
(setq enh-note-attr nil))

(defun enh-read-notebook (&optional default)

(let ((notebook-name-attr-alist (enh-get-notebook-name-attr-alist)))
  (enutil-aget (completing-read
                "Notebook:"
                notebook-name-attr-alist
                nil
                t
                default)
               notebook-name-attr-alist)))

(defun enh-read-saved-search (&optional prompt)

(let ((search-name-query-alist (enh-get-search-name-attr-alist)))
  (enutil-aget (completing-read
                (if prompt
                    prompt
                  "Saved search:")
                search-name-query-alist
                nil t)
               search-name-query-alist)))

(defun enh-get-notebook-name-attr-alist ()

"Get the notebook alist for completion from command output"
(let (result)
  (maphash
   (lambda (guid attr)
     (setq result
           (cons
            (cons (enutil-aget 'name attr)
                  attr)
            result)))
   (enh-get-notebook-attrs))
  result))

(defun enh-get-tag-name-alist ()

"Get the tag alist for completion from command output"
(let (result)
  (maphash
   (lambda (guid attr)
     (setq result
           (cons
            (list (enutil-aget 'name attr))
            result)))
   (enh-get-tag-attrs))
  result))

(defun enh-get-search-name-attr-alist ()

"Get the alist for completion from command output"
(let (result)
  (maphash
   (lambda (guid attr)
     (setq result
           (cons
            (cons (enutil-aget 'name attr)
                  attr)
            result)))
   (enh-get-search-attrs))
  result))

(defun enh-update-note-and-new-tag-attrs (note-attr)

(let ((tag-guids (enutil-aget 'tagGuids note-attr))
      (tag-attrs (enh-get-tag-attrs)))
  (when (catch 'result
          (mapc
           (lambda (guid)
             (unless (gethash guid tag-attrs)
               (throw 'result t)))
           tag-guids)
          nil)
    (enh-clear-onmem-cache)
    (enh-browsing-reflesh-page 'tag-list))))

(defun enh-tag-guids-to-comma-separated-names (tag-guids &optional maxlen)

(let (line)
  (setq line
        (mapconcat
         (lambda (guid)
           (enutil-aget 'name (enh-get-tag-attr guid)))
         tag-guids
         ","))
  (if maxlen
      (truncate-string-to-width line maxlen)
    line)))

(defun enh-tag-names-to-comma-separated-oct-names (tag-names)

(mapconcat #'identity (mapcar 'enutil-string-to-oct tag-names) ","))

;;(defun enh-tag-guids-to-names (tag-guids) ;; (mapcar ;; (lambda (tag-guid) ;; (enutil-aget 'name (enh-get-tag-attr tag-guid))) ;; tag-guids))

(defun enh-notebook-name-to-guid (notebook-name)

(let ((notebook-attrs (enh-get-notebook-attrs)))
  (catch 'guid
    (maphash
     (lambda (guid attr)
       (let ((name (enutil-aget 'name attr)))
         (if (equal notebook-name name)
             (throw 'guid guid))))
     notebook-attrs))))

(defun enh-tag-names-to-guids (tag-names)

(let ((tag-attrs (enh-get-tag-attrs)))
  (mapcar
   (lambda (name)
     (catch 'guid
       (maphash
        (lambda (guid attr)
          (let ((tag-name (enutil-aget 'name attr)))
            (if (equal tag-name name)
                (throw 'guid guid))))
        tag-attrs)))
   tag-names)))

(defun enh-read-edit-mode (default)

(let ((edit-mode
       (completing-read "Edit mode (type \"TEXT\" or \"XHTML\"):"
                        '(("TEXT") ("XHTML"))
                        nil
                        t
                        default)))
  (if (and edit-mode (not (string= edit-mode "")))
      edit-mode
    default)))

(defun enh-bookmark-make-record ()

"Make a emacs bookmark entry for a evernote buffer."
`(,(buffer-name)
  ,@(bookmark-make-record-default 'no-file)
  ;; if bookmark-bmenu-toggle-filenames is t and a bookmark record doesn't
  ;; have filename field, , Emacs23.2 raises an error.
  ;; Here is the workaround suggested by ARISAWA Akihiro.
  (filename . ,(format "%s (evernote:%s)" (buffer-name) evernote-note-guid))
  (handler . enh-bookmark-jump)))

(defun enh-bookmark-jump (bookmark) ;; Note: don't rename this function for the bookmark file compatibility

"Default bookmark handler for evernote buffers."
(enh-command-with-auth
 (let ((filename (bookmark-get-filename bookmark)))
   (if (and filename (string-match "(evernote:\\(.*\\))$" filename))
       (progn
         (let* ((guid (substring filename (match-beginning 1) (match-end 1)))
                (attr (enh-command-get-note-attr guid)))
           (enh-base-open-note-common attr)
           (let ((buf (current-buffer)))
             (bookmark-default-handler
              `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark))))))
     (message (format "Invalid bookmark %s" (bookmark-name-from-full-record bookmark)))))))

(defun enh-bookmark-prepare ()

(interactive)
(set (make-local-variable 'bookmark-make-record-function)
     'enh-bookmark-make-record))

(defun enh-password-cache-load ()

"Load the password cache from the file"
(when (and evernote-password-cache
           (file-exists-p enh-password-cache-file))
  (with-current-buffer (get-buffer-create enh-password-cache-buffer)
    (insert-file-contents enh-password-cache-file)
    (read (current-buffer)))))

(defun enh-password-cache-save (user-password)

"Save the password cache to the file"
(when evernote-password-cache
  (with-current-buffer (get-buffer-create enh-password-cache-buffer)
    (write-region (prin1-to-string user-password) nil
                  enh-password-cache-file))))

(defun enh-password-cache-close ()

"Close the password cache buffer"
(when (get-buffer enh-password-cache-buffer)
  (kill-buffer enh-password-cache-buffer)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; General util functions (enutil-xxx) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun enutil-completing-read-multiple

(prompt table &optional predicate require-match initial-input hist def inherit-input-method)
"Read multiple strings with completion. and return nil if no input is given"
(let (results)
  (setq results
        (completing-read-multiple prompt
                                  table
                                  predicate
                                  require-match
                                  initial-input
                                  hist
                                  def
                                  inherit-input-method))
  (delete "" results)))

(defun enutil-aget (key alist)

(let ((result-cons (assoc key alist)))
  (when result-cons
    (cdr result-cons))))

(defun enutil-aset (key alist value)

(let ((result-cons (assoc key alist)))
  (if result-cons
      (setcdr result-cons value)
    (setq alist (cons (cons key value) alist)))
  alist))

(defun enutil-get-current-line-string ()

(save-excursion
  (buffer-substring
   (progn
     (beginning-of-line)
     (point))
   (progn
     (end-of-line)
     (point)))))

(defun enutil-erase-buffer-forcibly ()

(let ((buffer-read-only nil))
  (erase-buffer)))

(defun enutil-get-first-line (str)

"Get first line of the string"
(let ((begin (string-match "^.*$" str)))
  (substring str begin (match-end 0))))

(defun enutil-get-minibuffer-string ()

(save-excursion
  (enutil-set-buffer-to-minibuffer)
  (buffer-substring
   (progn
     (goto-char (+ 1 (minibuffer-prompt-width)))
     (point))
   (progn
     (end-of-line)
     (point)))))

(defun enutil-set-minibuffer-string (str)

(save-excursion
  (enutil-set-buffer-to-minibuffer)
  (delete-region
   (progn
     (goto-char (+ 1 (minibuffer-prompt-width)))
     (point))
   (progn
     (end-of-line)
     (point)))
  (insert str)))

(defun enutil-set-buffer-to-minibuffer ()

(set-buffer (window-buffer (active-minibuffer-window))))

(defun enutil-minibuffer-tmp-message (msg)

(save-excursion
  (goto-char (point-max))
  (save-excursion (insert " " msg))
  (sit-for 1)
  (delete-region (point) (point-max))))

(defun enutil-move-cursor-to-window (buf &optional pop)

"Move cursor to the window associated with the bufer"
(let ((buf-window (get-buffer-window buf)))
  (if buf-window
      (select-window buf-window)
    (if pop
        (pop-to-buffer buf)
      (switch-to-buffer buf)))))

(defun enutil-get-first-sexp-in-buffer ()

(condition-case nil
    (car (read-from-string
          (buffer-substring
           (point-min)
           (point-max))))
  (error nil)))

(defun enutil-hash-mapcar (func hash)

(let (result)
  (maphash
   (lambda (key value)
     (cons
      (funcall func key value)
      result)))
  (nreverse result)))

(defun enutil-to-ruby-string (str)

(if str
    (progn
      (setq str (replace-regexp-in-string "\\\\" "\\\\\\\\" str))
      (setq str (replace-regexp-in-string "'" "\\\\'" str))
      (concat  "'" str "'"))
  "nil"))

(defun enutil-to-ruby-string-list (str-list return-empty-array)

(if str-list
    (concat
     "["
     (mapconcat #'enutil-to-ruby-string str-list ",")
     "]")
  (if return-empty-array
      "[]"
    "nil")))

(defun enutil-buffer-string (buf)

(save-excursion
  (set-buffer buf)
  (buffer-string)))

(provide 'evernote-mode)

;;(setq debug-on-error t)

;; Local Variables: ;; indent-tabs-mode: nil ;; End: