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