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 g_object_unref(thisimage);
193 gtk_list_store_set(GTK_LIST_STORE(priv->model),
194 &iter, FIELD_PIXMAP, pixbuf, -1);
197 g_object_unref(thisimage);
198 } while (gtk_tree_model_iter_next(priv->model, &iter));
201 static void do_image_added(EntangleSession *session G_GNUC_UNUSED,
205 EntangleSessionBrowser *browser = data;
206 EntangleSessionBrowserPrivate *priv = browser->priv;
208 GtkTreePath *path = NULL;
209 int mod = entangle_image_get_last_modified(img);
210 gchar *name = g_path_get_basename(entangle_image_get_filename(img));
212 ENTANGLE_DEBUG("Request image %s for new image", entangle_image_get_filename(img));
213 entangle_pixbuf_loader_load(ENTANGLE_PIXBUF_LOADER(priv->loader), img);
215 gtk_list_store_append(GTK_LIST_STORE(priv->model), &iter);
217 gtk_list_store_set(GTK_LIST_STORE(priv->model),
220 FIELD_PIXMAP, priv->blank,
224 ENTANGLE_DEBUG("ADD IMAGE EXTRA %p", img);
225 path = gtk_tree_model_get_path(priv->model, &iter);
227 entangle_session_browser_select_path(browser, path);
228 entangle_session_browser_scroll_to_path(browser, path, FALSE, 0, 0);
230 gtk_tree_path_free(path);
232 gtk_widget_queue_resize(GTK_WIDGET(browser));
237 static void do_model_unload(EntangleSessionBrowser *browser)
239 EntangleSessionBrowserPrivate *priv = browser->priv;
242 ENTANGLE_DEBUG("Unload model");
244 g_signal_handler_disconnect(priv->session,
245 priv->sigImageAdded);
246 g_signal_handler_disconnect(priv->loader,
247 priv->sigThumbReady);
249 count = entangle_session_image_count(priv->session);
250 for (int i = 0 ; i < count ; i++) {
251 EntangleImage *img = entangle_session_image_get(priv->session, i);
252 entangle_pixbuf_loader_unload(ENTANGLE_PIXBUF_LOADER(priv->loader), img);
255 g_object_unref(priv->blank);
256 gtk_list_store_clear(GTK_LIST_STORE(priv->model));
259 static void do_model_load(EntangleSessionBrowser *browser)
261 EntangleSessionBrowserPrivate *priv = browser->priv;
266 ENTANGLE_DEBUG("Load model");
268 g_object_get(priv->loader,
273 priv->blank = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
274 gdk_pixbuf_fill(priv->blank, 0x000000FF);
276 priv->sigImageAdded = g_signal_connect(priv->session, "session-image-added",
277 G_CALLBACK(do_image_added), browser);
278 priv->sigThumbReady = g_signal_connect(priv->loader, "pixbuf-loaded",
279 G_CALLBACK(do_thumb_loaded), browser);
281 count = entangle_session_image_count(priv->session);
282 for (int i = 0 ; i < count ; i++) {
283 EntangleImage *img = entangle_session_image_get(priv->session, i);
284 int mod = entangle_image_get_last_modified(img);
286 gchar *name = g_path_get_basename(entangle_image_get_filename(img));
288 gtk_list_store_append(GTK_LIST_STORE(priv->model), &iter);
289 ENTANGLE_DEBUG("ADD IMAGE FIRST %p", img);
290 gtk_list_store_set(GTK_LIST_STORE(priv->model), &iter,
292 FIELD_PIXMAP, priv->blank,
297 entangle_pixbuf_loader_load(ENTANGLE_PIXBUF_LOADER(priv->loader), img);
301 GtkTreePath *path = NULL;
302 path = gtk_tree_path_new_from_indices(count - 1, -1);
304 entangle_session_browser_select_path(ENTANGLE_SESSION_BROWSER(browser), path);
305 entangle_session_browser_scroll_to_path(ENTANGLE_SESSION_BROWSER(browser), path, FALSE, 0, 0);
307 gtk_tree_path_free(path);
313 do_image_sort_modified(GtkTreeModel *model,
316 gpointer data G_GNUC_UNUSED)
320 gtk_tree_model_get(model, a, FIELD_LASTMOD, &ai, -1);
321 gtk_tree_model_get(model, b, FIELD_LASTMOD, &bi, -1);
327 static void entangle_session_browser_get_property(GObject *object,
332 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(object);
333 EntangleSessionBrowserPrivate *priv = browser->priv;
337 g_value_set_object(value, priv->session);
341 g_value_set_object(value, priv->loader);
344 case PROP_HADJUSTMENT:
345 g_value_set_object (value, priv->hadjustment);
348 case PROP_VADJUSTMENT:
349 g_value_set_object (value, priv->vadjustment);
352 case PROP_HSCROLL_POLICY:
353 g_value_set_enum (value, priv->hscroll_policy);
356 case PROP_VSCROLL_POLICY:
357 g_value_set_enum (value, priv->vscroll_policy);
361 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
366 static void entangle_session_browser_set_property(GObject *object,
371 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(object);
372 EntangleSessionBrowserPrivate *priv = browser->priv;
374 ENTANGLE_DEBUG("Set prop on session browser %d", prop_id);
378 entangle_session_browser_set_session(browser, g_value_get_object(value));
382 entangle_session_browser_set_thumbnail_loader(browser, g_value_get_object(value));
385 case PROP_HADJUSTMENT:
386 entangle_session_browser_set_hadjustment(browser, g_value_get_object(value));
389 case PROP_VADJUSTMENT:
390 entangle_session_browser_set_vadjustment(browser, g_value_get_object(value));
393 case PROP_HSCROLL_POLICY:
394 priv->hscroll_policy = g_value_get_enum (value);
395 gtk_widget_queue_resize (GTK_WIDGET (browser));
398 case PROP_VSCROLL_POLICY:
399 priv->vscroll_policy = g_value_get_enum (value);
400 gtk_widget_queue_resize (GTK_WIDGET (browser));
404 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
410 verify_items(EntangleSessionBrowser *browser)
415 for (items = browser->priv->items; items; items = items->next) {
416 EntangleSessionBrowserItem *item = items->data;
418 if (item->index != i)
419 ENTANGLE_DEBUG("List item does not match its index: "
420 "item index %d and list index %d\n", item->index, i);
427 entangle_session_browser_cell_layout_get_area(GtkCellLayout *cell_layout)
429 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(cell_layout);
430 EntangleSessionBrowserPrivate *priv = browser->priv;
432 return priv->cell_area;
437 entangle_session_browser_set_cell_data(EntangleSessionBrowser *browser,
438 EntangleSessionBrowserItem *item)
440 gboolean iters_persist;
443 iters_persist = gtk_tree_model_get_flags(browser->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
445 if (!iters_persist) {
448 path = gtk_tree_path_new_from_indices (item->index, -1);
449 if (!gtk_tree_model_get_iter (browser->priv->model, &iter, path))
451 gtk_tree_path_free (path);
456 gtk_cell_area_apply_attributes(browser->priv->cell_area,
457 browser->priv->model,
458 &iter, FALSE, FALSE);
462 /* This ensures that all widths have been cached in the
463 * context and we have proper alignments to go on.
466 entangle_session_browser_cache_widths(EntangleSessionBrowser *browser)
470 g_signal_handler_block (browser->priv->cell_area_context,
471 browser->priv->context_changed_id);
473 for (items = browser->priv->items; items; items = items->next) {
474 EntangleSessionBrowserItem *item = items->data;
476 /* Only fetch the width of items with invalidated sizes */
477 if (item->cell_area.width < 0) {
478 entangle_session_browser_set_cell_data(browser, item);
479 gtk_cell_area_get_preferred_width(browser->priv->cell_area,
480 browser->priv->cell_area_context,
481 GTK_WIDGET(browser), NULL, NULL);
485 g_signal_handler_unblock(browser->priv->cell_area_context,
486 browser->priv->context_changed_id);
491 entangle_session_browser_item_invalidate_size(EntangleSessionBrowserItem *item)
493 item->cell_area.width = -1;
494 item->cell_area.height = -1;
499 entangle_session_browser_invalidate_sizes (EntangleSessionBrowser *browser)
501 /* Clear all item sizes */
502 g_list_foreach (browser->priv->items,
503 (GFunc)entangle_session_browser_item_invalidate_size, NULL);
505 /* Reset the context */
506 if (browser->priv->cell_area_context) {
507 g_signal_handler_block (browser->priv->cell_area_context,
508 browser->priv->context_changed_id);
509 gtk_cell_area_context_reset (browser->priv->cell_area_context);
510 g_signal_handler_unblock (browser->priv->cell_area_context,
511 browser->priv->context_changed_id);
514 /* Re-layout the items */
515 entangle_session_browser_queue_layout(browser);
520 entangle_session_browser_context_changed(GtkCellAreaContext *context G_GNUC_UNUSED,
524 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
526 if (!strcmp(pspec->name, "minimum-width") ||
527 !strcmp(pspec->name, "natural-width") ||
528 !strcmp(pspec->name, "minimum-height") ||
529 !strcmp(pspec->name, "natural-height"))
530 entangle_session_browser_invalidate_sizes(browser);
535 entangle_session_browser_row_changed(GtkTreeModel *model G_GNUC_UNUSED,
537 GtkTreeIter *iter G_GNUC_UNUSED,
540 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
542 /* ignore changes in branches */
543 if (gtk_tree_path_get_depth(path) > 1)
546 /* An icon view subclass might add it's own model and populate
547 * things at init() time instead of waiting for the constructor()
550 if (browser->priv->cell_area)
551 gtk_cell_area_stop_editing(browser->priv->cell_area, TRUE);
553 /* Here we can use a "grow-only" strategy for optimization
554 * and only invalidate a single item and queue a relayout
555 * instead of invalidating the whole thing.
557 * For now EntangleSessionBrowser still cant deal with huge models
558 * so just invalidate the whole thing when the model
561 entangle_session_browser_invalidate_sizes(browser);
563 verify_items(browser);
567 static EntangleSessionBrowserItem *entangle_session_browser_item_new(void)
569 EntangleSessionBrowserItem *item;
571 item = g_slice_new0(EntangleSessionBrowserItem);
573 item->cell_area.width = -1;
574 item->cell_area.height = -1;
580 static void entangle_session_browser_item_free(EntangleSessionBrowserItem *item)
582 g_return_if_fail (item != NULL);
584 g_slice_free(EntangleSessionBrowserItem, item);
589 entangle_session_browser_row_inserted(GtkTreeModel *model G_GNUC_UNUSED,
594 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
596 EntangleSessionBrowserItem *item;
597 gboolean iters_persist;
600 /* ignore changes in branches */
601 if (gtk_tree_path_get_depth(path) > 1)
604 iters_persist = gtk_tree_model_get_flags(browser->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
606 index = gtk_tree_path_get_indices(path)[0];
608 item = entangle_session_browser_item_new();
615 /* FIXME: We can be more efficient here,
616 we can store a tail pointer and use that when
617 appending (which is a rather common operation)
619 browser->priv->items = g_list_insert(browser->priv->items,
622 list = g_list_nth (browser->priv->items, index + 1);
623 for (; list; list = list->next) {
628 verify_items(browser);
630 entangle_session_browser_queue_layout(browser);
635 entangle_session_browser_row_deleted(GtkTreeModel *model G_GNUC_UNUSED,
639 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
641 EntangleSessionBrowserItem *item;
643 gboolean emit = FALSE;
645 /* ignore changes in branches */
646 if (gtk_tree_path_get_depth(path) > 1)
649 index = gtk_tree_path_get_indices(path)[0];
651 list = g_list_nth(browser->priv->items, index);
654 if (browser->priv->cell_area)
655 gtk_cell_area_stop_editing(browser->priv->cell_area, TRUE);
660 entangle_session_browser_item_free(item);
662 for (next = list->next; next; next = next->next) {
667 browser->priv->items = g_list_delete_link(browser->priv->items, list);
669 verify_items(browser);
671 entangle_session_browser_queue_layout(browser);
674 g_signal_emit(browser, browser_signals[SIGNAL_SELECTION_CHANGED], 0);
678 entangle_session_browser_rows_reordered(GtkTreeModel *model,
679 GtkTreePath *parent G_GNUC_UNUSED,
684 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(data);
687 GList *items = NULL, *list;
688 EntangleSessionBrowserItem **item_array;
691 /* ignore changes in branches */
695 if (browser->priv->cell_area)
696 gtk_cell_area_stop_editing(browser->priv->cell_area, TRUE);
698 length = gtk_tree_model_iter_n_children(model, NULL);
700 order = g_new(gint, length);
701 for (i = 0; i < length; i++)
702 order[new_order[i]] = i;
704 item_array = g_new(EntangleSessionBrowserItem *, length);
705 for (i = 0, list = browser->priv->items; list != NULL; list = list->next, i++)
706 item_array[order[i]] = list->data;
709 for (i = length - 1; i >= 0; i--) {
710 item_array[i]->index = i;
711 items = g_list_prepend(items, item_array[i]);
715 g_list_free(browser->priv->items);
716 browser->priv->items = items;
718 entangle_session_browser_queue_layout(browser);
720 verify_items(browser);
725 entangle_session_browser_build_items(EntangleSessionBrowser *browser)
729 gboolean iters_persist;
732 iters_persist = gtk_tree_model_get_flags(browser->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST;
734 if (!gtk_tree_model_get_iter_first(browser->priv->model,
741 EntangleSessionBrowserItem *item = entangle_session_browser_item_new();
748 items = g_list_prepend(items, item);
749 } while (gtk_tree_model_iter_next(browser->priv->model, &iter));
751 browser->priv->items = g_list_reverse(items);
756 entangle_session_browser_realize(GtkWidget *widget)
758 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
759 EntangleSessionBrowserPrivate *priv = browser->priv;
760 GtkAllocation allocation;
762 GdkWindowAttr attributes;
763 gint attributes_mask;
764 GtkStyleContext *context;
766 gtk_widget_set_realized(widget, TRUE);
768 gtk_widget_get_allocation(widget, &allocation);
770 /* Make the main, clipping window */
771 attributes.window_type = GDK_WINDOW_CHILD;
772 attributes.x = allocation.x;
773 attributes.y = allocation.y;
774 attributes.width = allocation.width;
775 attributes.height = allocation.height;
776 attributes.wclass = GDK_INPUT_OUTPUT;
777 attributes.visual = gtk_widget_get_visual(widget);
778 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
780 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
782 window = gdk_window_new(gtk_widget_get_parent_window(widget),
783 &attributes, attributes_mask);
784 gtk_widget_set_window(widget, window);
785 gdk_window_set_user_data(window, widget);
787 gtk_widget_get_allocation(widget, &allocation);
789 /* Make the window for the icon view */
792 attributes.width = MAX (priv->width, allocation.width);
793 attributes.height = MAX (priv->height, allocation.height);
794 attributes.event_mask = (GDK_EXPOSURE_MASK |
796 GDK_POINTER_MOTION_MASK |
797 GDK_BUTTON_PRESS_MASK |
798 GDK_BUTTON_RELEASE_MASK |
800 GDK_KEY_RELEASE_MASK) |
801 gtk_widget_get_events(widget);
803 priv->bin_window = gdk_window_new(window,
804 &attributes, attributes_mask);
805 gdk_window_set_user_data(priv->bin_window, widget);
807 context = gtk_widget_get_style_context(widget);
809 gtk_style_context_save(context);
810 gtk_style_context_add_class(context, GTK_STYLE_CLASS_VIEW);
811 gtk_style_context_set_background(context, priv->bin_window);
812 gtk_style_context_restore(context);
814 gdk_window_show(priv->bin_window);
819 entangle_session_browser_unrealize(GtkWidget *widget)
821 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
822 EntangleSessionBrowserPrivate *priv = browser->priv;
824 gdk_window_set_user_data(priv->bin_window, NULL);
825 gdk_window_destroy(priv->bin_window);
826 priv->bin_window = NULL;
828 GTK_WIDGET_CLASS(entangle_session_browser_parent_class)->unrealize(widget);
833 entangle_session_browser_scroll_to_item(EntangleSessionBrowser *browser,
834 EntangleSessionBrowserItem *item)
836 EntangleSessionBrowserPrivate *priv = browser->priv;
837 GtkWidget *widget = GTK_WIDGET(browser);
838 GtkAdjustment *hadj, *vadj;
839 GtkAllocation allocation;
841 GdkRectangle item_area;
843 item_area.x = item->cell_area.x - priv->item_padding;
844 item_area.y = item->cell_area.y - priv->item_padding;
845 item_area.width = item->cell_area.width + priv->item_padding * 2;
846 item_area.height = item->cell_area.height + priv->item_padding * 2;
848 gdk_window_get_position(priv->bin_window, &x, &y);
849 gtk_widget_get_allocation(widget, &allocation);
851 hadj = priv->hadjustment;
852 vadj = priv->vadjustment;
854 if (y + item_area.y < 0)
855 gtk_adjustment_set_value(vadj,
856 gtk_adjustment_get_value(vadj)
858 else if (y + item_area.y + item_area.height > allocation.height)
859 gtk_adjustment_set_value(vadj,
860 gtk_adjustment_get_value(vadj)
861 + y + item_area.y + item_area.height - allocation.height);
863 if (x + item_area.x < 0)
864 gtk_adjustment_set_value(hadj,
865 gtk_adjustment_get_value(hadj)
867 else if (x + item_area.x + item_area.width > allocation.width)
868 gtk_adjustment_set_value(hadj,
869 gtk_adjustment_get_value(hadj)
870 + x + item_area.x + item_area.width - allocation.width);
872 gtk_adjustment_changed(hadj);
873 gtk_adjustment_changed(vadj);
877 static EntangleSessionBrowserItem *
878 entangle_session_browser_get_item_at_coords(EntangleSessionBrowser *browser,
881 gboolean only_in_cell,
882 GtkCellRenderer **cell_at_pos)
884 EntangleSessionBrowserPrivate *priv = browser->priv;
890 for (items = priv->items; items; items = items->next) {
891 EntangleSessionBrowserItem *item = items->data;
892 GdkRectangle *item_area = (GdkRectangle *)item;
894 if (x >= item_area->x - priv->column_spacing / 2 &&
895 x <= item_area->x + item_area->width + priv->column_spacing / 2 &&
897 y <= item_area->y + item_area->height) {
898 if (only_in_cell || cell_at_pos) {
899 GtkCellRenderer *cell = NULL;
901 entangle_session_browser_set_cell_data(browser, item);
903 if (x >= item_area->x && x <= item_area->x + item_area->width &&
904 y >= item_area->y && y <= item_area->y + item_area->height)
905 cell = gtk_cell_area_get_cell_at_position(priv->cell_area,
906 priv->cell_area_context,
915 return cell != NULL ? item : NULL;
927 entangle_session_browser_queue_draw_item(EntangleSessionBrowser *browser,
928 EntangleSessionBrowserItem *item)
930 EntangleSessionBrowserPrivate *priv = browser->priv;
932 GdkRectangle *item_area = (GdkRectangle *)item;
934 rect.x = item_area->x - priv->item_padding;
935 rect.y = item_area->y - priv->item_padding;
936 rect.width = item_area->width + priv->item_padding * 2;
937 rect.height = item_area->height + priv->item_padding * 2;
939 if (priv->bin_window)
940 gdk_window_invalidate_rect(priv->bin_window, &rect, TRUE);
945 entangle_session_browser_unselect_all_internal(EntangleSessionBrowser *browser)
947 EntangleSessionBrowserPrivate *priv = browser->priv;
948 gboolean dirty = FALSE;
951 for (items = priv->items; items; items = items->next) {
952 EntangleSessionBrowserItem *item = items->data;
954 if (item->selected) {
955 item->selected = FALSE;
957 entangle_session_browser_queue_draw_item(browser, item);
966 entangle_session_browser_select_item(EntangleSessionBrowser *browser,
967 EntangleSessionBrowserItem *item)
972 item->selected = TRUE;
974 g_signal_emit(browser, browser_signals[SIGNAL_SELECTION_CHANGED], 0);
976 entangle_session_browser_queue_draw_item(browser, item);
981 entangle_session_browser_unselect_item(EntangleSessionBrowser *browser,
982 EntangleSessionBrowserItem *item)
987 item->selected = FALSE;
989 g_signal_emit(browser, browser_signals[SIGNAL_SELECTION_CHANGED], 0);
991 entangle_session_browser_queue_draw_item(browser, item);
996 entangle_session_browser_select_path(EntangleSessionBrowser *browser,
999 EntangleSessionBrowserPrivate *priv = browser->priv;
1000 EntangleSessionBrowserItem *item = NULL;
1002 if (gtk_tree_path_get_depth (path) > 0)
1003 item = g_list_nth_data(priv->items,
1004 gtk_tree_path_get_indices(path)[0]);
1007 entangle_session_browser_unselect_all_internal(browser);
1008 entangle_session_browser_select_item(browser, item);
1014 entangle_session_browser_scroll_to_path(EntangleSessionBrowser *browser,
1020 EntangleSessionBrowserPrivate *priv = browser->priv;
1021 EntangleSessionBrowserItem *item = NULL;
1022 GtkWidget *widget = GTK_WIDGET(browser);
1024 if (gtk_tree_path_get_depth(path) > 0)
1025 item = g_list_nth_data(priv->items,
1026 gtk_tree_path_get_indices(path)[0]);
1028 if (!item || item->cell_area.width < 0 ||
1029 !gtk_widget_get_realized (widget)) {
1030 if (priv->scroll_to_path)
1031 gtk_tree_row_reference_free(priv->scroll_to_path);
1033 priv->scroll_to_path = NULL;
1036 priv->scroll_to_path = gtk_tree_row_reference_new_proxy(G_OBJECT(browser),
1039 priv->scroll_to_use_align = use_align;
1040 priv->scroll_to_row_align = row_align;
1041 priv->scroll_to_col_align = col_align;
1047 GtkAllocation allocation;
1050 GdkRectangle item_area = {
1051 item->cell_area.x - priv->item_padding,
1052 item->cell_area.y - priv->item_padding,
1053 item->cell_area.width + priv->item_padding * 2,
1054 item->cell_area.height + priv->item_padding * 2
1057 gdk_window_get_position(priv->bin_window, &x, &y);
1059 gtk_widget_get_allocation(widget, &allocation);
1061 offset = y + item_area.y - row_align * (allocation.height - item_area.height);
1063 gtk_adjustment_set_value(priv->vadjustment,
1064 gtk_adjustment_get_value(priv->vadjustment) + offset);
1066 offset = x + item_area.x - col_align * (allocation.width - item_area.width);
1068 gtk_adjustment_set_value(priv->hadjustment,
1069 gtk_adjustment_get_value(priv->hadjustment) + offset);
1071 gtk_adjustment_changed(priv->hadjustment);
1072 gtk_adjustment_changed(priv->vadjustment);
1074 entangle_session_browser_scroll_to_item(browser, item);
1080 entangle_session_browser_button_press(GtkWidget *widget,
1081 GdkEventButton *event)
1083 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1084 EntangleSessionBrowserPrivate *priv = browser->priv;
1085 EntangleSessionBrowserItem *item;
1086 gboolean dirty = FALSE;
1087 GtkCellRenderer *cell = NULL;
1089 if (event->window != priv->bin_window)
1092 if (!gtk_widget_has_focus(widget))
1093 gtk_widget_grab_focus(widget);
1095 if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
1096 item = entangle_session_browser_get_item_at_coords(browser,
1102 * We consider only the cells' area as the item area if the
1103 * item is not selected, but if it *is* selected, the complete
1104 * selection rectangle is considered to be part of the item.
1106 if (item != NULL && !item->selected) {
1107 entangle_session_browser_unselect_all_internal(browser);
1109 item->selected = TRUE;
1110 entangle_session_browser_queue_draw_item(browser, item);
1111 entangle_session_browser_scroll_to_item(browser, item);
1116 g_signal_emit(browser, browser_signals[SIGNAL_SELECTION_CHANGED], 0);
1118 return event->button == 1;
1123 entangle_session_browser_key_release(GtkWidget *widget,
1126 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1127 EntangleSessionBrowserPrivate *priv = browser->priv;
1128 GList *list, *prev = NULL;
1130 switch (event->keyval) {
1132 for (list = priv->items; list != NULL; list = list->next) {
1133 EntangleSessionBrowserItem *item = list->data;
1135 if (item->selected && list->next) {
1136 EntangleSessionBrowserItem *next = list->next->data;
1137 entangle_session_browser_unselect_item(browser, item);
1138 entangle_session_browser_select_item(browser, next);
1139 entangle_session_browser_scroll_to_item(browser, next);
1146 for (list = priv->items; list != NULL; list = list->next) {
1147 EntangleSessionBrowserItem *item = list->data;
1149 if (item->selected && prev) {
1150 EntangleSessionBrowserItem *prior = prev->data;
1151 entangle_session_browser_unselect_item(browser, item);
1152 entangle_session_browser_select_item(browser, prior);
1153 entangle_session_browser_scroll_to_item(browser, prior);
1161 return GTK_WIDGET_CLASS(entangle_session_browser_parent_class)->key_press_event(widget, event);
1167 entangle_session_browser_size_allocate(GtkWidget *widget,
1168 GtkAllocation *allocation)
1170 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1171 EntangleSessionBrowserPrivate *priv = browser->priv;
1173 gtk_widget_set_allocation(widget, allocation);
1175 if (gtk_widget_get_realized(widget)) {
1176 gdk_window_move_resize(gtk_widget_get_window(widget),
1177 allocation->x, allocation->y,
1178 allocation->width, allocation->height);
1179 gdk_window_resize(priv->bin_window,
1180 MAX(priv->width, allocation->width),
1181 MAX(priv->height, allocation->height));
1184 entangle_session_browser_layout(browser);
1186 /* Delay signal emission */
1187 g_object_freeze_notify(G_OBJECT(priv->hadjustment));
1188 g_object_freeze_notify(G_OBJECT(priv->vadjustment));
1190 entangle_session_browser_set_hadjustment_values(browser);
1191 entangle_session_browser_set_vadjustment_values(browser);
1193 if (gtk_widget_get_realized(widget) &&
1194 priv->scroll_to_path) {
1196 path = gtk_tree_row_reference_get_path(priv->scroll_to_path);
1197 gtk_tree_row_reference_free(priv->scroll_to_path);
1198 priv->scroll_to_path = NULL;
1200 entangle_session_browser_scroll_to_path(browser, path,
1201 priv->scroll_to_use_align,
1202 priv->scroll_to_row_align,
1203 priv->scroll_to_col_align);
1204 gtk_tree_path_free(path);
1207 /* Emit any pending signals now */
1208 g_object_thaw_notify(G_OBJECT(priv->hadjustment));
1209 g_object_thaw_notify(G_OBJECT(priv->vadjustment));
1213 static void entangle_session_browser_destroy(GtkWidget *widget)
1215 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1216 EntangleSessionBrowserPrivate *priv = browser->priv;
1218 if (priv->layout_idle_id != 0) {
1219 g_source_remove(priv->layout_idle_id);
1220 priv->layout_idle_id = 0;
1223 if (priv->scroll_to_path != NULL) {
1224 gtk_tree_row_reference_free(priv->scroll_to_path);
1225 priv->scroll_to_path = NULL;
1228 if (priv->hadjustment != NULL) {
1229 g_object_unref(priv->hadjustment);
1230 priv->hadjustment = NULL;
1233 if (priv->vadjustment != NULL) {
1234 g_object_unref(priv->vadjustment);
1235 priv->vadjustment = NULL;
1238 GTK_WIDGET_CLASS(entangle_session_browser_parent_class)->destroy(widget);
1242 static void entangle_session_browser_finalize(GObject *object)
1244 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(object);
1245 EntangleSessionBrowserPrivate *priv = browser->priv;
1247 if (priv->session && priv->loader)
1248 do_model_unload(browser);
1250 if (priv->cell_area_context) {
1251 g_signal_handler_disconnect(priv->cell_area_context, priv->context_changed_id);
1252 priv->context_changed_id = 0;
1254 g_object_unref(priv->cell_area_context);
1255 priv->cell_area_context = NULL;
1259 g_object_unref(priv->session);
1261 g_object_unref(priv->loader);
1263 G_OBJECT_CLASS (entangle_session_browser_parent_class)->finalize (object);
1268 entangle_session_browser_cell_layout_init(GtkCellLayoutIface *iface)
1270 iface->get_area = entangle_session_browser_cell_layout_get_area;
1273 static void entangle_session_browser_class_init(EntangleSessionBrowserClass *klass)
1275 GObjectClass *object_class = G_OBJECT_CLASS(klass);
1276 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1278 object_class->finalize = entangle_session_browser_finalize;
1279 object_class->get_property = entangle_session_browser_get_property;
1280 object_class->set_property = entangle_session_browser_set_property;
1282 widget_class->destroy = entangle_session_browser_destroy;
1283 widget_class->realize = entangle_session_browser_realize;
1284 widget_class->unrealize = entangle_session_browser_unrealize;
1285 widget_class->draw = entangle_session_browser_draw;
1286 widget_class->button_press_event = entangle_session_browser_button_press;
1287 widget_class->key_release_event = entangle_session_browser_key_release;
1288 widget_class->size_allocate = entangle_session_browser_size_allocate;
1290 g_object_class_install_property(object_class,
1292 g_param_spec_object("session",
1294 "Session to be displayed",
1295 ENTANGLE_TYPE_SESSION,
1297 G_PARAM_STATIC_NAME |
1298 G_PARAM_STATIC_NICK |
1299 G_PARAM_STATIC_BLURB));
1301 g_object_class_install_property(object_class,
1303 g_param_spec_object("thumbnail-loader",
1306 ENTANGLE_TYPE_THUMBNAIL_LOADER,
1308 G_PARAM_STATIC_NAME |
1309 G_PARAM_STATIC_NICK |
1310 G_PARAM_STATIC_BLURB));
1312 /* Scrollable interface properties */
1313 g_object_class_override_property(object_class, PROP_HADJUSTMENT, "hadjustment");
1314 g_object_class_override_property(object_class, PROP_VADJUSTMENT, "vadjustment");
1315 g_object_class_override_property(object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1316 g_object_class_override_property(object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1318 browser_signals[SIGNAL_SELECTION_CHANGED] =
1319 g_signal_new("selection-changed",
1320 G_TYPE_FROM_CLASS(object_class),
1322 G_STRUCT_OFFSET(EntangleSessionBrowserClass, selection_changed),
1324 g_cclosure_marshal_VOID__VOID,
1327 g_type_class_add_private(klass, sizeof(EntangleSessionBrowserPrivate));
1331 EntangleSessionBrowser *entangle_session_browser_new(void)
1333 return ENTANGLE_SESSION_BROWSER(g_object_new(ENTANGLE_TYPE_SESSION_BROWSER, NULL));
1337 static void entangle_session_browser_init(EntangleSessionBrowser *browser)
1339 EntangleSessionBrowserPrivate *priv;
1342 priv = browser->priv = ENTANGLE_SESSION_BROWSER_GET_PRIVATE(browser);
1344 priv->model = GTK_TREE_MODEL(gtk_list_store_new(FIELD_LAST,
1345 ENTANGLE_TYPE_IMAGE,
1350 gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(priv->model),
1351 do_image_sort_modified, NULL, NULL);
1352 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(priv->model),
1353 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
1354 GTK_SORT_ASCENDING);
1356 priv->cell_area = gtk_cell_area_box_new ();
1357 g_object_ref_sink(priv->cell_area);
1358 gtk_orientable_set_orientation(GTK_ORIENTABLE (priv->cell_area), GTK_ORIENTATION_VERTICAL);
1360 priv->cell_area_context = gtk_cell_area_create_context(priv->cell_area);
1361 priv->context_changed_id =
1362 g_signal_connect(priv->cell_area_context, "notify",
1363 G_CALLBACK(entangle_session_browser_context_changed), browser);
1365 priv->pixbuf_cell = gtk_cell_renderer_pixbuf_new();
1366 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(browser), priv->pixbuf_cell, FALSE);
1368 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(browser),
1370 "pixbuf", FIELD_PIXMAP,
1373 g_object_set(priv->pixbuf_cell,
1379 priv->text_cell = gtk_cell_renderer_text_new();
1380 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(browser), priv->text_cell, FALSE);
1381 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(browser),
1391 g_object_set(priv->text_cell,
1392 "alignment", PANGO_ALIGN_CENTER,
1393 "wrap-mode", PANGO_WRAP_WORD_CHAR,
1396 "foreground-set", TRUE,
1397 "foreground-rgba", &fg,
1400 g_signal_connect(priv->model,
1402 G_CALLBACK (entangle_session_browser_row_changed),
1404 g_signal_connect(priv->model,
1406 G_CALLBACK (entangle_session_browser_row_inserted),
1408 g_signal_connect(priv->model,
1410 G_CALLBACK (entangle_session_browser_row_deleted),
1412 g_signal_connect(priv->model,
1414 G_CALLBACK (entangle_session_browser_rows_reordered),
1417 entangle_session_browser_build_items(browser);
1418 entangle_session_browser_queue_layout(browser);
1421 priv->item_padding = 0;
1422 priv->column_spacing = 6;
1424 gtk_widget_set_can_focus(GTK_WIDGET(browser), TRUE);
1429 entangle_session_browser_get_selected_items(EntangleSessionBrowser *browser)
1431 EntangleSessionBrowserPrivate *priv = browser->priv;
1433 GList *selected = NULL;
1435 for (list = priv->items; list != NULL; list = list->next) {
1436 EntangleSessionBrowserItem *item = list->data;
1438 if (item->selected) {
1439 GtkTreePath *path = gtk_tree_path_new_from_indices(item->index, -1);
1441 selected = g_list_prepend(selected, path);
1449 EntangleImage *entangle_session_browser_selected_image(EntangleSessionBrowser *browser)
1451 EntangleSessionBrowserPrivate *priv = browser->priv;
1453 EntangleImage *img = NULL;
1458 items = entangle_session_browser_get_selected_items(browser);
1463 path = g_list_nth_data(items, 0);
1467 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->model), &iter, path))
1470 memset(&val, 0, sizeof val);
1471 gtk_tree_model_get_value(GTK_TREE_MODEL(priv->model), &iter, 0, &val);
1473 img = g_value_get_object(&val);
1476 g_list_foreach(items, (GFunc)(gtk_tree_path_free), NULL);
1482 void entangle_session_browser_set_thumbnail_loader(EntangleSessionBrowser *browser,
1483 EntangleThumbnailLoader *loader)
1485 EntangleSessionBrowserPrivate *priv = browser->priv;
1489 do_model_unload(browser);
1491 g_object_unref(priv->loader);
1493 priv->loader = loader;
1495 g_object_ref(priv->loader);
1498 do_model_load(browser);
1503 EntangleThumbnailLoader *entangle_session_browser_get_thumbnail_loader(EntangleSessionBrowser *browser)
1505 EntangleSessionBrowserPrivate *priv = browser->priv;
1507 return priv->loader;
1511 void entangle_session_browser_set_session(EntangleSessionBrowser *browser,
1512 EntangleSession *session)
1514 EntangleSessionBrowserPrivate *priv = browser->priv;
1516 if (priv->session) {
1518 do_model_unload(browser);
1519 g_object_unref(priv->session);
1521 priv->session = session;
1522 if (priv->session) {
1523 g_object_ref(priv->session);
1526 do_model_load(browser);
1531 EntangleSession *entangle_session_browser_get_session(EntangleSessionBrowser *browser)
1533 EntangleSessionBrowserPrivate *priv = browser->priv;
1535 return priv->session;
1540 entangle_session_browser_paint_item(EntangleSessionBrowser *browser,
1542 EntangleSessionBrowserItem *item,
1546 GdkRectangle cell_area;
1547 GtkStateFlags state = 0;
1548 GtkCellRendererState flags = 0;
1549 GtkStyleContext *style_context;
1550 GtkWidget *widget = GTK_WIDGET (browser);
1551 EntangleSessionBrowserPrivate *priv = browser->priv;
1553 entangle_session_browser_set_cell_data(browser, item);
1555 style_context = gtk_widget_get_style_context(widget);
1557 gtk_style_context_save(style_context);
1558 gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_VIEW);
1559 gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_CELL);
1561 if (item->selected) {
1562 state |= GTK_STATE_FLAG_SELECTED;
1563 flags |= GTK_CELL_RENDERER_SELECTED;
1565 gtk_style_context_set_state(style_context, state);
1566 gtk_render_background(style_context, cr,
1567 x - browser->priv->item_padding,
1568 y - browser->priv->item_padding,
1569 item->cell_area.width + browser->priv->item_padding * 2,
1570 item->cell_area.height + browser->priv->item_padding * 2);
1575 cell_area.width = item->cell_area.width;
1576 cell_area.height = item->cell_area.height;
1578 gtk_cell_area_render(priv->cell_area,
1579 priv->cell_area_context,
1580 widget, cr, &cell_area, &cell_area, flags,
1583 gtk_style_context_restore(style_context);
1588 entangle_session_browser_draw(GtkWidget *widget,
1591 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(widget);
1592 EntangleSessionBrowserPrivate *priv = browser->priv;
1594 int ww, wh; /* Available drawing area extents */
1596 ww = gdk_window_get_width(gtk_widget_get_window(widget));
1597 wh = gdk_window_get_height(gtk_widget_get_window(widget));
1599 cairo_set_source_rgb(cr, 0, 0, 0);
1600 cairo_rectangle(cr, 0, 0, ww, wh);
1603 if (!gtk_cairo_should_draw_window (cr, priv->bin_window))
1607 gtk_cairo_transform_to_window(cr, widget, priv->bin_window);
1608 cairo_set_line_width(cr, 1.);
1610 for (icons = priv->items; icons; icons = icons->next) {
1611 EntangleSessionBrowserItem *item = icons->data;
1612 GdkRectangle paint_area;
1614 paint_area.x = ((GdkRectangle *)item)->x - priv->item_padding;
1615 paint_area.y = ((GdkRectangle *)item)->y - priv->item_padding;
1616 paint_area.width = ((GdkRectangle *)item)->width + priv->item_padding * 2;
1617 paint_area.height = ((GdkRectangle *)item)->height + priv->item_padding * 2;
1621 cairo_set_source_rgba(cr, 1, 1, 1, 0.1);
1622 cairo_rectangle(cr, paint_area.x, paint_area.y, paint_area.width, paint_area.height);
1628 cairo_rectangle(cr, paint_area.x, paint_area.y, paint_area.width, paint_area.height);
1631 if (gdk_cairo_get_clip_rectangle(cr, NULL))
1632 entangle_session_browser_paint_item(browser, cr, item,
1633 ((GdkRectangle *)item)->x, ((GdkRectangle *)item)->y);
1645 entangle_session_browser_layout_row(EntangleSessionBrowser *browser,
1648 gint *maximum_width)
1650 EntangleSessionBrowserPrivate *priv = browser->priv;
1651 GtkWidget *widget = GTK_WIDGET(browser);
1652 GtkAllocation allocation;
1653 gint x, current_width;
1656 gint max_height = 0;
1663 current_width += 2 * priv->margin;
1665 gtk_widget_get_allocation(widget, &allocation);
1667 /* In the first loop we iterate horizontally until we hit allocation width
1668 * and collect the aligned height-for-width */
1669 items = priv->items;
1671 EntangleSessionBrowserItem *item = items->data;
1672 GdkRectangle *item_area = (GdkRectangle *)item;
1674 item_area->width = item_width;
1676 current_width += item_area->width + priv->item_padding * 2;
1678 /* Get this item's particular width & height (all alignments are cached by now) */
1679 entangle_session_browser_set_cell_data(browser, item);
1680 gtk_cell_area_get_preferred_height_for_width(priv->cell_area,
1681 priv->cell_area_context,
1685 current_width += priv->column_spacing;
1687 item_area->y = *y + priv->item_padding;
1688 item_area->x = x + priv->item_padding;
1690 x = current_width - priv->margin;
1692 if (current_width > *maximum_width)
1693 *maximum_width = current_width;
1698 items = items->next;
1701 gtk_cell_area_context_get_preferred_height_for_width(priv->cell_area_context, item_width, &max_height, NULL);
1702 gtk_cell_area_context_allocate(priv->cell_area_context, item_width, max_height);
1704 /* In the second loop the item height has been aligned and derived and
1705 * we just set the height and handle rtl layout */
1706 for (items = priv->items; items != NULL; items = items->next) {
1707 EntangleSessionBrowserItem *item = items->data;
1708 GdkRectangle *item_area = (GdkRectangle *)item;
1710 /* All items in the same row get the same height */
1711 item_area->height = max_height;
1714 /* Adjust the new y coordinate. */
1715 *y += max_height + priv->item_padding * 2;
1720 adjust_wrap_width(EntangleSessionBrowser *browser)
1722 gint wrap_width = 50;
1724 /* Here we go with the same old guess, try the icon size and set double
1725 * the size of the first icon found in the list, naive but works much
1727 if (browser->priv->items) {
1728 entangle_session_browser_set_cell_data(browser, browser->priv->items->data);
1729 gtk_cell_renderer_get_preferred_width(browser->priv->pixbuf_cell,
1730 GTK_WIDGET(browser),
1733 wrap_width = MAX(wrap_width * 2, 50);
1736 g_object_set(browser->priv->text_cell, "wrap-width", wrap_width, NULL);
1737 g_object_set(browser->priv->text_cell, "width", wrap_width, NULL);
1742 entangle_session_browser_layout(EntangleSessionBrowser *browser)
1744 EntangleSessionBrowserPrivate *priv = browser->priv;
1745 GtkAllocation allocation;
1746 GtkWidget *widget = GTK_WIDGET(browser);
1747 gint y = 0, maximum_width = 0;
1749 gboolean size_changed = FALSE;
1751 if (priv->layout_idle_id != 0) {
1752 g_source_remove (priv->layout_idle_id);
1753 priv->layout_idle_id = 0;
1756 /* Update the wrap width for the text cell before going and requesting sizes */
1757 adjust_wrap_width (browser);
1759 /* Update the context widths for any invalidated items */
1760 entangle_session_browser_cache_widths(browser);
1762 /* Fetch the new item width if needed */
1763 gtk_cell_area_context_get_preferred_width(priv->cell_area_context,
1766 gtk_cell_area_context_allocate(priv->cell_area_context, item_width, -1);
1770 entangle_session_browser_layout_row(browser,
1772 &y, &maximum_width);
1774 if (maximum_width != priv->width) {
1775 priv->width = maximum_width;
1776 size_changed = TRUE;
1781 if (y != priv->height) {
1783 size_changed = TRUE;
1786 entangle_session_browser_set_hadjustment_values(browser);
1787 entangle_session_browser_set_vadjustment_values(browser);
1790 gtk_widget_queue_resize_no_redraw (widget);
1792 gtk_widget_get_allocation(widget, &allocation);
1793 if (gtk_widget_get_realized(widget))
1794 gdk_window_resize(priv->bin_window,
1795 MAX(priv->width, allocation.width),
1796 MAX(priv->height, allocation.height));
1798 gtk_widget_queue_draw(widget);
1803 entangle_session_browser_process_updates(EntangleSessionBrowser *browser)
1805 EntangleSessionBrowserPrivate *priv = browser->priv;
1807 /* Prior to drawing, we check if a layout has been scheduled. If so,
1808 * do it now that all cell view items have valid sizes before we proceeed
1809 * (and resize the bin_window if required).
1811 if (priv->layout_idle_id != 0)
1812 entangle_session_browser_layout(browser);
1814 gdk_window_process_updates(priv->bin_window, TRUE);
1819 layout_callback(gpointer opaque)
1821 EntangleSessionBrowser *browser = ENTANGLE_SESSION_BROWSER(opaque);
1822 EntangleSessionBrowserPrivate *priv = browser->priv;
1824 priv->layout_idle_id = 0;
1826 entangle_session_browser_layout(browser);
1833 entangle_session_browser_queue_layout(EntangleSessionBrowser *browser)
1835 EntangleSessionBrowserPrivate *priv = browser->priv;
1837 if (priv->layout_idle_id != 0)
1840 priv->layout_idle_id =
1841 gdk_threads_add_idle_full(ENTANGLE_SESSION_BROWSER_PRIORITY_LAYOUT,
1842 layout_callback, browser, NULL);
1847 entangle_session_browser_set_hadjustment_values(EntangleSessionBrowser *browser)
1849 EntangleSessionBrowserPrivate *priv = browser->priv;
1850 GtkAllocation allocation;
1851 GtkAdjustment *adj = priv->hadjustment;
1853 gtk_widget_get_allocation(GTK_WIDGET(browser), &allocation);
1855 gtk_adjustment_configure(adj,
1856 gtk_adjustment_get_value(adj),
1858 MAX(allocation.width, priv->width),
1859 allocation.width * 0.1,
1860 allocation.width * 0.9,
1866 entangle_session_browser_set_vadjustment_values(EntangleSessionBrowser *browser)
1868 EntangleSessionBrowserPrivate *priv = browser->priv;
1869 GtkAllocation allocation;
1870 GtkAdjustment *adj = priv->vadjustment;
1872 gtk_widget_get_allocation(GTK_WIDGET(browser), &allocation);
1874 gtk_adjustment_configure(adj,
1875 gtk_adjustment_get_value(adj),
1877 MAX(allocation.height, priv->height),
1878 allocation.height * 0.1,
1879 allocation.height * 0.9,
1885 entangle_session_browser_set_hadjustment(EntangleSessionBrowser *browser,
1886 GtkAdjustment *adjustment)
1888 EntangleSessionBrowserPrivate *priv = browser->priv;
1890 if (adjustment && priv->hadjustment == adjustment)
1893 if (priv->hadjustment != NULL) {
1894 g_signal_handlers_disconnect_matched(priv->hadjustment,
1895 G_SIGNAL_MATCH_DATA,
1896 0, 0, NULL, NULL, browser);
1897 g_object_unref(priv->hadjustment);
1901 adjustment = gtk_adjustment_new(0.0, 0.0, 0.0,
1904 g_signal_connect(adjustment, "value-changed",
1905 G_CALLBACK(entangle_session_browser_adjustment_changed), browser);
1906 priv->hadjustment = g_object_ref_sink(adjustment);
1907 entangle_session_browser_set_hadjustment_values(browser);
1909 g_object_notify(G_OBJECT(browser), "hadjustment");
1914 entangle_session_browser_set_vadjustment(EntangleSessionBrowser *browser,
1915 GtkAdjustment *adjustment)
1917 EntangleSessionBrowserPrivate *priv = browser->priv;
1919 if (adjustment && priv->vadjustment == adjustment)
1922 if (priv->vadjustment != NULL) {
1923 g_signal_handlers_disconnect_matched(priv->vadjustment,
1924 G_SIGNAL_MATCH_DATA,
1925 0, 0, NULL, NULL, browser);
1926 g_object_unref (priv->vadjustment);
1930 adjustment = gtk_adjustment_new(0.0, 0.0, 0.0,
1933 g_signal_connect(adjustment, "value-changed",
1934 G_CALLBACK(entangle_session_browser_adjustment_changed), browser);
1935 priv->vadjustment = g_object_ref_sink (adjustment);
1936 entangle_session_browser_set_vadjustment_values (browser);
1938 g_object_notify(G_OBJECT(browser), "vadjustment");
1943 entangle_session_browser_adjustment_changed(GtkAdjustment *adjustment G_GNUC_UNUSED,
1944 EntangleSessionBrowser *browser)
1946 EntangleSessionBrowserPrivate *priv = browser->priv;
1948 if (gtk_widget_get_realized(GTK_WIDGET(browser))) {
1949 gdk_window_move(priv->bin_window,
1950 - gtk_adjustment_get_value(priv->hadjustment),
1951 - gtk_adjustment_get_value(priv->vadjustment));
1953 entangle_session_browser_process_updates(browser);
1962 * indent-tabs-mode: nil