a203f39e180f96a043210559a23e12f6e45209e0
[geeqie.git] / src / filelist.c
1 /*
2  * GQview image viewer
3  * (C)1999 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 = custom_filter;
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 = find_file_in_list(image_get_path());
340         gint total = file_count();
341
342         if (current >= 0)
343                 {
344                 if (current < total - 1)
345                         {
346                         file_image_change_to(current + 1);
347                         }
348                 }
349         else
350                 {
351                 file_image_change_to(0);
352                 }
353 }
354
355 void file_prev_image()
356 {
357         gint current = find_file_in_list(image_get_path());
358
359         if (current >= 0)
360                 {
361                 if (current > 0)
362                         {
363                         file_image_change_to(current - 1);
364                         }
365                 }
366         else
367                 {
368                 file_image_change_to(file_count() - 1);
369                 }
370 }
371
372 void file_first_image()
373 {
374         gint current = find_file_in_list(image_get_path());
375         if (current != 0 && file_count() > 0)
376                 {
377                 file_image_change_to(0);
378                 }
379 }
380
381 void file_last_image()
382 {
383         gint current = find_file_in_list(image_get_path());
384         gint count = file_count();
385         if (current != count - 1 && count > 0)
386                 {
387                 file_image_change_to(count - 1);
388                 }
389 }
390
391 /*
392  *-----------------------------------------------------------------------------
393  * file delete/rename update routines
394  *-----------------------------------------------------------------------------
395  */
396
397 static gint file_find_closest_unaccounted(gint row, gint count, GList *ignore_list)
398 {
399         GList *list = NULL;
400         GList *work;
401         gint rev = row - 1;
402         row ++;
403
404         work = ignore_list;
405         while(work)
406                 {
407                 gint f = find_file_in_list(work->data);
408                 if (f >= 0) list = g_list_append(list, GINT_TO_POINTER(f));
409                 work = work->next;
410                 }
411
412         while(list)
413                 {
414                 gint c = TRUE;
415                 work = list;
416                 while(work && c)
417                         {
418                         gpointer p = work->data;
419                         work = work->next;
420                         if (row == GPOINTER_TO_INT(p))
421                                 {
422                                 row++;
423                                 c = FALSE;
424                                 }
425                         if (rev == GPOINTER_TO_INT(p))
426                                 {
427                                 rev--;
428                                 c = FALSE;
429                                 }
430                         if (!c) list = g_list_remove(list, p);
431                         }
432                 if (c && list)
433                         {
434                         g_list_free(list);
435                         list = NULL;
436                         }
437                 }
438         if (row > count - 1)
439                 {
440                 if (rev < 0)
441                         return -1;
442                 else
443                         return rev;
444                 }
445         else
446                 {
447                 return row;
448                 }
449 }
450
451 void file_is_gone(gchar *path, GList *ignore_list)
452 {
453         GList *list;
454         gchar *name;
455         gint row;
456         gint new_row = -1;
457         row = find_file_in_list(path);
458         if (row < 0) return;
459
460         if (file_is_selected(row) /* && file_selection_count() == 1 */)
461                 {
462                 gint n = file_count();
463                 if (ignore_list)
464                         {
465                         new_row = file_find_closest_unaccounted(row, n, ignore_list);
466                         if (debug) printf("row = %d, closest is %d\n", row, new_row);
467                         }
468                 else
469                         {
470                         if (row + 1 < n)
471                                 {
472                                 new_row = row + 1;
473                                 }
474                         else if (row > 0)
475                                 {
476                                 new_row = row - 1;
477                                 }
478                         }
479                 gtk_clist_unselect_all(GTK_CLIST(file_clist));
480                 if (new_row >= 0)
481                         {
482                         gtk_clist_select_row(GTK_CLIST(file_clist), new_row, -1);
483                         file_image_change_to(new_row);
484                         }
485                 else
486                         {
487                         image_change_to(NULL);
488                         }
489                 }
490
491         gtk_clist_remove(GTK_CLIST(file_clist), row);
492         list = g_list_nth(file_list, row);
493         name = list->data;
494         file_list = g_list_remove(file_list, name);
495         g_free(name);
496         update_status_label(NULL);
497 }
498
499 void file_is_renamed(gchar *source, gchar *dest)
500 {
501         gint row;
502         gchar *source_base;
503         gchar *dest_base;
504
505         if (image_get_path() && !strcmp(source, image_get_path()))
506                 {
507                 image_set_path(dest);
508                 }
509
510         row = find_file_in_list(source);
511         if (row < 0) return;
512
513         source_base = remove_level_from_path(source);
514         dest_base = remove_level_from_path(dest);
515
516         if (strcmp(source_base, dest_base) == 0)
517                 {
518                 gchar *name;
519                 gint n;
520                 GList *work = g_list_nth(file_list, row);
521                 name = work->data;
522                 file_list = g_list_remove(file_list, name);
523                 g_free(name);
524                 name = g_strdup(filename_from_path(dest));
525                 file_list = g_list_insert_sorted(file_list, name, (GCompareFunc) sort_list_cb);
526                 n = g_list_index(file_list, name);
527
528                 if (gtk_clist_get_cell_type(GTK_CLIST(file_clist), row, 0) != GTK_CELL_PIXTEXT)
529                         {
530                         gtk_clist_set_text (GTK_CLIST(file_clist), row, 0, name);
531                         }
532                 else
533                         {
534                         guint8 spacing = 0;
535                         GdkPixmap *pixmap = NULL;
536                         GdkBitmap *mask = NULL;
537                         gtk_clist_get_pixtext(GTK_CLIST(file_clist), row, 0,
538                                 NULL, &spacing, &pixmap, &mask);
539                         gtk_clist_set_pixtext(GTK_CLIST(file_clist), row, 0,
540                                 name, spacing, pixmap, mask);
541                         }
542
543                 gtk_clist_set_row_data(GTK_CLIST(file_clist), row, name);
544                 gtk_clist_row_move(GTK_CLIST(file_clist), row, n);
545                 }
546         else
547                 {
548                 GList *work = g_list_nth(file_list, row);
549                 gchar *name = work->data;
550                 file_list = g_list_remove(file_list, name);
551                 gtk_clist_remove(GTK_CLIST(file_clist), row);
552                 g_free(name);
553                 update_status_label(NULL);
554                 }
555
556         g_free(source_base);
557         g_free(dest_base);
558         
559 }
560
561 /*
562  *-----------------------------------------------------------------------------
563  * directory list callbacks
564  *-----------------------------------------------------------------------------
565  */
566
567 void dir_select_cb(GtkWidget *widget, gint row, gint col,
568                    GdkEvent *event, gpointer data)
569 {
570         gchar *name;
571         gchar *new_path;
572         name = gtk_clist_get_row_data (GTK_CLIST(dir_clist), row);
573         if (strcmp(name, ".") == 0)
574                 {
575                 new_path = g_strdup(current_path);
576                 }
577         else if (strcmp(name, "..") == 0)
578                 {
579                 new_path = remove_level_from_path(current_path);
580                 }
581         else
582                 {
583                 if (strcmp(current_path, "/") == 0)
584                         new_path = g_strconcat(current_path, name, NULL);
585                 else
586                         new_path = g_strconcat(current_path, "/", name, NULL);
587                 }
588         filelist_change_to(new_path);
589         g_free(new_path);
590 }
591
592 void dir_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
593 {
594         gint row = -1;
595         gint col = -1;
596
597         gtk_clist_get_selection_info (GTK_CLIST (widget), bevent->x, bevent->y, &row, &col);
598
599         if (bevent->button == 2)
600                 {
601                 gtk_object_set_user_data(GTK_OBJECT(dir_clist), GINT_TO_POINTER(row));
602                 }
603 }
604
605 /*
606  *-----------------------------------------------------------------------------
607  * file list callbacks
608  *-----------------------------------------------------------------------------
609  */
610
611 void file_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
612 {
613         gint row = -1;
614         gint col = -1;
615
616         gtk_clist_get_selection_info (GTK_CLIST (widget), bevent->x, bevent->y, &row, &col);
617         if (row == -1 || col == -1)
618                 {
619                 filelist_click_row = -1;
620                 return;
621                 }
622
623         filelist_click_row = row;
624
625         if (bevent->button == 3)
626                 {
627                 file_clist_highlight_set();
628                 gtk_menu_popup (GTK_MENU(menu_file_popup), NULL, NULL, NULL, NULL,
629                                 bevent->button, bevent->time);
630                 }
631 }
632
633 void file_select_cb(GtkWidget *widget, gint row, gint col,
634                    GdkEvent *event, gpointer data)
635 {
636         gchar *name;
637         gchar *path;
638
639         if (file_selection_count() != 1)
640                 {
641                 update_status_label(NULL);
642                 return;
643                 }
644
645         name = gtk_clist_get_row_data(GTK_CLIST(file_clist), row);
646         path = g_strconcat(current_path, "/", name, NULL);
647         image_change_to(path);
648         update_status_label(NULL);
649         g_free(path);
650 }
651
652 void file_unselect_cb(GtkWidget *widget, gint row, gint col,
653                    GdkEvent *event, gpointer data)
654 {
655 #if 0
656         gchar *name;
657         gchar *path;
658
659         name = gtk_clist_get_row_data(GTK_CLIST(file_clist), row);
660         path = g_strconcat(current_path, "/", name, NULL);
661
662         if (strcmp(path, image_get_path()) == 0)
663                 {
664                 if (file_selection_count() > 0 && !file_is_selected(find_file_in_list(image_get_path())) )
665                         {
666                         gint new_row = GPOINTER_TO_INT(GTK_CLIST(file_clist)->selection->data);
667                         gchar *new_name = gtk_clist_get_row_data(GTK_CLIST(file_clist), new_row);
668                         gchar *new_path = g_strconcat(current_path, "/", new_name, NULL);
669                         image_change_to(new_path);
670                         g_free(new_path);
671                         }
672                 }
673         g_free(path);
674 #endif
675         update_status_label(NULL);
676 }
677
678 /*
679  *-----------------------------------------------------------------------------
680  * file list highlight utils
681  *-----------------------------------------------------------------------------
682  */
683
684 void file_clist_highlight_set()
685 {
686         if (file_clicked_is_selected()) return;
687
688         gtk_clist_set_background(GTK_CLIST(file_clist), filelist_click_row,
689                 &GTK_WIDGET (file_clist)->style->bg[GTK_STATE_PRELIGHT]);
690         gtk_clist_set_foreground(GTK_CLIST(file_clist), filelist_click_row,
691                 &GTK_WIDGET (file_clist)->style->fg[GTK_STATE_PRELIGHT]);
692 }
693
694 void file_clist_highlight_unset()
695 {
696         if (file_clicked_is_selected()) return;
697
698         gtk_clist_set_background(GTK_CLIST(file_clist), filelist_click_row, NULL);
699         gtk_clist_set_foreground(GTK_CLIST(file_clist), filelist_click_row, NULL);
700 }
701
702 /*
703  *-----------------------------------------------------------------------------
704  * path entry and history menu
705  *-----------------------------------------------------------------------------
706  */
707
708 void path_entry_cb(gchar *newdir, gpointer data)
709 {
710         gchar *new_path = g_strdup(newdir);
711         parse_out_relatives(new_path);
712         if (isdir(new_path))
713                 filelist_change_to(new_path);
714         else if (isfile(new_path))
715                 {
716                 gchar *path = remove_level_from_path(new_path);
717                 filelist_change_to(path);
718                 g_free(path);
719                 image_change_to(new_path);
720                 }
721         g_free(new_path);
722 }
723
724 static void history_menu_select_cb(GtkWidget *widget, gpointer data)
725 {
726         gchar *new_path = data;
727         filelist_change_to(new_path);
728 }
729
730 static gchar *truncate_hist_text(gchar *t, gint l)
731 {
732         gchar *tp;
733         gchar *tbuf;
734         if (l >= strlen(t)) return g_strdup(t);
735         tp = t + strlen(t) - l;
736         while (tp[0] != '/' && tp < t + strlen(t)) tp++;
737                 /* this checks to see if directory name is longer than l, if so
738                  * reset the length of name to l, it's better to have a partial
739                  * name than no name at all.
740                  */
741         if (tp >= t + strlen(t)) tp = t + strlen(t) - l;
742         tbuf = g_strconcat("/...", tp, NULL);
743         return tbuf;
744 }
745
746 static void filelist_set_history(gchar *path)
747 {
748         static GList *history_list = NULL;
749         gchar *buf;
750         gchar *buf_ptr;
751         GtkWidget *menu;
752         GtkWidget *item;
753
754         if (!path) return;
755
756         gtk_entry_set_text(GTK_ENTRY(path_entry), current_path);
757
758         if (history_list)
759                 {
760                 g_list_foreach(history_list, (GFunc)g_free, NULL);
761                 g_list_free(history_list);
762                 history_list = NULL;
763                 }
764
765         menu = gtk_menu_new();
766
767         buf = g_strdup(path);
768         buf_ptr = buf + strlen(buf) - 1 ;
769         while (buf_ptr > buf)
770                 {
771                 gchar *full_path;
772                 gchar *truncated;
773                 truncated = truncate_hist_text(buf, 32);
774
775                 full_path = g_strdup(buf);
776                 history_list = g_list_append(history_list, full_path);
777
778                 item = gtk_menu_item_new_with_label (truncated);
779                 gtk_signal_connect (GTK_OBJECT (item), "activate",
780                         (GtkSignalFunc) history_menu_select_cb, full_path);
781
782                 gtk_menu_append (GTK_MENU (menu), item);
783                 gtk_widget_show (item);
784
785                 g_free(truncated);
786
787                 while (buf_ptr[0] != '/' && buf_ptr > buf) buf_ptr--;
788                 buf_ptr[0] = '\0';
789                 }
790         g_free(buf);
791
792         item = gtk_menu_item_new_with_label ("/");
793
794         gtk_signal_connect (GTK_OBJECT (item), "activate",
795                 (GtkSignalFunc) history_menu_select_cb, "/");
796
797         gtk_menu_append (GTK_MENU (menu), item);
798         gtk_widget_show (item);
799
800         gtk_option_menu_set_menu(GTK_OPTION_MENU(history_menu), menu);
801 }
802
803 /*
804  *-----------------------------------------------------------------------------
805  * list update routines (public)
806  *-----------------------------------------------------------------------------
807  */
808
809 static gint thumbs_running = 0;
810
811 void interrupt_thumbs()
812 {
813         if (thumbs_running > 0) thumbs_running ++;
814 }
815
816 void filelist_populate_clist()
817 {
818         GList *work;
819         gint width;
820         gint tmp_width;
821         gint row;
822         gchar *image_name = NULL;
823         gchar *buf;
824
825         gint row_p = 0;
826         gchar *text;
827         guint8 spacing;
828         GdkPixmap *nopixmap;
829         GdkBitmap *nomask;
830
831         interrupt_thumbs();
832
833         filelist_set_history(current_path);
834
835         gtk_clist_freeze (GTK_CLIST (dir_clist));
836         gtk_clist_clear (GTK_CLIST (dir_clist));
837
838         width = 0;
839         work = dir_list;
840         while(work)
841                 {
842                 gchar *buf[2];
843                 buf[0] = work->data;
844                 buf[1] = NULL;
845                 row = gtk_clist_append(GTK_CLIST(dir_clist), buf);
846                 gtk_clist_set_row_data (GTK_CLIST(dir_clist), row, work->data);
847                 tmp_width = gdk_string_width(dir_clist->style->font, buf[0]);
848                 if (tmp_width > width) width = tmp_width;
849                 work = work->next;
850                 }
851
852         gtk_clist_set_column_width(GTK_CLIST(dir_clist), 0, width);
853         gtk_clist_thaw(GTK_CLIST (dir_clist));
854
855         buf = remove_level_from_path(image_get_path());
856         if (buf && strcmp(buf, current_path) == 0)
857                 {
858                 image_name = image_get_name();
859                 }
860         g_free(buf);
861
862         gtk_clist_freeze (GTK_CLIST (file_clist));
863
864         if (!thumbnails_enabled)
865                 {
866                 gtk_clist_set_row_height (GTK_CLIST(file_clist),
867                         GTK_WIDGET(file_clist)->style->font->ascent +
868                         GTK_WIDGET(file_clist)->style->font->descent + 1);
869                 }
870         else
871                 {
872                 gtk_clist_set_row_height (GTK_CLIST(file_clist), thumb_max_height + 2);
873                 maintain_thumbnail_dir(current_path, FALSE);
874                 }
875
876         width = 0;
877         work = file_list;
878
879         while(work)
880                 {
881                 gint has_pixmap;
882                 gint match;
883                 gchar *name = work->data;
884                 gint done = FALSE;
885
886                 while (!done)
887                         {
888                         if (GTK_CLIST(file_clist)->rows > row_p)
889                                 {
890                                 if (gtk_clist_get_cell_type(GTK_CLIST(file_clist),row_p, 0) == GTK_CELL_PIXTEXT)
891                                         {
892                                         gtk_clist_get_pixtext(GTK_CLIST(file_clist), row_p, 0, &text, &spacing, &nopixmap, &nomask);
893                                         has_pixmap = TRUE;
894                                         }
895                                 else
896                                         {
897                                         gtk_clist_get_text(GTK_CLIST(file_clist), row_p, 0, &text);
898                                         has_pixmap = FALSE;
899                                         }
900                                 match = strcmp(name, text);
901                                 }
902                         else
903                                 {
904                                 match = -1;
905                                 }
906
907                         if (match < 0)
908                                 {
909                                 gchar *buf[2];
910                                 buf[0] = name;
911                                 buf[1] = NULL;
912                                 row = gtk_clist_insert(GTK_CLIST(file_clist), row_p, buf);
913                                 gtk_clist_set_row_data (GTK_CLIST(file_clist), row, name);
914                                 if (thumbnails_enabled)
915                                         gtk_clist_set_shift(GTK_CLIST(file_clist), row, 0, 0, 5 + thumb_max_width);
916                                 done = TRUE;
917                                 if (image_name && strcmp(name, image_name) == 0)
918                                         gtk_clist_select_row(GTK_CLIST(file_clist), row, 0);
919                                 }
920                         else if (match > 0)
921                                 {
922                                 gtk_clist_remove(GTK_CLIST(file_clist), row_p);
923                                 }
924                         else
925                                 {
926                                 if (thumbnails_enabled && !has_pixmap)
927                                         gtk_clist_set_shift(GTK_CLIST(file_clist), row_p, 0, 0, 5 + thumb_max_width);
928                                 if (!thumbnails_enabled/* && has_pixmap*/)
929                                         {
930                                         gtk_clist_set_text(GTK_CLIST(file_clist), row_p, 0, name);
931                                         gtk_clist_set_shift(GTK_CLIST(file_clist), row_p, 0, 0, 0);
932                                         }
933                                 gtk_clist_set_row_data (GTK_CLIST(file_clist), row_p, name);
934                                 done = TRUE;
935                                 }
936                         }
937                 row_p++;
938
939                 if (thumbnails_enabled)
940                         tmp_width = gdk_string_width(file_clist->style->font, name) + thumb_max_width + 5;
941                 else
942                         tmp_width = gdk_string_width(file_clist->style->font, name);
943                 if (tmp_width > width) width = tmp_width;
944                 work = work->next;
945                 }
946
947         while (GTK_CLIST(file_clist)->rows > row_p)
948                 gtk_clist_remove(GTK_CLIST(file_clist), row_p);
949
950         gtk_clist_set_column_width(GTK_CLIST(file_clist), 0, width);
951         gtk_clist_thaw(GTK_CLIST (file_clist));
952
953         if (thumbnails_enabled)
954                 {
955                 GList *done_list = NULL;
956                 gint past_run;
957                 gint finished = FALSE;
958                 gint j;
959                 gint count = 0;
960                 update_status_label(_("Loading thumbs..."));
961
962                 for (j = 0; j < GTK_CLIST(file_clist)->rows; j++)
963                         {
964                         done_list = g_list_prepend(done_list, GINT_TO_POINTER(FALSE));
965                         }
966
967                 /* load thumbs */
968
969                 while (!finished && done_list)
970                         {
971                         gint p = -1;
972                         gint r = -1;
973                         gint c = -1;
974                         gtk_clist_get_selection_info (GTK_CLIST(file_clist), 1, 1, &r, &c);
975                         if (r != -1)
976                                 {
977                                 work = g_list_nth(done_list, r);
978                                 while (work)
979                                         {
980                                         if (gtk_clist_row_is_visible(GTK_CLIST(file_clist), r))
981                                                 {
982                                                 if (!GPOINTER_TO_INT(work->data))
983                                                         {
984                                                         work->data = GINT_TO_POINTER(TRUE);
985                                                         p = r;
986                                                         work = NULL;
987                                                         }
988                                                 else
989                                                         {
990                                                         r++;
991                                                         work = work->next;
992                                                         }
993                                                 }
994                                         else
995                                                 {
996                                                 work = NULL;
997                                                 }
998                                         }
999                                 }
1000                         if (p == -1)
1001                                 {
1002                                 work = done_list;
1003                                 r = 0;
1004                                 while(work && p == -1)
1005                                         {
1006                                         if (!GPOINTER_TO_INT(work->data))
1007                                                 {
1008                                                 p = r;
1009                                                 work->data = GINT_TO_POINTER(TRUE);
1010                                                 }
1011                                         else
1012                                                 {
1013                                                 r++;
1014                                                 work = work->next;
1015                                                 if (!work) finished = TRUE;
1016                                                 }
1017                                         }
1018                                 }
1019
1020                         count++;
1021
1022                         if (!finished && gtk_clist_get_cell_type(GTK_CLIST(file_clist), p, 0) != GTK_CELL_PIXTEXT)
1023                                 {
1024                                 GdkPixmap *pixmap = NULL;
1025                                 GdkBitmap *mask = NULL;
1026                                 gchar *name;
1027                                 gchar *path;
1028
1029                                 thumbs_running ++;
1030                                 past_run = thumbs_running;
1031                                 while(gtk_events_pending()) gtk_main_iteration();
1032                                 if (thumbs_running > past_run)
1033                                         {
1034                                         thumbs_running -= 2;
1035                                         update_progressbar(0.0);
1036                                         update_status_label(NULL);
1037                                         g_list_free(done_list);
1038                                         return;
1039                                         }
1040                                 thumbs_running --;
1041
1042                                 name = gtk_clist_get_row_data(GTK_CLIST(file_clist), p);
1043                                 path = g_strconcat (current_path, "/", name, NULL);
1044                                 spacing = create_thumbnail(path, &pixmap, &mask);
1045                                 g_free(path);
1046                                 gtk_clist_set_pixtext (GTK_CLIST(file_clist), p, 0, name, spacing + 5, pixmap, mask);
1047                                 gtk_clist_set_shift(GTK_CLIST(file_clist), p, 0, 0, 0);
1048
1049                                 update_progressbar((gfloat)(count) / GTK_CLIST(file_clist)->rows);
1050                                 }
1051                         }
1052                 update_progressbar(0.0);
1053                 g_list_free(done_list);
1054                 }
1055
1056         update_status_label(NULL);
1057 }
1058
1059 void filelist_refresh()
1060 {
1061         filelist_read(current_path);
1062         filelist_populate_clist();
1063         filelist_click_row = -1;
1064 }
1065
1066 void filelist_change_to(gchar *path)
1067 {
1068         if (!isdir(path)) return;
1069
1070         g_free(current_path);
1071         current_path = g_strdup(path);
1072
1073         filelist_refresh();
1074 }