Add some wrappers in view_dir.c and simplify even more.
[geeqie.git] / src / view_dir_list.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12 #include "main.h"
13 #include "view_dir_list.h"
14
15 #include "dnd.h"
16 #include "dupe.h"
17 #include "filelist.h"
18 #include "layout.h"
19 #include "layout_image.h"
20 #include "layout_util.h"
21 #include "utilops.h"
22 #include "ui_bookmark.h"
23 #include "ui_fileops.h"
24 #include "ui_menu.h"
25 #include "ui_tree_edit.h"
26 #include "view_dir.h"
27
28 #include <gdk/gdkkeysyms.h> /* for keyboard values */
29
30
31 #define VDLIST_PAD 4
32
33 #define VDLIST_INFO(_vd_, _part_) (((ViewDirInfoList *)(_vd_->info))->_part_)
34
35
36 /*
37  *-----------------------------------------------------------------------------
38  * misc
39  *-----------------------------------------------------------------------------
40  */
41
42 gint vdlist_find_row(ViewDir *vd, FileData *fd, GtkTreeIter *iter)
43 {
44         GtkTreeModel *store;
45         gint valid;
46         gint row = 0;
47
48         store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
49         valid = gtk_tree_model_get_iter_first(store, iter);
50         while (valid)
51                 {
52                 FileData *fd_n;
53                 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, DIR_COLUMN_POINTER, &fd_n, -1);
54                 if (fd_n == fd) return row;
55
56                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
57                 row++;
58                 }
59
60         return -1;
61 }
62
63
64 FileData *vdlist_row_by_path(ViewDir *vd, const gchar *path, gint *row)
65 {
66         GList *work;
67         gint n;
68
69         if (!path)
70                 {
71                 if (row) *row = -1;
72                 return NULL;
73                 }
74
75         n = 0;
76         work = VDLIST_INFO(vd, list);
77         while (work)
78                 {
79                 FileData *fd = work->data;
80                 if (strcmp(fd->path, path) == 0)
81                         {
82                         if (row) *row = n;
83                         return fd;
84                         }
85                 work = work->next;
86                 n++;
87                 }
88
89         if (row) *row = -1;
90         return NULL;
91 }
92
93 /*
94  *-----------------------------------------------------------------------------
95  * dnd
96  *-----------------------------------------------------------------------------
97  */
98
99 static void vdlist_scroll_to_row(ViewDir *vd, FileData *fd, gfloat y_align)
100 {
101         GtkTreeIter iter;
102
103         if (GTK_WIDGET_REALIZED(vd->view) && vd_find_row(vd, fd, &iter) >= 0)
104                 {
105                 GtkTreeModel *store;
106                 GtkTreePath *tpath;
107
108                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
109                 tpath = gtk_tree_model_get_path(store, &iter);
110                 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vd->view), tpath, NULL, TRUE, y_align, 0.0);
111                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vd->view), tpath, NULL, FALSE);
112                 gtk_tree_path_free(tpath);
113
114                 if (!GTK_WIDGET_HAS_FOCUS(vd->view)) gtk_widget_grab_focus(vd->view);
115                 }
116 }
117
118 /*
119  *-----------------------------------------------------------------------------
120  * main
121  *-----------------------------------------------------------------------------
122  */ 
123
124 void vdlist_select_row(ViewDir *vd, FileData *fd)
125 {
126         if (fd && vd->select_func)
127                 {
128                 gchar *path;
129
130                 path = g_strdup(fd->path);
131                 vd->select_func(vd, path, vd->select_data);
132                 g_free(path);
133                 }
134 }
135
136 const gchar *vdlist_row_get_path(ViewDir *vd, gint row)
137 {
138         FileData *fd;
139
140         fd = g_list_nth_data(VDLIST_INFO(vd, list), row);
141
142         if (fd) return fd->path;
143
144         return NULL;
145 }
146
147 static void vdlist_populate(ViewDir *vd)
148 {
149         GtkListStore *store;
150         GList *work;
151
152         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view)));
153         gtk_list_store_clear(store);
154
155         work = VDLIST_INFO(vd, list);
156         while (work)
157                 {
158                 FileData *fd;
159                 GtkTreeIter iter;
160                 GdkPixbuf *pixbuf;
161
162                 fd = work->data;
163
164                 if (access_file(fd->path, R_OK | X_OK) && fd->name)
165                         {
166                         if (fd->name[0] == '.' && fd->name[1] == '\0')
167                                 {
168                                 pixbuf = vd->pf->open;
169                                 }
170                         else if (fd->name[0] == '.' && fd->name[1] == '.' && fd->name[2] == '\0')
171                                 {
172                                 pixbuf = vd->pf->parent;
173                                 }
174                         else
175                                 {
176                                 pixbuf = vd->pf->close;
177                                 }
178                         }
179                 else
180                         {
181                         pixbuf = vd->pf->deny;
182                         }
183
184                 gtk_list_store_append(store, &iter);
185                 gtk_list_store_set(store, &iter,
186                                    DIR_COLUMN_POINTER, fd,
187                                    DIR_COLUMN_ICON, pixbuf,
188                                    DIR_COLUMN_NAME, fd->name, -1);
189
190                 work = work->next;
191                 }
192
193         vd->click_fd = NULL;
194         vd->drop_fd = NULL;
195 }
196
197 gint vdlist_set_path(ViewDir *vd, const gchar *path)
198 {
199         gint ret;
200         FileData *fd;
201         gchar *old_path = NULL;
202         gchar *filepath;
203
204         if (!path) return FALSE;
205         if (vd->path && strcmp(path, vd->path) == 0) return TRUE;
206
207         if (vd->path)
208                 {
209                 gchar *base;
210
211                 base = remove_level_from_path(vd->path);
212                 if (strcmp(base, path) == 0)
213                         {
214                         old_path = g_strdup(filename_from_path(vd->path));
215                         }
216                 g_free(base);
217                 }
218
219         g_free(vd->path);
220         vd->path = g_strdup(path);
221
222         filelist_free(VDLIST_INFO(vd, list));
223         VDLIST_INFO(vd, list) = NULL;
224
225         ret = filelist_read(vd->path, NULL, &VDLIST_INFO(vd, list));
226
227         VDLIST_INFO(vd, list) = filelist_sort(VDLIST_INFO(vd, list), SORT_NAME, TRUE);
228
229         /* add . and .. */
230
231         if (strcmp(vd->path, "/") != 0)
232                 {
233                 filepath = g_strconcat(vd->path, "/", "..", NULL); 
234                 fd = file_data_new_simple(filepath);
235                 VDLIST_INFO(vd, list) = g_list_prepend(VDLIST_INFO(vd, list), fd);
236                 g_free(filepath);
237                 }
238         
239         if (options->file_filter.show_dot_directory)
240                 {
241                 filepath = g_strconcat(vd->path, "/", ".", NULL); 
242                 fd = file_data_new_simple(filepath);
243                 VDLIST_INFO(vd, list) = g_list_prepend(VDLIST_INFO(vd, list), fd);
244                 g_free(filepath);
245         }
246
247         vdlist_populate(vd);
248
249         if (old_path)
250                 {
251                 /* scroll to make last path visible */
252                 FileData *found = NULL;
253                 GList *work;
254
255                 work = VDLIST_INFO(vd, list);
256                 while (work && !found)
257                         {
258                         FileData *fd = work->data;
259                         if (strcmp(old_path, fd->name) == 0) found = fd;
260                         work = work->next;
261                         }
262
263                 if (found) vdlist_scroll_to_row(vd, found, 0.5);
264
265                 g_free(old_path);
266                 return ret;
267                 }
268
269         if (GTK_WIDGET_REALIZED(vd->view))
270                 {
271                 gtk_tree_view_scroll_to_point(GTK_TREE_VIEW(vd->view), 0, 0);
272                 }
273
274         return ret;
275 }
276
277 void vdlist_refresh(ViewDir *vd)
278 {
279         gchar *path;
280
281         path = g_strdup(vd->path);
282         vd->path = NULL;
283         vdlist_set_path(vd, path);
284         g_free(path);
285 }
286
287 gint vdlist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
288 {
289         ViewDir *vd = data;
290         GtkTreePath *tpath;
291         
292         if (event->keyval != GDK_Menu) return FALSE;
293
294         gtk_tree_view_get_cursor(GTK_TREE_VIEW(vd->view), &tpath, NULL);
295         if (tpath)
296                 {
297                 GtkTreeModel *store;
298                 GtkTreeIter iter;
299
300                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
301                 gtk_tree_model_get_iter(store, &iter, tpath);
302                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &vd->click_fd, -1);
303                 
304                 gtk_tree_path_free(tpath);
305                 }
306         else
307                 {
308                 vd->click_fd = NULL;
309                 }
310
311         vd_color_set(vd, vd->click_fd, TRUE);
312
313         vd->popup = vd_pop_menu(vd, vd->click_fd);
314
315         gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, vd_menu_position_cb, vd, 0, GDK_CURRENT_TIME);
316
317         return TRUE;
318 }
319
320 gint vdlist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
321 {
322         ViewDir *vd = data;
323         GtkTreePath *tpath;
324         GtkTreeIter iter;
325         FileData *fd = NULL;
326
327         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
328                                           &tpath, NULL, NULL, NULL))
329                 {
330                 GtkTreeModel *store;
331
332                 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
333                 gtk_tree_model_get_iter(store, &iter, tpath);
334                 gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &fd, -1);
335                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
336                 gtk_tree_path_free(tpath);
337                 }
338
339         vd->click_fd = fd;
340         vd_color_set(vd, vd->click_fd, TRUE);
341
342         if (bevent->button == 3)
343                 {
344                 vd->popup = vd_pop_menu(vd, vd->click_fd);
345                 gtk_menu_popup(GTK_MENU(vd->popup), NULL, NULL, NULL, NULL,
346                                bevent->button, bevent->time);
347                 }
348
349         return TRUE;
350 }
351
352 void vdlist_destroy_cb(GtkWidget *widget, gpointer data)
353 {
354         ViewDir *vd = data;
355
356         vd_dnd_drop_scroll_cancel(vd);
357         widget_auto_scroll_stop(vd->view);
358
359         filelist_free(VDLIST_INFO(vd, list));
360 }
361
362 ViewDir *vdlist_new(ViewDir *vd, const gchar *path)
363 {
364         GtkListStore *store;
365         GtkTreeSelection *selection;
366         GtkTreeViewColumn *column;
367         GtkCellRenderer *renderer;
368
369         vd->info = g_new0(ViewDirInfoList, 1);
370         vd->type = DIRVIEW_LIST;
371
372         VDLIST_INFO(vd, list) = NULL;
373
374         store = gtk_list_store_new(4, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN);
375         vd->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
376         g_object_unref(store);
377
378         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vd->view), FALSE);
379         gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vd->view), FALSE);
380         g_signal_connect(G_OBJECT(vd->view), "row_activated",
381                          G_CALLBACK(vd_activate_cb), vd);
382
383         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vd->view));
384         gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);
385
386         column = gtk_tree_view_column_new();
387         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
388
389         renderer = gtk_cell_renderer_pixbuf_new();
390         gtk_tree_view_column_pack_start(column, renderer, FALSE);
391         gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", DIR_COLUMN_ICON);
392         gtk_tree_view_column_set_cell_data_func(column, renderer, vd_color_cb, vd, NULL);
393
394         renderer = gtk_cell_renderer_text_new();
395         gtk_tree_view_column_pack_start(column, renderer, TRUE);
396         gtk_tree_view_column_add_attribute(column, renderer, "text", DIR_COLUMN_NAME);
397         gtk_tree_view_column_set_cell_data_func(column, renderer, vd_color_cb, vd, NULL);
398
399         gtk_tree_view_append_column(GTK_TREE_VIEW(vd->view), column);
400
401         g_signal_connect(G_OBJECT(vd->view), "key_press_event",
402                            G_CALLBACK(vd_press_key_cb), vd);
403         gtk_container_add(GTK_CONTAINER(vd->widget), vd->view);
404         gtk_widget_show(vd->view);
405
406         vd_dnd_init(vd);
407
408         g_signal_connect(G_OBJECT(vd->view), "button_press_event",
409                          G_CALLBACK(vd_press_cb), vd);
410         g_signal_connect(G_OBJECT(vd->view), "button_release_event",
411                          G_CALLBACK(vd_release_cb), vd);
412
413         if (path) vdlist_set_path(vd, path);
414
415         return vd;
416 }