summaryrefslogtreecommitdiff
path: root/src/caja-zoom-control.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/caja-zoom-control.c')
-rw-r--r--src/caja-zoom-control.c996
1 files changed, 996 insertions, 0 deletions
diff --git a/src/caja-zoom-control.c b/src/caja-zoom-control.c
new file mode 100644
index 00000000..a11ca9e9
--- /dev/null
+++ b/src/caja-zoom-control.c
@@ -0,0 +1,996 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/*
+ * Caja
+ *
+ * Copyright (C) 2000 Eazel, Inc.
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Caja is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Caja is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Andy Hertzfeld <[email protected]>
+ * Alexander Larsson <[email protected]>
+ *
+ * This is the zoom control for the location bar
+ *
+ */
+
+#include <config.h>
+#include "caja-zoom-control.h"
+
+#include <atk/atkaction.h>
+#include <glib/gi18n.h>
+#include <eel/eel-accessibility.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-graphic-effects.h>
+#include <eel/eel-gtk-extensions.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <libcaja-private/caja-file-utilities.h>
+#include <libcaja-private/caja-global-preferences.h>
+#include <libcaja-private/caja-marshal.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+enum
+{
+ ZOOM_IN,
+ ZOOM_OUT,
+ ZOOM_TO_LEVEL,
+ ZOOM_TO_DEFAULT,
+ CHANGE_VALUE,
+ LAST_SIGNAL
+};
+
+struct CajaZoomControlDetails
+{
+ GtkWidget *zoom_in;
+ GtkWidget *zoom_out;
+ GtkWidget *zoom_label;
+ GtkWidget *zoom_button;
+
+ CajaZoomLevel zoom_level;
+ CajaZoomLevel min_zoom_level;
+ CajaZoomLevel max_zoom_level;
+ gboolean has_min_zoom_level;
+ gboolean has_max_zoom_level;
+ GList *preferred_zoom_levels;
+
+ gboolean marking_menu_items;
+};
+
+
+static guint signals[LAST_SIGNAL];
+
+static gpointer accessible_parent_class;
+
+static const char * const caja_zoom_control_accessible_action_names[] =
+{
+ N_("Zoom In"),
+ N_("Zoom Out"),
+ N_("Zoom to Default"),
+};
+
+static const int caja_zoom_control_accessible_action_signals[] =
+{
+ ZOOM_IN,
+ ZOOM_OUT,
+ ZOOM_TO_DEFAULT,
+};
+
+static const char * const caja_zoom_control_accessible_action_descriptions[] =
+{
+ N_("Increase the view size"),
+ N_("Decrease the view size"),
+ N_("Use the normal view size")
+};
+
+static GtkMenu *create_zoom_menu (CajaZoomControl *zoom_control);
+
+static GType caja_zoom_control_accessible_get_type (void);
+
+/* button assignments */
+#define CONTEXTUAL_MENU_BUTTON 3
+
+#define NUM_ACTIONS ((int)G_N_ELEMENTS (caja_zoom_control_accessible_action_names))
+
+G_DEFINE_TYPE (CajaZoomControl, caja_zoom_control, GTK_TYPE_HBOX);
+
+static void
+caja_zoom_control_finalize (GObject *object)
+{
+ g_list_free (CAJA_ZOOM_CONTROL (object)->details->preferred_zoom_levels);
+
+ G_OBJECT_CLASS (caja_zoom_control_parent_class)->finalize (object);
+}
+
+static void
+zoom_button_clicked (GtkButton *button, CajaZoomControl *zoom_control)
+{
+ g_signal_emit (zoom_control, signals[ZOOM_TO_DEFAULT], 0);
+}
+
+static void
+zoom_popup_menu_show (GdkEventButton *event, CajaZoomControl *zoom_control)
+{
+ eel_pop_up_context_menu (create_zoom_menu (zoom_control),
+ EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+ EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+ event);
+}
+
+static void
+menu_position_under_widget (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ GtkWidget *widget;
+ GtkWidget *container;
+ GtkRequisition req;
+ GtkRequisition menu_req;
+ GdkRectangle monitor;
+ int monitor_num;
+ GdkScreen *screen;
+ GtkAllocation allocation;
+
+ widget = GTK_WIDGET (user_data);
+ g_assert (GTK_IS_WIDGET (widget));
+
+ container = gtk_widget_get_ancestor (widget, GTK_TYPE_CONTAINER);
+ g_assert (container != NULL);
+
+ gtk_widget_size_request (widget, &req);
+ gtk_widget_size_request (GTK_WIDGET (menu), &menu_req);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+ monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
+ if (monitor_num < 0)
+ {
+ monitor_num = 0;
+ }
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
+ if (!gtk_widget_get_has_window (widget))
+ {
+ *x += allocation.x;
+ *y += allocation.y;
+ }
+
+ if (gtk_widget_get_direction (container) == GTK_TEXT_DIR_LTR)
+ {
+ *x += allocation.width - req.width;
+ }
+ else
+ {
+ *x += req.width - menu_req.width;
+ }
+
+ if ((*y + allocation.height + menu_req.height) <= monitor.y + monitor.height)
+ {
+ *y += allocation.height;
+ }
+ else if ((*y - menu_req.height) >= monitor.y)
+ {
+ *y -= menu_req.height;
+ }
+ else if (monitor.y + monitor.height - (*y + allocation.height) > *y)
+ {
+ *y += allocation.height;
+ }
+ else
+ {
+ *y -= menu_req.height;
+ }
+
+ *push_in = FALSE;
+}
+
+
+static void
+zoom_popup_menu (GtkWidget *widget, CajaZoomControl *zoom_control)
+{
+ GtkMenu *menu;
+
+ menu = create_zoom_menu (zoom_control);
+ gtk_menu_popup (menu, NULL, NULL,
+ menu_position_under_widget, widget,
+ 0, gtk_get_current_event_time ());
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
+}
+
+/* handle button presses */
+static gboolean
+caja_zoom_control_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ CajaZoomControl *zoom_control)
+{
+ if (event->type != GDK_BUTTON_PRESS)
+ {
+ return FALSE;
+ }
+
+ /* check for the context menu button and show the menu */
+ if (event->button == CONTEXTUAL_MENU_BUTTON)
+ {
+ zoom_popup_menu_show (event, zoom_control);
+ return TRUE;
+ }
+ /* We don't change our state (to reflect the new zoom) here.
+ The zoomable will call back with the new level.
+ Actually, the callback goes to the viewframe containing the
+ zoomable which, in turn, emits zoom_level_changed,
+ which someone (e.g. caja_window) picks up and handles by
+ calling into is - caja_zoom_control_set_zoom_level.
+ */
+
+ return FALSE;
+}
+
+static void
+zoom_out_clicked (GtkButton *button,
+ CajaZoomControl *zoom_control)
+{
+ if (caja_zoom_control_can_zoom_out (zoom_control))
+ {
+ g_signal_emit (G_OBJECT (zoom_control), signals[ZOOM_OUT], 0);
+ }
+}
+
+static void
+zoom_in_clicked (GtkButton *button,
+ CajaZoomControl *zoom_control)
+{
+ if (caja_zoom_control_can_zoom_in (zoom_control))
+ {
+ g_signal_emit (G_OBJECT (zoom_control), signals[ZOOM_IN], 0);
+ }
+}
+
+static void
+set_label_size (CajaZoomControl *zoom_control)
+{
+ const char *text;
+ PangoLayout *layout;
+ int width;
+ int height;
+
+ text = gtk_label_get_text (GTK_LABEL (zoom_control->details->zoom_label));
+ layout = gtk_label_get_layout (GTK_LABEL (zoom_control->details->zoom_label));
+ pango_layout_set_text (layout, "100%", -1);
+ pango_layout_get_pixel_size (layout, &width, &height);
+ gtk_widget_set_size_request (zoom_control->details->zoom_label, width, height);
+ gtk_label_set_text (GTK_LABEL (zoom_control->details->zoom_label),
+ text);
+}
+
+static void
+label_style_set_callback (GtkWidget *label,
+ GtkStyle *style,
+ gpointer user_data)
+{
+ set_label_size (CAJA_ZOOM_CONTROL (user_data));
+}
+
+static void
+caja_zoom_control_init (CajaZoomControl *zoom_control)
+{
+ GtkWidget *image;
+ int i;
+
+ zoom_control->details = G_TYPE_INSTANCE_GET_PRIVATE (zoom_control, CAJA_TYPE_ZOOM_CONTROL, CajaZoomControlDetails);
+
+ zoom_control->details->zoom_level = CAJA_ZOOM_LEVEL_STANDARD;
+ zoom_control->details->min_zoom_level = CAJA_ZOOM_LEVEL_SMALLEST;
+ zoom_control->details->max_zoom_level = CAJA_ZOOM_LEVEL_LARGEST;
+ zoom_control->details->has_min_zoom_level = TRUE;
+ zoom_control->details->has_max_zoom_level = TRUE;
+
+ for (i = CAJA_ZOOM_LEVEL_LARGEST; i >= CAJA_ZOOM_LEVEL_SMALLEST; i--)
+ {
+ zoom_control->details->preferred_zoom_levels = g_list_prepend (
+ zoom_control->details->preferred_zoom_levels,
+ GINT_TO_POINTER (i));
+ }
+
+ image = gtk_image_new_from_stock (GTK_STOCK_ZOOM_OUT, GTK_ICON_SIZE_MENU);
+ zoom_control->details->zoom_out = gtk_button_new ();
+ gtk_button_set_focus_on_click (GTK_BUTTON (zoom_control->details->zoom_out), FALSE);
+ gtk_button_set_relief (GTK_BUTTON (zoom_control->details->zoom_out),
+ GTK_RELIEF_NONE);
+ gtk_widget_set_tooltip_text (zoom_control->details->zoom_out,
+ _("Decrease the view size"));
+ g_signal_connect (G_OBJECT (zoom_control->details->zoom_out),
+ "clicked", G_CALLBACK (zoom_out_clicked),
+ zoom_control);
+ gtk_container_add (GTK_CONTAINER (zoom_control->details->zoom_out), image);
+ gtk_box_pack_start (GTK_BOX (zoom_control),
+ zoom_control->details->zoom_out, FALSE, FALSE, 0);
+
+ zoom_control->details->zoom_button = gtk_button_new ();
+ gtk_button_set_focus_on_click (GTK_BUTTON (zoom_control->details->zoom_button), FALSE);
+ gtk_button_set_relief (GTK_BUTTON (zoom_control->details->zoom_button),
+ GTK_RELIEF_NONE);
+ gtk_widget_set_tooltip_text (zoom_control->details->zoom_button,
+ _("Use the normal view size"));
+
+ gtk_widget_add_events (GTK_WIDGET (zoom_control->details->zoom_button),
+ GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK);
+
+ g_signal_connect (G_OBJECT (zoom_control->details->zoom_button),
+ "button-press-event",
+ G_CALLBACK (caja_zoom_control_button_press_event),
+ zoom_control);
+
+ g_signal_connect (G_OBJECT (zoom_control->details->zoom_button),
+ "clicked", G_CALLBACK (zoom_button_clicked),
+ zoom_control);
+
+ g_signal_connect (G_OBJECT (zoom_control->details->zoom_button),
+ "popup-menu", G_CALLBACK (zoom_popup_menu),
+ zoom_control);
+
+ zoom_control->details->zoom_label = gtk_label_new ("100%");
+ g_signal_connect (zoom_control->details->zoom_label,
+ "style_set",
+ G_CALLBACK (label_style_set_callback),
+ zoom_control);
+ set_label_size (zoom_control);
+
+ gtk_container_add (GTK_CONTAINER (zoom_control->details->zoom_button), zoom_control->details->zoom_label);
+
+ gtk_box_pack_start (GTK_BOX (zoom_control),
+ zoom_control->details->zoom_button, TRUE, TRUE, 0);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_ZOOM_IN, GTK_ICON_SIZE_MENU);
+ zoom_control->details->zoom_in = gtk_button_new ();
+ gtk_button_set_focus_on_click (GTK_BUTTON (zoom_control->details->zoom_in), FALSE);
+ gtk_button_set_relief (GTK_BUTTON (zoom_control->details->zoom_in),
+ GTK_RELIEF_NONE);
+ gtk_widget_set_tooltip_text (zoom_control->details->zoom_in,
+ _("Increase the view size"));
+ g_signal_connect (G_OBJECT (zoom_control->details->zoom_in),
+ "clicked", G_CALLBACK (zoom_in_clicked),
+ zoom_control);
+
+ gtk_container_add (GTK_CONTAINER (zoom_control->details->zoom_in), image);
+ gtk_box_pack_start (GTK_BOX (zoom_control),
+ zoom_control->details->zoom_in, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (zoom_control->details->zoom_out);
+ gtk_widget_show_all (zoom_control->details->zoom_button);
+ gtk_widget_show_all (zoom_control->details->zoom_in);
+}
+
+/* Allocate a new zoom control */
+GtkWidget *
+caja_zoom_control_new (void)
+{
+ return gtk_widget_new (caja_zoom_control_get_type (), NULL);
+}
+
+static void
+caja_zoom_control_redraw (CajaZoomControl *zoom_control)
+{
+ int percent;
+ char *num_str;
+
+ gtk_widget_set_sensitive (zoom_control->details->zoom_in,
+ caja_zoom_control_can_zoom_in (zoom_control));
+ gtk_widget_set_sensitive (zoom_control->details->zoom_out,
+ caja_zoom_control_can_zoom_out (zoom_control));
+
+ percent = floor ((100.0 * caja_get_relative_icon_size_for_zoom_level (zoom_control->details->zoom_level)) + .2);
+ num_str = g_strdup_printf ("%d%%", percent);
+ gtk_label_set_text (GTK_LABEL (zoom_control->details->zoom_label), num_str);
+ g_free (num_str);
+}
+
+/* routines to create and handle the zoom menu */
+
+static void
+zoom_menu_callback (GtkMenuItem *item, gpointer callback_data)
+{
+ CajaZoomLevel zoom_level;
+ CajaZoomControl *zoom_control;
+ gboolean can_zoom;
+
+ zoom_control = CAJA_ZOOM_CONTROL (callback_data);
+
+ /* Don't do anything if we're just setting the toggle state of menu items. */
+ if (zoom_control->details->marking_menu_items)
+ {
+ return;
+ }
+
+ /* Don't send the signal if the menuitem was toggled off */
+ if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)))
+ {
+ return;
+ }
+
+ zoom_level = (CajaZoomLevel) GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "zoom_level"));
+
+ /* Assume we can zoom and then check whether we're right. */
+ can_zoom = TRUE;
+ if (zoom_control->details->has_min_zoom_level &&
+ zoom_level < zoom_control->details->min_zoom_level)
+ can_zoom = FALSE; /* no, we're below the minimum zoom level. */
+ if (zoom_control->details->has_max_zoom_level &&
+ zoom_level > zoom_control->details->max_zoom_level)
+ can_zoom = FALSE; /* no, we're beyond the upper zoom level. */
+
+ /* if we can zoom */
+ if (can_zoom)
+ {
+ g_signal_emit (zoom_control, signals[ZOOM_TO_LEVEL], 0, zoom_level);
+ }
+}
+
+static GtkRadioMenuItem *
+create_zoom_menu_item (CajaZoomControl *zoom_control, GtkMenu *menu,
+ CajaZoomLevel zoom_level,
+ GtkRadioMenuItem *previous_radio_item)
+{
+ GtkWidget *menu_item;
+ char *item_text;
+ GSList *radio_item_group;
+ int percent;
+
+ /* Set flag so that callback isn't activated when set_active called
+ * to set toggle state of other radio items.
+ */
+ zoom_control->details->marking_menu_items = TRUE;
+
+ percent = floor ((100.0 * caja_get_relative_icon_size_for_zoom_level (zoom_level)) + .5);
+ item_text = g_strdup_printf ("%d%%", percent);
+
+ radio_item_group = previous_radio_item == NULL
+ ? NULL
+ : gtk_radio_menu_item_get_group (previous_radio_item);
+ menu_item = gtk_radio_menu_item_new_with_label (radio_item_group, item_text);
+ g_free (item_text);
+
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
+ zoom_level == zoom_control->details->zoom_level);
+
+ g_object_set_data (G_OBJECT (menu_item), "zoom_level", GINT_TO_POINTER (zoom_level));
+ g_signal_connect_object (menu_item, "activate",
+ G_CALLBACK (zoom_menu_callback), zoom_control, 0);
+
+ gtk_widget_show (menu_item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+
+ zoom_control->details->marking_menu_items = FALSE;
+
+ return GTK_RADIO_MENU_ITEM (menu_item);
+}
+
+static GtkMenu *
+create_zoom_menu (CajaZoomControl *zoom_control)
+{
+ GtkMenu *menu;
+ GtkRadioMenuItem *previous_item;
+ GList *node;
+
+ menu = GTK_MENU (gtk_menu_new ());
+
+ previous_item = NULL;
+ for (node = zoom_control->details->preferred_zoom_levels; node != NULL; node = node->next)
+ {
+ previous_item = create_zoom_menu_item
+ (zoom_control, menu, GPOINTER_TO_INT (node->data), previous_item);
+ }
+
+ return menu;
+}
+
+static AtkObject *
+caja_zoom_control_get_accessible (GtkWidget *widget)
+{
+ AtkObject *accessible;
+
+ accessible = eel_accessibility_get_atk_object (widget);
+
+ if (accessible)
+ {
+ return accessible;
+ }
+
+ accessible = g_object_new
+ (caja_zoom_control_accessible_get_type (), NULL);
+
+ return eel_accessibility_set_atk_object_return (widget, accessible);
+}
+
+static void
+caja_zoom_control_change_value (CajaZoomControl *zoom_control,
+ GtkScrollType scroll)
+{
+ switch (scroll)
+ {
+ case GTK_SCROLL_STEP_DOWN :
+ if (caja_zoom_control_can_zoom_out (zoom_control))
+ {
+ g_signal_emit (zoom_control, signals[ZOOM_OUT], 0);
+ }
+ break;
+ case GTK_SCROLL_STEP_UP :
+ if (caja_zoom_control_can_zoom_in (zoom_control))
+ {
+ g_signal_emit (zoom_control, signals[ZOOM_IN], 0);
+ }
+ break;
+ default :
+ g_warning ("Invalid scroll type %d for CajaZoomControl:change_value", scroll);
+ }
+}
+
+void
+caja_zoom_control_set_zoom_level (CajaZoomControl *zoom_control,
+ CajaZoomLevel zoom_level)
+{
+ zoom_control->details->zoom_level = zoom_level;
+ caja_zoom_control_redraw (zoom_control);
+}
+
+void
+caja_zoom_control_set_parameters (CajaZoomControl *zoom_control,
+ CajaZoomLevel min_zoom_level,
+ CajaZoomLevel max_zoom_level,
+ gboolean has_min_zoom_level,
+ gboolean has_max_zoom_level,
+ GList *zoom_levels)
+{
+ g_return_if_fail (CAJA_IS_ZOOM_CONTROL (zoom_control));
+
+ zoom_control->details->min_zoom_level = min_zoom_level;
+ zoom_control->details->max_zoom_level = max_zoom_level;
+ zoom_control->details->has_min_zoom_level = has_min_zoom_level;
+ zoom_control->details->has_max_zoom_level = has_max_zoom_level;
+
+ g_list_free (zoom_control->details->preferred_zoom_levels);
+ zoom_control->details->preferred_zoom_levels = zoom_levels;
+
+ caja_zoom_control_redraw (zoom_control);
+}
+
+CajaZoomLevel
+caja_zoom_control_get_zoom_level (CajaZoomControl *zoom_control)
+{
+ return zoom_control->details->zoom_level;
+}
+
+CajaZoomLevel
+caja_zoom_control_get_min_zoom_level (CajaZoomControl *zoom_control)
+{
+ return zoom_control->details->min_zoom_level;
+}
+
+CajaZoomLevel
+caja_zoom_control_get_max_zoom_level (CajaZoomControl *zoom_control)
+{
+ return zoom_control->details->max_zoom_level;
+}
+
+gboolean
+caja_zoom_control_has_min_zoom_level (CajaZoomControl *zoom_control)
+{
+ return zoom_control->details->has_min_zoom_level;
+}
+
+gboolean
+caja_zoom_control_has_max_zoom_level (CajaZoomControl *zoom_control)
+{
+ return zoom_control->details->has_max_zoom_level;
+}
+
+gboolean
+caja_zoom_control_can_zoom_in (CajaZoomControl *zoom_control)
+{
+ return !zoom_control->details->has_max_zoom_level ||
+ (zoom_control->details->zoom_level
+ < zoom_control->details->max_zoom_level);
+}
+
+gboolean
+caja_zoom_control_can_zoom_out (CajaZoomControl *zoom_control)
+{
+ return !zoom_control->details->has_min_zoom_level ||
+ (zoom_control->details->zoom_level
+ > zoom_control->details->min_zoom_level);
+}
+
+static gboolean
+caja_zoom_control_scroll_event (GtkWidget *widget, GdkEventScroll *event)
+{
+ CajaZoomControl *zoom_control;
+
+ zoom_control = CAJA_ZOOM_CONTROL (widget);
+
+ if (event->type != GDK_SCROLL)
+ {
+ return FALSE;
+ }
+
+ if (event->direction == GDK_SCROLL_DOWN &&
+ caja_zoom_control_can_zoom_out (zoom_control))
+ {
+ g_signal_emit (widget, signals[ZOOM_OUT], 0);
+ }
+ else if (event->direction == GDK_SCROLL_UP &&
+ caja_zoom_control_can_zoom_in (zoom_control))
+ {
+ g_signal_emit (widget, signals[ZOOM_IN], 0);
+ }
+
+ /* We don't change our state (to reflect the new zoom) here. The zoomable will
+ * call back with the new level. Actually, the callback goes to the view-frame
+ * containing the zoomable which, in turn, emits zoom_level_changed, which
+ * someone (e.g. caja_window) picks up and handles by calling into us -
+ * caja_zoom_control_set_zoom_level.
+ */
+ return TRUE;
+}
+
+
+
+static void
+caja_zoom_control_class_init (CajaZoomControlClass *class)
+{
+ GtkWidgetClass *widget_class;
+ GtkBindingSet *binding_set;
+
+ G_OBJECT_CLASS (class)->finalize = caja_zoom_control_finalize;
+
+ widget_class = GTK_WIDGET_CLASS (class);
+
+ widget_class->get_accessible = caja_zoom_control_get_accessible;
+ widget_class->scroll_event = caja_zoom_control_scroll_event;
+
+ class->change_value = caja_zoom_control_change_value;
+
+ signals[ZOOM_IN] =
+ g_signal_new ("zoom_in",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CajaZoomControlClass,
+ zoom_in),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[ZOOM_OUT] =
+ g_signal_new ("zoom_out",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CajaZoomControlClass,
+ zoom_out),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[ZOOM_TO_LEVEL] =
+ g_signal_new ("zoom_to_level",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CajaZoomControlClass,
+ zoom_to_level),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ signals[ZOOM_TO_DEFAULT] =
+ g_signal_new ("zoom_to_default",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (CajaZoomControlClass,
+ zoom_to_default),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[CHANGE_VALUE] =
+ g_signal_new ("change_value",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (CajaZoomControlClass,
+ change_value),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1, GTK_TYPE_SCROLL_TYPE);
+
+ binding_set = gtk_binding_set_by_class (class);
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KP_Subtract, 0,
+ "change_value",
+ 1, GTK_TYPE_SCROLL_TYPE,
+ GTK_SCROLL_STEP_DOWN);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_minus, 0,
+ "change_value",
+ 1, GTK_TYPE_SCROLL_TYPE,
+ GTK_SCROLL_STEP_DOWN);
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KP_Equal, 0,
+ "zoom_to_default",
+ 0);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KP_Equal, 0,
+ "zoom_to_default",
+ 0);
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KP_Add, 0,
+ "change_value",
+ 1, GTK_TYPE_SCROLL_TYPE,
+ GTK_SCROLL_STEP_UP);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_plus, 0,
+ "change_value",
+ 1, GTK_TYPE_SCROLL_TYPE,
+ GTK_SCROLL_STEP_UP);
+
+ g_type_class_add_private (G_OBJECT_CLASS (class), sizeof (CajaZoomControlDetails));
+}
+
+static gboolean
+caja_zoom_control_accessible_do_action (AtkAction *accessible, int i)
+{
+ GtkWidget *widget;
+
+ g_assert (i >= 0 && i < NUM_ACTIONS);
+
+ widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+ if (!widget)
+ {
+ return FALSE;
+ }
+
+ g_signal_emit (widget,
+ signals[caja_zoom_control_accessible_action_signals [i]],
+ 0);
+
+ return TRUE;
+}
+
+static int
+caja_zoom_control_accessible_get_n_actions (AtkAction *accessible)
+{
+
+ return NUM_ACTIONS;
+}
+
+static const char* caja_zoom_control_accessible_action_get_description(AtkAction* accessible, int i)
+{
+ g_assert(i >= 0 && i < NUM_ACTIONS);
+
+ return _(caja_zoom_control_accessible_action_descriptions[i]);
+}
+
+static const char* caja_zoom_control_accessible_action_get_name(AtkAction* accessible, int i)
+{
+ g_assert (i >= 0 && i < NUM_ACTIONS);
+
+ return _(caja_zoom_control_accessible_action_names[i]);
+}
+
+static void caja_zoom_control_accessible_action_interface_init(AtkActionIface* iface)
+{
+ iface->do_action = caja_zoom_control_accessible_do_action;
+ iface->get_n_actions = caja_zoom_control_accessible_get_n_actions;
+ iface->get_description = caja_zoom_control_accessible_action_get_description;
+ iface->get_name = caja_zoom_control_accessible_action_get_name;
+}
+
+static void
+caja_zoom_control_accessible_get_current_value (AtkValue *accessible,
+ GValue *value)
+{
+ CajaZoomControl *control;
+
+ g_value_init (value, G_TYPE_INT);
+
+ control = CAJA_ZOOM_CONTROL (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
+ if (!control)
+ {
+ g_value_set_int (value, CAJA_ZOOM_LEVEL_STANDARD);
+ return;
+ }
+
+ g_value_set_int (value, control->details->zoom_level);
+}
+
+static void
+caja_zoom_control_accessible_get_maximum_value (AtkValue *accessible,
+ GValue *value)
+{
+ CajaZoomControl *control;
+
+ g_value_init (value, G_TYPE_INT);
+
+ control = CAJA_ZOOM_CONTROL (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
+ if (!control)
+ {
+ g_value_set_int (value, CAJA_ZOOM_LEVEL_STANDARD);
+ return;
+ }
+
+ g_value_set_int (value, control->details->max_zoom_level);
+}
+
+static void
+caja_zoom_control_accessible_get_minimum_value (AtkValue *accessible,
+ GValue *value)
+{
+ CajaZoomControl *control;
+
+ g_value_init (value, G_TYPE_INT);
+
+ control = CAJA_ZOOM_CONTROL (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
+ if (!control)
+ {
+ g_value_set_int (value, CAJA_ZOOM_LEVEL_STANDARD);
+ return;
+ }
+
+ g_value_set_int (value, control->details->min_zoom_level);
+}
+
+static CajaZoomLevel
+nearest_preferred (CajaZoomControl *zoom_control, CajaZoomLevel value)
+{
+ CajaZoomLevel last_value;
+ CajaZoomLevel current_value;
+ GList *l;
+
+ if (!zoom_control->details->preferred_zoom_levels)
+ {
+ return value;
+ }
+
+ last_value = GPOINTER_TO_INT (zoom_control->details->preferred_zoom_levels->data);
+ current_value = last_value;
+
+ for (l = zoom_control->details->preferred_zoom_levels; l != NULL; l = l->next)
+ {
+ current_value = GPOINTER_TO_INT (l->data);
+
+ if (current_value > value)
+ {
+ float center = (last_value + current_value) / 2;
+
+ return (value < center) ? last_value : current_value;
+
+ }
+
+ last_value = current_value;
+ }
+
+ return current_value;
+}
+
+static gboolean
+caja_zoom_control_accessible_set_current_value (AtkValue *accessible,
+ const GValue *value)
+{
+ CajaZoomControl *control;
+ CajaZoomLevel zoom;
+
+ control = CAJA_ZOOM_CONTROL (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
+ if (!control)
+ {
+ return FALSE;
+ }
+
+ zoom = nearest_preferred (control, g_value_get_int (value));
+
+ g_signal_emit (control, signals[ZOOM_TO_LEVEL], 0, zoom);
+
+ return TRUE;
+}
+
+static void
+caja_zoom_control_accessible_value_interface_init (AtkValueIface *iface)
+{
+ iface->get_current_value = caja_zoom_control_accessible_get_current_value;
+ iface->get_maximum_value = caja_zoom_control_accessible_get_maximum_value;
+ iface->get_minimum_value = caja_zoom_control_accessible_get_minimum_value;
+ iface->set_current_value = caja_zoom_control_accessible_set_current_value;
+}
+
+static const char* caja_zoom_control_accessible_get_name(AtkObject* accessible)
+{
+ return _("Zoom");
+}
+
+static const char* caja_zoom_control_accessible_get_description(AtkObject* accessible)
+{
+ return _("Set the zoom level of the current view");
+}
+
+static void
+caja_zoom_control_accessible_initialize (AtkObject *accessible,
+ gpointer data)
+{
+ if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL)
+ {
+ ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data);
+ }
+ atk_object_set_role (accessible, ATK_ROLE_DIAL);
+}
+
+static void
+caja_zoom_control_accessible_class_init (AtkObjectClass *klass)
+{
+ accessible_parent_class = g_type_class_peek_parent (klass);
+
+ klass->get_name = caja_zoom_control_accessible_get_name;
+ klass->get_description = caja_zoom_control_accessible_get_description;
+ klass->initialize = caja_zoom_control_accessible_initialize;
+}
+
+static GType
+caja_zoom_control_accessible_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ static GInterfaceInfo atk_action_info =
+ {
+ (GInterfaceInitFunc)caja_zoom_control_accessible_action_interface_init,
+ (GInterfaceFinalizeFunc)NULL,
+ NULL
+ };
+
+ static GInterfaceInfo atk_value_info =
+ {
+ (GInterfaceInitFunc)caja_zoom_control_accessible_value_interface_init,
+ (GInterfaceFinalizeFunc)NULL,
+ NULL
+ };
+
+ type = eel_accessibility_create_derived_type
+ ("CajaZoomControlAccessible",
+ GTK_TYPE_HBOX,
+ caja_zoom_control_accessible_class_init);
+
+ g_type_add_interface_static (type, ATK_TYPE_ACTION,
+ &atk_action_info);
+ g_type_add_interface_static (type, ATK_TYPE_VALUE,
+ &atk_value_info);
+ }
+
+ return type;
+}
+
+void
+caja_zoom_control_set_active_appearance (CajaZoomControl *zoom_control, gboolean is_active)
+{
+ gtk_widget_set_sensitive (gtk_bin_get_child (GTK_BIN (zoom_control->details->zoom_in)), is_active);
+ gtk_widget_set_sensitive (gtk_bin_get_child (GTK_BIN (zoom_control->details->zoom_out)), is_active);
+ gtk_widget_set_sensitive (zoom_control->details->zoom_label, is_active);
+}