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