Initial import of ephy (rev# 7126) from svn
[ephy-soc.git] / src / ephy-notebook.c
blobd7831feeff0c49b80c5e63bce2c9f59458ce1046
1 /*
2 * Copyright © 2002 Christophe Fergeau
3 * Copyright © 2003, 2004 Marco Pesenti Gritti
4 * Copyright © 2003, 2004, 2005 Christian Persch
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 2, or (at your option)
9 * 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, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * $Id: ephy-notebook.c 6952 2007-03-11 19:42:02Z chpe $
23 #include "config.h"
25 #include "ephy-notebook.h"
26 #include "eel-gconf-extensions.h"
27 #include "ephy-prefs.h"
28 #include "ephy-marshal.h"
29 #include "ephy-file-helpers.h"
30 #include "ephy-dnd.h"
31 #include "ephy-embed.h"
32 #include "ephy-window.h"
33 #include "ephy-shell.h"
34 #include "ephy-spinner.h"
35 #include "ephy-link.h"
36 #include "ephy-debug.h"
38 #include <glib/gi18n.h>
39 #include <gtk/gtkeventbox.h>
40 #include <gtk/gtkhbox.h>
41 #include <gtk/gtklabel.h>
42 #include <gtk/gtkwidget.h>
43 #include <gtk/gtktooltips.h>
44 #include <gtk/gtkstock.h>
45 #include <gtk/gtkimage.h>
46 #include <gtk/gtkbutton.h>
47 #include <gtk/gtkaccelgroup.h>
48 #include <gtk/gtkiconfactory.h>
50 #define TAB_WIDTH_N_CHARS 15
52 #define AFTER_ALL_TABS -1
53 #define NOT_IN_APP_WINDOWS -2
55 #define INSANE_NUMBER_OF_URLS 20
57 /* Tooltip related */
58 #define TOOLTIP_MAX_LENGTH 256
60 /* Until https://bugzilla.mozilla.org/show_bug.cgi?id=296002 is fixed */
61 #define KEEP_TAB_IN_SAME_TOPLEVEL
63 #define EPHY_NOTEBOOK_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_NOTEBOOK, EphyNotebookPrivate))
65 struct _EphyNotebookPrivate
67 GList *focused_pages;
68 GtkTooltips *title_tips;
69 guint tabs_vis_notifier_id;
71 guint show_tabs : 1;
72 guint dnd_enabled : 1;
75 static void ephy_notebook_init (EphyNotebook *notebook);
76 static void ephy_notebook_class_init (EphyNotebookClass *klass);
77 static void ephy_notebook_finalize (GObject *object);
78 static int ephy_notebook_insert_page (GtkNotebook *notebook,
79 GtkWidget *child,
80 GtkWidget *tab_label,
81 GtkWidget *menu_label,
82 int position);
83 static void ephy_notebook_remove (GtkContainer *container,
84 GtkWidget *tab_widget);
86 static const GtkTargetEntry url_drag_types [] =
88 { EPHY_DND_URI_LIST_TYPE, 0, 0 },
89 { EPHY_DND_URL_TYPE, 0, 1 }
92 enum
94 PROP_0,
95 PROP_DND_ENABLED,
96 PROP_SHOW_TABS
99 enum
101 TAB_CLOSE_REQUEST,
102 LAST_SIGNAL
105 static guint signals[LAST_SIGNAL];
106 static GObjectClass *parent_class;
108 GType
109 ephy_notebook_get_type (void)
111 static GType type = 0;
113 if (G_UNLIKELY (type == 0))
115 const GTypeInfo our_info =
117 sizeof (EphyNotebookClass),
118 NULL, /* base_init */
119 NULL, /* base_finalize */
120 (GClassInitFunc) ephy_notebook_class_init,
121 NULL,
122 NULL, /* class_data */
123 sizeof (EphyNotebook),
124 0, /* n_preallocs */
125 (GInstanceInitFunc) ephy_notebook_init
128 const GInterfaceInfo link_info =
130 NULL,
131 NULL,
132 NULL
135 type = g_type_register_static (GTK_TYPE_NOTEBOOK,
136 "EphyNotebook",
137 &our_info, 0);
138 g_type_add_interface_static (type,
139 EPHY_TYPE_LINK,
140 &link_info);
143 return type;
146 void
147 ephy_notebook_set_dnd_enabled (EphyNotebook *notebook,
148 gboolean enabled)
150 EphyNotebookPrivate *priv = notebook->priv;
152 priv->dnd_enabled = enabled;
153 /* FIXME abort any DNDs in progress */
155 g_object_notify (G_OBJECT (notebook), "dnd-enabled");
158 static void
159 ephy_notebook_get_property (GObject *object,
160 guint prop_id,
161 GValue *value,
162 GParamSpec *pspec)
164 EphyNotebook *notebook = EPHY_NOTEBOOK (object);
165 EphyNotebookPrivate *priv = notebook->priv;
167 switch (prop_id)
169 case PROP_DND_ENABLED:
170 g_value_set_boolean (value, priv->dnd_enabled);
171 break;
172 case PROP_SHOW_TABS:
173 g_value_set_boolean (value, priv->show_tabs);
174 break;
178 static void
179 ephy_notebook_set_property (GObject *object,
180 guint prop_id,
181 const GValue *value,
182 GParamSpec *pspec)
184 EphyNotebook *notebook = EPHY_NOTEBOOK (object);
186 switch (prop_id)
188 case PROP_DND_ENABLED:
189 ephy_notebook_set_dnd_enabled (notebook, g_value_get_boolean (value));
190 break;
191 case PROP_SHOW_TABS:
192 ephy_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
193 break;
197 static void
198 ephy_notebook_class_init (EphyNotebookClass *klass)
200 GObjectClass *object_class = G_OBJECT_CLASS (klass);
201 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
202 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
204 parent_class = g_type_class_peek_parent (klass);
206 object_class->finalize = ephy_notebook_finalize;
207 object_class->get_property = ephy_notebook_get_property;
208 object_class->set_property = ephy_notebook_set_property;
210 container_class->remove = ephy_notebook_remove;
212 notebook_class->insert_page = ephy_notebook_insert_page;
214 signals[TAB_CLOSE_REQUEST] =
215 g_signal_new ("tab-close-request",
216 G_OBJECT_CLASS_TYPE (object_class),
217 G_SIGNAL_RUN_LAST,
218 G_STRUCT_OFFSET (EphyNotebookClass, tab_close_req),
219 NULL, NULL,
220 g_cclosure_marshal_VOID__OBJECT,
221 G_TYPE_NONE,
223 EPHY_TYPE_TAB);
225 g_object_class_install_property (object_class,
226 PROP_DND_ENABLED,
227 g_param_spec_boolean ("dnd-enabled",
228 "DND enabled",
229 "Whether the notebook allows tab reordering by DND",
230 TRUE,
231 G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
233 g_object_class_install_property (object_class,
234 PROP_SHOW_TABS,
235 g_param_spec_boolean ("show-tabs",
236 "Show tabs",
237 "Whether the notebook shows the tabs bar",
238 TRUE,
239 G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
241 g_type_class_add_private (object_class, sizeof (EphyNotebookPrivate));
245 /* FIXME remove when gtknotebook's func for this becomes public, bug #.... */
246 static EphyNotebook *
247 find_notebook_at_pointer (gint abs_x, gint abs_y)
249 GdkWindow *win_at_pointer, *toplevel_win;
250 gpointer toplevel = NULL;
251 gint x, y;
253 /* FIXME multi-head */
254 win_at_pointer = gdk_window_at_pointer (&x, &y);
255 if (win_at_pointer == NULL)
257 /* We are outside all windows containing a notebook */
258 return NULL;
261 toplevel_win = gdk_window_get_toplevel (win_at_pointer);
263 /* get the GtkWidget which owns the toplevel GdkWindow */
264 gdk_window_get_user_data (toplevel_win, &toplevel);
266 /* toplevel should be an EphyWindow */
267 if (toplevel != NULL && EPHY_IS_WINDOW (toplevel))
269 return EPHY_NOTEBOOK (ephy_window_get_notebook
270 (EPHY_WINDOW (toplevel)));
273 return NULL;
276 static gboolean
277 is_in_notebook_window (EphyNotebook *notebook,
278 gint abs_x, gint abs_y)
280 EphyNotebook *nb_at_pointer;
282 nb_at_pointer = find_notebook_at_pointer (abs_x, abs_y);
284 return nb_at_pointer == notebook;
287 static gint
288 find_tab_num_at_pos (EphyNotebook *notebook, gint abs_x, gint abs_y)
290 GtkPositionType tab_pos;
291 int page_num = 0;
292 GtkNotebook *nb = GTK_NOTEBOOK (notebook);
293 GtkWidget *page;
295 tab_pos = gtk_notebook_get_tab_pos (GTK_NOTEBOOK (notebook));
297 if (GTK_NOTEBOOK (notebook)->first_tab == NULL)
299 return AFTER_ALL_TABS;
302 /* For some reason unfullscreen + quick click can
303 cause a wrong click event to be reported to the tab */
304 if (!is_in_notebook_window(notebook, abs_x, abs_y))
306 return NOT_IN_APP_WINDOWS;
309 while ((page = gtk_notebook_get_nth_page (nb, page_num)))
311 GtkWidget *tab;
312 gint max_x, max_y;
313 gint x_root, y_root;
315 tab = gtk_notebook_get_tab_label (nb, page);
316 g_return_val_if_fail (tab != NULL, -1);
318 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (tab)))
320 page_num++;
321 continue;
324 gdk_window_get_origin (GDK_WINDOW (tab->window),
325 &x_root, &y_root);
327 max_x = x_root + tab->allocation.x + tab->allocation.width;
328 max_y = y_root + tab->allocation.y + tab->allocation.height;
330 if (((tab_pos == GTK_POS_TOP)
331 || (tab_pos == GTK_POS_BOTTOM))
332 &&(abs_x<=max_x))
334 return page_num;
336 else if (((tab_pos == GTK_POS_LEFT)
337 || (tab_pos == GTK_POS_RIGHT))
338 && (abs_y<=max_y))
340 return page_num;
343 page_num++;
345 return AFTER_ALL_TABS;
348 static gboolean
349 button_press_cb (EphyNotebook *notebook,
350 GdkEventButton *event,
351 gpointer data)
353 int tab_clicked;
355 tab_clicked = find_tab_num_at_pos (notebook, event->x_root, event->y_root);
357 if (event->type == GDK_BUTTON_PRESS &&
358 event->button == 3 &&
359 (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
361 if (tab_clicked == -1)
363 /* consume event, so that we don't pop up the context menu when
364 * the mouse if not over a tab label
366 return TRUE;
369 /* switch to the page the mouse is over, but don't consume the event */
370 gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), tab_clicked);
373 return FALSE;
376 static void
377 ephy_notebook_switch_page_cb (GtkNotebook *notebook,
378 GtkNotebookPage *page,
379 guint page_num,
380 gpointer data)
382 EphyNotebook *nb = EPHY_NOTEBOOK (notebook);
383 GtkWidget *child;
385 child = gtk_notebook_get_nth_page (notebook, page_num);
387 /* Remove the old page, we dont want to grow unnecessarily
388 * the list */
389 if (nb->priv->focused_pages)
391 nb->priv->focused_pages =
392 g_list_remove (nb->priv->focused_pages, child);
395 nb->priv->focused_pages = g_list_append (nb->priv->focused_pages,
396 child);
399 static void
400 notebook_drag_data_received_cb (GtkWidget* widget, GdkDragContext *context,
401 gint x, gint y, GtkSelectionData *selection_data,
402 guint info, guint time, EphyTab *tab)
404 EphyWindow *window;
405 GtkWidget *notebook;
407 g_signal_stop_emission_by_name (widget, "drag_data_received");
409 if (eel_gconf_get_boolean (CONF_LOCKDOWN_DISABLE_ARBITRARY_URL)) return;
411 if (selection_data->length <= 0 || selection_data->data == NULL) return;
413 window = EPHY_WINDOW (gtk_widget_get_toplevel (widget));
414 notebook = ephy_window_get_notebook (window);
416 if (selection_data->target == gdk_atom_intern (EPHY_DND_URL_TYPE, FALSE))
418 char **split;
420 /* URL_TYPE has format: url \n title */
421 split = g_strsplit ((const gchar *)selection_data->data, "\n", 2);
422 if (split != NULL && split[0] != NULL && split[0][0] != '\0')
424 ephy_link_open (EPHY_LINK (notebook), split[0], tab,
425 tab ? 0 : EPHY_LINK_NEW_TAB);
427 g_strfreev (split);
429 else if (selection_data->target == gdk_atom_intern (EPHY_DND_URI_LIST_TYPE, FALSE))
431 char **uris;
432 int i;
434 uris = gtk_selection_data_get_uris (selection_data);
435 if (uris == NULL) return;
437 for (i = 0; uris[i] != NULL && i < INSANE_NUMBER_OF_URLS; i++)
439 tab = ephy_link_open (EPHY_LINK (notebook), uris[i],
440 tab, i == 0 ? 0 : EPHY_LINK_NEW_TAB);
443 g_strfreev (uris);
445 else
447 char *text;
449 text = (char *) gtk_selection_data_get_text (selection_data);
450 if (text != NULL) {
451 ephy_link_open (EPHY_LINK (notebook), text, tab,
452 tab ? 0 : EPHY_LINK_NEW_TAB);
453 g_free (text);
459 * update_tabs_visibility: Hide tabs if there is only one tab
460 * and the pref is not set.
462 static void
463 update_tabs_visibility (EphyNotebook *nb, gboolean before_inserting)
465 gboolean show_tabs;
466 guint num;
468 num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb));
470 if (before_inserting) num++;
472 show_tabs = (eel_gconf_get_boolean (CONF_ALWAYS_SHOW_TABS_BAR) || num > 1) &&
473 nb->priv->show_tabs == TRUE;
475 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), show_tabs);
478 static void
479 tabs_visibility_notifier (GConfClient *client,
480 guint cnxn_id,
481 GConfEntry *entry,
482 EphyNotebook *nb)
484 update_tabs_visibility (nb, FALSE);
487 static void
488 ephy_notebook_init (EphyNotebook *notebook)
490 notebook->priv = EPHY_NOTEBOOK_GET_PRIVATE (notebook);
492 gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
493 gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
494 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
496 notebook->priv->title_tips = gtk_tooltips_new ();
497 gtk_tooltips_force_window (notebook->priv->title_tips);
498 gtk_label_set_max_width_chars (GTK_LABEL (notebook->priv->title_tips->tip_label), TOOLTIP_MAX_LENGTH);
499 gtk_label_set_ellipsize (GTK_LABEL (notebook->priv->title_tips->tip_label), PANGO_ELLIPSIZE_END);
500 g_object_ref_sink (notebook->priv->title_tips);
502 notebook->priv->show_tabs = TRUE;
503 notebook->priv->dnd_enabled = TRUE;
505 g_signal_connect (notebook, "button-press-event",
506 (GCallback)button_press_cb, NULL);
507 g_signal_connect_after (notebook, "switch-page",
508 G_CALLBACK (ephy_notebook_switch_page_cb),
509 NULL);
511 /* Set up drag-and-drop target */
512 g_signal_connect (notebook, "drag-data-received",
513 G_CALLBACK (notebook_drag_data_received_cb),
514 NULL);
515 gtk_drag_dest_set (GTK_WIDGET (notebook),
516 GTK_DEST_DEFAULT_MOTION |
517 GTK_DEST_DEFAULT_DROP,
518 url_drag_types, G_N_ELEMENTS (url_drag_types),
519 GDK_ACTION_MOVE | GDK_ACTION_COPY);
520 gtk_drag_dest_add_text_targets (GTK_WIDGET(notebook));
522 notebook->priv->tabs_vis_notifier_id = eel_gconf_notification_add
523 (CONF_ALWAYS_SHOW_TABS_BAR,
524 (GConfClientNotifyFunc)tabs_visibility_notifier, notebook);
527 static void
528 ephy_notebook_finalize (GObject *object)
530 EphyNotebook *notebook = EPHY_NOTEBOOK (object);
532 eel_gconf_notification_remove (notebook->priv->tabs_vis_notifier_id);
534 if (notebook->priv->focused_pages)
536 g_list_free (notebook->priv->focused_pages);
538 g_object_unref (notebook->priv->title_tips);
540 G_OBJECT_CLASS (parent_class)->finalize (object);
543 static void
544 sync_load_status (EphyTab *tab, GParamSpec *pspec, GtkWidget *proxy)
546 GtkWidget *spinner, *icon;
548 spinner = GTK_WIDGET (g_object_get_data (G_OBJECT (proxy), "spinner"));
549 icon = GTK_WIDGET (g_object_get_data (G_OBJECT (proxy), "icon"));
550 g_return_if_fail (spinner != NULL && icon != NULL);
552 if (ephy_tab_get_load_status (tab))
554 gtk_widget_hide (icon);
555 gtk_widget_show (spinner);
556 ephy_spinner_start (EPHY_SPINNER (spinner));
558 else
560 ephy_spinner_stop (EPHY_SPINNER (spinner));
561 gtk_widget_hide (spinner);
562 gtk_widget_show (icon);
566 static void
567 sync_icon (EphyTab *tab,
568 GParamSpec *pspec,
569 GtkWidget *proxy)
571 GtkImage *icon;
573 icon = GTK_IMAGE (g_object_get_data (G_OBJECT (proxy), "icon"));
574 g_return_if_fail (icon != NULL);
576 gtk_image_set_from_pixbuf (icon, ephy_tab_get_icon (tab));
579 static void
580 sync_label (EphyTab *tab, GParamSpec *pspec, GtkWidget *proxy)
582 GtkWidget *label, *ebox;
583 GtkTooltips *tips;
584 const char *title;
586 ebox = GTK_WIDGET (g_object_get_data (G_OBJECT (proxy), "label-ebox"));
587 tips = GTK_TOOLTIPS (g_object_get_data (G_OBJECT (proxy), "tooltips"));
588 label = GTK_WIDGET (g_object_get_data (G_OBJECT (proxy), "label"));
590 g_return_if_fail (ebox != NULL && tips != NULL && label != NULL);
592 title = ephy_tab_get_title (tab);
594 if (title)
596 gtk_label_set_text (GTK_LABEL (label), title);
597 gtk_tooltips_set_tip (tips, ebox, title, NULL);
601 static void
602 close_button_clicked_cb (GtkWidget *widget, GtkWidget *tab)
604 GtkWidget *notebook;
606 notebook = gtk_widget_get_parent (tab);
607 g_signal_emit (notebook, signals[TAB_CLOSE_REQUEST], 0, tab);
610 static void
611 tab_label_style_set_cb (GtkWidget *hbox,
612 GtkStyle *previous_style,
613 gpointer user_data)
615 PangoFontMetrics *metrics;
616 PangoContext *context;
617 GtkWidget *button;
618 int char_width, h, w;
620 context = gtk_widget_get_pango_context (hbox);
621 metrics = pango_context_get_metrics (context,
622 hbox->style->font_desc,
623 pango_context_get_language (context));
625 char_width = pango_font_metrics_get_approximate_digit_width (metrics);
626 pango_font_metrics_unref (metrics);
628 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (hbox),
629 GTK_ICON_SIZE_MENU, &w, &h);
631 gtk_widget_set_size_request
632 (hbox, TAB_WIDTH_N_CHARS * PANGO_PIXELS(char_width) + 2 * w, -1);
634 button = g_object_get_data (G_OBJECT (hbox), "close-button");
635 gtk_widget_set_size_request (button, w + 2, h + 2);
638 static GtkWidget *
639 build_tab_label (EphyNotebook *nb, EphyTab *tab)
641 GtkWidget *hbox, *label_hbox, *label_ebox;
642 GtkWidget *label, *close_button, *image, *spinner, *icon;
643 GtkRcStyle *rcstyle;
645 /* set hbox spacing and label padding (see below) so that there's an
646 * equal amount of space around the label */
647 hbox = gtk_hbox_new (FALSE, 4);
649 label_ebox = gtk_event_box_new ();
650 gtk_event_box_set_visible_window (GTK_EVENT_BOX (label_ebox), FALSE);
651 gtk_box_pack_start (GTK_BOX (hbox), label_ebox, TRUE, TRUE, 0);
653 label_hbox = gtk_hbox_new (FALSE, 4);
654 gtk_container_add (GTK_CONTAINER (label_ebox), label_hbox);
656 /* setup close button */
657 close_button = gtk_button_new ();
658 gtk_button_set_relief (GTK_BUTTON (close_button),
659 GTK_RELIEF_NONE);
660 /* don't allow focus on the close button */
661 gtk_button_set_focus_on_click (GTK_BUTTON (close_button), FALSE);
663 rcstyle = gtk_rc_style_new ();
664 rcstyle->xthickness = rcstyle->ythickness = 0;
665 gtk_widget_modify_style (close_button, rcstyle);
666 gtk_rc_style_unref (rcstyle);
668 image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
669 gtk_container_add (GTK_CONTAINER (close_button), image);
670 gtk_box_pack_start (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
672 gtk_tooltips_set_tip (nb->priv->title_tips, close_button,
673 _("Close tab"), NULL);
675 g_signal_connect (close_button, "clicked",
676 G_CALLBACK (close_button_clicked_cb), tab);
678 /* setup load feedback */
679 spinner = ephy_spinner_new ();
680 ephy_spinner_set_size (EPHY_SPINNER (spinner), GTK_ICON_SIZE_MENU);
681 gtk_box_pack_start (GTK_BOX (label_hbox), spinner, FALSE, FALSE, 0);
683 /* setup site icon, empty by default */
684 icon = gtk_image_new ();
685 gtk_box_pack_start (GTK_BOX (label_hbox), icon, FALSE, FALSE, 0);
687 /* setup label */
688 label = gtk_label_new ("");
689 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
690 gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE);
691 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
692 gtk_misc_set_padding (GTK_MISC (label), 0, 0);
693 gtk_box_pack_start (GTK_BOX (label_hbox), label, TRUE, TRUE, 0);
695 /* Set minimal size */
696 g_signal_connect (hbox, "style-set",
697 G_CALLBACK (tab_label_style_set_cb), NULL);
699 /* Set up drag-and-drop target */
700 g_signal_connect (hbox, "drag_data_received",
701 G_CALLBACK (notebook_drag_data_received_cb), tab);
702 gtk_drag_dest_set (hbox, GTK_DEST_DEFAULT_ALL,
703 url_drag_types, G_N_ELEMENTS (url_drag_types),
704 GDK_ACTION_MOVE | GDK_ACTION_COPY);
705 gtk_drag_dest_add_text_targets (hbox);
707 gtk_widget_show (hbox);
708 gtk_widget_show (label_ebox);
709 gtk_widget_show (label_hbox);
710 gtk_widget_show (label);
711 gtk_widget_show (image);
712 gtk_widget_show (close_button);
714 g_object_set_data (G_OBJECT (hbox), "label", label);
715 g_object_set_data (G_OBJECT (hbox), "label-ebox", label_ebox);
716 g_object_set_data (G_OBJECT (hbox), "spinner", spinner);
717 g_object_set_data (G_OBJECT (hbox), "icon", icon);
718 g_object_set_data (G_OBJECT (hbox), "close-button", close_button);
719 g_object_set_data (G_OBJECT (hbox), "tooltips", nb->priv->title_tips);
721 /* Hook the label up to the tab properties */
722 sync_icon (tab, NULL, hbox);
723 sync_label (tab, NULL, hbox);
724 sync_load_status (tab, NULL, hbox);
726 g_signal_connect_object (tab, "notify::icon",
727 G_CALLBACK (sync_icon), hbox, 0);
728 g_signal_connect_object (tab, "notify::title",
729 G_CALLBACK (sync_label), hbox, 0);
730 g_signal_connect_object (tab, "notify::load-status",
731 G_CALLBACK (sync_load_status), hbox, 0);
733 return hbox;
736 void
737 ephy_notebook_set_show_tabs (EphyNotebook *nb, gboolean show_tabs)
739 nb->priv->show_tabs = show_tabs;
741 update_tabs_visibility (nb, FALSE);
744 GList *
745 ephy_notebook_get_focused_pages (EphyNotebook *nb)
747 g_return_val_if_fail (EPHY_IS_NOTEBOOK (nb), NULL);
749 return nb->priv->focused_pages;
752 static int
753 ephy_notebook_insert_page (GtkNotebook *gnotebook,
754 GtkWidget *tab_widget,
755 GtkWidget *tab_label,
756 GtkWidget *menu_label,
757 int position)
759 EphyNotebook *notebook = EPHY_NOTEBOOK (gnotebook);
761 /* Destroy passed-in tab label */
762 if (tab_label != NULL)
764 g_object_ref_sink (tab_label);
765 g_object_unref (tab_label);
768 g_assert (EPHY_IS_TAB (tab_widget));
770 tab_label = build_tab_label (notebook, EPHY_TAB (tab_widget));
772 update_tabs_visibility (notebook, TRUE);
774 position = GTK_NOTEBOOK_CLASS (parent_class)->insert_page (gnotebook,
775 tab_widget,
776 tab_label,
777 menu_label,
778 position);
780 gtk_notebook_set_tab_reorderable (gnotebook, tab_widget, TRUE);
782 return position;
786 ephy_notebook_add_tab (EphyNotebook *notebook,
787 EphyTab *tab,
788 int position,
789 gboolean jump_to)
791 GtkNotebook *gnotebook = GTK_NOTEBOOK (notebook);
793 g_return_val_if_fail (EPHY_IS_NOTEBOOK (notebook), -1);
795 position = gtk_notebook_insert_page (GTK_NOTEBOOK (notebook),
796 GTK_WIDGET (tab),
797 NULL,
798 position);
800 /* FIXME gtk bug! */
801 /* The signal handler may have reordered the tabs */
802 position = gtk_notebook_page_num (gnotebook, GTK_WIDGET (tab));
804 if (jump_to)
806 gtk_notebook_set_current_page (gnotebook, position);
807 g_object_set_data (G_OBJECT (tab), "jump_to",
808 GINT_TO_POINTER (jump_to));
812 return position;
815 static void
816 smart_tab_switching_on_closure (EphyNotebook *notebook,
817 GtkWidget *tab)
819 EphyNotebookPrivate *priv = notebook->priv;
820 gboolean jump_to;
822 jump_to = GPOINTER_TO_INT (g_object_get_data
823 (G_OBJECT (tab), "jump_to"));
825 if (!jump_to || !priv->focused_pages)
827 gtk_notebook_next_page (GTK_NOTEBOOK (notebook));
829 else
831 GList *l;
832 GtkWidget *child;
833 int page_num;
835 /* activate the last focused tab */
836 l = g_list_last (priv->focused_pages);
837 child = GTK_WIDGET (l->data);
838 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (notebook),
839 child);
840 gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
841 page_num);
845 static void
846 ephy_notebook_remove (GtkContainer *container,
847 GtkWidget *tab_widget)
849 GtkNotebook *gnotebook = GTK_NOTEBOOK (container);
850 EphyNotebook *notebook = EPHY_NOTEBOOK (container);
851 EphyNotebookPrivate *priv = notebook->priv;
852 GtkWidget *tab_label, *ebox;
853 int position, curr;
855 g_assert (EPHY_IS_TAB (tab_widget));
857 /* Remove the page from the focused pages list */
858 priv->focused_pages = g_list_remove (priv->focused_pages, tab_widget);
860 position = gtk_notebook_page_num (gnotebook, tab_widget);
861 curr = gtk_notebook_get_current_page (gnotebook);
863 if (position == curr)
865 smart_tab_switching_on_closure (notebook, tab_widget);
868 /* Prepare tab label for destruction */
869 tab_label = gtk_notebook_get_tab_label (gnotebook, tab_widget);
870 ebox = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "label-ebox"));
871 gtk_tooltips_set_tip (GTK_TOOLTIPS (priv->title_tips), ebox, NULL, NULL);
873 g_signal_handlers_disconnect_by_func
874 (tab_widget, G_CALLBACK (sync_icon), tab_label);
875 g_signal_handlers_disconnect_by_func
876 (tab_widget, G_CALLBACK (sync_label), tab_label);
877 g_signal_handlers_disconnect_by_func
878 (tab_widget, G_CALLBACK (sync_load_status), tab_label);
880 GTK_CONTAINER_CLASS (parent_class)->remove (container, tab_widget);
882 update_tabs_visibility (notebook, FALSE);
884 /* if that was the last tab, destroy the window */
885 if (gtk_notebook_get_n_pages (gnotebook) == 0)
887 gtk_widget_destroy (gtk_widget_get_toplevel (GTK_WIDGET (notebook)));