Mercurial > hg > xemacs-beta
diff lisp/utils/speedbar.el @ 100:4be1180a9e89 r20-1b2
Import from CVS: tag r20-1b2
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:15:11 +0200 |
parents | |
children | 85ec50267440 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/utils/speedbar.el Mon Aug 13 09:15:11 2007 +0200 @@ -0,0 +1,1394 @@ +;;; speedbar - quick access to files and tags +;;; +;;; Copyright (C) 1996 Eric M. Ludlam +;;; +;;; Author: Eric M. Ludlam <zappo@gnu.ai.mit.edu> +;;; RCS: $Id: speedbar.el,v 1.1 1997/02/17 06:40:14 steve Exp $ +;;; Version: 0.3.1 +;;; Keywords: file, tags, tools +;;; +;;; 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 2, 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, you can either send email to this +;;; program's author (see below) or write to: +;;; +;;; The Free Software Foundation, Inc. +;;; 675 Mass Ave. +;;; Cambridge, MA 02139, USA. +;;; +;;; Please send bug reports, etc. to zappo@gnu.ai.mit.edu. +;;; + +;;; Commentary: +;;; +;;; The speedbar provides a frame in which files, and locations in +;;; files are displayed. These items can be clicked on with mouse-2 +;;; in order to make the last active frame display that file location. +;;; +;;; If you want to choose it from a menu or something, do this: +;;; +;;; (autoload 'speedbar-frame-mode "speedbar" "Popup a speedbar frame" t) +;;; (define-key-after (lookup-key global-map [menu-bar tools]) +;;; [speedbar] '("Speedbar" . speedbar-frame-mode) [calendar]) +;;; +;;; To activate speedbar without the menu, type: M-x speedbar-frame-mode RET +;;; +;;; Once a speedbar frame is active, it takes advantage of idle time +;;; to keep it's contents updated. The contents is usually a list of +;;; files in the directory of the currently active buffer. When +;;; applicable, tags in the active file can be expanded. +;;; +;;; Speedbar uses multiple methods for creating tags to jump to. +;;; When the variable `speedbar-use-imenu-package' is set, then +;;; speedbar will first try to use imenu to get tags. If the mode of +;;; the buffer doesn't support imenu, then etags is used. Using Imenu +;;; has the advantage that tags are cached, so opening and closing +;;; tags lists is faster. Speedbar-imenu will also load the file into +;;; a non-selected buffer so clicking the file later will be faster. +;;; +;;; To add new files types into the speedbar, modify +;;; `speedbar-file-regexp' to include the extension of the file type +;;; you wish to include. If speedbar complains that the file type is +;;; not supported, that means there is no built in support from imenu, +;;; and the etags part wasn't set up right. +;;; +;;; To add new file types to imenu, see the documentation in the +;;; file imenu.el that comes with emacs. To add new file types which +;;; etags supports, you need to modify the variable +;;; `speedbar-fetch-etags-parse-list'. This variable is an +;;; association list with each element of the form: (extension-regex +;;; . parse-one-line) The extension-regex would be something like +;;; "\\.c$" for a .c file, and the parse-one-line would be either a +;;; regular expression where match tag 1 is the element you wish +;;; displayed as a tag. If you need to do something more complex, +;;; then you can also write a function which parses one line, and put +;;; its symbol there instead. +;;; +;;; If the updates are going to slow for you, modify the variable +;;; `speedbar-update-speed' to a longer idle time before updates. +;;; +;;; If you navigate directories, you will probably notice that you +;;; will navigate to a directory which is eventually replaced after +;;; you go back to editing a file (unless you pull up a new file.) +;;; The delay time before this happens is in +;;; `speedbar-navigating-speed', and defaults to 20 seconds. +;;; +;;; XEmacs users may want to change the default timeouts for +;;; `speedbar-update-speed' to something longer as XEmacs doesn't have +;;; idle timers, the speedbar timer keeps going off arbitrarilly while +;;; you're typing. It's quite pesky. +;;; +;;; To get speedbar-configure-faces to work, you will need to +;;; download my eieio package from my ftp site. +;;; +;;; EIEIO is NOT required when using speedbar. It is ONLY needed +;;; if you want to use a fancy dialog face editor for speedbar. + +;;; Speedbar updates can be found at: +;;; ftp://ftp.ultranet.com/pub/zappo/speedbar.*.el +;;; + +;;; HISTORY: +;;; 0.1 Initial Revision +;;; 0.2 Fixed problem with x-pointer-shape causing future frames not +;;; to be created. +;;; Fixed annoying habit of `speedbar-update-contents' to make +;;; it possible to accidentally kill the speedbar buffer. +;;; Clicking directory names now only changes the contents of +;;; the speedbar, and does not cause a dired mode to appear. +;;; Clicking the <+> next to the directory does cause dired to +;;; be run. +;;; Added XEmacs support, which means timer support moved to a +;;; platform independant call. +;;; Added imenu support. Now modes are supported by imenu +;;; first, and etags only if the imenu call doesn't work. +;;; Imenu is a little faster than etags, and is more emacs +;;; friendly. +;;; Added more user control variables described in the commentary. +;;; Added smart recentering when nodes are opened and closed. +;;; 0.3 x-pointer-shape fixed for emacs 19.35, so I put that check in. +;;; Added invisible codes to the beginning of each line. +;;; Added list aproach to node expansion for easier addition of new +;;; types of things to expand by +;;; Added multi-level path name support +;;; Added multi-level tag name support. +;;; Only mouse-2 is now used for node expansion +;;; Added keys e + - to edit expand, and contract node lines +;;; Added longer legal file regexp for all those modes which support +;;; imenu. (pascal, fortran90, ada, pearl) +;;; Fixed centering algorithm +;;; Tried to choose background independent colors. Made more robust. +;;; Rearranged code into a more logical order +;;; 0.3.1 Fixed doc & broken keybindings +;;; Added mode hooks. +;;; Improved color selection to be background mode smart +;;; `nil' passed to `speedbar-frame-mode' now toggles the frame as +;;; advertised in the doc string +;;; +;;; TODO: +;;; 1) Rember contents of directories when leaving them so it's faster +;;; when returning. +;;; 2) List of directories to never visit. (User might be browsing +;;; there temporarilly such as info files, documentation and the +;;; like) +;;; 3) Implement SHIFT-mouse2 to rescan buffers with imenu. +;;; 4) Better XEmacs support of menus and button-bar +;;; 5) More functions to create buttons and options +;;; 6) filtering algoritms to reduce the number of tags/files +;;; displayed. +;;; 7) Build `speedbar-file-regexp' on the fly. +;;; 8) More intelligent current file highlighting. + +(defvar speedbar-xemacsp (string-match "XEmacs" emacs-version)) + +(defvar speedbar-initial-expansion-list + '(speedbar-directory-buttons speedbar-default-directory-list) + "*List of functions to call to fill in the speedbar buffer whenever +a top level update is issued. These functions will allways get the +default directory to use passed in as the first parameter, and a 0 as +the second parameter. They must assume that the cursor is at the +postion where they start inserting buttons.") + +(defvar speedbar-show-unknown-files nil + "*Non-nil shows files with a ? in the expansion tag for files we can't +expand. `nil' means don't show the file in the list.") + +;; Xemacs timers aren't based on idleness. Therefore tune it down a little +;; or suffer mightilly! +(defvar speedbar-update-speed (if speedbar-xemacsp 5 1) + "*Time in seconds of idle time needed before speedbar will update +it's buffer to match what you've been doing in your other frame.") +(defvar speedbar-navigating-speed 10 + "*Idle time to wait before re-running the timer proc to pick up any new +activity if the user has started navigating directories in the speedbar.") + +(defvar speedbar-width 20 + "*Initial size of the speedbar window") + +(defvar speedbar-scrollbar-width 10 + "*Initial sizeo of the speedbar scrollbar. The thinner, the more +display room you will have.") + +(defvar speedbar-raise-lower t + "*Non-nil means speedbar will auto raise and lower itself. When this +is set, you can have only a tiny strip visible under your main emacs, +and it will raise and lower itself when you put the pointer in it.") + +(defvar speedbar-use-imenu-package (not speedbar-xemacsp) + "*Optionally use the imenu package instead of etags for parsing. This +is experimental for performace testing.") + +(defvar speedbar-before-delete-hook nil + "*Hooks called before deletiing the speedbar frame.") + +(defvar speedbar-mode-hook nil + "*Hooks called after creating a speedbar buffer") + +(defvar speedbar-timer-hook nil + "*Hooks called after running the speedbar timer function") + +(defvar speedbar-file-unshown-regexp + (let ((nstr "") (noext completion-ignored-extensions)) + (while noext + (setq nstr (concat nstr (regexp-quote (car noext)) "$" + (if (cdr noext) "\\|" "")) + noext (cdr noext))) + (concat nstr "\\|#[^#]+#$\\|\\.\\.?$")) + "*Regular expression matching files we don't want to display in a +speedbar buffer") + +(defvar speedbar-file-regexp + (if speedbar-use-imenu-package + "\\(\\.\\([CchH]\\|c\\(++\\|pp\\)\\|f90\\|ada\\|pl?\\|el\\|t\\(ex\\(i\\(nfo\\)?\\)?\\|cl\\)\\|emacs\\)$\\)\\|[Mm]akefile\\(\\.in\\)?" + "\\.\\([CchH]\\|c\\(++\\|pp\\)\\|p\\|el\\|tex\\(i\\(nfo\\)?\\)?\\|emacs\\)$") + "*Regular expresson matching files we know how to expand.") + +(defvar speedbar-syntax-table nil + "Syntax-table used on the speedbar") + +(if speedbar-syntax-table + nil + (setq speedbar-syntax-table (make-syntax-table)) + ;; turn off paren matching around here. + (modify-syntax-entry ?\' " " speedbar-syntax-table) + (modify-syntax-entry ?\" " " speedbar-syntax-table) + (modify-syntax-entry ?( " " speedbar-syntax-table) + (modify-syntax-entry ?) " " speedbar-syntax-table) + (modify-syntax-entry ?[ " " speedbar-syntax-table) + (modify-syntax-entry ?] " " speedbar-syntax-table)) + + +(defvar speedbar-key-map nil + "Keymap used in speedbar buffer.") +(defvar speedbar-menu-map nil + "Keymap used in speedbar menu buffer.") + +(if speedbar-key-map + nil + (setq speedbar-key-map (make-keymap)) + (suppress-keymap speedbar-key-map t) + + (define-key speedbar-key-map "e" 'speedbar-edit-line) + (define-key speedbar-key-map "+" 'speedbar-expand-line) + (define-key speedbar-key-map "-" 'speedbar-contract-line) + + (if (string-match "XEmacs" emacs-version) + (progn + ;; bind mouse bindings so we can manipulate the items on each line + (define-key speedbar-key-map 'button2 'speedbar-click) + + ;; Xemacs users. You probably want your own toolbar for + ;; the speedbar frame or mode or whatever. Make some buttons + ;; and mail me how to do it! + ;; Also, how do you disable all those menu items? Email me that too + ;; as it would be most helpful. + ) + ;; bind mouse bindings so we can manipulate the items on each line + (define-key speedbar-key-map [mouse-2] 'speedbar-click) + (define-key speedbar-key-map [down-mouse-2] 'speedbar-quick-mouse) + + ;; this was meant to do a rescan or something + ;;(define-key speedbar-key-map [shift-mouse-2] 'speedbar-hard-click) + + ;; disable all menus - we don't have a lot of space to play with + ;; in such a skinny frame. + (define-key speedbar-key-map [menu-bar buffer] 'undefined) + (define-key speedbar-key-map [menu-bar files] 'undefined) + (define-key speedbar-key-map [menu-bar tools] 'undefined) + (define-key speedbar-key-map [menu-bar edit] 'undefined) + (define-key speedbar-key-map [menu-bar search] 'undefined) + (define-key speedbar-key-map [menu-bar help-menu] 'undefined) + + ;; This lets the user scroll as if we had a scrollbar... well maybe not + (define-key speedbar-key-map [mode-line mouse-2] 'speedbar-mouse-hscroll) + + ;; Create a menu for speedbar + (setq speedbar-menu-map (make-sparse-keymap)) + (define-key speedbar-key-map [menu-bar speedbar] + (cons "Speedbar" speedbar-menu-map)) + (define-key speedbar-menu-map [close] + (cons "Close" 'speedbar-close-frame)) + (define-key speedbar-menu-map [clonfigure] + (cons "Configure Faces" 'speedbar-configure-faces)) + (define-key speedbar-menu-map [configopt] + (cons "Configure Options" 'speedbar-configure-options)) + (define-key speedbar-menu-map [Update] + (cons "Update" 'speedbar-update-contents)) + )) + +(put 'speedbar-configure-faces 'menu-enable '(featurep 'dialog)) +(put 'speedbar-configure-options 'menu-enable '(featurep 'dialog)) + +(defvar speedbar-buffer nil + "The buffer displaying the speedbar.") +(defvar speedbar-frame nil + "The frame displaying speedbar.") +(defvar speedbar-timer nil + "The speedbar timer used for updating the buffer.") +(defvar speedbar-attached-frame nil + "The frame which started speedbar mode. This is the frame from +which all data displayed in the speedbar is gathered, and in which files +and such are displayed.") + +(defvar speedbar-last-selected-file nil + "The last file which was selected in speedbar buffer") + +(defvar speedbar-shown-directories nil + "Used to maintain list of directories simultaneously open in the current +speedbar.") + + +;;; +;;; Mode definitions/ user commands +;;; +;;;###autoload +(defun speedbar-frame-mode (&optional arg) + "Enable or disable use of a speedbar. Positive number means turn +on, negative turns speedbar off, and nil means toggle. Once the +speedbar frame is activated, a buffer in `speedbar-mode' will be +displayed. Currently, only one speedbar is supported at a time." + (interactive "P") + (if (not window-system) + (error "Speedbar is not useful outside of a windowing environement")) + ;; toggle frame on and off. + (if (not arg) (if speedbar-frame (setq arg -1) (setq arg 1))) + ;; turn the frame off on neg number + (if (and (numberp arg) (< arg 0)) + (progn + (run-hooks 'speedbar-before-delete-hook) + (if (and speedbar-frame (frame-live-p speedbar-frame)) + (delete-frame speedbar-frame)) + (speedbar-set-timer nil) + (setq speedbar-frame nil) + (if (bufferp speedbar-buffer) + (kill-buffer speedbar-buffer))) + ;; Set this as our currently attached frame + (setq speedbar-attached-frame (selected-frame)) + ;; Get the buffer to play with + (speedbar-mode) + ;; Get the frame to work in + (if (and speedbar-frame (frame-live-p speedbar-frame)) + (raise-frame speedbar-frame) + (let ((params (list + ;; Xemacs fails to delete speedbar + ;; if minibuffer is off. + ;; JTL <<<< Seems to be OK for 19.15. + ;; removed tool- & menubar. + ;; <<<< JTL + (cons 'minibuffer nil) + (cons 'width speedbar-width) + (cons 'height (frame-height)) + (cons 'scroll-bar-width speedbar-scrollbar-width) + (cons 'auto-raise speedbar-raise-lower) + (cons 'auto-lower speedbar-raise-lower) + '(modeline . nil) + '(border-width . 0) + '(unsplittable . t) + '(default-toolbar-visible-p . nil) + '(menubar-visible-p . nil)))) + (setq speedbar-frame + (if (< emacs-minor-version 35) + (make-frame params) + (let ((x-pointer-shape x-pointer-top-left-arrow) + (x-sensitive-text-pointer-shape x-pointer-hand2)) + (make-frame params))))) + ;; reset the selection variable + (setq speedbar-last-selected-file nil) + ;; Put the buffer into the frame + (save-window-excursion + (select-frame speedbar-frame) + (switch-to-buffer speedbar-buffer) + (setq default-minibuffer-frame speedbar-attached-frame)) + (speedbar-set-timer speedbar-update-speed) + ))) + +(defun speedbar-close-frame () + "Turn off speedbar mode" + (interactive) + (speedbar-frame-mode -1)) + +(defun speedbar-mode () + "Create and return a SPEEDBAR buffer. The speedbar buffer allows +the user to manage a list of directories and paths at different +depths. The first line represents the default path of the speedbar +frame. Each directory segment is a button which jumps speedbar's +default directory to that path. Buttons are activated by clicking +mouse-2. + +Each line starting with <+> represents a directory. Click on the <+> +to insert the directory listing into the current tree. Click on the +<-> to retract that list. Click on the directory name to go to that +directory as the default. + +Each line starting with [+] is a file. If the variable +`speedbar-show-unknown-files' is t, the lines starting with [?] are +files which don't have imenu support, but are not expressly ignored. +Files are completely ignored if they match `speedbar-file-unshown-regexp' +which is generated from `completion-ignored-extensions'. + +Click on the [+] to display a list of tags from that file. Click on +the [-] to retract the list. Click on the file name to edit the file +in the attached frame. + +If you open tags, you might find a node starting with {+}, which is a +category of tags. Click the {+} to expand the category. Jumpable +tags start with >. Click the name of the tag to go to that position +in the selected file. + +Keybindings: \\<speedbar-key-map> +\\[speedbar-click] Activate the button under the mouse. +\\[speedbar-edit-line] Edit the file/directory on this line. Same as clicking + on the name on the selected line.) +\\[speedbar-expand-line] Expand the current line. Same as clicking on the + on a line. +\\[speedbar-contract-line] Contract the current line. Same as clicking on the - on a line." + (setq speedbar-buffer (set-buffer (get-buffer-create "SPEEDBAR"))) + (kill-all-local-variables) + (setq major-mode 'speedbar-mode) + (setq mode-name "SB") + (use-local-map speedbar-key-map) + (set-syntax-table speedbar-syntax-table) + (setq mode-line-format + '("<< SPEEDBAR " (line-number-mode " %3l ") " >>")) + (setq font-lock-keywords nil) ;; no font-locking please + (setq truncate-lines t) + (if (not speedbar-xemacsp) (setq auto-show-mode nil)) ;no auto-show for FSF + (run-hooks 'speedbar-mode-hook) + (speedbar-update-contents) + ) + +(defun speedbar-mouse-hscroll (e) + "Read a mouse event from the mode line, and horizontally scroll if the +mouse is being clicked on the far left, or far right of the modeline." + (interactive "e") + (let* ((xp (car (nth 2 (car (cdr e))))) + (cpw (/ (frame-pixel-width) + (frame-width))) + (oc (1+ (/ xp cpw))) + ) + (cond ((< oc 3) + (scroll-left 2)) + ((> oc (- (window-width) 3)) + (scroll-right 2)) + (t (message "Click on the edge of the modeline to scroll left/right"))) + ;;(message "X: Pixel %d Char Pixels %d On char %d" xp cpw oc) + )) + + +;;; +;;; Utility functions +;;; +(defun speedbar-set-timer (timeout) + "Unset an old timer (if there is one) and activate a new timer with the +given timeout value." + (cond + ;; Xemacs + (speedbar-xemacsp + (if speedbar-timer + (progn (delete-itimer speedbar-timer) + (setq speedbar-timer nil))) + (if timeout + (setq speedbar-timer (start-itimer "speedbar" + 'speedbar-timer-fn + timeout + nil)))) + ;; GNU emacs + (t + (if speedbar-timer + (progn (cancel-timer speedbar-timer) + (setq speedbar-timer nil))) + (if timeout + (setq speedbar-timer + (run-with-idle-timer timeout nil 'speedbar-timer-fn)))) + )) + +(defmacro speedbar-with-writable (&rest forms) + "Allow the buffer to be writable and evaluate forms. Turn read-only back +on when done." + (list 'let '((speedbar-with-writable-buff (current-buffer))) + '(toggle-read-only -1) + (cons 'progn forms) + '(save-excursion (set-buffer speedbar-with-writable-buff) + (toggle-read-only 1)))) +(put 'speedbar-with-writable 'lisp-indent-function 0) + +(defun speedbar-make-button (start end face mouse function &optional token) + "Create a button from START to END, with FACE as the display face +and MOUSE and the mouse face. When this button is clicked on FUNCTION +will be run with the token parameter of TOKEN (any lisp object)" + (put-text-property start end 'face face) + (put-text-property start end 'mouse-face mouse) + (put-text-property start end 'invisible nil) + (if function (put-text-property start end 'speedbar-function function)) + (if token (put-text-property start end 'speedbar-token token)) + ) + +(defun speedbar-file-lists (directory) + "Create file lists for DIRECTORY. The car is the list of +directories, the cdr is list of files not matching ignored headers." + (let ((default-directory directory) + (dir (directory-files directory nil)) + (dirs nil) + (files nil)) + (while dir + (if (not (string-match speedbar-file-unshown-regexp (car dir))) + (if (file-directory-p (car dir)) + (setq dirs (cons (car dir) dirs)) + (setq files (cons (car dir) files)))) + (setq dir (cdr dir))) + (cons (nreverse dirs) (list (nreverse files)))) + ) + +(defun speedbar-directory-buttons (directory index) + "Inserts a single button group at point for DIRECTORY. Each directory +path part is a different button. If part of the path matches the user +directory ~, then it is replaced with a ~" + (let* ((tilde (expand-file-name "~")) + (dd (expand-file-name directory)) + (junk (string-match (regexp-quote tilde) dd)) + (displayme (if junk + (concat "~" (substring dd (match-end 0))) + dd)) + (p (point))) + (if (string-match "^~/?$" displayme) (setq displayme (concat tilde "/"))) + (insert displayme) + (save-excursion + (goto-char p) + (while (re-search-forward "\\([^/]+\\)/" nil t) + (speedbar-make-button (match-beginning 1) (match-end 1) + 'speedbar-directory-face + 'speedbar-highlight-face + 'speedbar-directory-buttons-follow + (if (= (match-beginning 1) p) + (expand-file-name "~/") ;the tilde + (buffer-substring-no-properties + p (match-end 0)))))) + (if (string-match "^/[^/]+/$" displayme) + (progn + (insert " ") + (let ((p (point))) + (insert "<root>") + (speedbar-make-button p (point) + 'speedbar-directory-face + 'speedbar-highlight-face + 'speedbar-directory-buttons-follow + "/")))) + (insert-char ?\n 1 nil))) + +(defun speedbar-make-tag-line (exp-button-type + exp-button-char exp-button-function + exp-button-data + tag-button tag-button-function tag-button-data + tag-button-face depth) + "Creates a tag line with BUTTON-TYPE for the small button that +expands or contracts a node (if applicable), and BUTTON-CHAR the +character in it (+, -, ?, etc). BUTTON-FUNCTION is the function to +call if it's clicked on. Button types are 'bracket, 'angle, 'curly, or nil. + +Next, TAG-BUTTON is the text of the tag. TAG-FUNCTION is the function +to call if clicked on, and TAG-DATA is the data to attach to the text +field (such a tag positioning, etc). TAG-FACE is a face used for this +type of tag. + +Lastly, DEPTH shows the depth of expansion. + +This function assumes that the cursor is in the speecbar window at the +position to insert a new item, and that the new item will end with a CR" + (let ((start (point)) + (end (progn + (insert (int-to-string depth) ":") + (point)))) + (put-text-property start end 'invisible t) + ) + (insert-char ? depth nil) + (put-text-property (- (point) depth) (point) 'invisible nil) + (let* ((exp-button (cond ((eq exp-button-type 'bracket) "[%c]") + ((eq exp-button-type 'angle) "<%c>") + ((eq exp-button-type 'curly) "{%c}") + (t ">"))) + (buttxt (format exp-button exp-button-char)) + (start (point)) + (end (progn (insert buttxt) (point))) + (bf (if exp-button-type 'speedbar-button-face nil)) + (mf (if exp-button-function 'speedbar-highlight-face nil)) + ) + (speedbar-make-button start end bf mf exp-button-function exp-button-data) + ) + (insert-char ? 1 nil) + (put-text-property (1- (point)) (point) 'invisible nil) + (let ((start (point)) + (end (progn (insert tag-button) (point)))) + (insert-char ?\n 1 nil) + (put-text-property (1- (point)) (point) 'invisible nil) + (speedbar-make-button start end tag-button-face + (if tag-button-function 'speedbar-highlight-face nil) + tag-button-function tag-button-data)) +) + +(defun speedbar-change-expand-button-char (char) + "Change the expanson button character to CHAR for the current line." + (save-excursion + (beginning-of-line) + (if (re-search-forward ":\\s-*.\\([-+?]\\)" (save-excursion (end-of-line) + (point)) t) + (speedbar-with-writable + (goto-char (match-beginning 1)) + (delete-char 1) + (insert-char char 1 t))))) + + +;;; +;;; Build button lists +;;; +(defun speedbar-insert-files-at-point (files level) + "Insert list of FILES starting at point, and indenting all files to LEVEL +depth. Tag exapndable items with a +, otherwise a ?. Don't highlight ? as +we don't know how to manage them. The input parameter FILES is a cons +cell of the form ( 'dir-list . 'file-list )" + ;; Start inserting all the directories + (let ((dirs (car files))) + (while dirs + (speedbar-make-tag-line 'angle ?+ 'speedbar-dired (car dirs) + (car dirs) 'speedbar-dir-follow nil + 'speedbar-directory-face level) + (setq dirs (cdr dirs)))) + (let ((lst (car (cdr files)))) + (while lst + (let* ((known (string-match speedbar-file-regexp (car lst))) + (expchar (if known ?+ ??)) + (fn (if known 'speedbar-tag-file nil))) + (if (or speedbar-show-unknown-files (/= expchar ??)) + (speedbar-make-tag-line 'bracket expchar fn (car lst) + (car lst) 'speedbar-find-file nil + 'speedbar-file-face level))) + (setq lst (cdr lst))))) + +(defun speedbar-default-directory-list (directory index) + "Inserts files for DIRECTORY with level INDEX at point" + (speedbar-insert-files-at-point + (speedbar-file-lists directory) index) + ) + +(defun speedbar-insert-generic-list (level lst expand-fun find-fun) + "At LEVEL, inserts a generic multi-level alist LIST. Associations with +lists get {+} tags (to expand into more nodes) and those with positions +just get a > as the indicator. {+} buttons will have the function +EXPAND-FUN and the token is the CDR list. The token name will have the +function FIND-FUN and not token." + ;; Remove imenu rescan button + (if (string= (car (car lst)) "*Rescan*") + (setq lst (cdr lst))) + ;; insert the parts + (while lst + (cond ((null (car-safe lst)) nil) ;this would be a separator + ((numberp (cdr-safe (car-safe lst))) + (speedbar-make-tag-line nil nil nil nil ;no expand button data + (car (car lst)) ;button name + find-fun ;function + (cdr (car lst)) ;token is position + 'speedbar-tag-face + (1+ level))) + ((listp (cdr-safe (car-safe lst))) + (speedbar-make-tag-line 'curly ?+ expand-fun (cdr (car lst)) + (car (car lst)) ;button name + nil nil 'speedbar-tag-face + (1+ level))) + (t (message "Ooops!"))) + (setq lst (cdr lst)))) + +;;; +;;; Timed functions +;;; +(defun speedbar-update-contents () + "Update the contents of the speedbar buffer." + (interactive) + (setq speedbar-last-selected-file nil) + (setq speedbar-shown-directories (list (expand-file-name default-directory))) + (let ((cbd default-directory) + (funclst speedbar-initial-expansion-list)) + (save-excursion + (set-buffer speedbar-buffer) + (speedbar-with-writable + (setq default-directory cbd) + (delete-region (point-min) (point-max)) + (while funclst + (funcall (car funclst) cbd 0) + (setq funclst (cdr funclst))))))) + +(defun speedbar-timer-fn () + "Run whenever emacs is idle to update the speedbar item" + (if (not (and speedbar-frame + (frame-live-p speedbar-frame) + speedbar-attached-frame + (frame-live-p speedbar-attached-frame))) + (speedbar-set-timer nil) + (unwind-protect + (if (frame-visible-p speedbar-frame) + (let ((af (selected-frame))) + (save-window-excursion + (select-frame speedbar-attached-frame) + ;; make sure we at least choose a window to + ;; get a good directory from + (if (string-match "\\*Minibuf-[0-9]+\\*" (buffer-name)) + (other-window 1)) + ;; Update all the contents if directories change! + (if (or (member (expand-file-name default-directory) + speedbar-shown-directories) + (eq af speedbar-frame) + (not (buffer-file-name)) + ) + nil + (message "Updating speedbar to: %s..." default-directory) + (speedbar-update-contents) + (message "Updating speedbar to: %s...done" default-directory))))) + ;; Reset the timer + (speedbar-set-timer speedbar-update-speed) + ;; Ok, un-underline old file, underline current file + (speedbar-update-current-file))) + (run-hooks 'speedbar-timer-hook) + ) + +(defun speedbar-update-current-file () + "Find out what the current file is, and update our visuals to indicate +what it is. This is specific to file names." + (let* ((lastf (selected-frame)) + (newcf (save-excursion + (select-frame speedbar-attached-frame) + (let ((rf (if (buffer-file-name) + (file-name-nondirectory (buffer-file-name)) + nil))) + (select-frame lastf) + rf))) + (lastb (current-buffer))) + (if (and newcf (not (string= newcf speedbar-last-selected-file))) + (progn + (select-frame speedbar-frame) + (set-buffer speedbar-buffer) + (speedbar-with-writable + (goto-char (point-min)) + (if (and + speedbar-last-selected-file + (re-search-forward + (concat " \\(" (regexp-quote speedbar-last-selected-file) "\\)\n") + nil t)) + (put-text-property (match-beginning 1) + (match-end 1) + 'face + 'speedbar-file-face)) + (goto-char (point-min)) + (if (re-search-forward + (concat " \\(" (regexp-quote newcf) "\\)\n") nil t) + (put-text-property (match-beginning 1) + (match-end 1) + 'face + 'speedbar-selected-face)) + (setq speedbar-last-selected-file newcf)) + (forward-line -1) + (speedbar-position-cursor-on-line) + (set-buffer lastb) + (select-frame lastf))))) + +;;; +;;; Clicking Activity +;;; +(defun speedbar-quick-mouse (e) + "Since mouse events are strange, this will keep the mouse nicely +positioned." + (interactive "e") + (mouse-set-point e) + (beginning-of-line) + (forward-char 3) + ) + +(defun speedbar-position-cursor-on-line () + "Position the cursor on a line." + (beginning-of-line) + (re-search-forward "[]>}]" (save-excursion (end-of-line) (point)) t)) + +(defun speedbar-line-path (depth) + "Retrieve the pathname associated with the current line. This may +require traversing backwards and combinding the default directory with +these items." + (save-excursion + (let ((path nil)) + (setq depth (1- depth)) + (while (/= depth -1) + (if (not (re-search-backward (format "^%d:" depth) nil t)) + (error "Error building path of tag") + (cond ((looking-at "[0-9]+:\\s-*<->\\s-+\\([^\n]+\\)$") + (setq path (concat (buffer-substring-no-properties + (match-beginning 1) (match-end 1)) + "/" + path))) + ((looking-at "[0-9]+:\\s-*[-]\\s-+\\([^\n]+\\)$") + ;; This is the start of our path. + (setq path (buffer-substring-no-properties + (match-beginning 1) (match-end 1)))))) + (setq depth (1- depth))) + (concat default-directory path)))) + +(defun speedbar-edit-line () + "Edit whatever tag or file is on the current speedbar line." + (interactive) + (beginning-of-line) + (re-search-forward "[]>}] [a-zA-Z0-9]" (save-excursion (end-of-line) (point))) + (speedbar-do-function-pointer)) + +(defun speedbar-expand-line () + "Expand the line under the cursor." + (interactive) + (beginning-of-line) + (re-search-forward ":\\s-*.\\+. " (save-excursion (end-of-line) (point))) + (forward-char -2) + (speedbar-do-function-pointer)) + +(defun speedbar-contract-line () + "Expand the line under the cursor." + (interactive) + (beginning-of-line) + (re-search-forward ":\\s-*.-. " (save-excursion (end-of-line) (point))) + (forward-char -2) + (speedbar-do-function-pointer)) + +(defun speedbar-click (e) + "When the user clicks mouse 1 on our speedbar, we must decide what +we want to do! The entire speedbar has functions attached to +buttons. All we have to do is extract from the buffer the information +we need. See `speedbar-mode' for the type of behaviour we want to achieve" + (interactive "e") + (mouse-set-point e) + (speedbar-do-function-pointer)) + +(defun speedbar-do-function-pointer () + "Look under the cursor and examine the text properties. From this extract +the file/tag name, token, indentation level and call a function if apropriate" + (let* ((fn (get-text-property (point) 'speedbar-function)) + (tok (get-text-property (point) 'speedbar-token)) + ;; The 1-,+ is safe because scaning starts AFTER the point + ;; specified. This lets the search include the character the + ;; cursor is on. + (tp (previous-single-property-change + (if (get-text-property (1+ (point)) 'speedbar-function) + (1+ (point)) (point)) 'speedbar-function)) + (np (next-single-property-change + (if (and (> (point) 1) (get-text-property (1- (point)) 'speedbar-function)) + (1- (point)) (point)) 'speedbar-function)) + (txt (buffer-substring-no-properties (or tp (point-min)) + (or np (point-max)))) + (dent (save-excursion (beginning-of-line) + (string-to-number + (if (looking-at "[0-9]+") + (buffer-substring-no-properties + (match-beginning 0) (match-end 0)) + "0"))))) + ;;(message "%S:%S:%S:%s" fn tok txt dent) + (and fn (funcall fn txt tok dent))) + (speedbar-position-cursor-on-line)) + +(defun speedbar-find-file (text token indent) + "Speedbar click handler for filenames. Clicking the filename loads +that file into the attached buffer." + (let ((cdd (speedbar-line-path indent))) + (select-frame speedbar-attached-frame) + (find-file (concat cdd text)) + (speedbar-update-current-file) + ;; Reset the timer with a new timeout when cliking a file + ;; in case the user was navigating directories, we can cancel + ;; that other timer. + (speedbar-set-timer speedbar-update-speed))) + +(defun speedbar-dir-follow (text token indent) + "Speedbar click handler for directory names. Clicking a directory will +cause the speedbar to list files in the selected subdirectory." + (setq default-directory + (concat (expand-file-name (concat (speedbar-line-path indent) text)) + "/")) + ;; Because we leave speedbar as the current buffer, + ;; update contents will change directory without + ;; having to touch the attached frame. + (speedbar-update-contents) + (speedbar-set-timer speedbar-navigating-speed) + (setq speedbar-last-selected-file nil) + (speedbar-update-current-file)) + + +(defun speedbar-dired (text token indent) + "Speedbar click handler for filenames. Clicking the filename loads +that file into the attached buffer." + (cond ((string-match "+" text) ;we have to expand this file + (setq speedbar-shown-directories + (cons (expand-file-name + (concat (speedbar-line-path indent) token "/")) + speedbar-shown-directories)) + (speedbar-change-expand-button-char ?-) + (save-excursion + (end-of-line) (forward-char 1) + (speedbar-with-writable + (speedbar-default-directory-list + (concat (speedbar-line-path indent) token "/") + (1+ indent))))) + ((string-match "-" text) ;we have to contract this node + (let ((oldl speedbar-shown-directories) + (newl nil) + (td (expand-file-name + (concat (speedbar-line-path indent) token)))) + (while oldl + (if (not (string-match (concat "^" (regexp-quote td)) (car oldl))) + (setq newl (cons (car oldl) newl))) + (setq oldl (cdr oldl))) + (setq speedbar-shown-directories newl)) + (speedbar-change-expand-button-char ?+) + (save-excursion + (end-of-line) (forward-char 1) + (speedbar-with-writable + (if (save-excursion (re-search-forward (format "^%d:" indent) nil t)) + (delete-region (point) (match-beginning 0)) + (delete-region (point) (point-max))))) + ) + (t (error "Ooops... not sure what to do."))) + (speedbar-center-buffer-smartly) + (setq speedbar-last-selected-file nil) + (save-excursion (speedbar-update-current-file))) + +(defun speedbar-directory-buttons-follow (text token ident) + "Speedbar click handler for default directory buttons." + (setq default-directory token) + ;; Because we leave speedbar as the current buffer, + ;; update contents will change directory without + ;; having to touch the attached frame. + (speedbar-update-contents) + (speedbar-set-timer speedbar-navigating-speed)) + +(defun speedbar-tag-file (text token indent) + "The cursor is on a selected line. Expand the tags in the specified +file. The parameter TXT and TOK are required, where TXT is the button +clicked, and TOK is the file to expand." + (cond ((string-match "+" text) ;we have to expand this file + (let* ((fn (expand-file-name (concat (speedbar-line-path indent) + token))) + (lst (if speedbar-use-imenu-package + (let ((tim (speedbar-fetch-dynamic-imenu fn))) + (if (eq tim t) + (speedbar-fetch-dynamic-etags fn) + tim)) + (speedbar-fetch-dynamic-etags fn)))) + ;; if no list, then remove expando button + (if (not lst) + (speedbar-change-expand-button-char ??) + (speedbar-change-expand-button-char ?-) + (speedbar-with-writable + (save-excursion + (end-of-line) (forward-char 1) + (speedbar-insert-generic-list indent + lst 'speedbar-tag-expand + 'speedbar-tag-find)))))) + ((string-match "-" text) ;we have to contract this node + (speedbar-change-expand-button-char ?+) + (speedbar-with-writable + (save-excursion + (end-of-line) (forward-char 1) + (if (save-excursion (re-search-forward (format "^%d:" indent) nil t)) + (delete-region (point) (match-beginning 0)) + (delete-region (point) (point-max)))))) + (t (error "Ooops... not sure what to do."))) + (speedbar-center-buffer-smartly)) + +(defun speedbar-tag-find (text token indent) + "For the tag in a file, goto that position" + (let ((file (speedbar-line-path indent))) + (select-frame speedbar-attached-frame) + (find-file file) + (save-excursion (speedbar-update-current-file)) + ;; Reset the timer with a new timeout when cliking a file + ;; in case the user was navigating directories, we can cancel + ;; that other timer. + (speedbar-set-timer speedbar-update-speed) + (goto-char token))) + +(defun speedbar-tag-expand (text token indent) + "For the tag in a file which is really a list of tags of a certain type, +expand or contract that list." + (cond ((string-match "+" text) ;we have to expand this file + (speedbar-change-expand-button-char ?-) + (speedbar-with-writable + (save-excursion + (end-of-line) (forward-char 1) + (speedbar-insert-generic-list indent + token 'speedbar-tag-expand + 'speedbar-tag-find)))) + ((string-match "-" text) ;we have to contract this node + (speedbar-change-expand-button-char ?+) + (speedbar-with-writable + (save-excursion + (end-of-line) (forward-char 1) + (if (save-excursion (re-search-forward (format "^%d:" indent) nil t)) + (delete-region (point) (match-beginning 0)))))) + (t (error "Ooops... not sure what to do."))) + (speedbar-center-buffer-smartly)) + +;;; +;;; Centering Utility +;;; +(defun speedbar-center-buffer-smartly () + "Look at the buffer, and center it so that which the user is most +interested in (as far as we can tell) is all visible. This assumes +that the cursor is on a file, or tag of a file which the user is +interested in." + (if (<= (count-lines (point-min) (point-max)) + (window-height (selected-window))) + ;; whole buffer fits + (let ((cp (point))) + (goto-char (point-min)) + (recenter 0) + (goto-char cp)) + ;; too big + (let (depth start end exp p) + (save-excursion + (beginning-of-line) + (setq depth (if (looking-at "[0-9]+") + (string-to-int (buffer-substring-no-properties + (match-beginning 0) (match-end 0))) + 0)) + (setq exp (format "^%d:\\s-*[[{<]\\([?+-]\\)[]>}]" depth))) + (save-excursion + (end-of-line) + (if (re-search-backward exp nil t) + (setq start (point)) + (error "Center error")) + (save-excursion ;Not sure about this part. + (end-of-line) + (setq p (point)) + (while (and (not (re-search-forward exp nil t)) + (>= depth 0)) + (setq depth (1- depth)) + (setq exp (format "^%d:\\s-*[[{<]\\([?+-]\\)[]>}]" depth))) + (if (/= (point) p) + (setq end (point)) + (setq end (point-max))))) + ;; Now work out the details of centering + (let ((nl (count-lines start end)) + (cp (point))) + (if (> nl (window-height (selected-window))) + ;; We can't fit it all, so just center on cursor + (progn (goto-char start) + (recenter 1)) + ;; we can fit everything on the screen, but... + (if (and (pos-visible-in-window-p start (selected-window)) + (pos-visible-in-window-p end (selected-window))) + ;; we are all set! + nil + ;; we need to do something... + (goto-char start) + (let ((newcent (/ (- (window-height (selected-window)) nl) 2)) + (lte (count-lines start (point-max)))) + (if (and (< (+ newcent lte) (window-height (selected-window))) + (> (- (window-height (selected-window)) lte 1) + newcent)) + (setq newcent (- (window-height (selected-window)) + lte 1))) + (recenter newcent)))) + (goto-char cp))))) + + +;;; +;;; Tag Management -- Imenu +;;; +(defun speedbar-fetch-dynamic-imenu (file) + "Use the imenu package to load in file, and extract all the items +tags we wish to display in the speedbar package." +;; (eval-when-compile (require 'imenu)) + (save-excursion + (set-buffer (find-file-noselect file)) + (condition-case nil + (imenu--make-index-alist t) + (error t)))) + + +;;; +;;; Tag Management -- etags (Not useful for FSF emacs) +;;; +(defvar speedbar-fetch-etags-parse-list + '(("\\.\\([cChH]\\|c++\\|cpp\\|cc\\)$" . speedbar-parse-c-or-c++tag) + ("\\.el\\|\\.emacs" . + "defun\\s-+\\(\\(\\w\\|[-_]\\)+\\)\\s-*\C-?") + ("\\.tex$" . speedbar-parse-tex-string) + ("\\.p" . + "\\(\\(FUNCTION\\|function\\|PROCEDURE\\|procedure\\)\\s-+\\([a-zA-Z0-9_.:]+\\)\\)\\s-*(?^?") + + ) + "*Alist matching extension vs an expression which will extract the +symbol name we wish to display as match 1. To add a new file type, you +would want to add a new association to the list, where the car +is the file match, and the cdr is the way to extract an element from +the tags output. If the output is complex, use a function symbol +instead of regexp. The function should expect to be at the beginning +of a line in the etags buffer. + +This variable is ignored if `speedbar-use-imenu-package' is `t'") + +(defvar speedbar-fetch-etags-command "etags" + "*Command used to create an etags file. + +This variable is ignored if `speedbar-use-imenu-package' is `t'") + +(defvar speedbar-fetch-etags-arguments '("-D" "-I" "-o" "-") + "*List of arguments to use with `speedbar-fetch-etags-command' to create +an etags output buffer. + +This variable is ignored if `speedbar-use-imenu-package' is `t'") + +(defun speedbar-fetch-dynamic-etags (file) + "For the complete file definition FILE, run etags as a subprocess, +fetch it's output, and create a list of symbols extracted, and their +position in FILE." + (let ((newlist nil)) + (unwind-protect + (save-excursion + (if (get-buffer "*etags tmp*") + (kill-buffer "*etags tmp*")) ;kill to clean it up + (set-buffer (get-buffer-create "*etags tmp*")) + (apply 'call-process speedbar-fetch-etags-command nil + (current-buffer) nil + (append speedbar-fetch-etags-arguments (list file))) + (goto-char (point-min)) + (let ((expr + (let ((exprlst speedbar-fetch-etags-parse-list) + (ans nil)) + (while (and (not ans) exprlst) + (if (string-match (car (car exprlst)) file) + (setq ans (car exprlst))) + (setq exprlst (cdr exprlst))) + (cdr ans)))) + (if expr + (let (tnl) + (while (not (save-excursion (end-of-line) (eobp))) + (save-excursion + (setq tnl (speedbar-extract-one-symbol expr))) + (if tnl (setq newlist (cons tnl newlist))) + (forward-line 1))) + (message "Sorry, no support for a file of that extension")))) + ) + (reverse newlist))) + +(defun speedbar-extract-one-symbol (expr) + "At point in current buffer, return nil, or one alist of the form +of a dotted pair: ( symbol . position ) from etags output. Parse the +output using the regular expression EXPR" + (let* ((sym (if (stringp expr) + (if (save-excursion + (re-search-forward expr (save-excursion + (end-of-line) + (point)) t)) + (buffer-substring-no-properties (match-beginning 1) + (match-end 1))) + (funcall expr))) + (pos (let ((j (re-search-forward "[\C-?\C-a]\\([0-9]+\\),\\([0-9]+\\)" + (save-excursion + (end-of-line) + (point)) + t))) + (if (and j sym) + (1+ (string-to-int (buffer-substring-no-properties + (match-beginning 2) + (match-end 2)))) + 0)))) + (if (/= pos 0) + (cons sym pos) + nil))) + +(defun speedbar-parse-c-or-c++tag () + "Parse a c or c++ tag, which tends to be a little complex." + (save-excursion + (let ((bound (save-excursion (end-of-line) (point)))) + (cond ((re-search-forward "\C-?\\([^\C-a]+\\)\C-a" bound t) + (buffer-substring-no-properties (match-beginning 1) + (match-end 1))) + ((re-search-forward "\\<\\([^ \t]+\\)\\s-+new(" bound t) + (buffer-substring-no-properties (match-beginning 1) + (match-end 1))) + ((re-search-forward "\\<\\([^ \t(]+\\)\\s-*(\C-?" bound t) + (buffer-substring-no-properties (match-beginning 1) + (match-end 1))) + (t nil)) + ))) + +(defun speedbar-parse-tex-string () + "Parse a tex string. Only find data which is relevant" + (save-excursion + (let ((bound (save-excursion (end-of-line) (point)))) + (cond ((re-search-forward "\\(section\\|chapter\\|cite\\)\\s-*{[^\C-?}]*}?" bound t) + (buffer-substring-no-properties (match-beginning 0) + (match-end 0))) + (t nil))))) + + +;;; +;;; configuration scripts (optional) +;;; +(defun speedbar-configure-options () + "Configure variable options for the speedbar program using dlg-config" + (interactive) + (require 'dlg-config) + (save-excursion + (select-frame speedbar-attached-frame) + (dlg-init) + (let ((oframe (create-widget "Speedbar Options" widget-frame + widget-toplevel-shell + :x 2 :y -3 + :frame-label "Speedbar Options")) + ) + (create-widget "show-unknown" widget-toggle-button oframe + :x 1 :y 1 :label-value "Show files that are not supported by imenu" + :state (data-object-symbol "speedbar-show-unknown-files" + :value speedbar-show-unknown-files + :symbol 'speedbar-show-unknown-files)) + + (create-widget "raiselower" widget-toggle-button oframe + :x 1 :y -1 :label-value "Use frame auto raise/lower property" + :state (data-object-symbol "speedbar-raise-lower" + :value speedbar-raise-lower + :symbol 'speedbar-raise-lower)) + + (create-widget "update-speed" widget-label oframe + :x 1 :y -2 :label-value "Update Delay :") + (create-widget "update-speed-txt" widget-text-field oframe + :width 5 :height 1 :x -2 :y t + :value (data-object-symbol-string-to-int + "update-speed" + :symbol 'speedbar-update-speed + :value (int-to-string speedbar-update-speed))) + (create-widget "update-speed-unit" widget-label oframe + :x -3 :y t :label-value "Seconds") + + (create-widget "navigating-speed" widget-label oframe + :x 1 :y -1 :label-value "Navigating Delay:") + (create-widget "navigating-speed-txt" widget-text-field oframe + :width 5 :height 1 :x -2 :y t + :value (data-object-symbol-string-to-int + "navigating-speed" + :symbol 'speedbar-navigating-speed + :value (int-to-string speedbar-navigating-speed))) + (create-widget "navigating-speed-unit" widget-label oframe + :x -3 :y t :label-value "Seconds") + + (create-widget "width" widget-label oframe + :x 1 :y -2 :label-value "Display Width :") + (create-widget "width-txt" widget-text-field oframe + :width 5 :height 1 :x -2 :y t + :value (data-object-symbol-string-to-int + "width" + :symbol 'speedbar-width + :value (int-to-string speedbar-width))) + (create-widget "width-unit" widget-label oframe + :x -3 :y t :label-value "Characters") + + (create-widget "scrollbar-width" widget-label oframe + :x 1 :y -1 :label-value "Scrollbar Width :") + (create-widget "scrollbar-width-txt" widget-text-field oframe + :width 5 :height 1 :x -2 :y t + :value (data-object-symbol-string-to-int + "width" + :symbol 'speedbar-width + :value (int-to-string speedbar-scrollbar-width))) + (create-widget "scrollbar-width-unit" widget-label oframe + :x -3 :y t :label-value "Pixels") + + + ) + (dlg-end) + (dialog-refresh) + )) + +(defun speedbar-configure-faces () + "Configure faces for the speedbar program using dlg-config." + (interactive) + (require 'dlg-config) + (save-excursion + (select-frame speedbar-attached-frame) + (dlg-faces '(speedbar-button-face + speedbar-file-face + speedbar-directory-face + speedbar-tag-face + speedbar-highlight-face + speedbar-selected-face)))) + +;;; +;;; Color loading section This is message *Blech!* +;;; +(defun speedbar-load-color (sym l-fg l-bg d-fg d-bg &optional bold italic underline) + "Create a color for SYM with a L-FG and L-BG color, or D-FG and +D-BG. Optionally make BOLD, ITALIC, or UNDERLINED if applicable. If +the background attribute of the current frame is determined to be +light (white, for example) then L-FG and L-BG is used. If not, then +D-FG and D-BG is used. This will allocate the colors in the best +possible mannor. This will allow me to store multiple defaults and +dynamically determine which colors to use." + (let* ((params (frame-parameters)) + (disp-res (if (fboundp 'x-get-resource) + (if speedbar-xemacsp + (x-get-resource ".displayType" "DisplayType" 'string) + (x-get-resource ".displayType" "DisplayType")) + nil)) + (display-type + (cond (disp-res (intern (downcase disp-res))) + ((and (fboundp 'x-display-color-p) (x-display-color-p)) 'color) + (t 'mono))) + (bg-res (if (fboundp 'x-get-resource) + (if speedbar-xemacsp + (x-get-resource ".backgroundMode" "BackgroundMode" 'string) + (x-get-resource ".backgroundMode" "BackgroundMode")) + nil)) + (bgmode + (cond (bg-res (intern (downcase bg-res))) + ((and params + (fboundp 'x-color-values) + (< (apply '+ (x-color-values + (cdr (assq 'background-color params)))) + (/ (apply '+ (x-color-values "white")) 3))) + 'dark) + (t 'light))) ;our default + (set-p (function (lambda (face-name resource) + (if speedbar-xemacsp + (x-get-resource + (concat face-name ".attribute" resource) + (concat "Face.Attribute" resource) + 'string) + (x-get-resource + (concat face-name ".attribute" resource) + (concat "Face.Attribute" resource))) + ))) + (nbg (cond ((eq bgmode 'dark) d-bg) + (t l-bg))) + (nfg (cond ((eq bgmode 'dark) d-fg) + (t l-fg)))) + + (if (not (eq display-type 'color)) + ;; we need a face of some sort, so just make due with default + (progn + (copy-face 'default sym) + (if bold (condition-case nil + (make-face-bold sym) + (error (message "Cannot make face %s bold!" + (symbol-name sym))))) + (if italic (condition-case nil + (make-face-italic sym) + (error (message "Cannot make face %s italic!" + (symbol-name sym))))) + (set-face-underline-p sym underline) + ) + ;; make a colorized version of a face. Be sure to check Xdefaults + ;; for possible overrides first! + (let ((newface (make-face sym))) + ;; For each attribute, check if it might already be set by Xdefaults + (if (and nfg (not (funcall set-p (symbol-name sym) "Foreground"))) + (set-face-foreground sym nfg)) + (if (and nbg (not (funcall set-p (symbol-name sym) "Background"))) + (set-face-background sym nbg)) + + (if bold (condition-case nil + (make-face-bold sym) + (error (message "Cannot make face %s bold!" + (symbol-name sym))))) + (if italic (condition-case nil + (make-face-italic sym) + (error (message "Cannot make face %s italic!" + (symbol-name sym))))) + (set-face-underline-p sym underline) + )))) + + ;; JTL <<<< +(if nil ;;(x-display-color-p) ;; just a quick hack so it will run. + ;; we can use customize for this. + ;; <<<< JTL + (progn + (speedbar-load-color 'speedbar-button-face "green4" "default" "green3" "default") + (speedbar-load-color 'speedbar-file-face "cyan4" nil "cyan" nil nil nil nil) + (speedbar-load-color 'speedbar-directory-face "blue4" nil "light blue" nil nil nil nil) + (speedbar-load-color 'speedbar-tag-face "brown" nil "yellow" nil nil nil nil) + (speedbar-load-color 'speedbar-selected-face "red" nil "red" nil nil nil t) + (speedbar-load-color 'speedbar-highlight-face nil "green" nil "sea green" nil nil nil) + ) ; color + (make-face 'speedbar-button-face) + ;;(make-face 'speedbar-file-face) + (copy-face 'bold 'speedbar-file-face) + (make-face 'speedbar-directory-face) + (make-face 'speedbar-tag-face) + ;;(make-face 'speedbar-selected-face) + (copy-face 'underline 'speedbar-selected-face) + ;;(make-face 'speedbar-highlight-face) + (copy-face 'highlight 'speedbar-highlight-face) + + ) ;; monochrome + +;;; end of lisp +(provide 'speedbar) + +;;; speedbar.el ends here