0.8.0
[geeqie.git] / src / filelist.c
1 /*
2  * GQview image viewer
3  * (C)2000 John Ellis
4  *
5  * Author: John Ellis
6  *
7  */
8
9 #include "gqview.h"
10
11 static gint filelist_click_row = -1;
12
13 static void update_progressbar(gfloat val);
14
15 static gint file_is_hidden(gchar *name);
16 static gint file_is_in_filter(gchar *name);
17 static void add_to_filter(gchar *text, gint add);
18
19 static gint sort_list_cb(void *a, void *b);
20 static void filelist_read(gchar *path);
21
22 static gint file_find_closest_unaccounted(gint row, gint count, GList *ignore_list);
23
24 static void history_menu_select_cb(GtkWidget *widget, gpointer data);
25 static gchar *truncate_hist_text(gchar *t, gint l);
26 static void filelist_set_history(gchar *path);
27
28 /*
29  *-----------------------------------------------------------------------------
30  * file status information (private)
31  *-----------------------------------------------------------------------------
32  */
33
34 static void update_progressbar(gfloat val)
35 {
36         gtk_progress_bar_update (GTK_PROGRESS_BAR(info_progress_bar), val);
37 }
38
39 void update_status_label(gchar *text)
40 {
41         gchar *buf;
42         gint count;
43         gchar *ss = "";
44
45         if (text)
46                 {
47                 gtk_label_set(GTK_LABEL(info_status), text);
48                 return;
49                 }
50
51         if (slideshow_is_running()) ss = _(" Slideshow");
52
53         count = file_selection_count();
54         if (count > 0)
55                 buf = g_strdup_printf(_("%d files (%d)%s"), file_count(), count, ss);
56         else
57                 buf = g_strdup_printf(_("%d files%s"), file_count(), ss);
58
59         gtk_label_set(GTK_LABEL(info_status), buf);
60         g_free(buf);
61 }
62
63 /*
64  *-----------------------------------------------------------------------------
65  * file filtering
66  *-----------------------------------------------------------------------------
67  */
68
69 static gint file_is_hidden(gchar *name)
70 {
71         if (name[0] != '.') return FALSE;
72         if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')) return FALSE;
73         return TRUE;
74 }
75
76 static gint file_is_in_filter(gchar *name)
77 {
78         GList *work;
79         if (!filename_filter || file_filter_disable) return TRUE;
80
81         work = filename_filter;
82         while (work)
83                 {
84                 gchar *filter = work->data;
85                 gint lf = strlen(filter);
86                 gint ln = strlen(name);
87                 if (ln >= lf)
88                         {
89                         if (strncasecmp(name + ln - lf, filter, lf) == 0) return TRUE;
90                         }
91                 work = work->next;
92                 }
93
94         return FALSE;
95 }
96
97 static void add_to_filter(gchar *text, gint add)
98 {
99         if (add) filename_filter = g_list_append(filename_filter, g_strdup(text));
100 }
101
102 void rebuild_file_filter()
103 {
104         if (filename_filter)
105                 {
106                 g_list_foreach(filename_filter,(GFunc)g_free,NULL);
107                 g_list_free(filename_filter);
108                 filename_filter = NULL;
109                 }
110
111         add_to_filter(".jpg", filter_include_jpg);
112         add_to_filter(".jpeg", filter_include_jpg);
113         add_to_filter(".xpm", filter_include_xpm);
114         add_to_filter(".tif", filter_include_tif);
115         add_to_filter(".tiff", filter_include_tif);
116         add_to_filter(".gif", filter_include_gif);
117         add_to_filter(".png", filter_include_png);
118         add_to_filter(".ppm", filter_include_ppm);
119         add_to_filter(".pgm", filter_include_pgm);
120         add_to_filter(".pcx", filter_include_pcx);
121         add_to_filter(".bmp", filter_include_bmp);
122
123         if (custom_filter)
124                 {
125                 gchar *buf = g_strdup(custom_filter);
126                 gchar *pos_ptr_b;
127                 gchar *pos_ptr_e = buf;
128                 while(pos_ptr_e[0] != '\0')
129                         {
130                         pos_ptr_b = pos_ptr_e;
131                         while (pos_ptr_e[0] != ';' && pos_ptr_e[0] != '\0') pos_ptr_e++;
132                         if (pos_ptr_e[0] == ';')
133                                 {
134                                 pos_ptr_e[0] = '\0';
135                                 pos_ptr_e++;
136                                 }
137                         add_to_filter(pos_ptr_b, TRUE);
138                         }
139                 g_free(buf);
140                 }
141 }
142
143 /*
144  *-----------------------------------------------------------------------------
145  * load file list (private)
146  *-----------------------------------------------------------------------------
147  */
148
149 static gint sort_list_cb(void *a, void *b)
150 {
151         return strcmp((gchar *)a, (gchar *)b);
152 }
153
154 static void filelist_read(gchar *path)
155 {
156         DIR *dp;
157         struct dirent *dir;
158         struct stat ent_sbuf;
159
160         if((dp = opendir(path))==NULL)
161                 {
162                 /* dir not found */
163                 return;
164                 }
165         
166         g_list_foreach(dir_list,(GFunc)g_free,NULL);
167         g_list_free(dir_list);
168         dir_list = NULL;
169
170         g_list_foreach(file_list,(GFunc)g_free,NULL);
171         g_list_free(file_list);
172         file_list = NULL;
173
174         while ((dir = readdir(dp)) != NULL)
175                 {
176                 /* skips removed files */
177                 if (dir->d_ino > 0)
178                         {
179                         gchar *name = dir->d_name;
180                         if (show_dot_files || !file_is_hidden(name))
181                                 {
182                                 gchar *filepath = g_strconcat(path, "/", name, NULL);
183                                 if (stat(filepath,&ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
184                                         {
185                                         dir_list = g_list_prepend(dir_list, g_strdup(name));
186                                         }
187                                 else
188                                         {
189                                         if (file_is_in_filter(name))
190                                                 file_list = g_list_prepend(file_list, g_strdup(name));
191                                         }
192                                 g_free(filepath);
193                                 }
194                         }
195                 }
196
197         closedir(dp);
198
199         dir_list = g_list_sort(dir_list, (GCompareFunc) sort_list_cb);
200         file_list = g_list_sort(file_list, (GCompareFunc) sort_list_cb);
201 }
202
203 /*
204  *-----------------------------------------------------------------------------
205  * file list utilities to retrieve information (public)
206  *-----------------------------------------------------------------------------
207  */
208
209 gint file_count()
210 {
211         return g_list_length(file_list);
212 }
213
214 gint file_selection_count()
215 {
216         gint count = 0;
217         GList *work = GTK_CLIST(file_clist)->selection;
218         while(work)
219                 {
220                 count++;
221                 if (debug) printf("s = %d\n", GPOINTER_TO_INT(work->data));
222                 work = work->next;
223                 }
224
225         if (debug) printf("files selected = %d\n", count);
226
227         return count;
228 }
229
230 gint find_file_in_list(gchar *path)
231 {
232         GList *work = file_list;
233         gchar *buf;
234         gchar *name;
235         gint count = -1;
236
237         if (!path) return -1;
238
239         buf = remove_level_from_path(path);
240         if (strcmp(buf, current_path) != 0)
241                 {
242                 g_free(buf);
243                 return -1;
244                 }
245         g_free(buf);
246
247         name = filename_from_path(path);
248         while(work)
249                 {
250                 count++;
251                 if (strcmp(name, work->data) == 0) return count;
252                 work = work->next;
253                 }
254
255         return -1;
256 }
257
258 gchar *file_get_path(gint row)
259 {
260         gchar *path = NULL;
261         gchar *name = gtk_clist_get_row_data(GTK_CLIST(file_clist), row);
262
263         if (name) path = g_strconcat(current_path, "/", name, NULL);
264
265         return path;
266 }
267
268 gint file_is_selected(gint row)
269 {
270         GList *work = GTK_CLIST(file_clist)->selection;
271
272         while(work)
273                 {
274                 if (GPOINTER_TO_INT(work->data) == row) return TRUE;
275                 work = work->next;
276                 }
277
278         return FALSE;
279 }
280
281 /*
282  *-----------------------------------------------------------------------------
283  * utilities to retrieve list of selected files (public)
284  *-----------------------------------------------------------------------------
285  */
286
287 GList *file_get_selected_list()
288 {
289         GList *list = NULL;
290         GList *work = GTK_CLIST(file_clist)->selection;
291
292         while(work)
293                 {
294                 gchar *name = gtk_clist_get_row_data(GTK_CLIST(file_clist),
295                         GPOINTER_TO_INT(work->data));
296                 list = g_list_prepend(list, g_strconcat(current_path, "/", name, NULL));
297                 work = work->next;
298                 }
299
300         list = g_list_reverse(list);
301
302         return list;
303 }
304
305 void free_selected_list(GList *list)
306 {
307         g_list_foreach(list, (GFunc)g_free, NULL);
308         g_list_free(list);
309 }
310
311 gint file_clicked_is_selected()
312 {
313         return file_is_selected(filelist_click_row);
314 }
315
316 gchar *file_clicked_get_path()
317 {
318         return file_get_path(filelist_click_row);
319 }
320
321 /*
322  *-----------------------------------------------------------------------------
323  * image change routines
324  *-----------------------------------------------------------------------------
325  */
326
327 void file_image_change_to(gint row)
328 {
329         gtk_clist_unselect_all(GTK_CLIST(file_clist));
330         gtk_clist_select_row(GTK_CLIST(file_clist), row, -1);
331         if (gtk_clist_row_is_visible(GTK_CLIST(file_clist), row) != GTK_VISIBILITY_FULL)
332                 {
333                 gtk_clist_moveto(GTK_CLIST(file_clist), row, -1, 0.5, 0.0);
334                 }
335 }
336
337 void file_next_image()
338 {
339         gint current;
340         gint total;
341
342         if (slideshow_is_running())
343                 {
344                 slideshow_next();
345                 return;
346                 }
347
348         current = find_file_in_list(image_get_path());
349         total = file_count();
350
351         if (current >= 0)
352                 {
353                 if (current < total - 1)
354                         {
355                         file_image_change_to(current + 1);
356                         }
357                 }
358         else
359                 {
360                 file_image_change_to(0);
361                 }
362 }
363
364 void file_prev_image()
365 {
366         gint current;
367
368         if (slideshow_is_running())
369                 {
370                 slideshow_prev();
371                 return;
372                 }
373
374         current = find_file_in_list(image_get_path());
375
376         if (current >= 0)
377                 {
378                 if (current > 0)
379                         {
380                         file_image_change_to(current - 1);
381                         }
382                 }
383         else
384                 {
385                 file_image_change_to(file_count() - 1);
386                 }
387 }
388
389 void file_first_image()
390 {
391         gint current = find_file_in_list(image_get_path());
392         if (current != 0 && file_count() > 0)
393                 {
394                 file_image_change_to(0);
395                 }
396 }
397
398 void file_last_image()
399 {
400         gint current = find_file_in_list(image_get_path());
401         gint count = file_count();
402         if (current != count - 1 && count > 0)
403                 {
404                 file_image_change_to(count - 1);
405                 }
406 }
407
408 /*
409  *-----------------------------------------------------------------------------
410  * file delete/rename update routines
411  *-----------------------------------------------------------------------------
412  */
413
414 static gint file_find_closest_unaccounted(gint row, gint count, GList *ignore_list)
415 {
416         GList *list = NULL;
417         GList *work;
418         gint rev = row - 1;
419         row ++;
420
421         work = ignore_list;
422         while(work)
423                 {
424                 gint f = find_file_in_list(work->data);
425                 if (f >= 0) list = g_list_append(list, GINT_TO_POINTER(f));
426                 work = work->next;
427                 }
428
429         while(list)
430                 {
431                 gint c = TRUE;
432                 work = list;
433                 while(work && c)
434                         {
435                         gpointer p = work->data;
436                         work = work->next;
437                         if (row == GPOINTER_TO_INT(p))
438                                 {
439                                 row++;
440                                 c = FALSE;
441                                 }
442                         if (rev == GPOINTER_TO_INT(p))
443                                 {
444                                 rev--;
445                                 c = FALSE;
446                                 }
447                         if (!c) list = g_list_remove(list, p);
448                         }
449                 if (c && list)
450                         {
451                         g_list_free(list);
452                         list = NULL;
453                         }
454                 }
455         if (row > count - 1)
456                 {
457                 if (rev < 0)
458                         return -1;
459                 else
460                         return rev;
461                 }
462         else
463                 {
464                 return row;
465                 }
466 }
467
468 void file_is_gone(gchar *path, GList *ignore_list)
469 {
470         GList *list;
471         gchar *name;
472         gint row;
473         gint new_row = -1;
474         row = find_file_in_list(path);
475         if (row < 0) return;
476
477         if (file_is_selected(row) /* && file_selection_count() == 1 */)
478                 {
479                 gint n = file_count();
480                 if (ignore_list)
481                         {
482                         new_row = file_find_closest_unaccounted(row, n, ignore_list);
483                         if (debug) printf("row = %d, closest is %d\n", row, new_row);
484                         }
485                 else
486                         {
487                         if (row + 1 < n)
488                                 {
489                                 new_row = row + 1;
490                                 }
491                         else if (row > 0)
492                                 {
493                                 new_row = row - 1;
494                                 }
495                         }
496                 gtk_clist_unselect_all(GTK_CLIST(file_clist));
497                 if (new_row >= 0)
498                         {
499                         gtk_clist_select_row(GTK_CLIST(file_clist), new_row, -1);
500                         file_image_change_to(new_row);
501                         }
502                 else
503                         {
504                         image_change_to(NULL);
505                         }
506                 }
507
508         gtk_clist_remove(GTK_CLIST(file_clist), row);
509         list = g_list_nth(file_list, row);
510         name = list->data;
511         file_list = g_list_remove(file_list, name);
512         g_free(name);
513         update_status_label(NULL);
514 }
515
516 void file_is_renamed(gchar *source, gchar *dest)
517 {
518         gint row;
519         gchar *source_base;
520         gchar *dest_base;
521
522         if (image_get_path() && !strcmp(source, image_get_path()))
523                 {
524                 image_set_path(dest);
525                 }
526
527         row = find_file_in_list(source);
528         if (row < 0) return;
529
530         source_base = remove_level_from_path(source);
531         dest_base = remove_level_from_path(dest);
532
533         if (strcmp(source_base, dest_base) == 0)
534                 {
535                 gchar *name;
536                 gint n;
537                 GList *work = g_list_nth(file_list, row);
538                 name = work->data;
539                 file_list = g_list_remove(file_list, name);
540                 g_free(name);
541                 name = g_strdup(filename_from_path(dest));
542                 file_list = g_list_insert_sorted(file_list, name, (GCompareFunc) sort_list_cb);
543                 n = g_list_index(file_list, name);
544
545                 if (gtk_clist_get_cell_type(GTK_CLIST(file_clist), row, 0) != GTK_CELL_PIXTEXT)
546                         {
547                         gtk_clist_set_text (GTK_CLIST(file_clist), row, 0, name);
548                         }
549                 else
550                         {
551                         guint8 spacing = 0;
552                         GdkPixmap *pixmap = NULL;
553                         GdkBitmap *mask = NULL;
554                         gtk_clist_get_pixtext(GTK_CLIST(file_clist), row, 0,
555                                 NULL, &spacing, &pixmap, &mask);
556                         gtk_clist_set_pixtext(GTK_CLIST(file_clist), row, 0,
557                                 name, spacing, pixmap, mask);
558                         }
559
560                 gtk_clist_set_row_data(GTK_CLIST(file_clist), row, name);
561                 gtk_clist_row_move(GTK_CLIST(file_clist), row, n);
562                 }
563         else
564                 {
565                 GList *work = g_list_nth(file_list, row);
566                 gchar *name = work->data;
567                 file_list = g_list_remove(file_list, name);
568                 gtk_clist_remove(GTK_CLIST(file_clist), row);
569                 g_free(name);
570                 update_status_label(NULL);
571                 }
572
573         g_free(source_base);
574         g_free(dest_base);
575         
576 }
577
578 /*
579  *-----------------------------------------------------------------------------
580  * directory list callbacks
581  *-----------------------------------------------------------------------------
582  */
583
584 void dir_select_cb(GtkWidget *widget, gint row, gint col,
585                    GdkEvent *event, gpointer data)
586 {
587         gchar *name;
588         gchar *new_path;
589         name = gtk_clist_get_row_data (GTK_CLIST(dir_clist), row);
590         if (strcmp(name, ".") == 0)
591                 {
592                 new_path = g_strdup(current_path);
593                 }
594         else if (strcmp(name, "..") == 0)
595                 {
596                 new_path = remove_level_from_path(current_path);
597                 }
598         else
599                 {
600                 if (strcmp(current_path, "/") == 0)
601                         new_path = g_strconcat(current_path, name, NULL);
602                 else
603                         new_path = g_strconcat(current_path, "/", name, NULL);
604                 }
605         filelist_change_to(new_path);
606         g_free(new_path);
607 }
608
609 void dir_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
610 {
611         gint row = -1;
612         gint col = -1;
613
614         gtk_clist_get_selection_info (GTK_CLIST (widget), bevent->x, bevent->y, &row, &col);
615
616         if (bevent->button == 2)
617                 {
618                 gtk_object_set_user_data(GTK_OBJECT(dir_clist), GINT_TO_POINTER(row));
619                 }
620 }
621
622 /*
623  *-----------------------------------------------------------------------------
624  * file list callbacks
625  *-----------------------------------------------------------------------------
626  */
627
628 void file_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
629 {
630         gint row = -1;
631         gint col = -1;
632
633         gtk_clist_get_selection_info (GTK_CLIST (widget), bevent->x, bevent->y, &row, &col);
634         if (row == -1 || col == -1)
635                 {
636                 filelist_click_row = -1;
637                 return;
638                 }
639
640         filelist_click_row = row;
641
642         if (bevent->button == 3)
643                 {
644                 file_clist_highlight_set();
645                 gtk_menu_popup (GTK_MENU(menu_file_popup), NULL, NULL, NULL, NULL,
646                                 bevent->button, bevent->time);
647                 }
648 }
649
650 void file_select_cb(GtkWidget *widget, gint row, gint col,
651                    GdkEvent *event, gpointer data)
652 {
653         gchar *name;
654         gchar *path;
655
656         if (file_selection_count() != 1)
657                 {
658                 update_status_label(NULL);
659                 return;
660                 }
661
662         name = gtk_clist_get_row_data(GTK_CLIST(file_clist), row);
663         path = g_strconcat(current_path, "/", name, NULL);
664         image_change_to(path);
665         update_status_label(NULL);
666         g_free(path);
667 }
668
669 void file_unselect_cb(GtkWidget *widget, gint row, gint col,
670                    GdkEvent *event, gpointer data)
671 {
672 #if 0
673         gchar *name;
674         gchar *path;
675
676         name = gtk_clist_get_row_data(GTK_CLIST(file_clist), row);
677         path = g_strconcat(current_path, "/", name, NULL);
678
679         if (strcmp(path, image_get_path()) == 0)
680                 {
681                 if (file_selection_count() > 0 && !file_is_selected(find_file_in_list(image_get_path())) )
682                         {
683                         gint new_row = GPOINTER_TO_INT(GTK_CLIST(file_clist)->selection->data);
684                         gchar *new_name = gtk_clist_get_row_data(GTK_CLIST(file_clist), new_row);
685                         gchar *new_path = g_strconcat(current_path, "/", new_name, NULL);
686                         image_change_to(new_path);
687                         g_free(new_path);
688                         }
689                 }
690         g_free(path);
691 #endif
692         update_status_label(NULL);
693 }
694
695 /*
696  *-----------------------------------------------------------------------------
697  * file list highlight utils
698  *-----------------------------------------------------------------------------
699  */
700
701 void file_clist_highlight_set()
702 {
703         if (file_clicked_is_selected()) return;
704
705         gtk_clist_set_background(GTK_CLIST(file_clist), filelist_click_row,
706                 &GTK_WIDGET (file_clist)->style->bg[GTK_STATE_PRELIGHT]);
707         gtk_clist_set_foreground(GTK_CLIST(file_clist), filelist_click_row,
708                 &GTK_WIDGET (file_clist)->style->fg[GTK_STATE_PRELIGHT]);
709 }
710
711 void file_clist_highlight_unset()
712 {
713         if (file_clicked_is_selected()) return;
714
715         gtk_clist_set_background(GTK_CLIST(file_clist), filelist_click_row, NULL);
716         gtk_clist_set_foreground(GTK_CLIST(file_clist), filelist_click_row, NULL);
717 }
718
719 /*
720  *-----------------------------------------------------------------------------
721  * path entry and history menu
722  *-----------------------------------------------------------------------------
723  */
724
725 void path_entry_tab_cb(gchar *newdir, gpointer data)
726 {
727         gchar *new_path;
728         gchar *buf;
729         gint found = FALSE;
730
731         new_path = g_strdup(newdir);
732         parse_out_relatives(new_path);
733         buf = remove_level_from_path(new_path);
734
735         if (buf && current_path && strcmp(buf, current_path) == 0)
736                 {
737                 GList *work;
738                 gchar *part;
739
740                 part = filename_from_path(new_path);
741                 work = file_list;
742
743                 while(part && work)
744                         {
745                         gchar *name = work->data;
746                         work = work->next;
747
748                         if (strncmp(part, name, strlen(part)) == 0)
749                                 {
750                                 gint row = g_list_index(file_list, name);
751                                 if (!gtk_clist_row_is_visible(GTK_CLIST(file_clist), row) != GTK_VISIBILITY_FULL)
752                                         {
753                                         gtk_clist_moveto(GTK_CLIST(file_clist), row, -1, 0.5, 0.0);
754                                         }
755                                 found = TRUE;
756                                 break;
757                                 }
758                         }
759                 }
760
761         if (!found && new_path && current_path &&
762             strcmp(new_path, current_path) != 0 && isdir(new_path))
763                 {
764                 filelist_change_to(new_path);
765                 /* we are doing tab completion, add '/' back */
766                 gtk_entry_append_text(GTK_ENTRY(path_entry), "/");
767                 }
768
769         g_free(buf);
770         g_free(new_path);
771 }
772
773 void path_entry_cb(gchar *newdir, gpointer data)
774 {
775         gchar *new_path = g_strdup(newdir);
776         parse_out_relatives(new_path);
777         if (isdir(new_path))
778                 filelist_change_to(new_path);
779         else if (isfile(new_path))
780                 {
781                 gchar *path = remove_level_from_path(new_path);
782                 filelist_change_to(path);
783                 g_free(path);
784                 image_change_to(new_path);
785                 }
786         g_free(new_path);
787 }
788
789 static void history_menu_select_cb(GtkWidget *widget, gpointer data)
790 {
791         gchar *new_path = data;
792         filelist_change_to(new_path);
793 }
794
795 static gchar *truncate_hist_text(gchar *t, gint l)
796 {
797         gchar *tp;
798         gchar *tbuf;
799         if (l >= strlen(t)) return g_strdup(t);
800         tp = t + strlen(t) - l;
801         while (tp[0] != '/' && tp < t + strlen(t)) tp++;
802                 /* this checks to see if directory name is longer than l, if so
803                  * reset the length of name to l, it's better to have a partial
804                  * name than no name at all.
805                  */
806         if (tp >= t + strlen(t)) tp = t + strlen(t) - l;
807         tbuf = g_strconcat("/...", tp, NULL);
808         return tbuf;
809 }
810
811 static void filelist_set_history(gchar *path)
812 {
813         static GList *history_list = NULL;
814         gchar *buf;
815         gchar *buf_ptr;
816         GtkWidget *menu;
817         GtkWidget *item;
818
819         if (!path) return;
820
821         gtk_entry_set_text(GTK_ENTRY(path_entry), current_path);
822
823         if (history_list)
824                 {
825                 g_list_foreach(history_list, (GFunc)g_free, NULL);
826                 g_list_free(history_list);
827                 history_list = NULL;
828                 }
829
830         menu = gtk_menu_new();
831
832         buf = g_strdup(path);
833         buf_ptr = buf + strlen(buf) - 1 ;
834         while (buf_ptr > buf)
835                 {
836                 gchar *full_path;
837                 gchar *truncated;
838                 truncated = truncate_hist_text(buf, 32);
839
840                 full_path = g_strdup(buf);
841                 history_list = g_list_append(history_list, full_path);
842
843                 item = gtk_menu_item_new_with_label (truncated);
844                 gtk_signal_connect (GTK_OBJECT (item), "activate",
845                         (GtkSignalFunc) history_menu_select_cb, full_path);
846
847                 gtk_menu_append (GTK_MENU (menu), item);
848                 gtk_widget_show (item);
849
850                 g_free(truncated);
851
852                 while (buf_ptr[0] != '/' && buf_ptr > buf) buf_ptr--;
853                 buf_ptr[0] = '\0';
854                 }
855         g_free(buf);
856
857         item = gtk_menu_item_new_with_label ("/");
858
859         gtk_signal_connect (GTK_OBJECT (item), "activate",
860                 (GtkSignalFunc) history_menu_select_cb, "/");
861
862         gtk_menu_append (GTK_MENU (menu), item);
863         gtk_widget_show (item);
864
865         gtk_option_menu_set_menu(GTK_OPTION_MENU(history_menu), menu);
866 }
867
868 /*
869  *-----------------------------------------------------------------------------
870  * list update routines (public)
871  *-----------------------------------------------------------------------------
872  */
873
874 static gint thumbs_running = 0;
875
876 void interrupt_thumbs()
877 {
878         if (thumbs_running > 0) thumbs_running ++;
879 }
880
881 void filelist_populate_clist()
882 {
883         GList *work;
884         gint width;
885         gint tmp_width;
886         gint row;
887         gchar *image_name = NULL;
888         gchar *buf;
889
890         gint row_p = 0;
891         gchar *text;
892         guint8 spacing;
893         GdkPixmap *nopixmap;
894         GdkBitmap *nomask;
895
896         interrupt_thumbs();
897
898         filelist_set_history(current_path);
899
900         gtk_clist_freeze (GTK_CLIST (dir_clist));
901         gtk_clist_clear (GTK_CLIST (dir_clist));
902
903         width = 0;
904         work = dir_list;
905         while(work)
906                 {
907                 gchar *buf[2];
908                 buf[0] = work->data;
909                 buf[1] = NULL;
910                 row = gtk_clist_append(GTK_CLIST(dir_clist), buf);
911                 gtk_clist_set_row_data (GTK_CLIST(dir_clist), row, work->data);
912                 tmp_width = gdk_string_width(dir_clist->style->font, buf[0]);
913                 if (tmp_width > width) width = tmp_width;
914                 work = work->next;
915                 }
916
917         gtk_clist_set_column_width(GTK_CLIST(dir_clist), 0, width);
918         gtk_clist_thaw(GTK_CLIST (dir_clist));
919
920         buf = remove_level_from_path(image_get_path());
921         if (buf && strcmp(buf, current_path) == 0)
922                 {
923                 image_name = image_get_name();
924                 }
925         g_free(buf);
926
927         gtk_clist_freeze (GTK_CLIST (file_clist));
928
929         if (!thumbnails_enabled)
930                 {
931                 gtk_clist_set_row_height (GTK_CLIST(file_clist),
932                         GTK_WIDGET(file_clist)->style->font->ascent +
933                         GTK_WIDGET(file_clist)->style->font->descent + 1);
934                 }
935         else
936                 {
937                 gtk_clist_set_row_height (GTK_CLIST(file_clist), thumb_max_height + 2);
938                 maintain_thumbnail_dir(current_path, FALSE);
939                 }
940
941         width = 0;
942         work = file_list;
943
944         while(work)
945                 {
946                 gint has_pixmap;
947                 gint match;
948                 gchar *name = work->data;
949                 gint done = FALSE;
950
951                 while (!done)
952                         {
953                         if (GTK_CLIST(file_clist)->rows > row_p)
954                                 {
955                                 if (gtk_clist_get_cell_type(GTK_CLIST(file_clist),row_p, 0) == GTK_CELL_PIXTEXT)
956                                         {
957                                         gtk_clist_get_pixtext(GTK_CLIST(file_clist), row_p, 0, &text, &spacing, &nopixmap, &nomask);
958                                         has_pixmap = TRUE;
959                                         }
960                                 else
961                                         {
962                                         gtk_clist_get_text(GTK_CLIST(file_clist), row_p, 0, &text);
963                                         has_pixmap = FALSE;
964                                         }
965                                 match = strcmp(name, text);
966                                 }
967                         else
968                                 {
969                                 match = -1;
970                                 }
971
972                         if (match < 0)
973                                 {
974                                 gchar *buf[2];
975                                 buf[0] = name;
976                                 buf[1] = NULL;
977                                 row = gtk_clist_insert(GTK_CLIST(file_clist), row_p, buf);
978                                 gtk_clist_set_row_data (GTK_CLIST(file_clist), row, name);
979                                 if (thumbnails_enabled)
980                                         gtk_clist_set_shift(GTK_CLIST(file_clist), row, 0, 0, 5 + thumb_max_width);
981                                 done = TRUE;
982                                 if (image_name && strcmp(name, image_name) == 0)
983                                         gtk_clist_select_row(GTK_CLIST(file_clist), row, 0);
984                                 }
985                         else if (match > 0)
986                                 {
987                                 gtk_clist_remove(GTK_CLIST(file_clist), row_p);
988                                 }
989                         else
990                                 {
991                                 if (thumbnails_enabled && !has_pixmap)
992                                         gtk_clist_set_shift(GTK_CLIST(file_clist), row_p, 0, 0, 5 + thumb_max_width);
993                                 if (!thumbnails_enabled/* && has_pixmap*/)
994                                         {
995                                         gtk_clist_set_text(GTK_CLIST(file_clist), row_p, 0, name);
996                                         gtk_clist_set_shift(GTK_CLIST(file_clist), row_p, 0, 0, 0);
997                                         }
998                                 gtk_clist_set_row_data (GTK_CLIST(file_clist), row_p, name);
999                                 done = TRUE;
1000                                 }
1001                         }
1002                 row_p++;
1003
1004                 if (thumbnails_enabled)
1005                         tmp_width = gdk_string_width(file_clist->style->font, name) + thumb_max_width + 5;
1006                 else
1007                         tmp_width = gdk_string_width(file_clist->style->font, name);
1008                 if (tmp_width > width) width = tmp_width;
1009                 work = work->next;
1010                 }
1011
1012         while (GTK_CLIST(file_clist)->rows > row_p)
1013                 gtk_clist_remove(GTK_CLIST(file_clist), row_p);
1014
1015         gtk_clist_set_column_width(GTK_CLIST(file_clist), 0, width);
1016         gtk_clist_thaw(GTK_CLIST (file_clist));
1017
1018         if (thumbnails_enabled)
1019                 {
1020                 GList *done_list = NULL;
1021                 gint past_run;
1022                 gint finished = FALSE;
1023                 gint j;
1024                 gint count = 0;
1025                 update_status_label(_("Loading thumbs..."));
1026
1027                 for (j = 0; j < GTK_CLIST(file_clist)->rows; j++)
1028                         {
1029                         done_list = g_list_prepend(done_list, GINT_TO_POINTER(FALSE));
1030                         }
1031
1032                 /* load thumbs */
1033
1034                 while (!finished && done_list)
1035                         {
1036                         gint p = -1;
1037                         gint r = -1;
1038                         gint c = -1;
1039                         gtk_clist_get_selection_info (GTK_CLIST(file_clist), 1, 1, &r, &c);
1040                         if (r != -1)
1041                                 {
1042                                 work = g_list_nth(done_list, r);
1043                                 while (work)
1044                                         {
1045                                         if (gtk_clist_row_is_visible(GTK_CLIST(file_clist), r))
1046                                                 {
1047                                                 if (!GPOINTER_TO_INT(work->data))
1048                                                         {
1049                                                         work->data = GINT_TO_POINTER(TRUE);
1050                                                         p = r;
1051                                                         work = NULL;
1052                                                         }
1053                                                 else
1054                                                         {
1055                                                         r++;
1056                                                         work = work->next;
1057                                                         }
1058                                                 }
1059                                         else
1060                                                 {
1061                                                 work = NULL;
1062                                                 }
1063                                         }
1064                                 }
1065                         if (p == -1)
1066                                 {
1067                                 work = done_list;
1068                                 r = 0;
1069                                 while(work && p == -1)
1070                                         {
1071                                         if (!GPOINTER_TO_INT(work->data))
1072                                                 {
1073                                                 p = r;
1074                                                 work->data = GINT_TO_POINTER(TRUE);
1075                                                 }
1076                                         else
1077                                                 {
1078                                                 r++;
1079                                                 work = work->next;
1080                                                 if (!work) finished = TRUE;
1081                                                 }
1082                                         }
1083                                 }
1084
1085                         count++;
1086
1087                         if (!finished && gtk_clist_get_cell_type(GTK_CLIST(file_clist), p, 0) != GTK_CELL_PIXTEXT)
1088                                 {
1089                                 GdkPixmap *pixmap = NULL;
1090                                 GdkBitmap *mask = NULL;
1091                                 gchar *name;
1092                                 gchar *path;
1093
1094                                 thumbs_running ++;
1095                                 past_run = thumbs_running;
1096                                 while(gtk_events_pending()) gtk_main_iteration();
1097                                 if (thumbs_running > past_run)
1098                                         {
1099                                         thumbs_running -= 2;
1100                                         update_progressbar(0.0);
1101                                         update_status_label(NULL);
1102                                         g_list_free(done_list);
1103                                         return;
1104                                         }
1105                                 thumbs_running --;
1106
1107                                 name = gtk_clist_get_row_data(GTK_CLIST(file_clist), p);
1108                                 path = g_strconcat (current_path, "/", name, NULL);
1109                                 spacing = create_thumbnail(path, &pixmap, &mask);
1110                                 g_free(path);
1111                                 gtk_clist_set_pixtext (GTK_CLIST(file_clist), p, 0, name, spacing + 5, pixmap, mask);
1112                                 gtk_clist_set_shift(GTK_CLIST(file_clist), p, 0, 0, 0);
1113
1114                                 update_progressbar((gfloat)(count) / GTK_CLIST(file_clist)->rows);
1115                                 }
1116                         }
1117                 update_progressbar(0.0);
1118                 g_list_free(done_list);
1119                 }
1120
1121         update_status_label(NULL);
1122 }
1123
1124 void filelist_refresh()
1125 {
1126         filelist_read(current_path);
1127         filelist_populate_clist();
1128         filelist_click_row = -1;
1129 }
1130
1131 void filelist_change_to(gchar *path)
1132 {
1133         if (!isdir(path)) return;
1134
1135         g_free(current_path);
1136         current_path = g_strdup(path);
1137
1138         filelist_refresh();
1139 }