1 ;;; liece-channel.el --- Various facility for channel operation.
2 ;; Copyright (C) 1998-2000 Daiki Ueno
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
7 ;; Keywords: IRC, liece
9 ;; This file is part of Liece.
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
32 (eval-when-compile (require 'liece-inlines))
34 (eval-when-compile (require 'liece-clfns))
36 (defconst liece-channel-regexp "[+&#!]")
37 (defconst liece-channel-modeless-regexp "[+!]")
39 (defvar liece-default-channel-representation-format "%s+%s")
41 (defconst liece-dcc-channel-representation-format "=%s")
43 (define-widget 'liece-channel-push-button 'push-button
45 :action 'liece-channel-push-button-action)
47 (defun liece-channel-push-button-action (widget &optional event)
48 (let ((chnl (liece-channel-virtual (widget-value widget))))
49 (if (or (liece-channel-member chnl liece-current-channels)
50 (y-or-n-p (format "Do you really join %s? " chnl)))
51 (liece-command-join chnl))))
53 ;;; Reader conventions
54 (defun liece-channel-p (chnl)
57 (concat "^" liece-channel-regexp))
60 (defun liece-channel-modeless-p (chnl)
63 (concat "^" liece-channel-modeless-regexp))
66 (defalias 'liece-channel-equal 'string-equal-ignore-case)
68 (defun liece-channel-member (chnl chnls)
69 "Return non-nil if CHNL is member of CHNLS."
72 (and (stringp item) (liece-channel-equal chnl item)))
75 (defun liece-channel-unread-p (chnl)
76 "Return non-nil if CHNL is unread channel."
79 (and (stringp item) (liece-channel-equal chnl item)))
80 liece-channel-unread-list))
82 (defun liece-channel-get-nicks (&optional chnl)
83 "Return CHNL or current channels's members as list."
84 (get (intern (or chnl liece-current-channel) liece-obarray) 'nick))
86 (defun liece-channel-get-operators (&optional chnl)
87 "Return CHNL or current channels's operators as list."
88 (get (intern (or chnl liece-current-channel) liece-obarray) 'oper))
90 (defun liece-channel-get-voices (&optional chnl)
91 "Return CHNL or current channels's voices as list."
92 (get (intern (or chnl liece-current-channel) liece-obarray) 'voice))
94 (defun liece-channel-get-topic (&optional chnl)
95 "Return CHNL or current channels's topic."
96 (get (intern (or chnl liece-current-channel) liece-obarray) 'topic))
98 (defun liece-channel-get-modes (&optional chnl)
99 "Return CHNL or current channels's mode."
100 (get (intern (or chnl liece-current-channel) liece-obarray)
103 (defun liece-channel-get-bans (&optional chnl)
104 "Return CHNL or current channels's ban list."
105 (get (intern (or chnl liece-current-channel) liece-obarray)
108 (defun liece-channel-get-invites (&optional chnl)
109 "Return CHNL or current channels's invite list."
110 (get (intern (or chnl liece-current-channel) liece-obarray)
113 (defun liece-channel-get-exceptions (&optional chnl)
114 "Return CHNL or current channels's exception list."
115 (get (intern (or chnl liece-current-channel) liece-obarray)
118 ;;; Internal functions
119 (defun liece-channel-remove (chnl chnls)
120 "Remove CHNL from CHNLS."
123 (and (stringp item) (liece-channel-equal chnl item)))
126 (defun liece-channel-delete (chnl chnls)
127 "Delete CHNL from CHNLS."
130 (and (stringp item) (liece-channel-equal chnl item)))
133 (defmacro liece-channel-set-topic (topic &optional chnl)
134 "Set CHNL or current channels's topic."
135 `(put (intern (or ,chnl liece-current-channel) liece-obarray)
138 (defmacro liece-channel-add-mode (mode &optional chnl)
139 "Add MODE as char to CHNL."
140 `(let ((modes (liece-string-to-list (or (liece-channel-get-modes ,chnl)
142 (or (memq ,mode modes)
144 (put (intern (or ,chnl liece-current-channel) liece-obarray)
145 'mode (mapconcat #'char-to-string modes ""))))
147 (defmacro liece-channel-remove-mode (mode &optional chnl)
148 "Remove MODE as char to CHNL."
149 `(let ((modes (liece-string-to-list (or (liece-channel-get-modes ,chnl)
152 (put (intern (or ,chnl liece-current-channel) liece-obarray)
153 'mode (mapconcat #'char-to-string modes ""))))
155 (defmacro liece-channel-set-mode (val mode &optional chnl)
156 "Set character VAL as channel MODE into the CHNL."
158 (liece-channel-add-mode ,mode ,chnl)
159 (liece-channel-remove-mode ,mode ,chnl)))
161 (defmacro liece-channel-add-ban (pattern &optional chnl)
162 "Add ban PATTERN as char to CHNL."
163 `(let ((patterns (liece-channel-get-bans ,chnl)))
164 (or (string-list-member-ignore-case ,pattern patterns)
165 (push ,pattern patterns))
166 (put (intern (or ,chnl liece-current-channel) liece-obarray)
169 (defmacro liece-channel-remove-ban (pattern &optional chnl)
170 "Remove ban PATTERN as char to CHNL."
171 `(let ((patterns (remove-if (lambda (elm) (string-equal ,pattern elm))
172 (liece-channel-get-bans ,chnl))))
173 (put (intern (or ,chnl liece-current-channel) liece-obarray)
176 (defmacro liece-channel-set-ban (chnl pattern val)
177 "Set ban PATTERN as char to CHNL."
179 (liece-channel-add-ban ,pattern ,chnl)
180 (liece-channel-remove-ban ,pattern ,chnl)))
182 (defmacro liece-channel-add-exception (pattern &optional chnl)
183 "Add exception PATTERN as char to CHNL."
184 `(let ((patterns (liece-channel-get-exceptions ,chnl)))
185 (or (string-list-member-ignore-case ,pattern patterns)
186 (push ,pattern patterns))
187 (put (intern (or ,chnl liece-current-channel) liece-obarray)
188 'exception patterns)))
190 (defmacro liece-channel-remove-exception (pattern &optional chnl)
191 "Remove exception PATTERN as char to CHNL."
192 `(let ((patterns (remove-if (lambda (elm) (string-equal ,pattern elm))
193 (liece-channel-get-exceptions ,chnl))))
194 (put (intern (or ,chnl liece-current-channel) liece-obarray)
195 'exception patterns)))
197 (defmacro liece-channel-set-exception (chnl pattern val)
198 "Set exception PATTERN as char to CHNL."
200 (liece-channel-add-exception ,pattern ,chnl)
201 (liece-channel-remove-exception ,pattern ,chnl)))
203 (defmacro liece-channel-add-invite (pattern &optional chnl)
204 "Add invite PATTERN as char to CHNL."
205 `(let ((patterns (liece-channel-get-invites ,chnl)))
206 (or (string-list-member-ignore-case ,pattern patterns)
207 (push ,pattern patterns))
208 (put (intern (or ,chnl liece-current-channel) liece-obarray)
211 (defmacro liece-channel-remove-invite (pattern &optional chnl)
212 "Remove invite PATTERN as char to CHNL."
213 `(let ((patterns (remove-if (lambda (elm) (string-equal ,pattern elm))
214 (liece-channel-get-invites ,chnl))))
215 (put (intern (or ,chnl liece-current-channel) liece-obarray)
218 (defmacro liece-channel-set-invite (chnl pattern val)
219 "Set invite PATTERN as char to CHNL."
221 (liece-channel-add-invite ,pattern ,chnl)
222 (liece-channel-remove-invite ,pattern ,chnl)))
224 (defun liece-channel-virtual (chnl)
225 "Convert channel name into internal representation.
226 \(For example if CHNL is a string \"#...:*\", it will be converted into
228 (let ((mapping liece-channel-conversion-map) match)
230 (if (string-equal-ignore-case (caar mapping) chnl)
231 (setq match (cdar mapping)))
237 ((and (string-match "^[#+]\\(.*\\):\\(.*\\)$" chnl)
238 (string= (match-string 2 chnl)
239 liece-channel-conversion-default-mask))
240 (if (eq ?+ (aref chnl 0))
241 (concat "-" (match-string 1 chnl))
242 (concat "%" (match-string 1 chnl))))
243 ((string= "" chnl) chnl)
244 ; ((eq ?! (aref chnl 0))
245 ; (concat "!" (substring chnl (1+ liece-channel-id-length))))
248 (defun liece-channel-real (chnl)
249 "Convert channel name into external representation.
250 \(For example if CHNL is a string \"%...\", it will be converted into
252 (let ((mapping liece-channel-conversion-map) match)
254 (if (string-equal-ignore-case (cdar mapping) chnl)
255 (setq match (caar mapping)))
259 ((eq ?% (aref chnl 0))
260 (concat "#" (substring chnl 1) ":"
261 liece-channel-conversion-default-mask))
262 ((eq ?- (aref chnl 0))
263 (concat "+" (substring chnl 1) ":"
264 liece-channel-conversion-default-mask))
268 (defun liece-command-toggle-channel-buffer-mode ()
269 "Toggle visibility of channel buffer."
271 (if (get-buffer liece-channel-buffer)
272 (setq liece-channel-buffer-mode (not liece-channel-buffer-mode)))
273 (liece-configure-windows))
275 (defmacro liece-channel-buffer-create (chnl)
276 "Create channel buffer of CHNL."
277 `(with-current-buffer
278 (liece-get-buffer-create (format liece-channel-buffer-format ,chnl))
279 (let (buffer-read-only)
280 (liece-insert-info (current-buffer)
281 (concat (funcall liece-format-time-function
284 (unless (eq major-mode 'liece-channel-mode)
285 (liece-channel-mode))
286 (set-alist 'liece-channel-buffer-alist ,chnl (current-buffer))
289 (defun liece-channel-join-internal (item chnls &optional hints)
290 (let (binding inserted)
291 (if (liece-channel-member item hints)
292 (do ((hint hints (cdr hint)) (chnl chnls (cdr chnl)))
293 ((not (or hint chnl)))
294 (if (and (car hint) (liece-channel-equal (car hint) item))
296 (push (car chnl) binding)))
297 (do ((hint hints (cdr hint)) (chnl chnls (cdr chnl)))
298 ((not (or hint chnl)))
299 (if (and (not inserted)
300 (not (or (car hint) (car chnl))))
304 (push (car chnl) binding))))
305 (or (liece-channel-member item binding)
309 (defun liece-channel-join (chnl &optional nosw)
310 "Initialize channel variables of CHNL.
311 If NOSW is non-nil do not switch to newly created channel."
312 (let ((cbuf (cdr (string-assoc-ignore-case chnl liece-channel-buffer-alist)))
313 (nbuf (cdr (string-assoc-ignore-case chnl liece-nick-buffer-alist))))
315 (setq cbuf (liece-channel-buffer-create chnl)))
317 (setq nbuf (liece-nick-buffer-create chnl)))
318 (if (liece-channel-p (liece-channel-real chnl))
319 (setq liece-current-channels
320 (liece-channel-join-internal
321 chnl liece-current-channels liece-default-channel-binding))
322 (setq liece-current-chat-partners
323 (liece-channel-join-internal chnl liece-current-chat-partners
324 liece-default-partner-binding)))
326 (liece-switch-to-channel chnl)
327 (setq liece-channel-buffer cbuf
328 liece-nick-buffer nbuf))
329 (liece-channel-change)))
331 (defun liece-channel-part-internal (item chnls &optional hints)
335 (if (and chnl (liece-channel-equal item chnl)) nil chnl))
337 (liece-channel-remove item chnls)))
339 (defun liece-channel-part (chnl &optional nosw)
340 "Finalize channel variables of CHNL.
341 If NOSW is non-nil do not switch to newly created channel."
343 ((eq liece-command-buffer-mode 'chat)
344 (setq liece-current-chat-partners
345 (liece-channel-part-internal chnl liece-current-chat-partners
346 liece-default-partner-binding))
348 (liece-channel-switch-to-last liece-current-chat-partners)))
350 (setq liece-current-channels
351 (liece-channel-part-internal chnl liece-current-channels
352 liece-default-channel-binding))
354 (liece-channel-switch-to-last liece-current-channels)))))
356 (defun liece-channel-last (chnls)
357 (car (last (delq nil (copy-sequence chnls)))))
359 (defmacro liece-channel-switch-to-last (chnls)
360 `(let ((chnl (liece-channel-last ,chnls)))
362 (liece-switch-to-channel chnl))
363 (liece-channel-change)))
365 (defun liece-channel-change ()
366 (let ((chnls (if (eq liece-command-buffer-mode 'chat)
367 liece-current-chat-partners
368 liece-current-channels))
371 (with-current-buffer liece-channel-list-buffer
372 (let ((n 1) buffer-read-only)
376 (setq chnl (liece-channel-virtual chnl)
377 string (format "%s,%d:%s" string n chnl))
378 (liece-channel-list-add-button n chnl))
380 (if (string-equal string "")
381 (if (eq liece-command-buffer-mode 'chat)
382 (setq liece-channels-indicator "No partner")
383 (setq liece-channels-indicator "No channel"))
384 (setq liece-channels-indicator (substring string 1)))
385 (liece-set-channel-indicator)
386 (setq chnl (if (eq liece-command-buffer-mode 'chat)
387 liece-current-chat-partner
388 liece-current-channel))
391 (run-hook-with-args 'liece-redisplay-buffer-functions chnl)))
392 (liece-redisplay-unread-mark)
393 (liece-configure-windows)))
395 (defsubst liece-channel-set-operator-1 (chnl user val)
396 (let* ((chnl (intern chnl liece-obarray)) (opers (get chnl 'oper)))
398 (or (string-list-member-ignore-case user opers)
399 (put chnl 'oper (cons user opers)))
400 (if (string-list-member-ignore-case user opers)
401 (put chnl 'oper (string-list-remove-ignore-case user opers))))))
403 (defsubst liece-channel-set-voice-1 (chnl user val)
404 (let* ((chnl (intern chnl liece-obarray)) (voices (get chnl 'voice)))
406 (or (string-list-member-ignore-case user voices)
407 (put chnl 'voice (cons user voices)))
408 (if (string-list-member-ignore-case user voices)
409 (put chnl 'voice (string-list-remove-ignore-case user voices))))))
411 (defun liece-channel-set-operator (chnl user val)
412 (let ((nbuf (cdr (string-assoc-ignore-case chnl liece-nick-buffer-alist)))
414 (liece-channel-set-operator-1 chnl user val)
415 (liece-channel-set-voice-1 chnl user val)
416 (setq user (concat (if val "@" " ") user)
417 xuser (concat (if val "[ +]" "@") (regexp-quote xuser)))
418 (with-current-buffer nbuf
419 (let (buffer-read-only)
420 (goto-char (point-min))
421 (liece-nick-replace xuser user nil t)))))
423 (defun liece-channel-set-voice (chnl user val)
424 (let ((nbuf (cdr (string-assoc-ignore-case chnl liece-nick-buffer-alist)))
426 (liece-channel-set-voice-1 chnl user val)
427 (setq user (concat (if val "+" " ") user)
428 xuser (concat (if val " " "\\+") (regexp-quote xuser)))
429 (with-current-buffer nbuf
430 (let (buffer-read-only)
431 (goto-char (point-min))
432 (liece-nick-replace xuser user nil t)))))
434 (defun liece-channel-prepare-partner (join-channel-var)
435 (setq liece-current-chat-partner
436 (or liece-current-chat-partner join-channel-var))
437 (let ((liece-command-buffer-mode 'chat))
438 (liece-channel-join join-channel-var t))
439 (liece-channel-change))
441 (defun liece-channel-buffer-invisible-p (chnl mode)
442 (let ((cbuf (liece-pick-buffer chnl)))
443 (or (liece-frozen (car cbuf))
445 (not (and (eq liece-command-buffer-mode 'chat)
446 liece-current-chat-partner
447 (string-equal-ignore-case
448 chnl liece-current-chat-partner))))
449 (not (and (eq liece-command-buffer-mode 'channel)
450 liece-current-channel
451 (string-equal-ignore-case
452 chnl liece-current-channel))))))
454 (defun liece-channel-prepare-representation (chnl &optional method name)
457 (format liece-dcc-channel-representation-format chnl))
459 (format liece-default-channel-representation-format name chnl))
462 (defun liece-channel-parse-representation (str)
466 (regexp-quote liece-dcc-channel-representation-format)
469 (vector 'dcc nil (match-string 1 str)))
472 (regexp-quote liece-default-channel-representation-format)
473 "\\([^ ]+\\)" "\\([^ ]+\\)")
475 (vector 'irc (match-string 1 str) (match-string 2 str)))
476 (t (vector 'irc nil str))))
478 (defun liece-channel-list-add-button (n chnl)
479 (insert (format "%2d: " n))
480 (if liece-highlight-mode
483 (liece-widget-convert-button
484 'liece-channel-push-button st (point) chnl))
488 (defun liece-channel-add-buttons (start end)
491 (while (re-search-forward
493 (concat "\\(^\\(" liece-time-prefix-regexp "\\)?"
494 "[][=<>(-][][=<>(-]?\\|\\s-+[+@]?\\)"
495 "\\([&#!%][^ :]*\\)"))
497 ;;(re-search-forward "\\s-+\\(\\)\\([-+]\\S-*\\)" end t)
498 (let* ((chnl-start (match-beginning 3))
499 (chnl-end (match-end 3))
500 (chnl (buffer-substring chnl-start chnl-end)))
501 (when liece-highlight-mode
502 (liece-widget-convert-button
503 'liece-channel-push-button chnl-start chnl-end chnl))))))
506 (defun liece-channel-redisplay-buffer (chnl)
508 (cdr (string-assoc-ignore-case
509 chnl liece-channel-buffer-alist)))
510 (window (liece-get-buffer-window liece-channel-buffer)))
511 (when (liece-channel-unread-p chnl)
512 (setq liece-channel-unread-list
513 (delete chnl liece-channel-unread-list))
514 (run-hook-with-args 'liece-channel-read-functions chnl))
516 (with-current-buffer buffer
517 (set-window-buffer window buffer)
518 (unless (liece-frozen buffer)
519 (set-window-point window (point-max)))
520 (setq liece-channel-buffer buffer)))))
523 (defun liece-channel-list-redisplay-buffer (chnl)
524 (let ((window (liece-get-buffer-window liece-channel-list-buffer)))
526 (save-selected-window
527 (select-window window)
528 (goto-char (point-min))
529 (search-forward chnl nil t)
530 (set-window-point window (match-beginning 0))
531 (when liece-highlight-mode
532 (let ((overlay (make-overlay (point)(match-end 0))))
535 (if (overlay-get ovl 'liece-channel)
536 (delete-overlay ovl))))
537 (overlay-put overlay 'face 'underline)
538 (overlay-put overlay 'liece-channel t)))))))
540 (provide 'liece-channel)
542 ;;; liece-channel.el ends here