1 /******************************* LICENCE **************************************
2 * Any code in this file may be redistributed or modified under the terms of
3 * the GNU General Public Licence as published by the Free Software
4 * Foundation; version 2 of the licence.
5 ****************************** END LICENCE ***********************************/
6 
7 /******************************************************************************
8 * Author:
9 * Andrew Smith, http://littlesvr.ca/misc/contactandrew.php
10 *
11 * Contributors:
12 *
13 ******************************************************************************/
14 
15 #include <gtk/gtk.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <dirent.h>
19 #include <sys/stat.h>
20 #include <stdbool.h>
21 #include <errno.h>
22 #include <libintl.h>
23 
24 #include "isomaster.h"
25 
26 extern AppSettings GBLappSettings;
27 extern char* GBLuserHomeDir;
28 
29 extern GtkWidget* GBLmainWindow;
30 
31 extern GtkWidget* GBLfsCurrentDirField;
32 extern GtkWidget* GBLfsTreeView;
33 extern GtkListStore* GBLfsListStore;
34 extern char* GBLfsCurrentDir;
35 
36 extern GdkPixbuf* GBLdirPixbuf;
37 extern GdkPixbuf* GBLfilePixbuf;
38 //~ extern GdkPixbuf* GBLsymlinkPixbuf;
39 
40 extern bool GBLisoChangesProbable;
41 
42 extern int errno;
43 
44 /* the column for the filename in the fs pane */
45 static GtkTreeViewColumn* GBLfilenameFsColumn;
46 
acceptFsPathCbk(GtkEntry * entry,gpointer user_data)47 void acceptFsPathCbk(GtkEntry *entry, gpointer user_data)
48 {
49     const char* newPath;
50     char* newPathTerminated;
51 
52     newPath = gtk_entry_get_text(entry);
53 
54     if(newPath[strlen(newPath) - 1] == '/')
55     {
56         changeFsDirectory((char*)newPath);
57     }
58     else
59     {
60         newPathTerminated = malloc(strlen(newPath) + 2);
61         if(newPathTerminated == NULL)
62             fatalError("newPathTerminated = malloc(strlen(newPath) + 2) failed");
63 
64         strcpy(newPathTerminated, newPath);
65         strcat(newPathTerminated, "/");
66 
67         changeFsDirectory(newPathTerminated);
68 
69         free(newPathTerminated);
70     }
71 }
72 
buildFsBrowser(GtkWidget * boxToPackInto)73 void buildFsBrowser(GtkWidget* boxToPackInto)
74 {
75     GtkWidget* scrolledWindow;
76     GtkTreeSelection *selection;
77     GtkCellRenderer* renderer;
78     GtkTreeViewColumn* column;
79 
80     GBLfsListStore = gtk_list_store_new(NUM_COLUMNS,
81                                         GDK_TYPE_PIXBUF, /* icon */
82                                         G_TYPE_STRING,  /* name */
83                                         G_TYPE_UINT64, /* size */
84                                         G_TYPE_UINT /* file type */
85                                         );
86 
87     scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
88     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledWindow),
89 				   GTK_POLICY_AUTOMATIC,
90 				   GTK_POLICY_AUTOMATIC);
91     gtk_box_pack_start(GTK_BOX(boxToPackInto), scrolledWindow, TRUE, TRUE, 0);
92     gtk_widget_show(scrolledWindow);
93 
94     /* view widget */
95     GBLfsTreeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(GBLfsListStore));
96     gtk_tree_view_set_search_column(GTK_TREE_VIEW(GBLfsTreeView), COLUMN_FILENAME);
97     g_object_unref(GBLfsListStore); /* destroy model automatically with view */
98     gtk_container_add(GTK_CONTAINER(scrolledWindow), GBLfsTreeView);
99     g_signal_connect(GBLfsTreeView, "row-activated", (GCallback)fsRowDblClickCbk, NULL);
100     g_signal_connect(GBLfsTreeView, "select-cursor-parent", (GCallback)fsGoUpDirTreeCbk, NULL);
101     /* The problem with this is that i get a popup menu before the row is selected.
102     * if i do a connect_after the handler never gets called. So no right-click menu. */
103     g_signal_connect(GBLfsTreeView, "button-press-event", (GCallback)fsButtonPressedCbk, NULL);
104     g_signal_connect(GBLfsTreeView, "button-release-event", (GCallback)fsButtonReleasedCbk, NULL);
105     gtk_widget_show(GBLfsTreeView);
106 
107     /* this won't be enabled until gtk allows me to drag a multiple selection */
108     //~ GtkTargetEntry targetEntry;
109     //~ targetEntry.target = "text/plain";
110     //~ targetEntry.flags = 0;
111     //~ targetEntry.info = 0;
112     //~ gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(GBLfsTreeView), GDK_BUTTON1_MASK,
113                                            //~ &targetEntry, 1, GDK_ACTION_COPY);
114 
115     /* enable multi-line selection */
116     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(GBLfsTreeView));
117     gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
118 
119     /* filename column */
120     GBLfilenameFsColumn = gtk_tree_view_column_new();
121     gtk_tree_view_column_set_title(GBLfilenameFsColumn, _("Name"));
122     gtk_tree_view_column_set_resizable(GBLfilenameFsColumn, TRUE);
123 
124     renderer = gtk_cell_renderer_pixbuf_new();
125     gtk_tree_view_column_pack_start(GBLfilenameFsColumn, renderer, FALSE);
126     gtk_tree_view_column_add_attribute(GBLfilenameFsColumn, renderer, "pixbuf", COLUMN_ICON);
127 
128     renderer = gtk_cell_renderer_text_new();
129     gtk_tree_view_column_pack_start(GBLfilenameFsColumn, renderer, TRUE);
130     gtk_tree_view_column_add_attribute(GBLfilenameFsColumn, renderer, "text", COLUMN_FILENAME);
131 
132     gtk_tree_view_column_set_sort_column_id(GBLfilenameFsColumn, COLUMN_FILENAME);
133     gtk_tree_view_column_set_expand(GBLfilenameFsColumn, TRUE);
134     gtk_tree_view_append_column(GTK_TREE_VIEW(GBLfsTreeView), GBLfilenameFsColumn);
135 
136     gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(GBLfsListStore), COLUMN_FILENAME,
137                                     sortByName, NULL, NULL);
138 
139     /* size column */
140     column = gtk_tree_view_column_new();
141     renderer = gtk_cell_renderer_text_new();
142     gtk_tree_view_column_set_title(column, _("Size"));
143     gtk_tree_view_column_pack_start(column, renderer, FALSE);
144     gtk_tree_view_column_add_attribute(column, renderer, "text", COLUMN_SIZE);
145     gtk_tree_view_column_set_cell_data_func(column, renderer, sizeCellDataFunc64, NULL, NULL);
146     gtk_tree_view_column_set_sort_column_id(column, COLUMN_SIZE);
147     gtk_tree_view_append_column(GTK_TREE_VIEW(GBLfsTreeView), column);
148 
149     gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(GBLfsListStore), COLUMN_SIZE,
150                                     sortBySize, NULL, NULL);
151 
152     /* set default sort */
153     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GBLfsListStore),
154                                          GBLappSettings.fsSortColumnId,
155                                          GBLappSettings.fsSortDirection);
156 
157     GBLdirPixbuf = NULL;
158     GBLfilePixbuf = NULL;
159 
160 #if GTK_MINOR_VERSION >= 6
161     GtkIconSet* iconSet;
162     GtkIconSize* iconSizes = NULL;
163     int numIconSizes;
164     GtkIconSize iconSize;
165 
166     /* CREATE pixbuf for directory */
167     iconSet = gtk_icon_factory_lookup_default(GTK_STOCK_DIRECTORY);
168     if(iconSet != NULL)
169     {
170         gtk_icon_set_get_sizes(iconSet, &iconSizes, &numIconSizes);
171         iconSize = iconSizes[0];
172         g_free(iconSizes);
173         //!! figure out proper size and resisze if necessary, see gtk-demo->stock->create_model()
174         GBLdirPixbuf = gtk_widget_render_icon(GBLfsTreeView, GTK_STOCK_DIRECTORY, iconSize, NULL);
175     }
176     /* END CREATE pixbuf for directory */
177 
178     /* CREATE pixbuf for file */
179     iconSet = gtk_icon_factory_lookup_default(GTK_STOCK_FILE);
180     if(iconSet != NULL)
181     {
182         gtk_icon_set_get_sizes(iconSet, &iconSizes, &numIconSizes);
183         iconSize = iconSizes[0];
184         g_free(iconSizes);
185         //!! figure out proper size and resisze if necessary, see gtk-demo->stock->create_model()
186         GBLfilePixbuf = gtk_widget_render_icon(GBLfsTreeView, GTK_STOCK_FILE, iconSize, NULL);
187     }
188     /* END CREATE pixbuf for file */
189 #endif
190 
191     if(GBLappSettings.fsCurrentDir != NULL)
192     {
193         bool rc;
194 
195         rc = changeFsDirectory(GBLappSettings.fsCurrentDir);
196         if(rc == false)
197             /* GBLuserHomeDir has just been set and tested a moment ago in findHomeDir() */
198             changeFsDirectory(GBLuserHomeDir);
199     }
200     else
201         changeFsDirectory(GBLuserHomeDir);
202 }
203 
buildFsLocator(GtkWidget * boxToPackInto)204 void buildFsLocator(GtkWidget* boxToPackInto)
205 {
206     GBLfsCurrentDirField = gtk_entry_new();
207     g_signal_connect(GBLfsCurrentDirField, "activate", (GCallback)acceptFsPathCbk, NULL);
208     gtk_box_pack_start(GTK_BOX(boxToPackInto), GBLfsCurrentDirField, FALSE, FALSE, 0);
209     gtk_widget_show(GBLfsCurrentDirField);
210 }
211 
changeFsDirectory(const char * newDirStr)212 bool changeFsDirectory(const char* newDirStr)
213 {
214     DIR* newDir;
215     struct dirent* nextItem; /* for contents of the directory */
216     char* nextItemPathAndName; /* for use with stat() */
217     GtkTreeIter listIterator;
218     int rc;
219     GtkTreeModel* model;
220     GtkWidget* warningDialog;
221 
222     newDir = opendir(newDirStr);
223     if(newDir == NULL)
224     {
225         warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
226                                                GTK_DIALOG_DESTROY_WITH_PARENT,
227                                                GTK_MESSAGE_ERROR,
228                                                GTK_BUTTONS_CLOSE,
229                                                _("Failed to open directory '%s', error %d"),
230                                                newDirStr,
231                                                errno);
232         gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
233         gtk_dialog_run(GTK_DIALOG(warningDialog));
234         gtk_widget_destroy(warningDialog);
235 
236         return false;
237     }
238 
239     /* for improved performance disconnect the model from tree view before udating it */
240     model = gtk_tree_view_get_model(GTK_TREE_VIEW(GBLfsTreeView));
241     g_object_ref(model);
242     gtk_tree_view_set_model(GTK_TREE_VIEW(GBLfsTreeView), NULL);
243 
244     /* this is the only way to disable sorting (for a huge performance boost) */
245     gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(GBLfsListStore), COLUMN_FILENAME,
246                                     sortVoid, NULL, NULL);
247     gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(GBLfsListStore), COLUMN_SIZE,
248                                     sortVoid, NULL, NULL);
249 
250     gtk_list_store_clear(GBLfsListStore);
251 
252 #if GTK_MINOR_VERSION >= 8
253     /* to make sure width of filename column isn't bigger than needed (need gtk 2.8) */
254     gtk_tree_view_column_queue_resize(GBLfilenameFsColumn);
255 #endif
256 
257     nextItemPathAndName = (char*)malloc(strlen(newDirStr) + 257);
258     if(nextItemPathAndName == NULL)
259         fatalError("changeFsDirectory(): malloc(strlen(newDirStr) + 257) failed");
260 
261     /* it may be possible but in any case very unlikely that readdir() will fail
262     * if it does, it returns NULL (same as end of dir) */
263     while( (nextItem = readdir(newDir)) != NULL )
264     {
265         /* skip current and parent directory */
266         if(strcmp(nextItem->d_name, ".") == 0 || strcmp(nextItem->d_name, "..") == 0)
267             continue;
268 
269         if(nextItem->d_name[0] == '.' && !GBLappSettings.showHiddenFilesFs)
270         /* skip hidden files/dirs */
271             continue;
272 
273         /* mind 256 is assumed in the malloc below */
274         if(strlen(nextItem->d_name) > 256)
275         {
276             warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
277                                                    GTK_DIALOG_DESTROY_WITH_PARENT,
278                                                    GTK_MESSAGE_ERROR,
279                                                    GTK_BUTTONS_CLOSE,
280                                                    _("Skipping directory entry because "
281                                                    "cannot handle filename longer than 256 chars"));
282             gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
283             gtk_dialog_run(GTK_DIALOG(warningDialog));
284             gtk_widget_destroy(warningDialog);
285             continue;
286         }
287 
288         strcpy(nextItemPathAndName, newDirStr);
289         strcat(nextItemPathAndName, nextItem->d_name);
290 
291         struct stat nextItemInfo;
292         rc = lstat(nextItemPathAndName, &nextItemInfo);
293         if(rc == -1)
294         {
295             warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
296                                                    GTK_DIALOG_DESTROY_WITH_PARENT,
297                                                    GTK_MESSAGE_ERROR,
298                                                    GTK_BUTTONS_CLOSE,
299                                                    _("Skipping directory entry because "
300                                                    "stat(%s) failed with %d"),
301                                                    nextItemPathAndName,
302                                                    errno);
303             gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
304             gtk_dialog_run(GTK_DIALOG(warningDialog));
305             gtk_widget_destroy(warningDialog);
306             continue;
307         }
308 
309         if(IS_DIR(nextItemInfo.st_mode))
310         /* directory */
311         {
312             gtk_list_store_append(GBLfsListStore, &listIterator);
313             gtk_list_store_set(GBLfsListStore, &listIterator,
314                                COLUMN_ICON, GBLdirPixbuf,
315                                COLUMN_FILENAME, nextItem->d_name,
316                                //COLUMN_FILENAME, g_filename_to_utf8(nextItem->d_name, -1, 0, 0, 0),
317                                //COLUMN_FILENAME, g_locale_to_utf8(nextItem->d_name, -1, 0, 0, 0),
318                                COLUMN_SIZE, 0LL,
319                                COLUMN_HIDDEN_TYPE, FILE_TYPE_DIRECTORY,
320                                -1);
321         }
322         else if(IS_REG_FILE(nextItemInfo.st_mode))
323         /* regular file */
324         {
325             gtk_list_store_append(GBLfsListStore, &listIterator);
326             gtk_list_store_set(GBLfsListStore, &listIterator,
327                                COLUMN_ICON, GBLfilePixbuf,
328                                COLUMN_FILENAME, nextItem->d_name,
329                                //COLUMN_FILENAME, g_filename_to_utf8(nextItem->d_name, -1, 0, 0, 0),
330                                //COLUMN_FILENAME, g_locale_to_utf8(nextItem->d_name, -1, 0, 0, 0),
331                                COLUMN_SIZE, nextItemInfo.st_size,
332                                COLUMN_HIDDEN_TYPE, FILE_TYPE_REGULAR,
333                                -1);
334         }
335         else if(IS_SYMLINK(nextItemInfo.st_mode))
336         /* symbolic link */
337         {
338             gtk_list_store_append(GBLfsListStore, &listIterator);
339             gtk_list_store_set(GBLfsListStore, &listIterator,
340                                COLUMN_ICON, GBLfilePixbuf,
341                                COLUMN_FILENAME, nextItem->d_name,
342                                COLUMN_SIZE, 0LL,
343                                COLUMN_HIDDEN_TYPE, FILE_TYPE_SYMLINK,
344                                -1);
345         }
346         /* else fancy file, ignore it */
347 
348     } /* while (dir contents) */
349 
350     free(nextItemPathAndName);
351 
352     closedir(newDir);
353 
354     /* reconnect the model and view now */
355     gtk_tree_view_set_model(GTK_TREE_VIEW(GBLfsTreeView), model);
356     g_object_unref(model);
357 
358     /* reenable sorting */
359     gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(GBLfsListStore), COLUMN_FILENAME,
360                                     sortByName, NULL, NULL);
361     gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(GBLfsListStore), COLUMN_SIZE,
362                                     sortBySize, NULL, NULL);
363 
364     /* set current directory string */
365     if(GBLfsCurrentDir != NULL)
366         free(GBLfsCurrentDir);
367     GBLfsCurrentDir = (char*)malloc(strlen(newDirStr) + 1);
368     if(GBLfsCurrentDir == NULL)
369         fatalError("changeFsDirectory(): malloc(strlen(newDirStr) + 1) failed");
370     strcpy(GBLfsCurrentDir, newDirStr);
371 
372     /* update the location field with the path and name */
373     gtk_entry_set_text(GTK_ENTRY(GBLfsCurrentDirField), GBLfsCurrentDir);
374 
375     return true;
376 }
377 
378 /******************************************************************************
379 * fsButtonPressedCbk()
380 * Make sure that a right-click on the view doesn't send the signal to the
381 * widget. If it did, a selection of multiple rows would be lost.
382 * I have a feeling someone did this shit in GTK just to piss on users */
fsButtonPressedCbk(GtkWidget * fsView,GdkEventButton * event,gpointer user_data)383 gboolean fsButtonPressedCbk(GtkWidget* fsView, GdkEventButton* event, gpointer user_data)
384 {
385     if(event->type == GDK_BUTTON_PRESS  &&  event->button == 3)
386     {
387         /* Stop event propagation */
388         /*!! Would be nice if I could only stop event propagation if click was on
389         * the selection, I have to look into how that may be done, if at all */
390         return TRUE;
391     }
392 
393     return FALSE;
394 }
395 
396 /******************************************************************************
397 * fsButtonReleasedCbk()
398 * Show context menu if releasing the right mouse button */
fsButtonReleasedCbk(GtkWidget * fsView,GdkEventButton * event,gpointer user_data)399 gboolean fsButtonReleasedCbk(GtkWidget* fsView, GdkEventButton* event, gpointer user_data)
400 {
401     if(event->type == GDK_BUTTON_RELEASE &&  event->button == 3)
402     {
403         showFsContextMenu(fsView, event);
404     }
405 
406     return FALSE;
407 }
408 
409 /* this is called from a button and via a treeview event so don't use the parameters */
fsGoUpDirTreeCbk(GtkButton * button,gpointer data)410 void fsGoUpDirTreeCbk(GtkButton *button, gpointer data)
411 {
412     int count;
413     bool done;
414     char* newCurrentDir;
415 
416     /* do nothing if already at root */
417     if(GBLfsCurrentDir[0] == '/' && GBLfsCurrentDir[1] == '\0')
418         return;
419 
420     /* need to allocate a new string because changeFsDirectory() uses it
421     * to copy from after freeing GBLfsCurrentDir */
422     newCurrentDir = (char*)malloc(strlen(GBLfsCurrentDir) + 1);
423     if(newCurrentDir == NULL)
424         fatalError("fsGoUpDirTree(): malloc(strlen(GBLfsCurrentDir) + 1) failed");
425     strcpy(newCurrentDir, GBLfsCurrentDir);
426 
427     /* look for the second last slash */
428     done = false;
429     for(count = strlen(newCurrentDir) - 1; !done; count--)
430     {
431         if(newCurrentDir[count - 1] == '/')
432         /* truncate the string */
433         {
434             newCurrentDir[count] = '\0';
435             changeFsDirectory(newCurrentDir);
436             done = true;
437         }
438     }
439 
440     free(newCurrentDir);
441 }
442 
fsRowDblClickCbk(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * col,gpointer data)443 void fsRowDblClickCbk(GtkTreeView* treeview, GtkTreePath* path,
444                       GtkTreeViewColumn* col, gpointer data)
445 {
446     GtkTreeModel* model;
447     GtkTreeIter iterator;
448     char* name;
449     char* selectedPathAndName;
450     int fileType;
451     GtkWidget* warningDialog;
452 
453     model = gtk_tree_view_get_model(treeview);
454 
455     if(gtk_tree_model_get_iter(model, &iterator, path) == FALSE)
456     {
457         warningDialog = gtk_message_dialog_new(GTK_WINDOW(GBLmainWindow),
458                                                GTK_DIALOG_DESTROY_WITH_PARENT,
459                                                GTK_MESSAGE_ERROR,
460                                                GTK_BUTTONS_CLOSE,
461                                                "GUI Error: 'fsRowDblClicked(): "
462                                                "gtk_tree_model_get_iter() failed'");
463         gtk_window_set_modal(GTK_WINDOW(warningDialog), TRUE);
464         gtk_dialog_run(GTK_DIALOG(warningDialog));
465         gtk_widget_destroy(warningDialog);
466         return;
467     }
468 
469     gtk_tree_model_get(model, &iterator, COLUMN_FILENAME, &name, -1);
470 
471     /* 2 in case i need to append a '/' */
472     selectedPathAndName = (char*)malloc(strlen(GBLfsCurrentDir) + strlen(name) + 2);
473     if(selectedPathAndName == NULL)
474         fatalError("fsRowDblClicked(): malloc(strlen(GBLfsCurrentDir) + strlen(name) + 2) failed");
475 
476     strcpy(selectedPathAndName, GBLfsCurrentDir);
477     strcat(selectedPathAndName, name);
478 
479     gtk_tree_model_get(model, &iterator, COLUMN_HIDDEN_TYPE, &fileType, -1);
480     if(fileType == FILE_TYPE_DIRECTORY)
481     /* change directory */
482     {
483         strcat(selectedPathAndName, "/");
484         changeFsDirectory(selectedPathAndName);
485     }
486     else if(fileType == FILE_TYPE_SYMLINK)
487     /* if it's a symlink to a dir, change directory */
488     {
489         struct stat statStruct;
490         int rc;
491 
492         rc = stat(selectedPathAndName, &statStruct);
493         if( rc == 0 && (statStruct.st_mode & S_IFDIR) )
494         {
495             strcat(selectedPathAndName, "/");
496             changeFsDirectory(selectedPathAndName);
497         }
498     }
499     else if(fileType == FILE_TYPE_REGULAR)
500     /* if it's an image, open it */
501     {
502         int strLen = strlen(name);
503 
504         if( (name[strLen - 4] == '.' &&
505              (name[strLen - 3] == 'i' || name[strLen - 3] == 'I') &&
506              (name[strLen - 2] == 's' || name[strLen - 2] == 'S') &&
507              (name[strLen - 1] == 'o' || name[strLen - 1] == 'O'))
508             ||
509             (name[strLen - 4] == '.' &&
510              (name[strLen - 3] == 'n' || name[strLen - 3] == 'N') &&
511              (name[strLen - 2] == 'r' || name[strLen - 2] == 'R') &&
512              (name[strLen - 1] == 'g' || name[strLen - 1] == 'G'))
513             ||
514             (name[strLen - 4] == '.' &&
515              (name[strLen - 3] == 'm' || name[strLen - 3] == 'M') &&
516              (name[strLen - 2] == 'd' || name[strLen - 2] == 'D') &&
517              (name[strLen - 1] == 'f' || name[strLen - 1] == 'F')) )
518         {
519             if( !GBLisoChangesProbable || confirmCloseIso() )
520             {
521                 openIso(selectedPathAndName);
522             }
523         }
524     }
525 
526 
527     free(selectedPathAndName);
528     g_free(name);
529 }
530 
refreshFsView(void)531 void refreshFsView(void)
532 {
533     char* fsCurrentDir; /* for changeFsDirectory() */
534 
535     fsCurrentDir = malloc(strlen(GBLfsCurrentDir) + 1);
536     if(fsCurrentDir == NULL)
537         fatalError("refreshFsView(): malloc(strlen(GBLfsCurrentDir) + 1) failed");
538     strcpy(fsCurrentDir, GBLfsCurrentDir);
539 
540     /* remember scroll position */
541     GdkRectangle visibleRect;
542     gtk_tree_view_get_visible_rect(GTK_TREE_VIEW(GBLfsTreeView), &visibleRect);
543 
544     changeFsDirectory(fsCurrentDir);
545 
546     /* need the -1 because if i call this function with the same coordinates that
547     * the view already has, the position is set to 0. think it's a gtk bug. */
548     gtk_tree_view_scroll_to_point(GTK_TREE_VIEW(GBLfsTreeView), visibleRect.x - 1, visibleRect.y - 1);
549 
550     free(fsCurrentDir);
551 }
552 
showFsContextMenu(GtkWidget * fsView,GdkEventButton * event)553 void showFsContextMenu(GtkWidget* fsView, GdkEventButton* event)
554 {
555     GtkWidget* menu;
556     GtkWidget* menuItem;
557     GtkTreeSelection* selection;
558     gint numSelectedRows;
559     GtkAccelGroup* accelGroup;
560 
561     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(GBLfsTreeView));
562 
563     numSelectedRows = gtk_tree_selection_count_selected_rows(selection);
564     if(numSelectedRows == 0)
565         return;
566 
567     /* have this here just so that the shortcut keys show in the context menu */
568     accelGroup = gtk_accel_group_new();
569     gtk_window_add_accel_group(GTK_WINDOW(GBLmainWindow), accelGroup);
570 
571     menu = gtk_menu_new();
572     gtk_menu_set_accel_group(GTK_MENU(menu), accelGroup);
573     /*
574     menuItem = gtk_image_menu_item_new_with_label(_("Rename"));
575     g_signal_connect(menuItem, "activate",
576                      (GCallback)renameSelectedBtnCbk, NULL);
577     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(menuItem), "<ISOMaster>/Contextmenu/Rename");
578     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
579     gtk_widget_show_all(menu);
580     if(numSelectedRows > 1)
581         gtk_widget_set_sensitive(menuItem, FALSE);
582     */
583     menuItem = gtk_image_menu_item_new_with_label(_("View"));
584     g_signal_connect(menuItem, "activate",
585                      (GCallback)viewSelectedBtnCbk, NULL);
586     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(menuItem), "<ISOMaster>/Contextmenu/View");
587     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
588     gtk_widget_show_all(menu);
589 
590     menuItem = gtk_image_menu_item_new_with_label(_("Edit"));
591     g_signal_connect(menuItem, "activate",
592                      (GCallback)editSelectedBtnCbk, NULL);
593     gtk_menu_item_set_accel_path(GTK_MENU_ITEM(menuItem), "<ISOMaster>/Contextmenu/Edit");
594     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
595     gtk_widget_show_all(menu);
596     /*
597     menuItem = gtk_image_menu_item_new_with_label(_("Change permissions"));
598     g_signal_connect(menuItem, "activate",
599                      (GCallback)changePermissionsBtnCbk, NULL);
600     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuItem);
601     gtk_widget_show_all(menu);
602     if(numSelectedRows > 1)
603         gtk_widget_set_sensitive(menuItem, FALSE);
604     */
605     gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
606                    event->button, gdk_event_get_time((GdkEvent*)event));
607 }
608 
showHiddenCbk(GtkButton * button,gpointer data)609 void showHiddenCbk(GtkButton *button, gpointer data)
610 {
611     GBLappSettings.showHiddenFilesFs = !GBLappSettings.showHiddenFilesFs;
612 
613     /* refresh fs view */
614     refreshFsView();
615 }
616