1
;;; select-xface.el --- Select X-Faces graphically
3
; Created: <97/08/22 12:49:31 teranisi>
4
; Time-stamp: <2002-02-17 23:46:09 teranisi>
6
;; Copyright (C) 1997-2002 Yuuichi Teranishi
8
;; Author: Yuuichi Teranishi <teranisi@gohome.org>
9
;; Maintainer: Yuuichi Teranishi <teranisi@gohome.org>
11
;; Target: Emacs, Mule, XEmacs
13
;; Select X-Face is free software; you can redistribute it and/or modify
14
;; it under the terms of the GNU General Public License as published by
15
;; the Free Software Foundation; either version 2, or
18
;; Select X-Face is distributed in the hope that it will be useful,
19
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
;; GNU General Public License for more details.
23
;; You should have received a copy of the GNU General Public License
24
;; along with GNU Emacs; see the file COPYING. If not, write to the
25
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26
;; Boston, MA 02111-1307, USA.
30
;; (autoload 'select-xface "select-xface" "Select X-Face" t)
33
;; (add-hook 'mew-draft-mode-hook
35
;; (define-key (current-local-map) "\C-c\C-x"
39
;; (add-hook 'gnus-message-setup-hook
41
;; (define-key (current-local-map) "\C-x4x"
44
;; (add-hook 'mh-letter-mode-hook
46
;; (define-key (current-local-map) "\C-x4x"
48
;; For MAIL, RMAIL, VM, cmail, Wanderlust:
49
;; (add-hook 'mail-mode-hook
51
;; (define-key (current-local-map) "\C-x4x"
59
(defconst select-xface-appname "Select X-Face")
60
(defconst select-xface-version-number "v0.15")
61
(defconst select-xface-codename "Live And Let Die")
63
(defconst select-xface-version
64
(concat select-xface-appname " "
65
select-xface-version-number " - \""
66
select-xface-codename "\""))
69
;; Users may set these variables
71
(defvar select-xface-directory "~/.xfaces"
72
"*files or subdirectories in this directory become the candidates.")
74
(defvar select-xface-height 5
75
"*height of the popup buffer.")
77
(defvar select-xface-field-insert-before "^X-Mailer:\\|^User-Agent:\\|^--"
78
"*insert xface field before this regexp.")
80
(defvar select-xface-mode-hook nil
81
"*hooks to be called after select-xface-mode starts.")
83
(defvar select-xface-insert-hook nil
84
"*hooks to be called after xface is inserted.")
86
(defvar select-xface-candidate-regexp "^[^.].*"
87
"*only file/directory names containing that regexp are adopted.")
89
(defvar select-xface-gzip-program "gzip"
92
(defvar select-xface-add-x-face-version-header nil
93
"*insert X-Face-Version header.")
95
(defvar select-xface-header-separator-regexp "\\(^--.*$\\)\\|\\(\n\n\\)"
96
"*header separator. (regexp)")
98
(defvar select-xface-display-func
103
(require 'highlight-headers)
104
(let ((highlight-headers-hack-x-face-p t)
105
(x-face-xmas-like-highlight-headers nil))
106
(highlight-headers beg end nil)))
107
((fboundp 'x-face-decode-message-header)
108
(x-face-decode-message-header beg end))
110
"xface display function.")
113
;; No setting is needed for these variables.
115
(defvar select-xface-candidate-list nil)
116
(defvar select-xface-face-list nil)
117
(defvar select-xface-current-list-pos 0)
118
(defvar select-xface-mode-map nil)
119
(defvar select-xface-parent-buffer nil)
120
(defvar select-xface-orig-window-config nil)
121
(defvar select-xface-popup-menu nil)
122
(defconst select-xface-buffer select-xface-appname)
124
(defmacro select-xface-header-end ()
126
(goto-char (point-min))
127
(if (re-search-forward select-xface-header-separator-regexp nil t)
130
(if select-xface-mode-map
132
(setq select-xface-mode-map (make-sparse-keymap))
133
(define-key select-xface-mode-map "p" 'select-xface-prev-face)
134
(define-key select-xface-mode-map "\C-p" 'select-xface-prev-face)
135
(define-key select-xface-mode-map [up] 'select-xface-prev-face)
136
(define-key select-xface-mode-map "n" 'select-xface-next-face)
137
(define-key select-xface-mode-map "\C-n" 'select-xface-next-face)
138
(define-key select-xface-mode-map [down] 'select-xface-next-face)
139
(define-key select-xface-mode-map "\C-m" 'select-xface-out)
140
(define-key select-xface-mode-map "o" 'select-xface-out)
141
(define-key select-xface-mode-map "q" 'select-xface-exit)
142
(define-key select-xface-mode-map "\C-g" 'select-xface-exit)
143
; (define-key select-xface-mode-map [?a up] 'select-xface-append-out-top)
144
(define-key select-xface-mode-map [?a right] 'select-xface-append-out-last)
145
; (define-key select-xface-mode-map [?a down] 'select-xface-append-out-bottom)
146
(define-key select-xface-mode-map [?a left] 'select-xface-append-out-first)
147
(define-key select-xface-mode-map " " 'select-xface-append-out-last)
148
(define-key select-xface-mode-map "r" 'select-xface-rebuild-face-list)
151
(defun select-xface-make-popup-menu ()
153
(if (not select-xface-popup-menu)
155
select-xface-popup-menu
156
select-xface-mode-map "Menu for Select X-Face"
158
["Prev" select-xface-prev-face t]
159
["Next" select-xface-next-face t]
160
["Output" select-xface-out t]
161
["Append" select-xface-append-out-last t]
163
["Quit" select-xface-exit t]
167
(easy-menu-add select-xface-popup-menu))
170
(defun select-xface-mode ()
171
;;a up select-xface-append-out-top append the current as the top face
172
;;a down select-xface-append-out-bottom append the current as the bottom face
173
"Major mode for select and insert X-Face: field in the current draft buffer.
175
RET select-xface-out output the current face
176
o select-xface-out output the current face
177
C-n select-xface-next-face display the next candidate
178
n select-xface-next-face display the next candidate
179
C-p select-xface-prev-face display the previous candidate
180
p select-xface-prev-face display the previous candidate
181
a right select-xface-append-out-last append the current as the last face
182
a left select-xface-append-out-first append the current as the first face
183
r select-xface-rebuild-face-list rebuild face list
184
SPC select-xface-append-out-last append the current as the last face
185
C-g select-xface-exit quit selecting face
186
q select-xface-exit quit selecting face
189
(setq major-mode 'select-xface-mode)
190
(setq mode-name select-xface-appname)
191
(use-local-map select-xface-mode-map)
192
(make-local-variable 'tab-width)
194
(select-xface-make-popup-menu)
195
(if (featurep 'xemacs)
196
(set-specifier text-cursor-visible-p (cons (current-buffer) nil)))
197
(run-hooks 'select-xface-mode-hook))
199
(defun select-xface-delete-all-xface-related-fields ()
200
"delete all xface related fields in the current buffer."
202
(goto-char (point-min))
203
(while (re-search-forward
204
"^\\(X-Face.*:\\) *\\(.*\\(\n[ \t].*\\)*\\)\n"
205
(select-xface-header-end) t)
206
(delete-region (match-beginning 0) (match-end 0))
207
(goto-char (point-min)))))
210
(defun select-xface-collect-xface-related-fields ()
211
"collect the xface related fields in the current buffer."
212
(let ((end (select-xface-header-end))
215
(goto-char (point-min))
216
(while (re-search-forward
217
"\\(^\\(X-Face-Type:\\)[ \t]*\\(.*\\(\n[ \t].*\\)*\\)\n\\)\\|\\(^\\(X-Face[-0-9]*:\\)[ \t]*\\(.*\\(\n[ \t].*\\)*\\)\n\\)" end t)
218
(setq ret (concat ret
219
(buffer-substring (match-beginning 0)
222
(let ((ret-len (length ret)))
223
(if (string= (substring ret (- ret-len 1) ret-len) "\n")
224
(setq ret (substring ret 0 (- ret-len 1)))))))
227
(defun select-xface-append-out-last ()
228
"append the current face as the last face."
230
(setq this-command 'select-xface-out)
231
(select-xface-insert (select-xface-make-out-xface)
235
(defun select-xface-append-out-first ()
236
"append the current face as the frst face."
238
(setq this-command 'select-xface-out)
239
(select-xface-insert (select-xface-make-out-xface)
242
(defun select-xface-append-out-top ()
243
"append the current face as the top face."
245
(select-xface-insert (select-xface-make-out-xface)
248
(defun select-xface-append-out-bottom ()
249
"append the current face as the bottom face."
251
(select-xface-insert (select-xface-make-out-xface)
254
(defun select-xface-make-append-xface (current-xface
257
"append the current face in the specified direction."
263
(concat "X-Face-Type: geometry=1x2\n"
265
current-xface "\n")))
266
((eq direction 'first)
268
(concat ;; "X-Face-Type: geometry=2x1\n"
271
((eq direction 'bottom)
273
(concat "X-Face-Type: geometry=1x2\n"
274
(if current-xface (concat current-xface "\n"))
278
(concat ;; "X-Face-Type: geometry=2x1\n"
279
(if current-xface (concat current-xface "\n"))
284
(defun select-xface-make-out-xface ()
285
"make output xface string from select-xface buffer."
286
(nth select-xface-current-list-pos select-xface-face-list))
288
(defun select-xface-out ()
289
"output the current face."
291
(select-xface-insert (select-xface-make-out-xface)))
293
(defun select-xface-insert (out-xface &optional append-direction)
294
"insert the xface into the draft buffer."
295
(save-window-excursion
297
(let ((inhibit-read-only t)
298
current-xface beg end
301
(set-buffer select-xface-parent-buffer)
304
(select-xface-collect-xface-related-fields)))
305
(select-xface-delete-all-xface-related-fields)
306
(goto-char (point-min))
307
(re-search-forward select-xface-field-insert-before end t)
311
(insert (select-xface-make-append-xface current-xface
316
(put-text-property beg (point) 'invisible nil)
317
(if (and (fboundp 'x-face-insert-version-header)
318
x-face-add-x-face-version-header)
321
(x-face-insert-version-header)
322
(if (not (eq beg (point)))
324
(put-text-property beg (point) 'invisible nil)
325
(insert (concat " with "
326
select-xface-version "\n")))))
327
(if select-xface-add-x-face-version-header
328
(insert (concat "X-Face-Version: " select-xface-version "\n"))))
329
(run-hooks 'select-xface-insert-hook)
335
(defun select-xface-prev-face ()
336
"display previous candidate."
338
(if (= 0 select-xface-current-list-pos)
339
(select-xface-set-list-pos (1- (length select-xface-face-list)))
340
(select-xface-set-list-pos (1- select-xface-current-list-pos))))
342
(defun select-xface-next-face ()
343
"display next candidate."
345
(if (= select-xface-current-list-pos (1- (length select-xface-face-list)))
346
(select-xface-set-list-pos 0)
347
(select-xface-set-list-pos (1+ select-xface-current-list-pos))))
349
(defun select-xface-set-invisible (string)
350
"set string invisible."
352
(goto-char (point-min))
353
(while (re-search-forward (concat "\\(" string "\\)") nil t)
355
(match-beginning 1) (match-end 1) 'invisible t))))
357
(defun select-xface-set-list-pos (pos)
358
"display nth candidate in the popup buffer."
359
(setq select-xface-current-list-pos pos)
360
; (message (format "%d th entry." pos))
361
(let ((buffer-read-only nil)
362
(entry (nth pos select-xface-face-list))
363
(xface-name (nth pos select-xface-candidate-list))
365
(setq xface-name (if xface-name xface-name "No name"))
369
(if (string-match "^X-Face-Type: .*\n" entry)
374
(funcall select-xface-display-func (point-min) (point-max))
375
(goto-char (point-min))
376
(while (re-search-forward "^X-Face-Type: .*\n" nil t)
377
(delete-region (match-beginning 0) (match-end 0)))
378
(re-search-backward "From:" nil t)
379
(select-xface-set-invisible "X-Face:")
380
(select-xface-set-invisible "From:")
381
(select-xface-set-invisible " ")
386
(funcall select-xface-display-func (point-min) (point-max))
387
(goto-char (point-min))
388
(select-xface-set-invisible "From:")
389
(select-xface-set-invisible " "))
390
(goto-char (point-max))
391
(if (featurep 'xemacs)
392
(goto-char (point-min))
393
(while (char-equal (char-before (point)) ?\n)
395
(set-buffer-modified-p nil)
396
(rename-buffer (concat select-xface-appname "(" xface-name ")"))
397
(setq select-xface-buffer (concat
398
select-xface-appname "(" xface-name ")"))
401
(defun select-xface-check-rgb (list)
402
"check if files in the path are rgb."
403
(let ((tmplist list))
404
(setq tmplist (delete "red" tmplist))
405
(setq tmplist (delete "green" tmplist))
406
(setq tmplist (delete "blue" tmplist))
409
(defun select-xface-make-candidate-list (path)
410
"make file list in the path."
411
(let (ret files attr)
413
(directory-files path nil select-xface-candidate-regexp nil))
414
(setq ret (delete "." ret))
415
(setq ret (delete ".." ret))
418
(setq attr (car files))
419
(if (car (file-attributes (expand-file-name attr path)))
420
(setq ret (delete attr ret)))
421
(setq files (cdr files)))
425
(defun select-xface-reduce-needless-character-in-buffer ()
426
"reduce needless character for xface in the buffer."
427
(if (= 0 (buffer-size))
429
;; reduce a space character in the beginning of the buffer.
430
(while (char-equal (char-after (point-min)) ? )
432
(goto-char (point-min))
434
;; reduce a newline character in the end of the buffer.
435
(while (char-equal (char-before (point-max)) ?\n)
437
(goto-char (1- (point-max)))
440
(defun select-xface-reduce-needless-character-in-string (string)
441
"reduce needless character for xface in the string."
442
(let ((strlen (length string))
446
;; reduce a newline character in the end of the string.
447
(while (and (not (= strlen 0))
448
(string= (substring ret-string (- strlen 1) strlen) "\n"))
449
(setq ret-string (substring ret-string 0 (- strlen 1)))
450
(setq strlen (- strlen 1)))
451
;; reduce a space character in the beginning of the string.
452
(while (and (not (= strlen 0))
453
(string= (substring ret-string 0 1) " "))
454
(setq ret-string (substring ret-string 1 strlen))
455
(setq strlen (- strlen 1))))
459
(defun select-xface-make-xface-string (candidate-name directory)
460
"make xface string from face file."
462
(let ((tmp-buffer (get-buffer-create (concat
464
(expand-file-name candidate-name
468
(coding-system-for-read 'binary)
469
(coding-system-for-write 'binary)
470
format-alist jka-compr-compression-info-list
471
child-clist ret-string)
472
(set-buffer tmp-buffer)
473
(if (not (file-directory-p
474
(expand-file-name candidate-name
477
(if (string-match "\\.xbm\\(\\.gz\\)?$" candidate-name)
479
;; use X-Face utility for xbm or gzipped xbm file.
481
(if (fboundp 'x-face-insert)
482
(let (x-face-add-x-face-version-header)
485
(expand-file-name candidate-name
486
select-xface-directory))
488
(setq select-xface-candidate-list
489
(delete candidate-name
490
select-xface-candidate-list))
492
"%s has illegal contents(ignored)."
494
(select-xface-reduce-needless-character-in-buffer)
496
(message "xbm file is not supported."))
500
(insert-file-contents
501
(expand-file-name candidate-name directory))
502
(if (looking-at "\x1f\x8b") ; gzipped?
504
(point-min) (point-max)
505
select-xface-gzip-program t t nil "-cd"))
506
(select-xface-reduce-needless-character-in-buffer)
507
(goto-char (point-min))
508
(if (not (re-search-forward "X-Face:" nil t))
509
(insert "X-Face: ")))
511
(select-xface-reduce-needless-character-in-string
513
(buffer-substring (point-min)
516
;; recursive subdirectory processing.
518
(setq child-clist (select-xface-make-candidate-list
519
(expand-file-name candidate-name
521
(if (select-xface-check-rgb child-clist)
525
(concat "X-Face-Type: RGB; geometry=1x1\n"
526
(select-xface-make-xface-string
528
(expand-file-name candidate-name directory))))
530
(concat ret-string "\n"
531
(select-xface-make-xface-string
533
(expand-file-name candidate-name directory))))
535
(concat ret-string "\n"
536
(select-xface-make-xface-string
538
(expand-file-name candidate-name directory)))))
539
;; (insert (format "X-Face-Type: geometry=%dx1\n"
540
;; (length child-flist)))
541
;; insert subdirectory candidates recursively.
544
(setq cur-cname (car child-clist))
547
(select-xface-make-xface-string
549
(expand-file-name candidate-name directory))
552
(setq child-clist (cdr child-clist)))
555
(setq ret-string (select-xface-reduce-needless-character-in-string
557
(if (string= ret-string "") (setq ret-string nil))
558
(kill-buffer tmp-buffer)
562
(defun select-xface-add-face-to-face-list (face &optional name)
563
"add face to select-xface-face-list. returns pos."
564
(let ((flist select-xface-face-list)
570
(if (string= face (car flist))
574
(setq flist (cdr flist))
577
(if (and face (not found))
578
(setq select-xface-face-list (append select-xface-face-list
582
(setq select-xface-candidate-list
584
select-xface-candidate-list))
585
(message "Same candidate already exists!"))))
586
(if (not face) (setq pos 0))
589
(defun select-xface-rebuild-face-list ()
590
"rebuild face list from directory."
592
(setq select-xface-candidate-list nil)
593
(setq select-xface-face-list nil)
594
(select-xface-make-face-list)
595
(if select-xface-face-list
596
(select-xface-set-list-pos 0)
597
(message "No X-Face candidates.")
602
(defun select-xface-make-face-list ()
603
"make face list from directory."
604
;; if select-xface-candidate-list is nil, all files or directories
605
;; that contain select-xface-candidate-regexp
606
;; in the select-xface-directory became the candidates.
607
(if (not select-xface-candidate-list)
608
(setq select-xface-candidate-list
609
(select-xface-make-candidate-list select-xface-directory)))
610
;; make face list from candidate list.
611
(if (not select-xface-face-list)
612
(select-xface-make-face-list-from-candidate-list
613
select-xface-candidate-list)))
615
(defun select-xface-make-face-list-from-candidate-list (candidate-list)
616
"make list of faces."
619
(clist candidate-list))
621
(select-xface-add-face-to-face-list
622
(select-xface-make-xface-string
623
(car clist) select-xface-directory)
625
(setq clist (cdr clist)))
626
ret-list ;; return value
629
(defun select-xface-pop-to-buffer (buf)
630
"split current window."
631
(split-window-vertically)
633
(switch-to-buffer buf))
635
(defun select-xface ()
636
"select xfaces graphically."
638
(if (not select-xface-display-func)
639
(message "cannot display X-Face in your environment.")
640
(setq select-xface-parent-buffer (buffer-name))
641
(select-xface-make-face-list)
642
;; add current face to candidate list.
644
(setq pos (select-xface-add-face-to-face-list
645
(select-xface-collect-xface-related-fields)))
646
(if select-xface-face-list
648
(setq select-xface-orig-window-config
649
(current-window-configuration))
650
(if (get-buffer select-xface-buffer)
651
(select-xface-pop-to-buffer select-xface-buffer)
653
(select-xface-pop-to-buffer select-xface-buffer)
654
(enlarge-window (- select-xface-height (window-height)))
655
(select-xface-set-list-pos pos)
658
(message "No X-Face candidates."))
662
(defun select-xface-exit ()
663
"quit selecting xface."
665
(pop-to-buffer select-xface-parent-buffer)
666
(set-window-configuration select-xface-orig-window-config)
667
(kill-buffer select-xface-buffer)
670
(provide 'select-xface)
671
;;; select-xface.el ends here