midori/src/midori-panel.c
Christian Dywan 4a488da7b1 Implement MidoriApp singleton and improve multiple window support
The new singleton holds the shared settings and trash. It is
further more used to keep track of all open windows and quits
when the last window was closed. It also watches the focus in
case you need something like a "current" browser.
2008-05-23 00:17:10 +02:00

562 lines
18 KiB
C

/*
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See the file COPYING for the full license text.
*/
#include "midori-panel.h"
#include "sokoke.h"
#include <glib/gi18n.h>
G_DEFINE_TYPE (MidoriPanel, midori_panel, GTK_TYPE_HBOX)
struct _MidoriPanelPrivate
{
GtkWidget* toolbar;
GtkWidget* toolbar_label;
GtkWidget* frame;
GtkWidget* toolbook;
GtkWidget* notebook;
GSList* group;
GtkMenu* menu;
};
#define MIDORI_PANEL_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
MIDORI_TYPE_PANEL, MidoriPanelPrivate))
enum
{
PROP_0,
PROP_SHADOW_TYPE,
PROP_MENU,
PROP_PAGE
};
enum {
CLOSE,
SWITCH_PAGE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
static void
midori_panel_finalize (GObject* object);
static void
midori_panel_set_property (GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec);
static void
midori_panel_get_property (GObject* object,
guint prop_id,
GValue* value,
GParamSpec* pspec);
static gboolean
midori_panel_close_cb (MidoriPanel* panel)
{
gtk_widget_hide (GTK_WIDGET (panel));
return FALSE;
}
static void
midori_cclosure_marshal_BOOLEAN__VOID (GClosure* closure,
GValue* return_value,
guint n_param_values,
const GValue* param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
typedef gboolean(*GMarshalFunc_BOOLEAN__VOID) (gpointer data1,
gpointer data2);
register GMarshalFunc_BOOLEAN__VOID callback;
register GCClosure* cc = (GCClosure*) closure;
register gpointer data1, data2;
gboolean v_return;
g_return_if_fail (return_value != NULL);
g_return_if_fail (n_param_values == 1);
if (G_CCLOSURE_SWAP_DATA (closure))
{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}
else
{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}
callback = (GMarshalFunc_BOOLEAN__VOID) (marshal_data
? marshal_data : cc->callback);
v_return = callback (data1, data2);
g_value_set_boolean (return_value, v_return);
}
static void
midori_panel_class_init (MidoriPanelClass* class)
{
signals[CLOSE] = g_signal_new (
"close",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_STRUCT_OFFSET (MidoriPanelClass, close),
g_signal_accumulator_true_handled,
NULL,
midori_cclosure_marshal_BOOLEAN__VOID,
G_TYPE_BOOLEAN, 0);
signals[SWITCH_PAGE] = g_signal_new (
"switch-page",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST),
G_STRUCT_OFFSET (MidoriPanelClass, switch_page),
0,
NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1,
G_TYPE_INT);
class->close = midori_panel_close_cb;
GObjectClass* gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = midori_panel_finalize;
gobject_class->set_property = midori_panel_set_property;
gobject_class->get_property = midori_panel_get_property;
GParamFlags flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT;
g_object_class_install_property (gobject_class,
PROP_SHADOW_TYPE,
g_param_spec_enum (
"shadow-type",
"Shadow Type",
_("Appearance of the shadow around each panel"),
GTK_TYPE_SHADOW_TYPE,
GTK_SHADOW_NONE,
flags));
g_object_class_install_property (gobject_class,
PROP_MENU,
g_param_spec_object (
"menu",
"Menu",
_("Menu to hold panel items"),
GTK_TYPE_MENU,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_PAGE,
g_param_spec_int (
"page",
"Page",
_("The index of the current page"),
-1, G_MAXINT, -1,
flags));
g_type_class_add_private (class, sizeof (MidoriPanelPrivate));
}
static void
midori_panel_button_close_clicked_cb (GtkWidget* toolitem,
MidoriPanel* panel)
{
gboolean return_value;
g_signal_emit (panel, signals[CLOSE], 0, &return_value);
}
static void
midori_panel_init (MidoriPanel* panel)
{
panel->priv = MIDORI_PANEL_GET_PRIVATE (panel);
MidoriPanelPrivate* priv = panel->priv;
// Create the sidebar
priv->toolbar = gtk_toolbar_new ();
gtk_toolbar_set_style (GTK_TOOLBAR (priv->toolbar), GTK_TOOLBAR_BOTH);
gtk_toolbar_set_icon_size (GTK_TOOLBAR (priv->toolbar),
GTK_ICON_SIZE_BUTTON);
gtk_toolbar_set_orientation (GTK_TOOLBAR (priv->toolbar),
GTK_ORIENTATION_VERTICAL);
gtk_box_pack_start (GTK_BOX (panel), priv->toolbar, FALSE, FALSE, 0);
gtk_widget_show_all (priv->toolbar);
GtkWidget* vbox = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (panel), vbox, TRUE, TRUE, 0);
// Create the titlebar
GtkWidget* labelbar = gtk_toolbar_new ();
gtk_toolbar_set_icon_size (GTK_TOOLBAR (labelbar), GTK_ICON_SIZE_MENU);
gtk_toolbar_set_style (GTK_TOOLBAR (labelbar), GTK_TOOLBAR_ICONS);
GtkToolItem* toolitem = gtk_tool_item_new ();
gtk_tool_item_set_expand (toolitem, TRUE);
priv->toolbar_label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (priv->toolbar_label), 0, 0.5);
gtk_container_add (GTK_CONTAINER (toolitem), priv->toolbar_label);
gtk_container_set_border_width (GTK_CONTAINER (toolitem), 6);
gtk_toolbar_insert (GTK_TOOLBAR (labelbar), toolitem, -1);
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_CLOSE);
gtk_tool_button_set_label (GTK_TOOL_BUTTON (toolitem), _("Close panel"));
sokoke_tool_item_set_tooltip_text (GTK_TOOL_ITEM (toolitem), _("Close panel"));
g_signal_connect (toolitem, "clicked",
G_CALLBACK (midori_panel_button_close_clicked_cb), panel);
gtk_toolbar_insert (GTK_TOOLBAR (labelbar), toolitem, -1);
gtk_box_pack_start (GTK_BOX (vbox), labelbar, FALSE, FALSE, 0);
gtk_widget_show_all (vbox);
// Create the toolbook
priv->toolbook = gtk_notebook_new ();
gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->toolbook), FALSE);
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->toolbook), FALSE);
gtk_box_pack_start (GTK_BOX (vbox), priv->toolbook, FALSE, FALSE, 0);
gtk_widget_show (priv->toolbook);
// Create the notebook
priv->notebook = gtk_notebook_new ();
gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE);
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
priv->frame = gtk_frame_new (NULL);
gtk_container_add (GTK_CONTAINER (priv->frame), priv->notebook);
gtk_box_pack_start (GTK_BOX (vbox), priv->frame, TRUE, TRUE, 0);
gtk_widget_show_all (priv->frame);
}
static void
midori_panel_finalize (GObject* object)
{
MidoriPanel* panel = MIDORI_PANEL (object);
MidoriPanelPrivate* priv = panel->priv;
if (priv->menu)
{
// FIXME: Remove all menu items
}
G_OBJECT_CLASS (midori_panel_parent_class)->finalize (object);
}
static void
midori_panel_set_property (GObject* object,
guint prop_id,
const GValue* value,
GParamSpec* pspec)
{
MidoriPanel* panel = MIDORI_PANEL (object);
MidoriPanelPrivate* priv = panel->priv;
switch (prop_id)
{
case PROP_SHADOW_TYPE:
gtk_frame_set_shadow_type (GTK_FRAME (priv->frame),
g_value_get_enum (value));
break;
case PROP_MENU:
katze_object_assign (priv->menu, g_value_get_object (value));
// FIXME: Move existing items to the new menu
break;
case PROP_PAGE:
midori_panel_set_current_page (panel, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
midori_panel_get_property (GObject* object,
guint prop_id,
GValue* value,
GParamSpec* pspec)
{
MidoriPanel* panel = MIDORI_PANEL (object);
MidoriPanelPrivate* priv = panel->priv;
switch (prop_id)
{
case PROP_SHADOW_TYPE:
g_value_set_enum (value,
gtk_frame_get_shadow_type (GTK_FRAME (priv->frame)));
break;
case PROP_MENU:
g_value_set_object (value, priv->menu);
break;
case PROP_PAGE:
g_value_set_int (value, midori_panel_get_current_page (panel));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* midori_panel_new:
*
* Creates a new empty panel.
*
* Return value: a new #MidoriPanel
**/
GtkWidget*
midori_panel_new (void)
{
MidoriPanel* panel = g_object_new (MIDORI_TYPE_PANEL,
NULL);
return GTK_WIDGET (panel);
}
static void
midori_panel_menu_item_activate_cb (GtkWidget* widget,
MidoriPanel* panel)
{
GtkWidget* child = g_object_get_data (G_OBJECT (widget), "page");
guint n = midori_panel_page_num (panel, child);
midori_panel_set_current_page (panel, n);
g_signal_emit (panel, signals[SWITCH_PAGE], 0, n);
}
/**
* midori_panel_append_page:
* @panel: a #MidoriPanel
* @child: the child widget
* @toolbar: a toolbar widget, or %NULL
* @icon: a stock ID or icon name, or %NULL
* @label: a string to use as the label, or %NULL
*
* Appends a new page to the panel. If @toolbar is specified it will
* be packaged above @child.
*
* If @icon is an icon name, the according image is used as an
* icon for this page.
*
* If @label is given, it is used as the label of this page.
*
* In the case of an error, -1 is returned.
*
* Return value: the index of the new page, or -1
**/
gint
midori_panel_append_page (MidoriPanel* panel,
GtkWidget* child,
GtkWidget* toolbar,
const gchar* icon,
const gchar* label)
{
g_return_val_if_fail (MIDORI_IS_PANEL (panel), -1);
g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
g_return_val_if_fail (!toolbar || GTK_IS_WIDGET (toolbar), -1);
MidoriPanelPrivate* priv = panel->priv;
GtkWidget* scrolled = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
GTK_WIDGET_SET_FLAGS (scrolled, GTK_CAN_FOCUS);
gtk_widget_show (scrolled);
GtkWidget* widget;
GObjectClass* gobject_class = G_OBJECT_GET_CLASS (child);
if (GTK_WIDGET_CLASS (gobject_class)->set_scroll_adjustments_signal)
widget = child;
else
{
widget = gtk_viewport_new (NULL, NULL);
gtk_widget_show (widget);
gtk_container_add (GTK_CONTAINER (widget), child);
}
gtk_container_add (GTK_CONTAINER (scrolled), widget);
gtk_container_add (GTK_CONTAINER (priv->notebook), scrolled);
if (!toolbar)
toolbar = gtk_event_box_new ();
gtk_widget_show (toolbar);
gtk_container_add (GTK_CONTAINER (priv->toolbook), toolbar);
guint n = midori_panel_page_num (panel, child);
const gchar* text = label ? label : _("Untitled");
g_object_set_data (G_OBJECT (child), "label", (gchar*)text);
GtkWidget* image;
GtkToolItem* toolitem = gtk_radio_tool_button_new (priv->group);
priv->group = gtk_radio_tool_button_get_group (GTK_RADIO_TOOL_BUTTON (
toolitem));
gtk_tool_button_set_label (GTK_TOOL_BUTTON (toolitem), text);
if (icon)
{
image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image);
}
g_object_set_data (G_OBJECT (toolitem), "page", child);
g_signal_connect (toolitem, "clicked",
G_CALLBACK (midori_panel_menu_item_activate_cb), panel);
gtk_widget_show_all (GTK_WIDGET (toolitem));
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), toolitem, -1);
if (priv->menu)
{
GtkWidget* menuitem = gtk_image_menu_item_new_with_label (text);
if (icon)
{
image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem),
image);
}
gtk_widget_show_all (menuitem);
g_object_set_data (G_OBJECT (menuitem), "page", child);
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_panel_menu_item_activate_cb),
panel);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), menuitem);
}
return n;
}
/**
* midori_panel_get_current_page:
* @panel: a #MidoriPanel
*
* Retrieves the index of the currently selected page.
*
* If @panel has no children, -1 is returned.
*
* Return value: the index of the current page, or -1
**/
gint
midori_panel_get_current_page (MidoriPanel* panel)
{
g_return_val_if_fail (MIDORI_IS_PANEL (panel), -1);
MidoriPanelPrivate* priv = panel->priv;
return gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
}
static GtkWidget*
_midori_panel_child_for_scrolled (MidoriPanel* panel,
GtkWidget* scrolled)
{
GtkWidget* child = gtk_bin_get_child (GTK_BIN (scrolled));
if (GTK_IS_VIEWPORT (child))
child = gtk_bin_get_child (GTK_BIN (child));
return child;
}
/**
* midori_panel_get_nth_page:
* @panel: a #MidoriPanel
*
* Retrieves the child widget of the nth page.
*
* If @panel has no children, %NULL is returned.
*
* Return value: the child widget of the new page, or %NULL
**/
GtkWidget*
midori_panel_get_nth_page (MidoriPanel* panel,
guint page_num)
{
g_return_val_if_fail (MIDORI_IS_PANEL (panel), NULL);
MidoriPanelPrivate* priv = panel->priv;
GtkWidget* scrolled = gtk_notebook_get_nth_page (
GTK_NOTEBOOK (priv->notebook), page_num);
if (scrolled)
return _midori_panel_child_for_scrolled (panel, scrolled);
return NULL;
}
/**
* midori_panel_get_n_pages:
* @panel: a #MidoriPanel
*
* Retrieves the number of pages contained in the panel.
*
* Return value: the number of pages
**/
guint
midori_panel_get_n_pages (MidoriPanel* panel)
{
g_return_val_if_fail (MIDORI_IS_PANEL (panel), 0);
MidoriPanelPrivate* priv = panel->priv;
return gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
}
static GtkWidget*
_midori_panel_scrolled_for_child (MidoriPanel* panel,
GtkWidget* child)
{
GtkWidget* scrolled = gtk_widget_get_parent (GTK_WIDGET (child));
if (GTK_IS_VIEWPORT (scrolled))
scrolled = gtk_widget_get_parent (scrolled);
return scrolled;
}
/**
* midori_panel_page_num:
* @panel: a #MidoriPanel
*
* Retrieves the index of the page associated to @widget.
*
* If @panel has no children, -1 is returned.
*
* Return value: the index of page associated to @widget, or -1
**/
gint
midori_panel_page_num (MidoriPanel* panel,
GtkWidget* child)
{
g_return_val_if_fail (MIDORI_IS_PANEL (panel), -1);
MidoriPanelPrivate* priv = panel->priv;
GtkWidget* scrolled = _midori_panel_scrolled_for_child (panel, child);
return gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), scrolled);
}
/**
* midori_panel_set_current_page:
* @panel: a #MidoriPanel
* @n: index of the page to switch to, or -1 to mean the last page
*
* Switches to the page with the given index.
*
* The child must be visible, otherwise the underlying GtkNotebook will
* silently ignore the attempt to switch the page.
**/
void
midori_panel_set_current_page (MidoriPanel* panel,
gint n)
{
g_return_if_fail (MIDORI_IS_PANEL (panel));
MidoriPanelPrivate* priv = panel->priv;
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->toolbook), n);
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), n);
GtkWidget* child = midori_panel_get_nth_page (panel, n);
if (child)
{
const gchar* label = g_object_get_data (G_OBJECT (child), "label");
g_object_set (priv->toolbar_label, "label", label, NULL);
}
}