6 # XSnap, X-Windows Snapshot. A GUI for the ImageMagick import command
8 # Software design, John Cristy (magick@dupont.com), March 1996
10 # Copyright (C) 1999-2010 ImageMagick Studio LLC, a non-profit organization
11 # dedicated to making software imaging solutions freely available.
13 # This software and documentation is provided "as is," and the copyright
14 # holders and contributing author(s) make no representations or warranties,
15 # express or implied, including but not limited to, warranties of
16 # merchantability or fitness for any particular purpose or that the use of
17 # the software or documentation will not infringe any third party patents,
18 # copyrights, trademarks or other rights.
20 # The copyright holders and contributing author(s) will not be held liable
21 # for any direct, indirect, special or consequential damages arising out of
22 # any use of the software or documentation, even if advised of the
23 # possibility of such damage.
25 # Permission is hereby granted to use, copy, modify, and distribute this
26 # source code, or portions hereof, documentation and executables, for any
27 # purpose, without fee, subject to the following restrictions:
29 # 1. The origin of this source code must not be misrepresented.
30 # 2. Altered versions must be plainly marked as such and must not be
31 # misrepresented as being the original source.
32 # 3. This Copyright notice may not be removed or altered from any source
33 # or altered source distribution.
35 # The copyright holders and contributing author(s) specifically permit,
36 # without fee, and encourage the use of this source code as a component for
37 # supporting image processing in commercial products. If you use this
38 # source code in a product, acknowledgment is not required but would be
43 # Create an alert window and display a message to the user.
45 proc Alert {dograb message args} {
47 # Initialize alert window.
49 catch {destroy .alert}
50 toplevel .alert -class alert
52 wm iconname .alert alert
56 +[expr {[winfo width .]+[winfo x .]+100}]+[expr {[winfo y .]+75}]
58 # Create alert window frame.
60 frame .alert.top -relief raised -border 1
61 frame .alert.bottom -relief raised -border 1
62 pack append .alert .alert.top {top fill expand} .alert.bottom \
64 message .alert.top.message -width 350 -justify left -text $message
65 pack append .alert.top .alert.top.message {top expand padx 5 pady 5}
66 if {[llength $args] > 0} {
68 # Create as many buttons as needed and arrange them from left to right.
70 set arg [lindex $args 0]
71 frame .alert.bottom.0 -relief sunken -border 1
72 pack append .alert.bottom .alert.bottom.0 {left expand padx 10 pady 10}
73 button .alert.bottom.0.button -text [lindex $arg 0] \
74 -command "[lindex $arg 1]; destroy .alert"
75 pack append .alert.bottom.0 .alert.bottom.0.button {expand padx 12 pady 12}
76 bind .alert <Return> "[lindex $arg 1]; destroy .alert"
79 foreach arg [lrange $args 1 end] {
80 button .alert.bottom.$i -text [lindex $arg 0] \
81 -command "[lindex $arg 1]; destroy .alert"
82 pack append .alert.bottom .alert.bottom.$i {left expand padx 20}
86 bind .alert <Any-Enter> [list focus .alert]
87 if {$dograb == "grab"} {
88 tkwait visibility .alert
96 # Proc AppendImageFormat appends the image format type to the filename.
98 proc AppendImageFormat {w} {
100 [$w.format.list get [lindex [$w.format.list curselection] 0]]
101 set filename [$w.file.entry get]
102 set extension [file extension $filename]
103 $w.file.entry delete \
104 [expr {[string length $filename]-[string length $extension]}] end
105 $w.file.entry insert end .
106 $w.file.entry insert end $snap(format)
110 # Proc Options creates the options window.
114 # Initialize snap window.
116 catch {destroy .options}
117 toplevel .options -class Options
118 wm title .options "Set Image Options"
120 wm transient .options .
121 wm geometry .options \
122 +[expr {[winfo width .]+[winfo x .]+25}]+[winfo y .]
124 # Create options window frame.
126 frame .options.input_title
127 label .options.input_title.label -text "Input"
128 pack .options.input_title.label
129 pack .options.input_title
130 frame .options.input -relief sunken -borderwidth 2
131 frame .options.input.checks
132 checkbutton .options.input.checks.border -text "Borders" -width 11 \
133 -anchor w -variable snap(border)
134 checkbutton .options.input.checks.frame -text "Frame" -width 11 \
135 -anchor w -variable snap(frame)
136 checkbutton .options.input.checks.screen -text "Screen" -width 11 \
137 -anchor w -variable snap(screen)
138 checkbutton .options.input.checks.descend -text "Descend" -anchor w \
139 -variable snap(descend)
140 pack .options.input.checks.border .options.input.checks.frame \
141 .options.input.checks.screen .options.input.checks.descend -side left
142 pack .options.input.checks
143 frame .options.input.delay
144 label .options.input.delay.label -text "Delay:" -width 9 -anchor w
145 scale .options.input.delay.scale -orient horizontal -length 11c \
146 -from 0 -to 120 -tickinterval 15 -variable snap(delay)
147 pack .options.input.delay.label .options.input.delay.scale -side left
148 pack .options.input.delay
149 frame .options.input.id
150 label .options.input.id.window -text "Window:" -width 9 -anchor w
151 entry .options.input.id.window_entry -width 18 -relief sunken \
152 -textvariable snap(window)
153 label .options.input.id.display -text "Display:"
154 entry .options.input.id.display_entry -width 18 -relief sunken \
155 -textvariable snap(display)
156 pack .options.input.id.window .options.input.id.window_entry \
157 .options.input.id.display .options.input.id.display_entry -side left
158 pack .options.input.checks .options.input.delay .options.input.id \
160 pack .options.input.id -pady 1m
161 pack .options.input -expand 1 -fill both
162 frame .options.processing_title
163 label .options.processing_title.label -text "Image Processing"
164 pack .options.processing_title.label
165 pack .options.processing_title
166 frame .options.processing -relief sunken -borderwidth 2
167 frame .options.processing.checks
168 checkbutton .options.processing.checks.dither -text "Dither" -width 11 \
169 -anchor w -variable snap(dither)
170 checkbutton .options.processing.checks.negate -text "Negate" -width 11 \
171 -anchor w -variable snap(negate)
172 checkbutton .options.processing.checks.monochrome -text "Monochrome" \
173 -width 11 -anchor w -variable snap(monochrome)
174 checkbutton .options.processing.checks.trim -text "Trim" -anchor w \
176 pack .options.processing.checks.dither .options.processing.checks.negate \
177 .options.processing.checks.monochrome .options.processing.checks.trim \
179 pack .options.processing.checks
180 frame .options.processing.colors
181 label .options.processing.colors.label -text "Colors:" -width 9 -anchor w
182 scale .options.processing.colors.scale -orient horizontal -length 11c \
183 -from 0 -to 256 -tickinterval 32 -variable snap(colors)
184 pack .options.processing.colors.label .options.processing.colors.scale \
186 pack .options.processing.colors
187 frame .options.processing.rotate
188 label .options.processing.rotate.label -text "Rotate:" -width 9 -anchor w
189 scale .options.processing.rotate.scale -orient horizontal -length 11c \
190 -from 0 -to 360 -tickinterval 45 -variable snap(degrees)
191 pack .options.processing.rotate.label .options.processing.rotate.scale \
193 pack .options.processing.rotate
194 pack .options.processing.checks .options.processing.colors \
195 .options.processing.rotate -padx 1m -anchor w
196 pack .options.processing -expand 1 -fill both
197 frame .options.output_title
198 label .options.output_title.label -text "Output"
199 pack .options.output_title.label
200 pack .options.output_title
201 frame .options.output -relief sunken -borderwidth 2
202 frame .options.output.checks
203 checkbutton .options.output.checks.compress -text "Compress" -width 11 \
204 -anchor w -variable snap(compress)
205 checkbutton .options.output.checks.interlace -text "Interlace" -width 11 \
206 -anchor w -variable snap(interlace)
207 checkbutton .options.output.checks.verbose -text "Verbose" -anchor w \
208 -variable snap(verbose)
209 pack .options.output.checks.compress .options.output.checks.interlace \
210 .options.output.checks.verbose -side left
211 pack .options.output.checks
212 frame .options.output.scene
213 label .options.output.scene.label -text "Scene:" -width 9 -anchor w
214 scale .options.output.scene.scale -orient horizontal -length 11c \
215 -from 0 -to 40 -tickinterval 5 -variable snap(scene)
216 pack .options.output.scene.label .options.output.scene.scale -side left
217 pack .options.output.scene
218 frame .options.output.comment
219 label .options.output.comment.label -text "Comment:" -width 9 -anchor w
220 entry .options.output.comment.entry -width 45 -relief sunken \
221 -textvariable snap(comment)
222 pack .options.output.comment.label .options.output.comment.entry \
224 pack .options.output.comment
225 frame .options.output.label
226 label .options.output.label.label -text "Label:" -width 9 -anchor w
227 entry .options.output.label.entry -width 45 -relief sunken \
228 -textvariable snap(label)
229 pack .options.output.label.label .options.output.label.entry -side left
230 pack .options.output.label
231 frame .options.output.id
232 label .options.output.id.page -text "Page:" -width 9 -anchor w
233 entry .options.output.id.page_entry -width 18 -relief sunken \
234 -textvariable snap(page)
235 label .options.output.id.density -text "Density:"
236 entry .options.output.id.density_entry -width 18 -relief sunken \
237 -textvariable snap(density)
238 pack .options.output.id.page .options.output.id.page_entry \
239 .options.output.id.density .options.output.id.density_entry -side left
240 pack .options.output.checks .options.output.scene \
241 .options.output.comment .options.output.label .options.output.id \
243 pack .options.output.id -pady 1m
244 pack .options.output -expand 1 -fill both
245 button .options.button -text Ok -command {destroy .options}
247 bind .options <Return> {destroy .options}
249 # Map options window.
251 pack .options.input_title .options.input .options.processing_title \
252 .options.processing .options.output_title .options.output .options.button \
253 -side top -padx 2m -pady 1m
257 # Proc Print prints the snapped image to a printer or command.
262 . configure -cursor watch
265 set command [concat $command $snap(snapshot)]
267 if {$snap(compress)} {
268 set option "-compress zip"
270 set command [concat $command $option]
271 set command [concat $command -density \"$snap(density)\"]
272 set command [concat $command -page \"$snap(page)\"]
273 set command [concat $command \"ps:|$snap(printer)\"]
275 . configure -cursor {}
279 # Proc PrintImage allows the user to provide a command name to print with.
283 # Initialize print window.
285 catch {destroy .print}
286 toplevel .print -class Print
287 wm title .print Print
289 wm transient .print .
291 +[expr {[winfo width .]+[winfo x .]+75}]+[expr {[winfo y .]+50}]
293 # Create print window frame.
296 scrollbar .print.format.scroll -command ".print.format.list yview"
297 listbox .print.format.list -yscroll ".print.format.scroll set" -setgrid 1 \
299 pack .print.format.scroll -side right -fill y
300 pack .print.format.list -side top -expand 1 -fill both
301 .print.format.list insert 0 \
302 Letter Tabloid Ledger Legal Statement Executive A3 A4 A5 B4 B5 Folio \
304 .print.format.list selection set 0
307 entry .print.file.entry -width 18 -relief sunken -textvariable snap(printer)
308 pack .print.file.entry -side right -expand 1 -fill both
311 button .print.buttons.print -text Print -command Print
312 button .print.buttons.cancel -text Cancel -command {destroy .print}
313 pack .print.buttons.print .print.buttons.cancel -side left -expand 1 \
319 pack .print.format .print.file .print.buttons -padx 2m -pady 2m -expand 1 \
325 # Proc Save saves the snapped image to disk.
330 if ![file readable $snap(snapshot)] {
331 Alert grab "You must snap an image before you can save it!" {" OK " {}}
335 . configure -cursor watch
338 set command [concat $command $snap(snapshot)]
340 if {$snap(compress)} {
341 set option "-compress zip"
343 set command [concat $command $option]
344 set command [concat $command -density \"$snap(density)\"]
345 set command [concat $command -page \"$snap(page)\"]
346 set filename $snap(filename)
347 if {$snap(format) != {}} {
348 set filename "$snap(format):$snap(filename)"
350 set command [concat $command $filename]
352 . configure -cursor {}
357 # Initialize save window.
359 catch {destroy .save}
360 toplevel .save -class Saves
361 wm title .save "Save As..."
365 +[expr {[winfo width .]+[winfo x .]+50}]+[expr {[winfo y .]+25}]
367 # Create save window frame.
370 scrollbar .save.format.scroll -command ".save.format.list yview"
371 listbox .save.format.list -yscroll ".save.format.scroll set" -setgrid 1 \
373 pack .save.format.scroll -side right -fill y
374 pack .save.format.list -side top -expand 1 -fill both
375 .save.format.list insert 0 \
376 ps avs bie bmp cmyk dcx eps epsf epsi fax fits gif gif87 gray g3 hdf \
377 histogram jbig jpeg jpg map matte miff mpg mtv pbm pcd pcx pdf pgm pict \
378 png ppm pnm ps2 ras rgb rle sgi sun tga tiff uyvy vid viff x xbm xpm \
380 .save.format.list selection set 0
383 entry .save.file.entry -width 18 -relief sunken -textvariable snap(filename)
384 pack .save.file.entry -side right -expand 1 -fill both
387 button .save.buttons.save -text Save -command Save
388 button .save.buttons.cancel -text Cancel -command {destroy .save}
389 pack .save.buttons.save .save.buttons.cancel -side left -expand 1 \
393 # Bind buttons to print window.
395 bind .save.format.list <ButtonRelease-1> {
397 [.save.format.list get [lindex [.save.format.list curselection] 0]]
399 bind .save.format.list <Double-Button-1> {AppendImageFormat .save}
403 pack .save.format .save.file .save.buttons -padx 2m -pady 2m -expand 1 \
409 # Proc ShowImage displays the full-sized snapped image in a top level window.
411 proc ShowImage { title name } {
412 catch {destroy .show}
413 toplevel .show -visual best
414 wm title .show $title
415 button .show.image -image $name -command {destroy .show}
420 # Proc Snap executes the ImageMagick import program to grab the image
421 # from the X server screen.
427 # Initialize import command.
430 set command [concat $command -depth 8]
435 set command [concat $command $option]
437 set command [concat $command -colors $snap(colors)]
439 set command [concat $command -comment \"$snap(comment)\"]
441 if {$snap(compress)} {
442 set option "-compress zip"
444 set command [concat $command $option]
446 set command [concat $command -delay $snap(delay)]
448 set command [concat $command -density \"$snap(density)\"]
449 if {$snap(descend)} {
450 set command [concat $command -descend]
452 set command [concat $command -display \"$snap(display)\"]
457 set command [concat $command $option]
462 set command [concat $command $option]
463 set option +interlace
464 if {$snap(interlace)} {
465 set option "-interlace plane"
467 set command [concat $command $option]
468 set command [concat $command -label \"$snap(label)\"]
469 set option +monochrome
470 if {$snap(monochrome)} {
471 set option -monochrome
473 set command [concat $command $option]
478 set command [concat $command $option]
479 set command [concat $command -page \"$snap(page)\"]
480 if {$snap(degrees)} {
481 set command [concat $command -rotate $snap(degrees)]
484 set command [concat $command -scene $snap(scene)]
490 set command [concat $command $option]
492 set command [concat $command -crop 0x0]
495 if {$snap(verbose)} {
498 set command [concat $command $option]
499 set command [concat $command $snap(snapshot)]
501 # Import the image from the X server screen.
503 . configure -cursor watch
509 catch {image delete snapshot}
510 image create photo snapshot -file $snap(snapshot)
512 # Convert to an image tile.
514 exec convert -geometry 320x320> $snap(snapshot) -depth 8 $snap(tile)
515 catch {image delete tile}
516 image create photo tile -file $snap(tile)
517 exec rm -f $snap(tile)
519 # Display tile image as a button.
521 if [winfo exists .canvas.label] {
522 destroy .canvas.label
523 destroy .canvas.button
525 label .canvas.label -text $snap(filename)
526 button .canvas.button -image tile -relief sunken -borderwidth 2 \
527 -command { ShowImage $snap(filename) snapshot }
528 pack .canvas.label .canvas.button -side top -expand 1 -fill both \
530 bind . <Return> { ShowImage $snap(filename) snapshot }
531 . configure -cursor {}
535 # Proc SnapWindow creates the top level window.
539 # Initialize snap window.
541 wm title . "X-Windows Snapshot"
542 wm iconname . "xsnap"
544 # Create snap window frame.
546 frame .toolbar -relief raised -bd 2
547 menubutton .toolbar.file -text "File" -menu .toolbar.file.menu -underline 0
548 menu .toolbar.file.menu
549 .toolbar.file.menu add command -label "Save" -command Save
550 .toolbar.file.menu add command -label "Save As ..." -command "SaveImage"
551 .toolbar.file.menu add command -label Print -command PrintImage
552 .toolbar.file.menu add separator
553 .toolbar.file.menu add command -label Quit \
554 -command { exec rm -f $snap(snapshot); exit }
555 pack .toolbar.file -side left
556 pack .toolbar -side top -fill x
557 canvas .canvas -width 256 -height 128
560 button .buttons.snap -text Snap -command Snap
561 button .buttons.options -text Options -command Options
562 pack .buttons.snap .buttons.options -side left -expand 1
563 pack .buttons -side bottom -fill x -padx 2m -pady 2m
567 pack .toolbar .canvas .buttons
571 # Initalize snap options.
575 set snap(comment) "Imported from %m image: %f"
579 set snap(density) 72x72
582 if [info exists env(DISPLAY)] {
583 set snap(display) $env(DISPLAY)
586 set snap(filename) magick.ps
589 set snap(interlace) 1
590 set snap(label) "%f %wx%h"
591 set snap(monochrome) 0
593 set snap(page) Letter
597 set snap(snapshot) /tmp/snap[pid].ppm
598 set snap(tile) /tmp/tile[pid].ppm
602 # Create top level snap window.
606 exec rm -f $snap(snapshot)