2008-04-30 Cosimo Cecchi <cosimoc@gnome.org>
[nautilus.git] / src / nautilus-zoom-control.c
blob5972706240febde260e0ce329b9b479eafd790c6
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /*
4 * Nautilus
6 * Copyright (C) 2000 Eazel, Inc.
7 * Copyright (C) 2004 Red Hat, Inc.
9 * Nautilus is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * Nautilus is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Author: Andy Hertzfeld <andy@eazel.com>
24 * Alexander Larsson <alexl@redhat.com>
26 * This is the zoom control for the location bar
30 #include <config.h>
31 #include "nautilus-zoom-control.h"
33 #include <atk/atkaction.h>
34 #include <glib/gi18n.h>
35 #include <eel/eel-accessibility.h>
36 #include <eel/eel-glib-extensions.h>
37 #include <eel/eel-graphic-effects.h>
38 #include <eel/eel-gtk-extensions.h>
39 #include <gtk/gtkaccessible.h>
40 #include <gtk/gtkmenu.h>
41 #include <gtk/gtkradiomenuitem.h>
42 #include <gtk/gtkstock.h>
43 #include <gtk/gtkbindings.h>
44 #include <gdk/gdkkeysyms.h>
45 #include <libgnome/gnome-macros.h>
46 #include <libnautilus-private/nautilus-file-utilities.h>
47 #include <libnautilus-private/nautilus-global-preferences.h>
48 #include <libnautilus-private/nautilus-marshal.h>
49 #include <math.h>
50 #include <stdlib.h>
51 #include <string.h>
53 enum {
54 ZOOM_IN,
55 ZOOM_OUT,
56 ZOOM_TO_LEVEL,
57 ZOOM_TO_DEFAULT,
58 CHANGE_VALUE,
59 LAST_SIGNAL
62 struct NautilusZoomControlDetails {
63 GtkWidget *zoom_in;
64 GtkWidget *zoom_out;
65 GtkWidget *zoom_label;
66 GtkWidget *zoom_button;
68 NautilusZoomLevel zoom_level;
69 NautilusZoomLevel min_zoom_level;
70 NautilusZoomLevel max_zoom_level;
71 gboolean has_min_zoom_level;
72 gboolean has_max_zoom_level;
73 GList *preferred_zoom_levels;
75 gboolean marking_menu_items;
79 static guint signals[LAST_SIGNAL];
81 static gpointer accessible_parent_class;
83 static const char * const nautilus_zoom_control_accessible_action_names[] = {
84 N_("Zoom In"),
85 N_("Zoom Out"),
86 N_("Zoom to Default"),
89 static const int nautilus_zoom_control_accessible_action_signals[] = {
90 ZOOM_IN,
91 ZOOM_OUT,
92 ZOOM_TO_DEFAULT,
95 static const char * const nautilus_zoom_control_accessible_action_descriptions[] = {
96 N_("Show the contents in more detail"),
97 N_("Show the contents in less detail"),
98 N_("Show in the default detail level"),
101 static GtkMenu *create_zoom_menu (NautilusZoomControl *zoom_control);
103 static GType nautilus_zoom_control_accessible_get_type (void);
105 /* button assignments */
106 #define CONTEXTUAL_MENU_BUTTON 3
108 #define NUM_ACTIONS ((int)G_N_ELEMENTS (nautilus_zoom_control_accessible_action_names))
110 GNOME_CLASS_BOILERPLATE (NautilusZoomControl, nautilus_zoom_control,
111 GtkHBox, GTK_TYPE_HBOX)
113 static void
114 nautilus_zoom_control_finalize (GObject *object)
116 g_list_free (NAUTILUS_ZOOM_CONTROL (object)->details->preferred_zoom_levels);
118 G_OBJECT_CLASS (parent_class)->finalize (object);
121 static void
122 zoom_button_clicked (GtkButton *button, NautilusZoomControl *zoom_control)
124 g_signal_emit (zoom_control, signals[ZOOM_TO_DEFAULT], 0);
127 static void
128 zoom_popup_menu_show (GdkEventButton *event, NautilusZoomControl *zoom_control)
130 eel_pop_up_context_menu (create_zoom_menu (zoom_control),
131 EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
132 EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
133 event);
136 static void
137 menu_position_under_widget (GtkMenu *menu,
138 gint *x,
139 gint *y,
140 gboolean *push_in,
141 gpointer user_data)
143 GtkWidget *widget;
144 GtkWidget *container;
145 GtkRequisition req;
146 GtkRequisition menu_req;
147 GdkRectangle monitor;
148 int monitor_num;
149 GdkScreen *screen;
151 widget = GTK_WIDGET (user_data);
152 g_assert (GTK_IS_WIDGET (widget));
154 container = gtk_widget_get_ancestor (widget, GTK_TYPE_CONTAINER);
155 g_assert (container != NULL);
157 gtk_widget_size_request (widget, &req);
158 gtk_widget_size_request (GTK_WIDGET (menu), &menu_req);
160 screen = gtk_widget_get_screen (GTK_WIDGET (menu));
161 monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
162 if (monitor_num < 0) {
163 monitor_num = 0;
165 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
167 gdk_window_get_origin (widget->window, x, y);
168 if (GTK_WIDGET_NO_WINDOW (widget)) {
169 *x += widget->allocation.x;
170 *y += widget->allocation.y;
173 if (gtk_widget_get_direction (container) == GTK_TEXT_DIR_LTR) {
174 *x += widget->allocation.width - req.width;
175 } else {
176 *x += req.width - menu_req.width;
179 if ((*y + widget->allocation.height + menu_req.height) <= monitor.y + monitor.height) {
180 *y += widget->allocation.height;
181 } else if ((*y - menu_req.height) >= monitor.y) {
182 *y -= menu_req.height;
183 } else if (monitor.y + monitor.height - (*y + widget->allocation.height) > *y) {
184 *y += widget->allocation.height;
185 } else {
186 *y -= menu_req.height;
189 *push_in = FALSE;
193 static void
194 zoom_popup_menu (GtkWidget *widget, NautilusZoomControl *zoom_control)
196 GtkMenu *menu;
198 menu = create_zoom_menu (zoom_control);
199 gtk_menu_popup (menu, NULL, NULL,
200 menu_position_under_widget, widget,
201 0, gtk_get_current_event_time ());
202 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
205 /* handle button presses */
206 static gboolean
207 nautilus_zoom_control_button_press_event (GtkWidget *widget,
208 GdkEventButton *event,
209 NautilusZoomControl *zoom_control)
211 if (event->type != GDK_BUTTON_PRESS) {
212 return FALSE;
215 /* check for the context menu button and show the menu */
216 if (event->button == CONTEXTUAL_MENU_BUTTON) {
217 zoom_popup_menu_show (event, zoom_control);
218 return TRUE;
220 /* We don't change our state (to reflect the new zoom) here.
221 The zoomable will call back with the new level.
222 Actually, the callback goes to the viewframe containing the
223 zoomable which, in turn, emits zoom_level_changed,
224 which someone (e.g. nautilus_window) picks up and handles by
225 calling into is - nautilus_zoom_control_set_zoom_level.
228 return FALSE;
231 static void
232 zoom_out_clicked (GtkButton *button,
233 NautilusZoomControl *zoom_control)
235 if (nautilus_zoom_control_can_zoom_out (zoom_control)) {
236 g_signal_emit (G_OBJECT (zoom_control), signals[ZOOM_OUT], 0);
240 static void
241 zoom_in_clicked (GtkButton *button,
242 NautilusZoomControl *zoom_control)
244 if (nautilus_zoom_control_can_zoom_in (zoom_control)) {
245 g_signal_emit (G_OBJECT (zoom_control), signals[ZOOM_IN], 0);
249 static void
250 set_label_size (NautilusZoomControl *zoom_control)
252 const char *text;
253 PangoLayout *layout;
254 int width;
255 int height;
257 text = gtk_label_get_text (GTK_LABEL (zoom_control->details->zoom_label));
258 layout = gtk_label_get_layout (GTK_LABEL (zoom_control->details->zoom_label));
259 pango_layout_set_text (layout, "100%", -1);
260 pango_layout_get_pixel_size (layout, &width, &height);
261 gtk_widget_set_size_request (zoom_control->details->zoom_label, width, height);
262 gtk_label_set_text (GTK_LABEL (zoom_control->details->zoom_label),
263 text);
266 static void
267 label_style_set_callback (GtkWidget *label,
268 GtkStyle *style,
269 gpointer user_data)
271 set_label_size (NAUTILUS_ZOOM_CONTROL (user_data));
274 static void
275 nautilus_zoom_control_instance_init (NautilusZoomControl *zoom_control)
277 GtkWidget *image;
278 int i;
280 zoom_control->details = G_TYPE_INSTANCE_GET_PRIVATE (zoom_control, NAUTILUS_TYPE_ZOOM_CONTROL, NautilusZoomControlDetails);
282 zoom_control->details->zoom_level = NAUTILUS_ZOOM_LEVEL_STANDARD;
283 zoom_control->details->min_zoom_level = NAUTILUS_ZOOM_LEVEL_SMALLEST;
284 zoom_control->details->max_zoom_level = NAUTILUS_ZOOM_LEVEL_LARGEST;
285 zoom_control->details->has_min_zoom_level = TRUE;
286 zoom_control->details->has_max_zoom_level = TRUE;
288 for (i = NAUTILUS_ZOOM_LEVEL_LARGEST; i >= NAUTILUS_ZOOM_LEVEL_SMALLEST; i--) {
289 zoom_control->details->preferred_zoom_levels = g_list_prepend (
290 zoom_control->details->preferred_zoom_levels,
291 GINT_TO_POINTER (i));
294 image = gtk_image_new_from_stock (GTK_STOCK_ZOOM_OUT, GTK_ICON_SIZE_MENU);
295 zoom_control->details->zoom_out = gtk_button_new ();
296 gtk_button_set_focus_on_click (GTK_BUTTON (zoom_control->details->zoom_out), FALSE);
297 gtk_button_set_relief (GTK_BUTTON (zoom_control->details->zoom_out),
298 GTK_RELIEF_NONE);
299 g_signal_connect (G_OBJECT (zoom_control->details->zoom_out),
300 "clicked", G_CALLBACK (zoom_out_clicked),
301 zoom_control);
302 gtk_container_add (GTK_CONTAINER (zoom_control->details->zoom_out), image);
303 gtk_box_pack_start (GTK_BOX (zoom_control),
304 zoom_control->details->zoom_out, FALSE, FALSE, 0);
306 zoom_control->details->zoom_button = gtk_button_new ();
307 gtk_button_set_focus_on_click (GTK_BUTTON (zoom_control->details->zoom_button), FALSE);
308 gtk_button_set_relief (GTK_BUTTON (zoom_control->details->zoom_button),
309 GTK_RELIEF_NONE);
311 gtk_widget_add_events (GTK_WIDGET (zoom_control->details->zoom_button),
312 GDK_BUTTON_PRESS_MASK
313 | GDK_BUTTON_RELEASE_MASK
314 | GDK_POINTER_MOTION_MASK);
316 g_signal_connect (G_OBJECT (zoom_control->details->zoom_button),
317 "button-press-event",
318 G_CALLBACK (nautilus_zoom_control_button_press_event),
319 zoom_control);
321 g_signal_connect (G_OBJECT (zoom_control->details->zoom_button),
322 "clicked", G_CALLBACK (zoom_button_clicked),
323 zoom_control);
325 g_signal_connect (G_OBJECT (zoom_control->details->zoom_button),
326 "popup-menu", G_CALLBACK (zoom_popup_menu),
327 zoom_control);
329 zoom_control->details->zoom_label = gtk_label_new ("100%");
330 g_signal_connect (zoom_control->details->zoom_label,
331 "style_set",
332 G_CALLBACK (label_style_set_callback),
333 zoom_control);
334 set_label_size (zoom_control);
336 gtk_container_add (GTK_CONTAINER (zoom_control->details->zoom_button), zoom_control->details->zoom_label);
338 gtk_box_pack_start (GTK_BOX (zoom_control),
339 zoom_control->details->zoom_button, TRUE, TRUE, 0);
341 image = gtk_image_new_from_stock (GTK_STOCK_ZOOM_IN, GTK_ICON_SIZE_MENU);
342 zoom_control->details->zoom_in = gtk_button_new ();
343 gtk_button_set_focus_on_click (GTK_BUTTON (zoom_control->details->zoom_in), FALSE);
344 gtk_button_set_relief (GTK_BUTTON (zoom_control->details->zoom_in),
345 GTK_RELIEF_NONE);
346 g_signal_connect (G_OBJECT (zoom_control->details->zoom_in),
347 "clicked", G_CALLBACK (zoom_in_clicked),
348 zoom_control);
350 gtk_container_add (GTK_CONTAINER (zoom_control->details->zoom_in), image);
351 gtk_box_pack_start (GTK_BOX (zoom_control),
352 zoom_control->details->zoom_in, FALSE, FALSE, 0);
354 gtk_widget_show_all (zoom_control->details->zoom_out);
355 gtk_widget_show_all (zoom_control->details->zoom_button);
356 gtk_widget_show_all (zoom_control->details->zoom_in);
359 /* Allocate a new zoom control */
360 GtkWidget *
361 nautilus_zoom_control_new (void)
363 return gtk_widget_new (nautilus_zoom_control_get_type (), NULL);
366 static void
367 nautilus_zoom_control_redraw (NautilusZoomControl *zoom_control)
369 int percent;
370 char *num_str;
372 gtk_widget_set_sensitive (zoom_control->details->zoom_in,
373 nautilus_zoom_control_can_zoom_in (zoom_control));
374 gtk_widget_set_sensitive (zoom_control->details->zoom_out,
375 nautilus_zoom_control_can_zoom_out (zoom_control));
377 percent = floor ((100.0 * nautilus_get_relative_icon_size_for_zoom_level (zoom_control->details->zoom_level)) + .5);
378 num_str = g_strdup_printf ("%d%%", percent);
379 gtk_label_set_text (GTK_LABEL (zoom_control->details->zoom_label), num_str);
380 g_free (num_str);
383 /* routines to create and handle the zoom menu */
385 static void
386 zoom_menu_callback (GtkMenuItem *item, gpointer callback_data)
388 NautilusZoomLevel zoom_level;
389 NautilusZoomControl *zoom_control;
390 gboolean can_zoom;
392 zoom_control = NAUTILUS_ZOOM_CONTROL (callback_data);
394 /* Don't do anything if we're just setting the toggle state of menu items. */
395 if (zoom_control->details->marking_menu_items) {
396 return;
399 /* Don't send the signal if the menuitem was toggled off */
400 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item))) {
401 return;
404 zoom_level = (NautilusZoomLevel) GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "zoom_level"));
406 /* Assume we can zoom and then check whether we're right. */
407 can_zoom = TRUE;
408 if (zoom_control->details->has_min_zoom_level &&
409 zoom_level < zoom_control->details->min_zoom_level)
410 can_zoom = FALSE; /* no, we're below the minimum zoom level. */
411 if (zoom_control->details->has_max_zoom_level &&
412 zoom_level > zoom_control->details->max_zoom_level)
413 can_zoom = FALSE; /* no, we're beyond the upper zoom level. */
415 /* if we can zoom */
416 if (can_zoom) {
417 g_signal_emit (zoom_control, signals[ZOOM_TO_LEVEL], 0, zoom_level);
421 static GtkRadioMenuItem *
422 create_zoom_menu_item (NautilusZoomControl *zoom_control, GtkMenu *menu,
423 NautilusZoomLevel zoom_level,
424 GtkRadioMenuItem *previous_radio_item)
426 GtkWidget *menu_item;
427 char *item_text;
428 GSList *radio_item_group;
429 int percent;
431 /* Set flag so that callback isn't activated when set_active called
432 * to set toggle state of other radio items.
434 zoom_control->details->marking_menu_items = TRUE;
436 percent = floor ((100.0 * nautilus_get_relative_icon_size_for_zoom_level (zoom_level)) + .5);
437 item_text = g_strdup_printf ("%d%%", percent);
439 radio_item_group = previous_radio_item == NULL
440 ? NULL
441 : gtk_radio_menu_item_get_group (previous_radio_item);
442 menu_item = gtk_radio_menu_item_new_with_label (radio_item_group, item_text);
443 g_free (item_text);
445 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
446 zoom_level == zoom_control->details->zoom_level);
448 g_object_set_data (G_OBJECT (menu_item), "zoom_level", GINT_TO_POINTER (zoom_level));
449 g_signal_connect_object (menu_item, "activate",
450 G_CALLBACK (zoom_menu_callback), zoom_control, 0);
452 gtk_widget_show (menu_item);
453 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
455 zoom_control->details->marking_menu_items = FALSE;
457 return GTK_RADIO_MENU_ITEM (menu_item);
460 static GtkMenu *
461 create_zoom_menu (NautilusZoomControl *zoom_control)
463 GtkMenu *menu;
464 GtkRadioMenuItem *previous_item;
465 GList *node;
467 menu = GTK_MENU (gtk_menu_new ());
469 previous_item = NULL;
470 for (node = zoom_control->details->preferred_zoom_levels; node != NULL; node = node->next) {
471 previous_item = create_zoom_menu_item
472 (zoom_control, menu, GPOINTER_TO_INT (node->data), previous_item);
475 return menu;
478 static AtkObject *
479 nautilus_zoom_control_get_accessible (GtkWidget *widget)
481 AtkObject *accessible;
483 accessible = eel_accessibility_get_atk_object (widget);
485 if (accessible) {
486 return accessible;
489 accessible = g_object_new
490 (nautilus_zoom_control_accessible_get_type (), NULL);
492 return eel_accessibility_set_atk_object_return (widget, accessible);
495 static void
496 nautilus_zoom_control_change_value (NautilusZoomControl *zoom_control,
497 GtkScrollType scroll)
499 switch (scroll) {
500 case GTK_SCROLL_STEP_DOWN :
501 if (nautilus_zoom_control_can_zoom_out (zoom_control)) {
502 g_signal_emit (zoom_control, signals[ZOOM_OUT], 0);
504 break;
505 case GTK_SCROLL_STEP_UP :
506 if (nautilus_zoom_control_can_zoom_in (zoom_control)) {
507 g_signal_emit (zoom_control, signals[ZOOM_IN], 0);
509 break;
510 default :
511 g_warning ("Invalid scroll type %d for NautilusZoomControl:change_value", scroll);
515 void
516 nautilus_zoom_control_set_zoom_level (NautilusZoomControl *zoom_control,
517 NautilusZoomLevel zoom_level)
519 zoom_control->details->zoom_level = zoom_level;
520 nautilus_zoom_control_redraw (zoom_control);
523 void
524 nautilus_zoom_control_set_parameters (NautilusZoomControl *zoom_control,
525 NautilusZoomLevel min_zoom_level,
526 NautilusZoomLevel max_zoom_level,
527 gboolean has_min_zoom_level,
528 gboolean has_max_zoom_level,
529 GList *zoom_levels)
531 g_return_if_fail (NAUTILUS_IS_ZOOM_CONTROL (zoom_control));
533 zoom_control->details->min_zoom_level = min_zoom_level;
534 zoom_control->details->max_zoom_level = max_zoom_level;
535 zoom_control->details->has_min_zoom_level = has_min_zoom_level;
536 zoom_control->details->has_max_zoom_level = has_max_zoom_level;
538 g_list_free (zoom_control->details->preferred_zoom_levels);
539 zoom_control->details->preferred_zoom_levels = zoom_levels;
541 nautilus_zoom_control_redraw (zoom_control);
544 NautilusZoomLevel
545 nautilus_zoom_control_get_zoom_level (NautilusZoomControl *zoom_control)
547 return zoom_control->details->zoom_level;
550 NautilusZoomLevel
551 nautilus_zoom_control_get_min_zoom_level (NautilusZoomControl *zoom_control)
553 return zoom_control->details->min_zoom_level;
556 NautilusZoomLevel
557 nautilus_zoom_control_get_max_zoom_level (NautilusZoomControl *zoom_control)
559 return zoom_control->details->max_zoom_level;
562 gboolean
563 nautilus_zoom_control_has_min_zoom_level (NautilusZoomControl *zoom_control)
565 return zoom_control->details->has_min_zoom_level;
568 gboolean
569 nautilus_zoom_control_has_max_zoom_level (NautilusZoomControl *zoom_control)
571 return zoom_control->details->has_max_zoom_level;
574 gboolean
575 nautilus_zoom_control_can_zoom_in (NautilusZoomControl *zoom_control)
577 return !zoom_control->details->has_max_zoom_level ||
578 (zoom_control->details->zoom_level
579 < zoom_control->details->max_zoom_level);
582 gboolean
583 nautilus_zoom_control_can_zoom_out (NautilusZoomControl *zoom_control)
585 return !zoom_control->details->has_min_zoom_level ||
586 (zoom_control->details->zoom_level
587 > zoom_control->details->min_zoom_level);
590 static gboolean
591 nautilus_zoom_control_scroll_event (GtkWidget *widget, GdkEventScroll *event)
593 NautilusZoomControl *zoom_control;
595 zoom_control = NAUTILUS_ZOOM_CONTROL (widget);
597 if (event->type != GDK_SCROLL) {
598 return FALSE;
601 if (event->direction == GDK_SCROLL_DOWN &&
602 nautilus_zoom_control_can_zoom_out (zoom_control)) {
603 g_signal_emit (widget, signals[ZOOM_OUT], 0);
604 } else if (event->direction == GDK_SCROLL_UP &&
605 nautilus_zoom_control_can_zoom_in (zoom_control)) {
606 g_signal_emit (widget, signals[ZOOM_IN], 0);
609 /* We don't change our state (to reflect the new zoom) here. The zoomable will
610 * call back with the new level. Actually, the callback goes to the view-frame
611 * containing the zoomable which, in turn, emits zoom_level_changed, which
612 * someone (e.g. nautilus_window) picks up and handles by calling into us -
613 * nautilus_zoom_control_set_zoom_level.
615 return TRUE;
620 static void
621 nautilus_zoom_control_class_init (NautilusZoomControlClass *class)
623 GtkWidgetClass *widget_class;
624 GtkBindingSet *binding_set;
626 G_OBJECT_CLASS (class)->finalize = nautilus_zoom_control_finalize;
628 widget_class = GTK_WIDGET_CLASS (class);
630 widget_class->get_accessible = nautilus_zoom_control_get_accessible;
631 widget_class->scroll_event = nautilus_zoom_control_scroll_event;
633 class->change_value = nautilus_zoom_control_change_value;
635 signals[ZOOM_IN] =
636 g_signal_new ("zoom_in",
637 G_TYPE_FROM_CLASS (class),
638 G_SIGNAL_RUN_LAST,
639 G_STRUCT_OFFSET (NautilusZoomControlClass,
640 zoom_in),
641 NULL, NULL,
642 g_cclosure_marshal_VOID__VOID,
643 G_TYPE_NONE, 0);
645 signals[ZOOM_OUT] =
646 g_signal_new ("zoom_out",
647 G_TYPE_FROM_CLASS (class),
648 G_SIGNAL_RUN_LAST,
649 G_STRUCT_OFFSET (NautilusZoomControlClass,
650 zoom_out),
651 NULL, NULL,
652 g_cclosure_marshal_VOID__VOID,
653 G_TYPE_NONE, 0);
655 signals[ZOOM_TO_LEVEL] =
656 g_signal_new ("zoom_to_level",
657 G_TYPE_FROM_CLASS (class),
658 G_SIGNAL_RUN_LAST,
659 G_STRUCT_OFFSET (NautilusZoomControlClass,
660 zoom_to_level),
661 NULL, NULL,
662 g_cclosure_marshal_VOID__INT,
663 G_TYPE_NONE,
665 G_TYPE_INT);
667 signals[ZOOM_TO_DEFAULT] =
668 g_signal_new ("zoom_to_default",
669 G_TYPE_FROM_CLASS (class),
670 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
671 G_STRUCT_OFFSET (NautilusZoomControlClass,
672 zoom_to_default),
673 NULL, NULL,
674 g_cclosure_marshal_VOID__VOID,
675 G_TYPE_NONE, 0);
677 signals[CHANGE_VALUE] =
678 g_signal_new ("change_value",
679 G_TYPE_FROM_CLASS (class),
680 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
681 G_STRUCT_OFFSET (NautilusZoomControlClass,
682 change_value),
683 NULL, NULL,
684 g_cclosure_marshal_VOID__ENUM,
685 G_TYPE_NONE, 1, GTK_TYPE_SCROLL_TYPE);
687 binding_set = gtk_binding_set_by_class (class);
689 gtk_binding_entry_add_signal (binding_set,
690 GDK_KP_Subtract, 0,
691 "change_value",
692 1, GTK_TYPE_SCROLL_TYPE,
693 GTK_SCROLL_STEP_DOWN);
694 gtk_binding_entry_add_signal (binding_set,
695 GDK_minus, 0,
696 "change_value",
697 1, GTK_TYPE_SCROLL_TYPE,
698 GTK_SCROLL_STEP_DOWN);
700 gtk_binding_entry_add_signal (binding_set,
701 GDK_KP_Equal, 0,
702 "zoom_to_default",
704 gtk_binding_entry_add_signal (binding_set,
705 GDK_KP_Equal, 0,
706 "zoom_to_default",
709 gtk_binding_entry_add_signal (binding_set,
710 GDK_KP_Add, 0,
711 "change_value",
712 1, GTK_TYPE_SCROLL_TYPE,
713 GTK_SCROLL_STEP_UP);
714 gtk_binding_entry_add_signal (binding_set,
715 GDK_plus, 0,
716 "change_value",
717 1, GTK_TYPE_SCROLL_TYPE,
718 GTK_SCROLL_STEP_UP);
720 g_type_class_add_private (G_OBJECT_CLASS (class), sizeof (NautilusZoomControlDetails));
723 static gboolean
724 nautilus_zoom_control_accessible_do_action (AtkAction *accessible, int i)
726 GtkWidget *widget;
728 g_assert (i >= 0 && i < NUM_ACTIONS);
730 widget = GTK_ACCESSIBLE (accessible)->widget;
731 if (!widget) {
732 return FALSE;
735 g_signal_emit (widget,
736 signals[nautilus_zoom_control_accessible_action_signals [i]],
739 return TRUE;
742 static int
743 nautilus_zoom_control_accessible_get_n_actions (AtkAction *accessible)
746 return NUM_ACTIONS;
749 static G_CONST_RETURN char *
750 nautilus_zoom_control_accessible_action_get_description (AtkAction *accessible,
751 int i)
753 g_assert (i >= 0 && i < NUM_ACTIONS);
755 return _(nautilus_zoom_control_accessible_action_descriptions[i]);
758 static G_CONST_RETURN char *
759 nautilus_zoom_control_accessible_action_get_name (AtkAction *accessible,
760 int i)
762 g_assert (i >= 0 && i < NUM_ACTIONS);
764 return _(nautilus_zoom_control_accessible_action_names[i]);
767 static void
768 nautilus_zoom_control_accessible_action_interface_init (AtkActionIface *iface)
770 iface->do_action = nautilus_zoom_control_accessible_do_action;
771 iface->get_n_actions = nautilus_zoom_control_accessible_get_n_actions;
772 iface->get_description = nautilus_zoom_control_accessible_action_get_description;
773 iface->get_name = nautilus_zoom_control_accessible_action_get_name;
776 static void
777 nautilus_zoom_control_accessible_get_current_value (AtkValue *accessible,
778 GValue *value)
780 NautilusZoomControl *control;
782 g_value_init (value, G_TYPE_INT);
784 control = NAUTILUS_ZOOM_CONTROL (GTK_ACCESSIBLE (accessible)->widget);
785 if (!control) {
786 g_value_set_int (value, NAUTILUS_ZOOM_LEVEL_STANDARD);
787 return;
790 g_value_set_int (value, control->details->zoom_level);
793 static void
794 nautilus_zoom_control_accessible_get_maximum_value (AtkValue *accessible,
795 GValue *value)
797 NautilusZoomControl *control;
799 g_value_init (value, G_TYPE_INT);
801 control = NAUTILUS_ZOOM_CONTROL (GTK_ACCESSIBLE (accessible)->widget);
802 if (!control) {
803 g_value_set_int (value, NAUTILUS_ZOOM_LEVEL_STANDARD);
804 return;
807 g_value_set_int (value, control->details->max_zoom_level);
810 static void
811 nautilus_zoom_control_accessible_get_minimum_value (AtkValue *accessible,
812 GValue *value)
814 NautilusZoomControl *control;
816 g_value_init (value, G_TYPE_INT);
818 control = NAUTILUS_ZOOM_CONTROL (GTK_ACCESSIBLE (accessible)->widget);
819 if (!control) {
820 g_value_set_int (value, NAUTILUS_ZOOM_LEVEL_STANDARD);
821 return;
824 g_value_set_int (value, control->details->min_zoom_level);
827 static NautilusZoomLevel
828 nearest_preferred (NautilusZoomControl *zoom_control, NautilusZoomLevel value)
830 NautilusZoomLevel last_value;
831 NautilusZoomLevel current_value;
832 GList *l;
834 if (!zoom_control->details->preferred_zoom_levels) {
835 return value;
838 last_value = GPOINTER_TO_INT (zoom_control->details->preferred_zoom_levels->data);
839 current_value = last_value;
841 for (l = zoom_control->details->preferred_zoom_levels; l != NULL; l = l->next) {
842 current_value = GPOINTER_TO_INT (l->data);
844 if (current_value > value) {
845 float center = (last_value + current_value) / 2;
847 return (value < center) ? last_value : current_value;
851 last_value = current_value;
854 return current_value;
857 static gboolean
858 nautilus_zoom_control_accessible_set_current_value (AtkValue *accessible,
859 const GValue *value)
861 NautilusZoomControl *control;
862 NautilusZoomLevel zoom;
864 control = NAUTILUS_ZOOM_CONTROL (GTK_ACCESSIBLE (accessible)->widget);
865 if (!control) {
866 return FALSE;
869 zoom = nearest_preferred (control, g_value_get_int (value));
871 g_signal_emit (control, signals[ZOOM_TO_LEVEL], 0, zoom);
873 return TRUE;
876 static void
877 nautilus_zoom_control_accessible_value_interface_init (AtkValueIface *iface)
879 iface->get_current_value = nautilus_zoom_control_accessible_get_current_value;
880 iface->get_maximum_value = nautilus_zoom_control_accessible_get_maximum_value;
881 iface->get_minimum_value = nautilus_zoom_control_accessible_get_minimum_value;
882 iface->set_current_value = nautilus_zoom_control_accessible_set_current_value;
885 static G_CONST_RETURN char *
886 nautilus_zoom_control_accessible_get_name (AtkObject *accessible)
888 return _("Zoom");
891 static G_CONST_RETURN char *
892 nautilus_zoom_control_accessible_get_description (AtkObject *accessible)
894 return _("Set the zoom level of the current view");
897 static void
898 nautilus_zoom_control_accessible_initialize (AtkObject *accessible,
899 gpointer data)
901 if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL) {
902 ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data);
904 atk_object_set_role (accessible, ATK_ROLE_DIAL);
907 static void
908 nautilus_zoom_control_accessible_class_init (AtkObjectClass *klass)
910 accessible_parent_class = g_type_class_peek_parent (klass);
912 klass->get_name = nautilus_zoom_control_accessible_get_name;
913 klass->get_description = nautilus_zoom_control_accessible_get_description;
914 klass->initialize = nautilus_zoom_control_accessible_initialize;
917 static GType
918 nautilus_zoom_control_accessible_get_type (void)
920 static GType type = 0;
922 if (!type) {
923 static GInterfaceInfo atk_action_info = {
924 (GInterfaceInitFunc)nautilus_zoom_control_accessible_action_interface_init,
925 (GInterfaceFinalizeFunc)NULL,
926 NULL
929 static GInterfaceInfo atk_value_info = {
930 (GInterfaceInitFunc)nautilus_zoom_control_accessible_value_interface_init,
931 (GInterfaceFinalizeFunc)NULL,
932 NULL
935 type = eel_accessibility_create_derived_type
936 ("NautilusZoomControlAccessible",
937 GTK_TYPE_HBOX,
938 nautilus_zoom_control_accessible_class_init);
940 g_type_add_interface_static (type, ATK_TYPE_ACTION,
941 &atk_action_info);
942 g_type_add_interface_static (type, ATK_TYPE_VALUE,
943 &atk_value_info);
946 return type;