2 * Entangle: Tethered Camera Control & Capture
4 * Copyright (C) 2009-2012 Daniel P. Berrange
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "entangle-debug.h"
26 #include "entangle-session-browser.h"
28 #define ENTANGLE_SESSION_BROWSER_PRIORITY_LAYOUT (GDK_PRIORITY_REDRAW + 5)
30 #define ENTANGLE_SESSION_BROWSER_GET_PRIVATE(obj) \
31 (G_TYPE_INSTANCE_GET_PRIVATE((obj), ENTANGLE_TYPE_SESSION_BROWSER, EntangleSessionBrowserPrivate))
33 typedef struct _EntangleSessionBrowserItem EntangleSessionBrowserItem;
34 struct _EntangleSessionBrowserItem
36 /* First member is always the rectangle so it
37 * can be cast to a rectangle. */
38 GdkRectangle cell_area;
46 guint selected_before_rubberbanding : 1;
49 struct _EntangleSessionBrowserPrivate {
50 EntangleSession *session;
51 EntangleThumbnailLoader *loader;
53 GtkCellArea *cell_area;
54 GtkCellAreaContext *cell_area_context;
56 GtkCellRenderer *pixbuf_cell;
57 GtkCellRenderer *text_cell;
61 gulong context_changed_id;
66 EntangleImage *selected;
70 GtkAdjustment *hadjustment;
71 GtkAdjustment *vadjustment;
73 GtkTreeRowReference *scroll_to_path;
74 gfloat scroll_to_row_align;
75 gfloat scroll_to_col_align;
76 guint scroll_to_use_align : 1;
78 /* GtkScrollablePolicy needs to be checked when
79 * driving the scrollable adjustment values */
80 guint hscroll_policy : 1;
81 guint vscroll_policy : 1;
88 GdkWindow *bin_window;
97 entangle_session_browser_adjustment_changed(GtkAdjustment *adjustment,
98 EntangleSessionBrowser *browser);
101 entangle_session_browser_set_hadjustment(EntangleSessionBrowser *browser,
102 GtkAdjustment *adjustment);
104 entangle_session_browser_set_vadjustment(EntangleSessionBrowser *browser,
105 GtkAdjustment *adjustment);
108 entangle_session_browser_set_hadjustment_values(EntangleSessionBrowser *browser);
110 entangle_session_browser_set_vadjustment_values(EntangleSessionBrowser *browser);
113 entangle_session_browser_draw(GtkWidget *widget,
117 entangle_session_browser_queue_layout(EntangleSessionBrowser *browser);
120 entangle_session_browser_cell_layout_init(GtkCellLayoutIface *iface);
122 entangle_session_browser_select_path(EntangleSessionBrowser *browser,
126 entangle_session_browser_scroll_to_path(EntangleSessionBrowser *browser,
133 entangle_session_browser_layout(EntangleSessionBrowser *browser);
136 G_DEFINE_TYPE_WITH_CODE(EntangleSessionBrowser, entangle_session_browser, GTK_TYPE_DRAWING_AREA,
137 G_IMPLEMENT_INTERFACE(GTK_TYPE_CELL_LAYOUT,
138 entangle_session_browser_cell_layout_init)
139 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
161 SIGNAL_SELECTION_CHANGED,
166 static guint browser_signals[SIGNAL_LAST] = { 0 };
169 static void do_thumb_loaded(EntanglePixbufLoader *loader,
170 EntangleImage *image,
173 EntangleSessionBrowser *browser = data;
174 EntangleSessionBrowserPrivate *priv = browser->priv;
178 ENTANGLE_DEBUG("Got pixbuf update on %p", image);
180 pixbuf = entangle_pixbuf_loader_get_pixbuf(loader, image);
184 if (!gtk_tree_model_get_iter_first(priv->model, &iter))
188 EntangleImage *thisimage;
189 gtk_tree_model_get(priv->model, &iter, FIELD_IMAGE, &thisimage, -1);
191 if (image == thisimage) {
192 gtk_list_store_set(GTK_LIST_STORE(priv->model),
193 &iter, FIELD_PIXMAP, pixbuf, -1);
197 } while (gtk_tree_model_iter_next(priv->model, &iter));
200 static void do_image_added(EntangleSession *session G_GNUC_UNUSED,
204 EntangleSessionBrowser *browser = data;
205 EntangleSessionBrowserPrivate *priv = browser->priv;
207 GtkTreePath *path = NULL;
208 int mod = entangle_image_get_last_modified(img);
209 gchar *name = g_path_get_basename(entangle_image_get_filename(img));
211 ENTANGLE_DEBUG("Request image %s for new image", entangle_image_get_filename(img));
212 entangle_pixbuf_loader_load(ENTANGLE_PIXBUF_LOADER(priv->loader), img);
214 gtk_list_store_append(GTK_LIST_STORE(priv->model), &iter);
216 gtk_list_store_set(GTK_LIST_STORE(priv->model),
219 FIELD_PIXMAP, priv->blank,
223 ENTANGLE_DEBUG("ADD IMAGE EXTRA %p", img);
224 path = gtk_tree_model_get_path(priv->model, &iter);
226 entangle_session_browser_select_path(browser, path);
227 entangle_session_browser_scroll_to_path(browser, path, FALSE, 0, 0);
229 gtk_tree_path_free(path);
231 gtk_widget_queue_resize(GTK_WIDGET(browser));
236 static void do_model_unload(EntangleSessionBrowser *browser)
238 EntangleSessionBrowserPrivate *priv = browser->priv;
241 ENTANGLE_DEBUG("Unload model");
243 g_signal_handler_disconnect(priv->session,
244 priv->sigImageAdded);
245 g_signal_handler_disconnect(priv->loader,
246 priv->sigThumbReady);
248 count = entangle_session_image_count(priv->session);
249 for (int i = 0 ; i < count ; i++) {
250 EntangleImage *img = entangle_session_image_get(priv->session, i);
251 entangle_pixbuf_loader_unload(ENTANGLE_PIXBUF_LOADER(priv->loader), img);
254 g_object_unref(priv->blank);
255 gtk_list_store_clear(GTK_LIST_STORE(priv->model));
258 static void do_model_load(EntangleSessionBrowser *browser)
260 EntangleSessionBrowserPrivate *priv = browser->priv;
265 ENTANGLE_DEBUG("Load model");
267 g_object_get(priv->loader,
272 priv->blank = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
273 gdk_pixbuf_fill(priv->blank, 0x000000FF);
275 priv->sigImageAdded = g_signal_connect(priv->session, "session-image-added",
276 G_CALLBACK(do_image_added), browser);
277 priv->sigThumbReady = g_signal_connect(priv->loader, "pixbuf-loaded",
278 G_CALLBACK(do_thumb_loaded), browser);
280 count = entangle_session_image_count(priv->session);
281 for (int i = 0 ; i < count ; i++) {
282 EntangleImage *img = entangle_session_image_get(priv->session, i);
283 int mod = entangle_image_get_last_modified(img);
285 gchar *name = g_path_get_basename(entangle_image_get_filename(img));
287 gtk_list_store_append(GTK_LIST_STORE(priv->model), &iter);
288 ENTANGLE_DEBUG("ADD IMAGE FIRST %p", img);
289 gtk_list_store_set(GTK_LIST_STORE(priv->model), &iter,
291 FIELD_PIXMAP, priv->blank,
296 entangle_pixbuf_loader_load(ENTANGLE_PIXBUF_LOADER(priv->loader), img);
300 GtkTreePath *path = NULL;
301 path = gtk_tree_path_new_from_indices(count - 1, -1);
303 entangle_session_browser_select_path(ENTANGLE_SESSION_BROWSER(browser), path);
304 entangle_session_browser_scroll_to_path(ENTANGLE_SESSION_BROWSER(browser), path, FALSE, 0, 0);
306 gtk_tree_path_free(path);
312 do_image_sort_modified(GtkTreeModel *model,
315 gpointer data G_GNUC_UNUSED)
319 gtk_tree_model_get(model, a, FIELD_LASTMOD, &ai, -1);
320 gtk_tree_model_get(model, b, FIELD_LASTMOD, &bi, -1);
326 static void entangle_session_browser_get_property(GObject *object,
331 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(object);
332 EntangleSessionBrowserPrivate *priv = browser->priv;
336 g_value_set_object(value, priv->session);
340 g_value_set_object(value, priv->loader);
343 case PROP_HADJUSTMENT:
344 g_value_set_object (value, priv->hadjustment);
347 case PROP_VADJUSTMENT:
348 g_value_set_object (value, priv->vadjustment);
351 case PROP_HSCROLL_POLICY:
352 g_value_set_enum (value, priv->hscroll_policy);
355 case PROP_VSCROLL_POLICY:
356 g_value_set_enum (value, priv->vscroll_policy);
360 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
365 static void entangle_session_browser_set_property(GObject *object,
370 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(object);
371 EntangleSessionBrowserPrivate *priv = browser->priv;
373 ENTANGLE_DEBUG("Set prop on session browser %d", prop_id);
377 entangle_session_browser_set_session(browser, g_value_get_object(value));
381 entangle_session_browser_set_thumbnail_loader(browser, g_value_get_object(value));
384 case PROP_HADJUSTMENT:
385 entangle_session_browser_set_hadjustment(browser, g_value_get_object(value));
388 case PROP_VADJUSTMENT:
389 entangle_session_browser_set_vadjustment(browser, g_value_get_object(value));
392 case PROP_HSCROLL_POLICY:
393 priv->hscroll_policy = g_value_get_enum (value);
394 gtk_widget_queue_resize (GTK_WIDGET (browser));
397 case PROP_VSCROLL_POLICY:
398 priv->vscroll_policy = g_value_get_enum (value);
399 gtk_widget_queue_resize (GTK_WIDGET (browser));
403 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
409 verify_items(EntangleSessionBrowser *browser)
414 for (items = browser->priv->items; items; items = items->next) {
415 EntangleSessionBrowserItem *item = items->data;
417 if (item->index != i)
418 ENTANGLE_DEBUG("List item does not match its index: "
419 "item index %d and list index %d\n", item->index, i);
426 entangle_session_browser_cell_layout_get_area(GtkCellLayout *cell_layout)
428 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(cell_layout);
429 EntangleSessionBrowserPrivate *priv = browser->priv;
431 return priv->cell_area;
436 entangle_session_browser_set_cell_data(EntangleSessionBrowser *browser,
437 EntangleSessionBrowserItem *item)
439 gboolean iters_persist;
442 iters_persist = gtk_tree_model_get_flags(browser->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
444 if (!iters_persist) {
447 path = gtk_tree_path_new_from_indices (item->index, -1);
448 if (!gtk_tree_model_get_iter (browser->priv->model, &iter, path))
450 gtk_tree_path_free (path);
455 gtk_cell_area_apply_attributes(browser->priv->cell_area,
456 browser->priv->model,
457 &iter, FALSE, FALSE);
461 /* This ensures that all widths have been cached in the
462 * context and we have proper alignments to go on.
465 entangle_session_browser_cache_widths(EntangleSessionBrowser *browser)
469 g_signal_handler_block (browser->priv->cell_area_context,
470 browser->priv->context_changed_id);
472 for (items = browser->priv->items; items; items = items->next) {
473 EntangleSessionBrowserItem *item = items->data;
475 /* Only fetch the width of items with invalidated sizes */
476 if (item->cell_area.width < 0) {
477 entangle_session_browser_set_cell_data(browser, item);
478 gtk_cell_area_get_preferred_width(browser->priv->cell_area,
479 browser->priv->cell_area_context,
480 GTK_WIDGET(browser), NULL, NULL);
484 g_signal_handler_unblock(browser->priv->cell_area_context,
485 browser->priv->context_changed_id);
490 entangle_session_browser_item_invalidate_size(EntangleSessionBrowserItem *item)
492 item->cell_area.width = -1;
493 item->cell_area.height = -1;
498 entangle_session_browser_invalidate_sizes (EntangleSessionBrowser *browser)
500 /* Clear all item sizes */
501 g_list_foreach (browser->priv->items,
502 (GFunc)entangle_session_browser_item_invalidate_size, NULL);
504 /* Reset the context */
505 if (browser->priv->cell_area_context) {
506 g_signal_handler_block (browser->priv->cell_area_context,
507 browser->priv->context_changed_id);
508 gtk_cell_area_context_reset (browser->priv->cell_area_context);
509 g_signal_handler_unblock (browser->priv->cell_area_context,
510 browser->priv->context_changed_id);
513 /* Re-layout the items */
514 entangle_session_browser_queue_layout(browser);
519 entangle_session_browser_context_changed(GtkCellAreaContext *context G_GNUC_UNUSED,
523 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
525 if (!strcmp(pspec->name, "minimum-width") ||
526 !strcmp(pspec->name, "natural-width") ||
527 !strcmp(pspec->name, "minimum-height") ||
528 !strcmp(pspec->name, "natural-height"))
529 entangle_session_browser_invalidate_sizes(browser);
534 entangle_session_browser_row_changed(GtkTreeModel *model G_GNUC_UNUSED,
536 GtkTreeIter *iter G_GNUC_UNUSED,
539 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
541 /* ignore changes in branches */
542 if (gtk_tree_path_get_depth(path) > 1)
545 /* An icon view subclass might add it's own model and populate
546 * things at init() time instead of waiting for the constructor()
549 if (browser->priv->cell_area)
550 gtk_cell_area_stop_editing(browser->priv->cell_area, TRUE);
552 /* Here we can use a "grow-only" strategy for optimization
553 * and only invalidate a single item and queue a relayout
554 * instead of invalidating the whole thing.
556 * For now EntangleSessionBrowser still cant deal with huge models
557 * so just invalidate the whole thing when the model
560 entangle_session_browser_invalidate_sizes(browser);
562 verify_items(browser);
566 static EntangleSessionBrowserItem *entangle_session_browser_item_new(void)
568 EntangleSessionBrowserItem *item;
570 item = g_slice_new0(EntangleSessionBrowserItem);
572 item->cell_area.width = -1;
573 item->cell_area.height = -1;
579 static void entangle_session_browser_item_free(EntangleSessionBrowserItem *item)
581 g_return_if_fail (item != NULL);
583 g_slice_free(EntangleSessionBrowserItem, item);
588 entangle_session_browser_row_inserted(GtkTreeModel *model G_GNUC_UNUSED,
593 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
595 EntangleSessionBrowserItem *item;
596 gboolean iters_persist;
599 /* ignore changes in branches */
600 if (gtk_tree_path_get_depth(path) > 1)
603 iters_persist = gtk_tree_model_get_flags(browser->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
605 index = gtk_tree_path_get_indices(path)[0];
607 item = entangle_session_browser_item_new();
614 /* FIXME: We can be more efficient here,
615 we can store a tail pointer and use that when
616 appending (which is a rather common operation)
618 browser->priv->items = g_list_insert(browser->priv->items,
621 list = g_list_nth (browser->priv->items, index + 1);
622 for (; list; list = list->next) {
627 verify_items(browser);
629 entangle_session_browser_queue_layout(browser);
634 entangle_session_browser_row_deleted(GtkTreeModel *model G_GNUC_UNUSED,
638 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
640 EntangleSessionBrowserItem *item;
642 gboolean emit = FALSE;
644 /* ignore changes in branches */
645 if (gtk_tree_path_get_depth(path) > 1)
648 index = gtk_tree_path_get_indices(path)[0];
650 list = g_list_nth(browser->priv->items, index);
653 if (browser->priv->cell_area)
654 gtk_cell_area_stop_editing(browser->priv->cell_area, TRUE);
659 entangle_session_browser_item_free(item);
661 for (next = list->next; next; next = next->next) {
666 browser->priv->items = g_list_delete_link(browser->priv->items, list);
668 verify_items(browser);
670 entangle_session_browser_queue_layout(browser);
673 g_signal_emit(browser, browser_signals[SIGNAL_SELECTION_CHANGED], 0);
677 entangle_session_browser_rows_reordered(GtkTreeModel *model,
678 GtkTreePath *parent G_GNUC_UNUSED,
683 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
686 GList *items = NULL, *list;
687 EntangleSessionBrowserItem **item_array;
690 /* ignore changes in branches */
694 if (browser->priv->cell_area)
695 gtk_cell_area_stop_editing(browser->priv->cell_area, TRUE);
697 length = gtk_tree_model_iter_n_children(model, NULL);
699 order = g_new(gint, length);
700 for (i = 0; i < length; i++)
701 order[new_order[i]] = i;
703 item_array = g_new(EntangleSessionBrowserItem *, length);
704 for (i = 0, list = browser->priv->items; list != NULL; list = list->next, i++)
705 item_array[order[i]] = list->data;
708 for (i = length - 1; i >= 0; i--) {
709 item_array[i]->index = i;
710 items = g_list_prepend(items, item_array[i]);
714 g_list_free(browser->priv->items);
715 browser->priv->items = items;
717 entangle_session_browser_queue_layout(browser);
719 verify_items(browser);
724 entangle_session_browser_build_items(EntangleSessionBrowser *browser)
728 gboolean iters_persist;
731 iters_persist = gtk_tree_model_get_flags(browser->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
733 if (!gtk_tree_model_get_iter_first(browser->priv->model,
740 EntangleSessionBrowserItem *item = entangle_session_browser_item_new();
747 items = g_list_prepend(items, item);
748 } while (gtk_tree_model_iter_next(browser->priv->model, &iter));
750 browser->priv->items = g_list_reverse(items);
755 entangle_session_browser_realize(GtkWidget *widget)
757 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
758 EntangleSessionBrowserPrivate *priv = browser->priv;
759 GtkAllocation allocation;
761 GdkWindowAttr attributes;
762 gint attributes_mask;
763 GtkStyleContext *context;
765 gtk_widget_set_realized(widget, TRUE);
767 gtk_widget_get_allocation(widget, &allocation);
769 /* Make the main, clipping window */
770 attributes.window_type = GDK_WINDOW_CHILD;
771 attributes.x = allocation.x;
772 attributes.y = allocation.y;
773 attributes.width = allocation.width;
774 attributes.height = allocation.height;
775 attributes.wclass = GDK_INPUT_OUTPUT;
776 attributes.visual = gtk_widget_get_visual(widget);
777 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
779 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
781 window = gdk_window_new(gtk_widget_get_parent_window(widget),
782 &attributes, attributes_mask);
783 gtk_widget_set_window(widget, window);
784 gdk_window_set_user_data(window, widget);
786 gtk_widget_get_allocation(widget, &allocation);
788 /* Make the window for the icon view */
791 attributes.width = MAX (priv->width, allocation.width);
792 attributes.height = MAX (priv->height, allocation.height);
793 attributes.event_mask = (GDK_EXPOSURE_MASK |
795 GDK_POINTER_MOTION_MASK |
796 GDK_BUTTON_PRESS_MASK |
797 GDK_BUTTON_RELEASE_MASK |
799 GDK_KEY_RELEASE_MASK) |
800 gtk_widget_get_events(widget);
802 priv->bin_window = gdk_window_new(window,
803 &attributes, attributes_mask);
804 gdk_window_set_user_data(priv->bin_window, widget);
806 context = gtk_widget_get_style_context(widget);
808 gtk_style_context_save(context);
809 gtk_style_context_add_class(context, GTK_STYLE_CLASS_VIEW);
810 gtk_style_context_set_background(context, priv->bin_window);
811 gtk_style_context_restore(context);
813 gdk_window_show(priv->bin_window);
818 entangle_session_browser_unrealize(GtkWidget *widget)
820 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
821 EntangleSessionBrowserPrivate *priv = browser->priv;
823 gdk_window_set_user_data(priv->bin_window, NULL);
824 gdk_window_destroy(priv->bin_window);
825 priv->bin_window = NULL;
827 GTK_WIDGET_CLASS(entangle_session_browser_parent_class)->unrealize(widget);
832 entangle_session_browser_scroll_to_item(EntangleSessionBrowser *browser,
833 EntangleSessionBrowserItem *item)
835 EntangleSessionBrowserPrivate *priv = browser->priv;
836 GtkWidget *widget = GTK_WIDGET(browser);
837 GtkAdjustment *hadj, *vadj;
838 GtkAllocation allocation;
840 GdkRectangle item_area;
842 item_area.x = item->cell_area.x - priv->item_padding;
843 item_area.y = item->cell_area.y - priv->item_padding;
844 item_area.width = item->cell_area.width + priv->item_padding * 2;
845 item_area.height = item->cell_area.height + priv->item_padding * 2;
847 gdk_window_get_position(priv->bin_window, &x, &y);
848 gtk_widget_get_allocation(widget, &allocation);
850 hadj = priv->hadjustment;
851 vadj = priv->vadjustment;
853 if (y + item_area.y < 0)
854 gtk_adjustment_set_value(vadj,
855 gtk_adjustment_get_value(vadj)
857 else if (y + item_area.y + item_area.height > allocation.height)
858 gtk_adjustment_set_value(vadj,
859 gtk_adjustment_get_value(vadj)
860 + y + item_area.y + item_area.height - allocation.height);
862 if (x + item_area.x < 0)
863 gtk_adjustment_set_value(hadj,
864 gtk_adjustment_get_value(hadj)
866 else if (x + item_area.x + item_area.width > allocation.width)
867 gtk_adjustment_set_value(hadj,
868 gtk_adjustment_get_value(hadj)
869 + x + item_area.x + item_area.width - allocation.width);
871 gtk_adjustment_changed(hadj);
872 gtk_adjustment_changed(vadj);
876 static EntangleSessionBrowserItem *
877 entangle_session_browser_get_item_at_coords(EntangleSessionBrowser *browser,
880 gboolean only_in_cell,
881 GtkCellRenderer **cell_at_pos)
883 EntangleSessionBrowserPrivate *priv = browser->priv;
889 for (items = priv->items; items; items = items->next) {
890 EntangleSessionBrowserItem *item = items->data;
891 GdkRectangle *item_area = (GdkRectangle *)item;
893 if (x >= item_area->x - priv->column_spacing / 2 &&
894 x <= item_area->x + item_area->width + priv->column_spacing / 2 &&
896 y <= item_area->y + item_area->height) {
897 if (only_in_cell || cell_at_pos) {
898 GtkCellRenderer *cell = NULL;
900 entangle_session_browser_set_cell_data(browser, item);
902 if (x >= item_area->x && x <= item_area->x + item_area->width &&
903 y >= item_area->y && y <= item_area->y + item_area->height)
904 cell = gtk_cell_area_get_cell_at_position(priv->cell_area,
905 priv->cell_area_context,
914 return cell != NULL ? item : NULL;
926 entangle_session_browser_queue_draw_item(EntangleSessionBrowser *browser,
927 EntangleSessionBrowserItem *item)
929 EntangleSessionBrowserPrivate *priv = browser->priv;
931 GdkRectangle *item_area = (GdkRectangle *)item;
933 rect.x = item_area->x - priv->item_padding;
934 rect.y = item_area->y - priv->item_padding;
935 rect.width = item_area->width + priv->item_padding * 2;
936 rect.height = item_area->height + priv->item_padding * 2;
938 if (priv->bin_window)
939 gdk_window_invalidate_rect(priv->bin_window, &rect, TRUE);
944 entangle_session_browser_unselect_all_internal(EntangleSessionBrowser *browser)
946 EntangleSessionBrowserPrivate *priv = browser->priv;
947 gboolean dirty = FALSE;
950 for (items = priv->items; items; items = items->next) {
951 EntangleSessionBrowserItem *item = items->data;
953 if (item->selected) {
954 item->selected = FALSE;
956 entangle_session_browser_queue_draw_item(browser, item);
965 entangle_session_browser_select_item(EntangleSessionBrowser *browser,
966 EntangleSessionBrowserItem *item)
971 item->selected = TRUE;
973 g_signal_emit(browser, browser_signals[SIGNAL_SELECTION_CHANGED], 0);
975 entangle_session_browser_queue_draw_item(browser, item);
980 entangle_session_browser_unselect_item(EntangleSessionBrowser *browser,
981 EntangleSessionBrowserItem *item)
986 item->selected = FALSE;
988 g_signal_emit(browser, browser_signals[SIGNAL_SELECTION_CHANGED], 0);
990 entangle_session_browser_queue_draw_item(browser, item);
995 entangle_session_browser_select_path(EntangleSessionBrowser *browser,
998 EntangleSessionBrowserPrivate *priv = browser->priv;
999 EntangleSessionBrowserItem *item = NULL;
1001 if (gtk_tree_path_get_depth (path) > 0)
1002 item = g_list_nth_data(priv->items,
1003 gtk_tree_path_get_indices(path)[0]);
1006 entangle_session_browser_unselect_all_internal(browser);
1007 entangle_session_browser_select_item(browser, item);
1013 entangle_session_browser_scroll_to_path(EntangleSessionBrowser *browser,
1019 EntangleSessionBrowserPrivate *priv = browser->priv;
1020 EntangleSessionBrowserItem *item = NULL;
1021 GtkWidget *widget = GTK_WIDGET(browser);
1023 if (gtk_tree_path_get_depth(path) > 0)
1024 item = g_list_nth_data(priv->items,
1025 gtk_tree_path_get_indices(path)[0]);
1027 if (!item || item->cell_area.width < 0 ||
1028 !gtk_widget_get_realized (widget)) {
1029 if (priv->scroll_to_path)
1030 gtk_tree_row_reference_free(priv->scroll_to_path);
1032 priv->scroll_to_path = NULL;
1035 priv->scroll_to_path = gtk_tree_row_reference_new_proxy(G_OBJECT(browser),
1038 priv->scroll_to_use_align = use_align;
1039 priv->scroll_to_row_align = row_align;
1040 priv->scroll_to_col_align = col_align;
1046 GtkAllocation allocation;
1049 GdkRectangle item_area = {
1050 item->cell_area.x - priv->item_padding,
1051 item->cell_area.y - priv->item_padding,
1052 item->cell_area.width + priv->item_padding * 2,
1053 item->cell_area.height + priv->item_padding * 2
1056 gdk_window_get_position(priv->bin_window, &x, &y);
1058 gtk_widget_get_allocation(widget, &allocation);
1060 offset = y + item_area.y - row_align * (allocation.height - item_area.height);
1062 gtk_adjustment_set_value(priv->vadjustment,
1063 gtk_adjustment_get_value(priv->vadjustment) + offset);
1065 offset = x + item_area.x - col_align * (allocation.width - item_area.width);
1067 gtk_adjustment_set_value(priv->hadjustment,
1068 gtk_adjustment_get_value(priv->hadjustment) + offset);
1070 gtk_adjustment_changed(priv->hadjustment);
1071 gtk_adjustment_changed(priv->vadjustment);
1073 entangle_session_browser_scroll_to_item(browser, item);
1079 entangle_session_browser_button_press(GtkWidget *widget,
1080 GdkEventButton *event)
1082 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1083 EntangleSessionBrowserPrivate *priv = browser->priv;
1084 EntangleSessionBrowserItem *item;
1085 gboolean dirty = FALSE;
1086 GtkCellRenderer *cell = NULL;
1088 if (event->window != priv->bin_window)
1091 if (!gtk_widget_has_focus(widget))
1092 gtk_widget_grab_focus(widget);
1094 if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
1095 item = entangle_session_browser_get_item_at_coords(browser,
1101 * We consider only the cells' area as the item area if the
1102 * item is not selected, but if it *is* selected, the complete
1103 * selection rectangle is considered to be part of the item.
1105 if (item != NULL && !item->selected) {
1106 entangle_session_browser_unselect_all_internal(browser);
1108 item->selected = TRUE;
1109 entangle_session_browser_queue_draw_item(browser, item);
1110 entangle_session_browser_scroll_to_item(browser, item);
1115 g_signal_emit(browser, browser_signals[SIGNAL_SELECTION_CHANGED], 0);
1117 return event->button == 1;
1122 entangle_session_browser_key_release(GtkWidget *widget,
1125 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1126 EntangleSessionBrowserPrivate *priv = browser->priv;
1127 GList *list, *prev = NULL;
1129 switch (event->keyval) {
1131 for (list = priv->items; list != NULL; list = list->next) {
1132 EntangleSessionBrowserItem *item = list->data;
1134 if (item->selected && list->next) {
1135 EntangleSessionBrowserItem *next = list->next->data;
1136 entangle_session_browser_unselect_item(browser, item);
1137 entangle_session_browser_select_item(browser, next);
1138 entangle_session_browser_scroll_to_item(browser, next);
1145 for (list = priv->items; list != NULL; list = list->next) {
1146 EntangleSessionBrowserItem *item = list->data;
1148 if (item->selected && prev) {
1149 EntangleSessionBrowserItem *prior = prev->data;
1150 entangle_session_browser_unselect_item(browser, item);
1151 entangle_session_browser_select_item(browser, prior);
1152 entangle_session_browser_scroll_to_item(browser, prior);
1160 return GTK_WIDGET_CLASS(entangle_session_browser_parent_class)->key_press_event(widget, event);
1166 entangle_session_browser_size_allocate(GtkWidget *widget,
1167 GtkAllocation *allocation)
1169 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1170 EntangleSessionBrowserPrivate *priv = browser->priv;
1172 gtk_widget_set_allocation(widget, allocation);
1174 if (gtk_widget_get_realized(widget)) {
1175 gdk_window_move_resize(gtk_widget_get_window(widget),
1176 allocation->x, allocation->y,
1177 allocation->width, allocation->height);
1178 gdk_window_resize(priv->bin_window,
1179 MAX(priv->width, allocation->width),
1180 MAX(priv->height, allocation->height));
1183 entangle_session_browser_layout(browser);
1185 /* Delay signal emission */
1186 g_object_freeze_notify(G_OBJECT(priv->hadjustment));
1187 g_object_freeze_notify(G_OBJECT(priv->vadjustment));
1189 entangle_session_browser_set_hadjustment_values(browser);
1190 entangle_session_browser_set_vadjustment_values(browser);
1192 if (gtk_widget_get_realized(widget) &&
1193 priv->scroll_to_path) {
1195 path = gtk_tree_row_reference_get_path(priv->scroll_to_path);
1196 gtk_tree_row_reference_free(priv->scroll_to_path);
1197 priv->scroll_to_path = NULL;
1199 entangle_session_browser_scroll_to_path(browser, path,
1200 priv->scroll_to_use_align,
1201 priv->scroll_to_row_align,
1202 priv->scroll_to_col_align);
1203 gtk_tree_path_free(path);
1206 /* Emit any pending signals now */
1207 g_object_thaw_notify(G_OBJECT(priv->hadjustment));
1208 g_object_thaw_notify(G_OBJECT(priv->vadjustment));
1212 static void entangle_session_browser_destroy(GtkWidget *widget)
1214 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1215 EntangleSessionBrowserPrivate *priv = browser->priv;
1217 if (priv->layout_idle_id != 0) {
1218 g_source_remove(priv->layout_idle_id);
1219 priv->layout_idle_id = 0;
1222 if (priv->scroll_to_path != NULL) {
1223 gtk_tree_row_reference_free(priv->scroll_to_path);
1224 priv->scroll_to_path = NULL;
1227 if (priv->hadjustment != NULL) {
1228 g_object_unref(priv->hadjustment);
1229 priv->hadjustment = NULL;
1232 if (priv->vadjustment != NULL) {
1233 g_object_unref(priv->vadjustment);
1234 priv->vadjustment = NULL;
1237 GTK_WIDGET_CLASS(entangle_session_browser_parent_class)->destroy(widget);
1241 static void entangle_session_browser_finalize(GObject *object)
1243 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(object);
1244 EntangleSessionBrowserPrivate *priv = browser->priv;
1246 if (priv->session && priv->loader)
1247 do_model_unload(browser);
1249 if (priv->cell_area_context) {
1250 g_signal_handler_disconnect(priv->cell_area_context, priv->context_changed_id);
1251 priv->context_changed_id = 0;
1253 g_object_unref(priv->cell_area_context);
1254 priv->cell_area_context = NULL;
1258 g_object_unref(priv->session);
1260 g_object_unref(priv->loader);
1262 G_OBJECT_CLASS (entangle_session_browser_parent_class)->finalize (object);
1267 entangle_session_browser_cell_layout_init(GtkCellLayoutIface *iface)
1269 iface->get_area = entangle_session_browser_cell_layout_get_area;
1272 static void entangle_session_browser_class_init(EntangleSessionBrowserClass *klass)
1274 GObjectClass *object_class = G_OBJECT_CLASS(klass);
1275 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1277 object_class->finalize = entangle_session_browser_finalize;
1278 object_class->get_property = entangle_session_browser_get_property;
1279 object_class->set_property = entangle_session_browser_set_property;
1281 widget_class->destroy = entangle_session_browser_destroy;
1282 widget_class->realize = entangle_session_browser_realize;
1283 widget_class->unrealize = entangle_session_browser_unrealize;
1284 widget_class->draw = entangle_session_browser_draw;
1285 widget_class->button_press_event = entangle_session_browser_button_press;
1286 widget_class->key_release_event = entangle_session_browser_key_release;
1287 widget_class->size_allocate = entangle_session_browser_size_allocate;
1289 g_object_class_install_property(object_class,
1291 g_param_spec_object("session",
1293 "Session to be displayed",
1294 ENTANGLE_TYPE_SESSION,
1296 G_PARAM_STATIC_NAME |
1297 G_PARAM_STATIC_NICK |
1298 G_PARAM_STATIC_BLURB));
1300 g_object_class_install_property(object_class,
1302 g_param_spec_object("thumbnail-loader",
1305 ENTANGLE_TYPE_THUMBNAIL_LOADER,
1307 G_PARAM_STATIC_NAME |
1308 G_PARAM_STATIC_NICK |
1309 G_PARAM_STATIC_BLURB));
1311 /* Scrollable interface properties */
1312 g_object_class_override_property(object_class, PROP_HADJUSTMENT, "hadjustment");
1313 g_object_class_override_property(object_class, PROP_VADJUSTMENT, "vadjustment");
1314 g_object_class_override_property(object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1315 g_object_class_override_property(object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1317 browser_signals[SIGNAL_SELECTION_CHANGED] =
1318 g_signal_new("selection-changed",
1319 G_TYPE_FROM_CLASS(object_class),
1321 G_STRUCT_OFFSET(EntangleSessionBrowserClass, selection_changed),
1323 g_cclosure_marshal_VOID__VOID,
1326 g_type_class_add_private(klass, sizeof(EntangleSessionBrowserPrivate));
1330 EntangleSessionBrowser *entangle_session_browser_new(void)
1332 return ENTANGLE_SESSION_BROWSER(g_object_new(ENTANGLE_TYPE_SESSION_BROWSER, NULL));
1336 static void entangle_session_browser_init(EntangleSessionBrowser *browser)
1338 EntangleSessionBrowserPrivate *priv;
1341 priv = browser->priv = ENTANGLE_SESSION_BROWSER_GET_PRIVATE(browser);
1343 priv->model = GTK_TREE_MODEL(gtk_list_store_new(FIELD_LAST,
1344 ENTANGLE_TYPE_IMAGE,
1349 gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(priv->model),
1350 do_image_sort_modified, NULL, NULL);
1351 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(priv->model),
1352 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
1353 GTK_SORT_ASCENDING);
1355 priv->cell_area = gtk_cell_area_box_new ();
1356 g_object_ref_sink(priv->cell_area);
1357 gtk_orientable_set_orientation(GTK_ORIENTABLE (priv->cell_area), GTK_ORIENTATION_VERTICAL);
1359 priv->cell_area_context = gtk_cell_area_create_context(priv->cell_area);
1360 priv->context_changed_id =
1361 g_signal_connect(priv->cell_area_context, "notify",
1362 G_CALLBACK(entangle_session_browser_context_changed), browser);
1364 priv->pixbuf_cell = gtk_cell_renderer_pixbuf_new();
1365 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(browser), priv->pixbuf_cell, FALSE);
1367 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(browser),
1369 "pixbuf", FIELD_PIXMAP,
1372 g_object_set(priv->pixbuf_cell,
1378 priv->text_cell = gtk_cell_renderer_text_new();
1379 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(browser), priv->text_cell, FALSE);
1380 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(browser),
1390 g_object_set(priv->text_cell,
1391 "alignment", PANGO_ALIGN_CENTER,
1392 "wrap-mode", PANGO_WRAP_WORD_CHAR,
1395 "foreground-set", TRUE,
1396 "foreground-rgba", &fg,
1399 g_signal_connect(priv->model,
1401 G_CALLBACK (entangle_session_browser_row_changed),
1403 g_signal_connect(priv->model,
1405 G_CALLBACK (entangle_session_browser_row_inserted),
1407 g_signal_connect(priv->model,
1409 G_CALLBACK (entangle_session_browser_row_deleted),
1411 g_signal_connect(priv->model,
1413 G_CALLBACK (entangle_session_browser_rows_reordered),
1416 entangle_session_browser_build_items(browser);
1417 entangle_session_browser_queue_layout(browser);
1420 priv->item_padding = 0;
1421 priv->column_spacing = 6;
1423 gtk_widget_set_can_focus(GTK_WIDGET(browser), TRUE);
1428 entangle_session_browser_get_selected_items(EntangleSessionBrowser *browser)
1430 EntangleSessionBrowserPrivate *priv = browser->priv;
1432 GList *selected = NULL;
1434 for (list = priv->items; list != NULL; list = list->next) {
1435 EntangleSessionBrowserItem *item = list->data;
1437 if (item->selected) {
1438 GtkTreePath *path = gtk_tree_path_new_from_indices(item->index, -1);
1440 selected = g_list_prepend(selected, path);
1448 EntangleImage *entangle_session_browser_selected_image(EntangleSessionBrowser *browser)
1450 EntangleSessionBrowserPrivate *priv = browser->priv;
1452 EntangleImage *img = NULL;
1457 items = entangle_session_browser_get_selected_items(browser);
1462 path = g_list_nth_data(items, 0);
1466 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->model), &iter, path))
1469 memset(&val, 0, sizeof val);
1470 gtk_tree_model_get_value(GTK_TREE_MODEL(priv->model), &iter, 0, &val);
1472 img = g_value_get_object(&val);
1475 g_list_foreach(items, (GFunc)(gtk_tree_path_free), NULL);
1481 void entangle_session_browser_set_thumbnail_loader(EntangleSessionBrowser *browser,
1482 EntangleThumbnailLoader *loader)
1484 EntangleSessionBrowserPrivate *priv = browser->priv;
1488 do_model_unload(browser);
1490 g_object_unref(priv->loader);
1492 priv->loader = loader;
1494 g_object_ref(priv->loader);
1497 do_model_load(browser);
1502 EntangleThumbnailLoader *entangle_session_browser_get_thumbnail_loader(EntangleSessionBrowser *browser)
1504 EntangleSessionBrowserPrivate *priv = browser->priv;
1506 return priv->loader;
1510 void entangle_session_browser_set_session(EntangleSessionBrowser *browser,
1511 EntangleSession *session)
1513 EntangleSessionBrowserPrivate *priv = browser->priv;
1515 if (priv->session) {
1517 do_model_unload(browser);
1518 g_object_unref(priv->session);
1520 priv->session = session;
1521 if (priv->session) {
1522 g_object_ref(priv->session);
1525 do_model_load(browser);
1530 EntangleSession *entangle_session_browser_get_session(EntangleSessionBrowser *browser)
1532 EntangleSessionBrowserPrivate *priv = browser->priv;
1534 return priv->session;
1539 entangle_session_browser_paint_item(EntangleSessionBrowser *browser,
1541 EntangleSessionBrowserItem *item,
1545 GdkRectangle cell_area;
1546 GtkStateFlags state = 0;
1547 GtkCellRendererState flags = 0;
1548 GtkStyleContext *style_context;
1549 GtkWidget *widget = GTK_WIDGET (browser);
1550 EntangleSessionBrowserPrivate *priv = browser->priv;
1552 entangle_session_browser_set_cell_data(browser, item);
1554 style_context = gtk_widget_get_style_context(widget);
1556 gtk_style_context_save(style_context);
1557 gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_VIEW);
1558 gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_CELL);
1560 if (item->selected) {
1561 state |= GTK_STATE_FLAG_SELECTED;
1562 flags |= GTK_CELL_RENDERER_SELECTED;
1564 gtk_style_context_set_state(style_context, state);
1565 gtk_render_background(style_context, cr,
1566 x - browser->priv->item_padding,
1567 y - browser->priv->item_padding,
1568 item->cell_area.width + browser->priv->item_padding * 2,
1569 item->cell_area.height + browser->priv->item_padding * 2);
1574 cell_area.width = item->cell_area.width;
1575 cell_area.height = item->cell_area.height;
1577 gtk_cell_area_render(priv->cell_area,
1578 priv->cell_area_context,
1579 widget, cr, &cell_area, &cell_area, flags,
1582 gtk_style_context_restore(style_context);
1587 entangle_session_browser_draw(GtkWidget *widget,
1590 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1591 EntangleSessionBrowserPrivate *priv = browser->priv;
1593 int ww, wh; /* Available drawing area extents */
1595 ww = gdk_window_get_width(gtk_widget_get_window(widget));
1596 wh = gdk_window_get_height(gtk_widget_get_window(widget));
1598 cairo_set_source_rgb(cr, 0, 0, 0);
1599 cairo_rectangle(cr, 0, 0, ww, wh);
1602 if (!gtk_cairo_should_draw_window (cr, priv->bin_window))
1606 gtk_cairo_transform_to_window(cr, widget, priv->bin_window);
1607 cairo_set_line_width(cr, 1.);
1609 for (icons = priv->items; icons; icons = icons->next) {
1610 EntangleSessionBrowserItem *item = icons->data;
1611 GdkRectangle paint_area;
1613 paint_area.x = ((GdkRectangle *)item)->x - priv->item_padding;
1614 paint_area.y = ((GdkRectangle *)item)->y - priv->item_padding;
1615 paint_area.width = ((GdkRectangle *)item)->width + priv->item_padding * 2;
1616 paint_area.height = ((GdkRectangle *)item)->height + priv->item_padding * 2;
1620 cairo_set_source_rgba(cr, 1, 1, 1, 0.1);
1621 cairo_rectangle(cr, paint_area.x, paint_area.y, paint_area.width, paint_area.height);
1627 cairo_rectangle(cr, paint_area.x, paint_area.y, paint_area.width, paint_area.height);
1630 if (gdk_cairo_get_clip_rectangle(cr, NULL))
1631 entangle_session_browser_paint_item(browser, cr, item,
1632 ((GdkRectangle *)item)->x, ((GdkRectangle *)item)->y);
1644 entangle_session_browser_layout_row(EntangleSessionBrowser *browser,
1647 gint *maximum_width)
1649 EntangleSessionBrowserPrivate *priv = browser->priv;
1650 GtkWidget *widget = GTK_WIDGET(browser);
1651 GtkAllocation allocation;
1652 gint x, current_width;
1655 gint max_height = 0;
1662 current_width += 2 * priv->margin;
1664 gtk_widget_get_allocation(widget, &allocation);
1666 /* In the first loop we iterate horizontally until we hit allocation width
1667 * and collect the aligned height-for-width */
1668 items = priv->items;
1670 EntangleSessionBrowserItem *item = items->data;
1671 GdkRectangle *item_area = (GdkRectangle *)item;
1673 item_area->width = item_width;
1675 current_width += item_area->width + priv->item_padding * 2;
1677 /* Get this item's particular width & height (all alignments are cached by now) */
1678 entangle_session_browser_set_cell_data(browser, item);
1679 gtk_cell_area_get_preferred_height_for_width(priv->cell_area,
1680 priv->cell_area_context,
1684 current_width += priv->column_spacing;
1686 item_area->y = *y + priv->item_padding;
1687 item_area->x = x + priv->item_padding;
1689 x = current_width - priv->margin;
1691 if (current_width > *maximum_width)
1692 *maximum_width = current_width;
1697 items = items->next;
1700 gtk_cell_area_context_get_preferred_height_for_width(priv->cell_area_context, item_width, &max_height, NULL);
1701 gtk_cell_area_context_allocate(priv->cell_area_context, item_width, max_height);
1703 /* In the second loop the item height has been aligned and derived and
1704 * we just set the height and handle rtl layout */
1705 for (items = priv->items; items != NULL; items = items->next) {
1706 EntangleSessionBrowserItem *item = items->data;
1707 GdkRectangle *item_area = (GdkRectangle *)item;
1709 /* All items in the same row get the same height */
1710 item_area->height = max_height;
1713 /* Adjust the new y coordinate. */
1714 *y += max_height + priv->item_padding * 2;
1719 adjust_wrap_width(EntangleSessionBrowser *browser)
1721 gint wrap_width = 50;
1723 /* Here we go with the same old guess, try the icon size and set double
1724 * the size of the first icon found in the list, naive but works much
1726 if (browser->priv->items) {
1727 entangle_session_browser_set_cell_data(browser, browser->priv->items->data);
1728 gtk_cell_renderer_get_preferred_width(browser->priv->pixbuf_cell,
1729 GTK_WIDGET(browser),
1732 wrap_width = MAX(wrap_width * 2, 50);
1735 g_object_set(browser->priv->text_cell, "wrap-width", wrap_width, NULL);
1736 g_object_set(browser->priv->text_cell, "width", wrap_width, NULL);
1741 entangle_session_browser_layout(EntangleSessionBrowser *browser)
1743 EntangleSessionBrowserPrivate *priv = browser->priv;
1744 GtkAllocation allocation;
1745 GtkWidget *widget = GTK_WIDGET(browser);
1746 gint y = 0, maximum_width = 0;
1748 gboolean size_changed = FALSE;
1750 if (priv->layout_idle_id != 0) {
1751 g_source_remove (priv->layout_idle_id);
1752 priv->layout_idle_id = 0;
1755 /* Update the wrap width for the text cell before going and requesting sizes */
1756 adjust_wrap_width (browser);
1758 /* Update the context widths for any invalidated items */
1759 entangle_session_browser_cache_widths(browser);
1761 /* Fetch the new item width if needed */
1762 gtk_cell_area_context_get_preferred_width(priv->cell_area_context,
1765 gtk_cell_area_context_allocate(priv->cell_area_context, item_width, -1);
1769 entangle_session_browser_layout_row(browser,
1771 &y, &maximum_width);
1773 if (maximum_width != priv->width) {
1774 priv->width = maximum_width;
1775 size_changed = TRUE;
1780 if (y != priv->height) {
1782 size_changed = TRUE;
1785 entangle_session_browser_set_hadjustment_values(browser);
1786 entangle_session_browser_set_vadjustment_values(browser);
1789 gtk_widget_queue_resize_no_redraw (widget);
1791 gtk_widget_get_allocation(widget, &allocation);
1792 if (gtk_widget_get_realized(widget))
1793 gdk_window_resize(priv->bin_window,
1794 MAX(priv->width, allocation.width),
1795 MAX(priv->height, allocation.height));
1797 gtk_widget_queue_draw(widget);
1802 entangle_session_browser_process_updates(EntangleSessionBrowser *browser)
1804 EntangleSessionBrowserPrivate *priv = browser->priv;
1806 /* Prior to drawing, we check if a layout has been scheduled. If so,
1807 * do it now that all cell view items have valid sizes before we proceeed
1808 * (and resize the bin_window if required).
1810 if (priv->layout_idle_id != 0)
1811 entangle_session_browser_layout(browser);
1813 gdk_window_process_updates(priv->bin_window, TRUE);
1818 layout_callback(gpointer opaque)
1820 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(opaque);
1821 EntangleSessionBrowserPrivate *priv = browser->priv;
1823 priv->layout_idle_id = 0;
1825 entangle_session_browser_layout(browser);
1832 entangle_session_browser_queue_layout(EntangleSessionBrowser *browser)
1834 EntangleSessionBrowserPrivate *priv = browser->priv;
1836 if (priv->layout_idle_id != 0)
1839 priv->layout_idle_id =
1840 gdk_threads_add_idle_full(ENTANGLE_SESSION_BROWSER_PRIORITY_LAYOUT,
1841 layout_callback, browser, NULL);
1846 entangle_session_browser_set_hadjustment_values(EntangleSessionBrowser *browser)
1848 EntangleSessionBrowserPrivate *priv = browser->priv;
1849 GtkAllocation allocation;
1850 GtkAdjustment *adj = priv->hadjustment;
1852 gtk_widget_get_allocation(GTK_WIDGET(browser), &allocation);
1854 gtk_adjustment_configure(adj,
1855 gtk_adjustment_get_value(adj),
1857 MAX(allocation.width, priv->width),
1858 allocation.width * 0.1,
1859 allocation.width * 0.9,
1865 entangle_session_browser_set_vadjustment_values(EntangleSessionBrowser *browser)
1867 EntangleSessionBrowserPrivate *priv = browser->priv;
1868 GtkAllocation allocation;
1869 GtkAdjustment *adj = priv->vadjustment;
1871 gtk_widget_get_allocation(GTK_WIDGET(browser), &allocation);
1873 gtk_adjustment_configure(adj,
1874 gtk_adjustment_get_value(adj),
1876 MAX(allocation.height, priv->height),
1877 allocation.height * 0.1,
1878 allocation.height * 0.9,
1884 entangle_session_browser_set_hadjustment(EntangleSessionBrowser *browser,
1885 GtkAdjustment *adjustment)
1887 EntangleSessionBrowserPrivate *priv = browser->priv;
1889 if (adjustment && priv->hadjustment == adjustment)
1892 if (priv->hadjustment != NULL) {
1893 g_signal_handlers_disconnect_matched(priv->hadjustment,
1894 G_SIGNAL_MATCH_DATA,
1895 0, 0, NULL, NULL, browser);
1896 g_object_unref(priv->hadjustment);
1900 adjustment = gtk_adjustment_new(0.0, 0.0, 0.0,
1903 g_signal_connect(adjustment, "value-changed",
1904 G_CALLBACK(entangle_session_browser_adjustment_changed), browser);
1905 priv->hadjustment = g_object_ref_sink(adjustment);
1906 entangle_session_browser_set_hadjustment_values(browser);
1908 g_object_notify(G_OBJECT(browser), "hadjustment");
1913 entangle_session_browser_set_vadjustment(EntangleSessionBrowser *browser,
1914 GtkAdjustment *adjustment)
1916 EntangleSessionBrowserPrivate *priv = browser->priv;
1918 if (adjustment && priv->vadjustment == adjustment)
1921 if (priv->vadjustment != NULL) {
1922 g_signal_handlers_disconnect_matched(priv->vadjustment,
1923 G_SIGNAL_MATCH_DATA,
1924 0, 0, NULL, NULL, browser);
1925 g_object_unref (priv->vadjustment);
1929 adjustment = gtk_adjustment_new(0.0, 0.0, 0.0,
1932 g_signal_connect(adjustment, "value-changed",
1933 G_CALLBACK(entangle_session_browser_adjustment_changed), browser);
1934 priv->vadjustment = g_object_ref_sink (adjustment);
1935 entangle_session_browser_set_vadjustment_values (browser);
1937 g_object_notify(G_OBJECT(browser), "vadjustment");
1942 entangle_session_browser_adjustment_changed(GtkAdjustment *adjustment G_GNUC_UNUSED,
1943 EntangleSessionBrowser *browser)
1945 EntangleSessionBrowserPrivate *priv = browser->priv;
1947 if (gtk_widget_get_realized(GTK_WIDGET(browser))) {
1948 gdk_window_move(priv->bin_window,
1949 - gtk_adjustment_get_value(priv->hadjustment),
1950 - gtk_adjustment_get_value(priv->vadjustment));
1952 entangle_session_browser_process_updates(browser);
1961 * indent-tabs-mode: nil