3489 lines
107 KiB
C
3489 lines
107 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*-
|
|
*
|
|
* moonotebook.c
|
|
*
|
|
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* See COPYING file that comes with this distribution.
|
|
*/
|
|
|
|
#include MOO_MARSHALS_H
|
|
#include "mooutils/moonotebook.h"
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gtk/gtk.h>
|
|
#include <string.h>
|
|
|
|
#define MIN_LABELS_WIDTH 10
|
|
#define DEFAULT_HBORDER 6
|
|
#define DEFAULT_VBORDER 3
|
|
#define LABEL_FOCUS_HEIGHT 2
|
|
#define LABEL_OVERLAP 2
|
|
#define LABEL_ALPHA 128
|
|
|
|
typedef struct {
|
|
/* XXX do something about text */
|
|
char *text;
|
|
GtkWidget *widget;
|
|
int width;
|
|
int height;
|
|
int offset;
|
|
} Label;
|
|
|
|
typedef struct {
|
|
Label *label;
|
|
GtkWidget *child;
|
|
GtkWidget *focus_child;
|
|
} Page;
|
|
|
|
typedef enum {
|
|
FOCUS_NONE = 0,
|
|
FOCUS_LEFT,
|
|
FOCUS_RIGHT,
|
|
FOCUS_ARROWS,
|
|
FOCUS_LABEL,
|
|
FOCUS_CHILD
|
|
} FocusType;
|
|
|
|
enum {
|
|
LEFT = 0,
|
|
RIGHT = 1
|
|
};
|
|
|
|
#define PopupFunc MooNotebookPopupCreationFunc
|
|
|
|
struct _MooNotebookPrivate {
|
|
GdkWindow *tab_window;
|
|
|
|
Page *current_page;
|
|
GSList *pages;
|
|
|
|
gboolean enable_popup;
|
|
PopupFunc popup_func;
|
|
gpointer popup_user_data;
|
|
|
|
gboolean enable_reordering;
|
|
gboolean button_pressed;
|
|
gboolean in_drag;
|
|
gboolean want_snapshot;
|
|
GdkPixmap *snapshot_pixmap;
|
|
GdkPixbuf *snapshot_pixbuf;
|
|
Page *drag_page;
|
|
int drag_page_index;
|
|
int drag_tab_x_delta;
|
|
int drag_tab_x;
|
|
int drag_mouse_x;
|
|
guint drag_scroll_id;
|
|
gboolean drag_scroll_right;
|
|
|
|
FocusType focus;
|
|
Page *focus_page;
|
|
|
|
gboolean show_tabs;
|
|
gboolean tabs_visible;
|
|
int tabs_height;
|
|
|
|
gboolean label_move_onscreen;
|
|
int labels_offset;
|
|
int labels_width; /* total labels width */
|
|
int labels_visible_width; /* labels window width */
|
|
guint label_vborder;
|
|
guint label_hborder;
|
|
gboolean labels_homogeneous;
|
|
|
|
GtkWidget *action_widgets[2];
|
|
int action_widgets_size[2];
|
|
|
|
gboolean arrows_gtk;
|
|
gboolean arrows_visible;
|
|
int arrows_size;
|
|
GtkWidget *arrows;
|
|
GtkWidget *left_arrow;
|
|
GtkWidget *right_arrow;
|
|
|
|
int child_height;
|
|
};
|
|
|
|
|
|
#if 0
|
|
/* XXX try to decide what these invariants are, and do check them */
|
|
#define DO_CHECK_INVARIANTS
|
|
static void NOTEBOOK_CHECK_INVARIANTS (MooNotebook *nb);
|
|
#else
|
|
#define NOTEBOOK_CHECK_INVARIANTS(nb) ((void) 0)
|
|
#endif
|
|
|
|
|
|
/*
|
|
* VISIBLE_FOREACH_START (notebook, page) {
|
|
* do_something_with_pages (notebook, page);
|
|
* } VISIBLE_FOREACH_END
|
|
*/
|
|
#define VISIBLE_FOREACH_START(nb,page) \
|
|
G_STMT_START { \
|
|
GSList *l__; \
|
|
for (l__ = nb->priv->pages; l__ != NULL; l__ = l__->next) \
|
|
{ \
|
|
Page *page = l__->data; \
|
|
if (GTK_WIDGET_VISIBLE (page->child)) \
|
|
|
|
#define VISIBLE_FOREACH_END \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
|
|
static void moo_notebook_finalize (GObject *object);
|
|
static void moo_notebook_destroy (GtkObject *object);
|
|
static void moo_notebook_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void moo_notebook_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static void moo_notebook_realize (GtkWidget *widget);
|
|
static void moo_notebook_unrealize (GtkWidget *widget);
|
|
static void moo_notebook_map (GtkWidget *widget);
|
|
static void moo_notebook_unmap (GtkWidget *widget);
|
|
|
|
static void moo_notebook_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition);
|
|
static void moo_notebook_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
|
|
static void moo_notebook_parent_set (GtkWidget *widget,
|
|
GtkWidget *previous_parent);
|
|
static gboolean moo_notebook_focus (GtkWidget *widget,
|
|
GtkDirectionType direction);
|
|
static gboolean moo_notebook_focus_in (GtkWidget *widget,
|
|
GdkEventFocus *event);
|
|
static gboolean moo_notebook_focus_out (GtkWidget *widget,
|
|
GdkEventFocus *event);
|
|
static gboolean moo_notebook_expose (GtkWidget *widget,
|
|
GdkEventExpose *event);
|
|
static void moo_notebook_draw_labels (MooNotebook *nb,
|
|
GdkEventExpose *event);
|
|
static void moo_notebook_draw_label (MooNotebook *nb,
|
|
Page *page,
|
|
GdkEventExpose *event);
|
|
static void moo_notebook_draw_dragged_label (MooNotebook *nb,
|
|
GdkEventExpose *event);
|
|
static void moo_notebook_draw_child_border (MooNotebook *nb,
|
|
GdkEventExpose *event);
|
|
|
|
// static gboolean moo_notebook_enter (GtkWidget *widget,
|
|
// GdkEventCrossing *event);
|
|
// static gboolean moo_notebook_leave (GtkWidget *widget,
|
|
// GdkEventCrossing *event);
|
|
|
|
static gboolean moo_notebook_button_press (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gboolean moo_notebook_button_release (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gboolean moo_notebook_key_press (GtkWidget *widget,
|
|
GdkEventKey *event);
|
|
static gboolean moo_notebook_motion (GtkWidget *widget,
|
|
GdkEventMotion *event);
|
|
static gboolean moo_notebook_scroll_event (GtkWidget *widget,
|
|
GdkEventScroll *event);
|
|
|
|
static void moo_notebook_forall (GtkContainer *container,
|
|
gboolean include_internals,
|
|
GtkCallback callback,
|
|
gpointer callback_data);
|
|
static void moo_notebook_add (GtkContainer *container,
|
|
GtkWidget *widget);
|
|
static void moo_notebook_remove (GtkContainer *container,
|
|
GtkWidget *widget);
|
|
static void moo_notebook_set_focus_child(GtkContainer *container,
|
|
GtkWidget *widget);
|
|
|
|
static void moo_notebook_switch_page (MooNotebook *nb,
|
|
guint page_num);
|
|
|
|
static void notebook_create_arrows (MooNotebook *nb);
|
|
static void left_arrow_clicked (GtkWidget *button,
|
|
MooNotebook *nb);
|
|
static void right_arrow_clicked (GtkWidget *button,
|
|
MooNotebook *nb);
|
|
|
|
static void child_visible_notify (GtkWidget *child,
|
|
GParamSpec *arg,
|
|
MooNotebook *nb);
|
|
static void moo_notebook_check_action_widgets (MooNotebook *nb);
|
|
static void moo_notebook_check_tabs (MooNotebook *nb);
|
|
|
|
static int page_index (MooNotebook *nb,
|
|
Page *page);
|
|
static int page_visible_index (MooNotebook *nb,
|
|
Page *page);
|
|
static int get_n_visible_pages (MooNotebook *nb);
|
|
static Page *get_nth_visible_page (MooNotebook *nb,
|
|
int n);
|
|
static int find_next_visible_page (MooNotebook *nb,
|
|
int n);
|
|
static Page *get_nth_page (MooNotebook *nb,
|
|
int n);
|
|
static Page *find_child (MooNotebook *nb,
|
|
GtkWidget *child);
|
|
static Page *find_grand_child (MooNotebook *nb,
|
|
GtkWidget *child);
|
|
static Page *find_label (MooNotebook *nb,
|
|
GtkWidget *label);
|
|
static void delete_page (MooNotebook *nb,
|
|
Page *page);
|
|
static GSList *get_visible_pages (MooNotebook *nb);
|
|
static void moo_notebook_check_arrows (MooNotebook *nb);
|
|
|
|
static void moo_notebook_set_homogeneous(MooNotebook *nb,
|
|
gboolean homogeneous);
|
|
static void labels_scroll (MooNotebook *nb,
|
|
GtkDirectionType where);
|
|
static void labels_size_request (MooNotebook *nb,
|
|
GtkRequisition *requisition);
|
|
static int labels_get_height_request (MooNotebook *nb);
|
|
static void labels_size_allocate (MooNotebook *nb,
|
|
GtkAllocation *allocation);
|
|
static void labels_scroll_to_offset (MooNotebook *nb,
|
|
int offset,
|
|
gboolean realloc);
|
|
static Page *find_label_at_x (MooNotebook *nb,
|
|
int x,
|
|
gboolean widget_only);
|
|
static Page *find_label_at_xy (MooNotebook *nb,
|
|
int x,
|
|
int y,
|
|
gboolean widget_only);
|
|
static void labels_move_label_onscreen (MooNotebook *nb,
|
|
Page *page,
|
|
gboolean realloc);
|
|
static void labels_invalidate (MooNotebook *nb);
|
|
|
|
static gboolean moo_notebook_maybe_popup (MooNotebook *notebook,
|
|
GdkEventButton *event);
|
|
static GtkWidget *popup_func (MooNotebook *notebook,
|
|
GtkWidget *child,
|
|
gpointer user_data);
|
|
|
|
/* MOO_TYPE_NOTEBOOK */
|
|
G_DEFINE_TYPE (MooNotebook, moo_notebook, GTK_TYPE_CONTAINER)
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_SHOW_TABS,
|
|
PROP_TAB_BORDER,
|
|
PROP_TAB_HBORDER,
|
|
PROP_TAB_VBORDER,
|
|
PROP_PAGE,
|
|
PROP_ENABLE_POPUP,
|
|
PROP_ENABLE_REORDERING,
|
|
PROP_HOMOGENEOUS
|
|
};
|
|
|
|
enum {
|
|
SWITCH_PAGE,
|
|
POPULATE_POPUP,
|
|
NUM_SIGNALS
|
|
};
|
|
|
|
static guint signals[NUM_SIGNALS];
|
|
|
|
static void moo_notebook_class_init (MooNotebookClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
|
|
|
|
gobject_class->finalize = moo_notebook_finalize;
|
|
gobject_class->set_property = moo_notebook_set_property;
|
|
gobject_class->get_property = moo_notebook_get_property;
|
|
|
|
gtkobject_class->destroy = moo_notebook_destroy;
|
|
|
|
widget_class->realize = moo_notebook_realize;
|
|
widget_class->unrealize = moo_notebook_unrealize;
|
|
widget_class->map = moo_notebook_map;
|
|
widget_class->unmap = moo_notebook_unmap;
|
|
widget_class->focus_in_event = moo_notebook_focus_in;
|
|
widget_class->focus_out_event = moo_notebook_focus_out;
|
|
widget_class->expose_event = moo_notebook_expose;
|
|
widget_class->size_request = moo_notebook_size_request;
|
|
widget_class->size_allocate = moo_notebook_size_allocate;
|
|
widget_class->parent_set = moo_notebook_parent_set;
|
|
// widget_class->enter_notify_event = moo_notebook_enter;
|
|
// widget_class->leave_notify_event = moo_notebook_leave;
|
|
widget_class->button_press_event = moo_notebook_button_press;
|
|
widget_class->button_release_event = moo_notebook_button_release;
|
|
widget_class->scroll_event = moo_notebook_scroll_event;
|
|
widget_class->key_press_event = moo_notebook_key_press;
|
|
widget_class->motion_notify_event = moo_notebook_motion;
|
|
widget_class->focus = moo_notebook_focus;
|
|
|
|
container_class->forall = moo_notebook_forall;
|
|
container_class->set_focus_child = moo_notebook_set_focus_child;
|
|
container_class->remove = moo_notebook_remove;
|
|
container_class->add = moo_notebook_add;
|
|
|
|
klass->switch_page = moo_notebook_switch_page;
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_PAGE,
|
|
g_param_spec_int ("page",
|
|
"Page",
|
|
"The index of the current page",
|
|
0, G_MAXINT, 0,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_TAB_BORDER,
|
|
g_param_spec_uint ("tab-border",
|
|
"Tab Border",
|
|
"Width of the border around the tab labels",
|
|
0, G_MAXUINT, 2,
|
|
G_PARAM_WRITABLE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_TAB_HBORDER,
|
|
g_param_spec_uint ("tab-hborder",
|
|
"Horizontal Tab Border",
|
|
"Width of the horizontal border of tab labels",
|
|
0, G_MAXUINT, 2,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_TAB_VBORDER,
|
|
g_param_spec_uint ("tab-vborder",
|
|
"Vertical Tab Border",
|
|
"Width of the vertical border of tab labels",
|
|
0, G_MAXUINT, 2,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SHOW_TABS,
|
|
g_param_spec_boolean ("show-tabs",
|
|
"Show Tabs",
|
|
"Whether tabs should be shown or not",
|
|
TRUE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_ENABLE_POPUP,
|
|
g_param_spec_boolean ("enable-popup",
|
|
"Enable Popup",
|
|
"If TRUE, pressing the right mouse button on the notebook pops up a menu",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_HOMOGENEOUS,
|
|
g_param_spec_boolean ("homogeneous",
|
|
"Homogeneous",
|
|
"Whether tabs should have homogeneous sizes",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_ENABLE_REORDERING,
|
|
g_param_spec_boolean ("enable-reordering",
|
|
"enable-reordering",
|
|
"enable-reordering",
|
|
TRUE,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
|
|
signals[SWITCH_PAGE] =
|
|
g_signal_new ("switch-page",
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (MooNotebookClass, switch_page),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__UINT,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_UINT);
|
|
|
|
signals[POPULATE_POPUP] =
|
|
g_signal_new ("populate-popup",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (MooNotebookClass, populate_popup),
|
|
g_signal_accumulator_true_handled, NULL,
|
|
_moo_marshal_BOOL__OBJECT_OBJECT,
|
|
G_TYPE_BOOLEAN, 2,
|
|
GTK_TYPE_WIDGET,
|
|
GTK_TYPE_MENU);
|
|
}
|
|
|
|
|
|
static void moo_notebook_init (MooNotebook *notebook)
|
|
{
|
|
GTK_WIDGET_SET_FLAGS (notebook, GTK_CAN_FOCUS);
|
|
GTK_WIDGET_SET_FLAGS (notebook, GTK_NO_WINDOW);
|
|
|
|
notebook->priv = g_new0 (MooNotebookPrivate, 1);
|
|
|
|
notebook->priv->enable_popup = FALSE;
|
|
notebook->priv->popup_func = popup_func;
|
|
notebook->priv->popup_user_data = NULL;
|
|
|
|
notebook->priv->enable_reordering = TRUE;
|
|
notebook->priv->button_pressed = FALSE;
|
|
|
|
notebook->priv->focus = FOCUS_NONE;
|
|
notebook->priv->focus_page = NULL;
|
|
|
|
notebook->priv->show_tabs = TRUE;
|
|
notebook->priv->tabs_visible = FALSE;
|
|
notebook->priv->tabs_height = 0;
|
|
|
|
notebook->priv->label_hborder = DEFAULT_HBORDER;
|
|
notebook->priv->label_vborder = DEFAULT_VBORDER;
|
|
notebook->priv->labels_homogeneous = FALSE;
|
|
|
|
notebook->priv->arrows_gtk = FALSE;
|
|
notebook->priv->arrows_visible = FALSE;
|
|
notebook->priv->arrows_size = 0;
|
|
|
|
notebook->priv->child_height = -1;
|
|
|
|
notebook_create_arrows (notebook);
|
|
}
|
|
|
|
|
|
static void notebook_create_arrows (MooNotebook *nb)
|
|
{
|
|
GtkWidget *box, *button, *arrow;
|
|
|
|
box = gtk_hbox_new (FALSE, 0);
|
|
|
|
button = gtk_button_new ();
|
|
gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
|
|
nb->priv->left_arrow = button;
|
|
arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_IN);
|
|
gtk_container_add (GTK_CONTAINER (button), arrow);
|
|
gtk_widget_show (button);
|
|
gtk_widget_show (arrow);
|
|
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
|
|
|
|
button = gtk_button_new ();
|
|
gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
|
|
nb->priv->right_arrow = button;
|
|
arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_IN);
|
|
gtk_container_add (GTK_CONTAINER (button), arrow);
|
|
gtk_widget_show (button);
|
|
gtk_widget_show (arrow);
|
|
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
|
|
|
|
nb->priv->arrows = box;
|
|
g_object_ref (box);
|
|
gtk_object_sink (GTK_OBJECT (box));
|
|
gtk_widget_set_parent (box, GTK_WIDGET (nb));
|
|
|
|
g_signal_connect (nb->priv->left_arrow, "clicked",
|
|
G_CALLBACK (left_arrow_clicked), nb);
|
|
g_signal_connect (nb->priv->right_arrow, "clicked",
|
|
G_CALLBACK (right_arrow_clicked), nb);
|
|
|
|
gtk_widget_set_sensitive (nb->priv->right_arrow, FALSE);
|
|
gtk_widget_set_sensitive (nb->priv->left_arrow, FALSE);
|
|
}
|
|
|
|
|
|
static void moo_notebook_destroy (GtkObject *object)
|
|
{
|
|
GSList *l;
|
|
MooNotebook *nb = MOO_NOTEBOOK (object);
|
|
|
|
for (l = nb->priv->pages; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
|
|
g_signal_handlers_disconnect_by_func (page->child,
|
|
(gpointer) child_visible_notify,
|
|
nb);
|
|
gtk_widget_unparent (page->child);
|
|
gtk_widget_destroy (page->child);
|
|
g_object_unref (page->child);
|
|
|
|
gtk_widget_unparent (page->label->widget);
|
|
gtk_widget_destroy (page->label->widget);
|
|
g_object_unref (page->label->widget);
|
|
|
|
g_free (page->label->text);
|
|
g_free (page->label);
|
|
g_free (page);
|
|
}
|
|
|
|
g_slist_free (nb->priv->pages);
|
|
nb->priv->pages = NULL;
|
|
nb->priv->current_page = NULL;
|
|
|
|
GTK_OBJECT_CLASS(moo_notebook_parent_class)->destroy (object);
|
|
}
|
|
|
|
|
|
static void moo_notebook_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_TAB_BORDER:
|
|
nb->priv->label_vborder = g_value_get_uint (value);
|
|
nb->priv->label_hborder = g_value_get_uint (value);
|
|
if (GTK_WIDGET_VISIBLE (nb) && nb->priv->tabs_visible)
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
g_object_freeze_notify (object);
|
|
g_object_notify (object, "tab-hborder");
|
|
g_object_notify (object, "tab-vborder");
|
|
g_object_thaw_notify (object);
|
|
break;
|
|
|
|
case PROP_TAB_HBORDER:
|
|
nb->priv->label_hborder = g_value_get_uint (value);
|
|
if (GTK_WIDGET_VISIBLE (nb) && nb->priv->tabs_visible)
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
g_object_notify (object, "tab-hborder");
|
|
break;
|
|
|
|
case PROP_TAB_VBORDER:
|
|
nb->priv->label_vborder = g_value_get_uint (value);
|
|
if (GTK_WIDGET_VISIBLE (nb) && nb->priv->tabs_visible)
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
g_object_notify (object, "tab-vborder");
|
|
break;
|
|
|
|
case PROP_PAGE:
|
|
moo_notebook_set_current_page (nb, g_value_get_int (value));
|
|
break;
|
|
|
|
case PROP_ENABLE_POPUP:
|
|
moo_notebook_enable_popup (nb, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_HOMOGENEOUS:
|
|
moo_notebook_set_homogeneous (nb, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_ENABLE_REORDERING:
|
|
moo_notebook_enable_reordering (nb, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_SHOW_TABS:
|
|
moo_notebook_set_show_tabs (nb, g_value_get_boolean (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
|
|
static void moo_notebook_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_TAB_HBORDER:
|
|
g_value_set_uint (value, nb->priv->label_hborder);
|
|
break;
|
|
case PROP_TAB_VBORDER:
|
|
g_value_set_uint (value, nb->priv->label_vborder);
|
|
break;
|
|
case PROP_PAGE:
|
|
g_value_set_int (value, moo_notebook_get_current_page (nb));
|
|
break;
|
|
case PROP_ENABLE_POPUP:
|
|
g_value_set_boolean (value, nb->priv->enable_popup);
|
|
break;
|
|
case PROP_HOMOGENEOUS:
|
|
g_value_set_boolean (value, nb->priv->labels_homogeneous);
|
|
break;
|
|
case PROP_ENABLE_REORDERING:
|
|
g_value_set_boolean (value, nb->priv->enable_reordering);
|
|
break;
|
|
case PROP_SHOW_TABS:
|
|
g_value_set_boolean (value, nb->priv->show_tabs);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
|
|
static void moo_notebook_finalize (GObject *object)
|
|
{
|
|
MooNotebook *notebook = MOO_NOTEBOOK (object);
|
|
|
|
g_object_unref (notebook->priv->arrows);
|
|
|
|
/* XXX */
|
|
|
|
g_free (notebook->priv);
|
|
G_OBJECT_CLASS (moo_notebook_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
GtkWidget *moo_notebook_new (void)
|
|
{
|
|
return GTK_WIDGET (g_object_new (MOO_TYPE_NOTEBOOK, NULL));
|
|
}
|
|
|
|
|
|
static void labels_size_request (MooNotebook *nb,
|
|
GtkRequisition *requisition)
|
|
{
|
|
GtkRequisition child_req;
|
|
|
|
g_assert (nb->priv->tabs_visible);
|
|
|
|
requisition->width = 0;
|
|
requisition->height = 0;
|
|
|
|
VISIBLE_FOREACH_START (nb, page)
|
|
{
|
|
gtk_widget_size_request (page->label->widget, &child_req);
|
|
requisition->height = MAX (requisition->height, child_req.height);
|
|
}
|
|
VISIBLE_FOREACH_END;
|
|
|
|
requisition->width = MIN_LABELS_WIDTH;
|
|
requisition->height += 2 * nb->priv->label_vborder + LABEL_FOCUS_HEIGHT;
|
|
}
|
|
|
|
|
|
#define MAKE_POSITIVE(a,b) \
|
|
G_STMT_START { \
|
|
a = MAX (a, 0); \
|
|
b = MAX (b, 0); \
|
|
} G_STMT_END
|
|
|
|
static void moo_notebook_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition)
|
|
{
|
|
GtkRequisition child_req;
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
int border_width = gtk_container_get_border_width (GTK_CONTAINER (nb));
|
|
int xthickness = widget->style->xthickness;
|
|
int ythickness = widget->style->ythickness;
|
|
|
|
NOTEBOOK_CHECK_INVARIANTS (nb);
|
|
|
|
requisition->width = 2 * border_width;
|
|
requisition->height = 2 * border_width;
|
|
|
|
if (nb->priv->current_page)
|
|
{
|
|
gtk_widget_size_request (nb->priv->current_page->child, requisition);
|
|
requisition->width = MAX (0, requisition->width);
|
|
requisition->height = MAX (0, requisition->height);
|
|
requisition->width += 2*border_width + 2*xthickness;
|
|
requisition->height += 2*border_width + 2*ythickness;
|
|
}
|
|
|
|
if (nb->priv->tabs_visible)
|
|
{
|
|
GtkRequisition action_req;
|
|
|
|
labels_size_request (nb, &child_req);
|
|
|
|
if (nb->priv->action_widgets[LEFT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[LEFT]))
|
|
{
|
|
gtk_widget_size_request (nb->priv->action_widgets[LEFT],
|
|
&action_req);
|
|
MAKE_POSITIVE (action_req.width, action_req.height);
|
|
child_req.width += action_req.width;
|
|
child_req.height = MAX (child_req.height, action_req.height);
|
|
}
|
|
|
|
if (nb->priv->action_widgets[RIGHT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[RIGHT]))
|
|
{
|
|
gtk_widget_size_request (nb->priv->action_widgets[RIGHT],
|
|
&action_req);
|
|
MAKE_POSITIVE (action_req.width, action_req.height);
|
|
child_req.width += action_req.width;
|
|
child_req.height = MAX (child_req.height, action_req.height);
|
|
}
|
|
|
|
if (nb->priv->arrows_visible)
|
|
{
|
|
gtk_widget_size_request (nb->priv->arrows, &action_req);
|
|
MAKE_POSITIVE (action_req.width, action_req.height);
|
|
child_req.width += action_req.width;
|
|
child_req.height = MAX (child_req.height, action_req.height);
|
|
}
|
|
|
|
if (child_req.width > 0)
|
|
requisition->width = MAX (requisition->width, child_req.width);
|
|
if (child_req.height > 0)
|
|
requisition->height += child_req.height;
|
|
}
|
|
}
|
|
|
|
|
|
static int labels_get_height_request (MooNotebook *nb)
|
|
{
|
|
int height = 0;
|
|
GtkRequisition child_req;
|
|
gboolean has_visible = FALSE;
|
|
|
|
g_assert (nb->priv->tabs_visible);
|
|
|
|
VISIBLE_FOREACH_START (nb, page)
|
|
{
|
|
gtk_widget_get_child_requisition (page->label->widget,
|
|
&child_req);
|
|
height = MAX (height, child_req.height);
|
|
has_visible = TRUE;
|
|
}
|
|
VISIBLE_FOREACH_END;
|
|
|
|
if (!has_visible)
|
|
return 0;
|
|
else
|
|
return height + 2 * nb->priv->label_vborder + LABEL_FOCUS_HEIGHT;
|
|
}
|
|
|
|
|
|
static int get_tab_window_width (MooNotebook *nb)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (nb);
|
|
return widget->allocation.width - nb->priv->action_widgets_size[LEFT] -
|
|
nb->priv->arrows_size - nb->priv->action_widgets_size[RIGHT] -
|
|
2 * gtk_container_get_border_width (GTK_CONTAINER (nb));
|
|
}
|
|
|
|
|
|
static void moo_notebook_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
GtkAllocation child_allocation, tabs_allocation;
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
int border_width = gtk_container_get_border_width (GTK_CONTAINER (nb));
|
|
int xthickness = widget->style->xthickness;
|
|
int ythickness = widget->style->ythickness;
|
|
|
|
NOTEBOOK_CHECK_INVARIANTS (nb);
|
|
|
|
widget->allocation = *allocation;
|
|
|
|
if (nb->priv->tabs_visible)
|
|
{
|
|
int height;
|
|
GtkRequisition left_req, right_req, arrows_req;
|
|
|
|
/* only reports height needed for tab labels */
|
|
height = labels_get_height_request (nb);
|
|
|
|
if (nb->priv->action_widgets[LEFT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[LEFT]))
|
|
{
|
|
gtk_widget_get_child_requisition (nb->priv->action_widgets[LEFT],
|
|
&left_req);
|
|
MAKE_POSITIVE (left_req.width, left_req.height);
|
|
height = MAX (left_req.height, height);
|
|
}
|
|
|
|
if (nb->priv->action_widgets[RIGHT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[RIGHT]))
|
|
{
|
|
gtk_widget_get_child_requisition (nb->priv->action_widgets[RIGHT],
|
|
&right_req);
|
|
MAKE_POSITIVE (right_req.width, right_req.height);
|
|
height = MAX (right_req.height, height);
|
|
}
|
|
|
|
if (nb->priv->arrows_visible)
|
|
{
|
|
gtk_widget_get_child_requisition (nb->priv->arrows,
|
|
&arrows_req);
|
|
MAKE_POSITIVE (arrows_req.width, arrows_req.height);
|
|
height = MAX (arrows_req.height, height);
|
|
}
|
|
|
|
nb->priv->tabs_height = CLAMP (height, 0, allocation->height - 2*border_width);
|
|
|
|
if (nb->priv->action_widgets[LEFT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[LEFT]))
|
|
{
|
|
nb->priv->action_widgets_size[LEFT] =
|
|
CLAMP (left_req.width, 0, allocation->width - 2*border_width);
|
|
}
|
|
|
|
if (nb->priv->action_widgets[RIGHT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[RIGHT]))
|
|
{
|
|
nb->priv->action_widgets_size[RIGHT] =
|
|
CLAMP (right_req.width, 0,
|
|
allocation->width - nb->priv->action_widgets_size[LEFT] - 2*border_width);
|
|
}
|
|
|
|
if (nb->priv->arrows_visible)
|
|
{
|
|
nb->priv->arrows_size =
|
|
CLAMP (arrows_req.width, 0,
|
|
allocation->width - nb->priv->action_widgets_size[LEFT]
|
|
- nb->priv->action_widgets_size[RIGHT] - 2*border_width);
|
|
}
|
|
}
|
|
|
|
nb->priv->child_height = allocation->height - nb->priv->tabs_height - 2*border_width;
|
|
|
|
tabs_allocation.x = 0;
|
|
tabs_allocation.y = 0;
|
|
tabs_allocation.width = get_tab_window_width (nb);
|
|
tabs_allocation.height = nb->priv->tabs_height;
|
|
|
|
if (GTK_WIDGET_REALIZED (widget) && nb->priv->tabs_visible)
|
|
gdk_window_move_resize (nb->priv->tab_window,
|
|
allocation->x + border_width + nb->priv->action_widgets_size[LEFT],
|
|
allocation->y + border_width,
|
|
tabs_allocation.width,
|
|
tabs_allocation.height);
|
|
|
|
if (nb->priv->tabs_visible)
|
|
{
|
|
if (nb->priv->action_widgets[LEFT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[LEFT]))
|
|
{
|
|
child_allocation.x = allocation->x + border_width;
|
|
child_allocation.y = allocation->y + border_width;
|
|
child_allocation.width = nb->priv->action_widgets_size[LEFT];
|
|
child_allocation.height = nb->priv->tabs_height;
|
|
gtk_widget_size_allocate (nb->priv->action_widgets[LEFT],
|
|
&child_allocation);
|
|
}
|
|
|
|
if (nb->priv->action_widgets[RIGHT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[RIGHT]))
|
|
{
|
|
child_allocation.x = allocation->x + allocation->width -
|
|
nb->priv->action_widgets_size[RIGHT] -
|
|
border_width;
|
|
child_allocation.y = allocation->y + border_width;
|
|
child_allocation.width = nb->priv->action_widgets_size[RIGHT];
|
|
child_allocation.height = nb->priv->tabs_height;
|
|
gtk_widget_size_allocate (nb->priv->action_widgets[RIGHT],
|
|
&child_allocation);
|
|
}
|
|
|
|
if (nb->priv->arrows_visible)
|
|
{
|
|
child_allocation.x = allocation->x + allocation->width -
|
|
nb->priv->action_widgets_size[RIGHT] -
|
|
nb->priv->arrows_size - border_width;
|
|
child_allocation.y = allocation->y + border_width;
|
|
child_allocation.width = nb->priv->arrows_size;
|
|
child_allocation.height = nb->priv->tabs_height;
|
|
gtk_widget_size_allocate (nb->priv->arrows, &child_allocation);
|
|
}
|
|
|
|
labels_size_allocate (nb, &tabs_allocation);
|
|
}
|
|
|
|
if (nb->priv->current_page)
|
|
{
|
|
int border_width = gtk_container_get_border_width (GTK_CONTAINER (nb));
|
|
child_allocation.x = allocation->x + border_width + xthickness;
|
|
child_allocation.y = allocation->y + nb->priv->tabs_height + border_width + ythickness;
|
|
child_allocation.width = MAX (0, allocation->width - 2*border_width - 2*xthickness);
|
|
child_allocation.height = MAX (0, nb->priv->child_height - 2*ythickness);
|
|
gtk_widget_size_allocate (nb->priv->current_page->child,
|
|
&child_allocation);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
mangle_class_name (char *string)
|
|
{
|
|
g_return_if_fail (string != NULL);
|
|
|
|
while ((string = strstr (string, "MooNotebook")))
|
|
{
|
|
string[0] = 'G';
|
|
string[1] = 't';
|
|
string[2] = 'k';
|
|
string += strlen ("MooNotebook");
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
update_notebook_style (GtkWidget *widget)
|
|
{
|
|
GtkStyle *style;
|
|
GtkSettings *settings;
|
|
char *path, *class_path;
|
|
|
|
if (!gtk_widget_has_screen (widget))
|
|
return;
|
|
|
|
settings = gtk_widget_get_settings (widget);
|
|
|
|
gtk_widget_path (widget, NULL, &path, NULL);
|
|
mangle_class_name (path);
|
|
gtk_widget_class_path (widget, NULL, &class_path, NULL);
|
|
mangle_class_name (class_path);
|
|
|
|
style = gtk_rc_get_style_by_paths (settings, path, class_path,
|
|
GTK_TYPE_NOTEBOOK);
|
|
gtk_widget_set_style (widget, style);
|
|
|
|
g_free (path);
|
|
g_free (class_path);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_notebook_parent_set (GtkWidget *widget,
|
|
G_GNUC_UNUSED GtkWidget *previous_parent)
|
|
{
|
|
update_notebook_style (widget);
|
|
}
|
|
|
|
|
|
static void moo_notebook_realize (GtkWidget *widget)
|
|
{
|
|
static GdkWindowAttr attributes;
|
|
gint attributes_mask;
|
|
MooNotebook *nb;
|
|
GSList *l;
|
|
int border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
|
|
|
|
nb = MOO_NOTEBOOK (widget);
|
|
|
|
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
|
|
|
|
widget->window = gtk_widget_get_parent_window (widget);
|
|
g_object_ref (widget->window);
|
|
|
|
widget->style = gtk_style_attach (widget->style, widget->window);
|
|
|
|
/* Tabs window */
|
|
attributes.x = widget->allocation.x + border_width + nb->priv->action_widgets_size[LEFT];
|
|
attributes.y = widget->allocation.y + border_width;
|
|
attributes.width = widget->allocation.width - nb->priv->action_widgets_size[LEFT] -
|
|
nb->priv->arrows_size - nb->priv->action_widgets_size[RIGHT] -
|
|
2*border_width;
|
|
attributes.height = nb->priv->tabs_height;
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.event_mask = gtk_widget_get_events (widget) |
|
|
GDK_EXPOSURE_MASK |
|
|
GDK_POINTER_MOTION_MASK |
|
|
GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK |
|
|
GDK_KEY_PRESS_MASK |
|
|
GDK_KEY_RELEASE_MASK |
|
|
GDK_ENTER_NOTIFY_MASK |
|
|
GDK_LEAVE_NOTIFY_MASK |
|
|
GDK_SCROLL_MASK;
|
|
|
|
attributes.visual = gtk_widget_get_visual (widget);
|
|
attributes.colormap = gtk_widget_get_colormap (widget);
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
|
|
|
|
nb->priv->tab_window = gdk_window_new (widget->window, &attributes, attributes_mask);
|
|
gdk_window_set_user_data (nb->priv->tab_window, widget);
|
|
|
|
update_notebook_style (widget);
|
|
gtk_style_set_background (widget->style, nb->priv->tab_window, GTK_STATE_NORMAL);
|
|
|
|
for (l = nb->priv->pages; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
gtk_widget_set_parent_window (page->label->widget, nb->priv->tab_window);
|
|
}
|
|
}
|
|
|
|
|
|
static void moo_notebook_unrealize (GtkWidget *widget)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
|
|
gdk_window_set_user_data (nb->priv->tab_window, NULL);
|
|
gdk_window_destroy (nb->priv->tab_window);
|
|
nb->priv->tab_window = NULL;
|
|
|
|
GTK_WIDGET_CLASS(moo_notebook_parent_class)->unrealize (widget);
|
|
}
|
|
|
|
|
|
static void moo_notebook_map (GtkWidget *widget)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
GtkWidget *left = nb->priv->action_widgets[LEFT];
|
|
GtkWidget *right = nb->priv->action_widgets[RIGHT];
|
|
|
|
g_return_if_fail (GTK_WIDGET_REALIZED (widget) == TRUE);
|
|
|
|
if (GTK_WIDGET_MAPPED (widget))
|
|
return;
|
|
|
|
GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
|
|
|
|
if (nb->priv->tabs_visible)
|
|
{
|
|
gdk_window_show (nb->priv->tab_window);
|
|
|
|
if (left && GTK_WIDGET_VISIBLE (left))
|
|
gtk_widget_map (left);
|
|
|
|
if (right && GTK_WIDGET_VISIBLE (right))
|
|
gtk_widget_map (right);
|
|
|
|
if (nb->priv->arrows_visible)
|
|
gtk_widget_map (nb->priv->arrows);
|
|
|
|
VISIBLE_FOREACH_START (nb, page)
|
|
{
|
|
gtk_widget_map (page->label->widget);
|
|
}
|
|
VISIBLE_FOREACH_END;
|
|
}
|
|
|
|
if (nb->priv->current_page)
|
|
gtk_widget_map (nb->priv->current_page->child);
|
|
}
|
|
|
|
|
|
static void moo_notebook_unmap (GtkWidget *widget)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
GtkWidget *left = nb->priv->action_widgets[LEFT];
|
|
GtkWidget *right = nb->priv->action_widgets[RIGHT];
|
|
|
|
if (!GTK_WIDGET_MAPPED (widget))
|
|
return;
|
|
|
|
if (nb->priv->current_page)
|
|
gtk_widget_unmap (nb->priv->current_page->child);
|
|
|
|
VISIBLE_FOREACH_START (nb, page)
|
|
{
|
|
gtk_widget_map (page->label->widget);
|
|
}
|
|
VISIBLE_FOREACH_END;
|
|
|
|
if (nb->priv->arrows_visible)
|
|
gtk_widget_unmap (nb->priv->arrows);
|
|
|
|
if (right && GTK_WIDGET_MAPPED (right))
|
|
gtk_widget_unmap (right);
|
|
|
|
if (left && GTK_WIDGET_MAPPED (left))
|
|
gtk_widget_unmap (left);
|
|
|
|
gdk_window_hide (nb->priv->tab_window);
|
|
|
|
GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
|
|
}
|
|
|
|
|
|
static void moo_notebook_forall (GtkContainer *container,
|
|
gboolean include_internals,
|
|
GtkCallback callback,
|
|
gpointer callback_data)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (container);
|
|
GSList *l;
|
|
|
|
for (l = nb->priv->pages; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
callback (page->child, callback_data);
|
|
if (include_internals && page != nb->priv->drag_page)
|
|
callback (page->label->widget, callback_data);
|
|
}
|
|
|
|
if (include_internals)
|
|
{
|
|
if (nb->priv->action_widgets[LEFT])
|
|
callback (nb->priv->action_widgets[LEFT], callback_data);
|
|
if (nb->priv->action_widgets[RIGHT])
|
|
callback (nb->priv->action_widgets[RIGHT], callback_data);
|
|
callback (nb->priv->arrows, callback_data);
|
|
}
|
|
}
|
|
|
|
|
|
static void moo_notebook_draw_child_border (MooNotebook *nb,
|
|
GdkEventExpose *event)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (nb);
|
|
Page *page = nb->priv->current_page;
|
|
int border_width = gtk_container_get_border_width (GTK_CONTAINER (nb));
|
|
gboolean draw_gap = TRUE;
|
|
int gap_x, gap_width;
|
|
|
|
if (!page)
|
|
return;
|
|
|
|
if (nb->priv->in_drag || !nb->priv->tabs_visible)
|
|
{
|
|
draw_gap = FALSE;
|
|
}
|
|
else
|
|
{
|
|
int tab_width = get_tab_window_width (nb);
|
|
|
|
gap_x = page->label->offset - nb->priv->labels_offset;
|
|
gap_width = page->label->width;
|
|
|
|
if (gap_x < 0)
|
|
{
|
|
if (gap_x + gap_width <= 0)
|
|
{
|
|
draw_gap = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gap_width += gap_x;
|
|
gap_x = 0;
|
|
}
|
|
}
|
|
else if (gap_x >= tab_width)
|
|
{
|
|
draw_gap = FALSE;
|
|
}
|
|
else if (gap_x + gap_width >= tab_width)
|
|
{
|
|
gap_width = tab_width - gap_x;
|
|
}
|
|
|
|
gap_x += nb->priv->action_widgets_size[LEFT];
|
|
}
|
|
|
|
if (draw_gap)
|
|
{
|
|
gtk_paint_box_gap (widget->style,
|
|
event->window,
|
|
GTK_STATE_NORMAL,
|
|
GTK_SHADOW_OUT,
|
|
&event->area,
|
|
widget,
|
|
(char*) "notebook",
|
|
widget->allocation.x + border_width,
|
|
widget->allocation.y + border_width + nb->priv->tabs_height,
|
|
widget->allocation.width - 2*border_width,
|
|
nb->priv->child_height,
|
|
GTK_POS_TOP,
|
|
gap_x, gap_width);
|
|
}
|
|
else
|
|
{
|
|
gtk_paint_box (widget->style,
|
|
event->window,
|
|
GTK_STATE_NORMAL,
|
|
GTK_SHADOW_OUT,
|
|
&event->area,
|
|
widget,
|
|
(char*) "notebook",
|
|
widget->allocation.x + border_width,
|
|
widget->allocation.y + border_width + nb->priv->tabs_height,
|
|
widget->allocation.width - 2*border_width,
|
|
nb->priv->child_height);
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean moo_notebook_expose (GtkWidget *widget,
|
|
GdkEventExpose *event)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
|
|
if (event->window == nb->priv->tab_window)
|
|
moo_notebook_draw_labels (nb, event);
|
|
|
|
if (event->window == widget->window)
|
|
moo_notebook_draw_child_border (nb, event);
|
|
|
|
GTK_WIDGET_CLASS(moo_notebook_parent_class)->expose_event (widget, event);
|
|
|
|
if (nb->priv->in_drag && event->window == nb->priv->tab_window)
|
|
moo_notebook_draw_dragged_label (nb, event);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
moo_notebook_focus_in (GtkWidget *widget,
|
|
G_GNUC_UNUSED GdkEventFocus *event)
|
|
{
|
|
/* XXX invalidate only one label, not whole tab window */
|
|
if (MOO_NOTEBOOK(widget)->priv->focus_page)
|
|
labels_invalidate (MOO_NOTEBOOK (widget));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
moo_notebook_focus_out (GtkWidget *widget,
|
|
G_GNUC_UNUSED GdkEventFocus *event)
|
|
{
|
|
/* XXX invalidate only one label, not whole tab window */
|
|
if (MOO_NOTEBOOK(widget)->priv->focus_page)
|
|
labels_invalidate (MOO_NOTEBOOK (widget));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void moo_notebook_add (GtkContainer *container,
|
|
GtkWidget *widget)
|
|
{
|
|
moo_notebook_insert_page (MOO_NOTEBOOK (container), widget, NULL, -1);
|
|
}
|
|
|
|
|
|
/* XXX focus */
|
|
static void moo_notebook_remove (GtkContainer *container,
|
|
GtkWidget *widget)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (container);
|
|
Page *page;
|
|
|
|
g_return_if_fail (widget != nb->priv->arrows);
|
|
|
|
if (nb->priv->action_widgets[LEFT] == widget ||
|
|
nb->priv->action_widgets[RIGHT] == widget)
|
|
{
|
|
if (nb->priv->action_widgets[LEFT] == widget)
|
|
nb->priv->action_widgets[LEFT] = NULL;
|
|
else
|
|
nb->priv->action_widgets[RIGHT] = NULL;
|
|
|
|
gtk_widget_unparent (widget);
|
|
g_object_unref (widget);
|
|
|
|
moo_notebook_check_action_widgets (nb);
|
|
}
|
|
else if ((page = find_child (nb, widget)))
|
|
{
|
|
g_signal_handlers_disconnect_by_func (widget,
|
|
(gpointer) child_visible_notify,
|
|
nb);
|
|
gtk_widget_unparent (widget);
|
|
g_object_unref (widget);
|
|
gtk_widget_unparent (page->label->widget);
|
|
g_object_unref (page->label->widget);
|
|
|
|
delete_page (nb, page);
|
|
}
|
|
else if ((page = find_label (nb, widget)))
|
|
{
|
|
gtk_widget_unparent (widget);
|
|
g_object_unref (widget);
|
|
|
|
g_free (page->label->text);
|
|
page->label->text = g_strdup_printf ("Page %d", page_index (nb, page));
|
|
widget = gtk_label_new (page->label->text);
|
|
|
|
g_object_ref (widget);
|
|
gtk_object_sink (GTK_OBJECT (widget));
|
|
|
|
if (GTK_WIDGET_REALIZED (nb))
|
|
gtk_widget_set_parent_window (widget, nb->priv->tab_window);
|
|
|
|
gtk_widget_set_parent (widget, GTK_WIDGET (nb));
|
|
}
|
|
else
|
|
{
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
}
|
|
|
|
|
|
static void child_visible_notify (GtkWidget *child,
|
|
G_GNUC_UNUSED GParamSpec *arg,
|
|
MooNotebook *nb)
|
|
{
|
|
Page *page;
|
|
|
|
if ((page = find_child (nb, child)))
|
|
{
|
|
if (GTK_WIDGET_VISIBLE (child))
|
|
{
|
|
if (nb->priv->tabs_visible)
|
|
{
|
|
gtk_widget_show (page->label->widget);
|
|
GTK_WIDGET_SET_FLAGS (nb, GTK_CAN_FOCUS);
|
|
}
|
|
|
|
if (!nb->priv->current_page)
|
|
nb->priv->current_page = page;
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_hide (page->label->widget);
|
|
if (nb->priv->current_page == page)
|
|
{
|
|
int n;
|
|
nb->priv->current_page = NULL;
|
|
n = find_next_visible_page (nb, page_index (nb, page));
|
|
if (n >= 0)
|
|
moo_notebook_set_current_page (nb, n);
|
|
else
|
|
GTK_WIDGET_UNSET_FLAGS (nb, GTK_CAN_FOCUS);
|
|
}
|
|
}
|
|
}
|
|
|
|
moo_notebook_check_action_widgets (nb);
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
}
|
|
|
|
|
|
void moo_notebook_set_show_tabs (MooNotebook *notebook,
|
|
gboolean show_tabs)
|
|
{
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
|
|
if (show_tabs == notebook->priv->show_tabs)
|
|
return;
|
|
|
|
notebook->priv->show_tabs = show_tabs;
|
|
moo_notebook_check_tabs (notebook);
|
|
gtk_widget_queue_resize (GTK_WIDGET (notebook));
|
|
|
|
g_object_notify (G_OBJECT (notebook), "show-tabs");
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* Children
|
|
*/
|
|
|
|
static int page_index (MooNotebook *nb,
|
|
Page *page)
|
|
{
|
|
g_return_val_if_fail (page != NULL, -1);
|
|
return g_slist_index (nb->priv->pages, page);
|
|
}
|
|
|
|
|
|
static int page_visible_index (MooNotebook *nb,
|
|
Page *page)
|
|
{
|
|
int i = 0;
|
|
|
|
g_return_val_if_fail (page != NULL, -1);
|
|
|
|
VISIBLE_FOREACH_START (nb, p) {
|
|
if (p == page)
|
|
return i;
|
|
else
|
|
i++;
|
|
} VISIBLE_FOREACH_END;
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int get_n_visible_pages (MooNotebook *nb)
|
|
{
|
|
int n = 0;
|
|
|
|
VISIBLE_FOREACH_START (nb, p) {
|
|
n++;
|
|
} VISIBLE_FOREACH_END;
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
static Page *get_nth_visible_page (MooNotebook *nb,
|
|
int n)
|
|
{
|
|
int i = 0;
|
|
|
|
VISIBLE_FOREACH_START (nb, page) {
|
|
if (i == n)
|
|
return page;
|
|
else
|
|
i++;
|
|
} VISIBLE_FOREACH_END;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* XXX focus */
|
|
gint moo_notebook_insert_page (MooNotebook *nb,
|
|
GtkWidget *child,
|
|
GtkWidget *label,
|
|
gint position)
|
|
{
|
|
Page *page;
|
|
|
|
g_return_val_if_fail (MOO_IS_NOTEBOOK (nb), -1);
|
|
g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
|
|
g_return_val_if_fail (child->parent == NULL, -1);
|
|
g_return_val_if_fail (!label || GTK_IS_WIDGET (label), -1);
|
|
|
|
/* XXX GTK_WIDGET_SET_FLAGS (nb, GTK_CAN_FOCUS); */
|
|
|
|
if (position < 0 || position > moo_notebook_get_n_pages (nb))
|
|
position = moo_notebook_get_n_pages (nb);
|
|
|
|
page = g_new0 (Page, 1);
|
|
page->child = child;
|
|
page->label = g_new0 (Label, 1);
|
|
nb->priv->pages = g_slist_insert (nb->priv->pages, page, position);
|
|
|
|
if (!label)
|
|
{
|
|
page->label->text = g_strdup_printf ("Page %d", position);
|
|
label = gtk_label_new (page->label->text);
|
|
}
|
|
|
|
page->label->widget = label;
|
|
|
|
g_object_ref (child);
|
|
gtk_object_sink (GTK_OBJECT (child));
|
|
g_object_ref (label);
|
|
gtk_object_sink (GTK_OBJECT (label));
|
|
|
|
if (GTK_WIDGET_REALIZED (nb))
|
|
gtk_widget_set_parent_window (label, nb->priv->tab_window);
|
|
|
|
gtk_widget_set_parent (child, GTK_WIDGET (nb));
|
|
gtk_widget_set_parent (label, GTK_WIDGET (nb));
|
|
|
|
if (GTK_WIDGET_VISIBLE (child) && nb->priv->tabs_visible)
|
|
{
|
|
/* XXX do soemthing about tabs */
|
|
GTK_WIDGET_SET_FLAGS (nb, GTK_CAN_FOCUS);
|
|
gtk_widget_show (label);
|
|
}
|
|
|
|
g_signal_connect (child, "notify::visible",
|
|
G_CALLBACK (child_visible_notify), nb);
|
|
|
|
if (!nb->priv->current_page && GTK_WIDGET_VISIBLE (child))
|
|
moo_notebook_set_current_page (nb, position);
|
|
|
|
moo_notebook_check_tabs (nb);
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
|
|
return position;
|
|
}
|
|
|
|
|
|
void moo_notebook_reorder_child (MooNotebook *notebook,
|
|
GtkWidget *child,
|
|
gint position)
|
|
{
|
|
Page *page;
|
|
int n_pages;
|
|
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
|
|
page = find_child (notebook, child);
|
|
g_return_if_fail (page != NULL);
|
|
|
|
n_pages = moo_notebook_get_n_pages (notebook);
|
|
|
|
if (position < 0 || position > n_pages - 1)
|
|
position = n_pages - 1;
|
|
|
|
if (page_index (notebook, page) == position)
|
|
return;
|
|
|
|
notebook->priv->pages = g_slist_remove (notebook->priv->pages, page);
|
|
notebook->priv->pages = g_slist_insert (notebook->priv->pages, page, position);
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (notebook));
|
|
}
|
|
|
|
|
|
static Page *find_child (MooNotebook *nb,
|
|
GtkWidget *child)
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = nb->priv->pages; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
if (page->child == child)
|
|
return page;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static Page *find_grand_child (MooNotebook *nb,
|
|
GtkWidget *child)
|
|
{
|
|
GSList *l;
|
|
|
|
g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
|
|
|
|
for (l = nb->priv->pages; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
if (page->child == child || gtk_widget_is_ancestor (child, page->child))
|
|
return page;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static Page *find_label (MooNotebook *nb,
|
|
GtkWidget *label)
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = nb->priv->pages; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
if (page->label->widget == label)
|
|
return page;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static Page *get_nth_page (MooNotebook *nb,
|
|
int n)
|
|
{
|
|
if (n < 0)
|
|
return NULL;
|
|
else
|
|
return g_slist_nth_data (nb->priv->pages, n);
|
|
}
|
|
|
|
|
|
static int find_next_visible_page (MooNotebook *nb,
|
|
int n)
|
|
{
|
|
int num_pages = moo_notebook_get_n_pages (nb);
|
|
|
|
if (!num_pages)
|
|
return -1;
|
|
|
|
if (n > 0)
|
|
{
|
|
int i;
|
|
for (i = MIN (n - 1, num_pages - 1); i >= 0; --i)
|
|
{
|
|
Page *page = get_nth_page (nb, i);
|
|
if (GTK_WIDGET_VISIBLE (page->child))
|
|
return i;
|
|
}
|
|
}
|
|
|
|
if (n < num_pages)
|
|
{
|
|
int i;
|
|
for (i = MAX (0, n); i < num_pages; ++i)
|
|
{
|
|
Page *page = get_nth_page (nb, i);
|
|
if (GTK_WIDGET_VISIBLE (page->child))
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static void delete_page (MooNotebook *nb,
|
|
Page *page)
|
|
{
|
|
int n = page_index (nb, page);
|
|
g_return_if_fail (n >= 0);
|
|
|
|
if (page == nb->priv->current_page)
|
|
nb->priv->current_page = NULL;
|
|
|
|
if (page->focus_child)
|
|
g_object_weak_unref (G_OBJECT (page->focus_child),
|
|
(GWeakNotify) g_nullify_pointer,
|
|
&page->focus_child);
|
|
|
|
g_free (page->label->text);
|
|
g_free (page->label);
|
|
g_free (page);
|
|
|
|
nb->priv->pages = g_slist_remove (nb->priv->pages, page);
|
|
|
|
n = find_next_visible_page (nb, n);
|
|
|
|
if (n >= 0)
|
|
moo_notebook_set_current_page (nb, n);
|
|
|
|
moo_notebook_check_tabs (nb);
|
|
}
|
|
|
|
|
|
void moo_notebook_remove_page (MooNotebook *notebook,
|
|
gint page_num)
|
|
{
|
|
Page *page;
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
page = get_nth_page (notebook, page_num);
|
|
g_return_if_fail (page != NULL);
|
|
gtk_container_remove (GTK_CONTAINER (notebook), page->child);
|
|
}
|
|
|
|
|
|
gint moo_notebook_get_n_pages (MooNotebook *notebook)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_NOTEBOOK (notebook), 0);
|
|
return g_slist_length (notebook->priv->pages);
|
|
}
|
|
|
|
|
|
gint moo_notebook_page_num (MooNotebook *notebook,
|
|
GtkWidget *child)
|
|
{
|
|
Page *page;
|
|
|
|
g_return_val_if_fail (MOO_IS_NOTEBOOK (notebook), -1);
|
|
g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
|
|
|
|
page = find_child (notebook, child);
|
|
|
|
if (page)
|
|
return page_index (notebook, page);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
gint moo_notebook_get_current_page (MooNotebook *notebook)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_NOTEBOOK (notebook), -1);
|
|
if (notebook->priv->current_page)
|
|
return page_index (notebook, notebook->priv->current_page);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
GtkWidget* moo_notebook_get_nth_page (MooNotebook *notebook,
|
|
gint page_num)
|
|
{
|
|
Page *page;
|
|
|
|
g_return_val_if_fail (MOO_IS_NOTEBOOK (notebook), NULL);
|
|
|
|
page = get_nth_page (notebook, page_num);
|
|
|
|
if (page)
|
|
return page->child;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_notebook_switch_page (MooNotebook *nb,
|
|
guint page_num)
|
|
{
|
|
Page *page;
|
|
FocusType old_focus = nb->priv->focus;
|
|
|
|
page = nb->priv->current_page;
|
|
|
|
/* TODO: gtk_widget_set_child_visible */
|
|
|
|
if (page)
|
|
gtk_widget_unmap (page->child);
|
|
|
|
page = get_nth_page (nb, page_num);
|
|
g_return_if_fail (page != NULL);
|
|
|
|
if (GTK_WIDGET_REALIZED (nb))
|
|
gtk_widget_map (page->child);
|
|
gtk_widget_show (page->label->widget);
|
|
|
|
nb->priv->current_page = page;
|
|
nb->priv->label_move_onscreen = TRUE;
|
|
|
|
/* XXX write some focus_child() */
|
|
if (old_focus == FOCUS_CHILD)
|
|
gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
g_object_notify (G_OBJECT (nb), "page");
|
|
}
|
|
|
|
|
|
void moo_notebook_set_current_page (MooNotebook *notebook,
|
|
gint page_num)
|
|
{
|
|
int num_pages;
|
|
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
|
|
num_pages = moo_notebook_get_n_pages (notebook);
|
|
|
|
if (!num_pages || page_num >= num_pages)
|
|
return;
|
|
|
|
if (page_num < 0)
|
|
page_num = num_pages - 1;
|
|
|
|
if (page_num == moo_notebook_get_current_page (notebook))
|
|
return;
|
|
|
|
g_signal_emit (notebook, signals[SWITCH_PAGE], 0, (guint) page_num);
|
|
}
|
|
|
|
|
|
static GSList *get_visible_pages (MooNotebook *nb)
|
|
{
|
|
GSList *list = NULL, *l;
|
|
for (l = nb->priv->pages; l != NULL; l = l->next)
|
|
if (GTK_WIDGET_VISIBLE (((Page*)l->data)->child))
|
|
list = g_slist_prepend (list, l->data);
|
|
return g_slist_reverse (list);
|
|
}
|
|
|
|
|
|
void moo_notebook_set_action_widget (MooNotebook *notebook,
|
|
GtkWidget *widget,
|
|
gboolean right)
|
|
{
|
|
GtkWidget **widget_ptr;
|
|
|
|
right = right ? TRUE : FALSE;
|
|
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
|
|
g_return_if_fail (!widget || widget->parent == NULL);
|
|
|
|
widget_ptr = right ? ¬ebook->priv->action_widgets[RIGHT] :
|
|
¬ebook->priv->action_widgets[LEFT];
|
|
|
|
if (widget == *widget_ptr)
|
|
return;
|
|
|
|
if (*widget_ptr)
|
|
gtk_container_remove (GTK_CONTAINER (notebook), *widget_ptr);
|
|
|
|
g_return_if_fail (*widget_ptr == NULL);
|
|
|
|
if (widget)
|
|
{
|
|
*widget_ptr = widget;
|
|
g_object_ref (widget);
|
|
gtk_object_sink (GTK_OBJECT (widget));
|
|
gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
|
|
}
|
|
|
|
moo_notebook_check_action_widgets (notebook);
|
|
gtk_widget_queue_resize (GTK_WIDGET (notebook));
|
|
}
|
|
|
|
|
|
GtkWidget *moo_notebook_get_action_widget (MooNotebook *notebook,
|
|
gboolean right)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_NOTEBOOK (notebook), NULL);
|
|
if (right)
|
|
return notebook->priv->action_widgets[RIGHT];
|
|
else
|
|
return notebook->priv->action_widgets[LEFT];
|
|
}
|
|
|
|
|
|
static void moo_notebook_check_action_widgets (MooNotebook *nb)
|
|
{
|
|
if (!nb->priv->action_widgets[LEFT] ||
|
|
!GTK_WIDGET_VISIBLE (nb->priv->action_widgets[LEFT]))
|
|
nb->priv->action_widgets_size[LEFT] = 0;
|
|
if (!nb->priv->action_widgets[RIGHT] ||
|
|
!GTK_WIDGET_VISIBLE (nb->priv->action_widgets[RIGHT]))
|
|
nb->priv->action_widgets_size[RIGHT] = 0;
|
|
moo_notebook_check_tabs (nb);
|
|
}
|
|
|
|
|
|
static void moo_notebook_check_tabs (MooNotebook *nb)
|
|
{
|
|
if (!nb->priv->show_tabs)
|
|
nb->priv->tabs_visible = FALSE;
|
|
else
|
|
nb->priv->tabs_visible = get_n_visible_pages (nb) ||
|
|
(nb->priv->action_widgets[LEFT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[LEFT])) ||
|
|
(nb->priv->action_widgets[RIGHT] &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->action_widgets[RIGHT]));
|
|
|
|
if (!nb->priv->tabs_visible)
|
|
{
|
|
if (nb->priv->tab_window)
|
|
gdk_window_hide (nb->priv->tab_window);
|
|
|
|
nb->priv->tabs_height = 0;
|
|
}
|
|
else if (nb->priv->tab_window)
|
|
{
|
|
gdk_window_show (nb->priv->tab_window);
|
|
}
|
|
|
|
NOTEBOOK_CHECK_INVARIANTS (nb);
|
|
}
|
|
|
|
|
|
GtkWidget *moo_notebook_get_tab_label (MooNotebook *notebook,
|
|
GtkWidget *child)
|
|
{
|
|
Page *page;
|
|
|
|
g_return_val_if_fail (MOO_IS_NOTEBOOK (notebook), NULL);
|
|
g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
|
|
|
|
page = find_child (notebook, child);
|
|
g_return_val_if_fail (page != NULL, NULL);
|
|
|
|
return page->label->widget;
|
|
}
|
|
|
|
|
|
void moo_notebook_set_tab_label (MooNotebook *notebook,
|
|
GtkWidget *child,
|
|
GtkWidget *tab_label)
|
|
{
|
|
Page *page;
|
|
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
g_return_if_fail (GTK_IS_WIDGET (child));
|
|
g_return_if_fail (GTK_IS_WIDGET (tab_label));
|
|
g_return_if_fail (tab_label->parent == NULL);
|
|
|
|
page = find_child (notebook, child);
|
|
g_return_if_fail (page != NULL);
|
|
|
|
gtk_widget_unparent (page->label->widget);
|
|
g_object_unref (page->label->widget);
|
|
|
|
g_free (page->label->text);
|
|
page->label->text = NULL;
|
|
|
|
page->label->widget = tab_label;
|
|
g_object_ref (tab_label);
|
|
gtk_object_sink (GTK_OBJECT (tab_label));
|
|
|
|
if (GTK_WIDGET_REALIZED (notebook))
|
|
gtk_widget_set_parent_window (tab_label, notebook->priv->tab_window);
|
|
|
|
gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook));
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* Labels
|
|
*/
|
|
|
|
static void labels_size_allocate (MooNotebook *nb,
|
|
GtkAllocation *allocation)
|
|
{
|
|
GtkAllocation child_alloc;
|
|
GtkRequisition child_req;
|
|
GSList *list, *l;
|
|
int width, max_offset, height;
|
|
gboolean move_onscreen_again = FALSE;
|
|
gboolean invalidate = FALSE;
|
|
|
|
if (!(list = get_visible_pages (nb)))
|
|
return;
|
|
|
|
height = allocation->height;
|
|
|
|
if (nb->priv->in_drag)
|
|
{
|
|
Page *page = nb->priv->drag_page;
|
|
int i = g_slist_index (list, page);
|
|
|
|
g_return_if_fail (i >= 0);
|
|
|
|
if (i != nb->priv->drag_page_index)
|
|
{
|
|
list = g_slist_remove (list, page);
|
|
list = g_slist_insert (list, page, nb->priv->drag_page_index);
|
|
}
|
|
}
|
|
|
|
if (nb->priv->labels_homogeneous)
|
|
{
|
|
int max_width = 2*LABEL_OVERLAP;
|
|
|
|
for (l = list; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
GtkWidget *label = page->label->widget;
|
|
|
|
gtk_widget_get_child_requisition (label, &child_req);
|
|
|
|
max_width = MAX (max_width, child_req.width + 2 * (int) nb->priv->label_hborder);
|
|
}
|
|
|
|
for (l = list, width = 0; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
|
|
if (max_width != page->label->width)
|
|
invalidate = TRUE;
|
|
|
|
page->label->width = max_width;
|
|
page->label->height = allocation->height;
|
|
|
|
if (width)
|
|
{
|
|
page->label->offset = width - LABEL_OVERLAP;
|
|
width += (page->label->width - LABEL_OVERLAP);
|
|
}
|
|
else
|
|
{
|
|
page->label->offset = 0;
|
|
width += page->label->width;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (l = list, width = 0; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
GtkWidget *label = page->label->widget;
|
|
int new_width;
|
|
|
|
gtk_widget_get_child_requisition (label, &child_req);
|
|
|
|
new_width = MAX (0, child_req.width) + 2 * nb->priv->label_hborder;
|
|
new_width = MAX (new_width, 2*LABEL_OVERLAP);
|
|
|
|
if (new_width != page->label->width)
|
|
invalidate = TRUE;
|
|
|
|
page->label->width = new_width;
|
|
page->label->height = allocation->height;
|
|
|
|
if (width)
|
|
{
|
|
page->label->offset = width - LABEL_OVERLAP;
|
|
width += (page->label->width - LABEL_OVERLAP);
|
|
}
|
|
else
|
|
{
|
|
page->label->offset = 0;
|
|
width += page->label->width;
|
|
}
|
|
}
|
|
}
|
|
|
|
max_offset = MAX (width - allocation->width, 0);
|
|
nb->priv->labels_offset = CLAMP (nb->priv->labels_offset, 0, max_offset);
|
|
nb->priv->labels_width = width;
|
|
nb->priv->labels_visible_width = allocation->width;
|
|
|
|
if (nb->priv->arrows_visible)
|
|
{
|
|
if (nb->priv->labels_width <=
|
|
nb->priv->labels_visible_width + nb->priv->arrows_size)
|
|
{
|
|
nb->priv->arrows_visible = FALSE;
|
|
nb->priv->arrows_size = 0;
|
|
gtk_widget_hide (nb->priv->arrows);
|
|
}
|
|
|
|
moo_notebook_check_arrows (nb);
|
|
}
|
|
else
|
|
{
|
|
if (nb->priv->labels_width > nb->priv->labels_visible_width)
|
|
{
|
|
nb->priv->arrows_visible = TRUE;
|
|
gtk_widget_show (nb->priv->arrows);
|
|
move_onscreen_again = TRUE;
|
|
}
|
|
}
|
|
|
|
if (nb->priv->arrows_gtk || nb->priv->label_move_onscreen)
|
|
{
|
|
labels_move_label_onscreen (nb, nb->priv->current_page, FALSE);
|
|
if (!move_onscreen_again)
|
|
nb->priv->label_move_onscreen = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* TODO is something needed here? */
|
|
}
|
|
|
|
for (l = list; l != NULL; l = l->next)
|
|
{
|
|
Page *page = l->data;
|
|
GtkWidget *label = page->label->widget;
|
|
|
|
gtk_widget_get_child_requisition (label, &child_req);
|
|
|
|
if (page != nb->priv->drag_page)
|
|
child_alloc.x = page->label->offset;
|
|
else
|
|
child_alloc.x = nb->priv->drag_tab_x;
|
|
|
|
child_alloc.x += (nb->priv->label_hborder - nb->priv->labels_offset);
|
|
child_alloc.width = child_req.width;
|
|
child_alloc.y = (height - LABEL_FOCUS_HEIGHT)/2 -
|
|
child_req.height/2 + LABEL_FOCUS_HEIGHT;
|
|
child_alloc.height = child_req.height;
|
|
|
|
gtk_widget_size_allocate (label, &child_alloc);
|
|
}
|
|
|
|
if (invalidate)
|
|
labels_invalidate (nb);
|
|
|
|
g_slist_free (list);
|
|
}
|
|
|
|
|
|
static void moo_notebook_draw_label (MooNotebook *nb,
|
|
Page *page,
|
|
GdkEventExpose *event)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (nb);
|
|
GdkWindow *window = nb->priv->tab_window;
|
|
int x, y, height;
|
|
GtkStateType state;
|
|
|
|
if (page == nb->priv->current_page)
|
|
{
|
|
y = 0;
|
|
state = GTK_STATE_NORMAL;
|
|
}
|
|
else
|
|
{
|
|
y = LABEL_FOCUS_HEIGHT;
|
|
state = GTK_STATE_ACTIVE;
|
|
}
|
|
|
|
x = page->label->offset - nb->priv->labels_offset;
|
|
height = nb->priv->tabs_height - y;
|
|
|
|
gtk_paint_extension (widget->style,
|
|
window,
|
|
state,
|
|
GTK_SHADOW_OUT,
|
|
&event->area,
|
|
widget,
|
|
(char*) "tab",
|
|
x, y,
|
|
page->label->width,
|
|
height,
|
|
GTK_POS_BOTTOM);
|
|
|
|
if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (nb)) &&
|
|
page == nb->priv->focus_page)
|
|
{
|
|
int focus_width;
|
|
|
|
gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
|
|
|
|
gtk_paint_focus (widget->style,
|
|
window,
|
|
state,
|
|
&event->area,
|
|
widget,
|
|
"tab",
|
|
page->label->widget->allocation.x - focus_width,
|
|
page->label->widget->allocation.y - focus_width,
|
|
page->label->widget->allocation.width + 2 * focus_width,
|
|
page->label->widget->allocation.height + 2 * focus_width);
|
|
}
|
|
}
|
|
|
|
static void moo_notebook_draw_labels (MooNotebook *nb,
|
|
GdkEventExpose *event)
|
|
{
|
|
if (!nb->priv->current_page)
|
|
return;
|
|
|
|
VISIBLE_FOREACH_START (nb, page)
|
|
{
|
|
if (page != nb->priv->current_page)
|
|
moo_notebook_draw_label (nb, page, event);
|
|
}
|
|
VISIBLE_FOREACH_END;
|
|
|
|
if (!nb->priv->in_drag)
|
|
moo_notebook_draw_label (nb, nb->priv->current_page, event);
|
|
}
|
|
|
|
|
|
static void moo_notebook_draw_dragged_label (MooNotebook *nb,
|
|
GdkEventExpose *event)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (nb);
|
|
int width, height;
|
|
|
|
g_return_if_fail (nb->priv->drag_page != NULL);
|
|
|
|
width = nb->priv->drag_page->label->width;
|
|
height = nb->priv->tabs_height;
|
|
|
|
if (nb->priv->want_snapshot)
|
|
{
|
|
GdkPixbuf *pixbuf;
|
|
guchar *pixels;
|
|
int rowstride, row, i;
|
|
|
|
g_return_if_fail (nb->priv->snapshot_pixmap == NULL &&
|
|
nb->priv->snapshot_pixbuf == NULL);
|
|
|
|
/* TODO: this event may not cover whole label area */
|
|
moo_notebook_draw_label (nb, nb->priv->drag_page, event);
|
|
gtk_container_propagate_expose (GTK_CONTAINER (nb),
|
|
nb->priv->drag_page->label->widget,
|
|
event);
|
|
|
|
nb->priv->want_snapshot = FALSE;
|
|
|
|
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE,
|
|
8, width, height);
|
|
|
|
if (!gdk_pixbuf_get_from_drawable (pixbuf, nb->priv->tab_window,
|
|
gdk_drawable_get_colormap (nb->priv->tab_window),
|
|
nb->priv->drag_page->label->offset - nb->priv->labels_offset,
|
|
0, 0, 0,
|
|
width, height))
|
|
{
|
|
g_critical ("could not create pixbuf");
|
|
g_object_unref (pixbuf);
|
|
|
|
nb->priv->snapshot_pixmap =
|
|
gdk_pixmap_new (nb->priv->tab_window,
|
|
width, height, -1);
|
|
gdk_draw_drawable (nb->priv->snapshot_pixmap,
|
|
widget->style->bg_gc[GTK_STATE_NORMAL],
|
|
nb->priv->tab_window,
|
|
nb->priv->drag_page->label->offset - nb->priv->labels_offset,
|
|
0, 0, 0,
|
|
width, height);
|
|
}
|
|
else
|
|
{
|
|
nb->priv->snapshot_pixbuf = pixbuf;
|
|
|
|
pixels = gdk_pixbuf_get_pixels (pixbuf);
|
|
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
|
|
|
|
for (row = 0; row < height; ++row)
|
|
for (i = 0; i < width; ++i)
|
|
pixels[row*rowstride + 4*i + 3] = LABEL_ALPHA;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GdkRectangle area = {
|
|
nb->priv->drag_tab_x - nb->priv->labels_offset,
|
|
0, width, height
|
|
};
|
|
|
|
if (!gdk_rectangle_intersect (&area, &event->area, &area))
|
|
return;
|
|
|
|
g_return_if_fail (nb->priv->snapshot_pixmap != NULL || nb->priv->snapshot_pixbuf != NULL);
|
|
|
|
if (nb->priv->snapshot_pixbuf)
|
|
gdk_draw_pixbuf (nb->priv->tab_window,
|
|
widget->style->bg_gc[GTK_STATE_NORMAL],
|
|
nb->priv->snapshot_pixbuf,
|
|
area.x - nb->priv->drag_tab_x + nb->priv->labels_offset,
|
|
area.y,
|
|
area.x,
|
|
area.y,
|
|
area.width,
|
|
area.height,
|
|
GDK_RGB_DITHER_NONE, 0, 0);
|
|
else
|
|
gdk_draw_drawable (nb->priv->tab_window,
|
|
widget->style->bg_gc[GTK_STATE_NORMAL],
|
|
nb->priv->snapshot_pixmap,
|
|
event->area.x - nb->priv->drag_tab_x + nb->priv->labels_offset,
|
|
event->area.y,
|
|
event->area.x,
|
|
event->area.y,
|
|
event->area.width,
|
|
event->area.height);
|
|
}
|
|
}
|
|
|
|
|
|
static void moo_notebook_check_arrows (MooNotebook *nb)
|
|
{
|
|
gboolean sensitive[2];
|
|
GSList *visible;
|
|
Page *page;
|
|
|
|
if (!nb->priv->arrows_visible)
|
|
return;
|
|
|
|
if (!(visible = get_visible_pages (nb)))
|
|
{
|
|
sensitive[LEFT] = sensitive[RIGHT] = FALSE;
|
|
}
|
|
else if (nb->priv->arrows_gtk)
|
|
{
|
|
page = nb->priv->current_page;
|
|
|
|
g_return_if_fail (page != NULL);
|
|
g_return_if_fail (g_slist_find (visible, page) != NULL);
|
|
|
|
sensitive[LEFT] = (page != visible->data);
|
|
sensitive[RIGHT] = (page != g_slist_last(visible)->data);
|
|
}
|
|
else
|
|
{
|
|
sensitive[LEFT] = (nb->priv->labels_offset > 0);
|
|
sensitive[RIGHT] = nb->priv->labels_width <= nb->priv->labels_visible_width ||
|
|
nb->priv->labels_offset < nb->priv->labels_width - nb->priv->labels_visible_width - 1;
|
|
}
|
|
|
|
/* TODO focus */
|
|
gtk_widget_set_sensitive (nb->priv->left_arrow, sensitive[LEFT]);
|
|
gtk_widget_set_sensitive (nb->priv->right_arrow, sensitive[RIGHT]);
|
|
|
|
g_slist_free (visible);
|
|
}
|
|
|
|
|
|
static void left_arrow_clicked (GtkWidget *button,
|
|
MooNotebook *nb)
|
|
{
|
|
g_return_if_fail (button == nb->priv->left_arrow);
|
|
|
|
if (nb->priv->arrows_gtk)
|
|
{
|
|
int n = moo_notebook_get_current_page (nb) - 1;
|
|
g_return_if_fail (n >= 0);
|
|
moo_notebook_set_current_page (nb, n);
|
|
}
|
|
else
|
|
{
|
|
labels_scroll (nb, GTK_DIR_LEFT);
|
|
}
|
|
}
|
|
|
|
|
|
static void right_arrow_clicked (GtkWidget *button,
|
|
MooNotebook *nb)
|
|
{
|
|
g_return_if_fail (button == nb->priv->right_arrow);
|
|
|
|
if (nb->priv->arrows_gtk)
|
|
{
|
|
int n = moo_notebook_get_current_page (nb) + 1;
|
|
g_return_if_fail (n < moo_notebook_get_n_pages (nb));
|
|
moo_notebook_set_current_page (nb, n);
|
|
}
|
|
else
|
|
{
|
|
labels_scroll (nb, GTK_DIR_RIGHT);
|
|
}
|
|
}
|
|
|
|
|
|
#define SCROLL_PAD 6
|
|
|
|
static void labels_scroll (MooNotebook *nb,
|
|
GtkDirectionType where)
|
|
{
|
|
GSList *visible;
|
|
Page *page;
|
|
int offset, index_;
|
|
|
|
g_return_if_fail (!nb->priv->arrows_gtk);
|
|
g_return_if_fail (nb->priv->tabs_visible);
|
|
|
|
if (nb->priv->labels_visible_width >= nb->priv->labels_width)
|
|
return;
|
|
|
|
visible = get_visible_pages (nb);
|
|
g_return_if_fail (visible != NULL);
|
|
|
|
if (where == GTK_DIR_LEFT)
|
|
{
|
|
page = find_label_at_x (nb, 0, FALSE);
|
|
index_ = g_slist_index (visible, page);
|
|
g_return_if_fail (index_ >= 0);
|
|
|
|
if (index_ == 0)
|
|
offset = 0;
|
|
else
|
|
offset = page->label->offset - SCROLL_PAD;
|
|
}
|
|
else
|
|
{
|
|
page = find_label_at_x (nb, nb->priv->labels_visible_width - 1, FALSE);
|
|
index_ = g_slist_index (visible, page);
|
|
g_return_if_fail (index_ >= 0);
|
|
|
|
if (index_ == (int) g_slist_length (visible) - 1)
|
|
offset = nb->priv->labels_width - nb->priv->labels_visible_width;
|
|
else
|
|
offset = page->label->offset + page->label->width -
|
|
nb->priv->labels_visible_width + SCROLL_PAD;
|
|
}
|
|
|
|
labels_scroll_to_offset (nb, offset, TRUE);
|
|
}
|
|
|
|
|
|
static void labels_scroll_to_offset (MooNotebook *nb,
|
|
int offset,
|
|
gboolean realloc)
|
|
{
|
|
int delta;
|
|
|
|
offset = CLAMP (offset, 0, nb->priv->labels_width - nb->priv->labels_visible_width);
|
|
delta = offset - nb->priv->labels_offset;
|
|
|
|
nb->priv->labels_offset += delta;
|
|
|
|
if (realloc)
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
}
|
|
|
|
|
|
static Page*
|
|
find_label_at_x (MooNotebook *nb,
|
|
int x,
|
|
gboolean widget_only)
|
|
{
|
|
return find_label_at_xy (nb, x, nb->priv->tabs_height / 2, widget_only);
|
|
}
|
|
|
|
|
|
static Page*
|
|
find_label_at_xy (MooNotebook *nb,
|
|
int x,
|
|
int y,
|
|
gboolean widget_only)
|
|
{
|
|
/* TODO labels overlap */
|
|
|
|
if (y < 0 || y >= nb->priv->tabs_height)
|
|
return NULL;
|
|
|
|
/* here it must go through all the pages because they may be swapped on drag */
|
|
/* XXX so it could just check if we are in drag */
|
|
VISIBLE_FOREACH_START(nb, page)
|
|
{
|
|
int lx = page->label->offset - nb->priv->labels_offset;
|
|
int lwidth = page->label->width;
|
|
int ax = page->label->widget->allocation.x;
|
|
int awidth = page->label->widget->allocation.width;
|
|
int ay = page->label->widget->allocation.y;
|
|
int aheight = page->label->widget->allocation.height;
|
|
|
|
if (x >= lx && x < lx + lwidth)
|
|
{
|
|
if (widget_only)
|
|
{
|
|
if (x >= ax && x < ax + awidth && y >= ay && y >= ay + aheight)
|
|
return page;
|
|
else
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return page;
|
|
}
|
|
}
|
|
}
|
|
VISIBLE_FOREACH_END;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void labels_move_label_onscreen (MooNotebook *nb,
|
|
Page *page,
|
|
gboolean realloc)
|
|
{
|
|
int offset;
|
|
|
|
g_return_if_fail (page != NULL);
|
|
|
|
if (nb->priv->labels_visible_width >= nb->priv->labels_width)
|
|
return;
|
|
|
|
if (page->label->width >= nb->priv->labels_visible_width ||
|
|
page->label->offset < nb->priv->labels_offset)
|
|
{
|
|
offset = page->label->offset - SCROLL_PAD;
|
|
}
|
|
else if (page->label->offset + page->label->width >
|
|
nb->priv->labels_offset + nb->priv->labels_visible_width)
|
|
{
|
|
offset = page->label->offset + page->label->width -
|
|
nb->priv->labels_visible_width + SCROLL_PAD;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
labels_scroll_to_offset (nb, offset, realloc);
|
|
}
|
|
|
|
|
|
static void moo_notebook_set_homogeneous(MooNotebook *nb,
|
|
gboolean homogeneous)
|
|
{
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (nb));
|
|
if (nb->priv->labels_homogeneous != homogeneous)
|
|
{
|
|
nb->priv->labels_homogeneous = homogeneous;
|
|
if (nb->priv->tabs_visible)
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Mouse stuff
|
|
*/
|
|
|
|
#define SCROLL_TIMEOUT 50
|
|
#define SCROLL_STEP 5
|
|
|
|
static void tab_drag_cancel (MooNotebook *nb);
|
|
static void tab_drag_start (MooNotebook *nb,
|
|
GdkEventMotion *event);
|
|
static void tab_drag_motion (MooNotebook *nb,
|
|
GdkEventMotion *event);
|
|
static void tab_drag_end (MooNotebook *nb,
|
|
gboolean drop);
|
|
static void drag_scroll_start (MooNotebook *nb,
|
|
gboolean right);
|
|
static void drag_scroll_stop (MooNotebook *nb);
|
|
static gboolean drag_scroll (MooNotebook *nb);
|
|
|
|
|
|
static void tab_drag_cancel (MooNotebook *nb)
|
|
{
|
|
g_print ("cancel\n");
|
|
tab_drag_end (nb, FALSE);
|
|
}
|
|
|
|
|
|
static void drag_scroll_start (MooNotebook *nb,
|
|
gboolean right)
|
|
{
|
|
if (nb->priv->drag_scroll_id)
|
|
{
|
|
if (nb->priv->drag_scroll_right != right)
|
|
g_source_remove (nb->priv->drag_scroll_id);
|
|
else
|
|
return;
|
|
}
|
|
|
|
nb->priv->drag_scroll_right = right;
|
|
nb->priv->drag_scroll_id = g_timeout_add (SCROLL_TIMEOUT,
|
|
(GSourceFunc) drag_scroll,
|
|
nb);
|
|
}
|
|
|
|
|
|
static void drag_scroll_stop (MooNotebook *nb)
|
|
{
|
|
if (nb->priv->drag_scroll_id)
|
|
g_source_remove (nb->priv->drag_scroll_id);
|
|
nb->priv->drag_scroll_id = 0;
|
|
}
|
|
|
|
|
|
static gboolean drag_scroll (MooNotebook *nb)
|
|
{
|
|
int delta;
|
|
int offset = nb->priv->labels_offset;
|
|
int max_offset = nb->priv->labels_width - nb->priv->labels_visible_width;
|
|
int width = nb->priv->drag_page->label->width;
|
|
|
|
if (max_offset <= 0)
|
|
{
|
|
drag_scroll_stop (nb);
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
if (nb->priv->drag_scroll_right)
|
|
{
|
|
if (nb->priv->drag_mouse_x > nb->priv->labels_visible_width)
|
|
delta = SCROLL_STEP * (2 + (nb->priv->drag_mouse_x - nb->priv->labels_visible_width)/width);
|
|
else
|
|
delta = SCROLL_STEP;
|
|
|
|
if (offset + delta > max_offset)
|
|
delta = MAX (0, max_offset - offset);
|
|
}
|
|
else
|
|
{
|
|
if (nb->priv->drag_mouse_x < 0)
|
|
delta = -SCROLL_STEP * (2 + (-nb->priv->drag_mouse_x)/width);
|
|
else
|
|
delta = -SCROLL_STEP;
|
|
|
|
if (offset + delta < 0)
|
|
delta = MIN (-offset, 0);
|
|
}
|
|
|
|
if (!delta)
|
|
return TRUE;
|
|
|
|
nb->priv->labels_offset += delta;
|
|
nb->priv->drag_tab_x -= delta;
|
|
|
|
tab_drag_motion (nb, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void tab_drag_end (MooNotebook *nb,
|
|
gboolean drop)
|
|
{
|
|
int index_;
|
|
Page *old_page;
|
|
Page *drag_page = nb->priv->drag_page;
|
|
|
|
nb->priv->button_pressed = FALSE;
|
|
nb->priv->in_drag = FALSE;
|
|
nb->priv->drag_page = NULL;
|
|
|
|
if (nb->priv->snapshot_pixbuf)
|
|
g_object_unref (nb->priv->snapshot_pixbuf);
|
|
if (nb->priv->snapshot_pixmap)
|
|
g_object_unref (nb->priv->snapshot_pixmap);
|
|
nb->priv->snapshot_pixbuf = NULL;
|
|
nb->priv->snapshot_pixmap = NULL;
|
|
nb->priv->want_snapshot = FALSE;
|
|
|
|
drag_scroll_stop (nb);
|
|
|
|
index_ = page_visible_index (nb, drag_page);
|
|
|
|
if (drop && index_ != nb->priv->drag_page_index)
|
|
{
|
|
index_ = nb->priv->drag_page_index;
|
|
old_page = get_nth_visible_page (nb, index_);
|
|
g_return_if_fail (old_page != NULL);
|
|
|
|
index_ = page_index (nb, old_page);
|
|
moo_notebook_reorder_child (nb, drag_page->child, index_);
|
|
labels_move_label_onscreen (nb, get_nth_page (nb, index_), FALSE);
|
|
}
|
|
|
|
labels_invalidate (nb);
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
}
|
|
|
|
|
|
static gboolean
|
|
translate_coords (GdkWindow *parent_window,
|
|
GdkWindow *window,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
while (window != parent_window)
|
|
{
|
|
int dx, dy;
|
|
|
|
gdk_window_get_position (window, &dx, &dy);
|
|
(*x) += dx;
|
|
(*y) += dy;
|
|
|
|
window = gdk_window_get_parent (window);
|
|
|
|
if (!window)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void tab_drag_start (MooNotebook *nb,
|
|
GdkEventMotion *event)
|
|
{
|
|
Page *page;
|
|
GdkRectangle rect;
|
|
int event_x, event_y;
|
|
|
|
g_return_if_fail (nb->priv->button_pressed);
|
|
|
|
event_x = event->x;
|
|
event_y = event->y;
|
|
|
|
if (!translate_coords (nb->priv->tab_window, event->window, &event_x, &event_y))
|
|
g_return_if_reached ();
|
|
|
|
page = nb->priv->current_page;
|
|
g_return_if_fail (page != NULL);
|
|
nb->priv->drag_page = page;
|
|
nb->priv->drag_page_index = page_visible_index (nb, page);
|
|
|
|
nb->priv->drag_tab_x_delta = event_x + nb->priv->labels_offset - page->label->offset;
|
|
nb->priv->in_drag = TRUE;
|
|
|
|
nb->priv->want_snapshot = TRUE;
|
|
|
|
rect.x = page->label->offset - nb->priv->labels_offset;
|
|
rect.y = 0;
|
|
rect.width = page->label->width;
|
|
rect.height = nb->priv->tabs_height;
|
|
|
|
gdk_window_invalidate_rect (nb->priv->tab_window, &rect, TRUE);
|
|
gdk_window_process_updates (nb->priv->tab_window, TRUE);
|
|
}
|
|
|
|
|
|
static void tab_drag_motion (MooNotebook *nb,
|
|
GdkEventMotion *event)
|
|
{
|
|
int x, new_index, width, offset, num, i;
|
|
GSList *visible, *l;
|
|
Page *drag_page;
|
|
int event_x, event_y;
|
|
|
|
if (!nb->priv->in_drag)
|
|
{
|
|
tab_drag_cancel (nb);
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
if (event)
|
|
{
|
|
event_x = event->x;
|
|
event_y = event->y;
|
|
|
|
if (!translate_coords (nb->priv->tab_window, event->window, &event_x, &event_y))
|
|
g_return_if_reached ();
|
|
}
|
|
else
|
|
{
|
|
event_x = nb->priv->drag_mouse_x;
|
|
}
|
|
|
|
width = nb->priv->drag_page->label->width;
|
|
num = get_n_visible_pages (nb);
|
|
|
|
if (event)
|
|
{
|
|
x = event_x + nb->priv->labels_offset - nb->priv->drag_tab_x_delta;
|
|
nb->priv->drag_mouse_x = event_x;
|
|
}
|
|
else
|
|
{
|
|
x = nb->priv->drag_mouse_x + nb->priv->labels_offset - nb->priv->drag_tab_x_delta;
|
|
}
|
|
|
|
nb->priv->drag_tab_x = x;
|
|
|
|
drag_page = nb->priv->drag_page;
|
|
visible = get_visible_pages (nb);
|
|
visible = g_slist_remove (visible, drag_page);
|
|
visible = g_slist_insert (visible, drag_page, nb->priv->drag_page_index);
|
|
|
|
new_index = nb->priv->drag_page_index;
|
|
|
|
for (l = visible, i = 0, offset = 0; l != NULL; l = l->next, ++i)
|
|
{
|
|
Page *page;
|
|
int min_width;
|
|
|
|
page = l->data;
|
|
min_width = MIN (page->label->width, width);
|
|
|
|
if (i == new_index)
|
|
{
|
|
offset += page->label->width;
|
|
continue;
|
|
}
|
|
|
|
if (i == 0)
|
|
{
|
|
if (x + min_width/2 < offset + min_width)
|
|
{
|
|
new_index = i;
|
|
break;
|
|
}
|
|
}
|
|
else if (i == num - 1)
|
|
{
|
|
if (x + min_width/2 >= offset)
|
|
{
|
|
new_index = i;
|
|
break;
|
|
}
|
|
}
|
|
else if (x + min_width/2 >= offset &&
|
|
x + min_width/2 < offset + min_width)
|
|
{
|
|
new_index = i;
|
|
break;
|
|
}
|
|
|
|
offset += page->label->width;
|
|
}
|
|
|
|
nb->priv->drag_page_index = new_index;
|
|
|
|
if (nb->priv->labels_width > nb->priv->labels_visible_width)
|
|
{
|
|
x += (nb->priv->drag_tab_x_delta - nb->priv->labels_offset);
|
|
|
|
if (x < width/3)
|
|
drag_scroll_start (nb, LEFT);
|
|
else if (x + width/3 > nb->priv->labels_visible_width)
|
|
drag_scroll_start (nb, RIGHT);
|
|
else
|
|
drag_scroll_stop (nb);
|
|
}
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (nb));
|
|
g_slist_free (visible);
|
|
}
|
|
|
|
|
|
static gboolean moo_notebook_button_press (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
Page *page;
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
int x, y;
|
|
|
|
x = event->x;
|
|
y = event->y;
|
|
|
|
if (!translate_coords (nb->priv->tab_window, event->window, &x, &y))
|
|
return FALSE;
|
|
|
|
if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
|
|
{
|
|
if (nb->priv->button_pressed || nb->priv->in_drag)
|
|
{
|
|
tab_drag_cancel (nb);
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
if (!nb->priv->focus && !GTK_WIDGET_HAS_FOCUS (widget))
|
|
gtk_widget_grab_focus (widget);
|
|
|
|
page = find_label_at_xy (nb, x, y, FALSE);
|
|
|
|
if (page)
|
|
{
|
|
gboolean focus_label = FALSE;
|
|
|
|
if (page == nb->priv->current_page)
|
|
{
|
|
focus_label = TRUE;
|
|
}
|
|
else
|
|
{
|
|
moo_notebook_set_current_page (nb, page_index (nb, page));
|
|
g_assert (page == nb->priv->current_page);
|
|
if (page->focus_child && GTK_WIDGET_CAN_FOCUS (page->focus_child))
|
|
gtk_widget_grab_focus (page->focus_child);
|
|
else if (!gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD))
|
|
focus_label = TRUE;
|
|
}
|
|
|
|
if (focus_label &&
|
|
!gtk_widget_child_focus (page->label->widget, GTK_DIR_TAB_FORWARD))
|
|
{
|
|
nb->priv->focus = FOCUS_NONE;
|
|
nb->priv->focus_page = page;
|
|
if (!GTK_WIDGET_HAS_FOCUS (widget))
|
|
gtk_widget_grab_focus (widget);
|
|
labels_invalidate (nb);
|
|
}
|
|
|
|
if (get_n_visible_pages (nb) > 1)
|
|
nb->priv->button_pressed = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
|
|
{
|
|
return moo_notebook_maybe_popup (nb, event);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean moo_notebook_button_release (GtkWidget *widget,
|
|
G_GNUC_UNUSED GdkEventButton *event)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
|
|
if (!nb->priv->button_pressed)
|
|
return FALSE;
|
|
|
|
nb->priv->button_pressed = FALSE;
|
|
|
|
if (nb->priv->in_drag)
|
|
tab_drag_end (nb, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
moo_notebook_scroll_event (GtkWidget *widget,
|
|
GdkEventScroll *event)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
int x, y;
|
|
|
|
x = event->x;
|
|
y = event->y;
|
|
|
|
if (!translate_coords (nb->priv->tab_window, event->window, &x, &y))
|
|
return FALSE;
|
|
|
|
switch (event->direction)
|
|
{
|
|
case GDK_SCROLL_UP:
|
|
case GDK_SCROLL_LEFT:
|
|
labels_scroll (nb, GTK_DIR_LEFT);
|
|
return TRUE;
|
|
|
|
case GDK_SCROLL_DOWN:
|
|
case GDK_SCROLL_RIGHT:
|
|
labels_scroll (nb, GTK_DIR_RIGHT);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean moo_notebook_key_press (GtkWidget *widget,
|
|
GdkEventKey *event)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
|
|
if (nb->priv->in_drag && event->keyval == GDK_Escape)
|
|
{
|
|
tab_drag_end (nb, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean moo_notebook_motion (GtkWidget *widget,
|
|
GdkEventMotion *event)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
|
|
if (!nb->priv->button_pressed)
|
|
{
|
|
if (nb->priv->in_drag)
|
|
{
|
|
tab_drag_cancel (nb);
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (nb->priv->in_drag)
|
|
{
|
|
tab_drag_motion (nb, event);
|
|
return TRUE;
|
|
}
|
|
|
|
tab_drag_start (nb, event);
|
|
tab_drag_motion (nb, event);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void moo_notebook_enable_reordering (MooNotebook *notebook,
|
|
gboolean enable)
|
|
{
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
notebook->priv->enable_reordering = enable;
|
|
g_object_notify (G_OBJECT (notebook), "enable-reordering");
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Popup menu
|
|
*/
|
|
|
|
static void popup_position_func (G_GNUC_UNUSED GtkMenu *menu,
|
|
gint *x,
|
|
gint *y,
|
|
gboolean *push_in,
|
|
gpointer user_data)
|
|
{
|
|
struct {
|
|
MooNotebook *nb;
|
|
Page *page;
|
|
GdkEventButton *event;
|
|
} *data = user_data;
|
|
|
|
g_return_if_fail (data != NULL);
|
|
g_return_if_fail (data->nb != NULL && data->nb->priv->tab_window != NULL);
|
|
g_return_if_fail (data->page != NULL);
|
|
|
|
if (data->event)
|
|
gdk_window_get_origin (data->event->window, x, y);
|
|
else
|
|
gdk_window_get_origin (data->nb->priv->tab_window, x, y);
|
|
|
|
if (data->event)
|
|
{
|
|
(*x) += data->event->x;
|
|
(*y) += data->event->y;
|
|
}
|
|
else
|
|
{
|
|
(*x) += (data->page->label->offset - data->nb->priv->labels_offset +
|
|
data->page->label->width * 2 / 3);
|
|
(*y) += (data->nb->priv->tabs_height * 2 / 3);
|
|
}
|
|
|
|
*push_in = TRUE;
|
|
}
|
|
|
|
|
|
static gboolean moo_notebook_do_popup (MooNotebook *nb,
|
|
Page *page,
|
|
GdkEventButton *event)
|
|
{
|
|
GtkWidget *menu;
|
|
gboolean dont = FALSE;
|
|
struct {
|
|
MooNotebook *nb;
|
|
Page *page;
|
|
GdkEventButton *event;
|
|
} data;
|
|
|
|
g_return_val_if_fail (nb->priv->popup_func != NULL, FALSE);
|
|
g_return_val_if_fail (page != NULL, FALSE);
|
|
|
|
menu = nb->priv->popup_func (nb, page->child, nb->priv->popup_user_data);
|
|
g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
|
|
|
|
g_object_ref (menu);
|
|
gtk_object_sink (GTK_OBJECT (menu));
|
|
|
|
g_signal_emit (nb, signals[POPULATE_POPUP], 0, page->child, menu, &dont);
|
|
|
|
if (dont)
|
|
{
|
|
g_object_unref (menu);
|
|
return FALSE;
|
|
}
|
|
|
|
data.nb = nb;
|
|
data.page = page;
|
|
data.event = event;
|
|
|
|
gtk_menu_popup (GTK_MENU (menu),NULL, NULL,
|
|
popup_position_func, &data,
|
|
event ? event->button : 0,
|
|
event ? event->time : gtk_get_current_event_time ());
|
|
|
|
g_object_unref (menu);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean moo_notebook_maybe_popup (MooNotebook *nb,
|
|
GdkEventButton *event)
|
|
{
|
|
Page *page;
|
|
int event_x, event_y;
|
|
|
|
if (!nb->priv->enable_popup)
|
|
return FALSE;
|
|
|
|
event_x = event->x;
|
|
event_y = event->y;
|
|
|
|
if (!translate_coords (nb->priv->tab_window, event->window, &event_x, &event_y))
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
page = find_label_at_xy (nb, event_x, event_y, FALSE);
|
|
|
|
if (!page)
|
|
return FALSE;
|
|
|
|
return moo_notebook_do_popup (nb, page, event);
|
|
}
|
|
|
|
|
|
void moo_notebook_enable_popup (MooNotebook *notebook,
|
|
gboolean enable)
|
|
{
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
notebook->priv->enable_popup = enable;
|
|
g_object_notify (G_OBJECT (notebook), "enable-popup");
|
|
}
|
|
|
|
|
|
void moo_notebook_set_popup_creation_func(MooNotebook *notebook,
|
|
PopupFunc func,
|
|
gpointer user_data)
|
|
{
|
|
g_return_if_fail (MOO_IS_NOTEBOOK (notebook));
|
|
|
|
if (!func)
|
|
return moo_notebook_set_popup_creation_func (notebook, popup_func, NULL);
|
|
|
|
notebook->priv->popup_func = func;
|
|
notebook->priv->popup_user_data = user_data;
|
|
}
|
|
|
|
|
|
static GtkWidget *popup_func (G_GNUC_UNUSED MooNotebook *notebook,
|
|
G_GNUC_UNUSED GtkWidget *child,
|
|
G_GNUC_UNUSED gpointer user_data)
|
|
{
|
|
return gtk_menu_new ();
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Focus and keyboard navigation
|
|
*/
|
|
|
|
static gboolean focus_to_action_widget (MooNotebook *nb,
|
|
int n,
|
|
GtkDirectionType direction)
|
|
{
|
|
GtkWidget *widget = nb->priv->action_widgets[n];
|
|
|
|
if (!nb->priv->tabs_visible || !widget || !GTK_WIDGET_VISIBLE (widget))
|
|
return FALSE;
|
|
|
|
return gtk_widget_child_focus (widget, direction);
|
|
}
|
|
|
|
static void labels_invalidate (MooNotebook *nb)
|
|
{
|
|
GdkRectangle rect;
|
|
GtkWidget *widget = GTK_WIDGET(nb);
|
|
int border_width = gtk_container_get_border_width (GTK_CONTAINER (nb));
|
|
|
|
if (!GTK_WIDGET_MAPPED (nb))
|
|
return;
|
|
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.width = nb->priv->labels_visible_width;
|
|
rect.height = nb->priv->tabs_height;
|
|
gdk_window_invalidate_rect (nb->priv->tab_window, &rect, TRUE);
|
|
|
|
rect.x = widget->allocation.x + border_width;
|
|
rect.y = widget->allocation.y + border_width + nb->priv->tabs_height;
|
|
rect.width = widget->allocation.width - 2*border_width;
|
|
rect.height = 2*widget->style->ythickness;
|
|
gdk_window_invalidate_rect (widget->window, &rect, FALSE);
|
|
}
|
|
|
|
static gboolean focus_to_next_label (MooNotebook *nb,
|
|
GtkDirectionType direction,
|
|
gboolean forward)
|
|
{
|
|
Page *page, *next;
|
|
GSList *visible;
|
|
|
|
g_return_val_if_fail (nb->priv->focus == FOCUS_LABEL || !nb->priv->focus, FALSE);
|
|
g_return_val_if_fail (nb->priv->tabs_visible, FALSE);
|
|
|
|
page = nb->priv->focus_page;
|
|
g_return_val_if_fail (page != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_WIDGET_VISIBLE (page->child), FALSE);
|
|
|
|
visible = get_visible_pages (nb);
|
|
g_return_val_if_fail (g_slist_find (visible, page) != NULL, FALSE);
|
|
|
|
if (forward)
|
|
{
|
|
if (page == g_slist_last(visible)->data)
|
|
return FALSE;
|
|
else
|
|
next = g_slist_nth_data (visible, g_slist_index (visible, page) + 1);
|
|
}
|
|
else
|
|
{
|
|
if (page == visible->data)
|
|
return FALSE;
|
|
else
|
|
next = g_slist_nth_data (visible, g_slist_index (visible, page) - 1);
|
|
}
|
|
|
|
g_return_val_if_fail (next != NULL, FALSE);
|
|
|
|
if (nb->priv->arrows_gtk)
|
|
{
|
|
moo_notebook_set_current_page (nb, page_index (nb, next));
|
|
g_return_val_if_fail (nb->priv->current_page == next, TRUE);
|
|
}
|
|
else
|
|
{
|
|
labels_move_label_onscreen (nb, next, TRUE);
|
|
}
|
|
|
|
if (!gtk_widget_child_focus (next->label->widget, direction))
|
|
{
|
|
nb->priv->focus = FOCUS_NONE;
|
|
nb->priv->focus_page = next;
|
|
gtk_widget_grab_focus (GTK_WIDGET (nb));
|
|
labels_invalidate (nb);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean focus_to_labels (MooNotebook *nb,
|
|
GtkDirectionType direction,
|
|
gboolean forward)
|
|
{
|
|
Page *page;
|
|
|
|
if (!nb->priv->tabs_visible)
|
|
return FALSE;
|
|
|
|
if (!nb->priv->current_page)
|
|
return FALSE;
|
|
|
|
if (nb->priv->arrows_gtk)
|
|
{
|
|
page = nb->priv->current_page;
|
|
}
|
|
else
|
|
{
|
|
if (forward)
|
|
{
|
|
page = find_label_at_x (nb, 0, FALSE);
|
|
}
|
|
else
|
|
{
|
|
int x = MIN (nb->priv->labels_width, nb->priv->labels_visible_width) - 1;
|
|
page = find_label_at_x (nb, x, FALSE);
|
|
}
|
|
}
|
|
|
|
g_return_val_if_fail (page != NULL, FALSE);
|
|
|
|
if (!gtk_widget_child_focus (page->label->widget, direction))
|
|
{
|
|
nb->priv->focus = FOCUS_NONE;
|
|
nb->priv->focus_page = page;
|
|
gtk_widget_grab_focus (GTK_WIDGET (nb));
|
|
labels_invalidate (nb);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean focus_to_arrows (MooNotebook *nb,
|
|
GtkDirectionType direction)
|
|
{
|
|
if (!nb->priv->tabs_visible || !nb->priv->arrows_visible)
|
|
return FALSE;
|
|
return gtk_widget_child_focus (nb->priv->arrows, direction);
|
|
}
|
|
|
|
static gboolean focus_to_child (MooNotebook *nb,
|
|
GtkDirectionType direction)
|
|
{
|
|
if (!nb->priv->current_page)
|
|
return FALSE;
|
|
return gtk_widget_child_focus (nb->priv->current_page->child, direction);
|
|
}
|
|
|
|
static gboolean moo_notebook_focus (GtkWidget *widget,
|
|
GtkDirectionType direction)
|
|
{
|
|
MooNotebook *nb = MOO_NOTEBOOK (widget);
|
|
Page *page;
|
|
|
|
switch (nb->priv->focus)
|
|
{
|
|
case FOCUS_NONE:
|
|
if (gtk_widget_is_focus (widget))
|
|
{
|
|
nb->priv->focus = FOCUS_LABEL;
|
|
return moo_notebook_focus (widget, direction);
|
|
}
|
|
|
|
switch (direction)
|
|
{
|
|
case GTK_DIR_TAB_FORWARD:
|
|
case GTK_DIR_RIGHT:
|
|
case GTK_DIR_DOWN:
|
|
return focus_to_action_widget (nb, LEFT, direction) ||
|
|
focus_to_labels (nb, direction, TRUE) ||
|
|
focus_to_arrows (nb, direction) ||
|
|
focus_to_action_widget (nb, RIGHT, direction) ||
|
|
focus_to_child (nb, direction);
|
|
|
|
case GTK_DIR_TAB_BACKWARD:
|
|
case GTK_DIR_LEFT:
|
|
return focus_to_action_widget (nb, RIGHT, direction) ||
|
|
focus_to_arrows (nb, direction) ||
|
|
focus_to_labels (nb, direction, FALSE) ||
|
|
focus_to_action_widget (nb, LEFT, direction) ||
|
|
focus_to_child (nb, direction);
|
|
|
|
case GTK_DIR_UP:
|
|
return focus_to_child (nb, direction) ||
|
|
focus_to_action_widget (nb, LEFT, direction) ||
|
|
focus_to_labels (nb, direction, TRUE) ||
|
|
focus_to_arrows (nb, direction) ||
|
|
focus_to_action_widget (nb, RIGHT, direction);
|
|
}
|
|
break;
|
|
|
|
case FOCUS_LEFT:
|
|
if (gtk_widget_child_focus (nb->priv->action_widgets[LEFT], direction))
|
|
return TRUE;
|
|
|
|
switch (direction)
|
|
{
|
|
case GTK_DIR_TAB_FORWARD:
|
|
return focus_to_labels (nb, direction, TRUE) ||
|
|
focus_to_arrows (nb, direction) ||
|
|
focus_to_action_widget (nb, RIGHT, direction) ||
|
|
focus_to_child (nb, direction);
|
|
|
|
case GTK_DIR_RIGHT:
|
|
return focus_to_labels (nb, direction, TRUE) ||
|
|
focus_to_arrows (nb, direction) ||
|
|
focus_to_action_widget (nb, RIGHT, direction);
|
|
|
|
case GTK_DIR_TAB_BACKWARD:
|
|
case GTK_DIR_UP:
|
|
case GTK_DIR_LEFT:
|
|
return FALSE;
|
|
|
|
case GTK_DIR_DOWN:
|
|
return focus_to_child (nb, direction);
|
|
}
|
|
break;
|
|
|
|
case FOCUS_RIGHT:
|
|
if (gtk_widget_child_focus (nb->priv->action_widgets[RIGHT], direction))
|
|
return TRUE;
|
|
|
|
switch (direction)
|
|
{
|
|
case GTK_DIR_TAB_FORWARD:
|
|
case GTK_DIR_RIGHT:
|
|
case GTK_DIR_UP:
|
|
return FALSE;
|
|
|
|
case GTK_DIR_TAB_BACKWARD:
|
|
case GTK_DIR_LEFT:
|
|
return focus_to_arrows (nb, direction) ||
|
|
focus_to_labels (nb, direction, FALSE) ||
|
|
focus_to_action_widget (nb, LEFT, direction);
|
|
|
|
case GTK_DIR_DOWN:
|
|
return focus_to_child (nb, direction);
|
|
}
|
|
break;
|
|
|
|
case FOCUS_ARROWS:
|
|
if (gtk_widget_child_focus (nb->priv->arrows, direction))
|
|
return TRUE;
|
|
|
|
switch (direction)
|
|
{
|
|
case GTK_DIR_TAB_FORWARD:
|
|
case GTK_DIR_RIGHT:
|
|
return focus_to_action_widget (nb, RIGHT, direction);
|
|
|
|
case GTK_DIR_UP:
|
|
return FALSE;
|
|
|
|
case GTK_DIR_TAB_BACKWARD:
|
|
case GTK_DIR_LEFT:
|
|
return focus_to_labels (nb, direction, FALSE) ||
|
|
focus_to_action_widget (nb, LEFT, direction);
|
|
|
|
case GTK_DIR_DOWN:
|
|
return focus_to_child (nb, direction);
|
|
}
|
|
break;
|
|
|
|
case FOCUS_LABEL:
|
|
if (nb->priv->focus_page &&
|
|
GTK_WIDGET_VISIBLE (nb->priv->focus_page->child) &&
|
|
gtk_widget_child_focus (nb->priv->focus_page->label->widget, direction))
|
|
return TRUE;
|
|
|
|
switch (direction)
|
|
{
|
|
case GTK_DIR_RIGHT:
|
|
return focus_to_next_label (nb, direction, TRUE) ||
|
|
focus_to_arrows (nb, direction) ||
|
|
focus_to_action_widget (nb, RIGHT, direction);
|
|
|
|
case GTK_DIR_LEFT:
|
|
return focus_to_next_label (nb, direction, FALSE) ||
|
|
focus_to_action_widget (nb, LEFT, direction);
|
|
|
|
case GTK_DIR_TAB_FORWARD:
|
|
return focus_to_arrows (nb, direction) ||
|
|
focus_to_action_widget (nb, RIGHT, direction);
|
|
|
|
case GTK_DIR_UP:
|
|
return FALSE;
|
|
|
|
case GTK_DIR_TAB_BACKWARD:
|
|
return focus_to_action_widget (nb, LEFT, direction);
|
|
|
|
case GTK_DIR_DOWN:
|
|
return focus_to_child (nb, direction);
|
|
}
|
|
break;
|
|
|
|
case FOCUS_CHILD:
|
|
page = nb->priv->current_page;
|
|
if (gtk_widget_child_focus (page->child, direction))
|
|
return TRUE;
|
|
|
|
switch (direction)
|
|
{
|
|
case GTK_DIR_RIGHT:
|
|
case GTK_DIR_LEFT:
|
|
case GTK_DIR_DOWN:
|
|
case GTK_DIR_TAB_FORWARD:
|
|
return FALSE;
|
|
|
|
case GTK_DIR_UP:
|
|
case GTK_DIR_TAB_BACKWARD:
|
|
return focus_to_action_widget (nb, LEFT, direction) ||
|
|
focus_to_labels (nb, direction, TRUE) ||
|
|
focus_to_arrows (nb, direction) ||
|
|
focus_to_action_widget (nb, RIGHT, direction);
|
|
}
|
|
break;
|
|
}
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
|
|
static void moo_notebook_set_focus_child(GtkContainer *container,
|
|
GtkWidget *child)
|
|
{
|
|
Page *page;
|
|
GtkWidget *focus_child, *window;
|
|
MooNotebook *nb = MOO_NOTEBOOK (container);
|
|
FocusType old_focus;
|
|
|
|
window = gtk_widget_get_toplevel (GTK_WIDGET (nb));
|
|
|
|
if (GTK_IS_WINDOW (window))
|
|
{
|
|
focus_child = gtk_window_get_focus (GTK_WINDOW (window));
|
|
|
|
if (focus_child && (page = find_grand_child (nb, focus_child)))
|
|
{
|
|
if (page->focus_child != focus_child)
|
|
{
|
|
if (page->focus_child)
|
|
g_object_weak_unref (G_OBJECT (page->focus_child),
|
|
(GWeakNotify) g_nullify_pointer,
|
|
&page->focus_child);
|
|
|
|
page->focus_child = focus_child;
|
|
|
|
g_object_weak_ref (G_OBJECT (page->focus_child),
|
|
(GWeakNotify) g_nullify_pointer,
|
|
&page->focus_child);
|
|
}
|
|
}
|
|
}
|
|
|
|
old_focus = nb->priv->focus;
|
|
|
|
if (child)
|
|
{
|
|
if (nb->priv->focus_page)
|
|
labels_invalidate (nb);
|
|
nb->priv->focus_page = NULL;
|
|
}
|
|
|
|
if (!child)
|
|
{
|
|
nb->priv->focus = FOCUS_NONE;
|
|
}
|
|
else if ((page = find_child (nb, child)))
|
|
{
|
|
nb->priv->focus = FOCUS_CHILD;
|
|
|
|
if (page != nb->priv->current_page)
|
|
moo_notebook_set_current_page (nb, page_index (nb, page));
|
|
}
|
|
else if ((page = find_label (nb, child)))
|
|
{
|
|
nb->priv->focus = FOCUS_LABEL;
|
|
nb->priv->focus_page = page;
|
|
|
|
if (nb->priv->arrows_gtk && page != nb->priv->current_page)
|
|
moo_notebook_set_current_page (nb, page_index (nb, page));
|
|
else if (!nb->priv->arrows_gtk)
|
|
labels_move_label_onscreen (nb, page, TRUE);
|
|
}
|
|
else if (child == nb->priv->action_widgets[LEFT])
|
|
{
|
|
nb->priv->focus = FOCUS_LEFT;
|
|
}
|
|
else if (child == nb->priv->action_widgets[RIGHT])
|
|
{
|
|
nb->priv->focus = FOCUS_RIGHT;
|
|
}
|
|
else if (child == nb->priv->arrows)
|
|
{
|
|
nb->priv->focus = FOCUS_ARROWS;
|
|
}
|
|
|
|
GTK_CONTAINER_CLASS(moo_notebook_parent_class)->set_focus_child (container, child);
|
|
}
|