1 /*
2  * Copyright (C) 2011 Nick Schermer <nick@xfce.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #endif
26 #ifdef HAVE_ERRNO_H
27 #include <errno.h>
28 #endif
29 
30 #include <libxfce4util/libxfce4util.h>
31 #include <libxfce4ui/libxfce4ui.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <xfconf/xfconf.h>
34 #include <glib/gstdio.h>
35 
36 #include <src/appfinder-window.h>
37 #include <src/appfinder-model.h>
38 #include <src/appfinder-category-model.h>
39 #include <src/appfinder-preferences.h>
40 #include <src/appfinder-actions.h>
41 #include <src/appfinder-private.h>
42 
43 #ifdef GDK_WINDOWING_X11
44 #include <gdk/gdkx.h>
45 #define APPFINDER_WIDGET_XID(widget) ((guint) GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (widget))))
46 #else
47 #define APPFINDER_WIDGET_XID(widget) (0)
48 #endif
49 
50 
51 
52 #define DEFAULT_WINDOW_WIDTH   400
53 #define DEFAULT_WINDOW_HEIGHT  400
54 #define DEFAULT_PANED_POSITION 180
55 
56 #define XFCE_APPFINDER_LOCAL_PREFIX "file://"
57 
58 
59 static void       xfce_appfinder_window_finalize                      (GObject                     *object);
60 static void       xfce_appfinder_window_unmap                         (GtkWidget                   *widget);
61 static gboolean   xfce_appfinder_window_key_press_event               (GtkWidget                   *widget,
62                                                                        GdkEventKey                 *event);
63 static gboolean   xfce_appfinder_window_window_state_event            (GtkWidget                   *widget,
64                                                                        GdkEventWindowState         *event);
65 static void       xfce_appfinder_window_view                          (XfceAppfinderWindow         *window);
66 static gboolean   xfce_appfinder_window_popup_menu                    (GtkWidget                   *view,
67                                                                        XfceAppfinderWindow         *window);
68 static void       xfce_appfinder_window_set_padding                   (GtkWidget                   *entry,
69                                                                        GtkWidget                   *align);
70 static gboolean   xfce_appfinder_window_completion_match_func         (GtkEntryCompletion          *completion,
71                                                                        const gchar                 *key,
72                                                                        GtkTreeIter                 *iter,
73                                                                        gpointer                     data);
74 static void       xfce_appfinder_window_entry_changed                 (XfceAppfinderWindow         *window);
75 static void       xfce_appfinder_window_entry_activate                (GtkEditable                 *entry,
76                                                                        XfceAppfinderWindow         *window);
77 static gboolean   xfce_appfinder_window_entry_key_press_event         (GtkWidget                   *entry,
78                                                                        GdkEventKey                 *event,
79                                                                        XfceAppfinderWindow         *window);
80 static void       xfce_appfinder_window_entry_icon_released           (GtkEntry                    *entry,
81                                                                        GtkEntryIconPosition         icon_pos,
82                                                                        GdkEvent                    *event,
83                                                                        XfceAppfinderWindow         *window);
84 static void       xfce_appfinder_window_drag_begin                    (GtkWidget                   *widget,
85                                                                        GdkDragContext              *drag_context,
86                                                                        XfceAppfinderWindow         *window);
87 static void       xfce_appfinder_window_drag_data_get                 (GtkWidget                   *widget,
88                                                                        GdkDragContext              *drag_context,
89                                                                        GtkSelectionData            *data,
90                                                                        guint                        info,
91                                                                        guint                        drag_time,
92                                                                        XfceAppfinderWindow         *window);
93 static gboolean   xfce_appfinder_window_treeview_key_press_event      (GtkWidget                   *widget,
94                                                                        GdkEventKey                 *event,
95                                                                        XfceAppfinderWindow         *window);
96 static void       xfce_appfinder_window_category_changed              (GtkTreeSelection            *selection,
97                                                                        XfceAppfinderWindow         *window);
98 static void       xfce_appfinder_window_category_set_categories       (XfceAppfinderModel          *model,
99                                                                        XfceAppfinderWindow         *window);
100 static void       xfce_appfinder_window_preferences                   (GtkWidget                   *button,
101                                                                        XfceAppfinderWindow         *window);
102 static void       xfce_appfinder_window_property_changed              (XfconfChannel               *channel,
103                                                                        const gchar                 *prop,
104                                                                        const GValue                *value,
105                                                                        XfceAppfinderWindow         *window);
106 static gboolean   xfce_appfinder_window_item_visible                  (GtkTreeModel                *model,
107                                                                        GtkTreeIter                 *iter,
108                                                                        gpointer                     data);
109 static void       xfce_appfinder_window_item_changed                  (XfceAppfinderWindow         *window);
110 static void       xfce_appfinder_window_row_activated                 (XfceAppfinderWindow         *window);
111 static void       xfce_appfinder_window_icon_theme_changed            (XfceAppfinderWindow         *window);
112 static void       xfce_appfinder_window_launch_clicked                (XfceAppfinderWindow         *window);
113 static void       xfce_appfinder_window_execute                       (XfceAppfinderWindow         *window,
114                                                                        gboolean                     close_on_succeed);
115 static gint       xfce_appfinder_window_sort_items                    (GtkTreeModel                *model,
116                                                                        GtkTreeIter                 *a,
117                                                                        GtkTreeIter                 *b,
118                                                                        gpointer                     data);
119 static gboolean   xfce_appfinder_should_sort_icon_view                (void);
120 static void       xfce_appfinder_window_update_frecency               (XfceAppfinderWindow         *window,
121                                                                        GtkTreeModel                *model,
122                                                                        const gchar                 *uri);
123 static gint       xfce_appfinder_window_sort_items_frecency           (GtkTreeModel                *model,
124                                                                        GtkTreeIter                 *a,
125                                                                        GtkTreeIter                 *b,
126                                                                        gpointer                     data);
127 
128 struct _XfceAppfinderWindowClass
129 {
130   GtkWindowClass __parent__;
131 };
132 
133 struct _XfceAppfinderWindow
134 {
135   GtkWindow __parent__;
136 
137   XfceAppfinderModel         *model;
138 
139   GtkTreeModel               *sort_model;
140   GtkTreeModel               *filter_model;
141 
142   XfceAppfinderCategoryModel *category_model;
143 
144   XfceAppfinderActions       *actions;
145 
146   GtkIconTheme               *icon_theme;
147 
148   GtkEntryCompletion         *completion;
149 
150   XfconfChannel              *channel;
151 
152   GtkWidget                  *paned;
153   GtkWidget                  *entry;
154   GtkWidget                  *image;
155   GtkWidget                  *view;
156   GtkWidget                  *viewscroll;
157   GtkWidget                  *sidepane;
158 
159   GdkPixbuf                  *icon_find;
160 
161   GtkWidget                  *button_launch;
162 
163   GarconMenuDirectory        *filter_category;
164   gchar                      *filter_text;
165 
166   guint                       idle_entry_changed_id;
167 
168   gint                        last_window_height;
169 
170   gulong                      property_watch_id;
171   gulong                      categories_changed_id;
172 };
173 
174 static const GtkTargetEntry target_list[] =
175 {
176   { "text/uri-list", 0, 0 }
177 };
178 
179 
180 
G_DEFINE_TYPE(XfceAppfinderWindow,xfce_appfinder_window,GTK_TYPE_WINDOW)181 G_DEFINE_TYPE (XfceAppfinderWindow, xfce_appfinder_window, GTK_TYPE_WINDOW)
182 
183 
184 
185 static void
186 xfce_appfinder_window_class_init (XfceAppfinderWindowClass *klass)
187 {
188   GObjectClass   *gobject_class;
189   GtkWidgetClass *gtkwidget_class;
190 
191   gobject_class = G_OBJECT_CLASS (klass);
192   gobject_class->finalize = xfce_appfinder_window_finalize;
193 
194   gtkwidget_class = GTK_WIDGET_CLASS (klass);
195   gtkwidget_class->unmap = xfce_appfinder_window_unmap;
196   gtkwidget_class->key_press_event = xfce_appfinder_window_key_press_event;
197   gtkwidget_class->window_state_event = xfce_appfinder_window_window_state_event;
198 }
199 
200 
201 
202 static void
xfce_appfinder_window_init(XfceAppfinderWindow * window)203 xfce_appfinder_window_init (XfceAppfinderWindow *window)
204 {
205   GtkWidget          *vbox;
206   GtkWidget          *entry;
207   GtkWidget          *pane;
208   GtkWidget          *scroll;
209   GtkWidget          *sidepane;
210   GtkWidget          *image;
211   GtkWidget          *hbox;
212   GtkTreeViewColumn  *column;
213   GtkCellRenderer    *renderer;
214   GtkTreeSelection   *selection;
215   GtkWidget          *button;
216   GtkEntryCompletion *completion;
217   GtkCellRenderer    *cell;
218   gint                integer;
219 
220   window->channel = xfconf_channel_get ("xfce4-appfinder");
221   window->last_window_height = xfconf_channel_get_int (window->channel, "/last/window-height", DEFAULT_WINDOW_HEIGHT);
222 
223   window->category_model = xfce_appfinder_category_model_new ();
224   xfconf_g_property_bind (window->channel, "/category-icon-size", G_TYPE_UINT,
225                           G_OBJECT (window->category_model), "icon-size");
226 
227   window->model = xfce_appfinder_model_get (xfconf_channel_get_bool (window->channel, "/sort-by-frecency", FALSE));
228   xfconf_g_property_bind (window->channel, "/item-icon-size", G_TYPE_UINT,
229                           G_OBJECT (window->model), "icon-size");
230 
231   gtk_window_set_title (GTK_WINDOW (window), _("Application Finder"));
232   integer = xfconf_channel_get_int (window->channel, "/last/window-width", DEFAULT_WINDOW_WIDTH);
233   gtk_window_set_default_size (GTK_WINDOW (window), integer, -1);
234   gtk_window_set_icon_name (GTK_WINDOW (window), "org.xfce.appfinder");
235 
236   if (xfconf_channel_get_bool (window->channel, "/always-center", FALSE))
237     {
238       gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
239     }
240 
241   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
242   gtk_container_add (GTK_CONTAINER (window), vbox);
243   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
244   gtk_widget_show (vbox);
245 
246   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
247   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
248   gtk_widget_show (hbox);
249 
250   window->icon_find = xfce_appfinder_model_load_pixbuf (XFCE_APPFINDER_STOCK_FIND, XFCE_APPFINDER_ICON_SIZE_48);
251   window->image = image = gtk_image_new_from_pixbuf (window->icon_find);
252   gtk_widget_set_size_request (image, 48, 48);
253   gtk_widget_set_halign(image, GTK_ALIGN_CENTER);
254   gtk_container_add (GTK_CONTAINER (hbox), image);
255   gtk_widget_show (image);
256 
257   window->entry = entry = gtk_entry_new ();
258   gtk_widget_set_halign(entry, GTK_ALIGN_FILL);
259   gtk_widget_set_valign (entry, GTK_ALIGN_CENTER);
260   gtk_widget_set_hexpand (entry, TRUE);
261   gtk_container_add (GTK_CONTAINER (hbox), entry);
262   g_signal_connect (G_OBJECT (entry), "icon-release",
263       G_CALLBACK (xfce_appfinder_window_entry_icon_released), window);
264   g_signal_connect (G_OBJECT (entry), "realize",
265       G_CALLBACK (xfce_appfinder_window_set_padding), entry);
266   g_signal_connect_swapped (G_OBJECT (entry), "changed",
267       G_CALLBACK (xfce_appfinder_window_entry_changed), window);
268   g_signal_connect (G_OBJECT (entry), "activate",
269       G_CALLBACK (xfce_appfinder_window_entry_activate), window);
270   g_signal_connect (G_OBJECT (entry), "key-press-event",
271       G_CALLBACK (xfce_appfinder_window_entry_key_press_event), window);
272   gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
273                                      GTK_ENTRY_ICON_SECONDARY,
274                                      XFCE_APPFINDER_STOCK_GO_DOWN);
275   gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry),
276                                    GTK_ENTRY_ICON_SECONDARY,
277                                    _("Toggle view mode"));
278   gtk_widget_show (entry);
279 
280   window->completion = completion = gtk_entry_completion_new ();
281   gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (window->model));
282   gtk_entry_completion_set_match_func (completion, xfce_appfinder_window_completion_match_func, window, NULL);
283   g_object_set (G_OBJECT (completion), "text-column", XFCE_APPFINDER_MODEL_COLUMN_COMMAND, NULL);
284   gtk_entry_completion_set_popup_single_match (completion, TRUE);
285   gtk_entry_completion_set_inline_completion (completion, TRUE);
286 
287   cell = gtk_cell_renderer_text_new ();
288   g_object_set (G_OBJECT (cell), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
289   gtk_cell_renderer_set_fixed_size (cell, 1, -1);
290   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), cell, TRUE);
291   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion), cell, "text", XFCE_APPFINDER_MODEL_COLUMN_COMMAND);
292 
293   if (xfconf_channel_get_bool (window->channel, "/disable-completion", FALSE))
294     gtk_entry_completion_set_popup_completion (completion, FALSE);
295 
296   window->paned = pane = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
297   gtk_box_pack_start (GTK_BOX (vbox), pane, TRUE, TRUE, 0);
298   integer = xfconf_channel_get_int (window->channel, "/last/pane-position", DEFAULT_PANED_POSITION);
299   gtk_paned_set_position (GTK_PANED (pane), integer);
300   g_object_set (G_OBJECT (pane), "position-set", TRUE, NULL);
301 
302   scroll = gtk_scrolled_window_new (NULL, NULL);
303   gtk_paned_add1 (GTK_PANED (pane), scroll);
304   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_ETCHED_IN);
305   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
306   gtk_widget_show (scroll);
307 
308   if (xfconf_channel_get_bool (window->channel, "/hide-category-pane", FALSE))
309     gtk_widget_set_visible (scroll, FALSE);
310 
311   sidepane = window->sidepane = gtk_tree_view_new_with_model (GTK_TREE_MODEL (window->category_model));
312   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (sidepane), FALSE);
313   gtk_tree_view_set_enable_search (GTK_TREE_VIEW (sidepane), FALSE);
314   g_signal_connect_swapped (G_OBJECT (sidepane), "start-interactive-search",
315       G_CALLBACK (gtk_widget_grab_focus), entry);
316   g_signal_connect (G_OBJECT (sidepane), "key-press-event",
317       G_CALLBACK (xfce_appfinder_window_treeview_key_press_event), window);
318   gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (sidepane),
319       xfce_appfinder_category_model_row_separator_func, NULL, NULL);
320   gtk_container_add (GTK_CONTAINER (scroll), sidepane);
321   gtk_widget_show (sidepane);
322 
323   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sidepane));
324   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
325   g_signal_connect (G_OBJECT (selection), "changed",
326       G_CALLBACK (xfce_appfinder_window_category_changed), window);
327 
328   column = gtk_tree_view_column_new ();
329   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
330   gtk_tree_view_append_column (GTK_TREE_VIEW (sidepane), GTK_TREE_VIEW_COLUMN (column));
331 
332   renderer = gtk_cell_renderer_pixbuf_new ();
333   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, FALSE);
334   gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
335                                        "pixbuf", XFCE_APPFINDER_CATEGORY_MODEL_COLUMN_ICON, NULL);
336 
337   renderer = gtk_cell_renderer_text_new ();
338   g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
339   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, TRUE);
340   gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
341                                        "text", XFCE_APPFINDER_CATEGORY_MODEL_COLUMN_NAME, NULL);
342 
343   window->viewscroll = scroll = gtk_scrolled_window_new (NULL, NULL);
344   gtk_paned_add2 (GTK_PANED (pane), scroll);
345   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_ETCHED_IN);
346   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
347   gtk_widget_show (scroll);
348 
349   /* set the icon or tree view */
350   xfce_appfinder_window_view (window);
351 
352   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
353   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
354   gtk_widget_show (hbox);
355 
356   button = gtk_button_new_with_mnemonic (_("_Preferences"));
357   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
358   g_signal_connect (G_OBJECT (button), "clicked",
359       G_CALLBACK (xfce_appfinder_window_preferences), window);
360   gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
361   gtk_button_set_always_show_image(GTK_BUTTON(button), TRUE);
362   gtk_widget_show (button);
363 
364   image = gtk_image_new_from_icon_name (XFCE_APPFINDER_STOCK_PREFERENCES, GTK_ICON_SIZE_BUTTON);
365   gtk_button_set_image (GTK_BUTTON (button), image);
366 
367   window->button_launch = button = gtk_button_new_with_mnemonic (_("La_unch"));
368   gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
369   g_signal_connect_swapped (G_OBJECT (button), "clicked",
370       G_CALLBACK (xfce_appfinder_window_launch_clicked), window);
371   gtk_widget_set_sensitive (button, FALSE);
372   gtk_button_set_always_show_image(GTK_BUTTON(button), TRUE);
373   gtk_widget_show (button);
374 
375   image = gtk_image_new_from_icon_name (XFCE_APPFINDER_STOCK_EXECUTE, GTK_ICON_SIZE_BUTTON);
376   gtk_button_set_image (GTK_BUTTON (button), image);
377 
378   window->icon_theme = gtk_icon_theme_get_for_screen (gtk_window_get_screen (GTK_WINDOW (window)));
379   g_signal_connect_swapped (G_OBJECT (window->icon_theme), "changed",
380       G_CALLBACK (xfce_appfinder_window_icon_theme_changed), window);
381   g_object_ref (G_OBJECT (window->icon_theme));
382 
383   /* load categories in the model */
384   xfce_appfinder_window_category_set_categories (NULL, window);
385   window->categories_changed_id =
386       g_signal_connect (G_OBJECT (window->model), "categories-changed",
387                         G_CALLBACK (xfce_appfinder_window_category_set_categories),
388                         window);
389 
390   /* monitor xfconf property changes */
391   window->property_watch_id =
392     g_signal_connect (G_OBJECT (window->channel), "property-changed",
393         G_CALLBACK (xfce_appfinder_window_property_changed), window);
394 
395   APPFINDER_DEBUG ("constructed window");
396 }
397 
398 
399 
400 static void
xfce_appfinder_window_finalize(GObject * object)401 xfce_appfinder_window_finalize (GObject *object)
402 {
403   XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (object);
404 
405   if (window->idle_entry_changed_id != 0)
406     g_source_remove (window->idle_entry_changed_id);
407 
408   g_signal_handler_disconnect (window->channel, window->property_watch_id);
409   g_signal_handler_disconnect (window->model, window->categories_changed_id);
410 
411   /* release our reference on the icon theme */
412   g_signal_handlers_disconnect_by_func (G_OBJECT (window->icon_theme),
413       xfce_appfinder_window_icon_theme_changed, window);
414   g_object_unref (G_OBJECT (window->icon_theme));
415 
416   g_object_unref (G_OBJECT (window->model));
417   g_object_unref (G_OBJECT (window->category_model));
418   g_object_unref (G_OBJECT (window->filter_model));
419   g_object_unref (G_OBJECT (window->sort_model));
420   g_object_unref (G_OBJECT (window->completion));
421   g_object_unref (G_OBJECT (window->icon_find));
422 
423   if (window->actions != NULL)
424     g_object_unref (G_OBJECT (window->actions));
425 
426   if (window->filter_category != NULL)
427     g_object_unref (G_OBJECT (window->filter_category));
428   g_free (window->filter_text);
429 
430   (*G_OBJECT_CLASS (xfce_appfinder_window_parent_class)->finalize) (object);
431 }
432 
433 
434 
435 static void
xfce_appfinder_window_unmap(GtkWidget * widget)436 xfce_appfinder_window_unmap (GtkWidget *widget)
437 {
438   XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (widget);
439   gint                 width, height;
440   gint                 position;
441 
442   position = gtk_paned_get_position (GTK_PANED (window->paned));
443   gtk_window_get_size (GTK_WINDOW (window), &width, &height);
444   if (!gtk_widget_get_visible (window->paned))
445     height = window->last_window_height;
446 
447   (*GTK_WIDGET_CLASS (xfce_appfinder_window_parent_class)->unmap) (widget);
448 
449   xfconf_channel_set_int (window->channel, "/last/window-height", height);
450   xfconf_channel_set_int (window->channel, "/last/window-width", width);
451   xfconf_channel_set_int (window->channel, "/last/pane-position", position);
452 
453   return (*GTK_WIDGET_CLASS (xfce_appfinder_window_parent_class)->unmap) (widget);
454 }
455 
456 
457 
458 static gboolean
xfce_appfinder_window_key_press_event(GtkWidget * widget,GdkEventKey * event)459 xfce_appfinder_window_key_press_event (GtkWidget   *widget,
460                                        GdkEventKey *event)
461 {
462   XfceAppfinderWindow   *window = XFCE_APPFINDER_WINDOW (widget);
463   GtkWidget             *entry;
464   XfceAppfinderIconSize  icon_size = XFCE_APPFINDER_ICON_SIZE_DEFAULT_ITEM;
465 
466   if (event->keyval == GDK_KEY_Escape)
467     {
468       gtk_widget_destroy (widget);
469       return TRUE;
470     }
471 
472   if ((event->state & GDK_CONTROL_MASK) != 0)
473     {
474       switch (event->keyval)
475         {
476         case GDK_KEY_l:
477           entry = XFCE_APPFINDER_WINDOW (widget)->entry;
478 
479           gtk_widget_grab_focus (entry);
480           gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
481 
482           return TRUE;
483 
484         case GDK_KEY_1:
485         case GDK_KEY_2:
486           /* toggle between icon and tree view */
487           xfconf_channel_set_bool (window->channel, "/icon-view",
488                                    event->keyval == GDK_KEY_1);
489           return TRUE;
490 
491         case GDK_KEY_plus:
492         case GDK_KEY_minus:
493         case GDK_KEY_KP_Add:
494         case GDK_KEY_KP_Subtract:
495           g_object_get (G_OBJECT (window->model), "icon-size", &icon_size, NULL);
496           if ((event->keyval == GDK_KEY_plus || event->keyval == GDK_KEY_KP_Add))
497             {
498               if (icon_size < XFCE_APPFINDER_ICON_SIZE_LARGEST)
499                 icon_size++;
500             }
501           else if (icon_size > XFCE_APPFINDER_ICON_SIZE_SMALLEST)
502             {
503               icon_size--;
504             }
505           g_object_set (G_OBJECT (window->model), "icon-size", icon_size, NULL);
506           return TRUE;
507 
508         case GDK_KEY_0:
509         case GDK_KEY_KP_0:
510           g_object_set (G_OBJECT (window->model), "icon-size", icon_size, NULL);
511           return TRUE;
512         }
513     }
514 
515   return  (*GTK_WIDGET_CLASS (xfce_appfinder_window_parent_class)->key_press_event) (widget, event);
516 }
517 
518 
519 
520 static gboolean
xfce_appfinder_window_window_state_event(GtkWidget * widget,GdkEventWindowState * event)521 xfce_appfinder_window_window_state_event (GtkWidget           *widget,
522                                           GdkEventWindowState *event)
523 {
524   XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (widget);
525   gint                 width;
526 
527   if (!gtk_widget_get_visible (window->paned))
528     {
529       if ((event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0)
530         {
531           gtk_window_unmaximize (GTK_WINDOW (widget));
532 
533           /* set sensible width instead of taking entire width */
534           width = xfconf_channel_get_int (window->channel, "/last/window-width", DEFAULT_WINDOW_WIDTH);
535           gtk_window_resize (GTK_WINDOW (widget), width, 100 /* should be corrected by wm */);
536         }
537 
538       if ((event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0)
539         gtk_window_unfullscreen (GTK_WINDOW (widget));
540     }
541 
542   if ((*GTK_WIDGET_CLASS (xfce_appfinder_window_parent_class)->window_state_event) != NULL)
543     return (*GTK_WIDGET_CLASS (xfce_appfinder_window_parent_class)->window_state_event) (widget, event);
544 
545   return FALSE;
546 }
547 
548 
549 
550 static void
xfce_appfinder_window_set_item_width(XfceAppfinderWindow * window)551 xfce_appfinder_window_set_item_width (XfceAppfinderWindow *window)
552 {
553   gint                   width = 0, padding = 0;
554   gint                   text_column_idx, column_idx = 0;
555   XfceAppfinderIconSize  icon_size;
556   GtkOrientation         item_orientation = GTK_ORIENTATION_VERTICAL;
557   GList                 *renderers;
558   GList                 *li;
559 
560   appfinder_return_if_fail (GTK_IS_ICON_VIEW (window->view));
561 
562   g_object_get (G_OBJECT (window->model), "icon-size", &icon_size, NULL);
563 
564   /* some hard-coded values for the cell size that seem to work fine */
565   switch (icon_size)
566     {
567     case XFCE_APPFINDER_ICON_SIZE_SMALLEST:
568       padding = 2;
569       width = 16 * 3.75;
570       break;
571 
572     case XFCE_APPFINDER_ICON_SIZE_SMALLER:
573       padding = 2;
574       width = 24 * 3;
575       break;
576 
577     case XFCE_APPFINDER_ICON_SIZE_SMALL:
578       padding = 4;
579       width = 36 * 2.5;
580       break;
581 
582     case XFCE_APPFINDER_ICON_SIZE_NORMAL:
583       padding = 4;
584       width = 48 * 2;
585       break;
586 
587     case XFCE_APPFINDER_ICON_SIZE_LARGE:
588       padding = 6;
589       width = 64 * 1.5;
590       break;
591 
592     case XFCE_APPFINDER_ICON_SIZE_LARGER:
593       padding = 6;
594       width = 96 * 1.75;
595       break;
596 
597     case XFCE_APPFINDER_ICON_SIZE_LARGEST:
598       padding = 6;
599       width = 128 * 1.25;
600       break;
601     }
602 
603   if (xfconf_channel_get_bool (window->channel, "/text-beside-icons", FALSE))
604     {
605       item_orientation = GTK_ORIENTATION_HORIZONTAL;
606       width *= 2;
607     }
608 
609   gtk_icon_view_set_item_orientation (GTK_ICON_VIEW (window->view), item_orientation);
610   gtk_icon_view_set_item_padding (GTK_ICON_VIEW (window->view), padding);
611   gtk_icon_view_set_item_width (GTK_ICON_VIEW (window->view), width);
612 
613   renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (window->view));
614   text_column_idx = gtk_icon_view_get_text_column (GTK_ICON_VIEW (window->view));
615 
616   for (li = renderers; li != NULL; li = li->next, column_idx++) {
617     /* work around the yalign = 0.0 gtk uses */
618     if (item_orientation == GTK_ORIENTATION_HORIZONTAL) {
619       g_object_set (li->data, "yalign", 0.5, NULL);
620     }
621 
622     /* do not wrap when text beside icons is enabled */
623     if (column_idx == text_column_idx) {
624       g_object_set (li->data, "ellipsize",
625                     item_orientation == GTK_ORIENTATION_HORIZONTAL ?
626                     PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE, NULL);
627     }
628   }
629   g_list_free (renderers);
630 }
631 
632 
633 
634 static gboolean
xfce_appfinder_window_view_button_press_event(GtkWidget * widget,GdkEventButton * event,XfceAppfinderWindow * window)635 xfce_appfinder_window_view_button_press_event (GtkWidget           *widget,
636                                                GdkEventButton      *event,
637                                                XfceAppfinderWindow *window)
638 {
639   gint         x, y;
640   GtkTreePath *path;
641   gboolean     have_selection = FALSE;
642 
643   if (event->button == 3
644       && event->type == GDK_BUTTON_PRESS)
645     {
646       if (GTK_IS_TREE_VIEW (widget))
647         {
648           gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (widget),
649                                                              event->x, event->y, &x, &y);
650 
651           if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), x, y, &path, NULL, NULL, NULL))
652             {
653               gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
654               gtk_tree_path_free (path);
655               have_selection = TRUE;
656             }
657         }
658       else
659         {
660           path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (widget), event->x, event->y);
661           if (path != NULL)
662             {
663               gtk_icon_view_select_path (GTK_ICON_VIEW (widget), path);
664               gtk_icon_view_set_cursor (GTK_ICON_VIEW (widget), path, NULL, FALSE);
665               gtk_tree_path_free (path);
666               have_selection = TRUE;
667             }
668         }
669 
670       if (have_selection)
671         return xfce_appfinder_window_popup_menu (widget, window);
672     }
673 
674   return FALSE;
675 }
676 
677 
678 
679 static void
xfce_appfinder_window_view(XfceAppfinderWindow * window)680 xfce_appfinder_window_view (XfceAppfinderWindow *window)
681 {
682   GtkTreeViewColumn *column;
683   GtkCellRenderer   *renderer;
684   GtkTreeSelection  *selection;
685   GtkWidget         *view;
686   gboolean           icon_view;
687 
688   icon_view = xfconf_channel_get_bool (window->channel, "/icon-view", FALSE);
689   if (window->view != NULL)
690     {
691       if (icon_view && GTK_IS_ICON_VIEW (window->view))
692         return;
693       gtk_widget_destroy (window->view);
694     }
695 
696   window->filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (window->model), NULL);
697   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (window->filter_model), xfce_appfinder_window_item_visible, window, NULL);
698 
699   window->sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (window->filter_model));
700   if (xfconf_channel_get_bool (window->channel, "/sort-by-frecency", FALSE))
701     gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (window->sort_model), xfce_appfinder_window_sort_items_frecency, window->entry, NULL);
702   else
703     gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (window->sort_model), xfce_appfinder_window_sort_items, window->entry, NULL);
704 
705   if (icon_view)
706     {
707       window->view = view = gtk_icon_view_new_with_model (
708         xfce_appfinder_should_sort_icon_view () ?
709           window->sort_model :
710           window->filter_model);
711 
712       gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (view), GTK_SELECTION_SINGLE);
713       gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (view), XFCE_APPFINDER_MODEL_COLUMN_ICON);
714       gtk_icon_view_set_text_column (GTK_ICON_VIEW (view), XFCE_APPFINDER_MODEL_COLUMN_TITLE);
715       gtk_icon_view_set_tooltip_column (GTK_ICON_VIEW (view), XFCE_APPFINDER_MODEL_COLUMN_TOOLTIP);
716       gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (view), 0);
717       xfce_appfinder_window_set_item_width (window);
718 
719       g_signal_connect_swapped (G_OBJECT (view), "selection-changed",
720           G_CALLBACK (xfce_appfinder_window_item_changed), window);
721       g_signal_connect_swapped (G_OBJECT (view), "item-activated",
722           G_CALLBACK (xfce_appfinder_window_row_activated), window);
723     }
724   else
725     {
726       window->view = view = gtk_tree_view_new_with_model (window->sort_model);
727       gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
728       gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
729       gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (view), XFCE_APPFINDER_MODEL_COLUMN_TOOLTIP);
730       g_signal_connect_swapped (G_OBJECT (view), "row-activated",
731           G_CALLBACK (xfce_appfinder_window_row_activated), window);
732       g_signal_connect_swapped (G_OBJECT (view), "start-interactive-search",
733           G_CALLBACK (gtk_widget_grab_focus), window->entry);
734       g_signal_connect (G_OBJECT (view), "key-press-event",
735            G_CALLBACK (xfce_appfinder_window_treeview_key_press_event), window);
736 
737       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
738       gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
739       g_signal_connect_swapped (G_OBJECT (selection), "changed",
740           G_CALLBACK (xfce_appfinder_window_item_changed), window);
741 
742       column = gtk_tree_view_column_new ();
743       gtk_tree_view_column_set_spacing (column, 2);
744       gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
745       gtk_tree_view_append_column (GTK_TREE_VIEW (view), GTK_TREE_VIEW_COLUMN (column));
746 
747       renderer = gtk_cell_renderer_pixbuf_new ();
748       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, FALSE);
749       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (column), renderer,
750                                       "pixbuf", XFCE_APPFINDER_MODEL_COLUMN_ICON, NULL);
751 
752       renderer = gtk_cell_renderer_text_new ();
753       g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
754       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, TRUE);
755       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (column), renderer,
756                                       "markup", XFCE_APPFINDER_MODEL_COLUMN_ABSTRACT, NULL);
757     }
758 
759   gtk_drag_source_set (view, GDK_BUTTON1_MASK, target_list,
760                        G_N_ELEMENTS (target_list), GDK_ACTION_COPY);
761   g_signal_connect (G_OBJECT (view), "drag-begin",
762       G_CALLBACK (xfce_appfinder_window_drag_begin), window);
763   g_signal_connect (G_OBJECT (view), "drag-data-get",
764       G_CALLBACK (xfce_appfinder_window_drag_data_get), window);
765   g_signal_connect (G_OBJECT (view), "popup-menu",
766       G_CALLBACK (xfce_appfinder_window_popup_menu), window);
767   g_signal_connect (G_OBJECT (view), "button-press-event",
768       G_CALLBACK (xfce_appfinder_window_view_button_press_event), window);
769   gtk_container_add (GTK_CONTAINER (window->viewscroll), view);
770   gtk_widget_show (view);
771 }
772 
773 
774 
775 static gboolean
xfce_appfinder_window_view_get_selected(XfceAppfinderWindow * window,GtkTreeModel ** model,GtkTreeIter * iter)776 xfce_appfinder_window_view_get_selected (XfceAppfinderWindow  *window,
777                                          GtkTreeModel        **model,
778                                          GtkTreeIter          *iter)
779 {
780   GtkTreeSelection *selection;
781   gboolean          have_iter;
782   GList            *items;
783 
784   appfinder_return_val_if_fail (XFCE_IS_APPFINDER_WINDOW (window), FALSE);
785   appfinder_return_val_if_fail (model != NULL, FALSE);
786   appfinder_return_val_if_fail (iter != NULL, FALSE);
787 
788   if (GTK_IS_TREE_VIEW (window->view))
789     {
790       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->view));
791       have_iter = gtk_tree_selection_get_selected (selection, model, iter);
792     }
793   else
794     {
795       items = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (window->view));
796       appfinder_assert (g_list_length (items) <= 1);
797       if (items != NULL)
798         {
799           *model = gtk_icon_view_get_model (GTK_ICON_VIEW (window->view));
800           have_iter = gtk_tree_model_get_iter (*model, iter, items->data);
801 
802           gtk_tree_path_free (items->data);
803           g_list_free (items);
804         }
805       else
806         {
807           have_iter = FALSE;
808         }
809     }
810 
811   return have_iter;
812 }
813 
814 
815 
816 static gboolean
xfce_appfinder_window_view_get_selected_path(XfceAppfinderWindow * window,GtkTreePath ** path)817 xfce_appfinder_window_view_get_selected_path (XfceAppfinderWindow  *window,
818                                               GtkTreePath         **path)
819 {
820   GtkTreeModel *model;
821   GtkTreeIter   iter;
822 
823   if (!xfce_appfinder_window_view_get_selected (window, &model, &iter))
824     return FALSE;
825 
826   *path = gtk_tree_model_get_path (model, &iter);
827 
828   return (*path != NULL);
829 }
830 
831 
832 
833 static void
xfce_appfinder_window_popup_menu_toggle_bookmark(GtkWidget * mi,XfceAppfinderWindow * window)834 xfce_appfinder_window_popup_menu_toggle_bookmark (GtkWidget           *mi,
835                                                   XfceAppfinderWindow *window)
836 {
837   const gchar  *uri;
838   GFile        *gfile;
839   gchar        *desktop_id;
840   GtkWidget    *menu = gtk_widget_get_parent (mi);
841   GtkTreeModel *model;
842   GError       *error = NULL;
843 
844   uri = g_object_get_data (G_OBJECT (menu), "uri");
845   if (uri != NULL)
846     {
847       gfile = g_file_new_for_uri (uri);
848       desktop_id = g_file_get_basename (gfile);
849       g_object_unref (G_OBJECT (gfile));
850 
851       /* toggle bookmarks */
852       model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (window->filter_model));
853       xfce_appfinder_model_bookmark_toggle (XFCE_APPFINDER_MODEL (model), desktop_id, &error);
854 
855       g_free (desktop_id);
856 
857       if (G_UNLIKELY (error != NULL))
858         {
859           g_printerr ("%s: failed to save bookmarks: %s\n", G_LOG_DOMAIN, error->message);
860           g_error_free (error);
861         }
862     }
863 }
864 
865 
866 
867 static void
xfce_appfinder_window_popup_menu_execute(GtkWidget * mi,XfceAppfinderWindow * window)868 xfce_appfinder_window_popup_menu_execute (GtkWidget           *mi,
869                                           XfceAppfinderWindow *window)
870 {
871   xfce_appfinder_window_execute (window, FALSE);
872 }
873 
874 
875 
876 static void
xfce_appfinder_window_popup_menu_edit(GtkWidget * mi,XfceAppfinderWindow * window)877 xfce_appfinder_window_popup_menu_edit (GtkWidget           *mi,
878                                        XfceAppfinderWindow *window)
879 {
880   GError      *error = NULL;
881   gchar       *cmd;
882   const gchar *uri;
883   GtkWidget   *menu = gtk_widget_get_parent (mi);
884 
885   appfinder_return_if_fail (GTK_IS_MENU (menu));
886   appfinder_return_if_fail (XFCE_IS_APPFINDER_WINDOW (window));
887 
888   uri = g_object_get_data (G_OBJECT (menu), "uri");
889   if (G_LIKELY (uri != NULL))
890     {
891       cmd = g_strdup_printf ("exo-desktop-item-edit --xid=0x%x '%s'",
892                              APPFINDER_WIDGET_XID (window), uri);
893       if (!g_spawn_command_line_async (cmd, &error))
894         {
895           xfce_dialog_show_error (GTK_WINDOW (window), error,
896                                   _("Failed to launch desktop item editor"));
897           g_error_free (error);
898         }
899       g_free (cmd);
900     }
901 }
902 
903 
904 
905 static void
xfce_appfinder_window_popup_menu_revert(GtkWidget * mi,XfceAppfinderWindow * window)906 xfce_appfinder_window_popup_menu_revert (GtkWidget           *mi,
907                                          XfceAppfinderWindow *window)
908 {
909   const gchar *uri;
910   const gchar *name;
911   GError      *error;
912   GtkWidget   *menu = gtk_widget_get_parent (mi);
913 
914   appfinder_return_if_fail (GTK_IS_MENU (menu));
915   appfinder_return_if_fail (XFCE_IS_APPFINDER_WINDOW (window));
916 
917   name = g_object_get_data (G_OBJECT (menu), "name");
918   if (name == NULL)
919     return;
920 
921   if (xfce_dialog_confirm (GTK_WINDOW (window), XFCE_APPFINDER_STOCK_REVERT_TO_SAVED, NULL,
922           _("This will permanently remove the custom desktop file from your home directory."),
923           _("Are you sure you want to revert \"%s\"?"), name))
924     {
925       uri = g_object_get_data (G_OBJECT (menu), "uri");
926       if (uri != NULL)
927         {
928           if (g_unlink (uri + 7) == -1)
929             {
930               error = g_error_new (G_FILE_ERROR, g_file_error_from_errno (errno),
931                                    "%s: %s", uri + 7, g_strerror (errno));
932               xfce_dialog_show_error (GTK_WINDOW (window), error,
933                                       _("Failed to remove desktop file"));
934               g_error_free (error);
935             }
936         }
937     }
938 }
939 
940 
941 
942 static void
xfce_appfinder_window_popup_menu_hide(GtkWidget * mi,XfceAppfinderWindow * window)943 xfce_appfinder_window_popup_menu_hide (GtkWidget           *mi,
944                                        XfceAppfinderWindow *window)
945 {
946   const gchar  *uri;
947   const gchar  *name;
948   GtkWidget    *menu = gtk_widget_get_parent (mi);
949   gchar        *path;
950   gchar        *message;
951   gchar       **dirs;
952   guint         i;
953   const gchar  *relpath;
954   XfceRc       *rc;
955 
956   appfinder_return_if_fail (GTK_IS_MENU (menu));
957   appfinder_return_if_fail (XFCE_IS_APPFINDER_WINDOW (window));
958 
959   name = g_object_get_data (G_OBJECT (menu), "name");
960   if (name == NULL)
961     return;
962 
963   path = xfce_resource_save_location (XFCE_RESOURCE_DATA, "applications/", FALSE);
964   /* I18N: the first %s will be replace with users' applications directory, the
965    * second with Hidden=true */
966   message = g_strdup_printf (_("To unhide the item you have to manually "
967                                "remove the desktop file from \"%s\" or "
968                                "open the file in the same directory and "
969                                "remove the line \"%s\"."), path, "Hidden=true");
970 
971   if (xfce_dialog_confirm (GTK_WINDOW (window), NULL, _("_Hide"), message,
972           _("Are you sure you want to hide \"%s\"?"), name))
973     {
974       uri = g_object_get_data (G_OBJECT (menu), "uri");
975       if (uri != NULL)
976         {
977           /* lookup the correct relative path */
978           dirs = xfce_resource_lookup_all (XFCE_RESOURCE_DATA, "applications/");
979           for (i = 0; dirs[i] != NULL; i++)
980             {
981               if (g_str_has_prefix (uri + 7, dirs[i]))
982                 {
983                   /* relative path to XFCE_RESOURCE_DATA */
984                   relpath = uri + 7 + strlen (dirs[i]) - 13;
985 
986                   /* xfcerc can handle everything else */
987                   rc = xfce_rc_config_open (XFCE_RESOURCE_DATA, relpath, FALSE);
988                   xfce_rc_set_group (rc, G_KEY_FILE_DESKTOP_GROUP);
989                   xfce_rc_write_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_HIDDEN, TRUE);
990                   xfce_rc_close (rc);
991 
992                   break;
993                 }
994             }
995           g_strfreev (dirs);
996         }
997     }
998 
999   g_free (path);
1000   g_free (message);
1001 }
1002 
1003 
1004 
1005 static gboolean
xfce_appfinder_window_popup_menu(GtkWidget * view,XfceAppfinderWindow * window)1006 xfce_appfinder_window_popup_menu (GtkWidget           *view,
1007                                   XfceAppfinderWindow *window)
1008 {
1009   GtkWidget    *menu;
1010   GtkTreeModel *model;
1011   GtkTreeIter   iter;
1012   gchar        *title;
1013   gchar        *uri;
1014   GtkWidget    *mi;
1015   GtkWidget    *image;
1016   GtkWidget    *box;
1017   GtkWidget    *label;
1018   gchar        *path;
1019   gboolean      uri_is_local;
1020   gboolean      is_bookmark;
1021 
1022   if (xfce_appfinder_window_view_get_selected (window, &model, &iter))
1023     {
1024       gtk_tree_model_get (model, &iter,
1025                           XFCE_APPFINDER_MODEL_COLUMN_TITLE, &title,
1026                           XFCE_APPFINDER_MODEL_COLUMN_URI, &uri,
1027                           XFCE_APPFINDER_MODEL_COLUMN_BOOKMARK, &is_bookmark,
1028                           -1);
1029 
1030       /* custom command don't have an uri */
1031       if (uri == NULL)
1032         {
1033           g_free (title);
1034           return FALSE;
1035         }
1036 
1037       uri_is_local = g_str_has_prefix (uri, XFCE_APPFINDER_LOCAL_PREFIX);
1038 
1039       menu = gtk_menu_new ();
1040       gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
1041       g_object_set_data_full (G_OBJECT (menu), "uri", uri, g_free);
1042       g_object_set_data_full (G_OBJECT (menu), "name", title, g_free);
1043       g_object_set_data (G_OBJECT (menu), "model", model);
1044       g_signal_connect (G_OBJECT (menu), "selection-done",
1045           G_CALLBACK (gtk_widget_destroy), NULL);
1046 
1047       /* Title */
1048       mi = gtk_menu_item_new ();
1049       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1050       gtk_widget_set_sensitive (mi, FALSE);
1051 
1052       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1053       label = gtk_label_new (title);
1054       image = gtk_image_new_from_icon_name ("", GTK_ICON_SIZE_MENU);
1055       gtk_container_add (GTK_CONTAINER (box), image);
1056       gtk_container_add (GTK_CONTAINER (box), label);
1057       gtk_container_add (GTK_CONTAINER (mi), box);
1058       gtk_widget_show_all (mi);
1059 
1060       /* Separator */
1061       mi = gtk_separator_menu_item_new ();
1062       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1063       gtk_widget_show (mi);
1064 
1065       /* Add/Remove from Bookmarks */
1066       mi = gtk_menu_item_new ();
1067       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1068       g_signal_connect (G_OBJECT (mi), "activate",
1069           G_CALLBACK (xfce_appfinder_window_popup_menu_toggle_bookmark), window);
1070 
1071       if (is_bookmark)
1072         image = gtk_image_new_from_icon_name (XFCE_APPFINDER_STOCK_REMOVE, GTK_ICON_SIZE_MENU);
1073       else
1074         image = gtk_image_new_from_icon_name (XFCE_APPFINDER_STOCK_BOOKMARK_NEW, GTK_ICON_SIZE_MENU);
1075 
1076       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1077       label = gtk_label_new (is_bookmark ? _("Remove From Bookmarks") : _("Add to Bookmarks"));
1078       gtk_container_add (GTK_CONTAINER (box), image);
1079       gtk_container_add (GTK_CONTAINER (box), label);
1080       gtk_container_add (GTK_CONTAINER (mi), box);
1081       gtk_widget_show_all (mi);
1082 
1083       /* Separator */
1084       mi = gtk_separator_menu_item_new ();
1085       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1086       gtk_widget_show (mi);
1087 
1088       /* Launch */
1089       mi = gtk_menu_item_new ();
1090       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1091       g_signal_connect (G_OBJECT (mi), "activate",
1092           G_CALLBACK (xfce_appfinder_window_popup_menu_execute), window);
1093 
1094       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1095       label = gtk_label_new_with_mnemonic (_("La_unch"));
1096       image = gtk_image_new_from_icon_name (XFCE_APPFINDER_STOCK_EXECUTE, GTK_ICON_SIZE_MENU);
1097       gtk_container_add (GTK_CONTAINER (box), image);
1098       gtk_container_add (GTK_CONTAINER (box), label);
1099       gtk_container_add (GTK_CONTAINER (mi), box);
1100       gtk_widget_show_all (mi);
1101 
1102       /* Edit */
1103       mi = gtk_menu_item_new ();
1104       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1105       g_signal_connect (G_OBJECT (mi), "activate",
1106           G_CALLBACK (xfce_appfinder_window_popup_menu_edit), window);
1107 
1108       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1109       label = gtk_label_new_with_mnemonic (_("_Edit"));
1110       image = gtk_image_new_from_icon_name (XFCE_APPFINDER_STOCK_EDIT, GTK_ICON_SIZE_MENU);
1111       gtk_container_add (GTK_CONTAINER (box), image);
1112       gtk_container_add (GTK_CONTAINER (box), label);
1113       gtk_container_add (GTK_CONTAINER (mi), box);
1114       gtk_widget_show_all (mi);
1115 
1116       /* Revert */
1117       mi = gtk_menu_item_new ();
1118       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1119       g_signal_connect (G_OBJECT (mi), "activate",
1120           G_CALLBACK (xfce_appfinder_window_popup_menu_revert), window);
1121       path = xfce_resource_save_location (XFCE_RESOURCE_DATA, "applications/", FALSE);
1122       gtk_widget_set_sensitive (mi, uri_is_local && g_str_has_prefix (uri + 7, path));
1123       g_free (path);
1124 
1125       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1126       label = gtk_label_new_with_mnemonic (_("_Revert"));
1127       image = gtk_image_new_from_icon_name (XFCE_APPFINDER_STOCK_REVERT_TO_SAVED, GTK_ICON_SIZE_MENU);
1128       gtk_container_add (GTK_CONTAINER (box), image);
1129       gtk_container_add (GTK_CONTAINER (box), label);
1130       gtk_container_add (GTK_CONTAINER (mi), box);
1131       gtk_widget_show_all (mi);
1132 
1133       /* Hide */
1134       mi = gtk_menu_item_new ();
1135       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1136       gtk_widget_set_sensitive (mi, uri_is_local);
1137       g_signal_connect (G_OBJECT (mi), "activate",
1138           G_CALLBACK (xfce_appfinder_window_popup_menu_hide), window);
1139 
1140       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1141       label = gtk_label_new_with_mnemonic (_("_Hide"));
1142       image = gtk_image_new_from_icon_name ("", GTK_ICON_SIZE_MENU);
1143       gtk_container_add (GTK_CONTAINER (box), image);
1144       gtk_container_add (GTK_CONTAINER (box), label);
1145       gtk_container_add (GTK_CONTAINER (mi), box);
1146       gtk_widget_show_all (mi);
1147 
1148       gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
1149 
1150       return TRUE;
1151     }
1152 
1153   return FALSE;
1154 }
1155 
1156 
1157 
1158 static void
xfce_appfinder_window_update_image(XfceAppfinderWindow * window,GdkPixbuf * pixbuf)1159 xfce_appfinder_window_update_image (XfceAppfinderWindow *window,
1160                                     GdkPixbuf           *pixbuf)
1161 {
1162   if (pixbuf == NULL)
1163     pixbuf = window->icon_find;
1164 
1165   /* gtk doesn't check this */
1166   if (gtk_image_get_pixbuf (GTK_IMAGE (window->image)) != pixbuf)
1167     gtk_image_set_from_pixbuf (GTK_IMAGE (window->image), pixbuf);
1168 }
1169 
1170 
1171 
1172 static void
xfce_appfinder_window_set_padding(GtkWidget * entry,GtkWidget * align)1173 xfce_appfinder_window_set_padding (GtkWidget *entry,
1174                                    GtkWidget *align)
1175 {
1176   gint          padding;
1177   GtkAllocation alloc;
1178 
1179   /* 48 is the icon size of XFCE_APPFINDER_ICON_SIZE_48 */
1180   gtk_widget_get_allocation (entry, &alloc);
1181   padding = (48 - alloc.height) / 2;
1182   // gtk_alignment_set_padding (GTK_ALIGNMENT (align), MAX (0, padding), 0, 0, 0);
1183   gtk_widget_set_margin_top (align, MAX (0, padding));
1184 }
1185 
1186 
1187 
1188 static gboolean
xfce_appfinder_window_completion_match_func(GtkEntryCompletion * completion,const gchar * key,GtkTreeIter * iter,gpointer data)1189 xfce_appfinder_window_completion_match_func (GtkEntryCompletion *completion,
1190                                              const gchar        *key,
1191                                              GtkTreeIter        *iter,
1192                                              gpointer            data)
1193 {
1194   const gchar *text;
1195 
1196   XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (data);
1197 
1198   appfinder_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
1199   appfinder_return_val_if_fail (XFCE_IS_APPFINDER_WINDOW (data), FALSE);
1200   appfinder_return_val_if_fail (GTK_TREE_MODEL (window->model)
1201       == gtk_entry_completion_get_model (completion), FALSE);
1202 
1203   /* don't use the casefolded key generated by gtk */
1204   text = gtk_entry_get_text (GTK_ENTRY (window->entry));
1205 
1206   return xfce_appfinder_model_get_visible_command (window->model, iter, text);
1207 }
1208 
1209 
1210 
1211 static gboolean
xfce_appfinder_window_entry_changed_idle(gpointer data)1212 xfce_appfinder_window_entry_changed_idle (gpointer data)
1213 {
1214   XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (data);
1215   const gchar         *text;
1216   GdkPixbuf           *pixbuf;
1217   gchar               *normalized;
1218   GtkTreePath         *path;
1219   GtkTreeSelection    *selection;
1220 
1221   text = gtk_entry_get_text (GTK_ENTRY (window->entry));
1222 
1223   if (gtk_widget_get_visible (window->paned))
1224     {
1225       g_free (window->filter_text);
1226 
1227       if (IS_STRING (text))
1228         {
1229           normalized = g_utf8_normalize (text, -1, G_NORMALIZE_ALL);
1230           window->filter_text = g_utf8_casefold (normalized, -1);
1231           g_free (normalized);
1232         }
1233       else
1234         {
1235           window->filter_text = NULL;
1236         }
1237 
1238       APPFINDER_DEBUG ("refilter entry");
1239 
1240       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (window->filter_model));
1241       APPFINDER_DEBUG ("FILTER TEXT: %s\n", window->filter_text);
1242 
1243       if (GTK_IS_TREE_VIEW (window->view))
1244         {
1245           gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (window->view), 0, 0);
1246 
1247           if (window->filter_text == NULL)
1248             {
1249               /* if filter is empty, clear selection */
1250               selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->view));
1251               gtk_tree_selection_unselect_all (selection);
1252 
1253               /* reset cursor */
1254               path = gtk_tree_path_new_first ();
1255               gtk_tree_view_set_cursor (GTK_TREE_VIEW (window->view), path, NULL, FALSE);
1256               gtk_tree_path_free (path);
1257             }
1258           else if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (window->view), &path, NULL))
1259             {
1260               /* otherwise select the first match */
1261               gtk_tree_view_set_cursor (GTK_TREE_VIEW (window->view), path, NULL, FALSE);
1262               gtk_tree_path_free (path);
1263             }
1264         }
1265       else
1266         {
1267           if (window->filter_text == NULL)
1268             {
1269               /* if filter is empty, clear selection */
1270               gtk_icon_view_unselect_all (GTK_ICON_VIEW (window->view));
1271             }
1272           else if (gtk_icon_view_get_visible_range (GTK_ICON_VIEW (window->view), &path, NULL))
1273             {
1274               /* otherwise select the first match */
1275               gtk_icon_view_select_path (GTK_ICON_VIEW (window->view), path);
1276               gtk_icon_view_set_cursor (GTK_ICON_VIEW (window->view), path, NULL, FALSE);
1277               gtk_tree_path_free (path);
1278             }
1279         }
1280     }
1281   else
1282     {
1283       gtk_widget_set_sensitive (window->button_launch, IS_STRING (text));
1284 
1285       /* Clear entry's error icon and tooltip */
1286       gtk_entry_set_icon_from_icon_name (GTK_ENTRY (window->entry), GTK_ENTRY_ICON_PRIMARY, NULL);
1287       gtk_entry_set_icon_tooltip_text (GTK_ENTRY (window->entry), GTK_ENTRY_ICON_PRIMARY, NULL);
1288 
1289       pixbuf = xfce_appfinder_model_get_icon_for_command (window->model, text);
1290       xfce_appfinder_window_update_image (window, pixbuf);
1291       if (pixbuf != NULL)
1292         g_object_unref (G_OBJECT (pixbuf));
1293     }
1294 
1295 
1296   return FALSE;
1297 }
1298 
1299 
1300 
1301 static void
xfce_appfinder_window_entry_changed_idle_destroyed(gpointer data)1302 xfce_appfinder_window_entry_changed_idle_destroyed (gpointer data)
1303 {
1304   XFCE_APPFINDER_WINDOW (data)->idle_entry_changed_id = 0;
1305 }
1306 
1307 
1308 
1309 static void
xfce_appfinder_window_entry_changed(XfceAppfinderWindow * window)1310 xfce_appfinder_window_entry_changed (XfceAppfinderWindow *window)
1311 {
1312   if (window->idle_entry_changed_id != 0)
1313     g_source_remove (window->idle_entry_changed_id);
1314 
1315   window->idle_entry_changed_id =
1316       gdk_threads_add_idle_full (G_PRIORITY_DEFAULT, xfce_appfinder_window_entry_changed_idle,
1317                        window, xfce_appfinder_window_entry_changed_idle_destroyed);
1318 }
1319 
1320 
1321 
1322 static void
xfce_appfinder_window_entry_activate(GtkEditable * entry,XfceAppfinderWindow * window)1323 xfce_appfinder_window_entry_activate (GtkEditable         *entry,
1324                                       XfceAppfinderWindow *window)
1325 {
1326   GList            *selected_items;
1327   GtkTreeSelection *selection;
1328   gboolean          has_selection = FALSE;
1329 
1330   if (gtk_widget_get_visible (window->paned))
1331     {
1332       if (GTK_IS_TREE_VIEW (window->view))
1333         {
1334           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->view));
1335           has_selection = gtk_tree_selection_count_selected_rows (selection) > 0;
1336         }
1337       else
1338         {
1339           selected_items = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (window->view));
1340           has_selection = (selected_items != NULL);
1341           g_list_free_full (selected_items, (GDestroyNotify) gtk_tree_path_free);
1342         }
1343 
1344       if (has_selection)
1345         xfce_appfinder_window_execute (window, TRUE);
1346     }
1347   else if (gtk_widget_get_sensitive (window->button_launch))
1348     {
1349       gtk_button_clicked (GTK_BUTTON (window->button_launch));
1350     }
1351 }
1352 
1353 
1354 
1355 static gboolean
xfce_appfinder_window_pointer_is_grabbed(GtkWidget * widget)1356 xfce_appfinder_window_pointer_is_grabbed (GtkWidget *widget)
1357 {
1358   GdkSeat          *seat;
1359   GdkDevice        *pointer;
1360   GdkDisplay       *display;
1361 
1362   display = gtk_widget_get_display (widget);
1363   seat = gdk_display_get_default_seat (display);
1364   pointer = gdk_seat_get_pointer (seat);
1365   return gdk_display_device_is_grabbed (display, pointer);
1366 }
1367 
1368 
1369 
1370 static gboolean
xfce_appfinder_window_entry_key_press_event(GtkWidget * entry,GdkEventKey * event,XfceAppfinderWindow * window)1371 xfce_appfinder_window_entry_key_press_event (GtkWidget           *entry,
1372                                              GdkEventKey         *event,
1373                                              XfceAppfinderWindow *window)
1374 {
1375   gboolean          expand;
1376   gboolean          is_expanded;
1377   GtkTreePath      *path;
1378 
1379   if (event->keyval == GDK_KEY_Tab &&
1380       !gtk_widget_get_visible (window->paned) &&
1381       xfce_appfinder_window_pointer_is_grabbed (entry))
1382     {
1383       /* don't tab to the close button */
1384       return TRUE;
1385     }
1386 
1387   if (event->keyval == GDK_KEY_Up ||
1388       event->keyval == GDK_KEY_Down)
1389     {
1390       expand = (event->keyval == GDK_KEY_Down);
1391       is_expanded = gtk_widget_get_visible (window->paned);
1392 
1393       if (is_expanded != expand)
1394         {
1395           /* don't break entry completion navigation in collapsed mode */
1396           if (!is_expanded && xfce_appfinder_window_pointer_is_grabbed (entry))
1397             {
1398               /* window is still collapsed and the pointer is grabbed
1399                * by the popup menu, do nothing with the event */
1400               return FALSE;
1401             }
1402 
1403           xfce_appfinder_window_set_expanded (window, expand);
1404           return TRUE;
1405         }
1406 
1407       /* The first item is usually selected, so we should jump to 2nd one
1408        * when down key is pressed */
1409       if (is_expanded && expand &&
1410           GTK_IS_TREE_VIEW (window->view) && /* does not make sense for icon view */
1411           xfce_appfinder_window_view_get_selected_path (window, &path))
1412         {
1413           gtk_tree_path_next (path);
1414           gtk_tree_view_set_cursor (GTK_TREE_VIEW (window->view), path, NULL, FALSE);
1415           gtk_tree_path_free (path);
1416         }
1417     }
1418 
1419   return FALSE;
1420 }
1421 
1422 
1423 
1424 static void
xfce_appfinder_window_drag_begin(GtkWidget * widget,GdkDragContext * drag_context,XfceAppfinderWindow * window)1425 xfce_appfinder_window_drag_begin (GtkWidget           *widget,
1426                                   GdkDragContext      *drag_context,
1427                                   XfceAppfinderWindow *window)
1428 {
1429   GtkTreeModel *model;
1430   GtkTreeIter   iter;
1431   GdkPixbuf    *pixbuf;
1432 
1433   if (xfce_appfinder_window_view_get_selected (window, &model, &iter))
1434     {
1435       gtk_tree_model_get (model, &iter, XFCE_APPFINDER_MODEL_COLUMN_ICON_LARGE, &pixbuf, -1);
1436       if (G_LIKELY (pixbuf != NULL))
1437         {
1438           gtk_drag_set_icon_pixbuf (drag_context, pixbuf, 0, 0);
1439           g_object_unref (G_OBJECT (pixbuf));
1440         }
1441     }
1442   else
1443     {
1444       gtk_drag_set_icon_name (drag_context, XFCE_APPFINDER_STOCK_DIALOG_ERROR, 0, 0);
1445     }
1446 }
1447 
1448 
1449 
1450 static void
xfce_appfinder_window_drag_data_get(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * data,guint info,guint drag_time,XfceAppfinderWindow * window)1451 xfce_appfinder_window_drag_data_get (GtkWidget           *widget,
1452                                      GdkDragContext      *drag_context,
1453                                      GtkSelectionData    *data,
1454                                      guint                info,
1455                                      guint                drag_time,
1456                                      XfceAppfinderWindow *window)
1457 {
1458   GtkTreeModel *model;
1459   GtkTreeIter   iter;
1460   gchar        *uris[2];
1461 
1462   if (xfce_appfinder_window_view_get_selected (window, &model, &iter))
1463     {
1464       uris[1] = NULL;
1465       gtk_tree_model_get (model, &iter, XFCE_APPFINDER_MODEL_COLUMN_URI, &uris[0], -1);
1466       gtk_selection_data_set_uris (data, uris);
1467       g_free (uris[0]);
1468     }
1469 }
1470 
1471 
1472 
1473 static gboolean
xfce_appfinder_window_treeview_key_press_event(GtkWidget * widget,GdkEventKey * event,XfceAppfinderWindow * window)1474 xfce_appfinder_window_treeview_key_press_event (GtkWidget           *widget,
1475                                                 GdkEventKey         *event,
1476                                                 XfceAppfinderWindow *window)
1477 {
1478   GdkEvent     ev;
1479   GtkTreePath *path;
1480 
1481   if (widget == window->view)
1482     {
1483       if (event->keyval == GDK_KEY_Control_L ||
1484           event->keyval == GDK_KEY_Control_R ||
1485           event->keyval == GDK_KEY_Shift_L ||
1486           event->keyval == GDK_KEY_Shift_R ||
1487           event->keyval == GDK_KEY_Alt_L ||
1488           event->keyval == GDK_KEY_Alt_R ||
1489           event->keyval == GDK_KEY_Right ||
1490           event->keyval == GDK_KEY_Down)
1491           return FALSE;
1492 
1493       if (event->keyval == GDK_KEY_Left)
1494         {
1495           if (gtk_widget_get_realized (window->sidepane))
1496             gtk_widget_grab_focus (window->sidepane);
1497           return TRUE;
1498         }
1499 
1500       if (event->keyval == GDK_KEY_Up)
1501         {
1502           if (xfce_appfinder_window_view_get_selected_path (window, &path))
1503             {
1504               if (!gtk_tree_path_prev (path))
1505                 gtk_widget_grab_focus (window->entry);
1506               gtk_tree_path_free (path);
1507             }
1508 
1509           return FALSE;
1510         }
1511 
1512       gtk_widget_grab_focus (window->entry);
1513 
1514       ev.key = *event;
1515       gtk_propagate_event (window->entry, &ev);
1516 
1517       return TRUE;
1518     }
1519 
1520   if (widget == window->sidepane)
1521     {
1522       if (event->keyval == GDK_KEY_Right)
1523         {
1524           gtk_widget_grab_focus (window->view);
1525           return TRUE;
1526         }
1527     }
1528 
1529   return FALSE;
1530 }
1531 
1532 
1533 
1534 static void
xfce_appfinder_window_entry_icon_released(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkEvent * event,XfceAppfinderWindow * window)1535 xfce_appfinder_window_entry_icon_released (GtkEntry             *entry,
1536                                            GtkEntryIconPosition  icon_pos,
1537                                            GdkEvent             *event,
1538                                            XfceAppfinderWindow  *window)
1539 {
1540   if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
1541     xfce_appfinder_window_set_expanded (window, !gtk_widget_get_visible (window->paned));
1542 }
1543 
1544 
1545 
1546 static void
xfce_appfinder_window_category_changed(GtkTreeSelection * selection,XfceAppfinderWindow * window)1547 xfce_appfinder_window_category_changed (GtkTreeSelection    *selection,
1548                                         XfceAppfinderWindow *window)
1549 {
1550   GtkTreeIter          iter;
1551   GtkTreeModel        *model;
1552   GarconMenuDirectory *category;
1553   gchar               *name;
1554 
1555   if (gtk_tree_selection_get_selected (selection, &model, &iter))
1556     {
1557       gtk_tree_model_get (model, &iter,
1558                           XFCE_APPFINDER_CATEGORY_MODEL_COLUMN_DIRECTORY, &category,
1559                           XFCE_APPFINDER_CATEGORY_MODEL_COLUMN_NAME, &name, -1);
1560 
1561       if (window->filter_category != category)
1562         {
1563           if (window->filter_category != NULL)
1564             g_object_unref (G_OBJECT (window->filter_category));
1565 
1566           if (category == NULL)
1567             window->filter_category = NULL;
1568           else
1569             window->filter_category = GARCON_MENU_DIRECTORY (g_object_ref (G_OBJECT (category)));
1570 
1571           APPFINDER_DEBUG ("refilter category");
1572 
1573           /* update visible items */
1574           if (GTK_IS_TREE_VIEW (window->view))
1575             model = gtk_tree_view_get_model (GTK_TREE_VIEW (window->view));
1576           else
1577             model = gtk_icon_view_get_model (GTK_ICON_VIEW (window->view));
1578           gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (window->filter_model));
1579 
1580           /* store last category */
1581           if (xfconf_channel_get_bool (window->channel, "/remember-category", FALSE))
1582             xfconf_channel_set_string (window->channel, "/last/category", name);
1583         }
1584 
1585       g_free (name);
1586       if (category != NULL)
1587         g_object_unref (G_OBJECT (category));
1588     }
1589 }
1590 
1591 
1592 
1593 static void
xfce_appfinder_window_category_set_categories(XfceAppfinderModel * signal_from_model,XfceAppfinderWindow * window)1594 xfce_appfinder_window_category_set_categories (XfceAppfinderModel  *signal_from_model,
1595                                                XfceAppfinderWindow *window)
1596 {
1597   GSList           *categories;
1598   GtkTreePath      *path;
1599   gchar            *name = NULL;
1600   GtkTreeModel     *model;
1601   GtkTreeIter       iter;
1602   GtkTreeSelection *selection;
1603 
1604   appfinder_return_if_fail (GTK_IS_TREE_VIEW (window->sidepane));
1605 
1606   if (signal_from_model != NULL)
1607     {
1608       /* reload from the model, make sure we restore the selected category */
1609       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->sidepane));
1610       if (gtk_tree_selection_get_selected (selection, &model, &iter))
1611         gtk_tree_model_get (model, &iter, XFCE_APPFINDER_CATEGORY_MODEL_COLUMN_NAME, &name, -1);
1612     }
1613 
1614   /* update the categories */
1615   categories = xfce_appfinder_model_get_categories (window->model);
1616   if (categories != NULL)
1617     xfce_appfinder_category_model_set_categories (window->category_model, categories);
1618   g_slist_free (categories);
1619 
1620   if (name == NULL && xfconf_channel_get_bool (window->channel, "/remember-category", FALSE))
1621     name = xfconf_channel_get_string (window->channel, "/last/category", NULL);
1622 
1623   path = xfce_appfinder_category_model_find_category (window->category_model, name);
1624   if (path != NULL)
1625     {
1626       gtk_tree_view_set_cursor (GTK_TREE_VIEW (window->sidepane), path, NULL, FALSE);
1627       gtk_tree_path_free (path);
1628     }
1629 
1630   g_free (name);
1631 }
1632 
1633 
1634 
1635 static void
xfce_appfinder_window_preferences(GtkWidget * button,XfceAppfinderWindow * window)1636 xfce_appfinder_window_preferences (GtkWidget           *button,
1637                                    XfceAppfinderWindow *window)
1638 {
1639   appfinder_return_if_fail (GTK_IS_WIDGET (button));
1640 
1641   /* preload the actions, to make sure there are default values */
1642   if (window->actions == NULL)
1643     window->actions = xfce_appfinder_actions_get ();
1644 
1645   xfce_appfinder_preferences_show (gtk_widget_get_screen (button));
1646 }
1647 
1648 
1649 
1650 static void
xfce_appfinder_window_property_changed(XfconfChannel * channel,const gchar * prop,const GValue * value,XfceAppfinderWindow * window)1651 xfce_appfinder_window_property_changed (XfconfChannel       *channel,
1652                                         const gchar         *prop,
1653                                         const GValue        *value,
1654                                         XfceAppfinderWindow *window)
1655 {
1656   appfinder_return_if_fail (XFCE_IS_APPFINDER_WINDOW (window));
1657   appfinder_return_if_fail (window->channel == channel);
1658 
1659   if (g_strcmp0 (prop, "/icon-view") == 0)
1660     {
1661       xfce_appfinder_window_view (window);
1662     }
1663   else if (g_strcmp0 (prop, "/item-icon-size") == 0)
1664     {
1665       if (GTK_IS_ICON_VIEW (window->view))
1666         xfce_appfinder_window_set_item_width (window);
1667     }
1668   else if (g_strcmp0 (prop, "/text-beside-icons") == 0)
1669     {
1670       if (GTK_IS_ICON_VIEW (window->view))
1671         xfce_appfinder_window_set_item_width (window);
1672     }
1673   else if (g_strcmp0 (prop, "/hide-category-pane") == 0)
1674     {
1675       gboolean hide = g_value_get_boolean (value);
1676       gtk_widget_set_visible (gtk_widget_get_parent (window->sidepane), !hide);
1677 
1678       if (hide) {
1679         GtkTreePath *path = gtk_tree_path_new_from_string ("0");
1680         gtk_tree_view_set_cursor (GTK_TREE_VIEW (window->sidepane), path, NULL, FALSE);
1681         gtk_tree_path_free (path);
1682       }
1683     }
1684 }
1685 
1686 
1687 
1688 static gboolean
xfce_appfinder_window_item_visible(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)1689 xfce_appfinder_window_item_visible (GtkTreeModel *model,
1690                                     GtkTreeIter  *iter,
1691                                     gpointer      data)
1692 {
1693   XfceAppfinderWindow *window = XFCE_APPFINDER_WINDOW (data);
1694   const gchar *filter_string = gtk_entry_get_text (GTK_ENTRY (window->entry));
1695 
1696   return xfce_appfinder_model_get_visible (XFCE_APPFINDER_MODEL (model),
1697                                            iter,
1698                                            window->filter_category,
1699                                            filter_string,
1700                                            window->filter_text);
1701 }
1702 
1703 
1704 
1705 static void
xfce_appfinder_window_item_changed(XfceAppfinderWindow * window)1706 xfce_appfinder_window_item_changed (XfceAppfinderWindow *window)
1707 {
1708   GtkTreeIter       iter;
1709   GtkTreeModel     *model;
1710   gboolean          can_launch;
1711   GdkPixbuf        *pixbuf;
1712 
1713   if (gtk_widget_get_visible (window->paned))
1714     {
1715       can_launch = xfce_appfinder_window_view_get_selected (window, &model, &iter);
1716       gtk_widget_set_sensitive (window->button_launch, can_launch);
1717 
1718       if (can_launch)
1719         {
1720           gtk_tree_model_get (model, &iter, XFCE_APPFINDER_MODEL_COLUMN_ICON_LARGE, &pixbuf, -1);
1721           if (G_LIKELY (pixbuf != NULL))
1722             {
1723               xfce_appfinder_window_update_image (window, pixbuf);
1724               g_object_unref (G_OBJECT (pixbuf));
1725             }
1726         }
1727       else
1728         {
1729           xfce_appfinder_window_update_image (window, NULL);
1730         }
1731     }
1732 }
1733 
1734 
1735 
1736 static void
xfce_appfinder_window_row_activated(XfceAppfinderWindow * window)1737 xfce_appfinder_window_row_activated (XfceAppfinderWindow *window)
1738 {
1739   if (gtk_widget_get_sensitive (window->button_launch))
1740     gtk_button_clicked (GTK_BUTTON (window->button_launch));
1741 }
1742 
1743 
1744 
1745 static void
xfce_appfinder_window_icon_theme_changed(XfceAppfinderWindow * window)1746 xfce_appfinder_window_icon_theme_changed (XfceAppfinderWindow *window)
1747 {
1748   appfinder_return_if_fail (XFCE_IS_APPFINDER_WINDOW (window));
1749 
1750   if (window->icon_find != NULL)
1751     g_object_unref (G_OBJECT (window->icon_find));
1752   window->icon_find = xfce_appfinder_model_load_pixbuf (XFCE_APPFINDER_STOCK_FIND, XFCE_APPFINDER_ICON_SIZE_48);
1753 
1754   /* drop cached pixbufs */
1755   if (G_LIKELY (window->model != NULL))
1756     xfce_appfinder_model_icon_theme_changed (window->model);
1757 
1758   if (G_LIKELY (window->category_model != NULL))
1759     xfce_appfinder_category_model_icon_theme_changed (window->category_model);
1760 
1761   /* update state */
1762   xfce_appfinder_window_entry_changed (window);
1763   xfce_appfinder_window_item_changed (window);
1764 }
1765 
1766 
1767 
1768 static gboolean
xfce_appfinder_window_execute_command(const gchar * text,GdkScreen * screen,XfceAppfinderWindow * window,gboolean only_custom_cmd,gboolean * save_cmd,GError ** error)1769 xfce_appfinder_window_execute_command (const gchar          *text,
1770                                        GdkScreen            *screen,
1771                                        XfceAppfinderWindow  *window,
1772                                        gboolean              only_custom_cmd,
1773                                        gboolean             *save_cmd,
1774                                        GError              **error)
1775 {
1776   gboolean  succeed = FALSE;
1777   gchar    *action_cmd = NULL;
1778   gchar    *expanded;
1779 
1780   appfinder_return_val_if_fail (error != NULL && *error == NULL, FALSE);
1781   appfinder_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
1782 
1783   if (!IS_STRING (text))
1784     return TRUE;
1785 
1786   if (window->actions == NULL)
1787     window->actions = xfce_appfinder_actions_get ();
1788 
1789   /* try to match a custom action */
1790   action_cmd = xfce_appfinder_actions_execute (window->actions, text, save_cmd, error);
1791   if (*error != NULL)
1792     return FALSE;
1793   else if (action_cmd != NULL)
1794     text = action_cmd;
1795   else if (only_custom_cmd)
1796     return FALSE;
1797 
1798   if (IS_STRING (text))
1799     {
1800       /* expand variables */
1801       expanded = xfce_expand_variables (text, NULL);
1802 
1803       /* spawn the command */
1804       APPFINDER_DEBUG ("spawn \"%s\"", expanded);
1805 #if LIBXFCE4UI_CHECK_VERSION (4, 15, 6)
1806       succeed = xfce_spawn_command_line (screen, expanded, FALSE, FALSE, TRUE, error);
1807 #else
1808       succeed = xfce_spawn_command_line_on_screen (screen, expanded, FALSE, FALSE, error);
1809 #endif
1810       g_free (expanded);
1811     }
1812 
1813   g_free (action_cmd);
1814 
1815   return succeed;
1816 }
1817 
1818 
1819 
1820 static void
xfce_appfinder_window_launch_clicked(XfceAppfinderWindow * window)1821 xfce_appfinder_window_launch_clicked (XfceAppfinderWindow *window)
1822 {
1823   xfce_appfinder_window_execute (window, TRUE);
1824 }
1825 
1826 
1827 
1828 static void
xfce_appfinder_window_execute(XfceAppfinderWindow * window,gboolean close_on_succeed)1829 xfce_appfinder_window_execute (XfceAppfinderWindow *window,
1830                                gboolean             close_on_succeed)
1831 {
1832   GtkTreeModel *model, *child_model;
1833   GtkTreeIter   iter, child_iter;
1834   GError       *error = NULL;
1835   gboolean      result = FALSE;
1836   GdkScreen    *screen;
1837   const gchar  *text;
1838   gchar        *cmd = NULL;
1839   gboolean      regular_command = FALSE;
1840   gboolean      save_cmd;
1841   gboolean      only_custom_cmd = FALSE;
1842   const gchar  *uri;
1843 
1844   screen = gtk_window_get_screen (GTK_WINDOW (window));
1845   if (gtk_widget_get_visible (window->paned))
1846     {
1847       if (!gtk_widget_get_sensitive (window->button_launch))
1848         {
1849           only_custom_cmd = TRUE;
1850           goto entry_execute;
1851         }
1852 
1853       if (xfce_appfinder_window_view_get_selected (window, &model, &iter))
1854         {
1855           child_model = model;
1856           gtk_tree_model_get (model, &iter, XFCE_APPFINDER_MODEL_COLUMN_URI, &uri, -1);
1857 
1858           if (GTK_IS_TREE_MODEL_SORT (model) || xfce_appfinder_should_sort_icon_view ())
1859             {
1860               gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model), &child_iter, &iter);
1861               iter = child_iter;
1862               child_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model));
1863             }
1864 
1865           gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (child_model), &child_iter, &iter);
1866           result = xfce_appfinder_model_execute (window->model, &child_iter, screen, &regular_command, &error);
1867 
1868           xfce_appfinder_window_update_frecency (window, model, uri);
1869           if (!result && regular_command)
1870             {
1871               gtk_tree_model_get (model, &child_iter, XFCE_APPFINDER_MODEL_COLUMN_COMMAND, &cmd, -1);
1872               result = xfce_appfinder_window_execute_command (cmd, screen, window, FALSE, NULL, &error);
1873               g_free (cmd);
1874             }
1875         }
1876     }
1877   else
1878     {
1879       if (!gtk_widget_get_sensitive (window->button_launch))
1880         return;
1881 
1882       entry_execute:
1883 
1884       text = gtk_entry_get_text (GTK_ENTRY (window->entry));
1885       save_cmd = TRUE;
1886 
1887       if (xfce_appfinder_window_execute_command (text, screen, window, only_custom_cmd, &save_cmd, &error))
1888         {
1889           if (save_cmd)
1890             result = xfce_appfinder_model_save_command (window->model, text, &error);
1891           else
1892             result = TRUE;
1893         }
1894     }
1895 
1896   if (!only_custom_cmd)
1897     {
1898       gtk_entry_set_icon_from_icon_name (GTK_ENTRY (window->entry), GTK_ENTRY_ICON_PRIMARY,
1899                                      result ? NULL : XFCE_APPFINDER_STOCK_DIALOG_ERROR);
1900       gtk_entry_set_icon_tooltip_text (GTK_ENTRY (window->entry), GTK_ENTRY_ICON_PRIMARY,
1901                                        error != NULL ? error->message : NULL);
1902     }
1903 
1904   if (error != NULL)
1905     {
1906       g_printerr ("%s: failed to execute: %s\n", G_LOG_DOMAIN, error->message);
1907       g_error_free (error);
1908     }
1909 
1910   if (result && close_on_succeed)
1911     gtk_widget_destroy (GTK_WIDGET (window));
1912 }
1913 
1914 
1915 
1916 static void
xfce_appfinder_window_update_frecency(XfceAppfinderWindow * window,GtkTreeModel * model,const gchar * uri)1917 xfce_appfinder_window_update_frecency (XfceAppfinderWindow *window,
1918                                         GtkTreeModel        *model,
1919                                         const gchar         *uri)
1920 {
1921   GFile      *gfile;
1922   gchar      *desktop_id;
1923   GError     *error = NULL;
1924 
1925   if (uri != NULL)
1926     {
1927       gfile = g_file_new_for_uri (uri);
1928       desktop_id = g_file_get_basename (gfile);
1929       g_object_unref (G_OBJECT (gfile));
1930       APPFINDER_DEBUG ("desktop : %s", desktop_id);
1931 
1932       model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (window->filter_model));
1933       xfce_appfinder_model_update_frecency (XFCE_APPFINDER_MODEL (model), desktop_id, &error);
1934 
1935       g_free (desktop_id);
1936       g_free (error);
1937     }
1938 }
1939 
1940 
1941 
1942 void
xfce_appfinder_window_set_expanded(XfceAppfinderWindow * window,gboolean expanded)1943 xfce_appfinder_window_set_expanded (XfceAppfinderWindow *window,
1944                                     gboolean             expanded)
1945 {
1946   GdkGeometry         hints;
1947   gint                width;
1948   GtkEntryCompletion *completion;
1949 
1950   APPFINDER_DEBUG ("set expand = %s", expanded ? "true" : "false");
1951 
1952   /* force window geomentry */
1953   if (expanded)
1954     {
1955       gtk_window_set_geometry_hints (GTK_WINDOW (window), NULL, NULL, 0);
1956       gtk_window_get_size (GTK_WINDOW (window), &width, NULL);
1957       gtk_window_resize (GTK_WINDOW (window), width, window->last_window_height);
1958     }
1959   else
1960     {
1961       if (gtk_widget_get_visible (GTK_WIDGET (window)))
1962         gtk_window_get_size (GTK_WINDOW (window), NULL, &window->last_window_height);
1963 
1964       hints.max_height = -1;
1965       hints.max_width = G_MAXINT;
1966       gtk_window_set_geometry_hints (GTK_WINDOW (window), NULL, &hints, GDK_HINT_MAX_SIZE);
1967     }
1968 
1969   /* show/hide pane with treeviews */
1970   gtk_widget_set_visible (window->paned, expanded);
1971 
1972   /* toggle icon */
1973   gtk_entry_set_icon_from_icon_name (GTK_ENTRY (window->entry), GTK_ENTRY_ICON_SECONDARY,
1974                                      expanded ? XFCE_APPFINDER_STOCK_GO_UP : XFCE_APPFINDER_STOCK_GO_DOWN);
1975   gtk_entry_set_icon_from_icon_name (GTK_ENTRY (window->entry), GTK_ENTRY_ICON_PRIMARY, NULL);
1976 
1977   /* update completion (remove completed text of restart completion) */
1978   completion = gtk_entry_get_completion (GTK_ENTRY (window->entry));
1979   if (completion != NULL)
1980     gtk_editable_delete_selection (GTK_EDITABLE (window->entry));
1981   gtk_entry_set_completion (GTK_ENTRY (window->entry), expanded ? NULL : window->completion);
1982   if (!expanded && gtk_entry_get_text_length (GTK_ENTRY (window->entry)) > 0)
1983     gtk_entry_completion_insert_prefix (window->completion);
1984 
1985   /* update state */
1986   xfce_appfinder_window_entry_changed (window);
1987   xfce_appfinder_window_item_changed (window);
1988 }
1989 
1990 
1991 
1992 static gint
xfce_appfinder_window_sort_items(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer data)1993 xfce_appfinder_window_sort_items (GtkTreeModel *model,
1994                                   GtkTreeIter  *a,
1995                                   GtkTreeIter  *b,
1996                                   gpointer      data)
1997 {
1998   gchar        *normalized, *casefold, *title_a, *title_b, *found;
1999   GtkWidget    *entry = GTK_WIDGET (data);
2000   gint          result = -1;
2001 
2002   appfinder_return_val_if_fail (GTK_IS_ENTRY (entry), 0);
2003 
2004   normalized = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (entry)), -1, G_NORMALIZE_ALL);
2005   casefold = g_utf8_casefold (normalized, -1);
2006   g_free (normalized);
2007 
2008   gtk_tree_model_get (model, a, XFCE_APPFINDER_MODEL_COLUMN_TITLE, &title_a, -1);
2009   normalized = g_utf8_normalize (title_a, -1, G_NORMALIZE_ALL);
2010   g_free (title_a);
2011   title_a = g_utf8_casefold (normalized, -1);
2012   g_free (normalized);
2013 
2014   gtk_tree_model_get (model, b, XFCE_APPFINDER_MODEL_COLUMN_TITLE, &title_b, -1);
2015   normalized = g_utf8_normalize (title_b, -1, G_NORMALIZE_ALL);
2016   g_free (title_b);
2017   title_b = g_utf8_casefold (normalized, -1);
2018   g_free (normalized);
2019 
2020   if (g_strcmp0 (casefold, "") == 0)
2021     result = g_strcmp0 (title_a, title_b);
2022   else
2023     {
2024       found = g_strrstr (title_a, casefold);
2025       if (found)
2026         result -= (G_MAXINT - (found - title_a));
2027 
2028       found = g_strrstr (title_b, casefold);
2029       if (found)
2030         result += (G_MAXINT - (found - title_b));
2031     }
2032 
2033   g_free (casefold);
2034   g_free (title_a);
2035   g_free (title_b);
2036   return result;
2037 }
2038 
2039 
2040 
2041 static gint
xfce_appfinder_window_sort_items_frecency(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer data)2042 xfce_appfinder_window_sort_items_frecency  (GtkTreeModel *model,
2043                                             GtkTreeIter  *a,
2044                                             GtkTreeIter  *b,
2045                                             gpointer      data)
2046 {
2047   guint        a_freq, b_freq;
2048   guint64      a_rec, b_rec;
2049   guint        a_res, b_res;
2050 
2051   gtk_tree_model_get (model, a, XFCE_APPFINDER_MODEL_COLUMN_FREQUENCY, &a_freq, -1);
2052   gtk_tree_model_get (model, b, XFCE_APPFINDER_MODEL_COLUMN_FREQUENCY, &b_freq, -1);
2053 
2054   gtk_tree_model_get (model, a, XFCE_APPFINDER_MODEL_COLUMN_RECENCY, &a_rec, -1);
2055   gtk_tree_model_get (model, b, XFCE_APPFINDER_MODEL_COLUMN_RECENCY, &b_rec, -1);
2056 
2057   a_res = xfce_appfinder_model_calculate_frecency (a_freq, a_rec);
2058   b_res = xfce_appfinder_model_calculate_frecency (b_freq, b_rec);
2059 
2060   /* Sort according the frecency */
2061   if (b_res - a_res != 0)
2062     return b_res - a_res;
2063 
2064   /* If they have the same frecency, fallback to the alphabetical order */
2065   return xfce_appfinder_window_sort_items (model, a, b, data);
2066 }
2067 
2068 
2069 
2070 /*
2071  * Checks for gtk => 3.22.27 during runtime.
2072  * This is necessary because sort model for icon view did not work as expected.
2073  */
2074 static gboolean
xfce_appfinder_should_sort_icon_view(void)2075 xfce_appfinder_should_sort_icon_view (void)
2076 {
2077   return gtk_get_major_version () >= 3  &&
2078          gtk_get_minor_version () >= 22 &&
2079          gtk_get_micro_version () >= 27;
2080 }
2081