medit/moo/mooutils/moopane.cpp
2016-01-31 00:03:05 -08:00

1812 lines
47 KiB
C++

/*
* moopane.c
*
* Copyright (C) 2004-2010 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
*
* This file is part of medit. medit 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with medit. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* class:MooPane: (parent GtkObject) (moo.lua 0) (moo.private 1)
**/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "marshals.h"
#include "moopaned.h"
#include "moo-pixbufs.h"
#include <string.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#define SPACING_IN_BUTTON 4
#define OPEN_PANE_TIMEOUT 200
#include "mooutils-misc.h"
#include "moocompat.h"
#include "moohelp.h"
#include "mooutils-gobject.h"
#include "mooi18n.h"
#include "moocpp/moocpp.h"
using namespace moo;
struct _MooPane {
GtkObject base;
char *id;
MooPaned *parent;
GtkWidget *child;
GtkWidget *child_holder;
MooPaneLabel *label;
GtkWidget *frame;
GtkWidget *handle;
GtkWidget *small_handle;
GtkWidget *button;
GtkWidget *label_widget;
GtkWidget *icon_widget;
GtkWidget *sticky_button;
GtkWidget *detach_button;
GtkWidget *close_button;
GtkWidget *frame_label_embedded;
GtkWidget *frame_label_window;
char *frame_label_text;
gboolean frame_label_markup;
/* XXX weak pointer */
gpointer focus_child;
GtkWidget *window;
GtkWidget *keep_on_top_button;
GtkWidget *window_child_holder;
MooPaneParams *params;
guint open_timeout;
guint button_highlight : 1;
guint drag_dest_enabled : 1;
guint detachable : 1;
guint removable : 1;
guint params_changed_blocked : 1;
};
struct _MooPaneClass {
GtkObjectClass base_class;
gboolean (*remove) (MooPane *pane);
};
G_DEFINE_TYPE (MooPane, moo_pane, GTK_TYPE_OBJECT)
enum {
PROP_0,
PROP_ID,
PROP_LABEL,
PROP_PARAMS,
PROP_DETACHABLE,
PROP_REMOVABLE
};
enum {
REMOVE,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
static void
set_pane_window_icon_and_title (MooPane *pane)
{
if (pane->window && pane->label)
{
if (pane->label->icon_pixbuf)
gtk_window_set_icon (GTK_WINDOW (pane->window), pane->label->icon_pixbuf);
else if (pane->label->icon_stock_id)
_moo_window_set_icon_from_stock (GTK_WINDOW (pane->window), pane->label->icon_stock_id);
if (pane->label->window_title)
gtk_window_set_title (GTK_WINDOW (pane->window), pane->label->window_title);
else
gtk_window_set_title (GTK_WINDOW (pane->window), pane->label->label);
}
}
static void
update_label_widgets (MooPane *pane)
{
if (pane->label && pane->label_widget)
{
gtk_label_set_text (GTK_LABEL (pane->label_widget), pane->label->label);
g_object_set (pane->label_widget, "visible", pane->label->label != NULL, NULL);
}
if (pane->label && pane->icon_widget)
{
if (pane->label->icon_pixbuf)
gtk_image_set_from_pixbuf (GTK_IMAGE (pane->icon_widget),
pane->label->icon_pixbuf);
else if (pane->label->icon_stock_id)
gtk_image_set_from_stock (GTK_IMAGE (pane->icon_widget),
pane->label->icon_stock_id,
GTK_ICON_SIZE_MENU);
g_object_set (pane->icon_widget, "visible",
pane->label->icon_pixbuf || pane->label->icon_stock_id,
NULL);
}
set_pane_window_icon_and_title (pane);
}
/**
* moo_pane_set_label:
**/
void
moo_pane_set_label (MooPane *pane,
MooPaneLabel *label)
{
MooPaneLabel *tmp;
g_return_if_fail (MOO_IS_PANE (pane));
g_return_if_fail (label != NULL);
tmp = pane->label;
pane->label = moo_pane_label_copy (label);
moo_pane_label_free (tmp);
update_label_widgets (pane);
g_object_notify (G_OBJECT (pane), "label");
}
/**
* moo_pane_get_params:
**/
MooPaneParams *
moo_pane_get_params (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return moo_pane_params_copy (pane->params);
}
/**
* moo_pane_get_label:
**/
MooPaneLabel *
moo_pane_get_label (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return moo_pane_label_copy (pane->label);
}
/**
* moo_pane_set_params:
**/
void
moo_pane_set_params (MooPane *pane,
MooPaneParams *params)
{
MooPaneParams *old_params;
g_return_if_fail (MOO_IS_PANE (pane));
g_return_if_fail (params != NULL);
old_params = pane->params;
pane->params = moo_pane_params_copy (params);
if (old_params->detached != params->detached)
{
pane->params->detached = old_params->detached;
if (old_params->detached)
moo_paned_attach_pane (pane->parent, pane);
else
moo_paned_detach_pane (pane->parent, pane);
}
moo_pane_params_free (old_params);
g_object_notify (G_OBJECT (pane), "params");
}
/**
* moo_pane_set_detachable:
**/
void
moo_pane_set_detachable (MooPane *pane,
gboolean detachable)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (detachable == pane->detachable)
return;
pane->detachable = detachable != 0;
if (pane->params->detached && !detachable)
moo_paned_attach_pane (pane->parent, pane);
if (pane->detach_button)
g_object_set (pane->detach_button, "visible", pane->detachable, NULL);
g_object_notify (G_OBJECT (pane), "detachable");
}
/**
* moo_pane_set_removable:
**/
void
moo_pane_set_removable (MooPane *pane,
gboolean removable)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (removable == pane->removable)
return;
pane->removable = removable != 0;
if (pane->close_button)
g_object_set (pane->close_button, "visible", pane->removable, NULL);
g_object_notify (G_OBJECT (pane), "removable");
}
/**
* moo_pane_get_detachable:
**/
gboolean
moo_pane_get_detachable (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), FALSE);
return pane->detachable;
}
/**
* moo_pane_get_removable:
**/
gboolean
moo_pane_get_removable (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), FALSE);
return pane->removable;
}
static void
moo_pane_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MooPane *pane = MOO_PANE (object);
switch (prop_id)
{
case PROP_LABEL:
moo_pane_set_label (pane, reinterpret_cast<MooPaneLabel*> (g_value_get_boxed (value)));
break;
case PROP_PARAMS:
moo_pane_set_params (pane, reinterpret_cast<MooPaneParams*> (g_value_get_boxed (value)));
break;
case PROP_DETACHABLE:
moo_pane_set_detachable (pane, g_value_get_boolean (value));
break;
case PROP_REMOVABLE:
moo_pane_set_removable (pane, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
moo_pane_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MooPane *pane = MOO_PANE (object);
switch (prop_id)
{
case PROP_ID:
g_value_set_string (value, pane->id);
break;
case PROP_LABEL:
g_value_set_boxed (value, pane->label);
break;
case PROP_PARAMS:
g_value_set_boxed (value, pane->params);
break;
case PROP_DETACHABLE:
g_value_set_boolean (value, pane->detachable != 0);
break;
case PROP_REMOVABLE:
g_value_set_boolean (value, pane->removable != 0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
moo_pane_init (MooPane *pane)
{
pane->detachable = TRUE;
pane->removable = TRUE;
pane->params = moo_pane_params_new (NULL, FALSE, FALSE, FALSE);
pane->id = NULL;
pane->label = NULL;
pane->child = NULL;
pane->child_holder = NULL;
pane->frame = NULL;
pane->handle = NULL;
pane->small_handle = NULL;
pane->button = NULL;
pane->label_widget = NULL;
pane->icon_widget = NULL;
pane->sticky_button = NULL;
pane->detach_button = NULL;
pane->close_button = NULL;
pane->focus_child = NULL;
pane->window = NULL;
pane->keep_on_top_button = NULL;
pane->window_child_holder = NULL;
}
static void
moo_pane_finalize (GObject *object)
{
MooPane *pane = MOO_PANE (object);
g_free (pane->id);
moo_pane_label_free (pane->label);
moo_pane_params_free (pane->params);
G_OBJECT_CLASS (moo_pane_parent_class)->finalize (object);
}
static void
moo_pane_dispose (GObject *object)
{
MooPane *pane = MOO_PANE (object);
if (pane->child)
{
GtkWidget *tmp = pane->child;
pane->child = NULL;
g_object_unref (tmp);
}
if (pane->frame)
{
gtk_widget_unparent (pane->frame);
pane->frame = NULL;
}
if (pane->window)
{
gtk_widget_destroy (pane->window);
pane->window = NULL;
}
if (pane->open_timeout)
{
g_source_remove (pane->open_timeout);
pane->open_timeout = 0;
}
G_OBJECT_CLASS (moo_pane_parent_class)->dispose (object);
}
static void
moo_pane_class_init (MooPaneClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = moo_pane_set_property;
gobject_class->get_property = moo_pane_get_property;
gobject_class->finalize = moo_pane_finalize;
gobject_class->dispose = moo_pane_dispose;
g_object_class_install_property (gobject_class, PROP_ID,
g_param_spec_string ("id", "id", "id",
NULL, G_PARAM_READABLE));
g_object_class_install_property (gobject_class, PROP_LABEL,
g_param_spec_boxed ("label", "label", "label",
MOO_TYPE_PANE_LABEL, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_PARAMS,
g_param_spec_boxed ("params", "params", "params",
MOO_TYPE_PANE_PARAMS, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_DETACHABLE,
g_param_spec_boolean ("detachable", "detachable", "detachable",
TRUE, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_REMOVABLE,
g_param_spec_boolean ("removable", "removable", "removable",
TRUE, (GParamFlags) G_PARAM_READWRITE));
signals[REMOVE] =
g_signal_new ("remove",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooPaneClass, remove),
g_signal_accumulator_true_handled, NULL,
_moo_marshal_BOOL__VOID,
G_TYPE_BOOLEAN, 0);
}
static void
close_button_clicked (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (pane->parent)
_moo_pane_try_remove (pane);
}
static void
hide_button_clicked (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (pane->parent)
moo_paned_hide_pane (pane->parent);
}
static void
attach_button_clicked (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (pane->parent)
_moo_paned_attach_pane (pane->parent, pane);
}
static void
detach_button_clicked (MooPane *pane)
{
moo_paned_detach_pane (pane->parent, pane);
}
static void
sticky_button_toggled (GtkToggleButton *button,
MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
moo_paned_set_sticky_pane (pane->parent, gtk_toggle_button_get_active (button));
}
static void
update_sticky_button (MooPane *pane)
{
if (pane->parent)
{
gboolean sticky, active;
g_object_get (pane->parent, "sticky-pane", &sticky, NULL);
g_object_get (pane->sticky_button, "active", &active, NULL);
if (active != sticky)
g_object_set (pane->sticky_button, "active", sticky, NULL);
}
}
/**
* moo_pane_set_frame_markup:
*
* @pane:
* @markup: (type const-utf8) (allow-none):
**/
void
moo_pane_set_frame_markup (MooPane *pane,
const char *text)
{
char *tmp;
g_return_if_fail (MOO_IS_PANE (pane));
if (!text)
{
moo_pane_set_frame_text (pane, NULL);
return;
}
tmp = pane->frame_label_text;
pane->frame_label_text = g_strdup (text);
pane->frame_label_markup = TRUE;
g_free (tmp);
if (pane->frame_label_embedded)
gtk_label_set_markup (GTK_LABEL (pane->frame_label_embedded), text);
if (pane->frame_label_window)
gtk_label_set_markup (GTK_LABEL (pane->frame_label_window), text);
}
/**
* moo_pane_set_frame_text:
*
* @pane:
* @text: (type const-utf8) (allow-none):
**/
void
moo_pane_set_frame_text (MooPane *pane,
const char *text)
{
char *tmp;
g_return_if_fail (MOO_IS_PANE (pane));
tmp = pane->frame_label_text;
pane->frame_label_text = g_strdup (text);
pane->frame_label_markup = FALSE;
g_free (tmp);
if (pane->frame_label_embedded)
gtk_label_set_text (GTK_LABEL (pane->frame_label_embedded), text);
if (pane->frame_label_window)
gtk_label_set_text (GTK_LABEL (pane->frame_label_window), text);
}
static GtkWidget *
create_button (MooPane *pane,
GtkWidget *toolbar,
const char *tip,
gboolean toggle,
int padding,
MooSmallIcon icon)
{
GtkWidget *button;
GtkWidget *icon_widget;
if (toggle)
button = gtk_toggle_button_new ();
else
button = gtk_button_new ();
g_object_set_data (G_OBJECT (button), "moo-pane", pane);
gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
_moo_widget_set_tooltip (button, tip);
icon_widget = _moo_create_small_icon (icon);
gtk_container_add (GTK_CONTAINER (button), icon_widget);
gtk_box_pack_end (GTK_BOX (toolbar), button, FALSE, FALSE, padding);
gtk_widget_show_all (button);
return button;
}
static GtkWidget *
create_frame_widget (MooPane *pane,
MooPanePosition position,
gboolean embedded)
{
GtkWidget *vbox, *toolbar, *separator, *handle, *table, *child_holder;
GtkWidget *handle_hbox, *frame_label;
vbox = gtk_vbox_new (FALSE, 0);
gtk_widget_show (vbox);
toolbar = gtk_hbox_new (FALSE, 0);
handle = gtk_event_box_new ();
gtk_widget_show (handle);
gtk_box_pack_start (GTK_BOX (toolbar), handle, TRUE, TRUE, 3);
pane->handle = handle;
handle_hbox = gtk_hbox_new (FALSE, 0);
gtk_widget_show (handle_hbox);
gtk_container_add (GTK_CONTAINER (pane->handle), handle_hbox);
frame_label = gtk_label_new (NULL);
gtk_widget_show (frame_label);
gtk_box_pack_start (GTK_BOX (handle_hbox), frame_label, TRUE, TRUE, 0);
gtk_misc_set_alignment (GTK_MISC (frame_label), .0, .5);
gtk_misc_set_padding (GTK_MISC (frame_label), 6, 0);
gtk_label_set_ellipsize (GTK_LABEL (frame_label), PANGO_ELLIPSIZE_END);
if (pane->frame_label_markup)
gtk_label_set_markup (GTK_LABEL (frame_label), pane->frame_label_text);
else
gtk_label_set_text (GTK_LABEL (frame_label), pane->frame_label_text);
if (embedded)
pane->frame_label_embedded = frame_label;
else
pane->frame_label_window = frame_label;
pane->small_handle = gtk_event_box_new ();
gtk_widget_show (pane->small_handle);
gtk_box_pack_start (GTK_BOX (handle_hbox), pane->small_handle, TRUE, TRUE, 0);
if (embedded)
{
GtkWidget *hide_button;
pane->close_button = create_button (pane, toolbar,
_("Remove pane"), FALSE, 3,
MOO_SMALL_ICON_CLOSE);
g_object_set_data (G_OBJECT (pane->close_button), "moo-pane", pane);
g_signal_connect_swapped (pane->close_button, "clicked",
G_CALLBACK (close_button_clicked),
pane);
if (!pane->removable)
gtk_widget_hide (pane->close_button);
hide_button = create_button (pane, toolbar,
_("Hide pane"), FALSE, 0,
MOO_SMALL_ICON_HIDE);
pane->sticky_button = create_button (pane, toolbar,
_("Sticky"), TRUE, 0,
MOO_SMALL_ICON_STICKY);
pane->detach_button = create_button (pane, toolbar,
_("Detach pane"), FALSE, 0,
MOO_SMALL_ICON_DETACH);
g_signal_connect_swapped (hide_button, "clicked",
G_CALLBACK (hide_button_clicked), pane);
g_signal_connect_swapped (pane->detach_button, "clicked",
G_CALLBACK (detach_button_clicked), pane);
}
else
{
GtkWidget *attach_button;
attach_button = create_button (pane, toolbar,
_("Attach"), FALSE, 0,
MOO_SMALL_ICON_ATTACH);
pane->keep_on_top_button = create_button (pane, toolbar,
_("Keep on top"), TRUE, 0,
MOO_SMALL_ICON_KEEP_ON_TOP);
g_object_set_data (G_OBJECT (attach_button), "moo-pane", pane);
g_signal_connect_swapped (attach_button, "clicked",
G_CALLBACK (attach_button_clicked), pane);
}
gtk_widget_show (toolbar);
gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
separator = gtk_hseparator_new ();
gtk_widget_show (separator);
gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
child_holder = gtk_vbox_new (FALSE, 0);
gtk_widget_show (child_holder);
gtk_box_pack_start (GTK_BOX (vbox), child_holder, TRUE, TRUE, 0);
if (embedded)
pane->child_holder = child_holder;
else
pane->window_child_holder = child_holder;
table = gtk_table_new (2, 2, FALSE);
switch (position)
{
case MOO_PANE_POS_LEFT:
case MOO_PANE_POS_RIGHT:
separator = gtk_vseparator_new ();
break;
case MOO_PANE_POS_TOP:
case MOO_PANE_POS_BOTTOM:
separator = gtk_hseparator_new ();
break;
}
gtk_widget_show (separator);
switch (position)
{
case MOO_PANE_POS_LEFT:
gtk_table_attach (GTK_TABLE (table), separator,
0, 1, 0, 1,
GtkAttachOptions (0), GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (table), vbox,
1, 2, 0, 1,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
break;
case MOO_PANE_POS_TOP:
gtk_table_attach (GTK_TABLE (table), separator,
0, 1, 0, 1,
GtkAttachOptions (0), GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (table), vbox,
0, 1, 1, 2,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
break;
case MOO_PANE_POS_RIGHT:
gtk_table_attach (GTK_TABLE (table), separator,
1, 2, 0, 1,
GtkAttachOptions (0), GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (table), vbox,
0, 1, 0, 1,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
break;
case MOO_PANE_POS_BOTTOM:
gtk_table_attach (GTK_TABLE (table), separator,
0, 1, 1, 2,
GtkAttachOptions (0), GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (table), vbox,
0, 1, 0, 1,
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
break;
}
return table;
}
static GtkWidget *
create_label_widget (MooPanePosition position,
GtkWidget **label_widget,
GtkWidget **icon_widget)
{
GtkWidget *box = NULL;
g_return_val_if_fail (position < 4, NULL);
*label_widget = gtk_label_new (NULL);
switch (position)
{
case MOO_PANE_POS_LEFT:
gtk_label_set_angle (GTK_LABEL (*label_widget), 90);
break;
case MOO_PANE_POS_RIGHT:
gtk_label_set_angle (GTK_LABEL (*label_widget), 270);
break;
default:
break;
}
*icon_widget = gtk_image_new ();
switch (position)
{
case MOO_PANE_POS_LEFT:
case MOO_PANE_POS_RIGHT:
box = gtk_vbox_new (FALSE, SPACING_IN_BUTTON);
break;
default:
box = gtk_hbox_new (FALSE, SPACING_IN_BUTTON);
break;
}
switch (position)
{
case MOO_PANE_POS_LEFT:
gtk_box_pack_start (GTK_BOX (box), *label_widget, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (box), *icon_widget, FALSE, FALSE, 0);
break;
default:
gtk_box_pack_start (GTK_BOX (box), *icon_widget, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (box), *label_widget, FALSE, FALSE, 0);
break;
}
gtk_widget_show (box);
return box;
}
static void
paned_enable_detaching_notify (MooPane *pane)
{
gboolean enable;
g_object_get (pane->parent, "enable-detaching", &enable, NULL);
g_object_set (pane->detach_button, "visible", enable && pane->detachable, NULL);
}
static void
paned_sticky_pane_notify (MooPane *pane)
{
update_sticky_button (pane);
}
static void
button_drag_leave (GtkWidget *button,
G_GNUC_UNUSED GdkDragContext *context,
G_GNUC_UNUSED guint time,
MooPane *pane)
{
if (pane->open_timeout)
g_source_remove (pane->open_timeout);
pane->open_timeout = 0;
if (pane->button_highlight)
gtk_drag_unhighlight (button);
pane->button_highlight = FALSE;
}
static gboolean
drag_open_pane (MooPane *pane)
{
moo_pane_open (pane);
if (pane->button_highlight)
gtk_drag_unhighlight (pane->button);
pane->button_highlight = FALSE;
pane->open_timeout = 0;
return FALSE;
}
static gboolean
button_drag_motion (GtkWidget *button,
GdkDragContext *context,
G_GNUC_UNUSED int x,
G_GNUC_UNUSED int y,
guint time,
MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), FALSE);
if (moo_paned_is_open (pane->parent) &&
moo_paned_get_open_pane (pane->parent) == pane)
{
goto out;
}
if (pane->params->detached)
goto out;
if (!pane->button_highlight)
{
gtk_drag_highlight (button);
pane->button_highlight = TRUE;
}
if (!pane->open_timeout)
pane->open_timeout = g_timeout_add (OPEN_PANE_TIMEOUT,
(GSourceFunc) drag_open_pane,
pane);
gdk_drag_status (context, GdkDragAction (0), time);
return TRUE;
out:
if (pane->button_highlight)
gtk_drag_unhighlight (button);
pane->button_highlight = FALSE;
if (pane->open_timeout)
g_source_remove (pane->open_timeout);
pane->open_timeout = 0;
gdk_drag_status (context, GdkDragAction (0), time);
return TRUE;
}
static void
setup_button_dnd (MooPane *pane)
{
gtk_drag_dest_set (pane->button, GtkDestDefaults (0), NULL, 0, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (pane->button, "drag-motion",
G_CALLBACK (button_drag_motion), pane);
g_signal_connect (pane->button, "drag-leave",
G_CALLBACK (button_drag_leave), pane);
}
/**
* moo_pane_set_drag_dest:
**/
void
moo_pane_set_drag_dest (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
moo_pane_unset_drag_dest (pane);
pane->drag_dest_enabled = TRUE;
if (pane->button)
setup_button_dnd (pane);
}
/**
* moo_pane_unset_drag_dest:
**/
void
moo_pane_unset_drag_dest (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (pane->drag_dest_enabled && pane->button)
{
gtk_drag_dest_unset (pane->button);
g_signal_handlers_disconnect_by_func (pane->button,
(gpointer) button_drag_motion,
pane);
g_signal_handlers_disconnect_by_func (pane->button,
(gpointer) button_drag_leave,
pane);
}
if (pane->open_timeout)
g_source_remove (pane->open_timeout);
pane->open_timeout = 0;
if (pane->button_highlight)
{
gtk_drag_unhighlight (pane->button);
pane->button_highlight = FALSE;
}
}
static void
create_widgets (MooPane *pane,
MooPanePosition position,
GdkWindow *pane_window)
{
GtkWidget *label;
pane->frame = create_frame_widget (pane, position, TRUE);
update_sticky_button (pane);
gtk_widget_set_parent_window (pane->frame, pane_window);
gtk_widget_set_parent (pane->frame, GTK_WIDGET (pane->parent));
gtk_box_pack_start (GTK_BOX (pane->child_holder), pane->child, TRUE, TRUE, 0);
pane->button = gtk_toggle_button_new ();
gtk_widget_show (pane->button);
gtk_button_set_focus_on_click (GTK_BUTTON (pane->button), FALSE);
label = create_label_widget (position,
&pane->label_widget,
&pane->icon_widget);
gtk_container_add (GTK_CONTAINER (pane->button), label);
gtk_widget_show (label);
update_label_widgets (pane);
if (pane->drag_dest_enabled)
setup_button_dnd (pane);
g_object_set_data (G_OBJECT (pane->button), "moo-pane", pane);
g_object_set_data (G_OBJECT (pane->child), "moo-pane", pane);
g_object_set_data (G_OBJECT (pane->frame), "moo-pane", pane);
g_object_set_data (G_OBJECT (pane->handle), "moo-pane", pane);
g_signal_connect (pane->sticky_button, "toggled",
G_CALLBACK (sticky_button_toggled), pane);
}
MooPane *
_moo_pane_new (GtkWidget *child,
MooPaneLabel *label)
{
MooPane *pane;
g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
pane = MOO_PANE (g_object_new (MOO_TYPE_PANE, nullptr));
pane->child = GTK_WIDGET (g_object_ref (child));
gtk_widget_show (pane->child);
g_object_set_data (G_OBJECT (pane->child), "moo-pane", pane);
if (label)
moo_pane_set_label (pane, label);
return pane;
}
/**
* moo_pane_get_id:
*
* Returns: (type const-utf8)
**/
const char *
moo_pane_get_id (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return pane->id;
}
void
_moo_pane_set_id (MooPane *pane,
const char *id)
{
char *tmp;
g_return_if_fail (MOO_IS_PANE (pane));
tmp = pane->id;
pane->id = g_strdup (id);
g_free (tmp);
}
void
_moo_pane_set_parent (MooPane *pane,
gpointer parent,
GdkWindow *pane_window)
{
g_return_if_fail (MOO_IS_PANE (pane));
g_return_if_fail (MOO_IS_PANED (parent));
g_return_if_fail (pane->parent == NULL || pane->parent == parent);
g_return_if_fail (pane->child != NULL || pane->parent == parent);
if (pane->parent == parent)
{
gtk_widget_set_parent_window (pane->frame, pane_window);
}
else
{
pane->parent = MOO_PANED (parent);
create_widgets (pane, _moo_paned_get_position (pane->parent), pane_window);
g_signal_connect_swapped (parent, "notify::enable-detaching",
G_CALLBACK (paned_enable_detaching_notify),
pane);
g_signal_connect_swapped (parent, "notify::sticky-pane",
G_CALLBACK (paned_sticky_pane_notify),
pane);
}
}
void
_moo_pane_size_request (MooPane *pane,
GtkRequisition *req)
{
g_return_if_fail (MOO_IS_PANE (pane) && pane->frame != NULL);
gtk_widget_size_request (pane->frame, req);
}
void
_moo_pane_size_allocate (MooPane *pane,
GtkAllocation *allocation)
{
g_return_if_fail (MOO_IS_PANE (pane) && pane->frame != NULL);
gtk_widget_size_allocate (pane->frame, allocation);
}
void
_moo_pane_get_size_request (MooPane *pane,
GtkRequisition *req)
{
g_return_if_fail (MOO_IS_PANE (pane) && pane->frame != NULL);
gtk_widget_get_child_requisition (pane->frame, req);
}
GtkWidget *
_moo_pane_get_frame (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return pane->frame;
}
GtkWidget *
_moo_pane_get_focus_child (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return object_ref_cast_opt<GtkWidget> (pane->focus_child);
}
GtkWidget *
_moo_pane_get_button (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return pane->button;
}
void
_moo_pane_get_handle (MooPane *pane,
GtkWidget **big,
GtkWidget **small_)
{
g_return_if_fail (MOO_IS_PANE (pane));
*big = pane->handle;
*small_ = pane->small_handle;
}
GtkWidget *
_moo_pane_get_window (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return pane->window;
}
/**
* moo_pane_get_child:
**/
GtkWidget *
moo_pane_get_child (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return pane->child;
}
gpointer
_moo_pane_get_parent (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), NULL);
return pane->parent;
}
void
_moo_pane_params_changed (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (!pane->params_changed_blocked)
g_object_notify (G_OBJECT (pane), "params");
}
void
_moo_pane_freeze_params (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
pane->params_changed_blocked = TRUE;
}
void
_moo_pane_thaw_params (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
pane->params_changed_blocked = FALSE;
}
gboolean
_moo_pane_get_detached (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), FALSE);
return pane->params->detached;
}
void
_moo_pane_unparent (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (pane->parent)
{
g_signal_handlers_disconnect_by_func (pane->parent, (gpointer) paned_enable_detaching_notify, pane);
g_signal_handlers_disconnect_by_func (pane->parent, (gpointer) paned_sticky_pane_notify, pane);
pane->parent = NULL;
gtk_container_remove (GTK_CONTAINER (pane->child_holder), pane->child);
gtk_widget_unparent (pane->frame);
pane->child_holder = NULL;
pane->frame = NULL;
pane->handle = NULL;
pane->small_handle = NULL;
pane->button = NULL;
pane->label_widget = NULL;
pane->icon_widget = NULL;
pane->sticky_button = NULL;
pane->detach_button = NULL;
pane->close_button = NULL;
if (pane->window)
gtk_widget_destroy (pane->window);
pane->window = NULL;
pane->keep_on_top_button = NULL;
pane->window_child_holder = NULL;
pane->focus_child = NULL;
}
}
static GtkWidget *
find_focus (GtkWidget *widget)
{
GtkWidget *focus_child, *window;
if (!widget)
return NULL;
window = gtk_widget_get_toplevel (widget);
if (!GTK_IS_WINDOW (window))
return NULL;
focus_child = gtk_window_get_focus (GTK_WINDOW (window));
if (focus_child && gtk_widget_is_ancestor (focus_child, widget))
return focus_child;
else
return NULL;
}
void
_moo_pane_update_focus_child (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (pane->focus_child)
g_object_remove_weak_pointer (G_OBJECT (pane->focus_child), &pane->focus_child);
pane->focus_child = find_focus (pane->child);
if (pane->focus_child)
g_object_add_weak_pointer (G_OBJECT (pane->focus_child), &pane->focus_child);
}
static gboolean
pane_window_delete_event (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), FALSE);
moo_paned_attach_pane (pane->parent, pane);
return TRUE;
}
static void
keep_on_top_button_toggled (GtkToggleButton *button,
MooPane *pane)
{
gboolean active;
g_return_if_fail (MOO_IS_PANE (pane));
active = gtk_toggle_button_get_active (button);
pane->params->keep_on_top = active;
if (pane->params->keep_on_top)
{
GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET (pane->parent));
if (GTK_IS_WINDOW (parent))
gtk_window_set_transient_for (GTK_WINDOW (pane->window),
GTK_WINDOW (parent));
}
else
{
gtk_window_set_transient_for (GTK_WINDOW (pane->window), NULL);
}
_moo_pane_params_changed (pane);
}
static gboolean
pane_window_configure (GtkWidget *window,
GdkEventConfigure *event,
MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), FALSE);
g_return_val_if_fail (pane->window == window, FALSE);
pane->params->window_position.x = event->x;
pane->params->window_position.y = event->y;
pane->params->window_position.width = event->width;
pane->params->window_position.height = event->height;
_moo_pane_params_changed (pane);
return FALSE;
}
static void
create_pane_window (MooPane *pane)
{
int width = -1;
int height = -1;
GtkWidget *frame;
GtkWindow *window;
if (pane->window)
return;
pane->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
window = GTK_WINDOW (pane->window);
moo_help_connect_keys (pane->window);
set_pane_window_icon_and_title (pane);
gtk_window_set_type_hint (GTK_WINDOW (pane->window),
GDK_WINDOW_TYPE_HINT_UTILITY);
switch (_moo_paned_get_position (pane->parent))
{
case MOO_PANE_POS_LEFT:
case MOO_PANE_POS_RIGHT:
width = moo_paned_get_pane_size (pane->parent);
height = GTK_WIDGET(pane->parent)->allocation.height;
break;
case MOO_PANE_POS_TOP:
case MOO_PANE_POS_BOTTOM:
height = moo_paned_get_pane_size (pane->parent);
width = GTK_WIDGET(pane->parent)->allocation.width;
break;
}
gtk_window_set_default_size (window, width, height);
g_signal_connect_swapped (window, "delete-event",
G_CALLBACK (pane_window_delete_event), pane);
frame = create_frame_widget (pane, _moo_paned_get_position (pane->parent), FALSE);
gtk_widget_show (frame);
gtk_container_add (GTK_CONTAINER (pane->window), frame);
g_object_set_data (G_OBJECT (pane->window), "moo-pane", pane);
g_object_set_data (G_OBJECT (pane->keep_on_top_button), "moo-pane", pane);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pane->keep_on_top_button),
pane->params->keep_on_top);
g_signal_connect (pane->keep_on_top_button, "toggled",
G_CALLBACK (keep_on_top_button_toggled), pane);
g_signal_connect (pane->window, "configure-event",
G_CALLBACK (pane_window_configure), pane);
}
/* FIXME use gtk_widget_reparent(), it does work now */
static void
reparent (GtkWidget *widget,
GtkWidget *old_container,
GtkWidget *new_container)
{
g_object_ref (widget);
gtk_container_remove (GTK_CONTAINER (old_container), widget);
gtk_container_add (GTK_CONTAINER (new_container), widget);
g_object_unref (widget);
}
void
_moo_pane_detach (MooPane *pane)
{
gboolean visible;
g_return_if_fail (MOO_IS_PANE (pane));
if (pane->params->detached)
return;
pane->params->detached = TRUE;
create_pane_window (pane);
reparent (pane->child, pane->child_holder, pane->window_child_holder);
if (pane->params->keep_on_top)
{
GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET (pane->parent));
if (GTK_IS_WINDOW (parent))
gtk_window_set_transient_for (GTK_WINDOW (pane->window),
GTK_WINDOW (parent));
}
else
{
gtk_window_set_transient_for (GTK_WINDOW (pane->window), NULL);
}
if (pane->focus_child)
gtk_widget_grab_focus (GTK_WIDGET (pane->focus_child));
else
gtk_widget_child_focus (pane->child, GTK_DIR_TAB_FORWARD);
g_object_get (pane->window, "visible", &visible, NULL);
if (!visible &&
pane->params->window_position.width > 0 &&
pane->params->window_position.height > 0)
{
gtk_window_move (GTK_WINDOW (pane->window),
pane->params->window_position.x,
pane->params->window_position.y);
gtk_window_set_default_size (GTK_WINDOW (pane->window),
pane->params->window_position.width,
pane->params->window_position.height);
}
gtk_window_present (GTK_WINDOW (pane->window));
_moo_pane_params_changed (pane);
}
void
_moo_pane_attach (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
if (!pane->params->detached)
return;
pane->params->detached = FALSE;
if (pane->focus_child)
g_object_remove_weak_pointer (G_OBJECT (pane->focus_child), &pane->focus_child);
pane->focus_child = find_focus (pane->child);
if (pane->focus_child)
g_object_add_weak_pointer (G_OBJECT (pane->focus_child), &pane->focus_child);
reparent (pane->child, pane->window_child_holder, pane->child_holder);
gtk_widget_hide (pane->window);
_moo_pane_params_changed (pane);
}
void
_moo_pane_try_remove (MooPane *pane)
{
gboolean ret;
g_return_if_fail (MOO_IS_PANE (pane));
g_return_if_fail (pane->parent != NULL);
g_object_ref (pane);
g_signal_emit (pane, signals[REMOVE], 0, &ret);
if (!ret && pane->parent && pane->child)
moo_paned_remove_pane (pane->parent, pane->child);
g_object_unref (pane);
}
/**
* moo_pane_open:
**/
void
moo_pane_open (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
g_return_if_fail (pane->parent != NULL);
moo_paned_open_pane (pane->parent, pane);
}
/**
* moo_pane_present:
**/
void
moo_pane_present (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
g_return_if_fail (pane->parent != NULL);
moo_paned_present_pane (pane->parent, pane);
}
/**
* moo_pane_attach:
**/
void
moo_pane_attach (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
g_return_if_fail (pane->parent != NULL);
moo_paned_attach_pane (pane->parent, pane);
}
/**
* moo_pane_detach:
**/
void
moo_pane_detach (MooPane *pane)
{
g_return_if_fail (MOO_IS_PANE (pane));
g_return_if_fail (pane->parent != NULL);
moo_paned_detach_pane (pane->parent, pane);
}
/**
* moo_pane_get_index:
**/
int
moo_pane_get_index (MooPane *pane)
{
g_return_val_if_fail (MOO_IS_PANE (pane), -1);
if (pane->parent)
return moo_paned_get_pane_num (pane->parent, pane->child);
else
return -1;
}
typedef enum {
ICON_PIXBUFS,
ICON_ARROW_UP,
ICON_ARROW_DOWN,
ICON_ARROW_LEFT,
ICON_ARROW_RIGHT
} IconType;
typedef struct {
GtkWidget base;
GdkPixbuf **pixbufs;
const guchar *data;
IconType type;
} MooIconWidget;
typedef struct {
GtkWidgetClass base_class;
} MooIconWidgetClass;
GType _moo_icon_widget_get_type (void) G_GNUC_CONST;
#define MOO_TYPE_ICON_WIDGET (_moo_icon_widget_get_type ())
#define MOO_ICON_WIDGET(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOO_TYPE_ICON_WIDGET, MooIconWidget))
G_DEFINE_TYPE (MooIconWidget, _moo_icon_widget, GTK_TYPE_WIDGET)
static void
_moo_icon_widget_init (MooIconWidget *icon)
{
GTK_WIDGET_SET_NO_WINDOW (icon);
icon->pixbufs = NULL;
icon->data = NULL;
icon->type = ICON_PIXBUFS;
}
static void
free_pixbufs (MooIconWidget *icon)
{
if (icon->pixbufs)
{
int i;
for (i = 0; i < 5 /* magic */; ++i)
g_object_unref (icon->pixbufs[i]);
g_free (icon->pixbufs);
icon->pixbufs = NULL;
}
}
static void
moo_icon_widget_style_set (GtkWidget *widget,
GtkStyle *old_style)
{
GTK_WIDGET_CLASS (_moo_icon_widget_parent_class)->style_set (widget, old_style);
free_pixbufs ((MooIconWidget*) widget);
}
static void
moo_icon_widget_dispose (GObject *object)
{
free_pixbufs ((MooIconWidget*) object);
G_OBJECT_CLASS (_moo_icon_widget_parent_class)->dispose (object);
}
static GdkPixbuf *
get_pixbuf (MooIconWidget *icon)
{
if (!icon->pixbufs)
{
int state;
GtkWidget *widget = GTK_WIDGET (icon);
g_return_val_if_fail (icon->data != NULL, NULL);
icon->pixbufs = g_new0 (GdkPixbuf*, 5 /* magic */);
for (state = 0; state < 5 /* magic */; ++state)
{
GdkPixbuf *pixbuf;
guchar *pixels, *p;
int width, height, rowstride, n_channels;
GdkColor *color;
int x, y;
pixbuf = gdk_pixbuf_new_from_inline (-1, icon->data, TRUE, NULL);
g_return_val_if_fail (pixbuf != NULL, NULL);
icon->pixbufs[state] = pixbuf;
pixels = gdk_pixbuf_get_pixels (pixbuf);
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
n_channels = gdk_pixbuf_get_n_channels (pixbuf);
g_assert (n_channels == 4);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
color = &widget->style->fg[state];
for (x = 0; x < width; ++x)
{
for (y = 0; y < height; ++y)
{
p = pixels + y * rowstride + x * n_channels;
if (p[3] != 0)
{
p[0] = color->red >> 8;
p[1] = color->green >> 8;
p[2] = color->blue >> 8;
}
}
}
}
}
return icon->pixbufs[GTK_WIDGET_STATE (icon)];
}
static void
draw_pixbuf (GtkWidget *widget,
GdkEventExpose *event)
{
GdkPixbuf *pixbuf;
int pixbuf_width, pixbuf_height;
int x, y;
pixbuf = get_pixbuf ((MooIconWidget*) widget);
g_return_if_fail (pixbuf != NULL);
pixbuf_width = gdk_pixbuf_get_width (pixbuf);
pixbuf_height = gdk_pixbuf_get_height (pixbuf);
x = widget->allocation.x + (widget->allocation.width - pixbuf_width) / 2;
y = widget->allocation.y + (widget->allocation.height - pixbuf_height) / 2;
gdk_draw_pixbuf (event->window,
widget->style->black_gc,
pixbuf,
0, 0, x, y, pixbuf_width, pixbuf_height,
GDK_RGB_DITHER_NORMAL, 0, 0);
}
static void
draw_arrow (GtkWidget *widget,
GdkEventExpose *event)
{
GtkArrowType arrow_type;
int x, y, width, height;
switch (((MooIconWidget*)widget)->type)
{
case ICON_ARROW_UP:
arrow_type = GTK_ARROW_UP;
break;
case ICON_ARROW_DOWN:
arrow_type = GTK_ARROW_DOWN;
break;
case ICON_ARROW_LEFT:
arrow_type = GTK_ARROW_LEFT;
break;
case ICON_ARROW_RIGHT:
arrow_type = GTK_ARROW_RIGHT;
break;
default:
g_return_if_reached ();
}
width = 3 * widget->allocation.width / 4;
height = 3 * widget->allocation.height / 4;
x = widget->allocation.x + width / 6;
y = widget->allocation.y + height / 6;
gtk_paint_arrow (widget->style,
event->window,
GtkStateType (GTK_WIDGET_STATE (widget)),
GTK_SHADOW_IN,
&event->area,
widget,
NULL,
arrow_type,
TRUE,
x, y, width, height);
}
static gboolean
moo_icon_widget_expose_event (GtkWidget *widget,
GdkEventExpose *event)
{
MooIconWidget *icon = (MooIconWidget*) widget;
switch (icon->type)
{
case ICON_PIXBUFS:
draw_pixbuf (widget, event);
break;
case ICON_ARROW_UP:
case ICON_ARROW_DOWN:
case ICON_ARROW_LEFT:
case ICON_ARROW_RIGHT:
draw_arrow (widget, event);
break;
}
return FALSE;
}
static void
_moo_icon_widget_class_init (MooIconWidgetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = moo_icon_widget_dispose;
widget_class->style_set = moo_icon_widget_style_set;
widget_class->expose_event = moo_icon_widget_expose_event;
}
GtkWidget *
_moo_create_small_icon (MooSmallIcon icon)
{
MooIconWidget *icon_widget;
const guchar *data = NULL;
switch (icon)
{
case MOO_SMALL_ICON_HIDE:
data = MOO_HIDE_ICON;
break;
case MOO_SMALL_ICON_STICKY:
data = MOO_STICKY_ICON;
break;
case MOO_SMALL_ICON_CLOSE:
data = MOO_CLOSE_ICON;
break;
case MOO_SMALL_ICON_DETACH:
data = MOO_DETACH_ICON;
break;
case MOO_SMALL_ICON_ATTACH:
data = MOO_ATTACH_ICON;
break;
case MOO_SMALL_ICON_KEEP_ON_TOP:
data = MOO_KEEP_ON_TOP_ICON;
break;
}
g_return_val_if_fail (data != NULL, NULL);
icon_widget = MOO_ICON_WIDGET (g_object_new (MOO_TYPE_ICON_WIDGET, nullptr));
icon_widget->data = data;
gtk_widget_set_size_request (GTK_WIDGET (icon_widget), 7, 7 /* magic */);
return GTK_WIDGET (icon_widget);
}
GtkWidget *
_moo_create_arrow_icon (GtkArrowType arrow_type)
{
MooIconWidget *icon_widget;
IconType icon_type;
switch (arrow_type)
{
case GTK_ARROW_UP:
icon_type = ICON_ARROW_UP;
break;
case GTK_ARROW_DOWN:
icon_type = ICON_ARROW_DOWN;
break;
case GTK_ARROW_LEFT:
icon_type = ICON_ARROW_LEFT;
break;
case GTK_ARROW_RIGHT:
icon_type = ICON_ARROW_RIGHT;
break;
default:
g_return_val_if_reached (NULL);
}
icon_widget = MOO_ICON_WIDGET (g_object_new (MOO_TYPE_ICON_WIDGET, nullptr));
icon_widget->type = icon_type;
return GTK_WIDGET (icon_widget);
}