diff options
| author | Alex Valavanis <valavanisalex@gmail.com> | 2011-07-09 14:26:35 +0000 |
|---|---|---|
| committer | Alex Valavanis <valavanisalex@gmail.com> | 2011-07-09 14:26:35 +0000 |
| commit | 351ad21da89e374b88b2214977f6e67ce4795ff0 (patch) | |
| tree | 71f70a4a72b52aac691b50d9d104387bdba84083 /src | |
| parent | Next step in refactoring color management. More to come. (diff) | |
| download | inkscape-351ad21da89e374b88b2214977f6e67ce4795ff0.tar.gz inkscape-351ad21da89e374b88b2214977f6e67ce4795ff0.zip | |
Merge upstream GDL 0.7.8 changes
(bzr r10430)
Diffstat (limited to 'src')
30 files changed, 5031 insertions, 32 deletions
diff --git a/src/libgdl/Makefile_insert b/src/libgdl/Makefile_insert index 5869633ea..2276aa801 100644 --- a/src/libgdl/Makefile_insert +++ b/src/libgdl/Makefile_insert @@ -40,4 +40,4 @@ libgdl_libgdl_a_SOURCES = \ libgdl/libgdltypebuiltins.c \ libgdl/libgdlmarshal.h \ libgdl/libgdlmarshal.c \ - libgdl/libgdl.h + libgdl/gdl.h diff --git a/src/libgdl/gdl-combo-button.c b/src/libgdl/gdl-combo-button.c new file mode 100644 index 000000000..6414a8110 --- /dev/null +++ b/src/libgdl/gdl-combo-button.c @@ -0,0 +1,383 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * gdl-combo-button.c + * + * Copyright (C) 2003 Jeroen Zwartepoorte + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> +#include "gdl-tools.h" +#include "gdl-combo-button.h" + +struct _GdlComboButtonPrivate { + GtkWidget *default_button; + GtkWidget *image; + GtkWidget *label; + GtkWidget *menu_button; + GtkWidget *menu; + gboolean menu_popped_up; +}; + +GDL_CLASS_BOILERPLATE (GdlComboButton, gdl_combo_button, GtkHBox, GTK_TYPE_HBOX); + +static void +default_button_clicked_cb (GtkButton *button, + gpointer user_data) +{ + GdlComboButton *combo; + GdlComboButtonPrivate *priv; + + combo = GDL_COMBO_BUTTON (user_data); + priv = combo->priv; + + if (!priv->menu_popped_up) + g_signal_emit_by_name (G_OBJECT (combo), + "activate-default", NULL); +} + +static gboolean +default_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (event->type == GDK_BUTTON_PRESS && event->button == 1) { + GTK_BUTTON (priv->menu_button)->button_down = TRUE; + gtk_button_pressed (GTK_BUTTON (priv->menu_button)); + } + + return FALSE; +} + +static gboolean +default_button_release_event_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (event->button == 1) { + gtk_button_released (GTK_BUTTON (priv->menu_button)); + } + + return FALSE; +} + +static gboolean +button_enter_notify_cb (GtkWidget *widget, + GdkEventCrossing *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (event->detail != GDK_NOTIFY_INFERIOR) { + GTK_BUTTON (priv->default_button)->in_button = TRUE; + GTK_BUTTON (priv->menu_button)->in_button = TRUE; + gtk_button_enter (GTK_BUTTON (priv->default_button)); + gtk_button_enter (GTK_BUTTON (priv->menu_button)); + } + + return TRUE; +} + +static gboolean +button_leave_notify_cb (GtkWidget *widget, + GdkEventCrossing *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (priv->menu_popped_up) + return TRUE; + + if (event->detail != GDK_NOTIFY_INFERIOR) { + GTK_BUTTON (priv->default_button)->in_button = FALSE; + GTK_BUTTON (priv->menu_button)->in_button = FALSE; + gtk_button_leave (GTK_BUTTON (priv->default_button)); + gtk_button_leave (GTK_BUTTON (priv->menu_button)); + } + + return TRUE; +} + +static void +menu_position_func (GtkMenu *menu, + gint *x_return, + gint *y_return, + gboolean *push_in, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + GtkAllocation *allocation; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + allocation = &(priv->default_button->allocation); + + gdk_window_get_origin (priv->default_button->window, x_return, y_return); + + *x_return += allocation->x; + *y_return += allocation->height; +} + +static gboolean +menu_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + if (event->type == GDK_BUTTON_PRESS && + (event->button == 1 || event->button == 3)) { + GTK_BUTTON (priv->menu_button)->button_down = TRUE; + + gtk_button_pressed (GTK_BUTTON (priv->menu_button)); + + priv->menu_popped_up = TRUE; + gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL, + menu_position_func, combo_button, + event->button, event->time); + } + + return TRUE; +} + +static void +menu_deactivate_cb (GtkMenuShell *menu_shell, + gpointer user_data) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (user_data); + priv = combo_button->priv; + + priv->menu_popped_up = FALSE; + + GTK_BUTTON (priv->menu_button)->button_down = FALSE; + GTK_BUTTON (priv->menu_button)->in_button = FALSE; + GTK_BUTTON (priv->default_button)->in_button = FALSE; + gtk_button_leave (GTK_BUTTON (priv->menu_button)); + gtk_button_leave (GTK_BUTTON (priv->default_button)); + gtk_button_clicked (GTK_BUTTON (priv->menu_button)); +} + +static void +menu_detacher (GtkWidget *widget, + GtkMenu *menu) +{ + GdlComboButton *combo_button; + + combo_button = GDL_COMBO_BUTTON (widget); + + g_signal_handlers_disconnect_by_func (G_OBJECT (menu), + menu_deactivate_cb, + combo_button); + combo_button->priv->menu = NULL; +} + +static void +gdl_combo_button_destroy (GtkObject *object) +{ + GdlComboButton *combo_button; + GdlComboButtonPrivate *priv; + + combo_button = GDL_COMBO_BUTTON (object); + priv = combo_button->priv; + + if (priv) { + g_free (priv); + combo_button->priv = NULL; + } + + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + +static void +gdl_combo_button_class_init (GdlComboButtonClass *klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (klass); + object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + + object_class->destroy = gdl_combo_button_destroy; + + g_signal_new ("activate-default", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdlComboButtonClass, activate_default), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +gdl_combo_button_instance_init (GdlComboButton *combo_button) +{ + GdlComboButtonPrivate *priv; + GtkWidget *hbox, *align, *arrow; + + priv = g_new (GdlComboButtonPrivate, 1); + combo_button->priv = priv; + + priv->menu = NULL; + priv->menu_popped_up = FALSE; + + priv->default_button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->default_button), GTK_RELIEF_NONE); + + /* Following code copied from gtk_button_construct_child. */ + priv->label = gtk_label_new (""); + gtk_label_set_use_underline (GTK_LABEL (priv->label), TRUE); + gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label), + priv->default_button); + + priv->image = gtk_image_new (); + hbox = gtk_hbox_new (FALSE, 2); + + align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + + gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (hbox), priv->label, FALSE, FALSE, 0); + + gtk_container_add (GTK_CONTAINER (priv->default_button), align); + gtk_container_add (GTK_CONTAINER (align), hbox); + /* End copied block. */ + + gtk_box_pack_start (GTK_BOX (combo_button), priv->default_button, + FALSE, FALSE, 0); + gtk_widget_show_all (priv->default_button); + + priv->menu_button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (priv->menu_button), GTK_RELIEF_NONE); + arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (priv->menu_button), arrow); + gtk_box_pack_start (GTK_BOX (combo_button), priv->menu_button, FALSE, + FALSE, 0); + gtk_widget_show_all (priv->menu_button); + + /* Default button. */ + g_signal_connect (G_OBJECT (priv->default_button), "clicked", + G_CALLBACK (default_button_clicked_cb), combo_button); + g_signal_connect (G_OBJECT (priv->default_button), "button_press_event", + G_CALLBACK (default_button_press_event_cb), combo_button); + g_signal_connect (G_OBJECT (priv->default_button), "button_release_event", + G_CALLBACK (default_button_release_event_cb), combo_button); + g_signal_connect (G_OBJECT (priv->default_button), "enter_notify_event", + G_CALLBACK (button_enter_notify_cb), combo_button); + g_signal_connect (G_OBJECT (priv->default_button), "leave_notify_event", + G_CALLBACK (button_leave_notify_cb), combo_button); + + /* Menu button. */ + g_signal_connect (G_OBJECT (priv->menu_button), "button_press_event", + G_CALLBACK (menu_button_press_event_cb), combo_button); + g_signal_connect (G_OBJECT (priv->menu_button), "enter_notify_event", + G_CALLBACK (button_enter_notify_cb), combo_button); + g_signal_connect (G_OBJECT (priv->menu_button), "leave_notify_event", + G_CALLBACK (button_leave_notify_cb), combo_button); +} + +GtkWidget * +gdl_combo_button_new (void) +{ + GtkWidget *combo_button; + + combo_button = GTK_WIDGET (g_object_new (GDL_TYPE_COMBO_BUTTON, NULL)); + + return combo_button; +} + +void +gdl_combo_button_set_icon (GdlComboButton *combo_button, + GdkPixbuf *pixbuf) +{ + GdlComboButtonPrivate *priv; + + g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button)); + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + + priv = combo_button->priv; + + gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf); +} + +void +gdl_combo_button_set_label (GdlComboButton *combo_button, + const gchar *label) +{ + GdlComboButtonPrivate *priv; + + g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button)); + g_return_if_fail (label != NULL); + + priv = combo_button->priv; + + gtk_label_set_text (GTK_LABEL (priv->label), label); +} + +void +gdl_combo_button_set_menu (GdlComboButton *combo_button, + GtkMenu *menu) +{ + GdlComboButtonPrivate *priv; + + g_return_if_fail (GDL_IS_COMBO_BUTTON (combo_button)); + g_return_if_fail (GTK_IS_MENU (menu)); + + priv = combo_button->priv; + + if (priv->menu != NULL) + gtk_menu_detach (GTK_MENU (priv->menu)); + + priv->menu = GTK_WIDGET (menu); + if (menu == NULL) + return; + + gtk_menu_attach_to_widget (menu, GTK_WIDGET (combo_button), menu_detacher); + + g_signal_connect (G_OBJECT (menu), "deactivate", + G_CALLBACK (menu_deactivate_cb), combo_button); +} diff --git a/src/libgdl/gdl-combo-button.h b/src/libgdl/gdl-combo-button.h new file mode 100644 index 000000000..6e80af0b6 --- /dev/null +++ b/src/libgdl/gdl-combo-button.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * gdl-combo-button.h + * + * Copyright (C) 2003 Jeroen Zwartepoorte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GDL_COMBO_BUTTON_H_ +#define _GDL_COMBO_BUTTON_H_ + +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtkmenu.h> + +G_BEGIN_DECLS + +#define GDL_TYPE_COMBO_BUTTON (gdl_combo_button_get_type ()) +#define GDL_COMBO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_COMBO_BUTTON, GdlComboButton)) +#define GDL_COMBO_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDL_TYPE_COMBO_BUTTON, GdlComboButtonClass)) +#define GDL_IS_COMBO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_COMBO_BUTTON)) +#define GDL_IS_COMBO_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDL_TYPE_COMBO_BUTTON)) + +typedef struct _GdlComboButton GdlComboButton; +typedef struct _GdlComboButtonPrivate GdlComboButtonPrivate; +typedef struct _GdlComboButtonClass GdlComboButtonClass; + +struct _GdlComboButton { + GtkHBox parent; + + GdlComboButtonPrivate *priv; +}; + +struct _GdlComboButtonClass { + GtkHBoxClass parent_class; + + /* Signals. */ + void (* activate_default) (GdlComboButton *combo_button); +}; + +GType gdl_combo_button_get_type (void); +GtkWidget *gdl_combo_button_new (void); + +void gdl_combo_button_set_icon (GdlComboButton *combo_button, + GdkPixbuf *pixbuf); +void gdl_combo_button_set_label (GdlComboButton *combo_button, + const gchar *label); +void gdl_combo_button_set_menu (GdlComboButton *combo_button, + GtkMenu *menu); + +G_END_DECLS + +#endif /* _GDL_COMBO_BUTTON_H_ */ diff --git a/src/libgdl/gdl-data-frame.c b/src/libgdl/gdl-data-frame.c new file mode 100644 index 000000000..d6fb19533 --- /dev/null +++ b/src/libgdl/gdl-data-frame.c @@ -0,0 +1,297 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2001 Dave Camp <dave@ximian.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gdl-i18n.h" +#include "gdl-tools.h" +#include <string.h> + +#include "gdl-data-view.h" +#include "gdl-data-frame.h" +#include "gdl-data-model.h" +#include "gdl-data-row.h" + +struct _GdlDataFramePrivate { + GdkRectangle shadow_r; + GdkRectangle frame_r; + GdkRectangle titlebar_r; + GdkRectangle title_r; + GdkRectangle close_r; + GdkRectangle row_r; + + int shadow_offset; + int titlebar_height; + char *title; + + GdlDataRow *row; + + PangoLayout *layout; + + gboolean selected; +}; + +static void gdl_data_frame_class_init (GdlDataFrameClass *klass); +static void gdl_data_frame_instance_init (GdlDataFrame *obj); +static void gdl_data_frame_finalize (GObject *object); + +GDL_CLASS_BOILERPLATE (GdlDataFrame, gdl_data_frame, GObject, G_TYPE_OBJECT); + +#define PAD 2 +#define BORDER 1 + +#define CENTERY(r1, r2) { r1.y = ((r2.y + (r2.height / 2)) - (r1.height / 2)); } + +void +gdl_data_frame_layout (GdlDataFrame *frame) +{ + GdkPixbuf *close_pixbuf; + /* Sizes */ + if (frame->priv->row) { + gdl_data_row_get_size (frame->priv->row, + NULL, NULL, + &frame->priv->row_r.width, + &frame->priv->row_r.height); + } else { + frame->priv->row_r.height = frame->priv->row_r.width = 0; + } + + if (frame->priv->layout) { + pango_layout_get_pixel_size (frame->priv->layout, + &frame->priv->title_r.width, + &frame->priv->title_r.height); + } else { + frame->priv->title_r.width = frame->priv->title_r.height = 0; + } + + close_pixbuf = gdl_data_view_get_close_pixbuf (frame->view); + if (close_pixbuf) { + frame->priv->close_r.width = + gdk_pixbuf_get_width (close_pixbuf); + frame->priv->close_r.height = + gdk_pixbuf_get_width (close_pixbuf); + } else { + frame->priv->close_r.width = frame->priv->close_r.height = 0; + } + + frame->priv->titlebar_r.height = MAX (frame->priv->titlebar_height, + frame->priv->title_r.height); + frame->priv->titlebar_r.height = MAX (frame->priv->titlebar_r.height, + frame->priv->close_r.height); + + frame->priv->frame_r.width = 2 * BORDER + 3 * PAD + frame->priv->title_r.width + frame->priv->close_r.width; + frame->priv->frame_r.width = MAX (frame->priv->frame_r.width, + frame->priv->row_r.width + 2 * BORDER + 2 * PAD); + frame->priv->frame_r.height = frame->priv->row_r.height + frame->priv->titlebar_r.height + 2 * PAD + 2 * BORDER; + frame->priv->titlebar_r.width = frame->priv->frame_r.width - BORDER; + frame->priv->shadow_r.width = frame->priv->frame_r.width; + frame->priv->shadow_r.height = frame->priv->frame_r.height; + + /* Locations */ + frame->priv->frame_r.x = frame->area.x; + frame->priv->frame_r.y = frame->area.y; + + frame->priv->shadow_r.x = frame->priv->frame_r.x + frame->priv->shadow_offset; + frame->priv->shadow_r.y = frame->priv->frame_r.y + frame->priv->shadow_offset; + frame->priv->titlebar_r.x = frame->priv->frame_r.x + BORDER; + frame->priv->titlebar_r.y = frame->priv->frame_r.y + BORDER; + frame->priv->title_r.x = frame->priv->frame_r.x + BORDER + PAD; + CENTERY (frame->priv->title_r, frame->priv->titlebar_r); + frame->priv->close_r.x = (frame->priv->frame_r.x + frame->priv->frame_r.width) - (frame->priv->close_r.width + BORDER + PAD); + CENTERY (frame->priv->close_r, frame->priv->titlebar_r); + + if (frame->priv->row) { + frame->priv->row_r.x = frame->priv->frame_r.x + BORDER + PAD; + frame->priv->row_r.y = frame->priv->titlebar_r.y + frame->priv->titlebar_r.height + PAD; + gdl_data_row_layout (frame->priv->row, &frame->priv->row_r); + } else { + frame->priv->row_r.x = frame->priv->row_r.y = 0; + } + + frame->area.width = frame->priv->frame_r.width + frame->priv->shadow_offset; + frame->area.height = frame->priv->frame_r.height + frame->priv->shadow_offset; +} + +#if 0 /* not used */ +static void +change_layout (GdlDataFrame *frame) +{ + char *text = frame->priv->title ? frame->priv->title : "?"; + pango_layout_set_text (frame->priv->layout, text, strlen (text)); +} +#endif + +#define EXPLODE(r) (r).x, (r).y, (r).width, (r).height + +void +gdl_data_frame_draw (GdlDataFrame *frame, GdkDrawable *drawable, + GdkRectangle *expose_area) +{ + GdkRectangle inter; + guint8 state = + frame->priv->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL; + + gdk_draw_rectangle (drawable, + GTK_WIDGET (frame->view)->style->dark_gc[state], + TRUE, + EXPLODE (frame->priv->shadow_r)); + gdk_draw_rectangle (drawable, + GTK_WIDGET (frame->view)->style->base_gc[GTK_STATE_NORMAL], + TRUE, + EXPLODE (frame->priv->frame_r)); + gdk_draw_rectangle (drawable, + GTK_WIDGET (frame->view)->style->black_gc, + FALSE, + EXPLODE (frame->priv->frame_r)); + gdk_draw_rectangle (drawable, + GTK_WIDGET (frame->view)->style->bg_gc[state], + TRUE, + EXPLODE (frame->priv->titlebar_r)); + gdk_draw_layout (drawable, + GTK_WIDGET (frame->view)->style->fg_gc[state], + frame->priv->title_r.x, frame->priv->title_r.y, + frame->priv->layout); + + if (gdk_rectangle_intersect (expose_area, &frame->priv->close_r, &inter)) { + GdkPixbuf *pixbuf = gdl_data_view_get_close_pixbuf (frame->view); + gdk_draw_pixbuf (drawable, NULL, pixbuf, + 0, 0, + inter.x - frame->priv->close_r.x, + inter.y - frame->priv->close_r.y, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + GDK_RGB_DITHER_NORMAL, 0, 0); + } + + if (frame->priv->row) { + if (gdk_rectangle_intersect (expose_area, &frame->priv->row_r, + &inter)) { + gdl_data_row_render (frame->priv->row, drawable, + &inter, + frame->priv->selected ? GTK_CELL_RENDERER_SELECTED : 0); + } + } +} + +void +gdl_data_frame_class_init (GdlDataFrameClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *)klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gdl_data_frame_finalize; +} + +void +gdl_data_frame_instance_init (GdlDataFrame *frame) +{ + frame->priv = g_new0 (GdlDataFramePrivate, 1); + frame->area.x = frame->area.y = 0; + frame->priv->shadow_offset = 3; + frame->priv->titlebar_height = 20; + + frame->area.height = frame->area.width = 100; +} + +void +gdl_data_frame_finalize (GObject *object) +{ + GdlDataFrame *frame = GDL_DATA_FRAME (object); + + if (frame->priv) { + g_free (frame->priv->title); + g_object_unref (frame->priv->layout); + g_object_unref (frame->priv->row); + + g_free (frame->priv); + frame->priv = NULL; + } + GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +void +gdl_data_frame_set_selected (GdlDataFrame *frame, + gboolean val) +{ + frame->priv->selected = val; + + gdk_window_invalidate_rect (GTK_LAYOUT (frame->view)->bin_window, + &frame->priv->frame_r, + TRUE); +} + +gboolean +gdl_data_frame_button_press (GdlDataFrame *frame, + GdkEventButton *event) +{ + return FALSE; +} + +void +gdl_data_frame_set_position (GdlDataFrame *frame, + int x, + int y) +{ + frame->area.x = x; + frame->area.y = y; + + gdl_data_frame_layout (frame); +} + +static void +setup_layout (GdlDataFrame *frame) +{ + PangoFontDescription *font_desc = + pango_font_description_copy (GTK_WIDGET (frame->view)->style->font_desc); + + pango_font_description_set_weight (font_desc, + PANGO_WEIGHT_BOLD); + + frame->priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (frame->view), + frame->priv->title ? frame->priv->title : "?"); + pango_layout_set_font_description (frame->priv->layout, + font_desc); + pango_font_description_free (font_desc); +} + + +GdlDataFrame * +gdl_data_frame_new (GdlDataView *view, + GdlDataRow *row) +{ + GdlDataFrame *frame; + frame = GDL_DATA_FRAME (g_object_new (GDL_TYPE_DATA_FRAME, NULL)); + + frame->view = view; + + frame->priv->row = row; + frame->priv->title = g_strdup (gdl_data_row_get_title (row)); + + setup_layout (frame); + + gdl_data_frame_layout (frame); + + return frame; +} diff --git a/src/libgdl/gdl-data-frame.h b/src/libgdl/gdl-data-frame.h new file mode 100644 index 000000000..740c38293 --- /dev/null +++ b/src/libgdl/gdl-data-frame.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2001 Dave Camp <dave@ximian.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef GDL_DATA_FRAME_H +#define GDL_DATA_FRAME_H + +#include <glib-object.h> +#include <gdl/gdl-data-row.h> + +G_BEGIN_DECLS + +#define GDL_TYPE_DATA_FRAME (gdl_data_frame_get_type ()) +#define GDL_DATA_FRAME(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DATA_FRAME, GdlDataFrame)) +#define GDL_DATA_FRAME_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_DATA_VIEW_FRAM, GdlDataFrame)) +#define GDL_IS_DATA_FRAME(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DATA_FRAME)) +#define GDL_IS_DATA_FRAME_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DATA_FRAME)) +#define GDL_DATA_FRAME_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GDL_TYPE_DATA_FRAME, GdlDataFrameClass)) + +typedef struct _GdlDataFrame GdlDataFrame; +typedef struct _GdlDataFramePrivate GdlDataFramePrivate; +typedef struct _GdlDataFrameClass GdlDataFrameClass; + +struct _GdlDataFrame { + GObject parent; + + GdlDataView *view; + GdkRectangle area; + + GdlDataFramePrivate *priv; +}; + +struct _GdlDataFrameClass { + GObjectClass parent_class; +}; + +GType gdl_data_frame_get_type (void); +GdlDataFrame *gdl_data_frame_new (GdlDataView *view, + GdlDataRow *row); +void gdl_data_frame_layout (GdlDataFrame *frame); +void gdl_data_frame_draw (GdlDataFrame *item, + GdkDrawable *drawable, + GdkRectangle *expose_area); +void gdl_data_frame_set_selected (GdlDataFrame *frame, + gboolean val); +gboolean gdl_data_frame_button_press (GdlDataFrame *frame, + GdkEventButton *event); +void gdl_data_frame_set_position (GdlDataFrame *frame, + int x, + int y); + +G_END_DECLS + +#endif diff --git a/src/libgdl/gdl-data-model-test.c b/src/libgdl/gdl-data-model-test.c new file mode 100644 index 000000000..ec6ed4d50 --- /dev/null +++ b/src/libgdl/gdl-data-model-test.c @@ -0,0 +1,240 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gdl-i18n.h" + +#include "gdl-data-model-test.h" +#include "gdl-data-model.h" + +#include <string.h> +#include <gtk/gtkcellrenderertext.h> +#include <gtk/gtkcellrenderertoggle.h> + +GObjectClass *parent_class; + +typedef struct _DataItem { + char *name; + char *value; + char *path; + struct _DataItem *children; +} DataItem; + +DataItem data1[] = { + { "foo1", "foo1", "0:0", NULL}, + { "bar1", "bar1", "0:1", NULL }, + { "baz1", "baz1", "0:2", NULL }, + { NULL, NULL, NULL, NULL } + +}; +DataItem data2[] = { + { "foo2", "foo2", "1:0", NULL }, + { "bar2", "bar2", "1:1", NULL }, + { "baz2", "baz2", "1:2", NULL }, + { NULL, NULL, NULL, NULL } + +}; + +DataItem data5[] = { + { "1", "1", "2:2:1:0", NULL }, + { "2", "2", "2:2:1:1", NULL }, + { "3", "3", "2:2:1:2", NULL }, + { "4", "4", "2:2:1:3", NULL }, + { "5", "5", "2:2:1:4", NULL }, + { "6", "6", "2:2:1:5", NULL }, + { NULL, NULL, NULL, NULL } + +}; +DataItem data4[] = { + { "foo4", "foo4", "2:2:0", NULL }, + { "bar4", "[...]", "2:2:1", data5 }, + { "baz4", "baz4", "2:2:2", NULL }, + { NULL, NULL, NULL, NULL } + +}; +DataItem data3[] = { + { "foo foo", "foo3", "2:0", NULL }, + { "bar3", "1", "2:1", NULL }, + { "baz3", "{...}", "2:2", data4 }, + { NULL, NULL, NULL, NULL } + +}; + +DataItem root[] = { + { "test-data", "value1", "0", NULL } , + { "test-data2", "value2", "1", NULL } , + { "test-data3", "{...}", "2", data3 } , + { NULL, NULL, NULL } +}; + +static gboolean +get_iter (GdlDataModel *dm, GdlDataIter *iter, GtkTreePath *path) +{ + int *i = gtk_tree_path_get_indices (path); + int n = gtk_tree_path_get_depth (path); + DataItem *item; + + g_assert (i); + item = &root[*i++]; + + while (--n) { + item = &item->children[*i++]; + } + + iter->data1 = item; + + return TRUE; +} + +static GtkTreePath * +get_path (GdlDataModel *dm, GdlDataIter *iter) +{ + DataItem *item = iter->data1; + return gtk_tree_path_new_from_string (item->path); +} + +static void +get_name (GdlDataModel *dm, GdlDataIter *iter, char **name) +{ + DataItem *item = iter->data1; + *name = item->name; +} + +static void +get_value (GdlDataModel *dm, GdlDataIter *iter, GValue *value) +{ + DataItem *item = iter->data1; + if (strcmp (item->name, "bar3")) { + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, item->value); + } else { + g_value_init (value, G_TYPE_BOOLEAN); + g_value_set_boolean (value, !strcmp (item->value, "1")); + } +} + +static void +get_renderer (GdlDataModel *dm, GdlDataIter *iter, + GtkCellRenderer **renderer, char **field, + gboolean *is_editable) +{ + DataItem *item = iter->data1; + if (!strcmp (item->name, "bar3")) { + *renderer = g_object_new (gtk_cell_renderer_toggle_get_type (), + "activatable", TRUE, NULL); + *field = "active"; + } else { + *renderer = g_object_new (gtk_cell_renderer_text_get_type (), + "editable", TRUE, NULL); + *field = "text"; + } + *is_editable = (item->children == NULL); +} + +static gboolean +iter_next (GdlDataModel *dm, GdlDataIter *iter) +{ + DataItem *item = iter->data1; + item++; + if (item->name) { + iter->data1 = item; + return TRUE; + } else { + return FALSE; + } +} + +static gboolean +iter_children (GdlDataModel *dm, GdlDataIter *iter, GdlDataIter *parent) +{ + DataItem *item = parent->data1; + + item = &item->children[0]; + if (item) { + iter->data1 = item; + return TRUE; + } else { + return FALSE; + } +} + +static gboolean +iter_has_child (GdlDataModel *dm, GdlDataIter *iter) +{ + DataItem *item = iter->data1; + if (item->children) { + return TRUE; + } else { + return FALSE; + } +} + + +static void +gdl_data_model_test_instance_init (GdlDataModelTest *model) +{ +} + +static void +gdl_data_model_test_finalize (GObject *object) +{ + (*parent_class->finalize) (object); +} + +static void +gdl_data_model_test_class_init (GdlDataModelTestClass *klass) +{ + GObjectClass *object_class; + parent_class = g_type_class_peek_parent (klass); + object_class = (GObjectClass *)klass; + object_class->finalize = gdl_data_model_test_finalize; +} + +static void +gdl_data_model_test_data_model_init (GdlDataModelIface *iface) +{ + iface->get_iter = get_iter; + iface->get_path = get_path; + iface->get_name = get_name; + iface->get_value = get_value; + iface->get_renderer = get_renderer; + iface->iter_next = iter_next; + iface->iter_children = iter_children; + iface->iter_has_child = iter_has_child; +} + +GType +gdl_data_model_test_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo data_model_test_info = { + sizeof (GdlDataModelTestClass), + NULL, NULL, + (GClassInitFunc) gdl_data_model_test_class_init, + NULL, NULL, + sizeof (GdlDataModelTest), 0, + (GInstanceInitFunc) gdl_data_model_test_instance_init + }; + + static const GInterfaceInfo data_model_info = { + (GInterfaceInitFunc) gdl_data_model_test_data_model_init, + NULL, NULL + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "GdlDataModelTest", + &data_model_test_info, 0); + g_type_add_interface_static (type, + GDL_TYPE_DATA_MODEL, + &data_model_info); + } + return type; +} + +GdlDataModelTest * +gdl_data_model_test_new (void) +{ + return GDL_DATA_MODEL_TEST (g_object_new (gdl_data_model_test_get_type (), NULL)); +} diff --git a/src/libgdl/gdl-data-model-test.h b/src/libgdl/gdl-data-model-test.h new file mode 100644 index 000000000..c8add8daf --- /dev/null +++ b/src/libgdl/gdl-data-model-test.h @@ -0,0 +1,32 @@ +#ifndef GDL_DATA_MODEL_TEST_H +#define GDL_DATA_MODEL_TEST_H + +#include <glib.h> +#include "gdl-data-model.h" + +G_BEGIN_DECLS + +#define GDL_TYPE_DATA_MODEL_TEST (gdl_data_model_test_get_type ()) +#define GDL_DATA_MODEL_TEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_DATA_MODEL_TEST, GdlDataModelTest)) +#define GDL_IS_DATA_MODEL_TEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_DATA_MODEL_TEST)) + + +typedef struct _GdlDataModelTest GdlDataModelTest; +typedef struct _GdlDataModelTestClass GdlDataModelTestClass; + +struct _GdlDataModelTest { + GObject parent; + + int stamp; +}; + +struct _GdlDataModelTestClass { + GObjectClass parent_class; +}; + +GType gdl_data_model_test_get_type (void); +GdlDataModelTest *gdl_data_model_test_new (void); + +G_END_DECLS + +#endif diff --git a/src/libgdl/gdl-data-model.c b/src/libgdl/gdl-data-model.c new file mode 100644 index 000000000..69fbb93d5 --- /dev/null +++ b/src/libgdl/gdl-data-model.c @@ -0,0 +1,160 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2001 Dave Camp <dave@ximian.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gdl-data-model.h" + +gboolean +gdl_data_model_get_iter (GdlDataModel *dm, + GdlDataIter *iter, + GtkTreePath *path) +{ + g_return_val_if_fail (dm != NULL, FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (GDL_DATA_MODEL_GET_IFACE (dm)->get_iter != NULL, + FALSE); + + return (*GDL_DATA_MODEL_GET_IFACE (dm)->get_iter) (dm, iter, path); +} + +GtkTreePath * +gdl_data_model_get_path (GdlDataModel *dm, + GdlDataIter *iter) +{ + g_return_val_if_fail (dm != NULL, NULL); + g_return_val_if_fail (iter != NULL, NULL); + g_return_val_if_fail (GDL_DATA_MODEL_GET_IFACE (dm)->get_path != NULL, + NULL); + + return (*GDL_DATA_MODEL_GET_IFACE (dm)->get_path) (dm, iter); +} + +void +gdl_data_model_get_name (GdlDataModel *dm, + GdlDataIter *iter, + char **name) +{ + g_return_if_fail (dm != NULL); + g_return_if_fail (iter != NULL); + g_return_if_fail (name != NULL); + g_return_if_fail (GDL_DATA_MODEL_GET_IFACE (dm)->get_name != NULL); + + (*GDL_DATA_MODEL_GET_IFACE (dm)->get_name) (dm, iter, name); +} + +void +gdl_data_model_get_value (GdlDataModel *dm, + GdlDataIter *iter, + GValue *value) +{ + g_return_if_fail (dm != NULL); + g_return_if_fail (iter != NULL); + g_return_if_fail (value != NULL); + g_return_if_fail (GDL_DATA_MODEL_GET_IFACE (dm)->get_value != NULL); + + (*GDL_DATA_MODEL_GET_IFACE (dm)->get_value) (dm, iter, value); +} + +void +gdl_data_model_get_renderer (GdlDataModel *dm, + GdlDataIter *iter, + GtkCellRenderer **renderer, + char **field, + gboolean *is_editable) +{ + g_return_if_fail (dm != NULL); + g_return_if_fail (iter != NULL); + g_return_if_fail (renderer != NULL); + g_return_if_fail (GDL_DATA_MODEL_GET_IFACE (dm)->get_renderer != NULL); + + (*GDL_DATA_MODEL_GET_IFACE (dm)->get_renderer) (dm, iter, + renderer, field, + is_editable); +} + +gboolean +gdl_data_model_iter_next (GdlDataModel *dm, + GdlDataIter *iter) +{ + g_return_val_if_fail (dm != NULL, FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (GDL_DATA_MODEL_GET_IFACE (dm)->iter_next != NULL, FALSE); + + return (*GDL_DATA_MODEL_GET_IFACE (dm)->iter_next) (dm, iter); +} + +gboolean +gdl_data_model_iter_children (GdlDataModel *dm, + GdlDataIter *iter, + GdlDataIter *parent) +{ + g_return_val_if_fail (dm != NULL, FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (GDL_DATA_MODEL_GET_IFACE (dm)->iter_children != NULL, FALSE); + + return (*GDL_DATA_MODEL_GET_IFACE (dm)->iter_children) (dm, iter, parent); +} + +gboolean +gdl_data_model_iter_has_child (GdlDataModel *dm, + GdlDataIter *iter) +{ + g_return_val_if_fail (dm != NULL, FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (GDL_DATA_MODEL_GET_IFACE (dm)->iter_has_child != NULL, FALSE); + + return (*GDL_DATA_MODEL_GET_IFACE (dm)->iter_has_child) (dm, iter); +} + +static void +gdl_data_model_base_init (gpointer g_class) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + } +} + +GType +gdl_data_model_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof (GdlDataModelIface), + gdl_data_model_base_init, + NULL, NULL, NULL, NULL, 0, 0, NULL + }; + + type = g_type_register_static (G_TYPE_INTERFACE, + "GdlDataModel", + &info, 0); + g_type_interface_add_prerequisite (type, G_TYPE_OBJECT); + } + + return type; +} diff --git a/src/libgdl/gdl-data-model.h b/src/libgdl/gdl-data-model.h new file mode 100644 index 000000000..521a65d0c --- /dev/null +++ b/src/libgdl/gdl-data-model.h @@ -0,0 +1,105 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2001 Dave Camp <dave@ximian.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef GDL_DATA_MODEL_H +#define GDL_DATA_MODEL_H + +#include <glib.h> +#include <glib-object.h> + +/* Using GtkTreePath to save time */ +#include <gtk/gtktreemodel.h> +#include <gtk/gtkcellrenderer.h> + +G_BEGIN_DECLS + +#define GDL_TYPE_DATA_MODEL (gdl_data_model_get_type ()) +#define GDL_DATA_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_DATA_MODEL, GdlDataModel)) +#define GDL_IS_DATA_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_DATA_MODEL)) +#define GDL_DATA_MODEL_GET_IFACE(obj) ((GdlDataModelIface *)g_type_interface_peek (((GTypeInstance *)GDL_DATA_MODEL (obj))->g_class, GDL_TYPE_DATA_MODEL)) + +typedef struct _GdlDataModel GdlDataModel; +typedef struct _GdlDataIter GdlDataIter; +typedef struct _GdlDataModelIface GdlDataModelIface; + +struct _GdlDataIter { + int stamp; + + gpointer data1; + gpointer data2; + gpointer data3; +}; + +struct _GdlDataModelIface { + GTypeInterface g_iface; + + /* Signals */ + void (*path_changed) (GdlDataModel *dm, GtkTreePath *path); + void (*path_inserted) (GdlDataModel *dm, GtkTreePath *path); + void (*path_deleted) (GdlDataModel *dm, GtkTreePath *path); + + /* Virtual Table */ + gboolean (*get_iter) (GdlDataModel *dm, GdlDataIter *iter, + GtkTreePath *path); + GtkTreePath* (*get_path) (GdlDataModel *dm, GdlDataIter *iter); + + void (*get_name) (GdlDataModel *dm, GdlDataIter *iter, + char **name); + void (*get_value) (GdlDataModel *dm, GdlDataIter *iter, + GValue *value); + void (*get_renderer) (GdlDataModel *dm, GdlDataIter *iter, + GtkCellRenderer **renderer, char **field, + gboolean *is_editable); + gboolean (*iter_next) (GdlDataModel *dm, GdlDataIter *iter); + gboolean (*iter_children) (GdlDataModel *dm, GdlDataIter *iter, + GdlDataIter *parent); + gboolean (*iter_has_child) (GdlDataModel *dm, GdlDataIter *iter); +}; + +GType gdl_data_model_get_type (void); +gboolean gdl_data_model_get_iter (GdlDataModel *dm, + GdlDataIter *iter, + GtkTreePath *path); +GtkTreePath *gdl_data_model_get_path (GdlDataModel *dm, + GdlDataIter *iter); +void gdl_data_model_get_name (GdlDataModel *dm, + GdlDataIter *iter, + char **name); +void gdl_data_model_get_value (GdlDataModel *dm, + GdlDataIter *iter, + GValue *value); +void gdl_data_model_get_renderer (GdlDataModel *dm, + GdlDataIter *iter, + GtkCellRenderer **renderer, + char **field, + gboolean *is_editable); +gboolean gdl_data_model_iter_next (GdlDataModel *dm, + GdlDataIter *iter); +gboolean gdl_data_model_iter_children (GdlDataModel *dm, + GdlDataIter *iter, + GdlDataIter *children); +gboolean gdl_data_model_iter_has_child (GdlDataModel *dm, + GdlDataIter *iter); + +G_END_DECLS + +#endif diff --git a/src/libgdl/gdl-data-row.c b/src/libgdl/gdl-data-row.c new file mode 100644 index 000000000..666c658fe --- /dev/null +++ b/src/libgdl/gdl-data-row.c @@ -0,0 +1,604 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2001 Dave Camp <dave@ximian.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gdl-i18n.h" +#include "gdl-tools.h" +#include "gdl-data-row.h" +#include "gdl-data-model.h" + +#include <gtk/gtktreemodel.h> +#include <gtk/gtkcellrenderer.h> + +struct _GdlDataRowPrivate { + GdlDataModel *model; + GtkTreePath *path; + GdlDataView *view; + + char *name; + + /* area_r + * +- title_r + * | +- name_r + * | +- sep_r + * +- data_r + * +- expand_r + * +- cell_r + */ + + GdkRectangle area_r; + + GdkRectangle title_r; + GdkRectangle name_r; + GdkRectangle sep_r; + + GdkRectangle data_r; + GdkRectangle expand_r; + GdkRectangle cell_r; + + gboolean multi; + GtkCellRenderer *cell; + GList *subrows; + + char *renderer_field; + + gboolean expanded; + gboolean focused; + gboolean editable; + + int split; + int child_split; + gboolean selected; +}; + +GDL_CLASS_BOILERPLATE (GdlDataRow, gdl_data_row, GObject, G_TYPE_OBJECT); + + +static void +expand (GdlDataRow *row) +{ + GdlDataIter iter; + gboolean valid; + + if (gdl_data_model_get_iter (row->priv->model, &iter, row->priv->path)) { + valid = gdl_data_model_iter_children (row->priv->model, + &iter, &iter); + while (valid) { + GdlDataRow *new_row; + GtkTreePath *path; + + path = gdl_data_model_get_path (row->priv->model, + &iter); + new_row = gdl_data_row_new (row->priv->view, + path); + row->priv->subrows = + g_list_prepend (row->priv->subrows, new_row); + gtk_tree_path_free (path); + + valid = gdl_data_model_iter_next (row->priv->model, + &iter); + } + row->priv->subrows = g_list_reverse (row->priv->subrows); + + row->priv->expanded = TRUE; + gdl_data_view_layout (GDL_DATA_VIEW (row->priv->view)); + gtk_widget_queue_draw (GTK_WIDGET (row->priv->view)); + } +} + +static void +contract (GdlDataRow *row) +{ + GList *l; + for (l = row->priv->subrows; l != NULL; l = l->next) { + g_object_unref (G_OBJECT (l->data)); + } + g_list_free (row->priv->subrows); + row->priv->subrows = NULL; + + row->priv->expanded = FALSE; + gdl_data_view_layout (GDL_DATA_VIEW (row->priv->view)); + gtk_widget_queue_draw (GTK_WIDGET (row->priv->view)); +} + +static void +load_path (GdlDataRow *row) +{ + GdlDataIter iter; + + /* Make sure the path has been unloaded */ + g_return_if_fail (row->priv->name == NULL); + g_return_if_fail (row->priv->cell == NULL); + + if (gdl_data_model_get_iter (row->priv->model, + &iter, row->priv->path)) { + GValue val = { 0, }; + char *str; + + gdl_data_model_get_name (row->priv->model, &iter, + &str); + row->priv->name = g_strdup (str); + + if (gdl_data_model_iter_has_child (row->priv->model, &iter)) { + row->priv->multi = TRUE; + } + + gdl_data_model_get_renderer (row->priv->model, &iter, + &row->priv->cell, + &str, + &row->priv->editable); + g_object_ref (GTK_OBJECT (row->priv->cell)); + gtk_object_sink (GTK_OBJECT (row->priv->cell)); + + row->priv->renderer_field = g_strdup (str); + gdl_data_model_get_value (row->priv->model, &iter, &val); + + g_object_set_property (G_OBJECT (row->priv->cell), + row->priv->renderer_field, + &val); + g_value_unset (&val); + } +} + +static void +unload_path (GdlDataRow *row) +{ + if (row->priv->renderer_field) { + g_free (row->priv->renderer_field); + row->priv->renderer_field = NULL; + } + + if (row->priv->name) { + g_free (row->priv->name); + row->priv->name = NULL; + } + + if (row->priv->cell) { + g_object_unref (row->priv->cell); + row->priv->cell = NULL; + } + + if (row->priv->subrows) { + GList *l; + for (l = row->priv->subrows; l != NULL; l = l->next) { + g_object_unref (G_OBJECT (l->data)); + } + g_list_free (row->priv->subrows); + row->priv->subrows = NULL; + } +} + +static void +gdl_data_row_instance_init (GdlDataRow *row) +{ + row->priv = g_new0 (GdlDataRowPrivate, 1); +} + +static void +gdl_data_row_finalize (GObject *object) +{ + GdlDataRow *row = GDL_DATA_ROW (object); + if (row->priv) { + unload_path (row); + + if (row->priv->path) { + gtk_tree_path_free (row->priv->path); + row->priv->path = NULL; + } + + + g_object_unref (row->priv->model); + + g_free (row->priv); + row->priv = NULL; + } +} + +static void +gdl_data_row_class_init (GdlDataRowClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*) klass; + gobject_class->finalize = gdl_data_row_finalize; + + parent_class = g_type_class_peek_parent (klass); +} + +GdlDataRow * +gdl_data_row_new (GdlDataView *view, + GtkTreePath *path) +{ + GdlDataRow *row; + + row = GDL_DATA_ROW (g_object_new (gdl_data_row_get_type (), + NULL)); + + row->priv->view = view; + row->priv->model = g_object_ref (view->model); + row->priv->path = gtk_tree_path_copy (path); + load_path (row); + + return row; +} + + +#define PAD 3 + +static void +layout_row (GdlDataRow *row, + int x, int y, int width, int height) +{ + PangoLayout *layout; + + /* sizes */ + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (row->priv->view), + row->priv->name); + pango_layout_get_pixel_size (layout, + &row->priv->name_r.width, + &row->priv->name_r.height); + g_object_unref (layout); + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (row->priv->view), "="); + pango_layout_get_pixel_size (layout, + &row->priv->sep_r.width, + &row->priv->sep_r.height); + g_object_unref (layout); + + row->priv->title_r.width = + MAX (row->priv->name_r.width + row->priv->sep_r.width + PAD, + row->priv->split); + row->priv->title_r.height = + MAX (row->priv->name_r.height, row->priv->sep_r.width); + + if (row->priv->cell) { + gtk_cell_renderer_get_size (row->priv->cell, + GTK_WIDGET (row->priv->view), + NULL, NULL, NULL, + &row->priv->cell_r.width, + &row->priv->cell_r.height); + } else { + row->priv->cell_r.width = row->priv->cell_r.height = 0; + } + + row->priv->data_r.width = row->priv->cell_r.width; + row->priv->data_r.height = row->priv->cell_r.height; + + if (row->priv->multi) { + row->priv->expand_r.width = 10; + row->priv->expand_r.height = 10; + + row->priv->data_r.width += row->priv->expand_r.width; + row->priv->data_r.height = MAX (row->priv->expand_r.height, + row->priv->data_r.height); + + if (row->priv->expanded) { + GList *l; + int name_w = 0, data_w = 0; + for (l = row->priv->subrows; l != NULL; l = l->next) { + int w1, w2, h; + gdl_data_row_get_size (GDL_DATA_ROW (l->data), + &w1, &w2, NULL, &h); + name_w = MAX (name_w, w1); + data_w = MAX (data_w, w2); + row->priv->data_r.height += h; + } + row->priv->child_split = name_w; + row->priv->data_r.width = + MAX (name_w + data_w + 3 * PAD, + row->priv->data_r.width); + row->priv->data_r.height += 2 * PAD; + } + } + + row->priv->area_r.width = MAX (width, + row->priv->data_r.width + row->priv->title_r.width + PAD); + + row->priv->area_r.height = MAX (height, + (MAX (row->priv->data_r.height, + row->priv->title_r.height))); + + /* Positions */ + + row->priv->area_r.x = x; + row->priv->area_r.y = y; + + row->priv->title_r.x = x; + row->priv->title_r.y = y + ((row->priv->area_r.height) / 2) - (row->priv->title_r.height / 2); + + row->priv->name_r.x = x; + row->priv->name_r.y = y + ((row->priv->area_r.height) / 2) - (row->priv->name_r.height / 2); + + row->priv->sep_r.x = row->priv->title_r.x + row->priv->title_r.width - row->priv->sep_r.width; + row->priv->sep_r.y = y + ((row->priv->area_r.height) / 2) - (row->priv->sep_r.height / 2); + + row->priv->data_r.x = row->priv->title_r.x + row->priv->title_r.width + PAD; + row->priv->data_r.y = y; + + /* Readjust the data area size to fit */ + row->priv->data_r.width = row->priv->area_r.width - (row->priv->title_r.width + PAD); + row->priv->data_r.height = row->priv->area_r.height; + + if (row->priv->multi) { + row->priv->expand_r.x = row->priv->data_r.x; + row->priv->expand_r.y = row->priv->data_r.y + ((row->priv->cell_r.height) / 2) - (row->priv->expand_r.height / 2); + + row->priv->cell_r.y = row->priv->data_r.y; + row->priv->cell_r.height = MAX (row->priv->expand_r.height, row->priv->cell_r.height); + row->priv->cell_r.width = (row->priv->data_r.width - row->priv->expand_r.width); + + row->priv->cell_r.x = row->priv->expand_r.x + row->priv->expand_r.width; + } else { + row->priv->cell_r = row->priv->data_r; + } +} + +void +gdl_data_row_get_size (GdlDataRow *row, int *sep_width, + int *cell_width, int *total_width, int *height) +{ + layout_row (row, 0, 0, 0, 0); + + if (sep_width) { + *sep_width = row->priv->name_r.width + row->priv->sep_r.width + PAD; + } + + if (cell_width) *cell_width = row->priv->data_r.width; + if (total_width) *total_width = row->priv->area_r.width; + if (height) *height = row->priv->area_r.height; +} + +void +gdl_data_row_set_show_name (GdlDataRow *row, gboolean show_name) +{ +} + +void +gdl_data_row_layout (GdlDataRow *row, GdkRectangle *alloc) +{ + layout_row (row, alloc->x, alloc->y, alloc->width, alloc->height); + + if (row->priv->multi && row->priv->expanded) { + GList *l; + GdkRectangle sub; + sub.y = row->priv->expand_r.y + row->priv->expand_r.width + PAD; + sub.x = row->priv->data_r.x + PAD; + sub.width = row->priv->data_r.width - 2 * PAD; + sub.height = row->priv->data_r.height - 2 * PAD; + + for (l = row->priv->subrows; l != NULL; l = l->next) { + gdl_data_row_get_size (GDL_DATA_ROW (l->data), + NULL, NULL, NULL, &sub.height); + gdl_data_row_set_split (GDL_DATA_ROW (l->data), + row->priv->child_split); + gdl_data_row_layout (GDL_DATA_ROW (l->data), + &sub); + sub.y += sub.height; + } + } +} + +#if 0 +#define DRAWR(r) { gdk_draw_rectangle (drawable, GTK_WIDGET (row->priv->view)->style->text_gc[GTK_STATE_NORMAL],FALSE,row->priv->r.x,row->priv->r.y,row->priv->r.width, row->priv->r.height); } +#else +#define DRAWR(r) +#endif + + +void +gdl_data_row_render (GdlDataRow *row, GdkDrawable *drawable, + GdkRectangle *expose_area, + GtkCellRendererState flags) +{ + PangoLayout *layout; + + guint state = GTK_STATE_NORMAL; + + if (row->priv->selected) { + if (flags & GTK_CELL_RENDERER_SELECTED) + state = GTK_STATE_SELECTED; + else + state = GTK_STATE_ACTIVE; + gtk_paint_flat_box (GTK_WIDGET (row->priv->view)->style, + drawable, state, + GTK_SHADOW_NONE, expose_area, + GTK_WIDGET (row->priv->view), "cell_even", + row->priv->area_r.x, row->priv->area_r.y, + row->priv->area_r.width + 1, + row->priv->area_r.height + 1); + + } + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (row->priv->view), + row->priv->name); + gdk_draw_layout (drawable, + GTK_WIDGET (row->priv->view)->style->text_gc[state], + row->priv->name_r.x, row->priv->name_r.y, layout); + g_object_unref (layout); + DRAWR(name_r); + + layout = gtk_widget_create_pango_layout (GTK_WIDGET (row->priv->view), "="); + gdk_draw_layout (drawable, + GTK_WIDGET (row->priv->view)->style->text_gc[state], + row->priv->sep_r.x, row->priv->sep_r.y, layout); + g_object_unref (layout); + DRAWR(sep_r); + DRAWR(title_r); + + if (row->priv->cell) { + if (row->priv->focused) { + gtk_paint_focus (GTK_WIDGET (row->priv->view)->style, + drawable, + GTK_WIDGET_STATE (GTK_WIDGET (row->priv->view)), + NULL, GTK_WIDGET (row->priv->view), + "treeview", + row->priv->cell_r.x - 1, + row->priv->cell_r.y - 1, + row->priv->cell_r.width + 2, + row->priv->cell_r.height + 2); + } + + gtk_cell_renderer_render (row->priv->cell, + drawable, GTK_WIDGET (row->priv->view), + &row->priv->area_r, + &row->priv->cell_r, + expose_area, + row->priv->selected ? GTK_CELL_RENDERER_SELECTED : 0); + DRAWR(cell_r); + } + if (row->priv->multi) { + gtk_paint_expander (GTK_WIDGET (row->priv->view)->style, + drawable, + GTK_WIDGET_STATE (GTK_WIDGET (row->priv->view)), + expose_area, + GTK_WIDGET (row->priv->view), + "gdldataview", + row->priv->expand_r.x + row->priv->expand_r.width / 2, + row->priv->expand_r.y + row->priv->expand_r.height / 2, + row->priv->expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED); + DRAWR(expand_r); + + if (row->priv->expanded) { + GList *l; + + for (l = row->priv->subrows; l != NULL; l = l->next) { + gdl_data_row_render (GDL_DATA_ROW (l->data), + drawable, + expose_area, flags); + } + } + gdk_draw_rectangle (drawable, + GTK_WIDGET (row->priv->view)->style->text_gc[GTK_STATE_NORMAL], + FALSE, + row->priv->data_r.x, + row->priv->data_r.y, + row->priv->data_r.width, + row->priv->data_r.height); + } + DRAWR(data_r); +} + + +GdlDataRow * +gdl_data_row_at (GdlDataRow *row, int x, int y) +{ + if (!GDL_POINT_IN (x, y, &row->priv->area_r)) { + return NULL; + } + + if (row->priv->multi && row->priv->expanded) { + GList *l; + for (l = row->priv->subrows; l != NULL; l = l->next) { + GdlDataRow *ret = gdl_data_row_at (GDL_DATA_ROW (l->data), x, y); + if (ret) + return ret; + } + } + + return row; +} + +static gboolean +button_press_event (GdlDataRow *row, GdkEventButton *event, + GtkCellEditable **editable_widget) +{ + if (editable_widget) + *editable_widget = NULL; + + if (GDL_POINT_IN (event->x, event->y, &row->priv->expand_r)) { + if (row->priv->expanded) + contract (row); + else + expand (row); + } + + if (GDL_POINT_IN (event->x, event->y, &row->priv->cell_r) + && row->priv->editable) { + g_return_val_if_fail (editable_widget, FALSE); + *editable_widget = gtk_cell_renderer_start_editing + (row->priv->cell, + (GdkEvent*)event, + GTK_WIDGET (row->priv->view), + "1:2:3", + &row->priv->area_r, + &row->priv->cell_r, + GTK_CELL_RENDERER_SELECTED); + } + + return FALSE; + +} + + +gboolean +gdl_data_row_event (GdlDataRow *row, GdkEvent *event, + GtkCellEditable **editable_widget) +{ + switch (((GdkEventAny *)event)->type) { + case GDK_BUTTON_PRESS: + return button_press_event (row, + (GdkEventButton *)event, + editable_widget); + default: + break; + } + return FALSE; +} + +void +gdl_data_row_get_cell_area (GdlDataRow *row, + GdkRectangle *rect) +{ + *rect = row->priv->cell_r; +} + +void +gdl_data_row_set_split (GdlDataRow *row, int split) +{ + row->priv->split = split; +} + +void +gdl_data_row_set_selected (GdlDataRow *row, gboolean selected) +{ + row->priv->selected = selected; + + /* FIXME: invalidate here */ + gtk_widget_queue_draw (GTK_WIDGET (row->priv->view)); +} + +void +gdl_data_row_set_focused (GdlDataRow *row, gboolean focused) +{ + row->priv->focused = focused; + + /* FIXME: invalidate here */ + gtk_widget_queue_draw (GTK_WIDGET (row->priv->view)); +} + +const char * +gdl_data_row_get_title (GdlDataRow *row) +{ + return row->priv->name; +} diff --git a/src/libgdl/gdl-data-row.h b/src/libgdl/gdl-data-row.h new file mode 100644 index 000000000..d4928958d --- /dev/null +++ b/src/libgdl/gdl-data-row.h @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2001 Dave Camp <dave@ximian.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef GDL_DATA_ROW_H +#define GDL_DATA_ROW_H + +#include <glib.h> + +#include <glib-object.h> +#include <gtk/gtktreemodel.h> +#include <gtk/gtkcellrenderer.h> +#include <gdl/gdl-data-view.h> + +G_BEGIN_DECLS + +#define GDL_TYPE_DATA_ROW (gdl_data_row_get_type ()) +#define GDL_DATA_ROW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_DATA_ROW, GdlDataRow)) +#define GDL_DATA_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDL_TYPE_DATA_ROW, GdlDataRowClass)) +#define GDL_IS_DATA_ROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_DATA_ROW)) +#define GDL_IS_DATA_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DATA_ROW)) + +typedef struct _GdlDataRow GdlDataRow; +typedef struct _GdlDataRowClass GdlDataRowClass; +typedef struct _GdlDataRowPrivate GdlDataRowPrivate; + +struct _GdlDataRow { + GObject parent; + + GdlDataRowPrivate *priv; +}; + +struct _GdlDataRowClass { + GObjectClass parent_class; +}; + +GType gdl_data_row_get_type (void); +GdlDataRow *gdl_data_row_new (GdlDataView *view, + GtkTreePath *path); +void gdl_data_row_get_size (GdlDataRow *row, + int *text_w, + int *cell_w, + int *total_width, + int *height); +void gdl_data_row_set_show_name (GdlDataRow *row, + gboolean show_name); +void gdl_data_row_layout (GdlDataRow *row, + GdkRectangle *alloc); +void gdl_data_row_render (GdlDataRow *row, + GdkDrawable *drawable, + GdkRectangle *expose_area, + GtkCellRendererState flags); +GdlDataRow *gdl_data_row_at (GdlDataRow *row, + int x, + int y); +gboolean gdl_data_row_event (GdlDataRow *row, + GdkEvent *event, + GtkCellEditable **editable_widget); +void gdl_data_row_get_cell_area (GdlDataRow *row, + GdkRectangle *rect); +void gdl_data_row_set_split (GdlDataRow *row, + int split); +void gdl_data_row_set_selected (GdlDataRow *row, + gboolean selected); +void gdl_data_row_set_focused (GdlDataRow *row, + gboolean focused); +const char *gdl_data_row_get_title (GdlDataRow *row); + + +G_END_DECLS + +#endif diff --git a/src/libgdl/gdl-data-view.c b/src/libgdl/gdl-data-view.c new file mode 100644 index 000000000..81e1795f7 --- /dev/null +++ b/src/libgdl/gdl-data-view.c @@ -0,0 +1,526 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2001 Dave Camp <dave@ximian.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gdl-i18n.h" +#include "gdl-tools.h" +#include "gdl-data-view.h" +#include "gdl-data-frame.h" + +#include "tree-expand.xpm" +#include "tree-contract.xpm" + +struct _GdlDataViewPrivate { + GList *frames; + GList *rows; + GList *widgets; + + GdlDataFrame *selected_frame; + GdlDataRow *selected_row; + + GtkCellEditable *editable; + + GdkPixbuf *close_pixbuf; + GdkPixbuf *expand_pixbuf; + GdkPixbuf *contract_pixbuf; +}; + +typedef struct { + GtkWidget *widget; + int x, y, height, width; +} ChildWidget; + +static void gdl_data_view_instance_init (GdlDataView *dv); +static void gdl_data_view_class_init (GdlDataViewClass *klass); + +GDL_CLASS_BOILERPLATE (GdlDataView, gdl_data_view, GtkLayout, GTK_TYPE_LAYOUT); + + +#define GRID_SPACING 15 + +static void +paint_grid (GtkWidget *widget, GdkDrawable *drawable, + int offset_x, int offset_y, int width, int height) +{ + GdlDataView *dv; + int x; + int y; + + g_return_if_fail (GDL_IS_DATA_VIEW (widget)); + dv = GDL_DATA_VIEW (widget); + + x = offset_x + ((GRID_SPACING - (offset_x % GRID_SPACING)) % GRID_SPACING); + + /* Draw grid points */ + for (; x < width; x += GRID_SPACING) { + y = offset_y + ((GRID_SPACING - (offset_y % GRID_SPACING)) % GRID_SPACING); + + for (; y < height; y += GRID_SPACING) { + gdk_draw_point (drawable, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + x, y); + } + } +} + +static void +expose_frames (GdlDataView *view, GdkEventExpose *event) +{ + GList *l; + + for (l = view->priv->frames; l != NULL; l = l->next) { + GdkRectangle intersect; + GdlDataFrame *frame = GDL_DATA_FRAME (l->data); + if (gdk_rectangle_intersect (&frame->area, + &event->area, + &intersect)) { + gdl_data_frame_draw (GDL_DATA_FRAME (l->data), + GTK_LAYOUT(view)->bin_window, + &intersect); + } + } +} + +static void +expose_widgets (GdlDataView *view, GdkEventExpose *event) +{ + GList *l; + + for (l = view->priv->widgets; l != NULL; l = l->next) { + ChildWidget *child = l->data; + gtk_container_propagate_expose (GTK_CONTAINER (view), + child->widget, event); + } +} + +static gboolean +gdl_data_view_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + if (GTK_WIDGET_DRAWABLE (widget)) { + if (event->window == GTK_LAYOUT (widget)->bin_window) { + paint_grid (widget, GTK_LAYOUT (widget)->bin_window, + event->area.x, event->area.y, + event->area.width, event->area.height); + expose_frames (GDL_DATA_VIEW (widget), event); + expose_widgets (GDL_DATA_VIEW (widget), event); + return TRUE; + } else { + GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); + } + } + + return FALSE; +} + +static GdlDataFrame * +frame_at (GdlDataView *dv, int x, int y) +{ + GList *l; + for (l = dv->priv->frames; l != NULL; l = l->next) { + GdlDataFrame *frame = l->data; + if (x >= frame->area.x && x <= frame->area.x + frame->area.width + && y >= frame->area.y && y <= frame->area.y + frame->area.height) { + return frame; + } + } + return NULL; +} + +static GdlDataRow * +row_at (GdlDataView *view, int x, int y) +{ + GList *l; + GdlDataRow *ret = NULL; + for (l = view->priv->rows; l != NULL; l = l->next) { + GdlDataRow *row = l->data; + ret = gdl_data_row_at (row, x, y); + if (ret) break; + } + return ret; +} + +static void +gdl_data_view_put (GdlDataView *view, GtkWidget *widget, + int x, int y, int width, int height) +{ + ChildWidget *child = g_new0 (ChildWidget, 1); + + child->widget = widget; + child->x = x; + child->y = y; + child->width = width; + child->height = height; + + view->priv->widgets = g_list_append (view->priv->widgets, child); + + if (GTK_WIDGET_REALIZED (view)) { + gtk_widget_set_parent_window (child->widget, + GTK_LAYOUT (view)->bin_window); + } + + gtk_widget_set_parent (child->widget, GTK_WIDGET (view)); +} + +static void +stop_editing (GdlDataView *dv) +{ + if (dv->priv->editable) { + gtk_cell_editable_editing_done (dv->priv->editable); + gtk_cell_editable_remove_widget (dv->priv->editable); + } +} + +static void +remove_widget_cb (GtkCellEditable *cell_editable, GdlDataView *view) +{ + if (view->priv->editable) { + view->priv->editable = NULL; + gdl_data_row_set_focused (view->priv->selected_row, FALSE); + gtk_widget_grab_focus (GTK_WIDGET (view)); + gtk_container_remove (GTK_CONTAINER (view), + GTK_WIDGET (cell_editable)); + } +} + +static gboolean +button_press_event_cb (GdlDataView *dv, GdkEventButton *event, gpointer data) +{ + GdlDataFrame *frame; + GdlDataRow *row; + gboolean ret = FALSE; + + stop_editing (dv); + + if (event->type == GDK_BUTTON_PRESS) { + frame = frame_at (dv, event->x, event->y); + if (frame) { + if (dv->priv->selected_frame) { + gdl_data_frame_set_selected (dv->priv->selected_frame, FALSE); + } + gdl_data_frame_set_selected (frame, TRUE); + dv->priv->selected_frame = frame; + } + + row = row_at (dv, event->x, event->y); + if (row) { + GtkCellEditable *editable; + + if (dv->priv->selected_row) { + gdl_data_row_set_selected (dv->priv->selected_row, + FALSE); + } + dv->priv->selected_row = row; + gdl_data_row_set_selected (row, TRUE); + ret = gdl_data_row_event (row, (GdkEvent*)event, + &editable); + if (editable) { + GdkRectangle area; + dv->priv->editable = editable; + gtk_cell_editable_start_editing (editable, + (GdkEvent*)event); + + gdl_data_row_get_cell_area (row, &area); + gdl_data_view_put (dv, + GTK_WIDGET (editable), + area.x, + area.y, + area.width, + area.height); + + gtk_widget_grab_focus (GTK_WIDGET (editable)); + dv->priv->editable = editable; + gdl_data_row_set_focused (row, TRUE); + + g_signal_connect + (G_OBJECT (editable), + "remove_widget", + G_CALLBACK (remove_widget_cb), dv); + } + } + } + + return ret; +} + +static void +gdl_data_view_instance_init (GdlDataView *dv) +{ + GTK_WIDGET_SET_FLAGS (dv, GTK_CAN_FOCUS); + dv->priv = g_new0 (GdlDataViewPrivate, 1); + + g_signal_connect (G_OBJECT (dv), "button_press_event", + G_CALLBACK (button_press_event_cb), + NULL); + + dv->priv->close_pixbuf = gtk_widget_render_icon (GTK_WIDGET (dv), + "gtk-close", + GTK_ICON_SIZE_MENU, + "gdl-data-view-close"); + + dv->priv->expand_pixbuf = + gdk_pixbuf_new_from_xpm_data ((const char **)tree_expand_xpm); + + dv->priv->contract_pixbuf = + gdk_pixbuf_new_from_xpm_data ((const char **)tree_contract_xpm); +} + +static void +gdl_data_view_realize (GtkWidget *widget) +{ + GList *l; + GdlDataView *view = GDL_DATA_VIEW (widget); + + GDL_CALL_PARENT (GTK_WIDGET_CLASS, realize, (widget)); + + for (l = view->priv->widgets; l != NULL; l = l->next) { + ChildWidget *child = l->data; + gtk_widget_set_parent_window (child->widget, + GTK_LAYOUT (view)->bin_window); + } +} + +static void +gdl_data_view_size_request (GtkWidget *widget, GtkRequisition *req) +{ + GList *l; + + req->width = req->height = 0; + + for (l = GDL_DATA_VIEW (widget)->priv->widgets; l != NULL; l = l->next) { + GtkRequisition child_req; + ChildWidget *child = l->data; + + gtk_widget_size_request (child->widget, &child_req); + } +} + + +static void +gdl_data_view_size_allocate (GtkWidget *widget, GtkAllocation *alloc) +{ + GdlDataView *view = GDL_DATA_VIEW (widget); + GList *l; +; + for (l = view->priv->widgets; l != NULL; l = l->next) { + ChildWidget *child = l->data; + GtkAllocation child_alloc; + + child_alloc.x = child->x; + child_alloc.y = child->y; + child_alloc.width = child->width; + child_alloc.height = child->height; + + gtk_widget_size_allocate (child->widget, &child_alloc); + } + GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate, (widget, alloc)); +} + +static void +gdl_data_view_forall (GtkContainer *container, gboolean include_internals, + GtkCallback callback, gpointer callback_data) +{ + GdlDataView *view = GDL_DATA_VIEW (container); + GList *l; + + for (l = view->priv->widgets; l != NULL; l = l->next) { + ChildWidget *child = l->data; + (*callback) (child->widget, callback_data); + } +} + +static void +gdl_data_view_remove (GtkContainer *container, GtkWidget *widget) +{ + GList *l; + GdlDataView *view = GDL_DATA_VIEW (container); + + for (l = view->priv->widgets; l != NULL; l = l->next) { + ChildWidget *child = l->data; + if (child->widget == widget) { + gtk_widget_unparent (widget); + view->priv->widgets = + g_list_remove_link (view->priv->widgets, l); + g_list_free_1 (l); + g_free (child); + return; + } + } +} + +static void +gdl_data_view_destroy (GtkObject *obj) +{ + GdlDataView *dv = GDL_DATA_VIEW (obj); + + stop_editing (dv); + + if (dv->priv) { + GList *l; + for (l = dv->priv->frames; l != NULL; l = l->next) { + g_object_unref (G_OBJECT (l->data)); + } + g_list_free (dv->priv->frames); + + g_object_unref (dv->priv->close_pixbuf); + g_object_unref (dv->priv->expand_pixbuf); + g_object_unref (dv->priv->contract_pixbuf); + + g_free (dv->priv); + dv->priv = NULL; + } + GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (obj)); +} + +static void +gdl_data_view_class_init (GdlDataViewClass *klass) +{ + GtkObjectClass *object_class = (GtkObjectClass *)klass; + GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; + GtkContainerClass *container_class = (GtkContainerClass *)klass; + + parent_class = gtk_type_class (GTK_TYPE_LAYOUT); + + container_class->forall = gdl_data_view_forall; + container_class->remove = gdl_data_view_remove; + + widget_class->expose_event = gdl_data_view_expose; + widget_class->realize = gdl_data_view_realize; + /* FIXME: unrealize */ + widget_class->size_request = gdl_data_view_size_request; + widget_class->size_allocate = gdl_data_view_size_allocate; + object_class->destroy = gdl_data_view_destroy; + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("expander-size", + _("Expander Size"), + _("Size of the expander arrow."), + 0, + G_MAXINT, + 10, + G_PARAM_READABLE)); +} + +GtkWidget * +gdl_data_view_new (void) +{ + GdlDataView *dv; + dv = g_object_new (gdl_data_view_get_type (), NULL); + return GTK_WIDGET (dv); +} + +void +gdl_data_view_set_model (GdlDataView *dv, GdlDataModel *model) +{ + GtkTreePath *path; + GdlDataIter iter; + gboolean iter_valid; + int x = 5; + + dv->model = model; + + path = gtk_tree_path_new_from_string ("0"); + + iter_valid = gdl_data_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); + + while (iter_valid) { + GdlDataFrame *frame; + GdlDataRow *row; + + path = gdl_data_model_get_path (model, &iter); + + row = gdl_data_row_new (dv, path); + frame = gdl_data_frame_new (dv, row); + gdl_data_frame_set_position (frame, x, 5); + + dv->priv->frames = g_list_append (dv->priv->frames, + frame); + dv->priv->rows = g_list_append (dv->priv->rows, row); + + gtk_tree_path_free (path); + + x += 150; + + iter_valid = gdl_data_model_iter_next (model, &iter); + } +} + +void +gdl_data_view_layout (GdlDataView *view) +{ + GList *l; + for (l = view->priv->frames; l != NULL; l = l->next) { + gdl_data_frame_layout (GDL_DATA_FRAME (l->data)); + } +} + +GdkPixbuf * +gdl_data_view_get_close_pixbuf (GdlDataView *view) +{ + return view->priv->close_pixbuf; +} + +void +gdl_data_view_set_close_pixbuf (GdlDataView *view, GdkPixbuf *pixbuf) +{ + if (view->priv->close_pixbuf) { + g_object_unref (view->priv->close_pixbuf); + } + + view->priv->close_pixbuf = g_object_ref (pixbuf); +} + +GdkPixbuf * +gdl_data_view_get_expand_pixbuf (GdlDataView *view) +{ + return view->priv->expand_pixbuf; +} + +void +gdl_data_view_set_expand_pixbuf (GdlDataView *view, GdkPixbuf *pixbuf) +{ + if (view->priv->expand_pixbuf) { + g_object_unref (view->priv->expand_pixbuf); + } + + view->priv->expand_pixbuf = g_object_ref (pixbuf); +} + +GdkPixbuf * +gdl_data_view_get_contract_pixbuf (GdlDataView *view) +{ + return view->priv->contract_pixbuf; +} + +void +gdl_data_view_set_contract_pixbuf (GdlDataView *view, GdkPixbuf *pixbuf) +{ + if (view->priv->contract_pixbuf) { + g_object_unref (view->priv->contract_pixbuf); + } + + view->priv->contract_pixbuf = g_object_ref (pixbuf); +} diff --git a/src/libgdl/gdl-data-view.h b/src/libgdl/gdl-data-view.h new file mode 100644 index 000000000..a29132074 --- /dev/null +++ b/src/libgdl/gdl-data-view.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2001 Dave Camp <dave@ximian.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef GDL_DATA_VIEW_H +#define GDL_DATA_VIEW_H + +#include <gdl/gdl-data-model.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GDL_TYPE_DATA_VIEW (gdl_data_view_get_type ()) +#define GDL_DATA_VIEW(obj) (GTK_CHECK_CAST ((obj), GDL_TYPE_DATA_VIEW, GdlDataView)) +#define GDL_DATA_VIEW_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DATA_VIEW, GdlDataViewClass)) +#define GDL_IS_DATA_VIEW(obj) (GTK_CHECK_TYPE ((obj), GDL_TYPE_DATA_VIEW)) +#define GDL_IS_DATA_VIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DATA_VIEW)) +#define GDL_DATA_VIEW_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GDL_TYPE_DATA_VIEW, GdlDataViewClass)) + +typedef struct _GdlDataView GdlDataView; +typedef struct _GdlDataViewClass GdlDataViewClass; +typedef struct _GdlDataViewPrivate GdlDataViewPrivate; + +#define GDL_POINT_IN(x1,y1,r) ((x1) >= (r)->x && x1 < (r)->x + (r)->width && (y1) >= (r)->y && y1 < (r)->y + (r)->height) + +struct _GdlDataView { + GtkLayout layout; + + GdlDataModel *model; + + GdlDataViewPrivate *priv; +}; + +struct _GdlDataViewClass { + GtkLayoutClass parent_class; +}; + +GtkType gdl_data_view_get_type (void); +GtkWidget *gdl_data_view_new (void); +void gdl_data_view_set_model (GdlDataView *view, + GdlDataModel *model); +void gdl_data_view_layout (GdlDataView *view); +GdkPixbuf *gdl_data_view_get_close_pixbuf (GdlDataView *view); +void gdl_data_view_set_close_pixbuf (GdlDataView *view, + GdkPixbuf *pixbuf); +GdkPixbuf *gdl_data_view_get_expand_pixbuf (GdlDataView *view); +void gdl_data_view_set_expand_pixbuf (GdlDataView *view, + GdkPixbuf *pixbuf); +GdkPixbuf *gdl_data_view_get_contract_pixbuf (GdlDataView *view); +void gdl_data_view_set_contract_pixbuf (GdlDataView *view, + GdkPixbuf *pixbuf); + +#endif diff --git a/src/libgdl/gdl-dock-item-grip.c b/src/libgdl/gdl-dock-item-grip.c index 2513313ef..2101d9621 100644 --- a/src/libgdl/gdl-dock-item-grip.c +++ b/src/libgdl/gdl-dock-item-grip.c @@ -293,10 +293,10 @@ gdl_dock_item_grip_set_property (GObject *object, case PROP_ITEM: grip->item = g_value_get_object (value); if (grip->item) { - g_signal_connect (grip->item, "notify::long_name", + g_signal_connect (grip->item, "notify::long-name", G_CALLBACK (gdl_dock_item_grip_item_notify), grip); - g_signal_connect (grip->item, "notify::stock_id", + g_signal_connect (grip->item, "notify::stock-id", G_CALLBACK (gdl_dock_item_grip_item_notify), grip); g_signal_connect (grip->item, "notify::behavior", diff --git a/src/libgdl/gdl-dock-item.c b/src/libgdl/gdl-dock-item.c index c01737636..db31ade30 100644 --- a/src/libgdl/gdl-dock-item.c +++ b/src/libgdl/gdl-dock-item.c @@ -1257,8 +1257,9 @@ gdl_dock_item_dock (GdlDockObject *object, GdlDockPlacement position, GValue *other_data) { - GdlDockObject *new_parent, *parent; - gboolean add_ourselves_first; + GdlDockObject *new_parent = NULL; + GdlDockObject *parent, *requestor_parent; + gboolean add_ourselves_first = FALSE; guint available_space=0; gint pref_size=-1; @@ -1372,11 +1373,16 @@ gdl_dock_item_dock (GdlDockObject *object, pref_size = req.width; break; case GDL_DOCK_CENTER: - new_parent = g_object_new (gdl_dock_object_type_from_nick ("notebook"), - "preferred-width", object_req.width, - "preferred-height", object_req.height, - NULL); - add_ourselves_first = TRUE; + /* If the parent is already a DockNotebook, we don't need + to create a new one. */ + if (!GDL_IS_DOCK_NOTEBOOK (parent)) + { + new_parent = g_object_new (gdl_dock_object_type_from_nick ("notebook"), + "preferred-width", object_req.width, + "preferred-height", object_req.height, + NULL); + add_ourselves_first = TRUE; + } break; default: { @@ -1396,9 +1402,12 @@ gdl_dock_item_dock (GdlDockObject *object, gdl_dock_object_freeze (parent); /* ref ourselves since we could be destroyed when detached */ - g_object_ref (object); - GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_REFLOW); - gdl_dock_object_detach (object, FALSE); + if (new_parent) + { + g_object_ref (object); + GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_REFLOW); + gdl_dock_object_detach (object, FALSE); + } /* freeze the new parent, so reduce won't get called before it's actually added to our parent */ @@ -1424,7 +1433,14 @@ gdl_dock_item_dock (GdlDockObject *object, /* show automatic object */ if (gtk_widget_get_visible (GTK_WIDGET (object))) + { gtk_widget_show (GTK_WIDGET (new_parent)); + GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_REFLOW); + gdl_dock_object_thaw (new_parent); + } + else // If the parent is already a DockNotebook, we don't need + // to create a new one. + gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (requestor)); /* use extra docking parameter */ if (position != GDL_DOCK_CENTER && other_data && @@ -1437,10 +1453,17 @@ gdl_dock_item_dock (GdlDockObject *object, g_object_set (G_OBJECT (new_parent), "position", splitpos, NULL); } - GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_REFLOW); g_object_unref (object); - gdl_dock_object_thaw (new_parent); + requestor_parent = gdl_dock_object_get_parent_object (requestor); + if (GDL_IS_DOCK_NOTEBOOK (requestor_parent)) + { + /* Activate the page we just added */ + GdlDockItem* notebook = GDL_DOCK_ITEM (gdl_dock_object_get_parent_object (requestor)); + gtk_notebook_set_page (GTK_NOTEBOOK (notebook->child), + gtk_notebook_page_num (GTK_NOTEBOOK (notebook->child), GTK_WIDGET (requestor))); + } + if (parent) gdl_dock_object_thaw (parent); diff --git a/src/libgdl/gdl-dock-layout.c b/src/libgdl/gdl-dock-layout.c new file mode 100644 index 000000000..c3b0a4dac --- /dev/null +++ b/src/libgdl/gdl-dock-layout.c @@ -0,0 +1,1411 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gdl-i18n.h" +#include <string.h> +#include <stdlib.h> +#include <libxml/parser.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include "gdl-dock-layout.h" +#include "gdl-tools.h" +#include "gdl-dock-placeholder.h" + + +/* ----- Private variables ----- */ + +enum { + PROP_0, + PROP_MASTER, + PROP_DIRTY +}; + +#define ROOT_ELEMENT "dock-layout" +#define DEFAULT_LAYOUT "__default__" +#define LAYOUT_ELEMENT_NAME "layout" +#define NAME_ATTRIBUTE_NAME "name" + +#define LAYOUT_GLADE_FILE "layout.glade" + +enum { + COLUMN_NAME, + COLUMN_SHOW, + COLUMN_LOCKED, + COLUMN_ITEM +}; + +#define COLUMN_EDITABLE COLUMN_SHOW + +struct _GdlDockLayoutPrivate { + xmlDocPtr doc; + + /* layout list models */ + GtkListStore *items_model; + GtkListStore *layouts_model; + + /* idle control */ + gboolean idle_save_pending; +}; + +typedef struct _GdlDockLayoutUIData GdlDockLayoutUIData; + +struct _GdlDockLayoutUIData { + GdlDockLayout *layout; + GtkWidget *locked_check; + GtkTreeSelection *selection; +}; + + +/* ----- Private prototypes ----- */ + +static void gdl_dock_layout_class_init (GdlDockLayoutClass *klass); + +static void gdl_dock_layout_instance_init (GdlDockLayout *layout); + +static void gdl_dock_layout_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static void gdl_dock_layout_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdl_dock_layout_dispose (GObject *object); + +static void gdl_dock_layout_build_doc (GdlDockLayout *layout); + +static xmlNodePtr gdl_dock_layout_find_layout (GdlDockLayout *layout, + const gchar *name); + +static void gdl_dock_layout_build_models (GdlDockLayout *layout); + + +/* ----- Private implementation ----- */ + +GDL_CLASS_BOILERPLATE (GdlDockLayout, gdl_dock_layout, GObject, G_TYPE_OBJECT); + +static void +gdl_dock_layout_class_init (GdlDockLayoutClass *klass) +{ + GObjectClass *g_object_class = (GObjectClass *) klass; + + g_object_class->set_property = gdl_dock_layout_set_property; + g_object_class->get_property = gdl_dock_layout_get_property; + g_object_class->dispose = gdl_dock_layout_dispose; + + g_object_class_install_property ( + g_object_class, PROP_MASTER, + g_param_spec_object ("master", _("Master"), + _("GdlDockMaster object which the layout object " + "is attached to"), + GDL_TYPE_DOCK_MASTER, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + g_object_class, PROP_DIRTY, + g_param_spec_boolean ("dirty", _("Dirty"), + _("True if the layouts have changed and need to be " + "saved to a file"), + FALSE, + G_PARAM_READABLE)); +} + +static void +gdl_dock_layout_instance_init (GdlDockLayout *layout) +{ + layout->master = NULL; + layout->dirty = FALSE; + layout->_priv = g_new0 (GdlDockLayoutPrivate, 1); + layout->_priv->idle_save_pending = FALSE; + + gdl_dock_layout_build_models (layout); +} + +static void +gdl_dock_layout_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlDockLayout *layout = GDL_DOCK_LAYOUT (object); + + switch (prop_id) { + case PROP_MASTER: + gdl_dock_layout_attach (layout, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + }; +} + +static void +gdl_dock_layout_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlDockLayout *layout = GDL_DOCK_LAYOUT (object); + + switch (prop_id) { + case PROP_MASTER: + g_value_set_object (value, layout->master); + break; + case PROP_DIRTY: + g_value_set_boolean (value, layout->dirty); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + }; +} + +static void +gdl_dock_layout_dispose (GObject *object) +{ + GdlDockLayout *layout; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDL_IS_DOCK_LAYOUT (object)); + + layout = GDL_DOCK_LAYOUT (object); + + if (layout->master) + gdl_dock_layout_attach (layout, NULL); + + if (layout->_priv) { + if (layout->_priv->idle_save_pending) { + layout->_priv->idle_save_pending = FALSE; + g_idle_remove_by_data (layout); + } + + if (layout->_priv->doc) { + xmlFreeDoc (layout->_priv->doc); + layout->_priv->doc = NULL; + } + + if (layout->_priv->items_model) { + g_object_unref (layout->_priv->items_model); + g_object_unref (layout->_priv->layouts_model); + layout->_priv->items_model = NULL; + layout->_priv->layouts_model = NULL; + } + + xmlFreeDoc(layout->_priv->doc); + g_free (layout->_priv); + layout->_priv = NULL; + } +} + +static void +gdl_dock_layout_build_doc (GdlDockLayout *layout) +{ + g_return_if_fail (layout->_priv->doc == NULL); + + layout->_priv->doc = xmlNewDoc (BAD_CAST "1.0"); + layout->_priv->doc->children = xmlNewDocNode (layout->_priv->doc, NULL, + BAD_CAST ROOT_ELEMENT, NULL); +} + +static xmlNodePtr +gdl_dock_layout_find_layout (GdlDockLayout *layout, + const gchar *name) +{ + xmlNodePtr node; + gboolean found = FALSE; + + g_return_val_if_fail (layout != NULL, NULL); + + if (!layout->_priv->doc) + return NULL; + + /* get document root */ + node = layout->_priv->doc->children; + for (node = node->children; node; node = node->next) { + xmlChar *layout_name; + + if (strcmp ((char*)node->name, LAYOUT_ELEMENT_NAME)) + /* skip non-layout element */ + continue; + + /* we want the first layout */ + if (!name) + break; + + layout_name = xmlGetProp (node, BAD_CAST NAME_ATTRIBUTE_NAME); + if (!strcmp (name, (char*)layout_name)) + found = TRUE; + xmlFree (layout_name); + + if (found) + break; + }; + return node; +} + +static void +gdl_dock_layout_build_models (GdlDockLayout *layout) +{ + if (!layout->_priv->items_model) { + layout->_priv->items_model = gtk_list_store_new (4, + G_TYPE_STRING, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_POINTER); + gtk_tree_sortable_set_sort_column_id ( + GTK_TREE_SORTABLE (layout->_priv->items_model), + COLUMN_NAME, GTK_SORT_ASCENDING); + } + + if (!layout->_priv->layouts_model) { + layout->_priv->layouts_model = gtk_list_store_new (2, G_TYPE_STRING, + G_TYPE_BOOLEAN); + gtk_tree_sortable_set_sort_column_id ( + GTK_TREE_SORTABLE (layout->_priv->layouts_model), + COLUMN_NAME, GTK_SORT_ASCENDING); + } +} + +static void +build_list (GdlDockObject *object, GList **list) +{ + /* add only items, not toplevels */ + if (GDL_IS_DOCK_ITEM (object)) + *list = g_list_prepend (*list, object); +} + +static void +update_items_model (GdlDockLayout *layout) +{ + GList *items, *l; + GtkTreeIter iter; + GtkListStore *store; + gchar *long_name; + gboolean locked; + + g_return_if_fail (layout != NULL); + g_return_if_fail (layout->_priv->items_model != NULL); + + if (!layout->master) + return; + + /* build items list */ + items = NULL; + gdl_dock_master_foreach (layout->master, (GFunc) build_list, &items); + + /* walk the current model */ + store = layout->_priv->items_model; + + /* update items model data after a layout load */ + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) { + gboolean valid = TRUE; + + while (valid) { + GdlDockItem *item; + + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, + COLUMN_ITEM, &item, + -1); + if (item) { + /* look for the object in the items list */ + for (l = items; l && l->data != item; l = l->next); + + if (l) { + /* found, update data */ + g_object_get (item, + "long-name", &long_name, + "locked", &locked, + NULL); + gtk_list_store_set (store, &iter, + COLUMN_NAME, long_name, + COLUMN_SHOW, GDL_DOCK_OBJECT_ATTACHED (item), + COLUMN_LOCKED, locked, + -1); + g_free (long_name); + + /* remove the item from the linked list and keep on walking the model */ + items = g_list_delete_link (items, l); + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + + } else { + /* not found, which means the item has been removed */ + valid = gtk_list_store_remove (store, &iter); + + } + + } else { + /* not a valid row */ + valid = gtk_list_store_remove (store, &iter); + } + } + } + + /* add any remaining objects */ + for (l = items; l; l = l->next) { + GdlDockObject *object = l->data; + + g_object_get (object, + "long-name", &long_name, + "locked", &locked, + NULL); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COLUMN_ITEM, object, + COLUMN_NAME, long_name, + COLUMN_SHOW, GDL_DOCK_OBJECT_ATTACHED (object), + COLUMN_LOCKED, locked, + -1); + g_free (long_name); + } + + g_list_free (items); +} + +static void +update_layouts_model (GdlDockLayout *layout) +{ + GList *items, *l; + GtkTreeIter iter; + + g_return_if_fail (layout != NULL); + g_return_if_fail (layout->_priv->layouts_model != NULL); + + /* build layouts list */ + gtk_list_store_clear (layout->_priv->layouts_model); + items = gdl_dock_layout_get_layouts (layout, FALSE); + for (l = items; l; l = l->next) { + gtk_list_store_append (layout->_priv->layouts_model, &iter); + gtk_list_store_set (layout->_priv->layouts_model, &iter, + COLUMN_NAME, l->data, COLUMN_EDITABLE, TRUE, + -1); + g_free (l->data); + }; + g_list_free (items); +} + + +/* ------- UI functions & callbacks ------ */ + +static void +load_layout_cb (GtkWidget *w, + gpointer data) +{ + GdlDockLayoutUIData *ui_data = (GdlDockLayoutUIData *) data; + + GtkTreeModel *model; + GtkTreeIter iter; + GdlDockLayout *layout = ui_data->layout; + gchar *name; + + g_return_if_fail (layout != NULL); + + if (gtk_tree_selection_get_selected (ui_data->selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, + COLUMN_NAME, &name, + -1); + gdl_dock_layout_load_layout (layout, name); + g_free (name); + } +} + +static void +delete_layout_cb (GtkWidget *w, gpointer data) +{ + GdlDockLayoutUIData *ui_data = (GdlDockLayoutUIData *) data; + + GtkTreeModel *model; + GtkTreeIter iter; + GdlDockLayout *layout = ui_data->layout; + gchar *name; + + g_return_if_fail (layout != NULL); + + if (gtk_tree_selection_get_selected (ui_data->selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, + COLUMN_NAME, &name, + -1); + gdl_dock_layout_delete_layout (layout, name); + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + g_free (name); + }; +} + +static void +show_toggled_cb (GtkCellRendererToggle *renderer, + gchar *path_str, + gpointer data) +{ + GdlDockLayoutUIData *ui_data = (GdlDockLayoutUIData *) data; + + GdlDockLayout *layout = ui_data->layout; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path = gtk_tree_path_new_from_string (path_str); + gboolean value; + GdlDockItem *item; + + g_return_if_fail (layout != NULL); + + model = GTK_TREE_MODEL (layout->_priv->items_model); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + COLUMN_SHOW, &value, + COLUMN_ITEM, &item, + -1); + + value = !value; + if (value) + gdl_dock_item_show_item (item); + else + gdl_dock_item_hide_item (item); + + gtk_tree_path_free (path); +} + +static void +all_locked_toggled_cb (GtkWidget *widget, + gpointer data) +{ + GdlDockLayoutUIData *ui_data = (GdlDockLayoutUIData *) data; + GdlDockMaster *master; + gboolean locked; + + g_return_if_fail (ui_data->layout != NULL); + master = ui_data->layout->master; + g_return_if_fail (master != NULL); + + locked = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + g_object_set (master, "locked", locked ? 1 : 0, NULL); +} + +static void +layout_ui_destroyed (GtkWidget *widget, + gpointer user_data) +{ + GdlDockLayoutUIData *ui_data; + + /* widget is the GtkContainer */ + ui_data = g_object_get_data (G_OBJECT (widget), "ui_data"); + if (ui_data) { + if (ui_data->layout) { + if (ui_data->layout->master) + /* disconnet the notify handler */ + g_signal_handlers_disconnect_matched (ui_data->layout->master, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, + ui_data); + + g_object_remove_weak_pointer (G_OBJECT (ui_data->layout), + (gpointer *) &ui_data->layout); + ui_data->layout = NULL; + } + g_object_set_data (G_OBJECT (widget), "ui_data", NULL); + g_free (ui_data); + } +} + +static void +master_locked_notify_cb (GdlDockMaster *master, + GParamSpec *pspec, + gpointer user_data) +{ + GdlDockLayoutUIData *ui_data = (GdlDockLayoutUIData *) user_data; + gint locked; + + g_object_get (master, "locked", &locked, NULL); + if (locked == -1) { + gtk_toggle_button_set_inconsistent ( + GTK_TOGGLE_BUTTON (ui_data->locked_check), TRUE); + } + else { + gtk_toggle_button_set_inconsistent ( + GTK_TOGGLE_BUTTON (ui_data->locked_check), FALSE); + gtk_toggle_button_set_active ( + GTK_TOGGLE_BUTTON (ui_data->locked_check), (locked == 1)); + } +} + +static GladeXML * +load_interface (const gchar *top_widget) +{ + GladeXML *gui; + gchar *gui_file; + + /* load ui */ + gui_file = g_build_filename (GDL_GLADEDIR, LAYOUT_GLADE_FILE, NULL); + gui = glade_xml_new (gui_file, top_widget, GETTEXT_PACKAGE); + g_free (gui_file); + if (!gui) { + /* FIXME: pop up an error dialog */ + g_warning (_("Could not load layout user interface file '%s'"), + LAYOUT_GLADE_FILE); + return NULL; + }; + return gui; +} + +static GtkWidget * +gdl_dock_layout_construct_items_ui (GdlDockLayout *layout) +{ + GladeXML *gui; + GtkWidget *container; + GtkWidget *items_list; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + GdlDockLayoutUIData *ui_data; + + /* load the interface if it wasn't provided */ + gui = load_interface ("items_vbox"); + + if (!gui) + return NULL; + + /* get the container */ + container = glade_xml_get_widget (gui, "items_vbox"); + + ui_data = g_new0 (GdlDockLayoutUIData, 1); + ui_data->layout = layout; + g_object_add_weak_pointer (G_OBJECT (layout), + (gpointer *) &ui_data->layout); + g_object_set_data (G_OBJECT (container), "ui_data", ui_data); + + /* get ui widget references */ + ui_data->locked_check = glade_xml_get_widget (gui, "locked_check"); + items_list = glade_xml_get_widget (gui, "items_list"); + + /* locked check connections */ + g_signal_connect (ui_data->locked_check, "toggled", + (GCallback) all_locked_toggled_cb, ui_data); + if (layout->master) { + g_signal_connect (layout->master, "notify::locked", + (GCallback) master_locked_notify_cb, ui_data); + /* force update now */ + master_locked_notify_cb (layout->master, NULL, ui_data); + } + + /* set models */ + gtk_tree_view_set_model (GTK_TREE_VIEW (items_list), + GTK_TREE_MODEL (layout->_priv->items_model)); + + /* construct list views */ + renderer = gtk_cell_renderer_toggle_new (); + g_signal_connect (renderer, "toggled", + G_CALLBACK (show_toggled_cb), ui_data); + column = gtk_tree_view_column_new_with_attributes (_("Visible"), + renderer, + "active", COLUMN_SHOW, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (items_list), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Item"), + renderer, + "text", COLUMN_NAME, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (items_list), column); + + /* connect signals */ + g_signal_connect (container, "destroy", (GCallback) layout_ui_destroyed, NULL); + + g_object_unref (gui); + + return container; +} + +static void +cell_edited_cb (GtkCellRendererText *cell, + const gchar *path_string, + const gchar *new_text, + gpointer data) +{ + GdlDockLayoutUIData *ui_data = data; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + gchar *name; + xmlNodePtr node; + + model = GTK_TREE_MODEL (ui_data->layout->_priv->layouts_model); + path = gtk_tree_path_new_from_string (path_string); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, COLUMN_NAME, &name, -1); + + node = gdl_dock_layout_find_layout (ui_data->layout, name); + g_free (name); + g_return_if_fail (node != NULL); + + xmlSetProp (node, BAD_CAST NAME_ATTRIBUTE_NAME, BAD_CAST new_text); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, COLUMN_NAME, new_text, + COLUMN_EDITABLE, TRUE, -1); + + gdl_dock_layout_save_layout (ui_data->layout, new_text); + + gtk_tree_path_free (path); +} + +static GtkWidget * +gdl_dock_layout_construct_layouts_ui (GdlDockLayout *layout) +{ + GladeXML *gui; + GtkWidget *container; + GtkWidget *layouts_list; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + GdlDockLayoutUIData *ui_data; + + /* load the interface if it wasn't provided */ + gui = load_interface ("layouts_vbox"); + + if (!gui) + return NULL; + + /* get the container */ + container = glade_xml_get_widget (gui, "layouts_vbox"); + + ui_data = g_new0 (GdlDockLayoutUIData, 1); + ui_data->layout = layout; + g_object_add_weak_pointer (G_OBJECT (layout), + (gpointer *) &ui_data->layout); + g_object_set_data (G_OBJECT (container), "ui-data", ui_data); + + /* get ui widget references */ + layouts_list = glade_xml_get_widget (gui, "layouts_list"); + + /* set models */ + gtk_tree_view_set_model (GTK_TREE_VIEW (layouts_list), + GTK_TREE_MODEL (layout->_priv->layouts_model)); + + /* construct list views */ + renderer = gtk_cell_renderer_text_new (); + g_signal_connect (G_OBJECT (renderer), "edited", + G_CALLBACK (cell_edited_cb), ui_data); + column = gtk_tree_view_column_new_with_attributes (_("Name"), renderer, + "text", COLUMN_NAME, + "editable", COLUMN_EDITABLE, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (layouts_list), column); + + ui_data->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (layouts_list)); + + /* connect signals */ + glade_xml_signal_connect_data (gui, "on_load_button_clicked", + GTK_SIGNAL_FUNC (load_layout_cb), ui_data); + glade_xml_signal_connect_data (gui, "on_delete_button_clicked", + GTK_SIGNAL_FUNC (delete_layout_cb), ui_data); + + g_signal_connect (container, "destroy", (GCallback) layout_ui_destroyed, NULL); + + g_object_unref (gui); + + return container; +} + +static GtkWidget * +gdl_dock_layout_construct_ui (GdlDockLayout *layout) +{ + GtkWidget *container, *child; + + container = gtk_notebook_new (); + gtk_widget_show (container); + + child = gdl_dock_layout_construct_items_ui (layout); + if (child) + gtk_notebook_append_page (GTK_NOTEBOOK (container), + child, + gtk_label_new (_("Dock items"))); + + child = gdl_dock_layout_construct_layouts_ui (layout); + if (child) + gtk_notebook_append_page (GTK_NOTEBOOK (container), + child, + gtk_label_new (_("Saved layouts"))); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (container), 0); + + return container; +} + +/* ----- Save & Load layout functions --------- */ + +#define GDL_DOCK_PARAM_CONSTRUCTION(p) \ + (((p)->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) != 0) + +static GdlDockObject * +gdl_dock_layout_setup_object (GdlDockMaster *master, + xmlNodePtr node, + gint *n_after_params, + GParameter **after_params) +{ + GdlDockObject *object = NULL; + GType object_type; + xmlChar *object_name; + GObjectClass *object_class = NULL; + + GParamSpec **props; + guint n_props, i; + GParameter *params = NULL; + gint n_params = 0; + GValue serialized = { 0, }; + + object_name = xmlGetProp (node, BAD_CAST GDL_DOCK_NAME_PROPERTY); + if (object_name && strlen ((char*)object_name) > 0) { + /* the object must already be bound to the master */ + object = gdl_dock_master_get_object (master, (char*)object_name); + + xmlFree (object_name); + object_type = object ? G_TYPE_FROM_INSTANCE (object) : G_TYPE_NONE; + } + else { + /* the object should be automatic, so create it by + retrieving the object type from the dock registry */ + object_type = gdl_dock_object_type_from_nick ((char*)node->name); + if (object_type == G_TYPE_NONE) { + g_warning (_("While loading layout: don't know how to create " + "a dock object whose nick is '%s'"), node->name); + } + } + + if (object_type == G_TYPE_NONE || !G_TYPE_IS_CLASSED (object_type)) + return NULL; + + object_class = g_type_class_ref (object_type); + props = g_object_class_list_properties (object_class, &n_props); + + /* create parameter slots */ + /* extra parameter is the master */ + params = g_new0 (GParameter, n_props + 1); + *after_params = g_new0 (GParameter, n_props); + *n_after_params = 0; + + /* initialize value used for transformations */ + g_value_init (&serialized, GDL_TYPE_DOCK_PARAM); + + for (i = 0; i < n_props; i++) { + xmlChar *xml_prop; + + /* process all exported properties, skip + GDL_DOCK_NAME_PROPERTY, since named items should + already by in the master */ + if (!(props [i]->flags & GDL_DOCK_PARAM_EXPORT) || + !strcmp (props [i]->name, GDL_DOCK_NAME_PROPERTY)) + continue; + + /* get the property from xml if there is one */ + xml_prop = xmlGetProp (node, BAD_CAST props [i]->name); + if (xml_prop) { + g_value_set_static_string (&serialized, (char*)xml_prop); + + if (!GDL_DOCK_PARAM_CONSTRUCTION (props [i]) && + (props [i]->flags & GDL_DOCK_PARAM_AFTER)) { + (*after_params) [*n_after_params].name = props [i]->name; + g_value_init (&((* after_params) [*n_after_params].value), + props [i]->value_type); + g_value_transform (&serialized, + &((* after_params) [*n_after_params].value)); + (*n_after_params)++; + } + else if (!object || (!GDL_DOCK_PARAM_CONSTRUCTION (props [i]) && object)) { + params [n_params].name = props [i]->name; + g_value_init (&(params [n_params].value), props [i]->value_type); + g_value_transform (&serialized, &(params [n_params].value)); + n_params++; + } + xmlFree (xml_prop); + } + } + g_value_unset (&serialized); + g_free (props); + + if (!object) { + params [n_params].name = GDL_DOCK_MASTER_PROPERTY; + g_value_init (¶ms [n_params].value, GDL_TYPE_DOCK_MASTER); + g_value_set_object (¶ms [n_params].value, master); + n_params++; + + /* construct the object if we have to */ + /* set the master, so toplevels are created correctly and + other objects are bound */ + object = g_object_newv (object_type, n_params, params); + } + else { + /* set the parameters to the existing object */ + for (i = 0; i < n_params; i++) + g_object_set_property (G_OBJECT (object), + params [i].name, + ¶ms [i].value); + } + + /* free the parameters (names are static/const strings) */ + for (i = 0; i < n_params; i++) + g_value_unset (¶ms [i].value); + g_free (params); + + /* finally unref object class */ + g_type_class_unref (object_class); + + return object; +} + +static void +gdl_dock_layout_recursive_build (GdlDockMaster *master, + xmlNodePtr parent_node, + GdlDockObject *parent) +{ + GdlDockObject *object; + xmlNodePtr node; + + g_return_if_fail (master != NULL && parent_node != NULL); + + /* if parent is NULL we should build toplevels */ + for (node = parent_node->children; node; node = node->next) { + GParameter *after_params = NULL; + gint n_after_params = 0, i; + + object = gdl_dock_layout_setup_object (master, node, + &n_after_params, + &after_params); + + if (object) { + gdl_dock_object_freeze (object); + + /* recurse here to catch placeholders */ + gdl_dock_layout_recursive_build (master, node, object); + + if (GDL_IS_DOCK_PLACEHOLDER (object)) + /* placeholders are later attached to the parent */ + gdl_dock_object_detach (object, FALSE); + + /* apply "after" parameters */ + for (i = 0; i < n_after_params; i++) { + g_object_set_property (G_OBJECT (object), + after_params [i].name, + &after_params [i].value); + /* unset and free the value */ + g_value_unset (&after_params [i].value); + } + g_free (after_params); + + /* add the object to the parent */ + if (parent) { + if (GDL_IS_DOCK_PLACEHOLDER (object)) + gdl_dock_placeholder_attach (GDL_DOCK_PLACEHOLDER (object), + parent); + else if (gdl_dock_object_is_compound (parent)) { + gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (object)); + if (GTK_WIDGET_VISIBLE (parent)) + gtk_widget_show (GTK_WIDGET (object)); + } + } + else { + GdlDockObject *controller = gdl_dock_master_get_controller (master); + if (controller != object && GTK_WIDGET_VISIBLE (controller)) + gtk_widget_show (GTK_WIDGET (object)); + } + + /* call reduce just in case any child is missing */ + if (gdl_dock_object_is_compound (object)) + gdl_dock_object_reduce (object); + + gdl_dock_object_thaw (object); + } + } +} + +static void +_gdl_dock_layout_foreach_detach (GdlDockObject *object) +{ + gdl_dock_object_detach (object, TRUE); +} + +static void +gdl_dock_layout_foreach_toplevel_detach (GdlDockObject *object) +{ + gtk_container_foreach (GTK_CONTAINER (object), + (GtkCallback) _gdl_dock_layout_foreach_detach, + NULL); +} + +static void +gdl_dock_layout_load (GdlDockMaster *master, xmlNodePtr node) +{ + g_return_if_fail (master != NULL && node != NULL); + + /* start by detaching all items from the toplevels */ + gdl_dock_master_foreach_toplevel (master, TRUE, + (GFunc) gdl_dock_layout_foreach_toplevel_detach, + NULL); + + gdl_dock_layout_recursive_build (master, node, NULL); +} + +static void +gdl_dock_layout_foreach_object_save (GdlDockObject *object, + gpointer user_data) +{ + struct { + xmlNodePtr where; + GHashTable *placeholders; + } *info = user_data, info_child; + + xmlNodePtr node; + guint n_props, i; + GParamSpec **props; + GValue attr = { 0, }; + + g_return_if_fail (object != NULL && GDL_IS_DOCK_OBJECT (object)); + g_return_if_fail (info->where != NULL); + + node = xmlNewChild (info->where, + NULL, /* ns */ + BAD_CAST gdl_dock_object_nick_from_type (G_TYPE_FROM_INSTANCE (object)), + BAD_CAST NULL); /* contents */ + + /* get object exported attributes */ + props = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), + &n_props); + g_value_init (&attr, GDL_TYPE_DOCK_PARAM); + for (i = 0; i < n_props; i++) { + GParamSpec *p = props [i]; + + if (p->flags & GDL_DOCK_PARAM_EXPORT) { + GValue v = { 0, }; + + /* export this parameter */ + /* get the parameter value */ + g_value_init (&v, p->value_type); + g_object_get_property (G_OBJECT (object), + p->name, + &v); + + /* only save the object "name" if it is set + (i.e. don't save the empty string) */ + if (strcmp (p->name, GDL_DOCK_NAME_PROPERTY) || + g_value_get_string (&v)) { + if (g_value_transform (&v, &attr)) + xmlSetProp (node, BAD_CAST p->name, BAD_CAST g_value_get_string (&attr)); + } + + /* free the parameter value */ + g_value_unset (&v); + } + } + g_value_unset (&attr); + g_free (props); + + info_child = *info; + info_child.where = node; + + /* save placeholders for the object */ + if (info->placeholders && !GDL_IS_DOCK_PLACEHOLDER (object)) { + GList *lph = g_hash_table_lookup (info->placeholders, object); + for (; lph; lph = lph->next) + gdl_dock_layout_foreach_object_save (GDL_DOCK_OBJECT (lph->data), + (gpointer) &info_child); + } + + /* recurse the object if appropiate */ + if (gdl_dock_object_is_compound (object)) { + gtk_container_foreach (GTK_CONTAINER (object), + (GtkCallback) gdl_dock_layout_foreach_object_save, + (gpointer) &info_child); + } +} + +static void +add_placeholder (GdlDockObject *object, + GHashTable *placeholders) +{ + if (GDL_IS_DOCK_PLACEHOLDER (object)) { + GdlDockObject *host; + GList *l; + + g_object_get (object, "host", &host, NULL); + if (host) { + l = g_hash_table_lookup (placeholders, host); + /* add the current placeholder to the list of placeholders + for that host */ + if (l) + g_hash_table_steal (placeholders, host); + + l = g_list_prepend (l, object); + g_hash_table_insert (placeholders, host, l); + g_object_unref (host); + } + } +} + +static void +gdl_dock_layout_save (GdlDockMaster *master, + xmlNodePtr where) +{ + struct { + xmlNodePtr where; + GHashTable *placeholders; + } info; + + GHashTable *placeholders; + + g_return_if_fail (master != NULL && where != NULL); + + /* build the placeholder's hash: the hash keeps lists of + * placeholders associated to each object, so that we can save the + * placeholders when we are saving the object (since placeholders + * don't show up in the normal widget hierarchy) */ + placeholders = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) g_list_free); + gdl_dock_master_foreach (master, (GFunc) add_placeholder, placeholders); + + /* save the layout recursively */ + info.where = where; + info.placeholders = placeholders; + + gdl_dock_master_foreach_toplevel (master, TRUE, + (GFunc) gdl_dock_layout_foreach_object_save, + (gpointer) &info); + + g_hash_table_destroy (placeholders); +} + + +/* ----- Public interface ----- */ + +GdlDockLayout * +gdl_dock_layout_new (GdlDock *dock) +{ + GdlDockMaster *master = NULL; + + /* get the master of the given dock */ + if (dock) + master = GDL_DOCK_OBJECT_GET_MASTER (dock); + + return g_object_new (GDL_TYPE_DOCK_LAYOUT, + "master", master, + NULL); +} + +static gboolean +gdl_dock_layout_idle_save (GdlDockLayout *layout) +{ + /* save default layout */ + gdl_dock_layout_save_layout (layout, NULL); + + layout->_priv->idle_save_pending = FALSE; + + return FALSE; +} + +static void +gdl_dock_layout_layout_changed_cb (GdlDockMaster *master, + GdlDockLayout *layout) +{ + /* update model */ + update_items_model (layout); + + if (!layout->_priv->idle_save_pending) { + g_idle_add ((GSourceFunc) gdl_dock_layout_idle_save, layout); + layout->_priv->idle_save_pending = TRUE; + } +} + +void +gdl_dock_layout_attach (GdlDockLayout *layout, + GdlDockMaster *master) +{ + g_return_if_fail (layout != NULL); + g_return_if_fail (master == NULL || GDL_IS_DOCK_MASTER (master)); + + if (layout->master) { + g_signal_handlers_disconnect_matched (layout->master, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, layout); + g_object_unref (layout->master); + } + + gtk_list_store_clear (layout->_priv->items_model); + + layout->master = master; + if (layout->master) { + g_object_ref (layout->master); + g_signal_connect (layout->master, "layout-changed", + (GCallback) gdl_dock_layout_layout_changed_cb, + layout); + } + + update_items_model (layout); +} + +gboolean +gdl_dock_layout_load_layout (GdlDockLayout *layout, + const gchar *name) +{ + xmlNodePtr node; + gchar *layout_name; + + g_return_val_if_fail (layout != NULL, FALSE); + + if (!layout->_priv->doc || !layout->master) + return FALSE; + + if (!name) + layout_name = DEFAULT_LAYOUT; + else + layout_name = (gchar *) name; + + node = gdl_dock_layout_find_layout (layout, layout_name); + if (!node && !name) + /* return the first layout if the default name failed to load */ + node = gdl_dock_layout_find_layout (layout, NULL); + + if (node) { + gdl_dock_layout_load (layout->master, node); + return TRUE; + } else + return FALSE; +} + +void +gdl_dock_layout_save_layout (GdlDockLayout *layout, + const gchar *name) +{ + xmlNodePtr node; + gchar *layout_name; + + g_return_if_fail (layout != NULL); + g_return_if_fail (layout->master != NULL); + + if (!layout->_priv->doc) + gdl_dock_layout_build_doc (layout); + + if (!name) + layout_name = DEFAULT_LAYOUT; + else + layout_name = (gchar *) name; + + /* delete any previously node with the same name */ + node = gdl_dock_layout_find_layout (layout, layout_name); + if (node) { + xmlUnlinkNode (node); + xmlFreeNode (node); + }; + + /* create the new node */ + node = xmlNewChild (layout->_priv->doc->children, NULL, + BAD_CAST LAYOUT_ELEMENT_NAME, NULL); + xmlSetProp (node, BAD_CAST NAME_ATTRIBUTE_NAME, BAD_CAST layout_name); + + /* save the layout */ + gdl_dock_layout_save (layout->master, node); + layout->dirty = TRUE; + g_object_notify (G_OBJECT (layout), "dirty"); +} + +void +gdl_dock_layout_delete_layout (GdlDockLayout *layout, + const gchar *name) +{ + xmlNodePtr node; + + g_return_if_fail (layout != NULL); + + /* don't allow the deletion of the default layout */ + if (!name || !strcmp (DEFAULT_LAYOUT, name)) + return; + + node = gdl_dock_layout_find_layout (layout, name); + if (node) { + xmlUnlinkNode (node); + xmlFreeNode (node); + layout->dirty = TRUE; + g_object_notify (G_OBJECT (layout), "dirty"); + } +} + +void +gdl_dock_layout_run_manager (GdlDockLayout *layout) +{ + GtkWidget *dialog, *container; + GtkWidget *parent = NULL; + + g_return_if_fail (layout != NULL); + + if (!layout->master) + /* not attached to a dock yet */ + return; + + container = gdl_dock_layout_construct_ui (layout); + if (!container) + return; + + parent = GTK_WIDGET (gdl_dock_master_get_controller (layout->master)); + if (parent) + parent = gtk_widget_get_toplevel (parent); + + dialog = gtk_dialog_new_with_buttons (_("Layout managment"), + parent ? GTK_WINDOW (parent) : NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 300); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), container); + + gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); +} + +gboolean +gdl_dock_layout_load_from_file (GdlDockLayout *layout, + const gchar *filename) +{ + gboolean retval = FALSE; + + if (layout->_priv->doc) { + xmlFreeDoc (layout->_priv->doc); + layout->_priv->doc = NULL; + layout->dirty = FALSE; + g_object_notify (G_OBJECT (layout), "dirty"); + } + + /* FIXME: cannot open symlinks */ + if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { + layout->_priv->doc = xmlParseFile (filename); + if (layout->_priv->doc) { + xmlNodePtr root = layout->_priv->doc->children; + /* minimum validation: test the root element */ + if (root && !strcmp ((char*)root->name, ROOT_ELEMENT)) { + update_layouts_model (layout); + retval = TRUE; + } else { + xmlFreeDoc (layout->_priv->doc); + layout->_priv->doc = NULL; + } + } + } + + return retval; +} + +gboolean +gdl_dock_layout_save_to_file (GdlDockLayout *layout, + const gchar *filename) +{ + FILE *file_handle; + int bytes; + gboolean retval = FALSE; + + g_return_val_if_fail (layout != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + /* if there is still no xml doc, create an empty one */ + if (!layout->_priv->doc) + gdl_dock_layout_build_doc (layout); + + file_handle = fopen (filename, "w"); + if (file_handle) { + bytes = xmlDocDump (file_handle, layout->_priv->doc); + if (bytes >= 0) { + layout->dirty = FALSE; + g_object_notify (G_OBJECT (layout), "dirty"); + retval = TRUE; + }; + fclose (file_handle); + }; + + return retval; +} + +gboolean +gdl_dock_layout_is_dirty (GdlDockLayout *layout) +{ + g_return_val_if_fail (layout != NULL, FALSE); + + return layout->dirty; +}; + +GList * +gdl_dock_layout_get_layouts (GdlDockLayout *layout, + gboolean include_default) +{ + GList *retval = NULL; + xmlNodePtr node; + + g_return_val_if_fail (layout != NULL, NULL); + + if (!layout->_priv->doc) + return NULL; + + node = layout->_priv->doc->children; + for (node = node->children; node; node = node->next) { + xmlChar *name; + + if (strcmp ((char*)node->name, LAYOUT_ELEMENT_NAME)) + continue; + + name = xmlGetProp (node, BAD_CAST NAME_ATTRIBUTE_NAME); + if (include_default || strcmp ((char*)name, DEFAULT_LAYOUT)) + retval = g_list_prepend (retval, g_strdup ((char*)name)); + xmlFree (name); + }; + retval = g_list_reverse (retval); + + return retval; +} + +GtkWidget * +gdl_dock_layout_get_ui (GdlDockLayout *layout) +{ + GtkWidget *ui; + + g_return_val_if_fail (layout != NULL, NULL); + ui = gdl_dock_layout_construct_ui (layout); + + return ui; +} + +GtkWidget * +gdl_dock_layout_get_items_ui (GdlDockLayout *layout) +{ + GtkWidget *ui; + + g_return_val_if_fail (layout != NULL, NULL); + ui = gdl_dock_layout_construct_items_ui (layout); + + return ui; +} + +GtkWidget * +gdl_dock_layout_get_layouts_ui (GdlDockLayout *layout) +{ + GtkWidget *ui; + + g_return_val_if_fail (layout != NULL, NULL); + ui = gdl_dock_layout_construct_layouts_ui (layout); + + return ui; +} diff --git a/src/libgdl/gdl-dock-layout.h b/src/libgdl/gdl-dock-layout.h new file mode 100644 index 000000000..2ce5d13b3 --- /dev/null +++ b/src/libgdl/gdl-dock-layout.h @@ -0,0 +1,98 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This file is part of the GNOME Devtools Libraries. + * + * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef __GDL_DOCK_LAYOUT_H__ +#define __GDL_DOCK_LAYOUT_H__ + +#include <glib-object.h> +#include "libgdl/gdl-dock-master.h" +#include "libgdl/gdl-dock.h" + +G_BEGIN_DECLS + +/* standard macros */ +#define GDL_TYPE_DOCK_LAYOUT (gdl_dock_layout_get_type ()) +#define GDL_DOCK_LAYOUT(object) (GTK_CHECK_CAST ((object), GDL_TYPE_DOCK_LAYOUT, GdlDockLayout)) +#define GDL_DOCK_LAYOUT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GDL_TYPE_DOCK_LAYOUT, GdlDockLayoutClass)) +#define GDL_IS_DOCK_LAYOUT(object) (GTK_CHECK_TYPE ((object), GDL_TYPE_DOCK_LAYOUT)) +#define GDL_IS_DOCK_LAYOUT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GDL_TYPE_DOCK_LAYOUT)) +#define GDL_DOCK_LAYOUT_GET_CLASS(object) (GTK_CHECK_GET_CLASS ((object), GDL_TYPE_DOCK_LAYOUT, GdlDockLayoutClass)) + +/* data types & structures */ +typedef struct _GdlDockLayout GdlDockLayout; +typedef struct _GdlDockLayoutClass GdlDockLayoutClass; +typedef struct _GdlDockLayoutPrivate GdlDockLayoutPrivate; + +struct _GdlDockLayout { + GObject g_object; + + gboolean dirty; + GdlDockMaster *master; + + GdlDockLayoutPrivate *_priv; +}; + +struct _GdlDockLayoutClass { + GObjectClass g_object_class; +}; + + +/* public interface */ + +GType gdl_dock_layout_get_type (void); + +GdlDockLayout *gdl_dock_layout_new (GdlDock *dock); + +void gdl_dock_layout_attach (GdlDockLayout *layout, + GdlDockMaster *master); + +gboolean gdl_dock_layout_load_layout (GdlDockLayout *layout, + const gchar *name); + +void gdl_dock_layout_save_layout (GdlDockLayout *layout, + const gchar *name); + +void gdl_dock_layout_delete_layout (GdlDockLayout *layout, + const gchar *name); + +GList *gdl_dock_layout_get_layouts (GdlDockLayout *layout, + gboolean include_default); + +void gdl_dock_layout_run_manager (GdlDockLayout *layout); + +gboolean gdl_dock_layout_load_from_file (GdlDockLayout *layout, + const gchar *filename); + +gboolean gdl_dock_layout_save_to_file (GdlDockLayout *layout, + const gchar *filename); + +gboolean gdl_dock_layout_is_dirty (GdlDockLayout *layout); + +GtkWidget *gdl_dock_layout_get_ui (GdlDockLayout *layout); +GtkWidget *gdl_dock_layout_get_items_ui (GdlDockLayout *layout); +GtkWidget *gdl_dock_layout_get_layouts_ui (GdlDockLayout *layout); + +G_END_DECLS + +#endif + + diff --git a/src/libgdl/gdl-dock-object.c b/src/libgdl/gdl-dock-object.c index 9bdcd18ed..129cc28d9 100644 --- a/src/libgdl/gdl-dock-object.c +++ b/src/libgdl/gdl-dock-object.c @@ -396,6 +396,7 @@ gdl_dock_object_real_reduce (GdlDockObject *object) children = gtk_container_get_children (GTK_CONTAINER (object)); if (g_list_length (children) <= 1) { GList *l; + GList *dchildren = NULL; /* detach ourselves and then re-attach our children to our current parent. if we are not currently attached, the @@ -403,18 +404,41 @@ gdl_dock_object_real_reduce (GdlDockObject *object) if (parent) gdl_dock_object_freeze (parent); gdl_dock_object_freeze (object); - gdl_dock_object_detach (object, FALSE); + /* Detach the children before detaching this object, since in this + * way the children can have access to the whole object hierarchy. + * Set the InDetach flag now, so the children know that this object + * is going to be detached. */ + + + GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_DETACH); + for (l = children; l; l = l->next) { - GdlDockObject *child = GDL_DOCK_OBJECT (l->data); + GdlDockObject *child; + + if (!GDL_IS_DOCK_OBJECT (l->data)) + continue; + + child = GDL_DOCK_OBJECT (l->data); g_object_ref (child); - GDL_DOCK_OBJECT_SET_FLAGS (child, GDL_DOCK_IN_REFLOW); gdl_dock_object_detach (child, FALSE); + GDL_DOCK_OBJECT_SET_FLAGS (child, GDL_DOCK_IN_REFLOW); if (parent) - gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (child)); + dchildren = g_list_append (dchildren, child); GDL_DOCK_OBJECT_UNSET_FLAGS (child, GDL_DOCK_IN_REFLOW); - g_object_unref (child); } + /* Now it can be detached */ + gdl_dock_object_detach (object, FALSE); + + /* After detaching the reduced object, we can add the + children (the only child in fact) to the new parent */ + for (l = dchildren; l; l = l->next) { + gtk_container_add (GTK_CONTAINER (parent), l->data); + g_object_unref (l->data); + } + g_list_free (dchildren); + + /* sink the widget, so any automatic floating widget is destroyed */ g_object_ref_sink (object); /* don't reenter */ @@ -469,6 +493,9 @@ gdl_dock_object_detach (GdlDockObject *object, { g_return_if_fail (object != NULL); + if (!GDL_IS_DOCK_OBJECT (object)) + return; + if (!GDL_DOCK_OBJECT_ATTACHED (object)) return; diff --git a/src/libgdl/gdl-dock-placeholder.c b/src/libgdl/gdl-dock-placeholder.c index ca7763a55..33934e2e0 100644 --- a/src/libgdl/gdl-dock-placeholder.c +++ b/src/libgdl/gdl-dock-placeholder.c @@ -189,14 +189,14 @@ gdl_dock_placeholder_class_init (GdlDockPlaceholderClass *klass) g_object_class_install_property ( g_object_class, PROP_FLOAT_X, g_param_spec_int ("floatx", _("X-Coordinate"), - _("X coordinate for dock when floating"), + _("X-Coordinate for dock when floating"), -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | GDL_DOCK_PARAM_EXPORT)); g_object_class_install_property ( g_object_class, PROP_FLOAT_Y, g_param_spec_int ("floaty", _("Y-Coordinate"), - _("Y coordinate for dock when floating"), + _("Y-Coordinate for dock when floating"), -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | GDL_DOCK_PARAM_EXPORT)); diff --git a/src/libgdl/gdl-icons.c b/src/libgdl/gdl-icons.c new file mode 100644 index 000000000..2af2a8a9a --- /dev/null +++ b/src/libgdl/gdl-icons.c @@ -0,0 +1,267 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gdl-icons.c + * + * Copyright (C) 2000-2001 Dave Camp + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Dave Camp, Jeroen Zwartepoorte + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gdl-i18n.h" +#include "gdl-tools.h" +#include <string.h> +#include <libgnomeui/gnome-icon-lookup.h> +#include <libgnomevfs/gnome-vfs-ops.h> +#include "gdl-icons.h" + +enum { + PROP_BOGUS, + PROP_ICON_SIZE, +}; + +#define GDL_ICONS_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDL_TYPE_ICONS, GdlIconsPrivate)) + +typedef struct _GdlIconsPrivate GdlIconsPrivate; + +struct _GdlIconsPrivate { + int icon_size; + + GtkIconTheme *icon_theme; + GHashTable *icons; +}; + +GDL_CLASS_BOILERPLATE (GdlIcons, gdl_icons, GObject, G_TYPE_OBJECT); + +static void +gdl_icons_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdlIconsPrivate *priv = GDL_ICONS_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ICON_SIZE: + g_value_set_int (value, priv->icon_size); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdl_icons_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdlIconsPrivate *priv = GDL_ICONS_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ICON_SIZE: + priv->icon_size = g_value_get_int (value); + g_hash_table_destroy (priv->icons); + priv->icons = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gdk_pixbuf_unref); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +theme_changed_cb (GtkIconTheme *theme, + gpointer user_data) +{ + GdlIconsPrivate *priv = GDL_ICONS_GET_PRIVATE (user_data); + + g_hash_table_destroy (priv->icons); + priv->icons = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gdk_pixbuf_unref); +} + +static void +gdl_icons_dispose (GObject *object) +{ + GdlIconsPrivate *priv = GDL_ICONS_GET_PRIVATE (object); + + if (priv->icon_theme) { + /* Don't do that - look a GTK+ docs */ + /* g_object_unref (priv->icon_theme); */ + priv->icon_theme = NULL; + } + + if (priv->icons) { + g_hash_table_destroy (priv->icons); + priv->icons = NULL; + } +} + +static void +gdl_icons_class_init (GdlIconsClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + object_class->dispose = gdl_icons_dispose; + object_class->get_property = gdl_icons_get_property; + object_class->set_property = gdl_icons_set_property; + + g_object_class_install_property (object_class, PROP_ICON_SIZE, + g_param_spec_int ("icon-size", + _("Icon size"), + _("Icon size"), + 12, 256, 24, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (GdlIconsPrivate)); +} + +static void +gdl_icons_instance_init (GdlIcons *icons) +{ + GdlIconsPrivate *priv = GDL_ICONS_GET_PRIVATE (icons); + + priv->icon_theme = gtk_icon_theme_get_default (); + /* gtk_icon_theme_get_default() does not ref the returned object */ + /* but API docs state the you should NOT ref it */ + /* g_object_ref (priv->icon_theme);*/ + g_signal_connect_object (G_OBJECT (priv->icon_theme), "changed", + G_CALLBACK (theme_changed_cb), icons, 0); + priv->icons = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gdk_pixbuf_unref); +} + +GdlIcons * +gdl_icons_new (int icon_size) +{ + return GDL_ICONS (g_object_new (GDL_TYPE_ICONS, + "icon-size", icon_size, + NULL)); +} + +GdkPixbuf * +gdl_icons_get_folder_icon (GdlIcons *icons) +{ + g_return_val_if_fail (icons != NULL, NULL); + g_return_val_if_fail (GDL_IS_ICONS (icons), NULL); + + return gdl_icons_get_mime_icon (icons, "application/directory-normal"); +} + +GdkPixbuf * +gdl_icons_get_uri_icon (GdlIcons *icons, + const char *uri) +{ + GnomeVFSFileInfo *info; + GdkPixbuf *pixbuf; + + g_return_val_if_fail (icons != NULL, NULL); + g_return_val_if_fail (GDL_IS_ICONS (icons), NULL); + g_return_val_if_fail (uri != NULL, NULL); + + info = gnome_vfs_file_info_new (); + gnome_vfs_get_file_info (uri, info, + GNOME_VFS_FILE_INFO_FOLLOW_LINKS | + GNOME_VFS_FILE_INFO_GET_MIME_TYPE | + GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE); + if (info->mime_type) + pixbuf = gdl_icons_get_mime_icon (icons, info->mime_type); + else + pixbuf = gdl_icons_get_mime_icon (icons, "gnome-fs-regular"); + gnome_vfs_file_info_unref (info); + + return pixbuf; +} + +GdkPixbuf * +gdl_icons_get_mime_icon (GdlIcons *icons, + const char *mime_type) +{ + GdkPixbuf *pixbuf; + char *icon_name; + + g_return_val_if_fail (icons != NULL, NULL); + g_return_val_if_fail (GDL_IS_ICONS (icons), NULL); + g_return_val_if_fail (mime_type != NULL, NULL); + + GdlIconsPrivate *priv = GDL_ICONS_GET_PRIVATE (icons); + + pixbuf = g_hash_table_lookup (priv->icons, mime_type); + if (pixbuf != NULL) { + g_object_ref (G_OBJECT (pixbuf)); + return pixbuf; + } + + if (!strcmp (mime_type, "application/directory-normal")) { + icon_name = g_strdup ("gnome-fs-directory"); + } else { + icon_name = gnome_icon_lookup (priv->icon_theme, + NULL, + NULL, + NULL, + NULL, + mime_type, + GNOME_ICON_LOOKUP_FLAGS_NONE, + NULL); + } + + if (!icon_name) { + /* Return regular icon if one doesn't exist for mime type. */ + if (!strcmp (mime_type, "gnome-fs-regular")) + return NULL; + else + return gdl_icons_get_mime_icon (icons, "gnome-fs-regular"); + } else { + if (!gtk_icon_theme_has_icon (priv->icon_theme, icon_name)) { + g_free (icon_name); + if (!strcmp (mime_type, "gnome-fs-regular")) + return NULL; + else + return gdl_icons_get_mime_icon (icons, "gnome-fs-regular"); + } else { + pixbuf = gtk_icon_theme_load_icon (priv->icon_theme, + icon_name, + priv->icon_size, + 0, /* lookup flags */ + NULL); + g_free (icon_name); + + if (pixbuf == NULL) { + if (!strcmp (mime_type, "gnome-fs-regular")) + return NULL; + else + return gdl_icons_get_mime_icon (icons, + "gnome-fs-regular"); + } + } + } + + g_hash_table_insert (priv->icons, g_strdup (mime_type), pixbuf); + g_object_ref (pixbuf); + + return pixbuf; +} diff --git a/src/libgdl/gdl-icons.h b/src/libgdl/gdl-icons.h new file mode 100644 index 000000000..79f3bba85 --- /dev/null +++ b/src/libgdl/gdl-icons.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* gdl-icons.h + * + * Copyright (C) 2000-2001 JP Rosevear + * 2000 Dave Camp + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: JP Rosevear, Dave Camp, Jeroen Zwartepoorte + */ + +#ifndef _GDL_ICONS_H_ +#define _GDL_ICONS_H_ + +#include <glib-object.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +G_BEGIN_DECLS + +#define GDL_TYPE_ICONS (gdl_icons_get_type ()) +#define GDL_ICONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDL_TYPE_ICONS, GdlIcons)) +#define GDL_ICONS_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((klass), GDL_TYPE_ICONS, GdlIconsClass)) +#define GDL_IS_ICONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDL_TYPE_ICONS)) +#define GDL_IS_ICONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GDL_TYPE_ICONS)) + +typedef struct _GdlIcons GdlIcons; +typedef struct _GdlIconsClass GdlIconsClass; + +struct _GdlIcons { + GObject parent; +}; + +struct _GdlIconsClass { + GObjectClass parent_class; +}; + +GType gdl_icons_get_type (void); +GdlIcons *gdl_icons_new (int icon_size); + +GdkPixbuf *gdl_icons_get_folder_icon (GdlIcons *icons); +GdkPixbuf *gdl_icons_get_uri_icon (GdlIcons *icons, + const char *uri); +GdkPixbuf *gdl_icons_get_mime_icon (GdlIcons *icons, + const char *mime_type); + +G_END_DECLS + +#endif /* _GDL_ICONS_H_ */ diff --git a/src/libgdl/gdl-switcher.c b/src/libgdl/gdl-switcher.c index eccd66ce2..24ec72126 100644 --- a/src/libgdl/gdl-switcher.c +++ b/src/libgdl/gdl-switcher.c @@ -55,7 +55,7 @@ static void gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *stock_id, const GdkPixbuf *pixbuf_icon, gint switcher_id); -static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id); +/* static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id); */ static void gdl_switcher_select_page (GdlSwitcher *switcher, gint switcher_id); static void gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id); static void gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show); @@ -514,7 +514,7 @@ gdl_switcher_expose (GtkWidget *widget, GdkEventExpose *event) } } return GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS, expose_event, - (widget, event), FALSE); + (widget, event), FALSE); } static void @@ -678,11 +678,9 @@ gdl_switcher_select_page (GdlSwitcher *switcher, gint id) static void gdl_switcher_class_init (GdlSwitcherClass *klass) { - GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); - (void)notebook_class; container_class->forall = gdl_switcher_forall; container_class->remove = gdl_switcher_remove; @@ -763,11 +761,11 @@ gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *label, gtk_widget_show (hbox); if (stock_id) { - icon_widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON); + icon_widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU); } else if (pixbuf_icon) { icon_widget = gtk_image_new_from_pixbuf (pixbuf_icon); } else { - icon_widget = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_BUTTON); + icon_widget = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU); } gtk_widget_show (icon_widget); @@ -810,6 +808,7 @@ gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *label, gtk_widget_queue_resize (GTK_WIDGET (switcher)); } +#if 0 static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id) { @@ -827,6 +826,7 @@ gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id) } gtk_widget_queue_resize (GTK_WIDGET (switcher)); } +#endif static void gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id) diff --git a/src/libgdl/libgdl.h b/src/libgdl/gdl.h index 5ee84e1ae..e47dc310d 100644 --- a/src/libgdl/libgdl.h +++ b/src/libgdl/gdl.h @@ -28,10 +28,12 @@ #include "libgdl/gdl-dock-master.h" #include "libgdl/gdl-dock.h" #include "libgdl/gdl-dock-item.h" +#include "libgdl/gdl-dock-layout.h" #include "libgdl/gdl-dock-paned.h" #include "libgdl/gdl-dock-notebook.h" #include "libgdl/gdl-dock-tablabel.h" #include "libgdl/gdl-dock-bar.h" +#include "libgdl/gdl-combo-button.h" #include "libgdl/gdl-switcher.h" #endif diff --git a/src/libgdl/libgdltypebuiltins.h b/src/libgdl/libgdltypebuiltins.h index 8be5decb1..f5e6ea17b 100644 --- a/src/libgdl/libgdltypebuiltins.h +++ b/src/libgdl/libgdltypebuiltins.h @@ -4,7 +4,7 @@ #ifndef __LIBGDLTYPEBUILTINS_H__ #define __LIBGDLTYPEBUILTINS_H__ 1 -#include "libgdl/libgdl.h" +#include "libgdl/gdl.h" G_BEGIN_DECLS diff --git a/src/libgdl/test-combo-button.c b/src/libgdl/test-combo-button.c new file mode 100644 index 000000000..35ce3fff3 --- /dev/null +++ b/src/libgdl/test-combo-button.c @@ -0,0 +1,111 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* test-combo-button.c + * + * Copyright (C) 2003 Jeroen Zwartepoorte + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> +#include "gdl-combo-button.h" + +static void +combo_button_activate_default_cb (GdlComboButton *combo, + gpointer data) +{ + g_message ("combo_button_activate_default_cb"); +} + +int +main (int argc, char **argv) +{ + GtkWidget *window, *hbox, *combo, *menu, *menuitem; + GdkPixbuf *icon; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (G_OBJECT (window), "delete_event", + G_CALLBACK (gtk_main_quit), NULL); + gtk_window_set_title (GTK_WINDOW (window), "Combo button test"); + gtk_window_set_resizable (GTK_WINDOW (window), FALSE); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), hbox); + + combo = gtk_button_new_from_stock (GTK_STOCK_OPEN); + gtk_button_set_relief (GTK_BUTTON (combo), GTK_RELIEF_NONE); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + + menu = gtk_menu_new (); + menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_OPEN, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + gtk_widget_show_all (menu); + + combo = gdl_combo_button_new (); + gdl_combo_button_set_label (GDL_COMBO_BUTTON (combo), "Run"); + gdl_combo_button_set_menu (GDL_COMBO_BUTTON (combo), GTK_MENU (menu)); + icon = gtk_widget_render_icon (combo, GTK_STOCK_EXECUTE, + GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); + gdl_combo_button_set_icon (GDL_COMBO_BUTTON (combo), icon); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + + g_signal_connect (combo, "activate_default", + G_CALLBACK (combo_button_activate_default_cb), NULL); + + combo = gtk_button_new_from_stock (GTK_STOCK_SAVE); + gtk_button_set_relief (GTK_BUTTON (combo), GTK_RELIEF_NONE); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + + menu = gtk_menu_new (); + menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_OPEN, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE, NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + gtk_widget_show_all (menu); + + combo = gdl_combo_button_new (); + gdl_combo_button_set_label (GDL_COMBO_BUTTON (combo), "Open"); + gdl_combo_button_set_menu (GDL_COMBO_BUTTON (combo), GTK_MENU (menu)); + icon = gtk_widget_render_icon (combo, GTK_STOCK_OPEN, + GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); + gdl_combo_button_set_icon (GDL_COMBO_BUTTON (combo), icon); + gtk_widget_set_sensitive (combo, FALSE); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + + g_signal_connect (combo, "activate_default", + G_CALLBACK (combo_button_activate_default_cb), NULL); + + menu = gtk_menu_new (); + combo = gdl_combo_button_new (); + gdl_combo_button_set_label (GDL_COMBO_BUTTON (combo), "Open"); + gdl_combo_button_set_menu (GDL_COMBO_BUTTON (combo), GTK_MENU (menu)); + icon = gtk_widget_render_icon (combo, GTK_STOCK_OPEN, + GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); + gdl_combo_button_set_icon (GDL_COMBO_BUTTON (combo), icon); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + + gtk_widget_show_all (window); + + gtk_main (); + + return 0; +} diff --git a/src/libgdl/test-dataview.c b/src/libgdl/test-dataview.c new file mode 100644 index 000000000..bc89cbd4f --- /dev/null +++ b/src/libgdl/test-dataview.c @@ -0,0 +1,43 @@ +#include <config.h> +#include <gtk/gtk.h> +#include <libgnome/libgnome.h> + +#include "gdl-data-view.h" +#include "gdl-data-model-test.h" + +int +main (int argc, char *argv[]) +{ + GtkWidget *win; + GtkWidget *view; + GdlDataModel *model; + GtkWidget *vbox; + + gtk_init (&argc, &argv); + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size (GTK_WINDOW (win), 500, 200); + + vbox = gtk_vbox_new (FALSE, 5); + + view = gdl_data_view_new (); + + gtk_layout_set_hadjustment (GTK_LAYOUT (view), NULL); + gtk_layout_set_vadjustment (GTK_LAYOUT (view), NULL); + + + model = GDL_DATA_MODEL (gdl_data_model_test_new ()); + gdl_data_view_set_model (GDL_DATA_VIEW (view), + model); + + gtk_box_pack_start (GTK_BOX (vbox), view, TRUE, TRUE, 0); + + gtk_container_add (GTK_CONTAINER (win), vbox); + + gtk_widget_show_all (win); + gtk_widget_grab_focus (GTK_WIDGET (view)); + + gtk_main (); + + return 0; +} diff --git a/src/libgdl/test-dock.c b/src/libgdl/test-dock.c new file mode 100644 index 000000000..1e9c80111 --- /dev/null +++ b/src/libgdl/test-dock.c @@ -0,0 +1,311 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <gtk/gtk.h> + +#include "gdl-tools.h" + +#include "gdl-dock.h" +#include "gdl-dock-item.h" +#include "gdl-dock-notebook.h" +#include "gdl-dock-layout.h" +#include "gdl-dock-placeholder.h" +#include "gdl-dock-bar.h" +#include "gdl-switcher.h" + +#include <glib.h> + +/* ---- end of debugging code */ + +static void +on_style_button_toggled (GtkRadioButton *button, GdlDock *dock) +{ + gboolean active; + GdlDockMaster *master = GDL_DOCK_OBJECT_GET_MASTER (dock); + GdlSwitcherStyle style = + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), + "__style_id")); + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + if (active) { + g_object_set (master, "switcher-style", style, NULL); + } +} + +static GtkWidget * +create_style_button (GtkWidget *dock, GtkWidget *box, GtkWidget *group, + GdlSwitcherStyle style, const gchar *style_text) +{ + GdlSwitcherStyle current_style; + GtkWidget *button1; + GdlDockMaster *master = GDL_DOCK_OBJECT_GET_MASTER (dock); + + g_object_get (master, "switcher-style", ¤t_style, NULL); + button1 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (group), + style_text); + gtk_widget_show (button1); + g_object_set_data (G_OBJECT (button1), "__style_id", + GINT_TO_POINTER (style)); + if (current_style == style) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE); + } + g_signal_connect (button1, "toggled", + G_CALLBACK (on_style_button_toggled), + dock); + gtk_box_pack_start (GTK_BOX (box), button1, FALSE, FALSE, 0); + return button1; +} + +static GtkWidget * +create_styles_item (GtkWidget *dock) +{ + GtkWidget *vbox1; + GtkWidget *group; + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox1); + + group = create_style_button (dock, vbox1, NULL, + GDL_SWITCHER_STYLE_ICON, "Only icon"); + group = create_style_button (dock, vbox1, group, + GDL_SWITCHER_STYLE_TEXT, "Only text"); + group = create_style_button (dock, vbox1, group, + GDL_SWITCHER_STYLE_BOTH, + "Both icons and texts"); + group = create_style_button (dock, vbox1, group, + GDL_SWITCHER_STYLE_TOOLBAR, + "Desktop toolbar style"); + group = create_style_button (dock, vbox1, group, + GDL_SWITCHER_STYLE_TABS, + "Notebook tabs"); + return vbox1; +} + +static GtkWidget * +create_item (const gchar *button_title) +{ + GtkWidget *vbox1; + GtkWidget *button1; + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox1); + + button1 = gtk_button_new_with_label (button_title); + gtk_widget_show (button1); + gtk_box_pack_start (GTK_BOX (vbox1), button1, TRUE, TRUE, 0); + + return vbox1; +} + +/* creates a simple widget with a textbox inside */ +static GtkWidget * +create_text_item () +{ + GtkWidget *vbox1; + GtkWidget *scrolledwindow1; + GtkWidget *text; + + vbox1 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox1); + + scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow1); + gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow1, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow1), + GTK_SHADOW_ETCHED_IN); + text = gtk_text_view_new (); + g_object_set (text, "wrap-mode", GTK_WRAP_WORD, NULL); + gtk_widget_show (text); + gtk_container_add (GTK_CONTAINER (scrolledwindow1), text); + + return vbox1; +} + +static void +button_dump_cb (GtkWidget *button, gpointer data) +{ + /* Dump XML tree. */ + gdl_dock_layout_save_to_file (GDL_DOCK_LAYOUT (data), "layout.xml"); + g_spawn_command_line_async ("cat layout.xml", NULL); +} + +static void +run_layout_manager_cb (GtkWidget *w, gpointer data) +{ + GdlDockLayout *layout = GDL_DOCK_LAYOUT (data); + gdl_dock_layout_run_manager (layout); +} + +static void +save_layout_cb (GtkWidget *w, gpointer data) +{ + GdlDockLayout *layout = GDL_DOCK_LAYOUT (data); + GtkWidget *dialog, *hbox, *label, *entry; + gint response; + + dialog = gtk_dialog_new_with_buttons ("New Layout", + NULL, + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, + GTK_RESPONSE_OK, + NULL); + + hbox = gtk_hbox_new (FALSE, 8); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 8); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new ("Name:"); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); + + gtk_widget_show_all (hbox); + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response == GTK_RESPONSE_OK) { + const gchar *name = gtk_entry_get_text (GTK_ENTRY (entry)); + gdl_dock_layout_save_layout (layout, name); + } + + gtk_widget_destroy (dialog); +} + +int +main (int argc, char **argv) +{ + GtkWidget *item1, *item2, *item3; + GtkWidget *items [4]; + GtkWidget *win, *table, *button, *box; + int i; + GdlDockLayout *layout; + GtkWidget *dock, *dockbar; + + gtk_init (&argc, &argv); + + /*gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);*/ + + /* window creation */ + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (win, "delete_event", + G_CALLBACK (gtk_main_quit), NULL); + gtk_window_set_title (GTK_WINDOW (win), "Docking widget test"); + gtk_window_set_default_size (GTK_WINDOW (win), 400, 400); + + /* table */ + table = gtk_vbox_new (FALSE, 5); + gtk_container_add (GTK_CONTAINER (win), table); + gtk_container_set_border_width (GTK_CONTAINER (table), 10); + + /* create the dock */ + dock = gdl_dock_new (); + + /* ... and the layout manager */ + layout = gdl_dock_layout_new (GDL_DOCK (dock)); + + /* create the dockbar */ + dockbar = gdl_dock_bar_new (GDL_DOCK (dock)); + gdl_dock_bar_set_style(GDL_DOCK_BAR(dockbar), GDL_DOCK_BAR_TEXT); + + box = gtk_hbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (table), box, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (box), dockbar, FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (box), dock, TRUE, TRUE, 0); + + /* create the dock items */ + item1 = gdl_dock_item_new ("item1", "Item #1", GDL_DOCK_ITEM_BEH_LOCKED); + gtk_container_add (GTK_CONTAINER (item1), create_text_item ()); + gdl_dock_add_item (GDL_DOCK (dock), GDL_DOCK_ITEM (item1), + GDL_DOCK_TOP); + gtk_widget_show (item1); + + item2 = gdl_dock_item_new_with_stock ("item2", "Item #2: Select the switcher style for notebooks", + GTK_STOCK_EXECUTE, + GDL_DOCK_ITEM_BEH_NORMAL); + g_object_set (item2, "resize", FALSE, NULL); + gtk_container_add (GTK_CONTAINER (item2), create_styles_item (dock)); + gdl_dock_add_item (GDL_DOCK (dock), GDL_DOCK_ITEM (item2), + GDL_DOCK_RIGHT); + gtk_widget_show (item2); + + item3 = gdl_dock_item_new_with_stock ("item3", "Item #3 has accented characters (áéÃóúñ)", + GTK_STOCK_CONVERT, + GDL_DOCK_ITEM_BEH_NORMAL | + GDL_DOCK_ITEM_BEH_CANT_CLOSE); + gtk_container_add (GTK_CONTAINER (item3), create_item ("Button 3")); + gdl_dock_add_item (GDL_DOCK (dock), GDL_DOCK_ITEM (item3), + GDL_DOCK_BOTTOM); + gtk_widget_show (item3); + + items [0] = gdl_dock_item_new_with_stock ("Item #4", "Item #4", + GTK_STOCK_JUSTIFY_FILL, + GDL_DOCK_ITEM_BEH_NORMAL | + GDL_DOCK_ITEM_BEH_CANT_ICONIFY); + gtk_container_add (GTK_CONTAINER (items [0]), create_text_item ()); + gtk_widget_show (items [0]); + gdl_dock_add_item (GDL_DOCK (dock), GDL_DOCK_ITEM (items [0]), GDL_DOCK_BOTTOM); + for (i = 1; i < 3; i++) { + gchar name[10]; + + snprintf (name, sizeof (name), "Item #%d", i + 4); + items [i] = gdl_dock_item_new_with_stock (name, name, GTK_STOCK_NEW, + GDL_DOCK_ITEM_BEH_NORMAL); + gtk_container_add (GTK_CONTAINER (items [i]), create_text_item ()); + gtk_widget_show (items [i]); + + gdl_dock_object_dock (GDL_DOCK_OBJECT (items [0]), + GDL_DOCK_OBJECT (items [i]), + GDL_DOCK_CENTER, NULL); + }; + + /* tests: manually dock and move around some of the items */ + gdl_dock_item_dock_to (GDL_DOCK_ITEM (item3), GDL_DOCK_ITEM (item1), + GDL_DOCK_TOP, -1); + + gdl_dock_item_dock_to (GDL_DOCK_ITEM (item2), GDL_DOCK_ITEM (item3), + GDL_DOCK_RIGHT, -1); + + gdl_dock_item_dock_to (GDL_DOCK_ITEM (item2), GDL_DOCK_ITEM (item3), + GDL_DOCK_LEFT, -1); + + gdl_dock_item_dock_to (GDL_DOCK_ITEM (item2), NULL, + GDL_DOCK_FLOATING, -1); + + box = gtk_hbox_new (TRUE, 5); + gtk_box_pack_end (GTK_BOX (table), box, FALSE, FALSE, 0); + + button = gtk_button_new_from_stock (GTK_STOCK_SAVE); + g_signal_connect (button, "clicked", + G_CALLBACK (save_layout_cb), layout); + gtk_box_pack_end (GTK_BOX (box), button, FALSE, TRUE, 0); + + button = gtk_button_new_with_label ("Layout Manager"); + g_signal_connect (button, "clicked", + G_CALLBACK (run_layout_manager_cb), layout); + gtk_box_pack_end (GTK_BOX (box), button, FALSE, TRUE, 0); + + button = gtk_button_new_with_label ("Dump XML"); + g_signal_connect (button, "clicked", + G_CALLBACK (button_dump_cb), layout); + gtk_box_pack_end (GTK_BOX (box), button, FALSE, TRUE, 0); + + gtk_widget_show_all (win); + + gdl_dock_placeholder_new ("ph1", GDL_DOCK_OBJECT (dock), GDL_DOCK_TOP, FALSE); + gdl_dock_placeholder_new ("ph2", GDL_DOCK_OBJECT (dock), GDL_DOCK_BOTTOM, FALSE); + gdl_dock_placeholder_new ("ph3", GDL_DOCK_OBJECT (dock), GDL_DOCK_LEFT, FALSE); + gdl_dock_placeholder_new ("ph4", GDL_DOCK_OBJECT (dock), GDL_DOCK_RIGHT, FALSE); + + gtk_main (); + + g_object_unref (layout); + + return 0; +} diff --git a/src/ui/dialog/dock-behavior.h b/src/ui/dialog/dock-behavior.h index b865af545..98c111719 100644 --- a/src/ui/dialog/dock-behavior.h +++ b/src/ui/dialog/dock-behavior.h @@ -21,7 +21,7 @@ #include "ui/widget/dock-item.h" -#include "libgdl/libgdl.h" +#include "libgdl/gdl.h" #include "behavior.h" diff --git a/src/ui/widget/dock-item.h b/src/ui/widget/dock-item.h index 79d69d862..1780b7525 100644 --- a/src/ui/widget/dock-item.h +++ b/src/ui/widget/dock-item.h @@ -19,7 +19,7 @@ #include <gtkmm/paned.h> #include <gtkmm/window.h> -#include "libgdl/libgdl.h" +#include "libgdl/gdl.h" namespace Inkscape { namespace UI { diff --git a/src/ui/widget/dock.h b/src/ui/widget/dock.h index 5836cf83f..bd5685348 100644 --- a/src/ui/widget/dock.h +++ b/src/ui/widget/dock.h @@ -20,7 +20,7 @@ #include "ui/widget/dock-item.h" -#include "libgdl/libgdl.h" +#include "libgdl/gdl.h" namespace Inkscape { namespace UI { |
