diff lisp/erc/erc-speedbar.el @ 68451:fc745b05e928

Revision: emacs@sv.gnu.org/emacs--devo--0--patch-22 Creator: Michael Olson <mwolson@gnu.org> Install ERC.
author Miles Bader <miles@gnu.org>
date Sun, 29 Jan 2006 13:08:58 +0000
parents
children 87ff4350c1ce
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/erc/erc-speedbar.el	Sun Jan 29 13:08:58 2006 +0000
@@ -0,0 +1,369 @@
+;;; erc-speedbar.el --- Speedbar support for ERC
+
+;; Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+;; Author: Mario Lang <mlang@delysid.org>
+;; Contributor: Eric M. Ludlam <eric@siege-engine.com>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This module provides integration of ERC into the Speedbar.
+
+;;; TODO / ideas:
+
+;; * Write intelligent update function:
+;;   update-channel, update-nick, remove-nick-from-channel, ...
+;; * Use indicator-strings for op/voice
+;; * Extract/convert face notes field from bbdb if available and show
+;;   it using sb-image.el
+;;
+;;; Code:
+
+(require 'erc)
+(require 'speedbar)
+(condition-case nil (require 'dframe) (error nil))
+
+;;; Customization:
+
+(defgroup erc-speedbar nil
+  "Integration of ERC in the Speedbar"
+  :group 'erc)
+
+(defcustom erc-speedbar-sort-users-type 'activity
+  "How channel nicknames are sorted.
+
+'activity     - Sort users by channel activity
+'alphabetical - Sort users alphabetically
+nil           - Do not sort users"
+  :group 'erc-speedbar
+  :type '(choice (const :tag "Sort users by channel activity" activity)
+		 (const :tag "Sort users alphabetically" alphabetical)
+		 (const :tag "Do not sort users" nil)))
+
+(defvar erc-speedbar-key-map nil
+  "Keymap used when in erc display mode.")
+
+(defun erc-install-speedbar-variables ()
+  "Install those variables used by speedbar to enhance ERC."
+  (if erc-speedbar-key-map
+      nil
+    (setq erc-speedbar-key-map (speedbar-make-specialized-keymap))
+
+    ;; Basic tree features
+    (define-key erc-speedbar-key-map "e" 'speedbar-edit-line)
+    (define-key erc-speedbar-key-map "\C-m" 'speedbar-edit-line)
+    (define-key erc-speedbar-key-map "+" 'speedbar-expand-line)
+    (define-key erc-speedbar-key-map "=" 'speedbar-expand-line)
+    (define-key erc-speedbar-key-map "-" 'speedbar-contract-line))
+
+  (speedbar-add-expansion-list '("ERC" erc-speedbar-menu-items
+				 erc-speedbar-key-map
+				 erc-speedbar-server-buttons))
+  (speedbar-add-mode-functions-list
+   '("ERC" (speedbar-item-info . erc-speedbar-item-info))))
+
+(defvar erc-speedbar-menu-items
+  '(["Goto buffer" speedbar-edit-line t]
+    ["Expand Node" speedbar-expand-line
+     (save-excursion (beginning-of-line)
+		     (looking-at "[0-9]+: *.\\+. "))]
+    ["Contract Node" speedbar-contract-line
+     (save-excursion (beginning-of-line)
+		     (looking-at "[0-9]+: *.-. "))])
+  "Additional menu-items to add to speedbar frame.")
+
+;; Make sure our special speedbar major mode is loaded
+(if (featurep 'speedbar)
+    (erc-install-speedbar-variables)
+  (add-hook 'speedbar-load-hook 'erc-install-speedbar-variables))
+
+;;; ERC hierarchy display method
+;;;###autoload
+(defun erc-speedbar-browser ()
+  "Initialize speedbar to display an ERC browser.
+This will add a speedbar major display mode."
+  (interactive)
+  (require 'speedbar)
+  ;; Make sure that speedbar is active
+  (speedbar-frame-mode 1)
+  ;; Now, throw us into Info mode on speedbar.
+  (speedbar-change-initial-expansion-list "ERC")
+  (speedbar-get-focus))
+
+(defun erc-speedbar-buttons (buffer)
+  "Create buttons for speedbar in BUFFER."
+  (erase-buffer)
+  (let (serverp chanp)
+    (with-current-buffer buffer
+      (setq serverp (eq buffer (process-buffer erc-server-process)))
+      (setq chanp (erc-channel-p (erc-default-target)))
+      (setq queryp (erc-query-buffer-p)))
+    (cond (serverp
+	   (erc-speedbar-channel-buttons nil 0 buffer))
+	  (chanp
+	   (erc-speedbar-insert-target buffer 0)
+	   (forward-line -1)
+	   (erc-speedbar-expand-channel "+" buffer 0))
+	  (queryp
+	   (erc-speedbar-insert-target buffer 0))
+	  (t (ignore)))))
+
+(defun erc-speedbar-server-buttons (directory depth)
+  "Insert the initial list of servers you are connected to."
+  (let ((servers (erc-buffer-list
+		  (lambda ()
+		    (eq (current-buffer)
+			(process-buffer erc-server-process))))))
+    (when servers
+      (speedbar-with-writable
+	(dolist (server servers)
+	  (speedbar-make-tag-line
+	   'bracket ?+ 'erc-speedbar-expand-server server
+	   (buffer-name server) 'erc-speedbar-goto-buffer server nil
+	   depth))
+	t))))
+
+(defun erc-speedbar-expand-server (text server indent)
+  (cond ((string-match "+" text)
+	 (speedbar-change-expand-button-char ?-)
+	 (if (speedbar-with-writable
+	       (save-excursion
+		 (end-of-line) (forward-char 1)
+		 (erc-speedbar-channel-buttons nil (1+ indent) server)))
+	     (speedbar-change-expand-button-char ?-)
+	   (speedbar-change-expand-button-char ??)))
+	((string-match "-" text)	;we have to contract this node
+	 (speedbar-change-expand-button-char ?+)
+	 (speedbar-delete-subblock indent))
+	(t (error "Ooops... not sure what to do")))
+  (speedbar-center-buffer-smartly))
+
+(defun erc-speedbar-channel-buttons (directory depth server-buffer)
+  (when (get-buffer server-buffer)
+    (let* ((proc (with-current-buffer server-buffer erc-server-process))
+	   (targets (erc-buffer-list
+		     (lambda ()
+		       (not (eq (process-buffer erc-server-process)
+				(current-buffer))))
+		     proc)))
+      (when targets
+	(speedbar-with-writable
+	  (dolist (target targets)
+	    (erc-speedbar-insert-target target depth))
+	  t)))))
+
+(defun erc-speedbar-insert-target (buffer depth)
+  (if (with-current-buffer buffer
+	(erc-channel-p (erc-default-target)))
+      (speedbar-make-tag-line
+       'bracket ?+ 'erc-speedbar-expand-channel buffer
+       (buffer-name buffer) 'erc-speedbar-goto-buffer buffer nil
+       depth)
+    ;; Query target
+    (speedbar-make-tag-line
+     nil nil nil nil
+     (buffer-name buffer) 'erc-speedbar-goto-buffer buffer nil
+     depth)))
+
+(defun erc-speedbar-expand-channel (text channel indent)
+  "For the line matching TEXT, in CHANNEL, expand or contract a line.
+INDENT is the current indentation level."
+  (cond
+   ((string-match "+" text)
+    (speedbar-change-expand-button-char ?-)
+    (speedbar-with-writable
+     (save-excursion
+       (end-of-line) (forward-char 1)
+       (let ((modes (with-current-buffer channel
+		      (concat (apply 'concat
+				     erc-channel-modes)
+			      (cond
+			       ((and erc-channel-user-limit
+				     erc-channel-key)
+				(if erc-show-channel-key-p
+				    (format "lk %.0f %s"
+					    erc-channel-user-limit
+					    erc-channel-key)
+				  (format "kl %.0f" erc-channel-user-limit)))
+			       (erc-channel-user-limit
+				;; Emacs has no bignums
+				(format "l %.0f" erc-channel-user-limit))
+			       (erc-channel-key
+				(if erc-show-channel-key-p
+				    (format "k %s" erc-channel-key)
+				  "k"))
+			       (t "")))))
+	     (topic (erc-controls-interpret
+		     (with-current-buffer channel erc-channel-topic))))
+	 (speedbar-make-tag-line
+	  'angle ?i nil nil
+	  (concat "Modes: +" modes) nil nil nil
+	  (1+ indent))
+	 (unless (string= topic "")
+	   (speedbar-make-tag-line
+	    'angle ?i nil nil
+	    (concat "Topic: " topic) nil nil nil
+	    (1+ indent)))
+	 (let ((names (cond ((eq erc-speedbar-sort-users-type 'alphabetical)
+			     (erc-sort-channel-users-alphabetically
+			      (with-current-buffer channel
+				(erc-get-channel-user-list))))
+			    ((eq erc-speedbar-sort-users-type 'activity)
+			     (erc-sort-channel-users-by-activity
+			      (with-current-buffer channel
+				(erc-get-channel-user-list))))
+			    (t (with-current-buffer channel
+				 (erc-get-channel-user-list))))))
+	   (when names
+	     (speedbar-with-writable
+	      (dolist (entry names)
+		(erc-speedbar-insert-user entry ?+ (1+ indent))))))))))
+   ((string-match "-" text)
+    (speedbar-change-expand-button-char ?+)
+    (speedbar-delete-subblock indent))
+   (t (error "Ooops... not sure what to do")))
+  (speedbar-center-buffer-smartly))
+
+(defun erc-speedbar-insert-user (entry exp-char indent)
+  "Insert one user based on the channel member list ENTRY.
+EXP-CHAR is the expansion character to use.
+INDENT is the current indentation level."
+  (let* ((user (car entry))
+	 (cuser (cdr entry))
+	 (nick (erc-server-user-nickname user))
+	 (host (erc-server-user-host user))
+	 (info (erc-server-user-info user))
+	 (login (erc-server-user-login user))
+	 (name (erc-server-user-full-name user))
+	 (voice (and cuser (erc-channel-user-voice cuser)))
+	 (op (and cuser (erc-channel-user-op cuser)))
+	 (nick-str (concat (if op "@" "") (if voice "+" "") nick))
+	 (finger (concat login (when (or login host) "@") host))
+	 (sbtoken (list finger name info)))
+    (if (or login host name info) ; we want to be expandable
+	(speedbar-make-tag-line
+	 'bracket ?+ 'erc-speedbar-expand-user sbtoken
+	 nick-str nil sbtoken nil
+	 indent)
+      (when (equal exp-char ?-)
+	(forward-line -1)
+	(erc-speedbar-expand-user "+" (list finger name info) indent))
+      (speedbar-make-tag-line
+       'statictag ?? nil nil
+       nick-str nil nil nil
+       indent))))
+
+(defun erc-speedbar-update-channel (buffer)
+  "Update the speedbar information about a ERC buffer. The update
+is only done when the channel is actually expanded already."
+  ;; This is only a rude hack and doesn't care about multiserver usage
+  ;; yet, consider this a brain storming, better ideas?
+  (with-current-buffer speedbar-buffer
+    (save-excursion
+      (goto-char (point-min))
+      (when (re-search-forward (concat "^1: *.+. *"
+				       (regexp-quote (buffer-name buffer)))
+			       nil t)
+	(beginning-of-line)
+	(speedbar-delete-subblock 1)
+	(erc-speedbar-expand-channel "+" buffer 1)))))
+
+(defun erc-speedbar-expand-user (text token indent)
+  (cond ((string-match "+" text)
+	 (speedbar-change-expand-button-char ?-)
+	 (speedbar-with-writable
+	   (save-excursion
+	     (end-of-line) (forward-char 1)
+	     (let ((finger (nth 0 token))
+		   (name (nth 1 token))
+		   (info (nth 2 token)))
+	       (when finger
+		 (speedbar-make-tag-line
+		  nil nil nil nil
+		  finger nil nil nil
+		  (1+ indent)))
+	       (when name
+		 (speedbar-make-tag-line
+		  nil nil nil nil
+		  name nil nil nil
+		  (1+ indent)))
+	       (when info
+		 (speedbar-make-tag-line
+		  nil nil nil nil
+		  info nil nil nil
+		  (1+ indent)))))))
+	((string-match "-" text)
+	 (speedbar-change-expand-button-char ?+)
+	 (speedbar-delete-subblock indent))
+	(t (error "Ooops... not sure what to do")))
+  (speedbar-center-buffer-smartly))
+
+(defun erc-speedbar-goto-buffer (text buffer indent)
+  "When user clicks on TEXT, goto an ERC buffer.
+The INDENT level is ignored."
+  (if (featurep 'dframe)
+      (progn
+	(dframe-select-attached-frame speedbar-frame)
+	(let ((bwin (get-buffer-window buffer 0)))
+	  (if bwin
+	      (progn
+		(select-window bwin)
+		(raise-frame (window-frame bwin)))
+	    (if dframe-power-click
+		(let ((pop-up-frames t))
+		  (select-window (display-buffer buffer)))
+	      (dframe-select-attached-frame speedbar-frame)
+	      (switch-to-buffer buffer)))))
+    (let ((bwin (get-buffer-window buffer 0)))
+      (if bwin
+	  (progn
+	    (select-window bwin)
+	    (raise-frame (window-frame bwin)))
+	(if speedbar-power-click
+	    (let ((pop-up-frames t)) (select-window (display-buffer buffer)))
+	  (select-frame speedbar-attached-frame)
+	  (switch-to-buffer buffer))))))
+
+(defun erc-speedbar-line-text ()
+  "Return the text for the item on the current line."
+  (beginning-of-line)
+  (when (re-search-forward "[]>] " nil t)
+    (buffer-substring-no-properties (point) (point-at-eol))))
+
+(defun erc-speedbar-item-info ()
+  "Display information about the current buffer on the current line."
+  (let ((data (speedbar-line-token))
+	(txt (erc-speedbar-line-text)))
+    (cond ((and data (listp data))
+	   (message "%s: %s" txt (car data)))
+	  ((bufferp data)
+	   (message "Channel: %s" txt))
+	  (t
+	   (message "%s" txt)))))
+
+(provide 'erc-speedbar)
+;;; erc-speedbar.el ends here
+;;
+;; Local Variables:
+;; indent-tabs-mode: t
+;; tab-width: 8
+;; End:
+
+;; arch-tag: 7a6558a4-3308-4bf5-a284-e1d042c933c6