1 /*-
2 * Copyright (c) 2008 Jannis Pohlmann <jannis@xfce.org>
3 * Copyright (c) 2004-2006 os-cillation e.K.
4 * Copyright (c) 2002,2004 Anders Carlsson <andersca@gnu.org>
5 *
6 * Written by Benedikt Meurer <benny@xfce.org>.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23
24 /* Modified by Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
25 * on 2009-08-30 for use in libfm */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 /* #ifdef HAVE_MATH_H */
32 #include <math.h>
33 /* #endif */
34 #ifdef HAVE_MEMORY_H
35 #include <memory.h>
36 #endif
37 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43
44 #include <gdk/gdkkeysyms.h>
45
46 /*
47 #include <exo/exo-config.h>
48 #include <exo/exo-enum-types.h>
49 #include <exo/exo-icon-view.h>
50 #include <exo/exo-marshal.h>
51 #include <exo/exo-private.h>
52 #include <exo/exo-string.h>
53 #include <exo/exo-alias.h>
54 */
55 #include "exo-icon-view.h"
56 #include "exo-string.h"
57 #include "exo-marshal.h"
58 #include "exo-private.h"
59
60 /* libfm specific */
61 #include "gtk-compat.h"
62
63 /* from exo/exo-marshal.h */
64 #if defined(G_PARAM_STATIC_NAME) && defined(G_PARAM_STATIC_NICK) && defined(G_PARAM_STATIC_BLURB)
65 #define EXO_PARAM_READABLE (G_PARAM_READABLE \
66 | G_PARAM_STATIC_NAME \
67 | G_PARAM_STATIC_NICK \
68 | G_PARAM_STATIC_BLURB)
69 #define EXO_PARAM_WRITABLE (G_PARAM_WRITABLE \
70 | G_PARAM_STATIC_NAME \
71 | G_PARAM_STATIC_NICK \
72 | G_PARAM_STATIC_BLURB)
73 #define EXO_PARAM_READWRITE (G_PARAM_READWRITE \
74 | G_PARAM_STATIC_NAME \
75 | G_PARAM_STATIC_NICK \
76 | G_PARAM_STATIC_BLURB)
77 #else
78 #define EXO_PARAM_READABLE (G_PARAM_READABLE)
79 #define EXO_PARAM_WRITABLE (G_PARAM_WRITABLE)
80 #define EXO_PARAM_READWRITE (G_PARAM_READWRITE)
81 #endif
82
83 #define I_(string) g_intern_static_string(string)
84
85 /* from exo/exo-enum-types.h */
86 GType
exo_icon_view_layout_mode_get_type(void)87 exo_icon_view_layout_mode_get_type (void)
88 {
89 static GType type = 0;
90 if (type == 0) {
91 static const GEnumValue values[] = {
92 { EXO_ICON_VIEW_LAYOUT_ROWS, "EXO_ICON_VIEW_LAYOUT_ROWS", "rows" },
93 { EXO_ICON_VIEW_LAYOUT_COLS, "EXO_ICON_VIEW_LAYOUT_COLS", "cols" },
94 { 0, NULL, NULL }
95 };
96 type = g_enum_register_static ("ExoIconViewLayoutMode", values);
97 }
98 return type;
99 }
100 #define EXO_TYPE_ICON_VIEW_LAYOUT_MODE (exo_icon_view_layout_mode_get_type())
101 /* enumerations from "exo-mount-point.h" */
102
103
104 /* the search dialog timeout (in ms) */
105 #define EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT (5000)
106
107 #define SCROLL_EDGE_SIZE 15
108
109
110
111 /* Property identifiers */
112 enum
113 {
114 PROP_0,
115 PROP_PIXBUF_COLUMN,
116 PROP_TEXT_COLUMN,
117 PROP_MARKUP_COLUMN,
118 PROP_SELECTION_MODE,
119 PROP_LAYOUT_MODE,
120 PROP_ORIENTATION,
121 PROP_MODEL,
122 PROP_COLUMNS,
123 PROP_ITEM_WIDTH,
124 PROP_SPACING,
125 PROP_ROW_SPACING,
126 PROP_COLUMN_SPACING,
127 PROP_MARGIN,
128 PROP_REORDERABLE,
129 PROP_SINGLE_CLICK,
130 PROP_SINGLE_CLICK_TIMEOUT,
131 PROP_ENABLE_SEARCH,
132 PROP_SEARCH_COLUMN,
133 /* For scrollable interface */
134 PROP_HADJUSTMENT,
135 PROP_VADJUSTMENT,
136 PROP_HSCROLL_POLICY,
137 PROP_VSCROLL_POLICY
138 };
139
140 /* Signal identifiers */
141 enum
142 {
143 ITEM_ACTIVATED,
144 SELECTION_CHANGED,
145 SELECT_ALL,
146 UNSELECT_ALL,
147 SELECT_CURSOR_ITEM,
148 TOGGLE_CURSOR_ITEM,
149 MOVE_CURSOR,
150 ACTIVATE_CURSOR_ITEM,
151 START_INTERACTIVE_SEARCH,
152 LAST_SIGNAL
153 };
154
155 /* Icon view flags */
156 typedef enum
157 {
158 EXO_ICON_VIEW_DRAW_KEYFOCUS = (1l << 0), /* whether to draw keyboard focus */
159 EXO_ICON_VIEW_ITERS_PERSIST = (1l << 1), /* whether current model provides persistent iterators */
160 } ExoIconViewFlags;
161
162 #define EXO_ICON_VIEW_SET_FLAG(icon_view, flag) G_STMT_START{ (EXO_ICON_VIEW (icon_view)->priv->flags |= flag); }G_STMT_END
163 #define EXO_ICON_VIEW_UNSET_FLAG(icon_view, flag) G_STMT_START{ (EXO_ICON_VIEW (icon_view)->priv->flags &= ~(flag)); }G_STMT_END
164 #define EXO_ICON_VIEW_FLAG_SET(icon_view, flag) ((EXO_ICON_VIEW (icon_view)->priv->flags & (flag)) == (flag))
165
166
167
168 typedef struct _ExoIconViewCellInfo ExoIconViewCellInfo;
169 typedef struct _ExoIconViewChild ExoIconViewChild;
170 typedef struct _ExoIconViewItem ExoIconViewItem;
171
172
173
174 #define EXO_ICON_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EXO_TYPE_ICON_VIEW, ExoIconViewPrivate))
175 #define EXO_ICON_VIEW_CELL_INFO(obj) ((ExoIconViewCellInfo *) (obj))
176 #define EXO_ICON_VIEW_CHILD(obj) ((ExoIconViewChild *) (obj))
177 #define EXO_ICON_VIEW_ITEM(obj) ((ExoIconViewItem *) (obj))
178
179
180
181 static void exo_icon_view_cell_layout_init (GtkCellLayoutIface *iface);
182 static void exo_icon_view_dispose (GObject *object);
183 static void exo_icon_view_finalize (GObject *object);
184 static void exo_icon_view_get_property (GObject *object,
185 guint prop_id,
186 GValue *value,
187 GParamSpec *pspec);
188 static void exo_icon_view_set_property (GObject *object,
189 guint prop_id,
190 const GValue *value,
191 GParamSpec *pspec);
192 static void exo_icon_view_realize (GtkWidget *widget);
193 static void exo_icon_view_unrealize (GtkWidget *widget);
194 #if GTK_CHECK_VERSION(3, 0, 0)
195 static void exo_icon_view_state_flags_changed (GtkWidget *widget,
196 GtkStateFlags previous_state);
197 #else
198 static void exo_icon_view_state_changed (GtkWidget *widget,
199 GtkStateType previous_state);
200 #endif
201
202 #if GTK_CHECK_VERSION(3, 0, 0)
203 static void exo_icon_view_get_preferred_width (GtkWidget *widget,
204 gint *minimal_width,
205 gint *natural_width);
206 static void exo_icon_view_get_preferred_height (GtkWidget *widget,
207 gint *minimal_height,
208 gint *natural_height);
209 #endif
210 static void exo_icon_view_size_request (GtkWidget *widget,
211 GtkRequisition *requisition);
212 static void exo_icon_view_size_allocate (GtkWidget *widget,
213 GtkAllocation *allocation);
214 static void exo_icon_view_style_set (GtkWidget *widget,
215 GtkStyle *previous_style);
216 #if GTK_CHECK_VERSION(3, 0, 0)
217 static gboolean exo_icon_view_draw (GtkWidget *widget,
218 cairo_t *cr);
219 #else
220 static gboolean exo_icon_view_expose_event (GtkWidget *widget,
221 GdkEventExpose *event);
222 #endif
223 static gboolean exo_icon_view_motion_notify_event (GtkWidget *widget,
224 GdkEventMotion *event);
225 static gboolean exo_icon_view_button_press_event (GtkWidget *widget,
226 GdkEventButton *event);
227 static gboolean exo_icon_view_button_release_event (GtkWidget *widget,
228 GdkEventButton *event);
229 static gboolean exo_icon_view_scroll_event (GtkWidget *widget,
230 GdkEventScroll *event);
231 static gboolean exo_icon_view_key_press_event (GtkWidget *widget,
232 GdkEventKey *event);
233 static gboolean exo_icon_view_focus_out_event (GtkWidget *widget,
234 GdkEventFocus *event);
235 static gboolean exo_icon_view_leave_notify_event (GtkWidget *widget,
236 GdkEventCrossing *event);
237 static void exo_icon_view_remove (GtkContainer *container,
238 GtkWidget *widget);
239 static void exo_icon_view_forall (GtkContainer *container,
240 gboolean include_internals,
241 GtkCallback callback,
242 gpointer callback_data);
243 static AtkObject *exo_icon_view_get_accessible (GtkWidget *widget);
244 #if !GTK_CHECK_VERSION(3, 0, 0)
245 static void exo_icon_view_set_adjustments (ExoIconView *icon_view,
246 GtkAdjustment *hadj,
247 GtkAdjustment *vadj);
248 #else
249 static void exo_icon_view_set_hadjustment (ExoIconView *icon_view,
250 GtkAdjustment *hadj);
251 static void exo_icon_view_set_vadjustment (ExoIconView *icon_view,
252 GtkAdjustment *vadj);
253 #endif
254 static void exo_icon_view_real_select_all (ExoIconView *icon_view);
255 static void exo_icon_view_real_unselect_all (ExoIconView *icon_view);
256 static void exo_icon_view_real_select_cursor_item (ExoIconView *icon_view);
257 static void exo_icon_view_real_toggle_cursor_item (ExoIconView *icon_view);
258 static gboolean exo_icon_view_real_activate_cursor_item (ExoIconView *icon_view);
259 static gboolean exo_icon_view_real_start_interactive_search (ExoIconView *icon_view);
260 static void exo_icon_view_adjustment_changed (GtkAdjustment *adjustment,
261 ExoIconView *icon_view);
262 static gint exo_icon_view_layout_cols (ExoIconView *icon_view,
263 gint item_height,
264 gint *x,
265 gint *maximum_height,
266 gint max_rows);
267 static gint exo_icon_view_layout_rows (ExoIconView *icon_view,
268 gint item_width,
269 gint *y,
270 gint *maximum_width,
271 gint max_cols);
272 static void exo_icon_view_layout (ExoIconView *icon_view);
273 static void exo_icon_view_paint_item (ExoIconView *icon_view,
274 ExoIconViewItem *item,
275 GdkRectangle *area,
276 #if GTK_CHECK_VERSION(3, 0, 0)
277 cairo_t *drawable,
278 #else
279 GdkDrawable *drawable,
280 #endif
281 gint x,
282 gint y,
283 gboolean draw_focus);
284 static void exo_icon_view_queue_draw_item (ExoIconView *icon_view,
285 ExoIconViewItem *item);
286 static void exo_icon_view_queue_layout (ExoIconView *icon_view);
287 static void exo_icon_view_set_cursor_item (ExoIconView *icon_view,
288 ExoIconViewItem *item,
289 gint cursor_cell);
290 static void exo_icon_view_start_rubberbanding (ExoIconView *icon_view,
291 gint x,
292 gint y);
293 static void exo_icon_view_stop_rubberbanding (ExoIconView *icon_view);
294 static void exo_icon_view_update_rubberband_selection (ExoIconView *icon_view);
295 static gboolean exo_icon_view_item_hit_test (ExoIconView *icon_view,
296 ExoIconViewItem *item,
297 gint x,
298 gint y,
299 gint width,
300 gint height);
301 static gboolean exo_icon_view_unselect_all_internal (ExoIconView *icon_view);
302 static void exo_icon_view_calculate_item_size (ExoIconView *icon_view,
303 ExoIconViewItem *item);
304 static void exo_icon_view_calculate_item_size2 (ExoIconView *icon_view,
305 ExoIconViewItem *item,
306 gint *max_width,
307 gint *max_height);
308 static void exo_icon_view_update_rubberband (gpointer data);
309 static void exo_icon_view_invalidate_sizes (ExoIconView *icon_view);
310 static void exo_icon_view_add_move_binding (GtkBindingSet *binding_set,
311 guint keyval,
312 guint modmask,
313 GtkMovementStep step,
314 gint count);
315 static gboolean exo_icon_view_real_move_cursor (ExoIconView *icon_view,
316 GtkMovementStep step,
317 gint count);
318 static void exo_icon_view_move_cursor_up_down (ExoIconView *icon_view,
319 gint count);
320 static void exo_icon_view_move_cursor_page_up_down (ExoIconView *icon_view,
321 gint count);
322 static void exo_icon_view_move_cursor_left_right (ExoIconView *icon_view,
323 gint count);
324 static void exo_icon_view_move_cursor_start_end (ExoIconView *icon_view,
325 gint count);
326 static void exo_icon_view_scroll_to_item (ExoIconView *icon_view,
327 ExoIconViewItem *item);
328 static void exo_icon_view_select_item (ExoIconView *icon_view,
329 ExoIconViewItem *item);
330 static void exo_icon_view_unselect_item (ExoIconView *icon_view,
331 ExoIconViewItem *item);
332 static gboolean exo_icon_view_select_all_between (ExoIconView *icon_view,
333 ExoIconViewItem *anchor,
334 ExoIconViewItem *cursor);
335 static ExoIconViewItem * exo_icon_view_get_item_at_coords (const ExoIconView *icon_view,
336 gint x,
337 gint y,
338 gboolean only_in_cell,
339 ExoIconViewCellInfo **cell_at_pos);
340 static void exo_icon_view_get_cell_area (ExoIconView *icon_view,
341 ExoIconViewItem *item,
342 ExoIconViewCellInfo *cell_info,
343 GdkRectangle *cell_area);
344 static ExoIconViewCellInfo *exo_icon_view_get_cell_info (ExoIconView *icon_view,
345 GtkCellRenderer *renderer);
346 static void exo_icon_view_set_cell_data (const ExoIconView *icon_view,
347 ExoIconViewItem *item);
348 static void exo_icon_view_cell_layout_pack_start (GtkCellLayout *layout,
349 GtkCellRenderer *renderer,
350 gboolean expand);
351 static void exo_icon_view_cell_layout_pack_end (GtkCellLayout *layout,
352 GtkCellRenderer *renderer,
353 gboolean expand);
354 static void exo_icon_view_cell_layout_add_attribute (GtkCellLayout *layout,
355 GtkCellRenderer *renderer,
356 const gchar *attribute,
357 gint column);
358 static void exo_icon_view_cell_layout_clear (GtkCellLayout *layout);
359 static void exo_icon_view_cell_layout_clear_attributes (GtkCellLayout *layout,
360 GtkCellRenderer *renderer);
361 static void exo_icon_view_cell_layout_set_cell_data_func (GtkCellLayout *layout,
362 GtkCellRenderer *cell,
363 GtkCellLayoutDataFunc func,
364 gpointer func_data,
365 GDestroyNotify destroy);
366 static void exo_icon_view_cell_layout_reorder (GtkCellLayout *layout,
367 GtkCellRenderer *cell,
368 gint position);
369 static void exo_icon_view_item_activate_cell (ExoIconView *icon_view,
370 ExoIconViewItem *item,
371 ExoIconViewCellInfo *cell_info,
372 GdkEvent *event);
373 static void exo_icon_view_put (ExoIconView *icon_view,
374 GtkWidget *widget,
375 ExoIconViewItem *item,
376 gint cell);
377 static void exo_icon_view_remove_widget (GtkCellEditable *editable,
378 ExoIconView *icon_view);
379 static void exo_icon_view_start_editing (ExoIconView *icon_view,
380 ExoIconViewItem *item,
381 ExoIconViewCellInfo *cell_info,
382 GdkEvent *event);
383 static void exo_icon_view_stop_editing (ExoIconView *icon_view,
384 gboolean cancel_editing);
385
386 /* Source side drag signals */
387 static void exo_icon_view_drag_begin (GtkWidget *widget,
388 GdkDragContext *context);
389 static void exo_icon_view_drag_end (GtkWidget *widget,
390 GdkDragContext *context);
391 static void exo_icon_view_drag_data_get (GtkWidget *widget,
392 GdkDragContext *context,
393 GtkSelectionData *selection_data,
394 guint info,
395 guint drag_time);
396 static void exo_icon_view_drag_data_delete (GtkWidget *widget,
397 GdkDragContext *context);
398
399 /* Target side drag signals */
400 static void exo_icon_view_drag_leave (GtkWidget *widget,
401 GdkDragContext *context,
402 guint drag_time);
403 static gboolean exo_icon_view_drag_motion (GtkWidget *widget,
404 GdkDragContext *context,
405 gint x,
406 gint y,
407 guint drag_time);
408 static gboolean exo_icon_view_drag_drop (GtkWidget *widget,
409 GdkDragContext *context,
410 gint x,
411 gint y,
412 guint drag_time);
413 static void exo_icon_view_drag_data_received (GtkWidget *widget,
414 GdkDragContext *context,
415 gint x,
416 gint y,
417 GtkSelectionData *selection_data,
418 guint info,
419 guint drag_time);
420 static gboolean exo_icon_view_maybe_begin_drag (ExoIconView *icon_view,
421 GdkEventMotion *event);
422
423 static void remove_scroll_timeout (ExoIconView *icon_view);
424
425 /* single-click autoselection support */
426 static gboolean exo_icon_view_single_click_timeout (gpointer user_data);
427 static void exo_icon_view_single_click_timeout_destroy (gpointer user_data);
428
429 /* Interactive search support */
430 static void exo_icon_view_search_activate (GtkEntry *entry,
431 ExoIconView *icon_view);
432 static void exo_icon_view_search_dialog_hide (GtkWidget *search_dialog,
433 ExoIconView *icon_view);
434 static void exo_icon_view_search_ensure_directory (ExoIconView *icon_view);
435 static void exo_icon_view_search_init (GtkWidget *search_entry,
436 ExoIconView *icon_view);
437 static gboolean exo_icon_view_search_iter (ExoIconView *icon_view,
438 GtkTreeModel *model,
439 GtkTreeIter *iter,
440 const gchar *text,
441 gint *count,
442 gint n);
443 static void exo_icon_view_search_move (GtkWidget *widget,
444 ExoIconView *icon_view,
445 gboolean move_up);
446 #if GTK_CHECK_VERSION(2, 20, 0)
447 static void exo_icon_view_search_preedit_changed (GtkEntry *entry,
448 gchar *preedit,
449 ExoIconView *icon_view);
450 #else
451 static void exo_icon_view_search_preedit_changed (GtkIMContext *im_context,
452 ExoIconView *icon_view);
453 #endif
454 static gboolean exo_icon_view_search_start (ExoIconView *icon_view,
455 gboolean keybinding);
456 static gboolean exo_icon_view_search_equal_func (GtkTreeModel *model,
457 gint column,
458 const gchar *key,
459 GtkTreeIter *iter,
460 gpointer user_data);
461 static void exo_icon_view_search_position_func (ExoIconView *icon_view,
462 GtkWidget *search_dialog,
463 gpointer user_data);
464 static gboolean exo_icon_view_search_button_press_event (GtkWidget *widget,
465 GdkEventButton *event,
466 ExoIconView *icon_view);
467 static gboolean exo_icon_view_search_delete_event (GtkWidget *widget,
468 GdkEventAny *event,
469 ExoIconView *icon_view);
470 static gboolean exo_icon_view_search_key_press_event (GtkWidget *widget,
471 GdkEventKey *event,
472 ExoIconView *icon_view);
473 static gboolean exo_icon_view_search_scroll_event (GtkWidget *widget,
474 GdkEventScroll *event,
475 ExoIconView *icon_view);
476 static gboolean exo_icon_view_search_timeout (gpointer user_data);
477 static void exo_icon_view_search_timeout_destroy (gpointer user_data);
478
479
480
481 struct _ExoIconViewCellInfo
482 {
483 GtkCellRenderer *cell;
484 guint expand : 1;
485 guint pack : 1;
486 guint editing : 1;
487 gint position;
488 GSList *attributes;
489 GtkCellLayoutDataFunc func;
490 gpointer func_data;
491 GDestroyNotify destroy;
492 gboolean is_text;
493 };
494
495 struct _ExoIconViewChild
496 {
497 ExoIconViewItem *item;
498 GtkWidget *widget;
499 gint cell;
500 };
501
502 struct _ExoIconViewItem
503 {
504 GtkTreeIter iter;
505
506 /* Bounding box (a value of -1 for width indicates
507 * that the item needs to be layouted first)
508 */
509 GdkRectangle area;
510
511 /* Individual cells.
512 * box[i] is the actual area occupied by cell i,
513 * before, after are used to calculate the cell
514 * area relative to the box.
515 * See exo_icon_view_get_cell_area().
516 */
517 gint n_cells;
518 GdkRectangle *box;
519 gint index;
520 gint *before;
521 gint *after;
522
523 guint row : ((sizeof (guint) / 2) * 8) - 1;
524 guint col : ((sizeof (guint) / 2) * 8) - 1;
525 guint selected : 1;
526 guint selected_before_rubberbanding : 1;
527 };
528
529 struct _ExoIconViewPrivate
530 {
531 gint width, height;
532 gint rows, cols;
533
534 GtkSelectionMode selection_mode;
535
536 ExoIconViewLayoutMode layout_mode;
537
538 GdkWindow *bin_window;
539
540 GList *children;
541
542 GtkTreeModel *model;
543
544 GList *items;
545
546 GtkAdjustment *hadjustment;
547 GtkAdjustment *vadjustment;
548 #if GTK_CHECK_VERSION(3, 0, 0)
549 /* GtkScrollablePolicy needs to be checked when
550 * driving the scrollable adjustment values */
551 guint hscroll_policy : 1;
552 guint vscroll_policy : 1;
553 #endif
554
555 gint layout_idle_id;
556
557 gboolean doing_rubberband;
558 gint rubberband_x_1, rubberband_y_1;
559 gint rubberband_x2, rubberband_y2;
560
561 gint scroll_timeout_id;
562 gint scroll_value_diff;
563 gint event_last_x, event_last_y;
564
565 ExoIconViewItem *anchor_item;
566 ExoIconViewItem *cursor_item;
567 ExoIconViewItem *edited_item;
568 GtkCellEditable *editable;
569 ExoIconViewItem *prelit_item;
570
571 ExoIconViewItem *last_single_clicked;
572
573 GList *cell_list;
574 gint n_cells;
575
576 gint cursor_cell;
577
578 GtkOrientation orientation;
579
580 gint columns;
581 gint item_width;
582 gint spacing;
583 gint row_spacing;
584 gint column_spacing;
585 gint margin;
586
587 gint text_column;
588 gint markup_column;
589 gint pixbuf_column;
590
591 gint pixbuf_cell;
592 gint text_cell;
593
594 /* Drag-and-drop. */
595 GdkModifierType start_button_mask;
596 gint pressed_button;
597 gint press_start_x;
598 gint press_start_y;
599
600 GtkTargetList *source_targets;
601 GdkDragAction source_actions;
602
603 GtkTargetList *dest_targets;
604 GdkDragAction dest_actions;
605
606 GtkTreeRowReference *dest_item;
607 ExoIconViewDropPosition dest_pos;
608
609 /* delayed scrolling */
610 GtkTreeRowReference *scroll_to_path;
611 gfloat scroll_to_row_align;
612 gfloat scroll_to_col_align;
613 guint scroll_to_use_align : 1;
614
615 /* misc flags */
616 guint source_set : 1;
617 guint dest_set : 1;
618 guint reorderable : 1;
619 guint empty_view_drop :1;
620
621 guint ctrl_pressed : 1;
622 guint shift_pressed : 1;
623
624 guint dnd_locked : 1;
625
626 /* Single-click support
627 * The single_click_timeout is the timeout after which the
628 * prelited item will be automatically selected in single
629 * click mode (0 to disable).
630 */
631 guint single_click : 1;
632 guint single_click_timeout;
633 guint single_click_timeout_id;
634 guint single_click_timeout_state;
635
636 /* Interactive search support */
637 guint enable_search : 1;
638 guint search_imcontext_changed : 1;
639 gint search_column;
640 gint search_selected_iter;
641 gint search_timeout_id;
642 gboolean search_disable_popdown;
643 ExoIconViewSearchEqualFunc search_equal_func;
644 gpointer search_equal_data;
645 GDestroyNotify search_equal_destroy;
646 ExoIconViewSearchPositionFunc search_position_func;
647 gpointer search_position_data;
648 GDestroyNotify search_position_destroy;
649 gint search_entry_changed_id;
650 GtkWidget *search_entry;
651 GtkWidget *search_window;
652
653 /* ExoIconViewFlags */
654 guint flags;
655 };
656
657
658
659 static guint icon_view_signals[LAST_SIGNAL];
660
661
662
G_DEFINE_TYPE_WITH_CODE(ExoIconView,exo_icon_view,GTK_TYPE_CONTAINER,G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL)G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,exo_icon_view_cell_layout_init))663 G_DEFINE_TYPE_WITH_CODE (ExoIconView, exo_icon_view, GTK_TYPE_CONTAINER,
664 #if GTK_CHECK_VERSION(3, 0, 0)
665 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)
666 #endif
667 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, exo_icon_view_cell_layout_init))
668
669
670
671 static void
672 exo_icon_view_class_init (ExoIconViewClass *klass)
673 {
674 GtkContainerClass *gtkcontainer_class;
675 GtkWidgetClass *gtkwidget_class;
676 GtkBindingSet *gtkbinding_set;
677 GObjectClass *gobject_class;
678
679 /* add our private data to the type's instances */
680 g_type_class_add_private (klass, sizeof (ExoIconViewPrivate));
681
682 gobject_class = G_OBJECT_CLASS (klass);
683 gobject_class->dispose = exo_icon_view_dispose;
684 gobject_class->finalize = exo_icon_view_finalize;
685 gobject_class->set_property = exo_icon_view_set_property;
686 gobject_class->get_property = exo_icon_view_get_property;
687
688 gtkwidget_class = GTK_WIDGET_CLASS (klass);
689 gtkwidget_class->realize = exo_icon_view_realize;
690 gtkwidget_class->unrealize = exo_icon_view_unrealize;
691 gtkwidget_class->get_accessible = exo_icon_view_get_accessible;
692 #if GTK_CHECK_VERSION(3, 0, 0)
693 gtkwidget_class->state_flags_changed = exo_icon_view_state_flags_changed;
694 gtkwidget_class->get_preferred_width = exo_icon_view_get_preferred_width;
695 gtkwidget_class->get_preferred_height = exo_icon_view_get_preferred_height;
696 #else
697 gtkwidget_class->state_changed = exo_icon_view_state_changed;
698 gtkwidget_class->size_request = exo_icon_view_size_request;
699 #endif
700 gtkwidget_class->size_allocate = exo_icon_view_size_allocate;
701 gtkwidget_class->style_set = exo_icon_view_style_set;
702 #if GTK_CHECK_VERSION(3, 0, 0)
703 gtkwidget_class->draw = exo_icon_view_draw;
704 #else
705 gtkwidget_class->expose_event = exo_icon_view_expose_event;
706 #endif
707 gtkwidget_class->motion_notify_event = exo_icon_view_motion_notify_event;
708 gtkwidget_class->button_press_event = exo_icon_view_button_press_event;
709 gtkwidget_class->button_release_event = exo_icon_view_button_release_event;
710 gtkwidget_class->scroll_event = exo_icon_view_scroll_event;
711 gtkwidget_class->key_press_event = exo_icon_view_key_press_event;
712 gtkwidget_class->focus_out_event = exo_icon_view_focus_out_event;
713 gtkwidget_class->leave_notify_event = exo_icon_view_leave_notify_event;
714 gtkwidget_class->drag_begin = exo_icon_view_drag_begin;
715 gtkwidget_class->drag_end = exo_icon_view_drag_end;
716 gtkwidget_class->drag_data_get = exo_icon_view_drag_data_get;
717 gtkwidget_class->drag_data_delete = exo_icon_view_drag_data_delete;
718 gtkwidget_class->drag_leave = exo_icon_view_drag_leave;
719 gtkwidget_class->drag_motion = exo_icon_view_drag_motion;
720 gtkwidget_class->drag_drop = exo_icon_view_drag_drop;
721 gtkwidget_class->drag_data_received = exo_icon_view_drag_data_received;
722
723 gtkcontainer_class = GTK_CONTAINER_CLASS (klass);
724 gtkcontainer_class->remove = exo_icon_view_remove;
725 gtkcontainer_class->forall = exo_icon_view_forall;
726
727
728 #if !GTK_CHECK_VERSION(3, 0, 0)
729 klass->set_scroll_adjustments = exo_icon_view_set_adjustments;
730 #endif
731 klass->select_all = exo_icon_view_real_select_all;
732 klass->unselect_all = exo_icon_view_real_unselect_all;
733 klass->select_cursor_item = exo_icon_view_real_select_cursor_item;
734 klass->toggle_cursor_item = exo_icon_view_real_toggle_cursor_item;
735 klass->move_cursor = exo_icon_view_real_move_cursor;
736 klass->activate_cursor_item = exo_icon_view_real_activate_cursor_item;
737 klass->start_interactive_search = exo_icon_view_real_start_interactive_search;
738
739 /**
740 * ExoIconView:column-spacing:
741 *
742 * The column-spacing property specifies the space which is inserted between
743 * the columns of the icon view.
744 *
745 * Since: 0.3.1
746 **/
747 g_object_class_install_property (gobject_class,
748 PROP_COLUMN_SPACING,
749 g_param_spec_int ("column-spacing",
750 _("Column Spacing"),
751 _("Space which is inserted between grid column"),
752 0, G_MAXINT, 6,
753 EXO_PARAM_READWRITE));
754
755 /**
756 * ExoIconView:columns:
757 *
758 * The columns property contains the number of the columns in which the
759 * items should be displayed. If it is -1, the number of columns will
760 * be chosen automatically to fill the available area.
761 *
762 * Since: 0.3.1
763 **/
764 g_object_class_install_property (gobject_class,
765 PROP_COLUMNS,
766 g_param_spec_int ("columns",
767 _("Number of columns"),
768 _("Number of columns to display"),
769 -1, G_MAXINT, -1,
770 EXO_PARAM_READWRITE));
771
772 /**
773 * ExoIconView:enable-search:
774 *
775 * View allows user to search through columns interactively.
776 *
777 * Since: 0.3.1.3
778 **/
779 g_object_class_install_property (gobject_class,
780 PROP_ENABLE_SEARCH,
781 g_param_spec_boolean ("enable-search",
782 _("Enable Search"),
783 _("View allows user to search through columns interactively"),
784 TRUE,
785 EXO_PARAM_READWRITE));
786
787
788 /**
789 * ExoIconView:item-width:
790 *
791 * The item-width property specifies the width to use for each item.
792 * If it is set to -1, the icon view will automatically determine a
793 * suitable item size.
794 *
795 * Since: 0.3.1
796 **/
797 g_object_class_install_property (gobject_class,
798 PROP_ITEM_WIDTH,
799 g_param_spec_int ("item-width",
800 _("Width for each item"),
801 _("The width used for each item"),
802 -1, G_MAXINT, -1,
803 EXO_PARAM_READWRITE));
804
805 /**
806 * ExoIconView:layout-mode:
807 *
808 * The layout-mode property specifies the way items are layed out in
809 * the #ExoIconView. This can be either %EXO_ICON_VIEW_LAYOUT_ROWS,
810 * which is the default, where items are layed out horizontally in
811 * rows from top to bottom, or %EXO_ICON_VIEW_LAYOUT_COLS, where items
812 * are layed out vertically in columns from left to right.
813 *
814 * Since: 0.3.1.5
815 **/
816 g_object_class_install_property (gobject_class,
817 PROP_LAYOUT_MODE,
818 g_param_spec_enum ("layout-mode",
819 _("Layout mode"),
820 _("The layout mode"),
821 EXO_TYPE_ICON_VIEW_LAYOUT_MODE,
822 EXO_ICON_VIEW_LAYOUT_ROWS,
823 EXO_PARAM_READWRITE));
824
825 /**
826 * ExoIconView:margin:
827 *
828 * The margin property specifies the space which is inserted
829 * at the edges of the icon view.
830 *
831 * Since: 0.3.1
832 **/
833 g_object_class_install_property (gobject_class,
834 PROP_MARGIN,
835 g_param_spec_int ("margin",
836 _("Margin"),
837 _("Space which is inserted at the edges of the icon view"),
838 0, G_MAXINT, 6,
839 EXO_PARAM_READWRITE));
840
841 /**
842 * ExoIconView:markup-column:
843 *
844 * The markup-column property contains the number of the model column
845 * containing markup information to be displayed. The markup column must be
846 * of type #G_TYPE_STRING. If this property and the text-column property
847 * are both set to column numbers, it overrides the text column.
848 * If both are set to -1, no texts are displayed.
849 **/
850 g_object_class_install_property (gobject_class,
851 PROP_MARKUP_COLUMN,
852 g_param_spec_int ("markup-column",
853 _("Markup column"),
854 _("Model column used to retrieve the text if using Pango markup"),
855 -1, G_MAXINT, -1,
856 EXO_PARAM_READWRITE));
857
858 /**
859 * ExoIconView:model:
860 *
861 * The model property contains the #GtkTreeModel, which should be
862 * display by this icon view. Setting this property to %NULL turns
863 * off the display of anything.
864 **/
865 g_object_class_install_property (gobject_class,
866 PROP_MODEL,
867 g_param_spec_object ("model",
868 _("Icon View Model"),
869 _("The model for the icon view"),
870 GTK_TYPE_TREE_MODEL,
871 EXO_PARAM_READWRITE));
872
873 /**
874 * ExoIconView:orientation:
875 *
876 * The orientation property specifies how the cells (i.e. the icon and
877 * the text) of the item are positioned relative to each other.
878 **/
879 g_object_class_install_property (gobject_class,
880 PROP_ORIENTATION,
881 g_param_spec_enum ("orientation",
882 _("Orientation"),
883 _("How the text and icon of each item are positioned relative to each other"),
884 GTK_TYPE_ORIENTATION,
885 GTK_ORIENTATION_VERTICAL,
886 EXO_PARAM_READWRITE));
887
888 /**
889 * ExoIconView:pixbuf-column:
890 *
891 * The ::pixbuf-column property contains the number of the model column
892 * containing the pixbufs which are displayed. The pixbuf column must be
893 * of type #GDK_TYPE_PIXBUF. Setting this property to -1 turns off the
894 * display of pixbufs.
895 **/
896 g_object_class_install_property (gobject_class,
897 PROP_PIXBUF_COLUMN,
898 g_param_spec_int ("pixbuf-column",
899 _("Pixbuf column"),
900 _("Model column used to retrieve the icon pixbuf from"),
901 -1, G_MAXINT, -1,
902 EXO_PARAM_READWRITE));
903
904 /**
905 * ExoIconView:reorderable:
906 *
907 * The reorderable property specifies if the items can be reordered
908 * by Drag and Drop.
909 *
910 * Since: 0.3.1
911 **/
912 g_object_class_install_property (gobject_class,
913 PROP_REORDERABLE,
914 g_param_spec_boolean ("reorderable",
915 _("Reorderable"),
916 _("View is reorderable"),
917 FALSE,
918 EXO_PARAM_READWRITE));
919
920 /**
921 * ExoIconView:row-spacing:
922 *
923 * The row-spacing property specifies the space which is inserted between
924 * the rows of the icon view.
925 *
926 * Since: 0.3.1
927 **/
928 g_object_class_install_property (gobject_class,
929 PROP_ROW_SPACING,
930 g_param_spec_int ("row-spacing",
931 _("Row Spacing"),
932 _("Space which is inserted between grid rows"),
933 0, G_MAXINT, 6,
934 EXO_PARAM_READWRITE));
935
936 /**
937 * ExoIconView:search-column:
938 *
939 * Model column to search through when searching through code.
940 *
941 * Since: 0.3.1.3
942 **/
943 g_object_class_install_property (gobject_class,
944 PROP_SEARCH_COLUMN,
945 g_param_spec_int ("search-column",
946 _("Search Column"),
947 _("Model column to search through when searching through item"),
948 -1, G_MAXINT, -1,
949 EXO_PARAM_READWRITE));
950
951 /**
952 * ExoIconView:selection-mode:
953 *
954 * The selection-mode property specifies the selection mode of
955 * icon view. If the mode is #GTK_SELECTION_MULTIPLE, rubberband selection
956 * is enabled, for the other modes, only keyboard selection is possible.
957 **/
958 g_object_class_install_property (gobject_class,
959 PROP_SELECTION_MODE,
960 g_param_spec_enum ("selection-mode",
961 _("Selection mode"),
962 _("The selection mode"),
963 GTK_TYPE_SELECTION_MODE,
964 GTK_SELECTION_SINGLE,
965 EXO_PARAM_READWRITE));
966
967 /**
968 * ExoIconView:single-click:
969 *
970 * Determines whether items can be activated by single or double clicks.
971 *
972 * Since: 0.3.1.3
973 **/
974 g_object_class_install_property (gobject_class,
975 PROP_SINGLE_CLICK,
976 g_param_spec_boolean ("single-click",
977 _("Single Click"),
978 _("Whether the items in the view can be activated with single clicks"),
979 FALSE,
980 EXO_PARAM_READWRITE));
981
982 /**
983 * ExoIconView:single-click-timeout:
984 *
985 * The amount of time in milliseconds after which a prelited item (an item
986 * which is hovered by the mouse cursor) will be selected automatically in
987 * single click mode. A value of %0 disables the automatic selection.
988 *
989 * Since: 0.3.1.5
990 **/
991 g_object_class_install_property (gobject_class,
992 PROP_SINGLE_CLICK_TIMEOUT,
993 g_param_spec_uint ("single-click-timeout",
994 _("Single Click Timeout"),
995 _("The amount of time after which the item under the mouse cursor will be selected automatically in single click mode"),
996 0, G_MAXUINT, 0,
997 EXO_PARAM_READWRITE));
998
999 /**
1000 * ExoIconView:spacing:
1001 *
1002 * The spacing property specifies the space which is inserted between
1003 * the cells (i.e. the icon and the text) of an item.
1004 *
1005 * Since: 0.3.1
1006 **/
1007 g_object_class_install_property (gobject_class,
1008 PROP_SPACING,
1009 g_param_spec_int ("spacing",
1010 _("Spacing"),
1011 _("Space which is inserted between cells of an item"),
1012 0, G_MAXINT, 0,
1013 EXO_PARAM_READWRITE));
1014
1015 /**
1016 * ExoIconView:text-column:
1017 *
1018 * The text-column property contains the number of the model column
1019 * containing the texts which are displayed. The text column must be
1020 * of type #G_TYPE_STRING. If this property and the markup-column
1021 * property are both set to -1, no texts are displayed.
1022 **/
1023 g_object_class_install_property (gobject_class,
1024 PROP_TEXT_COLUMN,
1025 g_param_spec_int ("text-column",
1026 _("Text column"),
1027 _("Model column used to retrieve the text from"),
1028 -1, G_MAXINT, -1,
1029 EXO_PARAM_READWRITE));
1030
1031
1032 #if GTK_CHECK_VERSION(3, 0, 0)
1033 /* Scrollable interface properties */
1034 g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
1035 g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment");
1036 g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1037 g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1038 #endif
1039
1040 gtk_widget_class_install_style_property (gtkwidget_class,
1041 g_param_spec_boxed ("selection-box-color",
1042 _("Selection Box Color"),
1043 _("Color of the selection box"),
1044 GDK_TYPE_COLOR,
1045 EXO_PARAM_READABLE));
1046
1047 gtk_widget_class_install_style_property (gtkwidget_class,
1048 g_param_spec_uchar ("selection-box-alpha",
1049 _("Selection Box Alpha"),
1050 _("Opacity of the selection box"),
1051 0, 0xff,
1052 0x40,
1053 EXO_PARAM_READABLE));
1054
1055 /**
1056 * ExoIconView::item-activated:
1057 * @icon_view : a #ExoIconView.
1058 * @path :
1059 **/
1060 icon_view_signals[ITEM_ACTIVATED] =
1061 g_signal_new (I_("item-activated"),
1062 G_TYPE_FROM_CLASS (gobject_class),
1063 G_SIGNAL_RUN_LAST,
1064 G_STRUCT_OFFSET (ExoIconViewClass, item_activated),
1065 NULL, NULL,
1066 g_cclosure_marshal_VOID__BOXED,
1067 G_TYPE_NONE, 1,
1068 GTK_TYPE_TREE_PATH);
1069
1070 /**
1071 * ExoIconView::selection-changed:
1072 * @icon_view : a #ExoIconView.
1073 **/
1074 icon_view_signals[SELECTION_CHANGED] =
1075 g_signal_new (I_("selection-changed"),
1076 G_TYPE_FROM_CLASS (gobject_class),
1077 G_SIGNAL_RUN_FIRST,
1078 G_STRUCT_OFFSET (ExoIconViewClass, selection_changed),
1079 NULL, NULL,
1080 g_cclosure_marshal_VOID__VOID,
1081 G_TYPE_NONE, 0);
1082
1083 #if !GTK_CHECK_VERSION(3, 0, 0)
1084 /**
1085 * ExoIconView::set-scroll-adjustments:
1086 * @icon_view : a #ExoIconView.
1087 * @hadjustment :
1088 * @vadjustment :
1089 **/
1090 gtkwidget_class->set_scroll_adjustments_signal =
1091 g_signal_new (I_("set-scroll-adjustments"),
1092 G_TYPE_FROM_CLASS (gobject_class),
1093 G_SIGNAL_RUN_LAST,
1094 G_STRUCT_OFFSET (ExoIconViewClass, set_scroll_adjustments),
1095 NULL, NULL,
1096 _exo_marshal_VOID__OBJECT_OBJECT,
1097 G_TYPE_NONE, 2,
1098 GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1099 #endif
1100
1101 /**
1102 * ExoIconView::select-all:
1103 * @icon_view : a #ExoIconView.
1104 **/
1105 icon_view_signals[SELECT_ALL] =
1106 g_signal_new (I_("select-all"),
1107 G_TYPE_FROM_CLASS (gobject_class),
1108 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1109 G_STRUCT_OFFSET (ExoIconViewClass, select_all),
1110 NULL, NULL,
1111 g_cclosure_marshal_VOID__VOID,
1112 G_TYPE_NONE, 0);
1113
1114 /**
1115 * ExoIconView::unselect-all:
1116 * @icon_view : a #ExoIconView.
1117 **/
1118 icon_view_signals[UNSELECT_ALL] =
1119 g_signal_new (I_("unselect-all"),
1120 G_TYPE_FROM_CLASS (gobject_class),
1121 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1122 G_STRUCT_OFFSET (ExoIconViewClass, unselect_all),
1123 NULL, NULL,
1124 g_cclosure_marshal_VOID__VOID,
1125 G_TYPE_NONE, 0);
1126
1127 /**
1128 * ExoIconView::select-cursor-item:
1129 * @icon_view : a #ExoIconView.
1130 **/
1131 icon_view_signals[SELECT_CURSOR_ITEM] =
1132 g_signal_new (I_("select-cursor-item"),
1133 G_TYPE_FROM_CLASS (gobject_class),
1134 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1135 G_STRUCT_OFFSET (ExoIconViewClass, select_cursor_item),
1136 NULL, NULL,
1137 g_cclosure_marshal_VOID__VOID,
1138 G_TYPE_NONE, 0);
1139
1140 /**
1141 * ExoIconView::toggle-cursor-item:
1142 * @icon_view : a #ExoIconView.
1143 **/
1144 icon_view_signals[TOGGLE_CURSOR_ITEM] =
1145 g_signal_new (I_("toggle-cursor-item"),
1146 G_TYPE_FROM_CLASS (gobject_class),
1147 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1148 G_STRUCT_OFFSET (ExoIconViewClass, toggle_cursor_item),
1149 NULL, NULL,
1150 g_cclosure_marshal_VOID__VOID,
1151 G_TYPE_NONE, 0);
1152
1153 /**
1154 * ExoIconView::activate-cursor-item:
1155 * @icon_view : a #ExoIconView.
1156 *
1157 * Return value:
1158 **/
1159 icon_view_signals[ACTIVATE_CURSOR_ITEM] =
1160 g_signal_new (I_("activate-cursor-item"),
1161 G_TYPE_FROM_CLASS (gobject_class),
1162 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1163 G_STRUCT_OFFSET (ExoIconViewClass, activate_cursor_item),
1164 NULL, NULL,
1165 _exo_marshal_BOOLEAN__VOID,
1166 G_TYPE_BOOLEAN, 0);
1167
1168 /**
1169 * ExoIconView::start-interactive-search:
1170 * @iconb_view : a #ExoIconView.
1171 *
1172 * Return value:
1173 **/
1174 icon_view_signals[START_INTERACTIVE_SEARCH] =
1175 g_signal_new (I_("start-interactive-search"),
1176 G_TYPE_FROM_CLASS (gobject_class),
1177 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1178 G_STRUCT_OFFSET (ExoIconViewClass, start_interactive_search),
1179 NULL, NULL,
1180 _exo_marshal_BOOLEAN__VOID,
1181 G_TYPE_BOOLEAN, 0);
1182
1183 /**
1184 * ExoIconView::move-cursor:
1185 * @icon_view : a #ExoIconView.
1186 * @step :
1187 * @count :
1188 *
1189 * Return value:
1190 **/
1191 icon_view_signals[MOVE_CURSOR] =
1192 g_signal_new (I_("move-cursor"),
1193 G_TYPE_FROM_CLASS (gobject_class),
1194 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1195 G_STRUCT_OFFSET (ExoIconViewClass, move_cursor),
1196 NULL, NULL,
1197 _exo_marshal_BOOLEAN__ENUM_INT,
1198 G_TYPE_BOOLEAN, 2,
1199 GTK_TYPE_MOVEMENT_STEP,
1200 G_TYPE_INT);
1201
1202 /* Key bindings */
1203 gtkbinding_set = gtk_binding_set_by_class (klass);
1204 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_a, GDK_CONTROL_MASK, "select-all", 0);
1205 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1206 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_space, GDK_CONTROL_MASK, "toggle-cursor-item", 0);
1207 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_space, 0, "activate-cursor-item", 0);
1208 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_Return, 0, "activate-cursor-item", 0);
1209 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_ISO_Enter, 0, "activate-cursor-item", 0);
1210 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_KP_Enter, 0, "activate-cursor-item", 0);
1211 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1212 gtk_binding_entry_add_signal (gtkbinding_set, GDK_KEY_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1213
1214 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
1215 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
1216 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
1217 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
1218 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_p, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, -1);
1219 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_n, GDK_CONTROL_MASK, GTK_MOVEMENT_DISPLAY_LINES, 1);
1220 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1);
1221 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_BUFFER_ENDS, -1);
1222 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1);
1223 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_BUFFER_ENDS, 1);
1224 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Page_Up, 0, GTK_MOVEMENT_PAGES, -1);
1225 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Page_Up, 0, GTK_MOVEMENT_PAGES, -1);
1226 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Page_Down, 0, GTK_MOVEMENT_PAGES, 1);
1227 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1);
1228 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1229 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1230 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
1231 exo_icon_view_add_move_binding (gtkbinding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
1232 }
1233
1234
1235
1236 static void
exo_icon_view_cell_layout_init(GtkCellLayoutIface * iface)1237 exo_icon_view_cell_layout_init (GtkCellLayoutIface *iface)
1238 {
1239 iface->pack_start = exo_icon_view_cell_layout_pack_start;
1240 iface->pack_end = exo_icon_view_cell_layout_pack_end;
1241 iface->clear = exo_icon_view_cell_layout_clear;
1242 iface->add_attribute = exo_icon_view_cell_layout_add_attribute;
1243 iface->set_cell_data_func = exo_icon_view_cell_layout_set_cell_data_func;
1244 iface->clear_attributes = exo_icon_view_cell_layout_clear_attributes;
1245 iface->reorder = exo_icon_view_cell_layout_reorder;
1246 }
1247
1248
1249
1250 static void
exo_icon_view_init(ExoIconView * icon_view)1251 exo_icon_view_init (ExoIconView *icon_view)
1252 {
1253 icon_view->priv = EXO_ICON_VIEW_GET_PRIVATE (icon_view);
1254
1255 icon_view->priv->selection_mode = GTK_SELECTION_SINGLE;
1256 icon_view->priv->pressed_button = -1;
1257 icon_view->priv->press_start_x = -1;
1258 icon_view->priv->press_start_y = -1;
1259 icon_view->priv->text_column = -1;
1260 icon_view->priv->markup_column = -1;
1261 icon_view->priv->pixbuf_column = -1;
1262 icon_view->priv->text_cell = -1;
1263 icon_view->priv->pixbuf_cell = -1;
1264
1265 gtk_widget_set_can_focus (GTK_WIDGET (icon_view), TRUE);
1266
1267 #if !GTK_CHECK_VERSION(3, 0, 0)
1268 exo_icon_view_set_adjustments (icon_view, NULL, NULL);
1269 #endif
1270
1271 icon_view->priv->cursor_cell = -1;
1272
1273 icon_view->priv->orientation = GTK_ORIENTATION_VERTICAL;
1274
1275 icon_view->priv->columns = -1;
1276 icon_view->priv->item_width = -1;
1277 icon_view->priv->row_spacing = 6;
1278 icon_view->priv->column_spacing = 6;
1279 icon_view->priv->margin = 6;
1280
1281 icon_view->priv->enable_search = TRUE;
1282 icon_view->priv->search_column = -1;
1283 icon_view->priv->search_equal_func = exo_icon_view_search_equal_func;
1284 icon_view->priv->search_position_func = exo_icon_view_search_position_func;
1285
1286 icon_view->priv->flags = EXO_ICON_VIEW_DRAW_KEYFOCUS;
1287 }
1288
1289
1290
1291 static void
exo_icon_view_dispose(GObject * object)1292 exo_icon_view_dispose (GObject *object)
1293 {
1294 ExoIconView *icon_view = EXO_ICON_VIEW (object);
1295
1296 /* cancel any pending search timeout */
1297 if (G_UNLIKELY (icon_view->priv->search_timeout_id != 0))
1298 g_source_remove (icon_view->priv->search_timeout_id);
1299
1300 /* destroy the interactive search dialog */
1301 if (G_UNLIKELY (icon_view->priv->search_window != NULL))
1302 {
1303 gtk_widget_destroy (icon_view->priv->search_window);
1304 icon_view->priv->search_entry = NULL;
1305 icon_view->priv->search_window = NULL;
1306 }
1307
1308 /* drop search equal and position functions (if any) */
1309 exo_icon_view_set_search_equal_func (icon_view, NULL, NULL, NULL);
1310 exo_icon_view_set_search_position_func (icon_view, NULL, NULL, NULL);
1311
1312 /* reset the drag dest item */
1313 exo_icon_view_set_drag_dest_item (icon_view, NULL, EXO_ICON_VIEW_NO_DROP);
1314
1315 /* drop the scroll to path (if any) */
1316 if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
1317 {
1318 gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
1319 icon_view->priv->scroll_to_path = NULL;
1320 }
1321
1322 /* reset the model (also stops any active editing) */
1323 exo_icon_view_set_model (icon_view, NULL);
1324
1325 /* drop the scroll timer */
1326 remove_scroll_timeout (icon_view);
1327
1328 (*G_OBJECT_CLASS (exo_icon_view_parent_class)->dispose) (object);
1329 }
1330
1331
1332
1333 static void
exo_icon_view_finalize(GObject * object)1334 exo_icon_view_finalize (GObject *object)
1335 {
1336 ExoIconView *icon_view = EXO_ICON_VIEW (object);
1337
1338 /* drop the scroll adjustments */
1339 g_object_unref (G_OBJECT (icon_view->priv->hadjustment));
1340 g_object_unref (G_OBJECT (icon_view->priv->vadjustment));
1341
1342 /* drop the cell renderers */
1343 exo_icon_view_cell_layout_clear (GTK_CELL_LAYOUT (icon_view));
1344
1345 /* be sure to cancel the single click timeout */
1346 if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
1347 g_source_remove (icon_view->priv->single_click_timeout_id);
1348
1349 /* kill the layout idle source (it's important to have this last!) */
1350 if (G_UNLIKELY (icon_view->priv->layout_idle_id != 0))
1351 g_source_remove (icon_view->priv->layout_idle_id);
1352
1353 (*G_OBJECT_CLASS (exo_icon_view_parent_class)->finalize) (object);
1354 }
1355
1356
1357 static void
exo_icon_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1358 exo_icon_view_get_property (GObject *object,
1359 guint prop_id,
1360 GValue *value,
1361 GParamSpec *pspec)
1362 {
1363 const ExoIconViewPrivate *priv = EXO_ICON_VIEW (object)->priv;
1364
1365 switch (prop_id)
1366 {
1367 case PROP_COLUMN_SPACING:
1368 g_value_set_int (value, priv->column_spacing);
1369 break;
1370
1371 case PROP_COLUMNS:
1372 g_value_set_int (value, priv->columns);
1373 break;
1374
1375 case PROP_ENABLE_SEARCH:
1376 g_value_set_boolean (value, priv->enable_search);
1377 break;
1378
1379 case PROP_ITEM_WIDTH:
1380 g_value_set_int (value, priv->item_width);
1381 break;
1382
1383 case PROP_MARGIN:
1384 g_value_set_int (value, priv->margin);
1385 break;
1386
1387 case PROP_MARKUP_COLUMN:
1388 g_value_set_int (value, priv->markup_column);
1389 break;
1390
1391 case PROP_MODEL:
1392 g_value_set_object (value, priv->model);
1393 break;
1394
1395 case PROP_ORIENTATION:
1396 g_value_set_enum (value, priv->orientation);
1397 break;
1398
1399 case PROP_PIXBUF_COLUMN:
1400 g_value_set_int (value, priv->pixbuf_column);
1401 break;
1402
1403 case PROP_REORDERABLE:
1404 g_value_set_boolean (value, priv->reorderable);
1405 break;
1406
1407 case PROP_ROW_SPACING:
1408 g_value_set_int (value, priv->row_spacing);
1409 break;
1410
1411 case PROP_SEARCH_COLUMN:
1412 g_value_set_int (value, priv->search_column);
1413 break;
1414
1415 case PROP_SELECTION_MODE:
1416 g_value_set_enum (value, priv->selection_mode);
1417 break;
1418
1419 case PROP_SINGLE_CLICK:
1420 g_value_set_boolean (value, priv->single_click);
1421 break;
1422
1423 case PROP_SINGLE_CLICK_TIMEOUT:
1424 g_value_set_uint (value, priv->single_click_timeout);
1425 break;
1426
1427 case PROP_SPACING:
1428 g_value_set_int (value, priv->spacing);
1429 break;
1430
1431 case PROP_TEXT_COLUMN:
1432 g_value_set_int (value, priv->text_column);
1433 break;
1434
1435 case PROP_LAYOUT_MODE:
1436 g_value_set_enum (value, priv->layout_mode);
1437 break;
1438
1439 #if GTK_CHECK_VERSION(3, 0, 0)
1440 case PROP_HADJUSTMENT:
1441 g_value_set_object (value, priv->hadjustment);
1442 break;
1443 case PROP_VADJUSTMENT:
1444 g_value_set_object (value, priv->vadjustment);
1445 break;
1446 case PROP_HSCROLL_POLICY:
1447 g_value_set_enum (value, priv->hscroll_policy);
1448 break;
1449 case PROP_VSCROLL_POLICY:
1450 g_value_set_enum (value, priv->vscroll_policy);
1451 break;
1452 #endif
1453
1454 default:
1455 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1456 break;
1457 }
1458 }
1459
1460
1461
1462 static void
exo_icon_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1463 exo_icon_view_set_property (GObject *object,
1464 guint prop_id,
1465 const GValue *value,
1466 GParamSpec *pspec)
1467 {
1468 ExoIconView *icon_view = EXO_ICON_VIEW (object);
1469
1470 switch (prop_id)
1471 {
1472 case PROP_COLUMN_SPACING:
1473 exo_icon_view_set_column_spacing (icon_view, g_value_get_int (value));
1474 break;
1475
1476 case PROP_COLUMNS:
1477 exo_icon_view_set_columns (icon_view, g_value_get_int (value));
1478 break;
1479
1480 case PROP_ENABLE_SEARCH:
1481 exo_icon_view_set_enable_search (icon_view, g_value_get_boolean (value));
1482 break;
1483
1484 case PROP_ITEM_WIDTH:
1485 exo_icon_view_set_item_width (icon_view, g_value_get_int (value));
1486 break;
1487
1488 case PROP_MARGIN:
1489 exo_icon_view_set_margin (icon_view, g_value_get_int (value));
1490 break;
1491
1492 case PROP_MODEL:
1493 exo_icon_view_set_model (icon_view, g_value_get_object (value));
1494 break;
1495
1496 case PROP_ORIENTATION:
1497 exo_icon_view_set_orientation (icon_view, g_value_get_enum (value));
1498 break;
1499
1500 case PROP_REORDERABLE:
1501 exo_icon_view_set_reorderable (icon_view, g_value_get_boolean (value));
1502 break;
1503
1504 case PROP_ROW_SPACING:
1505 exo_icon_view_set_row_spacing (icon_view, g_value_get_int (value));
1506 break;
1507
1508 case PROP_SEARCH_COLUMN:
1509 exo_icon_view_set_search_column (icon_view, g_value_get_int (value));
1510 break;
1511
1512 case PROP_SELECTION_MODE:
1513 exo_icon_view_set_selection_mode (icon_view, g_value_get_enum (value));
1514 break;
1515
1516 case PROP_SINGLE_CLICK:
1517 exo_icon_view_set_single_click (icon_view, g_value_get_boolean (value));
1518 break;
1519
1520 case PROP_SINGLE_CLICK_TIMEOUT:
1521 exo_icon_view_set_single_click_timeout (icon_view, g_value_get_uint (value));
1522 break;
1523
1524 case PROP_SPACING:
1525 exo_icon_view_set_spacing (icon_view, g_value_get_int (value));
1526 break;
1527
1528 case PROP_LAYOUT_MODE:
1529 exo_icon_view_set_layout_mode (icon_view, g_value_get_enum (value));
1530 break;
1531
1532 #if GTK_CHECK_VERSION(3, 0, 0)
1533 case PROP_HADJUSTMENT:
1534 exo_icon_view_set_hadjustment (icon_view, g_value_get_object (value));
1535 break;
1536 case PROP_VADJUSTMENT:
1537 exo_icon_view_set_vadjustment (icon_view, g_value_get_object (value));
1538 break;
1539 case PROP_HSCROLL_POLICY:
1540 icon_view->priv->hscroll_policy = g_value_get_enum (value);
1541 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
1542 break;
1543 case PROP_VSCROLL_POLICY:
1544 icon_view->priv->vscroll_policy = g_value_get_enum (value);
1545 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
1546 break;
1547 #endif
1548
1549 default:
1550 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1551 break;
1552 }
1553 }
1554
1555
1556
1557 static void
exo_icon_view_realize(GtkWidget * widget)1558 exo_icon_view_realize (GtkWidget *widget)
1559 {
1560 ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1561 GdkWindow *window;
1562 #if GTK_CHECK_VERSION(3, 0, 0)
1563 GtkStyleContext *style;
1564 #else
1565 GtkStyle *style;
1566 #endif
1567 GdkWindowAttr attributes;
1568 GtkAllocation allocation;
1569 gint attributes_mask;
1570
1571 #if GTK_CHECK_VERSION(2, 20, 0)
1572 gtk_widget_set_realized (widget, TRUE);
1573 #else
1574 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1575 #endif
1576
1577 /* Allocate the clipping window */
1578 gtk_widget_get_allocation (widget, &allocation);
1579 attributes.window_type = GDK_WINDOW_CHILD;
1580 attributes.x = allocation.x;
1581 attributes.y = allocation.y;
1582 attributes.width = allocation.width;
1583 attributes.height = allocation.height;
1584 attributes.wclass = GDK_INPUT_OUTPUT;
1585 attributes.visual = gtk_widget_get_visual (widget);
1586 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1587 #if GTK_CHECK_VERSION(3, 0, 0)
1588 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1589 #else
1590 attributes.colormap = gtk_widget_get_colormap (widget);
1591 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1592 #endif
1593 window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1594 gtk_widget_set_window (widget, window);
1595 gdk_window_set_user_data (window, widget);
1596
1597 /* Allocate the icons window */
1598 attributes.x = 0;
1599 attributes.y = 0;
1600 attributes.width = MAX (priv->width, allocation.width);
1601 attributes.height = MAX (priv->height, allocation.height);
1602 attributes.event_mask = GDK_EXPOSURE_MASK
1603 | GDK_SCROLL_MASK
1604 | GDK_POINTER_MOTION_MASK
1605 | GDK_BUTTON_PRESS_MASK
1606 | GDK_BUTTON_RELEASE_MASK
1607 | GDK_KEY_PRESS_MASK
1608 | GDK_KEY_RELEASE_MASK
1609 | gtk_widget_get_events (widget);
1610 priv->bin_window = gdk_window_new (window, &attributes, attributes_mask);
1611 gdk_window_set_user_data (priv->bin_window, widget);
1612
1613 /* Attach style/background */
1614 #if GTK_CHECK_VERSION(3, 0, 0)
1615 style = gtk_widget_get_style_context (widget);
1616 gtk_style_context_set_background (style, priv->bin_window);
1617 gtk_style_context_set_background (style, window);
1618 #else
1619 style = gtk_style_attach (gtk_widget_get_style (widget), window);
1620 gtk_widget_set_style (widget, style);
1621 gdk_window_set_background (priv->bin_window, &style->base[gtk_widget_get_state(widget)]);
1622 gdk_window_set_background (window, &style->base[gtk_widget_get_state(widget)]);
1623 #endif
1624
1625 /* map the icons window */
1626 gdk_window_show (priv->bin_window);
1627 }
1628
1629
1630
1631 static void
exo_icon_view_unrealize(GtkWidget * widget)1632 exo_icon_view_unrealize (GtkWidget *widget)
1633 {
1634 ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1635
1636 /* drop the icons window */
1637 gdk_window_set_user_data (priv->bin_window, NULL);
1638 gdk_window_destroy (priv->bin_window);
1639 priv->bin_window = NULL;
1640
1641 /* let GtkWidget destroy children and widget->window */
1642 if (GTK_WIDGET_CLASS (exo_icon_view_parent_class)->unrealize)
1643 (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->unrealize) (widget);
1644 }
1645
1646
1647 #if GTK_CHECK_VERSION(3, 0, 0)
exo_icon_view_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)1648 static void exo_icon_view_state_flags_changed (GtkWidget *widget,
1649 GtkStateFlags previous_state)
1650 #else
1651 static void exo_icon_view_state_changed (GtkWidget *widget,
1652 GtkStateType previous_state)
1653 #endif
1654 {
1655 ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1656 GdkWindow *window = gtk_widget_get_window (widget);
1657
1658 if (gtk_widget_get_realized (widget))
1659 {
1660 #if GTK_CHECK_VERSION(3, 0, 0)
1661 GtkStyleContext *style = gtk_widget_get_style_context (widget);
1662 gtk_style_context_save (style);
1663 gtk_style_context_add_class (style, GTK_STYLE_CLASS_VIEW);
1664 gtk_style_context_set_background (style, priv->bin_window);
1665 gtk_style_context_set_background (style, window);
1666 gtk_style_context_restore (style);
1667 #else
1668 GtkStyle *style = gtk_widget_get_style (widget);
1669 gdk_window_set_background (priv->bin_window, &style->base[gtk_widget_get_state(widget)]);
1670 gdk_window_set_background (window, &style->base[gtk_widget_get_state(widget)]);
1671 #endif
1672 }
1673
1674 gtk_widget_queue_draw (widget);
1675 }
1676
1677
1678 static void
exo_icon_view_size_request(GtkWidget * widget,GtkRequisition * requisition)1679 exo_icon_view_size_request (GtkWidget *widget,
1680 GtkRequisition *requisition)
1681 {
1682 const ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1683 ExoIconViewChild *child;
1684 GtkRequisition child_requisition;
1685 GList *lp;
1686
1687 /* well, this is easy */
1688 requisition->width = priv->width;
1689 requisition->height = priv->height;
1690
1691 /* handle the child widgets */
1692 for (lp = priv->children; lp != NULL; lp = lp->next)
1693 {
1694 child = lp->data;
1695 if (gtk_widget_get_visible (child->widget))
1696 #if GTK_CHECK_VERSION(3, 0, 0)
1697 gtk_widget_get_preferred_size (child->widget, NULL, &child_requisition);
1698 #else
1699 gtk_widget_size_request (child->widget, &child_requisition);
1700 #endif
1701 }
1702 }
1703
1704 #if GTK_CHECK_VERSION(3, 0, 0)
1705 static void
exo_icon_view_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)1706 exo_icon_view_get_preferred_width (GtkWidget *widget,
1707 gint *minimal_width,
1708 gint *natural_width)
1709 {
1710 GtkRequisition requisition;
1711
1712 exo_icon_view_size_request (widget, &requisition);
1713
1714 *minimal_width = *natural_width = requisition.width;
1715 }
1716
1717 static void
exo_icon_view_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)1718 exo_icon_view_get_preferred_height (GtkWidget *widget,
1719 gint *minimal_height,
1720 gint *natural_height)
1721 {
1722 GtkRequisition requisition;
1723
1724 exo_icon_view_size_request (widget, &requisition);
1725
1726 *minimal_height = *natural_height = requisition.height;
1727 }
1728 #endif
1729
1730
1731 static void
exo_icon_view_allocate_children(ExoIconView * icon_view)1732 exo_icon_view_allocate_children (ExoIconView *icon_view)
1733 {
1734 const ExoIconViewPrivate *priv = icon_view->priv;
1735 const ExoIconViewChild *child;
1736 GtkAllocation allocation;
1737 const GList *lp;
1738 gint focus_line_width;
1739 gint focus_padding;
1740
1741 for (lp = priv->children; lp != NULL; lp = lp->next)
1742 {
1743 child = EXO_ICON_VIEW_CHILD (lp->data);
1744
1745 /* totally ignore our child's requisition */
1746 if (child->cell < 0)
1747 allocation = child->item->area;
1748 else
1749 allocation = child->item->box[child->cell];
1750
1751 /* increase the item area by focus width/padding */
1752 gtk_widget_style_get (GTK_WIDGET (icon_view), "focus-line-width", &focus_line_width, "focus-padding", &focus_padding, NULL);
1753 allocation.x = MAX (0, allocation.x - (focus_line_width + focus_padding));
1754 allocation.y = MAX (0, allocation.y - (focus_line_width + focus_padding));
1755 allocation.width = MIN (priv->width - allocation.x, allocation.width + 2 * (focus_line_width + focus_padding));
1756 allocation.height = MIN (priv->height - allocation.y, allocation.height + 2 * (focus_line_width + focus_padding));
1757
1758 /* allocate the area to the child */
1759 gtk_widget_size_allocate (child->widget, &allocation);
1760 }
1761 }
1762
1763
1764
1765 static void
exo_icon_view_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1766 exo_icon_view_size_allocate (GtkWidget *widget,
1767 GtkAllocation *allocation)
1768 {
1769 GtkAdjustment *hadjustment;
1770 GtkAdjustment *vadjustment;
1771 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
1772
1773 /* FIXME: gtk 3.4.2 calls this with weird values sometimes.
1774 Don't know if that is a bug of gtk or something else.
1775 Since that will break the view a workaround is required. */
1776 if (allocation->x < 0)
1777 allocation->x = 0;
1778 if (allocation->y < 0)
1779 allocation->y = 0;
1780
1781 /* apply the new size allocation */
1782 gtk_widget_set_allocation (widget, allocation);
1783
1784 /* move/resize the clipping window, the icons window
1785 * will be handled by exo_icon_view_layout().
1786 */
1787 if (gtk_widget_get_realized (widget))
1788 gdk_window_move_resize (gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height);
1789
1790 /* layout the items */
1791 exo_icon_view_layout (icon_view);
1792
1793 /* allocate space to the widgets (editing) */
1794 exo_icon_view_allocate_children (icon_view);
1795
1796 /* update the horizontal scroll adjustment accordingly */
1797 hadjustment = icon_view->priv->hadjustment;
1798 gtk_adjustment_set_page_size(hadjustment, allocation->width);
1799 gtk_adjustment_set_page_increment(hadjustment, allocation->width * 0.9);
1800 gtk_adjustment_set_step_increment(hadjustment, allocation->width * 0.1);
1801 gtk_adjustment_set_lower(hadjustment, 0);
1802 gtk_adjustment_set_upper(hadjustment, MAX (allocation->width, icon_view->priv->width));
1803 if (gtk_adjustment_get_value(hadjustment) > gtk_adjustment_get_upper(hadjustment) - gtk_adjustment_get_page_size(hadjustment))
1804 gtk_adjustment_set_value (hadjustment, MAX (0, gtk_adjustment_get_upper(hadjustment) - gtk_adjustment_get_page_size(hadjustment)));
1805
1806 /* update the vertical scroll adjustment accordingly */
1807 vadjustment = icon_view->priv->vadjustment;
1808 gtk_adjustment_set_page_size(vadjustment, allocation->height);
1809 gtk_adjustment_set_page_increment(vadjustment, allocation->height * 0.9);
1810 gtk_adjustment_set_step_increment(vadjustment, allocation->height * 0.1);
1811 gtk_adjustment_set_lower(vadjustment, 0);
1812 gtk_adjustment_set_upper(vadjustment, MAX (allocation->height, icon_view->priv->height));
1813 if (gtk_adjustment_get_value(vadjustment) > gtk_adjustment_get_upper(vadjustment) - gtk_adjustment_get_page_size(vadjustment))
1814 gtk_adjustment_set_value (vadjustment, MAX (0, gtk_adjustment_get_upper(vadjustment) - gtk_adjustment_get_page_size(vadjustment)));
1815
1816 /* we need to emit "changed" ourselves */
1817 gtk_adjustment_changed (hadjustment);
1818 gtk_adjustment_changed (vadjustment);
1819 #if GTK_CHECK_VERSION(3, 0, 0)
1820 g_object_notify (G_OBJECT (icon_view), "hadjustment");
1821 g_object_notify (G_OBJECT (icon_view), "vadjustment");
1822 #endif
1823 }
1824
1825
1826
1827 static void
exo_icon_view_style_set(GtkWidget * widget,GtkStyle * previous_style)1828 exo_icon_view_style_set (GtkWidget *widget,
1829 GtkStyle *previous_style)
1830 {
1831 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
1832 GtkStyle *style;
1833
1834 /* let GtkWidget do its work */
1835 (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->style_set) (widget, previous_style);
1836
1837 /* apply the new style for the bin_window if we're realized */
1838 style = gtk_widget_get_style (widget);
1839 if (gtk_widget_get_realized (widget))
1840 gdk_window_set_background (icon_view->priv->bin_window, &style->base[gtk_widget_get_state(widget)]);
1841 }
1842
1843
1844
1845 static gboolean
1846 #if GTK_CHECK_VERSION(3, 0, 0)
exo_icon_view_draw(GtkWidget * widget,cairo_t * cr)1847 exo_icon_view_draw (GtkWidget *widget,
1848 cairo_t *cr)
1849 #else
1850 exo_icon_view_expose_event (GtkWidget *widget,
1851 GdkEventExpose *event)
1852 #endif
1853 {
1854 ExoIconViewDropPosition dest_pos;
1855 ExoIconViewPrivate *priv = EXO_ICON_VIEW (widget)->priv;
1856 ExoIconViewItem *dest_item = NULL;
1857 ExoIconViewItem *item;
1858 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
1859 GtkTreePath *path;
1860 GdkRectangle rubber_rect;
1861 GdkRectangle rect;
1862 const GList *lp;
1863 gint dest_index = -1;
1864 #if !GTK_CHECK_VERSION(3, 0, 0)
1865 GdkColor *fill_color_gdk;
1866 guchar fill_color_alpha = 0;
1867 gboolean rtl;
1868 gint event_area_last;
1869 GdkRectangle event_area;
1870 cairo_t *cr;
1871 GtkStyle *style;
1872
1873 /* verify that the expose happened on the icon window */
1874 if (G_UNLIKELY (event->window != priv->bin_window))
1875 return FALSE;
1876 #else
1877 GtkStyleContext *style;
1878
1879 if (!gtk_cairo_should_draw_window (cr, priv->bin_window))
1880 return FALSE;
1881 #endif
1882
1883 /* don't handle expose if the layout isn't done yet; the layout
1884 * method will schedule a redraw when done.
1885 */
1886 if (G_UNLIKELY (priv->layout_idle_id != 0))
1887 return FALSE;
1888
1889 /* scroll to the previously remembered path (if any) */
1890 if (G_UNLIKELY (priv->scroll_to_path != NULL))
1891 {
1892 /* grab the path from the reference and invalidate the reference */
1893 path = gtk_tree_row_reference_get_path (priv->scroll_to_path);
1894 gtk_tree_row_reference_free (priv->scroll_to_path);
1895 priv->scroll_to_path = NULL;
1896
1897 /* check if the reference was still valid */
1898 if (G_LIKELY (path != NULL))
1899 {
1900 /* try to scroll again */
1901 exo_icon_view_scroll_to_path (icon_view, path,
1902 priv->scroll_to_use_align,
1903 priv->scroll_to_row_align,
1904 priv->scroll_to_col_align);
1905
1906 /* release the path */
1907 gtk_tree_path_free (path);
1908 }
1909 }
1910
1911 /* check if we need to draw a drag indicator */
1912 exo_icon_view_get_drag_dest_item (icon_view, &path, &dest_pos);
1913 if (G_UNLIKELY (path != NULL))
1914 {
1915 dest_index = gtk_tree_path_get_indices (path)[0];
1916 gtk_tree_path_free (path);
1917 }
1918
1919 #if GTK_CHECK_VERSION(3, 0, 0)
1920 cairo_save (cr);
1921 gtk_cairo_transform_to_window (cr, widget, priv->bin_window);
1922 #else
1923 rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
1924 event_area = event->area;
1925
1926 /* determine the last interesting coordinate (depending on the layout mode) */
1927 event_area_last = (priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS)
1928 ? event_area.y + event_area.height
1929 : event_area.x + event_area.width;
1930 #endif
1931
1932 /* paint all items that are affected by the expose event */
1933 for (lp = priv->items; lp != NULL; lp = lp->next)
1934 {
1935 /* check if this item is in the visible area */
1936 item = EXO_ICON_VIEW_ITEM (lp->data);
1937 #if !GTK_CHECK_VERSION(3, 0, 0)
1938 if (G_LIKELY (priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
1939 {
1940 if (item->area.y > event_area_last)
1941 break;
1942 else if (item->area.y + item->area.height < event_area.y)
1943 continue;
1944 }
1945 else if (rtl)
1946 {
1947 if (item->area.x > event_area_last)
1948 continue;
1949 else if (item->area.x + item->area.width < event_area.x)
1950 break;
1951 }
1952 else
1953 {
1954 if (item->area.x > event_area_last)
1955 break;
1956 else if (item->area.x + item->area.width < event_area.x)
1957 continue;
1958 }
1959
1960 /* check if this item needs an update */
1961 if (G_LIKELY (gdk_region_rect_in (event->region, &item->area) != GDK_OVERLAP_RECTANGLE_OUT))
1962 {
1963 exo_icon_view_paint_item (icon_view, item, &event_area, event->window, item->area.x, item->area.y, TRUE);
1964 #else
1965 cairo_save (cr);
1966 cairo_rectangle (cr, item->area.x, item->area.y, item->area.width, item->area.height);
1967 cairo_clip (cr);
1968
1969 if (gdk_cairo_get_clip_rectangle (cr, NULL))
1970 {
1971 exo_icon_view_paint_item (icon_view, item, &item->area, cr, item->area.x, item->area.y, TRUE);
1972 #endif
1973 if (G_UNLIKELY (dest_index == item->index))
1974 dest_item = item;
1975 }
1976 #if GTK_CHECK_VERSION(3, 0, 0)
1977 cairo_restore (cr);
1978 #endif
1979 }
1980
1981 if (G_UNLIKELY (dest_item != NULL || priv->doing_rubberband))
1982 #if GTK_CHECK_VERSION(3, 0, 0)
1983 style = gtk_widget_get_style_context (widget);
1984 #else
1985 style = gtk_widget_get_style (widget);
1986 #endif
1987
1988 /* draw the drag indicator */
1989 if (G_UNLIKELY (dest_item != NULL))
1990 {
1991 switch (dest_pos)
1992 {
1993 case EXO_ICON_VIEW_DROP_INTO:
1994 rect = dest_item->area;
1995 break;
1996 case GTK_ICON_VIEW_DROP_ABOVE:
1997 rect.x = dest_item->area.x;
1998 rect.y = dest_item->area.y - 1;
1999 rect.width = dest_item->area.width;
2000 rect.height = 2;
2001 break;
2002 case GTK_ICON_VIEW_DROP_LEFT:
2003 rect.x = dest_item->area.x - 1;
2004 rect.y = dest_item->area.y;
2005 rect.width = 2;
2006 rect.height = dest_item->area.height;
2007 break;
2008 case GTK_ICON_VIEW_DROP_BELOW:
2009 rect.x = dest_item->area.x;
2010 rect.y = dest_item->area.y + dest_item->area.height - 1;
2011 rect.width = dest_item->area.width;
2012 rect.height = 2;
2013 break;
2014 case GTK_ICON_VIEW_DROP_RIGHT:
2015 rect.x = dest_item->area.x + dest_item->area.width - 1;
2016 rect.y = dest_item->area.y;
2017 rect.width = 2;
2018 rect.height = dest_item->area.height;
2019 case EXO_ICON_VIEW_NO_DROP:
2020 rect.x = rect.y = rect.width = rect.height = 0;
2021 break;
2022
2023 default:
2024 g_assert_not_reached ();
2025 }
2026 #if GTK_CHECK_VERSION(3, 0, 0)
2027 gtk_render_focus (style, cr,
2028 #else
2029 gtk_paint_focus (style, priv->bin_window,
2030 gtk_widget_get_state (widget), NULL, widget,
2031 "iconview-drop-indicator",
2032 #endif
2033 rect.x, rect.y, rect.width, rect.height);
2034 }
2035
2036 /* draw the rubberband border */
2037 if (G_UNLIKELY (priv->doing_rubberband))
2038 {
2039 /* calculate the rubberband area */
2040 rubber_rect.x = MIN (priv->rubberband_x_1, priv->rubberband_x2);
2041 rubber_rect.y = MIN (priv->rubberband_y_1, priv->rubberband_y2);
2042 rubber_rect.width = ABS (priv->rubberband_x_1 - priv->rubberband_x2) + 1;
2043 rubber_rect.height = ABS (priv->rubberband_y_1 - priv->rubberband_y2) + 1;
2044
2045 #if !GTK_CHECK_VERSION(3, 0, 0)
2046 if (gdk_rectangle_intersect (&rubber_rect, &event_area, &rect))
2047 {
2048 cr = gdk_cairo_create (event->window);
2049 gtk_widget_style_get (widget,
2050 "selection-box-color", &fill_color_gdk,
2051 "selection-box-alpha", &fill_color_alpha,
2052 NULL);
2053 if (!fill_color_gdk)
2054 {
2055 style = gtk_widget_get_style (widget);
2056 fill_color_gdk = gdk_color_copy (&style->base[GTK_STATE_SELECTED]);
2057 }
2058
2059 /* draw the area */
2060 cairo_set_source_rgba (cr,
2061 fill_color_gdk->red / 65535.,
2062 fill_color_gdk->green / 65535.,
2063 fill_color_gdk->blue / 65535.,
2064 fill_color_alpha / 255.);
2065 gdk_cairo_rectangle (cr, &rect);
2066 cairo_clip (cr);
2067 cairo_paint (cr);
2068 /* draw the border */
2069 cairo_set_source_rgb (cr,
2070 fill_color_gdk->red / 65535.,
2071 fill_color_gdk->green / 65535.,
2072 fill_color_gdk->blue / 65535.);
2073 cairo_rectangle (cr, rubber_rect.x + 0.5, rubber_rect.y + 0.5, rubber_rect.width - 1, rubber_rect.height - 1);
2074 cairo_set_line_width (cr, 1);
2075 cairo_stroke (cr);
2076 gdk_color_free (fill_color_gdk);
2077 cairo_destroy (cr);
2078 }
2079 #else
2080 gtk_style_context_save (style);
2081 gtk_style_context_add_class (style, GTK_STYLE_CLASS_RUBBERBAND);
2082
2083 gdk_cairo_rectangle (cr, &rubber_rect);
2084 cairo_clip (cr);
2085
2086 gtk_render_background (style, cr,
2087 rubber_rect.x, rubber_rect.y,
2088 rubber_rect.width, rubber_rect.height);
2089 gtk_render_frame (style, cr,
2090 rubber_rect.x, rubber_rect.y,
2091 rubber_rect.width, rubber_rect.height);
2092
2093 gtk_style_context_restore (style);
2094 #endif
2095 }
2096
2097 /* let the GtkContainer forward the expose event to all children */
2098 #if GTK_CHECK_VERSION(3, 0, 0)
2099 cairo_restore (cr);
2100 (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->draw) (widget, cr);
2101 #else
2102 (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->expose_event) (widget, event);
2103 #endif
2104
2105 return FALSE;
2106 }
2107
2108
2109
2110 static gboolean
2111 rubberband_scroll_timeout (gpointer user_data)
2112 {
2113 GtkAdjustment *adjustment;
2114 ExoIconView *icon_view = EXO_ICON_VIEW (user_data);
2115 gdouble value;
2116
2117 /* ensure that source isn't removed yet */
2118 if(g_source_is_destroyed(g_main_current_source()))
2119 {
2120 return FALSE;
2121 }
2122
2123 /* determine the adjustment for the scroll direction */
2124 adjustment = (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS)
2125 ? icon_view->priv->vadjustment
2126 : icon_view->priv->hadjustment;
2127
2128 /* determine the new scroll value */
2129 value = MIN (gtk_adjustment_get_value(adjustment) + icon_view->priv->scroll_value_diff, gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
2130
2131 /* apply the new value */
2132 gtk_adjustment_set_value (adjustment, value);
2133
2134 /* update the rubberband */
2135 exo_icon_view_update_rubberband (icon_view);
2136
2137 return TRUE;
2138 }
2139
2140
2141 static gboolean
2142 exo_icon_view_motion_notify_event (GtkWidget *widget,
2143 GdkEventMotion *event)
2144 {
2145 ExoIconViewItem *item;
2146 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2147 GdkCursor *cursor;
2148 gint size;
2149 gint abso;
2150 GtkAllocation allocation;
2151
2152 exo_icon_view_maybe_begin_drag (icon_view, event);
2153 gtk_widget_get_allocation (widget, &allocation);
2154
2155 if (icon_view->priv->doing_rubberband)
2156 {
2157 exo_icon_view_update_rubberband (widget);
2158
2159 if (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS)
2160 {
2161 abso = event->y - icon_view->priv->height *
2162 (gtk_adjustment_get_value(icon_view->priv->vadjustment) /
2163 (gtk_adjustment_get_upper(icon_view->priv->vadjustment) -
2164 gtk_adjustment_get_lower(icon_view->priv->vadjustment)));
2165
2166 size = allocation.height;
2167 }
2168 else
2169 {
2170 abso = event->x - icon_view->priv->width *
2171 (gtk_adjustment_get_value(icon_view->priv->hadjustment) /
2172 (gtk_adjustment_get_upper(icon_view->priv->hadjustment) -
2173 gtk_adjustment_get_lower(icon_view->priv->hadjustment)));
2174
2175 size = allocation.width;
2176 }
2177
2178 if (abso < 0 || abso > size)
2179 {
2180 if (abso < 0)
2181 icon_view->priv->scroll_value_diff = abso;
2182 else
2183 icon_view->priv->scroll_value_diff = abso - size;
2184 icon_view->priv->event_last_x = event->x;
2185 icon_view->priv->event_last_y = event->y;
2186
2187 if (icon_view->priv->scroll_timeout_id == 0)
2188 icon_view->priv->scroll_timeout_id = gdk_threads_add_timeout (30, rubberband_scroll_timeout,
2189 icon_view);
2190 }
2191 else
2192 {
2193 remove_scroll_timeout (icon_view);
2194 }
2195 }
2196 else
2197 {
2198 item = exo_icon_view_get_item_at_coords (icon_view, event->x, event->y, TRUE, NULL);
2199 if (item != icon_view->priv->prelit_item)
2200 {
2201 if (G_LIKELY (icon_view->priv->prelit_item != NULL))
2202 exo_icon_view_queue_draw_item (icon_view, icon_view->priv->prelit_item);
2203 icon_view->priv->prelit_item = item;
2204 if (G_LIKELY (item != NULL))
2205 exo_icon_view_queue_draw_item (icon_view, item);
2206
2207 /* check if we are in single click mode right now */
2208 if (G_UNLIKELY (icon_view->priv->single_click))
2209 {
2210 /* display a hand cursor when pointer is above an item */
2211 if (G_LIKELY (item != NULL))
2212 {
2213 /* hand2 seems to be what we should use */
2214 cursor = gdk_cursor_new (GDK_HAND2);
2215 gdk_window_set_cursor (event->window, cursor);
2216 gdk_cursor_unref (cursor);
2217 }
2218 else
2219 {
2220 /* reset the cursor */
2221 gdk_window_set_cursor (event->window, NULL);
2222 }
2223
2224 /* check if autoselection is enabled */
2225 if (G_LIKELY (icon_view->priv->single_click_timeout > 0))
2226 {
2227 /* drop any running timeout */
2228 if (G_LIKELY (icon_view->priv->single_click_timeout_id != 0))
2229 g_source_remove (icon_view->priv->single_click_timeout_id);
2230
2231 /* remember the current event state */
2232 icon_view->priv->single_click_timeout_state = event->state;
2233
2234 /* schedule a new timeout */
2235 icon_view->priv->single_click_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, icon_view->priv->single_click_timeout,
2236 exo_icon_view_single_click_timeout, icon_view,
2237 exo_icon_view_single_click_timeout_destroy);
2238 }
2239 }
2240 }
2241 }
2242
2243 return TRUE;
2244 }
2245
2246
2247
2248 static void
2249 exo_icon_view_remove (GtkContainer *container,
2250 GtkWidget *widget)
2251 {
2252 ExoIconViewChild *child;
2253 ExoIconView *icon_view = EXO_ICON_VIEW (container);
2254 GList *lp;
2255
2256 for (lp = icon_view->priv->children; lp != NULL; lp = lp->next)
2257 {
2258 child = lp->data;
2259 if (G_LIKELY (child->widget == widget))
2260 {
2261 icon_view->priv->children = g_list_delete_link (icon_view->priv->children, lp);
2262 gtk_widget_unparent (widget);
2263 g_slice_free (ExoIconViewChild, child);
2264 return;
2265 }
2266 }
2267 }
2268
2269
2270
2271 static void
2272 exo_icon_view_forall (GtkContainer *container,
2273 gboolean include_internals,
2274 GtkCallback callback,
2275 gpointer callback_data)
2276 {
2277 ExoIconView *icon_view = EXO_ICON_VIEW (container);
2278 GList *lp;
2279
2280 for (lp = icon_view->priv->children; lp != NULL; lp = lp->next)
2281 (*callback) (((ExoIconViewChild *) lp->data)->widget, callback_data);
2282 }
2283
2284 static void
2285 exo_icon_view_item_selected_changed (ExoIconView *icon_view,
2286 ExoIconViewItem *item)
2287 {
2288 AtkObject *obj;
2289 AtkObject *item_obj;
2290
2291 obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view));
2292 if (obj != NULL)
2293 {
2294 item_obj = atk_object_ref_accessible_child (obj, item->index);
2295 if (item_obj != NULL)
2296 {
2297 atk_object_notify_state_change (item_obj, ATK_STATE_SELECTED, item->selected);
2298 g_object_unref (item_obj);
2299 }
2300 }
2301 }
2302
2303
2304 static void
2305 exo_icon_view_item_activate_cell (ExoIconView *icon_view,
2306 ExoIconViewItem *item,
2307 ExoIconViewCellInfo *info,
2308 GdkEvent *event)
2309 {
2310 GtkCellRendererMode mode;
2311 GdkRectangle cell_area;
2312 GtkTreePath *path;
2313 gboolean visible;
2314 gchar *path_string;
2315
2316 exo_icon_view_set_cell_data (icon_view, item);
2317
2318 g_object_get (G_OBJECT (info->cell), "visible", &visible, "mode", &mode, NULL);
2319
2320 if (G_UNLIKELY (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE))
2321 {
2322 exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
2323
2324 path = gtk_tree_path_new_from_indices (item->index, -1);
2325 path_string = gtk_tree_path_to_string (path);
2326 gtk_tree_path_free (path);
2327
2328 gtk_cell_renderer_activate (info->cell, event, GTK_WIDGET (icon_view), path_string, &cell_area, &cell_area, 0);
2329
2330 g_free (path_string);
2331 }
2332 }
2333
2334
2335
2336 static void
2337 exo_icon_view_put (ExoIconView *icon_view,
2338 GtkWidget *widget,
2339 ExoIconViewItem *item,
2340 gint cell)
2341 {
2342 ExoIconViewChild *child;
2343
2344 /* allocate the new child */
2345 child = g_slice_new (ExoIconViewChild);
2346 child->widget = widget;
2347 child->item = item;
2348 child->cell = cell;
2349
2350 /* hook up the child */
2351 icon_view->priv->children = g_list_append (icon_view->priv->children, child);
2352
2353 /* setup the parent for the child */
2354 if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
2355 gtk_widget_set_parent_window (child->widget, icon_view->priv->bin_window);
2356 gtk_widget_set_parent (widget, GTK_WIDGET (icon_view));
2357 }
2358
2359
2360
2361 static void
2362 exo_icon_view_remove_widget (GtkCellEditable *editable,
2363 ExoIconView *icon_view)
2364 {
2365 ExoIconViewItem *item;
2366 GList *lp;
2367
2368 if (G_LIKELY (icon_view->priv->edited_item != NULL))
2369 {
2370 item = icon_view->priv->edited_item;
2371 icon_view->priv->edited_item = NULL;
2372 icon_view->priv->editable = NULL;
2373
2374 for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
2375 ((ExoIconViewCellInfo *) lp->data)->editing = FALSE;
2376
2377 if (gtk_widget_has_focus (GTK_WIDGET (editable)))
2378 gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2379
2380 g_signal_handlers_disconnect_by_func (editable, exo_icon_view_remove_widget, icon_view);
2381 gtk_container_remove (GTK_CONTAINER (icon_view), GTK_WIDGET (editable));
2382
2383 exo_icon_view_queue_draw_item (icon_view, item);
2384 }
2385 }
2386
2387
2388
2389 static void
2390 exo_icon_view_start_editing (ExoIconView *icon_view,
2391 ExoIconViewItem *item,
2392 ExoIconViewCellInfo *info,
2393 GdkEvent *event)
2394 {
2395 GtkCellRendererMode mode;
2396 GtkCellEditable *editable;
2397 GdkRectangle cell_area;
2398 GtkTreePath *path;
2399 gboolean visible;
2400 gchar *path_string;
2401
2402 /* setup cell data for the given item */
2403 exo_icon_view_set_cell_data (icon_view, item);
2404
2405 /* check if the cell is visible and editable (given the updated cell data) */
2406 g_object_get (info->cell, "visible", &visible, "mode", &mode, NULL);
2407 if (G_LIKELY (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE))
2408 {
2409 /* draw keyboard focus while editing */
2410 EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
2411
2412 /* determine the cell area */
2413 exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
2414
2415 /* determine the tree path */
2416 path = gtk_tree_path_new_from_indices (item->index, -1);
2417 path_string = gtk_tree_path_to_string (path);
2418 gtk_tree_path_free (path);
2419
2420 /* allocate the editable from the cell renderer */
2421 editable = gtk_cell_renderer_start_editing (info->cell, event, GTK_WIDGET (icon_view), path_string, &cell_area, &cell_area, 0);
2422
2423 /* ugly hack, but works */
2424 if (g_object_class_find_property (G_OBJECT_GET_CLASS (editable), "has-frame") != NULL)
2425 g_object_set (editable, "has-frame", TRUE, NULL);
2426
2427 /* setup the editing widget */
2428 icon_view->priv->edited_item = item;
2429 icon_view->priv->editable = editable;
2430 info->editing = TRUE;
2431
2432 exo_icon_view_put (icon_view, GTK_WIDGET (editable), item, info->position);
2433 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (editable), (GdkEvent *)event);
2434 gtk_widget_grab_focus (GTK_WIDGET (editable));
2435 g_signal_connect (G_OBJECT (editable), "remove-widget", G_CALLBACK (exo_icon_view_remove_widget), icon_view);
2436
2437 /* cleanup */
2438 g_free (path_string);
2439 }
2440 }
2441
2442
2443
2444 static void
2445 exo_icon_view_stop_editing (ExoIconView *icon_view,
2446 gboolean cancel_editing)
2447 {
2448 ExoIconViewItem *item;
2449 GtkCellRenderer *cell = NULL;
2450 GList *lp;
2451
2452 if (icon_view->priv->edited_item == NULL)
2453 return;
2454
2455 /*
2456 * This is very evil. We need to do this, because
2457 * gtk_cell_editable_editing_done may trigger exo_icon_view_row_changed
2458 * later on. If exo_icon_view_row_changed notices
2459 * icon_view->priv->edited_item != NULL, it'll call
2460 * exo_icon_view_stop_editing again. Bad things will happen then.
2461 *
2462 * Please read that again if you intend to modify anything here.
2463 */
2464
2465 item = icon_view->priv->edited_item;
2466 icon_view->priv->edited_item = NULL;
2467
2468 for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
2469 {
2470 ExoIconViewCellInfo *info = lp->data;
2471 if (info->editing)
2472 {
2473 cell = info->cell;
2474 break;
2475 }
2476 }
2477
2478 if (G_UNLIKELY (cell == NULL))
2479 return;
2480
2481 gtk_cell_renderer_stop_editing (cell, cancel_editing);
2482 if (G_LIKELY (!cancel_editing))
2483 gtk_cell_editable_editing_done (icon_view->priv->editable);
2484
2485 icon_view->priv->edited_item = item;
2486
2487 gtk_cell_editable_remove_widget (icon_view->priv->editable);
2488 }
2489
2490
2491
2492 static gboolean
2493 exo_icon_view_button_press_event (GtkWidget *widget,
2494 GdkEventButton *event)
2495 {
2496 ExoIconViewCellInfo *info = NULL;
2497 GtkCellRendererMode mode;
2498 ExoIconViewItem *item;
2499 ExoIconView *icon_view;
2500 GtkTreePath *path;
2501 gboolean dirty = FALSE;
2502 gint cursor_cell;
2503 gpointer drag_data;
2504
2505 icon_view = EXO_ICON_VIEW (widget);
2506
2507 if (event->window != icon_view->priv->bin_window)
2508 return FALSE;
2509
2510 /* the widget can be destroyed by click so let keep reference on it */
2511 g_object_ref(widget);
2512
2513 /* stop any pending "single-click-timeout" */
2514 if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
2515 g_source_remove (icon_view->priv->single_click_timeout_id);
2516
2517 if (G_UNLIKELY (!gtk_widget_has_focus (widget)))
2518 gtk_widget_grab_focus (widget);
2519
2520 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2521 {
2522 if (G_LIKELY (icon_view->priv->dnd_locked))
2523 {
2524 /* re-enable Gtk+ DnD callbacks if they were disabled by a
2525 double click before, otherwise DnD will not work */
2526 drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
2527 if (G_LIKELY (drag_data != NULL))
2528 {
2529 g_signal_handlers_unblock_matched (G_OBJECT (icon_view),
2530 G_SIGNAL_MATCH_DATA,
2531 0, 0, NULL, NULL,
2532 drag_data);
2533 }
2534 icon_view->priv->dnd_locked = FALSE;
2535 }
2536 item = exo_icon_view_get_item_at_coords (icon_view,
2537 event->x, event->y,
2538 TRUE,
2539 &info);
2540 if (item != NULL)
2541 {
2542 g_object_get (info->cell, "mode", &mode, NULL);
2543
2544 if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE ||
2545 mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2546 cursor_cell = g_list_index (icon_view->priv->cell_list, info);
2547 else
2548 cursor_cell = -1;
2549
2550 exo_icon_view_scroll_to_item (icon_view, item);
2551
2552 if (icon_view->priv->selection_mode == GTK_SELECTION_NONE)
2553 {
2554 exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2555 }
2556 else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
2557 (event->state & GDK_SHIFT_MASK))
2558 {
2559 if (!(event->state & GDK_CONTROL_MASK))
2560 exo_icon_view_unselect_all_internal (icon_view);
2561
2562 exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2563 if (!icon_view->priv->anchor_item)
2564 icon_view->priv->anchor_item = item;
2565 else
2566 exo_icon_view_select_all_between (icon_view,
2567 icon_view->priv->anchor_item,
2568 item);
2569 dirty = TRUE;
2570 }
2571 else
2572 {
2573 if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ||
2574 ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) &&
2575 (event->state & GDK_CONTROL_MASK))
2576 {
2577 item->selected = !item->selected;
2578 exo_icon_view_queue_draw_item (icon_view, item);
2579 dirty = TRUE;
2580 }
2581 else
2582 {
2583 if (!item->selected)
2584 {
2585 exo_icon_view_unselect_all_internal (icon_view);
2586
2587 item->selected = TRUE;
2588 exo_icon_view_queue_draw_item (icon_view, item);
2589 dirty = TRUE;
2590 }
2591 }
2592 exo_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2593 icon_view->priv->anchor_item = item;
2594 }
2595
2596 /* Save press to possibly begin a drag */
2597 if (icon_view->priv->pressed_button < 0)
2598 {
2599 icon_view->priv->pressed_button = event->button;
2600 icon_view->priv->press_start_x = event->x;
2601 icon_view->priv->press_start_y = event->y;
2602 }
2603
2604 if (G_LIKELY (icon_view->priv->last_single_clicked == NULL))
2605 icon_view->priv->last_single_clicked = item;
2606
2607 /* cancel the current editing, if it exists */
2608 exo_icon_view_stop_editing (icon_view, TRUE);
2609
2610 if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2611 exo_icon_view_item_activate_cell (icon_view, item, info,
2612 (GdkEvent *)event);
2613 else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2614 exo_icon_view_start_editing (icon_view, item, info,
2615 (GdkEvent *)event);
2616 }
2617 else
2618 {
2619 /* cancel the current editing, if it exists */
2620 exo_icon_view_stop_editing (icon_view, TRUE);
2621
2622 if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE &&
2623 !(event->state & GDK_CONTROL_MASK))
2624 {
2625 dirty = exo_icon_view_unselect_all_internal (icon_view);
2626 }
2627
2628 if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
2629 exo_icon_view_start_rubberbanding (icon_view, event->x, event->y);
2630 }
2631 }
2632 else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
2633 {
2634 /* ignore double-click events in single-click mode */
2635 if (G_LIKELY (!icon_view->priv->single_click))
2636 {
2637 item = exo_icon_view_get_item_at_coords (icon_view,
2638 event->x, event->y,
2639 TRUE,
2640 NULL);
2641 if (G_LIKELY (item != NULL))
2642 {
2643 path = gtk_tree_path_new_from_indices (item->index, -1);
2644 exo_icon_view_item_activated (icon_view, path);
2645 gtk_tree_path_free (path);
2646 /* bug #3615031: don't start DnD by double click */
2647 if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
2648 gtk_widget_get_realized(widget))
2649 {
2650 drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
2651 if (G_LIKELY (drag_data != NULL))
2652 {
2653 g_signal_handlers_block_matched (G_OBJECT (icon_view),
2654 G_SIGNAL_MATCH_DATA,
2655 0, 0, NULL, NULL,
2656 drag_data);
2657 icon_view->priv->dnd_locked = TRUE;
2658 }
2659
2660 }
2661 }
2662 }
2663
2664 icon_view->priv->last_single_clicked = NULL;
2665 icon_view->priv->pressed_button = -1;
2666 }
2667
2668 /* grab focus and stop drawing the keyboard focus indicator on single clicks */
2669 if (G_LIKELY (event->type != GDK_2BUTTON_PRESS && event->type != GDK_3BUTTON_PRESS))
2670 {
2671 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
2672 gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2673 EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
2674 }
2675
2676 if (dirty)
2677 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
2678
2679 /* SF bug #929: we have to drop prelit state to drop tooltip, see text renderer */
2680 icon_view->priv->prelit_item = NULL;
2681
2682 /* release reference that was taken above */
2683 g_object_unref(widget);
2684
2685 return event->button == 1;
2686 }
2687
2688
2689
2690 static gboolean
2691 exo_icon_view_button_release_event (GtkWidget *widget,
2692 GdkEventButton *event)
2693 {
2694 ExoIconViewItem *item;
2695 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2696 GtkTreePath *path;
2697
2698 /* the widget can be destroyed by click so let keep reference on it */
2699 g_object_ref(widget);
2700
2701 if (icon_view->priv->pressed_button == (gint) event->button)
2702 {
2703 /* check if we're in single click mode */
2704 if (G_UNLIKELY (icon_view->priv->single_click && (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == 0))
2705 {
2706 /* determine the item at the mouse coords and check if this is the last single clicked one */
2707 item = exo_icon_view_get_item_at_coords (icon_view, event->x, event->y, TRUE, NULL);
2708 if (G_LIKELY (item != NULL && item == icon_view->priv->last_single_clicked))
2709 {
2710 /* emit an "item-activated" signal for this item */
2711 path = gtk_tree_path_new_from_indices (item->index, -1);
2712 exo_icon_view_item_activated (icon_view, path);
2713 gtk_tree_path_free (path);
2714 }
2715
2716 /* reset the last single clicked item */
2717 icon_view->priv->last_single_clicked = NULL;
2718 }
2719
2720 /* reset the pressed_button state */
2721 icon_view->priv->pressed_button = -1;
2722 }
2723
2724 exo_icon_view_stop_rubberbanding (icon_view);
2725
2726 remove_scroll_timeout (icon_view);
2727
2728 /* release reference that was taken above */
2729 g_object_unref(widget);
2730
2731 return TRUE;
2732 }
2733
2734
2735
2736 static gboolean
2737 exo_icon_view_scroll_event (GtkWidget *widget,
2738 GdkEventScroll *event)
2739 {
2740 GtkAdjustment *adjustment;
2741 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2742 gdouble delta;
2743 gdouble value;
2744
2745 /* we don't care for scroll events in "rows" layout mode, as
2746 * that's completely handled by GtkScrolledWindow.
2747 */
2748 if (icon_view->priv->layout_mode != EXO_ICON_VIEW_LAYOUT_COLS)
2749 return FALSE;
2750
2751 /* also, we don't care for anything but Up/Down, as
2752 * everything else will be handled by GtkScrolledWindow.
2753 */
2754 if (event->direction != GDK_SCROLL_UP && event->direction != GDK_SCROLL_DOWN)
2755 return FALSE;
2756
2757 /* we also don't care for scroll events with Shift/Ctrl/Alt pressed */
2758 if ((event->state & gtk_accelerator_get_default_mod_mask()) != 0)
2759 return FALSE;
2760
2761 /* determine the horizontal adjustment */
2762 adjustment = icon_view->priv->hadjustment;
2763
2764 /* determine the scroll delta */
2765 delta = pow (gtk_adjustment_get_page_size(adjustment), 2.0 / 3.0);
2766 delta = (event->direction == GDK_SCROLL_UP) ? -delta : delta;
2767
2768 /* apply the new adjustment value */
2769 value = CLAMP (gtk_adjustment_get_value(adjustment) + delta, gtk_adjustment_get_lower(adjustment), gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
2770 gtk_adjustment_set_value (adjustment, value);
2771
2772 return TRUE;
2773 }
2774
2775
2776
2777 static gboolean
2778 exo_icon_view_key_press_event (GtkWidget *widget,
2779 GdkEventKey *event)
2780 {
2781 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2782 GdkScreen *screen;
2783 GdkEvent *new_event;
2784 gboolean retval;
2785 gulong popup_menu_id;
2786 gchar *new_text;
2787 gchar *old_text;
2788
2789 /* let the parent class handle the key bindings and stuff */
2790 if ((*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->key_press_event) (widget, event))
2791 return TRUE;
2792
2793 /* 'space' keypress should not start search even if there is no selection */
2794 if (G_UNLIKELY (event->keyval == GDK_KEY_space))
2795 return FALSE;
2796
2797 /* check if typeahead search is enabled */
2798 if (G_UNLIKELY (!icon_view->priv->enable_search))
2799 return FALSE;
2800
2801 exo_icon_view_search_ensure_directory (icon_view);
2802
2803 /* make sure the search window is realized */
2804 gtk_widget_realize (icon_view->priv->search_window);
2805
2806 /* make a copy of the current text */
2807 old_text = gtk_editable_get_chars (GTK_EDITABLE (icon_view->priv->search_entry), 0, -1);
2808
2809 /* make sure we don't accidently popup the context menu */
2810 popup_menu_id = g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "popup-menu", G_CALLBACK (gtk_true), NULL);
2811
2812 /* move the search window offscreen */
2813 screen = gtk_widget_get_screen (GTK_WIDGET (icon_view));
2814 gtk_window_move (GTK_WINDOW (icon_view->priv->search_window),
2815 gdk_screen_get_width (screen) + 1,
2816 gdk_screen_get_height (screen) + 1);
2817 gtk_widget_show (icon_view->priv->search_window);
2818
2819 /* allocate a new event to forward */
2820 new_event = gdk_event_copy ((GdkEvent *) event);
2821 g_object_unref (G_OBJECT (new_event->key.window));
2822 new_event->key.window = g_object_ref (G_OBJECT (gtk_widget_get_window (icon_view->priv->search_entry)));
2823
2824 /* send the event to the search entry. If the "preedit-changed" signal is
2825 * emitted during this event, priv->search_imcontext_changed will be set.
2826 */
2827 icon_view->priv->search_imcontext_changed = FALSE;
2828 retval = gtk_widget_event (icon_view->priv->search_entry, new_event);
2829 gtk_widget_hide (icon_view->priv->search_window);
2830
2831 /* release the temporary event */
2832 gdk_event_free (new_event);
2833
2834 /* disconnect the popup menu prevention */
2835 g_signal_handler_disconnect (G_OBJECT (icon_view->priv->search_entry), popup_menu_id);
2836
2837 /* we check to make sure that the entry tried to handle the,
2838 * and that the text has actually changed.
2839 */
2840 new_text = gtk_editable_get_chars (GTK_EDITABLE (icon_view->priv->search_entry), 0, -1);
2841 retval = retval && (strcmp (new_text, old_text) != 0);
2842 g_free (old_text);
2843 g_free (new_text);
2844
2845 /* if we're in a preedit or the text was modified */
2846 if (icon_view->priv->search_imcontext_changed || retval)
2847 {
2848 if (exo_icon_view_search_start (icon_view, FALSE))
2849 {
2850 gtk_widget_grab_focus (GTK_WIDGET (icon_view));
2851 return TRUE;
2852 }
2853 else
2854 {
2855 gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
2856 return FALSE;
2857 }
2858 }
2859
2860 return FALSE;
2861 }
2862
2863
2864
2865 static gboolean
2866 exo_icon_view_focus_out_event (GtkWidget *widget,
2867 GdkEventFocus *event)
2868 {
2869 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
2870
2871 /* be sure to cancel any single-click timeout */
2872 if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
2873 g_source_remove (icon_view->priv->single_click_timeout_id);
2874
2875 /* reset the cursor if we're still realized */
2876 if (G_LIKELY (icon_view->priv->bin_window != NULL))
2877 gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
2878
2879 /* destroy the interactive search dialog */
2880 if (G_UNLIKELY (icon_view->priv->search_window != NULL))
2881 exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
2882
2883 /* schedule a redraw with the new focus state */
2884 gtk_widget_queue_draw (widget);
2885
2886 return FALSE;
2887 }
2888
2889
2890
2891 static gboolean
2892 exo_icon_view_leave_notify_event (GtkWidget *widget,
2893 GdkEventCrossing *event)
2894 {
2895 /* reset cursor to default */
2896 if (gtk_widget_get_realized (widget))
2897 gdk_window_set_cursor (gtk_widget_get_window(widget), NULL);
2898
2899 /* call the parent's leave_notify_event (if any) */
2900 if (GTK_WIDGET_CLASS (exo_icon_view_parent_class)->leave_notify_event != NULL)
2901 return (*GTK_WIDGET_CLASS (exo_icon_view_parent_class)->leave_notify_event) (widget, event);
2902
2903 /* other signal handlers may be invoked */
2904 return FALSE;
2905 }
2906
2907
2908 static void
2909 exo_icon_view_update_rubberband (gpointer data)
2910 {
2911 ExoIconView *icon_view;
2912 gint x, y;
2913 GdkRectangle old_area;
2914 GdkRectangle new_area;
2915 GdkRectangle common;
2916 #if GTK_CHECK_VERSION(3, 0, 0)
2917 cairo_region_t *invalid_region;
2918 #else
2919 GdkRegion *invalid_region;
2920 #endif
2921
2922 icon_view = EXO_ICON_VIEW (data);
2923
2924 gdk_window_get_device_position (icon_view->priv->bin_window,
2925 gtk_get_current_event_device(), &x, &y, NULL);
2926
2927 x = MAX (x, 0);
2928 y = MAX (y, 0);
2929
2930 old_area.x = MIN (icon_view->priv->rubberband_x_1,
2931 icon_view->priv->rubberband_x2);
2932 old_area.y = MIN (icon_view->priv->rubberband_y_1,
2933 icon_view->priv->rubberband_y2);
2934 old_area.width = ABS (icon_view->priv->rubberband_x2 -
2935 icon_view->priv->rubberband_x_1) + 1;
2936 old_area.height = ABS (icon_view->priv->rubberband_y2 -
2937 icon_view->priv->rubberband_y_1) + 1;
2938
2939 new_area.x = MIN (icon_view->priv->rubberband_x_1, x);
2940 new_area.y = MIN (icon_view->priv->rubberband_y_1, y);
2941 new_area.width = ABS (x - icon_view->priv->rubberband_x_1) + 1;
2942 new_area.height = ABS (y - icon_view->priv->rubberband_y_1) + 1;
2943
2944 #if GTK_CHECK_VERSION(3, 0, 0)
2945 invalid_region = cairo_region_create_rectangle (&old_area);
2946 cairo_region_union_rectangle (invalid_region, &new_area);
2947 #else
2948 invalid_region = gdk_region_rectangle (&old_area);
2949 gdk_region_union_with_rect (invalid_region, &new_area);
2950 #endif
2951
2952 gdk_rectangle_intersect (&old_area, &new_area, &common);
2953 if (common.width > 2 && common.height > 2)
2954 {
2955 #if GTK_CHECK_VERSION(3, 0, 0)
2956 cairo_region_t *common_region;
2957 #else
2958 GdkRegion *common_region;
2959 #endif
2960
2961 /* make sure the border is invalidated */
2962 common.x += 1;
2963 common.y += 1;
2964 common.width -= 2;
2965 common.height -= 2;
2966
2967 #if GTK_CHECK_VERSION(3, 0, 0)
2968 common_region = cairo_region_create_rectangle (&common);
2969
2970 cairo_region_subtract (invalid_region, common_region);
2971 cairo_region_destroy (common_region);
2972 #else
2973 common_region = gdk_region_rectangle (&common);
2974
2975 gdk_region_subtract (invalid_region, common_region);
2976 gdk_region_destroy (common_region);
2977 #endif
2978 }
2979
2980 gdk_window_invalidate_region (icon_view->priv->bin_window, invalid_region, TRUE);
2981
2982 #if GTK_CHECK_VERSION(3, 0, 0)
2983 cairo_region_destroy (invalid_region);
2984 #else
2985 gdk_region_destroy (invalid_region);
2986 #endif
2987
2988 icon_view->priv->rubberband_x2 = x;
2989 icon_view->priv->rubberband_y2 = y;
2990
2991 exo_icon_view_update_rubberband_selection (icon_view);
2992 }
2993
2994
2995 static void
2996 exo_icon_view_start_rubberbanding (ExoIconView *icon_view,
2997 gint x,
2998 gint y)
2999 {
3000 gpointer drag_data;
3001 GList *items;
3002
3003 /* be sure to disable any previously active rubberband */
3004 exo_icon_view_stop_rubberbanding (icon_view);
3005
3006 for (items = icon_view->priv->items; items; items = items->next)
3007 {
3008 ExoIconViewItem *item = items->data;
3009 item->selected_before_rubberbanding = item->selected;
3010 }
3011
3012 icon_view->priv->rubberband_x_1 = x;
3013 icon_view->priv->rubberband_y_1 = y;
3014 icon_view->priv->rubberband_x2 = x;
3015 icon_view->priv->rubberband_y2 = y;
3016
3017 icon_view->priv->doing_rubberband = TRUE;
3018
3019 gtk_grab_add (GTK_WIDGET (icon_view));
3020
3021 /* be sure to disable Gtk+ DnD callbacks, because else rubberbanding will be interrupted */
3022 drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
3023 if (G_LIKELY (drag_data != NULL))
3024 {
3025 g_signal_handlers_block_matched (G_OBJECT (icon_view),
3026 G_SIGNAL_MATCH_DATA,
3027 0, 0, NULL, NULL,
3028 drag_data);
3029 icon_view->priv->dnd_locked = TRUE;
3030 }
3031 }
3032
3033
3034
3035 static void
3036 exo_icon_view_stop_rubberbanding (ExoIconView *icon_view)
3037 {
3038 gpointer drag_data;
3039
3040 if (G_LIKELY (icon_view->priv->doing_rubberband))
3041 {
3042 icon_view->priv->doing_rubberband = FALSE;
3043 gtk_grab_remove (GTK_WIDGET (icon_view));
3044 gtk_widget_queue_draw (GTK_WIDGET (icon_view));
3045 }
3046
3047 if (G_LIKELY (icon_view->priv->dnd_locked))
3048 {
3049 /* re-enable Gtk+ DnD callbacks again */
3050 drag_data = g_object_get_data (G_OBJECT (icon_view), I_("gtk-site-data"));
3051 if (G_LIKELY (drag_data != NULL))
3052 {
3053 g_signal_handlers_unblock_matched (G_OBJECT (icon_view),
3054 G_SIGNAL_MATCH_DATA,
3055 0, 0, NULL, NULL,
3056 drag_data);
3057 }
3058 icon_view->priv->dnd_locked = FALSE;
3059 }
3060 }
3061
3062
3063
3064 static void
3065 exo_icon_view_update_rubberband_selection (ExoIconView *icon_view)
3066 {
3067 ExoIconViewItem *item;
3068 gboolean selected;
3069 gboolean changed = FALSE;
3070 gboolean is_in;
3071 GList *lp;
3072 gint x, y;
3073 gint width;
3074 gint height;
3075
3076 /* determine the new rubberband area */
3077 x = MIN (icon_view->priv->rubberband_x_1, icon_view->priv->rubberband_x2);
3078 y = MIN (icon_view->priv->rubberband_y_1, icon_view->priv->rubberband_y2);
3079 width = ABS (icon_view->priv->rubberband_x_1 - icon_view->priv->rubberband_x2);
3080 height = ABS (icon_view->priv->rubberband_y_1 - icon_view->priv->rubberband_y2);
3081
3082 /* check all items */
3083 for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
3084 {
3085 item = EXO_ICON_VIEW_ITEM (lp->data);
3086
3087 is_in = exo_icon_view_item_hit_test (icon_view, item, x, y, width, height);
3088
3089 selected = is_in ^ item->selected_before_rubberbanding;
3090
3091 if (G_UNLIKELY (item->selected != selected))
3092 {
3093 changed = TRUE;
3094 item->selected = selected;
3095 exo_icon_view_queue_draw_item (icon_view, item);
3096 }
3097 }
3098
3099 if (G_LIKELY (changed))
3100 g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
3101 }
3102
3103
3104
3105 static gboolean
3106 exo_icon_view_item_hit_test (ExoIconView *icon_view,
3107 ExoIconViewItem *item,
3108 gint x,
3109 gint y,
3110 gint width,
3111 gint height)
3112 {
3113 GList *l;
3114 GdkRectangle box;
3115
3116 for (l = icon_view->priv->cell_list; l; l = l->next)
3117 {
3118 ExoIconViewCellInfo *info = (ExoIconViewCellInfo *)l->data;
3119
3120 if (!gtk_cell_renderer_get_visible(info->cell))
3121 continue;
3122
3123 /* libfm: bug #3390778: item->box isn't allocated yet here! bad design! */
3124 if (!item->box)
3125 continue;
3126
3127 box = item->box[info->position];
3128
3129 if (MIN (x + width, box.x + box.width) - MAX (x, box.x) > 0 &&
3130 MIN (y + height, box.y + box.height) - MAX (y, box.y) > 0)
3131 return TRUE;
3132 }
3133
3134 return FALSE;
3135 }
3136
3137
3138
3139 static gboolean
3140 exo_icon_view_unselect_all_internal (ExoIconView *icon_view)
3141 {
3142 ExoIconViewItem *item;
3143 gboolean dirty = FALSE;
3144 GList *lp;
3145
3146 if (G_LIKELY (icon_view->priv->selection_mode != GTK_SELECTION_NONE))
3147 {
3148 for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
3149 {
3150 item = EXO_ICON_VIEW_ITEM (lp->data);
3151 if (item->selected)
3152 {
3153 dirty = TRUE;
3154 item->selected = FALSE;
3155 exo_icon_view_queue_draw_item (icon_view, item);
3156 exo_icon_view_item_selected_changed (icon_view, item);
3157 }
3158 }
3159 }
3160
3161 return dirty;
3162 }
3163
3164
3165 #if !GTK_CHECK_VERSION(3, 0, 0)
3166 static void
3167 exo_icon_view_set_adjustments (ExoIconView *icon_view,
3168 GtkAdjustment *hadj,
3169 GtkAdjustment *vadj)
3170 {
3171 gboolean need_adjust = FALSE;
3172
3173 if (hadj)
3174 _exo_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
3175 else
3176 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3177 if (vadj)
3178 _exo_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
3179 else
3180 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3181
3182 if (icon_view->priv->hadjustment && (icon_view->priv->hadjustment != hadj))
3183 {
3184 g_signal_handlers_disconnect_matched (icon_view->priv->hadjustment, G_SIGNAL_MATCH_DATA,
3185 0, 0, NULL, NULL, icon_view);
3186 g_object_unref (icon_view->priv->hadjustment);
3187 }
3188
3189 if (icon_view->priv->vadjustment && (icon_view->priv->vadjustment != vadj))
3190 {
3191 g_signal_handlers_disconnect_matched (icon_view->priv->vadjustment, G_SIGNAL_MATCH_DATA,
3192 0, 0, NULL, NULL, icon_view);
3193 g_object_unref (icon_view->priv->vadjustment);
3194 }
3195
3196 if (icon_view->priv->hadjustment != hadj)
3197 {
3198 icon_view->priv->hadjustment = hadj;
3199 g_object_ref_sink (icon_view->priv->hadjustment);
3200
3201 g_signal_connect (icon_view->priv->hadjustment, "value-changed",
3202 G_CALLBACK (exo_icon_view_adjustment_changed),
3203 icon_view);
3204 need_adjust = TRUE;
3205 }
3206
3207 if (icon_view->priv->vadjustment != vadj)
3208 {
3209 icon_view->priv->vadjustment = vadj;
3210 g_object_ref_sink (icon_view->priv->vadjustment);
3211
3212 g_signal_connect (icon_view->priv->vadjustment, "value-changed",
3213 G_CALLBACK (exo_icon_view_adjustment_changed),
3214 icon_view);
3215 need_adjust = TRUE;
3216 }
3217
3218 if (need_adjust)
3219 exo_icon_view_adjustment_changed (NULL, icon_view);
3220 }
3221 #else
3222 static void
3223 exo_icon_view_set_hadjustment (ExoIconView *icon_view,
3224 GtkAdjustment *hadj)
3225 {
3226 gboolean need_adjust = FALSE;
3227
3228 if (hadj)
3229 _exo_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
3230 else
3231 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3232
3233 if (icon_view->priv->hadjustment && (icon_view->priv->hadjustment != hadj))
3234 {
3235 g_signal_handlers_disconnect_matched (icon_view->priv->hadjustment, G_SIGNAL_MATCH_DATA,
3236 0, 0, NULL, NULL, icon_view);
3237 g_object_unref (icon_view->priv->hadjustment);
3238 }
3239
3240 if (icon_view->priv->hadjustment != hadj)
3241 {
3242 icon_view->priv->hadjustment = hadj;
3243 g_object_ref_sink (icon_view->priv->hadjustment);
3244
3245 g_signal_connect (icon_view->priv->hadjustment, "value-changed",
3246 G_CALLBACK (exo_icon_view_adjustment_changed),
3247 icon_view);
3248 need_adjust = TRUE;
3249 }
3250
3251 if (need_adjust)
3252 exo_icon_view_adjustment_changed (NULL, icon_view);
3253 g_object_notify (G_OBJECT (icon_view), "hadjustment");
3254 }
3255
3256
3257 static void
3258 exo_icon_view_set_vadjustment (ExoIconView *icon_view,
3259 GtkAdjustment *vadj)
3260 {
3261 gboolean need_adjust = FALSE;
3262
3263 if (vadj)
3264 _exo_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
3265 else
3266 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
3267
3268 if (icon_view->priv->vadjustment && (icon_view->priv->vadjustment != vadj))
3269 {
3270 g_signal_handlers_disconnect_matched (icon_view->priv->vadjustment, G_SIGNAL_MATCH_DATA,
3271 0, 0, NULL, NULL, icon_view);
3272 g_object_unref (icon_view->priv->vadjustment);
3273 }
3274
3275 if (icon_view->priv->vadjustment != vadj)
3276 {
3277 icon_view->priv->vadjustment = vadj;
3278 g_object_ref_sink (icon_view->priv->vadjustment);
3279
3280 g_signal_connect (icon_view->priv->vadjustment, "value-changed",
3281 G_CALLBACK (exo_icon_view_adjustment_changed),
3282 icon_view);
3283 need_adjust = TRUE;
3284 }
3285
3286 if (need_adjust)
3287 exo_icon_view_adjustment_changed (NULL, icon_view);
3288 g_object_notify (G_OBJECT (icon_view), "vadjustment");
3289 }
3290 #endif
3291
3292
3293 static void
3294 exo_icon_view_real_select_all (ExoIconView *icon_view)
3295 {
3296 exo_icon_view_select_all (icon_view);
3297 }
3298
3299
3300
3301 static void
3302 exo_icon_view_real_unselect_all (ExoIconView *icon_view)
3303 {
3304 exo_icon_view_unselect_all (icon_view);
3305 }
3306
3307
3308
3309 static void
3310 exo_icon_view_real_select_cursor_item (ExoIconView *icon_view)
3311 {
3312 exo_icon_view_unselect_all (icon_view);
3313
3314 if (icon_view->priv->cursor_item != NULL)
3315 exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
3316 }
3317
3318
3319
3320 static gboolean
3321 exo_icon_view_real_activate_cursor_item (ExoIconView *icon_view)
3322 {
3323 GtkTreePath *path;
3324 GtkCellRendererMode mode;
3325 ExoIconViewCellInfo *info = NULL;
3326
3327 if (!icon_view->priv->cursor_item)
3328 return FALSE;
3329
3330 info = g_list_nth_data (icon_view->priv->cell_list,
3331 icon_view->priv->cursor_cell);
3332
3333 if (info)
3334 {
3335 g_object_get (info->cell, "mode", &mode, NULL);
3336
3337 if (mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3338 {
3339 exo_icon_view_item_activate_cell (icon_view,
3340 icon_view->priv->cursor_item,
3341 info, NULL);
3342 return TRUE;
3343 }
3344 else if (mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3345 {
3346 exo_icon_view_start_editing (icon_view,
3347 icon_view->priv->cursor_item,
3348 info, NULL);
3349 return TRUE;
3350 }
3351 }
3352
3353 path = gtk_tree_path_new_from_indices (icon_view->priv->cursor_item->index, -1);
3354 exo_icon_view_item_activated (icon_view, path);
3355 gtk_tree_path_free (path);
3356
3357 return TRUE;
3358 }
3359
3360
3361
3362 static gboolean
3363 exo_icon_view_real_start_interactive_search (ExoIconView *icon_view)
3364 {
3365 return exo_icon_view_search_start (icon_view, TRUE);
3366 }
3367
3368
3369
3370 static void
3371 exo_icon_view_real_toggle_cursor_item (ExoIconView *icon_view)
3372 {
3373 if (G_LIKELY (icon_view->priv->cursor_item != NULL))
3374 {
3375 switch (icon_view->priv->selection_mode)
3376 {
3377 case GTK_SELECTION_NONE:
3378 break;
3379
3380 case GTK_SELECTION_BROWSE:
3381 exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
3382 break;
3383
3384 case GTK_SELECTION_SINGLE:
3385 if (icon_view->priv->cursor_item->selected)
3386 exo_icon_view_unselect_item (icon_view, icon_view->priv->cursor_item);
3387 else
3388 exo_icon_view_select_item (icon_view, icon_view->priv->cursor_item);
3389 break;
3390
3391 case GTK_SELECTION_MULTIPLE:
3392 icon_view->priv->cursor_item->selected = !icon_view->priv->cursor_item->selected;
3393 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
3394 exo_icon_view_item_selected_changed (icon_view, icon_view->priv->cursor_item);
3395 exo_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item);
3396 break;
3397
3398 default:
3399 g_assert_not_reached ();
3400 }
3401 }
3402 }
3403
3404
3405
3406 static void
3407 exo_icon_view_adjustment_changed (GtkAdjustment *adjustment,
3408 ExoIconView *icon_view)
3409 {
3410 if (gtk_widget_get_realized (GTK_WIDGET(icon_view)))
3411 {
3412 gdk_window_move (icon_view->priv->bin_window, -gtk_adjustment_get_value(icon_view->priv->hadjustment), -gtk_adjustment_get_value(icon_view->priv->vadjustment));
3413
3414 if (G_UNLIKELY (icon_view->priv->doing_rubberband))
3415 exo_icon_view_update_rubberband (GTK_WIDGET (icon_view));
3416
3417 gdk_window_process_updates (icon_view->priv->bin_window, TRUE);
3418 }
3419 }
3420
3421
3422 static GList*
3423 exo_icon_view_layout_single_row (ExoIconView *icon_view,
3424 GList *first_item,
3425 gint item_width,
3426 gint row,
3427 gint *y,
3428 gint *maximum_width,
3429 gint max_cols)
3430 {
3431 ExoIconViewPrivate *priv = icon_view->priv;
3432 ExoIconViewItem *item;
3433 gboolean rtl;
3434 GList *last_item;
3435 GList *items = first_item;
3436 gint *max_width;
3437 gint *max_height;
3438 gint focus_width;
3439 gint current_width;
3440 gint colspan;
3441 gint col = 0;
3442 gint x;
3443 gint i;
3444 GtkAllocation allocation;
3445
3446 rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
3447 gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3448
3449 max_width = g_newa (gint, priv->n_cells);
3450 max_height = g_newa (gint, priv->n_cells);
3451 for (i = priv->n_cells; --i >= 0; )
3452 {
3453 max_width[i] = 0;
3454 max_height[i] = 0;
3455 }
3456
3457 gtk_widget_style_get (GTK_WIDGET (icon_view),
3458 "focus-line-width", &focus_width,
3459 NULL);
3460
3461 x = priv->margin + focus_width;
3462 current_width = 2 * (priv->margin + focus_width);
3463
3464 for (items = first_item; items != NULL; items = items->next)
3465 {
3466 item = EXO_ICON_VIEW_ITEM (items->data);
3467
3468 exo_icon_view_calculate_item_size (icon_view, item);
3469 colspan = 1 + (item->area.width - 1) / (item_width + priv->column_spacing);
3470
3471 item->area.width = colspan * item_width + (colspan - 1) * priv->column_spacing;
3472
3473 current_width += item->area.width + priv->column_spacing + 2 * focus_width;
3474
3475 if (G_LIKELY (items != first_item))
3476 {
3477 if ((priv->columns <= 0 && current_width > allocation.width) ||
3478 (priv->columns > 0 && col >= priv->columns) ||
3479 (max_cols > 0 && col >= max_cols))
3480 break;
3481 }
3482
3483 item->area.y = *y + focus_width;
3484 item->area.x = rtl ? allocation.width - item->area.width - x : x;
3485
3486 x = current_width - (priv->margin + focus_width);
3487
3488 for (i = 0; i < priv->n_cells; i++)
3489 {
3490 max_width[i] = MAX (max_width[i], item->box[i].width);
3491 max_height[i] = MAX (max_height[i], item->box[i].height);
3492 }
3493
3494 if (current_width > *maximum_width)
3495 *maximum_width = current_width;
3496
3497 item->row = row;
3498 item->col = col;
3499
3500 col += colspan;
3501 }
3502
3503 last_item = items;
3504
3505 /* Now go through the row again and align the icons */
3506 for (items = first_item; items != last_item; items = items->next)
3507 {
3508 item = EXO_ICON_VIEW_ITEM (items->data);
3509
3510 exo_icon_view_calculate_item_size2 (icon_view, item, max_width, max_height);
3511
3512 /* We may want to readjust the new y coordinate. */
3513 if (item->area.y + item->area.height + focus_width + priv->row_spacing > *y)
3514 *y = item->area.y + item->area.height + focus_width + priv->row_spacing;
3515
3516 if (G_UNLIKELY (rtl))
3517 item->col = col - 1 - item->col;
3518 }
3519
3520 return last_item;
3521 }
3522
3523
3524
3525 static GList*
3526 exo_icon_view_layout_single_col (ExoIconView *icon_view,
3527 GList *first_item,
3528 gint item_height,
3529 gint col,
3530 gint *x,
3531 gint *maximum_height,
3532 gint max_rows)
3533 {
3534 ExoIconViewPrivate *priv = icon_view->priv;
3535 ExoIconViewItem *item;
3536 GList *items = first_item;
3537 GList *last_item;
3538 gint *max_width;
3539 gint *max_height;
3540 gint focus_width;
3541 gint current_height;
3542 gint rowspan;
3543 gint row = 0;
3544 gint y;
3545 gint i;
3546 GtkAllocation allocation;
3547
3548 max_width = g_newa (gint, priv->n_cells);
3549 max_height = g_newa (gint, priv->n_cells);
3550 for (i = priv->n_cells; --i >= 0; )
3551 {
3552 max_width[i] = 0;
3553 max_height[i] = 0;
3554 }
3555
3556 gtk_widget_style_get (GTK_WIDGET (icon_view),
3557 "focus-line-width", &focus_width,
3558 NULL);
3559 gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3560
3561 y = priv->margin + focus_width;
3562 current_height = 2 * (priv->margin + focus_width);
3563
3564 for (items = first_item; items != NULL; items = items->next)
3565 {
3566 item = EXO_ICON_VIEW_ITEM (items->data);
3567
3568 exo_icon_view_calculate_item_size (icon_view, item);
3569
3570 rowspan = 1 + (item->area.height - 1) / (item_height + priv->row_spacing);
3571
3572 item->area.height = rowspan * item_height + (rowspan - 1) * priv->row_spacing;
3573
3574 current_height += item->area.height + priv->row_spacing + 2 * focus_width;
3575
3576 if (G_LIKELY (items != first_item))
3577 {
3578 if (current_height >= allocation.height ||
3579 (max_rows > 0 && row >= max_rows))
3580 break;
3581 }
3582
3583 item->area.y = y + focus_width;
3584 item->area.x = *x;
3585
3586 y = current_height - (priv->margin + focus_width);
3587
3588 for (i = 0; i < priv->n_cells; i++)
3589 {
3590 max_width[i] = MAX (max_width[i], item->box[i].width);
3591 max_height[i] = MAX (max_height[i], item->box[i].height);
3592 }
3593
3594 if (current_height > *maximum_height)
3595 *maximum_height = current_height;
3596
3597 item->row = row;
3598 item->col = col;
3599
3600 row += rowspan;
3601 }
3602
3603 last_item = items;
3604
3605 /* Now go through the column again and align the icons */
3606 for (items = first_item; items != last_item; items = items->next)
3607 {
3608 item = EXO_ICON_VIEW_ITEM (items->data);
3609
3610 exo_icon_view_calculate_item_size2 (icon_view, item, max_width, max_height);
3611
3612 /* We may want to readjust the new x coordinate. */
3613 if (item->area.x + item->area.width + focus_width + priv->column_spacing > *x)
3614 *x = item->area.x + item->area.width + focus_width + priv->column_spacing;
3615 }
3616
3617 return last_item;
3618 }
3619
3620
3621
3622 static void
3623 exo_icon_view_set_adjustment_upper (GtkAdjustment *adj,
3624 gdouble upper)
3625 {
3626 if (upper != gtk_adjustment_get_upper(adj))
3627 {
3628 gdouble min = MAX (0.0, upper - gtk_adjustment_get_page_size(adj));
3629 gboolean value_changed = FALSE;
3630
3631 gtk_adjustment_set_upper(adj, upper);
3632
3633 if (gtk_adjustment_get_value(adj) > min)
3634 {
3635 gtk_adjustment_set_value(adj, min);
3636 value_changed = TRUE;
3637 }
3638
3639 gtk_adjustment_changed (adj);
3640
3641 if (value_changed)
3642 gtk_adjustment_value_changed (adj);
3643 }
3644 }
3645
3646
3647
3648 static gint
3649 exo_icon_view_layout_cols (ExoIconView *icon_view,
3650 gint item_height,
3651 gint *x,
3652 gint *maximum_height,
3653 gint max_rows)
3654 {
3655 GList *icons = icon_view->priv->items;
3656 GList *items;
3657 gboolean rtl;
3658 gint col = 0;
3659 gint rows = 0;
3660 gint shift;
3661
3662 rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
3663
3664 shift = icon_view->priv->margin;
3665 *x = shift;
3666
3667 do
3668 {
3669 items = icons; /* it will be used below */
3670 icons = exo_icon_view_layout_single_col (icon_view, icons,
3671 item_height, col,
3672 &shift, maximum_height, max_rows);
3673
3674 if (rtl)
3675 {
3676 GList *items2;
3677 ExoIconViewItem *item;
3678 gint i;
3679
3680 /* update width */
3681 shift -= icon_view->priv->margin; /* width of the new column */
3682 *x += shift;
3683 /* shift all previous items to right so new column will be left one */
3684 if (items) for (items2 = icon_view->priv->items; items2 != items; items2 = items2->next)
3685 {
3686 item = EXO_ICON_VIEW_ITEM (items2->data);
3687
3688 item->area.x += shift;
3689 for (i = 0; i < icon_view->priv->n_cells; i++)
3690 item->box[i].x += shift;
3691 }
3692 /* prepare for the next col */
3693 shift = icon_view->priv->margin;
3694 }
3695 else
3696 *x = shift;
3697 /* count the number of rows in the first column */
3698 if (G_UNLIKELY (col == 0))
3699 {
3700 for (items = icon_view->priv->items, rows = 0; items != icons; items = items->next, ++rows)
3701 ;
3702 }
3703
3704 col++;
3705 }
3706 while (icons != NULL);
3707
3708 *x += icon_view->priv->margin;
3709 icon_view->priv->cols = col;
3710
3711 return rows;
3712 }
3713
3714
3715
3716 static gint
3717 exo_icon_view_layout_rows (ExoIconView *icon_view,
3718 gint item_width,
3719 gint *y,
3720 gint *maximum_width,
3721 gint max_cols)
3722 {
3723 GList *icons = icon_view->priv->items;
3724 GList *items;
3725 gint row = 0;
3726 gint cols = 0;
3727
3728 *y = icon_view->priv->margin;
3729
3730 do
3731 {
3732 icons = exo_icon_view_layout_single_row (icon_view, icons,
3733 item_width, row,
3734 y, maximum_width, max_cols);
3735
3736 /* count the number of columns in the first row */
3737 if (G_UNLIKELY (row == 0))
3738 {
3739 for (items = icon_view->priv->items, cols = 0; items != icons; items = items->next, ++cols)
3740 ;
3741 }
3742
3743 row++;
3744 }
3745 while (icons != NULL);
3746
3747 *y += icon_view->priv->margin;
3748 icon_view->priv->rows = row;
3749
3750 return cols;
3751 }
3752
3753
3754
3755 static void
3756 exo_icon_view_layout (ExoIconView *icon_view)
3757 {
3758 ExoIconViewPrivate *priv = icon_view->priv;
3759 ExoIconViewItem *item;
3760 GList *icons;
3761 gint maximum_height = 0;
3762 gint maximum_width = 0;
3763 gint item_height;
3764 gint item_width;
3765 gint rows, cols;
3766 gint x, y;
3767 GtkAllocation allocation;
3768 #if GTK_CHECK_VERSION(2, 20, 0)
3769 GtkRequisition requisition;
3770 #endif
3771
3772 /* verify that we still have a valid model */
3773 if (G_UNLIKELY (priv->model == NULL))
3774 return;
3775
3776 gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
3777
3778 /* determine the layout mode */
3779 if (G_LIKELY (priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
3780 {
3781 /* calculate item sizes on-demand */
3782 item_width = priv->item_width;
3783 if (item_width < 0)
3784 {
3785 for (icons = priv->items; icons != NULL; icons = icons->next)
3786 {
3787 item = icons->data;
3788 exo_icon_view_calculate_item_size (icon_view, item);
3789 item_width = MAX (item_width, item->area.width);
3790 }
3791 }
3792
3793 cols = exo_icon_view_layout_rows (icon_view, item_width, &y, &maximum_width, 0);
3794
3795 /* If, by adding another column, we increase the height of the icon view, thus forcing a
3796 * vertical scrollbar to appear that would prevent the last column from being able to fit,
3797 * we need to relayout the icons with one less column.
3798 */
3799 if (cols == priv->cols + 1 && y > allocation.height &&
3800 priv->height <= allocation.height)
3801 {
3802 cols = exo_icon_view_layout_rows (icon_view, item_width, &y, &maximum_width, priv->cols);
3803 }
3804
3805 priv->width = maximum_width;
3806 priv->height = y;
3807 priv->cols = cols;
3808 }
3809 else
3810 {
3811 /* calculate item sizes on-demand */
3812 for (icons = priv->items, item_height = 0; icons != NULL; icons = icons->next)
3813 {
3814 item = icons->data;
3815 exo_icon_view_calculate_item_size (icon_view, item);
3816 item_height = MAX (item_height, item->area.height);
3817 }
3818
3819 rows = exo_icon_view_layout_cols (icon_view, item_height, &x, &maximum_height, 0);
3820
3821 /* If, by adding another row, we increase the width of the icon view, thus forcing a
3822 * horizontal scrollbar to appear that would prevent the last row from being able to fit,
3823 * we need to relayout the icons with one less row.
3824 */
3825 if (rows == priv->rows + 1 && x > allocation.width &&
3826 priv->width <= allocation.width)
3827 {
3828 rows = exo_icon_view_layout_cols (icon_view, item_height, &x, &maximum_height, priv->rows);
3829 }
3830 else if (x < allocation.width &&
3831 gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL)
3832 {
3833 /* shift items to align right border */
3834 gint shift = allocation.width - x, i;
3835 for (icons = priv->items; icons != NULL; icons = icons->next)
3836 {
3837 item = icons->data;
3838 item->area.x += shift;
3839 for (i = 0; i < icon_view->priv->n_cells; i++)
3840 item->box[i].x += shift;
3841 }
3842 }
3843
3844 priv->height = maximum_height;
3845 priv->width = x;
3846 priv->rows = rows;
3847 }
3848
3849 exo_icon_view_set_adjustment_upper (priv->hadjustment, priv->width);
3850 exo_icon_view_set_adjustment_upper (priv->vadjustment, priv->height);
3851
3852 #if GTK_CHECK_VERSION(2, 20, 0)
3853 gtk_widget_get_requisition (GTK_WIDGET (icon_view), &requisition);
3854 if (priv->width != requisition.width
3855 || priv->height != requisition.height)
3856 #else
3857 if (priv->width != GTK_WIDGET (icon_view)->requisition.width
3858 || priv->height != GTK_WIDGET (icon_view)->requisition.height)
3859 #endif
3860 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (icon_view));
3861
3862 if (gtk_widget_get_realized (GTK_WIDGET(icon_view)))
3863 {
3864 gdk_window_resize (priv->bin_window,
3865 MAX (priv->width, allocation.width),
3866 MAX (priv->height, allocation.height));
3867 }
3868
3869 /* drop any pending layout idle source */
3870 if (priv->layout_idle_id != 0)
3871 g_source_remove (priv->layout_idle_id);
3872
3873 gtk_widget_queue_draw (GTK_WIDGET (icon_view));
3874 }
3875
3876
3877
3878 static void
3879 exo_icon_view_get_cell_area (ExoIconView *icon_view,
3880 ExoIconViewItem *item,
3881 ExoIconViewCellInfo *info,
3882 GdkRectangle *cell_area)
3883 {
3884 if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3885 {
3886 cell_area->x = item->box[info->position].x - item->before[info->position];
3887 cell_area->y = item->area.y;
3888 cell_area->width = item->box[info->position].width + item->before[info->position] + item->after[info->position];
3889 cell_area->height = item->area.height;
3890 }
3891 else
3892 {
3893 cell_area->x = item->area.x;
3894 cell_area->y = item->box[info->position].y - item->before[info->position];
3895 cell_area->width = item->area.width;
3896 cell_area->height = item->box[info->position].height + item->before[info->position] + item->after[info->position];
3897 }
3898 }
3899
3900
3901
3902 static void
3903 exo_icon_view_calculate_item_size (ExoIconView *icon_view,
3904 ExoIconViewItem *item)
3905 {
3906 ExoIconViewCellInfo *info;
3907 GList *lp;
3908 gchar *buffer;
3909 #if GTK_CHECK_VERSION(3, 0, 0)
3910 GtkRequisition requisition;
3911 #endif
3912
3913 if (G_LIKELY (item->area.width != -1))
3914 return;
3915
3916 if (G_UNLIKELY (item->n_cells != icon_view->priv->n_cells))
3917 {
3918 /* apply the new cell size */
3919 item->n_cells = icon_view->priv->n_cells;
3920
3921 /* release the memory chunk (if any) */
3922 g_free (item->box);
3923
3924 /* allocate a single memory chunk for box, after and before */
3925 buffer = g_malloc0 (item->n_cells * (sizeof (GdkRectangle) + 2 * sizeof (gint)));
3926
3927 /* assign the memory */
3928 item->box = (GdkRectangle *) buffer;
3929 item->after = (gint *) (buffer + item->n_cells * sizeof (GdkRectangle));
3930 item->before = item->after + item->n_cells;
3931 }
3932
3933 exo_icon_view_set_cell_data (icon_view, item);
3934
3935 item->area.width = 0;
3936 item->area.height = 0;
3937 for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
3938 {
3939 info = EXO_ICON_VIEW_CELL_INFO (lp->data);
3940 if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell)))
3941 continue;
3942
3943 #if GTK_CHECK_VERSION(3, 0, 0)
3944 gtk_cell_renderer_get_preferred_size (info->cell, GTK_WIDGET (icon_view),
3945 NULL, &requisition);
3946 item->box[info->position].width = requisition.width;
3947 item->box[info->position].height = requisition.height;
3948 #else
3949 gtk_cell_renderer_get_size (info->cell, GTK_WIDGET (icon_view),
3950 NULL, NULL, NULL,
3951 &item->box[info->position].width,
3952 &item->box[info->position].height);
3953 #endif
3954
3955 if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3956 {
3957 item->area.width += item->box[info->position].width + (info->position > 0 ? icon_view->priv->spacing : 0);
3958 item->area.height = MAX (item->area.height, item->box[info->position].height);
3959 }
3960 else
3961 {
3962 item->area.width = MAX (item->area.width, item->box[info->position].width);
3963 item->area.height += item->box[info->position].height + (info->position > 0 ? icon_view->priv->spacing : 0);
3964 }
3965 }
3966 }
3967
3968
3969
3970 static void
3971 exo_icon_view_calculate_item_size2 (ExoIconView *icon_view,
3972 ExoIconViewItem *item,
3973 gint *max_width,
3974 gint *max_height)
3975 {
3976 ExoIconViewCellInfo *info;
3977 GdkRectangle *box;
3978 GdkRectangle cell_area;
3979 gboolean rtl;
3980 GList *lp;
3981 gint spacing;
3982 gint i, k;
3983 gint xpad, ypad;
3984 gfloat xalign, yalign;
3985
3986 rtl = (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL);
3987
3988 spacing = icon_view->priv->spacing;
3989
3990 if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
3991 {
3992 item->area.height = 0;
3993 for (i = 0; i < icon_view->priv->n_cells; ++i)
3994 {
3995 if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3996 item->area.height = MAX (item->area.height, max_height[i]);
3997 else
3998 item->area.height += max_height[i] + (i > 0 ? spacing : 0);
3999 }
4000 }
4001 else
4002 {
4003 item->area.width = 0;
4004 for (i = 0; i < icon_view->priv->n_cells; ++i)
4005 {
4006 if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
4007 item->area.width += max_width[i] + (i > 0 ? spacing : 0);
4008 else
4009 item->area.width = MAX (item->area.width, max_width[i]);
4010 }
4011 }
4012
4013 cell_area.x = item->area.x;
4014 cell_area.y = item->area.y;
4015
4016 for (k = 0; k < 2; ++k)
4017 {
4018 for (lp = icon_view->priv->cell_list, i = 0; lp != NULL; lp = lp->next, ++i)
4019 {
4020 info = EXO_ICON_VIEW_CELL_INFO (lp->data);
4021 if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell) || info->pack == (k ? GTK_PACK_START : GTK_PACK_END)))
4022 continue;
4023
4024 if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
4025 {
4026 cell_area.width = item->box[info->position].width;
4027 cell_area.height = item->area.height;
4028 }
4029 else
4030 {
4031 cell_area.width = item->area.width;
4032 cell_area.height = max_height[i];
4033 }
4034
4035 box = item->box + info->position;
4036 gtk_cell_renderer_get_alignment (info->cell, &xalign, &yalign);
4037 gtk_cell_renderer_get_padding (info->cell, &xpad, &ypad);
4038 box->x = cell_area.x + (rtl ? (1.0 - xalign) : xalign) * (cell_area.width - box->width - (2 * xpad));
4039 box->x = MAX (box->x, 0);
4040 box->y = cell_area.y + yalign * (cell_area.height - box->height - (2 * ypad));
4041 box->y = MAX (box->y, 0);
4042
4043 if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
4044 {
4045 item->before[info->position] = item->box[info->position].x - cell_area.x;
4046 item->after[info->position] = cell_area.width - item->box[info->position].width - item->before[info->position];
4047 cell_area.x += cell_area.width + spacing;
4048 }
4049 else
4050 {
4051 if (item->box[info->position].width > item->area.width)
4052 {
4053 item->area.width = item->box[info->position].width;
4054 cell_area.width = item->area.width;
4055 }
4056 item->before[info->position] = item->box[info->position].y - cell_area.y;
4057 item->after[info->position] = cell_area.height - item->box[info->position].height - item->before[info->position];
4058 cell_area.y += cell_area.height + spacing;
4059 }
4060 }
4061 }
4062
4063 if (G_UNLIKELY (rtl && icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL))
4064 {
4065 for (i = 0; i < icon_view->priv->n_cells; i++)
4066 item->box[i].x = item->area.x + item->area.width - (item->box[i].x + item->box[i].width - item->area.x);
4067 }
4068 }
4069
4070
4071
4072 static void
4073 exo_icon_view_invalidate_sizes (ExoIconView *icon_view)
4074 {
4075 GList *lp;
4076
4077 for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
4078 EXO_ICON_VIEW_ITEM (lp->data)->area.width = -1;
4079 exo_icon_view_queue_layout (icon_view);
4080 }
4081
4082
4083
4084 static void
4085 exo_icon_view_paint_item (ExoIconView *icon_view,
4086 ExoIconViewItem *item,
4087 GdkRectangle *area,
4088 #if GTK_CHECK_VERSION(3, 0, 0)
4089 cairo_t *drawable,
4090 #else
4091 GdkDrawable *drawable,
4092 #endif
4093 gint x,
4094 gint y,
4095 gboolean draw_focus)
4096 {
4097 GtkCellRendererState flags;
4098 ExoIconViewCellInfo *info;
4099 //GtkStateType state;
4100 GdkRectangle cell_area;
4101 //gboolean rtl;
4102 GList *lp;
4103
4104 if (G_UNLIKELY (icon_view->priv->model == NULL))
4105 return;
4106
4107 exo_icon_view_set_cell_data (icon_view, item);
4108
4109 //rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL;
4110
4111 if (item->selected)
4112 {
4113 flags = GTK_CELL_RENDERER_SELECTED;
4114 //state = gtk_widget_has_focus (icon_view) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
4115 #if 0
4116 /* FIXME We hardwire background drawing behind text cell renderers
4117 * here. This is ugly, but it's done to be consistent with GtkIconView.
4118 * The additional info->is_text attribute is used for performance
4119 * optimization and should be removed alongside the following code. */
4120
4121 cr = gdk_cairo_create (drawable);
4122
4123 for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
4124 {
4125 info = EXO_ICON_VIEW_CELL_INFO (lp->data);
4126
4127 if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell)))
4128 continue;
4129
4130 if (info->is_text)
4131 {
4132 exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
4133
4134 x_0 = x - item->area.x + cell_area.x;
4135 y_0 = x - item->area.x + cell_area.y;
4136 x_1 = x_0 + cell_area.width;
4137 y_1 = y_0 + cell_area.height;
4138
4139 cairo_move_to (cr, x_0 + 5, y_0);
4140 cairo_line_to (cr, x_1 - 5, y_0);
4141 cairo_curve_to (cr, x_1 - 5, y_0, x_1, y_0, x_1, y_0 + 5);
4142 cairo_line_to (cr, x_1, y_1 - 5);
4143 cairo_curve_to (cr, x_1, y_1 - 5, x_1, y_1, x_1 - 5, y_1);
4144 cairo_line_to (cr, x_0 + 5, y_1);
4145 cairo_curve_to (cr, x_0 + 5, y_1, x_0, y_1, x_0, y_1 - 5);
4146 cairo_line_to (cr, x_0, y_0 + 5);
4147 cairo_curve_to (cr, x_0, y_0 + 5, x_0, y_0, x_0 + 5, y_0);
4148
4149 gdk_cairo_set_source_color (cr, >K_WIDGET (icon_view)->style->base[state]);
4150
4151 cairo_fill (cr);
4152 }
4153 }
4154
4155 cairo_destroy (cr);
4156
4157 /* FIXME Ugly code ends here */
4158 #endif
4159 }
4160 else
4161 {
4162 flags = 0;
4163 //state = GTK_STATE_NORMAL;
4164 }
4165
4166 if (G_UNLIKELY (icon_view->priv->prelit_item == item))
4167 flags |= GTK_CELL_RENDERER_PRELIT;
4168 if (G_UNLIKELY (EXO_ICON_VIEW_FLAG_SET (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS) && icon_view->priv->cursor_item == item))
4169 flags |= GTK_CELL_RENDERER_FOCUSED;
4170
4171 #ifdef DEBUG_ICON_VIEW
4172 gdk_draw_rectangle (drawable,
4173 GTK_WIDGET (icon_view)->style->black_gc,
4174 FALSE,
4175 x, y,
4176 item->area.width, item->area.height);
4177 #endif
4178
4179 for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
4180 {
4181 info = EXO_ICON_VIEW_CELL_INFO (lp->data);
4182
4183 if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell)))
4184 continue;
4185
4186 exo_icon_view_get_cell_area (icon_view, item, info, &cell_area);
4187
4188 #ifdef DEBUG_ICON_VIEW
4189 gdk_draw_rectangle (drawable,
4190 GTK_WIDGET (icon_view)->style->black_gc,
4191 FALSE,
4192 x - item->area.x + cell_area.x,
4193 y - item->area.y + cell_area.y,
4194 cell_area.width, cell_area.height);
4195
4196 gdk_draw_rectangle (drawable,
4197 GTK_WIDGET (icon_view)->style->black_gc,
4198 FALSE,
4199 x - item->area.x + item->box[info->position].x,
4200 y - item->area.y + item->box[info->position].y,
4201 item->box[info->position].width, item->box[info->position].height);
4202 #endif
4203
4204 cell_area.x = x - item->area.x + cell_area.x;
4205 cell_area.y = y - item->area.y + cell_area.y;
4206
4207 gtk_cell_renderer_render (info->cell,
4208 drawable,
4209 GTK_WIDGET (icon_view),
4210 &cell_area, &cell_area,
4211 #if !GTK_CHECK_VERSION(3, 0, 0)
4212 area,
4213 #endif
4214 flags);
4215
4216 }
4217 }
4218
4219
4220
4221 static void
4222 exo_icon_view_queue_draw_item (ExoIconView *icon_view,
4223 ExoIconViewItem *item)
4224 {
4225 GdkRectangle rect;
4226 gint focus_width;
4227
4228 gtk_widget_style_get (GTK_WIDGET (icon_view),
4229 "focus-line-width", &focus_width,
4230 NULL);
4231
4232 rect.x = item->area.x - focus_width;
4233 rect.y = item->area.y - focus_width;
4234 rect.width = item->area.width + 2 * focus_width;
4235 rect.height = item->area.height + 2 * focus_width;
4236
4237 if (icon_view->priv->bin_window)
4238 gdk_window_invalidate_rect (icon_view->priv->bin_window, &rect, TRUE);
4239 }
4240
4241
4242
4243 static gboolean
4244 layout_callback (gpointer user_data)
4245 {
4246 ExoIconView *icon_view = EXO_ICON_VIEW (user_data);
4247
4248 if(!g_source_is_destroyed(g_main_current_source()))
4249 exo_icon_view_layout (icon_view);
4250
4251 return FALSE;
4252 }
4253
4254
4255
4256 static void
4257 layout_destroy (gpointer user_data)
4258 {
4259 EXO_ICON_VIEW (user_data)->priv->layout_idle_id = 0;
4260 }
4261
4262
4263
4264 static void
4265 exo_icon_view_queue_layout (ExoIconView *icon_view)
4266 {
4267 if (G_UNLIKELY (icon_view->priv->layout_idle_id == 0))
4268 icon_view->priv->layout_idle_id = gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, layout_callback, icon_view, layout_destroy);
4269 }
4270
4271
4272
4273 static void
4274 exo_icon_view_set_cursor_item (ExoIconView *icon_view,
4275 ExoIconViewItem *item,
4276 gint cursor_cell)
4277 {
4278 AtkObject *obj;
4279 AtkObject *item_obj;
4280 AtkObject *cursor_item_obj;
4281
4282 /* When hitting this path from keynav, the focus cell is
4283 * already set, we dont need to notify the atk object
4284 * but we still need to queue the draw here (in the case
4285 * that the focus cell changes but not the cursor item).
4286 */
4287 exo_icon_view_queue_draw_item (icon_view, item);
4288
4289 if (icon_view->priv->cursor_item == item &&
4290 (cursor_cell < 0 || cursor_cell == icon_view->priv->cursor_cell))
4291 return;
4292
4293 obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view));
4294 if (icon_view->priv->cursor_item != NULL)
4295 {
4296 exo_icon_view_queue_draw_item (icon_view, icon_view->priv->cursor_item);
4297 if (obj != NULL)
4298 {
4299 cursor_item_obj = atk_object_ref_accessible_child (obj, icon_view->priv->cursor_item->index);
4300 if (cursor_item_obj != NULL)
4301 atk_object_notify_state_change (cursor_item_obj, ATK_STATE_FOCUSED, FALSE);
4302 }
4303 }
4304 icon_view->priv->cursor_item = item;
4305 if (cursor_cell >= 0)
4306 icon_view->priv->cursor_cell = cursor_cell;
4307
4308 /* Notify that accessible focus object has changed */
4309 item_obj = atk_object_ref_accessible_child (obj, item->index);
4310
4311 if (item_obj != NULL)
4312 {
4313 #if !ATK_CHECK_VERSION(2, 9, 4)
4314 atk_focus_tracker_notify (item_obj);
4315 #endif
4316 atk_object_notify_state_change (item_obj, ATK_STATE_FOCUSED, TRUE);
4317 g_object_unref (item_obj);
4318 }
4319 }
4320
4321
4322
4323 static ExoIconViewItem*
4324 exo_icon_view_get_item_at_coords (const ExoIconView *icon_view,
4325 gint x,
4326 gint y,
4327 gboolean only_in_cell,
4328 ExoIconViewCellInfo **cell_at_pos)
4329 {
4330 const ExoIconViewPrivate *priv = icon_view->priv;
4331 ExoIconViewCellInfo *info;
4332 ExoIconViewItem *item;
4333 GdkRectangle box;
4334 const GList *items;
4335 const GList *lp;
4336
4337 for (items = priv->items; items != NULL; items = items->next)
4338 {
4339 item = items->data;
4340 if (x >= item->area.x - priv->row_spacing / 2 && x <= item->area.x + item->area.width + priv->row_spacing / 2 &&
4341 y >= item->area.y - priv->column_spacing / 2 && y <= item->area.y + item->area.height + priv->column_spacing / 2)
4342 {
4343 if (only_in_cell || cell_at_pos)
4344 {
4345 exo_icon_view_set_cell_data (icon_view, item);
4346 for (lp = priv->cell_list; lp != NULL; lp = lp->next)
4347 {
4348 /* check if the cell is visible */
4349 info = (ExoIconViewCellInfo *) lp->data;
4350 if (!gtk_cell_renderer_get_visible(info->cell))
4351 continue;
4352
4353 box = item->box[info->position];
4354 if ((x >= box.x && x <= box.x + box.width &&
4355 y >= box.y && y <= box.y + box.height) ||
4356 (x >= box.x &&
4357 x <= box.x + box.width &&
4358 y >= box.y &&
4359 y <= box.y + box.height))
4360 {
4361 if (cell_at_pos != NULL)
4362 *cell_at_pos = info;
4363
4364 return item;
4365 }
4366 }
4367
4368 if (only_in_cell)
4369 return NULL;
4370
4371 if (cell_at_pos != NULL)
4372 *cell_at_pos = NULL;
4373 }
4374
4375 return item;
4376 }
4377 }
4378
4379 return NULL;
4380 }
4381
4382
4383
4384 static void
4385 exo_icon_view_select_item (ExoIconView *icon_view,
4386 ExoIconViewItem *item)
4387 {
4388 if (item->selected || icon_view->priv->selection_mode == GTK_SELECTION_NONE)
4389 return;
4390 else if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
4391 exo_icon_view_unselect_all_internal (icon_view);
4392
4393 item->selected = TRUE;
4394
4395 exo_icon_view_queue_draw_item (icon_view, item);
4396
4397 exo_icon_view_item_selected_changed (icon_view, item);
4398 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4399 }
4400
4401
4402
4403 static void
4404 exo_icon_view_unselect_item (ExoIconView *icon_view,
4405 ExoIconViewItem *item)
4406 {
4407 if (!item->selected)
4408 return;
4409
4410 if (icon_view->priv->selection_mode == GTK_SELECTION_NONE ||
4411 icon_view->priv->selection_mode == GTK_SELECTION_BROWSE)
4412 return;
4413
4414 item->selected = FALSE;
4415
4416 exo_icon_view_item_selected_changed (icon_view, item);
4417 g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
4418
4419 exo_icon_view_queue_draw_item (icon_view, item);
4420 }
4421
4422
4423 static void
4424 verify_items (ExoIconView *icon_view)
4425 {
4426 GList *items;
4427 int i = 0;
4428
4429 for (items = icon_view->priv->items; items; items = items->next)
4430 {
4431 ExoIconViewItem *item = items->data;
4432
4433 if (item->index != i)
4434 g_error ("List item does not match its index: "
4435 "item index %d and list index %d\n", item->index, i);
4436
4437 i++;
4438 }
4439 }
4440
4441
4442 static void
4443 exo_icon_view_row_changed (GtkTreeModel *model,
4444 GtkTreePath *path,
4445 GtkTreeIter *iter,
4446 ExoIconView *icon_view)
4447 {
4448 ExoIconViewItem *item;
4449
4450 item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
4451
4452 /* stop editing this item */
4453 if (G_UNLIKELY (item == icon_view->priv->edited_item))
4454 exo_icon_view_stop_editing (icon_view, TRUE);
4455
4456 /* emit "selection-changed" if the item is selected */
4457 if (G_UNLIKELY (item->selected))
4458 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4459
4460 /* recalculate layout (a value of -1 for width
4461 * indicates that the item needs to be layouted).
4462 */
4463 item->area.width = -1;
4464 exo_icon_view_queue_layout (icon_view);
4465 verify_items (icon_view);
4466 }
4467
4468
4469
4470 static void
4471 exo_icon_view_row_inserted (GtkTreeModel *model,
4472 GtkTreePath *path,
4473 GtkTreeIter *iter,
4474 ExoIconView *icon_view)
4475 {
4476 ExoIconViewItem *item;
4477 GList *list;
4478 gint idx;
4479
4480 idx = gtk_tree_path_get_indices (path)[0];
4481
4482 /* allocate the new item */
4483 item = g_slice_new0 (ExoIconViewItem);
4484 item->iter = *iter;
4485 item->area.width = -1;
4486 item->index = idx;
4487 icon_view->priv->items = g_list_insert (icon_view->priv->items, item, idx);
4488
4489 list = g_list_nth (icon_view->priv->items, idx + 1);
4490 for (; list; list = list->next)
4491 {
4492 item = list->data;
4493
4494 item->index++;
4495 }
4496 verify_items (icon_view);
4497
4498 /* recalculate the layout */
4499 exo_icon_view_queue_layout (icon_view);
4500 }
4501
4502
4503
4504 static void
4505 exo_icon_view_row_deleted (GtkTreeModel *model,
4506 GtkTreePath *path,
4507 ExoIconView *icon_view)
4508 {
4509 ExoIconViewItem *item;
4510 gboolean changed = FALSE;
4511 GList *list, *next;
4512
4513 /* determine the position and the item for the path */
4514 list = g_list_nth (icon_view->priv->items, gtk_tree_path_get_indices (path)[0]);
4515 next = list->next;
4516 item = list->data;
4517
4518 if (G_UNLIKELY (item == icon_view->priv->edited_item))
4519 exo_icon_view_stop_editing (icon_view, TRUE);
4520
4521 /* use the next item (if any) as anchor, else use prev, otherwise reset anchor */
4522 if (G_UNLIKELY (item == icon_view->priv->anchor_item))
4523 icon_view->priv->anchor_item = (list->next != NULL) ? list->next->data : ((list->prev != NULL) ? list->prev->data : NULL);
4524
4525 /* use the next item (if any) as cursor, else use prev, otherwise reset cursor */
4526 if (G_UNLIKELY (item == icon_view->priv->cursor_item))
4527 icon_view->priv->cursor_item = (list->next != NULL) ? list->next->data : ((list->prev != NULL) ? list->prev->data : NULL);
4528
4529 if (G_UNLIKELY (item == icon_view->priv->prelit_item))
4530 {
4531 /* reset the prelit item */
4532 icon_view->priv->prelit_item = NULL;
4533
4534 /* cancel any pending single click timer */
4535 if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
4536 g_source_remove (icon_view->priv->single_click_timeout_id);
4537
4538 /* in single click mode, we also reset the cursor when realized */
4539 if (G_UNLIKELY (icon_view->priv->single_click && gtk_widget_get_realized (GTK_WIDGET(icon_view))))
4540 gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
4541 }
4542
4543 /* check if the selection changed */
4544 if (G_UNLIKELY (item->selected))
4545 changed = TRUE;
4546
4547 /* release the item resources */
4548 g_free (item->box);
4549
4550 /* drop the item from the list */
4551 icon_view->priv->items = g_list_delete_link (icon_view->priv->items, list);
4552
4553 /* release the item */
4554 g_slice_free (ExoIconViewItem, item);
4555
4556 /* update indices */
4557 for (; next; next = next->next)
4558 {
4559 item = next->data;
4560
4561 item->index--;
4562 }
4563 verify_items (icon_view);
4564
4565 /* recalculate the layout */
4566 exo_icon_view_queue_layout (icon_view);
4567
4568 /* if we removed a previous selected item, we need
4569 * to tell others that we have a new selection.
4570 */
4571 if (G_UNLIKELY (changed))
4572 g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
4573 }
4574
4575
4576
4577 static void
4578 exo_icon_view_rows_reordered (GtkTreeModel *model,
4579 GtkTreePath *parent,
4580 GtkTreeIter *iter,
4581 gint *new_order,
4582 ExoIconView *icon_view)
4583 {
4584 GList **list_array;
4585 GList *list;
4586 gint *order;
4587 gint length;
4588 gint i;
4589
4590 /* cancel any editing attempt */
4591 exo_icon_view_stop_editing (icon_view, TRUE);
4592
4593 /* determine the number of items to reorder */
4594 length = gtk_tree_model_iter_n_children (model, NULL);
4595 if (G_UNLIKELY (length == 0))
4596 return;
4597
4598 list_array = g_newa (GList *, length);
4599 order = g_newa (gint, length);
4600
4601 for (i = 0; i < length; i++)
4602 order[new_order[i]] = i;
4603
4604 for (i = 0, list = icon_view->priv->items; list != NULL; list = list->next, i++)
4605 list_array[order[i]] = list;
4606
4607 /* hook up the first item */
4608 icon_view->priv->items = list_array[0];
4609 list_array[0]->prev = NULL;
4610 ((ExoIconViewItem*)list_array[0]->data)->index = 0;
4611
4612 /* hook up the remaining items */
4613 for (i = 1; i < length; ++i)
4614 {
4615 ((ExoIconViewItem*)list_array[i]->data)->index = i;
4616 list_array[i - 1]->next = list_array[i];
4617 list_array[i]->prev = list_array[i - 1];
4618 }
4619
4620 /* hook up the last item */
4621 list_array[length - 1]->next = NULL;
4622
4623 exo_icon_view_queue_layout (icon_view);
4624 verify_items (icon_view);
4625 }
4626
4627
4628
4629 static void
4630 exo_icon_view_add_move_binding (GtkBindingSet *binding_set,
4631 guint keyval,
4632 guint modmask,
4633 GtkMovementStep step,
4634 gint count)
4635 {
4636
4637 gtk_binding_entry_add_signal (binding_set, keyval, modmask, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4638
4639 /* skip shift+n and shift+p because this blocks type-ahead search.
4640 * see http://bugzilla.xfce.org/show_bug.cgi?id=4633
4641 */
4642 if (G_LIKELY (keyval != GDK_KEY_p && keyval != GDK_KEY_n))
4643 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4644
4645 if ((modmask & GDK_CONTROL_MASK) != GDK_CONTROL_MASK)
4646 {
4647 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4648 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK, "move-cursor", 2, G_TYPE_ENUM, step, G_TYPE_INT, count);
4649 }
4650 }
4651
4652
4653
4654 static gboolean
4655 exo_icon_view_real_move_cursor (ExoIconView *icon_view,
4656 GtkMovementStep step,
4657 gint count)
4658 {
4659 GdkModifierType state;
4660
4661 _exo_return_val_if_fail (EXO_ICON_VIEW (icon_view), FALSE);
4662 _exo_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
4663 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
4664 step == GTK_MOVEMENT_DISPLAY_LINES ||
4665 step == GTK_MOVEMENT_PAGES ||
4666 step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
4667
4668 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4669 return FALSE;
4670
4671 exo_icon_view_stop_editing (icon_view, FALSE);
4672 EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
4673 gtk_widget_grab_focus (GTK_WIDGET (icon_view));
4674
4675 if (gtk_get_current_event_state (&state))
4676 {
4677 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
4678 icon_view->priv->ctrl_pressed = TRUE;
4679 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
4680 icon_view->priv->shift_pressed = TRUE;
4681 }
4682 /* else we assume not pressed */
4683
4684 switch (step)
4685 {
4686 case GTK_MOVEMENT_LOGICAL_POSITIONS:
4687 case GTK_MOVEMENT_VISUAL_POSITIONS:
4688 exo_icon_view_move_cursor_left_right (icon_view, count);
4689 break;
4690 case GTK_MOVEMENT_DISPLAY_LINES:
4691 exo_icon_view_move_cursor_up_down (icon_view, count);
4692 break;
4693 case GTK_MOVEMENT_PAGES:
4694 exo_icon_view_move_cursor_page_up_down (icon_view, count);
4695 break;
4696 case GTK_MOVEMENT_BUFFER_ENDS:
4697 exo_icon_view_move_cursor_start_end (icon_view, count);
4698 break;
4699 default:
4700 g_assert_not_reached ();
4701 }
4702
4703 icon_view->priv->ctrl_pressed = FALSE;
4704 icon_view->priv->shift_pressed = FALSE;
4705
4706 return TRUE;
4707 }
4708
4709
4710
4711 static gint
4712 find_cell (ExoIconView *icon_view,
4713 ExoIconViewItem *item,
4714 gint cell,
4715 GtkOrientation orientation,
4716 gint step,
4717 gint *count)
4718 {
4719 gint n_focusable;
4720 gint *focusable;
4721 gint first_text;
4722 gint current;
4723 gint i, k;
4724 GList *l;
4725
4726 if (icon_view->priv->orientation != orientation)
4727 return cell;
4728
4729 exo_icon_view_set_cell_data (icon_view, item);
4730
4731 focusable = g_new0 (gint, icon_view->priv->n_cells);
4732 n_focusable = 0;
4733
4734 first_text = 0;
4735 current = 0;
4736 for (k = 0; k < 2; k++)
4737 for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
4738 {
4739 ExoIconViewCellInfo *info = (ExoIconViewCellInfo *)l->data;
4740 GtkCellRendererMode mode;
4741
4742 if (info->pack == (k ? GTK_PACK_START : GTK_PACK_END))
4743 continue;
4744
4745 if (!gtk_cell_renderer_get_visible(info->cell))
4746 continue;
4747
4748 if (GTK_IS_CELL_RENDERER_TEXT (info->cell))
4749 first_text = i;
4750
4751 g_object_get(info->cell, "mode", &mode, NULL);
4752 if (mode != GTK_CELL_RENDERER_MODE_INERT)
4753 {
4754 if (cell == i)
4755 current = n_focusable;
4756
4757 focusable[n_focusable] = i;
4758
4759 n_focusable++;
4760 }
4761 }
4762
4763 if (n_focusable == 0)
4764 focusable[n_focusable++] = first_text;
4765
4766 if (cell < 0)
4767 {
4768 current = step > 0 ? 0 : n_focusable - 1;
4769 cell = focusable[current];
4770 }
4771
4772 if (current + *count < 0)
4773 {
4774 cell = -1;
4775 *count = current + *count;
4776 }
4777 else if (current + *count > n_focusable - 1)
4778 {
4779 cell = -1;
4780 *count = current + *count - (n_focusable - 1);
4781 }
4782 else
4783 {
4784 cell = focusable[current + *count];
4785 *count = 0;
4786 }
4787
4788 g_free (focusable);
4789
4790 return cell;
4791 }
4792
4793
4794
4795 static ExoIconViewItem *
4796 find_item_page_up_down (ExoIconView *icon_view,
4797 ExoIconViewItem *current,
4798 gint count)
4799 {
4800 GList *item = g_list_find (icon_view->priv->items, current);
4801 GList *next;
4802 gint col = current->col;
4803 gint y = current->area.y + count * gtk_adjustment_get_page_size(icon_view->priv->vadjustment);
4804
4805 if (count > 0)
4806 {
4807 for (; item != NULL; item = item->next)
4808 {
4809 for (next = item->next; next; next = next->next)
4810 if (EXO_ICON_VIEW_ITEM (next->data)->col == col)
4811 break;
4812
4813 if (next == NULL || EXO_ICON_VIEW_ITEM (next->data)->area.y > y)
4814 break;
4815 }
4816 }
4817 else
4818 {
4819 for (; item != NULL; item = item->prev)
4820 {
4821 for (next = item->prev; next; next = next->prev)
4822 if (EXO_ICON_VIEW_ITEM (next->data)->col == col)
4823 break;
4824
4825 if (next == NULL || EXO_ICON_VIEW_ITEM (next->data)->area.y < y)
4826 break;
4827 }
4828 }
4829
4830 return (item != NULL) ? item->data : NULL;
4831 }
4832
4833
4834
4835 static gboolean
4836 exo_icon_view_select_all_between (ExoIconView *icon_view,
4837 ExoIconViewItem *anchor,
4838 ExoIconViewItem *cursor)
4839 {
4840 GList *items;
4841 ExoIconViewItem *item, *last;
4842 gboolean dirty = FALSE;
4843
4844 for (items = icon_view->priv->items; items; items = items->next)
4845 {
4846 item = items->data;
4847
4848 if (item == anchor)
4849 {
4850 last = cursor;
4851 break;
4852 }
4853 else if (item == cursor)
4854 {
4855 last = anchor;
4856 break;
4857 }
4858 }
4859
4860 for (; items; items = items->next)
4861 {
4862 item = items->data;
4863
4864 if (!item->selected)
4865 {
4866 dirty = TRUE;
4867 item->selected = TRUE;
4868 exo_icon_view_item_selected_changed (icon_view, item);
4869 }
4870
4871 exo_icon_view_queue_draw_item (icon_view, item);
4872
4873 if (item == last)
4874 break;
4875 }
4876
4877 return dirty;
4878 }
4879
4880
4881
4882 static void
4883 exo_icon_view_move_cursor_up_down (ExoIconView *icon_view,
4884 gint count)
4885 {
4886 ExoIconViewItem *item;
4887 gboolean dirty = FALSE;
4888 GList *list;
4889 gint cell = -1;
4890 gint step;
4891
4892 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4893 return;
4894
4895 if (!icon_view->priv->cursor_item)
4896 {
4897 if (count > 0)
4898 list = icon_view->priv->items;
4899 else
4900 list = g_list_last (icon_view->priv->items);
4901
4902 item = list ? list->data : NULL;
4903 }
4904 else
4905 {
4906 item = icon_view->priv->cursor_item;
4907 cell = icon_view->priv->cursor_cell;
4908 step = count > 0 ? 1 : -1;
4909 while (item)
4910 {
4911 cell = find_cell (icon_view, item, cell,
4912 GTK_ORIENTATION_VERTICAL,
4913 step, &count);
4914 if (count == 0)
4915 break;
4916
4917 /* determine the list position for the item */
4918 list = g_list_find (icon_view->priv->items, item);
4919
4920 if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
4921 {
4922 /* determine the item in the next/prev row */
4923 if (step > 0)
4924 {
4925 for (list = list->next; list != NULL; list = list->next)
4926 if (EXO_ICON_VIEW_ITEM (list->data)->row == item->row + step
4927 && EXO_ICON_VIEW_ITEM (list->data)->col == item->col)
4928 break;
4929 }
4930 else
4931 {
4932 for (list = list->prev; list != NULL; list = list->prev)
4933 if (EXO_ICON_VIEW_ITEM (list->data)->row == item->row + step
4934 && EXO_ICON_VIEW_ITEM (list->data)->col == item->col)
4935 break;
4936 }
4937 }
4938 else
4939 {
4940 list = (step > 0) ? list->next : list->prev;
4941 }
4942
4943 /* check if we found a matching item */
4944 item = (list != NULL) ? list->data : NULL;
4945
4946 count = count - step;
4947 }
4948 }
4949
4950 if (item == icon_view->priv->cursor_item)
4951 gtk_widget_error_bell (GTK_WIDGET (icon_view));
4952
4953 if (!item)
4954 return;
4955
4956 if (icon_view->priv->ctrl_pressed ||
4957 !icon_view->priv->shift_pressed ||
4958 !icon_view->priv->anchor_item ||
4959 icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
4960 icon_view->priv->anchor_item = item;
4961
4962 exo_icon_view_set_cursor_item (icon_view, item, cell);
4963
4964 if (!icon_view->priv->ctrl_pressed &&
4965 icon_view->priv->selection_mode != GTK_SELECTION_NONE)
4966 {
4967 dirty = exo_icon_view_unselect_all_internal (icon_view);
4968 dirty = exo_icon_view_select_all_between (icon_view,
4969 icon_view->priv->anchor_item,
4970 item) || dirty;
4971 }
4972
4973 exo_icon_view_scroll_to_item (icon_view, item);
4974
4975 if (dirty)
4976 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
4977 }
4978
4979
4980
4981 static void
4982 exo_icon_view_move_cursor_page_up_down (ExoIconView *icon_view,
4983 gint count)
4984 {
4985 ExoIconViewItem *item;
4986 gboolean dirty = FALSE;
4987
4988 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
4989 return;
4990
4991 if (!icon_view->priv->cursor_item)
4992 {
4993 GList *list;
4994
4995 if (count > 0)
4996 list = icon_view->priv->items;
4997 else
4998 list = g_list_last (icon_view->priv->items);
4999
5000 item = list ? list->data : NULL;
5001 }
5002 else
5003 item = find_item_page_up_down (icon_view,
5004 icon_view->priv->cursor_item,
5005 count);
5006
5007 if (item == icon_view->priv->cursor_item)
5008 gtk_widget_error_bell (GTK_WIDGET (icon_view));
5009
5010 if (!item)
5011 return;
5012
5013 if (icon_view->priv->ctrl_pressed ||
5014 !icon_view->priv->shift_pressed ||
5015 !icon_view->priv->anchor_item ||
5016 icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5017 icon_view->priv->anchor_item = item;
5018
5019 exo_icon_view_set_cursor_item (icon_view, item, -1);
5020
5021 if (!icon_view->priv->ctrl_pressed &&
5022 icon_view->priv->selection_mode != GTK_SELECTION_NONE)
5023 {
5024 dirty = exo_icon_view_unselect_all_internal (icon_view);
5025 dirty = exo_icon_view_select_all_between (icon_view,
5026 icon_view->priv->anchor_item,
5027 item) || dirty;
5028 }
5029
5030 exo_icon_view_scroll_to_item (icon_view, item);
5031
5032 if (dirty)
5033 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5034 }
5035
5036
5037
5038 static void
5039 exo_icon_view_move_cursor_left_right (ExoIconView *icon_view,
5040 gint count)
5041 {
5042 ExoIconViewItem *item;
5043 gboolean dirty = FALSE;
5044 GList *list;
5045 gint cell = -1;
5046 gint step;
5047
5048 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
5049 return;
5050
5051 if (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL)
5052 count *= -1;
5053
5054 if (!icon_view->priv->cursor_item)
5055 {
5056 if (count > 0)
5057 list = icon_view->priv->items;
5058 else
5059 list = g_list_last (icon_view->priv->items);
5060
5061 item = list ? list->data : NULL;
5062 }
5063 else
5064 {
5065 item = icon_view->priv->cursor_item;
5066 cell = icon_view->priv->cursor_cell;
5067 step = count > 0 ? 1 : -1;
5068 while (item)
5069 {
5070 cell = find_cell (icon_view, item, cell,
5071 GTK_ORIENTATION_HORIZONTAL,
5072 step, &count);
5073 if (count == 0)
5074 break;
5075
5076 /* lookup the item in the list */
5077 list = g_list_find (icon_view->priv->items, item);
5078
5079 if (G_LIKELY (icon_view->priv->layout_mode == EXO_ICON_VIEW_LAYOUT_ROWS))
5080 {
5081 /* determine the next/prev list item depending on step,
5082 * support wrapping around on the edges, as requested
5083 * in http://bugzilla.xfce.org/show_bug.cgi?id=1623.
5084 */
5085 list = (step > 0) ? list->next : list->prev;
5086 }
5087 else
5088 {
5089 /* determine the item in the next/prev row */
5090 if (step > 0)
5091 {
5092 for (list = list->next; list != NULL; list = list->next)
5093 if (EXO_ICON_VIEW_ITEM (list->data)->col == item->col + step
5094 && EXO_ICON_VIEW_ITEM (list->data)->row == item->row)
5095 break;
5096 }
5097 else
5098 {
5099 for (list = list->prev; list != NULL; list = list->prev)
5100 if (EXO_ICON_VIEW_ITEM (list->data)->col == item->col + step
5101 && EXO_ICON_VIEW_ITEM (list->data)->row == item->row)
5102 break;
5103 }
5104 }
5105
5106 /* determine the item for the list position (if any) */
5107 item = (list != NULL) ? list->data : NULL;
5108
5109 count = count - step;
5110 }
5111 }
5112
5113 if (item == icon_view->priv->cursor_item)
5114 gtk_widget_error_bell (GTK_WIDGET (icon_view));
5115
5116 if (!item)
5117 return;
5118
5119 if (icon_view->priv->ctrl_pressed ||
5120 !icon_view->priv->shift_pressed ||
5121 !icon_view->priv->anchor_item ||
5122 icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5123 icon_view->priv->anchor_item = item;
5124
5125 exo_icon_view_set_cursor_item (icon_view, item, cell);
5126
5127 if (!icon_view->priv->ctrl_pressed &&
5128 icon_view->priv->selection_mode != GTK_SELECTION_NONE)
5129 {
5130 dirty = exo_icon_view_unselect_all_internal (icon_view);
5131 dirty = exo_icon_view_select_all_between (icon_view,
5132 icon_view->priv->anchor_item,
5133 item) || dirty;
5134 }
5135
5136 exo_icon_view_scroll_to_item (icon_view, item);
5137
5138 if (dirty)
5139 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5140 }
5141
5142
5143
5144 static void
5145 exo_icon_view_move_cursor_start_end (ExoIconView *icon_view,
5146 gint count)
5147 {
5148 ExoIconViewItem *item;
5149 gboolean dirty = FALSE;
5150 GList *lp;
5151
5152 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
5153 return;
5154
5155 lp = (count < 0) ? icon_view->priv->items : g_list_last (icon_view->priv->items);
5156 item = lp ? EXO_ICON_VIEW_ITEM (lp->data) : NULL;
5157
5158 if (item == icon_view->priv->cursor_item)
5159 gtk_widget_error_bell (GTK_WIDGET (icon_view));
5160
5161 if (G_UNLIKELY (item == NULL))
5162 return;
5163
5164 if (icon_view->priv->ctrl_pressed ||
5165 !icon_view->priv->shift_pressed ||
5166 !icon_view->priv->anchor_item ||
5167 icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5168 icon_view->priv->anchor_item = item;
5169
5170 exo_icon_view_set_cursor_item (icon_view, item, -1);
5171
5172 if (!icon_view->priv->ctrl_pressed &&
5173 icon_view->priv->selection_mode != GTK_SELECTION_NONE)
5174 {
5175 dirty = exo_icon_view_unselect_all_internal (icon_view);
5176 dirty = exo_icon_view_select_all_between (icon_view,
5177 icon_view->priv->anchor_item,
5178 item) || dirty;
5179 }
5180
5181 exo_icon_view_scroll_to_item (icon_view, item);
5182
5183 if (G_UNLIKELY (dirty))
5184 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
5185 }
5186
5187
5188
5189 static void
5190 exo_icon_view_scroll_to_item (ExoIconView *icon_view,
5191 ExoIconViewItem *item)
5192 {
5193 gint x, y;
5194 gint focus_width;
5195 GtkAllocation allocation;
5196 GList *lp;
5197 GdkRectangle rect;
5198
5199 gtk_widget_style_get (GTK_WIDGET (icon_view),
5200 "focus-line-width", &focus_width,
5201 NULL);
5202 gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
5203
5204 gdk_window_get_position (icon_view->priv->bin_window, &x, &y);
5205
5206 rect.x = item->area.x;
5207 rect.y = item->area.y;
5208 rect.width = rect.height = 0;
5209 for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
5210 {
5211 ExoIconViewCellInfo *info = EXO_ICON_VIEW_CELL_INFO (lp->data);
5212 if (G_UNLIKELY (!gtk_cell_renderer_get_visible(info->cell)))
5213 continue;
5214
5215 if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
5216 {
5217 rect.width += item->box[info->position].width + (info->position > 0 ? icon_view->priv->spacing : 0);
5218 rect.height = MAX (rect.height, item->box[info->position].height);
5219 }
5220 else
5221 {
5222 rect.width = MAX (rect.width, item->box[info->position].width);
5223 rect.height += item->box[info->position].height + (info->position > 0 ? icon_view->priv->spacing : 0);
5224 }
5225 }
5226
5227 if (y + rect.y - focus_width < 0)
5228 gtk_adjustment_set_value (icon_view->priv->vadjustment,
5229 gtk_adjustment_get_value(icon_view->priv->vadjustment) + y + rect.y - focus_width);
5230 else if (y + rect.y + rect.height + focus_width > allocation.height)
5231 gtk_adjustment_set_value (icon_view->priv->vadjustment,
5232 gtk_adjustment_get_value(icon_view->priv->vadjustment) + y + rect.y + rect.height
5233 + focus_width - allocation.height);
5234
5235 if (x + rect.x - focus_width < 0)
5236 {
5237 gtk_adjustment_set_value (icon_view->priv->hadjustment,
5238 gtk_adjustment_get_value(icon_view->priv->hadjustment) + x + rect.x - focus_width);
5239 }
5240 else if (x + rect.x + rect.width + focus_width > allocation.width
5241 && rect.width < allocation.width)
5242 {
5243 /* the second condition above is to make sure that we don't scroll horizontally if the item
5244 * width is larger than the allocation width. Fixes a weird scrolling bug in the compact view.
5245 * See http://bugzilla.xfce.org/show_bug.cgi?id=1683 for details.
5246 */
5247
5248 gtk_adjustment_set_value (icon_view->priv->hadjustment,
5249 gtk_adjustment_get_value(icon_view->priv->hadjustment) + x + rect.x + rect.width
5250 + focus_width - allocation.width);
5251 }
5252
5253 gtk_adjustment_changed (icon_view->priv->hadjustment);
5254 gtk_adjustment_changed (icon_view->priv->vadjustment);
5255 }
5256
5257
5258
5259 static ExoIconViewCellInfo *
5260 exo_icon_view_get_cell_info (ExoIconView *icon_view,
5261 GtkCellRenderer *renderer)
5262 {
5263 GList *lp;
5264
5265 for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
5266 if (EXO_ICON_VIEW_CELL_INFO (lp->data)->cell == renderer)
5267 return lp->data;
5268
5269 return NULL;
5270 }
5271
5272
5273
5274 static void
5275 exo_icon_view_set_cell_data (const ExoIconView *icon_view,
5276 ExoIconViewItem *item)
5277 {
5278 ExoIconViewCellInfo *info;
5279 GtkTreePath *path;
5280 GtkTreeIter iter;
5281 GValue value = {0, };
5282 GSList *slp;
5283 GList *lp;
5284
5285 if (G_UNLIKELY (!EXO_ICON_VIEW_FLAG_SET (icon_view, EXO_ICON_VIEW_ITERS_PERSIST)))
5286 {
5287 path = gtk_tree_path_new_from_indices (item->index, -1);
5288 gtk_tree_model_get_iter (icon_view->priv->model, &iter, path);
5289 gtk_tree_path_free (path);
5290 }
5291 else
5292 {
5293 iter = item->iter;
5294 }
5295
5296 for (lp = icon_view->priv->cell_list; lp != NULL; lp = lp->next)
5297 {
5298 info = EXO_ICON_VIEW_CELL_INFO (lp->data);
5299
5300 for (slp = info->attributes; slp != NULL && slp->next != NULL; slp = slp->next->next)
5301 {
5302 gtk_tree_model_get_value (icon_view->priv->model, &iter, GPOINTER_TO_INT (slp->next->data), &value);
5303 g_object_set_property (G_OBJECT (info->cell), slp->data, &value);
5304 g_value_unset (&value);
5305 }
5306
5307 if (G_UNLIKELY (info->func != NULL))
5308 (*info->func) (GTK_CELL_LAYOUT (icon_view), info->cell, icon_view->priv->model, &iter, info->func_data);
5309 }
5310 }
5311
5312
5313
5314 static void
5315 free_cell_attributes (ExoIconViewCellInfo *info)
5316 {
5317 GSList *lp;
5318
5319 for (lp = info->attributes; lp != NULL && lp->next != NULL; lp = lp->next->next)
5320 g_free (lp->data);
5321 g_slist_free (info->attributes);
5322 info->attributes = NULL;
5323 }
5324
5325
5326
5327 static void
5328 free_cell_info (ExoIconViewCellInfo *info)
5329 {
5330 if (G_UNLIKELY (info->destroy != NULL))
5331 (*info->destroy) (info->func_data);
5332
5333 free_cell_attributes (info);
5334 g_object_unref (G_OBJECT (info->cell));
5335 g_slice_free (ExoIconViewCellInfo, info);
5336 }
5337
5338
5339
5340 static void
5341 exo_icon_view_cell_layout_pack_start (GtkCellLayout *layout,
5342 GtkCellRenderer *renderer,
5343 gboolean expand)
5344 {
5345 ExoIconViewCellInfo *info;
5346 ExoIconView *icon_view = EXO_ICON_VIEW (layout);
5347
5348 _exo_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
5349 _exo_return_if_fail (exo_icon_view_get_cell_info (icon_view, renderer) == NULL);
5350
5351 g_object_ref_sink (renderer);
5352
5353 info = g_slice_new0 (ExoIconViewCellInfo);
5354 info->cell = renderer;
5355 info->expand = expand ? TRUE : FALSE;
5356 info->pack = GTK_PACK_START;
5357 info->position = icon_view->priv->n_cells;
5358 info->is_text = GTK_IS_CELL_RENDERER_TEXT (renderer);
5359
5360 icon_view->priv->cell_list = g_list_append (icon_view->priv->cell_list, info);
5361 icon_view->priv->n_cells++;
5362
5363 exo_icon_view_invalidate_sizes (icon_view);
5364 }
5365
5366
5367
5368 static void
5369 exo_icon_view_cell_layout_pack_end (GtkCellLayout *layout,
5370 GtkCellRenderer *renderer,
5371 gboolean expand)
5372 {
5373 ExoIconViewCellInfo *info;
5374 ExoIconView *icon_view = EXO_ICON_VIEW (layout);
5375
5376 _exo_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
5377 _exo_return_if_fail (exo_icon_view_get_cell_info (icon_view, renderer) == NULL);
5378
5379 g_object_ref_sink (renderer);
5380
5381 info = g_slice_new0 (ExoIconViewCellInfo);
5382 info->cell = renderer;
5383 info->expand = expand ? TRUE : FALSE;
5384 info->pack = GTK_PACK_END;
5385 info->position = icon_view->priv->n_cells;
5386 info->is_text = GTK_IS_CELL_RENDERER_TEXT (renderer);
5387
5388 icon_view->priv->cell_list = g_list_append (icon_view->priv->cell_list, info);
5389 icon_view->priv->n_cells++;
5390
5391 exo_icon_view_invalidate_sizes (icon_view);
5392 }
5393
5394
5395
5396 static void
5397 exo_icon_view_cell_layout_add_attribute (GtkCellLayout *layout,
5398 GtkCellRenderer *renderer,
5399 const gchar *attribute,
5400 gint column)
5401 {
5402 ExoIconViewCellInfo *info;
5403
5404 info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), renderer);
5405 if (G_LIKELY (info != NULL))
5406 {
5407 info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column));
5408 info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
5409
5410 exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5411 }
5412 }
5413
5414
5415
5416 static void
5417 exo_icon_view_cell_layout_clear (GtkCellLayout *layout)
5418 {
5419 ExoIconView *icon_view = EXO_ICON_VIEW (layout);
5420
5421 g_list_foreach (icon_view->priv->cell_list, (GFunc) free_cell_info, NULL);
5422 g_list_free (icon_view->priv->cell_list);
5423 icon_view->priv->cell_list = NULL;
5424 icon_view->priv->n_cells = 0;
5425
5426 exo_icon_view_invalidate_sizes (icon_view);
5427 }
5428
5429
5430
5431 static void
5432 exo_icon_view_cell_layout_set_cell_data_func (GtkCellLayout *layout,
5433 GtkCellRenderer *cell,
5434 GtkCellLayoutDataFunc func,
5435 gpointer func_data,
5436 GDestroyNotify destroy)
5437 {
5438 ExoIconViewCellInfo *info;
5439 GDestroyNotify notify;
5440
5441 info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), cell);
5442 if (G_LIKELY (info != NULL))
5443 {
5444 if (G_UNLIKELY (info->destroy != NULL))
5445 {
5446 notify = info->destroy;
5447 info->destroy = NULL;
5448 (*notify) (info->func_data);
5449 }
5450
5451 info->func = func;
5452 info->func_data = func_data;
5453 info->destroy = destroy;
5454
5455 exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5456 }
5457 }
5458
5459
5460
5461 static void
5462 exo_icon_view_cell_layout_clear_attributes (GtkCellLayout *layout,
5463 GtkCellRenderer *renderer)
5464 {
5465 ExoIconViewCellInfo *info;
5466
5467 info = exo_icon_view_get_cell_info (EXO_ICON_VIEW (layout), renderer);
5468 if (G_LIKELY (info != NULL))
5469 {
5470 free_cell_attributes (info);
5471
5472 exo_icon_view_invalidate_sizes (EXO_ICON_VIEW (layout));
5473 }
5474 }
5475
5476
5477
5478 static void
5479 exo_icon_view_cell_layout_reorder (GtkCellLayout *layout,
5480 GtkCellRenderer *cell,
5481 gint position)
5482 {
5483 ExoIconViewCellInfo *info;
5484 ExoIconView *icon_view = EXO_ICON_VIEW (layout);
5485 GList *lp;
5486 gint n;
5487
5488 icon_view = EXO_ICON_VIEW (layout);
5489
5490 info = exo_icon_view_get_cell_info (icon_view, cell);
5491 if (G_LIKELY (info != NULL))
5492 {
5493 lp = g_list_find (icon_view->priv->cell_list, info);
5494
5495 icon_view->priv->cell_list = g_list_remove_link (icon_view->priv->cell_list, lp);
5496 icon_view->priv->cell_list = g_list_insert (icon_view->priv->cell_list, info, position);
5497
5498 for (lp = icon_view->priv->cell_list, n = 0; lp != NULL; lp = lp->next, ++n)
5499 EXO_ICON_VIEW_CELL_INFO (lp->data)->position = n;
5500
5501 exo_icon_view_invalidate_sizes (icon_view);
5502 }
5503 }
5504
5505
5506
5507 /**
5508 * exo_icon_view_new:
5509 *
5510 * Creates a new #ExoIconView widget
5511 *
5512 * Return value: A newly created #ExoIconView widget
5513 **/
5514 GtkWidget*
5515 exo_icon_view_new (void)
5516 {
5517 return g_object_new (EXO_TYPE_ICON_VIEW, NULL);
5518 }
5519
5520
5521
5522 /**
5523 * exo_icon_view_new_with_model:
5524 * @model: The model.
5525 *
5526 * Creates a new #ExoIconView widget with the model @model.
5527 *
5528 * Return value: A newly created #ExoIconView widget.
5529 **/
5530 GtkWidget*
5531 exo_icon_view_new_with_model (GtkTreeModel *model)
5532 {
5533 g_return_val_if_fail (model == NULL || GTK_IS_TREE_MODEL (model), NULL);
5534
5535 return g_object_new (EXO_TYPE_ICON_VIEW,
5536 "model", model,
5537 NULL);
5538 }
5539
5540
5541
5542 /**
5543 * exo_icon_view_widget_to_icon_coords:
5544 * @icon_view : a #ExoIconView.
5545 * @wx : widget x coordinate.
5546 * @wy : widget y coordinate.
5547 * @ix : return location for icon x coordinate or %NULL.
5548 * @iy : return location for icon y coordinate or %NULL.
5549 *
5550 * Converts widget coordinates to coordinates for the icon window
5551 * (the full scrollable area of the icon view).
5552 **/
5553 void
5554 exo_icon_view_widget_to_icon_coords (const ExoIconView *icon_view,
5555 gint wx,
5556 gint wy,
5557 gint *ix,
5558 gint *iy)
5559 {
5560 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5561
5562 if (G_LIKELY (ix != NULL))
5563 *ix = wx + gtk_adjustment_get_value(icon_view->priv->hadjustment);
5564 if (G_LIKELY (iy != NULL))
5565 *iy = wy + gtk_adjustment_get_value(icon_view->priv->vadjustment);
5566 }
5567
5568
5569
5570 /**
5571 * exo_icon_view_icon_to_widget_coords:
5572 * @icon_view : a #ExoIconView.
5573 * @ix : icon x coordinate.
5574 * @iy : icon y coordinate.
5575 * @wx : return location for widget x coordinate or %NULL.
5576 * @wy : return location for widget y coordinate or %NULL.
5577 *
5578 * Converts icon view coordinates (coordinates in full scrollable
5579 * area of the icon view) to widget coordinates.
5580 **/
5581 void
5582 exo_icon_view_icon_to_widget_coords (const ExoIconView *icon_view,
5583 gint ix,
5584 gint iy,
5585 gint *wx,
5586 gint *wy)
5587 {
5588 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5589
5590 if (G_LIKELY (wx != NULL))
5591 *wx = ix - gtk_adjustment_get_value(icon_view->priv->hadjustment);
5592 if (G_LIKELY (wy != NULL))
5593 *wy = iy - gtk_adjustment_get_value(icon_view->priv->vadjustment);
5594 }
5595
5596
5597
5598 /**
5599 * exo_icon_view_get_path_at_pos:
5600 * @icon_view : A #ExoIconView.
5601 * @x : The x position to be identified
5602 * @y : The y position to be identified
5603 *
5604 * Finds the path at the point (@x, @y), relative to widget coordinates.
5605 * See exo_icon_view_get_item_at_pos(), if you are also interested in
5606 * the cell at the specified position.
5607 *
5608 * Return value: The #GtkTreePath corresponding to the icon or %NULL
5609 * if no icon exists at that position.
5610 **/
5611 GtkTreePath*
5612 exo_icon_view_get_path_at_pos (const ExoIconView *icon_view,
5613 gint x,
5614 gint y)
5615 {
5616 ExoIconViewItem *item;
5617
5618 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
5619
5620 /* translate the widget coordinates to icon window coordinates */
5621 x += gtk_adjustment_get_value(icon_view->priv->hadjustment);
5622 y += gtk_adjustment_get_value(icon_view->priv->vadjustment);
5623
5624 item = exo_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL);
5625
5626 return (item != NULL) ? gtk_tree_path_new_from_indices (item->index, -1) : NULL;
5627 }
5628
5629
5630
5631 /**
5632 * exo_icon_view_get_item_at_pos:
5633 * @icon_view: A #ExoIconView.
5634 * @x: The x position to be identified
5635 * @y: The y position to be identified
5636 * @path: Return location for the path, or %NULL
5637 * @cell: Return location for the renderer responsible for the cell
5638 * at (@x, @y), or %NULL
5639 *
5640 * Finds the path at the point (@x, @y), relative to widget coordinates.
5641 * In contrast to exo_icon_view_get_path_at_pos(), this function also
5642 * obtains the cell at the specified position. The returned path should
5643 * be freed with gtk_tree_path_free().
5644 *
5645 * Return value: %TRUE if an item exists at the specified position
5646 *
5647 * Since: 0.3.1
5648 **/
5649 gboolean
5650 exo_icon_view_get_item_at_pos (const ExoIconView *icon_view,
5651 gint x,
5652 gint y,
5653 GtkTreePath **path,
5654 GtkCellRenderer **cell)
5655 {
5656 ExoIconViewCellInfo *info = NULL;
5657 ExoIconViewItem *item;
5658
5659 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
5660
5661 item = exo_icon_view_get_item_at_coords (icon_view, x, y, TRUE, &info);
5662
5663 if (G_LIKELY (path != NULL))
5664 *path = (item != NULL) ? gtk_tree_path_new_from_indices (item->index, -1) : NULL;
5665
5666 if (G_LIKELY (cell != NULL))
5667 *cell = (info != NULL) ? info->cell : NULL;
5668
5669 return (item != NULL);
5670 }
5671
5672
5673
5674 /**
5675 * exo_icon_view_get_visible_range:
5676 * @icon_view : A #ExoIconView
5677 * @start_path : Return location for start of region, or %NULL
5678 * @end_path : Return location for end of region, or %NULL
5679 *
5680 * Sets @start_path and @end_path to be the first and last visible path.
5681 * Note that there may be invisible paths in between.
5682 *
5683 * Both paths should be freed with gtk_tree_path_free() after use.
5684 *
5685 * Return value: %TRUE, if valid paths were placed in @start_path and @end_path
5686 *
5687 * Since: 0.3.1
5688 **/
5689 gboolean
5690 exo_icon_view_get_visible_range (const ExoIconView *icon_view,
5691 GtkTreePath **start_path,
5692 GtkTreePath **end_path)
5693 {
5694 const ExoIconViewPrivate *priv = icon_view->priv;
5695 const ExoIconViewItem *item;
5696 const GList *lp;
5697 gint start_index = -1;
5698 gint end_index = -1;
5699 gint i;
5700
5701 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
5702
5703 if (priv->hadjustment == NULL || priv->vadjustment == NULL)
5704 return FALSE;
5705
5706 if (start_path == NULL && end_path == NULL)
5707 return FALSE;
5708
5709 for (i = 0, lp = priv->items; lp != NULL; ++i, lp = lp->next)
5710 {
5711 item = (const ExoIconViewItem *) lp->data;
5712 if ((item->area.x + item->area.width >= (gint) gtk_adjustment_get_value(priv->hadjustment)) &&
5713 (item->area.y + item->area.height >= (gint) gtk_adjustment_get_value(priv->vadjustment)) &&
5714 (item->area.x <= (gint) (gtk_adjustment_get_value(priv->hadjustment) + gtk_adjustment_get_page_size(priv->hadjustment))) &&
5715 (item->area.y <= (gint) (gtk_adjustment_get_value(priv->vadjustment) + gtk_adjustment_get_page_size(priv->vadjustment))))
5716 {
5717 if (start_index == -1)
5718 start_index = i;
5719 end_index = i;
5720 }
5721 }
5722
5723 if (start_path != NULL && start_index != -1)
5724 *start_path = gtk_tree_path_new_from_indices (start_index, -1);
5725 if (end_path != NULL && end_index != -1)
5726 *end_path = gtk_tree_path_new_from_indices (end_index, -1);
5727
5728 return (start_index != -1);
5729 }
5730
5731
5732
5733 /**
5734 * exo_icon_view_selected_foreach:
5735 * @icon_view : A #ExoIconView.
5736 * @func : The funcion to call for each selected icon.
5737 * @data : User data to pass to the function.
5738 *
5739 * Calls a function for each selected icon. Note that the model or
5740 * selection cannot be modified from within this function.
5741 **/
5742 void
5743 exo_icon_view_selected_foreach (ExoIconView *icon_view,
5744 ExoIconViewForeachFunc func,
5745 gpointer data)
5746 {
5747 GtkTreePath *path;
5748 GList *lp;
5749
5750 path = gtk_tree_path_new_first ();
5751 for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
5752 {
5753 if (EXO_ICON_VIEW_ITEM (lp->data)->selected)
5754 (*func) (icon_view, path, data);
5755 gtk_tree_path_next (path);
5756 }
5757 gtk_tree_path_free (path);
5758 }
5759
5760
5761
5762 /**
5763 * exo_icon_view_get_selection_mode:
5764 * @icon_view : A #ExoIconView.
5765 *
5766 * Gets the selection mode of the @icon_view.
5767 *
5768 * Return value: the current selection mode
5769 **/
5770 GtkSelectionMode
5771 exo_icon_view_get_selection_mode (const ExoIconView *icon_view)
5772 {
5773 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), GTK_SELECTION_SINGLE);
5774 return icon_view->priv->selection_mode;
5775 }
5776
5777
5778
5779 /**
5780 * exo_icon_view_set_selection_mode:
5781 * @icon_view : A #ExoIconView.
5782 * @mode : The selection mode
5783 *
5784 * Sets the selection mode of the @icon_view.
5785 **/
5786 void
5787 exo_icon_view_set_selection_mode (ExoIconView *icon_view,
5788 GtkSelectionMode mode)
5789 {
5790 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5791
5792 if (G_LIKELY (mode != icon_view->priv->selection_mode))
5793 {
5794 if (mode == GTK_SELECTION_NONE || icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
5795 exo_icon_view_unselect_all (icon_view);
5796
5797 icon_view->priv->selection_mode = mode;
5798
5799 g_object_notify (G_OBJECT (icon_view), "selection-mode");
5800 }
5801 }
5802
5803
5804
5805 /**
5806 * exo_icon_view_get_layout_mode:
5807 * @icon_view : A #ExoIconView.
5808 *
5809 * Returns the #ExoIconViewLayoutMode used to layout the
5810 * items in the @icon_view.
5811 *
5812 * Return value: the layout mode of @icon_view.
5813 *
5814 * Since: 0.3.1.5
5815 **/
5816 ExoIconViewLayoutMode
5817 exo_icon_view_get_layout_mode (const ExoIconView *icon_view)
5818 {
5819 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), EXO_ICON_VIEW_LAYOUT_ROWS);
5820 return icon_view->priv->layout_mode;
5821 }
5822
5823
5824
5825 /**
5826 * exo_icon_view_set_layout_mode:
5827 * @icon_view : a #ExoIconView.
5828 * @layout_mode : the new #ExoIconViewLayoutMode for @icon_view.
5829 *
5830 * Sets the layout mode of @icon_view to @layout_mode.
5831 *
5832 * Since: 0.3.1.5
5833 **/
5834 void
5835 exo_icon_view_set_layout_mode (ExoIconView *icon_view,
5836 ExoIconViewLayoutMode layout_mode)
5837 {
5838 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5839
5840 /* check if we have a new setting */
5841 if (G_LIKELY (icon_view->priv->layout_mode != layout_mode))
5842 {
5843 /* apply the new setting */
5844 icon_view->priv->layout_mode = layout_mode;
5845
5846 /* cancel any active cell editor */
5847 exo_icon_view_stop_editing (icon_view, TRUE);
5848
5849 /* invalidate the current item sizes */
5850 exo_icon_view_invalidate_sizes (icon_view);
5851 exo_icon_view_queue_layout (icon_view);
5852
5853 /* notify listeners */
5854 g_object_notify (G_OBJECT (icon_view), "layout-mode");
5855 }
5856 }
5857
5858
5859
5860 /**
5861 * exo_icon_view_get_model:
5862 * @icon_view : a #ExoIconView
5863 *
5864 * Returns the model the #ExoIconView is based on. Returns %NULL if the
5865 * model is unset.
5866 *
5867 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
5868 **/
5869 GtkTreeModel*
5870 exo_icon_view_get_model (const ExoIconView *icon_view)
5871 {
5872 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
5873 return icon_view->priv->model;
5874 }
5875
5876
5877
5878 /**
5879 * exo_icon_view_set_model:
5880 * @icon_view : A #ExoIconView.
5881 * @model : The model.
5882 *
5883 * Sets the model for a #ExoIconView.
5884 * If the @icon_view already has a model set, it will remove
5885 * it before setting the new model. If @model is %NULL, then
5886 * it will unset the old model.
5887 **/
5888 void
5889 exo_icon_view_set_model (ExoIconView *icon_view,
5890 GtkTreeModel *model)
5891 {
5892 ExoIconViewItem *item;
5893 GtkTreeIter iter;
5894 GList *items = NULL;
5895 GList *lp;
5896 gint n;
5897
5898 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
5899 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5900
5901 /* verify that we don't already use that model */
5902 if (G_UNLIKELY (icon_view->priv->model == model))
5903 return;
5904
5905 /* verify the new model */
5906 if (G_LIKELY (model != NULL))
5907 {
5908 g_return_if_fail (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_LIST_ONLY);
5909
5910 if (G_UNLIKELY (icon_view->priv->pixbuf_column != -1))
5911 g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->pixbuf_column) == GDK_TYPE_PIXBUF);
5912
5913 if (G_UNLIKELY (icon_view->priv->text_column != -1))
5914 g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->text_column) == G_TYPE_STRING);
5915
5916 if (G_UNLIKELY (icon_view->priv->markup_column != -1))
5917 g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->markup_column) == G_TYPE_STRING);
5918 }
5919
5920 /* be sure to cancel any pending editor */
5921 exo_icon_view_stop_editing (icon_view, TRUE);
5922
5923 /* disconnect from the previous model */
5924 if (G_LIKELY (icon_view->priv->model != NULL))
5925 {
5926 /* disconnect signals handlers from the previous model */
5927 g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_changed, icon_view);
5928 g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_inserted, icon_view);
5929 g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_row_deleted, icon_view);
5930 g_signal_handlers_disconnect_by_func (G_OBJECT (icon_view->priv->model), exo_icon_view_rows_reordered, icon_view);
5931
5932 /* release our reference on the model */
5933 g_object_unref (G_OBJECT (icon_view->priv->model));
5934
5935 /* drop all items belonging to the previous model */
5936 for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
5937 {
5938 g_free (EXO_ICON_VIEW_ITEM (lp->data)->box);
5939 g_slice_free (ExoIconViewItem, lp->data);
5940 }
5941 g_list_free (icon_view->priv->items);
5942 icon_view->priv->items = NULL;
5943
5944 /* reset statistics */
5945 icon_view->priv->search_column = -1;
5946 icon_view->priv->anchor_item = NULL;
5947 icon_view->priv->cursor_item = NULL;
5948 icon_view->priv->prelit_item = NULL;
5949 icon_view->priv->last_single_clicked = NULL;
5950 icon_view->priv->width = 0;
5951 icon_view->priv->height = 0;
5952
5953 /* cancel any pending single click timer */
5954 if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
5955 g_source_remove (icon_view->priv->single_click_timeout_id);
5956
5957 /* reset cursor when in single click mode and realized */
5958 if (G_UNLIKELY (icon_view->priv->single_click && gtk_widget_get_realized (GTK_WIDGET(icon_view)))) gdk_window_set_cursor (icon_view->priv->bin_window, NULL);
5959 }
5960
5961 /* be sure to drop any previous scroll_to_path reference,
5962 * as it points to the old (no longer valid) model.
5963 */
5964 if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
5965 {
5966 gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
5967 icon_view->priv->scroll_to_path = NULL;
5968 }
5969
5970 /* activate the new model */
5971 icon_view->priv->model = model;
5972
5973 /* connect to the new model */
5974 if (G_LIKELY (model != NULL))
5975 {
5976 /* take a reference on the model */
5977 g_object_ref (G_OBJECT (model));
5978
5979 /* connect signals */
5980 g_signal_connect (G_OBJECT (model), "row-changed", G_CALLBACK (exo_icon_view_row_changed), icon_view);
5981 g_signal_connect (G_OBJECT (model), "row-inserted", G_CALLBACK (exo_icon_view_row_inserted), icon_view);
5982 g_signal_connect (G_OBJECT (model), "row-deleted", G_CALLBACK (exo_icon_view_row_deleted), icon_view);
5983 g_signal_connect (G_OBJECT (model), "rows-reordered", G_CALLBACK (exo_icon_view_rows_reordered), icon_view);
5984
5985 /* check if the new model supports persistent iterators */
5986 if (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_ITERS_PERSIST)
5987 EXO_ICON_VIEW_SET_FLAG (icon_view, EXO_ICON_VIEW_ITERS_PERSIST);
5988 else
5989 EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_ITERS_PERSIST);
5990
5991 /* determine an appropriate search column */
5992 if (icon_view->priv->search_column <= 0)
5993 {
5994 /* we simply use the first string column */
5995 for (n = 0; n < gtk_tree_model_get_n_columns (model); ++n)
5996 if (g_value_type_transformable (gtk_tree_model_get_column_type (model, n), G_TYPE_STRING))
5997 {
5998 icon_view->priv->search_column = n;
5999 break;
6000 }
6001 }
6002
6003 /* build up the initial items list */
6004 if (gtk_tree_model_get_iter_first (model, &iter))
6005 {
6006 n = 0;
6007 do
6008 {
6009 item = g_slice_new0 (ExoIconViewItem);
6010 item->iter = iter;
6011 item->area.width = -1;
6012 item->index = n++;
6013 items = g_list_prepend (items, item);
6014 }
6015 while (gtk_tree_model_iter_next (model, &iter));
6016 }
6017 icon_view->priv->items = g_list_reverse (items);
6018
6019 /* layout the new items */
6020 exo_icon_view_queue_layout (icon_view);
6021 }
6022
6023 /* hide the interactive search dialog (if any) */
6024 if (G_LIKELY (icon_view->priv->search_window != NULL))
6025 exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
6026
6027 /* notify listeners */
6028 g_object_notify (G_OBJECT (icon_view), "model");
6029
6030 if (gtk_widget_get_realized (GTK_WIDGET (icon_view)))
6031 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
6032 }
6033
6034
6035
6036 static void
6037 update_text_cell (ExoIconView *icon_view)
6038 {
6039 ExoIconViewCellInfo *info;
6040 GList *l;
6041 gint i;
6042
6043 if (icon_view->priv->text_column == -1 &&
6044 icon_view->priv->markup_column == -1)
6045 {
6046 if (icon_view->priv->text_cell != -1)
6047 {
6048 info = g_list_nth_data (icon_view->priv->cell_list,
6049 icon_view->priv->text_cell);
6050
6051 icon_view->priv->cell_list = g_list_remove (icon_view->priv->cell_list, info);
6052
6053 free_cell_info (info);
6054
6055 icon_view->priv->n_cells--;
6056 icon_view->priv->text_cell = -1;
6057 }
6058 }
6059 else
6060 {
6061 if (icon_view->priv->text_cell == -1)
6062 {
6063 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6064 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), cell, FALSE);
6065 for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
6066 {
6067 info = l->data;
6068 if (info->cell == cell)
6069 {
6070 icon_view->priv->text_cell = i;
6071 break;
6072 }
6073 }
6074 }
6075
6076 info = g_list_nth_data (icon_view->priv->cell_list,
6077 icon_view->priv->text_cell);
6078
6079 if (icon_view->priv->markup_column != -1)
6080 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
6081 info->cell,
6082 "markup", icon_view->priv->markup_column,
6083 NULL);
6084 else
6085 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
6086 info->cell,
6087 "text", icon_view->priv->text_column,
6088 NULL);
6089 }
6090 }
6091
6092 static void
6093 update_pixbuf_cell (ExoIconView *icon_view)
6094 {
6095 ExoIconViewCellInfo *info;
6096 GList *l;
6097 gint i;
6098
6099 if (icon_view->priv->pixbuf_column == -1)
6100 {
6101 if (icon_view->priv->pixbuf_cell != -1)
6102 {
6103 info = g_list_nth_data (icon_view->priv->cell_list,
6104 icon_view->priv->pixbuf_cell);
6105
6106 icon_view->priv->cell_list = g_list_remove (icon_view->priv->cell_list, info);
6107
6108 free_cell_info (info);
6109
6110 icon_view->priv->n_cells--;
6111 icon_view->priv->pixbuf_cell = -1;
6112 }
6113 }
6114 else
6115 {
6116 if (icon_view->priv->pixbuf_cell == -1)
6117 {
6118 GtkCellRenderer *cell = gtk_cell_renderer_pixbuf_new ();
6119
6120 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), cell, FALSE);
6121 for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
6122 {
6123 info = l->data;
6124 if (info->cell == cell)
6125 {
6126 icon_view->priv->pixbuf_cell = i;
6127 break;
6128 }
6129 }
6130 }
6131
6132 info = g_list_nth_data (icon_view->priv->cell_list,
6133 icon_view->priv->pixbuf_cell);
6134
6135 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
6136 info->cell,
6137 "pixbuf", icon_view->priv->pixbuf_column,
6138 NULL);
6139 }
6140 }
6141
6142
6143
6144 /**
6145 * exo_icon_view_select_path:
6146 * @icon_view : A #ExoIconView.
6147 * @path : The #GtkTreePath to be selected.
6148 *
6149 * Selects the row at @path.
6150 **/
6151 void
6152 exo_icon_view_select_path (ExoIconView *icon_view,
6153 GtkTreePath *path)
6154 {
6155 ExoIconViewItem *item;
6156
6157 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6158 g_return_if_fail (icon_view->priv->model != NULL);
6159 g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6160
6161 item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6162 if (G_LIKELY (item != NULL))
6163 exo_icon_view_select_item (icon_view, item);
6164 }
6165
6166
6167
6168 /**
6169 * exo_icon_view_unselect_path:
6170 * @icon_view : A #ExoIconView.
6171 * @path : The #GtkTreePath to be unselected.
6172 *
6173 * Unselects the row at @path.
6174 **/
6175 void
6176 exo_icon_view_unselect_path (ExoIconView *icon_view,
6177 GtkTreePath *path)
6178 {
6179 ExoIconViewItem *item;
6180
6181 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6182 g_return_if_fail (icon_view->priv->model != NULL);
6183 g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6184
6185 item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6186 if (G_LIKELY (item != NULL))
6187 exo_icon_view_unselect_item (icon_view, item);
6188 }
6189
6190
6191
6192 /**
6193 * exo_icon_view_get_selected_items:
6194 * @icon_view: A #ExoIconView.
6195 *
6196 * Creates a list of paths of all selected items. Additionally, if you are
6197 * planning on modifying the model after calling this function, you may
6198 * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
6199 * To do this, you can use gtk_tree_row_reference_new().
6200 *
6201 * To free the return value, use:
6202 * <informalexample><programlisting>
6203 * g_list_foreach (list, gtk_tree_path_free, NULL);
6204 * g_list_free (list);
6205 * </programlisting></informalexample>
6206 *
6207 * Return value: A #GList containing a #GtkTreePath for each selected row.
6208 **/
6209 GList*
6210 exo_icon_view_get_selected_items (const ExoIconView *icon_view)
6211 {
6212 GList *selected = NULL;
6213 GList *lp;
6214 gint i;
6215
6216 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
6217
6218 for (i = 0, lp = icon_view->priv->items; lp != NULL; ++i, lp = lp->next)
6219 {
6220 if (EXO_ICON_VIEW_ITEM (lp->data)->selected)
6221 selected = g_list_append (selected, gtk_tree_path_new_from_indices (i, -1));
6222 }
6223
6224 return selected;
6225 }
6226
6227
6228
6229 gint exo_icon_view_count_selected_items (const ExoIconView *icon_view)
6230 {
6231 GList *lp;
6232 gint i = 0;
6233
6234 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), 0);
6235
6236 for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
6237 {
6238 if (EXO_ICON_VIEW_ITEM (lp->data)->selected)
6239 i++;
6240 }
6241
6242 return i;
6243 }
6244
6245
6246
6247 /**
6248 * exo_icon_view_select_all:
6249 * @icon_view : A #ExoIconView.
6250 *
6251 * Selects all the icons. @icon_view must has its selection mode set
6252 * to #GTK_SELECTION_MULTIPLE.
6253 **/
6254 void
6255 exo_icon_view_select_all (ExoIconView *icon_view)
6256 {
6257 GList *items;
6258 gboolean dirty = FALSE;
6259
6260 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6261
6262 if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
6263 return;
6264
6265 for (items = icon_view->priv->items; items; items = items->next)
6266 {
6267 ExoIconViewItem *item = items->data;
6268
6269 if (!item->selected)
6270 {
6271 dirty = TRUE;
6272 item->selected = TRUE;
6273 exo_icon_view_queue_draw_item (icon_view, item);
6274 }
6275 }
6276
6277 if (dirty)
6278 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
6279 }
6280
6281
6282
6283 /**
6284 * exo_icon_view_unselect_all:
6285 * @icon_view : A #ExoIconView.
6286 *
6287 * Unselects all the icons.
6288 **/
6289 void
6290 exo_icon_view_unselect_all (ExoIconView *icon_view)
6291 {
6292 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6293
6294 if (G_UNLIKELY (icon_view->priv->selection_mode == GTK_SELECTION_BROWSE))
6295 return;
6296
6297 if (exo_icon_view_unselect_all_internal (icon_view))
6298 g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0);
6299 }
6300
6301
6302
6303 /**
6304 * exo_icon_view_path_is_selected:
6305 * @icon_view: A #ExoIconView.
6306 * @path: A #GtkTreePath to check selection on.
6307 *
6308 * Returns %TRUE if the icon pointed to by @path is currently
6309 * selected. If @icon does not point to a valid location, %FALSE is returned.
6310 *
6311 * Return value: %TRUE if @path is selected.
6312 **/
6313 gboolean
6314 exo_icon_view_path_is_selected (const ExoIconView *icon_view,
6315 GtkTreePath *path)
6316 {
6317 ExoIconViewItem *item;
6318
6319 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
6320 g_return_val_if_fail (icon_view->priv->model != NULL, FALSE);
6321 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
6322
6323 item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6324
6325 return (item != NULL && item->selected);
6326 }
6327
6328
6329
6330 /**
6331 * exo_icon_view_item_activated:
6332 * @icon_view : a #ExoIconView
6333 * @path : the #GtkTreePath to be activated
6334 *
6335 * Activates the item determined by @path.
6336 **/
6337 void
6338 exo_icon_view_item_activated (ExoIconView *icon_view,
6339 GtkTreePath *path)
6340 {
6341 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6342 g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6343
6344 g_signal_emit (icon_view, icon_view_signals[ITEM_ACTIVATED], 0, path);
6345 }
6346
6347
6348
6349 /**
6350 * exo_icon_view_get_cursor:
6351 * @icon_view : A #ExoIconView
6352 * @path : Return location for the current cursor path, or %NULL
6353 * @cell : Return location the current focus cell, or %NULL
6354 *
6355 * Fills in @path and @cell with the current cursor path and cell.
6356 * If the cursor isn't currently set, then *@path will be %NULL.
6357 * If no cell currently has focus, then *@cell will be %NULL.
6358 *
6359 * The returned #GtkTreePath must be freed with gtk_tree_path_free().
6360 *
6361 * Return value: %TRUE if the cursor is set.
6362 *
6363 * Since: 0.3.1
6364 **/
6365 gboolean
6366 exo_icon_view_get_cursor (const ExoIconView *icon_view,
6367 GtkTreePath **path,
6368 GtkCellRenderer **cell)
6369 {
6370 ExoIconViewCellInfo *info;
6371 ExoIconViewItem *item;
6372
6373 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
6374
6375 item = icon_view->priv->cursor_item;
6376 info = (icon_view->priv->cursor_cell < 0) ? NULL : g_list_nth_data (icon_view->priv->cell_list, icon_view->priv->cursor_cell);
6377
6378 if (G_LIKELY (path != NULL))
6379 *path = (item != NULL) ? gtk_tree_path_new_from_indices (item->index, -1) : NULL;
6380
6381 if (G_LIKELY (cell != NULL))
6382 *cell = (info != NULL) ? info->cell : NULL;
6383
6384 return (item != NULL);
6385 }
6386
6387
6388
6389 /**
6390 * exo_icon_view_set_cursor:
6391 * @icon_view : a #ExoIconView
6392 * @path : a #GtkTreePath
6393 * @cell : a #GtkCellRenderer or %NULL
6394 * @start_editing : %TRUE if the specified cell should start being edited.
6395 *
6396 * Sets the current keyboard focus to be at @path, and selects it. This is
6397 * useful when you want to focus the user's attention on a particular item.
6398 * If @cell is not %NULL, then focus is given to the cell specified by
6399 * it. Additionally, if @start_editing is %TRUE, then editing should be
6400 * started in the specified cell.
6401 *
6402 * This function is often followed by <literal>gtk_widget_grab_focus
6403 * (icon_view)</literal> in order to give keyboard focus to the widget.
6404 * Please note that editing can only happen when the widget is realized.
6405 *
6406 * Since: 0.3.1
6407 **/
6408 void
6409 exo_icon_view_set_cursor (ExoIconView *icon_view,
6410 GtkTreePath *path,
6411 GtkCellRenderer *cell,
6412 gboolean start_editing)
6413 {
6414 ExoIconViewItem *item;
6415 ExoIconViewCellInfo *info = NULL;
6416 GList *l;
6417 gint i, cell_pos;
6418
6419 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6420 g_return_if_fail (path != NULL);
6421 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
6422
6423 exo_icon_view_stop_editing (icon_view, TRUE);
6424
6425 item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6426 if (G_UNLIKELY (item == NULL))
6427 return;
6428
6429 cell_pos = -1;
6430 for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++)
6431 {
6432 info = l->data;
6433
6434 if (info->cell == cell)
6435 {
6436 cell_pos = i;
6437 break;
6438 }
6439
6440 info = NULL;
6441 }
6442
6443 /* place the cursor on the item */
6444 exo_icon_view_set_cursor_item (icon_view, item, cell_pos);
6445 icon_view->priv->anchor_item = item;
6446
6447 /* scroll to the item (maybe delayed) */
6448 exo_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0f, 0.0f);
6449
6450 if (start_editing)
6451 exo_icon_view_start_editing (icon_view, item, info, NULL);
6452 }
6453
6454
6455
6456 /**
6457 * exo_icon_view_scroll_to_path:
6458 * @icon_view: A #ExoIconView.
6459 * @path: The path of the item to move to.
6460 * @use_align: whether to use alignment arguments, or %FALSE.
6461 * @row_align: The vertical alignment of the item specified by @path.
6462 * @col_align: The horizontal alignment of the item specified by @column.
6463 *
6464 * Moves the alignments of @icon_view to the position specified by @path.
6465 * @row_align determines where the row is placed, and @col_align determines where
6466 * @column is placed. Both are expected to be between 0.0 and 1.0.
6467 * 0.0 means left/top alignment, 1.0 means right/bottom alignment, 0.5 means center.
6468 *
6469 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
6470 * tree does the minimum amount of work to scroll the item onto the screen.
6471 * This means that the item will be scrolled to the edge closest to its current
6472 * position. If the item is currently visible on the screen, nothing is done.
6473 *
6474 * This function only works if the model is set, and @path is a valid row on the
6475 * model. If the model changes before the @tree_view is realized, the centered
6476 * path will be modified to reflect this change.
6477 *
6478 * Since: 0.3.1
6479 **/
6480 void
6481 exo_icon_view_scroll_to_path (ExoIconView *icon_view,
6482 GtkTreePath *path,
6483 gboolean use_align,
6484 gfloat row_align,
6485 gfloat col_align)
6486 {
6487 ExoIconViewItem *item;
6488
6489 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6490 g_return_if_fail (gtk_tree_path_get_depth (path) > 0);
6491 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
6492 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
6493
6494 /* Delay scrolling if either not realized or pending layout() */
6495 if (!gtk_widget_get_realized (GTK_WIDGET (icon_view)) || icon_view->priv->layout_idle_id != 0)
6496 {
6497 /* release the previous scroll_to_path reference */
6498 if (G_UNLIKELY (icon_view->priv->scroll_to_path != NULL))
6499 gtk_tree_row_reference_free (icon_view->priv->scroll_to_path);
6500
6501 /* remember a reference for the new path and settings */
6502 icon_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path);
6503 icon_view->priv->scroll_to_use_align = use_align;
6504 icon_view->priv->scroll_to_row_align = row_align;
6505 icon_view->priv->scroll_to_col_align = col_align;
6506 }
6507 else
6508 {
6509 item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices(path)[0]);
6510 if (G_UNLIKELY (item == NULL))
6511 return;
6512
6513 if (use_align)
6514 {
6515 gint x, y;
6516 gint focus_width;
6517 gfloat offset, value;
6518 GtkAllocation allocation;
6519
6520 gtk_widget_style_get (GTK_WIDGET (icon_view),
6521 "focus-line-width", &focus_width,
6522 NULL);
6523 gtk_widget_get_allocation (GTK_WIDGET (icon_view), &allocation);
6524
6525 gdk_window_get_position (icon_view->priv->bin_window, &x, &y);
6526
6527 offset = y + item->area.y - focus_width -
6528 row_align * (allocation.height - item->area.height);
6529 value = CLAMP (gtk_adjustment_get_value(icon_view->priv->vadjustment) + offset,
6530 gtk_adjustment_get_lower(icon_view->priv->vadjustment),
6531 gtk_adjustment_get_upper(icon_view->priv->vadjustment) - gtk_adjustment_get_page_size(icon_view->priv->vadjustment));
6532 gtk_adjustment_set_value (icon_view->priv->vadjustment, value);
6533
6534 offset = x + item->area.x - focus_width -
6535 col_align * (allocation.width - item->area.width);
6536 value = CLAMP (gtk_adjustment_get_value(icon_view->priv->hadjustment) + offset,
6537 gtk_adjustment_get_lower(icon_view->priv->hadjustment),
6538 gtk_adjustment_get_upper(icon_view->priv->hadjustment) - gtk_adjustment_get_page_size(icon_view->priv->hadjustment));
6539 gtk_adjustment_set_value (icon_view->priv->hadjustment, value);
6540
6541 gtk_adjustment_changed (icon_view->priv->hadjustment);
6542 gtk_adjustment_changed (icon_view->priv->vadjustment);
6543 }
6544 else
6545 {
6546 exo_icon_view_scroll_to_item (icon_view, item);
6547 }
6548 }
6549 }
6550
6551
6552
6553 /**
6554 * exo_icon_view_get_orientation:
6555 * @icon_view : a #ExoIconView
6556 *
6557 * Returns the value of the ::orientation property which determines
6558 * whether the labels are drawn beside the icons instead of below.
6559 *
6560 * Return value: the relative position of texts and icons
6561 *
6562 * Since: 0.3.1
6563 **/
6564 GtkOrientation
6565 exo_icon_view_get_orientation (const ExoIconView *icon_view)
6566 {
6567 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), GTK_ORIENTATION_VERTICAL);
6568 return icon_view->priv->orientation;
6569 }
6570
6571
6572
6573 /**
6574 * exo_icon_view_set_orientation:
6575 * @icon_view : a #ExoIconView
6576 * @orientation : the relative position of texts and icons
6577 *
6578 * Sets the ::orientation property which determines whether the labels
6579 * are drawn beside the icons instead of below.
6580 *
6581 * Since: 0.3.1
6582 **/
6583 void
6584 exo_icon_view_set_orientation (ExoIconView *icon_view,
6585 GtkOrientation orientation)
6586 {
6587 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6588
6589 if (G_LIKELY (icon_view->priv->orientation != orientation))
6590 {
6591 icon_view->priv->orientation = orientation;
6592
6593 exo_icon_view_stop_editing (icon_view, TRUE);
6594 exo_icon_view_invalidate_sizes (icon_view);
6595
6596 update_text_cell (icon_view);
6597 update_pixbuf_cell (icon_view);
6598
6599 g_object_notify (G_OBJECT (icon_view), "orientation");
6600 }
6601 }
6602
6603
6604
6605 /**
6606 * exo_icon_view_get_columns:
6607 * @icon_view: a #ExoIconView
6608 *
6609 * Returns the value of the ::columns property.
6610 *
6611 * Return value: the number of columns, or -1
6612 */
6613 gint
6614 exo_icon_view_get_columns (const ExoIconView *icon_view)
6615 {
6616 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6617 return icon_view->priv->columns;
6618 }
6619
6620
6621
6622 /**
6623 * exo_icon_view_set_columns:
6624 * @icon_view : a #ExoIconView
6625 * @columns : the number of columns
6626 *
6627 * Sets the ::columns property which determines in how
6628 * many columns the icons are arranged. If @columns is
6629 * -1, the number of columns will be chosen automatically
6630 * to fill the available area.
6631 *
6632 * Since: 0.3.1
6633 */
6634 void
6635 exo_icon_view_set_columns (ExoIconView *icon_view,
6636 gint columns)
6637 {
6638 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6639
6640 if (G_LIKELY (icon_view->priv->columns != columns))
6641 {
6642 icon_view->priv->columns = columns;
6643
6644 exo_icon_view_stop_editing (icon_view, TRUE);
6645 exo_icon_view_queue_layout (icon_view);
6646
6647 g_object_notify (G_OBJECT (icon_view), "columns");
6648 }
6649 }
6650
6651
6652
6653 /**
6654 * exo_icon_view_get_item_width:
6655 * @icon_view: a #ExoIconView
6656 *
6657 * Returns the value of the ::item-width property.
6658 *
6659 * Return value: the width of a single item, or -1
6660 *
6661 * Since: 0.3.1
6662 */
6663 gint
6664 exo_icon_view_get_item_width (const ExoIconView *icon_view)
6665 {
6666 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6667 return icon_view->priv->item_width;
6668 }
6669
6670
6671
6672 /**
6673 * exo_icon_view_set_item_width:
6674 * @icon_view : a #ExoIconView
6675 * @item_width : the width for each item
6676 *
6677 * Sets the ::item-width property which specifies the width
6678 * to use for each item. If it is set to -1, the icon view will
6679 * automatically determine a suitable item size.
6680 *
6681 * Since: 0.3.1
6682 */
6683 void
6684 exo_icon_view_set_item_width (ExoIconView *icon_view,
6685 gint item_width)
6686 {
6687 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6688
6689 if (icon_view->priv->item_width != item_width)
6690 {
6691 icon_view->priv->item_width = item_width;
6692
6693 exo_icon_view_stop_editing (icon_view, TRUE);
6694 exo_icon_view_invalidate_sizes (icon_view);
6695
6696 update_text_cell (icon_view);
6697
6698 g_object_notify (G_OBJECT (icon_view), "item-width");
6699 }
6700 }
6701
6702
6703
6704 /**
6705 * exo_icon_view_get_spacing:
6706 * @icon_view: a #ExoIconView
6707 *
6708 * Returns the value of the ::spacing property.
6709 *
6710 * Return value: the space between cells
6711 *
6712 * Since: 0.3.1
6713 */
6714 gint
6715 exo_icon_view_get_spacing (const ExoIconView *icon_view)
6716 {
6717 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6718 return icon_view->priv->spacing;
6719 }
6720
6721
6722
6723 /**
6724 * exo_icon_view_set_spacing:
6725 * @icon_view : a #ExoIconView
6726 * @spacing : the spacing
6727 *
6728 * Sets the ::spacing property which specifies the space
6729 * which is inserted between the cells (i.e. the icon and
6730 * the text) of an item.
6731 *
6732 * Since: 0.3.1
6733 */
6734 void
6735 exo_icon_view_set_spacing (ExoIconView *icon_view,
6736 gint spacing)
6737 {
6738 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6739
6740 if (G_LIKELY (icon_view->priv->spacing != spacing))
6741 {
6742 icon_view->priv->spacing = spacing;
6743
6744 exo_icon_view_stop_editing (icon_view, TRUE);
6745 exo_icon_view_invalidate_sizes (icon_view);
6746
6747 g_object_notify (G_OBJECT (icon_view), "spacing");
6748 }
6749 }
6750
6751
6752
6753 /**
6754 * exo_icon_view_get_row_spacing:
6755 * @icon_view: a #ExoIconView
6756 *
6757 * Returns the value of the ::row-spacing property.
6758 *
6759 * Return value: the space between rows
6760 *
6761 * Since: 0.3.1
6762 */
6763 gint
6764 exo_icon_view_get_row_spacing (const ExoIconView *icon_view)
6765 {
6766 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6767 return icon_view->priv->row_spacing;
6768 }
6769
6770
6771
6772 /**
6773 * exo_icon_view_set_row_spacing:
6774 * @icon_view : a #ExoIconView
6775 * @row_spacing : the row spacing
6776 *
6777 * Sets the ::row-spacing property which specifies the space
6778 * which is inserted between the rows of the icon view.
6779 *
6780 * Since: 0.3.1
6781 */
6782 void
6783 exo_icon_view_set_row_spacing (ExoIconView *icon_view,
6784 gint row_spacing)
6785 {
6786 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6787
6788 if (G_LIKELY (icon_view->priv->row_spacing != row_spacing))
6789 {
6790 icon_view->priv->row_spacing = row_spacing;
6791
6792 exo_icon_view_stop_editing (icon_view, TRUE);
6793 exo_icon_view_invalidate_sizes (icon_view);
6794
6795 g_object_notify (G_OBJECT (icon_view), "row-spacing");
6796 }
6797 }
6798
6799
6800
6801 /**
6802 * exo_icon_view_get_column_spacing:
6803 * @icon_view: a #ExoIconView
6804 *
6805 * Returns the value of the ::column-spacing property.
6806 *
6807 * Return value: the space between columns
6808 *
6809 * Since: 0.3.1
6810 **/
6811 gint
6812 exo_icon_view_get_column_spacing (const ExoIconView *icon_view)
6813 {
6814 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6815 return icon_view->priv->column_spacing;
6816 }
6817
6818
6819
6820 /**
6821 * exo_icon_view_set_column_spacing:
6822 * @icon_view : a #ExoIconView
6823 * @column_spacing : the column spacing
6824 *
6825 * Sets the ::column-spacing property which specifies the space
6826 * which is inserted between the columns of the icon view.
6827 *
6828 * Since: 0.3.1
6829 **/
6830 void
6831 exo_icon_view_set_column_spacing (ExoIconView *icon_view,
6832 gint column_spacing)
6833 {
6834 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6835
6836 if (G_LIKELY (icon_view->priv->column_spacing != column_spacing))
6837 {
6838 icon_view->priv->column_spacing = column_spacing;
6839
6840 exo_icon_view_stop_editing (icon_view, TRUE);
6841 exo_icon_view_invalidate_sizes (icon_view);
6842
6843 g_object_notify (G_OBJECT (icon_view), "column-spacing");
6844 }
6845 }
6846
6847
6848
6849 /**
6850 * exo_icon_view_get_margin:
6851 * @icon_view : a #ExoIconView
6852 *
6853 * Returns the value of the ::margin property.
6854 *
6855 * Return value: the space at the borders
6856 *
6857 * Since: 0.3.1
6858 **/
6859 gint
6860 exo_icon_view_get_margin (const ExoIconView *icon_view)
6861 {
6862 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
6863 return icon_view->priv->margin;
6864 }
6865
6866
6867
6868 /**
6869 * exo_icon_view_set_margin:
6870 * @icon_view : a #ExoIconView
6871 * @margin : the margin
6872 *
6873 * Sets the ::margin property which specifies the space
6874 * which is inserted at the top, bottom, left and right
6875 * of the icon view.
6876 *
6877 * Since: 0.3.1
6878 **/
6879 void
6880 exo_icon_view_set_margin (ExoIconView *icon_view,
6881 gint margin)
6882 {
6883 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
6884
6885 if (G_LIKELY (icon_view->priv->margin != margin))
6886 {
6887 icon_view->priv->margin = margin;
6888
6889 exo_icon_view_stop_editing (icon_view, TRUE);
6890 exo_icon_view_invalidate_sizes (icon_view);
6891
6892 g_object_notify (G_OBJECT (icon_view), "margin");
6893 }
6894 }
6895
6896
6897
6898 /* Get/set whether drag_motion requested the drag data and
6899 * drag_data_received should thus not actually insert the data,
6900 * since the data doesn't result from a drop.
6901 */
6902 static void
6903 set_status_pending (GdkDragContext *context,
6904 GdkDragAction suggested_action)
6905 {
6906 g_object_set_data (G_OBJECT (context),
6907 I_("exo-icon-view-status-pending"),
6908 GINT_TO_POINTER (suggested_action));
6909 }
6910
6911 static GdkDragAction
6912 get_status_pending (GdkDragContext *context)
6913 {
6914 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), I_("exo-icon-view-status-pending")));
6915 }
6916
6917 static void
6918 unset_reorderable (ExoIconView *icon_view)
6919 {
6920 if (icon_view->priv->reorderable)
6921 {
6922 icon_view->priv->reorderable = FALSE;
6923 g_object_notify (G_OBJECT (icon_view), "reorderable");
6924 }
6925 }
6926
6927 static void
6928 clear_source_info (ExoIconView *icon_view)
6929 {
6930 if (icon_view->priv->source_targets)
6931 gtk_target_list_unref (icon_view->priv->source_targets);
6932 icon_view->priv->source_targets = NULL;
6933
6934 icon_view->priv->source_set = FALSE;
6935 }
6936
6937 static void
6938 clear_dest_info (ExoIconView *icon_view)
6939 {
6940 if (icon_view->priv->dest_targets)
6941 gtk_target_list_unref (icon_view->priv->dest_targets);
6942 icon_view->priv->dest_targets = NULL;
6943
6944 icon_view->priv->dest_set = FALSE;
6945 }
6946
6947 static void
6948 set_source_row (GdkDragContext *context,
6949 GtkTreeModel *model,
6950 GtkTreePath *source_row)
6951 {
6952 if (source_row)
6953 g_object_set_data_full (G_OBJECT (context),
6954 I_("exo-icon-view-source-row"),
6955 gtk_tree_row_reference_new (model, source_row),
6956 (GDestroyNotify) gtk_tree_row_reference_free);
6957 else
6958 g_object_set_data_full (G_OBJECT (context),
6959 I_("exo-icon-view-source-row"),
6960 NULL, NULL);
6961 }
6962
6963 static GtkTreePath*
6964 get_source_row (GdkDragContext *context)
6965 {
6966 GtkTreeRowReference *ref;
6967
6968 ref = g_object_get_data (G_OBJECT (context), I_("exo-icon-view-source-row"));
6969
6970 if (ref)
6971 return gtk_tree_row_reference_get_path (ref);
6972 else
6973 return NULL;
6974 }
6975
6976 typedef struct
6977 {
6978 GtkTreeRowReference *dest_row;
6979 gboolean empty_view_drop;
6980 gboolean drop_append_mode;
6981 } DestRow;
6982
6983 static void
6984 dest_row_free (gpointer data)
6985 {
6986 DestRow *dr = (DestRow *)data;
6987
6988 gtk_tree_row_reference_free (dr->dest_row);
6989 g_slice_free (DestRow, dr);
6990 }
6991
6992 static void
6993 set_dest_row (GdkDragContext *context,
6994 GtkTreeModel *model,
6995 GtkTreePath *dest_row,
6996 gboolean empty_view_drop,
6997 gboolean drop_append_mode)
6998 {
6999 DestRow *dr;
7000
7001 if (!dest_row)
7002 {
7003 g_object_set_data_full (G_OBJECT (context),
7004 I_("exo-icon-view-dest-row"),
7005 NULL, NULL);
7006 return;
7007 }
7008
7009 dr = g_slice_new0 (DestRow);
7010
7011 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
7012 dr->empty_view_drop = empty_view_drop;
7013 dr->drop_append_mode = drop_append_mode;
7014 g_object_set_data_full (G_OBJECT (context),
7015 I_("exo-icon-view-dest-row"),
7016 dr, (GDestroyNotify) dest_row_free);
7017 }
7018
7019
7020
7021 static GtkTreePath*
7022 get_dest_row (GdkDragContext *context)
7023 {
7024 DestRow *dr;
7025
7026 dr = g_object_get_data (G_OBJECT (context), I_("exo-icon-view-dest-row"));
7027
7028 if (dr)
7029 {
7030 GtkTreePath *path = NULL;
7031
7032 if (dr->dest_row)
7033 path = gtk_tree_row_reference_get_path (dr->dest_row);
7034 else if (dr->empty_view_drop)
7035 path = gtk_tree_path_new_from_indices (0, -1);
7036 else
7037 path = NULL;
7038
7039 if (path && dr->drop_append_mode)
7040 gtk_tree_path_next (path);
7041
7042 return path;
7043 }
7044 else
7045 return NULL;
7046 }
7047
7048
7049
7050 static gboolean
7051 check_model_dnd (GtkTreeModel *model,
7052 GType required_iface,
7053 const gchar *_signal)
7054 {
7055 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
7056 {
7057 g_warning ("You must override the default '%s' handler "
7058 "on ExoIconView when using models that don't support "
7059 "the %s interface and enabling drag-and-drop. The simplest way to do this "
7060 "is to connect to '%s' and call "
7061 "g_signal_stop_emission_by_name() in your signal handler to prevent "
7062 "the default handler from running. Look at the source code "
7063 "for the default handler in gtkiconview.c to get an idea what "
7064 "your handler should do. (gtkiconview.c is in the GTK+ source "
7065 "code.) If you're using GTK+ from a language other than C, "
7066 "there may be a more natural way to override default handlers, e.g. via derivation.",
7067 _signal, g_type_name (required_iface), _signal);
7068 return FALSE;
7069 }
7070 else
7071 return TRUE;
7072 }
7073
7074
7075
7076 static void
7077 remove_scroll_timeout (ExoIconView *icon_view)
7078 {
7079 if (icon_view->priv->scroll_timeout_id != 0)
7080 {
7081 g_source_remove (icon_view->priv->scroll_timeout_id);
7082
7083 icon_view->priv->scroll_timeout_id = 0;
7084 }
7085 }
7086
7087
7088
7089 static void
7090 exo_icon_view_autoscroll (ExoIconView *icon_view)
7091 {
7092 gint px, py, x, y, width, height;
7093 gint hoffset, voffset;
7094 gfloat value;
7095 GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (icon_view));
7096
7097 gdk_window_get_device_position (window,
7098 gdk_device_manager_get_client_pointer(
7099 gdk_display_get_device_manager(
7100 gdk_window_get_display(window))),
7101 &px, &py, NULL);
7102 #if GTK_CHECK_VERSION(3, 0, 0)
7103 gdk_window_get_geometry (window, &x, &y, &width, &height);
7104 #else
7105 gdk_window_get_geometry (window, &x, &y, &width, &height, NULL);
7106 #endif
7107
7108 /* see if we are near the edge. */
7109 voffset = py - (y + 2 * SCROLL_EDGE_SIZE);
7110 if (voffset > 0)
7111 voffset = MAX (py - (y + height - 2 * SCROLL_EDGE_SIZE), 0);
7112
7113 hoffset = px - (x + 2 * SCROLL_EDGE_SIZE);
7114 if (hoffset > 0)
7115 hoffset = MAX (px - (x + width - 2 * SCROLL_EDGE_SIZE), 0);
7116
7117 if (voffset != 0)
7118 {
7119 value = CLAMP (gtk_adjustment_get_value(icon_view->priv->vadjustment) + voffset,
7120 gtk_adjustment_get_lower(icon_view->priv->vadjustment),
7121 gtk_adjustment_get_upper(icon_view->priv->vadjustment) - gtk_adjustment_get_page_size(icon_view->priv->vadjustment));
7122 gtk_adjustment_set_value (icon_view->priv->vadjustment, value);
7123 }
7124 if (hoffset != 0)
7125 {
7126 value = CLAMP (gtk_adjustment_get_value(icon_view->priv->hadjustment) + hoffset,
7127 gtk_adjustment_get_lower(icon_view->priv->hadjustment),
7128 gtk_adjustment_get_upper(icon_view->priv->hadjustment) - gtk_adjustment_get_page_size(icon_view->priv->hadjustment));
7129 gtk_adjustment_set_value (icon_view->priv->hadjustment, value);
7130 }
7131 }
7132
7133
7134 static gboolean
7135 drag_scroll_timeout (gpointer data)
7136 {
7137 ExoIconView *icon_view = EXO_ICON_VIEW (data);
7138
7139 if(!g_source_is_destroyed(g_main_current_source()))
7140 exo_icon_view_autoscroll (icon_view);
7141
7142 return TRUE;
7143 }
7144
7145
7146 static gboolean
7147 set_destination (ExoIconView *icon_view,
7148 GdkDragContext *context,
7149 gint x,
7150 gint y,
7151 GdkDragAction *suggested_action,
7152 GdkAtom *target)
7153 {
7154 GtkWidget *widget;
7155 GtkTreePath *path = NULL;
7156 ExoIconViewDropPosition pos;
7157 ExoIconViewDropPosition old_pos;
7158 GtkTreePath *old_dest_path = NULL;
7159 gboolean can_drop = FALSE;
7160
7161 widget = GTK_WIDGET (icon_view);
7162
7163 *suggested_action = 0;
7164 *target = GDK_NONE;
7165
7166 if (!icon_view->priv->dest_set)
7167 {
7168 /* someone unset us as a drag dest, note that if
7169 * we return FALSE drag_leave isn't called
7170 */
7171
7172 exo_icon_view_set_drag_dest_item (icon_view,
7173 NULL,
7174 EXO_ICON_VIEW_DROP_LEFT);
7175
7176 remove_scroll_timeout (EXO_ICON_VIEW (widget));
7177
7178 return FALSE; /* no longer a drop site */
7179 }
7180
7181 *target = gtk_drag_dest_find_target (widget, context, icon_view->priv->dest_targets);
7182 if (*target == GDK_NONE)
7183 return FALSE;
7184
7185 if (!exo_icon_view_get_dest_item_at_pos (icon_view, x, y, &path, &pos))
7186 {
7187 gint n_children;
7188 GtkTreeModel *model;
7189
7190 /* the row got dropped on empty space, let's setup a special case
7191 */
7192
7193 if (path)
7194 gtk_tree_path_free (path);
7195
7196 model = exo_icon_view_get_model (icon_view);
7197
7198 n_children = gtk_tree_model_iter_n_children (model, NULL);
7199 if (n_children)
7200 {
7201 pos = EXO_ICON_VIEW_DROP_BELOW;
7202 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
7203 }
7204 else
7205 {
7206 pos = EXO_ICON_VIEW_DROP_ABOVE;
7207 path = gtk_tree_path_new_from_indices (0, -1);
7208 }
7209
7210 can_drop = TRUE;
7211
7212 goto out;
7213 }
7214
7215 g_assert (path);
7216
7217 exo_icon_view_get_drag_dest_item (icon_view,
7218 &old_dest_path,
7219 &old_pos);
7220
7221 if (old_dest_path)
7222 gtk_tree_path_free (old_dest_path);
7223
7224 if (TRUE /* FIXME if the location droppable predicate */)
7225 {
7226 can_drop = TRUE;
7227 }
7228
7229 out:
7230 if (can_drop)
7231 {
7232 GtkWidget *source_widget;
7233
7234 *suggested_action = gdk_drag_context_get_suggested_action (context);
7235 source_widget = gtk_drag_get_source_widget (context);
7236
7237 if (source_widget == widget)
7238 {
7239 /* Default to MOVE, unless the user has
7240 * pressed ctrl or shift to affect available actions
7241 */
7242 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
7243 *suggested_action = GDK_ACTION_MOVE;
7244 }
7245
7246 exo_icon_view_set_drag_dest_item (EXO_ICON_VIEW (widget),
7247 path, pos);
7248 }
7249 else
7250 {
7251 /* can't drop here */
7252 exo_icon_view_set_drag_dest_item (EXO_ICON_VIEW (widget),
7253 NULL,
7254 EXO_ICON_VIEW_DROP_LEFT);
7255 }
7256
7257 if (path)
7258 gtk_tree_path_free (path);
7259
7260 return TRUE;
7261 }
7262
7263 static GtkTreePath*
7264 get_logical_destination (ExoIconView *icon_view,
7265 gboolean *drop_append_mode)
7266 {
7267 /* adjust path to point to the row the drop goes in front of */
7268 GtkTreePath *path = NULL;
7269 ExoIconViewDropPosition pos;
7270
7271 *drop_append_mode = FALSE;
7272
7273 exo_icon_view_get_drag_dest_item (icon_view, &path, &pos);
7274
7275 if (path == NULL)
7276 return NULL;
7277
7278 if (pos == EXO_ICON_VIEW_DROP_RIGHT ||
7279 pos == EXO_ICON_VIEW_DROP_BELOW)
7280 {
7281 GtkTreeIter iter;
7282 GtkTreeModel *model = icon_view->priv->model;
7283
7284 if (!gtk_tree_model_get_iter (model, &iter, path) ||
7285 !gtk_tree_model_iter_next (model, &iter))
7286 *drop_append_mode = TRUE;
7287 else
7288 {
7289 *drop_append_mode = FALSE;
7290 gtk_tree_path_next (path);
7291 }
7292 }
7293
7294 return path;
7295 }
7296
7297 static gboolean
7298 exo_icon_view_maybe_begin_drag (ExoIconView *icon_view,
7299 GdkEventMotion *event)
7300 {
7301 GdkDragContext *context;
7302 GtkTreePath *path = NULL;
7303 gint button;
7304 GtkTreeModel *model;
7305 gboolean retval = FALSE;
7306
7307 if (!icon_view->priv->source_set)
7308 goto out;
7309
7310 if (icon_view->priv->pressed_button < 0)
7311 goto out;
7312
7313 if (!gtk_drag_check_threshold (GTK_WIDGET (icon_view),
7314 icon_view->priv->press_start_x,
7315 icon_view->priv->press_start_y,
7316 event->x, event->y))
7317 goto out;
7318
7319 model = exo_icon_view_get_model (icon_view);
7320
7321 if (model == NULL)
7322 goto out;
7323
7324 button = icon_view->priv->pressed_button;
7325 icon_view->priv->pressed_button = -1;
7326
7327 path = exo_icon_view_get_path_at_pos (icon_view,
7328 icon_view->priv->press_start_x,
7329 icon_view->priv->press_start_y);
7330
7331 if (path == NULL)
7332 goto out;
7333
7334 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
7335 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
7336 path))
7337 goto out;
7338
7339 /* FIXME Check whether we're a start button, if not return FALSE and
7340 * free path
7341 */
7342
7343 /* Now we can begin the drag */
7344
7345 retval = TRUE;
7346
7347 context = gtk_drag_begin (GTK_WIDGET (icon_view),
7348 icon_view->priv->source_targets,
7349 icon_view->priv->source_actions,
7350 button,
7351 (GdkEvent*)event);
7352
7353 set_source_row (context, model, path);
7354
7355 out:
7356 if (path)
7357 gtk_tree_path_free (path);
7358
7359 return retval;
7360 }
7361
7362 /* Source side drag signals */
7363 static void
7364 exo_icon_view_drag_begin (GtkWidget *widget,
7365 GdkDragContext *context)
7366 {
7367 ExoIconView *icon_view;
7368 ExoIconViewItem *item;
7369 GdkPixbuf *icon;
7370 gint x, y;
7371 GtkTreePath *path;
7372
7373 icon_view = EXO_ICON_VIEW (widget);
7374
7375 /* if the user uses a custom DnD impl, we don't set the icon here */
7376 if (!icon_view->priv->dest_set && !icon_view->priv->source_set)
7377 return;
7378
7379 item = exo_icon_view_get_item_at_coords (icon_view,
7380 icon_view->priv->press_start_x,
7381 icon_view->priv->press_start_y,
7382 TRUE,
7383 NULL);
7384
7385 _exo_return_if_fail (item != NULL);
7386
7387 x = icon_view->priv->press_start_x - item->area.x + 1;
7388 y = icon_view->priv->press_start_y - item->area.y + 1;
7389
7390 path = gtk_tree_path_new_from_indices (item->index, -1);
7391 icon = exo_icon_view_create_drag_icon (icon_view, path);
7392 gtk_tree_path_free (path);
7393
7394 gtk_drag_set_icon_pixbuf (context,
7395 icon,
7396 x, y);
7397
7398 g_object_unref (icon);
7399 }
7400
7401 static void
7402 exo_icon_view_drag_end (GtkWidget *widget,
7403 GdkDragContext *context)
7404 {
7405 /* do nothing */
7406 }
7407
7408 static void
7409 exo_icon_view_drag_data_get (GtkWidget *widget,
7410 GdkDragContext *context,
7411 GtkSelectionData *selection_data,
7412 guint info,
7413 guint drag_time)
7414 {
7415 ExoIconView *icon_view;
7416 GtkTreeModel *model;
7417 GtkTreePath *source_row;
7418
7419 icon_view = EXO_ICON_VIEW (widget);
7420 model = exo_icon_view_get_model (icon_view);
7421
7422 if (model == NULL)
7423 return;
7424
7425 if (!icon_view->priv->dest_set)
7426 return;
7427
7428 source_row = get_source_row (context);
7429
7430 if (source_row == NULL)
7431 return;
7432
7433 /* We can implement the GTK_TREE_MODEL_ROW target generically for
7434 * any model; for DragSource models there are some other targets
7435 * we also support.
7436 */
7437
7438 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
7439 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
7440 source_row,
7441 selection_data))
7442 goto done;
7443
7444 /* If drag_data_get does nothing, try providing row data. */
7445 if (gtk_selection_data_get_target(selection_data) == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
7446 gtk_tree_set_row_drag_data (selection_data,
7447 model,
7448 source_row);
7449
7450 done:
7451 gtk_tree_path_free (source_row);
7452 }
7453
7454 static void
7455 exo_icon_view_drag_data_delete (GtkWidget *widget,
7456 GdkDragContext *context)
7457 {
7458 GtkTreeModel *model;
7459 ExoIconView *icon_view;
7460 GtkTreePath *source_row;
7461
7462 icon_view = EXO_ICON_VIEW (widget);
7463 model = exo_icon_view_get_model (icon_view);
7464
7465 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
7466 return;
7467
7468 if (!icon_view->priv->dest_set)
7469 return;
7470
7471 source_row = get_source_row (context);
7472
7473 if (source_row == NULL)
7474 return;
7475
7476 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
7477 source_row);
7478
7479 gtk_tree_path_free (source_row);
7480
7481 set_source_row (context, NULL, NULL);
7482 }
7483
7484 /* Target side drag signals */
7485 static void
7486 exo_icon_view_drag_leave (GtkWidget *widget,
7487 GdkDragContext *context,
7488 guint drag_time)
7489 {
7490 ExoIconView *icon_view;
7491
7492 icon_view = EXO_ICON_VIEW (widget);
7493
7494 /* unset any highlight row */
7495 exo_icon_view_set_drag_dest_item (icon_view,
7496 NULL,
7497 EXO_ICON_VIEW_DROP_LEFT);
7498
7499 remove_scroll_timeout (icon_view);
7500 }
7501
7502 static gboolean
7503 exo_icon_view_drag_motion (GtkWidget *widget,
7504 GdkDragContext *context,
7505 gint x,
7506 gint y,
7507 guint drag_time)
7508 {
7509 ExoIconViewDropPosition pos;
7510 GdkDragAction suggested_action = 0;
7511 GtkTreePath *path = NULL;
7512 ExoIconView *icon_view = EXO_ICON_VIEW (widget);
7513 gboolean empty;
7514 GdkAtom target;
7515
7516 if (!set_destination (icon_view, context, x, y, &suggested_action, &target))
7517 return FALSE;
7518
7519 exo_icon_view_get_drag_dest_item (icon_view, &path, &pos);
7520
7521 /* we only know this *after* set_desination_row */
7522 empty = icon_view->priv->empty_view_drop;
7523
7524 if (path == NULL && !empty)
7525 {
7526 /* Can't drop here. */
7527 gdk_drag_status (context, 0, drag_time);
7528 }
7529 else
7530 {
7531 if (icon_view->priv->scroll_timeout_id == 0)
7532 icon_view->priv->scroll_timeout_id = gdk_threads_add_timeout (50, drag_scroll_timeout, icon_view);
7533
7534 if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
7535 {
7536 /* Request data so we can use the source row when
7537 * determining whether to accept the drop
7538 */
7539 set_status_pending (context, suggested_action);
7540 gtk_drag_get_data (widget, context, target, drag_time);
7541 }
7542 else
7543 {
7544 set_status_pending (context, 0);
7545 gdk_drag_status (context, suggested_action, drag_time);
7546 }
7547 }
7548
7549 if (path != NULL)
7550 gtk_tree_path_free (path);
7551
7552 return TRUE;
7553 }
7554
7555 static gboolean
7556 exo_icon_view_drag_drop (GtkWidget *widget,
7557 GdkDragContext *context,
7558 gint x,
7559 gint y,
7560 guint drag_time)
7561 {
7562 ExoIconView *icon_view;
7563 GtkTreePath *path;
7564 GdkDragAction suggested_action = 0;
7565 GdkAtom target = GDK_NONE;
7566 GtkTreeModel *model;
7567 gboolean drop_append_mode;
7568
7569 icon_view = EXO_ICON_VIEW (widget);
7570 model = exo_icon_view_get_model (icon_view);
7571
7572 remove_scroll_timeout (EXO_ICON_VIEW (widget));
7573
7574 if (!icon_view->priv->dest_set)
7575 return FALSE;
7576
7577 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
7578 return FALSE;
7579
7580 if (!set_destination (icon_view, context, x, y, &suggested_action, &target))
7581 return FALSE;
7582
7583 path = get_logical_destination (icon_view, &drop_append_mode);
7584
7585 if (target != GDK_NONE && path != NULL)
7586 {
7587 /* in case a motion had requested drag data, change things so we
7588 * treat drag data receives as a drop.
7589 */
7590 set_status_pending (context, 0);
7591 set_dest_row (context, model, path,
7592 icon_view->priv->empty_view_drop, drop_append_mode);
7593 }
7594
7595 if (path)
7596 gtk_tree_path_free (path);
7597
7598 /* Unset this thing */
7599 exo_icon_view_set_drag_dest_item (icon_view, NULL, EXO_ICON_VIEW_DROP_LEFT);
7600
7601 if (target != GDK_NONE)
7602 {
7603 gtk_drag_get_data (widget, context, target, drag_time);
7604 return TRUE;
7605 }
7606 else
7607 return FALSE;
7608 }
7609
7610 static void
7611 exo_icon_view_drag_data_received (GtkWidget *widget,
7612 GdkDragContext *context,
7613 gint x,
7614 gint y,
7615 GtkSelectionData *selection_data,
7616 guint info,
7617 guint drag_time)
7618 {
7619 GtkTreePath *path;
7620 gboolean accepted = FALSE;
7621 GtkTreeModel *model;
7622 ExoIconView *icon_view;
7623 GtkTreePath *dest_row;
7624 GdkDragAction suggested_action;
7625 gboolean drop_append_mode;
7626
7627 icon_view = EXO_ICON_VIEW (widget);
7628 model = exo_icon_view_get_model (icon_view);
7629
7630 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
7631 return;
7632
7633 if (!icon_view->priv->dest_set)
7634 return;
7635
7636 suggested_action = get_status_pending (context);
7637
7638 if (suggested_action)
7639 {
7640 /* We are getting this data due to a request in drag_motion,
7641 * rather than due to a request in drag_drop, so we are just
7642 * supposed to call drag_status, not actually paste in the
7643 * data.
7644 */
7645 path = get_logical_destination (icon_view, &drop_append_mode);
7646
7647 if (path == NULL)
7648 suggested_action = 0;
7649
7650 if (suggested_action)
7651 {
7652 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
7653 path,
7654 selection_data))
7655 suggested_action = 0;
7656 }
7657
7658 gdk_drag_status (context, suggested_action, drag_time);
7659
7660 if (path)
7661 gtk_tree_path_free (path);
7662
7663 /* If you can't drop, remove user drop indicator until the next motion */
7664 if (suggested_action == 0)
7665 exo_icon_view_set_drag_dest_item (icon_view,
7666 NULL,
7667 EXO_ICON_VIEW_DROP_LEFT);
7668 return;
7669 }
7670
7671
7672 dest_row = get_dest_row (context);
7673
7674 if (dest_row == NULL)
7675 return;
7676
7677 if (gtk_selection_data_get_length(selection_data) >= 0)
7678 {
7679 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
7680 dest_row,
7681 selection_data))
7682 accepted = TRUE;
7683 }
7684
7685 gtk_drag_finish (context,
7686 accepted,
7687 (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
7688 drag_time);
7689
7690 gtk_tree_path_free (dest_row);
7691
7692 /* drop dest_row */
7693 set_dest_row (context, NULL, NULL, FALSE, FALSE);
7694 }
7695
7696
7697
7698 /**
7699 * exo_icon_view_enable_model_drag_source:
7700 * @icon_view : a #GtkIconTreeView
7701 * @start_button_mask : Mask of allowed buttons to start drag
7702 * @targets : the table of targets that the drag will support
7703 * @n_targets : the number of items in @targets
7704 * @actions : the bitmask of possible actions for a drag from this widget
7705 *
7706 * Turns @icon_view into a drag source for automatic DND.
7707 *
7708 * Since: 0.3.1
7709 **/
7710 void
7711 exo_icon_view_enable_model_drag_source (ExoIconView *icon_view,
7712 GdkModifierType start_button_mask,
7713 const GtkTargetEntry *targets,
7714 gint n_targets,
7715 GdkDragAction actions)
7716 {
7717 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7718
7719 gtk_drag_source_set (GTK_WIDGET (icon_view), 0, NULL, 0, actions);
7720
7721 clear_source_info (icon_view);
7722 icon_view->priv->start_button_mask = start_button_mask;
7723 icon_view->priv->source_targets = gtk_target_list_new (targets, n_targets);
7724 icon_view->priv->source_actions = actions;
7725
7726 icon_view->priv->source_set = TRUE;
7727
7728 unset_reorderable (icon_view);
7729 }
7730
7731
7732
7733 /**
7734 * exo_icon_view_enable_model_drag_dest:
7735 * @icon_view : a #ExoIconView
7736 * @targets : the table of targets that the drag will support
7737 * @n_targets : the number of items in @targets
7738 * @actions : the bitmask of possible actions for a drag from this widget
7739 *
7740 * Turns @icon_view into a drop destination for automatic DND.
7741 *
7742 * Since: 0.3.1
7743 **/
7744 void
7745 exo_icon_view_enable_model_drag_dest (ExoIconView *icon_view,
7746 const GtkTargetEntry *targets,
7747 gint n_targets,
7748 GdkDragAction actions)
7749 {
7750 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7751
7752 gtk_drag_dest_set (GTK_WIDGET (icon_view), 0, NULL, 0, actions);
7753
7754 clear_dest_info (icon_view);
7755
7756 icon_view->priv->dest_targets = gtk_target_list_new (targets, n_targets);
7757 icon_view->priv->dest_actions = actions;
7758
7759 icon_view->priv->dest_set = TRUE;
7760
7761 unset_reorderable (icon_view);
7762 }
7763
7764
7765
7766 /**
7767 * exo_icon_view_unset_model_drag_source:
7768 * @icon_view : a #ExoIconView
7769 *
7770 * Undoes the effect of #exo_icon_view_enable_model_drag_source().
7771 *
7772 * Since: 0.3.1
7773 **/
7774 void
7775 exo_icon_view_unset_model_drag_source (ExoIconView *icon_view)
7776 {
7777 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7778
7779 if (icon_view->priv->source_set)
7780 {
7781 gtk_drag_source_unset (GTK_WIDGET (icon_view));
7782 clear_source_info (icon_view);
7783 }
7784
7785 unset_reorderable (icon_view);
7786 }
7787
7788
7789
7790 /**
7791 * exo_icon_view_unset_model_drag_dest:
7792 * @icon_view : a #ExoIconView
7793 *
7794 * Undoes the effect of #exo_icon_view_enable_model_drag_dest().
7795 *
7796 * Since: 0.3.1
7797 **/
7798 void
7799 exo_icon_view_unset_model_drag_dest (ExoIconView *icon_view)
7800 {
7801 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7802
7803 if (icon_view->priv->dest_set)
7804 {
7805 gtk_drag_dest_unset (GTK_WIDGET (icon_view));
7806 clear_dest_info (icon_view);
7807 }
7808
7809 unset_reorderable (icon_view);
7810 }
7811
7812
7813
7814 /**
7815 * exo_icon_view_set_drag_dest_item:
7816 * @icon_view : a #ExoIconView
7817 * @path : The path of the item to highlight, or %NULL.
7818 * @pos : Specifies whether to drop, relative to the item
7819 *
7820 * Sets the item that is highlighted for feedback.
7821 *
7822 * Since: 0.3.1
7823 */
7824 void
7825 exo_icon_view_set_drag_dest_item (ExoIconView *icon_view,
7826 GtkTreePath *path,
7827 ExoIconViewDropPosition pos)
7828 {
7829 ExoIconViewItem *item;
7830 GtkTreePath *previous_path;
7831
7832 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7833
7834 /* Note; this function is exported to allow a custom DND
7835 * implementation, so it can't touch TreeViewDragInfo
7836 */
7837
7838 if (icon_view->priv->dest_item != NULL)
7839 {
7840 /* determine and reset the previous path */
7841 previous_path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item);
7842 gtk_tree_row_reference_free (icon_view->priv->dest_item);
7843 icon_view->priv->dest_item = NULL;
7844
7845 /* check if the path is still valid */
7846 if (G_LIKELY (previous_path != NULL))
7847 {
7848 /* schedule a redraw for the previous path */
7849 item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices (previous_path)[0]);
7850 if (G_LIKELY (item != NULL))
7851 exo_icon_view_queue_draw_item (icon_view, item);
7852 gtk_tree_path_free (previous_path);
7853 }
7854 }
7855
7856 /* special case a drop on an empty model */
7857 icon_view->priv->empty_view_drop = FALSE;
7858 if (pos == EXO_ICON_VIEW_NO_DROP && path
7859 && gtk_tree_path_get_depth (path) == 1
7860 && gtk_tree_path_get_indices (path)[0] == 0)
7861 {
7862 gint n_children;
7863
7864 n_children = gtk_tree_model_iter_n_children (icon_view->priv->model,
7865 NULL);
7866
7867 if (n_children == 0)
7868 icon_view->priv->empty_view_drop = TRUE;
7869 }
7870
7871 icon_view->priv->dest_pos = pos;
7872
7873 if (G_LIKELY (path != NULL))
7874 {
7875 /* take a row reference for the new item path */
7876 icon_view->priv->dest_item = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), icon_view->priv->model, path);
7877
7878 /* schedule a redraw on the new path */
7879 item = g_list_nth_data (icon_view->priv->items, gtk_tree_path_get_indices (path)[0]);
7880 if (G_LIKELY (item != NULL))
7881 exo_icon_view_queue_draw_item (icon_view, item);
7882 }
7883 }
7884
7885
7886
7887 /**
7888 * exo_icon_view_get_drag_dest_item:
7889 * @icon_view : a #ExoIconView
7890 * @path : Return location for the path of the highlighted item, or %NULL.
7891 * @pos : Return location for the drop position, or %NULL
7892 *
7893 * Gets information about the item that is highlighted for feedback.
7894 *
7895 * Since: 0.3.1
7896 **/
7897 void
7898 exo_icon_view_get_drag_dest_item (ExoIconView *icon_view,
7899 GtkTreePath **path,
7900 ExoIconViewDropPosition *pos)
7901 {
7902 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
7903
7904 if (path)
7905 {
7906 if (icon_view->priv->dest_item)
7907 *path = gtk_tree_row_reference_get_path (icon_view->priv->dest_item);
7908 else
7909 *path = NULL;
7910 }
7911
7912 if (pos)
7913 *pos = icon_view->priv->dest_pos;
7914 }
7915
7916
7917
7918 /**
7919 * exo_icon_view_get_dest_item_at_pos:
7920 * @icon_view : a #ExoIconView
7921 * @drag_x : the position to determine the destination item for
7922 * @drag_y : the position to determine the destination item for
7923 * @path : Return location for the path of the highlighted item, or %NULL.
7924 * @pos : Return location for the drop position, or %NULL
7925 *
7926 * Determines the destination item for a given position.
7927 *
7928 * Both @drag_x and @drag_y are given in icon window coordinates. Use
7929 * #exo_icon_view_widget_to_icon_coords() if you need to translate
7930 * widget coordinates first.
7931 *
7932 * Return value: whether there is an item at the given position.
7933 *
7934 * Since: 0.3.1
7935 **/
7936 gboolean
7937 exo_icon_view_get_dest_item_at_pos (ExoIconView *icon_view,
7938 gint drag_x,
7939 gint drag_y,
7940 GtkTreePath **path,
7941 ExoIconViewDropPosition *pos)
7942 {
7943 ExoIconViewItem *item;
7944
7945 /* Note; this function is exported to allow a custom DND
7946 * implementation, so it can't touch TreeViewDragInfo
7947 */
7948
7949 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
7950 g_return_val_if_fail (drag_x >= 0, FALSE);
7951 g_return_val_if_fail (drag_y >= 0, FALSE);
7952 g_return_val_if_fail (icon_view->priv->bin_window != NULL, FALSE);
7953
7954 if (G_LIKELY (path != NULL))
7955 *path = NULL;
7956
7957 item = exo_icon_view_get_item_at_coords (icon_view, drag_x, drag_y, FALSE, NULL);
7958
7959 if (G_UNLIKELY (item == NULL))
7960 return FALSE;
7961
7962 if (G_LIKELY (path != NULL))
7963 *path = gtk_tree_path_new_from_indices (item->index, -1);
7964
7965 if (G_LIKELY (pos != NULL))
7966 {
7967 if (drag_x < item->area.x + item->area.width / 4)
7968 *pos = EXO_ICON_VIEW_DROP_LEFT;
7969 else if (drag_x > item->area.x + item->area.width * 3 / 4)
7970 *pos = EXO_ICON_VIEW_DROP_RIGHT;
7971 else if (drag_y < item->area.y + item->area.height / 4)
7972 *pos = EXO_ICON_VIEW_DROP_ABOVE;
7973 else if (drag_y > item->area.y + item->area.height * 3 / 4)
7974 *pos = EXO_ICON_VIEW_DROP_BELOW;
7975 else
7976 *pos = EXO_ICON_VIEW_DROP_INTO;
7977 }
7978
7979 return TRUE;
7980 }
7981
7982
7983
7984 /**
7985 * exo_icon_view_create_drag_icon:
7986 * @icon_view : a #ExoIconView
7987 * @path : a #GtkTreePath in @icon_view
7988 *
7989 * Creates a #GdkPixbuf representation of the item at @path.
7990 * This image is used for a drag icon.
7991 *
7992 * Return value: a newly-allocated pixmap of the drag icon.
7993 *
7994 * Since: 0.3.1
7995 **/
7996 GdkPixbuf*
7997 exo_icon_view_create_drag_icon (ExoIconView *icon_view,
7998 GtkTreePath *path)
7999 {
8000 GdkRectangle area;
8001 GtkWidget *widget = GTK_WIDGET (icon_view);
8002 #if GTK_CHECK_VERSION(3, 0, 0)
8003 cairo_surface_t *s;
8004 #else
8005 GdkPixmap *drawable;
8006 #endif
8007 GdkPixbuf *pixbuf;
8008 cairo_t *cr;
8009 GList *lp;
8010 GtkStyle *style;
8011 gint idx;
8012
8013 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
8014 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, NULL);
8015
8016 /* verify that the widget is realized */
8017 if (G_UNLIKELY (!gtk_widget_get_realized (GTK_WIDGET (icon_view))))
8018 return NULL;
8019
8020 idx = gtk_tree_path_get_indices (path)[0];
8021 style = gtk_widget_get_style (widget);
8022
8023 for (lp = icon_view->priv->items; lp != NULL; lp = lp->next)
8024 {
8025 ExoIconViewItem *item = lp->data;
8026 if (G_UNLIKELY (idx == item->index))
8027 {
8028 #if GTK_CHECK_VERSION(3, 0, 0)
8029 s = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
8030 item->area.width + 2,
8031 item->area.height + 2);
8032
8033 cr = cairo_create (s);
8034 #else
8035 drawable = gdk_pixmap_new (icon_view->priv->bin_window,
8036 item->area.width + 2,
8037 item->area.height + 2,
8038 -1);
8039
8040 cr = gdk_cairo_create (drawable);
8041 #endif
8042 gdk_cairo_set_source_color (cr, &style->base[gtk_widget_get_state (widget)]);
8043 cairo_rectangle (cr, 0, 0, item->area.width + 2, item->area.height + 2);
8044 cairo_fill (cr);
8045
8046 area.x = 0;
8047 area.y = 0;
8048 area.width = item->area.width;
8049 area.height = item->area.height;
8050
8051 #if GTK_CHECK_VERSION(3, 0, 0)
8052 exo_icon_view_paint_item (icon_view, item, &area, cr, 1, 1, FALSE);
8053 #else
8054 /* NOTE: this is inefficient but Gtk+2 uses GtkWindow for render */
8055 exo_icon_view_paint_item (icon_view, item, &area, drawable, 1, 1, FALSE);
8056 #endif
8057
8058 gdk_cairo_set_source_color (cr, &style->black);
8059 cairo_rectangle (cr, 1, 1, item->area.width + 1, item->area.height + 1);
8060 cairo_stroke (cr);
8061
8062 cairo_destroy (cr);
8063
8064 #if GTK_CHECK_VERSION(3, 0, 0)
8065 pixbuf = gdk_pixbuf_get_from_surface (s, 0, 0,
8066 item->area.width + 2,
8067 item->area.height + 2);
8068 cairo_surface_destroy (s);
8069
8070 return pixbuf;
8071 #else
8072 pixbuf = gdk_pixbuf_get_from_drawable (NULL, drawable,
8073 gdk_drawable_get_colormap (drawable),
8074 0, 0, 0, 0,
8075 item->area.width + 2,
8076 item->area.height + 2);
8077 g_object_unref (drawable);
8078 return pixbuf;
8079 #endif
8080 }
8081 }
8082
8083 return NULL;
8084 }
8085
8086
8087
8088 /**
8089 * exo_icon_view_get_reorderable:
8090 * @icon_view : a #ExoIconView
8091 *
8092 * Retrieves whether the user can reorder the list via drag-and-drop.
8093 * See exo_icon_view_set_reorderable().
8094 *
8095 * Return value: %TRUE if the list can be reordered.
8096 *
8097 * Since: 0.3.1
8098 **/
8099 gboolean
8100 exo_icon_view_get_reorderable (ExoIconView *icon_view)
8101 {
8102 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8103
8104 return icon_view->priv->reorderable;
8105 }
8106
8107
8108
8109 /**
8110 * exo_icon_view_set_reorderable:
8111 * @icon_view : A #ExoIconView.
8112 * @reorderable : %TRUE, if the list of items can be reordered.
8113 *
8114 * This function is a convenience function to allow you to reorder models that
8115 * support the #GtkTreeDragSourceIface and the #GtkTreeDragDestIface. Both
8116 * #GtkTreeStore and #GtkListStore support these. If @reorderable is %TRUE, then
8117 * the user can reorder the model by dragging and dropping rows. The
8118 * developer can listen to these changes by connecting to the model's
8119 * ::row-inserted and ::row-deleted signals.
8120 *
8121 * This function does not give you any degree of control over the order -- any
8122 * reordering is allowed. If more control is needed, you should probably
8123 * handle drag and drop manually.
8124 *
8125 * Since: 0.3.1
8126 **/
8127 void
8128 exo_icon_view_set_reorderable (ExoIconView *icon_view,
8129 gboolean reorderable)
8130 {
8131 static const GtkTargetEntry item_targets[] =
8132 {
8133 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0, },
8134 };
8135
8136 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8137
8138 reorderable = (reorderable != FALSE);
8139
8140 if (G_UNLIKELY (icon_view->priv->reorderable == reorderable))
8141 return;
8142
8143 if (G_LIKELY (reorderable))
8144 {
8145 exo_icon_view_enable_model_drag_source (icon_view, GDK_BUTTON1_MASK, item_targets, G_N_ELEMENTS (item_targets), GDK_ACTION_MOVE);
8146 exo_icon_view_enable_model_drag_dest (icon_view, item_targets, G_N_ELEMENTS (item_targets), GDK_ACTION_MOVE);
8147 }
8148 else
8149 {
8150 exo_icon_view_unset_model_drag_source (icon_view);
8151 exo_icon_view_unset_model_drag_dest (icon_view);
8152 }
8153
8154 icon_view->priv->reorderable = reorderable;
8155
8156 g_object_notify (G_OBJECT (icon_view), "reorderable");
8157 }
8158
8159
8160
8161 /*----------------------*
8162 * Single-click support *
8163 *----------------------*/
8164
8165 /**
8166 * exo_icon_view_get_single_click:
8167 * @icon_view : a #ExoIconView.
8168 *
8169 * Returns %TRUE if @icon_view is currently in single click mode,
8170 * else %FALSE will be returned.
8171 *
8172 * Return value: whether @icon_view is currently in single click mode.
8173 *
8174 * Since: 0.3.1.3
8175 **/
8176 gboolean
8177 exo_icon_view_get_single_click (const ExoIconView *icon_view)
8178 {
8179 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8180 return icon_view->priv->single_click;
8181 }
8182
8183
8184
8185 /**
8186 * exo_icon_view_set_single_click:
8187 * @icon_view : a #ExoIconView.
8188 * @single_click : %TRUE for single click, %FALSE for double click mode.
8189 *
8190 * If @single_click is %TRUE, @icon_view will be in single click mode
8191 * afterwards, else @icon_view will be in double click mode.
8192 *
8193 * Since: 0.3.1.3
8194 **/
8195 void
8196 exo_icon_view_set_single_click (ExoIconView *icon_view,
8197 gboolean single_click)
8198 {
8199 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8200
8201 /* normalize the value */
8202 single_click = !!single_click;
8203
8204 /* check if we have a new setting here */
8205 if (icon_view->priv->single_click != single_click)
8206 {
8207 icon_view->priv->single_click = single_click;
8208 g_object_notify (G_OBJECT (icon_view), "single-click");
8209 }
8210 }
8211
8212
8213
8214 /**
8215 * exo_icon_view_get_single_click_timeout:
8216 * @icon_view : a #ExoIconView.
8217 *
8218 * Returns the amount of time in milliseconds after which the
8219 * item under the mouse cursor will be selected automatically
8220 * in single click mode. A value of %0 means that the behavior
8221 * is disabled and the user must alter the selection manually.
8222 *
8223 * Return value: the single click autoselect timeout or %0 if
8224 * the behavior is disabled.
8225 *
8226 * Since: 0.3.1.5
8227 **/
8228 guint
8229 exo_icon_view_get_single_click_timeout (const ExoIconView *icon_view)
8230 {
8231 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), 0u);
8232 return icon_view->priv->single_click_timeout;
8233 }
8234
8235
8236
8237 /**
8238 * exo_icon_view_set_single_click_timeout:
8239 * @icon_view : a #ExoIconView.
8240 * @single_click_timeout : the new timeout or %0 to disable.
8241 *
8242 * If @single_click_timeout is a value greater than zero, it specifies
8243 * the amount of time in milliseconds after which the item under the
8244 * mouse cursor will be selected automatically in single click mode.
8245 * A value of %0 for @single_click_timeout disables the autoselection
8246 * for @icon_view.
8247 *
8248 * This setting does not have any effect unless the @icon_view is in
8249 * single-click mode, see exo_icon_view_set_single_click().
8250 *
8251 * Since: 0.3.1.5
8252 **/
8253 void
8254 exo_icon_view_set_single_click_timeout (ExoIconView *icon_view,
8255 guint single_click_timeout)
8256 {
8257 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8258
8259 /* check if we have a new setting */
8260 if (icon_view->priv->single_click_timeout != single_click_timeout)
8261 {
8262 /* apply the new setting */
8263 icon_view->priv->single_click_timeout = single_click_timeout;
8264
8265 /* be sure to cancel any pending single click timeout */
8266 if (G_UNLIKELY (icon_view->priv->single_click_timeout_id != 0))
8267 g_source_remove (icon_view->priv->single_click_timeout_id);
8268
8269 /* notify listeners */
8270 g_object_notify (G_OBJECT (icon_view), "single-click-timeout");
8271 }
8272 }
8273
8274
8275
8276 static gboolean
8277 exo_icon_view_single_click_timeout (gpointer user_data)
8278 {
8279 ExoIconViewItem *item;
8280 gboolean dirty = FALSE;
8281 ExoIconView *icon_view = EXO_ICON_VIEW (user_data);
8282
8283 /* ensure that source isn't removed yet */
8284 if(g_source_is_destroyed(g_main_current_source()))
8285 return FALSE;
8286
8287 /* verify that we are in single-click mode, have focus and a prelit item */
8288 if (gtk_widget_has_focus (GTK_WIDGET (icon_view)) && icon_view->priv->single_click && icon_view->priv->prelit_item != NULL)
8289 {
8290 /* work on the prelit item */
8291 item = icon_view->priv->prelit_item;
8292
8293 /* be sure the item is fully visible */
8294 exo_icon_view_scroll_to_item (icon_view, item);
8295
8296 /* change the selection appropriately */
8297 if (G_UNLIKELY (icon_view->priv->selection_mode == GTK_SELECTION_NONE))
8298 {
8299 exo_icon_view_set_cursor_item (icon_view, item, -1);
8300 }
8301 else if ((icon_view->priv->single_click_timeout_state & GDK_SHIFT_MASK) != 0
8302 && icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
8303 {
8304 if (!(icon_view->priv->single_click_timeout_state & GDK_CONTROL_MASK))
8305 /* unselect all previously selected items */
8306 exo_icon_view_unselect_all_internal (icon_view);
8307
8308 /* select all items between the anchor and the prelit item */
8309 exo_icon_view_set_cursor_item (icon_view, item, -1);
8310 if (icon_view->priv->anchor_item == NULL)
8311 icon_view->priv->anchor_item = item;
8312 else
8313 exo_icon_view_select_all_between (icon_view, icon_view->priv->anchor_item, item);
8314
8315 /* selection was changed */
8316 dirty = TRUE;
8317 }
8318 else
8319 {
8320 if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ||
8321 ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) &&
8322 (icon_view->priv->single_click_timeout_state & GDK_CONTROL_MASK) != 0)
8323 {
8324 item->selected = !item->selected;
8325 exo_icon_view_queue_draw_item (icon_view, item);
8326 dirty = TRUE;
8327 }
8328 else if (!item->selected)
8329 {
8330 exo_icon_view_unselect_all_internal (icon_view);
8331 exo_icon_view_queue_draw_item (icon_view, item);
8332 item->selected = TRUE;
8333 dirty = TRUE;
8334 }
8335 exo_icon_view_set_cursor_item (icon_view, item, -1);
8336 icon_view->priv->anchor_item = item;
8337 }
8338 }
8339
8340 /* emit "selection-changed" and stop drawing keyboard
8341 * focus indicator if the selection was altered
8342 */
8343 if (G_LIKELY (dirty))
8344 {
8345 /* reset "draw keyfocus" flag */
8346 EXO_ICON_VIEW_UNSET_FLAG (icon_view, EXO_ICON_VIEW_DRAW_KEYFOCUS);
8347
8348 /* emit "selection-changed" */
8349 g_signal_emit (G_OBJECT (icon_view), icon_view_signals[SELECTION_CHANGED], 0);
8350 }
8351
8352 return FALSE;
8353 }
8354
8355
8356
8357 static void
8358 exo_icon_view_single_click_timeout_destroy (gpointer user_data)
8359 {
8360 EXO_ICON_VIEW (user_data)->priv->single_click_timeout_id = 0;
8361 }
8362
8363
8364
8365 /*----------------------------*
8366 * Interactive search support *
8367 *----------------------------*/
8368
8369 /**
8370 * exo_icon_view_get_enable_search:
8371 * @icon_view : an #ExoIconView.
8372 *
8373 * Returns whether or not the @icon_view allows to start
8374 * interactive searching by typing in text.
8375 *
8376 * Return value: whether or not to let the user search
8377 * interactively.
8378 *
8379 * Since: 0.3.1.3
8380 **/
8381 gboolean
8382 exo_icon_view_get_enable_search (const ExoIconView *icon_view)
8383 {
8384 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8385 return icon_view->priv->enable_search;
8386 }
8387
8388
8389
8390 /**
8391 * exo_icon_view_set_enable_search:
8392 * @icon_view : an #ExoIconView.
8393 * @enable_search : %TRUE if the user can search interactively.
8394 *
8395 * If @enable_search is set, then the user can type in text to search through
8396 * the @icon_view interactively (this is sometimes called "typeahead find").
8397 *
8398 * Note that even if this is %FALSE, the user can still initiate a search
8399 * using the "start-interactive-search" key binding.
8400 *
8401 * Since: 0.3.1.3
8402 **/
8403 void
8404 exo_icon_view_set_enable_search (ExoIconView *icon_view,
8405 gboolean enable_search)
8406 {
8407 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8408
8409 enable_search = !!enable_search;
8410
8411 if (G_LIKELY (icon_view->priv->enable_search != enable_search))
8412 {
8413 icon_view->priv->enable_search = enable_search;
8414 g_object_notify (G_OBJECT (icon_view), "enable-search");
8415 }
8416 }
8417
8418
8419
8420 /**
8421 * exo_icon_view_get_search_column:
8422 * @icon_view : an #ExoIconView.
8423 *
8424 * Returns the column searched on by the interactive search code.
8425 *
8426 * Return value: the column the interactive search code searches in.
8427 *
8428 * Since: 0.3.1.3
8429 **/
8430 gint
8431 exo_icon_view_get_search_column (const ExoIconView *icon_view)
8432 {
8433 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), -1);
8434 return icon_view->priv->search_column;
8435 }
8436
8437
8438
8439 /**
8440 * exo_icon_view_set_search_column:
8441 * @icon_view : an #ExoIconView.
8442 * @search_column : the column of the model to search in, or -1 to disable searching.
8443 *
8444 * Sets @search_column as the column where the interactive search code should search in.
8445 *
8446 * If the search column is set, user can use the "start-interactive-search" key
8447 * binding to bring up search popup. The "enable-search" property controls
8448 * whether simply typing text will also start an interactive search.
8449 *
8450 * Note that @search_column refers to a column of the model.
8451 *
8452 * Since: 0.3.1.3
8453 **/
8454 void
8455 exo_icon_view_set_search_column (ExoIconView *icon_view,
8456 gint search_column)
8457 {
8458 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8459 g_return_if_fail (search_column >= -1);
8460
8461 if (G_LIKELY (icon_view->priv->search_column != search_column))
8462 {
8463 icon_view->priv->search_column = search_column;
8464 g_object_notify (G_OBJECT (icon_view), "search-column");
8465 }
8466 }
8467
8468
8469
8470 /**
8471 * exo_icon_view_get_search_equal_func:
8472 * @icon_view : an #ExoIconView.
8473 *
8474 * Returns the compare function currently in use.
8475 *
8476 * Return value: the currently used compare function for the search code.
8477 *
8478 * Since: 0.3.1.3
8479 **/
8480 ExoIconViewSearchEqualFunc
8481 exo_icon_view_get_search_equal_func (const ExoIconView *icon_view)
8482 {
8483 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
8484 return icon_view->priv->search_equal_func;
8485 }
8486
8487
8488
8489 /**
8490 * exo_icon_view_set_search_equal_func:
8491 * @icon_view : an #ExoIconView.
8492 * @search_equal_func : the compare function to use during the search, or %NULL.
8493 * @search_equal_data : user data to pass to @search_equal_func, or %NULL.
8494 * @search_equal_destroy : destroy notifier for @search_equal_data, or %NULL.
8495 *
8496 * Sets the compare function for the interactive search capabilities;
8497 * note that some like strcmp() returning 0 for equality
8498 * #ExoIconViewSearchEqualFunc returns %FALSE on matches.
8499 *
8500 * Specifying %NULL for @search_equal_func will reset @icon_view to use the default
8501 * search equal function.
8502 *
8503 * Since: 0.3.1.3
8504 **/
8505 void
8506 exo_icon_view_set_search_equal_func (ExoIconView *icon_view,
8507 ExoIconViewSearchEqualFunc search_equal_func,
8508 gpointer search_equal_data,
8509 GDestroyNotify search_equal_destroy)
8510 {
8511 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8512 g_return_if_fail (search_equal_func != NULL || (search_equal_data == NULL && search_equal_destroy == NULL));
8513
8514 /* destroy the previous data (if any) */
8515 if (G_UNLIKELY (icon_view->priv->search_equal_destroy != NULL))
8516 (*icon_view->priv->search_equal_destroy) (icon_view->priv->search_equal_data);
8517
8518 icon_view->priv->search_equal_func = (search_equal_func != NULL) ? search_equal_func : exo_icon_view_search_equal_func;
8519 icon_view->priv->search_equal_data = search_equal_data;
8520 icon_view->priv->search_equal_destroy = search_equal_destroy;
8521 }
8522
8523
8524
8525 /**
8526 * exo_icon_view_get_search_position_func:
8527 * @icon_view : an #ExoIconView.
8528 *
8529 * Returns the search dialog positioning function currently in use.
8530 *
8531 * Return value: the currently used function for positioning the search dialog.
8532 *
8533 * Since: 0.3.1.3
8534 **/
8535 ExoIconViewSearchPositionFunc
8536 exo_icon_view_get_search_position_func (const ExoIconView *icon_view)
8537 {
8538 g_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), NULL);
8539 return icon_view->priv->search_position_func;
8540 }
8541
8542
8543
8544 /**
8545 * exo_icon_view_set_search_position_func:
8546 * @icon_view : an #ExoIconView.
8547 * @search_position_func : the function to use to position the search dialog, or %NULL.
8548 * @search_position_data : user data to pass to @search_position_func, or %NULL.
8549 * @search_position_destroy : destroy notifier for @search_position_data, or %NULL.
8550 *
8551 * Sets the function to use when positioning the seach dialog.
8552 *
8553 * Specifying %NULL for @search_position_func will reset @icon_view to use the default
8554 * search position function.
8555 *
8556 * Since: 0.3.1.3
8557 **/
8558 void
8559 exo_icon_view_set_search_position_func (ExoIconView *icon_view,
8560 ExoIconViewSearchPositionFunc search_position_func,
8561 gpointer search_position_data,
8562 GDestroyNotify search_position_destroy)
8563 {
8564 g_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8565 g_return_if_fail (search_position_func != NULL || (search_position_data == NULL && search_position_destroy == NULL));
8566
8567 /* destroy the previous data (if any) */
8568 if (icon_view->priv->search_position_destroy != NULL)
8569 (*icon_view->priv->search_position_destroy) (icon_view->priv->search_position_data);
8570
8571 icon_view->priv->search_position_func = (search_position_func != NULL) ? search_position_func : exo_icon_view_search_position_func;
8572 icon_view->priv->search_position_data = search_position_data;
8573 icon_view->priv->search_position_destroy = search_position_destroy;
8574 }
8575
8576
8577
8578 static void
8579 exo_icon_view_search_activate (GtkEntry *entry,
8580 ExoIconView *icon_view)
8581 {
8582 GtkTreePath *path;
8583
8584 /* hide the interactive search dialog */
8585 exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
8586
8587 /* check if we have a cursor item, and if so, activate it */
8588 if (exo_icon_view_get_cursor (icon_view, &path, NULL))
8589 {
8590 /* only activate the cursor item if it's selected */
8591 if (exo_icon_view_path_is_selected (icon_view, path))
8592 exo_icon_view_item_activated (icon_view, path);
8593 gtk_tree_path_free (path);
8594 }
8595 }
8596
8597
8598
8599 static void
8600 exo_icon_view_search_dialog_hide (GtkWidget *search_dialog,
8601 ExoIconView *icon_view)
8602 {
8603 _exo_return_if_fail (GTK_IS_WIDGET (search_dialog));
8604 _exo_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8605
8606 if (icon_view->priv->search_disable_popdown)
8607 return;
8608
8609 /* disconnect the "changed" signal handler */
8610 if (icon_view->priv->search_entry_changed_id != 0)
8611 {
8612 g_signal_handler_disconnect (G_OBJECT (icon_view->priv->search_entry), icon_view->priv->search_entry_changed_id);
8613 icon_view->priv->search_entry_changed_id = 0;
8614 }
8615
8616 /* disable the flush timeout */
8617 if (icon_view->priv->search_timeout_id != 0)
8618 g_source_remove (icon_view->priv->search_timeout_id);
8619
8620 /* send focus-out event */
8621 _exo_gtk_widget_send_focus_change (icon_view->priv->search_entry, FALSE);
8622 gtk_widget_hide (search_dialog);
8623 gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
8624 }
8625
8626
8627
8628 static void
8629 exo_icon_view_search_ensure_directory (ExoIconView *icon_view)
8630 {
8631 GtkWidget *toplevel;
8632 GtkWidget *frame;
8633 GtkWidget *vbox;
8634 GtkWindowGroup *group;
8635
8636 /* determine the toplevel window */
8637 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (icon_view));
8638
8639 /* check if we already have a search window */
8640 if (G_LIKELY (icon_view->priv->search_window != NULL))
8641 {
8642 if ((group = gtk_window_get_group (GTK_WINDOW (toplevel))) != NULL)
8643 gtk_window_group_add_window (group, GTK_WINDOW (icon_view->priv->search_window));
8644 else if ((group = gtk_window_get_group (GTK_WINDOW (icon_view->priv->search_window))) != NULL)
8645 gtk_window_group_remove_window (group, GTK_WINDOW (icon_view->priv->search_window));
8646 return;
8647 }
8648
8649 /* allocate a new search window */
8650 icon_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8651 gtk_window_set_type_hint (GTK_WINDOW (icon_view->priv->search_window),
8652 GDK_WINDOW_TYPE_HINT_UTILITY);
8653 if ((group = gtk_window_get_group (GTK_WINDOW (toplevel))) != NULL)
8654 gtk_window_group_add_window (group, GTK_WINDOW (icon_view->priv->search_window));
8655 gtk_window_set_modal (GTK_WINDOW (icon_view->priv->search_window), TRUE);
8656 gtk_window_set_screen (GTK_WINDOW (icon_view->priv->search_window), gtk_widget_get_screen (GTK_WIDGET (icon_view)));
8657 gtk_window_set_transient_for (GTK_WINDOW (icon_view->priv->search_window), GTK_WINDOW (toplevel));
8658
8659 /* connect signal handlers */
8660 g_signal_connect (G_OBJECT (icon_view->priv->search_window), "delete-event", G_CALLBACK (exo_icon_view_search_delete_event), icon_view);
8661 g_signal_connect (G_OBJECT (icon_view->priv->search_window), "scroll-event", G_CALLBACK (exo_icon_view_search_scroll_event), icon_view);
8662 g_signal_connect (G_OBJECT (icon_view->priv->search_window), "key-press-event", G_CALLBACK (exo_icon_view_search_key_press_event), icon_view);
8663 g_signal_connect (G_OBJECT (icon_view->priv->search_window), "button-press-event", G_CALLBACK (exo_icon_view_search_button_press_event), icon_view);
8664
8665 /* allocate the frame widget */
8666 frame = g_object_new (GTK_TYPE_FRAME, "shadow-type", GTK_SHADOW_ETCHED_IN, NULL);
8667 gtk_container_add (GTK_CONTAINER (icon_view->priv->search_window), frame);
8668 gtk_widget_show (frame);
8669
8670 /* allocate the vertical box */
8671 vbox = g_object_new (GTK_TYPE_VBOX, "border-width", 3, NULL);
8672 gtk_container_add (GTK_CONTAINER (frame), vbox);
8673 gtk_widget_show (vbox);
8674
8675 /* allocate the search entry widget */
8676 icon_view->priv->search_entry = gtk_entry_new ();
8677 g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "activate", G_CALLBACK (exo_icon_view_search_activate), icon_view);
8678 #if GTK_CHECK_VERSION(2, 20, 0)
8679 g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "preedit-changed",
8680 G_CALLBACK (exo_icon_view_search_preedit_changed), icon_view);
8681 #else
8682 g_signal_connect (G_OBJECT (GTK_ENTRY (icon_view->priv->search_entry)->im_context), "preedit-changed",
8683 G_CALLBACK (exo_icon_view_search_preedit_changed), icon_view);
8684 #endif
8685 gtk_box_pack_start (GTK_BOX (vbox), icon_view->priv->search_entry, TRUE, TRUE, 0);
8686 gtk_widget_realize (icon_view->priv->search_entry);
8687 gtk_widget_show (icon_view->priv->search_entry);
8688 }
8689
8690
8691
8692 static void
8693 exo_icon_view_search_init (GtkWidget *search_entry,
8694 ExoIconView *icon_view)
8695 {
8696 GtkTreeModel *model;
8697 GtkTreeIter iter;
8698 const gchar *text;
8699 gint length;
8700 gint count = 0;
8701
8702 _exo_return_if_fail (GTK_IS_ENTRY (search_entry));
8703 _exo_return_if_fail (EXO_IS_ICON_VIEW (icon_view));
8704
8705 /* determine the current text for the search entry */
8706 text = gtk_entry_get_text (GTK_ENTRY (search_entry));
8707 if (G_UNLIKELY (text == NULL))
8708 return;
8709
8710 /* unselect all items */
8711 exo_icon_view_unselect_all (icon_view);
8712
8713 /* renew the flush timeout */
8714 if ((icon_view->priv->search_timeout_id != 0))
8715 {
8716 /* drop the previous timeout */
8717 g_source_remove (icon_view->priv->search_timeout_id);
8718
8719 /* schedule a new timeout */
8720 icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8721 exo_icon_view_search_timeout, icon_view,
8722 exo_icon_view_search_timeout_destroy);
8723 }
8724
8725 /* verify that we have a search text */
8726 length = strlen (text);
8727 if (length < 1)
8728 return;
8729
8730 /* verify that we have a valid model */
8731 model = exo_icon_view_get_model (icon_view);
8732 if (G_UNLIKELY (model == NULL))
8733 return;
8734
8735 /* start the interactive search */
8736 if (gtk_tree_model_get_iter_first (model, &iter))
8737 {
8738 /* let's see if we have a match */
8739 if (exo_icon_view_search_iter (icon_view, model, &iter, text, &count, 1))
8740 icon_view->priv->search_selected_iter = 1;
8741 }
8742 }
8743
8744
8745
8746 static gboolean
8747 exo_icon_view_search_iter (ExoIconView *icon_view,
8748 GtkTreeModel *model,
8749 GtkTreeIter *iter,
8750 const gchar *text,
8751 gint *count,
8752 gint n)
8753 {
8754 GtkTreePath *path;
8755
8756 _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
8757 _exo_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
8758 _exo_return_val_if_fail (count != NULL, FALSE);
8759
8760 /* search for a matching item */
8761 do
8762 {
8763 if (!(*icon_view->priv->search_equal_func) (model, icon_view->priv->search_column, text, iter, icon_view->priv->search_equal_data))
8764 {
8765 (*count) += 1;
8766 if (*count == n)
8767 {
8768 /* place cursor on the item and select it */
8769 path = gtk_tree_model_get_path (model, iter);
8770 exo_icon_view_select_path (icon_view, path);
8771 exo_icon_view_set_cursor (icon_view, path, NULL, FALSE);
8772 gtk_tree_path_free (path);
8773 return TRUE;
8774 }
8775 }
8776 }
8777 while (gtk_tree_model_iter_next (model, iter));
8778
8779 /* no match */
8780 return FALSE;
8781 }
8782
8783
8784
8785 static void
8786 exo_icon_view_search_move (GtkWidget *widget,
8787 ExoIconView *icon_view,
8788 gboolean move_up)
8789 {
8790 GtkTreeModel *model;
8791 const gchar *text;
8792 GtkTreeIter iter;
8793 gboolean retval;
8794 gint length;
8795 gint count = 0;
8796
8797 /* determine the current text for the search entry */
8798 text = gtk_entry_get_text (GTK_ENTRY (icon_view->priv->search_entry));
8799 if (G_UNLIKELY (text == NULL))
8800 return;
8801
8802 /* if we already selected the first item, we cannot go up */
8803 if (move_up && icon_view->priv->search_selected_iter == 1)
8804 return;
8805
8806 /* determine the length of the search text */
8807 length = strlen (text);
8808 if (G_UNLIKELY (length < 1))
8809 return;
8810
8811 /* unselect all items */
8812 exo_icon_view_unselect_all (icon_view);
8813
8814 /* verify that we have a valid model */
8815 model = exo_icon_view_get_model (icon_view);
8816 if (G_UNLIKELY (model == NULL))
8817 return;
8818
8819 /* determine the iterator to the first item */
8820 if (!gtk_tree_model_get_iter_first (model, &iter))
8821 return;
8822
8823 /* first attempt to search */
8824 retval = exo_icon_view_search_iter (icon_view, model, &iter, text, &count, move_up
8825 ? (icon_view->priv->search_selected_iter - 1)
8826 : (icon_view->priv->search_selected_iter + 1));
8827
8828 /* check if we found something */
8829 if (G_LIKELY (retval))
8830 {
8831 /* match found */
8832 icon_view->priv->search_selected_iter += move_up ? -1 : 1;
8833 }
8834 else
8835 {
8836 /* return to old iter */
8837 if (gtk_tree_model_get_iter_first (model, &iter))
8838 {
8839 count = 0;
8840 exo_icon_view_search_iter (icon_view, model, &iter, text, &count,
8841 icon_view->priv->search_selected_iter);
8842 }
8843 }
8844 }
8845
8846
8847
8848 static void
8849 #if GTK_CHECK_VERSION(2, 20, 0)
8850 exo_icon_view_search_preedit_changed (GtkEntry *entry,
8851 gchar *preedit,
8852 ExoIconView *icon_view)
8853 #else
8854 exo_icon_view_search_preedit_changed (GtkIMContext *im_context,
8855 ExoIconView *icon_view)
8856 #endif
8857 {
8858 icon_view->priv->search_imcontext_changed = TRUE;
8859
8860 /* re-register the search timeout */
8861 if (G_LIKELY (icon_view->priv->search_timeout_id != 0))
8862 {
8863 g_source_remove (icon_view->priv->search_timeout_id);
8864 icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8865 exo_icon_view_search_timeout, icon_view,
8866 exo_icon_view_search_timeout_destroy);
8867 }
8868 }
8869
8870
8871
8872 static gboolean
8873 exo_icon_view_search_start (ExoIconView *icon_view,
8874 gboolean keybinding)
8875 {
8876 GTypeClass *klass;
8877
8878 /* check if typeahead is enabled */
8879 if (G_UNLIKELY (!icon_view->priv->enable_search && !keybinding))
8880 return FALSE;
8881
8882 /* check if we already display the search window */
8883 if (icon_view->priv->search_window != NULL && gtk_widget_get_visible (icon_view->priv->search_window))
8884 return TRUE;
8885
8886 /* we only start interactive search if we have focus,
8887 * we don't want to start interactive search if one of
8888 * our children has the focus.
8889 */
8890 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
8891 return FALSE;
8892
8893 /* verify that we have a search column */
8894 if (G_UNLIKELY (icon_view->priv->search_column < 0))
8895 return FALSE;
8896
8897 exo_icon_view_search_ensure_directory (icon_view);
8898
8899 /* clear search entry if we were started by a keybinding */
8900 if (G_UNLIKELY (keybinding))
8901 gtk_entry_set_text (GTK_ENTRY (icon_view->priv->search_entry), "");
8902
8903 /* determine the position for the search dialog */
8904 (*icon_view->priv->search_position_func) (icon_view, icon_view->priv->search_window, icon_view->priv->search_position_data);
8905
8906 /* display the search dialog */
8907 gtk_widget_show (icon_view->priv->search_window);
8908
8909 /* connect "changed" signal for the entry */
8910 if (G_UNLIKELY (icon_view->priv->search_entry_changed_id == 0))
8911 {
8912 icon_view->priv->search_entry_changed_id = g_signal_connect (G_OBJECT (icon_view->priv->search_entry), "changed",
8913 G_CALLBACK (exo_icon_view_search_init), icon_view);
8914 }
8915
8916 /* start the search timeout */
8917 icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
8918 exo_icon_view_search_timeout, icon_view,
8919 exo_icon_view_search_timeout_destroy);
8920
8921 /* grab focus will select all the text, we don't want that to happen, so we
8922 * call the parent instance and bypass the selection change. This is probably
8923 * really hackish, but GtkTreeView does it as well *hrhr*
8924 */
8925 klass = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (icon_view->priv->search_entry));
8926 (*GTK_WIDGET_CLASS (klass)->grab_focus) (icon_view->priv->search_entry);
8927
8928 /* send focus-in event */
8929 _exo_gtk_widget_send_focus_change (icon_view->priv->search_entry, TRUE);
8930
8931 /* search first matching iter */
8932 exo_icon_view_search_init (icon_view->priv->search_entry, icon_view);
8933
8934 return TRUE;
8935 }
8936
8937
8938
8939 static gboolean
8940 exo_icon_view_search_equal_func (GtkTreeModel *model,
8941 gint column,
8942 const gchar *key,
8943 GtkTreeIter *iter,
8944 gpointer user_data)
8945 {
8946 const gchar *str;
8947 gboolean retval = TRUE;
8948 GValue transformed = { 0, };
8949 GValue value = { 0, };
8950 gchar *case_normalized_string = NULL;
8951 gchar *case_normalized_key = NULL;
8952 gchar *normalized_string;
8953 gchar *normalized_key;
8954
8955 /* determine the value for the column/iter */
8956 gtk_tree_model_get_value (model, iter, column, &value);
8957
8958 /* try to transform the value to a string */
8959 g_value_init (&transformed, G_TYPE_STRING);
8960 if (!g_value_transform (&value, &transformed))
8961 {
8962 g_value_unset (&value);
8963 return TRUE;
8964 }
8965 g_value_unset (&value);
8966
8967 /* check if we have a string value */
8968 str = g_value_get_string (&transformed);
8969 if (G_UNLIKELY (str == NULL))
8970 {
8971 g_value_unset (&transformed);
8972 return TRUE;
8973 }
8974
8975 /* normalize the string and the key */
8976 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
8977 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
8978
8979 /* check if we have normalized both string */
8980 if (G_LIKELY (normalized_string != NULL && normalized_key != NULL))
8981 {
8982 case_normalized_string = g_utf8_casefold (normalized_string, -1);
8983 case_normalized_key = g_utf8_casefold (normalized_key, -1);
8984
8985 /* compare the casefolded strings */
8986 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
8987 retval = FALSE;
8988 }
8989
8990 /* cleanup */
8991 g_free (case_normalized_string);
8992 g_free (case_normalized_key);
8993 g_value_unset (&transformed);
8994 g_free (normalized_string);
8995 g_free (normalized_key);
8996
8997 return retval;
8998 }
8999
9000
9001
9002 static void
9003 exo_icon_view_search_position_func (ExoIconView *icon_view,
9004 GtkWidget *search_dialog,
9005 gpointer user_data)
9006 {
9007 GtkRequisition requisition;
9008 GdkRectangle monitor;
9009 GdkWindow *view_window = gtk_widget_get_window (GTK_WIDGET (icon_view));
9010 GdkScreen *screen = gdk_window_get_screen (view_window);
9011 gint view_width, view_height;
9012 gint view_x, view_y;
9013 gint monitor_num;
9014 gint x, y;
9015
9016 /* determine the monitor geometry */
9017 monitor_num = gdk_screen_get_monitor_at_window (screen, view_window);
9018 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
9019
9020 /* make sure the search dialog is realized */
9021 gtk_widget_realize (search_dialog);
9022
9023 gdk_window_get_origin (view_window, &view_x, &view_y);
9024 #if GTK_CHECK_VERSION(2, 24, 0)
9025 view_width = gdk_window_get_width (view_window);
9026 view_height = gdk_window_get_height (view_window);
9027 #else
9028 gdk_drawable_get_size (view_window, &view_width, &view_height);
9029 #endif
9030 #if GTK_CHECK_VERSION(3, 0, 0)
9031 gtk_widget_get_preferred_size (search_dialog, NULL, &requisition);
9032 #else
9033 gtk_widget_size_request (search_dialog, &requisition);
9034 #endif
9035
9036 if (view_x + view_width > gdk_screen_get_width (screen))
9037 x = gdk_screen_get_width (screen) - requisition.width;
9038 else if (view_x + view_width - requisition.width < 0)
9039 x = 0;
9040 else
9041 x = view_x + view_width - requisition.width;
9042
9043 if (view_y + view_height + requisition.height > gdk_screen_get_height (screen))
9044 y = gdk_screen_get_height (screen) - requisition.height;
9045 else if (view_y + view_height < 0) /* isn't really possible ... */
9046 y = 0;
9047 else
9048 y = view_y + view_height;
9049
9050 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
9051 }
9052
9053
9054
9055 static gboolean
9056 exo_icon_view_search_button_press_event (GtkWidget *widget,
9057 GdkEventButton *event,
9058 ExoIconView *icon_view)
9059 {
9060 _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
9061 _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
9062
9063 /* hide the search dialog */
9064 exo_icon_view_search_dialog_hide (widget, icon_view);
9065
9066 if (event->window == icon_view->priv->bin_window)
9067 exo_icon_view_button_press_event (GTK_WIDGET (icon_view), event);
9068
9069 return TRUE;
9070 }
9071
9072
9073
9074 static gboolean
9075 exo_icon_view_search_delete_event (GtkWidget *widget,
9076 GdkEventAny *event,
9077 ExoIconView *icon_view)
9078 {
9079 _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
9080 _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
9081
9082 /* hide the search dialog */
9083 exo_icon_view_search_dialog_hide (widget, icon_view);
9084
9085 return TRUE;
9086 }
9087
9088
9089
9090 static gboolean
9091 exo_icon_view_search_key_press_event (GtkWidget *widget,
9092 GdkEventKey *event,
9093 ExoIconView *icon_view)
9094 {
9095 gboolean retval = FALSE;
9096
9097 _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
9098 _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
9099
9100
9101 /* close window and cancel the search */
9102 if (event->keyval == GDK_KEY_Escape || event->keyval == GDK_KEY_Tab)
9103 {
9104 exo_icon_view_search_dialog_hide (widget, icon_view);
9105 return TRUE;
9106 }
9107
9108 /* select previous matching iter */
9109 if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
9110 {
9111 exo_icon_view_search_move (widget, icon_view, TRUE);
9112 retval = TRUE;
9113 }
9114
9115 if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
9116 && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
9117 {
9118 exo_icon_view_search_move (widget, icon_view, TRUE);
9119 retval = TRUE;
9120 }
9121
9122 /* select next matching iter */
9123 if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
9124 {
9125 exo_icon_view_search_move (widget, icon_view, FALSE);
9126 retval = TRUE;
9127 }
9128
9129 if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
9130 && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G))
9131 {
9132 exo_icon_view_search_move (widget, icon_view, FALSE);
9133 retval = TRUE;
9134 }
9135
9136 /* renew the flush timeout */
9137 if (retval && (icon_view->priv->search_timeout_id != 0))
9138 {
9139 /* drop the previous timeout */
9140 g_source_remove (icon_view->priv->search_timeout_id);
9141
9142 /* schedule a new timeout */
9143 icon_view->priv->search_timeout_id = gdk_threads_add_timeout_full (G_PRIORITY_LOW, EXO_ICON_VIEW_SEARCH_DIALOG_TIMEOUT,
9144 exo_icon_view_search_timeout, icon_view,
9145 exo_icon_view_search_timeout_destroy);
9146 }
9147
9148 return retval;
9149 }
9150
9151
9152
9153 static gboolean
9154 exo_icon_view_search_scroll_event (GtkWidget *widget,
9155 GdkEventScroll *event,
9156 ExoIconView *icon_view)
9157 {
9158 gboolean retval = TRUE;
9159
9160 _exo_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
9161 _exo_return_val_if_fail (EXO_IS_ICON_VIEW (icon_view), FALSE);
9162
9163 if (event->direction == GDK_SCROLL_UP)
9164 exo_icon_view_search_move (widget, icon_view, TRUE);
9165 else if (event->direction == GDK_SCROLL_DOWN)
9166 exo_icon_view_search_move (widget, icon_view, FALSE);
9167 else
9168 retval = FALSE;
9169
9170 return retval;
9171 }
9172
9173
9174
9175 static gboolean
9176 exo_icon_view_search_timeout (gpointer user_data)
9177 {
9178 ExoIconView *icon_view = EXO_ICON_VIEW (user_data);
9179
9180 if(!g_source_is_destroyed(g_main_current_source()))
9181 exo_icon_view_search_dialog_hide (icon_view->priv->search_window, icon_view);
9182
9183 return FALSE;
9184 }
9185
9186
9187
9188 static void
9189 exo_icon_view_search_timeout_destroy (gpointer user_data)
9190 {
9191 EXO_ICON_VIEW (user_data)->priv->search_timeout_id = 0;
9192 }
9193
9194
9195 /* Accessibility Support */
9196
9197 static gpointer accessible_parent_class;
9198 static gpointer accessible_item_parent_class;
9199 static GQuark accessible_private_data_quark = 0;
9200
9201 #define EXO_TYPE_ICON_VIEW_ITEM_ACCESSIBLE (exo_icon_view_item_accessible_get_type ())
9202 #define EXO_ICON_VIEW_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXO_TYPE_ICON_VIEW_ITEM_ACCESSIBLE, ExoIconViewItemAccessible))
9203 #define EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXO_TYPE_ICON_VIEW_ITEM_ACCESSIBLE))
9204
9205 static GType exo_icon_view_item_accessible_get_type (void);
9206
9207 enum {
9208 ACTION_ACTIVATE,
9209 LAST_ACTION
9210 };
9211
9212 typedef struct
9213 {
9214 AtkObject parent;
9215 ExoIconViewItem *item;
9216 GtkWidget *widget;
9217 AtkStateSet *state_set;
9218 gchar *text;
9219 GtkTextBuffer *text_buffer;
9220 gchar *action_descriptions[LAST_ACTION];
9221 gchar *image_description;
9222 guint action_idle_handler;
9223 } ExoIconViewItemAccessible;
9224
9225 static const gchar *const exo_icon_view_item_accessible_action_names[] =
9226 {
9227 "activate",
9228 NULL
9229 };
9230
9231 static const gchar *const exo_icon_view_item_accessible_action_descriptions[] =
9232 {
9233 "Activate item",
9234 NULL
9235 };
9236 typedef struct _ExoIconViewItemAccessibleClass
9237 {
9238 AtkObjectClass parent_class;
9239
9240 } ExoIconViewItemAccessibleClass;
9241
9242 static gboolean exo_icon_view_item_accessible_is_showing (ExoIconViewItemAccessible *item);
9243
9244 static gboolean
9245 exo_icon_view_item_accessible_idle_do_action (gpointer data)
9246 {
9247 ExoIconViewItemAccessible *item;
9248 ExoIconView *icon_view;
9249 GtkTreePath *path;
9250
9251 if(g_source_is_destroyed(g_main_current_source()))
9252 return FALSE;
9253
9254 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (data);
9255 item->action_idle_handler = 0;
9256
9257 if (item->widget != NULL)
9258 {
9259 icon_view = EXO_ICON_VIEW (item->widget);
9260 path = gtk_tree_path_new_from_indices (item->item->index, -1);
9261 exo_icon_view_item_activated (icon_view, path);
9262 gtk_tree_path_free (path);
9263 }
9264
9265 return FALSE;
9266 }
9267
9268 static gboolean
9269 exo_icon_view_item_accessible_action_do_action (AtkAction *action,
9270 gint i)
9271 {
9272 ExoIconViewItemAccessible *item;
9273
9274 if (i < 0 || i >= LAST_ACTION)
9275 return FALSE;
9276
9277 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (action);
9278
9279 if (!EXO_IS_ICON_VIEW (item->widget))
9280 return FALSE;
9281
9282 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9283 return FALSE;
9284
9285 switch (i)
9286 {
9287 case ACTION_ACTIVATE:
9288 if (!item->action_idle_handler)
9289 item->action_idle_handler = gdk_threads_add_idle (exo_icon_view_item_accessible_idle_do_action, item);
9290 break;
9291 default:
9292 g_assert_not_reached ();
9293 return FALSE;
9294
9295 }
9296 return TRUE;
9297 }
9298
9299 static gint
9300 exo_icon_view_item_accessible_action_get_n_actions (AtkAction *action)
9301 {
9302 return LAST_ACTION;
9303 }
9304
9305 static const gchar *
9306 exo_icon_view_item_accessible_action_get_description (AtkAction *action,
9307 gint i)
9308 {
9309 ExoIconViewItemAccessible *item;
9310
9311 if (i < 0 || i >= LAST_ACTION)
9312 return NULL;
9313
9314 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (action);
9315
9316 if (item->action_descriptions[i])
9317 return item->action_descriptions[i];
9318 else
9319 return exo_icon_view_item_accessible_action_descriptions[i];
9320 }
9321
9322 static const gchar *
9323 exo_icon_view_item_accessible_action_get_name (AtkAction *action,
9324 gint i)
9325 {
9326 if (i < 0 || i >= LAST_ACTION)
9327 return NULL;
9328
9329 return exo_icon_view_item_accessible_action_names[i];
9330 }
9331
9332 static gboolean
9333 exo_icon_view_item_accessible_action_set_description (AtkAction *action,
9334 gint i,
9335 const gchar *description)
9336 {
9337 ExoIconViewItemAccessible *item;
9338
9339 if (i < 0 || i >= LAST_ACTION)
9340 return FALSE;
9341
9342 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (action);
9343
9344 g_free (item->action_descriptions[i]);
9345
9346 item->action_descriptions[i] = g_strdup (description);
9347
9348 return TRUE;
9349 }
9350
9351 static void
9352 atk_action_item_interface_init (AtkActionIface *iface)
9353 {
9354 iface->do_action = exo_icon_view_item_accessible_action_do_action;
9355 iface->get_n_actions = exo_icon_view_item_accessible_action_get_n_actions;
9356 iface->get_description = exo_icon_view_item_accessible_action_get_description;
9357 iface->get_name = exo_icon_view_item_accessible_action_get_name;
9358 iface->set_description = exo_icon_view_item_accessible_action_set_description;
9359 }
9360
9361 static const gchar *
9362 exo_icon_view_item_accessible_image_get_image_description (AtkImage *image)
9363 {
9364 ExoIconViewItemAccessible *item;
9365
9366 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (image);
9367
9368 return item->image_description;
9369 }
9370
9371 static gboolean
9372 exo_icon_view_item_accessible_image_set_image_description (AtkImage *image,
9373 const gchar *description)
9374 {
9375 ExoIconViewItemAccessible *item;
9376
9377 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (image);
9378
9379 g_free (item->image_description);
9380 item->image_description = g_strdup (description);
9381
9382 return TRUE;
9383 }
9384
9385 static gboolean
9386 get_pixbuf_box (ExoIconView *icon_view,
9387 ExoIconViewItem *item,
9388 GdkRectangle *box)
9389 {
9390 GList *l;
9391
9392 for (l = icon_view->priv->cell_list; l; l = l->next)
9393 {
9394 ExoIconViewCellInfo *info = l->data;
9395
9396 if (GTK_IS_CELL_RENDERER_PIXBUF (info->cell))
9397 {
9398 if (info->position < item->n_cells)
9399 *box = item->box[info->position];
9400
9401 return TRUE;
9402 }
9403 }
9404
9405 return FALSE;
9406 }
9407
9408 static gchar *
9409 get_text (ExoIconView *icon_view,
9410 ExoIconViewItem *item)
9411 {
9412 GList *l;
9413 gchar *text;
9414
9415 for (l = icon_view->priv->cell_list; l; l = l->next)
9416 {
9417 ExoIconViewCellInfo *info = l->data;
9418
9419 if (GTK_IS_CELL_RENDERER_TEXT (info->cell))
9420 {
9421 g_object_get (info->cell, "text", &text, NULL);
9422
9423 return text;
9424 }
9425 }
9426
9427 return NULL;
9428 }
9429
9430 static void
9431 exo_icon_view_item_accessible_image_get_image_size (AtkImage *image,
9432 gint *width,
9433 gint *height)
9434 {
9435 ExoIconViewItemAccessible *item;
9436 GdkRectangle box;
9437
9438 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (image);
9439
9440 if (!EXO_IS_ICON_VIEW (item->widget))
9441 return;
9442
9443 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9444 return;
9445
9446 if (get_pixbuf_box (EXO_ICON_VIEW (item->widget), item->item, &box))
9447 {
9448 *width = box.width;
9449 *height = box.height;
9450 }
9451 }
9452
9453 static void
9454 exo_icon_view_item_accessible_image_get_image_position (AtkImage *image,
9455 gint *x,
9456 gint *y,
9457 AtkCoordType coord_type)
9458 {
9459 ExoIconViewItemAccessible *item;
9460 AtkObject *parent_obj;
9461 GdkRectangle box;
9462
9463 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (image);
9464
9465 if (!EXO_IS_ICON_VIEW (item->widget))
9466 return;
9467
9468 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9469 return;
9470
9471 parent_obj = gtk_widget_get_accessible (item->widget);
9472 atk_component_get_extents (ATK_COMPONENT (parent_obj), x, y, NULL, NULL, coord_type);
9473
9474 if (get_pixbuf_box (EXO_ICON_VIEW (item->widget), item->item, &box))
9475 {
9476 *x+= box.x - item->item->area.x;
9477 *y+= box.y - item->item->area.y;
9478 }
9479
9480 }
9481
9482 static void
9483 atk_image_item_interface_init (AtkImageIface *iface)
9484 {
9485 iface->get_image_description = exo_icon_view_item_accessible_image_get_image_description;
9486 iface->set_image_description = exo_icon_view_item_accessible_image_set_image_description;
9487 iface->get_image_size = exo_icon_view_item_accessible_image_get_image_size;
9488 iface->get_image_position = exo_icon_view_item_accessible_image_get_image_position;
9489 }
9490
9491 static gchar *
9492 exo_icon_view_item_accessible_text_get_text (AtkText *text,
9493 gint start_pos,
9494 gint end_pos)
9495 {
9496 ExoIconViewItemAccessible *item;
9497 GtkTextIter start, end;
9498 GtkTextBuffer *buffer;
9499
9500 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9501
9502 if (!EXO_IS_ICON_VIEW (item->widget))
9503 return NULL;
9504
9505 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9506 return NULL;
9507
9508 buffer = item->text_buffer;
9509 gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
9510 if (end_pos < 0)
9511 gtk_text_buffer_get_end_iter (buffer, &end);
9512 else
9513 gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
9514
9515 return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
9516 }
9517
9518 static gunichar
9519 exo_icon_view_item_accessible_text_get_character_at_offset (AtkText *text,
9520 gint offset)
9521 {
9522 ExoIconViewItemAccessible *item;
9523 GtkTextIter start, end;
9524 GtkTextBuffer *buffer;
9525 gchar *string;
9526 gunichar unichar;
9527
9528 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9529
9530 if (!EXO_IS_ICON_VIEW (item->widget))
9531 return '\0';
9532
9533 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9534 return '\0';
9535
9536 buffer = item->text_buffer;
9537 if (offset >= gtk_text_buffer_get_char_count (buffer))
9538 return '\0';
9539
9540 gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
9541 end = start;
9542 gtk_text_iter_forward_char (&end);
9543 string = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
9544 unichar = g_utf8_get_char (string);
9545 g_free(string);
9546
9547 return unichar;
9548 }
9549
9550 static gchar*
9551 exo_icon_view_item_accessible_text_get_text_before_offset (AtkText *text,
9552 gint offset,
9553 AtkTextBoundary boundary_type,
9554 gint *start_offset,
9555 gint *end_offset)
9556 {
9557 ExoIconViewItemAccessible *item;
9558 GtkTextIter start, end;
9559 GtkTextBuffer *buffer;
9560
9561 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9562
9563 if (!EXO_IS_ICON_VIEW (item->widget))
9564 return NULL;
9565
9566 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9567 return NULL;
9568
9569 buffer = item->text_buffer;
9570
9571 if (!gtk_text_buffer_get_char_count (buffer))
9572 {
9573 *start_offset = 0;
9574 *end_offset = 0;
9575 return g_strdup ("");
9576 }
9577 gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
9578
9579 end = start;
9580
9581 switch (boundary_type)
9582 {
9583 case ATK_TEXT_BOUNDARY_CHAR:
9584 gtk_text_iter_backward_char(&start);
9585 break;
9586 case ATK_TEXT_BOUNDARY_WORD_START:
9587 if (!gtk_text_iter_starts_word (&start))
9588 gtk_text_iter_backward_word_start (&start);
9589 end = start;
9590 gtk_text_iter_backward_word_start(&start);
9591 break;
9592 case ATK_TEXT_BOUNDARY_WORD_END:
9593 if (gtk_text_iter_inside_word (&start) &&
9594 !gtk_text_iter_starts_word (&start))
9595 gtk_text_iter_backward_word_start (&start);
9596 while (!gtk_text_iter_ends_word (&start))
9597 {
9598 if (!gtk_text_iter_backward_char (&start))
9599 break;
9600 }
9601 end = start;
9602 gtk_text_iter_backward_word_start(&start);
9603 while (!gtk_text_iter_ends_word (&start))
9604 {
9605 if (!gtk_text_iter_backward_char (&start))
9606 break;
9607 }
9608 break;
9609 case ATK_TEXT_BOUNDARY_SENTENCE_START:
9610 if (!gtk_text_iter_starts_sentence (&start))
9611 gtk_text_iter_backward_sentence_start (&start);
9612 end = start;
9613 gtk_text_iter_backward_sentence_start (&start);
9614 break;
9615 case ATK_TEXT_BOUNDARY_SENTENCE_END:
9616 if (gtk_text_iter_inside_sentence (&start) &&
9617 !gtk_text_iter_starts_sentence (&start))
9618 gtk_text_iter_backward_sentence_start (&start);
9619 while (!gtk_text_iter_ends_sentence (&start))
9620 {
9621 if (!gtk_text_iter_backward_char (&start))
9622 break;
9623 }
9624 end = start;
9625 gtk_text_iter_backward_sentence_start (&start);
9626 while (!gtk_text_iter_ends_sentence (&start))
9627 {
9628 if (!gtk_text_iter_backward_char (&start))
9629 break;
9630 }
9631 break;
9632 case ATK_TEXT_BOUNDARY_LINE_START:
9633 case ATK_TEXT_BOUNDARY_LINE_END:
9634 break;
9635 }
9636
9637 *start_offset = gtk_text_iter_get_offset (&start);
9638 *end_offset = gtk_text_iter_get_offset (&end);
9639
9640 return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
9641 }
9642
9643 static gchar*
9644 exo_icon_view_item_accessible_text_get_text_at_offset (AtkText *text,
9645 gint offset,
9646 AtkTextBoundary boundary_type,
9647 gint *start_offset,
9648 gint *end_offset)
9649 {
9650 ExoIconViewItemAccessible *item;
9651 GtkTextIter start, end;
9652 GtkTextBuffer *buffer;
9653
9654 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9655
9656 if (!EXO_IS_ICON_VIEW (item->widget))
9657 return NULL;
9658
9659 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9660 return NULL;
9661
9662 buffer = item->text_buffer;
9663
9664 if (!gtk_text_buffer_get_char_count (buffer))
9665 {
9666 *start_offset = 0;
9667 *end_offset = 0;
9668 return g_strdup ("");
9669 }
9670 gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
9671
9672 end = start;
9673
9674 switch (boundary_type)
9675 {
9676 case ATK_TEXT_BOUNDARY_CHAR:
9677 gtk_text_iter_forward_char (&end);
9678 break;
9679 case ATK_TEXT_BOUNDARY_WORD_START:
9680 if (!gtk_text_iter_starts_word (&start))
9681 gtk_text_iter_backward_word_start (&start);
9682 if (gtk_text_iter_inside_word (&end))
9683 gtk_text_iter_forward_word_end (&end);
9684 while (!gtk_text_iter_starts_word (&end))
9685 {
9686 if (!gtk_text_iter_forward_char (&end))
9687 break;
9688 }
9689 break;
9690 case ATK_TEXT_BOUNDARY_WORD_END:
9691 if (gtk_text_iter_inside_word (&start) &&
9692 !gtk_text_iter_starts_word (&start))
9693 gtk_text_iter_backward_word_start (&start);
9694 while (!gtk_text_iter_ends_word (&start))
9695 {
9696 if (!gtk_text_iter_backward_char (&start))
9697 break;
9698 }
9699 gtk_text_iter_forward_word_end (&end);
9700 break;
9701 case ATK_TEXT_BOUNDARY_SENTENCE_START:
9702 if (!gtk_text_iter_starts_sentence (&start))
9703 gtk_text_iter_backward_sentence_start (&start);
9704 if (gtk_text_iter_inside_sentence (&end))
9705 gtk_text_iter_forward_sentence_end (&end);
9706 while (!gtk_text_iter_starts_sentence (&end))
9707 {
9708 if (!gtk_text_iter_forward_char (&end))
9709 break;
9710 }
9711 break;
9712 case ATK_TEXT_BOUNDARY_SENTENCE_END:
9713 if (gtk_text_iter_inside_sentence (&start) &&
9714 !gtk_text_iter_starts_sentence (&start))
9715 gtk_text_iter_backward_sentence_start (&start);
9716 while (!gtk_text_iter_ends_sentence (&start))
9717 {
9718 if (!gtk_text_iter_backward_char (&start))
9719 break;
9720 }
9721 gtk_text_iter_forward_sentence_end (&end);
9722 break;
9723 case ATK_TEXT_BOUNDARY_LINE_START:
9724 case ATK_TEXT_BOUNDARY_LINE_END:
9725 break;
9726 }
9727
9728
9729 *start_offset = gtk_text_iter_get_offset (&start);
9730 *end_offset = gtk_text_iter_get_offset (&end);
9731
9732 return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
9733 }
9734
9735 static gchar*
9736 exo_icon_view_item_accessible_text_get_text_after_offset (AtkText *text,
9737 gint offset,
9738 AtkTextBoundary boundary_type,
9739 gint *start_offset,
9740 gint *end_offset)
9741 {
9742 ExoIconViewItemAccessible *item;
9743 GtkTextIter start, end;
9744 GtkTextBuffer *buffer;
9745
9746 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9747
9748 if (!EXO_IS_ICON_VIEW (item->widget))
9749 return NULL;
9750
9751 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9752 return NULL;
9753
9754 buffer = item->text_buffer;
9755
9756 if (!gtk_text_buffer_get_char_count (buffer))
9757 {
9758 *start_offset = 0;
9759 *end_offset = 0;
9760 return g_strdup ("");
9761 }
9762 gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
9763
9764 end = start;
9765
9766 switch (boundary_type)
9767 {
9768 case ATK_TEXT_BOUNDARY_CHAR:
9769 gtk_text_iter_forward_char(&start);
9770 gtk_text_iter_forward_chars(&end, 2);
9771 break;
9772 case ATK_TEXT_BOUNDARY_WORD_START:
9773 if (gtk_text_iter_inside_word (&end))
9774 gtk_text_iter_forward_word_end (&end);
9775 while (!gtk_text_iter_starts_word (&end))
9776 {
9777 if (!gtk_text_iter_forward_char (&end))
9778 break;
9779 }
9780 start = end;
9781 if (!gtk_text_iter_is_end (&end))
9782 {
9783 gtk_text_iter_forward_word_end (&end);
9784 while (!gtk_text_iter_starts_word (&end))
9785 {
9786 if (!gtk_text_iter_forward_char (&end))
9787 break;
9788 }
9789 }
9790 break;
9791 case ATK_TEXT_BOUNDARY_WORD_END:
9792 gtk_text_iter_forward_word_end (&end);
9793 start = end;
9794 if (!gtk_text_iter_is_end (&end))
9795 gtk_text_iter_forward_word_end (&end);
9796 break;
9797 case ATK_TEXT_BOUNDARY_SENTENCE_START:
9798 if (gtk_text_iter_inside_sentence (&end))
9799 gtk_text_iter_forward_sentence_end (&end);
9800 while (!gtk_text_iter_starts_sentence (&end))
9801 {
9802 if (!gtk_text_iter_forward_char (&end))
9803 break;
9804 }
9805 start = end;
9806 if (!gtk_text_iter_is_end (&end))
9807 {
9808 gtk_text_iter_forward_sentence_end (&end);
9809 while (!gtk_text_iter_starts_sentence (&end))
9810 {
9811 if (!gtk_text_iter_forward_char (&end))
9812 break;
9813 }
9814 }
9815 break;
9816 case ATK_TEXT_BOUNDARY_SENTENCE_END:
9817 gtk_text_iter_forward_sentence_end (&end);
9818 start = end;
9819 if (!gtk_text_iter_is_end (&end))
9820 gtk_text_iter_forward_sentence_end (&end);
9821 break;
9822 case ATK_TEXT_BOUNDARY_LINE_START:
9823 case ATK_TEXT_BOUNDARY_LINE_END:
9824 break;
9825 }
9826 *start_offset = gtk_text_iter_get_offset (&start);
9827 *end_offset = gtk_text_iter_get_offset (&end);
9828
9829 return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
9830 }
9831
9832 static gint
9833 exo_icon_view_item_accessible_text_get_character_count (AtkText *text)
9834 {
9835 ExoIconViewItemAccessible *item;
9836
9837 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9838
9839 if (!EXO_IS_ICON_VIEW (item->widget))
9840 return 0;
9841
9842 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9843 return 0;
9844
9845 return gtk_text_buffer_get_char_count (item->text_buffer);
9846 }
9847
9848 static void
9849 exo_icon_view_item_accessible_text_get_character_extents (AtkText *text,
9850 gint offset,
9851 gint *x,
9852 gint *y,
9853 gint *width,
9854 gint *height,
9855 AtkCoordType coord_type)
9856 {
9857 ExoIconViewItemAccessible *item;
9858
9859 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9860
9861 if (!EXO_IS_ICON_VIEW (item->widget))
9862 return;
9863
9864 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9865 return;
9866
9867 }
9868
9869 static gint
9870 exo_icon_view_item_accessible_text_get_offset_at_point (AtkText *text,
9871 gint x,
9872 gint y,
9873 AtkCoordType coord_type)
9874 {
9875 ExoIconViewItemAccessible *item;
9876 gint offset = 0;
9877
9878 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (text);
9879
9880 if (!EXO_IS_ICON_VIEW (item->widget))
9881 return -1;
9882
9883 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9884 return -1;
9885
9886 return offset;
9887 }
9888
9889 static void
9890 atk_text_item_interface_init (AtkTextIface *iface)
9891 {
9892 iface->get_text = exo_icon_view_item_accessible_text_get_text;
9893 iface->get_character_at_offset = exo_icon_view_item_accessible_text_get_character_at_offset;
9894 iface->get_text_before_offset = exo_icon_view_item_accessible_text_get_text_before_offset;
9895 iface->get_text_at_offset = exo_icon_view_item_accessible_text_get_text_at_offset;
9896 iface->get_text_after_offset = exo_icon_view_item_accessible_text_get_text_after_offset;
9897 iface->get_character_count = exo_icon_view_item_accessible_text_get_character_count;
9898 iface->get_character_extents = exo_icon_view_item_accessible_text_get_character_extents;
9899 iface->get_offset_at_point = exo_icon_view_item_accessible_text_get_offset_at_point;
9900 }
9901
9902 static void
9903 exo_icon_view_item_accessible_get_extents (AtkComponent *component,
9904 gint *x,
9905 gint *y,
9906 gint *width,
9907 gint *height,
9908 AtkCoordType coord_type)
9909 {
9910 ExoIconViewItemAccessible *item;
9911 AtkObject *parent_obj;
9912 gint l_x, l_y;
9913
9914 g_return_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (component));
9915
9916 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (component);
9917 if (!GTK_IS_WIDGET (item->widget))
9918 return;
9919
9920 if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT))
9921 return;
9922
9923 *width = item->item->area.width;
9924 *height = item->item->area.height;
9925 if (exo_icon_view_item_accessible_is_showing (item))
9926 {
9927 parent_obj = gtk_widget_get_accessible (item->widget);
9928 atk_component_get_extents (ATK_COMPONENT (parent_obj), &l_x, &l_y, NULL,
9929 NULL, coord_type);
9930 *x = l_x + item->item->area.x;
9931 *y = l_y + item->item->area.y;
9932 }
9933 else
9934 {
9935 *x = G_MININT;
9936 *y = G_MININT;
9937 }
9938 }
9939
9940 static gboolean
9941 exo_icon_view_item_accessible_grab_focus (AtkComponent *component)
9942 {
9943 ExoIconViewItemAccessible *item;
9944 GtkWidget *toplevel;
9945
9946 g_return_val_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (component), FALSE);
9947
9948 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (component);
9949 if (!GTK_IS_WIDGET (item->widget))
9950 return FALSE;
9951
9952 gtk_widget_grab_focus (item->widget);
9953 exo_icon_view_set_cursor_item (EXO_ICON_VIEW (item->widget), item->item, -1);
9954 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->widget));
9955 if (gtk_widget_is_toplevel (toplevel))
9956 gtk_window_present (GTK_WINDOW (toplevel));
9957
9958 return TRUE;
9959 }
9960
9961 static void
9962 atk_component_item_interface_init (AtkComponentIface *iface)
9963 {
9964 iface->get_extents = exo_icon_view_item_accessible_get_extents;
9965 iface->grab_focus = exo_icon_view_item_accessible_grab_focus;
9966 }
9967
9968 static gboolean
9969 exo_icon_view_item_accessible_add_state (ExoIconViewItemAccessible *item,
9970 AtkStateType state_type,
9971 gboolean emit_signal)
9972 {
9973 gboolean rc;
9974
9975 rc = atk_state_set_add_state (item->state_set, state_type);
9976 /*
9977 * The signal should only be generated if the value changed,
9978 * not when the item is set up. So states that are set
9979 * initially should pass FALSE as the emit_signal argument.
9980 */
9981
9982 if (emit_signal)
9983 {
9984 atk_object_notify_state_change (ATK_OBJECT (item), state_type, TRUE);
9985 /* If state_type is ATK_STATE_VISIBLE, additional notification */
9986 if (state_type == ATK_STATE_VISIBLE)
9987 g_signal_emit_by_name (item, "visible-data-changed");
9988 }
9989
9990 return rc;
9991 }
9992
9993 static gboolean
9994 exo_icon_view_item_accessible_remove_state (ExoIconViewItemAccessible *item,
9995 AtkStateType state_type,
9996 gboolean emit_signal)
9997 {
9998 if (atk_state_set_contains_state (item->state_set, state_type))
9999 {
10000 gboolean rc;
10001
10002 rc = atk_state_set_remove_state (item->state_set, state_type);
10003 /*
10004 * The signal should only be generated if the value changed,
10005 * not when the item is set up. So states that are set
10006 * initially should pass FALSE as the emit_signal argument.
10007 */
10008
10009 if (emit_signal)
10010 {
10011 atk_object_notify_state_change (ATK_OBJECT (item), state_type, FALSE);
10012 /* If state_type is ATK_STATE_VISIBLE, additional notification */
10013 if (state_type == ATK_STATE_VISIBLE)
10014 g_signal_emit_by_name (item, "visible-data-changed");
10015 }
10016
10017 return rc;
10018 }
10019 else
10020 return FALSE;
10021 }
10022
10023 static gboolean
10024 exo_icon_view_item_accessible_is_showing (ExoIconViewItemAccessible *item)
10025 {
10026 ExoIconView *icon_view;
10027 GtkAllocation allocation;
10028 GdkRectangle visible_rect;
10029 gboolean is_showing;
10030
10031 /*
10032 * An item is considered "SHOWING" if any part of the item is in the
10033 * visible rectangle.
10034 */
10035
10036 if (!EXO_IS_ICON_VIEW (item->widget))
10037 return FALSE;
10038
10039 if (item->item == NULL)
10040 return FALSE;
10041
10042 gtk_widget_get_allocation (item->widget, &allocation);
10043
10044 icon_view = EXO_ICON_VIEW (item->widget);
10045 visible_rect.x = 0;
10046 if (icon_view->priv->hadjustment)
10047 visible_rect.x += gtk_adjustment_get_value (icon_view->priv->hadjustment);
10048 visible_rect.y = 0;
10049 if (icon_view->priv->hadjustment)
10050 visible_rect.y += gtk_adjustment_get_value (icon_view->priv->vadjustment);
10051 visible_rect.width = allocation.width;
10052 visible_rect.height = allocation.height;
10053
10054 if (((item->item->area.x + item->item->area.width) < visible_rect.x) ||
10055 ((item->item->area.y + item->item->area.height) < (visible_rect.y)) ||
10056 (item->item->area.x > (visible_rect.x + visible_rect.width)) ||
10057 (item->item->area.y > (visible_rect.y + visible_rect.height)))
10058 is_showing = FALSE;
10059 else
10060 is_showing = TRUE;
10061
10062 return is_showing;
10063 }
10064
10065 static gboolean
10066 exo_icon_view_item_accessible_set_visibility (ExoIconViewItemAccessible *item,
10067 gboolean emit_signal)
10068 {
10069 if (exo_icon_view_item_accessible_is_showing (item))
10070 return exo_icon_view_item_accessible_add_state (item, ATK_STATE_SHOWING,
10071 emit_signal);
10072 else
10073 return exo_icon_view_item_accessible_remove_state (item, ATK_STATE_SHOWING,
10074 emit_signal);
10075 }
10076
10077 static void
10078 exo_icon_view_item_accessible_object_init (ExoIconViewItemAccessible *item)
10079 {
10080 gint i;
10081
10082 item->state_set = atk_state_set_new ();
10083
10084 atk_state_set_add_state (item->state_set, ATK_STATE_ENABLED);
10085 atk_state_set_add_state (item->state_set, ATK_STATE_FOCUSABLE);
10086 atk_state_set_add_state (item->state_set, ATK_STATE_SENSITIVE);
10087 atk_state_set_add_state (item->state_set, ATK_STATE_SELECTABLE);
10088 atk_state_set_add_state (item->state_set, ATK_STATE_VISIBLE);
10089
10090 for (i = 0; i < LAST_ACTION; i++)
10091 item->action_descriptions[i] = NULL;
10092
10093 item->image_description = NULL;
10094
10095 item->action_idle_handler = 0;
10096 }
10097
10098 static void
10099 exo_icon_view_item_accessible_finalize (GObject *object)
10100 {
10101 ExoIconViewItemAccessible *item;
10102 gint i;
10103
10104 g_return_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (object));
10105
10106 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (object);
10107
10108 if (item->widget)
10109 g_object_remove_weak_pointer (G_OBJECT (item->widget), (gpointer) &item->widget);
10110
10111 if (item->state_set)
10112 g_object_unref (item->state_set);
10113
10114 if (item->text_buffer)
10115 g_object_unref (item->text_buffer);
10116
10117 for (i = 0; i < LAST_ACTION; i++)
10118 g_free (item->action_descriptions[i]);
10119
10120 g_free (item->image_description);
10121
10122 if (item->action_idle_handler)
10123 {
10124 g_source_remove (item->action_idle_handler);
10125 item->action_idle_handler = 0;
10126 }
10127
10128 G_OBJECT_CLASS (accessible_item_parent_class)->finalize (object);
10129 }
10130
10131 static AtkObject*
10132 exo_icon_view_item_accessible_get_parent (AtkObject *obj)
10133 {
10134 ExoIconViewItemAccessible *item;
10135
10136 g_return_val_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (obj), NULL);
10137 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (obj);
10138
10139 if (item->widget)
10140 return gtk_widget_get_accessible (item->widget);
10141 else
10142 return NULL;
10143 }
10144
10145 static gint
10146 exo_icon_view_item_accessible_get_index_in_parent (AtkObject *obj)
10147 {
10148 ExoIconViewItemAccessible *item;
10149
10150 g_return_val_if_fail (EXO_IS_ICON_VIEW_ITEM_ACCESSIBLE (obj), 0);
10151 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (obj);
10152
10153 return item->item->index;
10154 }
10155
10156 static AtkStateSet *
10157 exo_icon_view_item_accessible_ref_state_set (AtkObject *obj)
10158 {
10159 ExoIconViewItemAccessible *item;
10160 ExoIconView *icon_view;
10161
10162 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (obj);
10163 g_return_val_if_fail (item->state_set, NULL);
10164
10165 if (!item->widget)
10166 return NULL;
10167
10168 icon_view = EXO_ICON_VIEW (item->widget);
10169 if (icon_view->priv->cursor_item == item->item)
10170 atk_state_set_add_state (item->state_set, ATK_STATE_FOCUSED);
10171 else
10172 atk_state_set_remove_state (item->state_set, ATK_STATE_FOCUSED);
10173 if (item->item->selected)
10174 atk_state_set_add_state (item->state_set, ATK_STATE_SELECTED);
10175 else
10176 atk_state_set_remove_state (item->state_set, ATK_STATE_SELECTED);
10177
10178 return g_object_ref (item->state_set);
10179 }
10180
10181 static void
10182 exo_icon_view_item_accessible_class_init (AtkObjectClass *klass)
10183 {
10184 GObjectClass *gobject_class;
10185
10186 accessible_item_parent_class = g_type_class_peek_parent (klass);
10187
10188 gobject_class = (GObjectClass *)klass;
10189
10190 gobject_class->finalize = exo_icon_view_item_accessible_finalize;
10191
10192 klass->get_index_in_parent = exo_icon_view_item_accessible_get_index_in_parent;
10193 klass->get_parent = exo_icon_view_item_accessible_get_parent;
10194 klass->ref_state_set = exo_icon_view_item_accessible_ref_state_set;
10195 }
10196
10197 static GType
10198 exo_icon_view_item_accessible_get_type (void)
10199 {
10200 static GType type = 0;
10201
10202 if (!type)
10203 {
10204 const GTypeInfo tinfo =
10205 {
10206 sizeof (ExoIconViewItemAccessibleClass),
10207 (GBaseInitFunc) NULL, /* base init */
10208 (GBaseFinalizeFunc) NULL, /* base finalize */
10209 (GClassInitFunc) exo_icon_view_item_accessible_class_init, /* class init */
10210 (GClassFinalizeFunc) NULL, /* class finalize */
10211 NULL, /* class data */
10212 sizeof (ExoIconViewItemAccessible), /* instance size */
10213 0, /* nb preallocs */
10214 (GInstanceInitFunc) exo_icon_view_item_accessible_object_init, /* instance init */
10215 NULL /* value table */
10216 };
10217
10218 const GInterfaceInfo atk_component_info =
10219 {
10220 (GInterfaceInitFunc) atk_component_item_interface_init,
10221 (GInterfaceFinalizeFunc) NULL,
10222 NULL
10223 };
10224 const GInterfaceInfo atk_action_info =
10225 {
10226 (GInterfaceInitFunc) atk_action_item_interface_init,
10227 (GInterfaceFinalizeFunc) NULL,
10228 NULL
10229 };
10230 const GInterfaceInfo atk_image_info =
10231 {
10232 (GInterfaceInitFunc) atk_image_item_interface_init,
10233 (GInterfaceFinalizeFunc) NULL,
10234 NULL
10235 };
10236 const GInterfaceInfo atk_text_info =
10237 {
10238 (GInterfaceInitFunc) atk_text_item_interface_init,
10239 (GInterfaceFinalizeFunc) NULL,
10240 NULL
10241 };
10242
10243 type = g_type_register_static (ATK_TYPE_OBJECT,
10244 I_("ExoIconViewItemAccessible"), &tinfo, 0);
10245 g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
10246 &atk_component_info);
10247 g_type_add_interface_static (type, ATK_TYPE_ACTION,
10248 &atk_action_info);
10249 g_type_add_interface_static (type, ATK_TYPE_IMAGE,
10250 &atk_image_info);
10251 g_type_add_interface_static (type, ATK_TYPE_TEXT,
10252 &atk_text_info);
10253 }
10254
10255 return type;
10256 }
10257
10258 #define EXO_TYPE_ICON_VIEW_ACCESSIBLE (exo_icon_view_accessible_get_type ())
10259 #define EXO_ICON_VIEW_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXO_TYPE_ICON_VIEW_ACCESSIBLE, ExoIconViewAccessible))
10260 #define EXO_IS_ICON_VIEW_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EXO_TYPE_ICON_VIEW_ACCESSIBLE))
10261
10262 static GType exo_icon_view_accessible_get_type (void);
10263
10264 typedef struct
10265 {
10266 AtkObject parent;
10267 } ExoIconViewAccessible;
10268
10269 typedef struct
10270 {
10271 AtkObject *item;
10272 gint index;
10273 } ExoIconViewItemAccessibleInfo;
10274
10275 typedef struct
10276 {
10277 GList *items;
10278
10279 GtkAdjustment *old_hadj;
10280 GtkAdjustment *old_vadj;
10281
10282 GtkTreeModel *model;
10283
10284 } ExoIconViewAccessiblePrivate;
10285
10286 static ExoIconViewAccessiblePrivate *
10287 exo_icon_view_accessible_get_priv (AtkObject *accessible)
10288 {
10289 return g_object_get_qdata (G_OBJECT (accessible),
10290 accessible_private_data_quark);
10291 }
10292
10293 static void
10294 exo_icon_view_item_accessible_info_new (AtkObject *accessible,
10295 AtkObject *item,
10296 gint index)
10297 {
10298 ExoIconViewItemAccessibleInfo *info;
10299 ExoIconViewItemAccessibleInfo *tmp_info;
10300 ExoIconViewAccessiblePrivate *priv;
10301 GList *items;
10302
10303 info = g_new (ExoIconViewItemAccessibleInfo, 1);
10304 info->item = item;
10305 info->index = index;
10306
10307 priv = exo_icon_view_accessible_get_priv (accessible);
10308 items = priv->items;
10309 while (items)
10310 {
10311 tmp_info = items->data;
10312 if (tmp_info->index > index)
10313 break;
10314 items = items->next;
10315 }
10316 priv->items = g_list_insert_before (priv->items, items, info);
10317 priv->old_hadj = NULL;
10318 priv->old_vadj = NULL;
10319 }
10320
10321 static gint
10322 exo_icon_view_accessible_get_n_children (AtkObject *accessible)
10323 {
10324 ExoIconView *icon_view;
10325 GtkWidget *widget;
10326
10327 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
10328 if (!widget)
10329 return 0;
10330
10331 icon_view = EXO_ICON_VIEW (widget);
10332
10333 return g_list_length (icon_view->priv->items);
10334 }
10335
10336 static AtkObject *
10337 exo_icon_view_accessible_find_child (AtkObject *accessible,
10338 gint index)
10339 {
10340 ExoIconViewAccessiblePrivate *priv;
10341 ExoIconViewItemAccessibleInfo *info;
10342 GList *items;
10343
10344 priv = exo_icon_view_accessible_get_priv (accessible);
10345 items = priv->items;
10346
10347 while (items)
10348 {
10349 info = items->data;
10350 if (info->index == index)
10351 return info->item;
10352 items = items->next;
10353 }
10354 return NULL;
10355 }
10356
10357 static AtkObject *
10358 exo_icon_view_accessible_ref_child (AtkObject *accessible,
10359 gint index)
10360 {
10361 ExoIconView *icon_view;
10362 GtkWidget *widget;
10363 GList *icons;
10364 AtkObject *obj;
10365 ExoIconViewItemAccessible *a11y_item;
10366
10367 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
10368 if (!widget)
10369 return NULL;
10370
10371 icon_view = EXO_ICON_VIEW (widget);
10372 icons = g_list_nth (icon_view->priv->items, index);
10373 obj = NULL;
10374 if (icons)
10375 {
10376 ExoIconViewItem *item = icons->data;
10377
10378 g_return_val_if_fail (item->index == index, NULL);
10379 obj = exo_icon_view_accessible_find_child (accessible, index);
10380 if (!obj)
10381 {
10382 gchar *text;
10383
10384 obj = g_object_new (exo_icon_view_item_accessible_get_type (), NULL);
10385 exo_icon_view_item_accessible_info_new (accessible,
10386 obj,
10387 index);
10388 obj->role = ATK_ROLE_ICON;
10389 a11y_item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (obj);
10390 a11y_item->item = item;
10391 a11y_item->widget = widget;
10392 a11y_item->text_buffer = gtk_text_buffer_new (NULL);
10393
10394 exo_icon_view_set_cell_data (icon_view, item);
10395 text = get_text (icon_view, item);
10396 if (text)
10397 {
10398 gtk_text_buffer_set_text (a11y_item->text_buffer, text, -1);
10399 g_free (text);
10400 }
10401
10402 exo_icon_view_item_accessible_set_visibility (a11y_item, FALSE);
10403 g_object_add_weak_pointer (G_OBJECT (widget), (gpointer) &(a11y_item->widget));
10404 }
10405 g_object_ref (obj);
10406 }
10407 return obj;
10408 }
10409
10410 static void
10411 exo_icon_view_accessible_traverse_items (ExoIconViewAccessible *view,
10412 GList *list)
10413 {
10414 ExoIconViewAccessiblePrivate *priv;
10415 ExoIconViewItemAccessibleInfo *info;
10416 ExoIconViewItemAccessible *item;
10417 GList *items;
10418
10419 priv = exo_icon_view_accessible_get_priv (ATK_OBJECT (view));
10420 if (priv->items)
10421 {
10422 GtkWidget *widget;
10423 gboolean act_on_item;
10424
10425 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (view));
10426 if (widget == NULL)
10427 return;
10428
10429 items = priv->items;
10430
10431 act_on_item = (list == NULL);
10432
10433 while (items)
10434 {
10435
10436 info = (ExoIconViewItemAccessibleInfo *)items->data;
10437 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item);
10438
10439 if (act_on_item == FALSE && list == items)
10440 act_on_item = TRUE;
10441
10442 if (act_on_item)
10443 exo_icon_view_item_accessible_set_visibility (item, TRUE);
10444
10445 items = items->next;
10446 }
10447 }
10448 }
10449
10450 static void
10451 exo_icon_view_accessible_adjustment_changed (GtkAdjustment *adjustment,
10452 ExoIconView *icon_view)
10453 {
10454 AtkObject *obj;
10455 ExoIconViewAccessible *view;
10456
10457 /*
10458 * The scrollbars have changed
10459 */
10460 obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view));
10461 view = EXO_ICON_VIEW_ACCESSIBLE (obj);
10462
10463 exo_icon_view_accessible_traverse_items (view, NULL);
10464 }
10465
10466 static void
10467 exo_icon_view_accessible_set_scroll_adjustments (GtkWidget *widget,
10468 GtkAdjustment *hadj,
10469 GtkAdjustment *vadj)
10470 {
10471 AtkObject *atk_obj;
10472 ExoIconViewAccessiblePrivate *priv;
10473
10474 atk_obj = gtk_widget_get_accessible (widget);
10475 priv = exo_icon_view_accessible_get_priv (atk_obj);
10476
10477 if (priv->old_hadj != hadj)
10478 {
10479 if (priv->old_hadj)
10480 {
10481 g_object_remove_weak_pointer (G_OBJECT (priv->old_hadj),
10482 (gpointer *)&priv->old_hadj);
10483
10484 g_signal_handlers_disconnect_by_func (priv->old_hadj,
10485 (gpointer) exo_icon_view_accessible_adjustment_changed,
10486 widget);
10487 }
10488 priv->old_hadj = hadj;
10489 if (priv->old_hadj)
10490 {
10491 g_object_add_weak_pointer (G_OBJECT (priv->old_hadj),
10492 (gpointer *)&priv->old_hadj);
10493 g_signal_connect (hadj,
10494 "value-changed",
10495 G_CALLBACK (exo_icon_view_accessible_adjustment_changed),
10496 widget);
10497 }
10498 }
10499 if (priv->old_vadj != vadj)
10500 {
10501 if (priv->old_vadj)
10502 {
10503 g_object_remove_weak_pointer (G_OBJECT (priv->old_vadj),
10504 (gpointer *)&priv->old_vadj);
10505
10506 g_signal_handlers_disconnect_by_func (priv->old_vadj,
10507 (gpointer) exo_icon_view_accessible_adjustment_changed,
10508 widget);
10509 }
10510 priv->old_vadj = vadj;
10511 if (priv->old_vadj)
10512 {
10513 g_object_add_weak_pointer (G_OBJECT (priv->old_vadj),
10514 (gpointer *)&priv->old_vadj);
10515 g_signal_connect (vadj,
10516 "value-changed",
10517 G_CALLBACK (exo_icon_view_accessible_adjustment_changed),
10518 widget);
10519 }
10520 }
10521 }
10522
10523 static void
10524 exo_icon_view_accessible_model_row_changed (GtkTreeModel *tree_model,
10525 GtkTreePath *path,
10526 GtkTreeIter *iter,
10527 gpointer user_data)
10528 {
10529 AtkObject *atk_obj;
10530 gint index;
10531 GtkWidget *widget;
10532 ExoIconView *icon_view;
10533 ExoIconViewItem *item;
10534 //ExoIconViewAccessible *a11y_view;
10535 ExoIconViewItemAccessible *a11y_item;
10536 const gchar *name;
10537 gchar *text;
10538
10539 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data));
10540 //a11y_view = EXO_ICON_VIEW_ACCESSIBLE (atk_obj);
10541 index = gtk_tree_path_get_indices(path)[0];
10542 a11y_item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (
10543 exo_icon_view_accessible_find_child (atk_obj, index));
10544
10545 if (a11y_item)
10546 {
10547 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_obj));
10548 icon_view = EXO_ICON_VIEW (widget);
10549 item = a11y_item->item;
10550
10551 name = atk_object_get_name (ATK_OBJECT (a11y_item));
10552
10553 if (!name || strcmp (name, "") == 0)
10554 {
10555 exo_icon_view_set_cell_data (icon_view, item);
10556 text = get_text (icon_view, item);
10557 if (text)
10558 {
10559 gtk_text_buffer_set_text (a11y_item->text_buffer, text, -1);
10560 g_free (text);
10561 }
10562 }
10563 }
10564
10565 g_signal_emit_by_name (atk_obj, "visible-data-changed");
10566
10567 return;
10568 }
10569
10570 static void
10571 exo_icon_view_accessible_model_row_inserted (GtkTreeModel *tree_model,
10572 GtkTreePath *path,
10573 GtkTreeIter *iter,
10574 gpointer user_data)
10575 {
10576 ExoIconViewAccessiblePrivate *priv;
10577 ExoIconViewItemAccessibleInfo *info;
10578 ExoIconViewAccessible *view;
10579 ExoIconViewItemAccessible *item;
10580 GList *items;
10581 GList *tmp_list;
10582 AtkObject *atk_obj;
10583 gint index;
10584
10585 index = gtk_tree_path_get_indices(path)[0];
10586 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data));
10587 view = EXO_ICON_VIEW_ACCESSIBLE (atk_obj);
10588 priv = exo_icon_view_accessible_get_priv (atk_obj);
10589
10590 items = priv->items;
10591 tmp_list = NULL;
10592 while (items)
10593 {
10594 info = items->data;
10595 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item);
10596 if (info->index != item->item->index)
10597 {
10598 if (info->index < index)
10599 g_warning ("Unexpected index value on insertion %d %d", index, info->index);
10600
10601 if (tmp_list == NULL)
10602 tmp_list = items;
10603
10604 info->index = item->item->index;
10605 }
10606
10607 items = items->next;
10608 }
10609 exo_icon_view_accessible_traverse_items (view, tmp_list);
10610 g_signal_emit_by_name (atk_obj, "children-changed::add",
10611 index, NULL, NULL);
10612 return;
10613 }
10614
10615 static void
10616 exo_icon_view_accessible_model_row_deleted (GtkTreeModel *tree_model,
10617 GtkTreePath *path,
10618 gpointer user_data)
10619 {
10620 ExoIconViewAccessiblePrivate *priv;
10621 ExoIconViewItemAccessibleInfo *info;
10622 ExoIconViewAccessible *view;
10623 ExoIconViewItemAccessible *item;
10624 GList *items;
10625 GList *tmp_list;
10626 GList *deleted_item;
10627 AtkObject *atk_obj;
10628 gint index;
10629
10630 index = gtk_tree_path_get_indices(path)[0];
10631 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data));
10632 view = EXO_ICON_VIEW_ACCESSIBLE (atk_obj);
10633 priv = exo_icon_view_accessible_get_priv (atk_obj);
10634
10635 items = priv->items;
10636 tmp_list = NULL;
10637 deleted_item = NULL;
10638 info = NULL;
10639 while (items)
10640 {
10641 info = items->data;
10642 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item);
10643 if (info->index == index)
10644 {
10645 deleted_item = items;
10646 }
10647 if (info->index != item->item->index)
10648 {
10649 if (tmp_list == NULL)
10650 tmp_list = items;
10651
10652 info->index = item->item->index;
10653 }
10654
10655 items = items->next;
10656 }
10657 exo_icon_view_accessible_traverse_items (view, tmp_list);
10658 if (deleted_item)
10659 {
10660 info = deleted_item->data;
10661 exo_icon_view_item_accessible_add_state (EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item), ATK_STATE_DEFUNCT, TRUE);
10662 g_signal_emit_by_name (atk_obj, "children-changed::remove",
10663 index, NULL, NULL);
10664 priv->items = g_list_remove_link (priv->items, deleted_item);
10665 g_free (info);
10666 }
10667
10668 return;
10669 }
10670
10671 static gint
10672 exo_icon_view_accessible_item_compare (ExoIconViewItemAccessibleInfo *i1,
10673 ExoIconViewItemAccessibleInfo *i2)
10674 {
10675 return i1->index - i2->index;
10676 }
10677
10678 static void
10679 exo_icon_view_accessible_model_rows_reordered (GtkTreeModel *tree_model,
10680 GtkTreePath *path,
10681 GtkTreeIter *iter,
10682 gint *new_order,
10683 gpointer user_data)
10684 {
10685 ExoIconViewAccessiblePrivate *priv;
10686 ExoIconViewItemAccessibleInfo *info;
10687 ExoIconView *icon_view;
10688 ExoIconViewItemAccessible *item;
10689 GList *items;
10690 AtkObject *atk_obj;
10691 gint *order;
10692 gint length, i;
10693
10694 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data));
10695 icon_view = EXO_ICON_VIEW (user_data);
10696 priv = exo_icon_view_accessible_get_priv (atk_obj);
10697
10698 length = gtk_tree_model_iter_n_children (tree_model, NULL);
10699
10700 order = g_new (gint, length);
10701 for (i = 0; i < length; i++)
10702 order [new_order[i]] = i;
10703
10704 items = priv->items;
10705 while (items)
10706 {
10707 info = items->data;
10708 item = EXO_ICON_VIEW_ITEM_ACCESSIBLE (info->item);
10709 info->index = order[info->index];
10710 item->item = g_list_nth_data (icon_view->priv->items, info->index);
10711 items = items->next;
10712 }
10713 g_free (order);
10714 priv->items = g_list_sort (priv->items,
10715 (GCompareFunc)exo_icon_view_accessible_item_compare);
10716
10717 return;
10718 }
10719
10720 static void
10721 exo_icon_view_accessible_disconnect_model_signals (GtkTreeModel *model,
10722 GtkWidget *widget)
10723 {
10724 GObject *obj;
10725
10726 obj = G_OBJECT (model);
10727 g_signal_handlers_disconnect_by_func (obj, (gpointer) exo_icon_view_accessible_model_row_changed, widget);
10728 g_signal_handlers_disconnect_by_func (obj, (gpointer) exo_icon_view_accessible_model_row_inserted, widget);
10729 g_signal_handlers_disconnect_by_func (obj, (gpointer) exo_icon_view_accessible_model_row_deleted, widget);
10730 g_signal_handlers_disconnect_by_func (obj, (gpointer) exo_icon_view_accessible_model_rows_reordered, widget);
10731 }
10732
10733 static void
10734 exo_icon_view_accessible_connect_model_signals (ExoIconView *icon_view)
10735 {
10736 GObject *obj;
10737
10738 obj = G_OBJECT (icon_view->priv->model);
10739 g_signal_connect_data (obj, "row-changed",
10740 (GCallback) exo_icon_view_accessible_model_row_changed,
10741 icon_view, NULL, 0);
10742 g_signal_connect_data (obj, "row-inserted",
10743 (GCallback) exo_icon_view_accessible_model_row_inserted,
10744 icon_view, NULL, G_CONNECT_AFTER);
10745 g_signal_connect_data (obj, "row-deleted",
10746 (GCallback) exo_icon_view_accessible_model_row_deleted,
10747 icon_view, NULL, G_CONNECT_AFTER);
10748 g_signal_connect_data (obj, "rows-reordered",
10749 (GCallback) exo_icon_view_accessible_model_rows_reordered,
10750 icon_view, NULL, G_CONNECT_AFTER);
10751 }
10752
10753 static void
10754 exo_icon_view_accessible_clear_cache (ExoIconViewAccessiblePrivate *priv)
10755 {
10756 ExoIconViewItemAccessibleInfo *info;
10757 GList *items;
10758
10759 items = priv->items;
10760 while (items)
10761 {
10762 info = (ExoIconViewItemAccessibleInfo *) items->data;
10763 g_object_unref (info->item);
10764 g_free (items->data);
10765 items = items->next;
10766 }
10767 g_list_free (priv->items);
10768 priv->items = NULL;
10769 }
10770
10771 static void
10772 exo_icon_view_accessible_notify_gtk (GObject *obj,
10773 GParamSpec *pspec)
10774 {
10775 ExoIconView *icon_view;
10776 GtkWidget *widget;
10777 AtkObject *atk_obj;
10778 ExoIconViewAccessiblePrivate *priv;
10779
10780 if (strcmp (pspec->name, "model") == 0)
10781 {
10782 widget = GTK_WIDGET (obj);
10783 atk_obj = gtk_widget_get_accessible (widget);
10784 priv = exo_icon_view_accessible_get_priv (atk_obj);
10785 if (priv->model)
10786 {
10787 g_object_remove_weak_pointer (G_OBJECT (priv->model),
10788 (gpointer *)&priv->model);
10789 exo_icon_view_accessible_disconnect_model_signals (priv->model, widget);
10790 }
10791 exo_icon_view_accessible_clear_cache (priv);
10792
10793 icon_view = EXO_ICON_VIEW (obj);
10794 priv->model = icon_view->priv->model;
10795 /* If there is no model the ExoIconView is probably being destroyed */
10796 if (priv->model)
10797 {
10798 g_object_add_weak_pointer (G_OBJECT (priv->model), (gpointer *)&priv->model);
10799 exo_icon_view_accessible_connect_model_signals (icon_view);
10800 }
10801 }
10802
10803 return;
10804 }
10805
10806 static void
10807 exo_icon_view_accessible_initialize (AtkObject *accessible,
10808 gpointer data)
10809 {
10810 ExoIconViewAccessiblePrivate *priv;
10811 ExoIconView *icon_view;
10812
10813 if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize)
10814 ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data);
10815
10816 priv = g_new0 (ExoIconViewAccessiblePrivate, 1);
10817 g_object_set_qdata (G_OBJECT (accessible),
10818 accessible_private_data_quark,
10819 priv);
10820
10821 icon_view = EXO_ICON_VIEW (data);
10822 if (icon_view->priv->hadjustment)
10823 {
10824 priv->old_hadj = icon_view->priv->hadjustment;
10825 g_object_add_weak_pointer (G_OBJECT (priv->old_hadj), (gpointer *)&priv->old_hadj);
10826 g_signal_connect (icon_view->priv->hadjustment,
10827 "value-changed",
10828 G_CALLBACK (exo_icon_view_accessible_adjustment_changed),
10829 icon_view);
10830 }
10831 if (icon_view->priv->vadjustment)
10832 {
10833 priv->old_vadj = icon_view->priv->vadjustment;
10834 g_object_add_weak_pointer (G_OBJECT (priv->old_vadj), (gpointer *)&priv->old_vadj);
10835 g_signal_connect (icon_view->priv->vadjustment,
10836 "value-changed",
10837 G_CALLBACK (exo_icon_view_accessible_adjustment_changed),
10838 icon_view);
10839 }
10840 g_signal_connect_after (data,
10841 "set-scroll-adjustments",
10842 G_CALLBACK (exo_icon_view_accessible_set_scroll_adjustments),
10843 NULL);
10844 g_signal_connect (data,
10845 "notify",
10846 G_CALLBACK (exo_icon_view_accessible_notify_gtk),
10847 NULL);
10848
10849 priv->model = icon_view->priv->model;
10850 if (priv->model)
10851 {
10852 g_object_add_weak_pointer (G_OBJECT (priv->model), (gpointer *)&priv->model);
10853 exo_icon_view_accessible_connect_model_signals (icon_view);
10854 }
10855
10856 accessible->role = ATK_ROLE_LAYERED_PANE;
10857 }
10858
10859 static void
10860 exo_icon_view_accessible_finalize (GObject *object)
10861 {
10862 ExoIconViewAccessiblePrivate *priv;
10863
10864 priv = exo_icon_view_accessible_get_priv (ATK_OBJECT (object));
10865 exo_icon_view_accessible_clear_cache (priv);
10866
10867 g_free (priv);
10868
10869 G_OBJECT_CLASS (accessible_parent_class)->finalize (object);
10870 }
10871
10872 #if !GTK_CHECK_VERSION(3, 0, 0)
10873 static void
10874 exo_icon_view_accessible_destroyed (GtkWidget *widget,
10875 GtkAccessible *accessible)
10876 {
10877 AtkObject *atk_obj;
10878 ExoIconViewAccessiblePrivate *priv;
10879
10880 atk_obj = ATK_OBJECT (accessible);
10881 priv = exo_icon_view_accessible_get_priv (atk_obj);
10882 if (priv->old_hadj)
10883 {
10884 g_object_remove_weak_pointer (G_OBJECT (priv->old_hadj),
10885 (gpointer *)&priv->old_hadj);
10886
10887 g_signal_handlers_disconnect_by_func (priv->old_hadj,
10888 (gpointer) exo_icon_view_accessible_adjustment_changed,
10889 widget);
10890 priv->old_hadj = NULL;
10891 }
10892 if (priv->old_vadj)
10893 {
10894 g_object_remove_weak_pointer (G_OBJECT (priv->old_vadj),
10895 (gpointer *)&priv->old_vadj);
10896
10897 g_signal_handlers_disconnect_by_func (priv->old_vadj,
10898 (gpointer) exo_icon_view_accessible_adjustment_changed,
10899 widget);
10900 priv->old_vadj = NULL;
10901 }
10902 }
10903
10904 static void
10905 exo_icon_view_accessible_connect_widget_destroyed (GtkAccessible *accessible)
10906 {
10907 if (gtk_accessible_get_widget (accessible))
10908 {
10909 g_signal_connect_after (accessible->widget,
10910 "destroy",
10911 G_CALLBACK (exo_icon_view_accessible_destroyed),
10912 accessible);
10913 }
10914 GTK_ACCESSIBLE_CLASS (accessible_parent_class)->connect_widget_destroyed (accessible);
10915 }
10916 #endif
10917
10918 static void
10919 exo_icon_view_accessible_class_init (AtkObjectClass *klass)
10920 {
10921 GObjectClass *gobject_class;
10922 #if !GTK_CHECK_VERSION(3, 0, 0)
10923 GtkAccessibleClass *accessible_class;
10924 #endif
10925
10926 accessible_parent_class = g_type_class_peek_parent (klass);
10927
10928 gobject_class = (GObjectClass *)klass;
10929
10930 gobject_class->finalize = exo_icon_view_accessible_finalize;
10931
10932 klass->get_n_children = exo_icon_view_accessible_get_n_children;
10933 klass->ref_child = exo_icon_view_accessible_ref_child;
10934 klass->initialize = exo_icon_view_accessible_initialize;
10935
10936 #if !GTK_CHECK_VERSION(3, 0, 0)
10937 accessible_class = (GtkAccessibleClass *)klass;
10938 accessible_class->connect_widget_destroyed = exo_icon_view_accessible_connect_widget_destroyed;
10939 #endif
10940
10941 accessible_private_data_quark = g_quark_from_static_string ("icon_view-accessible-private-data");
10942 }
10943
10944 static AtkObject*
10945 exo_icon_view_accessible_ref_accessible_at_point (AtkComponent *component,
10946 gint x,
10947 gint y,
10948 AtkCoordType coord_type)
10949 {
10950 GtkWidget *widget;
10951 ExoIconView *icon_view;
10952 ExoIconViewItem *item;
10953 gint x_pos, y_pos;
10954
10955 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
10956 if (widget == NULL)
10957 /* State is defunct */
10958 return NULL;
10959
10960 icon_view = EXO_ICON_VIEW (widget);
10961 atk_component_get_extents (component, &x_pos, &y_pos, NULL, NULL, coord_type);
10962 item = exo_icon_view_get_item_at_coords (icon_view, x - x_pos, y - y_pos, TRUE, NULL);
10963 if (item)
10964 return exo_icon_view_accessible_ref_child (ATK_OBJECT (component), item->index);
10965
10966 return NULL;
10967 }
10968
10969 static void
10970 atk_component_interface_init (AtkComponentIface *iface)
10971 {
10972 iface->ref_accessible_at_point = exo_icon_view_accessible_ref_accessible_at_point;
10973 }
10974
10975 static gboolean
10976 exo_icon_view_accessible_add_selection (AtkSelection *selection,
10977 gint i)
10978 {
10979 GtkWidget *widget;
10980 ExoIconView *icon_view;
10981 ExoIconViewItem *item;
10982
10983 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
10984 if (widget == NULL)
10985 return FALSE;
10986
10987 icon_view = EXO_ICON_VIEW (widget);
10988
10989 item = g_list_nth_data (icon_view->priv->items, i);
10990
10991 if (!item)
10992 return FALSE;
10993
10994 exo_icon_view_select_item (icon_view, item);
10995
10996 return TRUE;
10997 }
10998
10999 static gboolean
11000 exo_icon_view_accessible_clear_selection (AtkSelection *selection)
11001 {
11002 GtkWidget *widget;
11003 ExoIconView *icon_view;
11004
11005 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11006 if (widget == NULL)
11007 return FALSE;
11008
11009 icon_view = EXO_ICON_VIEW (widget);
11010 exo_icon_view_unselect_all (icon_view);
11011
11012 return TRUE;
11013 }
11014
11015 static AtkObject*
11016 exo_icon_view_accessible_ref_selection (AtkSelection *selection,
11017 gint i)
11018 {
11019 GList *l;
11020 GtkWidget *widget;
11021 ExoIconView *icon_view;
11022 ExoIconViewItem *item;
11023
11024 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11025 if (widget == NULL)
11026 return NULL;
11027
11028 icon_view = EXO_ICON_VIEW (widget);
11029
11030 l = icon_view->priv->items;
11031 while (l)
11032 {
11033 item = l->data;
11034 if (item->selected)
11035 {
11036 if (i == 0)
11037 return atk_object_ref_accessible_child (gtk_widget_get_accessible (widget), item->index);
11038 else
11039 i--;
11040 }
11041 l = l->next;
11042 }
11043
11044 return NULL;
11045 }
11046
11047 static gint
11048 exo_icon_view_accessible_get_selection_count (AtkSelection *selection)
11049 {
11050 GtkWidget *widget;
11051 ExoIconView *icon_view;
11052 ExoIconViewItem *item;
11053 GList *l;
11054 gint count;
11055
11056 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11057 if (widget == NULL)
11058 return 0;
11059
11060 icon_view = EXO_ICON_VIEW (widget);
11061
11062 l = icon_view->priv->items;
11063 count = 0;
11064 while (l)
11065 {
11066 item = l->data;
11067
11068 if (item->selected)
11069 count++;
11070
11071 l = l->next;
11072 }
11073
11074 return count;
11075 }
11076
11077 static gboolean
11078 exo_icon_view_accessible_is_child_selected (AtkSelection *selection,
11079 gint i)
11080 {
11081 GtkWidget *widget;
11082 ExoIconView *icon_view;
11083 ExoIconViewItem *item;
11084
11085 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11086 if (widget == NULL)
11087 return FALSE;
11088
11089 icon_view = EXO_ICON_VIEW (widget);
11090
11091 item = g_list_nth_data (icon_view->priv->items, i);
11092 if (!item)
11093 return FALSE;
11094
11095 return item->selected;
11096 }
11097
11098 static gboolean
11099 exo_icon_view_accessible_remove_selection (AtkSelection *selection,
11100 gint i)
11101 {
11102 GtkWidget *widget;
11103 ExoIconView *icon_view;
11104 ExoIconViewItem *item;
11105 GList *l;
11106 gint count;
11107
11108 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11109 if (widget == NULL)
11110 return FALSE;
11111
11112 icon_view = EXO_ICON_VIEW (widget);
11113 l = icon_view->priv->items;
11114 count = 0;
11115 while (l)
11116 {
11117 item = l->data;
11118 if (item->selected)
11119 {
11120 if (count == i)
11121 {
11122 exo_icon_view_unselect_item (icon_view, item);
11123 return TRUE;
11124 }
11125 count++;
11126 }
11127 l = l->next;
11128 }
11129
11130 return FALSE;
11131 }
11132
11133 static gboolean
11134 exo_icon_view_accessible_select_all_selection (AtkSelection *selection)
11135 {
11136 GtkWidget *widget;
11137 ExoIconView *icon_view;
11138
11139 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection));
11140 if (widget == NULL)
11141 return FALSE;
11142
11143 icon_view = EXO_ICON_VIEW (widget);
11144 exo_icon_view_select_all (icon_view);
11145 return TRUE;
11146 }
11147
11148 static void
11149 exo_icon_view_accessible_selection_interface_init (AtkSelectionIface *iface)
11150 {
11151 iface->add_selection = exo_icon_view_accessible_add_selection;
11152 iface->clear_selection = exo_icon_view_accessible_clear_selection;
11153 iface->ref_selection = exo_icon_view_accessible_ref_selection;
11154 iface->get_selection_count = exo_icon_view_accessible_get_selection_count;
11155 iface->is_child_selected = exo_icon_view_accessible_is_child_selected;
11156 iface->remove_selection = exo_icon_view_accessible_remove_selection;
11157 iface->select_all_selection = exo_icon_view_accessible_select_all_selection;
11158 }
11159
11160 static GType exo_icon_view_accessible_get_type (void)
11161 {
11162 static GType type = 0;
11163
11164 if (!type)
11165 {
11166 GTypeInfo tinfo =
11167 {
11168 0, /* class size */
11169 (GBaseInitFunc) NULL, /* base init */
11170 (GBaseFinalizeFunc) NULL, /* base finalize */
11171 (GClassInitFunc) exo_icon_view_accessible_class_init,
11172 (GClassFinalizeFunc) NULL, /* class finalize */
11173 NULL, /* class data */
11174 0, /* instance size */
11175 0, /* nb preallocs */
11176 (GInstanceInitFunc) NULL, /* instance init */
11177 NULL /* value table */
11178 };
11179 const GInterfaceInfo atk_component_info =
11180 {
11181 (GInterfaceInitFunc) atk_component_interface_init,
11182 (GInterfaceFinalizeFunc) NULL,
11183 NULL
11184 };
11185 const GInterfaceInfo atk_selection_info =
11186 {
11187 (GInterfaceInitFunc) exo_icon_view_accessible_selection_interface_init,
11188 (GInterfaceFinalizeFunc) NULL,
11189 NULL
11190 };
11191
11192 /*
11193 * Figure out the size of the class and instance
11194 * we are deriving from
11195 */
11196 AtkObjectFactory *factory;
11197 GType derived_type;
11198 GTypeQuery query;
11199 GType derived_atk_type;
11200
11201 derived_type = g_type_parent (EXO_TYPE_ICON_VIEW);
11202 factory = atk_registry_get_factory (atk_get_default_registry (),
11203 derived_type);
11204 derived_atk_type = atk_object_factory_get_accessible_type (factory);
11205 g_type_query (derived_atk_type, &query);
11206 tinfo.class_size = query.class_size;
11207 tinfo.instance_size = query.instance_size;
11208
11209 type = g_type_register_static (derived_atk_type,
11210 I_("ExoIconViewAccessible"),
11211 &tinfo, 0);
11212 g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
11213 &atk_component_info);
11214 g_type_add_interface_static (type, ATK_TYPE_SELECTION,
11215 &atk_selection_info);
11216 }
11217 return type;
11218 }
11219
11220 static AtkObject *exo_icon_view_accessible_new (GObject *obj)
11221 {
11222 AtkObject *accessible;
11223
11224 g_return_val_if_fail (GTK_IS_WIDGET (obj), NULL);
11225
11226 accessible = g_object_new (exo_icon_view_accessible_get_type (), NULL);
11227 atk_object_initialize (accessible, obj);
11228
11229 return accessible;
11230 }
11231
11232 static GType exo_icon_view_accessible_factory_get_accessible_type (void)
11233 {
11234 return exo_icon_view_accessible_get_type ();
11235 }
11236
11237 static AtkObject* exo_icon_view_accessible_factory_create_accessible (GObject *obj)
11238 {
11239 return exo_icon_view_accessible_new (obj);
11240 }
11241
11242 static void exo_icon_view_accessible_factory_class_init (AtkObjectFactoryClass *klass)
11243 {
11244 klass->create_accessible = exo_icon_view_accessible_factory_create_accessible;
11245 klass->get_accessible_type = exo_icon_view_accessible_factory_get_accessible_type;
11246 }
11247
11248 static GType exo_icon_view_accessible_factory_get_type (void)
11249 {
11250 static GType type = 0;
11251
11252 if (!type)
11253 {
11254 const GTypeInfo tinfo =
11255 {
11256 sizeof (AtkObjectFactoryClass),
11257 NULL, /* base_init */
11258 NULL, /* base_finalize */
11259 (GClassInitFunc) exo_icon_view_accessible_factory_class_init,
11260 NULL, /* class_finalize */
11261 NULL, /* class_data */
11262 sizeof (AtkObjectFactory),
11263 0, /* n_preallocs */
11264 NULL, NULL
11265 };
11266
11267 type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
11268 I_("ExoIconViewAccessibleFactory"),
11269 &tinfo, 0);
11270 }
11271 return type;
11272 }
11273
11274 static AtkObject *exo_icon_view_get_accessible (GtkWidget *widget)
11275 {
11276 static gboolean first_time = TRUE;
11277
11278 if (first_time)
11279 {
11280 AtkObjectFactory *factory;
11281 AtkRegistry *registry;
11282 GType derived_type;
11283 GType derived_atk_type;
11284
11285 /*
11286 * Figure out whether accessibility is enabled by looking at the
11287 * type of the accessible object which would be created for
11288 * the parent type of ExoIconView.
11289 */
11290 derived_type = g_type_parent (EXO_TYPE_ICON_VIEW);
11291
11292 registry = atk_get_default_registry ();
11293 factory = atk_registry_get_factory (registry,
11294 derived_type);
11295 derived_atk_type = atk_object_factory_get_accessible_type (factory);
11296 if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
11297 atk_registry_set_factory_type (registry,
11298 EXO_TYPE_ICON_VIEW,
11299 exo_icon_view_accessible_factory_get_type ());
11300 first_time = FALSE;
11301 }
11302 return GTK_WIDGET_CLASS (exo_icon_view_parent_class)->get_accessible (widget);
11303 }
11304
11305 /*
11306 #define __EXO_ICON_VIEW_C__
11307 #include <exo/exo-aliasdef.c>
11308 */
11309