ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/CV/bin/cv
Revision: 1.47
Committed: Wed Jul 20 09:06:37 2005 UTC (18 years, 11 months ago) by root
Branch: MAIN
Changes since 1.46: +112 -17 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #!/opt/bin/perl
2    
3 root 1.37 use Cwd ();
4     use Encode ();
5    
6 root 1.12 use Gtk2 -init;
7     use Gtk2::Gdk::Keysyms;
8    
9     use Gtk2::CV::ImageWindow;
10     use Gtk2::CV::Schnauzer;
11    
12 root 1.16 use Gtk2::CV;
13    
14     Gtk2::Rc->parse (Gtk2::CV::find_rcfile "gtkrc");
15    
16 root 1.20 use File::Spec;
17    
18 root 1.40 require Gtk2::CV::Plugin;
19     require "$ENV{HOME}/.cvrc" if -r "$ENV{HOME}/.cvrc";
20    
21 root 1.20 my $mainwin;
22 root 1.12 my $viewer;
23     my $schnauzer;
24 root 1.20 my $info;
25 root 1.21 my $help;
26 root 1.12
27 root 1.25 my $schnauzer_idx = 0;
28    
29 root 1.12 sub new_schnauzer {
30 root 1.20 my $s = new Gtk2::CV::Schnauzer;
31 root 1.12
32     $s->signal_connect_after (key_press_event => \&std_keys);
33 root 1.20 $s->signal_connect (activate => sub {
34     my $label = sprintf "%s (%d)",
35     (File::Spec->splitpath ($_[1]))[2],
36     -s $_[1];
37     $info->set_label ($label);
38     $viewer->load_image ($_[1]);
39     });
40 root 1.12
41 root 1.40 Gtk2::CV::Plugin->call (new_schnauzer => $s);
42    
43 root 1.12 $s;
44     }
45    
46     sub std_keys {
47     my $key = $_[1]->keyval;
48     my $state = $_[1]->state;
49    
50 root 1.21 my $ctrl = $state * "control-mask";
51 root 1.12
52     if ($key == $Gtk2::Gdk::Keysyms{q}) {
53     main_quit Gtk2;
54     } elsif ($ctrl && $key == $Gtk2::Gdk::Keysyms{v}) {
55 root 1.20 my $w = new Gtk2::Window;
56 root 1.26
57     $w->set_title ("CV: Schnauzer");
58 root 1.20 $w->add (my $s = new_schnauzer);
59     $s->set_dir (File::Spec->curdir);
60 root 1.25 $s->set_geometry_hints;
61 root 1.21 $w->show_all;
62 root 1.25
63 root 1.21 } elsif ($ctrl && $key == $Gtk2::Gdk::Keysyms{h}) {
64     unless ($help) {
65     require Gtk2::PodViewer;
66    
67     $help = new Gtk2::Window;
68 root 1.26 $help->set_title ("CV: Help");
69 root 1.21 $help->set_default_size (500, 300);
70     $help->signal_connect (delete_event => sub { $help->hide; 1 });
71    
72     $help->add (my $sw = new Gtk2::ScrolledWindow);
73     $sw->add (my $h = new Gtk2::PodViewer);
74    
75     #binmode DATA, ":utf8";
76     $h->load_string (do { local $/; <DATA> });
77     }
78    
79     $help->show_all;
80 root 1.12 } else {
81 root 1.43 return 0;
82 root 1.12 }
83    
84 root 1.43 1
85 root 1.12 }
86    
87 root 1.20 {
88     $viewer = new Gtk2::CV::ImageWindow;
89 root 1.15
90 root 1.20 $viewer->set_title ("CV: Image");
91 root 1.15
92 root 1.43 $viewer->signal_connect (key_press_event => sub {
93     &std_keys
94     or $schnauzer->signal_emit (key_press_event => $_[1])
95     });
96 root 1.20 $viewer->signal_connect (delete_event => sub { main_quit Gtk2 });
97 root 1.13
98 root 1.20 $viewer->signal_connect (button3_press_event => sub {
99     $mainwin->visible
100     ? $mainwin->hide
101     : $mainwin->show_all;
102 root 1.23 1;
103 root 1.20 });
104    
105 root 1.40 Gtk2::CV::Plugin->call (new_imagewindow => $viewer);
106    
107 root 1.20 $schnauzer = new_schnauzer;
108    
109     $mainwin = new Gtk2::Window;
110     $mainwin->set_title ("CV");
111     $mainwin->add (my $vbox = new Gtk2::VBox);
112 root 1.23 $mainwin->signal_connect (delete_event => sub { $mainwin->hide; 1; });
113 root 1.20
114     $vbox->add ($schnauzer);
115 root 1.22 $vbox->pack_end (my $frame = new Gtk2::Frame, 0, 0, 0);
116 root 1.20 $frame->add (my $hbox = new Gtk2::HBox 0, 0);
117 root 1.45 $hbox->pack_start ((new Gtk2::Label "Info: "), 0, 0, 0);
118     $hbox->pack_end (my $labelwindow = new Gtk2::EventBox, 1, 1, 0);
119     $labelwindow->add ($info = new Gtk2::Label);
120     $labelwindow->signal_connect_after (size_request => sub { $_[1]->width (0); 0 });
121     $info->set (selectable => 1, xalign => 0, justify => "left");
122 root 1.25
123     $schnauzer->set_geometry_hints;
124 root 1.20 }
125 root 1.12
126     if (@ARGV) {
127 root 1.36 $schnauzer->set_paths ([map Glib::filename_to_unicode $_, @ARGV]);
128 root 1.31 $schnauzer->show_all;
129 root 1.46 $viewer->show_all;
130 root 1.18 $schnauzer->handle_key ($Gtk2::Gdk::Keysyms{space}, []);
131 root 1.17 } else {
132 root 1.20 $schnauzer->set_dir (File::Spec->curdir);
133 root 1.19 $mainwin->show_all;
134 root 1.34 $viewer->show_all;
135 root 1.12 }
136    
137 root 1.35 $viewer->show_all;
138    
139 root 1.12 main Gtk2;
140 root 1.21
141     __DATA__
142 root 1.12
143 root 1.11 =head1 NAME
144    
145 root 1.47 cv - a fast gtk+ image viewer loosely modeled after XV
146 root 1.11
147     =head1 SYNOPSIS
148    
149     cv [file...]
150    
151 root 1.47 =head1 FEATURES
152    
153     CV is supposed to work similar to the venerable XV image viewer, just
154     faster. Why faster?
155    
156     =over 4
157    
158     =item * optimized directory scanning algorithm
159    
160     The directory scanning in CV plays some tricks that - on most modern
161     filesystems - makes it possible to detect filetypes faster than stat()'ing
162     every file. This makes CV suitable for directories with lots of files
163     (10000+).
164    
165     This algorithm is quite unprecise - it doesn't make a difference between
166     files, device nodes, symlinks and the like, and filetype detection is done
167     using the file extension only.
168    
169     =item * use of asynchronous I/O
170    
171     CV tries to use asynchronous I/O whereever it makes sense, for example
172     while scanning directories, waiting for stat data or unlinking files. This
173     usually decreases scanning times for large directories a bit (especially
174     on RAID devices and over NFS).
175    
176     =item * fast image loading
177    
178     The time span between the user issuing a command and displaying the new
179     image should be as small as possible. CV uses optimized (especially
180     for JPEG) loading functions and sacrifices some quality (e.g no gamma
181     correction) to achieve this speed.
182    
183     =item * fast thumbnail creation
184    
185     Thumbnail creation is crucial for me, so it's better be fast. Thumbnail
186     creation for JPEGs has been specially optimized.
187    
188     =item * minimum optical clutter
189    
190     CV has no menus or other user interface elements that take up a lot of
191     screen space. The schnauzer windows can also be somewhat crowded.
192    
193     The point of an image viewer is viewing images, not a nice GUI. This is
194     similar to XV's behaviour.
195    
196     =item * efficient (and hard to learn) user interface
197    
198     CV uses key combinations. A lot. If you are an experienced XV user, you
199     will find most of these keys familiar. If not, CV might be hard to use at
200     first, but will be an efficient tool later.
201    
202     =item * i18n'ed filename handling throughout
203    
204     As long as glib can recognize your filename encoding (either UTF-8 or
205     locale-specific, depending on your settings) and you have the relevant
206     fonts, CV will display your filenames correctly.
207    
208     =item * extensible through plug-ins
209    
210     I have weird plug-ins that access remote databases to find a
211     directory. This is not likely to be of any use to other people. Likewise,
212     others might have weird requirements I cannot dream of.
213    
214     =item * filename clustering
215    
216     Among the standard plug-ins is a filename clustering plug-in, that (in
217     case of tens of thousands images in one directory) might be able to
218     cluster similar names together.
219    
220     =back
221    
222 root 1.11 =head1 DESCRIPTION
223    
224     =head2 THE IMAGE WINDOW
225    
226     You can use the following keys in the image window:
227    
228     q quit the program
229     < half the image size
230     > double the image size
231     , shrink the image by 10%
232     . enlarge the image by 10%
233     n reset to normal size
234     m maximize to screensize
235 root 1.47 M maximize to screensize, respecting image aspect
236 root 1.26 ctrl-m toggle maxpect-always mode
237 root 1.11 u uncrop
238     r set scaling mode to 'nearest' (fastest)
239     s set scaling mode to 'bilinear' (default)
240 root 1.47 shift-s set scaling mode to 'hyper' (slowest)
241 root 1.11 t rotate clockwise 90°
242     T rotate counterclockwise°
243     ctrl-v open a new visual schnauzer window for the current dir
244 root 1.47 ctrl-e run an editor ($CV_EDITOR or "gimp") on the current image
245     ctrl-p fire up the print fialog
246     escape cancel a crop action
247 root 1.11
248 root 1.33 And when playing movies, these additional keys are active:
249    
250     left rewind by 10 seconds
251     right forward by 10 seconds
252     down rewind by 60 seconds
253     up forward by 60 seconds
254     pg_up rewind by 600 seconds
255     pg_down forward by 600 seconds
256     o toggle on-screen display
257     p pause/unpause
258     escape stop playing
259     9 turn volume down
260     0 turn volume up
261    
262 root 1.47 Any other keys will be sent to the default schnauzer window, which can be
263     toggled on and off by right-clicking into the image window.
264 root 1.11
265 root 1.47 Left-clicking into the image window will let you crop the image (usually
266     to zoom into large images that CV scales down).
267 root 1.11
268     =head2 THE VISUAL SCHNAUZER
269    
270     You can use the following keys in the schnauzer window:
271    
272 root 1.47 ctrl-space,
273 root 1.11 space move to and display next image
274 root 1.47 ctrl-backspace,
275 root 1.11 backspace move to and display previous image
276 root 1.47 ctrl-return,
277     return display selected picture, or enter directory
278 root 1.11
279     cursor keys move selection
280     page-up move one page up
281     page-down move one page down
282     home move to first file
283     end move to last file
284    
285 root 1.47 ctrl-a select all files
286 root 1.24 ctrl-d delete selected files WITHOUT ASKING AGAIN
287 root 1.47 ctrl-g force generation of thumbnais for the selected files
288     ctrl-s rescan current direcory or files updates/deletes etc.
289 root 1.24 ctrl-u update selected (or all) icons if neccessary
290 root 1.47 ctrl-l don't use, will become a plug-in eventually
291    
292     0-9,
293     a-z find the first filename beginning with this letter
294    
295     Right-clicking into the schnauzer window displays a pop-up menu with
296     additional actions.
297    
298     =head1 FILES
299    
300     When starting, CV runs the F<.cvrc> file in your F<$HOME> directory as if
301     it were a perl script. in that, you will mostly load plug-ins.
302    
303     Example:
304    
305     system "fping -q -t 10 ether"
306     or require "/fs/cv/cvplugin.pl";
307    
308     This will load a plug-in, but only if the machine I<ether> is reachable
309     (supposedly the plug-in is networked in some way :).
310 root 1.11
311 root 1.27 =head1 ENVIRONMENT
312    
313     =over 4
314    
315 root 1.38 =item CV_EDITOR
316    
317     The program that gets executed when the user presses C<CTRL-e> in the
318     Schnauzer or image window. The default is C<gimp>.
319    
320 root 1.27 =item CV_PRINT_DESTINATION
321    
322     The default (perl-style) destination to use in the print dialog.
323    
324 root 1.38 =item CV_TRASHCAN
325    
326     When set, must point to a directory where all files that are deleted are
327     moved to. If unset, files that are deleted are really being deleted.
328    
329 root 1.27 =back
330    
331 root 1.23 =head1 SECURITY CONSIDERATIONS
332    
333     CV uses Pixbuf to load images. Pixbuf is not considered safe for this
334     purpose, though (from the gtk-2.2 release notes):
335    
336     "While efforts have been made to make gdk-pixbuf robust against invalid
337     images, using gdk-pixbuf to load untrusted data is not recommended, due to
338     the likelyhood that there are additional problems where an invalid image
339     could cause gdk-pixbuf to crash or worse."
340    
341 root 1.11 =head1 BUGS/TODO
342 root 1.23
343 root 1.47 Lots of functionality is missing.
344    
345     Pixbuf doesn't always honor G_BROKEN_FILENAMES, so accessing files with
346     names incompatible might utf-8 fail.
347 root 1.11
348     rotate on disk
349     lots of ui issues
350     save(?)
351     preferences
352 root 1.12 shift-cursor in schnauzer
353 root 1.11
354     =head1 AUTHOR
355    
356     Marc Lehmann <cv@plan9.de>.
357    
358     =cut
359 root 1.1