"Fossies" - the Fresh Open Source Software Archive 
Member "xzgv-0.9.2/src/updatetn.c" (3 Sep 2017, 14005 Bytes) of package /linux/misc/old/xzgv-0.9.2.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "updatetn.c" see the
Fossies "Dox" file reference documentation.
1 /* xzgv v0.2 - picture viewer for X, with file selector.
2 * Copyright (C) 1999 Russell Marks. See main.c for license details.
3 *
4 * updatetn.c - code for file details dialog.
5 */
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <dirent.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <gtk/gtk.h>
15 #include <gdk/gdkkeysyms.h>
16 #include "backend.h"
17 #include "main.h"
18 #include "rcfile.h" /* for thin_rows */
19 #include "dither.h"
20 #include "resizepic.h"
21 #include "confirm.h"
22 #include "misc.h"
23
24 #include "updatetn.h"
25
26
27 /* stuff for checking old directories (to avoid symlink loops) */
28 struct olddir_tag
29 {
30 dev_t device;
31 ino_t inode;
32 };
33
34 static struct olddir_tag *olddirs=NULL;
35 static int olddir_byte_size=64*sizeof(struct olddir_tag);
36 static int olddir_byte_incr=32*sizeof(struct olddir_tag);
37 static int num_olddirs=0;
38
39
40
41 /* allocate some initial space for the olddirs[] array. */
42 void olddir_init()
43 {
44 if(olddirs!=NULL) return; /* sanity check */
45
46 if((olddirs=malloc(olddir_byte_size))==NULL)
47 quit_no_mem();
48
49 num_olddirs=0;
50 }
51
52
53 /* make olddirs bigger if needed.
54 * call this *before writing each new entry*.
55 */
56 void olddir_resize_if_needed(int newent)
57 {
58 /* this is absurdly conservative, just to be on the safe side :-) */
59 if((newent+1)*sizeof(struct olddir_tag)>=olddir_byte_size)
60 {
61 olddir_byte_size+=olddir_byte_incr;
62 if((olddirs=realloc(olddirs,olddir_byte_size))==NULL)
63 quit_no_mem();
64 }
65 }
66
67
68 void olddir_uninit(void)
69 {
70 free(olddirs);
71 olddirs=NULL;
72 num_olddirs=0;
73 }
74
75
76 int makexv332(char *filename,char *xvpicfn,
77 unsigned char **xvpic_data,int *wp,int *hp,int *written_ok_ptr)
78 {
79 FILE *out;
80 int w,h,y;
81 unsigned char *smallpic;
82 xzgv_image *origpic;
83 int width,height;
84 int origw,origh;
85 int allow_crunch=1;
86 int written_ok=0;
87
88 /* read pic */
89 if((origpic=load_image(filename,1,&origw,&origh))==NULL)
90 return(0);
91
92 /* only allow crude resizing before nice resizing if that hasn't
93 * already (in effect) been done.
94 */
95 if(origpic->w!=origw || origpic->h!=origh)
96 allow_crunch=0;
97
98 /* resize */
99 w=80; h=60;
100 smallpic=resizepic(origpic->rgb,
101 width=origpic->w,height=origpic->h,&w,&h,
102 allow_crunch);
103
104 backend_image_destroy(origpic); /* finished with this */
105
106 /* dither */
107 ditherinit(w);
108 for(y=0;y<h;y++)
109 ditherline(smallpic+y*80*3,y,w);
110 ditherfinish();
111
112 /* write */
113 out=fopen(xvpicfn,"wb"); /* keep going even if we can't write */
114
115 /* try to be Gimp-friendly, but we can't tell what type the pic really is,
116 * so just presume it's RGB. :-/
117 */
118 if(out)
119 {
120 written_ok=1; /* bit bogus, should check writes too */
121
122 fprintf(out,"P7 332\n");
123 fprintf(out,"#IMGINFO:%dx%d RGB\n",origw,origh);
124 fprintf(out,"#END_OF_COMMENTS\n");
125 fprintf(out,"%d %d 255\n",w,h);
126
127 for(y=0;y<h;y++)
128 fwrite(smallpic+y*80*3,1,w,out);
129 fclose(out);
130 }
131
132 /* pack it into a w*h pixmap to return.
133 * Loop starts at 1, as the 0th line is already ok.
134 * (We could also realloc() to make it smaller, but there isn't
135 * really any point, it's only going to be around for a short time.)
136 */
137 for(y=1;y<h;y++)
138 memcpy(smallpic+y*w,smallpic+y*80*3,w);
139
140 *xvpic_data=smallpic;
141 *wp=w;
142 *hp=h;
143
144 if(written_ok_ptr)
145 *written_ok_ptr=written_ok;
146
147 return(1);
148 }
149
150
151 /* if needed, update thumbnail for file at row given. */
152 int update_one_tn(int row,GtkWidget **update_tn_win_ptr)
153 {
154 static char buf[1024];
155 FILE *test=NULL;
156 struct clist_data_tag *datptr;
157 unsigned char *xvpic_data;
158 GdkPixmap *pixmap,*small_pixmap;
159 struct stat realpic,xvpic;
160 int w,h;
161 char *ptr;
162 int written_ok=1; /* only 0 if we *tried* to write one and it failed */
163
164 gtk_clist_get_text(GTK_CLIST(clist),row,SELECTOR_NAME_COL,&ptr);
165 datptr=gtk_clist_get_row_data(GTK_CLIST(clist),row);
166
167 /* skip dirs, files we can't stat, and hidden files */
168 if(datptr->isdir || stat(ptr,&realpic)==-1 || *ptr=='.')
169 return(1);
170
171 strcpy(buf,".xvpics/");
172 strncat(buf,ptr,sizeof(buf)-8-2); /* above string is 8 chars long */
173
174 /* if not there, or pic is newer, or thumbnail is unreadable,
175 * make a thumbnail.
176 */
177 if(stat(buf,&xvpic)==-1 || realpic.st_mtime>xvpic.st_mtime ||
178 (test=fopen(buf,"rb"))==NULL)
179 {
180 /* make the row visible to show what we're doing */
181 make_visible_if_not(row);
182
183 /* make sure a page of thumbnails is there */
184 blocking_thumbnail_read_visible(update_tn_win_ptr);
185
186 /* give it a chance to redraw before loading or we end up with nasty
187 * `shearing' in the meantime.
188 */
189 if(*update_tn_win_ptr && mainwin)
190 do_gtk_stuff();
191
192 /* have to try and make dir too, it may not be there;
193 * result is ignored, as we're going to be making thumbnail for our
194 * current use whether we can write it or not.
195 */
196 mkdir(".xvpics",0777);
197
198 if(makexv332(ptr,buf,&xvpic_data,&w,&h,&written_ok))
199 {
200 /* now update pixmap, whether it wrote the thumbnail or not.
201 * (makexv332()'s ret value indicates if source image existed etc.)
202 */
203 pixmap=xvpic2pixmap(xvpic_data,w,h,&small_pixmap);
204 if(pixmap)
205 {
206 if(datptr->pm_norm) g_object_unref(datptr->pm_norm);
207 if(datptr->pm_small) g_object_unref(datptr->pm_small);
208 if(datptr->pm_norm_mask) g_object_unref(datptr->pm_norm_mask);
209 if(datptr->pm_small_mask) g_object_unref(datptr->pm_small_mask);
210 datptr->pm_norm=pixmap;
211 datptr->pm_small=small_pixmap;
212 datptr->pm_norm_mask=datptr->pm_small_mask=NULL;
213 gtk_clist_set_pixmap(GTK_CLIST(clist),row,SELECTOR_TN_COL,
214 thin_rows?datptr->pm_small:datptr->pm_norm,
215 NULL);
216 }
217
218 free(xvpic_data);
219 }
220 }
221
222 /* close the file we (may have) opened to test readability */
223 if(test) fclose(test);
224
225 return(written_ok);
226 }
227
228
229 /* update current dir's thumbnails;
230 * this bit is common to both normal update and recursive update.
231 */
232 void update_common(GtkWidget **update_tn_win_ptr,GtkWidget *progbar,
233 int recursive_mode)
234 {
235 int f;
236
237 if(*update_tn_win_ptr && mainwin)
238 gtk_progress_configure(GTK_PROGRESS(progbar),0.,0.,(float)numrows);
239
240 /* do GTK+ update early, in case we're running on a slow machine
241 * (where the first `file' (almost certainly a dir) will take a
242 * noticeable time to `update').
243 */
244 if(*update_tn_win_ptr && mainwin)
245 do_gtk_stuff();
246
247 for(f=0;f<numrows;f++)
248 {
249 if(!update_one_tn(f,update_tn_win_ptr) && recursive_mode)
250 return; /* unwritable thumbnails are pointless in recursive mode */
251
252 /* update progress bar, and give it a chance to draw */
253 if(*update_tn_win_ptr && mainwin)
254 {
255 gtk_progress_set_value(GTK_PROGRESS(progbar),(float)(f+1));
256 do_gtk_stuff();
257 }
258
259 if(!*update_tn_win_ptr || !mainwin)
260 break; /* they must have aborted */
261 }
262 }
263
264
265
266 /* returns window widget, and progbar in arg */
267 void make_update_win(GtkWidget **update_tn_win_ret,GtkWidget **progbar_ret)
268 {
269 GtkWidget *update_tn_win;
270 GtkWidget *progbar,*button;
271
272 update_tn_win=*update_tn_win_ret=gtk_dialog_new();
273
274 /* set returned pointer to NULL when destroyed */
275 gtk_signal_connect(GTK_OBJECT(update_tn_win),"destroy",
276 GTK_SIGNAL_FUNC(gtk_widget_destroyed),
277 update_tn_win_ret);
278
279 gtk_container_set_border_width(
280 GTK_CONTAINER(GTK_DIALOG(update_tn_win)->vbox),2);
281 gtk_container_set_border_width(
282 GTK_CONTAINER(GTK_DIALOG(update_tn_win)->action_area),0);
283
284 gtk_window_set_title(GTK_WINDOW(update_tn_win),"Updating Thumbnails");
285 gtk_window_set_policy(GTK_WINDOW(update_tn_win),FALSE,TRUE,FALSE);
286 gtk_widget_set_usize(update_tn_win,250,55);
287 gtk_window_set_position(GTK_WINDOW(update_tn_win),GTK_WIN_POS_CENTER);
288 gtk_window_set_modal(GTK_WINDOW(update_tn_win),TRUE);
289
290 progbar=*progbar_ret=gtk_progress_bar_new();
291 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(update_tn_win)->vbox),
292 progbar,TRUE,TRUE,2);
293 gtk_widget_show(progbar);
294
295 button=gtk_button_new_with_label("Cancel");
296 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(update_tn_win)->action_area),
297 button,TRUE,TRUE,2);
298 gtk_signal_connect_object(GTK_OBJECT(button),"clicked",
299 GTK_SIGNAL_FUNC(gtk_widget_destroy),
300 GTK_OBJECT(update_tn_win));
301 gtk_widget_grab_focus(button);
302 gtk_widget_show(button);
303
304 /* esc also aborts (even from main window!) */
305 gtk_widget_add_accelerator(button,"clicked",mainwin_accel_group,
306 GDK_Escape,0,0);
307
308
309 gtk_widget_show(update_tn_win);
310 }
311
312
313 /* callback for updating thumbnails. This routine is basically
314 * just GUI code and a loop - the real work is done by update_one_tn().
315 */
316 void cb_update_tn(void)
317 {
318 GtkWidget *update_tn_win,*progbar;
319 int was_reading=0;
320 GtkAdjustment *clist_vadj;
321 float prev_vadj_value;
322
323 /* if by some miracle there are no files, don't bother ;-) */
324 if(!numrows) return;
325
326 /* save vertical position in clist */
327 clist_vadj=gtk_clist_get_vadjustment(GTK_CLIST(clist));
328 prev_vadj_value=clist_vadj->value;
329
330 /* remove any running thumbnail read. We'll restart it after we're done.
331 * (This makes sure we pick up any not updated by this routine.)
332 */
333 if(thumbnail_read_running())
334 {
335 stop_thumbnail_read();
336 was_reading=1;
337 }
338
339 /* make sure a page of thumbnails are visible whether we had time to
340 * read them before or not.
341 */
342 blocking_thumbnail_read_visible(NULL);
343
344 /* we include dirs in our number-of-files-done calculations. This is
345 * a bit crap I s'pose, but it simplifies things, and makes sure you
346 * always get *some* activity, even if it's not doing anything as such.
347 */
348 make_update_win(&update_tn_win,&progbar);
349
350 /* have to pass update_tn_win by ref so it gets NULLed correctly */
351 update_common(&update_tn_win,progbar,0);
352
353 /* if they decided to quit completely, take the hint :-) */
354 if(!mainwin)
355 return;
356
357 /* if not already destroyed, blast window */
358 if(update_tn_win)
359 gtk_widget_destroy(update_tn_win);
360
361 /* restore vertical position in clist */
362 gtk_adjustment_set_value(clist_vadj,prev_vadj_value);
363
364 /* restart thumbnail-read if needed */
365 if(was_reading)
366 start_thumbnail_read();
367 }
368
369
370 void recursive_update_internal(GtkWidget **update_tn_win_ptr,
371 GtkWidget *progbar,char *dirname)
372 {
373 DIR *dir;
374 struct dirent *dent;
375 struct stat sbuf;
376 int ent;
377 char *old_cwd;
378 int f;
379
380 /* if they decided to abort or quit completely, give up :-) -
381 * the caller will return to the original dir anyway.
382 */
383 if(!mainwin || !*update_tn_win_ptr)
384 return;
385
386 /* save old cwd to avoid depending on chdir("..") to be sane :-) */
387 old_cwd=getcwd_allocated();
388
389 if(stat(dirname,&sbuf)==-1 || chdir(dirname)==-1)
390 {
391 free(old_cwd);
392 return;
393 }
394
395 /* see if we've done this one before (a symlink loop could cause this).
396 * XXX an array isn't exactly the most efficient thing to search...
397 */
398 for(f=0;f<num_olddirs;f++)
399 if(sbuf.st_dev==olddirs[f].device && sbuf.st_ino==olddirs[f].inode)
400 {
401 chdir(old_cwd);
402 free(old_cwd);
403 return;
404 }
405
406 /* save this as a visited dir */
407 ent=num_olddirs;
408 num_olddirs++;
409 olddir_resize_if_needed(num_olddirs);
410 olddirs[ent].device=sbuf.st_dev;
411 olddirs[ent].inode=sbuf.st_ino;
412
413 /* open the dir */
414 if((dir=opendir("."))==NULL)
415 {
416 chdir(old_cwd);
417 free(old_cwd);
418 return;
419 }
420
421 /* scan through it and recurse into any dirs */
422 while((dent=readdir(dir))!=NULL)
423 {
424 /* skip hidden files (even if we do allow searching these at
425 * some point, would want to skip `.'/`..'/`.xvpics').
426 */
427 if(dent->d_name[0]=='.')
428 continue;
429
430 /* skip if we can't stat it or it's not a dir */
431 if((stat(dent->d_name,&sbuf))==-1 || !S_ISDIR(sbuf.st_mode))
432 continue;
433
434 /* ok then, recurse. */
435 recursive_update_internal(update_tn_win_ptr,progbar,dent->d_name);
436 }
437
438 closedir(dir);
439
440 /* now update thumbnails for this dir */
441 if(*update_tn_win_ptr && mainwin)
442 {
443 reinit_dir(0,0); /* init without pastpos or cursor-pos-save */
444 stop_thumbnail_read();
445 if(!fast_recursive_update)
446 blocking_thumbnail_read_visible(update_tn_win_ptr);
447 update_common(update_tn_win_ptr,progbar,1);
448 }
449
450 /* return to previous dir */
451 chdir(old_cwd);
452 free(old_cwd);
453 }
454
455
456 void cb_update_tn_recursive_confirmed(void)
457 {
458 GtkWidget *update_tn_win,*progbar;
459 GtkAdjustment *clist_vadj;
460 float prev_vadj_value;
461 char *origdir;
462
463 if(!numrows) return;
464
465 /* save orig dir to return to (in case they abort) */
466 origdir=getcwd_allocated();
467
468 /* save vertical position in clist
469 * (may not be reasonable once we get back, but IIRC the value is
470 * bounds-tested, so that doesn't really matter)
471 */
472 clist_vadj=gtk_clist_get_vadjustment(GTK_CLIST(clist));
473 prev_vadj_value=clist_vadj->value;
474
475 /* also save focus row, via pastpos. */
476 new_pastpos(GTK_CLIST(clist)->focus_row);
477
478 /* remove any running thumbnail read. Once we return to this
479 * dir, it'll be scanned from scratch which will restart this.
480 */
481 stop_thumbnail_read();
482
483 /* currently we use the normal update win, and do progress
484 * for each dir we update. Not great, but easy. :-)
485 */
486 make_update_win(&update_tn_win,&progbar);
487
488
489 /* actually do the recursive update :-) */
490 olddir_init();
491 recursive_update_internal(&update_tn_win,progbar,".");
492 olddir_uninit();
493
494
495 /* if they decided to quit completely, take the hint :-) */
496 if(!mainwin)
497 {
498 /* probably unnecessary in practice, but WTF */
499 chdir(origdir);
500 free(origdir);
501 return;
502 }
503
504 /* if not already destroyed, blast window */
505 if(update_tn_win)
506 gtk_widget_destroy(update_tn_win);
507
508 /* return to original dir and rescan it */
509 chdir(origdir);
510 free(origdir);
511 reinit_dir(1,0); /* init with pastpos */
512
513 /* restore vertical position in clist */
514 gtk_adjustment_set_value(clist_vadj,prev_vadj_value);
515 }
516
517
518 void cb_update_tn_recursive(void)
519 {
520 /* recursive update can take ages; make sure they know. This also explains
521 * what recursive actually means, which may help people who don't know.
522 * (This is possibly annoying, but I still think it's a good idea.)
523 */
524 confirmation_dialog("Recursive Update",
525 "Recursive updates can take quite some time;\n"
526 "are you sure you want to update this\n"
527 "directory and all its subdirectories?",
528 cb_update_tn_recursive_confirmed);
529 }