diff options
| author | Miles Bader | 2006-01-29 13:08:58 +0000 |
|---|---|---|
| committer | Miles Bader | 2006-01-29 13:08:58 +0000 |
| commit | 597993cf4433604ea65e40d33ad6cfe83dab2fb7 (patch) | |
| tree | 9e9cc6dbc0968bc83d7657c17ecade6b56691f89 /lisp/erc/erc-button.el | |
| parent | 33c7860d38eb0f5416630b54a7a1b878810a5d3b (diff) | |
| download | emacs-597993cf4433604ea65e40d33ad6cfe83dab2fb7.tar.gz emacs-597993cf4433604ea65e40d33ad6cfe83dab2fb7.zip | |
Revision: emacs@sv.gnu.org/emacs--devo--0--patch-22
Creator: Michael Olson <mwolson@gnu.org>
Install ERC.
Diffstat (limited to 'lisp/erc/erc-button.el')
| -rw-r--r-- | lisp/erc/erc-button.el | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/lisp/erc/erc-button.el b/lisp/erc/erc-button.el new file mode 100644 index 00000000000..6c6998a3afc --- /dev/null +++ b/lisp/erc/erc-button.el | |||
| @@ -0,0 +1,504 @@ | |||
| 1 | ;; erc-button.el --- A way of buttonizing certain things in ERC buffers | ||
| 2 | |||
| 3 | ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, | ||
| 4 | ;; 2006 Free Software Foundation, Inc. | ||
| 5 | |||
| 6 | ;; Author: Mario Lang <mlang@delysid.org> | ||
| 7 | ;; Keywords: irc, button, url, regexp | ||
| 8 | ;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?ErcButton | ||
| 9 | |||
| 10 | ;; This file is part of GNU Emacs. | ||
| 11 | |||
| 12 | ;; GNU Emacs is free software; you can redistribute it and/or modify | ||
| 13 | ;; it under the terms of the GNU General Public License as published by | ||
| 14 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 15 | ;; any later version. | ||
| 16 | |||
| 17 | ;; GNU Emacs 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 | ||
| 20 | ;; GNU General Public License for more details. | ||
| 21 | |||
| 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. | ||
| 26 | |||
| 27 | ;;; Commentary: | ||
| 28 | |||
| 29 | ;; Heavily borrowed from gnus-art.el. Thanks to the original authors. | ||
| 30 | ;; This buttonizes nicks and other stuff to make it all clickable. | ||
| 31 | ;; To enable, add to your ~/.emacs: | ||
| 32 | ;; (require 'erc-button) | ||
| 33 | ;; (erc-button-mode 1) | ||
| 34 | ;; | ||
| 35 | ;; Todo: | ||
| 36 | ;; * Rewrite all this to do the same, but use button.el from GNU Emacs | ||
| 37 | ;; if it's available for xemacs too. Why? button.el is much faster, | ||
| 38 | ;; and much more elegant, and solves the problem we get with large buffers | ||
| 39 | ;; and a large erc-button-marker-list. | ||
| 40 | |||
| 41 | |||
| 42 | ;;; Code: | ||
| 43 | |||
| 44 | (require 'erc) | ||
| 45 | (require 'wid-edit) | ||
| 46 | (require 'erc-fill) | ||
| 47 | |||
| 48 | ;;; Minor Mode | ||
| 49 | |||
| 50 | (defgroup erc-button nil | ||
| 51 | "Define how text can be turned into clickable buttons." | ||
| 52 | :group 'erc) | ||
| 53 | |||
| 54 | ;;;###autoload (autoload 'erc-button-mode "erc-button" nil t) | ||
| 55 | (define-erc-module button nil | ||
| 56 | "This mode buttonizes all messages according to `erc-button-alist'." | ||
| 57 | ((add-hook 'erc-insert-modify-hook 'erc-button-add-buttons 'append) | ||
| 58 | (add-hook 'erc-send-modify-hook 'erc-button-add-buttons 'append) | ||
| 59 | (add-hook 'erc-complete-functions 'erc-button-next)) | ||
| 60 | ((remove-hook 'erc-insert-modify-hook 'erc-button-add-buttons) | ||
| 61 | (remove-hook 'erc-send-modify-hook 'erc-button-add-buttons) | ||
| 62 | (remove-hook 'erc-complete-functions 'erc-button-next))) | ||
| 63 | |||
| 64 | ;; Make XEmacs use `erc-button-face'. | ||
| 65 | (when (featurep 'xemacs) | ||
| 66 | (add-hook 'erc-mode-hook | ||
| 67 | (lambda () (set (make-local-variable 'widget-button-face) nil)))) | ||
| 68 | |||
| 69 | ;;; Variables | ||
| 70 | |||
| 71 | (defface erc-button '((t (:bold t))) | ||
| 72 | "ERC button face." | ||
| 73 | :group 'erc-faces) | ||
| 74 | |||
| 75 | (defcustom erc-button-face 'erc-button | ||
| 76 | "Face used for highlighting buttons in ERC buffers. | ||
| 77 | |||
| 78 | A button is a piece of text that you can activate by pressing | ||
| 79 | `RET' or `mouse-2' above it. See also `erc-button-keymap'." | ||
| 80 | :type 'face | ||
| 81 | :group 'erc-faces) | ||
| 82 | |||
| 83 | (defcustom erc-button-nickname-face 'erc-nick-default-face | ||
| 84 | "Face used for ERC nickname buttons." | ||
| 85 | :type 'face | ||
| 86 | :group 'erc-faces) | ||
| 87 | |||
| 88 | (defcustom erc-button-mouse-face 'highlight | ||
| 89 | "Face used for mouse highlighting in ERC buffers. | ||
| 90 | |||
| 91 | Buttons will be displayed in this face when the mouse cursor is | ||
| 92 | above them." | ||
| 93 | :type 'face | ||
| 94 | :group 'erc-faces) | ||
| 95 | |||
| 96 | (defcustom erc-button-url-regexp | ||
| 97 | (concat "\\(www\\.\\|\\(s?https?\\|" | ||
| 98 | "ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\):\\)" | ||
| 99 | "\\(//[-a-zA-Z0-9_.]+:[0-9]*\\)?" | ||
| 100 | "[-a-zA-Z0-9_=!?#$@~`%&*+\\/:;.,]+[-a-zA-Z0-9_=#$@~`%&*+\\/]") | ||
| 101 | "Regular expression that matches URLs." | ||
| 102 | :group 'erc-button | ||
| 103 | :type 'regexp) | ||
| 104 | |||
| 105 | (defcustom erc-button-wrap-long-urls nil | ||
| 106 | "If non-nil, \"long\" URLs matching `erc-button-url-regexp' will be wrapped. | ||
| 107 | |||
| 108 | If this variable is a number, consider URLs longer than its value to | ||
| 109 | be \"long\". If t, URLs will be considered \"long\" if they are | ||
| 110 | longer than `erc-fill-column'." | ||
| 111 | :group 'erc-button | ||
| 112 | :type '(choice integer boolean)) | ||
| 113 | |||
| 114 | (defcustom erc-button-buttonize-nicks t | ||
| 115 | "Flag indicating whether nicks should be buttonized or not." | ||
| 116 | :group 'erc-button | ||
| 117 | :type 'boolean) | ||
| 118 | |||
| 119 | (defcustom erc-button-rfc-url "http://www.faqs.org/rfcs/rfc%s.html" | ||
| 120 | "*URL used to browse rfc references. | ||
| 121 | %s is replaced by the number." | ||
| 122 | :group 'erc-button | ||
| 123 | :type 'string) | ||
| 124 | |||
| 125 | (defcustom erc-button-google-url "http://www.google.com/search?q=%s" | ||
| 126 | "*URL used to browse Google search references. | ||
| 127 | %s is replaced by the search string." | ||
| 128 | :group 'erc-button | ||
| 129 | :type 'string) | ||
| 130 | |||
| 131 | (defcustom erc-button-alist | ||
| 132 | ;; Since the callback is only executed when the user is clicking on | ||
| 133 | ;; a button, it makes no sense to optimize performance by | ||
| 134 | ;; bytecompiling lambdas in this alist. On the other hand, it makes | ||
| 135 | ;; things hard to maintain. | ||
| 136 | '(('nicknames 0 erc-button-buttonize-nicks erc-nick-popup 0) | ||
| 137 | (erc-button-url-regexp 0 t browse-url 0) | ||
| 138 | ("<URL: *\\([^<> ]+\\) *>" 0 t browse-url 1) | ||
| 139 | ("(\\(\\([^~\n \t@][^\n \t@]*\\)@\\([a-zA-Z0-9.:-]+\\)\\)" 1 t finger 2 3) | ||
| 140 | ;; emacs internal | ||
| 141 | ("[`]\\([a-zA-Z][-a-zA-Z_0-9]+\\)[']" 1 t erc-button-describe-symbol 1) | ||
| 142 | ;; pseudo links | ||
| 143 | ("\\bInfo:[\"]\\([^\"]+\\)[\"]" 0 t Info-goto-node 1) | ||
| 144 | ("\\b\\(Ward\\|Wiki\\|WardsWiki\\|TheWiki\\):\\([A-Z][a-z]+\\([A-Z][a-z]+\\)+\\)" | ||
| 145 | 0 t (lambda (page) | ||
| 146 | (browse-url (concat "http://c2.com/cgi-bin/wiki?" page))) | ||
| 147 | 2) | ||
| 148 | ("EmacsWiki:\\([A-Z][a-z]+\\([A-Z][a-z]+\\)+\\)" 0 t erc-browse-emacswiki 1) | ||
| 149 | ("Lisp:\\([a-zA-Z.+-]+\\)" 0 t erc-browse-emacswiki-lisp 1) | ||
| 150 | ("\\bGoogle:\\([^ \t\n\r\f]+\\)" | ||
| 151 | 0 t (lambda (keywords) | ||
| 152 | (browse-url (format erc-button-google-url keywords))) | ||
| 153 | 1) | ||
| 154 | ("\\brfc[#: ]?\\([0-9]+\\)" | ||
| 155 | 0 t (lambda (num) | ||
| 156 | (browse-url (format erc-button-rfc-url num))) | ||
| 157 | 1) | ||
| 158 | ;; other | ||
| 159 | ("\\s-\\(@\\([0-9][0-9][0-9]\\)\\)" 1 t erc-button-beats-to-time 2)) | ||
| 160 | "*Alist of regexps matching buttons in ERC buffers. | ||
| 161 | Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where | ||
| 162 | |||
| 163 | REGEXP is the string matching text around the button or a symbol | ||
| 164 | indicating a variable holding that string, or a list of | ||
| 165 | strings, or an alist with the strings in the car. Note that | ||
| 166 | entries in lists or alists are considered to be nicks or other | ||
| 167 | complete words. Therefore they are enclosed in \\< and \\> | ||
| 168 | while searching. REGEXP can also be the quoted symbol | ||
| 169 | 'nicknames, which matches the nickname of any user on the | ||
| 170 | current server. | ||
| 171 | |||
| 172 | BUTTON is the number of the regexp grouping actually matching the | ||
| 173 | button, This is ignored if REGEXP is 'nicknames. | ||
| 174 | |||
| 175 | FORM is a lisp expression which must eval to true for the button to | ||
| 176 | be added, | ||
| 177 | |||
| 178 | CALLBACK is the function to call when the user push this button. | ||
| 179 | CALLBACK can also be a symbol. Its variable value will be used | ||
| 180 | as the callback function. | ||
| 181 | |||
| 182 | PAR is a number of a regexp grouping whose text will be passed to | ||
| 183 | CALLBACK. There can be several PAR arguments. If REGEXP is | ||
| 184 | 'nicknames, these are ignored, and CALLBACK will be called with | ||
| 185 | the nickname matched as the argument." | ||
| 186 | :group 'erc-button | ||
| 187 | :type '(repeat | ||
| 188 | (list :tag "Button" | ||
| 189 | (choice :tag "Matches" | ||
| 190 | regexp | ||
| 191 | (variable :tag "Variable containing regexp") | ||
| 192 | (const :tag "Nicknames" 'nicknames)) | ||
| 193 | (integer :tag "Number of the regexp section that matches") | ||
| 194 | (choice :tag "When to buttonize" | ||
| 195 | (const :tag "Always" t) | ||
| 196 | (sexp :tag "Only when this evaluates to non-nil")) | ||
| 197 | (function :tag "Function to call when button is pressed") | ||
| 198 | (repeat :tag "Sections of regexp to send to the function" | ||
| 199 | :inline t | ||
| 200 | (integer :tag "Regexp section number"))))) | ||
| 201 | |||
| 202 | (defcustom erc-emacswiki-url "http://www.emacswiki.org/cgi-bin/wiki.pl?" | ||
| 203 | "*URL of the EmacsWiki Homepage." | ||
| 204 | :group 'erc-button | ||
| 205 | :type 'string) | ||
| 206 | |||
| 207 | (defcustom erc-emacswiki-lisp-url "http://www.emacswiki.org/elisp/" | ||
| 208 | "*URL of the EmacsWiki ELisp area." | ||
| 209 | :group 'erc-button | ||
| 210 | :type 'string) | ||
| 211 | |||
| 212 | (defvar erc-button-keymap | ||
| 213 | (let ((map (make-sparse-keymap))) | ||
| 214 | (define-key map (kbd "RET") 'erc-button-press-button) | ||
| 215 | (if (featurep 'xemacs) | ||
| 216 | (define-key map (kbd "<button2>") 'erc-button-click-button) | ||
| 217 | (define-key map (kbd "<mouse-2>") 'erc-button-click-button)) | ||
| 218 | (define-key map (kbd "TAB") 'erc-button-next) | ||
| 219 | (set-keymap-parent map erc-mode-map) | ||
| 220 | map) | ||
| 221 | "Local keymap for ERC buttons.") | ||
| 222 | |||
| 223 | (defvar erc-button-syntax-table | ||
| 224 | (let ((table (make-syntax-table))) | ||
| 225 | (modify-syntax-entry ?\( "w" table) | ||
| 226 | (modify-syntax-entry ?\) "w" table) | ||
| 227 | (modify-syntax-entry ?\[ "w" table) | ||
| 228 | (modify-syntax-entry ?\] "w" table) | ||
| 229 | (modify-syntax-entry ?\{ "w" table) | ||
| 230 | (modify-syntax-entry ?\} "w" table) | ||
| 231 | (modify-syntax-entry ?` "w" table) | ||
| 232 | (modify-syntax-entry ?' "w" table) | ||
| 233 | (modify-syntax-entry ?^ "w" table) | ||
| 234 | (modify-syntax-entry ?- "w" table) | ||
| 235 | (modify-syntax-entry ?_ "w" table) | ||
| 236 | (modify-syntax-entry ?| "w" table) | ||
| 237 | (modify-syntax-entry ?\\ "w" table) | ||
| 238 | table) | ||
| 239 | "Syntax table used when buttonizing messages. | ||
| 240 | This syntax table should make all the legal nick characters word | ||
| 241 | constituents.") | ||
| 242 | |||
| 243 | (defun erc-button-add-buttons () | ||
| 244 | "Find external references in the current buffer and make buttons of them. | ||
| 245 | \"External references\" are things like URLs, as | ||
| 246 | specified by `erc-button-alist'." | ||
| 247 | (interactive) | ||
| 248 | (save-excursion | ||
| 249 | (with-syntax-table erc-button-syntax-table | ||
| 250 | (let ((buffer-read-only nil) | ||
| 251 | (inhibit-point-motion-hooks t) | ||
| 252 | (inhibit-field-text-motion t) | ||
| 253 | (alist erc-button-alist) | ||
| 254 | entry regexp data) | ||
| 255 | (erc-button-remove-old-buttons) | ||
| 256 | (dolist (entry alist) | ||
| 257 | (if (equal (car entry) (quote (quote nicknames))) | ||
| 258 | (erc-button-add-nickname-buttons entry) | ||
| 259 | (progn | ||
| 260 | (setq regexp (or (and (stringp (car entry)) (car entry)) | ||
| 261 | (and (boundp (car entry)) | ||
| 262 | (symbol-value (car entry))))) | ||
| 263 | (cond ((stringp regexp) | ||
| 264 | (erc-button-add-buttons-1 regexp entry)) | ||
| 265 | ((and (listp regexp) (stringp (car regexp))) | ||
| 266 | (dolist (r regexp) | ||
| 267 | (erc-button-add-buttons-1 | ||
| 268 | (concat "\\<" (regexp-quote r) "\\>") | ||
| 269 | entry))) | ||
| 270 | ((and (listp regexp) (listp (car regexp)) | ||
| 271 | (stringp (caar regexp))) | ||
| 272 | (dolist (elem regexp) | ||
| 273 | (erc-button-add-buttons-1 | ||
| 274 | (concat "\\<" (regexp-quote (car elem)) "\\>") | ||
| 275 | entry))))))))))) | ||
| 276 | |||
| 277 | (defun erc-button-add-nickname-buttons (entry) | ||
| 278 | "Search through the buffer for nicknames, and add buttons." | ||
| 279 | (let ((form (nth 2 entry)) | ||
| 280 | (fun (nth 3 entry)) | ||
| 281 | bounds word) | ||
| 282 | (when (or (eq t form) | ||
| 283 | (eval form)) | ||
| 284 | (goto-char (point-min)) | ||
| 285 | (while (forward-word 1) | ||
| 286 | (setq bounds (bounds-of-thing-at-point 'word)) | ||
| 287 | (setq word (buffer-substring-no-properties | ||
| 288 | (car bounds) (cdr bounds))) | ||
| 289 | (if (erc-get-server-user word) | ||
| 290 | (erc-button-add-button (car bounds) (cdr bounds) | ||
| 291 | fun t (list word))))))) | ||
| 292 | |||
| 293 | (defun erc-button-add-buttons-1 (regexp entry) | ||
| 294 | "Search through the buffer for matches to ENTRY and add buttons." | ||
| 295 | (goto-char (point-min)) | ||
| 296 | (while (re-search-forward regexp nil t) | ||
| 297 | (let ((start (match-beginning (nth 1 entry))) | ||
| 298 | (end (match-end (nth 1 entry))) | ||
| 299 | (form (nth 2 entry)) | ||
| 300 | (fun (nth 3 entry)) | ||
| 301 | (data (mapcar 'match-string (nthcdr 4 entry)))) | ||
| 302 | (when (or (eq t form) | ||
| 303 | (eval form)) | ||
| 304 | (erc-button-add-button start end fun nil data regexp))))) | ||
| 305 | |||
| 306 | (defun erc-button-remove-old-buttons () | ||
| 307 | "Remove all existing buttons. | ||
| 308 | This is called with narrowing in effect, just before the text is | ||
| 309 | buttonized again. Removing a button means to remove all the properties | ||
| 310 | that `erc-button-add-button' adds, except for the face." | ||
| 311 | (remove-text-properties | ||
| 312 | (point-min) (point-max) | ||
| 313 | '(erc-callback nil | ||
| 314 | erc-data nil | ||
| 315 | mouse-face nil | ||
| 316 | keymap nil))) | ||
| 317 | |||
| 318 | (defun erc-button-add-button (from to fun nick-p &optional data regexp) | ||
| 319 | "Create a button between FROM and TO with callback FUN and data DATA. | ||
| 320 | NICK-P specifies if this is a nickname button. | ||
| 321 | REGEXP is the regular expression which matched for this button." | ||
| 322 | ;; Really nasty hack to <URL: > ise urls, and line-wrap them if | ||
| 323 | ;; they're going to be wider than `erc-fill-column'. | ||
| 324 | ;; This could be a lot cleaner, but it works for me -- lawrence. | ||
| 325 | (let (fill-column) | ||
| 326 | (when (and erc-button-wrap-long-urls | ||
| 327 | (string= regexp erc-button-url-regexp) | ||
| 328 | (> (- to from) | ||
| 329 | (setq fill-column (- (if (numberp erc-button-wrap-long-urls) | ||
| 330 | erc-button-wrap-long-urls | ||
| 331 | erc-fill-column) | ||
| 332 | (length erc-fill-prefix))))) | ||
| 333 | (setq to (prog1 (point-marker) (insert ">")) | ||
| 334 | from (prog2 (goto-char from) (point-marker) (insert "<URL: "))) | ||
| 335 | (let ((pos (copy-marker from))) | ||
| 336 | (while (> (- to pos) fill-column) | ||
| 337 | (goto-char (+ pos fill-column)) | ||
| 338 | (insert "\n" erc-fill-prefix) ; This ought to figure out | ||
| 339 | ; what type of filling we're | ||
| 340 | ; doing, and indent accordingly. | ||
| 341 | (move-marker pos (point)))))) | ||
| 342 | (if nick-p | ||
| 343 | (when erc-button-nickname-face | ||
| 344 | (erc-button-add-face from to erc-button-nickname-face)) | ||
| 345 | (when erc-button-face | ||
| 346 | (erc-button-add-face from to erc-button-face))) | ||
| 347 | (add-text-properties | ||
| 348 | from to | ||
| 349 | (nconc (and erc-button-mouse-face | ||
| 350 | (list 'mouse-face erc-button-mouse-face)) | ||
| 351 | (list 'erc-callback fun) | ||
| 352 | (list 'keymap erc-button-keymap) | ||
| 353 | (list 'rear-nonsticky t) | ||
| 354 | (and data (list 'erc-data data)))) | ||
| 355 | (widget-convert-button 'link from to :action 'erc-button-press-button | ||
| 356 | :suppress-face t | ||
| 357 | ;; Make XEmacs use our faces. | ||
| 358 | :button-face (if nick-p | ||
| 359 | erc-button-nickname-face | ||
| 360 | erc-button-face) | ||
| 361 | ;; Make XEmacs behave with mouse-clicks, for | ||
| 362 | ;; some reason, widget stuff overrides the | ||
| 363 | ;; 'keymap text-property. | ||
| 364 | :mouse-down-action 'erc-button-click-button)) | ||
| 365 | |||
| 366 | (defun erc-button-add-face (from to face) | ||
| 367 | "Add FACE to the region between FROM and TO." | ||
| 368 | ;; If we just use `add-text-property', then this will overwrite any | ||
| 369 | ;; face text property already used for the button. It will not be | ||
| 370 | ;; merged correctly. If we use overlays, then redisplay will be | ||
| 371 | ;; very slow with lots of buttons. This is why we manually merge | ||
| 372 | ;; face text properties. | ||
| 373 | (let ((old (erc-list (get-text-property from 'face))) | ||
| 374 | (pos from) | ||
| 375 | (end (next-single-property-change from 'face nil to)) | ||
| 376 | new) | ||
| 377 | ;; old is the face at pos, in list form. It is nil if there is no | ||
| 378 | ;; face at pos. If nil, the new face is FACE. If not nil, the | ||
| 379 | ;; new face is a list containing FACE and the old stuff. end is | ||
| 380 | ;; where this face changes. | ||
| 381 | (while (< pos to) | ||
| 382 | (setq new (if old (cons face old) face)) | ||
| 383 | (put-text-property pos end 'face new) | ||
| 384 | (setq pos end | ||
| 385 | old (erc-list (get-text-property pos 'face)) | ||
| 386 | end (next-single-property-change pos 'face nil to))))) | ||
| 387 | |||
| 388 | ;; widget-button-click calls with two args, we ignore the first. | ||
| 389 | ;; Since Emacs runs this directly, rather than with | ||
| 390 | ;; widget-button-click, we need to fake an extra arg in the | ||
| 391 | ;; interactive spec. | ||
| 392 | (defun erc-button-click-button (ignore event) | ||
| 393 | "Call `erc-button-press-button'." | ||
| 394 | (interactive "P\ne") | ||
| 395 | (save-excursion | ||
| 396 | (mouse-set-point event) | ||
| 397 | (erc-button-press-button))) | ||
| 398 | |||
| 399 | ;; XEmacs calls this via widget-button-press with a bunch of arguments | ||
| 400 | ;; which we don't care about. | ||
| 401 | (defun erc-button-press-button (&rest ignore) | ||
| 402 | "Check text at point for a callback function. | ||
| 403 | If the text at point has a `erc-callback' property, | ||
| 404 | call it with the value of the `erc-data' text property." | ||
| 405 | (interactive) | ||
| 406 | (let* ((data (get-text-property (point) 'erc-data)) | ||
| 407 | (fun (get-text-property (point) 'erc-callback))) | ||
| 408 | (unless fun | ||
| 409 | (message "No button at point")) | ||
| 410 | (when (and fun (symbolp fun) (not (fboundp fun))) | ||
| 411 | (error "Function %S is not bound" fun)) | ||
| 412 | (apply fun data))) | ||
| 413 | |||
| 414 | (defun erc-button-next () | ||
| 415 | "Go to the next button in this buffer." | ||
| 416 | (interactive) | ||
| 417 | (let ((here (point))) | ||
| 418 | (when (< here (erc-beg-of-input-line)) | ||
| 419 | (while (and (get-text-property here 'erc-callback) | ||
| 420 | (not (= here (point-max)))) | ||
| 421 | (setq here (1+ here))) | ||
| 422 | (while (and (not (get-text-property here 'erc-callback)) | ||
| 423 | (not (= here (point-max)))) | ||
| 424 | (setq here (1+ here))) | ||
| 425 | (if (< here (point-max)) | ||
| 426 | (goto-char here) | ||
| 427 | (error "No next button")) | ||
| 428 | t))) | ||
| 429 | |||
| 430 | (defun erc-browse-emacswiki (thing) | ||
| 431 | "Browse to thing in the emacs-wiki." | ||
| 432 | (browse-url (concat erc-emacswiki-url thing))) | ||
| 433 | |||
| 434 | (defun erc-browse-emacswiki-lisp (thing) | ||
| 435 | "Browse to THING in the emacs-wiki elisp area." | ||
| 436 | (browse-url (concat erc-emacswiki-lisp-url thing))) | ||
| 437 | |||
| 438 | ;;; Nickname buttons: | ||
| 439 | |||
| 440 | (defcustom erc-nick-popup-alist | ||
| 441 | '(("DeOp" . (erc-cmd-DEOP nick)) | ||
| 442 | ("Kick" . (erc-cmd-KICK (concat nick " " | ||
| 443 | (read-from-minibuffer | ||
| 444 | (concat "Kick " nick ", reason: "))))) | ||
| 445 | ("Msg" . (erc-cmd-MSG (concat nick " " | ||
| 446 | (read-from-minibuffer | ||
| 447 | (concat "Message to " nick ": "))))) | ||
| 448 | ("Op" . (erc-cmd-OP nick)) | ||
| 449 | ("Query" . (erc-cmd-QUERY nick)) | ||
| 450 | ("Whois" . (erc-cmd-WHOIS nick)) | ||
| 451 | ("Lastlog" . (erc-cmd-LASTLOG nick))) | ||
| 452 | "*An alist of possible actions to take on a nickname. | ||
| 453 | An entry looks like (\"Action\" . SEXP) where SEXP is evaluated with | ||
| 454 | the variable `nick' bound to the nick in question. | ||
| 455 | |||
| 456 | Examples: | ||
| 457 | (\"DebianDB\" . | ||
| 458 | (shell-command | ||
| 459 | (format | ||
| 460 | \"ldapsearch -x -P 2 -h db.debian.org -b dc=debian,dc=org ircnick=%s\" | ||
| 461 | nick)))" | ||
| 462 | :group 'erc-button | ||
| 463 | :type '(repeat (cons (string :tag "Op") | ||
| 464 | sexp))) | ||
| 465 | |||
| 466 | (defun erc-nick-popup (nick) | ||
| 467 | (let* ((completion-ignore-case t) | ||
| 468 | (action (completing-read (concat "What action to take on '" nick "'? ") | ||
| 469 | erc-nick-popup-alist)) | ||
| 470 | (code (cdr (assoc action erc-nick-popup-alist)))) | ||
| 471 | (when code | ||
| 472 | (erc-set-active-buffer (current-buffer)) | ||
| 473 | (eval code)))) | ||
| 474 | |||
| 475 | ;;; Callback functions | ||
| 476 | (defun erc-button-describe-symbol (symbol-name) | ||
| 477 | "Describe SYMBOL-NAME. | ||
| 478 | Use `describe-function' for functions, `describe-variable' for variables, | ||
| 479 | and `apropos' for other symbols." | ||
| 480 | (let ((symbol (intern-soft symbol-name))) | ||
| 481 | (cond ((and symbol (fboundp symbol)) | ||
| 482 | (describe-function symbol)) | ||
| 483 | ((and symbol (boundp symbol)) | ||
| 484 | (describe-variable symbol)) | ||
| 485 | (t (apropos symbol-name))))) | ||
| 486 | |||
| 487 | (defun erc-button-beats-to-time (beats) | ||
| 488 | "Display BEATS in a readable time format." | ||
| 489 | (let* ((seconds (- (* (string-to-number beats) 86.4) | ||
| 490 | 3600 | ||
| 491 | (- (car (current-time-zone))))) | ||
| 492 | (hours (mod (floor seconds 3600) 24)) | ||
| 493 | (minutes (mod (round seconds 60) 60))) | ||
| 494 | (message (format "@%s is %d:%02d local time" | ||
| 495 | beats hours minutes)))) | ||
| 496 | |||
| 497 | (provide 'erc-button) | ||
| 498 | |||
| 499 | ;;; erc-button.el ends here | ||
| 500 | ;; Local Variables: | ||
| 501 | ;; indent-tabs-mode: nil | ||
| 502 | ;; End: | ||
| 503 | |||
| 504 | ;; arch-tag: 7d23bed4-2f30-4273-a03f-d7a274c605c4 | ||