changeset 110896:0d6d71f9b91a

Interface improvements to cus-theme.el. * cus-edit.el (custom-face-widget-to-spec) (custom-face-get-current-spec, custom-face-state): New functions. (custom-face-set, custom-face-mark-to-save) (custom-face-value-create, custom-face-state-set): Use them. * cus-theme.el (custom-theme--listed-faces): New var. (customize-create-theme): Use *Custom Theme* as the buffer name. Set revert-buffer-function. Optional arg BUFFER. Insert all faces listed in custom-theme--listed-faces. (custom-theme-revert): New function. (custom-theme-add-variable, custom-theme-add-face): Insert at the bottom of the list. (custom-theme-write): Prompt for theme name if empty. (custom-theme-write-variables): Use dolist. (custom-theme-write-faces): Handle hidden (collapsed) widgets.
author Chong Yidong <cyd@stupidchicken.com>
date Sat, 09 Oct 2010 17:54:20 -0400
parents b803c876a460
children ddcc974a9f3d
files lisp/ChangeLog lisp/cus-edit.el lisp/cus-theme.el
diffstat 3 files changed, 235 insertions(+), 163 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Sat Oct 09 23:38:04 2010 +0200
+++ b/lisp/ChangeLog	Sat Oct 09 17:54:20 2010 -0400
@@ -1,3 +1,21 @@
+2010-10-09  Chong Yidong  <cyd@stupidchicken.com>
+
+	* cus-edit.el (custom-face-widget-to-spec)
+	(custom-face-get-current-spec, custom-face-state): New functions.
+	(custom-face-set, custom-face-mark-to-save)
+	(custom-face-value-create, custom-face-state-set): Use them.
+
+	* cus-theme.el (custom-theme--listed-faces): New var.
+	(customize-create-theme): Use *Custom Theme* as the buffer name.
+	Set revert-buffer-function.  Optional arg BUFFER.  Insert all
+	faces listed in custom-theme--listed-faces.
+	(custom-theme-revert): New function.
+	(custom-theme-add-variable, custom-theme-add-face): Insert at the
+	bottom of the list.
+	(custom-theme-write): Prompt for theme name if empty.
+	(custom-theme-write-variables): Use dolist.
+	(custom-theme-write-faces): Handle hidden (collapsed) widgets.
+
 2010-10-09  Alan Mackenzie  <acm@muc.de>
 
 	Enhance fontification of declarators to take account of the
--- a/lisp/cus-edit.el	Sat Oct 09 23:38:04 2010 +0200
+++ b/lisp/cus-edit.el	Sat Oct 09 17:54:20 2010 -0400
@@ -3379,6 +3379,30 @@
   "Return the customized SPEC in a form suitable for setting the face."
   (custom-filter-face-spec spec 3))
 
+(defun custom-face-widget-to-spec (widget)
+  "Return a face spec corresponding to WIDGET.
+WIDGET should be a `custom-face' widget."
+  (unless (eq (widget-type widget) 'custom-face)
+    (error "Invalid widget"))
+  (let ((child (car (widget-get widget :children))))
+    (custom-post-filter-face-spec
+     (if (eq (widget-type child) 'custom-face-edit)
+	 `((t ,(widget-value child)))
+       (widget-value child)))))
+
+(defun custom-face-get-current-spec (face)
+  (let ((spec (or (get face 'customized-face)
+		  (get face 'saved-face)
+		  (get face 'face-defface-spec)
+		  ;; Attempt to construct it.
+		  `((t ,(custom-face-attributes-get
+			 face (selected-frame)))))))
+    ;; If the user has changed this face in some other way,
+    ;; edit it as the user has specified it.
+    (if (not (face-spec-match-p face spec (selected-frame)))
+	(setq spec `((t ,(face-attr-construct face (selected-frame))))))
+    (custom-pre-filter-face-spec spec)))
+
 (defun custom-face-value-create (widget)
   "Create a list of the display specifications for WIDGET."
   (let* ((buttons (widget-get widget :buttons))
@@ -3464,21 +3488,10 @@
 	(unless (widget-get widget :custom-form)
 	  (widget-put widget :custom-form custom-face-default-form))
 
-	(let* ((spec (or (get symbol 'customized-face)
-			 (get symbol 'saved-face)
-			 (get symbol 'face-defface-spec)
-			 ;; Attempt to construct it.
-			 (list (list t (custom-face-attributes-get
-					symbol (selected-frame))))))
+	(let* ((spec (custom-face-get-current-spec symbol))
 	       (form (widget-get widget :custom-form))
 	       (indent (widget-get widget :indent))
 	       face-alist face-entry spec-default spec-match editor)
-	  ;; If the user has changed this face in some other way,
-	  ;; edit it as the user has specified it.
-	  (if (not (face-spec-match-p symbol spec (selected-frame)))
-	      (setq spec `((t ,(face-attr-construct symbol
-						    (selected-frame))))))
-	  (setq spec (custom-pre-filter-face-spec spec))
 
 	  ;; Find a display in SPEC matching the selected display.
 	  ;; This will use the usual face customization interface.
@@ -3570,43 +3583,43 @@
   (widget-put widget :custom-form 'lisp)
   (custom-redraw widget))
 
+(defun custom-face-state (face)
+  "Return the current state of the face FACE.
+This is one of `set', `saved', `changed', `themed', or `rogue'."
+  (let* ((comment (get face 'face-comment))
+	 (state
+	  (cond
+	   ((or (get face 'customized-face)
+		(get face 'customized-face-comment))
+	    (if (equal (get face 'customized-face-comment) comment)
+		'set
+	      'changed))
+	   ((or (get face 'saved-face)
+		(get face 'saved-face-comment))
+	    (if (equal (get face 'saved-face-comment) comment)
+		(cond
+		 ((eq 'user (caar (get face 'theme-face)))
+		  'saved)
+		 ((eq 'changed (caar (get face 'theme-face)))
+		  'changed)
+		 (t 'themed))
+	      'changed))
+	   ((get face 'face-defface-spec)
+	    (if (equal comment nil)
+		'standard
+	      'changed))
+	   (t 'rogue))))
+    ;; If the user called set-face-attribute to change the default for
+    ;; new frames, this face is "set outside of Customize".
+    (if (and (not (eq state 'rogue))
+	     (get face 'face-modified))
+	'changed
+      state)))
+
 (defun custom-face-state-set (widget)
   "Set the state of WIDGET."
-  (let* ((symbol (widget-value widget))
-	 (comment (get symbol 'face-comment))
-	 tmp temp
-	 (state
-	  (cond ((progn
-		   (setq tmp (get symbol 'customized-face))
-		   (setq temp (get symbol 'customized-face-comment))
-		   (or tmp temp))
-		 (if (equal temp comment)
-		     'set
-		   'changed))
-		((progn
-		   (setq tmp (get symbol 'saved-face))
-		   (setq temp (get symbol 'saved-face-comment))
-		   (or tmp temp))
-		 (if (equal temp comment)
-		     (cond
-		      ((eq 'user (caar (get symbol 'theme-face)))
-		       'saved)
-		      ((eq 'changed (caar (get symbol 'theme-face)))
-		       'changed)
-		      (t 'themed))
-		   'changed))
-		((get symbol 'face-defface-spec)
-		 (if (equal comment nil)
-		     'standard
-		   'changed))
-		(t
-		 'rogue))))
-    ;; If the user called set-face-attribute to change the default
-    ;; for new frames, this face is "set outside of Customize".
-    (if (and (not (eq state 'rogue))
-	     (get symbol 'face-modified))
-	(setq state 'changed))
-    (widget-put widget :custom-state state)))
+  (widget-put widget :custom-state
+	      (custom-face-state (widget-value widget))))
 
 (defun custom-face-action (widget &optional event)
   "Show the menu for `custom-face' WIDGET.
@@ -3626,11 +3639,7 @@
 (defun custom-face-set (widget)
   "Make the face attributes in WIDGET take effect."
   (let* ((symbol (widget-value widget))
-	 (child (car (widget-get widget :children)))
-	 (value (custom-post-filter-face-spec
-		 (if (eq (widget-type child) 'custom-face-edit)
-		     `((t ,(widget-value child)))
-		   (widget-value child))))
+	 (value  (custom-face-widget-to-spec widget))
 	 (comment-widget (widget-get widget :comment-widget))
 	 (comment (widget-value comment-widget)))
     (when (equal comment "")
@@ -3652,11 +3661,7 @@
 (defun custom-face-mark-to-save (widget)
   "Mark for saving the face edited by WIDGET."
   (let* ((symbol (widget-value widget))
-	 (child (car (widget-get widget :children)))
-	 (value (custom-post-filter-face-spec
-		 (if (eq (widget-type child) 'custom-face-edit)
-		     `((t ,(widget-value child)))
-		   (widget-value child))))
+	 (value  (custom-face-widget-to-spec widget))
 	 (comment-widget (widget-get widget :comment-widget))
 	 (comment (widget-value comment-widget)))
     (when (equal comment "")
--- a/lisp/cus-theme.el	Sat Oct 09 23:38:04 2010 +0200
+++ b/lisp/cus-theme.el	Sat Oct 09 17:54:20 2010 -0400
@@ -50,6 +50,7 @@
   (set (make-local-variable 'widget-button-face) custom-button)
   (set (make-local-variable 'widget-button-pressed-face) custom-button-pressed)
   (set (make-local-variable 'widget-mouse-face) custom-button-mouse)
+  (set (make-local-variable 'revert-buffer-function) 'custom-theme-revert)
   (when custom-raised-buttons
     (set (make-local-variable 'widget-push-button-prefix) "")
     (set (make-local-variable 'widget-push-button-suffix) "")
@@ -60,95 +61,118 @@
 (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)
+(defvar custom-theme-description nil)
+(defvar custom-theme-insert-variable-marker nil)
+(defvar custom-theme-insert-face-marker nil)
+
+(defvar custom-theme--listed-faces '(default fixed-pitch
+  variable-pitch escape-glyph minibuffer-prompt highlight region
+  shadow secondary-selection trailing-whitespace
+  font-lock-builtin-face font-lock-comment-delimiter-face
+  font-lock-comment-face font-lock-constant-face
+  font-lock-doc-face font-lock-function-name-face
+  font-lock-keyword-face font-lock-negation-char-face
+  font-lock-preprocessor-face font-lock-regexp-grouping-backslash
+  font-lock-regexp-grouping-construct font-lock-string-face
+  font-lock-type-face font-lock-variable-name-face
+  font-lock-warning-face button link link-visited fringe
+  header-line tooltip mode-line mode-line-buffer-id
+  mode-line-emphasis mode-line-highlight mode-line-inactive
+  isearch isearch-fail lazy-highlight match next-error
+  query-replace)
+  "Faces listed by default in the *Custom Theme* buffer.")
 
 ;;;###autoload
-(defun customize-create-theme ()
-  "Create a custom theme."
+(defun customize-create-theme (&optional buffer)
+  "Create a custom theme.
+BUFFER, if non-nil, should be a buffer to use."
   (interactive)
-  (switch-to-buffer (generate-new-buffer "*New Custom Theme*"))
+  (switch-to-buffer (or buffer (generate-new-buffer "*Custom Theme*")))
+  ;; Save current faces
   (let ((inhibit-read-only t))
     (erase-buffer))
   (custom-new-theme-mode)
   (make-local-variable 'custom-theme-name)
-  (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)
+  (set (make-local-variable 'custom-theme-faces) nil)
+  (set (make-local-variable 'custom-theme-variables) nil)
+  (set (make-local-variable 'custom-theme-description) "")
   (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.
+  (make-local-variable 'custom-theme-insert-variable-marker)
+  (make-local-variable 'custom-theme--listed-faces)
 
-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"
+		 :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"
+		 :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")
+  (widget-create 'push-button :notify 'revert-buffer " Revert ")
 
-  (widget-insert "\n\nTheme name: ")
+  (widget-insert "\n\nTheme name : ")
   (setq custom-theme-name
-	(widget-create 'editable-field
-		       :size 10
-		       user-login-name))
-  (widget-insert "\n\nDocumentation:\n")
+	(widget-create 'editable-field))
+  (widget-insert "Description: ")
   (setq custom-theme-description
 	(widget-create 'text
 		       :value (format-time-string "Created %Y-%m-%d.")))
-  (widget-insert "\n")
+  (widget-insert "             ")
+  (widget-create 'push-button
+     		 :notify (function custom-theme-write)
+     		 " Save Theme ")
+  ;; Face widgets
+  (widget-insert "\n\n  Theme faces:\n")
+  (let (widget)
+    (dolist (face custom-theme--listed-faces)
+      (widget-insert "  ")
+      (setq widget (widget-create 'custom-face
+				  :documentation-shown t
+				  :tag (custom-unlispify-tag-name face)
+				  :value face
+				  :display-style 'concise
+				  :custom-state 'hidden
+				  :sample-indent 34))
+      (custom-magic-reset widget)
+      (push (cons face widget) custom-theme-faces)))
+  (insert " ")
+  (setq custom-theme-insert-face-marker (point-marker))
+  (insert " ")
+  (widget-create 'push-button
+		 :tag "Insert Additional Face"
+		 :help-echo "Add another face to this theme."
+		 :follow-link 'mouse-face
+		 :button-face 'custom-link
+		 :mouse-face 'highlight
+		 :pressed-face 'highlight
+		 :action (lambda (widget &optional event)
+			   (call-interactively 'custom-theme-add-face)))
+  (widget-insert "\n\n  Theme variables:\n ")
+  (setq custom-theme-insert-variable-marker (point-marker))
+  (widget-insert ?\s)
   (widget-create 'push-button
 		 :tag "Insert Variable"
 		 :help-echo "Add another variable to this theme."
+		 :follow-link 'mouse-face
+		 :button-face 'custom-link
+		 :mouse-face 'highlight
+		 :pressed-face 'highlight
 		 :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
-		 :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)
-			   (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-insert ?\n)
   (widget-setup)
   (goto-char (point-min))
   (message ""))
 
+(defun custom-theme-revert (ignore-auto noconfirm)
+  (when (or noconfirm (y-or-n-p "Discard current changes? "))
+    (erase-buffer)
+    (customize-create-theme (current-buffer))))
+
 ;;; Theme variables
 
 (defun custom-theme-add-variable (symbol)
@@ -162,7 +186,7 @@
 	(t
 	 (save-excursion
 	   (goto-char custom-theme-insert-variable-marker)
-	   (widget-insert "\n")
+	   (widget-insert " ")
 	   (let ((widget (widget-create 'custom-variable
 					:tag (custom-unlispify-tag-name symbol)
 					:custom-level 0
@@ -171,6 +195,8 @@
 					:value symbol)))
 	     (push (cons symbol widget) custom-theme-variables)
 	     (custom-magic-reset widget))
+	   (widget-insert " ")
+	   (move-marker custom-theme-insert-variable-marker (point))
 	   (widget-setup)))))
 
 (defvar custom-theme-variable-menu
@@ -231,15 +257,19 @@
 	(t
 	 (save-excursion
 	   (goto-char custom-theme-insert-face-marker)
-	   (widget-insert "\n")
+	   (widget-insert " ")
 	   (let ((widget (widget-create 'custom-face
 					:tag (custom-unlispify-tag-name symbol)
 					:custom-level 0
 					:action 'custom-theme-face-action
 					:custom-state 'unknown
+					:display-style 'concise
+					:sample-indent 34
 					:value symbol)))
 	     (push (cons symbol widget) custom-theme-faces)
 	     (custom-magic-reset widget)
+	     (widget-insert " ")
+	     (move-marker custom-theme-insert-face-marker (point))
 	     (widget-setup))))))
 
 (defvar custom-theme-face-menu
@@ -288,9 +318,10 @@
 
 (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)))
+  (when (or (and (null custom-theme-variables)
+		 (null custom-theme-faces))
+	    (and (y-or-n-p "Discard current changes? ")
+		 (progn (revert-buffer) t)))
     (let ((theme (call-interactively 'custom-theme-merge-theme)))
       (unless (eq theme 'user)
 	(widget-value-set custom-theme-name (symbol-name theme)))
@@ -313,21 +344,26 @@
 
 (defun custom-theme-write (&rest ignore)
   (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))
+	 (vars  custom-theme-variables)
+	 (faces custom-theme-faces)
+	 filename)
+    (when (string-equal name "")
+      (setq name (read-from-minibuffer "Theme name: " (user-login-name)))
+      (widget-value-set custom-theme-name name))
     (cond ((or (string-equal name "")
-	      (string-equal name "user")
-	      (string-equal name "changed"))
+	       (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")))
+	   (error "Custom theme names should not contain spaces")))
+
+    (setq filename (expand-file-name (concat name "-theme.el")
+				     custom-theme-directory))
+    (and (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)
@@ -342,11 +378,13 @@
       (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)))))
+      (when (widget-get (cdr var) :children)
+	(widget-put (cdr var) :custom-state 'saved)
+	(custom-redraw-magic (cdr var))))
+    (dolist (face custom-theme-faces)
+      (when (widget-get (cdr face) :children)
+	(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.
@@ -357,22 +395,21 @@
       (princ " '")
       (princ theme)
       (princ "\n")
-      (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)
+      (dolist (spec vars)
+	(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 ")"))))
       (if (bolp)
 	  (princ " "))
       (princ ")")
@@ -388,19 +425,31 @@
       (princ " '")
       (princ theme)
       (princ "\n")
-      (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)
+      (dolist (spec faces)
+	(let* ((symbol (car spec))
+	       (widget (cdr spec))
+	       (child  (car-safe (widget-get widget :children)))
+	       (state  (if child
+			   (widget-get widget :custom-state)
+			 (custom-face-state symbol)))
+	       (value
+		(cond ((eq state 'standard)
+		       nil) ; do nothing
+		      (child
+		       (custom-face-widget-to-spec widget))
+		      (t
+		       ;; Widget is closed (hidden), but the face has
+		       ;; a non-standard value.  Try to extract that
+		       ;; value and save it.
+		       (custom-face-get-current-spec symbol)))))
+	  (when (and (facep symbol) value)
+	    (if (bolp)
+		(princ " '(")
+	      (princ "\n '("))
+	    (prin1 symbol)
+	    (princ " ")
+	    (prin1 value)
+	    (princ ")"))))
       (if (bolp)
 	  (princ " "))
       (princ ")")