1 /*
2  * pluma-panel.c
3  * This file is part of pluma
4  *
5  * Copyright (C) 2005 - Paolo Maggi
6  * Copyright (C) 2012-2021 MATE Developers
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 /*
25  * Modified by the pluma Team, 2005. See the AUTHORS file for a
26  * list of people on the pluma Team.
27  * See the ChangeLog files for a list of changes.
28  *
29  * $Id$
30  */
31 
32 #include "pluma-panel.h"
33 
34 #include <glib/gi18n.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdk.h>
37 #include <gdk/gdkkeysyms.h>
38 
39 #include "pluma-close-button.h"
40 #include "pluma-window.h"
41 #include "pluma-debug.h"
42 
43 #define PANEL_ITEM_KEY "PlumaPanelItemKey"
44 
45 struct _PlumaPanelPrivate
46 {
47 	GtkOrientation orientation;
48 
49 	/* Title bar (vertical panel only) */
50 	GtkWidget *title_image;
51 	GtkWidget *title_label;
52 
53 	/* Notebook */
54 	GtkWidget *notebook;
55 };
56 
57 typedef struct _PlumaPanelItem PlumaPanelItem;
58 
59 struct _PlumaPanelItem
60 {
61 	gchar *name;
62 	GtkWidget *icon;
63 };
64 
65 /* Properties */
66 enum {
67 	PROP_0,
68 	PROP_ORIENTATION
69 };
70 
71 /* Signals */
72 enum {
73 	ITEM_ADDED,
74 	ITEM_REMOVED,
75 	CLOSE,
76 	FOCUS_DOCUMENT,
77 	LAST_SIGNAL
78 };
79 
80 static guint signals[LAST_SIGNAL] = { 0 };
81 
82 static GObject	*pluma_panel_constructor	(GType type,
83 						 guint n_construct_properties,
84 						 GObjectConstructParam *construct_properties);
85 
86 
G_DEFINE_TYPE_WITH_PRIVATE(PlumaPanel,pluma_panel,GTK_TYPE_BOX)87 G_DEFINE_TYPE_WITH_PRIVATE (PlumaPanel, pluma_panel, GTK_TYPE_BOX)
88 
89 static void
90 pluma_panel_finalize (GObject *obj)
91 {
92 	if (G_OBJECT_CLASS (pluma_panel_parent_class)->finalize)
93 		(*G_OBJECT_CLASS (pluma_panel_parent_class)->finalize) (obj);
94 }
95 
96 static void
pluma_panel_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)97 pluma_panel_get_property (GObject    *object,
98 			  guint       prop_id,
99 			  GValue     *value,
100 			  GParamSpec *pspec)
101 {
102 	PlumaPanel *panel = PLUMA_PANEL (object);
103 
104 	switch (prop_id)
105 	{
106 		case PROP_ORIENTATION:
107 			g_value_set_enum(value, panel->priv->orientation);
108 			break;
109 		default:
110 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111 			break;
112 	}
113 }
114 
115 static void
pluma_panel_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)116 pluma_panel_set_property (GObject      *object,
117 			  guint         prop_id,
118 			  const GValue *value,
119 			  GParamSpec   *pspec)
120 {
121 	PlumaPanel *panel = PLUMA_PANEL (object);
122 
123 	switch (prop_id)
124 	{
125 		case PROP_ORIENTATION:
126 			panel->priv->orientation = g_value_get_enum (value);
127 			break;
128 		default:
129 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130 			break;
131 	}
132 }
133 
134 static void
pluma_panel_close(PlumaPanel * panel)135 pluma_panel_close (PlumaPanel *panel)
136 {
137 	gtk_widget_hide (GTK_WIDGET (panel));
138 }
139 
140 static void
pluma_panel_focus_document(PlumaPanel * panel)141 pluma_panel_focus_document (PlumaPanel *panel)
142 {
143 	GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (panel));
144 	if (gtk_widget_is_toplevel (toplevel) && PLUMA_IS_WINDOW (toplevel))
145 	{
146 		PlumaView *view;
147 
148 		view = pluma_window_get_active_view (PLUMA_WINDOW (toplevel));
149 		if (view != NULL)
150 			gtk_widget_grab_focus (GTK_WIDGET (view));
151 	}
152 }
153 
154 static void
pluma_panel_grab_focus(GtkWidget * w)155 pluma_panel_grab_focus (GtkWidget *w)
156 {
157 	gint n;
158 	GtkWidget *tab;
159 	PlumaPanel *panel = PLUMA_PANEL (w);
160 
161 	n = gtk_notebook_get_current_page (GTK_NOTEBOOK (panel->priv->notebook));
162 	if (n == -1)
163 		return;
164 
165 	tab = gtk_notebook_get_nth_page (GTK_NOTEBOOK (panel->priv->notebook),
166 					 n);
167 	g_return_if_fail (tab != NULL);
168 
169 	gtk_widget_grab_focus (tab);
170 }
171 
172 static void
pluma_panel_class_init(PlumaPanelClass * klass)173 pluma_panel_class_init (PlumaPanelClass *klass)
174 {
175 	GtkBindingSet *binding_set;
176 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
177 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
178 
179 	object_class->constructor = pluma_panel_constructor;
180 	object_class->finalize = pluma_panel_finalize;
181 	object_class->get_property = pluma_panel_get_property;
182 	object_class->set_property = pluma_panel_set_property;
183 
184 	g_object_class_install_property (object_class,
185 					 PROP_ORIENTATION,
186 					 g_param_spec_enum ("panel-orientation",
187 							    "Panel Orientation",
188 							    "The panel's orientation",
189 							    GTK_TYPE_ORIENTATION,
190 							    GTK_ORIENTATION_VERTICAL,
191 							    G_PARAM_WRITABLE |
192 							    G_PARAM_READABLE |
193 							    G_PARAM_CONSTRUCT_ONLY |
194 							    G_PARAM_STATIC_STRINGS));
195 
196 	widget_class->grab_focus = pluma_panel_grab_focus;
197 
198 	klass->close = pluma_panel_close;
199 	klass->focus_document = pluma_panel_focus_document;
200 
201 	signals[ITEM_ADDED] =
202 		g_signal_new ("item_added",
203 			      G_OBJECT_CLASS_TYPE (klass),
204 			      G_SIGNAL_RUN_FIRST,
205 			      G_STRUCT_OFFSET (PlumaPanelClass, item_added),
206 			      NULL, NULL,
207 			      g_cclosure_marshal_VOID__OBJECT,
208 			      G_TYPE_NONE,
209 			      1,
210 			      GTK_TYPE_WIDGET);
211 	signals[ITEM_REMOVED] =
212 		g_signal_new ("item_removed",
213 			      G_OBJECT_CLASS_TYPE (klass),
214 			      G_SIGNAL_RUN_FIRST,
215 			      G_STRUCT_OFFSET (PlumaPanelClass, item_removed),
216 			      NULL, NULL,
217 			      g_cclosure_marshal_VOID__OBJECT,
218 			      G_TYPE_NONE,
219 			      1,
220 			      GTK_TYPE_WIDGET);
221 
222 	/* Keybinding signals */
223 	signals[CLOSE] =
224 		g_signal_new ("close",
225 			      G_OBJECT_CLASS_TYPE (klass),
226 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
227 			      G_STRUCT_OFFSET (PlumaPanelClass, close),
228 		  	      NULL, NULL,
229 		  	      g_cclosure_marshal_VOID__VOID,
230 			      G_TYPE_NONE, 0);
231 	signals[FOCUS_DOCUMENT] =
232 		g_signal_new ("focus_document",
233 			      G_OBJECT_CLASS_TYPE (klass),
234 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
235 			      G_STRUCT_OFFSET (PlumaPanelClass, focus_document),
236 		  	      NULL, NULL,
237 		  	      g_cclosure_marshal_VOID__VOID,
238 			      G_TYPE_NONE, 0);
239 	binding_set = gtk_binding_set_by_class (klass);
240 
241 	gtk_binding_entry_add_signal (binding_set,
242 				      GDK_KEY_Escape,
243 				      0,
244 				      "close",
245 				      0);
246 	gtk_binding_entry_add_signal (binding_set,
247 				      GDK_KEY_Return,
248 				      GDK_CONTROL_MASK,
249 				      "focus_document",
250 				      0);
251 }
252 
253 /* This is ugly, since it supports only known
254  * storage types of GtkImage, otherwise fall back
255  * to the empty icon.
256  * See http://bugzilla.gnome.org/show_bug.cgi?id=317520.
257  */
258 static void
set_gtk_image_from_gtk_image(GtkImage * image,GtkImage * source)259 set_gtk_image_from_gtk_image (GtkImage *image,
260 			      GtkImage *source)
261 {
262 	switch (gtk_image_get_storage_type (source))
263 	{
264 	case GTK_IMAGE_EMPTY:
265 		gtk_image_clear (image);
266 		break;
267 	case GTK_IMAGE_PIXBUF:
268 		{
269 			GdkPixbuf *pb;
270 
271 			pb = gtk_image_get_pixbuf (source);
272 			gtk_image_set_from_pixbuf (image, pb);
273 		}
274 		break;
275 	case GTK_IMAGE_ANIMATION:
276 		{
277 			GdkPixbufAnimation *a;
278 
279 			a = gtk_image_get_animation (source);
280 			gtk_image_set_from_animation (image, a);
281 		}
282 		break;
283 	case GTK_IMAGE_ICON_NAME:
284 		{
285 			const gchar *n;
286 			GtkIconSize s;
287 
288 			gtk_image_get_icon_name (source, &n, &s);
289 			gtk_image_set_from_icon_name (image, n, s);
290 		}
291 		break;
292 	default:
293 		gtk_image_set_from_icon_name (image,
294 		                              "text-x-generic",
295 		                              GTK_ICON_SIZE_MENU);
296 	}
297 }
298 
299 static void
sync_title(PlumaPanel * panel,PlumaPanelItem * item)300 sync_title (PlumaPanel     *panel,
301 	    PlumaPanelItem *item)
302 {
303 	if (panel->priv->orientation != GTK_ORIENTATION_VERTICAL)
304 		return;
305 
306 	if (item != NULL)
307 	{
308 		gtk_label_set_text (GTK_LABEL (panel->priv->title_label),
309 				    item->name);
310 
311 		set_gtk_image_from_gtk_image (GTK_IMAGE (panel->priv->title_image),
312 					      GTK_IMAGE (item->icon));
313 	}
314 	else
315 	{
316 		gtk_label_set_text (GTK_LABEL (panel->priv->title_label),
317 				    _("Empty"));
318 
319 		gtk_image_set_from_icon_name (GTK_IMAGE (panel->priv->title_image),
320 		                              "text-x-generic",
321 		                              GTK_ICON_SIZE_MENU);
322 	}
323 }
324 
325 static void
notebook_page_changed(GtkNotebook * notebook,GtkWidget * page,guint page_num,PlumaPanel * panel)326 notebook_page_changed (GtkNotebook     *notebook,
327                        GtkWidget       *page,
328                        guint            page_num,
329                        PlumaPanel      *panel)
330 {
331 	GtkWidget *item;
332 	PlumaPanelItem *data;
333 
334 	item = gtk_notebook_get_nth_page (notebook, page_num);
335 	g_return_if_fail (item != NULL);
336 
337 	data = (PlumaPanelItem *)g_object_get_data (G_OBJECT (item),
338 						    PANEL_ITEM_KEY);
339 	g_return_if_fail (data != NULL);
340 
341 	sync_title (panel, data);
342 }
343 
344 static void
panel_show(PlumaPanel * panel,gpointer user_data)345 panel_show (PlumaPanel *panel,
346 	    gpointer    user_data)
347 {
348 	gint page;
349 	GtkNotebook *nb;
350 
351 	nb = GTK_NOTEBOOK (panel->priv->notebook);
352 
353 	page = gtk_notebook_get_current_page (nb);
354 
355 	if (page != -1)
356 		notebook_page_changed (nb, NULL, page, panel);
357 }
358 
359 static void
pluma_panel_init(PlumaPanel * panel)360 pluma_panel_init (PlumaPanel *panel)
361 {
362 	panel->priv = pluma_panel_get_instance_private (panel);
363 
364 	gtk_orientable_set_orientation (GTK_ORIENTABLE (panel), GTK_ORIENTATION_VERTICAL);
365 }
366 
367 static void
close_button_clicked_cb(GtkWidget * widget,GtkWidget * panel)368 close_button_clicked_cb (GtkWidget *widget,
369 			 GtkWidget *panel)
370 {
371 	gtk_widget_hide (panel);
372 }
373 
374 static GtkWidget *
create_close_button(PlumaPanel * panel)375 create_close_button (PlumaPanel *panel)
376 {
377 	GtkWidget *button;
378 
379 	button = pluma_close_button_new ();
380 
381 	gtk_widget_set_tooltip_text (button, _("Hide panel"));
382 
383 	g_signal_connect (button,
384 			  "clicked",
385 			  G_CALLBACK (close_button_clicked_cb),
386 			  panel);
387 
388 	return button;
389 }
390 
391 static void
build_notebook_for_panel(PlumaPanel * panel)392 build_notebook_for_panel (PlumaPanel *panel)
393 {
394 	/* Create the panel notebook */
395 	panel->priv->notebook = gtk_notebook_new ();
396 
397 	gtk_notebook_set_tab_pos (GTK_NOTEBOOK (panel->priv->notebook),
398 				  GTK_POS_BOTTOM);
399 	gtk_notebook_set_scrollable (GTK_NOTEBOOK (panel->priv->notebook),
400 				     TRUE);
401 	gtk_notebook_popup_enable (GTK_NOTEBOOK (panel->priv->notebook));
402 
403 	gtk_widget_show (GTK_WIDGET (panel->priv->notebook));
404 
405 	g_signal_connect (panel->priv->notebook,
406 			  "switch-page",
407 			  G_CALLBACK (notebook_page_changed),
408 			  panel);
409 }
410 
411 static void
build_horizontal_panel(PlumaPanel * panel)412 build_horizontal_panel (PlumaPanel *panel)
413 {
414 	GtkWidget *box;
415 	GtkWidget *sidebar;
416 	GtkWidget *close_button;
417 
418 	box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
419 
420 	gtk_box_pack_start (GTK_BOX (box),
421 			    panel->priv->notebook,
422 			    TRUE,
423 			    TRUE,
424 			    0);
425 
426 	/* Toolbar, close button and first separator */
427 	sidebar = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
428 	gtk_container_set_border_width (GTK_CONTAINER (sidebar), 4);
429 
430 	gtk_box_pack_start (GTK_BOX (box),
431 			    sidebar,
432 			    FALSE,
433 			    FALSE,
434 			    0);
435 
436 	close_button = create_close_button (panel);
437 
438 	gtk_box_pack_start (GTK_BOX (sidebar),
439 			    close_button,
440 			    FALSE,
441 			    FALSE,
442 			    0);
443 
444 	gtk_widget_show_all (box);
445 
446 	gtk_box_pack_start (GTK_BOX (panel),
447 			    box,
448 			    TRUE,
449 			    TRUE,
450 			    0);
451 }
452 
453 static void
build_vertical_panel(PlumaPanel * panel)454 build_vertical_panel (PlumaPanel *panel)
455 {
456 	GtkWidget *close_button;
457 	GtkWidget *title_hbox;
458 	GtkWidget *icon_name_hbox;
459 	GtkWidget *dummy_label;
460 
461 	/* Create title hbox */
462 	title_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
463 	gtk_container_set_border_width (GTK_CONTAINER (title_hbox), 5);
464 
465 	gtk_box_pack_start (GTK_BOX (panel), title_hbox, FALSE, FALSE, 0);
466 
467 	icon_name_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
468 	gtk_box_pack_start (GTK_BOX (title_hbox),
469 			    icon_name_hbox,
470 			    TRUE,
471 			    TRUE,
472 			    0);
473 
474 	panel->priv->title_image =
475 				gtk_image_new_from_icon_name ("text-x-generic",
476 				                              GTK_ICON_SIZE_MENU);
477 	gtk_box_pack_start (GTK_BOX (icon_name_hbox),
478 			    panel->priv->title_image,
479 			    FALSE,
480 			    TRUE,
481 			    0);
482 
483 	dummy_label = gtk_label_new (" ");
484 
485 	gtk_box_pack_start (GTK_BOX (icon_name_hbox),
486 			    dummy_label,
487 			    FALSE,
488 			    FALSE,
489 			    0);
490 
491 	panel->priv->title_label = gtk_label_new (_("Empty"));
492 	gtk_label_set_xalign (GTK_LABEL (panel->priv->title_label), 0.0);
493 	gtk_label_set_ellipsize(GTK_LABEL (panel->priv->title_label), PANGO_ELLIPSIZE_END);
494 
495 	gtk_box_pack_start (GTK_BOX (icon_name_hbox),
496 			    panel->priv->title_label,
497 			    TRUE,
498 			    TRUE,
499 			    0);
500 
501 	close_button = create_close_button (panel);
502 
503 	gtk_box_pack_start (GTK_BOX (title_hbox),
504 			    close_button,
505 			    FALSE,
506 			    FALSE,
507 			    0);
508 
509 	gtk_widget_show_all (title_hbox);
510 
511 	gtk_box_pack_start (GTK_BOX (panel),
512 			    panel->priv->notebook,
513 			    TRUE,
514 			    TRUE,
515 			    0);
516 }
517 
518 static GObject *
pluma_panel_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)519 pluma_panel_constructor (GType type,
520 			 guint n_construct_properties,
521 			 GObjectConstructParam *construct_properties)
522 {
523 
524 	/* Invoke parent constructor. */
525 	PlumaPanelClass *klass = PLUMA_PANEL_CLASS (g_type_class_peek (PLUMA_TYPE_PANEL));
526 	GObjectClass *parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
527 	GObject *obj = parent_class->constructor (type,
528 						  n_construct_properties,
529 						  construct_properties);
530 
531 	/* Build the panel, now that we know the orientation
532 			   (_init has been called previously) */
533 	PlumaPanel *panel = PLUMA_PANEL (obj);
534 
535 	build_notebook_for_panel (panel);
536   	if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
537   		build_horizontal_panel (panel);
538 	else
539 		build_vertical_panel (panel);
540 
541 	g_signal_connect (panel,
542 			  "show",
543 			  G_CALLBACK (panel_show),
544 			  NULL);
545 
546 	return obj;
547 }
548 
549 /**
550  * pluma_panel_new:
551  * @orientation: a #GtkOrientation
552  *
553  * Creates a new #PlumaPanel with the given @orientation. You shouldn't create
554  * a new panel use pluma_window_get_side_panel() or pluma_window_get_bottom_panel()
555  * instead.
556  *
557  * Returns: a new #PlumaPanel object.
558  */
559 GtkWidget *
pluma_panel_new(GtkOrientation orientation)560 pluma_panel_new (GtkOrientation orientation)
561 {
562 	return GTK_WIDGET (g_object_new (PLUMA_TYPE_PANEL, "panel-orientation", orientation, NULL));
563 }
564 
565 static GtkWidget *
build_tab_label(PlumaPanel * panel,GtkWidget * item,const gchar * name,GtkWidget * icon)566 build_tab_label (PlumaPanel  *panel,
567 		 GtkWidget   *item,
568 		 const gchar *name,
569 		 GtkWidget   *icon)
570 {
571 	GtkWidget *hbox, *label_hbox, *label_ebox;
572 	GtkWidget *label;
573 
574 	/* set hbox spacing and label padding (see below) so that there's an
575 	 * equal amount of space around the label */
576 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
577 
578 	label_ebox = gtk_event_box_new ();
579 	gtk_event_box_set_visible_window (GTK_EVENT_BOX (label_ebox), FALSE);
580 	gtk_box_pack_start (GTK_BOX (hbox), label_ebox, TRUE, TRUE, 0);
581 
582 	label_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
583 	gtk_container_add (GTK_CONTAINER (label_ebox), label_hbox);
584 
585 	/* setup icon */
586 	gtk_box_pack_start (GTK_BOX (label_hbox), icon, FALSE, FALSE, 0);
587 
588 	/* setup label */
589 	label = gtk_label_new (name);
590 	gtk_label_set_xalign (GTK_LABEL (label), 0.0);
591 	gtk_widget_set_margin_start (label, 0);
592 	gtk_widget_set_margin_end (label, 0);
593 	gtk_widget_set_margin_top (label, 0);
594 	gtk_widget_set_margin_bottom (label, 0);
595 	gtk_box_pack_start (GTK_BOX (label_hbox), label, TRUE, TRUE, 0);
596 
597 	gtk_widget_set_tooltip_text (label_ebox, name);
598 
599 	gtk_widget_show_all (hbox);
600 
601 	if (panel->priv->orientation == GTK_ORIENTATION_VERTICAL)
602 		gtk_widget_hide(label);
603 
604 	g_object_set_data (G_OBJECT (item), "label", label);
605 	g_object_set_data (G_OBJECT (item), "hbox", hbox);
606 
607 	return hbox;
608 }
609 
610 /**
611  * pluma_panel_add_item:
612  * @panel: a #PlumaPanel
613  * @item: the #GtkWidget to add to the @panel
614  * @name: the name to be shown in the @panel
615  * @image: the image to be shown in the @panel
616  *
617  * Adds a new item to the @panel.
618  */
619 void
pluma_panel_add_item(PlumaPanel * panel,GtkWidget * item,const gchar * name,GtkWidget * image)620 pluma_panel_add_item (PlumaPanel  *panel,
621 		      GtkWidget   *item,
622 		      const gchar *name,
623 		      GtkWidget   *image)
624 {
625 	PlumaPanelItem *data;
626 	GtkWidget *tab_label;
627 	GtkWidget *menu_label;
628 	gint w, h;
629 
630 	g_return_if_fail (PLUMA_IS_PANEL (panel));
631 	g_return_if_fail (GTK_IS_WIDGET (item));
632 	g_return_if_fail (name != NULL);
633 	g_return_if_fail (image == NULL || GTK_IS_IMAGE (image));
634 
635 	data = g_new (PlumaPanelItem, 1);
636 
637 	data->name = g_strdup (name);
638 
639 	if (image == NULL)
640 	{
641 		/* default to empty */
642 		data->icon = gtk_image_new_from_icon_name ("text-x-generic",
643 		                                           GTK_ICON_SIZE_MENU);
644 	}
645 	else
646 	{
647 		data->icon = image;
648 	}
649 
650 	gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
651 	gtk_widget_set_size_request (data->icon, w, h);
652 
653 	g_object_set_data (G_OBJECT (item),
654 		           PANEL_ITEM_KEY,
655 		           data);
656 
657 	tab_label = build_tab_label (panel, item, data->name, data->icon);
658 
659 	menu_label = gtk_label_new (name);
660 	gtk_label_set_xalign (GTK_LABEL (menu_label), 0.0);
661 
662 	if (!gtk_widget_get_visible (item))
663 		gtk_widget_show (item);
664 
665 	gtk_notebook_append_page_menu (GTK_NOTEBOOK (panel->priv->notebook),
666 				       item,
667 				       tab_label,
668 				       menu_label);
669 
670 	g_signal_emit (G_OBJECT (panel), signals[ITEM_ADDED], 0, item);
671 }
672 
673 /**
674  * pluma_panel_add_item_with_icon:
675  * @panel: a #PlumaPanel
676  * @item: the #GtkWidget to add to the @panel
677  * @name: the name to be shown in the @panel
678  * @icon_name: a icon name
679  *
680  * Same as pluma_panel_add_item() but using an image from icon name.
681  */
682 void
pluma_panel_add_item_with_icon(PlumaPanel * panel,GtkWidget * item,const gchar * name,const gchar * icon_name)683 pluma_panel_add_item_with_icon (PlumaPanel  *panel,
684 				GtkWidget   *item,
685 				const gchar *name,
686 				const gchar *icon_name)
687 {
688 	GtkWidget *icon = NULL;
689 
690 	if (icon_name != NULL)
691 	{
692 		icon = gtk_image_new_from_icon_name (icon_name,
693 		                                     GTK_ICON_SIZE_MENU);
694 	}
695 
696 	pluma_panel_add_item (panel, item, name, icon);
697 }
698 
699 /**
700  * pluma_panel_remove_item:
701  * @panel: a #PlumaPanel
702  * @item: the item to be removed from the panel
703  *
704  * Removes the widget @item from the panel if it is in the @panel and returns
705  * %TRUE if there was not any problem.
706  *
707  * Returns: %TRUE if it was well removed.
708  */
709 gboolean
pluma_panel_remove_item(PlumaPanel * panel,GtkWidget * item)710 pluma_panel_remove_item (PlumaPanel *panel,
711 			 GtkWidget  *item)
712 {
713 	PlumaPanelItem *data;
714 	gint page_num;
715 
716 	g_return_val_if_fail (PLUMA_IS_PANEL (panel), FALSE);
717 	g_return_val_if_fail (GTK_IS_WIDGET (item), FALSE);
718 
719 	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (panel->priv->notebook),
720 					  item);
721 
722 	if (page_num == -1)
723 		return FALSE;
724 
725 	data = (PlumaPanelItem *)g_object_get_data (G_OBJECT (item),
726 					            PANEL_ITEM_KEY);
727 	g_return_val_if_fail (data != NULL, FALSE);
728 
729 	g_free (data->name);
730 	g_free (data);
731 
732 	g_object_set_data (G_OBJECT (item),
733 		           PANEL_ITEM_KEY,
734 		           NULL);
735 
736 	/* ref the item to keep it alive during signal emission */
737 	g_object_ref (G_OBJECT (item));
738 
739 	gtk_notebook_remove_page (GTK_NOTEBOOK (panel->priv->notebook),
740 				  page_num);
741 
742 	/* if we removed all the pages, reset the title */
743 	if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (panel->priv->notebook)) == 0)
744 		sync_title (panel, NULL);
745 
746 	g_signal_emit (G_OBJECT (panel), signals[ITEM_REMOVED], 0, item);
747 
748 	g_object_unref (G_OBJECT (item));
749 
750 	return TRUE;
751 }
752 
753 /**
754  * pluma_panel_activate_item:
755  * @panel: a #PlumaPanel
756  * @item: the item to be activated
757  *
758  * Switches to the page that contains @item.
759  *
760  * Returns: %TRUE if it was activated
761  */
762 gboolean
pluma_panel_activate_item(PlumaPanel * panel,GtkWidget * item)763 pluma_panel_activate_item (PlumaPanel *panel,
764 			   GtkWidget  *item)
765 {
766 	gint page_num;
767 
768 	g_return_val_if_fail (PLUMA_IS_PANEL (panel), FALSE);
769 	g_return_val_if_fail (GTK_IS_WIDGET (item), FALSE);
770 
771 	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (panel->priv->notebook),
772 					  item);
773 
774 	if (page_num == -1)
775 		return FALSE;
776 
777 	gtk_notebook_set_current_page (GTK_NOTEBOOK (panel->priv->notebook),
778 				       page_num);
779 
780 	return TRUE;
781 }
782 
783 /**
784  * pluma_panel_item_is_active:
785  * @panel: a #PlumaPanel
786  * @item: a #GtkWidget
787  *
788  * Returns whether @item is the active widget in @panel
789  *
790  * Returns: %TRUE if @item is the active widget
791  */
792 gboolean
pluma_panel_item_is_active(PlumaPanel * panel,GtkWidget * item)793 pluma_panel_item_is_active (PlumaPanel *panel,
794 			    GtkWidget  *item)
795 {
796 	gint cur_page;
797 	gint page_num;
798 
799 	g_return_val_if_fail (PLUMA_IS_PANEL (panel), FALSE);
800 	g_return_val_if_fail (GTK_IS_WIDGET (item), FALSE);
801 
802 	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (panel->priv->notebook),
803 					  item);
804 
805 	if (page_num == -1)
806 		return FALSE;
807 
808 	cur_page = gtk_notebook_get_current_page (
809 				GTK_NOTEBOOK (panel->priv->notebook));
810 
811 	return (page_num == cur_page);
812 }
813 
814 /**
815  * pluma_panel_get_orientation:
816  * @panel: a #PlumaPanel
817  *
818  * Gets the orientation of the @panel.
819  *
820  * Returns: the #GtkOrientation of #PlumaPanel
821  */
822 GtkOrientation
pluma_panel_get_orientation(PlumaPanel * panel)823 pluma_panel_get_orientation (PlumaPanel *panel)
824 {
825 	g_return_val_if_fail (PLUMA_IS_PANEL (panel), GTK_ORIENTATION_VERTICAL);
826 
827 	return panel->priv->orientation;
828 }
829 
830 /**
831  * pluma_panel_get_n_items:
832  * @panel: a #PlumaPanel
833  *
834  * Gets the number of items in a @panel.
835  *
836  * Returns: the number of items contained in #PlumaPanel
837  */
838 gint
pluma_panel_get_n_items(PlumaPanel * panel)839 pluma_panel_get_n_items (PlumaPanel *panel)
840 {
841 	g_return_val_if_fail (PLUMA_IS_PANEL (panel), -1);
842 
843 	return gtk_notebook_get_n_pages (GTK_NOTEBOOK (panel->priv->notebook));
844 }
845 
846 gint
_pluma_panel_get_active_item_id(PlumaPanel * panel)847 _pluma_panel_get_active_item_id (PlumaPanel *panel)
848 {
849 	gint cur_page;
850 	GtkWidget *item;
851 	PlumaPanelItem *data;
852 
853 	g_return_val_if_fail (PLUMA_IS_PANEL (panel), 0);
854 
855 	cur_page = gtk_notebook_get_current_page (
856 				GTK_NOTEBOOK (panel->priv->notebook));
857 	if (cur_page == -1)
858 		return 0;
859 
860 	item = gtk_notebook_get_nth_page (
861 				GTK_NOTEBOOK (panel->priv->notebook),
862 				cur_page);
863 
864 	/* FIXME: for now we use as the hash of the name as id.
865 	 * However the name is not guaranteed to be unique and
866 	 * it is a translated string, so it's subotimal, but should
867 	 * be good enough for now since we don't want to add an
868 	 * ad hoc id argument.
869 	 */
870 
871 	data = (PlumaPanelItem *)g_object_get_data (G_OBJECT (item),
872 					            PANEL_ITEM_KEY);
873 	g_return_val_if_fail (data != NULL, 0);
874 
875 	return g_str_hash (data->name);
876 }
877 
878 void
_pluma_panel_set_active_item_by_id(PlumaPanel * panel,gint id)879 _pluma_panel_set_active_item_by_id (PlumaPanel *panel,
880 				    gint        id)
881 {
882 	gint n, i;
883 
884 	g_return_if_fail (PLUMA_IS_PANEL (panel));
885 
886 	if (id == 0)
887 		return;
888 
889 	n = gtk_notebook_get_n_pages (
890 				GTK_NOTEBOOK (panel->priv->notebook));
891 
892 	for (i = 0; i < n; i++)
893 	{
894 		GtkWidget *item;
895 		PlumaPanelItem *data;
896 
897 		item = gtk_notebook_get_nth_page (
898 				GTK_NOTEBOOK (panel->priv->notebook), i);
899 
900 		data = (PlumaPanelItem *)g_object_get_data (G_OBJECT (item),
901 						            PANEL_ITEM_KEY);
902 		g_return_if_fail (data != NULL);
903 
904 		if (g_str_hash (data->name) == id)
905 		{
906 			gtk_notebook_set_current_page (
907 				GTK_NOTEBOOK (panel->priv->notebook), i);
908 
909 			return;
910 		}
911 	}
912 }
913