GUI: Dead kittens.
[gnumeric.git] / src / widgets / gnm-notebook.c
blob57eef780a254a48b30b7ec4ea8a46362d128d357
1 /**
2 * gnm-notebook.c: Implements a button-only notebook.
4 * Copyright (c) 2008 Morten Welinder <terra@gnome.org>
5 * Copyright notices for included gtknotebook.c, see below.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19 **/
21 #include <gnumeric-config.h>
22 #include "gnm-notebook.h"
23 #include <goffice/goffice.h>
24 #include "gnm-i18n.h"
25 #include <gsf/gsf-impl-utils.h>
27 /* ------------------------------------------------------------------------- */
29 struct GnmNotebookButton_ {
31 * We need to derive from GtkLabel mostly for theming reasons,
32 * but GtkLabel is also special for clipping.
34 GtkLabel base;
36 PangoLayout *layout;
37 PangoLayout *layout_active;
39 PangoRectangle logical, logical_active;
40 int x_offset, x_offset_active;
42 GdkRGBA *fg, *bg;
45 typedef struct {
46 GtkLabelClass parent_class;
47 } GnmNotebookButtonClass;
49 static GObjectClass *gnm_notebook_button_parent_class;
51 enum {
52 NBB_PROP_0,
53 NBB_PROP_BACKGROUND_COLOR,
54 NBB_PROP_TEXT_COLOR
57 static void
58 gnm_notebook_button_finalize (GObject *obj)
60 GnmNotebookButton *nbb = GNM_NOTEBOOK_BUTTON (obj);
61 g_clear_object (&nbb->layout);
62 g_clear_object (&nbb->layout_active);
63 gdk_rgba_free (nbb->fg);
64 gdk_rgba_free (nbb->bg);
65 gnm_notebook_button_parent_class->finalize (obj);
68 static void
69 gnm_notebook_button_set_property (GObject *obj,
70 guint prop_id,
71 const GValue *value,
72 GParamSpec *pspec)
74 GnmNotebookButton *nbb = GNM_NOTEBOOK_BUTTON (obj);
76 switch (prop_id) {
77 case NBB_PROP_BACKGROUND_COLOR:
78 gdk_rgba_free (nbb->bg);
79 nbb->bg = g_value_dup_boxed (value);
80 gtk_widget_queue_draw (GTK_WIDGET (obj));
81 g_clear_object (&nbb->layout);
82 g_clear_object (&nbb->layout_active);
83 break;
84 case NBB_PROP_TEXT_COLOR:
85 gdk_rgba_free (nbb->fg);
86 nbb->fg = g_value_dup_boxed (value);
87 gtk_widget_queue_draw (GTK_WIDGET (obj));
88 gtk_widget_override_color (GTK_WIDGET (obj),
89 GTK_STATE_FLAG_NORMAL,
90 nbb->fg);
91 gtk_widget_override_color (GTK_WIDGET (obj),
92 GTK_STATE_FLAG_ACTIVE,
93 nbb->fg);
94 break;
95 default:
96 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
97 break;
101 static void
102 gnm_notebook_button_ensure_layout (GnmNotebookButton *nbb)
104 const char *text = gtk_label_get_text (GTK_LABEL (nbb));
106 if (nbb->layout) {
107 if (strcmp (text, pango_layout_get_text (nbb->layout)) == 0)
108 return;
109 pango_layout_set_text (nbb->layout, text, -1);
110 pango_layout_set_text (nbb->layout_active, text, -1);
111 } else {
112 PangoAttrList *attrs, *attrs_active;
113 PangoAttribute *attr;
114 PangoFontDescription *desc;
115 GtkWidget *widget = GTK_WIDGET (nbb);
116 GtkStyleContext *context =
117 gtk_widget_get_style_context (widget);
119 nbb->layout = gtk_widget_create_pango_layout (widget, text);
120 nbb->layout_active = gtk_widget_create_pango_layout (widget, text);
122 /* Common */
123 attrs = pango_attr_list_new ();
124 if (nbb->bg) {
125 attr = go_color_to_pango
126 (go_color_from_gdk_rgba (nbb->bg, NULL),
127 FALSE);
128 attr->start_index = 0;
129 attr->end_index = -1;
130 pango_attr_list_insert (attrs, attr);
132 attrs_active = pango_attr_list_copy (attrs);
134 // As-of gtk+ 3.20 we have to set the context state to the state
135 // we are querying for. This ought to work before gtk+ 3.20
136 // too.
137 gtk_style_context_save (context);
139 /* Normal */
140 gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
141 gtk_style_context_get (context, GTK_STATE_FLAG_NORMAL,
142 "font", &desc, NULL);
143 attr = pango_attr_font_desc_new (desc);
144 attr->start_index = 0;
145 attr->end_index = -1;
146 pango_attr_list_insert (attrs, attr);
147 pango_font_description_free (desc);
148 pango_layout_set_attributes (nbb->layout, attrs);
149 pango_attr_list_unref (attrs);
151 /* Active */
152 gtk_style_context_set_state (context, GTK_STATE_FLAG_ACTIVE);
153 gtk_style_context_get (context, GTK_STATE_FLAG_ACTIVE,
154 "font", &desc, NULL);
155 attr = pango_attr_font_desc_new (desc);
156 attr->start_index = 0;
157 attr->end_index = -1;
158 pango_attr_list_insert (attrs_active, attr);
159 pango_font_description_free (desc);
160 pango_layout_set_attributes (nbb->layout_active, attrs_active);
161 pango_attr_list_unref (attrs_active);
163 gtk_style_context_restore (context);
166 pango_layout_get_extents (nbb->layout, NULL, &nbb->logical);
167 pango_layout_get_extents (nbb->layout_active, NULL, &nbb->logical_active);
170 static void
171 gnm_notebook_button_screen_changed (GtkWidget *widget, G_GNUC_UNUSED GdkScreen *prev)
173 GnmNotebookButton *nbb = GNM_NOTEBOOK_BUTTON (widget);
174 g_clear_object (&nbb->layout);
175 g_clear_object (&nbb->layout_active);
178 static gboolean
179 gnm_notebook_button_draw (GtkWidget *widget, cairo_t *cr)
181 GnmNotebookButton *nbb = GNM_NOTEBOOK_BUTTON (widget);
182 GnmNotebook *nb = GNM_NOTEBOOK (gtk_widget_get_parent (widget));
183 GtkStyleContext *context = gtk_widget_get_style_context (widget);
184 gboolean is_active = (widget == gnm_notebook_get_current_label (nb));
185 GtkStateFlags state =
186 is_active ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL;
187 GtkBorder padding;
189 gtk_style_context_save (context);
190 gtk_style_context_set_state (context, state);
192 gtk_style_context_get_padding (context, state, &padding);
194 gnm_notebook_button_ensure_layout (nbb);
196 gtk_render_layout (context, cr,
197 padding.left + (is_active ? nbb->x_offset_active : nbb->x_offset),
199 is_active ? nbb->layout_active : nbb->layout);
201 gtk_style_context_restore (context);
202 return FALSE;
205 static GtkSizeRequestMode
206 gnm_notebook_button_get_request_mode (GtkWidget *widget)
208 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
211 static void
212 gnm_notebook_button_get_preferred_height (GtkWidget *widget,
213 gint *minimum,
214 gint *natural)
216 GnmNotebookButton *nbb = GNM_NOTEBOOK_BUTTON (widget);
217 GtkBorder padding;
218 GtkStyleContext *ctxt = gtk_widget_get_style_context (widget);
220 // As-of gtk+ 3.20 we have to set the context state to the state
221 // we are querying for. This ought to work before gtk+ 3.20 too.
222 gtk_style_context_save (ctxt);
223 gtk_style_context_set_state (ctxt, GTK_STATE_FLAG_NORMAL);
224 gtk_style_context_get_padding (ctxt,
225 GTK_STATE_FLAG_NORMAL,
226 &padding);
227 gtk_style_context_restore (ctxt);
229 gnm_notebook_button_ensure_layout (nbb);
231 *minimum = *natural =
232 (padding.top +
233 PANGO_PIXELS_CEIL (MAX (nbb->logical.height,
234 nbb->logical_active.height)) +
235 padding.bottom);
238 static void
239 gnm_notebook_button_get_preferred_width (GtkWidget *widget,
240 gint *minimum,
241 gint *natural)
243 GnmNotebookButton *nbb = GNM_NOTEBOOK_BUTTON (widget);
244 GtkBorder padding;
245 GtkStyleContext *ctxt = gtk_widget_get_style_context (widget);
247 // As-of gtk+ 3.20 we have to set the context state to the state
248 // we are querying for. This ought to work before gtk+ 3.20 too.
249 gtk_style_context_save (ctxt);
250 gtk_style_context_set_state (ctxt, GTK_STATE_FLAG_NORMAL);
251 gtk_style_context_get_padding (ctxt,
252 GTK_STATE_FLAG_NORMAL,
253 &padding);
254 gtk_style_context_restore (ctxt);
256 gnm_notebook_button_ensure_layout (nbb);
258 *minimum = *natural =
259 (padding.left +
260 PANGO_PIXELS_CEIL (MAX (nbb->logical.width,
261 nbb->logical_active.width)) +
262 padding.right);
265 static void
266 gnm_notebook_button_size_allocate (GtkWidget *widget,
267 GtkAllocation *allocation)
269 GnmNotebookButton *nbb = GNM_NOTEBOOK_BUTTON (widget);
271 gnm_notebook_button_ensure_layout (nbb);
272 nbb->x_offset =
273 (allocation->width - PANGO_PIXELS (nbb->logical.width)) / 2;
274 nbb->x_offset_active =
275 (allocation->width - PANGO_PIXELS (nbb->logical_active.width)) / 2;
277 GTK_WIDGET_CLASS(gnm_notebook_button_parent_class)
278 ->size_allocate (widget, allocation);
281 static void
282 gnm_notebook_button_class_init (GObjectClass *klass)
284 GtkWidgetClass *wclass = (GtkWidgetClass *)klass;
286 gnm_notebook_button_parent_class = g_type_class_peek_parent (klass);
287 klass->finalize = gnm_notebook_button_finalize;
288 klass->set_property = gnm_notebook_button_set_property;
290 g_object_class_install_property
291 (klass,
292 NBB_PROP_BACKGROUND_COLOR,
293 g_param_spec_boxed ("background-color",
294 P_("Background Color"),
295 P_("Override color to use for background"),
296 GDK_TYPE_RGBA,
297 G_PARAM_WRITABLE));
298 g_object_class_install_property
299 (klass,
300 NBB_PROP_TEXT_COLOR,
301 g_param_spec_boxed ("text-color",
302 P_("Text Color"),
303 P_("Override color to use for label"),
304 GDK_TYPE_RGBA,
305 G_PARAM_WRITABLE));
307 wclass->draw = gnm_notebook_button_draw;
308 wclass->screen_changed = gnm_notebook_button_screen_changed;
309 wclass->get_request_mode = gnm_notebook_button_get_request_mode;
310 wclass->get_preferred_width = gnm_notebook_button_get_preferred_width;
311 wclass->get_preferred_height = gnm_notebook_button_get_preferred_height;
312 wclass->size_allocate = gnm_notebook_button_size_allocate;
315 static void
316 gnm_notebook_button_init (GnmNotebookButton *nbb)
320 GSF_CLASS (GnmNotebookButton, gnm_notebook_button,
321 gnm_notebook_button_class_init,
322 gnm_notebook_button_init, GTK_TYPE_LABEL)
323 #if 0
325 #endif
327 /* ------------------------------------------------------------------------- */
329 struct _GnmNotebook {
330 GtkNotebook parent;
333 * This is the number of pixels from a regular notebook that
334 * we are not drawing. It is caused by the empty widgets
335 * that we have to use.
337 int dummy_height;
340 typedef struct {
341 GtkNotebookClass parent_class;
342 } GnmNotebookClass;
344 static GtkNotebookClass *gnm_notebook_parent_class;
346 #define DUMMY_KEY "GNM-NOTEBOOK-DUMMY-WIDGET"
348 static void
349 gnm_notebook_size_allocate (GtkWidget *widget,
350 GtkAllocation *allocation)
352 int i, h = 0;
353 GnmNotebook *gnb = (GnmNotebook *)widget;
354 GtkAllocation alc = *allocation;
356 for (i = 0; TRUE; i++) {
357 GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (widget), i);
358 GtkAllocation a;
359 if (!page)
360 break;
361 if (!gtk_widget_get_visible (page))
362 continue;
363 gtk_widget_get_allocation (page, &a);
364 h = MAX (h, a.height);
367 gnb->dummy_height = h;
369 alc.y -= h;
370 ((GtkWidgetClass *)gnm_notebook_parent_class)->size_allocate
371 (widget, &alc);
374 static gboolean
375 gnm_notebook_button_press (GtkWidget *widget,
376 GdkEventButton *event)
378 GnmNotebook *nb = GNM_NOTEBOOK (widget);
379 unsigned ui;
381 for (ui = 0; /* Nothing */; ui++) {
382 GtkWidget *child = gnm_notebook_get_nth_label (nb, ui);
383 GtkAllocation child_allocation;
385 if (!child)
386 break;
388 if (!gtk_widget_get_child_visible (child))
389 continue;
391 gtk_widget_get_allocation (child, &child_allocation);
393 if (event->x >= child_allocation.x &&
394 event->x < child_allocation.x + child_allocation.width &&
395 event->y >= child_allocation.y &&
396 event->y < child_allocation.y + child_allocation.height) {
397 if (0)
398 g_printerr ("Button %d pressed\n", ui);
399 if (gtk_widget_event (child, (GdkEvent*)event))
400 return TRUE;
401 else
402 break;
406 return GTK_WIDGET_CLASS(gnm_notebook_parent_class)
407 ->button_press_event (widget, event);
410 static GType
411 gnm_notebook_child_type (G_GNUC_UNUSED GtkContainer *container)
413 return GNM_NOTEBOOK_BUTTON_TYPE;
416 static void
417 gnm_notebook_class_init (GtkWidgetClass *klass)
419 GtkWidgetClass *wclass = (GtkWidgetClass *)klass;
420 GtkContainerClass *cclass = (GtkContainerClass *)klass;
422 gnm_notebook_parent_class = g_type_class_peek (GTK_TYPE_NOTEBOOK);
423 klass->size_allocate = gnm_notebook_size_allocate;
425 cclass->child_type = gnm_notebook_child_type;
427 wclass->button_press_event = gnm_notebook_button_press;
430 static void
431 gnm_notebook_init (GnmNotebook *notebook)
433 gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
434 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_BOTTOM);
435 gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
436 gtk_notebook_set_group_name (GTK_NOTEBOOK (notebook), "Gnumeric");
439 GSF_CLASS (GnmNotebook, gnm_notebook,
440 gnm_notebook_class_init, gnm_notebook_init, GTK_TYPE_NOTEBOOK)
443 gnm_notebook_get_n_visible (GnmNotebook *nb)
445 int count = 0;
446 GList *l, *children = gtk_container_get_children (GTK_CONTAINER (nb));
448 for (l = children; l; l = l->next) {
449 GtkWidget *child = l->data;
450 if (gtk_widget_get_visible (child))
451 count++;
454 g_list_free (children);
456 return count;
459 GtkWidget *
460 gnm_notebook_get_nth_label (GnmNotebook *nb, int n)
462 GtkWidget *page;
464 g_return_val_if_fail (GNM_IS_NOTEBOOK (nb), NULL);
466 page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (nb), n);
467 if (!page)
468 return NULL;
470 return gtk_notebook_get_tab_label (GTK_NOTEBOOK (nb), page);
473 GtkWidget *
474 gnm_notebook_get_current_label (GnmNotebook *nb)
476 int i;
478 g_return_val_if_fail (GNM_IS_NOTEBOOK (nb), NULL);
480 i = gtk_notebook_get_current_page (GTK_NOTEBOOK (nb));
481 return i == -1 ? NULL : gnm_notebook_get_nth_label (nb, i);
484 static void
485 cb_label_destroyed (G_GNUC_UNUSED GtkWidget *label, GtkWidget *dummy)
487 gtk_widget_destroy (dummy);
490 static void
491 cb_label_visibility (GtkWidget *label,
492 G_GNUC_UNUSED GParamSpec *pspec,
493 GtkWidget *dummy)
495 gtk_widget_set_visible (dummy, gtk_widget_get_visible (label));
498 void
499 gnm_notebook_insert_tab (GnmNotebook *nb, GtkWidget *label, int pos)
501 GtkWidget *dummy_page = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
502 gtk_widget_set_size_request (dummy_page, 1, 1);
504 g_object_set_data (G_OBJECT (label), DUMMY_KEY, dummy_page);
506 g_signal_connect_object (G_OBJECT (label), "destroy",
507 G_CALLBACK (cb_label_destroyed), dummy_page,
510 cb_label_visibility (label, NULL, dummy_page);
511 g_signal_connect_object (G_OBJECT (label), "notify::visible",
512 G_CALLBACK (cb_label_visibility), dummy_page,
515 gtk_notebook_insert_page (GTK_NOTEBOOK (nb), dummy_page, label, pos);
517 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (nb), dummy_page,
518 TRUE);
521 void
522 gnm_notebook_move_tab (GnmNotebook *nb, GtkWidget *label, int newpos)
524 GtkWidget *child = g_object_get_data (G_OBJECT (label), DUMMY_KEY);
525 gtk_notebook_reorder_child (GTK_NOTEBOOK (nb), child, newpos);
528 void
529 gnm_notebook_set_current_page (GnmNotebook *nb, int page)
531 gtk_notebook_set_current_page (GTK_NOTEBOOK (nb), page);
534 void
535 gnm_notebook_prev_page (GnmNotebook *nb)
537 gtk_notebook_prev_page (GTK_NOTEBOOK (nb));
540 void
541 gnm_notebook_next_page (GnmNotebook *nb)
543 gtk_notebook_next_page (GTK_NOTEBOOK (nb));