ea07bd9466dc4464885f23a70fea39a9cb9897aa
[riece] / lisp / riece-commands.el
1 ;;; riece-commands.el --- commands available in command buffer
2 ;; Copyright (C) 1998-2003 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 (eval-when-compile (require 'riece-inlines))
28
29 (require 'riece-channel)
30 (require 'riece-complete)
31 (require 'riece-display)
32 (require 'riece-version)
33 (require 'riece-server)
34 (require 'riece-misc)
35 (require 'riece-identity)
36 (require 'riece-message)
37
38 ;;; Channel movement:
39 (defun riece-command-switch-to-channel (channel)
40   (interactive
41    (list (completing-read "Channel/user: "
42                           (mapcar #'list riece-current-channels)
43                           nil t)))
44   (riece-switch-to-channel channel)
45   (riece-command-configure-windows))
46
47 (defun riece-command-switch-to-channel-by-number (number)
48   (interactive
49    (let ((command-name (symbol-name this-command)))
50      (if (string-match "[0-9]+$" command-name)
51          (list (string-to-number (match-string 0 command-name)))
52        (list (string-to-number (read-string "Number: "))))))
53   (let ((channels riece-current-channels)
54         (index 1))
55     (while (and channels
56                 (< index number))
57       (if (car channels)
58           (setq index (1+ index)))
59       (setq channels (cdr channels)))
60     (if (car channels)
61         (riece-command-switch-to-channel (car channels))
62       (error "No such number!"))))
63         
64 (eval-and-compile
65   (let ((number 1))
66     (while (<= number 20)
67       (defalias (intern (concat "riece-command-switch-to-channel-by-number-"
68                                 (number-to-string number)))
69         'riece-command-switch-to-channel-by-number)
70       (setq number (1+ number)))))
71
72 (defun riece-command-next-channel ()
73   "Select the next channel."
74   (interactive)
75   (let ((pointer (cdr (string-list-member-ignore-case
76                        riece-current-channel
77                        riece-current-channels))))
78     (while (and pointer
79                 (null (car pointer)))
80       (setq pointer (cdr pointer)))
81     (if (car pointer)
82         (riece-command-switch-to-channel (car pointer))
83       (error "No such channel!"))))
84
85 (defun riece-command-previous-channel ()
86   "Select the previous channel."
87   (interactive)
88   (let ((pointer (string-list-member-ignore-case
89                   riece-current-channel
90                   riece-current-channels))
91         (start riece-current-channels)
92         channel)
93     (while (and start (not (eq start pointer)))
94       (if (car start)
95           (setq channel (car start)))
96       (setq start (cdr start)))
97     (if channel
98         (riece-command-switch-to-channel channel)
99       (error "No such channel!"))))
100
101 (defun riece-command-select-command-buffer ()
102   "Select the command buffer."
103   (interactive)
104   (let ((window (get-buffer-window riece-command-buffer)))
105     (if window
106         (select-window window))))
107
108 (defun riece-command-configure-windows ()
109   (interactive)
110   (riece-redisplay-buffers t))
111
112 (defun riece-command-toggle-channel-buffer-mode ()
113   (interactive)
114   (setq riece-channel-buffer-mode
115         (not riece-channel-buffer-mode))
116   (riece-command-configure-windows))
117
118 (defun riece-command-toggle-user-list-buffer-mode ()
119   (interactive)
120   (setq riece-user-list-buffer-mode
121         (not riece-user-list-buffer-mode))
122   (riece-command-configure-windows))
123
124 (defun riece-command-toggle-channel-list-buffer-mode ()
125   (interactive)
126   (setq riece-channel-list-buffer-mode
127         (not riece-channel-list-buffer-mode))
128   (riece-command-configure-windows))
129
130 (defun riece-get-users-on-server ()
131   (riece-with-server-buffer
132    (let (users)
133      (mapatoms
134       (lambda (atom)
135         (unless (riece-channel-p (symbol-name atom))
136           (push (symbol-name atom) users)))
137       riece-obarray)
138      (if (member riece-real-nickname users)
139          users
140        (cons riece-real-nickname users)))))
141
142 (defun riece-command-finger (user &optional recurse)
143   (interactive
144    (let* ((completion-ignore-case t)
145           (user (completing-read
146                  "User: "
147                  (mapcar #'list (riece-get-users-on-server)))))
148      (list user current-prefix-arg)))
149   (if recurse
150       (riece-send-string (format "WHOIS %s %s\r\n" user user))
151     (riece-send-string (format "WHOIS %s\r\n" user))))
152
153 (defun riece-command-topic (topic)
154   (interactive
155    (list (read-from-minibuffer
156           "Topic: " (cons (or (riece-channel-get-topic
157                                riece-current-channel)
158                               "")
159                           0))))
160   (riece-send-string (format "TOPIC %s :%s\r\n"
161                              (riece-identity-prefix riece-current-channel)
162                              topic)))
163
164 (defun riece-command-invite (&optional user channel)
165   (interactive
166    (let ((completion-ignore-case t)
167          user channel)
168      (if current-prefix-arg
169          (setq channel
170                (completing-read
171                 "Channel: "
172                 (mapcar #'list riece-current-channels))))
173      (list (completing-read
174             "User: "
175             (mapcar #'list (riece-get-users-on-server)))
176            channel)))
177   (if channel
178       (riece-send-string (format "INVITE %s %s\r\n"
179                                  user (riece-identity-prefix channel)))
180     (riece-send-string (format "INVITE %s %s\r\n"
181                                user (riece-identity-prefix
182                                      riece-current-channel)))))
183
184 (defun riece-command-change-mode (channel change)
185   (interactive
186    (let* ((completion-ignore-case t)
187           (channel
188            (if current-prefix-arg
189                (completing-read
190                 "Channel/user: "
191                 (mapcar #'list riece-current-channels))
192              riece-current-channel))
193           (riece-overriding-server-name (riece-identity-server channel))
194           (riece-temp-minibuffer-message
195            (concat "[Available modes: "
196                    (riece-with-server-buffer
197                     (if (and (riece-channel-p channel)
198                              riece-supported-channel-modes)
199                         (apply #'string riece-supported-channel-modes)
200                       (if (and (not (riece-channel-p channel))
201                                riece-supported-user-modes)
202                           (apply #'string riece-supported-user-modes))))
203                    "]")))
204      (list channel
205            (read-from-minibuffer
206             (concat (riece-concat-modes channel "Mode (? for help)") ": ")
207             nil riece-minibuffer-map))))
208   (riece-send-string (format "MODE %s :%s\r\n" channel change)))
209
210 (defun riece-command-set-operators (users &optional arg)
211   (interactive
212    (let ((operators (riece-channel-get-operators riece-current-channel))
213          (completion-ignore-case t)
214          users)
215      (if current-prefix-arg
216          (setq users (riece-completing-read-multiple
217                       "Users"
218                       (mapcar #'list operators)))
219        (setq users (riece-completing-read-multiple
220                     "Users"
221                     (delq nil (mapcar (lambda (user)
222                                         (unless (member user operators)
223                                           (list user)))
224                                       (riece-channel-get-users
225                                        riece-current-channel))))))
226      (list users current-prefix-arg)))
227   (let (group)
228     (while users
229       (push (pop users) group)
230       (if (or (= (length group) 3)
231               (null users))
232           (riece-send-string
233            (format "MODE %s %c%s %s\r\n"
234                    (riece-identity-prefix riece-current-channel)
235                    (if current-prefix-arg
236                        ?-
237                      ?+)
238                    (make-string (length group) ?o)
239                    (mapconcat #'identity group " ")))))))
240
241 (defun riece-command-set-speakers (users &optional arg)
242   (interactive
243    (let ((speakers (riece-channel-get-speakers riece-current-channel))
244          (completion-ignore-case t)
245          users)
246      (if current-prefix-arg
247          (setq users (riece-completing-read-multiple
248                       "Users"
249                       (mapcar #'list speakers)))
250        (setq users (riece-completing-read-multiple
251                     "Users"
252                     (delq nil (mapcar (lambda (user)
253                                         (unless (member user speakers)
254                                           (list user)))
255                                       (riece-channel-get-users
256                                        riece-current-channel))))))
257      (list users current-prefix-arg)))
258   (let (group)
259     (while users
260       (push (pop users) group)
261       (if (or (= (length group) 3)
262               (null users))
263           (riece-send-string
264            (format "MODE %s %c%s %s\r\n"
265                    (riece-identity-prefix riece-current-channel)
266                    (if current-prefix-arg
267                        ?-
268                      ?+)
269                    (make-string (length group) ?v)
270                    (mapconcat #'identity group " ")))))))
271
272 (defun riece-command-send-message (message)
273   "Send MESSAGE to the current channel."
274   (if (equal message "")
275       (error "No text to send"))
276   (unless riece-current-channel
277     (error (substitute-command-keys
278             "Type \\[riece-command-join] to join a channel")))
279   (riece-send-string
280    (format "PRIVMSG %s :%s\r\n"
281            (riece-identity-prefix riece-current-channel)
282            message))
283   (riece-own-channel-message message))
284
285 (defun riece-command-enter-message ()
286   "Send the current line to the current channel."
287   (interactive)
288   (riece-command-send-message (buffer-substring
289                                (riece-line-beginning-position)
290                                (riece-line-end-position)))
291   (let ((next-line-add-newlines t))
292     (next-line 1)))
293
294 (defun riece-command-join-channel (target key)
295   (let ((server-name (riece-identity-server target))
296         process)
297     (if server-name
298         (setq process (cdr (assoc server-name riece-server-process-alist)))
299       (setq process riece-server-process))
300     (unless process
301       (error "%s" (substitute-command-keys
302                    "Type \\[riece-command-open-server] to open server.")))
303     (riece-process-send-string process
304                                (if key
305                                    (format "JOIN %s :%s\r\n"
306                                            (riece-identity-prefix target)
307                                            key)
308                                  (format "JOIN %s\r\n"
309                                          (riece-identity-prefix target))))))
310
311 (defun riece-command-join-partner (target)
312   (let ((pointer (riece-identity-member target riece-current-channels)))
313     (if pointer
314         (riece-command-switch-to-channel (car pointer))
315       (riece-join-channel target)
316       (riece-switch-to-channel target)
317       (riece-redisplay-buffers))))
318
319 (defun riece-command-join (target &optional key)
320   (interactive
321    (let ((completion-ignore-case t)
322          (target
323           (completing-read "Channel/user: "
324                            (mapcar #'list riece-current-channels)))
325          key)
326      (if (and current-prefix-arg
327               (riece-channel-p target))
328          (setq key
329                (riece-read-passwd (format "Key for %s: " target))))
330      (list target key)))
331   (let ((pointer (riece-identity-member target riece-current-channels)))
332     (if pointer
333         (riece-command-switch-to-channel (car pointer))
334       (if (riece-channel-p target)
335           (riece-command-join-channel target key)
336         (riece-command-join-partner target)))))
337
338 (defun riece-command-part-channel (target message)
339   (let ((server-name (riece-identity-server target))
340         process)
341     (if server-name
342         (setq process (cdr (assoc server-name riece-server-process-alist)))
343       (setq process riece-server-process))
344     (unless process
345       (error "%s" (substitute-command-keys
346                    "Type \\[riece-command-open-server] to open server.")))
347     (riece-process-send-string process
348                                (if message
349                                    (format "PART %s :%s\r\n"
350                                            (riece-identity-prefix target)
351                                            message)
352                                  (format "PART %s\r\n"
353                                          (riece-identity-prefix target))))))
354
355 (defun riece-command-part (target &optional message)
356   (interactive
357    (let ((completion-ignore-case t)
358          (target
359           (completing-read "Channel/user: "
360                            (mapcar #'list riece-current-channels)
361                            nil t (cons riece-current-channel 0)))
362          message)
363      (if (and current-prefix-arg
364               (riece-channel-p target))
365          (setq message (read-string "Message: ")))
366      (list target message)))
367   (if (riece-identity-member target riece-current-channels)
368       (if (riece-channel-p target)
369           (riece-command-part-channel target message)
370         (riece-part-channel target)
371         (riece-redisplay-buffers))
372     (error "You are not talking with %s" target)))
373
374 (defun riece-command-change-nickname (nickname)
375   "Change your nickname to NICK."
376   (interactive "sEnter your nickname: ")
377   (riece-send-string (format "NICK %s\r\n" nickname)))
378
379 (defun riece-command-scroll-down (lines)
380   "Scroll LINES down dialogue buffer from command buffer."
381   (interactive "P")
382   (let ((other-window-scroll-buffer
383          (if riece-channel-buffer-mode
384              riece-channel-buffer
385            riece-dialogue-buffer)))
386     (when (get-buffer-window other-window-scroll-buffer)
387       (condition-case nil
388           (scroll-other-window-down lines)
389         (beginning-of-buffer
390          (message "Beginning of buffer"))))))
391
392 (defun riece-command-scroll-up (lines)
393   "Scroll LINES up dialogue buffer from command buffer."
394   (interactive "P")
395   (let* ((other-window-scroll-buffer
396           (if riece-channel-buffer-mode
397               riece-channel-buffer
398             riece-dialogue-buffer)))
399     (when (get-buffer-window other-window-scroll-buffer)
400       (condition-case nil
401           (scroll-other-window lines)
402         (end-of-buffer
403          (message "End of buffer"))))))
404
405 (defun riece-command-nick-scroll-down (lines)
406   "Scroll LINES down nick buffer from command buffer."
407   (interactive "P")
408   (let ((other-window-scroll-buffer riece-user-list-buffer))
409     (when (get-buffer-window other-window-scroll-buffer)
410       (condition-case nil
411           (scroll-other-window-down lines)
412         (beginning-of-buffer
413          (message "Beginning of buffer"))))))
414
415 (defun riece-command-nick-scroll-up (lines)
416   "Scroll LINES up nick buffer from command buffer."
417   (interactive "P")
418   (let* ((other-window-scroll-buffer riece-user-list-buffer))
419     (when (get-buffer-window other-window-scroll-buffer)
420       (condition-case nil
421           (scroll-other-window lines)
422         (end-of-buffer
423          (message "End of buffer"))))))
424
425 (defun riece-command-toggle-away (&optional message)
426   "Mark yourself as being away."
427   (interactive
428    (if current-prefix-arg
429        (let ((message (read-string "Away message: ")))
430          (list message))))
431   (if message
432       (riece-send-string (format "AWAY :%s\r\n" message))
433     (riece-send-string "AWAY\r\n")))
434
435 (defun riece-command-toggle-freeze (&optional arg)
436   "Prevent automatic scrolling of the dialogue window.
437 If prefix argument ARG is non-nil, toggle frozen status."
438   (interactive "P")
439   (riece-freeze (if riece-channel-buffer-mode
440                     riece-channel-buffer
441                   riece-dialogue-buffer)
442                 (if arg (prefix-numeric-value arg))))
443
444 (defun riece-command-toggle-own-freeze (&optional arg)
445   "Prevent automatic scrolling of the dialogue window.
446 The difference from `riece-command-freeze' is that your messages are hidden.
447 If prefix argument ARG is non-nil, toggle frozen status."
448   (interactive "P")
449   (riece-own-freeze (if riece-channel-buffer-mode
450                         riece-channel-buffer
451                       riece-dialogue-buffer)
452                     (if arg (prefix-numeric-value arg))))
453
454 (defun riece-command-quit (&optional arg)
455   "Quit IRC."
456   (interactive "P")
457   (if (y-or-n-p "Really quit IRC? ")
458       (let ((message
459              (if arg
460                  (read-string "Message: ")
461                (or riece-quit-message
462                    (riece-extended-version)))))
463         (riece-close-all-server message))))
464
465 (defun riece-command-raw (command)
466   "Enter raw IRC command, which is sent to the server."
467   (interactive "sIRC command: ")
468   (riece-send-string (concat command "\r\n")))
469
470 (defun riece-command-end-of-buffer ()
471   "Get end of the dialogue buffer."
472   (interactive)
473   (let (buffer window)
474     (setq buffer (if riece-channel-buffer-mode
475                      riece-channel-buffer
476                    riece-dialogue-buffer))
477     (or (setq window (get-buffer-window buffer))
478         (setq window (get-buffer-window riece-dialogue-buffer)
479               buffer riece-dialogue-buffer))
480     (when window
481       (save-selected-window
482         (select-window window)
483         (goto-char (point-max))))))
484
485 (defun riece-command-copy-region (start end)
486   "Move current region between START and END to `kill-ring'."
487   (interactive "r")
488   (kill-new (buffer-substring-no-properties start end)))
489
490 (defun riece-command-open-server (server-name)
491   (interactive
492    (list (completing-read "Server: " riece-server-alist)))
493   (let ((process (riece-start-server
494                   (riece-server-name-to-server server-name)
495                   server-name)))
496     (with-current-buffer (process-buffer process)
497       (setq riece-server-name server-name))
498     (push (cons server-name process) riece-server-process-alist)))
499
500 (defun riece-command-close-server (server-name &optional message)
501   (interactive
502    (list (completing-read "Server: " riece-server-process-alist)
503          (if current-prefix-arg
504              (read-string "Message: ")
505            (or riece-quit-message
506                (riece-extended-version)))))
507   (riece-close-server server-name message))
508
509 (defun riece-command-universal-server-name-argument ()
510   (interactive)
511   (let* ((riece-overriding-server-name
512           (completing-read "Server: "
513                            riece-server-process-alist))
514          (command
515           (key-binding (read-key-sequence
516                         (format "Command to execute on \"%s\":"
517                                 riece-overriding-server-name)))))
518     (message "")
519     (call-interactively command)))
520
521 (provide 'riece-commands)
522
523 ;;; riece-commands.el ends here