thunar-abstract-icon-view.c 41.7 KB
Newer Older
1
/* vi:set et ai sw=2 sts=2 ts=2: */
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*-
 * Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gdk/gdkkeysyms.h>

#include <thunar/thunar-abstract-icon-view.h>
27
28
#include <thunar/thunar-gobject-extensions.h>
#include <thunar/thunar-gtk-extensions.h>
29
#include <thunar/thunar-launcher.h>
30
#include <thunar/thunar-preferences.h>
31
#include <thunar/thunar-private.h>
32
#include <thunar/thunar-window.h>
33
34
35



36
37
static void         thunar_abstract_icon_view_style_set             (GtkWidget                    *widget,
                                                                     GtkStyle                     *previous_style);
38
39
40
static GList       *thunar_abstract_icon_view_get_selected_items    (ThunarStandardView           *standard_view);
static void         thunar_abstract_icon_view_select_all            (ThunarStandardView           *standard_view);
static void         thunar_abstract_icon_view_unselect_all          (ThunarStandardView           *standard_view);
Nick Schermer's avatar
Nick Schermer committed
41
static void         thunar_abstract_icon_view_selection_invert      (ThunarStandardView           *standard_view);
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
static void         thunar_abstract_icon_view_select_path           (ThunarStandardView           *standard_view,
                                                                     GtkTreePath                  *path);
static void         thunar_abstract_icon_view_set_cursor            (ThunarStandardView           *standard_view,
                                                                     GtkTreePath                  *path,
                                                                     gboolean                      start_editing);
static void         thunar_abstract_icon_view_scroll_to_path        (ThunarStandardView           *standard_view,
                                                                     GtkTreePath                  *path,
                                                                     gboolean                      use_align,
                                                                     gfloat                        row_align,
                                                                     gfloat                        col_align);
static GtkTreePath *thunar_abstract_icon_view_get_path_at_pos       (ThunarStandardView           *standard_view,
                                                                     gint                          x,
                                                                     gint                          y);
static gboolean     thunar_abstract_icon_view_get_visible_range     (ThunarStandardView           *standard_view,
                                                                     GtkTreePath                 **start_path,
                                                                     GtkTreePath                 **end_path);
static void         thunar_abstract_icon_view_highlight_path        (ThunarStandardView           *standard_view,
                                                                     GtkTreePath                  *path);
60
61
62
63
64
65
66
static void         thunar_abstract_icon_view_connect_accelerators  (ThunarStandardView           *standard_view,
                                                                     GtkAccelGroup                *accel_group);
static void         thunar_abstract_icon_view_disconnect_accelerators(ThunarStandardView          *standard_view,
                                                                     GtkAccelGroup                *accel_group);
static void         thunar_abstract_icon_view_append_menu_items     (ThunarStandardView           *standard_view,
                                                                     GtkMenu                      *menu,
                                                                     GtkAccelGroup                *accel_group);
67
68
69
70
71
72
73
74
75
static void         thunar_abstract_icon_view_notify_model          (ExoIconView                  *view,
                                                                     GParamSpec                   *pspec,
                                                                     ThunarAbstractIconView       *abstract_icon_view);
static gboolean     thunar_abstract_icon_view_button_press_event    (ExoIconView                  *view,
                                                                     GdkEventButton               *event,
                                                                     ThunarAbstractIconView       *abstract_icon_view);
static gboolean     thunar_abstract_icon_view_button_release_event  (ExoIconView                  *view,
                                                                     GdkEventButton               *event,
                                                                     ThunarAbstractIconView       *abstract_icon_view);
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
76
77
static gboolean     thunar_abstract_icon_view_draw                  (ExoIconView                  *view,
                                                                     cairo_t                      *cr,
78
79
80
81
82
83
84
85
86
87
88
89
90
                                                                     ThunarAbstractIconView       *abstract_icon_view);
static gboolean     thunar_abstract_icon_view_key_press_event       (ExoIconView                  *view,
                                                                     GdkEventKey                  *event,
                                                                     ThunarAbstractIconView       *abstract_icon_view);
static gboolean     thunar_abstract_icon_view_motion_notify_event   (ExoIconView                  *view,
                                                                     GdkEventMotion               *event,
                                                                     ThunarAbstractIconView       *abstract_icon_view);
static void         thunar_abstract_icon_view_item_activated        (ExoIconView                  *view,
                                                                     GtkTreePath                  *path,
                                                                     ThunarAbstractIconView       *abstract_icon_view);
static void         thunar_abstract_icon_view_sort_column_changed   (GtkTreeSortable              *sortable,
                                                                     ThunarAbstractIconView       *abstract_icon_view);
static void         thunar_abstract_icon_view_zoom_level_changed    (ThunarAbstractIconView       *abstract_icon_view);
91
92
93
94
95
96
static void         thunar_abstract_icon_view_action_sort_by_name   (ThunarStandardView           *standard_view);
static void         thunar_abstract_icon_view_action_sort_by_size   (ThunarStandardView           *standard_view);
static void         thunar_abstract_icon_view_action_sort_by_type   (ThunarStandardView           *standard_view);
static void         thunar_abstract_icon_view_action_sort_by_date   (ThunarStandardView           *standard_view);
static void         thunar_abstract_icon_view_action_sort_ascending (ThunarStandardView           *standard_view);
static void         thunar_abstract_icon_view_action_sort_descending(ThunarStandardView           *standard_view);
97
98
99

struct _ThunarAbstractIconViewPrivate
{
100
101
102
  GtkSortType sort_order;
  gint        sort_column;

103
  /* mouse gesture support */
Nick Schermer's avatar
Nick Schermer committed
104
105
106
107
108
109
110
  gint   gesture_start_x;
  gint   gesture_start_y;
  gint   gesture_current_x;
  gint   gesture_current_y;
  gulong gesture_expose_id;
  gulong gesture_motion_id;
  gulong gesture_release_id;
111
112

  gboolean button_pressed;
113
114
115
};


116
117
static XfceGtkActionEntry thunar_abstract_icon_view_action_entries[] =
{
118
119
120
121
122
123
124
    { THUNAR_ABSTRACT_ICON_VIEW_ACTION_ARRANGE_ITEMS_MENU, "<Actions>/ThunarStandardView/arrange-items-menu",    "", XFCE_GTK_MENU_ITEM,       N_ ("Arran_ge Items"),        NULL,                                                NULL, G_CALLBACK (NULL),                                             },
    { THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_BY_NAME,       "<Actions>/ThunarStandardView/sort-by-name",          "", XFCE_GTK_RADIO_MENU_ITEM, N_ ("By _Name"),              N_ ("Keep items sorted by their name"),              NULL, G_CALLBACK (thunar_abstract_icon_view_action_sort_by_name),    },
    { THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_BY_SIZE,       "<Actions>/ThunarStandardView/sort-by-size",          "", XFCE_GTK_RADIO_MENU_ITEM, N_ ("By _Size"),              N_ ("Keep items sorted by their size"),              NULL, G_CALLBACK (thunar_abstract_icon_view_action_sort_by_size),    },
    { THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_BY_TYPE,       "<Actions>/ThunarStandardView/sort-by-type",          "", XFCE_GTK_RADIO_MENU_ITEM, N_ ("By _Type"),              N_ ("Keep items sorted by their type"),              NULL, G_CALLBACK (thunar_abstract_icon_view_action_sort_by_type),    },
    { THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_BY_MTIME,      "<Actions>/ThunarStandardView/sort-by-mtime",         "", XFCE_GTK_RADIO_MENU_ITEM, N_ ("By Modification _Date"), N_ ("Keep items sorted by their modification date"), NULL, G_CALLBACK (thunar_abstract_icon_view_action_sort_by_date),    },
    { THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_ASCENDING,     "<Actions>/ThunarStandardView/sort-ascending",        "", XFCE_GTK_RADIO_MENU_ITEM, N_ ("_Ascending"),            N_ ("Sort items in ascending order"),                NULL, G_CALLBACK (thunar_abstract_icon_view_action_sort_ascending),  },
    { THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_DESCENDING,    "<Actions>/ThunarStandardView/sort-descending",       "", XFCE_GTK_RADIO_MENU_ITEM, N_ ("_Descending"),           N_ ("Sort items in descending order"),               NULL, G_CALLBACK (thunar_abstract_icon_view_action_sort_descending), },
125
126
127
128
129
130
};

#define get_action_entry(id) xfce_gtk_get_action_entry_by_id(thunar_abstract_icon_view_action_entries,G_N_ELEMENTS(thunar_abstract_icon_view_action_entries),id)



131
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ThunarAbstractIconView, thunar_abstract_icon_view, THUNAR_TYPE_STANDARD_VIEW)
132
133
134
135
136
137
138



static void
thunar_abstract_icon_view_class_init (ThunarAbstractIconViewClass *klass)
{
  ThunarStandardViewClass *thunarstandard_view_class;
139
  GtkWidgetClass          *gtkwidget_class;
140

141
142
143
  gtkwidget_class = GTK_WIDGET_CLASS (klass);
  gtkwidget_class->style_set = thunar_abstract_icon_view_style_set;

144
145
146
147
  thunarstandard_view_class = THUNAR_STANDARD_VIEW_CLASS (klass);
  thunarstandard_view_class->get_selected_items = thunar_abstract_icon_view_get_selected_items;
  thunarstandard_view_class->select_all = thunar_abstract_icon_view_select_all;
  thunarstandard_view_class->unselect_all = thunar_abstract_icon_view_unselect_all;
Nick Schermer's avatar
Nick Schermer committed
148
  thunarstandard_view_class->selection_invert = thunar_abstract_icon_view_selection_invert;
149
150
151
152
153
154
  thunarstandard_view_class->select_path = thunar_abstract_icon_view_select_path;
  thunarstandard_view_class->set_cursor = thunar_abstract_icon_view_set_cursor;
  thunarstandard_view_class->scroll_to_path = thunar_abstract_icon_view_scroll_to_path;
  thunarstandard_view_class->get_path_at_pos = thunar_abstract_icon_view_get_path_at_pos;
  thunarstandard_view_class->get_visible_range = thunar_abstract_icon_view_get_visible_range;
  thunarstandard_view_class->highlight_path = thunar_abstract_icon_view_highlight_path;
155
156
157
158
159
  thunarstandard_view_class->append_menu_items = thunar_abstract_icon_view_append_menu_items;
  thunarstandard_view_class->connect_accelerators = thunar_abstract_icon_view_connect_accelerators;
  thunarstandard_view_class->disconnect_accelerators = thunar_abstract_icon_view_disconnect_accelerators;

  xfce_gtk_translate_action_entries (thunar_abstract_icon_view_action_entries, G_N_ELEMENTS (thunar_abstract_icon_view_action_entries));
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

  /**
   * ThunarAbstractIconView:column-spacing:
   *
   * The additional space inserted between columns in the
   * icon views.
   **/
  gtk_widget_class_install_style_property (gtkwidget_class,
                                           g_param_spec_int ("column-spacing",
                                                             "column-spacing",
                                                             "column-spacing",
                                                             0, G_MAXINT, 6,
                                                             EXO_PARAM_READABLE));

  /**
   * ThunarAbstractIconView:row-spacing:
   *
   * The additional space inserted between rows in the
   * icon views.
   **/
  gtk_widget_class_install_style_property (gtkwidget_class,
                                           g_param_spec_int ("row-spacing",
                                                             "row-spacing",
                                                             "row-spacing",
                                                             0, G_MAXINT, 6,
                                                             EXO_PARAM_READABLE));
186
187
188
189
190
191
192
193
194
195
}



static void
thunar_abstract_icon_view_init (ThunarAbstractIconView *abstract_icon_view)
{
  GtkWidget *view;

  /* connect private instance data */
196
  abstract_icon_view->priv = thunar_abstract_icon_view_get_instance_private (abstract_icon_view);
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

  /* stay informed about zoom-level changes, so we can force a re-layout on the abstract_icon view */
  g_signal_connect (G_OBJECT (abstract_icon_view), "notify::zoom-level", G_CALLBACK (thunar_abstract_icon_view_zoom_level_changed), NULL);

  /* create the real view */
  view = exo_icon_view_new ();
  g_signal_connect (G_OBJECT (view), "notify::model", G_CALLBACK (thunar_abstract_icon_view_notify_model), abstract_icon_view);
  g_signal_connect (G_OBJECT (view), "button-press-event", G_CALLBACK (thunar_abstract_icon_view_button_press_event), abstract_icon_view);
  g_signal_connect (G_OBJECT (view), "key-press-event", G_CALLBACK (thunar_abstract_icon_view_key_press_event), abstract_icon_view);
  g_signal_connect (G_OBJECT (view), "item-activated", G_CALLBACK (thunar_abstract_icon_view_item_activated), abstract_icon_view);
  g_signal_connect_swapped (G_OBJECT (view), "selection-changed", G_CALLBACK (thunar_standard_view_selection_changed), abstract_icon_view);
  gtk_container_add (GTK_CONTAINER (abstract_icon_view), view);
  gtk_widget_show (view);

  /* initialize the abstract icon view properties */
  exo_icon_view_set_enable_search (EXO_ICON_VIEW (view), TRUE);
  exo_icon_view_set_selection_mode (EXO_ICON_VIEW (view), GTK_SELECTION_MULTIPLE);

  /* add the abstract icon renderer */
  g_object_set (G_OBJECT (THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer), "follow-state", TRUE, NULL);
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer, FALSE);
  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer,
                                 "file", THUNAR_COLUMN_FILE);

  /* add the name renderer */
222
223
  /*FIXME text prelit*/
  /*g_object_set (G_OBJECT (THUNAR_STANDARD_VIEW (abstract_icon_view)->name_renderer), "follow-state", TRUE, NULL);*/
224
225
226
227
228
229
230
231
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), THUNAR_STANDARD_VIEW (abstract_icon_view)->name_renderer, TRUE);
  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), THUNAR_STANDARD_VIEW (abstract_icon_view)->name_renderer,
                                 "text", THUNAR_COLUMN_NAME);

  /* we need to listen to sort column changes to sync the menu items */
  g_signal_connect (G_OBJECT (THUNAR_STANDARD_VIEW (abstract_icon_view)->model), "sort-column-changed",
                    G_CALLBACK (thunar_abstract_icon_view_sort_column_changed), abstract_icon_view);
  thunar_abstract_icon_view_sort_column_changed (GTK_TREE_SORTABLE (THUNAR_STANDARD_VIEW (abstract_icon_view)->model), abstract_icon_view);
232
233

  /* update the icon view on size-allocate events */
234
235
  /* TODO: issue not reproducible anymore as of gtk 3.24.18
   * we can probably remove this in the future. */
236
237
  g_signal_connect_swapped (G_OBJECT (abstract_icon_view), "size-allocate",
                            G_CALLBACK (gtk_widget_queue_resize), view);
238
239
240
241
}



242
243
244
245
246
247
248
249
250
251
252
static void
thunar_abstract_icon_view_style_set (GtkWidget *widget,
                                     GtkStyle  *previous_style)
{
  gint column_spacing;
  gint row_spacing;

  /* determine the column/row spacing from the style */
  gtk_widget_style_get (widget, "column-spacing", &column_spacing, "row-spacing", &row_spacing, NULL);

  /* apply the column/row spacing to the icon view */
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
253
254
  exo_icon_view_set_column_spacing (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (widget))), column_spacing);
  exo_icon_view_set_row_spacing (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (widget))), row_spacing);
255
256
257
258
259
260
261

  /* call the parent handler */
  (*GTK_WIDGET_CLASS (thunar_abstract_icon_view_parent_class)->style_set) (widget, previous_style);
}



262
263
264
static GList*
thunar_abstract_icon_view_get_selected_items (ThunarStandardView *standard_view)
{
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
265
  return exo_icon_view_get_selected_items (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))));
266
267
268
269
270
271
272
}



static void
thunar_abstract_icon_view_select_all (ThunarStandardView *standard_view)
{
273
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view));
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
274
  exo_icon_view_select_all (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))));
275
276
277
278
279
280
281
}



static void
thunar_abstract_icon_view_unselect_all (ThunarStandardView *standard_view)
{
282
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view));
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
283
  exo_icon_view_unselect_all (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))));
284
285
286
287
}



Nick Schermer's avatar
Nick Schermer committed
288
289
290
291
static void
thunar_abstract_icon_view_selection_invert (ThunarStandardView *standard_view)
{
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view));
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
292
  exo_icon_view_selection_invert (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))));
Nick Schermer's avatar
Nick Schermer committed
293
294
295
296
}



297
298
299
300
static void
thunar_abstract_icon_view_select_path (ThunarStandardView *standard_view,
                                       GtkTreePath        *path)
{
301
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view));
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
302
  exo_icon_view_select_path (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))), path);
303
304
305
306
307
308
309
310
311
312
313
}



static void
thunar_abstract_icon_view_set_cursor (ThunarStandardView *standard_view,
                                      GtkTreePath        *path,
                                      gboolean            start_editing)
{
  GtkCellRendererMode mode;

314
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view));
315
316

  /* make sure the name renderer is editable */
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
317
318
  g_object_get ( G_OBJECT (standard_view->name_renderer), "mode", &mode, NULL);
  g_object_set ( G_OBJECT (standard_view->name_renderer), "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL);
319
320

  /* tell the abstract_icon view to start editing the given item */
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
321
  exo_icon_view_set_cursor (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))), path, standard_view->name_renderer, start_editing);
322
323

  /* reset the name renderer mode */
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
324
  g_object_set (G_OBJECT (standard_view->name_renderer), "mode", mode, NULL);
325
326
327
328
329
330
331
332
333
334
335
}



static void
thunar_abstract_icon_view_scroll_to_path (ThunarStandardView *standard_view,
                                          GtkTreePath        *path,
                                          gboolean            use_align,
                                          gfloat              row_align,
                                          gfloat              col_align)
{
336
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view));
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
337
  exo_icon_view_scroll_to_path (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))), path, use_align, row_align, col_align);
338
339
340
341
342
343
344
345
346
}



static GtkTreePath*
thunar_abstract_icon_view_get_path_at_pos (ThunarStandardView *standard_view,
                                           gint                x,
                                           gint                y)
{
347
  _thunar_return_val_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view), NULL);
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
348
  return exo_icon_view_get_path_at_pos (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))), x, y);
349
350
351
352
353
354
355
356
357
}



static gboolean
thunar_abstract_icon_view_get_visible_range (ThunarStandardView *standard_view,
                                             GtkTreePath       **start_path,
                                             GtkTreePath       **end_path)
{
358
  _thunar_return_val_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view), FALSE);
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
359
  return exo_icon_view_get_visible_range (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))), start_path, end_path);
360
361
362
363
364
365
366
367
}



static void
thunar_abstract_icon_view_highlight_path (ThunarStandardView *standard_view,
                                          GtkTreePath        *path)
{
368
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (standard_view));
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
369
  exo_icon_view_set_drag_dest_item (EXO_ICON_VIEW (gtk_bin_get_child (GTK_BIN (standard_view))), path, EXO_ICON_VIEW_DROP_INTO);
370
371
372
373
}



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
/**
 * thunar_abstract_icon_view_connect_accelerators:
 * @standard_view : a #ThunarStandardView
 * @accel_group : a #GtkAccelGroup to be used used for new menu items
 *
 * Connects all accelerators and corresponding default keys of this widget to the global accelerator list
 * The concrete implementation depends on the concrete widget which is implementing this view
 **/
static void
thunar_abstract_icon_view_connect_accelerators (ThunarStandardView *standard_view,
                                                GtkAccelGroup      *accel_group)
{
  ThunarAbstractIconView *abstract_icon_view = THUNAR_ABSTRACT_ICON_VIEW (standard_view);

  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));

  xfce_gtk_accel_map_add_entries (thunar_abstract_icon_view_action_entries,
                                  G_N_ELEMENTS (thunar_abstract_icon_view_action_entries));
  xfce_gtk_accel_group_connect_action_entries (accel_group,
                                               thunar_abstract_icon_view_action_entries,
                                               G_N_ELEMENTS (thunar_abstract_icon_view_action_entries),
                                               standard_view);
}



/**
 * thunar_abstract_icon_view_disconnect_accelerators:
 * @standard_view : a #ThunarStandardView
 * @accel_group : a #GtkAccelGroup to be used used for new menu items
 *
 * Disconnects all accelerators from the passed #GtkAccelGroup
 **/
static void
thunar_abstract_icon_view_disconnect_accelerators (ThunarStandardView *standard_view,
                                                   GtkAccelGroup      *accel_group)
{
  /* Dont listen to the accel keys defined by the action entries any more */
  xfce_gtk_accel_group_disconnect_action_entries (accel_group,
                                                  thunar_abstract_icon_view_action_entries,
                                                  G_N_ELEMENTS (thunar_abstract_icon_view_action_entries));
}



/**
 * thunar_abstract_icon_view_append_menu_items:
 * @standard_view : a #ThunarStandardView
 * @menu : the #GtkMenu to add the menu items
 * @accel_group : a #GtkAccelGroup to be used used for new menu items
 *
 * Appends widget-specific menu items to a #GtkMenu and connects them to the passed #GtkAccelGroup
 * Implements method 'append_menu_items' of #ThunarStandardView
 **/
static void
thunar_abstract_icon_view_append_menu_items (ThunarStandardView *standard_view,
                                             GtkMenu            *menu,
                                             GtkAccelGroup      *accel_group)
{
  ThunarAbstractIconView *abstract_icon_view = THUNAR_ABSTRACT_ICON_VIEW (standard_view);
  GtkWidget              *submenu;
  GtkWidget              *item;

  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));

  item = xfce_gtk_menu_item_new_from_action_entry (get_action_entry (THUNAR_ABSTRACT_ICON_VIEW_ACTION_ARRANGE_ITEMS_MENU), NULL, GTK_MENU_SHELL (menu));
  submenu =  gtk_menu_new();
  if (accel_group != NULL)
    gtk_menu_set_accel_group (GTK_MENU (submenu), accel_group);
  xfce_gtk_toggle_menu_item_new_from_action_entry (get_action_entry (THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_BY_NAME), G_OBJECT (standard_view),
                                                   abstract_icon_view->priv->sort_column == THUNAR_COLUMN_NAME, GTK_MENU_SHELL (submenu));
  xfce_gtk_toggle_menu_item_new_from_action_entry (get_action_entry (THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_BY_SIZE), G_OBJECT (standard_view),
                                                   abstract_icon_view->priv->sort_column == THUNAR_COLUMN_SIZE, GTK_MENU_SHELL (submenu));
  xfce_gtk_toggle_menu_item_new_from_action_entry (get_action_entry (THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_BY_TYPE), G_OBJECT (standard_view),
                                                   abstract_icon_view->priv->sort_column == THUNAR_COLUMN_TYPE, GTK_MENU_SHELL (submenu));
  xfce_gtk_toggle_menu_item_new_from_action_entry (get_action_entry (THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_BY_MTIME), G_OBJECT (standard_view),
                                                   abstract_icon_view->priv->sort_column == THUNAR_COLUMN_DATE_MODIFIED, GTK_MENU_SHELL (submenu));
451
  xfce_gtk_menu_append_seperator (GTK_MENU_SHELL (submenu));
452
453
454
455
456
457
458
459
460
461
  xfce_gtk_toggle_menu_item_new_from_action_entry (get_action_entry (THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_ASCENDING), G_OBJECT (standard_view),
                                                   abstract_icon_view->priv->sort_order == GTK_SORT_ASCENDING, GTK_MENU_SHELL (submenu));
  xfce_gtk_toggle_menu_item_new_from_action_entry (get_action_entry (THUNAR_ABSTRACT_ICON_VIEW_ACTION_SORT_DESCENDING), G_OBJECT (standard_view),
                                                   abstract_icon_view->priv->sort_order == GTK_SORT_DESCENDING, GTK_MENU_SHELL (submenu));
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), GTK_WIDGET (submenu));
  gtk_widget_show (item);
}



462
static const XfceGtkActionEntry*
463
464
thunar_abstract_icon_view_gesture_action (ThunarAbstractIconView *abstract_icon_view)
{
465
466
467
  GtkWidget *window;

  window = gtk_widget_get_toplevel (GTK_WIDGET (abstract_icon_view));
468
469
470
  if (abstract_icon_view->priv->gesture_start_y - abstract_icon_view->priv->gesture_current_y > 40
      && ABS (abstract_icon_view->priv->gesture_start_x - abstract_icon_view->priv->gesture_current_x) < 40)
    {
471
      return thunar_window_get_action_entry (THUNAR_WINDOW (window), THUNAR_WINDOW_ACTION_OPEN_PARENT);
472
473
474
475
    }
  else if (abstract_icon_view->priv->gesture_start_x - abstract_icon_view->priv->gesture_current_x > 40
      && ABS (abstract_icon_view->priv->gesture_start_y - abstract_icon_view->priv->gesture_current_y) < 40)
    {
476
      return thunar_window_get_action_entry (THUNAR_WINDOW (window), THUNAR_WINDOW_ACTION_BACK);
477
478
479
480
    }
  else if (abstract_icon_view->priv->gesture_current_x - abstract_icon_view->priv->gesture_start_x > 40
      && ABS (abstract_icon_view->priv->gesture_start_y - abstract_icon_view->priv->gesture_current_y) < 40)
    {
481
      return thunar_window_get_action_entry (THUNAR_WINDOW (window), THUNAR_WINDOW_ACTION_FORWARD);
482
483
484
485
    }
  else if (abstract_icon_view->priv->gesture_current_y - abstract_icon_view->priv->gesture_start_y > 40
      && ABS (abstract_icon_view->priv->gesture_start_x - abstract_icon_view->priv->gesture_current_x) < 40)
    {
486
      return thunar_window_get_action_entry (THUNAR_WINDOW (window), THUNAR_WINDOW_ACTION_RELOAD);
487
488
489
490
491
492
493
    }
  return NULL;
}



static void
494
thunar_abstract_icon_view_action_sort_by_name (ThunarStandardView *standard_view)
495
{
496
  ThunarAbstractIconView *abstract_icon_view = THUNAR_ABSTRACT_ICON_VIEW (standard_view);
497

498
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));
499

500
501
  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), THUNAR_COLUMN_NAME, abstract_icon_view->priv->sort_order);
}
502

503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560


static void
thunar_abstract_icon_view_action_sort_by_size (ThunarStandardView *standard_view)
{
  ThunarAbstractIconView *abstract_icon_view = THUNAR_ABSTRACT_ICON_VIEW (standard_view);

  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));

  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), THUNAR_COLUMN_SIZE, abstract_icon_view->priv->sort_order);
}



static void
thunar_abstract_icon_view_action_sort_by_type (ThunarStandardView *standard_view)
{
  ThunarAbstractIconView *abstract_icon_view = THUNAR_ABSTRACT_ICON_VIEW (standard_view);

  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));

  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), THUNAR_COLUMN_TYPE, abstract_icon_view->priv->sort_order);
}



static void
thunar_abstract_icon_view_action_sort_by_date (ThunarStandardView *standard_view)
{
  ThunarAbstractIconView *abstract_icon_view = THUNAR_ABSTRACT_ICON_VIEW (standard_view);

  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));

  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), THUNAR_COLUMN_DATE_MODIFIED, abstract_icon_view->priv->sort_order);
}



static void
thunar_abstract_icon_view_action_sort_ascending (ThunarStandardView *standard_view)
{
  ThunarAbstractIconView *abstract_icon_view = THUNAR_ABSTRACT_ICON_VIEW (standard_view);

  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));

  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), abstract_icon_view->priv->sort_column, GTK_SORT_ASCENDING);
}



static void
thunar_abstract_icon_view_action_sort_descending (ThunarStandardView *standard_view)
{
  ThunarAbstractIconView *abstract_icon_view = THUNAR_ABSTRACT_ICON_VIEW (standard_view);

  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));

  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (standard_view->model), abstract_icon_view->priv->sort_column, GTK_SORT_DESCENDING);
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
}



static void
thunar_abstract_icon_view_notify_model (ExoIconView            *view,
                                        GParamSpec             *pspec,
                                        ThunarAbstractIconView *abstract_icon_view)
{
  /* We need to set the search column here, as ExoIconView resets it
   * whenever a new model is set.
   */
  exo_icon_view_set_search_column (view, THUNAR_COLUMN_NAME);
}



static gboolean
thunar_abstract_icon_view_button_press_event (ExoIconView            *view,
                                              GdkEventButton         *event,
                                              ThunarAbstractIconView *abstract_icon_view)
{
583
  GtkTreePath       *path;
584

585
586
587
  abstract_icon_view->priv->button_pressed = TRUE;

  if (event->type == GDK_BUTTON_PRESS && event->button == 3)
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
    {
      /* open the context menu on right clicks */
      if (exo_icon_view_get_item_at_pos (view, event->x, event->y, &path, NULL))
        {
          /* select the path on which the user clicked if not selected yet */
          if (!exo_icon_view_path_is_selected (view, path))
            {
              /* we don't unselect all other items if Control is active */
              if ((event->state & GDK_CONTROL_MASK) == 0)
                exo_icon_view_unselect_all (view);
              exo_icon_view_select_path (view, path);
            }
          gtk_tree_path_free (path);

          /* queue the menu popup */
603
          thunar_standard_view_queue_popup (THUNAR_STANDARD_VIEW (abstract_icon_view), event);
604
605
606
607
608
609
610
        }
      else if ((event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
        {
          /* user clicked on an empty area, so we unselect everything
           * to make sure that the folder context menu is opened.
           */
          exo_icon_view_unselect_all (view);
611

612
          /* open the context menu */
613
          thunar_standard_view_context_menu (THUNAR_STANDARD_VIEW (abstract_icon_view));
614
615
616
617
        }

      return TRUE;
    }
618
  else if (event->type == GDK_BUTTON_PRESS && event->button == 2)
619
620
621
622
623
624
625
626
627
628
    {
      /* unselect all currently selected items */
      exo_icon_view_unselect_all (view);

      /* determine the path to the item that was middle-clicked */
      if (exo_icon_view_get_item_at_pos (view, event->x, event->y, &path, NULL))
        {
          /* select only the path to the item on which the user clicked */
          exo_icon_view_select_path (view, path);

629
630
          /* try to open the path as new window/tab, if possible */
          _thunar_standard_view_open_on_middle_click (THUNAR_STANDARD_VIEW (abstract_icon_view), path, event->state);
631
632
633
634
635
636
637
638

          /* cleanup */
          gtk_tree_path_free (path);
        }
      else if (event->type == GDK_BUTTON_PRESS)
        {
          abstract_icon_view->priv->gesture_start_x = abstract_icon_view->priv->gesture_current_x = event->x;
          abstract_icon_view->priv->gesture_start_y = abstract_icon_view->priv->gesture_current_y = event->y;
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
639
640
          abstract_icon_view->priv->gesture_expose_id = g_signal_connect_after (G_OBJECT (view), "draw",
                                                                                G_CALLBACK (thunar_abstract_icon_view_draw),
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
                                                                                G_OBJECT (abstract_icon_view));
          abstract_icon_view->priv->gesture_motion_id = g_signal_connect (G_OBJECT (view), "motion-notify-event",
                                                                          G_CALLBACK (thunar_abstract_icon_view_motion_notify_event),
                                                                          G_OBJECT (abstract_icon_view));
          abstract_icon_view->priv->gesture_release_id = g_signal_connect (G_OBJECT (view), "button-release-event",
                                                                           G_CALLBACK (thunar_abstract_icon_view_button_release_event),
                                                                           G_OBJECT (abstract_icon_view));
        }

      /* don't run the default handler here */
      return TRUE;
    }

  return FALSE;
}



static gboolean
thunar_abstract_icon_view_button_release_event (ExoIconView            *view,
                                                GdkEventButton         *event,
                                                ThunarAbstractIconView *abstract_icon_view)
{
664
665
  const XfceGtkActionEntry *action_entry;
  GtkWidget                *window;
666

667
668
669
670
671
  _thunar_return_val_if_fail (EXO_IS_ICON_VIEW (view), FALSE);
  _thunar_return_val_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view), FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_expose_id > 0, FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_motion_id > 0, FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_release_id > 0, FALSE);
672

673
674
675
676
677
678
  window = gtk_widget_get_toplevel (GTK_WIDGET (abstract_icon_view));

  /* execute the related callback  (if any) */
  action_entry = thunar_abstract_icon_view_gesture_action (abstract_icon_view);
  if (G_LIKELY (action_entry != NULL))
    ((void(*)(GtkWindow*))action_entry->callback)(GTK_WINDOW (window));
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700

  /* unregister the "expose-event" handler */
  g_signal_handler_disconnect (G_OBJECT (view), abstract_icon_view->priv->gesture_expose_id);
  abstract_icon_view->priv->gesture_expose_id = 0;

  /* unregister the "motion-notify-event" handler */
  g_signal_handler_disconnect (G_OBJECT (view), abstract_icon_view->priv->gesture_motion_id);
  abstract_icon_view->priv->gesture_motion_id = 0;

  /* unregister the "button-release-event" handler */
  g_signal_handler_disconnect (G_OBJECT (view), abstract_icon_view->priv->gesture_release_id);
  abstract_icon_view->priv->gesture_release_id = 0;

  /* redraw the abstract_icon view */
  gtk_widget_queue_draw (GTK_WIDGET (view));

  return FALSE;
}



static gboolean
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
701
702
703
thunar_abstract_icon_view_draw (ExoIconView            *view,
                                cairo_t                *cr,
                                ThunarAbstractIconView *abstract_icon_view)
704
{
705
706
707
  const XfceGtkActionEntry *action_entry = NULL;
  GdkPixbuf                *gesture_icon = NULL;
  gint                      x, y;
708

709
710
711
712
713
  _thunar_return_val_if_fail (EXO_IS_ICON_VIEW (view), FALSE);
  _thunar_return_val_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view), FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_expose_id > 0, FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_motion_id > 0, FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_release_id > 0, FALSE);
714

715
  /* shade the abstract_icon view content while performing mouse gestures */
716
  cairo_set_source_rgba (cr, 1, 1, 1, 0.7);
717
718
719
  cairo_paint (cr);

  /* determine the gesture action */
720
721
  action_entry = thunar_abstract_icon_view_gesture_action (abstract_icon_view);
  if (G_LIKELY (action_entry != NULL))
722
    {
723
      gesture_icon = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
724
                                               action_entry->menu_item_icon_name,
725
726
727
                                               32,
                                               GTK_ICON_LOOKUP_FORCE_SIZE,
                                               NULL);
728
      /* draw the rendered icon */
729
      if (G_LIKELY (gesture_icon != NULL))
730
        {
731
          /* x/y position of the icon */
732
733
          x = abstract_icon_view->priv->gesture_start_x - gdk_pixbuf_get_width (gesture_icon) / 2;
          y = abstract_icon_view->priv->gesture_start_y - gdk_pixbuf_get_height (gesture_icon) / 2;
734

735
736
          /* render the icon into the abstract_icon view window */
          gdk_cairo_set_source_pixbuf (cr, gesture_icon, x, y);
737
          cairo_rectangle (cr, x, y,
738
739
                           gdk_pixbuf_get_width (gesture_icon),
                           gdk_pixbuf_get_height (gesture_icon));
740
          cairo_fill (cr);
741
742

          /* release the stock abstract_icon */
743
          g_object_unref (G_OBJECT (gesture_icon));
744
745
746
747
748
749
750
751
752
753
754
755
756
        }
    }

  return FALSE;
}



static gboolean
thunar_abstract_icon_view_key_press_event (ExoIconView            *view,
                                           GdkEventKey            *event,
                                           ThunarAbstractIconView *abstract_icon_view)
{
757
758
  abstract_icon_view->priv->button_pressed = FALSE;

759
  /* popup context menu if "Menu" or "<Shift>F10" is pressed */
760
  if (event->keyval == GDK_KEY_Menu || ((event->state & GDK_SHIFT_MASK) != 0 && event->keyval == GDK_KEY_F10))
761
    {
762
      thunar_standard_view_context_menu (THUNAR_STANDARD_VIEW (abstract_icon_view));
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
      return TRUE;
    }

  return FALSE;
}



static gboolean
thunar_abstract_icon_view_motion_notify_event (ExoIconView            *view,
                                               GdkEventMotion         *event,
                                               ThunarAbstractIconView *abstract_icon_view)
{
  GdkRectangle area;

778
779
780
781
782
  _thunar_return_val_if_fail (EXO_IS_ICON_VIEW (view), FALSE);
  _thunar_return_val_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view), FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_expose_id > 0, FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_motion_id > 0, FALSE);
  _thunar_return_val_if_fail (abstract_icon_view->priv->gesture_release_id > 0, FALSE);
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813

  /* schedule a complete redraw on the first motion event */
  if (abstract_icon_view->priv->gesture_current_x == abstract_icon_view->priv->gesture_start_x
      && abstract_icon_view->priv->gesture_current_y == abstract_icon_view->priv->gesture_start_y)
    {
      gtk_widget_queue_draw (GTK_WIDGET (view));
    }
  else
    {
      /* otherwise, just redraw the action abstract_icon area */
      gtk_icon_size_lookup (GTK_ICON_SIZE_DND, &area.width, &area.height);
      area.x = abstract_icon_view->priv->gesture_start_x - area.width / 2;
      area.y = abstract_icon_view->priv->gesture_start_y - area.height / 2;
      gdk_window_invalidate_rect (event->window, &area, TRUE);
    }

  /* update the current gesture position */
  abstract_icon_view->priv->gesture_current_x = event->x;
  abstract_icon_view->priv->gesture_current_y = event->y;

  /* don't execute the default motion notify handler */
  return TRUE;
}



static void
thunar_abstract_icon_view_item_activated (ExoIconView            *view,
                                          GtkTreePath            *path,
                                          ThunarAbstractIconView *abstract_icon_view)
{
814
  GtkWidget *window;
815

816
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));
817

818
819
820
821
822
823
824
  /* be sure to have only the clicked item selected */
  if (abstract_icon_view->priv->button_pressed)
    {
      exo_icon_view_unselect_all (view);
      exo_icon_view_select_path (view, path);
    }

825
826
  window = gtk_widget_get_toplevel (GTK_WIDGET (abstract_icon_view));
  thunar_launcher_activate_selected_files (thunar_window_get_launcher (THUNAR_WINDOW (window)), THUNAR_LAUNCHER_CHANGE_DIRECTORY, NULL);
827
828
829
830
831
832
833
834
835
836
837
838
839
}



static void
thunar_abstract_icon_view_sort_column_changed (GtkTreeSortable        *sortable,
                                               ThunarAbstractIconView *abstract_icon_view)
{
  GtkSortType order;
  gint        column;

  if (gtk_tree_sortable_get_sort_column_id (sortable, &column, &order))
    {
840
841
      abstract_icon_view->priv->sort_column = column;
      abstract_icon_view->priv->sort_order = order;
842
843
844
845
846
847
848
849
    }
}



static void
thunar_abstract_icon_view_zoom_level_changed (ThunarAbstractIconView *abstract_icon_view)
{
850
  _thunar_return_if_fail (THUNAR_IS_ABSTRACT_ICON_VIEW (abstract_icon_view));
851
852

  /* we use the same trick as with ThunarDetailsView here, simply because its simple :-) */
Jonas Kümmerlin's avatar
Jonas Kümmerlin committed
853
  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (gtk_bin_get_child (GTK_BIN (abstract_icon_view))),
854
855
856
                                      THUNAR_STANDARD_VIEW (abstract_icon_view)->icon_renderer,
                                      NULL, NULL, NULL);
}