1 ;;; hui.el --- GNU Emacs User Interface to Hyperbole
3 ;; Copyright (C) 1991-1995, 2008 Free Software Foundation, Inc.
4 ;; Developed with support from Motorola Inc.
6 ;; Author: Bob Weiner, Brown U.
7 ;; Maintainer: Mats Lidell <matsl@contactor.se>
8 ;; Keywords: hypermedia
10 ;; This file is part of GNU Hyperbole.
12 ;; GNU Hyperbole is free software; you can redistribute it and/or
13 ;; modify it under the terms of the GNU General Public License as
14 ;; published by the Free Software Foundation; either version 3, or (at
15 ;; your option) any later version.
17 ;; GNU Hyperbole is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ;; General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
32 ;;; Other required Elisp libraries
35 (require 'hargs) (require 'set) (require 'hmail)
41 (defvar hui:ebut-delete-confirm-p t
42 "*Non-nil means prompt before interactively deleting explicit buttons.")
48 (defun hui:ebut-create (&optional start end)
49 "Creates an explicit but starting from label between optional START and END.
50 Indicates by delimiting and adding any necessary instance number of the button
52 (interactive (list (and (marker-position (hypb:mark-marker t))
54 (and (marker-position (hypb:mark-marker t))
56 (let ((default-lbl) lbl but-buf actype)
59 (hui:hbut-label-default start end (not (interactive-p)))
60 lbl (hui:hbut-label default-lbl "ebut-create"))
61 (if (not (equal lbl default-lbl)) (setq default-lbl nil))
63 (setq but-buf (if default-lbl (current-buffer) (hui:ebut-buf)))
64 (hui:buf-writable-err but-buf "ebut-create")
66 (hattr:set 'hbut:current 'loc (hui:key-src but-buf))
67 (hattr:set 'hbut:current 'dir (hui:key-dir but-buf))
68 (setq actype (hui:actype))
69 (hattr:set 'hbut:current 'actype actype)
70 (hattr:set 'hbut:current 'args (hargs:actype-get actype))
71 (hattr:set 'hbut:current 'action
72 (and (boundp 'hui:ebut-prompt-for-action)
73 hui:ebut-prompt-for-action (hui:action actype)))
75 (ebut:operate lbl nil)))
77 (defun hui:ebut-delete (but-key &optional key-src)
78 "Deletes explicit Hyperbole button given by BUT-KEY in optional KEY-SRC.
79 KEY-SRC may be a buffer or a pathname, when nil the current buffer is used.
80 Returns t if button is deleted, nil if user chooses not to delete or signals
81 an error otherwise. If called interactively, prompts user whether to delete
82 and derives BUT-KEY from the button that point is within.
83 Signals an error if point is not within a button."
84 (interactive (list (if (ebut:at-p)
85 (hattr:get 'hbut:current 'lbl-key)
89 "(ebut-delete): Point is not over the label of an existing button."))
90 ((not (stringp but-key))
92 "(ebut-delete): Invalid label key argument: '%s'." but-key)))
93 (let ((interactive (interactive-p)))
94 (if (and hui:ebut-delete-confirm-p interactive)
95 (if (y-or-n-p (format "Delete button %s%s%s? "
97 (hbut:key-to-label but-key) ebut:end))
98 (hui:ebut-delete-op interactive but-key key-src)
100 (hui:ebut-delete-op interactive but-key key-src))))
102 (defun hui:ebut-edit ()
103 "Creates or modifies an explicit Hyperbole button when conditions are met.
104 A region must have been delimited with the action-key and point must now be
105 within it before this function is called or it will do nothing. The region
106 must be no larger than the size given by 'ebut:max-len'. It must be entirely
107 within or entirely outside of an existing explicit button. When region is
108 within the button, the button is interactively modified. Otherwise, a new
109 button is created interactively with the region as the default label."
111 (let ((m (marker-position (hypb:mark-marker t)))
112 (op action-key-depress-prev-point) (p (point)) (lbl-key))
113 (if (and m (eq (marker-buffer m) (marker-buffer op))
114 (< op m) (<= (- m op) ebut:max-len)
117 (if (setq lbl-key (ebut:label-p))
118 (hui:ebut-modify lbl-key)
119 (hui:ebut-create op m))
122 (defun hui:ebut-modify (lbl-key)
123 "Modifies an explicit Hyperbole button given by LBL-KEY.
124 Signals an error when no such button is found in the current buffer."
125 (interactive (list (save-excursion
126 (hui:buf-writable-err (current-buffer) "ebut-modify")
129 (hargs:read-match "Button to modify: "
132 (let ((lbl (ebut:key-to-label lbl-key))
133 (but-buf (current-buffer))
137 (hui:buf-writable-err but-buf "ebut-modify"))
139 (or (setq but (ebut:get lbl-key but-buf))
140 (progn (pop-to-buffer but-buf)
141 (hypb:error "(ebut-modify): Invalid button, no data for '%s'." lbl)))
145 "Change button label to: "
148 (and (not (string= lbl "")) (<= (length lbl) ebut:max-len))))
150 (format "(ebut-modify): Enter a string of at most %s chars."
154 (hattr:set 'hbut:current 'loc (hui:key-src but-buf))
155 (hattr:set 'hbut:current 'dir (hui:key-dir but-buf))
156 (setq actype (hui:actype (hattr:get but 'actype)))
157 (hattr:set 'hbut:current 'actype actype)
158 (hattr:set 'hbut:current 'args (hargs:actype-get actype 'modifying))
159 (hattr:set 'hbut:current 'action
160 (and (boundp 'hui:ebut-prompt-for-action)
161 hui:ebut-prompt-for-action (hui:action actype)))
163 (ebut:operate lbl new-lbl)))
165 (defun hui:ebut-rename (curr-label new-label)
166 "Renames explicit Hyperbole button given by CURR-LABEL to NEW-LABEL.
167 If called interactively when point is not within an explicit button:
168 prompts for old and new button label values and performs rename.
169 If called interactively when point is within an explicit button:
170 saves button label and tells user to edit label, then call again.
171 second call changes the button's name from the stored value to the
173 Signals an error if any problem occurs."
176 (let (curr-label new-label)
177 (hui:buf-writable-err (current-buffer) "ebut-rename")
178 (if hui:ebut-label-prev
179 (setq curr-label hui:ebut-label-prev
180 new-label (ebut:label-p 'as-label))
183 (or (ebut:label-p 'as-label)
184 (let ((buts (ebut:alist)))
186 (hypb:error "(ebut-rename): No explicit buttons in buffer.")
187 (prog1 (hargs:read-match
188 "Button label to rename: "
189 buts nil t nil 'ebut)
192 "Rename button label to: "
195 (and (not (string= lbl ""))
196 (<= (length lbl) ebut:max-len))))
199 "(ebut-rename): Use a quoted string of at most %s chars."
202 (list curr-label new-label))))
207 (hui:buf-writable-err (current-buffer) "ebut-rename")
208 (if (or (not (stringp curr-label)) (string= curr-label ""))
209 (hypb:error "(ebut-rename): 'curr-label' must be a non-empty string: %s"
211 (and (stringp new-label) (string= new-label "")
212 (hypb:error "(ebut-rename): 'new-label' must be a non-empty string: %s"
214 (or (ebut:get (ebut:label-to-key curr-label))
215 (hypb:error "(ebut-rename): Can't rename %s since no button data."
219 (ebut:operate curr-label new-label)
220 (setq hui:ebut-label-prev nil)
221 (message "Renamed from '%s' to '%s'." curr-label new-label))
223 (setq hui:ebut-label-prev curr-label)
224 (message "Edit button label and use same command to finish rename."))
225 (t (hypb:error "(ebut-rename): Move point to within a button label."))))
227 (defun hui:ebut-search (string &optional match-part)
228 "Shows lines of files/buffers containing an explicit but match for STRING.
229 Returns number of buttons matched and displayed.
230 By default, only matches for whole button labels are found, optional MATCH-PART
231 enables partial matches. The match lines are shown in a buffer which serves as
232 a menu to find any of the occurrences."
233 (interactive (list (read-string "Search for button string: ")
234 (y-or-n-p "Enable partial matches? ")))
235 (if (not (stringp string))
236 (hypb:error "(ebut-search): String to search for is required."))
237 (let* ((prefix (if (> (length string) 14)
238 (substring string 0 13) string))
239 (out-buf (get-buffer-create (concat "*" prefix " Hypb Search*")))
240 (total (ebut:search string out-buf match-part)))
245 (if (fboundp 'outline-minor-mode)
246 (and (progn (goto-char 1)
247 (search-forward "\C-m" nil t))
248 (outline-minor-mode 1)))
249 (if (fboundp 'hproperty:but-create)
250 (hproperty:but-create nil nil (regexp-quote
251 (if match-part string
252 (concat ebut:start string ebut:end)))))
253 (goto-char (point-min))
254 (pop-to-buffer out-buf)
255 (if (interactive-p) (message "%d match%s." total
256 (if (> total 1) "es" ""))
258 (if (interactive-p) (message "No matches.")
261 (defun hui:error (&rest args)
262 (hypb:error "(hui:error): Obsolete, use hypb:error instead."))
264 (defun hui:gbut-create (lbl)
265 "Creates Hyperbole global button with LBL."
266 (interactive "sCreate global button labeled: ")
267 (let (but-buf actype)
269 (setq actype (hui:actype))
270 (setq but-buf (set-buffer (find-file-noselect gbut:file)))
271 (hui:buf-writable-err but-buf "ebut-create")
272 ;; This prevents movement of point which might be useful to user.
274 (goto-char (point-max))
275 (hattr:set 'hbut:current 'loc (hui:key-src but-buf))
276 (hattr:set 'hbut:current 'dir (hui:key-dir but-buf))
277 (hattr:set 'hbut:current 'actype actype)
278 (hattr:set 'hbut:current 'args (hargs:actype-get actype))
279 (hattr:set 'hbut:current 'action
280 (and (boundp 'hui:ebut-prompt-for-action)
281 hui:ebut-prompt-for-action (hui:action actype)))
282 (setq lbl (concat lbl (ebut:operate lbl nil)))
283 (goto-char (point-max))
287 (message "%s created." lbl)
290 (defun hui:gbut-modify (lbl-key)
291 "Modifies a global Hyperbole button given by LBL-KEY.
292 Signals an error when no such button is found."
293 (interactive (list (save-excursion
294 (hui:buf-writable-err
295 (find-file-noselect gbut:file) "gbut-modify")
297 (hargs:read-match "Global button to modify: "
298 (mapcar 'list (gbut:lbl-list))
300 (let ((lbl (hbut:key-to-label lbl-key))
301 (but-buf (find-file-noselect gbut:file))
305 (hui:buf-writable-err but-buf "gbut-modify"))
307 (or (setq but (ebut:get lbl-key but-buf))
308 (progn (pop-to-buffer but-buf)
310 "(gbut-modify): Invalid button, no data for '%s'." lbl)))
314 "Change global button label to: "
317 (and (not (string= lbl "")) (<= (length lbl) ebut:max-len))))
319 (format "(gbut-modify): Enter a string of at most %s chars."
323 (hattr:set 'hbut:current 'loc (hui:key-src but-buf))
324 (hattr:set 'hbut:current 'dir (hui:key-dir but-buf))
325 (setq actype (hui:actype (hattr:get but 'actype)))
326 (hattr:set 'hbut:current 'actype actype)
327 (hattr:set 'hbut:current 'args (hargs:actype-get actype 'modifying))
328 (hattr:set 'hbut:current 'action
329 (and (boundp 'hui:ebut-prompt-for-action)
330 hui:ebut-prompt-for-action (hui:action actype)))
332 (ebut:operate lbl new-lbl))))
334 (defun hui:hbut-act (&optional but)
335 "Executes action for optional Hyperbole button symbol BUT in current buffer.
336 Default is the current button."
338 (let ((but (hbut:at-p)) (lst))
341 ((setq lst (ebut:alist))
342 (ebut:get (ebut:label-to-key
343 (hargs:read-match "Button to execute: " lst nil t
344 (ebut:label-p 'as-label) 'ebut))))
345 (t (hypb:error "(hbut-act): No explicit buttons in buffer."))))))
346 (cond ((and (interactive-p) (null but))
347 (hypb:error "(hbut-act): No current button to activate."))
348 ((not (hbut:is-p but))
349 (hypb:error "(hbut-act): Button is invalid; it has no attributes."))
350 (t (or but (setq but 'hbut:current))
351 (hui:but-flash) (hyperb:act but))))
353 (defun hui:hbut-help (&optional but)
354 "Checks for and explains an optional button given by symbol, BUT.
355 BUT defaults to the button whose label point is within."
357 (setq but (or but (hbut:at-p)
358 (ebut:get (ebut:label-to-key
359 (hargs:read-match "Help for button: "
360 (ebut:alist) nil t nil 'ebut)))))
362 (hypb:error "(hbut-help): Move point to a valid Hyperbole button."))
363 (if (not (hbut:is-p but))
364 (cond (but (hypb:error "(hbut-help): Invalid button."))
366 "(hbut-help): Not on an implicit button and no buffer explicit buttons."))))
367 (let* ((sym-name (htype:names 'ibtypes (hattr:get but 'categ)))
368 (type-help-func (if sym-name (intern-soft (concat sym-name ":help")))))
369 (or (equal (hypb:indirect-function 'hui:but-flash)
370 (function (lambda nil)))
371 ;; Only flash button if point is on it.
372 (let ((lbl-key (hattr:get but 'lbl-key)))
374 (or (equal lbl-key (ebut:label-p))
375 (equal lbl-key (ibut:label-p)))
378 (funcall type-help-func but)
379 (let ((total (hbut:report but)))
380 (if total (hui:help-ebut-highlight))))))
382 (defun hui:hbut-label (default-label func-name)
383 "Reads button label from user using DEFAULT-LABEL and caller's FUNC-NAME."
384 (hargs:read "Button label: "
387 (and (not (string= lbl "")) (<= (length lbl) ebut:max-len))))
389 (format "(%s): Enter a string of at most %s chars."
390 func-name ebut:max-len)
393 (defun hui:hbut-label-default (start end &optional skip-len-test)
394 "Returns default label based on START and END region markers or points.
395 Optional SKIP-LEN-TEST means don't limit label to 'ebut:max-len' length.
396 Returns nil if START or END are invalid or if region fails length test.
398 Also has side effect of moving point to start of default label, if any."
399 (if (markerp start) (setq start (marker-position start)))
400 (if (markerp end) (setq end (marker-position end)))
401 ;; Test whether to use region as default button label.
402 (if (and (integerp start) (integerp end)
404 (<= (max (- end start) (- start end)) ebut:max-len)))
405 (progn (goto-char start)
406 (buffer-substring start end))))
408 (defun hui:hbut-report (&optional arg)
409 "Pretty prints attributes of current button, using optional prefix ARG.
412 (if (and arg (symbolp arg))
414 (let ((total (hbut:report arg)))
416 (progn (hui:help-ebut-highlight)
417 (message "%d button%s." total (if (/= total 1) "s" "")))))))
419 (fset 'hui:hbut-summarize 'hui:hbut-report)
421 (defun hui:link-directly ()
422 "Creates a Hyperbole link button at depress point, linked to release point.
423 See also documentation for 'hui:link-possible-types'."
424 (let* ((link-types (hui:link-possible-types))
425 (but-window action-key-depress-window)
426 (num-types (length link-types))
427 (release-window (selected-window))
429 type-and-args lbl-key but-loc but-dir)
430 (select-window action-key-depress-window)
431 (hui:buf-writable-err (current-buffer) "link-directly")
435 but-loc (hattr:get 'hbut:current 'loc)
436 but-dir (hattr:get 'hbut:current 'dir)
437 lbl-key (hattr:get 'hbut:current 'lbl-key)))
438 (setq but-loc (hui:key-src (current-buffer))
439 but-dir (hui:key-dir (current-buffer))
440 lbl-key (hbut:label-to-key
442 (if (marker-position (hypb:mark-marker t))
443 (hui:hbut-label-default
444 (region-beginning) (region-end)))
447 ;; XEmacs 21.5 thinks windows isn't live. Bug!?
448 (if (window-live-p release-window)
449 (select-window release-window))
451 (cond ((= num-types 0)
452 (error "(link-directly): No possible link type to create."))
454 (hui:link-create but-modify
455 but-window lbl-key but-loc but-dir
456 (setq type-and-args (car link-types))))
461 but-modify but-window
462 lbl-key but-loc but-dir
468 (lambda (type-and-args)
469 (setq type (car type-and-args))
473 "^\\(link-to\\|eval\\)-"
474 (setq item (symbol-name type)))
475 (setq item (substring
480 (intern (concat "actypes::"
481 (symbol-name type)))))))
483 (message "`%s' button %s type `%s'."
484 (hbut:key-to-label lbl-key)
485 (if but-modify "set to" "created with")
486 (car type-and-args))))
489 ;;; Private functions
492 (defun hui:action (actype &optional prompt)
493 "Prompts for and returns an action to override action from ACTYPE."
495 (let* ((act) (act-str)
496 (params (actype:params actype))
497 (params-str (and params (concat " " (prin1-to-string params))))
500 (while (and (setq act-str
502 (or prompt (concat "Action" params-str
505 (not (string= act-str ""))
507 (progn (setq act (read act-str)) nil)
509 (beep) (message "Invalid action syntax.")
511 (and (not (symbolp act))
513 ;; Use the weak condition that action must
514 ;; involve at least one of actype's parameters
515 ;; or else we assume the action is invalid, tell
516 ;; the user and provide another chance for entry.
521 (setq param (symbol-name param))
523 (concat "[\( \t\n,']"
530 (beep) (message "Action must use at least one parameter.")
533 (while (cond ((listp act)
534 (and act (setq head (car act))
535 (not (or (eq head 'lambda)
537 (eq head 'defmacro)))
538 (setq act (list 'lambda params act))
539 nil ;; terminate loop
542 (setq act (cons act params)))
544 (setq act (action:kbd-macro act 1)))
550 (defun hui:actype (&optional default-actype prompt)
551 "Using optional DEFAULT-ACTYPE, PROMPTs for a button action type.
552 DEFAULT-ACTYPE may be a valid symbol or symbol-name."
553 (and default-actype (symbolp default-actype)
555 (setq default-actype (symbol-name default-actype))
556 (if (string-match "actypes::" default-actype)
557 (setq default-actype (substring default-actype (match-end 0))))))
558 (if (or (null default-actype) (stringp default-actype))
561 (hargs:read-match (or prompt "Button's action type: ")
562 (mapcar 'list (htype:names 'actypes))
563 nil t default-actype 'actype)))
564 (hypb:error "(actype): Invalid default action type received.")
567 (defun hui:buf-writable-err (but-buf func-name)
568 "If BUT-BUF is read-only or is unwritable, signal an error from FUNC-NAME."
569 (let ((obuf (prog1 (current-buffer) (set-buffer but-buf)))
570 ;; (unwritable (and buffer-file-name
571 ;; (not (file-writable-p buffer-file-name))))
574 ;; Commented error out since some people want to be able to create
575 ;; buttons within files which they have purposely marked read-only.
577 ;; (format "(ebut-modify): You are not allowed to modify '%s'."
578 ;; (file-name-nondirectory buffer-file-name))))
582 "Button buffer '%s' is read-only. Use %s to change it."
583 (buffer-name but-buf)
585 (if (where-is-internal 'vc-toggle-read-only)
586 'vc-toggle-read-only 'toggle-read-only))
589 (if err (progn (pop-to-buffer but-buf) (hypb:error err)))))
591 (defun hui:ebut-buf (&optional prompt)
592 "Prompt for and return a buffer in which to place a button."
598 (or prompt "Button's buffer: ")
603 (let ((b (buffer-name buf)))
604 (if (and (not (string-match "mail\\*" b))
605 (not (string-match "\\*post-news\\*" b))
606 (string-match "\\`[* ]" b))
610 nil t (buffer-name) 'buffer))
611 (or (null buf-name) (equal buf-name "")))
613 (get-buffer buf-name)))
615 (defun hui:ebut-delete-op (interactive but-key key-src)
616 "INTERACTIVEly or not deletes explicit Hyperbole button given by BUT-KEY in KEY-SRC.
617 KEY-SRC may be a buffer or a pathname, when nil the current buffer is used.
618 Returns t if button is deleted, signals error otherwise. If called
619 with INTERACTIVE non-nil, derives BUT-KEY from the button that point is
621 (let ((buf (current-buffer)) (ebut))
624 (cond ((or (null key-src) (and (bufferp key-src) (setq buf key-src)))
625 (setq ebut (ebut:get but-key key-src)))
626 ((and (stringp key-src)
627 (setq buf (find-file-noselect key-src)))
628 (setq ebut (ebut:get but-key buf)))
629 (t (hypb:error "(ebut-delete): Invalid key-src: '%s'." key-src)))
632 (hypb:error "(ebut-delete): No valid %s button in %s."
633 (ebut:key-to-label but-key) buf))
635 (progn (set-buffer buf)
638 (call-interactively 'hui:ebut-unmark)
639 (message "Button deleted."))
640 (hui:ebut-unmark but-key key-src))
641 (if (hmail:reader-p) (hmail:msg-narrow))
643 (hypb:error "(ebut-delete): You may not delete buttons from this buffer."))))
645 (defun hui:ebut-delimit (start end instance-str)
646 (hypb:error "(hui:ebut-delimit): Obsolete, use ebut:delimit instead."))
648 (defun hui:ebut-operate (curr-label new-label)
649 (hypb:error "(hui:ebut-operate): Obsolete, use ebut:operate instead."))
651 (defun hui:ebut-unmark (&optional but-key key-src directory)
652 "Remove delimiters from button given by BUT-KEY in KEY-SRC of DIRECTORY.
653 All args are optional, the current button and buffer file are the defaults."
655 (let ((form (function
657 (let ((buffer-read-only) start end)
658 (setq start (match-beginning 0)
660 (and (fboundp 'hproperty:but-delete)
661 (hproperty:but-delete start))
662 (skip-chars-backward " \t\n")
663 (skip-chars-backward "0-9")
664 (if (= (preceding-char) (string-to-char ebut:instance-sep))
665 (setq start (1- (point))))
666 (if (search-backward ebut:start (- (point) ebut:max-len) t)
667 (if current-prefix-arg
668 ;; Remove button label, delimiters and preceding
670 (delete-region (max (point-min)
671 (1- (match-beginning 0)))
674 ;; Remove button delimiters only.
676 ;; Remove button ending delimiter
677 (delete-region start end)
678 ;; Remove button starting delimiter
679 (delete-region (match-beginning 0)
680 (match-end 0)))))))))
683 (if (search-forward ebut:end nil t) (funcall form)))
684 ;; Non-interactive invocation.
686 (if (and (or (null key-src) (eq key-src buffer-file-name))
687 (or (null directory) (eq directory default-directory)))
689 (set-buffer (find-file-noselect
690 (expand-file-name key-src directory))))
692 (goto-char (point-min))
693 (if (re-search-forward (ebut:label-regexp but-key) nil t)
694 (progn (funcall form)
695 ;; If modified a buffer other than the current one,
697 (or cur-p (save-buffer)))))))))
699 (defun hui:file-find (file-name)
700 "If FILE-NAME is readable, finds it, else signals an error."
701 (if (and (stringp file-name) (file-readable-p file-name))
702 (find-file file-name)
703 (hypb:error "(file-find): \"%s\" does not exist or is not readable."
706 (defun hui:hbut-term-highlight (start end)
707 "For terminals only: Emphasize a button spanning from START to END."
711 (narrow-to-region (point-min) start)
713 (setq inverse-video t)
714 (goto-char (point-min))
716 (narrow-to-region (point) end)
718 (setq inverse-video nil)
721 (defun hui:hbut-term-unhighlight (start end)
722 "For terminals only: Remove any emphasis from hyper-button at START to END."
726 (narrow-to-region (point-min) start)
728 (setq inverse-video nil))))
730 (defun hui:help-ebut-highlight ()
731 "Highlight any explicit buttons in help buffer associated with current buffer."
732 (if (fboundp 'hproperty:but-create)
735 (get-buffer (hypb:help-buf-name)))
736 (hproperty:but-create))))
738 (defun hui:htype-delete (htype-sym)
739 "Deletes HTYPE-SYM from use in current Hyperbole session.
740 HTYPE-SYM must be redefined for use again."
741 (and htype-sym (symbolp htype-sym)
743 (intern (hargs:read-match
744 (concat "Delete from " (symbol-name htype-sym) ": ")
745 (mapcar 'list (htype:names htype-sym))
746 nil t nil htype-sym))))
747 (htype:delete type htype-sym))))
749 (defun hui:htype-help (htype-sym &optional no-sort)
750 "Displays documentation for types from HTYPE-SYM which match to a regexp.
751 Optional NO-SORT means display in decreasing priority order (natural order)."
752 (and htype-sym (symbolp htype-sym)
753 (let* ((tstr (symbol-name htype-sym))
754 (tprefix (concat tstr "::"))
755 (buf-name (hypb:help-buf-name (capitalize tstr)))
756 (temp-buffer-show-hook
759 (set-buffer buf) (goto-char (point-min))
760 (replace-regexp "^" " ") (goto-char (point-min))
761 (replace-string (concat " " tprefix) "")
762 (goto-char (point-min)) (set-buffer-modified-p nil)
763 (display-buffer buf nil))))
764 (temp-buffer-show-function temp-buffer-show-hook)
765 (names (htype:names htype-sym))
766 (term (hargs:read-match
767 (concat (capitalize tstr)
768 " to describe (RTN for all): ")
769 (mapcar 'list (cons "" names))
770 nil t nil htype-sym))
774 (if (string= term "")
776 (mapcar (function (lambda (nm) (concat tprefix nm)))
778 (if no-sort type-names
779 (sort type-names 'string<)))
780 (cons (concat tprefix term) nil))
781 doc-list (delq nil (mapcar
784 (let ((doc (documentation
785 (intern-soft name))))
786 (if doc (cons name doc)))))
788 (with-output-to-temp-buffer buf-name
789 (mapcar (function (lambda (nm-doc-cons)
790 (princ (car nm-doc-cons)) (terpri)
791 (princ (cdr nm-doc-cons)) (terpri)))
794 (defun hui:key-dir (but-buf)
795 "Returns button key src directory based on BUT-BUF, a buffer."
796 (if (bufferp but-buf)
797 (let ((file (buffer-file-name but-buf)))
799 (file-name-directory (hpath:symlink-referent file))
800 (cdr (assq 'default-directory (buffer-local-variables but-buf)))))
801 (hypb:error "(hui:key-dir): '%s' is not a valid buffer.")))
803 (defun hui:key-src (but-buf)
804 "Returns button key src location based on BUT-BUF, a buffer.
805 This is BUT-BUF when button data is stored in the buffer and the
806 button's source file name when the button data is stored externally."
809 (cond ((hmail:mode-is-p) but-buf)
810 ((hpath:symlink-referent (buffer-file-name but-buf)))
813 (defun hui:link-create (modify but-window lbl-key but-loc but-dir type-and-args)
814 "Creates or modifies a new Hyperbole button.
815 If MODIFY is non-nil, modifies button at point in BUT-WINDOW,
816 otherwise, prompts for button label and creates a button.
817 LBL-KEY is internal form of button label. BUT-LOC is file or buffer
818 in which to create button. BUT-DIR is directory of BUT-LOC.
819 TYPE-AND-ARGS is the action type for the button followed by any arguments it requires."
820 (hattr:set 'hbut:current 'loc but-loc)
821 (hattr:set 'hbut:current 'dir but-dir)
822 (hattr:set 'hbut:current 'actype (intern-soft
825 (car type-and-args)))))
826 (hattr:set 'hbut:current 'args (cdr type-and-args))
828 (select-window but-window)
829 (let ((label (ebut:key-to-label lbl-key)))
830 (ebut:operate label (if modify label)))
833 (defun hui:link-possible-types ()
834 "Returns list of possible link types for a Hyperbole button link to point.
835 Each list element is a list of the link type and any arguments it requires.
837 The link types considered are fixed. Defining new link types will not alter
838 the possible types. The code must be changed to do that.
840 Referent Context Possible Link Type Returned
841 ----------------------------------------------------
842 Explicit Button link-to-ebut
844 Info Node link-to-Info-node
846 Mail Reader Msg link-to-mail
848 Outline Regexp Prefix link-to-string-match
850 Directory Name link-to-directory
852 File Name link-to-file
854 Koutline Cell link-to-kcell
856 Buffer attached to File link-to-file
858 Buffer without File link-to-buffer-tmp
860 Elisp Buffer at Start
861 or End of Sexpression eval-elisp
865 (list (if (ebut:at-p)
866 (list 'link-to-ebut buffer-file-name (ebut:label-p)))
867 (cond ((eq major-mode 'Info-mode)
868 (let ((hargs:reading-p 'Info-node))
869 (list 'link-to-Info-node (hargs:at-p))))
872 (list (rmail:msg-id-get) buffer-file-name)))
875 ;; If link is within an outline-regexp prefix, use
876 ;; a link-to-string-match.
877 ((and (boundp 'outline-regexp)
878 (stringp outline-regexp)
883 (if (looking-at outline-regexp)
888 (let ((heading (buffer-substring
890 (progn (beginning-of-line) (point))))
892 (while (search-backward heading nil t)
893 (setq occur (1+ occur)))
894 (list 'link-to-string-match
895 heading occur buffer-file-name))))
896 ((let ((hargs:reading-p 'directory))
897 (setq val (hargs:at-p t)))
898 (list 'link-to-directory val))
899 ((let ((hargs:reading-p 'file))
900 (setq val (hargs:at-p t)))
901 (list 'link-to-file val (point)))
902 ((eq major-mode 'kotl-mode)
903 (list 'link-to-kcell buffer-file-name (kcell-view:idstamp)))
905 (list 'link-to-file buffer-file-name (point)))
906 (t (list 'link-to-buffer-tmp (buffer-name)))
908 (and (fboundp 'smart-emacs-lisp-mode-p)
909 (smart-emacs-lisp-mode-p)
910 (or (= (char-syntax (following-char)) ?\()
911 (= (char-syntax (preceding-char)) ?\)))
912 (setq val (hargs:sexpression-p))
913 (list 'eval-elisp val))
918 ;;; Private variables
922 (defvar hui:ebut-label-prev nil
923 "String value of previous button name during an explicit button rename.
924 At other times, values must be nil.")