ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/CV/bin/cv
Revision: 1.49
Committed: Sun Jul 31 07:01:31 2005 UTC (18 years, 11 months ago) by root
Branch: MAIN
Changes since 1.48: +1 -1 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.49 $viewer->show_all;
136 root 1.17 } else {
137 root 1.20 $schnauzer->set_dir (File::Spec->curdir);
138 root 1.19 $mainwin->show_all;
139 root 1.34 $viewer->show_all;
140 root 1.12 }
141    
142 root 1.35 $viewer->show_all;
143    
144 root 1.12 main Gtk2;
145 root 1.21
146     __DATA__
147 root 1.12
148 root 1.11 =head1 NAME
149    
150 root 1.47 cv - a fast gtk+ image viewer loosely modeled after XV
151 root 1.11
152     =head1 SYNOPSIS
153    
154     cv [file...]
155    
156 root 1.47 =head1 FEATURES
157    
158     CV is supposed to work similar to the venerable XV image viewer, just
159     faster. Why faster?
160    
161     =over 4
162    
163     =item * optimized directory scanning algorithm
164    
165     The directory scanning in CV plays some tricks that - on most modern
166     filesystems - makes it possible to detect filetypes faster than stat()'ing
167     every file. This makes CV suitable for directories with lots of files
168     (10000+).
169    
170     This algorithm is quite unprecise - it doesn't make a difference between
171     files, device nodes, symlinks and the like, and filetype detection is done
172     using the file extension only.
173    
174     =item * use of asynchronous I/O
175    
176     CV tries to use asynchronous I/O whereever it makes sense, for example
177     while scanning directories, waiting for stat data or unlinking files. This
178     usually decreases scanning times for large directories a bit (especially
179     on RAID devices and over NFS).
180    
181     =item * fast image loading
182    
183     The time span between the user issuing a command and displaying the new
184     image should be as small as possible. CV uses optimized (especially
185     for JPEG) loading functions and sacrifices some quality (e.g no gamma
186     correction) to achieve this speed.
187    
188     =item * fast thumbnail creation
189    
190     Thumbnail creation is crucial for me, so it's better be fast. Thumbnail
191     creation for JPEGs has been specially optimized.
192    
193     =item * minimum optical clutter
194    
195     CV has no menus or other user interface elements that take up a lot of
196     screen space. The schnauzer windows can also be somewhat crowded.
197    
198     The point of an image viewer is viewing images, not a nice GUI. This is
199     similar to XV's behaviour.
200    
201     =item * efficient (and hard to learn) user interface
202    
203     CV uses key combinations. A lot. If you are an experienced XV user, you
204     will find most of these keys familiar. If not, CV might be hard to use at
205     first, but will be an efficient tool later.
206    
207     =item * i18n'ed filename handling throughout
208    
209     As long as glib can recognize your filename encoding (either UTF-8 or
210     locale-specific, depending on your settings) and you have the relevant
211     fonts, CV will display your filenames correctly.
212    
213     =item * extensible through plug-ins
214    
215     I have weird plug-ins that access remote databases to find a
216     directory. This is not likely to be of any use to other people. Likewise,
217     others might have weird requirements I cannot dream of.
218    
219     =item * filename clustering
220    
221     Among the standard plug-ins is a filename clustering plug-in, that (in
222     case of tens of thousands images in one directory) might be able to
223     cluster similar names together.
224    
225     =back
226    
227 root 1.11 =head1 DESCRIPTION
228    
229     =head2 THE IMAGE WINDOW
230    
231     You can use the following keys in the image window:
232    
233     q quit the program
234     < half the image size
235     > double the image size
236     , shrink the image by 10%
237     . enlarge the image by 10%
238     n reset to normal size
239     m maximize to screensize
240 root 1.47 M maximize to screensize, respecting image aspect
241 root 1.26 ctrl-m toggle maxpect-always mode
242 root 1.11 u uncrop
243     r set scaling mode to 'nearest' (fastest)
244     s set scaling mode to 'bilinear' (default)
245 root 1.47 shift-s set scaling mode to 'hyper' (slowest)
246 root 1.11 t rotate clockwise 90°
247     T rotate counterclockwise°
248     ctrl-v open a new visual schnauzer window for the current dir
249 root 1.47 ctrl-e run an editor ($CV_EDITOR or "gimp") on the current image
250     ctrl-p fire up the print fialog
251     escape cancel a crop action
252 root 1.11
253 root 1.33 And when playing movies, these additional keys are active:
254    
255     left rewind by 10 seconds
256     right forward by 10 seconds
257     down rewind by 60 seconds
258     up forward by 60 seconds
259     pg_up rewind by 600 seconds
260     pg_down forward by 600 seconds
261     o toggle on-screen display
262     p pause/unpause
263     escape stop playing
264     9 turn volume down
265     0 turn volume up
266    
267 root 1.47 Any other keys will be sent to the default schnauzer window, which can be
268     toggled on and off by right-clicking into the image window.
269 root 1.11
270 root 1.47 Left-clicking into the image window will let you crop the image (usually
271     to zoom into large images that CV scales down).
272 root 1.11
273     =head2 THE VISUAL SCHNAUZER
274    
275     You can use the following keys in the schnauzer window:
276    
277 root 1.47 ctrl-space,
278 root 1.11 space move to and display next image
279 root 1.47 ctrl-backspace,
280 root 1.11 backspace move to and display previous image
281 root 1.47 ctrl-return,
282     return display selected picture, or enter directory
283 root 1.11
284     cursor keys move selection
285     page-up move one page up
286     page-down move one page down
287     home move to first file
288     end move to last file
289    
290 root 1.47 ctrl-a select all files
291 root 1.24 ctrl-d delete selected files WITHOUT ASKING AGAIN
292 root 1.47 ctrl-g force generation of thumbnais for the selected files
293     ctrl-s rescan current direcory or files updates/deletes etc.
294 root 1.24 ctrl-u update selected (or all) icons if neccessary
295 root 1.47 ctrl-l don't use, will become a plug-in eventually
296    
297     0-9,
298     a-z find the first filename beginning with this letter
299    
300     Right-clicking into the schnauzer window displays a pop-up menu with
301     additional actions.
302    
303     =head1 FILES
304    
305     When starting, CV runs the F<.cvrc> file in your F<$HOME> directory as if
306     it were a perl script. in that, you will mostly load plug-ins.
307    
308     Example:
309    
310     system "fping -q -t 10 ether"
311     or require "/fs/cv/cvplugin.pl";
312    
313     This will load a plug-in, but only if the machine I<ether> is reachable
314     (supposedly the plug-in is networked in some way :).
315 root 1.11
316 root 1.27 =head1 ENVIRONMENT
317    
318     =over 4
319    
320 root 1.38 =item CV_EDITOR
321    
322     The program that gets executed when the user presses C<CTRL-e> in the
323     Schnauzer or image window. The default is C<gimp>.
324    
325 root 1.27 =item CV_PRINT_DESTINATION
326    
327     The default (perl-style) destination to use in the print dialog.
328    
329 root 1.38 =item CV_TRASHCAN
330    
331     When set, must point to a directory where all files that are deleted are
332     moved to. If unset, files that are deleted are really being deleted.
333    
334 root 1.27 =back
335    
336 root 1.23 =head1 SECURITY CONSIDERATIONS
337    
338     CV uses Pixbuf to load images. Pixbuf is not considered safe for this
339     purpose, though (from the gtk-2.2 release notes):
340    
341     "While efforts have been made to make gdk-pixbuf robust against invalid
342     images, using gdk-pixbuf to load untrusted data is not recommended, due to
343     the likelyhood that there are additional problems where an invalid image
344     could cause gdk-pixbuf to crash or worse."
345    
346 root 1.11 =head1 BUGS/TODO
347 root 1.23
348 root 1.47 Lots of functionality is missing.
349    
350     Pixbuf doesn't always honor G_BROKEN_FILENAMES, so accessing files with
351     names incompatible might utf-8 fail.
352 root 1.11
353     rotate on disk
354     lots of ui issues
355     save(?)
356     preferences
357 root 1.12 shift-cursor in schnauzer
358 root 1.11
359     =head1 AUTHOR
360    
361     Marc Lehmann <cv@plan9.de>.
362    
363     =cut
364 root 1.1