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