;;; css-palette.el (defconst css-palette:version "0.02") ;; Copyright (C) 2008 Niels Giesen ;; Author: Niels Giesen ;; Keywords: processes, css, multimedia, extensions, tools ;; Homepage: http://niels.kicks-ass.org/ ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; css-palette defines commands to have "palettes" inside a block ;; comment to circumvent the absence of (color or other) variable ;; definitions in the CSS specification. It can import and export GIMP ;; color palettes. See the documentation of `css-palette-mode' ;; for details of usage. ;;; Installation: ;; Something like: ;; put it in your load-path. ;; (autoload 'css-palette-mode "css-palette" "" t) ;; (add-hook 'css-mode-hook ;; (lambda () ;; (css-palette-mode t))) ;; Notes: ;; css-palette depends on css-color.el to do font-locking. ;; ccs-palette is orthogonal to css-mode, so it could probably be used ;; inside other language modes, provided they support multiline block ;; comments. ;;; Change log: ;; 2009-01-11 Lennart Borgman ;; - Minor code clean up. ;;; Code: (require 'css-color) (eval-when-compile (require 'cl)) ;i'm a bad bad boy... (defconst css-palette-hex-chars "0123456789abcdefABCDEF" "Composing chars in hexadecimal notation, save for the hash (#) sign.") (defvar css-palette-mode-map (let ((m (make-sparse-keymap))) (define-key m "\C-c\C-c" 'css-palette-update-all) (define-key m "\C-c\C-i" 'css-palette-insert-reference) (define-key m "\C-c\C-p" 'css-palette-import-from-GIMP) (define-key m "\C-c\C-f" 'css-palette-insert-files) m) "Mode map for `css-palette-mode'") ;;;###autoload (define-minor-mode css-palette-mode "Minor mode for palettes in CSS. The mode `css-palette-mode' acts on the first COLORS declaration in your file of the form: COLORS: \( c0 \"#6f5d25\" ;tainted sand c1 \"#000000\" ;Black c2 \"#cca42b\" ;goldenslumber c3 \"#6889cb\" ;far off sky c4 \"#fff\" ;strange aeons ) Such declarations should appear inside a block comment, in order to be parsed properly by the LISP reader. Type \\[css-palette-update-all], and any occurence of color: #f55; /*[c3]*/ will be updated with color: #6899cb; /*[c3]*/ The following commands are available to insert key-value pairs and palette declarations: \\{css-palette-mode-map} You can extend or redefine the types of palettes by defining a new palette specification of the form (PATTERN REGEXP REF-FOLLOWS-VALUE), named according to the naming scheme css-palette:my-type, where PATTERN is a pattern containing two (%s) format directives which will be filled in with the variable and its value, REGEXP is a regular expression to match a value - variable pattern, and REF-FOLLOWS-VALUE defined whether or not the reference comes after the value. This allows for more flexibility. Note that, although the w3c spec at URL `http://www.w3.org/TR/CSS2/syndata.html#comments' says that comments \" may occur anywhere between tokens, and their contents have no influence on the rendering\", Internet Explorer does not think so. Better keep all your comments after a \"statement\", as per the default. This means `css-palette' is ill-suited for use within shorthands. See variable `css-palette:colors' for an example of a palette type. The extension mechanism means that palette types can be used to contain arbitrary key-value mappings. Besides the colors palette, css-palette defines the palette definition variables `css-palette:colors-outside' and `css-palette:files', for colors with the reference outside and for file url()'s respectively. You can fine-control which palette types css-palette should look at via the variable `css-palette-types'. " nil "-palette" css-palette-mode-map (css-color-mode +1)) ;;;###autoload (defgroup css-palette nil "Customization group for css-palette library. See function `css-palette-mode' for documentation" :group 'css-color) (defcustom css-palette:colors `("%s; /*[%s]*/ " ,(concat "\\(" css-color-color-re ;; (mapconcat ;; 'identity ;; (list css-color-hex-re ;; css-color-hsl-re ;; css-color-rgb-re) "\\|") "\\)" "[[:space:]]*;[[:space:]]*\/\\*\\[\\([^[:space:]]+\\)\\]\\*\/") t) "Color palette specification. See function `css-palette-mode' for documentation" :group 'css-palette :type '(list (string :tag "Pattern") (regexp :tag "Regexp") (boolean :tag "Reversed"))) (defcustom css-palette:files '("url(%s); /*[%s]*/ " "url(\\([^)]+\\))[[:space:]]*;[[:space:]]*\/\\*\\[\\([^[:space:]]+\\)\\]\\*\/" t) "File palette specification. See function `css-palette-mode' for documentation" :group 'css-palette :type '(list (string :tag "Pattern") (regexp :tag "Regexp") (boolean :tag "Reversed"))) (defcustom css-palette-types '(colors) "List of palette types to check for in buffer. See function `css-palette-mode' for documentation" :group 'css-palette :type '(repeat (symbol :tag "Palette type"))) (make-variable-buffer-local 'css-palette-types) ;; (defun css-palette-mode-turn-on () ;; "Turn on `css-palette-mode'." ;; (css-palette-mode 1)) ;; ;;;###autoload ;; (defcustom css-palette-mode-activate-p nil ;; "Start `css-palette-mode' when `css-mode' is activated." ;; :group 'css-palette ;; :set (lambda (sym val) ;; (set-default sym val) ;; (if val ;; (add-hook 'css-mode-hook 'css-palette-mode-turn-on) ;; (remove-hook 'css-mode-hook 'css-palette-mode-turn-on))) ;; :type 'boolean) (defun css-palette-turn-on-in-buffer () "Turn on `css-palette-mode' in `css-mode'." (when (derived-mode-p 'css-mode) (message "turn-on-in-b:before (css-palette-mode 1) cb=%s" (current-buffer)) (css-palette-mode 1) (message "turn-on-in-b:after (css-palette-mode 1)") )) ;;;###autoload (define-globalized-minor-mode css-palette-global-mode css-palette-mode css-palette-turn-on-in-buffer :group 'css-color) (defun css-palette-get (key spec) (plist-get (css-palette-spec-to-plist (symbol-value (intern-soft (format "css-palette:%s" spec)))) key)) (defun css-palette-spec-to-plist (palette) (destructuring-bind (pattern regexp ref-follows-value) palette (list :regexp regexp :pattern pattern :ref-follows-value ref-follows-value))) (defun css-palette-choose-type () (intern-soft (if (null (cdr css-palette-types)) (car css-palette-types) (completing-read "Type: " (mapcar 'symbol-name css-palette-types))))) (defun css-palette-get-declaration (type) "Return `css-palette' declaration of TYPE in current buffer. If none is found, throw an error." (let ((type (symbol-name type))) (save-excursion (goto-char (point-min)) (or (re-search-forward (format "%s:" (upcase type)) nil t) (error "No %s declaration found in buffer; check value of variable `css-palette-types'" type)) (let ((palette (read (current-buffer)))) ;; Check (could be better..) (if (not (and (listp palette) (= 0 (% (length palette) 2)))) (error "Invalid %s " type)) palette)))) (defun css-palette-update (type) "Update buffer references for palette of TYPE." (interactive (list (css-palette-choose-type))) (let ((palette (css-palette-get-declaration type)) (regexp (css-palette-get :regexp type)) (ref-follows-value (css-palette-get :ref-follows-value type))) (flet ((getval (key palette) (let ((value (plist-get palette (intern-soft key)))) (if (null value) (error "%S not specified in %S palette " key type ;; (signal 'css-palette-not-found-error nil) ) value)))) (save-excursion (goto-char (point-min)) (while (re-search-forward regexp (point-max) t) (replace-match (getval (match-string-no-properties (if ref-follows-value 2 1)) palette) nil nil nil (if ref-follows-value 1 2)))))) (css-color-mode 1)) (defun css-palette-update-all () "Update all references for palettes in `css-palette-types'" (interactive) (catch 'err (mapc (lambda (type) (condition-case err (css-palette-update type) (if (y-or-n-p (format "%s, skip? " err)) nil))) css-palette-types))) ;; Reference Insertion (defun css-palette-insert-reference (type) "Insert `css-palette' reference of TYPE at point." (interactive (list (css-palette-choose-type))) (let* ((palette (css-palette-get-declaration type)) (ref-follows-value (css-palette-get :ref-follows-value type)) (pattern (css-palette-get :pattern type)) (var (completing-read (format "%s variable: " (capitalize (substring (symbol-name type) 0 -1))) (loop for i on palette by 'cddr collect (css-palette-colorify (symbol-name (car i)) (cadr i))))) (val (plist-get palette (read var)))) (insert (apply 'format pattern (if ref-follows-value (list val var) (list var val)))) (css-color-mode +1))) (defun css-palette-hex-color-p (str) (string-match "#\\([a-fA-F[:digit:]]\\{6\\}\\|[a-fA-F[:digit:]]\\{3\\}\\)" str)) (defun css-palette-colorify (string color) (let ((color (if (css-palette-hex-color-p color) color "#000"))) (propertize string 'font-lock-face (list :background color :foreground (css-color-foreground-color color) string) 'fontified t))) ;; Imports (defun css-palette-from-existing-colors () (interactive) (let ((palette) (count -1)) (save-excursion (goto-char (point-min)) (while (re-search-forward "#[[:digit:]a-fA-F]\\{6\\}\\>" nil t) (if (not (member (match-string-no-properties 0) palette)) (setq palette (append (list (match-string-no-properties 0) (intern(format "c%d" (incf count)))) palette))) (save-match-data (re-search-forward ";" nil t)) (insert (format "/*[%S]*/" (cadr (member (match-string-no-properties 0) palette)))))) (insert (format "COLORS:\n%S" (nreverse palette))) (forward-sexp -1) (forward-char 1) (while (not (looking-at ")")) (forward-sexp 2) (newline) (indent-for-tab-command)))) (defun css-palette-newest-GIMP-dir () "Return newest (version-wise) ~/.gimp-n.n/palettes directory on disk. Return `nil' if none such directory is found." (catch 'none (concat (or (car (last (directory-files "~/" t "^.gimp-[[:digit:].]\\{3,\\}"))) (throw 'none ())) "/palettes/"))) (defun css-palette-import-from-GIMP () "Import GIMP palette file as a `css-palette' palette. GIMP palettes can be made with the GIMP or on-line tools such as found at URL `http://colourlovers.com'." (interactive) (let ((file (read-file-name "File: " (css-palette-newest-GIMP-dir))) (this-buffer (current-buffer)) (count -1)) (insert "\nCOLORS:\n(\n") (with-temp-buffer (insert-file-contents file) (goto-char (point-min)) (while (re-search-forward (concat "^" "[[:space:]]*\\([[:digit:]]+\\)" ;red "[[:space:]]+\\([[:digit:]]+\\)" ;green "[[:space:]]+\\([[:digit:]]+\\)" ;blue "[[:space:]]+\\(.*\\)$") ;name (=> used as comment) nil t) (destructuring-bind (rb re gb ge bb be nb ne &rest ignore) (cddr (match-data t)) (let ((color (apply 'format "c%d \"#%02x%02x%02x\" ;%s\n" (incf count) (append (mapcar 'string-to-number (list (buffer-substring-no-properties rb re) (buffer-substring-no-properties gb ge) (buffer-substring-no-properties bb be))) (list (buffer-substring-no-properties nb ne)))))) (with-current-buffer this-buffer (insert color)))))) (insert ")") (message "C-c C-c to update colors"))) (defun css-palette-insert-files (dir) "Insert a `css-palette' declaration for all files in DIR. Filenames are relative. Main use-case: an image directory." (interactive "DDirectory: ") (save-excursion (let ((image-count -1)) (insert "\nFILES:\n(\n") (mapc (lambda (f) (insert (format "file-%d %S\n" (incf image-count) (file-relative-name f (file-name-directory (buffer-file-name)))))) (directory-files dir t "...+")) (insert ")\n\n")))) ;; Exports (defun css-palette-export-to-GIMP (type name columns) "Export the COLORS declaration to a GIMP (.gpl) palette. See also `gpl-mode' at URL `http://niels.kicks-ass.org/public/elisp/gpl.el'." (interactive (list (css-palette-choose-type) (read-string "Name: ") (read-number "Number of columns: " 2))) (let ((palette (css-palette-get-declaration type))) (find-file (concat (css-palette-newest-GIMP-dir) name ".gpl")) (insert (format "GIMP Palette Name: %s Columns: %d # " name columns)) (loop for i on palette by 'cddr do (multiple-value-bind (r g b)(css-color-hex-to-rgb (css-color-hexify-anystring (cadr i))) (insert (format "%3d %3d %3d\t%s\n" r g b (car i)))))) (if (featurep 'gpl) (gpl-mode))) (provide 'css-palette) ;; css-palette.el ends here