comparison lisp/printer.el @ 491:b3bbdc4058d7

[xemacs-hg @ 2001-04-30 09:26:20 by ben] fix mswindows printer support Implement headers and footers. Implement calling Print dialog box (#### but it doesn't quite work yet).
author ben
date Mon, 30 Apr 2001 09:26:20 +0000
parents 576fb035e263
children 98fb34b6fbe9
comparison
equal deleted inserted replaced
490:38fb9ae12edd 491:b3bbdc4058d7
66 Under Windows, use `mswindows-printer-list' to get names of installed 66 Under Windows, use `mswindows-printer-list' to get names of installed
67 printers." 67 printers."
68 :type 'string 68 :type 'string
69 :group 'printing) 69 :group 'printing)
70 70
71 (defcustom printer-page-header '(date buffer-name) 71 (defstruct Print-context pageno window start-time printer-name)
72
73 (defcustom printer-page-header '((face bold date) nil (face bold buffer-name))
72 "*Controls printed page header. 74 "*Controls printed page header.
73
74 #### not yet implemented.
75 75
76 This can be: 76 This can be:
77 - nil. Header is not printed. 77 - nil. Header is not printed.
78 - An fbound symbol or lambda expression. The function is called with 78 - An fbound symbol or lambda expression. The function is called with
79 one parameter, a print-context object, every time the headers need 79 one parameter, a print-context object, every time the headers need
80 to be set up. It can use the function `print-context-property' to 80 to be set up. It can use the function `print-context-property' to
81 query the properties of this object. The return value is treated as 81 query the properties of this object. The return value is treated as
82 if it was literally specified: i.e. it will be reprocessed. 82 if it was literally specified: i.e. it will be reprocessed.
83 - A list of up to three elements, for left, center and right portions 83 - A list of up to three elements, for left, center and right portions
84 of the header. Each of these can be 84 of the header. Each of these can be
85 - nil, not to print the portion 85 - nil, not to print the portion
86 - A string, which will be printed literally. 86 - A string, which will be printed literally.
87 - A predefined symbol, on of the following: 87 - A predefined symbol, on of the following:
88 printer-name Name of printer being printed to
88 short-file-name File name only, no path 89 short-file-name File name only, no path
89 long-file-name File name with its path 90 long-file-name File name with its path
90 buffer-name Buffer name 91 buffer-name Buffer name
91 date Date current when printing started 92 date Date current when printing started
92 time Time current when printing started 93 time Time current when printing started
93 page Current printout page number, 1-based 94 page Current printout page number, 1-based
94 user-id User logon id 95 user-id User logon id
95 user-name User full name 96 user-name User full name
97 - A list of three elements: (face FACE-NAME EXPR). EXPR is any of the
98 items given here. The item will be displayed in the given face.
96 - A cons of an extent and any of the items given here. The item will 99 - A cons of an extent and any of the items given here. The item will
97 be displayed using the extent's face, begin-glyph and end-glyph 100 be displayed using the extent's face, begin-glyph and end-glyph
98 properties. 101 properties.
99 - A list, each element of which is any of the items given here. 102 - A list, each element of which is any of the items given here.
100 Each element of the list is rendered in sequence. For example, 103 Each element of the list is rendered in sequence. For example,
103 a print-context object, as above. The return value is treated as 106 a print-context object, as above. The return value is treated as
104 if it was literally specified: i.e. it will be reprocessed." 107 if it was literally specified: i.e. it will be reprocessed."
105 :type 'sexp 108 :type 'sexp
106 :group 'printing) 109 :group 'printing)
107 110
108 (defcustom printer-page-footer '(nil page) 111 (defcustom printer-page-footer '(nil (face bold ("Page " page)))
109 "*Controls printed page footer. 112 "*Controls printed page footer.
110
111 #### not yet implemented.
112 113
113 Format is the same as `printer-page-header'." 114 Format is the same as `printer-page-header'."
114 :type 'sexp 115 :type 'sexp
115 :group 'printing) 116 :group 'printing)
116 117
118 (defun generate-header-element (element context)
119 (cond ((null element) nil)
120 ((stringp element) (insert element))
121 ((memq element '(printer-name
122 short-file-name long-file-name buffer-name
123 date time page user-id user-name))
124 (insert (print-context-property context element)))
125 ((and (consp element) (eq 'face (car element)))
126 (let ((p (point)))
127 (generate-header-element (third element) context)
128 (let ((x (make-extent p (point))))
129 (set-extent-face x (second element)))))
130 ((and (consp element) (extentp (car element)))
131 (let ((p (point)))
132 (generate-header-element (cdr element) context)
133 (let ((x (make-extent p (point))))
134 (set-extent-face x (extent-face (car element)))
135 (set-extent-begin-glyph x (extent-begin-glyph (car element)))
136 (set-extent-end-glyph x (extent-end-glyph (car element))))))
137 ((listp element)
138 (mapcar #'(lambda (el) (generate-header-element el context))
139 element))
140 ((functionp element)
141 (generate-header-element (funcall element context) context))
142 (t (error 'invalid-argument "Unknown header element" element))))
143
144 (defun generate-header-line (spec context)
145 (let* ((left (first spec))
146 (middle (second spec))
147 (right (third spec))
148 (left-start (point))
149 (middle-start (progn (generate-header-element left context)
150 (point)))
151 (right-start (progn (generate-header-element middle context)
152 (point)))
153 (right-end (progn (generate-header-element right context)
154 (point)))
155 (left-width (- middle-start left-start))
156 (middle-width (- right-start middle-start))
157 (right-width (- right-end right-start))
158 (winwidth (- (window-width (Print-context-window context)) 1))
159 (spaces1 (max (- (/ (- winwidth middle-width) 2) left-width) 0))
160 (spaces2 (max (- (- winwidth right-width)
161 (+ left-width spaces1 middle-width))
162 0)))
163 (goto-char right-start)
164 (insert-char ?\ spaces2)
165 (goto-char middle-start)
166 (insert-char ?\ spaces1)))
167
117 (defun print-context-property (print-context prop) 168 (defun print-context-property (print-context prop)
118 "Return property PROP of PRINT-CONTEXT. 169 "Return property PROP of PRINT-CONTEXT.
119 170
120 Valid properties are 171 Valid properties are
121 172
122 print-buffer Buffer being printed. 173 print-buffer Buffer being printed
123 print-window Window on printer device containing print buffer. 174 print-window Window on printer device containing print buffer
124 print-frame Frame on printer device corresponding to current page. 175 print-frame Frame on printer device corresponding to current page
125 print-device Device referring to printer. 176 print-device Device referring to printer
126 printer-name Name of printer being printed to. 177 print-start-time Time current when printing started (`current-time' format)
178 print-page Current printout page number, 1-based
179 printer-name Name of printer being printed to
127 short-file-name File name only, no path 180 short-file-name File name only, no path
128 long-file-name File name with its path 181 long-file-name File name with its path
129 buffer-name Buffer name 182 buffer-name Buffer name
130 date Date current when printing started 183 date Date current when printing started (as a string)
131 time Time current when printing started 184 time Time current when printing started (as a string)
132 page Current printout page number, 1-based 185 page Current printout page number, 1-based (as a string)
133 user-id User logon id 186 user-id User logon id (as a string)
134 user-name User full name" 187 user-name User full name"
135 (error "not yet implemented")) 188 (let* ((window (Print-context-window print-context))
136 189 (pageno (Print-context-pageno print-context))
137 (defun generic-print-buffer (&optional buffer) 190 (start-time (Print-context-start-time print-context))
191 (printer-name (Print-context-printer-name print-context))
192 (buffer (window-buffer window)))
193 (case prop
194 (print-buffer buffer)
195 (print-window window)
196 (print-frame (window-frame window))
197 (print-device (frame-device (window-frame window)))
198 (print-start-time start-time)
199 (print-page pageno)
200 (printer-name printer-name)
201 (short-file-name (let ((name (buffer-file-name buffer)))
202 (if name (file-name-nondirectory name) "")))
203 (long-file-name (let ((name (buffer-file-name buffer)))
204 (or name "")))
205 (buffer-name (buffer-name buffer))
206 (date (format-time-string "%x" start-time))
207 (time (format-time-string "%X" start-time))
208 (page (format "%d" pageno))
209 (user-id (format "%d" (user-uid)))
210 (user-name (format "%d" (user-login-name)))
211 (t (error 'invalid-argument "Unrecognized print-context property"
212 prop)))))
213
214 (defun generic-print-buffer (&optional buffer display-print-dialog)
138 "Print buffer BUFFER using a printing method appropriate to the O.S. being run. 215 "Print buffer BUFFER using a printing method appropriate to the O.S. being run.
139 Under Unix, `lpr' is normally used to spool out a no-frills version of the 216 Under Unix, `lpr' is normally used to spool out a no-frills version of the
140 buffer, or the `ps-print' package is used to pretty-print the buffer to a 217 buffer, or the `ps-print' package is used to pretty-print the buffer to a
141 PostScript printer. Under MS Windows, the built-in printing support is used. 218 PostScript printer. Under MS Windows, the built-in printing support is used.
142 219
220 If DISPLAY-PRINT-DIALOG is t, the print dialog will first be
221 displayed, allowing the user to select various printing settings
222 \(e.g. which printer to print to, the range of pages, number of copies,
223 modes such landscape/portrait/2-up/4-up [2 or 4 (small!) logical pages
224 per physical page], etc.). At this point the user can cancel the printing
225 operation using the dialog box, and `generic-print-buffer' will not print
226 anything. When called interactively, use a prefix arg to suppress the
227 display of the print dialog box.
228
143 If BUFFER is nil or omitted, the current buffer is used." 229 If BUFFER is nil or omitted, the current buffer is used."
144 (interactive) 230 ;; #### for some reason, displaying a dialog box makes the printing
145 (generic-print-region (point-min buffer) (point-max buffer) buffer)) 231 ;; fail unless y-or-n-p is called (see below). when this is fixed,
146 232 ;; remove one of the calls to `not' in the following line.
147 (defun generic-print-region (start end &optional buffer) 233 (interactive (list nil (not (not current-prefix-arg))))
234 (if (or (not (valid-specifier-tag-p 'msprinter))
235 (not display-print-dialog))
236 (generic-print-region (point-min buffer) (point-max buffer) buffer)
237 (let* ((d (make-device 'msprinter printer-name))
238 (props (make-dialog-box 'print :device d)))
239 (and props (generic-print-region (point-min buffer)
240 (point-max buffer) buffer
241 d props)))))
242
243 (defun generic-print-region (start end &optional buffer print-device props)
148 "Print region using a printing method appropriate to the O.S. being run. 244 "Print region using a printing method appropriate to the O.S. being run.
149 The region between START and END of BUFFER (defaults to the current 245 The region between START and END of BUFFER (defaults to the current
150 buffer) is printed. 246 buffer) is printed.
151 247
152 Under Unix, `lpr' is normally used to spool out a no-frills version of the 248 Under Unix, `lpr' is normally used to spool out a no-frills version of the
153 buffer, or the `ps-print' package is used to pretty-print the buffer to a 249 buffer, or the `ps-print' package is used to pretty-print the buffer to a
154 PostScript printer. Under MS Windows, the built-in printing support is used." 250 PostScript printer. Under MS Windows, the built-in printing support is used.
251
252 Optional PRINT-DEVICE is a device, already created, to use to do the
253 printing. This is typically used when this function was invoked from
254 `generic-print-buffer' and it displayed a dialog box. That function created
255 the device, and then the dialog box stuffed it with the user's selections
256 of how the buffer should be printed.
257
258 PROPS, if given, is typically the plist returned from the call to
259 `make-dialog-box' that displayed the Print box. It contains properties
260 relevant to us when we print.
261
262 Recognized properties are the same as those in `make-dialog-box':
263
264 name Printer device name. If omitted, the current system-selected
265 printer will be used.
266 from-page First page to print, 1-based. If omitted, printing starts from
267 the beginning.
268 to-page Last page to print, inclusive, If omitted, printing ends at
269 the end.
270 copies Number of copies to print. If omitted, one copy is printed."
155 (cond ((valid-specifier-tag-p 'msprinter) 271 (cond ((valid-specifier-tag-p 'msprinter)
156 (let (d f) 272 (let (d f header-buffer footer-buffer)
157 (setq buffer (decode-buffer buffer)) 273 (setq buffer (decode-buffer buffer))
158 (unwind-protect 274 (unwind-protect
159 (progn 275 (progn
160 (setq d (make-device 'msprinter printer-name)) 276 (setq d (or print-device
277 (make-device 'msprinter printer-name)))
161 (setq f (make-frame 278 (setq f (make-frame
162 (list* 'name (concat (substitute ?_ ?. 279 (list* 'name (concat
163 (buffer-name buffer)) 280 (substitute ?_ ?. (buffer-name buffer))
164 " - XEmacs") 281 " - XEmacs")
165 '(menubar-visible-p nil 282 '(menubar-visible-p
283 nil
166 has-modeline-p nil 284 has-modeline-p nil
167 default-toolbar-visible-p nil 285 default-toolbar-visible-p nil
168 default-gutter-visible-p nil 286 default-gutter-visible-p nil
169 minibuffer none 287 minibuffer none
170 modeline-shadow-thickness 0 288 modeline-shadow-thickness 0
172 horizontal-scrollbar-visible-p nil)) 290 horizontal-scrollbar-visible-p nil))
173 d)) 291 d))
174 (let* ((w (frame-root-window f)) 292 (let* ((w (frame-root-window f))
175 (vertdpi (cdr (device-system-metric d 'device-dpi))) 293 (vertdpi (cdr (device-system-metric d 'device-dpi)))
176 (pixel-vertical-clip-threshold (/ vertdpi 2)) 294 (pixel-vertical-clip-threshold (/ vertdpi 2))
177 (last-end 0) 295 (from-page (plist-get props 'from-page 1))
178 done) 296 (to-page (plist-get props 'to-page))
179 (set-window-buffer w (or buffer (current-buffer))) 297 (copies (plist-get props 'copies 1))
180 (set-window-start w start) 298 (context (make-Print-context
181 (while (not done) 299 :start-time (current-time)
182 (redisplay-frame f) 300 ;; #### bogus! we need accessors for
183 (print-job-eject-page f) 301 ;; print-settings objects.
184 (let ((this-end (window-end w)) 302 :printer-name
185 (pixvis (window-last-line-visible-height w))) 303 (or (plist-get props 'name)
186 ;; in case we get stuck somewhere, bow out 304 printer-name
187 ;; rather than printing an infinite number of 305 (mswindows-get-default-printer))))
188 ;; pages. #### this will fail with an image 306 header-window
189 ;; bigger than an entire page. but we really 307 footer-window)
190 ;; need this check here. we should be more 308
191 ;; clever in our check, to deal with this case. 309 (when printer-page-header
192 (if (or (= this-end last-end) 310 (let ((window-min-height 2))
193 ;; #### fuckme! window-end returns a value 311 (setq header-window w)
194 ;; outside of the valid range of buffer 312 (setq w (split-window w 2)))
195 ;; positions!!! 313 (setq header-buffer (generate-new-buffer " *header*"))
196 (>= this-end end)) 314 (set-window-buffer header-window header-buffer))
197 (setq done t) 315
198 (setq last-end this-end) 316 (when printer-page-footer
199 (set-window-start w this-end) 317 (let ((window-min-height 2))
200 (if pixvis 318 (setq footer-window
201 (save-selected-window 319 (split-window w (- (window-height w) 2))))
202 (select-window w) 320 (setq footer-buffer (generate-new-buffer " *footer*"))
203 ;; #### scroll-down should take a window arg. 321 (set-window-buffer footer-window footer-buffer))
204 (let ((window-pixel-scroll-increment pixvis)) 322
205 (scroll-down 1))))))))) 323 (setf (Print-context-window context) w)
324
325 ;; loop, printing one copy of document per loop
326 (while (> copies 0)
327 (let ((last-end 0) ; bufpos at end of previous page
328 reached-end ; t if we've reached the end of the
329 ; text we're printing
330 (pageno 1))
331 (set-window-buffer w buffer)
332 (set-window-start w start)
333
334 ;; loop, printing one page per loop
335 (while (and (not reached-end)
336 ;; stop at end of region of text or
337 ;; outside of ranges of pages given
338 (or (not to-page) (<= pageno to-page)))
339
340 (setf (Print-context-pageno context) pageno)
341
342 ;; only actually print the page if it's in the
343 ;; range.
344 (when (>= pageno from-page)
345 ;; none of these work.
346 ; (mapcar #'(lambda (foo)
347 ; (redisplay-device foo t))
348 ; (delete-if #'(lambda (foo)
349 ; (eq (device-type foo)
350 ; 'msprinter))
351 ; (device-list)))
352 ; (mapcar #'(lambda (foo)
353 ; (redraw-device foo t))
354 ; (delete-if #'(lambda (foo)
355 ; (eq (device-type foo)
356 ; 'msprinter))
357 ; (device-list)))
358 ; (sit-for 0.01)
359 ;; but this one sure as hell does.
360 ; (y-or-n-p "continue")
361 (when printer-page-header
362 (with-current-buffer header-buffer
363 (erase-buffer)
364 (generate-header-line printer-page-header
365 context)
366 (goto-char (point-min))
367 (set-window-start header-window (point-min))))
368
369 (when printer-page-footer
370 (with-current-buffer footer-buffer
371 (erase-buffer)
372 (insert "\n")
373 (generate-header-line printer-page-footer
374 context)
375 (goto-char (point-min))
376 (set-window-start footer-window (point-min))))
377
378 (redisplay-frame f)
379 (print-job-eject-page f)
380 )
381 ;; but use the GUARANTEE argument to `window-end'
382 ;; so that we get the right value even if we
383 ;; didn't do a redisplay.
384 (let ((this-end (window-end w t))
385 (pixvis (window-last-line-visible-height w)))
386 ;; in case we get stuck somewhere, bow out
387 ;; rather than printing an infinite number of
388 ;; pages. #### this will fail with an image
389 ;; bigger than an entire page. but we really
390 ;; need this check here. we should be more
391 ;; clever in our check, to deal with this case.
392 (if (or (= this-end last-end)
393 ;; #### fuckme! window-end returns a value
394 ;; outside of the valid range of buffer
395 ;; positions!!!
396 (>= this-end end))
397 (setq reached-end t)
398 (setq last-end this-end)
399 (set-window-start w this-end)
400 (if pixvis
401 (save-selected-window
402 (select-window w)
403 ;; #### scroll-down should take a
404 ;; window arg.
405 (let ((window-pixel-scroll-increment
406 pixvis))
407 (scroll-down 1))))))
408 (setq pageno (1+ pageno))))
409 (setq copies (1- copies)))))
206 (and f (delete-frame f)) 410 (and f (delete-frame f))
207 (and d (delete-device d)) 411 (and d (delete-device d))
412 (and header-buffer (kill-buffer header-buffer))
413 (and footer-buffer (kill-buffer footer-buffer))
208 ))) 414 )))
209 ((and (not (eq system-type 'windows-nt)) 415 ((and (not (eq system-type 'windows-nt))
210 (fboundp 'lpr-buffer)) 416 (fboundp 'lpr-buffer))
211 (lpr-region buffer)) 417 (lpr-region buffer))
212 (t (error "No print support available")))) 418 (t (error "No print support available"))))