]> git.donarmstrong.com Git - lilypond.git/blob - lilypond-mode.el
(warning): better robustness fix.
[lilypond.git] / lilypond-mode.el
1 ;;;
2 ;;; lilypond-mode.el --- Major mode for editing GNU LilyPond music scores
3 ;;;
4 ;;; source file of the GNU LilyPond music typesetter
5 ;;;  
6 ;;; (c) 1999--2001 Jan Nieuwenhuizen <janneke@gnu.org>
7 ;;; 
8 ;;; Changed 2001 Heikki Junes <heikki.junes@hut.fi>
9 ;;;    * Add PS-compilation, PS-viewing and MIDI-play (29th Aug 2001)
10 ;;;    * Keyboard shortcuts (12th Sep 2001)
11 ;;;    * Inserting tags, inspired on sgml-mode (11th Oct 2001)
12 ;;;
13 ;;; Changed 2002 Carlos Betancourt <carlos.betancourt@chello.be>
14 ;;;    * Added spanish-note-replacements
15
16 ;;; Inspired on auctex
17
18 ;;;
19 ;;; Add this to your ~/.emacs or ~/.emacs.el
20 ;;;     (load-library "lilypond-mode.el")
21 ;;;     (setq auto-mode-alist
22 ;;;      (cons '("\\.ly$" . LilyPond-mode) auto-mode-alist))
23 ;;; 
24
25 (load-library "lilypond-font-lock")
26 (load-library "lilypond-indent")
27
28 (require 'easymenu)
29 (require 'compile)
30
31 (defconst LilyPond-version "1.7.6"
32   "`LilyPond-mode' version number.")
33
34 (defconst LilyPond-help-address "bug-lilypond@gnu.org"
35   "Address accepting submission of bug reports.")
36
37 (defvar LilyPond-mode-hook nil
38   "*Hook called by `LilyPond-mode'.")
39
40 (defvar LilyPond-kick-xdvi nil
41   "If true, no simultaneous xdvi's are started, but reload signal is sent.")
42
43 (defvar LilyPond-command-history nil
44   "Command history list.")
45         
46 (defvar LilyPond-regexp-alist
47   '(("\\([a-zA-Z]?:?[^:( \t\n]+\\)[:( \t]+\\([0-9]+\\)[:) \t]" 1 2))
48   "Regexp used to match LilyPond errors.  See `compilation-error-regexp-alist'.")
49
50 (defcustom LilyPond-include-path ".:/tmp"
51   "* LilyPond include path."
52   :type 'string
53   :group 'LilyPond)
54
55
56 (defun LilyPond-check-files (derived originals extensions)
57   "Check that DERIVED is newer than any of the ORIGINALS.
58 Try each original with each member of EXTENSIONS, in all directories
59 in LilyPond-include-path."
60   (let ((found nil)
61         (regexp (concat "\\`\\("
62                         (mapconcat (function (lambda (dir)
63                                       (regexp-quote (expand-file-name dir))))
64                                    LilyPond-include-path "\\|")
65                         "\\).*\\("
66                         (mapconcat 'regexp-quote originals "\\|")
67                         "\\)\\.\\("
68                         (mapconcat 'regexp-quote extensions "\\|")
69                         "\\)\\'"))
70         (buffers (buffer-list)))
71     (while buffers
72       (let* ((buffer (car buffers))
73              (name (buffer-file-name buffer)))
74         (setq buffers (cdr buffers))
75         (if (and name (string-match regexp name))
76             (progn
77               (and (buffer-modified-p buffer)
78                    (or (not LilyPond-save-query)
79                        (y-or-n-p (concat "Save file "
80                                          (buffer-file-name buffer)
81                                          "? ")))
82                    (save-excursion (set-buffer buffer) (save-buffer)))
83               (if (file-newer-than-file-p name derived)
84                   (setq found t))))))
85     found))
86
87 (defun LilyPond-running ()
88   (let ((process (get-process "lilypond")))
89   (and process
90        (eq (process-status process) 'run))))
91
92 (defun Midi-running ()
93   (let ((process (get-process "midi")))
94   (and process
95        (eq (process-status process) 'run))))
96
97 (defun LilyPond-kill-job ()
98   "Kill the currently running LilyPond job."
99   (interactive)
100   ;; What bout TeX, Xdvi?
101   (quit-process (get-process "lilypond") t))
102
103 ;; URG, should only run LilyPond-compile for LilyPond
104 ;; not for tex,xdvi (ly2dvi?)
105 (defun LilyPond-compile-file (command name)
106   ;; We maybe should know what we run here (Lily, ly2dvi, tex)
107   ;; and adjust our error-matching regex ?
108   (compile-internal command "No more errors" name ))
109
110 ;; do we still need this, now that we're using compile-internal?
111 (defun LilyPond-save-buffer ()
112   "Save buffer and set default command for compiling."
113   (interactive)
114   (if (buffer-modified-p)
115       (progn (save-buffer)
116              (setq LilyPond-command-default "LilyPond"))))
117
118 ;;; return (dir base ext)
119 (defun split-file-name (name)
120   (let* ((i (string-match "[^/]*$" name))
121          (dir (if (> i 0) (substring name 0 i) "./"))
122          (file (substring name i (length name)))
123          (i (string-match "[^.]*$" file)))
124     (if (and
125          (> i 0)
126          (< i (length file)))
127         (list dir (substring file 0 (- i 1)) (substring file i (length file)))
128       (list dir file ""))))
129
130
131 ;; Should check whether in command-alist?
132 (defcustom LilyPond-command-default "LilyPond"
133   "Default command. Must identify a member of LilyPond-command-alist."
134
135   :group 'LilyPond
136   :type 'string)
137 ;;;(make-variable-buffer-local 'LilyPond-command-last)
138
139 (defvar LilyPond-command-current 'LilyPond-command-master)
140 ;;;(make-variable-buffer-local 'LilyPond-command-master)
141
142
143 ;; If non-nil, LilyPond-command-query will return the value of this
144 ;; variable instead of quering the user. 
145 (defvar LilyPond-command-force nil)
146
147 (defcustom LilyPond-xdvi-command "xdvi"
148   "Command used to display DVI files."
149
150   :group 'LilyPond
151   :type 'string)
152
153 (defcustom LilyPond-gv-command "gv -watch"
154   "Command used to display PS files."
155
156   :group 'LilyPond
157   :type 'string)
158
159 (defcustom LilyPond-midi-command "timidity"
160   "Command used to play MIDI files."
161
162   :group 'LilyPond
163   :type 'string)
164
165 (defcustom LilyPond-midi-all-command "timidity -ig"
166   "Command used to play MIDI files."
167
168   :group 'LilyPond
169   :type 'string)
170
171 ;; This is the major configuration variable.
172 (defcustom LilyPond-command-alist
173   `(
174     ("LilyPond" . ("lilypond %s" . "LaTeX"))
175     ("TeX" . ("tex '\\nonstopmode\\input %t'" . "View"))
176
177     ("2Dvi" . ("ly2dvi %s" . "View"))
178     ("2PS" . ("ly2dvi -P %s" . "View"))
179     ("2Midi" . ("lilypond -m %s" . "View"))
180
181     ("Book" . ("lilypond-book %x" . "LaTeX"))
182     ("LaTeX" . ("latex '\\nonstopmode\\input %l'" . "View"))
183
184     ;; point-n-click (arg: exits upop USR1)
185     ("SmartView" . ("xdvi %d" . "LilyPond"))
186     
187     ;; refreshes when kicked USR1
188     ("View" . (,(concat LilyPond-xdvi-command " %d") . "LilyPond"))
189
190     ("ViewPS" . (,(concat LilyPond-gv-command " %p") . "LilyPond"))
191
192     ("Midi" . (,(concat LilyPond-midi-command " %m") . "LilyPond"))
193     )
194
195   "AList of commands to execute on the current document.
196
197 The key is the name of the command as it will be presented to the
198 user, the value is a cons of the command string handed to the shell
199 after being expanded, and the next command to be executed upon
200 success.  The expansion is done using the information found in
201 LilyPond-expand-list.
202 "
203   :group 'LilyPond
204   :type '(repeat (cons :tag "Command Item"
205                        (string :tag "Key")
206                        (cons :tag "How"
207                         (string :tag "Command")
208                         (string :tag "Next Key")))))
209
210 ;; drop this?
211 (defcustom LilyPond-file-extensions '(".ly" ".sly" ".fly")
212   "*File extensions used by manually generated TeX files."
213   :group 'LilyPond
214   :type '(repeat (string :format "%v")))
215
216
217 (defcustom LilyPond-expand-alist 
218   '(
219     ("%s" . ".ly")
220     ("%t" . ".tex")
221     ("%d" . ".dvi")
222     ("%p" . ".ps")
223     ("%l" . ".latex")
224     ("%x" . ".tely")
225     ("%m" . ".midi")
226     )
227     
228   "Alist of expansion strings for LilyPond command names."
229   :group 'LilyPond
230   :type '(repeat (cons :tag "Alist item"
231                   (string :tag "Symbol")
232                   (string :tag "Expansion")))) 
233
234
235 (defcustom LilyPond-command-Show "View"
236   "*The default command to show (view or print) a LilyPond file.
237 Must be the car of an entry in `LilyPond-command-alist'."
238   :group 'LilyPond
239   :type 'string)
240   (make-variable-buffer-local 'LilyPond-command-Show)
241
242 (defcustom LilyPond-command-Print "Print"
243   "The name of the Print entry in LilyPond-command-Print."
244   :group 'LilyPond
245   :type 'string)
246
247 (defun xLilyPond-compile-sentinel (process msg)
248   (if (and process
249            (= 0 (process-exit-status process)))
250       (setq LilyPond-command-default
251               (cddr (assoc LilyPond-command-default LilyPond-command-alist)))))
252
253 ;; FIXME: shouldn't do this for stray View/xdvi
254 (defun LilyPond-compile-sentinel (buffer msg)
255   (if (string-match "^finished" msg)
256       (setq LilyPond-command-default
257             (cddr (assoc LilyPond-command-default LilyPond-command-alist)))))
258
259 ;;(make-variable-buffer-local 'compilation-finish-function)
260 (setq compilation-finish-function 'LilyPond-compile-sentinel)
261
262 (defun LilyPond-command-query (name)
263   "Query the user for what LilyPond command to use."
264   (let* ((default (cond ((if (string-equal name "emacs-lily")
265                              (LilyPond-check-files (concat name ".tex")
266                                                    (list name)
267                                                    LilyPond-file-extensions)
268                            (if (buffer-modified-p) 
269                                (if (y-or-n-p "Save buffer before next command? ")
270                                    (LilyPond-save-buffer)))
271                            ;;"LilyPond"
272                            LilyPond-command-default))
273                         (t LilyPond-command-default)))
274
275          (completion-ignore-case t)
276          
277          (answer (or LilyPond-command-force
278                      (completing-read
279                       (concat "Command: (default " default ") ")
280                       LilyPond-command-alist nil t nil 'LilyPond-command-history))))
281
282     ;; If the answer is "LilyPond" it will not be expanded to "LilyPond"
283     (let ((answer (car-safe (assoc answer LilyPond-command-alist))))
284       (if (and answer
285                (not (string-equal answer "")))
286           answer
287         default))))
288
289
290 ;; FIXME: find ``\score'' in buffers / make settable?
291 (defun LilyPond-master-file ()
292   ;; duh
293   (buffer-file-name))
294
295 (defun LilyPond-command-master ()
296   "Run command on the current document."
297   (interactive)
298   (LilyPond-command (LilyPond-command-query (LilyPond-master-file))
299                     'LilyPond-master-file))
300
301 (defun LilyPond-command-lilypond ()
302   "Run lilypond for the current document."
303   (interactive)
304   (LilyPond-command (LilyPond-command-menu "LilyPond") 'LilyPond-master-file)
305 )
306
307 (defun LilyPond-command-formatdvi ()
308   "Format the dvi output of the current document."
309   (interactive)
310   (LilyPond-command (LilyPond-command-menu "2Dvi") 'LilyPond-master-file)
311 )
312
313 (defun LilyPond-command-formatps ()
314   "Format the ps output of the current document."
315   (interactive)
316   (LilyPond-command (LilyPond-command-menu "2PS") 'LilyPond-master-file)
317 )
318
319 (defun LilyPond-command-smartview ()
320   "View the dvi output of current document."
321   (interactive)
322   (LilyPond-command (LilyPond-command-menu "SmartView") 'LilyPond-master-file)
323 )
324
325 (defun LilyPond-command-view ()
326   "View the dvi output of current document."
327   (interactive)
328   (LilyPond-command (LilyPond-command-menu "View") 'LilyPond-master-file)
329 )
330
331 (defun LilyPond-command-viewps ()
332   "View the ps output of current document."
333   (interactive)
334   (LilyPond-command (LilyPond-command-menu "ViewPS") 'LilyPond-master-file)
335 )
336
337 (defun LilyPond-command-midi ()
338   "Play midi corresponding to the current document."
339   (interactive)
340   (LilyPond-command (LilyPond-command-menu "Midi") 'LilyPond-master-file)
341 )
342
343 (defun count-rexp (start end rexp)
344   "Print number of found regular expressions in the region."
345   (interactive "r")
346   (save-excursion
347     (save-restriction
348       (narrow-to-region start end)
349       (goto-char (point-min))
350       (count-matches rexp))))
351
352 (defun count-midi-words ()
353   "Print number of scores before the curser."
354   (interactive)
355   (count-rexp (point-min) (point-max) "\\\\midi"))
356  
357 (defun count-midi-words-backwards ()
358   "Print number of scores before the curser."
359   (interactive)
360   (count-rexp (point-min) (point) "\\\\midi"))
361  
362 (defun LilyPond-command-next-midi ()
363   "Play next midi score of the current document."
364   (interactive)
365   (if (Midi-running)
366       (quit-process (get-process "midi") t)
367     (LilyPond-compile-file 
368      (let ((fname (LilyPond-master-file))
369            (allcount (string-to-number (substring (count-midi-words) 0 -12)))
370            (count (string-to-number (substring (count-midi-words-backwards) 0 -12))))
371        (concat  LilyPond-midi-command " "
372                 (substring fname 0 -3) ; suppose ".ly"
373                 (if (and (> allcount 1) (> count 0)) ; not first score
374                     (if (eq count allcount)          ; last score
375                         (concat "-" (number-to-string (+ count -1)))
376                       (concat "-" (number-to-string count))))
377                 ".midi"))
378      "Midi")))
379
380 (defun LilyPond-command-all-midi ()
381   "Play next midi score of the current document."
382   (interactive)
383   (if (Midi-running)
384       (quit-process (get-process "midi") t)
385     (LilyPond-compile-file 
386      (let ((fname (LilyPond-master-file))
387            (allcount (string-to-number (substring (count-midi-words) 0 -12))))
388        (concat  LilyPond-midi-all-command " "
389                 (if (> allcount 0) ; at least one midi-score
390                     (concat (substring fname 0 -3) ".midi "))
391                 (if (> allcount 1) ; more than one midi-score
392                     (concat (substring fname 0 -3) "-?.midi "))
393                 (if (> allcount 9) ; etc.
394                     (concat (substring fname 0 -3) "-??.midi"))
395                 (if (> allcount 99) ; not first score
396                     (concat (substring fname 0 -3) "-???.midi"))))
397      "Midi")))
398
399 (defun LilyPond-un-comment-region (start end level)
400   "Remove up to LEVEL comment characters from each line in the region."
401   (interactive "*r\np") 
402   (comment-region start end (- level)))
403
404 ;; FIXME, this is broken
405 (defun LilyPond-region-file (begin end)
406   (let (
407         ;; (dir "/tmp/")
408         ;; urg
409         (dir "./")
410         (base "emacs-lily")
411         ;; Hmm
412         (ext (if (string-match "^[\\]score" (buffer-substring begin end))
413                  ".ly"
414                (if (< 50 (abs (- begin end)))
415                    ".fly"
416                  ".sly"))))
417     (concat dir base ext)))
418
419 (defun LilyPond-command-region (begin end)
420   "Run LilyPond on the current region."
421   (interactive "r")
422   (write-region begin end (LilyPond-region-file begin end) nil 'nomsg)
423   (LilyPond-command (LilyPond-command-query
424                      (LilyPond-region-file begin end))
425                     '(lambda () (LilyPond-region-file begin end))))
426
427 (defun LilyPond-command-buffer ()
428   "Run LilyPond on buffer."
429   (interactive)
430   (LilyPond-command-region (point-min) (point-max)))
431
432 (defun LilyPond-command-expand (string file)
433   (let ((case-fold-search nil))
434     (if (string-match "%" string)
435         (let* ((b (match-beginning 0))
436                (e (+ b 2))
437                (l (split-file-name file))
438                (dir (car l))
439                (base (cadr l)))
440           (LilyPond-command-expand
441            (concat (substring string 0 b)
442                    dir
443                    base
444                    (let ((entry (assoc (substring string b e)
445                                        LilyPond-expand-alist)))
446                      (if entry (cdr entry) ""))
447                    (substring string e))
448            file))
449       string)))
450
451 (defun LilyPond-shell-process (name buffer command)
452   (let ((old (current-buffer)))
453     (switch-to-buffer-other-window buffer)
454     ;; If we empty the buffer don't see messages scroll by.
455     ;; (erase-buffer)
456     
457     (start-process-shell-command name buffer command)
458     (switch-to-buffer-other-window old)))
459   
460
461 (defun LilyPond-command (name file)
462   "Run command NAME on the file you get by calling FILE.
463
464 FILE is a function return a file name.  It has one optional argument,
465 the extension to use on the file.
466
467 Use the information in LilyPond-command-alist to determine how to run the
468 command."
469   
470   (let ((entry (assoc name LilyPond-command-alist)))
471     (if entry
472         (let ((command (LilyPond-command-expand (cadr entry)
473                                                 (apply file nil))))
474           (if (string-equal name "View")
475               (let ((buffer-xdvi (get-buffer-create "*view*")))
476                 (if LilyPond-kick-xdvi
477                   (let ((process-xdvi (get-buffer-process buffer-xdvi)))
478                     (if process-xdvi
479                         (signal-process (process-id process-xdvi) 'SIGUSR1)
480                       (LilyPond-shell-process name buffer-xdvi command)))
481                   (LilyPond-shell-process name buffer-xdvi command)))
482             (progn
483               (setq LilyPond-command-default name)
484               (LilyPond-compile-file command name)))))))
485           
486 ;; XEmacs stuff
487 ;; Sadly we need this for a macro in Emacs 19.
488 (eval-when-compile
489   ;; Imenu isn't used in XEmacs, so just ignore load errors.
490   (condition-case ()
491       (require 'imenu)
492     (error nil)))
493
494
495 ;;; Keymap
496
497 (defvar LilyPond-mode-map ()
498   "Keymap used in `LilyPond-mode' buffers.")
499
500 ;; Note:  if you make changes to the map, you must do
501 ;;    M-x set-variable LilyPond-mode-map nil
502 ;;    M-x eval-buffer
503 ;;    M-x LilyPond-mode
504 ;; to let the changest take effect
505
506 (if LilyPond-mode-map
507     ()
508   (setq LilyPond-mode-map (make-sparse-keymap))
509   (define-key LilyPond-mode-map "\C-c\C-l" 'LilyPond-command-lilypond)
510   (define-key LilyPond-mode-map "\C-c\C-r" 'LilyPond-command-region)
511   (define-key LilyPond-mode-map "\C-c\C-b" 'LilyPond-command-buffer)
512   (define-key LilyPond-mode-map "\C-c\C-k" 'LilyPond-kill-job)
513   (define-key LilyPond-mode-map "\C-c\C-c" 'LilyPond-command-master)
514   (define-key LilyPond-mode-map "\C-c\C-d" 'LilyPond-command-formatdvi)
515   (define-key LilyPond-mode-map "\C-c\C-f" 'LilyPond-command-formatps)
516   (define-key LilyPond-mode-map "\C-c\C-s" 'LilyPond-command-smartview)
517   (define-key LilyPond-mode-map "\C-c\C-v" 'LilyPond-command-view)
518   (define-key LilyPond-mode-map "\C-c\C-p" 'LilyPond-command-viewps)
519   (define-key LilyPond-mode-map "\C-c\C-m" 'LilyPond-command-next-midi)
520   (define-key LilyPond-mode-map "\C-x\C-s" 'LilyPond-save-buffer)
521   (define-key LilyPond-mode-map "\C-cf" 'font-lock-fontify-buffer)
522   (define-key LilyPond-mode-map "\C-ci" 'LilyPond-quick-note-insert)
523   (define-key LilyPond-mode-map "\C-cn" 'LilyPond-insert-tag-notes)
524   (define-key LilyPond-mode-map "\C-cs" 'LilyPond-insert-tag-score)
525   (define-key LilyPond-mode-map "\C-c:" 'LilyPond-un-comment-region)
526   (define-key LilyPond-mode-map "\C-c;" 'comment-region)
527   (define-key LilyPond-mode-map ")" 'LilyPond-electric-close-paren)
528   (define-key LilyPond-mode-map ">" 'LilyPond-electric-close-paren)
529   (define-key LilyPond-mode-map "}" 'LilyPond-electric-close-paren)
530   )
531
532 ;;; Menu Support
533
534 (defun LilyPond-quick-note-insert()
535   "Insert notes with fewer key strokes by using a simple keyboard piano."
536   (interactive)
537   (setq dutch-notes
538         '(("k" "a") ("l" "b") ("a" "c") ("s" "d") 
539           ("d" "e") ("f" "f") ("j" "g") ("r" "r")))
540   (setq dutch-note-ends '("eses" "es" "" "is" "isis"))
541   (setq dutch-note-replacements '("" ""))
542   (setq finnish-note-replacements
543         '(("eeses" "eses") ("ees" "es") ("aeses" "asas") ("aes" "as") ("b" "h")
544           ("beses" "heses") ("bes" "b") ("bis" "his") ("bisis" "hisis")))
545                               ; add more translations of the note names
546   (setq spanish-note-replacements
547         '(("c" "do") ("d" "re") ("e" "mi") ("f" "fa") ("g" "sol") ("a" "la") ("b" "si")
548       ("cis" "dos") ("cisis" "doss") ("ces" "dob") ("ceses" "dobb")
549       ("dis" "res") ("disis" "ress") ("des" "reb") ("deses" "rebb")
550       ("eis" "mis") ("eisis" "miss") ("ees" "mib") ("eeses" "mibb")
551       ("fis" "fas") ("fisis" "fass") ("fes" "fab") ("feses" "fabb")
552       ("gis" "sols") ("gisis" "solss") ("ges" "solb") ("geses" "solbb")
553       ("ais" "las") ("aisis" "lass") ("aes" "lab") ("aeses" "labb")
554       ("bis" "sis") ("bisis" "siss") ("bes" "sib") ("beses" "sibb")))
555   (setq other-keys "()<>~}")
556   (setq accid 0) (setq octav 0) (setq durat "") (setq dots 0)
557
558   (message "Press h for help.") (sit-for 0 750 1)
559
560   (setq note-replacements dutch-note-replacements)
561   (while (not (= 27 ; esc to quit
562     (setq x (read-char-exclusive 
563              (format " | a[_]s[_]d | f[_]j[_]k[_]l | r with ie ,' 12345678 . 0 (<~>)/}\\b\\n Esc \n | c | d | e | f | g | a | %s | r with %s%s%s%s"
564                      (if (string= (car(cdr(assoc "b" note-replacements))) "h")
565                          "h" "b")
566                      (nth (+ accid 2) dutch-note-ends)
567                      (make-string (abs octav) (if (> octav 0) ?' ?,)) 
568                      durat 
569                      (if (string= durat "") "" (make-string dots ?.)))))))
570 ;    (insert (number-to-string x)) ; test numbers for characters
571     (setq note (cdr (assoc (char-to-string x) dutch-notes)))
572     (cond
573      ((= x 127) (backward-kill-word 1)) ; backspace
574      ((= x 13) (progn (insert "\n") (LilyPond-indent-line)))) ; return
575     (setq x (char-to-string x))
576     (cond
577      ((and (string< x "9") (string< "0" x))
578       (progn (setq durat (int-to-string (expt 2 (- (string-to-int x) 1))))
579              (setq dots 0)))
580      ((string= x " ") (insert " "))
581      ((string= x "/") (progn (insert "\\times ")
582                              (while (not (and (string< x "9") (string< "0" x)))
583                                (setq x (char-to-string (read-char-exclusive "Insert a number for the denominator (\"x/\")"))))
584                              (insert (format "%s/" x)) (setq x "/")
585                              (while (not (and (string< x "9") (string< "0" x)))
586                                (setq x (char-to-string (read-char-exclusive "Insert a number for the numerator (\"/y\")"))))
587                              (insert (format "%s { " x))))
588      ((string= x "0") (progn (setq accid 0) (setq octav 0) 
589                              (setq durat "") (setq dots 0)))
590      ((string= x "i") (setq accid (if (= accid 2) 0 (max (+ accid 1) 1))))
591      ((string= x "e") (setq accid (if (= accid -2) 0 (min (+ accid -1) -1))))
592      ((string= x "'") (setq octav (if (= octav 4) 0 (max (+ octav 1) 1))))
593      ((string= x ",") (setq octav (if (= octav -4) 0 (min (+ octav -1) -1))))
594      ((string= x ".") (setq dots (if (= dots 4) 0 (+ dots 1))))
595      ((not (null (member x (split-string other-keys ""))))
596       (insert (format "%s " x)))
597      ((not (null note))
598       (progn
599         (setq note 
600               (format "%s%s" (car note) (if (string= "r" (car note)) "" 
601                                           (nth (+ accid 2) dutch-note-ends))))
602         (setq notetwo (car (cdr (assoc note note-replacements))))
603         (if (not (null notetwo)) (setq note notetwo))
604         (insert
605          (format "%s%s%s%s " 
606                  note
607                  (if (string= "r" note) ""
608                      (make-string (abs octav) (if (> octav 0) ?' ?,)))
609                  durat
610                  (if (string= durat "") "" (make-string dots ?.))))
611         (setq accid 0) (setq octav 0) (setq durat "") (setq dots 0)))
612      ((string= x "t") (progn (setq note-replacements dutch-note-replacements)
613                              (message "Selected Dutch notes") 
614                              (sit-for 0 750 1))) ; t
615      ((string= x "n") (progn (setq note-replacements finnish-note-replacements)
616                              (message "Selected Finnish/Deutsch notes") 
617                              (sit-for 0 750 1))) ; n
618                               ; add more translations of the note names
619      ((string= x "p") (progn (setq note-replacements spanish-note-replacements)
620                              (message "Selected Spanish notes") 
621                              (sit-for 0 750 1))) ; p
622      ((string= x "h") 
623       (progn (message "Insert notes with fewer key strokes. For example \"i,5.f\" produces \"fis,32. \".") (sit-for 5 0 1) 
624              (message "Add also \"a ~ a\"-ties, \"a ( ) b\"-slurs and \"< a b >\"-chords.") (sit-for 5 0 1) 
625              (message "Note names are in Du(t)ch by default. Hit 'n' for Fin(n)ish/Deutsch note names. Hit 'p' for S(p)anish note names") (sit-for 5 0 1) 
626              (message "Backspace deletes last note, return starts a new indented line and Esc quits.") (sit-for 5 0 1) 
627              (message "Insert note triplets \"\\times 2/3 { a b } \" by typing \"/23ab}\".") (sit-for 5 0 1) 
628              (message "Remember to add all other details as well.") (sit-for 5 0 1)))
629     )))
630
631 (define-skeleton LilyPond-insert-tag-notes
632   "LilyPond notes tag."
633   nil
634 ;  (if (bolp) nil ?\n)
635   "\\notes"
636   (if (y-or-n-p "Set \"\\relative\" attribute? ")
637       (concat " \\relative " (skeleton-read "Relative: " "" str)))
638   " { " _ " }")
639
640 (define-skeleton LilyPond-insert-tag-score
641   "LilyPond score tag."
642   nil
643   (if (bolp) nil ?\n)
644   "\\score {\n"
645   "   " _ "\n"
646   "   \\paper {  }\n"
647   (if (y-or-n-p "Insert \"\\header\" field? ")
648       (concat "   \\header {\n      " 
649               (skeleton-read "Piece: " "piece = " str) "\n"
650               (if (y-or-n-p "Insert \"opus\" field? ")
651                   (concat "      " (skeleton-read "Opus: " "opus = " str) "\n"))
652               "   }\n"))
653   (if (y-or-n-p "Insert \"\\midi\" field? ")
654       (concat "   \\midi { " 
655               (skeleton-read "Midi: " "\\tempo 4 = " str)  
656               " }\n"))
657   "}\n")
658
659 (defun LilyPond-command-menu-entry (entry)
660   ;; Return LilyPond-command-alist ENTRY as a menu item.
661   (let ((name (car entry)))
662     (cond ((and (string-equal name LilyPond-command-Print)
663                 LilyPond-printer-list)
664            (let ((command LilyPond-print-command)
665                  (lookup 1))
666              (append (list LilyPond-command-Print)
667                      (mapcar 'LilyPond-command-menu-printer-entry
668                              LilyPond-printer-list))))
669           (t
670            (vector name (list 'LilyPond-command-menu name) t)))))
671
672
673 (easy-menu-define LilyPond-command-menu
674     LilyPond-mode-map
675     "Menu used in LilyPond mode."
676   (append '("Command")
677           '(("Command on"
678              [ "Master File" LilyPond-command-select-master
679                :keys "C-c C-c" :style radio
680                :selected (eq LilyPond-command-current 'LilyPond-command-master) ]
681              [ "Buffer" LilyPond-command-select-buffer
682                :keys "C-c C-b" :style radio
683                :selected (eq LilyPond-command-current 'LilyPond-command-buffer) ]
684              [ "Region" LilyPond-command-select-region
685                :keys "C-c C-r" :style radio
686                :selected (eq LilyPond-command-current 'LilyPond-command-region) ]))
687 ;         (let ((file 'LilyPond-command-on-current))
688 ;           (mapcar 'LilyPond-command-menu-entry LilyPond-command-alist))
689 ;;; Some kind of mapping which includes :keys might be more elegant
690           '([ "LilyPond" (LilyPond-command (LilyPond-command-menu "LilyPond") 'LilyPond-master-file) :keys "C-c C-l"])
691           '([ "TeX" (LilyPond-command (LilyPond-command-menu "TeX") 'LilyPond-master-file) ])
692           '([ "2Dvi" (LilyPond-command (LilyPond-command-menu "2Dvi") 'LilyPond-master-file) :keys "C-c C-d"])
693           '([ "2PS" (LilyPond-command (LilyPond-command-menu "2PS") 'LilyPond-master-file) :keys "C-c C-f"])
694           '([ "2Midi" (LilyPond-command (LilyPond-command-menu "2Midi") 'LilyPond-master-file)])
695           '([ "Book" (LilyPond-command (LilyPond-command-menu "Book") 'LilyPond-master-file) ])
696           '([ "LaTeX" (LilyPond-command (LilyPond-command-menu "LaTeX") 'LilyPond-master-file) ])
697           '([ "SmartView" (LilyPond-command (LilyPond-command-menu "SmartView") 'LilyPond-master-file) :keys "C-c C-s"])
698           '([ "View" (LilyPond-command (LilyPond-command-menu "View") 'LilyPond-master-file) :keys "C-c C-v"])
699           '([ "ViewPS" (LilyPond-command (LilyPond-command-menu "ViewPS") 'LilyPond-master-file) :keys "C-c C-p"])
700           '([ "Midi (off)" (LilyPond-command-next-midi) :keys "C-c C-m"])
701           '([ "Midi all" (LilyPond-command-all-midi)])
702           ))
703
704 (easy-menu-define LilyPond-mode-menu
705   LilyPond-mode-map
706   "Menu used in LilyPond mode."
707   (append '("LilyPond")
708           '(("Insert"
709              [ "\\notes..."  LilyPond-insert-tag-notes t]
710              [ "\\score..."  LilyPond-insert-tag-score t]
711              ["Quick Notes"  LilyPond-quick-note-insert t]
712              ))
713           '(("Miscellaneous"
714              ["Uncomment Region" LilyPond-un-comment-region t]
715              ["Comment Region" comment-region t]
716              ["Refontify buffer" font-lock-fontify-buffer t]
717              ))
718           ))
719
720 (defconst LilyPond-imenu-generic-re "^\\([a-zA-Z_][a-zA-Z0-9_]*\\) *="
721   "Regexp matching Identifier definitions.")
722
723 (defvar LilyPond-imenu-generic-expression
724   (list (list nil LilyPond-imenu-generic-re 1))
725   "Expression for imenu")
726
727 (defun LilyPond-command-select-master ()
728   (interactive)
729   (message "Next command will be on the master file")
730   (setq LilyPond-command-current 'LilyPond-command-master))
731
732 (defun LilyPond-command-select-buffer ()
733   (interactive)
734   (message "Next command will be on the buffer")
735   (setq LilyPond-command-current 'LilyPond-command-buffer))
736
737 (defun LilyPond-command-select-region ()
738   (interactive)
739   (message "Next command will be on the region")
740   (setq LilyPond-command-current 'LilyPond-command-region))
741
742 (defun LilyPond-command-menu (name)
743   ;; Execute LilyPond-command-alist NAME from a menu.
744   (let ((LilyPond-command-force name))
745     (funcall LilyPond-command-current)))
746
747 (defun LilyPond-mode ()
748   "Major mode for editing LilyPond music files.
749
750 This mode knows about LilyPond keywords and line comments, not about
751 indentation or block comments.  It features easy compilation, error
752 finding and viewing of a LilyPond source buffer or region.
753
754 COMMANDS
755 \\{LilyPond-mode-map}
756 VARIABLES
757
758 LilyPond-command-alist\t\talist from name to command
759 LilyPond-xdvi-command\t\tcommand to display dvi files -- bit superfluous"
760   (interactive)
761   ;; set up local variables
762   (kill-all-local-variables)
763
764   (make-local-variable 'font-lock-defaults)
765   (setq font-lock-defaults '(LilyPond-font-lock-keywords))
766
767   ;; string and comments are fontified explicitly
768   (make-local-variable 'font-lock-keywords-only)
769   (setq font-lock-keywords-only t)
770
771   ;; Multi-line font-locking needs Emacs 21.1 or newer.
772   ;; For older versions there is hotkey "C-c f".
773   (make-local-variable 'font-lock-multiline) 
774   (setq font-lock-multiline t) 
775
776   (make-local-variable 'paragraph-separate)
777   (setq paragraph-separate "^[ \t]*$")
778
779   (make-local-variable 'paragraph-start)
780   (setq paragraph-start "^[ \t]*$")
781
782   (make-local-variable 'comment-start)
783   (setq comment-start "%")
784
785   (make-local-variable 'comment-start-skip)
786   (setq comment-start-skip "%{? *")
787
788   (make-local-variable 'comment-end)
789   (setq comment-end "")
790
791   (make-local-variable 'block-comment-start)
792   (setq block-comment-start "%{")
793
794   (make-local-variable 'block-comment-end)  
795   (setq block-comment-end   "%}")
796
797   (make-local-variable 'indent-line-function)
798   (setq indent-line-function 'LilyPond-indent-line)
799
800     (set-syntax-table LilyPond-mode-syntax-table)
801   (setq major-mode 'LilyPond-mode)
802   (setq mode-name "LilyPond")
803   (setq local-abbrev-table LilyPond-mode-abbrev-table)
804   (use-local-map LilyPond-mode-map)
805
806   (make-local-variable 'imenu-generic-expression)
807   (setq imenu-generic-expression LilyPond-imenu-generic-expression)
808   (imenu-add-to-menubar "Index")
809
810     ;; run the mode hook. LilyPond-mode-hook use is deprecated
811   (run-hooks 'LilyPond-mode-hook))
812
813 (defun LilyPond-version ()
814   "Echo the current version of `LilyPond-mode' in the minibuffer."
815   (interactive)
816   (message "Using `LilyPond-mode' version %s" LilyPond-version))
817
818 (provide 'lilypond-mode)
819 ;;; lilypond-mode.el ends here
820