ef25aa0d88dcd57435214ac49f6bb74ad8b6d2e4
[riece] / lisp / riece-addon.el
1 ;;; riece-addon.el --- add-on management
2 ;; Copyright (C) 1998-2004 Daiki Ueno
3
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Created: 1998-09-28
6 ;; Keywords: IRC, riece
7
8 ;; This file is part of Riece.
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Code:
26
27 (require 'font-lock)
28 (require 'riece-options)
29 (require 'riece-compat)
30 (require 'riece-misc)
31
32 (defgroup riece-addon-list nil
33   "Add-on listing."
34   :tag "Addon list"
35   :prefix "riece-addon-list-"
36   :group 'riece)
37
38 (defgroup riece-addon-list-faces nil
39   "Faces for riece-addon-list-mode."
40   :tag "Faces"
41   :prefix "riece-addon-list-"
42   :group 'riece-addon-list)
43
44 (defface riece-addon-list-enabled-face
45   '((((class color) (background dark))
46      (:foreground "PaleTurquoise" :bold t))
47     (t
48      (:bold t)))
49   "Face used for displaying the enabled addon."
50   :group 'riece-addon-list-faces)
51 (defvar riece-addon-list-enabled-face 'riece-addon-list-enabled-face)
52
53 (defface riece-addon-list-disabled-face
54   '((((class color) (background dark))
55      (:foreground "PaleTurquoise" :italic t))
56     (t
57      (:italic t)))
58   "Face used for displaying the disabled addon."
59   :group 'riece-addon-list-faces)
60 (defvar riece-addon-list-disabled-face 'riece-addon-list-disabled-face)
61
62 (defface riece-addon-list-unsupported-face
63   '((((class color) (background dark))
64      (:foreground "PaleTurquoise"))
65     (t
66      ()))
67   "Face used for displaying the unsupported addon."
68   :group 'riece-addon-list-faces)
69 (defvar riece-addon-list-unsupported-face 'riece-addon-list-unsupported-face)
70
71 (defface riece-addon-list-unknown-face
72   '((t
73      (:foreground "red")))
74   "Face used for displaying the unknown addon."
75   :group 'riece-addon-list-faces)
76 (defvar riece-addon-list-unknown-face 'riece-addon-list-unknown-face)
77
78 (defface riece-addon-list-description-face
79   '((((class color)
80       (background dark))
81      (:foreground "lightyellow"))
82     (((class color)
83       (background light))
84      (:foreground "blue4"))
85     (t
86      ()))
87   "Face used for displaying the description addon."
88   :group 'riece-addon-list-faces)
89 (defvar riece-addon-list-description-face 'riece-addon-list-description-face)
90
91 (defcustom riece-addon-list-mark-face-alist
92   '((?+ . riece-addon-list-enabled-face)
93     (?- . riece-addon-list-disabled-face)
94     (?! . riece-addon-list-unsupported-face)
95     (?? . riece-addon-list-unknown-face))
96   "An alist mapping marks on riece-addon-list-buffer to faces."
97   :type 'list
98   :group 'riece-addon-list)
99
100 (defcustom riece-addon-list-font-lock-keywords
101   '(("^\\([-+!?] [^:]+\\): \\(.*\\)"
102      (1 (cdr (assq (aref (match-string 1) 0)
103                    riece-addon-list-mark-face-alist)))
104      (2 riece-addon-list-description-face)))
105   "Default expressions to addon in riece-addon-list-mode."
106   :type '(repeat (list string))
107   :group 'riece-addon-list)
108
109 (defvar riece-addon-list-mode-map
110   (let ((keymap (make-sparse-keymap)))
111     (define-key keymap "+" 'riece-command-enable-addon)
112     (define-key keymap "-" 'riece-command-disable-addon)
113     (define-key keymap "n" 'next-line)
114     (define-key keymap "p" 'previous-line)
115     (define-key keymap " " 'scroll-up)
116     (define-key keymap [delete] 'scroll-down)
117     (define-key keymap "q" 'bury-buffer)
118     keymap))
119
120 (defun riece-load-and-build-addon-dependencies (addons)
121   (let ((load-path (cons riece-addon-directory load-path))
122         dependencies)
123     (while addons
124       (require (car addons))            ;error will be reported here
125       (let* ((requires
126               (funcall (or (intern-soft
127                             (concat (symbol-name (car addons)) "-requires"))
128                            #'ignore)))
129              (pointer requires)
130              entry)
131         ;; Increment succs' pred count.
132         (if (setq entry (assq (car addons) dependencies))
133             (setcar (cdr entry) (+ (length requires) (nth 1 entry)))
134           (setq dependencies (cons (list (car addons) (length requires))
135                                    dependencies)))
136         ;; Merge pred's succs.
137         (while pointer
138           (if (setq entry (assq (car pointer) dependencies))
139               (setcdr (cdr entry)
140                       (cons (car addons) (nthcdr 2 entry)))
141             (setq dependencies (cons (list (car pointer) 0 (car addons))
142                                      dependencies)))
143           (setq pointer (cdr pointer))))
144       (setq addons (cdr addons)))
145     dependencies))
146
147 (defun riece-resolve-addon-dependencies (addons)
148   (let ((pointer addons)
149         dependencies queue)
150     ;; Uniquify, first.
151     (while pointer
152       (if (memq (car pointer) (cdr pointer))
153           (setcar pointer nil))
154       (setq pointer (cdr pointer)))
155     (setq dependencies (riece-load-and-build-addon-dependencies
156                         (delq nil addons))
157           pointer dependencies)
158     ;; Sort them.
159     (while pointer
160       (if (zerop (nth 1 (car pointer)))
161           (setq dependencies (delq (car pointer) dependencies)
162                 queue (cons (car pointer) queue)))
163       (setq pointer (cdr pointer)))
164     (setq addons nil)
165     (while queue
166       (setq addons (cons (car (car queue)) addons)
167             pointer (nthcdr 2 (car queue)))
168       (while pointer
169         (let* ((entry (assq (car pointer) dependencies))
170                (count (1- (nth 1 entry))))
171           (if (zerop count)
172               (setq dependencies (delq entry dependencies)
173                     queue (nconc queue (list entry)))
174             (setcar (cdr entry) count)))
175         (setq pointer (cdr pointer)))
176       (setq queue (cdr queue)))
177     (if dependencies
178         (error "Circular add-on dependency found: %S" dependencies))
179     (nreverse addons)))
180
181 (defun riece-resolve-addons (addons)
182   (riece-resolve-addon-dependencies
183    (if (file-directory-p riece-addon-directory)
184        (append addons
185                (mapcar
186                 (lambda (name)
187                   (unless (file-directory-p
188                            (expand-file-name name riece-addon-directory))
189                     (intern (file-name-sans-extension name))))
190                 (directory-files riece-addon-directory nil "\\`[^.]" t)))
191      addons)))
192
193 (defun riece-insinuate-addon (addon &optional verbose)
194   (if (get addon 'riece-addon-insinuated)
195       (if verbose
196           (message "Add-on %S is alread insinuated" addon))
197     (funcall (intern (concat (symbol-name addon) "-insinuate")))
198     (put addon 'riece-addon-insinuated t)
199     (if verbose
200         (message "Add-on %S is insinuated" addon))))
201
202 (defun riece-enable-addon (addon &optional verbose)
203   (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled"))))
204     (if (null enabled)
205         (if verbose
206             (message "Add-on %S doesn't support enable/disable" addon))
207       (if (symbol-value enabled)
208           (if verbose
209               (message "Add-on %S is already enabled" addon))
210         (funcall (intern (concat (symbol-name addon) "-enable")))
211         (if verbose
212             (message "Add-on %S enabled" addon))))))
213
214 (defun riece-disable-addon (addon &optional verbose)
215   (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled"))))
216     (if (null enabled)
217         (if verbose
218             (message "Add-on %S doesn't support enable/disable" addon))
219       (if (symbol-value enabled)
220           (progn
221             (funcall (intern (concat (symbol-name addon) "-disable")))
222             (if verbose
223                 (message "Add-on %S disabled" addon)))
224         (if verbose
225             (message "Add-on %S is already enabled" addon))))))
226
227 (put 'riece-addon-list-mode 'font-lock-defaults
228      '(riece-addon-list-font-lock-keywords t))
229
230 (defun riece-addon-list-mode ()
231   "Major mode for displaying addon list.
232 All normal editing commands are turned off."
233   (kill-all-local-variables)
234   (buffer-disable-undo)
235   (setq major-mode 'riece-addon-list-mode
236         mode-name "AddOns"
237         mode-line-buffer-identification
238         (riece-mode-line-buffer-identification '("Riece: %12b"))
239         truncate-lines t
240         buffer-read-only t)
241   (use-local-map riece-addon-list-mode-map)
242   (make-local-variable 'font-lock-defaults)
243   (setq font-lock-defaults '(riece-addon-list-font-lock-keywords t))
244   ;; In XEmacs, auto-initialization of font-lock is not effective
245   ;; if buffer-file-name is not set.
246   (font-lock-set-defaults)
247   (run-hooks 'riece-addon-list-mode-hook))
248
249 (defun riece-command-list-addons ()
250   (interactive)
251   (set-buffer (riece-get-buffer-create "*AddOns*" 'riece-addon-list-mode))
252   (riece-addon-list-mode)
253   (let ((inhibit-read-only t)
254         buffer-read-only
255         (pointer (sort (copy-sequence riece-addons)
256                        (lambda (symbol1 symbol2)
257                                   (string-lessp (symbol-name symbol1)
258                                                 (symbol-name symbol2)))))
259         enabled description point)
260     (erase-buffer)
261     (riece-kill-all-overlays)
262     (while pointer
263       (setq enabled (intern-soft (concat (symbol-name (car pointer))
264                                          "-enabled"))
265             description (intern-soft (concat (symbol-name (car pointer))
266                                              "-description")))
267       (setq point (point))
268       (insert (format "%c %S: %s\n"
269                       (if (not (featurep (car pointer)))
270                           ??
271                         (if (null enabled)
272                             ?!
273                           (if (symbol-value enabled)
274                               ?+
275                             ?-)))
276                       (car pointer)
277                       (if description
278                           (symbol-value description)
279                         "(no description)")))
280       (put-text-property point (point) 'riece-addon (car pointer))
281       (setq pointer (cdr pointer)))
282     (insert "
283 Symbols in the leftmost column:
284
285   +     The add-on is enabled.
286   -     The add-on is disabled.
287   !     The add-on doesn't support enable/disable operation.
288   ?     The add-on status is unknown.
289 ")
290     (insert (substitute-command-keys "
291 Useful keys:
292
293   `\\[riece-command-enable-addon]' to enable the current add-on.
294   `\\[riece-command-disable-addon]' to disable the current add-on.
295 "))
296     (goto-char (point-min))
297     (pop-to-buffer (current-buffer))
298     (delete-other-windows)))
299
300 (defun riece-command-enable-addon (addon)
301   (interactive
302    (list
303     (or (if (eq major-mode 'riece-addon-list-mode)
304             (get-text-property (point) 'riece-addon))
305         (intern-soft
306          (completing-read "Add-on: "
307                           (mapcar (lambda (addon)
308                                     (list (symbol-name addon)))
309                                   riece-addons)
310                           (lambda (pointer)
311                             (let ((enabled
312                                    (intern-soft (concat (car pointer)
313                                                         "-enabled"))))
314                               (and enabled
315                                    (null (symbol-value enabled)))))
316                           t)))))
317   (riece-enable-addon addon t)
318   (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled"))))
319     (if (and (eq major-mode 'riece-addon-list-mode)
320              (get-text-property (point) 'riece-addon)
321              enabled (symbol-value enabled))
322         (save-excursion
323           (beginning-of-line)
324           (let ((point (point))
325                 (inhibit-read-only t)
326                 buffer-read-only)
327             (delete-char 1)
328             (insert "+")
329             (put-text-property point (point) 'riece-addon addon))))))
330
331 (defun riece-command-disable-addon (addon)
332   (interactive
333    (list
334     (or (if (eq major-mode 'riece-addon-list-mode)
335             (get-text-property (point) 'riece-addon))
336         (intern-soft
337          (completing-read "Add-on: "
338                           (mapcar (lambda (addon)
339                                     (list (symbol-name addon)))
340                                   riece-addons)
341                           (lambda (pointer)
342                             (let ((enabled
343                                    (intern-soft (concat (car pointer)
344                                                         "-enabled"))))
345                               (and enabled
346                                    (symbol-value enabled))))
347                           t)))))
348   (riece-disable-addon addon t)
349   (let ((enabled (intern-soft (concat (symbol-name addon) "-enabled"))))
350     (if (and (eq major-mode 'riece-addon-list-mode)
351              (get-text-property (point) 'riece-addon)
352              enabled (null (symbol-value enabled)))
353         (save-excursion
354           (beginning-of-line)
355           (let ((point (point))
356                 (inhibit-read-only t)
357                 buffer-read-only)
358             (delete-char 1)
359             (insert "-")
360             (put-text-property point (point) 'riece-addon addon))))))
361
362 (provide 'riece-addon)
363
364 ;;; riece-addon.el ends here