# HG changeset patch # User Chong Yidong # Date 1286853021 14400 # Node ID 17bbe431e61675132b481f7845e46d709d80db72 # Parent 51080327d1735b085cb26c276fe59ca7c6cdcf1c New interface for choosing Custom themes. * lisp/cus-edit.el (custom--initialize-widget-variables): New function. (Custom-mode): Use it. * lisp/cus-face.el (custom-theme-set-faces): Remove dead code. Obey custom--inhibit-theme-enable. * lisp/cus-theme.el (describe-theme, customize-themes) (custom-theme-save): New commands. (custom-new-theme-mode-map): Bind C-x C-s. (custom-new-theme-mode): Use custom--initialize-widget-variables. (customize-create-theme): New optional arg THEME. (custom-theme-revert): Use it. (custom-theme-visit-theme): Remove dead code. (custom-theme-merge-theme): Use custom-available-themes. (custom-theme-write): Make interactive. (custom-theme-write): Use custom-theme-name-valid-p. (describe-theme-1, custom-theme-choose-revert) (custom-theme-checkbox-toggle, custom-theme-selections-toggle): New funs. (custom-theme-allow-multiple-selections): New option. (custom-theme-choose-mode): New major mode. * lisp/custom.el (custom-theme-set-variables): Remove dead code. Obey custom--inhibit-theme-enable. (custom--inhibit-theme-enable): New var. (provide-theme): Obey it. (load-theme): Replace load with manual read/eval, in order to check for correctness. Use custom-theme-name-valid-p. (custom-theme-name-valid-p): New function. (custom-available-themes): Use it. * lisp/help-mode.el (help-theme-def, help-theme-edit): New buttons. diff -r 51080327d173 -r 17bbe431e616 lisp/ChangeLog --- a/lisp/ChangeLog Tue Oct 12 04:45:24 2010 +0200 +++ b/lisp/ChangeLog Mon Oct 11 23:10:21 2010 -0400 @@ -1,3 +1,38 @@ +2010-10-12 Chong Yidong + + * cus-theme.el (describe-theme, customize-themes) + (custom-theme-save): New commands. + (custom-new-theme-mode-map): Bind C-x C-s. + (custom-new-theme-mode): Use custom--initialize-widget-variables. + (customize-create-theme): New optional arg THEME. + (custom-theme-revert): Use it. + (custom-theme-visit-theme): Remove dead code. + (custom-theme-merge-theme): Use custom-available-themes. + (custom-theme-write): Make interactive. + (custom-theme-write): Use custom-theme-name-valid-p. + (describe-theme-1, custom-theme-choose-revert) + (custom-theme-checkbox-toggle, custom-theme-selections-toggle): + New funs. + (custom-theme-allow-multiple-selections): New option. + (custom-theme-choose-mode): New major mode. + + * custom.el (custom-theme-set-variables): Remove dead code. Obey + custom--inhibit-theme-enable. + (custom--inhibit-theme-enable): New var. + (provide-theme): Obey it. + (load-theme): Replace load with manual read/eval, in order to + check for correctness. Use custom-theme-name-valid-p. + (custom-theme-name-valid-p): New function. + (custom-available-themes): Use it. + + * cus-edit.el (custom--initialize-widget-variables): New function. + (Custom-mode): Use it. + + * cus-face.el (custom-theme-set-faces): Remove dead code. Obey + custom--inhibit-theme-enable. + + * help-mode.el (help-theme-def, help-theme-edit): New buttons. + 2010-10-12 Juanma Barranquero * net/telnet.el (telnet-mode-map): Fix previous change (bug#7193). diff -r 51080327d173 -r 17bbe431e616 lisp/cus-edit.el --- a/lisp/cus-edit.el Tue Oct 12 04:45:24 2010 +0200 +++ b/lisp/cus-edit.el Mon Oct 11 23:10:21 2010 -0400 @@ -439,9 +439,6 @@ ;;; Custom mode keymaps (defvar custom-mode-map - ;; This keymap should be dense, but a dense keymap would prevent inheriting - ;; "\r" bindings from the parent map. - ;; Actually, this misfeature of dense keymaps was fixed on 2001-11-26. (let ((map (make-keymap))) (set-keymap-parent map widget-keymap) (define-key map [remap self-insert-command] 'Custom-no-edit) @@ -4706,6 +4703,25 @@ (if (eq (widget-get (widget-get widget :parent) :custom-state) 'modified) (message "To install your edits, invoke [State] and choose the Set operation"))) +(defun custom--initialize-widget-variables () + (set (make-local-variable 'widget-documentation-face) 'custom-documentation) + (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) + ;; We need this because of the "More" button on docstrings. + ;; Otherwise clicking on "More" can push point offscreen, which + ;; causes the window to recenter on point, which pushes the + ;; newly-revealed docstring offscreen; which is annoying. -- cyd. + (set (make-local-variable 'widget-button-click-moves-point) t) + ;; When possible, use relief for buttons, not bracketing. This test + ;; may not be optimal. + (when custom-raised-buttons + (set (make-local-variable 'widget-push-button-prefix) "") + (set (make-local-variable 'widget-push-button-suffix) "") + (set (make-local-variable 'widget-link-prefix) "") + (set (make-local-variable 'widget-link-suffix) "")) + (setq show-trailing-whitespace nil)) + (define-derived-mode Custom-mode nil "Custom" "Major mode for editing customization buffers. @@ -4743,28 +4759,7 @@ (setq custom-tool-bar-map map)))) (make-local-variable 'custom-options) (make-local-variable 'custom-local-buffer) - (make-local-variable 'widget-documentation-face) - (setq widget-documentation-face 'custom-documentation) - (make-local-variable 'widget-button-face) - (setq widget-button-face custom-button) - (setq show-trailing-whitespace nil) - - ;; We need this because of the "More" button on docstrings. - ;; Otherwise clicking on "More" can push point offscreen, which - ;; causes the window to recenter on point, which pushes the - ;; newly-revealed docstring offscreen; which is annoying. -- cyd. - (set (make-local-variable 'widget-button-click-moves-point) t) - - (set (make-local-variable 'widget-button-pressed-face) custom-button-pressed) - (set (make-local-variable 'widget-mouse-face) custom-button-mouse) - - ;; When possible, use relief for buttons, not bracketing. This test - ;; may not be optimal. - (when custom-raised-buttons - (set (make-local-variable 'widget-push-button-prefix) "") - (set (make-local-variable 'widget-push-button-suffix) "") - (set (make-local-variable 'widget-link-prefix) "") - (set (make-local-variable 'widget-link-suffix) "")) + (custom--initialize-widget-variables) (add-hook 'widget-edit-functions 'custom-state-buffer-message nil t)) (put 'Custom-mode 'mode-class 'special) diff -r 51080327d173 -r 17bbe431e616 lisp/cus-face.el --- a/lisp/cus-face.el Tue Oct 12 04:45:24 2010 +0200 +++ b/lisp/cus-face.el Mon Oct 11 23:10:21 2010 -0400 @@ -319,42 +319,32 @@ FACE's list property `theme-face' \(using `custom-push-theme')." (custom-check-theme theme) (let ((immediate (get theme 'theme-immediate))) - (while args - (let ((entry (car args))) - (if (listp entry) - (let ((face (nth 0 entry)) - (spec (nth 1 entry)) - (now (nth 2 entry)) - (comment (nth 3 entry)) - oldspec) - ;; If FACE is actually an alias, customize the face it - ;; is aliased to. - (if (get face 'face-alias) - (setq face (get face 'face-alias))) - - (setq oldspec (get face 'theme-face)) - (when (not (and oldspec (eq 'user (caar oldspec)))) - (put face 'saved-face spec) - (put face 'saved-face-comment comment)) - - (custom-push-theme 'theme-face face theme 'set spec) - (when (or now immediate) - (put face 'force-face (if now 'rogue 'immediate))) - (when (or now immediate (facep face)) - (unless (facep face) - (make-empty-face face)) - (put face 'face-comment comment) - (put face 'face-override-spec nil) - (face-spec-set face spec t)) - (setq args (cdr args))) - ;; Old format, a plist of FACE SPEC pairs. - (let ((face (nth 0 args)) - (spec (nth 1 args))) - (if (get face 'face-alias) - (setq face (get face 'face-alias))) - (put face 'saved-face spec) - (custom-push-theme 'theme-face face theme 'set spec)) - (setq args (cdr (cdr args)))))))) + (dolist (entry args) + (unless (listp entry) + (error "Incompatible Custom theme spec")) + (let ((face (car entry)) + (spec (nth 1 entry))) + ;; If FACE is actually an alias, customize the face it + ;; is aliased to. + (if (get face 'face-alias) + (setq face (get face 'face-alias))) + (custom-push-theme 'theme-face face theme 'set spec) + (unless custom--inhibit-theme-enable + ;; Now set the face spec. + (let ((now (nth 2 entry)) + (comment (nth 3 entry)) + (oldspec (get face 'theme-face))) + (when (not (and oldspec (eq 'user (caar oldspec)))) + (put face 'saved-face spec) + (put face 'saved-face-comment comment)) + (when (or now immediate) + (put face 'force-face (if now 'rogue 'immediate))) + (when (or now immediate (facep face)) + (unless (facep face) + (make-empty-face face)) + (put face 'face-comment comment) + (put face 'face-override-spec nil) + (face-spec-set face spec t)))))))) ;; XEmacs compability function. In XEmacs, when you reset a Custom ;; Theme, you have to specify the theme to reset it to. We just apply diff -r 51080327d173 -r 17bbe431e616 lisp/cus-theme.el --- a/lisp/cus-theme.el Tue Oct 12 04:45:24 2010 +0200 +++ b/lisp/cus-theme.el Mon Oct 11 23:10:21 2010 -0400 @@ -35,27 +35,18 @@ (let ((map (make-keymap))) (set-keymap-parent map widget-keymap) (suppress-keymap map) + (define-key map "\C-x\C-s" 'custom-theme-write) (define-key map "n" 'widget-forward) (define-key map "p" 'widget-backward) map) "Keymap for `custom-new-theme-mode'.") -(define-derived-mode custom-new-theme-mode nil "New-Theme" - "Major mode for the buffer created by `customize-create-theme'. -Do not call this mode function yourself. It is only meant for internal -use by `customize-create-theme'." +(define-derived-mode custom-new-theme-mode nil "Cus-Theme" + "Major mode for editing Custom themes. +Do not call this mode function yourself. It is meant for internal use." (use-local-map custom-new-theme-mode-map) - (define-key custom-new-theme-mode-map [mouse-1] 'widget-move-and-invoke) - (set (make-local-variable 'widget-documentation-face) 'custom-documentation) - (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) "") - (set (make-local-variable 'widget-link-prefix) "") - (set (make-local-variable 'widget-link-suffix) ""))) + (custom--initialize-widget-variables) + (set (make-local-variable 'revert-buffer-function) 'custom-theme-revert)) (put 'custom-new-theme-mode 'mode-class 'special) (defvar custom-theme-name nil) @@ -82,17 +73,21 @@ query-replace) "Faces listed by default in the *Custom Theme* buffer.") +(defvar custom-theme--save-name) + ;;;###autoload -(defun customize-create-theme (&optional buffer) - "Create a custom theme. +(defun customize-create-theme (&optional theme buffer) + "Create or edit a custom theme. +THEME, if non-nil, should be an existing theme to edit. BUFFER, if non-nil, should be a buffer to use." (interactive) - (switch-to-buffer (or buffer (generate-new-buffer "*Custom Theme*"))) + (switch-to-buffer (get-buffer-create (or buffer "*Custom Theme*"))) ;; Save current faces (let ((inhibit-read-only t)) (erase-buffer)) (custom-new-theme-mode) (make-local-variable 'custom-theme-name) + (set (make-local-variable 'custom-theme--save-name) theme) (set (make-local-variable 'custom-theme-faces) nil) (set (make-local-variable 'custom-theme-variables) nil) (set (make-local-variable 'custom-theme-description) "") @@ -116,7 +111,8 @@ (widget-insert "\n\nTheme name : ") (setq custom-theme-name - (widget-create 'editable-field)) + (widget-create 'editable-field + :value (if theme (symbol-name theme) ""))) (widget-insert "Description: ") (setq custom-theme-description (widget-create 'text @@ -164,14 +160,15 @@ :action (lambda (widget &optional event) (call-interactively 'custom-theme-add-variable))) (widget-insert ?\n) + (if theme + (custom-theme-merge-theme theme)) (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)))) + (customize-create-theme custom-theme--save-name (current-buffer)))) ;;; Theme variables @@ -318,10 +315,8 @@ (defun custom-theme-visit-theme () (interactive) - (when (or (and (null custom-theme-variables) - (null custom-theme-faces)) - (and (y-or-n-p "Discard current changes? ") - (progn (revert-buffer) t))) + (when (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))) @@ -331,9 +326,14 @@ (widget-setup)))) (defun custom-theme-merge-theme (theme) - (interactive "SCustom theme name: ") - (unless (eq theme 'user) - (load-theme theme)) + (interactive + (list + (intern (completing-read "Merge custom theme: " + (mapcar 'symbol-name + (custom-available-themes)))))) + (unless (custom-theme-name-valid-p theme) + (error "Invalid theme name `%s'" theme)) + (load-theme theme) (let ((settings (get theme 'theme-settings))) (dolist (setting settings) (if (eq (car setting) 'theme-value) @@ -343,6 +343,7 @@ theme) (defun custom-theme-write (&rest ignore) + (interactive) (let* ((name (widget-value custom-theme-name)) (doc (widget-value custom-theme-description)) (vars custom-theme-variables) @@ -351,12 +352,8 @@ (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")) - (error "Custom themes cannot be named `%s'" name)) - ((string-match " " name) - (error "Custom theme names should not contain spaces"))) + (unless (custom-theme-name-valid-p (intern name)) + (error "Custom themes cannot be named `%s'" name)) (setq filename (expand-file-name (concat name "-theme.el") custom-theme-directory)) @@ -384,7 +381,8 @@ (dolist (face custom-theme-faces) (when (widget-get (cdr face) :children) (widget-put (cdr face) :custom-state 'saved) - (custom-redraw-magic (cdr face)))))) + (custom-redraw-magic (cdr face)))) + (message "Theme written to %s" filename))) (defun custom-theme-write-variables (theme vars) "Write a `custom-theme-set-variables' command for THEME. @@ -456,5 +454,196 @@ (unless (looking-at "\n") (princ "\n"))))) + +;;; Describing Custom themes. + +;;;###autoload +(defun describe-theme (theme) + "Display a description of the Custom theme THEME (a symbol)." + (interactive + (list + (intern (completing-read "Describe custom theme: " + (mapcar 'symbol-name + (custom-available-themes)))))) + (unless (custom-theme-name-valid-p theme) + (error "Invalid theme name `%s'" theme)) + (help-setup-xref (list 'describe-theme theme) + (called-interactively-p 'interactive)) + (with-help-window (help-buffer) + (with-current-buffer standard-output + (describe-theme-1 theme)))) + +(defun describe-theme-1 (theme) + (prin1 theme) + (princ " is a custom theme") + (let ((fn (locate-file (concat (symbol-name theme) "-theme.el") + (cons custom-theme-directory load-path) + '("" "c")))) + (when fn + (princ " in `") + (help-insert-xref-button (file-name-nondirectory fn) + 'help-theme-def fn) + (princ "'")) + (princ ".\n")) + (if (not (memq theme custom-known-themes)) + (princ "It is not loaded.") + (if (custom-theme-enabled-p theme) + (princ "It is loaded and enabled.\n") + (princ "It is loaded but disabled.\n")) + (princ "\nDocumentation:\n") + (princ (or (get theme 'theme-documentation) + "No documentation available."))) + (princ "\n\nYou can ") + (help-insert-xref-button "customize" 'help-theme-edit theme) + (princ " this theme.")) + + +;;; Theme chooser + +(defvar custom--listed-themes) + +(defcustom custom-theme-allow-multiple-selections nil + "Whether to allow multi-selections in the *Custom Themes* buffer." + :type 'boolean + :group 'custom-buffer) + +(defvar custom-theme-choose-mode-map + (let ((map (make-keymap))) + (set-keymap-parent map widget-keymap) + (suppress-keymap map) + (define-key map "\C-x\C-s" 'custom-theme-save) + (define-key map "n" 'widget-forward) + (define-key map "p" 'widget-backward) + (define-key map "?" 'custom-describe-theme) + map) + "Keymap for `custom-theme-choose-mode'.") + +(define-derived-mode custom-theme-choose-mode nil "Cus-Theme" + "Major mode for selecting Custom themes. +Do not call this mode function yourself. It is meant for internal use." + (use-local-map custom-theme-choose-mode-map) + (custom--initialize-widget-variables) + (set (make-local-variable 'revert-buffer-function) + (lambda (ignore-auto noconfirm) + (when (or noconfirm (y-or-n-p "Discard current choices? ")) + (customize-themes (current-buffer)))))) +(put 'custom-theme-choose-mode 'mode-class 'special) + +;;;###autoload +(defun customize-themes (&optional buffer) + "Display a selectable list of Custom themes. +When called from Lisp, BUFFER should be the buffer to use; if +omitted, a buffer named *Custom Themes* is used." + (interactive) + (pop-to-buffer (get-buffer-create (or buffer "*Custom Themes*"))) + (let ((inhibit-read-only t)) + (erase-buffer)) + (custom-theme-choose-mode) + (set (make-local-variable 'custom--listed-themes) nil) + (make-local-variable 'custom-theme-allow-multiple-selections) + (and (null custom-theme-allow-multiple-selections) + (> (length custom-enabled-themes) 1) + (setq custom-theme-allow-multiple-selections t)) + + (widget-insert + (substitute-command-keys + "Type RET or click to enable/disable listed custom themes. +Type \\[custom-describe-theme] to describe the theme at point. +Theme files are named *-theme.el in `")) + (when (stringp custom-theme-directory) + (widget-create 'link :value custom-theme-directory + :button-face 'custom-link + :mouse-face 'highlight + :pressed-face 'highlight + :help-echo "Describe `custom-theme-directory'." + :keymap custom-mode-link-map + :follow-link 'mouse-face + :action (lambda (widget &rest ignore) + (describe-variable 'custom-theme-directory))) + (widget-insert "' or `")) + (widget-create 'link :value "load-path" + :button-face 'custom-link + :mouse-face 'highlight + :pressed-face 'highlight + :help-echo "Describe `load-path'." + :keymap custom-mode-link-map + :follow-link 'mouse-face + :action (lambda (widget &rest ignore) + (describe-variable 'load-path))) + (widget-insert "'.\n\n") + (widget-create 'push-button + :tag " Save Theme Settings " + :help-echo "Save the selected themes for future sessions." + :action 'custom-theme-save) + (widget-insert ?\n) + (widget-create 'checkbox + :value custom-theme-allow-multiple-selections + :action 'custom-theme-selections-toggle) + (widget-insert (propertize " Allow more than one theme at a time" + 'face '(variable-pitch (:height 0.9)))) + + (widget-insert "\n\nAvailable Custom Themes:\n") + (let (widget) + (dolist (theme (custom-available-themes)) + (setq widget (widget-create 'checkbox + :value (custom-theme-enabled-p theme) + :theme-name theme + :action 'custom-theme-checkbox-toggle)) + (push (cons theme widget) custom--listed-themes) + (widget-create-child-and-convert widget 'push-button + :button-face-get 'ignore + :mouse-face-get 'ignore + :value (format " %s" theme) + :action 'widget-parent-action) + (widget-insert ?\n))) + (goto-char (point-min)) + (widget-setup)) + +(defun custom-theme-checkbox-toggle (widget &optional event) + (let ((this-theme (widget-get widget :theme-name))) + (if (widget-value widget) + ;; Disable the theme. + (disable-theme this-theme) + ;; Enable the theme. + (unless custom-theme-allow-multiple-selections + ;; If only one theme is allowed, disable all other themes and + ;; uncheck their boxes. + (dolist (theme custom-enabled-themes) + (and (not (eq theme this-theme)) + (assq theme custom--listed-themes) + (disable-theme theme))) + (dolist (theme custom--listed-themes) + (unless (eq (car theme) this-theme) + (widget-value-set (cdr theme) nil) + (widget-apply (cdr theme) :notify (cdr theme) event)))) + (load-theme this-theme))) + ;; Mark `custom-enabled-themes' as "set for current session". + (put 'custom-enabled-themes 'customized-value + (list (custom-quote custom-enabled-themes))) + ;; Check/uncheck the widget. + (widget-toggle-action widget event)) + +(defun custom-describe-theme () + "Describe the Custom theme on the current line." + (interactive) + (let ((widget (widget-at (line-beginning-position)))) + (and widget + (describe-theme (widget-get widget :theme-name))))) + +(defun custom-theme-save (&rest ignore) + (interactive) + (customize-save-variable 'custom-enabled-themes custom-enabled-themes) + (message "Custom themes saved for future sessions.")) + +(defun custom-theme-selections-toggle (widget &optional event) + (when (widget-value widget) + ;; Deactivate multiple-selections. + (if (> (length (delq nil (mapcar (lambda (x) (widget-value (cdr x))) + custom--listed-themes))) + 1) + (error "More than one theme is currently selected"))) + (widget-toggle-action widget event) + (setq custom-theme-allow-multiple-selections (widget-value widget))) + ;; arch-tag: cd6919bc-63af-410e-bae2-b6702e762344 ;;; cus-theme.el ends here diff -r 51080327d173 -r 17bbe431e616 lisp/custom.el --- a/lisp/custom.el Tue Oct 12 04:45:24 2010 +0200 +++ b/lisp/custom.el Mon Oct 11 23:10:21 2010 -0400 @@ -959,48 +959,39 @@ (t (or (nth 3 a2) (eq (get sym2 'custom-set) 'custom-set-minor-mode)))))))) - (while args - (let ((entry (car args))) - (if (listp entry) - (let* ((symbol (indirect-variable (nth 0 entry))) - (value (nth 1 entry)) - (now (nth 2 entry)) - (requests (nth 3 entry)) - (comment (nth 4 entry)) - set) - (when requests - (put symbol 'custom-requests requests) - (mapc 'require requests)) - (setq set (or (get symbol 'custom-set) 'custom-set-default)) - (put symbol 'saved-value (list value)) - (put symbol 'saved-variable-comment comment) - (custom-push-theme 'theme-value symbol theme 'set value) - ;; Allow for errors in the case where the setter has - ;; changed between versions, say, but let the user know. - (condition-case data - (cond (now - ;; Rogue variable, set it now. - (put symbol 'force-value t) - (funcall set symbol (eval value))) - ((default-boundp symbol) - ;; Something already set this, overwrite it. - (funcall set symbol (eval value)))) - (error - (message "Error setting %s: %s" symbol data))) - (setq args (cdr args)) - (and (or now (default-boundp symbol)) - (put symbol 'variable-comment comment))) - ;; I believe this is dead-code, because the `sort' code above would - ;; have burped before we could get here. --Stef - ;; Old format, a plist of SYMBOL VALUE pairs. - (message "Warning: old format `custom-set-variables'") - (ding) - (sit-for 2) - (let ((symbol (indirect-variable (nth 0 args))) - (value (nth 1 args))) + + (dolist (entry args) + (unless (listp entry) + (error "Incompatible Custom theme spec")) + (let* ((symbol (indirect-variable (nth 0 entry))) + (value (nth 1 entry))) + (custom-push-theme 'theme-value symbol theme 'set value) + (unless custom--inhibit-theme-enable + ;; Now set the variable. + (let* ((now (nth 2 entry)) + (requests (nth 3 entry)) + (comment (nth 4 entry)) + set) + (when requests + (put symbol 'custom-requests requests) + (mapc 'require requests)) + (setq set (or (get symbol 'custom-set) 'custom-set-default)) (put symbol 'saved-value (list value)) - (custom-push-theme 'theme-value symbol theme 'set value)) - (setq args (cdr (cdr args))))))) + (put symbol 'saved-variable-comment comment) + ;; Allow for errors in the case where the setter has + ;; changed between versions, say, but let the user know. + (condition-case data + (cond (now + ;; Rogue variable, set it now. + (put symbol 'force-value t) + (funcall set symbol (eval value))) + ((default-boundp symbol) + ;; Something already set this, overwrite it. + (funcall set symbol (eval value)))) + (error + (message "Error setting %s: %s" symbol data))) + (and (or now (default-boundp symbol)) + (put symbol 'variable-comment comment))))))) ;;; Defining themes. @@ -1072,6 +1063,12 @@ :group 'customize :version "22.1") +(defvar custom--inhibit-theme-enable nil + "If non-nil, loading a theme does not enable it. +This internal variable is set by `load-theme' when its NO-ENABLE +argument is non-nil, and it affects `custom-theme-set-variables', +`custom-theme-set-faces', and `provide-theme'." ) + (defun provide-theme (theme) "Indicate that this file provides THEME. This calls `provide' to provide the feature name stored in THEME's @@ -1081,35 +1078,83 @@ (error "Custom theme cannot be named %S" theme)) (custom-check-theme theme) (provide (get theme 'theme-feature)) - ;; Loading a theme also enables it. - (push theme custom-enabled-themes) - ;; `user' must always be the highest-precedence enabled theme. - ;; Make that remain true. (This has the effect of making user settings - ;; override the ones just loaded, too.) - (let ((custom-enabling-themes t)) - (enable-theme 'user))) + (unless custom--inhibit-theme-enable + ;; Loading a theme also enables it. + (push theme custom-enabled-themes) + ;; `user' must always be the highest-precedence enabled theme. + ;; Make that remain true. (This has the effect of making user settings + ;; override the ones just loaded, too.) + (let ((custom-enabling-themes t)) + (enable-theme 'user)))) -(defun load-theme (theme) +(defun load-theme (theme &optional no-enable) "Load a theme's settings from its file. -This also enables the theme; use `disable-theme' to disable it." +Normally, this also enables the theme; use `disable-theme' to +disable it. If optional arg NO-ENABLE is non-nil, don't enable +the theme." ;; Note we do no check for validity of the theme here. ;; This allows to pull in themes by a file-name convention (interactive (list (intern (completing-read "Load custom theme: " - (mapcar 'symbol-name (custom-available-themes)))))) + (mapcar 'symbol-name + (custom-available-themes)))))) + (unless (custom-theme-name-valid-p theme) + (error "Invalid theme name `%s'" theme)) ;; If reloading, clear out the old theme settings. (when (custom-theme-p theme) (disable-theme theme) (put theme 'theme-settings nil) (put theme 'theme-feature nil) (put theme 'theme-documentation nil)) - (let ((load-path (if (file-directory-p custom-theme-directory) - (cons custom-theme-directory load-path) - load-path))) - (load (symbol-name (custom-make-theme-feature theme))))) + (let ((fn (locate-file (concat (symbol-name theme) "-theme.el") + (cons custom-theme-directory load-path) + '("" "c")))) + (unless fn + (error "Unable to find theme file for `%s'." theme)) + ;; Instead of simply loading the theme file, read it manually. + (with-temp-buffer + (insert-file-contents fn) + (let ((custom--inhibit-theme-enable no-enable) + sexp scar) + (while (setq sexp (let ((read-circle nil)) + (condition-case nil + (read (current-buffer)) + (end-of-file nil)))) + ;; Perform some checks on each sexp before evaluating it. + (cond + ((not (listp sexp))) + ((eq (setq scar (car sexp)) 'deftheme) + (unless (eq (cadr sexp) theme) + (error "Incorrect theme name in `deftheme'")) + (and (symbolp (nth 1 sexp)) + (stringp (nth 2 sexp)) + (eval (list scar (nth 1 sexp) (nth 2 sexp))))) + ((or (eq scar 'custom-theme-set-variables) + (eq scar 'custom-theme-set-faces)) + (unless (equal (nth 1 sexp) `(quote ,theme)) + (error "Incorrect theme name in theme settings")) + (dolist (entry (cddr sexp)) + (unless (eq (car-safe entry) 'quote) + (error "Unsafe expression in theme settings"))) + (eval sexp)) + ((and (eq scar 'provide-theme) + (equal (cadr sexp) `(quote ,theme)) + (= (length sexp) 2)) + (eval sexp)))))))) + +(defun custom-theme-name-valid-p (name) + "Return t if NAME is a valid name for a Custom theme, nil otherwise. +NAME should be a symbol." + (and (symbolp name) + name + (not (or (zerop (length (symbol-name name))) + (eq name 'cus) + (eq name 'user) + (eq name 'changed))))) (defun custom-available-themes () + "Return a list of available Custom themes (symbols)." (let* ((load-path (if (file-directory-p custom-theme-directory) (cons custom-theme-directory load-path) load-path)) @@ -1120,7 +1165,7 @@ (setq file (file-name-nondirectory file)) (and (string-match "\\`\\(.+\\)-theme.el\\'" file) (setq sym (intern (match-string 1 file))) - (not (memq sym '(cus user changed color))) + (custom-theme-name-valid-p sym) (push sym themes)))) (delete-dups themes))) diff -r 51080327d173 -r 17bbe431e616 lisp/help-mode.el --- a/lisp/help-mode.el Tue Oct 12 04:45:24 2010 +0200 +++ b/lisp/help-mode.el Mon Oct 11 23:10:21 2010 -0400 @@ -255,6 +255,15 @@ 'help-function (lambda (file) (dired file)) 'help-echo (purecopy "mouse-2, RET: visit package directory")) +(define-button-type 'help-theme-def + :supertype 'help-xref + 'help-function 'find-file + 'help-echo (purecopy "mouse-2, RET: visit theme file")) + +(define-button-type 'help-theme-edit + :supertype 'help-xref + 'help-function 'customize-create-theme + 'help-echo (purecopy "mouse-2, RET: edit this theme file")) ;;;###autoload (defun help-mode ()