Initial Commit
[packages] / xemacs-packages / erc / erc-stamp.el
1 ;;; erc-stamp.el --- Timestamping for Emacs IRC CLient
2
3 ;; Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
4
5 ;; Author: Mario Lang <mlang@delysid.org>
6 ;; Keywords: comm, processes, timestamp
7 ;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?ErcStamp
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs 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)
14 ;; any later version.
15
16 ;; GNU Emacs 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.
20
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., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
25
26 ;;; Commentary:
27
28 ;; The code contained in this module is responsible for inserting
29 ;; timestamps into ERC buffers.  In order to actually activate this,
30 ;; you must call `erc-timestamp-mode'.
31
32 ;; You can choose between two different ways of inserting timestamps.
33 ;; Customize `erc-insert-timestamp-function' and
34 ;; `erc-insert-away-timestamp-function'.
35
36 ;;; Code:
37
38 (require 'erc)
39 (require 'erc-compat)
40
41 (defconst erc-stamp-version "$Revision: 1.45.2.4 $"
42   "ERC stamp mode revision.")
43
44 (defgroup erc-stamp nil
45   "For long conversation on IRC it is sometimes quite
46 useful to have individual messages timestamp.  This
47 group provides settings related to the format and display
48 of timestamp information in `erc-mode' buffer.
49
50 For timestamping to be activated, you just need to load `erc-stamp'
51 in your .emacs file or interactively using `load-library'."
52   :group 'erc)
53
54 (defcustom erc-timestamp-format "[%H:%M]"
55   "*If set to a string, messages will be timestamped.
56 This string is processed using `format-time-string'.
57 Good examples are \"%T\" and \"%H:%M\".
58
59 If nil, timestamping is turned off."
60   :group 'erc-stamp
61   :type '(choice (const nil)
62                  (string)))
63
64 (defcustom erc-insert-timestamp-function 'erc-insert-timestamp-right
65   "*Function to use to insert timestamps.
66
67 It takes a single argument STRING which is the final string
68 which all text-properties already appended.  This function only cares about
69 inserting this string at the right position.  Narrowing is in effect
70 while it is called, so (point-min) and (point-max) determine the region to
71 operate on."
72   :group 'erc-stamp
73   :type '(choice (const :tag "Right" erc-insert-timestamp-right)
74                  (const :tag "Left" erc-insert-timestamp-left)
75                  function))
76
77 (defcustom erc-away-timestamp-format "<%H:%M>"
78   "*Timestamp format used when marked as being away.
79
80 If nil, timestamping is turned off when away unless `erc-timestamp-format'
81 is set.
82
83 If `erc-timestamp-format' is set, this will not be used."
84   :group 'erc-stamp
85   :type '(choice (const nil)
86                  (string)))
87
88 (defcustom erc-insert-away-timestamp-function 'erc-insert-timestamp-right
89   "*Function to use to insert the away timestamp.
90
91 See `erc-insert-timestamp-function' for details."
92   :group 'erc-stamp
93   :type '(choice (const :tag "Right" erc-insert-timestamp-right)
94                  (const :tag "Left" erc-insert-timestamp-left)
95                  function))
96
97 (defcustom erc-hide-timestamps nil
98   "*If non-nil, timestamps will be invisible.
99
100 This is useful for logging, because, although timestamps will be
101 hidden, they will still be present in the logs."
102   :group 'erc-stamp
103   :type 'boolean)
104
105 (defcustom erc-echo-timestamps nil
106   "*If non-nil, print timestamp in the minibuffer when point is moved.
107 Using this variable, you can turn off normal timestamping,
108 and simply move point to an irc message to see its timestamp
109 printed in the minibuffer."
110   :group 'erc-stamp
111   :type 'boolean)
112
113 (defcustom erc-echo-timestamp-format "Timestamped %A, %H:%M:%S"
114   "*Format string to be used when `erc-echo-timestamps' is non-nil.
115 This string specifies the format of the timestamp being echoed in
116 the minibuffer."
117   :group 'erc-stamp
118   :type 'string)
119
120 (defcustom erc-timestamp-intangible t
121   "*Whether the timestamps should be intangible, i.e. prevent the point
122 from entering them and instead jump over them."
123   :group 'erc-stamp
124   :type 'boolean)
125
126 (defface erc-timestamp-face '((t (:bold t :foreground "green")))
127   "ERC timestamp face."
128   :group 'erc-faces)
129
130 ;;;###autoload (autoload 'erc-timestamp-mode "erc-stamp" nil t)
131 (define-erc-module stamp timestamp
132   "This mode timestamps messages in the channel buffers."
133   ((add-hook 'erc-mode-hook 'erc-munge-invisibility-spec)
134    (add-hook 'erc-insert-modify-hook 'erc-add-timestamp t)
135    (add-hook 'erc-send-modify-hook 'erc-add-timestamp t))
136   ((remove-hook 'erc-mode-hook 'erc-munge-invisibility-spec)
137    (remove-hook 'erc-insert-modify-hook 'erc-add-timestamp)
138    (remove-hook 'erc-send-modify-hook 'erc-add-timestamp)))
139
140 (defun erc-add-timestamp ()
141   "Add timestamp and text-properties to message.
142
143 This function is meant to be called from `erc-insert-modify-hook'
144 or `erc-send-modify-hook'."
145   (unless (get-text-property (point) 'invisible)
146     (let ((ct (current-time)))
147       (if (fboundp erc-insert-timestamp-function)
148           (funcall erc-insert-timestamp-function
149                    (erc-format-timestamp ct erc-timestamp-format))
150         (error "Timestamp function unbound"))
151       (when (and (fboundp erc-insert-away-timestamp-function)
152                  erc-away-timestamp-format
153                  (with-current-buffer (erc-server-buffer) erc-away)
154                  (not erc-timestamp-format))
155         (funcall erc-insert-away-timestamp-function
156                  (erc-format-timestamp ct erc-away-timestamp-format)))
157       (add-text-properties (point-min) (point-max)
158                            (list 'timestamp ct))
159       (add-text-properties (point-min) (point-max)
160                            (list 'point-entered 'erc-echo-timestamp)))))
161
162 (defvar erc-timestamp-last-inserted nil
163   "Last timestamp inserted into the buffer.")
164 (make-variable-buffer-local 'erc-timestamp-last-inserted)
165
166 (defcustom erc-timestamp-only-if-changed-flag t
167   "*Insert timestamp only if its value changed since last insertion.
168 If `erc-insert-timestamp-function' is `erc-insert-timestamp-left', a
169 string of spaces which is the same size as the timestamp is added to
170 the beginning of the line in its place. If you use
171 `erc-insert-timestamp-right', nothing gets inserted in place of the
172 timestamp."
173   :group 'erc-stamp
174   :type 'boolean)
175
176 (defcustom erc-timestamp-right-column nil
177   "*If non-nil, the column at which the timestamp is inserted,
178 if the timestamp is to be printed to the right.  If nil,
179 `erc-insert-timestamp-right' will use other means to determine
180 the correct column."
181   :group 'erc-stamp
182   :type '(choice
183           (integer :tag "Column number")
184           (const :tag "Unspecified" nil)))
185
186 (defcustom erc-timestamp-use-align-to (and (not (featurep 'xemacs))
187                                            (>= emacs-major-version 22)
188                                            (eq window-system 'x))
189   "*If non-nil, use the :align-to display property to align the stamp.
190 This gives better results when variable-width characters (like
191 Asian language characters and math symbols) precede a timestamp.
192 Unfortunately, it only works in Emacs 22 and when using the X
193 Window System.
194
195 A side effect of enabling this is that there will only be one
196 space before a right timestamp in any saved logs."
197   :group 'erc-stamp
198   :type 'boolean)
199
200 (defun erc-insert-timestamp-left (string)
201   "Insert timestamps at the beginning of the line."
202   (goto-char (point-min))
203   (let* ((ignore-p (and erc-timestamp-only-if-changed-flag
204                         (string-equal string erc-timestamp-last-inserted)))
205          (len (length string))
206          (s (if ignore-p (make-string len ? ) string)))
207     (unless ignore-p (setq erc-timestamp-last-inserted string))
208     (erc-put-text-property 0 len 'field 'erc-timestamp s)
209     (insert s)))
210
211 (defun erc-insert-aligned (string pos)
212   "Insert STRING at the POSth column.
213
214 If `erc-timestamp-use-align-to' is t, use the :align-to display
215 property to get to the POSth column."
216   (if (not erc-timestamp-use-align-to)
217       (indent-to pos)
218     (insert " ")
219     (put-text-property (1- (point)) (point) 'display
220                        (list 'space ':align-to pos)))
221   (insert string))
222
223 (defun erc-insert-timestamp-right (string)
224   "Insert timestamp on the right side of the screen.
225 STRING is the timestamp to insert.  The function is a possible value
226 for `erc-insert-timestamp-function'.
227
228 If `erc-timestamp-only-if-changed-flag' is nil, a timestamp is always
229 printed.  If this variable is non-nil, a timestamp is only printed if
230 it is different from the last.
231
232 If `erc-timestamp-right-column' is set, its value will be used as the
233 column at which the timestamp is to be printed.  If it is nil, and
234 `erc-fill-mode' is active, then the timestamp will be printed just
235 before `erc-fill-column'.  Otherwise, if the current buffer is
236 shown in a window, that window's width is used.  If the buffer is
237 not shown, and `fill-column' is set, then the timestamp will be
238 printed just `fill-column'.  As a last resort, the timestamp will
239 be printed just before the window-width."
240   (unless (and erc-timestamp-only-if-changed-flag
241                (string-equal string erc-timestamp-last-inserted))
242     (setq erc-timestamp-last-inserted string)
243     (goto-char (point-max))
244     (forward-char -1);; before the last newline
245     (let* ((current-window (get-buffer-window (current-buffer)))
246            (str-width (string-width string))
247            (pos (cond
248                  (erc-timestamp-right-column erc-timestamp-right-column)
249                  ((and (boundp 'erc-fill-mode)
250                        erc-fill-mode
251                        (boundp 'erc-fill-column)
252                        erc-fill-column)
253                   (1+ (- erc-fill-column str-width)))
254                  (fill-column
255                   (1+ (- fill-column str-width)))
256                  (t
257                   (- (window-width) str-width 1))))
258            (from (point))
259            (col (current-column))
260            indent)
261       ;; The following is a kludge used to calculate whether to move
262       ;; to the next line before inserting a stamp.  It allows for
263       ;; some margin of error if what is displayed on the line differs
264       ;; from the number of characters on the line.
265       (setq col (+ col (ceiling (/ (- col (- (point) (point-at-bol))) 1.6))))
266       (if (< col pos)
267           (erc-insert-aligned string pos)
268         (newline)
269         (indent-to pos)
270         (setq from (point))
271         (insert string))
272       (erc-put-text-property from (1+ (point)) 'field 'erc-timestamp)
273       (erc-put-text-property from (1+ (point)) 'rear-nonsticky t)
274       (when erc-timestamp-intangible
275         (erc-put-text-property from (1+ (point)) 'intangible t)))))
276
277 ;; for testing: (setq erc-timestamp-only-if-changed-flag nil)
278
279 (defun erc-format-timestamp (time format)
280   "Return TIME formatted as string according to FORMAT.
281 Return the empty string if FORMAT is nil."
282   (if format
283       (let ((ts (format-time-string format time)))
284         (erc-put-text-property 0 (length ts) 'face 'erc-timestamp-face ts)
285         (erc-put-text-property 0 (length ts) 'invisible 'timestamp ts)
286         (erc-put-text-property 0 (length ts)
287                                'isearch-open-invisible 'timestamp ts)
288         ;; N.B. Later use categories instead of this harmless, but
289         ;; inelegant, hack. -- BPT
290         (when erc-timestamp-intangible
291           (erc-put-text-property 0 (length ts) 'intangible t ts))
292         ts)
293     ""))
294
295 ;; This function is used to munge `buffer-invisibility-spec to an
296 ;; appropriate value. Currently, it only handles timestamps, thus its
297 ;; location.  If you add other features which affect invisibility,
298 ;; please modify this function and move it to a more appropriate
299 ;; location.
300 (defun erc-munge-invisibility-spec ()
301   (if erc-hide-timestamps
302       (setq buffer-invisibility-spec
303             (if (listp buffer-invisibility-spec)
304                 (cons 'timestamp buffer-invisibility-spec)
305               (list 't 'timestamp)))
306     (setq buffer-invisibility-spec
307           (if (listp buffer-invisibility-spec)
308               (remove 'timestamp buffer-invisibility-spec)
309             (list 't)))))
310
311 (defun erc-hide-timestamps ()
312   "Hide timestamp information from display."
313   (interactive)
314   (setq erc-hide-timestamps t)
315   (erc-munge-invisibility-spec))
316
317 (defun erc-show-timestamps ()
318   "Show timestamp information on display.
319 This function only works if `erc-timestamp-format' was previously
320 set, and timestamping is already active."
321   (interactive)
322   (setq erc-hide-timestamps nil)
323   (erc-munge-invisibility-spec))
324
325 (defun erc-echo-timestamp (before now)
326   "Print timestamp text-property of an IRC message.
327 Argument BEFORE is where point was before it got moved and
328 NOW is position of point currently."
329   (when erc-echo-timestamps
330     (let ((stamp (get-text-property now 'timestamp)))
331       (when stamp
332         (message (format-time-string erc-echo-timestamp-format
333                                      stamp))))))
334
335 (provide 'erc-stamp)
336
337 ;;; erc-stamp.el ends here
338 ;;
339 ;; Local Variables:
340 ;; indent-tabs-mode: t
341 ;; tab-width: 8
342 ;; End:
343
344 ;; arch-tag: 57aefab4-63e0-4c48-91d5-6efa145487e0