changeset 67982:a711a44049e2

* cus-theme.el: Rewrite the Custom New Theme Mode interface. (custom-new-theme-mode-map, custom-theme-insert-variable-marker) (custom-theme-insert-face-marker, custom-theme-variable-menu) (custom-theme-face-menu): New variables. (custom-theme-add-variable, custom-theme-variable-action) (custom-variable-reset-theme, custom-theme-delete-variable) (custom-face-reset-theme, custom-theme-face-action) (custom-theme-delete-face, custom-theme-merge-theme) (custom-theme-add-face, custom-theme-visit-theme): New functions.
author Chong Yidong <cyd@stupidchicken.com>
date Mon, 02 Jan 2006 22:02:11 +0000
parents 6e1f6ef9bc5f
children eea3a49a9d6c
files lisp/ChangeLog lisp/cus-theme.el
diffstat 2 files changed, 289 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Mon Jan 02 18:10:16 2006 +0000
+++ b/lisp/ChangeLog	Mon Jan 02 22:02:11 2006 +0000
@@ -1,3 +1,15 @@
+2006-01-02  Chong Yidong  <cyd@stupidchicken.com>
+
+	* cus-theme.el: Rewrite the Custom New Theme Mode interface.
+	(custom-new-theme-mode-map, custom-theme-insert-variable-marker)
+	(custom-theme-insert-face-marker, custom-theme-variable-menu)
+	(custom-theme-face-menu): New variables.
+	(custom-theme-add-variable, custom-theme-variable-action)
+	(custom-variable-reset-theme, custom-theme-delete-variable)
+	(custom-face-reset-theme, custom-theme-face-action)
+	(custom-theme-delete-face, custom-theme-merge-theme)
+	(custom-theme-add-face, custom-theme-visit-theme): New functions.
+
 2006-01-01  Chong Yidong  <cyd@stupidchicken.com>
 
 	* custom.el: Move Custom Themes commentary to start of theme code.
--- a/lisp/cus-theme.el	Mon Jan 02 18:10:16 2006 +0000
+++ b/lisp/cus-theme.el	Mon Jan 02 22:02:11 2006 +0000
@@ -58,18 +58,18 @@
     (set (make-local-variable 'widget-link-suffix) "")))
 (put 'custom-new-theme-mode 'mode-class 'special)
 
-(defvar custom-theme-name)
-(defvar custom-theme-variables)
-(defvar custom-theme-faces)
+(defvar custom-theme-name nil)
+(defvar custom-theme-variables nil)
+(defvar custom-theme-faces nil)
 (defvar custom-theme-description)
+(defvar custom-theme-insert-variable-marker)
+(defvar custom-theme-insert-face-marker)
 
 ;;;###autoload
 (defun customize-create-theme ()
   "Create a custom theme."
   (interactive)
-  (if (get-buffer "*New Custom Theme*")
-      (kill-buffer "*New Custom Theme*"))
-  (switch-to-buffer "*New Custom Theme*")
+  (switch-to-buffer (generate-new-buffer "*New Custom Theme*"))
   (let ((inhibit-read-only t))
     (erase-buffer))
   (custom-new-theme-mode)
@@ -77,17 +77,39 @@
   (make-local-variable 'custom-theme-variables)
   (make-local-variable 'custom-theme-faces)
   (make-local-variable 'custom-theme-description)
+  (make-local-variable 'custom-theme-insert-variable-marker)
+  (make-local-variable 'custom-theme-insert-face-marker)
   (widget-insert "This buffer helps you write a custom theme elisp file.
 This will help you share your customizations with other people.
 
-Just insert the names of all variables and faces you want the theme
-to include.  Then clicking mouse-2 or pressing RET on the [Done] button
-will write a theme file that sets all these variables and faces to their
-current global values.  It will write that file into the directory given
-by the variable `custom-theme-directory', usually \"~/.emacs.d/\".
+Insert the names of all variables and faces you want the theme to include.
+Invoke \"Save Theme\" to save the theme.  The theme file will be saved to
+the directory " custom-theme-directory "\n\n")
+  (widget-create 'push-button
+		 :tag "Visit Theme"
+		 :help-echo "Insert the settings of a pre-defined theme."
+		 :action (lambda (widget &optional event)
+			   (call-interactively 'custom-theme-visit-theme)))
+  (widget-insert "  ")
+  (widget-create 'push-button
+		 :tag "Merge Theme"
+		 :help-echo "Merge in the settings of a pre-defined theme."
+		 :action (lambda (widget &optional event)
+			   (call-interactively 'custom-theme-merge-theme)))
+  (widget-insert "  ")
+  (widget-create 'push-button
+     		 :notify (lambda (&rest ignore)
+			   (when (y-or-n-p "Discard current changes?")
+			     (kill-buffer (current-buffer))
+			     (customize-create-theme)))
+     		 "Reset Buffer")
+  (widget-insert "  ")
+  (widget-create 'push-button
+     		 :notify (function custom-theme-write)
+     		 "Save Theme")
+  (widget-insert "\n")
 
-To undo all your edits to the buffer, use the [Reset] button.\n\n")
-  (widget-insert "Theme name: ")
+  (widget-insert "\n\nTheme name: ")
   (setq custom-theme-name
 	(widget-create 'editable-field
 		       :size 10
@@ -96,76 +118,254 @@
   (setq custom-theme-description
 	(widget-create 'text
 		       :value (format-time-string "Created %Y-%m-%d.")))
-  (widget-insert "\nVariables:\n\n")
-  (setq custom-theme-variables
-     	(widget-create 'editable-list
-     		       :entry-format "%i %d %v"
-		       'variable))
-  (widget-insert "\nFaces:\n\n")
-  (setq custom-theme-faces
-     	(widget-create 'editable-list
-     		       :entry-format "%i %d %v"
-		       'face))
   (widget-insert "\n")
   (widget-create 'push-button
-     		 :notify (function custom-theme-write)
-     		 "Done")
-  (widget-insert " ")
+		 :tag "Insert Variable"
+		 :help-echo "Add another variable to this theme."
+		 :action (lambda (widget &optional event)
+			   (call-interactively 'custom-theme-add-variable)))
+  (widget-insert "\n")
+  (setq custom-theme-insert-variable-marker (point-marker))
+  (widget-insert "\n")
   (widget-create 'push-button
-     		 :notify (lambda (&rest ignore)
-     			   (customize-create-theme))
-     		 "Reset")
-  (widget-insert " ")
+		 :tag "Insert Face"
+		 :help-echo "Add another face to this theme."
+		 :action (lambda (widget &optional event)
+			   (call-interactively 'custom-theme-add-face)))
+  (widget-insert "\n")
+  (setq custom-theme-insert-face-marker (point-marker))
+  (widget-insert "\n")
   (widget-create 'push-button
      		 :notify (lambda (&rest ignore)
-     			   (bury-buffer))
-     		 "Bury Buffer")
+			   (when (y-or-n-p "Discard current changes?")
+			     (kill-buffer (current-buffer))
+			     (customize-create-theme)))
+     		 "Reset Buffer")
+  (widget-insert "  ")
+  (widget-create 'push-button
+     		 :notify (function custom-theme-write)
+     		 "Save Theme")
   (widget-insert "\n")
+  (widget-setup)
+  (goto-char (point-min))
+  (message ""))
+
+;;; Theme variables
+
+(defun custom-theme-add-variable (symbol)
+  (interactive "vVariable name: ")
+  (save-excursion
+    (goto-char custom-theme-insert-variable-marker)
+    (if (assq symbol custom-theme-variables)
+	(message "%s is already in the theme" (symbol-name symbol))
+      (widget-insert "\n")
+      (let ((widget (widget-create 'custom-variable
+				   :tag (custom-unlispify-tag-name symbol)
+				   :custom-level 0
+				   :action 'custom-theme-variable-action
+				   :custom-state 'unknown
+				   :value symbol)))
+	(push (cons symbol widget) custom-theme-variables)
+	(custom-magic-reset widget))
+      (widget-setup))))
+
+(defvar custom-theme-variable-menu
+  `(("Reset to Current" custom-redraw
+     (lambda (widget)
+       (and (boundp (widget-value widget))
+	    (memq (widget-get widget :custom-state)
+		  '(themed modified changed)))))
+    ("Reset to Theme Value" custom-variable-reset-theme
+     (lambda (widget)
+       (let ((theme  (intern (widget-value custom-theme-name)))
+	     (symbol (widget-value widget))
+	     found)
+	 (and (custom-theme-p theme)
+	      (dolist (setting (get theme 'theme-settings) found)
+	 	(if (and (eq (cadr setting) symbol)
+	 		 (eq (car  setting) 'theme-value))
+	 	    (setq found t)))))))
+    ("---" ignore ignore)
+    ("Delete" custom-theme-delete-variable nil))
+  "Alist of actions for the `custom-variable' widget in Custom Theme Mode.
+See the documentation for `custom-variable'.")
+
+(defun custom-theme-variable-action (widget &optional event)
+  "Show the Custom Theme Mode menu for a `custom-variable' widget.
+Optional EVENT is the location for the menu."
+  (let ((custom-variable-menu custom-theme-variable-menu))
+    (custom-variable-action widget event)))
+
+(defun custom-variable-reset-theme (widget)
+  "Reset WIDGET to its value for the currently edited theme."
+  (let ((theme  (intern (widget-value custom-theme-name)))
+	(symbol (widget-value widget))
+	found)
+    (dolist (setting (get theme 'theme-settings))
+      (if (and (eq (cadr setting) symbol)
+	       (eq (car  setting) 'theme-value))
+	  (setq found setting)))
+    (widget-value-set (car (widget-get widget :children))
+		      (nth 3 found)))
+  (widget-put widget :custom-state 'themed)
+  (custom-redraw-magic widget)
   (widget-setup))
 
+(defun custom-theme-delete-variable (widget)
+  (setq custom-theme-variables
+	(assq-delete-all (widget-value widget) custom-theme-variables))
+  (widget-delete widget))
+
+;;; Theme faces
+
+(defun custom-theme-add-face (symbol)
+  (interactive (list (read-face-name "Face name" nil nil)))
+  (save-excursion
+    (goto-char custom-theme-insert-face-marker)
+    (if (assq symbol custom-theme-faces)
+	(message "%s is already in the theme" (symbol-name symbol))
+      (widget-insert "\n")
+      (let ((widget (widget-create 'custom-face
+				   :tag (custom-unlispify-tag-name symbol)
+				   :custom-level 0
+				   :action 'custom-theme-face-action
+				   :custom-state 'unknown
+				   :value symbol)))
+	(push (cons symbol widget) custom-theme-faces)
+	(custom-magic-reset widget)
+	(widget-setup)))))
+
+(defvar custom-theme-face-menu
+  `(("Reset to Theme Value" custom-face-reset-theme
+     (lambda (widget)
+       (let ((theme  (intern (widget-value custom-theme-name)))
+	     (symbol (widget-value widget))
+	     found)
+	 (and (custom-theme-p theme)
+	      (dolist (setting (get theme 'theme-settings) found)
+	 	(if (and (eq (cadr setting) symbol)
+	 		 (eq (car  setting) 'theme-face))
+	 	    (setq found t)))))))
+    ("---" ignore ignore)
+    ("Delete" custom-theme-delete-face nil))
+  "Alist of actions for the `custom-variable' widget in Custom Theme Mode.
+See the documentation for `custom-variable'.")
+
+(defun custom-theme-face-action (widget &optional event)
+  "Show the Custom Theme Mode menu for a `custom-face' widget.
+Optional EVENT is the location for the menu."
+  (let ((custom-face-menu custom-theme-face-menu))
+    (custom-face-action widget event)))
+
+(defun custom-face-reset-theme (widget)
+  "Reset WIDGET to its value for the currently edited theme."
+  (let ((theme  (intern (widget-value custom-theme-name)))
+	(symbol (widget-value widget))
+	found)
+    (dolist (setting (get theme 'theme-settings))
+      (if (and (eq (cadr setting) symbol)
+	       (eq (car  setting) 'theme-face))
+	  (setq found setting)))
+    (widget-value-set (car (widget-get widget :children))
+		      (nth 3 found)))
+  (widget-put widget :custom-state 'themed)
+  (custom-redraw-magic widget)
+  (widget-setup))
+
+(defun custom-theme-delete-face (widget)
+  (setq custom-theme-faces
+	(assq-delete-all (widget-value widget) custom-theme-faces))
+  (widget-delete widget))
+
+;;; Reading and writing
+
+(defun custom-theme-visit-theme ()
+  (interactive)
+  (when (or (null custom-theme-variables)
+	    (if (y-or-n-p "Discard current changes?")
+		(progn (customize-create-theme) t)))
+    (let ((theme (call-interactively 'custom-theme-merge-theme)))
+      (unless (eq theme 'user)
+	(widget-value-set custom-theme-name (symbol-name theme)))
+      (widget-value-set custom-theme-description
+			(or (get theme 'theme-documentation)
+			    (format-time-string "Created %Y-%m-%d.")))
+      (widget-setup))))
+
+(defun custom-theme-merge-theme (theme)
+  (interactive "SCustom theme name: ")
+  (unless (eq theme 'user)
+    (load-theme theme))
+  (let ((settings (get theme 'theme-settings)))
+    (dolist (setting settings)
+      (if (eq (car setting) 'theme-value)
+	  (custom-theme-add-variable (cadr setting))
+	(custom-theme-add-face (cadr setting)))))
+  (disable-theme theme)
+  theme)
+
 (defun custom-theme-write (&rest ignore)
-  (let ((name (widget-value custom-theme-name))
-	(doc (widget-value custom-theme-description))
-	(variables (widget-value custom-theme-variables))
-	(faces (widget-value custom-theme-faces)))
-    (switch-to-buffer (concat name "-theme.el"))
-    (emacs-lisp-mode)
-    (unless (file-exists-p custom-theme-directory)
-      (make-directory (file-name-as-directory custom-theme-directory) t))
-    (setq default-directory custom-theme-directory)
-    (setq buffer-file-name (expand-file-name (concat name "-theme.el")))
-    (let ((inhibit-read-only t))
-      (erase-buffer))
-    (insert "(deftheme " name)
-    (when doc
-      (newline)
-      (insert "  \"" doc "\""))
-    (insert  ")\n")
-    (custom-theme-write-variables name variables)
-    (custom-theme-write-faces name faces)
-    (insert "\n(provide-theme '" name ")\n")
-    (save-buffer)))
+  (let* ((name (widget-value custom-theme-name))
+	 (filename (expand-file-name (concat name "-theme.el")
+				     custom-theme-directory))
+	 (doc (widget-value custom-theme-description))
+	 (vars custom-theme-variables)
+	 (faces custom-theme-faces))
+    (cond ((or (string-equal name "")
+	      (string-equal name "user")
+	      (string-equal name "changed"))
+	   (error "Custom themes cannot be named `%s'" name))
+	  ((string-match " " name)
+	   (error "Custom theme names should not contain spaces"))
+	  ((if (file-exists-p filename)
+	       (not (y-or-n-p
+		     (format "File %s exists.  Overwrite? " filename))))
+	   (error "Aborted")))
+    (with-temp-buffer
+      (emacs-lisp-mode)
+      (unless (file-exists-p custom-theme-directory)
+	(make-directory (file-name-as-directory custom-theme-directory) t))
+      (setq buffer-file-name filename)
+      (erase-buffer)
+      (insert "(deftheme " name)
+      (if doc (insert "\n  \"" doc "\""))
+      (insert  ")\n")
+      (custom-theme-write-variables name vars)
+      (custom-theme-write-faces name faces)
+      (insert "\n(provide-theme '" name ")\n")
+      (save-buffer))
+    (dolist (var vars)
+      (widget-put (cdr var) :custom-state 'saved)
+      (custom-redraw-magic (cdr var)))
+    (dolist (face faces)
+      (widget-put (cdr face) :custom-state 'saved)
+      (custom-redraw-magic (cdr face)))))
 
 (defun custom-theme-write-variables (theme vars)
   "Write a `custom-theme-set-variables' command for THEME.
 It includes all variables in list VARS."
-  ;; Most code is stolen from `custom-save-variables'.
   (when vars
     (let ((standard-output (current-buffer)))
       (princ "\n(custom-theme-set-variables\n")
       (princ " '")
       (princ theme)
       (princ "\n")
-      (mapc (lambda (symbol)
-	      (when (boundp symbol)
-		(unless (bolp)
-		  (princ "\n"))
-		(princ " '(")
-		(prin1 symbol)
-		(princ " ")
-		(prin1 (custom-quote (symbol-value symbol)))
-		(princ ")")))
-	      vars)
+      (mapc (lambda (spec)
+	      (let* ((symbol (car spec))
+		     (child (car-safe (widget-get (cdr spec) :children)))
+		     (value (if child
+				(widget-value child)
+			      ;; For hidden widgets, use the standard value
+			      (get symbol 'standard-value))))
+		(when (boundp symbol)
+		  (unless (bolp)
+		    (princ "\n"))
+		  (princ " '(")
+		  (prin1 symbol)
+		  (princ " ")
+		  (prin1 (custom-quote value))
+		  (princ ")"))))
+	    vars)
       (if (bolp)
 	  (princ " "))
       (princ ")")
@@ -181,18 +381,19 @@
       (princ " '")
       (princ theme)
       (princ "\n")
-      (mapc (lambda (symbol)
-	      (when (facep symbol)
-		(unless (bolp)
-		  (princ "\n"))
-		(princ " '(")
-		(prin1 symbol)
-		(princ " ")
-		(prin1 (list (append '(t)
-				     (custom-face-attributes-get
-				      'font-lock-comment-face nil))))
-		(princ ")")))
-	      faces)
+      (mapc (lambda (spec)
+	      (let* ((symbol (car spec))
+		     (child (car-safe (widget-get (cdr spec) :children)))
+		     (value (if child (widget-value child))))
+		(when (and (facep symbol) child)
+		  (unless (bolp)
+		    (princ "\n"))
+		  (princ " '(")
+		  (prin1 symbol)
+		  (princ " ")
+		  (prin1 value)
+		  (princ ")"))))
+	    faces)
       (if (bolp)
 	  (princ " "))
       (princ ")")