Initial import of ephy (rev# 7126) from svn
[ephy-soc.git] / src / .svn / text-base / ephy-tabs-menu.c.svn-base
blobde79472f0ca8cbc964b6e388cd2524c85facc79b
1 /*
2  *  Copyright © 2003  David Bordoley
3  *  Copyright © 2003-2004 Christian Persch
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  *  $Id$
20  */
22 #include "config.h"
24 #include "ephy-tabs-menu.h"
25 #include "ephy-notebook.h"
26 #include "ephy-marshal.h"
27 #include "ephy-shell.h"
28 #include "ephy-debug.h"
30 #include <glib/gi18n.h>
31 #include <gtk/gtklabel.h>
32 #include <gtk/gtkmenuitem.h>
33 #include <gtk/gtkaccelmap.h>
34 #include <gtk/gtkaction.h>
35 #include <gtk/gtktoggleaction.h>
36 #include <gtk/gtkradioaction.h>
37 #include <gtk/gtkuimanager.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <libxml/entities.h>
42 #define LABEL_WIDTH_CHARS 32
43 #define ACTION_VERB_FORMAT              "JmpTab%x"
44 #define ACTION_VERB_FORMAT_LENGTH       strlen (ACTION_VERB_FORMAT) + 14 + 1
45 #define ACCEL_PATH_FORMAT               "<Actions>/TabsActions/%s"
46 #define ACCEL_PATH_FORMAT_LENGTH        strlen (ACCEL_PATH_FORMAT) -2 + ACTION_VERB_FORMAT_LENGTH
47 #define DATA_KEY                        "EphyTabsMenu::Action"
49 #define EPHY_TABS_MENU_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_TABS_MENU, EphyTabsMenuPrivate))
51 struct _EphyTabsMenuPrivate
53         EphyWindow *window;
54         GtkActionGroup *action_group;
55         GtkAction *anchor_action;
56         guint ui_id;
59 enum
61         PROP_0,
62         PROP_WINDOW
65 static void     ephy_tabs_menu_class_init       (EphyTabsMenuClass *klass);
66 static void     ephy_tabs_menu_init             (EphyTabsMenu *menu);
68 GType
69 ephy_tabs_menu_get_type (void)
71         static GType type = 0;
73         if (G_UNLIKELY (type == 0))
74         {
75                 const GTypeInfo our_info =
76                 {
77                         sizeof (EphyTabsMenuClass),
78                         NULL, /* base_init */
79                         NULL, /* base_finalize */
80                         (GClassInitFunc) ephy_tabs_menu_class_init,
81                         NULL,
82                         NULL, /* class_data */
83                         sizeof (EphyTab),
84                         0, /* n_preallocs */
85                         (GInstanceInitFunc) ephy_tabs_menu_init
86                 };
88                 type = g_type_register_static (G_TYPE_OBJECT,
89                                                "EphyTabsMenu",
90                                                &our_info, 0);
91         }
93         return type;
96 static void
97 tab_action_activate_cb (GtkToggleAction *action,
98                         EphyTabsMenu *menu)
100         EphyTabsMenuPrivate *priv = menu->priv;
101         EphyTab *tab;
103         if (gtk_toggle_action_get_active (action) == FALSE)
104         {
105                 return;
106         }
108         tab = g_object_get_data (G_OBJECT (action), DATA_KEY);
109         g_return_if_fail (tab != NULL);
111         LOG ("tab_action_activate_cb tab=%p", tab);
113         if (ephy_window_get_active_tab (priv->window) != tab)
114         {
115                 ephy_window_jump_to_tab (priv->window, tab);
116         }
119 static void
120 sync_tab_title (EphyTab *tab,
121                 GParamSpec *pspec,
122                 GtkAction *action)
124         const char *title;
126         title = ephy_tab_get_title_composite (tab);
128         g_object_set (action, "label", title, NULL);
131 static void
132 notebook_page_added_cb (EphyNotebook *notebook,
133                         EphyTab *tab,
134                         guint position,
135                         EphyTabsMenu *menu)
137         EphyTabsMenuPrivate *priv = menu->priv;
138         GtkAction *action;
139         char verb[ACTION_VERB_FORMAT_LENGTH];
140         GSList *group;
142         LOG ("tab_added_cb tab=%p", tab);
144         g_snprintf (verb, sizeof (verb), ACTION_VERB_FORMAT,
145                     _ephy_tab_get_id (tab));
146   
147         action = g_object_new (GTK_TYPE_RADIO_ACTION,
148                                "name", verb,
149                                "tooltip", _("Switch to this tab"),
150                                NULL);
152         sync_tab_title (tab, NULL, action);
153         /* make sure the action is alive when handling the signal, see bug #169833 */
154         g_signal_connect_object (tab, "notify::title",
155                                  G_CALLBACK (sync_tab_title), action, 0);
157         gtk_action_group_add_action_with_accel (priv->action_group, action, NULL);
159         group = gtk_radio_action_get_group (GTK_RADIO_ACTION (priv->anchor_action));
160         gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
162         /* set this here too, since tab-added comes after notify::active-tab */
163         if (ephy_window_get_active_tab (priv->window) == tab)
164         {
165                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
166         }
168         g_object_set_data (G_OBJECT (tab), DATA_KEY, action);
169         g_object_set_data (G_OBJECT (action), DATA_KEY, tab);
171         g_signal_connect (action, "activate",
172                           G_CALLBACK (tab_action_activate_cb), menu);
174         g_object_unref (action);
176         ephy_tabs_menu_update (menu);
179 static void
180 notebook_page_removed_cb (EphyNotebook *notebook,
181                           EphyTab *tab,
182                           guint position,
183                           EphyTabsMenu *menu)
185         EphyTabsMenuPrivate *priv = menu->priv;
186         GtkAction *action;
187                                                                                                              
188         LOG ("tab_removed_cb tab=%p", tab);
190         action = g_object_get_data (G_OBJECT (tab), DATA_KEY);
191         g_return_if_fail (action != NULL);
193         g_signal_handlers_disconnect_by_func
194                 (tab, G_CALLBACK (sync_tab_title), action);
196         g_signal_handlers_disconnect_by_func
197                 (action, G_CALLBACK (tab_action_activate_cb), menu);
199         g_object_set_data (G_OBJECT (tab), DATA_KEY, NULL);
200         gtk_action_group_remove_action (priv->action_group, action);
202         ephy_tabs_menu_update (menu);
205 static void
206 notebook_page_reordered_cb (EphyNotebook *notebook,
207                             EphyTab *tab,
208                             guint position,
209                             EphyTabsMenu *menu)
211         LOG ("tabs_reordered_cb");
213         ephy_tabs_menu_update (menu);
216 static void
217 connect_proxy_cb (GtkActionGroup *action_group,
218                   GtkAction *action,
219                   GtkWidget *proxy,
220                   gpointer dummy)
222         if (GTK_IS_MENU_ITEM (proxy))
223         {
224                 GtkLabel *label;
226                 label = GTK_LABEL (GTK_BIN (proxy)->child);
228                 gtk_label_set_use_underline (label, FALSE);
229                 gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END);
230                 gtk_label_set_max_width_chars (label, LABEL_WIDTH_CHARS);
231         }
234 static void
235 sync_active_tab (EphyWindow *window,
236                  GParamSpec *pspec,
237                  EphyTabsMenu *menu)
239         EphyTab *tab;
240         GtkAction *action;
242         tab = ephy_window_get_active_tab (window);
243         if (tab == NULL) return;
245         LOG ("active tab is tab %p", tab);
247         action = g_object_get_data (G_OBJECT (tab), DATA_KEY);
248         /* happens initially, since the ::active-tab comes before
249         * the ::tab-added signal
250         */
251         /* FIXME that's not true with gtk+ 2.9 anymore */
252         if (action == NULL) return;
254         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
257 static void
258 ephy_tabs_menu_set_window (EphyTabsMenu *menu,
259                            EphyWindow *window)
261         EphyTabsMenuPrivate *priv = menu->priv;
262         GtkWidget *notebook;
263         GtkUIManager *manager;
265         priv->window = window;
267         manager = GTK_UI_MANAGER (ephy_window_get_ui_manager (window));
268         priv->action_group = gtk_action_group_new ("TabsActions");
269         gtk_ui_manager_insert_action_group (manager, priv->action_group, -1);
270         g_object_unref (priv->action_group);
272         priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION,
273                                             "name", "TabsMenuAnchorAction",
274                                             NULL);
275         gtk_action_group_add_action (priv->action_group, priv->anchor_action);
277         g_signal_connect (priv->action_group, "connect-proxy",
278                           G_CALLBACK (connect_proxy_cb), NULL);
280         g_signal_connect (window, "notify::active-tab",
281                           G_CALLBACK (sync_active_tab), menu);
283         notebook = ephy_window_get_notebook (window);
284         g_signal_connect_object (notebook, "page-added",
285                                  G_CALLBACK (notebook_page_added_cb), menu, 0);
286         g_signal_connect_object (notebook, "page-removed",
287                                  G_CALLBACK (notebook_page_removed_cb), menu, 0);
288         g_signal_connect_object (notebook, "page-reordered",
289                                  G_CALLBACK (notebook_page_reordered_cb), menu, 0);
292 static void
293 ephy_tabs_menu_set_property (GObject *object,
294                              guint prop_id,
295                              const GValue *value,
296                              GParamSpec *pspec)
298         EphyTabsMenu *menu = EPHY_TABS_MENU (object);
300         switch (prop_id)
301         {
302                 case PROP_WINDOW:
303                         ephy_tabs_menu_set_window (menu, g_value_get_object (value));
304                         break;
305         }
308 static void
309 ephy_tabs_menu_get_property (GObject *object,
310                              guint prop_id,
311                              GValue *value,
312                              GParamSpec *pspec)
314         /* no readable properties */
315         g_return_if_reached ();
318 static void
319 ephy_tabs_menu_class_init (EphyTabsMenuClass *klass)
321         GObjectClass *object_class = G_OBJECT_CLASS (klass);
323         object_class->set_property = ephy_tabs_menu_set_property;
324         object_class->get_property = ephy_tabs_menu_get_property;
326         g_object_class_install_property (object_class,
327                                          PROP_WINDOW,
328                                          g_param_spec_object ("window",
329                                                               "Window",
330                                                               "Parent window",
331                                                               EPHY_TYPE_WINDOW,
332                                                               G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
333                                                               G_PARAM_CONSTRUCT_ONLY));
335         g_type_class_add_private (object_class, sizeof (EphyTabsMenuPrivate));
338 static void
339 ephy_tabs_menu_init (EphyTabsMenu *menu)
341         menu->priv = EPHY_TABS_MENU_GET_PRIVATE (menu);
344 static void
345 ephy_tabs_menu_clean (EphyTabsMenu *menu)
347         EphyTabsMenuPrivate *p = menu->priv;
348         GtkUIManager *manager = GTK_UI_MANAGER (ephy_window_get_ui_manager (p->window));
350         if (p->ui_id != 0)
351         {
352                 gtk_ui_manager_remove_ui (manager, p->ui_id);
353                 gtk_ui_manager_ensure_update (manager);
354                 p->ui_id = 0;
355         }
358 EphyTabsMenu *
359 ephy_tabs_menu_new (EphyWindow *window)
361         return EPHY_TABS_MENU (g_object_new (EPHY_TYPE_TABS_MENU,
362                                              "window", window,
363                                              NULL));
366 static void
367 tab_set_action_accelerator (GtkActionGroup *action_group,
368                             GtkAction *action,
369                             guint tab_number)
371         const char *verb;
372         char accel_path[ACCEL_PATH_FORMAT_LENGTH];
373         char accel[7];
374         gint accel_number;
375         guint accel_key;
376         GdkModifierType accel_mods;
378         verb = gtk_action_get_name (action);
380         /* set the accel path for the menu item */
381         g_snprintf (accel_path, sizeof (accel_path),
382                     ACCEL_PATH_FORMAT, verb);
383         gtk_action_set_accel_path (action, accel_path);
385         /* Only the first ten tabs get accelerators starting from 1 through 0 */
386         if (tab_number < 10)
387         {
388                 accel_key = 0;
389                 accel_number = (tab_number + 1) % 10;
391                 g_snprintf (accel, sizeof (accel), "<alt>%d", accel_number);
393                 gtk_accelerator_parse (accel, &accel_key, &accel_mods);
395                 if (accel_key != 0)
396                 {
397                         gtk_accel_map_change_entry (accel_path, accel_key,
398                                                     accel_mods, TRUE);
399                 }
400         }
401         else
402         {
403                 gtk_accel_map_change_entry (accel_path, 0, 0, TRUE);
404         }
407 void
408 ephy_tabs_menu_update (EphyTabsMenu *menu)
410         EphyTabsMenuPrivate *p = menu->priv;
411         GtkUIManager *manager;
412         GtkAction *action;
413         GList *tabs = NULL, *l;
414         guint i = 0;
415         const char *verb;
417         LOG ("Rebuilding open tabs menu");
419         START_PROFILER ("Rebuilding tabs menu")
421         ephy_tabs_menu_clean (menu);
423         tabs = ephy_window_get_tabs (p->window);
425         if (g_list_length (tabs) == 0) return;
427         manager =  GTK_UI_MANAGER (ephy_window_get_ui_manager (p->window));
428         p->ui_id = gtk_ui_manager_new_merge_id (manager);
430         for (l = tabs; l != NULL; l = l->next)
431         {
432                 action = g_object_get_data (G_OBJECT (l->data), DATA_KEY);
433                 g_return_if_fail (action != NULL);
434   
435                 verb = gtk_action_get_name (action);
437                 tab_set_action_accelerator (p->action_group, action, i++);
439                 gtk_ui_manager_add_ui (manager, p->ui_id,
440                                        "/menubar/TabsMenu/TabsOpen",
441                                        verb, verb,
442                                        GTK_UI_MANAGER_MENUITEM, FALSE);
443         }
445         g_list_free (tabs);
447         STOP_PROFILER ("Rebuilding tabs menu")