1 /********************************************************************\
2 **                             _________________________________    **
3 **   A n t h o n y            |________    __    __    _________|   **
4 **                                     |  |o_|  |o_|  |             **
5 **           T h y s s e n           __|   __    __   |__           **
6 **                                __|   __|  |  |  |__   |__        **
7 **  `` Dragon Computing! ''    __|   __|     |  |     |__   |__     **
8 **                            |_____|        |__|        |_____|    **
9 **                                                                  **
10 \********************************************************************/
11 /*
12 ** images.c
13 **
14 **  This module controls the data structure of `items' used to hold all the
15 ** directory elements. The icon images represented by these elements are read
16 ** in and assigned to widgets to be displayed.
17 **
18 ** The goal here is to create a data structure which can merge in new icons
19 ** or changes to the currently displayed icons with the minimum of reads and
20 ** delays. The rescan in particular could and should be improved.
21 **
22 **                         Anthony Thyssen <anthony@dragon.cit.gu.edu.au>
23 */
24 #include "xbmbrowser.h"
25 
26 static Item   *file_list = NULL;     /* current list of all files */
27 static Item   *new_file_list = NULL; /* new file list just scanned (to merge) */
28 static Item   *old_file_list = NULL; /* old file list already loaded (merge) */
29 static Item  **last_link = NULL;     /* point to the last next ptr in merge */
30 
31 static Widget *widget_array = NULL;  /* array of widgets available to use */
32 static int     allocated = 0;        /* number of widgets in above array */
33 static int     managed = 0;          /* number of widgets being managed */
34 
35 static int items_allocated = 0;      /* current number of allocated items */
36 static int num_files;                /* number files in file_list */
37 static int num_visible;              /* number items which are visible */
38 static int file_counts[(int)NumFileTypes]; /* count of the various file types */
39 
40 #define DIR_LIMIT  256
41 static char *dir_list[DIR_LIMIT];    /* directories in directory menu */
42 static int   num_dirs;               /* number of directories in this list */
43 
44 
45 #ifdef DO_XPMS
46 
47 #ifndef NO_TRANS_COLOR
48 static XpmColorSymbol attr_colorsymbols[] = {
49     {NULL, "none",  0},  /* transparent pixel to assign filled in later */
50  };
51 #endif
52 
53 /* do we have to invert pixmap on monocrome displays so label gets it right */
54 static GC invert_pixmaps_gc = None;
55 
56 #endif
57 
58 /* ------------------------------------------------------------------------ */
59 /* --------------------- Item Allocation / Dellocation -------------------- */
60 
61 Item *
alloc_item()62 alloc_item()
63 /* allocate and default a file item */
64 {
65   Item *item = (Item *) XtMalloc( sizeof(Item) );
66 
67   item->next     = NULL;       /* set defaults */
68   item->fname[0] = '\0';
69   item->info[0]  = '\0';
70   item->type     = Unknown;        /* type is unknown */
71   item->visible  = False;          /* not visible - yet */
72   item->index    = -1;             /* no widget assigned */
73   item->pixmap   = (Pixmap)None;   /* no image loaded */
74   item->mask     = (Pixmap)None;   /* no mask either (of course) */
75 
76   items_allocated++;
77   return item;
78 }
79 
80 
81 static void
free_image(item)82 free_image(item)
83 /* Free any image that may be currently accociated with this item.
84 ** This is usally done either as a prelude to freeing the items
85 ** memory, or hiding the item from the user (why bother?).
86 */
87   Item *item;
88 {
89   /* remove images from widget if one is allocated */
90   if( item->index != -1 ) {
91     XtVaSetValues(widget_array[item->index],
92                   XtNpixmap, (XtArgVal)None,
93                   XtNmask,   (XtArgVal)None,
94                   XtNlabel,  (XtArgVal)NULL,  NULL);
95   }
96 
97   /* Free Pixmap (Bitmap) of the image */
98   if( item->pixmap != (Pixmap)None &&
99       item->pixmap != sym_bmaps[item->type] ) {
100     XFreePixmap(display, item->pixmap );
101 #ifdef DO_XPMS
102     if( item->type == Xpm ) {
103       /* free colors and attributes that were used by this pixmap only */
104       XFreeColors(display, colormap, item->attr.pixels, item->attr.npixels, 0);
105       XpmFreeAttributes((XpmAttributes *)&item->attr);
106     }
107 #endif
108   }
109 
110   /* Free Bitmap Mask of the image */
111   if( item->mask != (Pixmap)None &&
112       item->mask != sym_masks[item->type]) {
113     XFreePixmap(display, item->mask );
114   }
115   item->pixmap = (Pixmap)None;
116   item->mask = (Pixmap)None;
117 }
118 
119 
120 Item *
free_item(item)121 free_item(item)
122 /* Free the item and any aspect about that item. This is the ONLY place
123 ** where items are to be freed.  Returns the next item in the current list!
124 */
125   Item *item;
126 {
127   Item *next = item->next;
128 
129   free_image(item);
130   /* item->index = -1; */
131 
132   XtFree( (char *) item );
133 
134   items_allocated--;
135   return next;
136 }
137 
138 
139 void
free_list(list)140 free_list(list)
141 /* completely free all items in the given item list */
142   Item **list;   /* pointer to a list pointer */
143 {
144   while( *list != NULL )
145     *list = free_item( *list );
146 }
147 
148 /* ------------------------------------------------------------------------ */
149 /* --------------------------- Widget Control ----------------------------- */
150 
151 static void
allocate_widgets(n)152 allocate_widgets( n )
153 /* create enough widgets to display n icon images */
154   int n;
155 {
156   char name[8];
157   Widget w;
158 
159   if( allocated == n ) return;   /* just right -- no need to do anthing */
160 
161   if( allocated < n ) {   /* ok allocate more widgets */
162 
163     if(widget_array == NULL) {
164       widget_array = (Widget *)XtMalloc( n * sizeof(Widget) );
165       if( widget_array == NULL ) {
166         perror("xbmbrowser: XtMalloc widget_array");
167         abort();
168       }
169     } else {
170       widget_array = (Widget *)realloc(widget_array, n * sizeof(Widget) );
171       if( widget_array == NULL ) {
172         perror("xbmbrowser: realloc larger widget_array");
173         abort();
174       }
175     }
176 
177     for( ; allocated < n; allocated++ ) {
178       sprintf(name, "%d", allocated);
179       w = XtVaCreateWidget(name, iconLabelWidgetClass, iconbox, NULL);
180       XtOverrideTranslations(w, XtParseTranslationTable(icon_trans));
181       widget_array[allocated] = w;
182     }
183   }
184 
185 #if 0
186 /* This code will eventually cause a core dump when lots of directory
187 ** changes are performed by the user. The problem seems to occur when
188 ** widgets are really destroyed (they are just flaged for destruction).
189 ** I can't seem to find any problem with the code below however.
190 ** It just doesn't seem to work.       Anthony
191 */
192   else {  /* ok we have too many widgets -- remove about a 1/3 of them */
193     n = ( allocated + allocated + n ) / 3; /* the final level we want */
194 
195     for( allocated--; allocated >= n ; allocated-- )
196       XtDestroyWidget( widget_array[allocated] );
197     allocated++;
198 
199     widget_array = (Widget *)realloc(widget_array, n * sizeof(Widget) );
200     if( widget_array == NULL ) {
201       perror("xbmbrowser: realloc smaller widget_array");
202       abort();
203     }
204   }
205 
206   /* insure that allocated is now equal to n (which may be calculated above */
207   assert( allocated == n );
208 #endif
209 }
210 
211 
212 static void
assign_widget(item)213 assign_widget(item)
214 /* Set up the widget to display this item (index already assigned)
215 ** and insure it is using the appropriate resource settings for
216 ** correct display occording to the current user options.
217 */
218   Item *item;
219 {
220   /* do we label the widget? */
221   Boolean label = app_data.label_all || (
222          item->type == Xbm ? app_data.label_icons :
223          item->type == Xpm ? app_data.label_icons :
224          ( app_data.label_syms || IsDirItem(item) && app_data.label_dirs ) );
225 
226   XtVaSetValues( widget_array[item->index],
227        XtNinfoPtr,  (XtArgVal)item,                  /* pointer to more info */
228        XtNpixmap,   (XtArgVal)item->pixmap,          /* the icon image */
229        XtNmask,     (XtArgVal)item->mask,            /* its mask if present */
230        XtNisBitmap, (XtArgVal)(item->type != Xpm),   /* draw as a bitmap? */
231 
232        XtNlabel,    (XtArgVal)(                      /* display the filename? */
233 	    label ? item->fname : NULL ),
234        XtNshape,    (XtArgVal)(                      /* shape window on? */
235 	    item->type == Xbm ? app_data.solid_bgnd :
236 	    item->type == Xpm ? app_data.solid_bgnd :
237 				app_data.shape_syms ),
238        XtNforeground, (XtArgVal)(                    /* bitmap/label color */
239 	    item->type == Xbm ? app_data.icon_fg :
240 	    item->type == Xpm ? app_data.icon_fg :
241 				app_data.sym_fg  ),
242        XtNbackground, (XtArgVal)(                    /* background color */
243 	    item->type == Xbm ? app_data.icon_bg :
244 	    item->type == Xpm ? app_data.icon_tr :
245 				app_data.sym_bg  ),
246        NULL);
247 }
248 
249 
250 static void
assign_widgets()251 assign_widgets()
252 /* Assign all items to the widgets as required.
253 ** This allows us to adjust the current display after user option
254 ** changes without doing a full rescan of the current directory.
255 ** This should only be done when either
256 **    1/ widgets are unmanaged during new directory scan/merge
257 **    2/ none of the items have actually changed only the
258 **       assignments to the widgets themselves have changed.
259 */
260 {
261   Item *item;
262 
263   managed = 0;   /* re-calculating this now */
264 
265   for( item = file_list;  item != NULL;  item = item->next ) {
266     if( item->visible ) {
267       assert( item->pixmap != (Pixmap)None ); /* a pixmap must be present */
268       item->index = managed++;
269       assign_widget(item);  /* set up this widget to hold the item */
270     }
271   }
272 
273 }
274 
275 /* ------------------------------------------------------------------------ */
276 /* --------------------------- Item Image Loading ------------------------- */
277 
278 static void
load_image(item)279 load_image(item)
280 /* Attempt to determine and set the type of the file given and
281 ** load the image (if present) which is stored in that file.
282 */
283   Item *item;
284 {
285   int i, status;        /* junk integer, size of icon, return status */
286   int c;                /* junk char -- integer for EOF test */
287   unsigned int x, y;    /* size of bitmap/pixmap */
288   FILE *fp;             /* file pointer */
289 
290   /* insure the bitmap is not allocated already */
291   assert( item->pixmap == (Pixmap)None ||
292           item->pixmap == sym_bmaps[item->type]);
293 
294   /* if the type is a X pixmap -- skip direct to load pixmap */
295   if( item->type == Xpm || item->type == XpmBad )
296     goto load_xpm;
297 
298   /* Test if the file is not a binary file
299    * This should not be necessary, however the XReadBitmapFile and
300    * XpmReadPixmapFile functions have a nasty habit of crashing with
301    * some binary files.  --  Ashley
302    */
303    /* open the file and look to see if the first few chars are binary */
304    if((fp = fopen(item->fname,"r")) == (FILE *) NULL) {
305      /* can't open file -- declare it as such */
306      item->type = Unknown;
307      item->pixmap = (Pixmap)None;
308      (void) sprintf(item->info, "\"%s\", unreadable", item->fname );
309      return;
310    } else {
311      for(i = 0; i < 100; i++) {  /* check 100 chars */
312        c = getc(fp);
313        if(c == EOF)
314 	 break;
315        if( c > 127 || c < 7 ) {  /* a char with non ascii values */
316 	 fclose(fp);
317 	 item->type = Binary;
318          item->pixmap = (Pixmap)None;
319          /* (void) sprintf(item->info, "\"%s\", binary (0x%02x at %d)",
320          **            item->fname, c, i ); /**/
321 	 return;
322        }
323      }
324      fclose(fp);
325      if( i == 0 ) {  /* hey empty file -- declare it as such */
326        item->type = File;   /* leave it as a plain file */
327        item->pixmap = (Pixmap)None;
328        (void) sprintf(item->info, "\"%s\", empty file", item->fname );
329        return;
330      }
331 
332    } /* probably a non binary file so continue */
333 
334   /* presumably a text file -- so try an load it as a bitmap */
335   item->pixmap = None; /* Precaution */
336   status = XReadBitmapFile(display, DefaultRootWindow(display),
337                             item->fname, &x, &y, &item->pixmap, &i, &i);
338 
339   if( status == BitmapSuccess && item->pixmap != None ) {
340     item->type = Xbm;
341     (void) sprintf(item->info, "\"%s\", %dx%d bitmap", item->fname, x, y );
342     return;
343   }
344 
345 load_xpm:
346   /* Not a bitmap -- Ok try it as a Pixmap file */
347 #ifdef DO_XPMS
348   item->pixmap = (Pixmap)None;   /* Precaution */
349   item->mask = (Pixmap)None;
350 
351    /* Set the attributes */
352 #ifdef NO_TRANS_COLOR
353   item->attr.valuemask = XpmExactColors | XpmCloseness | XpmReturnPixels;
354 #else
355   item->attr.valuemask =
356           /* IN */    XpmColorSymbols | XpmExactColors | XpmCloseness
357           /* OUT */ | XpmReturnPixels;
358   item->attr.colorsymbols = attr_colorsymbols;  /* setup in merge_init() */
359   item->attr.numsymbols = XtNumber(attr_colorsymbols);
360 #endif /* NO_TRANS_COLOR */
361   item->attr.exactColors = 0;
362   item->attr.closeness = 40000;
363 
364   status = XpmReadPixmapFile(display, DefaultRootWindow(display),
365                     item->fname, &item->pixmap, &item->mask, &item->attr);
366 
367 
368   /* save a copy of the number of pixels (the kludge below may change this) */
369   c = item->attr.npixels;
370 
371 #ifndef NO_TRANS_COLOR
372 /*
373 ** WARNING: The Transparent color given to the Xpixmap library for the `None'
374 ** Color is returned in the  item->attr.pixels array.  This means I now have
375 ** to remove this color from the array so that it is not free'd later when we
376 ** are finished with the pixmap. This is only a problem when we use Color
377 ** Symbols to set the transparent color.  For more information see the
378 ** Changes file.
379 **
380 ** Note before release 3.4d if a None color was in the pixmap but was NOT the
381 ** first element in the colortable, the first element in the pixels array was
382 ** missing, and of course with the Arnaud's `philosophy' the None color pixel
383 ** is present in the appropriate place. As such the first color is not free'd
384 ** and can remain allocated for the life of the programs execution.
385 ** IE: A Color Allocation Leak existed before version 3.4d!
386 **
387 ** NOTE: this also has to be done on Monocrome Displays, as well as color
388 ** displays.
389 */
390   if ( item->mask != None ) { /* None color present in xpimap? */
391     int i,j;
392 
393     for( i = j = 0;  i < item->attr.npixels;  i++ ) {
394       if( item->attr.pixels[i] == app_data.icon_tr )
395         continue;  /* don't include the none pixel color */
396       item->attr.pixels[j++] = item->attr.pixels[i];
397     }
398     item->attr.npixels = j;  /* the number of pixels now */
399 
400 #if XpmFormat == 3 && XpmVersion == 4 && XpmRevision <= 6
401 #else
402     /* Assuming that the None color was only in the X pixmap file once only
403     ** (rarely otherwise) reduce the number of pixels returned by one.
404     ** NOTE:  a bug in the Xpm library before version 3.4d resulted in the
405     ** number of pixels returned being correct but results in a color
406     ** allocation leak, due to the first pixel value being missing.  */
407     c--;
408 #endif
409   } /* if mask present */
410 #endif /* NO_TRANS_COLOR */
411 
412   /* Check the return status of the Read Pixmap */
413   switch( status ) {
414     case XpmColorError:
415     case XpmSuccess:
416       item->type = Xpm;
417       if ( item->attr.width == 0 || item->attr.height == 0 ) {
418         /* this pixmap has NO PIXELS, and is a color table only!
419         ** remove the pixmap and let the default symbol be used
420         ** Suggestion by Thomas Cooke    <cooke@newice.stortek.com> */
421         free_image(item);
422       }
423       (void) sprintf(item->info, "\"%s\", %dx%d pixmap (%d colors%s)",
424                item->fname, item->attr.width, item->attr.height, c,
425                item->mask != None ? " + mask" : "" );
426 
427       if ( item->pixmap != None ) return; /* Return if success */
428       /* fall through if we failed after all! */
429 
430     case XpmColorFailed:
431     case XpmNoMemory:
432       item->type = XpmBad;
433       /* if a pixmap was assigned -- remove it */
434       /* this actually should never be needed but just in case */
435       free_image(item);
436       return;
437 
438     case XpmOpenFailed:
439     case XpmFileInvalid:
440       /* OK so it isn't a pixmap */
441       break;
442    }
443 #endif /* DO_XPMS */
444 
445   /* Non Bitmap/Pixmap file -- it must be a plain text file */
446   item->type = Text;
447   item->pixmap = None;
448   return;
449 }
450 
451 
452 static void
load_item(item)453 load_item(item)
454 /* Given an item, fill out all the other information required
455 ** by that item, including the images needed.
456 */
457   Item *item;
458 {
459   item->visible = True;
460   if ( !app_data.show_hidden        /* don't show hidden 'dot' files */
461        && item->fname[0] == '.'     /* and is a hidden file? */
462        && item->type != DirUp ) {   /* and is not the up directory */
463     item->visible = False;
464   }
465 
466   /* Try to load a image from this file and determine file type
467   ** more closly. This may also set the info line for the file if a load
468   ** was successful.  BadXpm's are NOT attempted again otherwise rescan's
469   ** become very very slow on large directories of X pixmaps.
470   */
471   if( item->type == File && item->visible )
472     load_image( item );
473 
474   /* Determine item visiblity as appropiate for current options */
475   switch( item->type ) {
476   case Dir:
477   case DirUp:
478   case DirLink:
479   case DirBad: /* Directory Items */
480     item->visible = item->visible &&
481                     !app_data.icons_only && app_data.show_dir;
482     break;
483   case Unknown: /* Special File -- pipe, device */
484   case Text:    /* Plain Text file */
485   case Binary:  /* Binary file */
486   case File:    /* New or Empty file */
487     item->visible = item->visible &&
488                     !app_data.icons_only && app_data.show_other;
489     break;
490   case XpmBad:  /* x pixmap which failed this load attempt */
491     item->visible = item->visible &&
492                     !app_data.icons_only && app_data.show_xpmbad;
493     break;
494   case Xbm:     /* X bitmap file */
495   case Xpm:     /* X pixmap file */
496     item->visible = True;  /* pixmaps always visible */
497     break;
498   default:      /* Unknown file type -- error should not happen */
499     item->visible = False;  /* just not visible */
500     assert( False ); /* assertion failure */
501   }
502 
503   /* if it is not visible -- finished */
504   if( !item->visible ) {
505     free_image(item);  /* ???? insure no images is set */
506     return;
507   }
508 
509   /* At this point the item must be visible (widget assigned latter) */
510 
511   /* assign default bitmap is none present */
512   if( item->pixmap == (Pixmap)None ) {
513     item->pixmap = sym_bmaps[item->type];
514     item->mask   = sym_masks[item->type];
515   }
516 
517   /* assign a default info line if none present */
518   if( item->info[0] != '"' ) {
519     char *desc = "Description Error"; /* default description -- just in case */
520 
521     switch ( item->type ) {
522       case Unknown: desc = "unknown";                    break;
523       case Dir:     desc = "directory";                  break;
524       case DirUp:   desc = "parent directory";           break;
525       case DirLink: desc = "directory sym-link";         break;
526       case DirBad:  desc = "directory (no access)";      break;
527       case File:    desc = "file (unknown)";             break;
528       case Text:    desc = "text";                       break;
529       case Binary:  desc = "binary";                     break;
530       case XpmBad:  desc = "pixmap (unable to display)"; break;
531     }
532     (void) sprintf(item->info, "\"%s\", %s", item->fname, desc);
533   }
534 
535 }
536 
537 /*------------------------------------------------------------------------*/
538 /* -------------------- List Merging and Updating ----------------------- */
539 
540 static void
merge_init()541 merge_init()
542 /* initialize the appropiate variables for to merge `new_file_list'
543 ** into the current `file_list'.
544 */
545 {
546   int i;  /* looping variable */
547 
548   /* first un-manage all the widgets and free the current file_list
549   ** This saves on multiple geometry requests while we do the work
550   */
551   if( managed > 0 ) {
552     XtUnmanageChildren(widget_array, managed);
553     managed = 0;
554   }
555 
556   old_file_list = file_list;  /* make the current list old */
557   file_list = NULL;           /* empty destination of merged file_lists */
558   last_link = &file_list;     /* last next ptr is the list ptr itself */
559 
560   /* zero all the counts required */
561   num_files   = 0;            /* reset number of files in current list */
562   num_dirs    = 0;            /* number of directoires in new dirmenu list */
563   num_visible = 0;            /* number of visible items requiring widgets */
564   for( i = 0 ; i < NumFileTypes ; i++ )
565      file_counts[i] = 0;
566 
567   /* initialize the directory list */
568   /* FUTURE: initialise this from user preferences? */
569   dir_list[num_dirs++] = "/";
570   dir_list[num_dirs++] = "~/";
571 
572 #ifdef DO_XPMS
573 #ifndef NO_TRANS_COLOR
574   /* set the transparent pixel color for X Pixmaps */
575   attr_colorsymbols[0].pixel = app_data.icon_tr;
576 #endif /* ! NO_TRANS_COLOR */
577 #endif /* DO_XPMS */
578 
579 }
580 
581 
582 static void
merge_item(item)583 merge_item(item)
584 /* Add this item to the current (merging) `file_list'.
585 ** Assigning the appropiate information line, bitmap.
586 ** And adjusting all file counts appropiately.
587 */
588   Item *item;
589 {
590   /* add item to the file_list */
591   *last_link = item;            /* append new item to merged list */
592   last_link = &(item->next);    /* move last_link to new last next ptr */
593   /* *last_ptr = NULL;          /* terminate current list (not needed?) */
594   num_files++;                  /* increment number of file in list */
595 
596   /* if this item is a directory add it to the directory menu list */
597   /* NOTE: What should be done if num_dirs found > DIR_LIMIT */
598   if( IsDirItem(item) ) {
599     if( num_dirs < DIR_LIMIT-1 )
600       dir_list[num_dirs++] = item->fname;
601   }
602 
603   /* Figure out the rest of the items requirments like
604   ** file type, load image, info line, if visible etc... */
605   load_item(item);
606 
607   /* count up the items of each type */
608   file_counts[item->type]++;
609 
610   /* This item is visible give it a widget */
611   if ( item->visible )
612     num_visible++;
613 }
614 
615 
616 static void
merge_finish()617 merge_finish()
618 /* Finialise the merger of the new and old file_lists, assign the
619 ** widgets, and set the default information line.
620 */
621 {
622   Item      *item;          /* looping variable */
623   int        i;             /* junk integer */
624 
625   /* insure the last next pointer in the newly merged list is NULL */
626   *last_link = NULL;
627 
628   /* finialise the directory menu list and install */
629   dir_list[num_dirs] = NULL;
630   XawListChange(dirlist, dir_list, 0, 0, True);
631   XtVaSetValues(dirlist, XtNdefaultColumns, (XtArgVal)(num_dirs/25+1), NULL);
632 
633   /* allocate/deallocate widgets as needed */
634   allocate_widgets( num_visible );
635 
636   /* Assign all items to the widgets appropriately */
637   assign_widgets();
638 
639   /* manage the widgets */
640   if( managed > 0 )
641     XtManageChildren(widget_array, managed);
642 
643   /* Set the default information label */
644 # define strend  label_info+strlen(label_info)
645   label_info[0] = '\0';  /* empty the string */
646   if( num_files <= 1 ) /* only the ".." directory? */
647     strcpy(label_info, "Empty Directory" );
648   if( (i = file_counts[Xbm]) > 0 )
649     sprintf(strend, " %d Bitmaps ", i );
650   if( (i = file_counts[Xpm]) > 0 )
651     sprintf(strend, " %d Pixmaps ", i );
652   if( (i = file_counts[XpmBad]) > 0 )
653     sprintf(strend, " %d Unloadable ", i );
654   if( (i = file_counts[File] + file_counts[Text] + file_counts[Binary]) > 0 )
655     sprintf(strend, " %d Others ", i );
656   if( (i = file_counts[Dir] + file_counts[DirLink] + file_counts[DirBad]) > 0 )
657     sprintf(strend, " %d Dirs ", i );
658   if( (i = file_counts[Unknown]) > 0 )
659     sprintf(strend, " %d Unknown ",    file_counts[Unknown] );
660 
661   XtVaSetValues(label, XtNlabel, (XtArgVal)label_info, NULL);
662 # undef strend
663 
664   /* check the results of the merger */
665   assert( managed == num_visible );         /* number of widgets */
666   assert( new_file_list == NULL );          /* both lists now empty */
667   assert( old_file_list == NULL );
668   assert( items_allocated == num_files );   /* allocation test */
669 
670 }
671 
672 
673 static void
merge_file_lists()674 merge_file_lists()
675 /* Merge the old_file_list and the new_file_list. */
676 {
677   int cmp;     /* comparision of top two elements of the lists */
678 
679   merge_init();
680 
681   while( old_file_list != NULL && new_file_list != NULL ) {
682     /* both lists have items in them */
683 
684     cmp = strcmp( old_file_list->fname, new_file_list->fname );
685 
686     if( cmp == 0 ) {  /* the file names are the same */
687       if ( !IsFileItem(new_file_list) ||
688            old_file_list->mtime != new_file_list->mtime ) {
689 
690         /* NOT A FILE  -- mtime is non-sense so just add the new item */
691         /* MODIFIED    -- same name but it has been modified or replaced */
692         old_file_list = free_item( old_file_list );  /* junk the old item */
693         cmp = 1;    /* ok now pretend that this is a new item -- ADDITION */
694         /* FALL THROUGH -- to ADDITION */
695 
696       } else {
697 
698         /* NO CHANGE -- same file in both lists without any modifications */
699         new_file_list = free_item( new_file_list ); /* junk the new item */
700         merge_item(old_file_list);         /* merge the old item */
701 	old_file_list = *last_link;        /* remove item from old list */
702 	continue;
703       }
704     }
705 
706     /* ADDITION -- a new file (or newly modified) to be merged */
707     if( cmp > 0 ) {  /* name of new item is smaller than in old */
708       merge_item(new_file_list);         /* merge the new item */
709       new_file_list = *last_link;        /* remove item from new list */
710       continue;
711     }
712 
713     /* DELETION -- the old file has been deleted */
714     if( cmp < 0 ) {  /* Hey old item name is smaller then new item name */
715       old_file_list = free_item( old_file_list ); /* delete old item */
716       continue;
717     }
718 
719     /* this point should never be reached */
720     /*NOTREACHED*/
721     assert( FALSE );
722   }
723 
724   /*                   --------------
725   ** At this point either one or both of the merging file lists
726   ** is empty. As such we can just finish of the merged list quickly
727   */
728   assert( new_file_list == NULL || old_file_list == NULL );
729 
730   /* ADD any more new items in the new_file_list */
731 
732   while( new_file_list != NULL ) {
733     merge_item(new_file_list);         /* merge the new item */
734     new_file_list = *last_link;        /* remove item from new list */
735   }
736   new_file_list = NULL;    /* new_file_list is now empty */
737 
738   /* DELETE any old items left in the old_file_list */
739   free_list(&old_file_list);
740 
741   /*                   --------------           */
742   /* Merger of file lists complete -- finialize */
743   merge_finish();
744 }
745 
746 
747 /*========================================================================*/
748 /* ------------------- Public Routines and Functions -------------------- */
749 
750 void
rescan_item(item)751 rescan_item(item)
752 /* If the given item is a visible file, and is newer
753 ** then rescan it loading any new image it may have now
754 */
755   Item  *item;
756 {
757   /* the files current modification time -- 0 if not a file or deleted */
758   time_t  time;
759 
760   if ( !item->visible  )
761     return;   /* not visible -- why were we called? */
762 
763   if ( IsFileItem(item) ) {
764     if ( (time = check_file_time(item->fname)) == 0 )
765       return;   /* it is deleted -- don't mess with things */
766 
767     if ( time > item->mtime ) { /* Update this items image */
768       free_image(item);         /* remove images from widget and free them */
769       load_image(item);         /* load the file image */
770       load_item(item);          /* set up the rest of the stuff */
771       assign_widget(item);      /* setup the widget again */
772     }
773   }
774 }
775 
776 
777 void
redisplay_images(unmap)778 redisplay_images(unmap)
779 /* Just redisplay the widgets after a slight option change */
780   Boolean unmap;  /* unmap the widget before reseting them? */
781 {
782   if( unmap && managed > 0 )
783     XtUnmanageChildren(widget_array, managed);
784 
785   assign_widgets();   /* Reset all the widget settings -- correctly */
786 
787   if( unmap && managed > 0 )
788     XtManageChildren(widget_array, managed);
789 }
790 
791 
792 void
reassign_images()793 reassign_images()
794 /* don't re-read the directory -- just re-assign the list */
795 {
796   new_file_list = file_list;  /* pretend that this is the new list */
797   file_list = NULL;
798 
799   /* totally reassign all items to the widgets */
800   merge_file_lists();         /* go through the list again */
801 }
802 
803 
804 void
rescan_images()805 rescan_images()
806 /* Do a fast rescan and merger of the images in the current directory.
807 ** The goal is to avoid re-reading files which were never changed.
808 */
809 {
810   new_file_list = get_files();  /* get items in the current directory */
811   merge_file_lists();           /* merge any changes into file_list */
812 }
813 
814 
815 void
scan_images()816 scan_images()
817 /* scan and display all the icon images in the current directory.
818 ** This is to be used when changing to new directories.
819 */
820 {
821   /* just free all the icon images and do a rescan which handles
822   ** the reading of new images perfectly fine on its own. We need
823   ** to unmanage them here, to avoid problems.
824   */
825   if( managed > 0 ) {
826     XtUnmanageChildren(widget_array, managed);
827     managed = 0;
828   }
829 
830   free_list(&file_list);
831   assert( items_allocated == 0 );  /* their should be no items left */
832 
833   rescan_images();
834 }
835 
836 /*========================================================================*/
837