"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 }