5 files changed, 248 insertions(+), 4 deletions(-)

M build.sh
M delfini.flk
M der-falke.flk
A => filktex.el
M zongbook.tex
M build.sh +1 -1
@@ 9,7 9,7 @@ fi
 if ! test -e Makefile; then ln -s ../MakeStuff/Makefile; fi
 
 for i in *ly; do
-    lilypond -dbackend=eps -dno-gs-load-fonts -dinclude-eps-fonts --pdf -o "$(basename "$i" .ly)"-lily $i
+    lilypond -dbackend=eps -dno-gs-load-fonts -dinclude-eps-fonts --pdf -o "$(basename "$i" .ly)" $i
     # cannot use pdf as backend with epubli
     # gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dCompatibilityLevel=1.3 -dPDFSETTINGS=/printer -dColorImageResolution=600 -dEmbedAllFonts=true -sOutputFile="$(basename "$i" .ly)".pdf "$(basename "$i" .ly)"-lily.pdf
     # rm "$(basename "$i" .ly)".pdf

          
M delfini.flk +1 -1
@@ 4,7 4,7 @@ 
 \subtitle{Für A}
 
 \catcode`\[=12%allow using [ as parameter
-\includegraphics[width=0.96\linewidth, keepaspectratio, trim=0 160cm 0 0]{delfini-tune}% trim, because lilypond creates full-page pngs
+\includegraphics[width=0.96\linewidth, keepaspectratio]{delfini-tune}% trim, because lilypond creates full-page pngs
 \catcode`\[=13%force [ to start chords again
 
 \Large

          
M der-falke.flk +1 -1
@@ 3,7 3,7 @@ 
 \index{Ich zog mir einen Falken}
 \subtitle{Für A, wohl von 1160}
 \catcode`\[=12%allow using [ as parameter
-\includegraphics[width=0.96\linewidth, keepaspectratio, trim=0 160cm 0 0]{der-falke-tune}
+\includegraphics[width=0.96\linewidth, keepaspectratio]{der-falke-tune}
 \catcode`\[=13%force [ to start chords again
 
 \Large

          
A => filktex.el +244 -0
@@ 0,0 1,244 @@ 
+;;; filktex -- support for editing filktex files
+
+;;; Commentary:
+
+;; TODO: adapt chords-above-text conversion from https://github.com/ssavitzky/Honu/blob/master/emacs/flktex-mode.el#L124
+
+;;; Code:
+
+; How to write a good font=lock-mode http://ergoemacs.org/emacs/elisp_syntax_coloring.html (TODO…)
+
+(defun replace-regexp-buffer (reg to)
+  (replace-regexp reg to nil (point-min) (point-max)))
+
+(defun songbook->filktex ()
+  "Convert a buffer from songbook to filktex."
+  (interactive)
+  (save-excursion
+    (replace-string "{soc}" "\\begin{refrain}" nil (point-min) (point-max))
+    (replace-string "{eoc}" "\\end{refrain}" nil (point-min) (point-max))
+    (replace-regexp-buffer "{c:Lyrics:\\(.*\\)}" "\\\\lyrics{\\1}")
+    (replace-regexp-buffer "{c:Music:\\(.*\\)}" "\\\\music{\\1}")
+    (replace-regexp-buffer "{c:\\(.*\\):}" "\\\\inset{\\1}")
+    (replace-regexp-buffer "{c:\\(.*\\)}" "\\\\inset{\\1}")
+    (replace-regexp-buffer "{\\(title\\):\\(.*\\)}" "\\\\begin{song}{\\2}")
+    (replace-regexp-buffer "{\\(t\\):\\(.*\\)}" "\\\\begin{song}{\\2}")
+    (replace-regexp-buffer "{\\(subtitle\\):\\(.*\\)}" "\\\\subtitle{\\2}")
+    (replace-regexp-buffer "{\\(st\\):\\(.*\\)}" "\\\\subtitle{\\2}")
+    (replace-regexp-buffer "{\\(tag\\):\\(.*\\)}" "\\\\tags{\\2}")
+    (replace-regexp-buffer "{\\(time\\):\\(.*\\)}" "\\\\timing{\\2}")
+    ;; add verse breaks
+    (replace-regexp-buffer "\n *\n" "\n\\\\\\\\\n")
+    ;; comment out unknown commands on their own lines
+    (replace-regexp-buffer "^ *{\\(.*\\)} *$" "% \\1")
+    ;; fix literal &
+    (replace-string "&" "\\&" nil (point-min) (point-max))
+    ;; add potentially missing begin song
+    (goto-char (point-min))
+    (when (not (search-forward "\\begin{song}"))
+      (insert "\\begin{song}{TITLE}"))
+    (goto-char (point-max))
+    (insert "\n\\end{song}")))
+
+(defun filktex-mode-previous-definition ()
+  "Go to the previous verse, or to the start of the file."
+  (interactive)
+  (beginning-of-line)
+  (if (search-backward-regexp (rx (sequence (or "\n" "\\" "}") (* whitespace) "\n" (* whitespace) (or word-start "["))) nil t)
+      (when (match-end 0) (goto-char (match-end 0)))
+    ;; if the search fails, go to the beginning of the file
+    (goto-char (point-min)))
+  (when (equal (char-before (point)) (string-to-char "["))
+    (forward-char -1)))
+
+(defun filktex-mode-next-definition ()
+  "Go to the next verse, or to the end of the file."
+  (interactive)
+  (when (not
+         (search-forward-regexp (rx (sequence (or "\n" "\\" "}") (* whitespace) "\n" (* whitespace) (or word-start "["))) nil t))
+    ;; if the search fails, go to the end of the file
+    (goto-char (point-max)))
+  (when (equal (char-before (point)) (string-to-char "["))
+    (forward-char -1)))
+
+
+(defun filktex-mode-control-activate ()
+  (local-set-key "\M-n" 'filktex-mode-next-definition)
+  (local-set-key "\M-p" 'filktex-mode-previous-definition))
+
+;; TODO: use tex-mode for keywords (require 'tex-mode)
+
+(define-generic-mode 'filktex-mode ;; note: see customize group font lock faces to select faces
+  '(("%" . nil)) ; comments
+  '("song" "subtitle" "tags" "timing" "tailnotice" "tailnote" "begin" "end" "pagebreak" "twocolumn")  ; keywords
+  '(("{\\|}" 0 'font-lock-builtin-face t) ;; any curly brace: { or }
+    ("\\[\\([^\]]+\\)\\]" 1 'font-lock-constant-face t) ;; chords, i.e. [Dm]
+    ("\\\\\\([^{ ]*\\){\\([^}]+\\)}" 1 'font-lock-function-face) ;; i.e. \subtitle{some song}
+    ("\\\\\\([^{ ]*\\){\\([^}]+\\)}" 2 'font-lock-string-face) ;; i.e. \subtitle{some song}
+    )
+  '(".*\\.flk") ; auto-mode-alist
+  '(filktex-mode-control-activate)
+  "Generic mode for filktex files.")
+
+
+;;; Convert lyrics from "chords above text" to chords inline in brackets.
+;;; by Steve Savitzky: https://github.com/ssavitzky/Honu/blob/master/emacs/flktex-mode.el#L124
+(defun flk-inline-chords-at-point ()
+    "Convert the two lines at point from chords-over-lyrics to chords-inline.
+If executed on an empty line, it inserts two backslashes to mark the end of a verse."
+    (interactive)
+    (beginning-of-line)
+    (if (looking-at "^[:space:]*$")
+	(progn (flk-gobble-line) (insert "\\\\"))
+      ;; this would be simpler in Haskell, where a string is just a list of characters.
+      ;; as it is, we use lists to avoid creating unnecessary intermediate strings.
+      (mapc 'insert (flk-inline-chords (flk-gobble-line) (flk-gobble-line))))
+    (insert "\n"))
+
+(defun flk-gobble-line ()
+  "Remove the line at point, and return it as a list of 1-character strings."
+  (let ((c (char-after)))
+    (if (or (null c) (char-equal ?\n c))
+	(progn (delete-char 1) nil)
+      (cons (string  c) (progn (delete-char 1) (flk-gobble-line)))
+      )))
+
+(defun flk-inline-chords (chords lyrics)
+  "This function takes a line of chords and a line of lyrics, passed as lists of
+single-character strings, and converts them to a single line with chords inlined
+in brackets, returned as a list of strings.
+  If the lyrics (second) line is empty, return both lines unchanged."
+  (if (null lyrics)
+      (reverse (cons "\n" (reverse chords)))
+    (reverse (flk-convert-chords chords lyrics nil))))
+
+(defun flk-convert-chords (chords lyrics result)
+  "This function converts a line of chords and a line of lyrics into a list of strings
+in reverse order.  We do it this way to take advantage of tail recursion."
+  (cond ((and (null chords) (null lyrics))
+	 result)			; we're done
+	((null chords)
+	 ;; nothing left in chords; this just pushes the remaining lyrics onto result
+	 (flk-convert-chords nil (cdr lyrics) (cons (car lyrics) result)))
+	((equal " " (car chords))
+	 (if (null lyrics)
+	     (flk-convert-chords (cdr chords) nil (cons " " result))
+	   (flk-convert-chords (cdr chords) (cdr lyrics) (cons (car lyrics) result))))
+	((flk-convert-chord chords lyrics "" (cons "[" result)))
+	))
+
+(defun flk-convert-chord (chords lyrics lyrics-under-chord result)
+  "Convert a chord at the front of lyrics, and follow it with the remaining lyrics.
+The lyrics under the chord are accumulated in a string."
+  (cond ((or (null chords) (equal " " (car chords)))
+	 ;; we're done with this chord.
+	 (flk-convert-chords chords lyrics (cons (concat "]" lyrics-under-chord) result)))
+	((null lyrics)
+	 (flk-convert-chord (cdr chords) nil
+			lyrics-under-chord
+			(cons (flk-convert-chord-char (car chords) (cdr chords)) result)))
+	((flk-convert-chord (cdr chords) (cdr lyrics)
+			(concat lyrics-under-chord (car lyrics))
+			(cons (flk-convert-chord-char (car chords) (cdr chords)) result)))
+	))
+
+;;; Convert a single character in a chord.
+;;;    # -> \sharp
+;;;    b -> \flat
+;;;    m -> \min -- just \m if followed by a or i
+;;;    \s -> \s if followed by u, else s
+;;;    
+(defun flk-convert-chord-char (char rest)
+  (cond ((equal "#" char) "\\sharp")
+	((equal "b" char) "\\flat")
+	((equal "m" char) (cond ((null rest) "\\min")
+				((or (equal "i" (car rest))
+				     (equal "a" (car rest)))
+				   "\\m")
+				("\\min")))
+	((equal "s" char) (cond ((and (listp rest)
+				      (equal "u" (car rest)))
+				 "\\s")
+				("s")))
+	(char)))
+
+; for debugging -- really ought to make a test suite
+;(setq ch '("a" " " "b" " " "c"))
+;(setq ly '("x" "y" " " "z"))
+;(flk-inline-chords ch ly)
+
+
+(defun filktex--new-song ()
+  "Choose a new file in folder CATEGORY."
+  (interactive)
+   (let* ((default-directory (expand-file-name "~/Schreibtisch/Liederbuch/"))
+          (read-file-name-function 'read-file-name-default)
+          (filename ".flk"))
+     (filktex--create-song-file
+      (read-file-name "Open new song file: " default-directory nil nil filename))))
+
+
+(defcustom filktex--song-template
+"\\begin{song}[long]{BASENAME} % long for double-page songs
+% to add a tune, see child-of-the-library.flk
+\\index{BASENAME} % shown in the first-line index
+% \\subtitle{}
+% \\begin{twocolumns}
+
+\\begin{refrain}
+    
+\\end{refrain}
+
+Songtext
+\\\\
+Songtext
+
+\\begin{refrain}
+    \\inset{REFRAIN}
+\\end{refrain}
+\\begin{bridge}
+    \\inset{BRIDGE}
+\\end{bridge}
+\\begin{note}
+\\end{note}
+\\begin{quotation}
+\\end{quotation}
+% \\columnbreak % break column for twocolumn-mode
+% \\pagebreak % break page for twocolumn-mode
+\\\\
+% \\end{twocolumns}
+% \\tags{funny}
+% \\category{english}
+% \\key{em}
+% \\timing{6:00}
+% \\music{} % copyright
+% \\lyrics{} % copyright
+% \\arranger{} % copyright
+% \\description{}
+% \\dedication{}
+% \\notice{} % copyright notice and other notes
+% \\tailnote{} % note at the end
+\\end{song}
+" "Song template for new songs."
+:type 'string :group 'filktex)
+
+(defun filktex--create-song-file (filename)
+  "Create the new article in the CATEGORY with the FILENAME, adding the required link in configure.ac."
+  (let* ((read-file-name-function 'read-file-name-default)
+         (nondirectory (file-name-nondirectory filename))
+         (basename (file-name-base filename)))
+    (save-mark-and-excursion
+      (save-window-excursion
+        (find-file-existing "zongbook.tex")
+        (goto-char (point-max))
+        (search-backward "\\file{")
+        (goto-char (point-at-eol))
+        (insert (concat "\n\\file{" nondirectory "}\n"))
+        (save-buffer)))
+        
+    (find-file filename)
+    (insert (string-replace "BASENAME" basename filktex--song-template))
+    (goto-char (point-min))))
+
+(provide 'filktex)
+;;; filktex.el ends here

          
M zongbook.tex +1 -1
@@ 18,10 18,10 @@ 
 \begin{document}
 
 \maketitle
+\thispagestyle{empty}
 \ThisULCornerWallPaper{0.38}{draketo-dragon}
 \ThisURCornerWallPaper{0.38}{draketo-dragon-mirrored}
 \ThisCenterWallPaper{0.5}{SDOeclipse-bgwhite-flipped}
-
 \label{fig:draketo-dragon}\label{fig:sdoeclipse-bgwhite}\label{fig:lisar}\label{fig:lisar-new}\label{fig:glider-new}\label{fig:hurricane}\label{fig:runemaster}\label{fig:thursagan}\label{fig:fencer}\label{fig:alanin-older}\label{fig:mage-red-female}\label{fig:mage-arch-female}
 \newpage