1 /*
2  * gedit-multi-notebook.c
3  * This file is part of gedit
4  *
5  * Copyright (C) 2010 - Ignacio Casal Quinteiro
6  *
7  * gedit 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.
11  *
12  * gedit 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.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with gedit; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA  02110-1301  USA
21  */
22 
23 #include "gedit-multi-notebook.h"
24 
25 #include "gedit-enum-types-private.h"
26 #include "gedit-settings.h"
27 #include "gedit-tab-private.h"
28 #include "gedit-tab.h"
29 
30 struct _GeditMultiNotebookPrivate
31 {
32 	GtkWidget *active_notebook;
33 	GList     *notebooks;
34 	gint       total_tabs;
35 
36 	GeditTab  *active_tab;
37 
38 	GeditNotebookShowTabsModeType show_tabs_mode;
39 	GSettings *ui_settings;
40 
41 	guint      show_tabs : 1;
42 	guint      removing_notebook : 1;
43 };
44 
45 enum
46 {
47 	PROP_0,
48 	PROP_ACTIVE_NOTEBOOK,
49 	PROP_ACTIVE_TAB,
50 	PROP_SHOW_TABS_MODE,
51 	LAST_PROP
52 };
53 
54 static GParamSpec *properties[LAST_PROP];
55 
56 enum
57 {
58 	NOTEBOOK_ADDED,
59 	NOTEBOOK_REMOVED,
60 	TAB_ADDED,
61 	TAB_REMOVED,
62 	SWITCH_TAB,
63 	TAB_CLOSE_REQUEST,
64 	CREATE_WINDOW,
65 	PAGE_REORDERED,
66 	SHOW_POPUP_MENU,
67 	LAST_SIGNAL
68 };
69 
70 static guint signals[LAST_SIGNAL];
71 
72 G_DEFINE_TYPE_WITH_PRIVATE (GeditMultiNotebook, gedit_multi_notebook, GTK_TYPE_GRID)
73 
74 static void	remove_notebook		(GeditMultiNotebook *mnb,
75 					 GtkWidget          *notebook);
76 
77 static void	update_tabs_visibility	(GeditMultiNotebook *mnb);
78 
79 static void
gedit_multi_notebook_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)80 gedit_multi_notebook_get_property (GObject    *object,
81 				   guint       prop_id,
82 				   GValue     *value,
83 				   GParamSpec *pspec)
84 {
85 	GeditMultiNotebook *mnb = GEDIT_MULTI_NOTEBOOK (object);
86 
87 	switch (prop_id)
88 	{
89 		case PROP_ACTIVE_NOTEBOOK:
90 			g_value_set_object (value,
91 					    mnb->priv->active_notebook);
92 			break;
93 		case PROP_ACTIVE_TAB:
94 			g_value_set_object (value,
95 					    mnb->priv->active_tab);
96 			break;
97 		case PROP_SHOW_TABS_MODE:
98 			g_value_set_enum (value,
99 					  mnb->priv->show_tabs_mode);
100 			break;
101 		default:
102 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
103 			break;
104 	}
105 }
106 
107 static void
gedit_multi_notebook_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)108 gedit_multi_notebook_set_property (GObject      *object,
109 				   guint         prop_id,
110 				   const GValue *value,
111 				   GParamSpec   *pspec)
112 {
113 	GeditMultiNotebook *mnb = GEDIT_MULTI_NOTEBOOK (object);
114 
115 	switch (prop_id)
116 	{
117 		case PROP_SHOW_TABS_MODE:
118 			mnb->priv->show_tabs_mode = g_value_get_enum (value);
119 			update_tabs_visibility (mnb);
120 			break;
121 		default:
122 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
123 			break;
124 	}
125 }
126 
127 static void
gedit_multi_notebook_dispose(GObject * object)128 gedit_multi_notebook_dispose (GObject *object)
129 {
130 	GeditMultiNotebook *mnb = GEDIT_MULTI_NOTEBOOK (object);
131 
132 	g_clear_object (&mnb->priv->ui_settings);
133 
134 	G_OBJECT_CLASS (gedit_multi_notebook_parent_class)->dispose (object);
135 }
136 
137 static void
gedit_multi_notebook_finalize(GObject * object)138 gedit_multi_notebook_finalize (GObject *object)
139 {
140 	GeditMultiNotebook *mnb = GEDIT_MULTI_NOTEBOOK (object);
141 
142 	g_list_free (mnb->priv->notebooks);
143 
144 	G_OBJECT_CLASS (gedit_multi_notebook_parent_class)->finalize (object);
145 }
146 
147 static void
gedit_multi_notebook_class_init(GeditMultiNotebookClass * klass)148 gedit_multi_notebook_class_init (GeditMultiNotebookClass *klass)
149 {
150 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
151 
152 	object_class->dispose = gedit_multi_notebook_dispose;
153 	object_class->finalize = gedit_multi_notebook_finalize;
154 	object_class->get_property = gedit_multi_notebook_get_property;
155 	object_class->set_property = gedit_multi_notebook_set_property;
156 
157 	properties[PROP_ACTIVE_NOTEBOOK] =
158 		g_param_spec_object ("active-notebook",
159 		                     "Active Notebook",
160 		                     "The Active Notebook",
161 		                     GEDIT_TYPE_NOTEBOOK,
162 		                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
163 	properties[PROP_ACTIVE_TAB] =
164 		g_param_spec_object ("active-tab",
165 		                     "Active Tab",
166 		                     "The Active Tab",
167 		                     GEDIT_TYPE_TAB,
168 		                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
169 	properties[PROP_SHOW_TABS_MODE] =
170 		g_param_spec_enum ("show-tabs-mode",
171 		                   "Show Tabs Mode",
172 		                   "When tabs should be shown",
173 		                   GEDIT_TYPE_NOTEBOOK_SHOW_TABS_MODE_TYPE,
174 		                   GEDIT_NOTEBOOK_SHOW_TABS_ALWAYS,
175 		                   G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
176 
177 	g_object_class_install_properties (object_class, LAST_PROP, properties);
178 
179 	signals[NOTEBOOK_ADDED] =
180 		g_signal_new ("notebook-added",
181 			      G_OBJECT_CLASS_TYPE (object_class),
182 			      G_SIGNAL_RUN_FIRST,
183 			      G_STRUCT_OFFSET (GeditMultiNotebookClass, notebook_added),
184 			      NULL, NULL, NULL,
185 			      G_TYPE_NONE,
186 			      1,
187 			      GEDIT_TYPE_NOTEBOOK);
188 	signals[NOTEBOOK_REMOVED] =
189 		g_signal_new ("notebook-removed",
190 			      G_OBJECT_CLASS_TYPE (object_class),
191 			      G_SIGNAL_RUN_FIRST,
192 			      G_STRUCT_OFFSET (GeditMultiNotebookClass, notebook_removed),
193 			      NULL, NULL, NULL,
194 			      G_TYPE_NONE,
195 			      1,
196 			      GEDIT_TYPE_NOTEBOOK);
197 	signals[TAB_ADDED] =
198 		g_signal_new ("tab-added",
199 			      G_OBJECT_CLASS_TYPE (object_class),
200 			      G_SIGNAL_RUN_FIRST,
201 			      G_STRUCT_OFFSET (GeditMultiNotebookClass, tab_added),
202 			      NULL, NULL, NULL,
203 			      G_TYPE_NONE,
204 			      2,
205 			      GEDIT_TYPE_NOTEBOOK,
206 			      GEDIT_TYPE_TAB);
207 	signals[TAB_REMOVED] =
208 		g_signal_new ("tab-removed",
209 			      G_OBJECT_CLASS_TYPE (object_class),
210 			      G_SIGNAL_RUN_FIRST,
211 			      G_STRUCT_OFFSET (GeditMultiNotebookClass, tab_removed),
212 			      NULL, NULL, NULL,
213 			      G_TYPE_NONE,
214 			      2,
215 			      GEDIT_TYPE_NOTEBOOK,
216 			      GEDIT_TYPE_TAB);
217 	signals[SWITCH_TAB] =
218 		g_signal_new ("switch-tab",
219 			      G_OBJECT_CLASS_TYPE (object_class),
220 			      G_SIGNAL_RUN_FIRST,
221 			      G_STRUCT_OFFSET (GeditMultiNotebookClass, switch_tab),
222 			      NULL, NULL, NULL,
223 			      G_TYPE_NONE,
224 			      4,
225 			      GEDIT_TYPE_NOTEBOOK,
226 			      GEDIT_TYPE_TAB,
227 			      GEDIT_TYPE_NOTEBOOK,
228 			      GEDIT_TYPE_TAB);
229 	signals[TAB_CLOSE_REQUEST] =
230 		g_signal_new ("tab-close-request",
231 			      G_OBJECT_CLASS_TYPE (object_class),
232 			      G_SIGNAL_RUN_FIRST,
233 			      G_STRUCT_OFFSET (GeditMultiNotebookClass, tab_close_request),
234 			      NULL, NULL, NULL,
235 			      G_TYPE_NONE,
236 			      2,
237 			      GEDIT_TYPE_NOTEBOOK,
238 			      GEDIT_TYPE_TAB);
239 	signals[CREATE_WINDOW] =
240 		g_signal_new ("create-window",
241 		              G_TYPE_FROM_CLASS (object_class),
242 		              G_SIGNAL_RUN_LAST,
243 		              G_STRUCT_OFFSET (GeditMultiNotebookClass, create_window),
244 		              NULL, NULL, NULL,
245 		              GTK_TYPE_NOTEBOOK, 4,
246 		              GEDIT_TYPE_NOTEBOOK, GTK_TYPE_WIDGET,
247 		              G_TYPE_INT, G_TYPE_INT);
248 	signals[PAGE_REORDERED] =
249 		g_signal_new ("page-reordered",
250 		              G_OBJECT_CLASS_TYPE (object_class),
251 		              G_SIGNAL_RUN_FIRST,
252 		              G_STRUCT_OFFSET (GeditMultiNotebookClass, page_reordered),
253 		              NULL, NULL, NULL,
254 		              G_TYPE_NONE,
255 		              3,
256 		              GEDIT_TYPE_NOTEBOOK, GTK_TYPE_WIDGET,
257 		              G_TYPE_INT);
258 	signals[SHOW_POPUP_MENU] =
259 		g_signal_new ("show-popup-menu",
260 			      G_OBJECT_CLASS_TYPE (object_class),
261 			      G_SIGNAL_RUN_FIRST,
262 			      G_STRUCT_OFFSET (GeditMultiNotebookClass, show_popup_menu),
263 			      NULL, NULL, NULL,
264 			      G_TYPE_NONE,
265 			      2,
266 			      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
267 			      GEDIT_TYPE_TAB);
268 }
269 
270 static void
notebook_show_popup_menu(GtkNotebook * notebook,GdkEvent * event,GeditTab * tab,GeditMultiNotebook * mnb)271 notebook_show_popup_menu (GtkNotebook        *notebook,
272                           GdkEvent           *event,
273                           GeditTab           *tab,
274                           GeditMultiNotebook *mnb)
275 {
276 	g_signal_emit (G_OBJECT (mnb), signals[SHOW_POPUP_MENU], 0, event, tab);
277 }
278 
279 static void
notebook_tab_close_request(GeditNotebook * notebook,GeditTab * tab,GeditMultiNotebook * mnb)280 notebook_tab_close_request (GeditNotebook      *notebook,
281 			    GeditTab           *tab,
282 			    GeditMultiNotebook *mnb)
283 {
284 	g_signal_emit (G_OBJECT (mnb), signals[TAB_CLOSE_REQUEST], 0,
285 		       notebook, tab);
286 }
287 
288 static GtkNotebook *
notebook_create_window(GeditNotebook * notebook,GtkWidget * child,gint x,gint y,GeditMultiNotebook * mnb)289 notebook_create_window (GeditNotebook      *notebook,
290 			GtkWidget          *child,
291 			gint                x,
292 			gint                y,
293 			GeditMultiNotebook *mnb)
294 {
295 	GtkNotebook *dest_notebook;
296 
297 	g_signal_emit (G_OBJECT (mnb), signals[CREATE_WINDOW], 0,
298 	               notebook, child, x, y, &dest_notebook);
299 
300 	return dest_notebook;
301 }
302 
303 static void
notebook_page_reordered(GeditNotebook * notebook,GtkWidget * child,guint page_num,GeditMultiNotebook * mnb)304 notebook_page_reordered (GeditNotebook      *notebook,
305 		         GtkWidget          *child,
306 		         guint               page_num,
307 		         GeditMultiNotebook *mnb)
308 {
309 	g_signal_emit (G_OBJECT (mnb), signals[PAGE_REORDERED], 0, notebook,
310 	               child, page_num);
311 }
312 
313 static void
set_active_tab(GeditMultiNotebook * mnb,GeditTab * tab)314 set_active_tab (GeditMultiNotebook *mnb,
315                 GeditTab           *tab)
316 {
317 	mnb->priv->active_tab = tab;
318 	g_object_notify_by_pspec (G_OBJECT (mnb), properties[PROP_ACTIVE_TAB]);
319 }
320 
321 static void
notebook_page_removed(GtkNotebook * notebook,GtkWidget * child,guint page_num,GeditMultiNotebook * mnb)322 notebook_page_removed (GtkNotebook        *notebook,
323 		       GtkWidget          *child,
324 		       guint               page_num,
325 		       GeditMultiNotebook *mnb)
326 {
327 	GeditTab *tab = GEDIT_TAB (child);
328 	guint num_tabs;
329 	gboolean last_notebook;
330 
331 	--mnb->priv->total_tabs;
332 	num_tabs = gtk_notebook_get_n_pages (notebook);
333 	last_notebook = (mnb->priv->notebooks->next == NULL);
334 
335 	if (mnb->priv->total_tabs == 0)
336 	{
337 		set_active_tab (mnb, NULL);
338 	}
339 
340 	g_signal_emit (G_OBJECT (mnb), signals[TAB_REMOVED], 0, notebook, tab);
341 
342 	/* Not last notebook but last tab of the notebook, this means we have
343 	   to remove the current notebook */
344 	if (num_tabs == 0 && !mnb->priv->removing_notebook &&
345 	    !last_notebook)
346 	{
347 		remove_notebook (mnb, GTK_WIDGET (notebook));
348 	}
349 
350 	update_tabs_visibility (mnb);
351 }
352 
353 static void
notebook_page_added(GtkNotebook * notebook,GtkWidget * child,guint page_num,GeditMultiNotebook * mnb)354 notebook_page_added (GtkNotebook        *notebook,
355 		     GtkWidget          *child,
356 		     guint               page_num,
357 		     GeditMultiNotebook *mnb)
358 {
359 	GeditTab *tab = GEDIT_TAB (child);
360 
361 	++mnb->priv->total_tabs;
362 
363 	update_tabs_visibility (mnb);
364 
365 	g_signal_emit (G_OBJECT (mnb), signals[TAB_ADDED], 0, notebook, tab);
366 }
367 
368 static void
notebook_switch_page(GtkNotebook * book,GtkWidget * pg,gint page_num,GeditMultiNotebook * mnb)369 notebook_switch_page (GtkNotebook        *book,
370 		      GtkWidget          *pg,
371 		      gint                page_num,
372 		      GeditMultiNotebook *mnb)
373 {
374 	GeditTab *tab;
375 
376 	/* When we switch a tab from a notebook that it is not the active one
377 	   the switch page is emitted before the set focus, so we do this check
378 	   and we avoid to call switch page twice */
379 	if (GTK_WIDGET (book) != mnb->priv->active_notebook)
380 		return;
381 
382 	/* CHECK: I don't know why but it seems notebook_switch_page is called
383 	two times every time the user change the active tab */
384 	tab = GEDIT_TAB (gtk_notebook_get_nth_page (book, page_num));
385 	if (tab != mnb->priv->active_tab)
386 	{
387 		GeditTab *old_tab;
388 
389 		old_tab = mnb->priv->active_tab;
390 		set_active_tab (mnb, tab);
391 		g_signal_emit (G_OBJECT (mnb), signals[SWITCH_TAB], 0,
392 			       mnb->priv->active_notebook, old_tab,
393 			       book, tab);
394 	}
395 }
396 
397 /* We need to figure out if the any of the internal widget of the notebook
398    has got the focus to set the active notebook */
399 static void
notebook_set_focus(GtkContainer * container,GtkWidget * widget,GeditMultiNotebook * mnb)400 notebook_set_focus (GtkContainer       *container,
401 		    GtkWidget          *widget,
402 		    GeditMultiNotebook *mnb)
403 {
404 	if (GEDIT_IS_NOTEBOOK (container) &&
405 	    GTK_WIDGET (container) != mnb->priv->active_notebook)
406 	{
407 		gint page_num;
408 
409 		mnb->priv->active_notebook = GTK_WIDGET (container);
410 
411 		page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (container));
412 		notebook_switch_page (GTK_NOTEBOOK (container), NULL,
413 				      page_num, mnb);
414 
415 		g_object_notify_by_pspec (G_OBJECT (mnb), properties[PROP_ACTIVE_NOTEBOOK]);
416 	}
417 }
418 
419 static void
show_tabs_changed(GObject * object,GParamSpec * pspec,gpointer * data)420 show_tabs_changed (GObject     *object,
421 		   GParamSpec  *pspec,
422 		   gpointer    *data)
423 {
424 	update_tabs_visibility (GEDIT_MULTI_NOTEBOOK (data));
425 }
426 
427 static void
update_tabs_visibility(GeditMultiNotebook * mnb)428 update_tabs_visibility (GeditMultiNotebook *mnb)
429 {
430 	gboolean show_tabs;
431 	GList *l;
432 
433 	if (mnb->priv->notebooks == NULL)
434 		return;
435 
436 	if (!mnb->priv->show_tabs)
437 	{
438 		show_tabs = FALSE;
439 	}
440 	else if (mnb->priv->notebooks->next == NULL) /* only one notebook */
441 	{
442 		switch (mnb->priv->show_tabs_mode)
443 		{
444 			case GEDIT_NOTEBOOK_SHOW_TABS_NEVER:
445 				show_tabs = FALSE;
446 				break;
447 			case GEDIT_NOTEBOOK_SHOW_TABS_AUTO:
448 				show_tabs = gtk_notebook_get_n_pages (GTK_NOTEBOOK (mnb->priv->notebooks->data)) > 1;
449 				break;
450 			case GEDIT_NOTEBOOK_SHOW_TABS_ALWAYS:
451 			default:
452 				show_tabs = TRUE;
453 				break;
454 		}
455 	}
456 	else
457 	{
458 		show_tabs = (mnb->priv->show_tabs_mode != GEDIT_NOTEBOOK_SHOW_TABS_NEVER);
459 	}
460 
461 	g_signal_handlers_block_by_func (mnb, show_tabs_changed, NULL);
462 
463 	for (l = mnb->priv->notebooks; l != NULL; l = l->next)
464 	{
465 		gtk_notebook_set_show_tabs (GTK_NOTEBOOK (l->data), show_tabs);
466 	}
467 
468 	g_signal_handlers_unblock_by_func (mnb, show_tabs_changed, NULL);
469 }
470 
471 static void
connect_notebook_signals(GeditMultiNotebook * mnb,GtkWidget * notebook)472 connect_notebook_signals (GeditMultiNotebook *mnb,
473 			  GtkWidget          *notebook)
474 {
475 	g_signal_connect (notebook,
476 			  "set-focus-child",
477 			  G_CALLBACK (notebook_set_focus),
478 			  mnb);
479 	g_signal_connect (notebook,
480 			  "page-added",
481 			  G_CALLBACK (notebook_page_added),
482 			  mnb);
483 	g_signal_connect (notebook,
484 			  "page-removed",
485 			  G_CALLBACK (notebook_page_removed),
486 			  mnb);
487 	g_signal_connect (notebook,
488 			  "switch-page",
489 			  G_CALLBACK (notebook_switch_page),
490 			  mnb);
491 	g_signal_connect (notebook,
492 			  "page-reordered",
493 			  G_CALLBACK (notebook_page_reordered),
494 			  mnb);
495 	g_signal_connect (notebook,
496 			  "create-window",
497 			  G_CALLBACK (notebook_create_window),
498 			  mnb);
499 	g_signal_connect (notebook,
500 			  "tab-close-request",
501 			  G_CALLBACK (notebook_tab_close_request),
502 			  mnb);
503 	g_signal_connect (notebook,
504 			  "show-popup-menu",
505 			  G_CALLBACK (notebook_show_popup_menu),
506 			  mnb);
507 	g_signal_connect (notebook,
508 			  "notify::show-tabs",
509 			  G_CALLBACK (show_tabs_changed),
510 			  mnb);
511 }
512 
513 static void
disconnect_notebook_signals(GeditMultiNotebook * mnb,GtkWidget * notebook)514 disconnect_notebook_signals (GeditMultiNotebook *mnb,
515 			     GtkWidget          *notebook)
516 {
517 	g_signal_handlers_disconnect_by_func (notebook, notebook_set_focus,
518 					      mnb);
519 	g_signal_handlers_disconnect_by_func (notebook, notebook_switch_page,
520 					      mnb);
521 	g_signal_handlers_disconnect_by_func (notebook, notebook_page_added,
522 					      mnb);
523 	g_signal_handlers_disconnect_by_func (notebook, notebook_page_removed,
524 					      mnb);
525 	g_signal_handlers_disconnect_by_func (notebook, notebook_page_reordered,
526 					      mnb);
527 	g_signal_handlers_disconnect_by_func (notebook, notebook_create_window,
528 					      mnb);
529 	g_signal_handlers_disconnect_by_func (notebook, notebook_tab_close_request,
530 					      mnb);
531 	g_signal_handlers_disconnect_by_func (notebook, notebook_show_popup_menu,
532 					      mnb);
533 	g_signal_handlers_disconnect_by_func (notebook, show_tabs_changed,
534 					      mnb);
535 }
536 
537 static void
add_notebook(GeditMultiNotebook * mnb,GtkWidget * notebook,gboolean main_container)538 add_notebook (GeditMultiNotebook *mnb,
539 	      GtkWidget          *notebook,
540 	      gboolean            main_container)
541 {
542 	gtk_widget_set_hexpand (notebook, TRUE);
543 	gtk_widget_set_vexpand (notebook, TRUE);
544 
545 	if (main_container)
546 	{
547 		gtk_container_add (GTK_CONTAINER (mnb), notebook);
548 
549 		mnb->priv->notebooks = g_list_append (mnb->priv->notebooks,
550 		                                      notebook);
551 	}
552 	else
553 	{
554 		GtkWidget *paned;
555 		GtkWidget *parent;
556 		GtkAllocation allocation;
557 		GtkWidget *active_notebook = mnb->priv->active_notebook;
558 		gint active_nb_pos;
559 
560 		paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
561 		gtk_widget_show (paned);
562 
563 		/* First we remove the active container from its parent to make
564 		   this we add a ref to it*/
565 		g_object_ref (active_notebook);
566 		parent = gtk_widget_get_parent (active_notebook);
567 		gtk_widget_get_allocation (active_notebook, &allocation);
568 
569 		gtk_container_remove (GTK_CONTAINER (parent), active_notebook);
570 		gtk_container_add (GTK_CONTAINER (parent), paned);
571 
572 		gtk_paned_pack1 (GTK_PANED (paned), active_notebook, TRUE, FALSE);
573 		g_object_unref (active_notebook);
574 
575 		gtk_paned_pack2 (GTK_PANED (paned), notebook, FALSE, FALSE);
576 
577 		/* We need to set the new paned in the right place */
578 		gtk_paned_set_position (GTK_PANED (paned),
579 		                        allocation.width / 2);
580 
581 		active_nb_pos = g_list_index (mnb->priv->notebooks,
582 		                              active_notebook);
583 		mnb->priv->notebooks = g_list_insert (mnb->priv->notebooks,
584 		                                      notebook,
585 		                                      active_nb_pos + 1);
586 	}
587 
588 	gtk_widget_show (notebook);
589 
590 	connect_notebook_signals (mnb, notebook);
591 
592 	g_signal_emit (G_OBJECT (mnb), signals[NOTEBOOK_ADDED], 0, notebook);
593 }
594 
595 static void
remove_notebook(GeditMultiNotebook * mnb,GtkWidget * notebook)596 remove_notebook (GeditMultiNotebook *mnb,
597 		 GtkWidget          *notebook)
598 {
599 	GtkWidget *parent;
600 	GtkWidget *grandpa;
601 	GList *children;
602 	GtkWidget *new_notebook;
603 	GList *current;
604 
605 	if (mnb->priv->notebooks->next == NULL)
606 	{
607 		g_warning ("You are trying to remove the main notebook");
608 		return;
609 	}
610 
611 	current = g_list_find (mnb->priv->notebooks,
612 			       notebook);
613 
614 	if (current->next != NULL)
615 	{
616 		new_notebook = GTK_WIDGET (current->next->data);
617 	}
618 	else
619 	{
620 		new_notebook = GTK_WIDGET (mnb->priv->notebooks->data);
621 	}
622 
623 	parent = gtk_widget_get_parent (notebook);
624 
625 	/* Now we destroy the widget, we get the children of parent and we destroy
626 	  parent too as the parent is an useless paned. Finally we add the child
627 	  into the grand parent */
628 	g_object_ref (notebook);
629 	mnb->priv->removing_notebook = TRUE;
630 
631 	gtk_widget_destroy (notebook);
632 
633 	mnb->priv->notebooks = g_list_remove (mnb->priv->notebooks,
634 					      notebook);
635 
636 	mnb->priv->removing_notebook = FALSE;
637 
638 	children = gtk_container_get_children (GTK_CONTAINER (parent));
639 	if (children->next != NULL)
640 	{
641 		g_warning ("The parent is not a paned");
642 		return;
643 	}
644 	grandpa = gtk_widget_get_parent (parent);
645 
646 	g_object_ref (children->data);
647 	gtk_container_remove (GTK_CONTAINER (parent),
648 			      GTK_WIDGET (children->data));
649 	gtk_widget_destroy (parent);
650 	gtk_container_add (GTK_CONTAINER (grandpa),
651 			   GTK_WIDGET (children->data));
652 	g_object_unref (children->data);
653 	g_list_free (children);
654 
655 	disconnect_notebook_signals (mnb, notebook);
656 
657 	g_signal_emit (G_OBJECT (mnb), signals[NOTEBOOK_REMOVED], 0, notebook);
658 	g_object_unref (notebook);
659 
660 	/* Let's make the active notebook grab the focus */
661 	gtk_widget_grab_focus (new_notebook);
662 }
663 
664 static void
gedit_multi_notebook_init(GeditMultiNotebook * mnb)665 gedit_multi_notebook_init (GeditMultiNotebook *mnb)
666 {
667 	GeditMultiNotebookPrivate *priv;
668 
669 	mnb->priv = gedit_multi_notebook_get_instance_private (mnb);
670 	priv = mnb->priv;
671 
672 	priv->removing_notebook = FALSE;
673 
674 	gtk_orientable_set_orientation (GTK_ORIENTABLE (mnb),
675 	                                GTK_ORIENTATION_VERTICAL);
676 
677 	priv->show_tabs_mode = GEDIT_NOTEBOOK_SHOW_TABS_ALWAYS;
678 	priv->show_tabs = TRUE;
679 
680 	priv->ui_settings = g_settings_new ("org.gnome.gedit.preferences.ui");
681 	g_settings_bind (priv->ui_settings,
682 			 GEDIT_SETTINGS_SHOW_TABS_MODE,
683 			 mnb,
684 			 "show-tabs-mode",
685 			 G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET);
686 
687 	priv->active_notebook = gedit_notebook_new ();
688 	add_notebook (mnb, priv->active_notebook, TRUE);
689 }
690 
691 GeditMultiNotebook *
gedit_multi_notebook_new()692 gedit_multi_notebook_new ()
693 {
694 	return g_object_new (GEDIT_TYPE_MULTI_NOTEBOOK, NULL);
695 }
696 
697 GeditNotebook *
gedit_multi_notebook_get_active_notebook(GeditMultiNotebook * mnb)698 gedit_multi_notebook_get_active_notebook (GeditMultiNotebook *mnb)
699 {
700 	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);
701 
702 	return GEDIT_NOTEBOOK (mnb->priv->active_notebook);
703 }
704 
705 gint
gedit_multi_notebook_get_n_notebooks(GeditMultiNotebook * mnb)706 gedit_multi_notebook_get_n_notebooks (GeditMultiNotebook *mnb)
707 {
708 	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), 0);
709 
710 	return g_list_length (mnb->priv->notebooks);
711 }
712 
713 GeditNotebook *
gedit_multi_notebook_get_nth_notebook(GeditMultiNotebook * mnb,gint notebook_num)714 gedit_multi_notebook_get_nth_notebook (GeditMultiNotebook *mnb,
715 				       gint                notebook_num)
716 {
717 	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);
718 
719 	return g_list_nth_data (mnb->priv->notebooks, notebook_num);
720 }
721 
722 GeditNotebook *
gedit_multi_notebook_get_notebook_for_tab(GeditMultiNotebook * mnb,GeditTab * tab)723 gedit_multi_notebook_get_notebook_for_tab (GeditMultiNotebook *mnb,
724                                            GeditTab           *tab)
725 {
726 	GList *l;
727 	gint page_num;
728 
729 	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);
730 	g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL);
731 
732 	l = mnb->priv->notebooks;
733 
734 	do
735 	{
736 		page_num = gtk_notebook_page_num (GTK_NOTEBOOK (l->data),
737 		                                  GTK_WIDGET (tab));
738 		if (page_num != -1)
739 			break;
740 
741 		l = g_list_next (l);
742 	} while (l != NULL && page_num == -1);
743 
744 	g_return_val_if_fail (page_num != -1, NULL);
745 
746 	return GEDIT_NOTEBOOK (l->data);
747 }
748 
749 gint
gedit_multi_notebook_get_notebook_num(GeditMultiNotebook * mnb,GeditNotebook * notebook)750 gedit_multi_notebook_get_notebook_num (GeditMultiNotebook *mnb,
751 				       GeditNotebook      *notebook)
752 {
753 	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), -1);
754 	g_return_val_if_fail (GEDIT_IS_NOTEBOOK (notebook), -1);
755 
756 	return g_list_index (mnb->priv->notebooks, notebook);
757 }
758 
759 gint
gedit_multi_notebook_get_n_tabs(GeditMultiNotebook * mnb)760 gedit_multi_notebook_get_n_tabs (GeditMultiNotebook *mnb)
761 {
762 	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), 0);
763 
764 	return mnb->priv->total_tabs;
765 }
766 
767 gint
gedit_multi_notebook_get_page_num(GeditMultiNotebook * mnb,GeditTab * tab)768 gedit_multi_notebook_get_page_num (GeditMultiNotebook *mnb,
769 				   GeditTab           *tab)
770 {
771 	GList *l;
772 	gint real_page_num = 0;
773 
774 	for (l = mnb->priv->notebooks; l != NULL; l = g_list_next (l))
775 	{
776 		gint page_num;
777 
778 		page_num = gtk_notebook_page_num (GTK_NOTEBOOK (l->data),
779 						  GTK_WIDGET (tab));
780 
781 		if (page_num != -1)
782 		{
783 			real_page_num += page_num;
784 			break;
785 		}
786 
787 		real_page_num += gtk_notebook_get_n_pages (GTK_NOTEBOOK (l->data));
788 	}
789 
790 	return real_page_num;
791 }
792 
793 GeditTab *
gedit_multi_notebook_get_active_tab(GeditMultiNotebook * mnb)794 gedit_multi_notebook_get_active_tab (GeditMultiNotebook *mnb)
795 {
796 	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);
797 
798 	return (mnb->priv->active_tab == NULL) ?
799 				NULL : GEDIT_TAB (mnb->priv->active_tab);
800 }
801 
802 void
gedit_multi_notebook_set_active_tab(GeditMultiNotebook * mnb,GeditTab * tab)803 gedit_multi_notebook_set_active_tab (GeditMultiNotebook *mnb,
804 				     GeditTab           *tab)
805 {
806 	GList *l;
807 	gint page_num;
808 
809 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
810 	g_return_if_fail (GEDIT_IS_TAB (tab) || tab == NULL);
811 
812 	/* use plain C cast since the active tab can be null */
813 	if (tab == (GeditTab *) mnb->priv->active_tab)
814 	{
815 		return;
816 	}
817 
818 	if (tab == NULL)
819 	{
820 		set_active_tab (mnb, NULL);
821 		return;
822 	}
823 
824 	l = mnb->priv->notebooks;
825 
826 	do
827 	{
828 		page_num = gtk_notebook_page_num (GTK_NOTEBOOK (l->data),
829 		                                  GTK_WIDGET (tab));
830 		if (page_num != -1)
831 			break;
832 
833 		l = g_list_next (l);
834 	} while (l != NULL && page_num == -1);
835 
836 	g_return_if_fail (page_num != -1);
837 
838 	gtk_notebook_set_current_page (GTK_NOTEBOOK (l->data), page_num);
839 
840 	if (GTK_WIDGET (l->data) != mnb->priv->active_notebook)
841 	{
842 		gtk_widget_grab_focus (GTK_WIDGET (l->data));
843 	}
844 }
845 
846 void
gedit_multi_notebook_set_current_page(GeditMultiNotebook * mnb,gint page_num)847 gedit_multi_notebook_set_current_page (GeditMultiNotebook *mnb,
848 				       gint                page_num)
849 {
850 	GList *l;
851 	gint pages = 0;
852 	gint single_num = page_num;
853 
854 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
855 
856 	for (l = mnb->priv->notebooks; l != NULL; l = g_list_next (l))
857 	{
858 		gint p;
859 
860 		p = gtk_notebook_get_n_pages (GTK_NOTEBOOK (l->data));
861 		pages += p;
862 
863 		if ((pages - 1) >= page_num)
864 			break;
865 
866 		single_num -= p;
867 	}
868 
869 	if (l == NULL)
870 		return;
871 
872 	if (GTK_WIDGET (l->data) != mnb->priv->active_notebook)
873 	{
874 		gtk_widget_grab_focus (GTK_WIDGET (l->data));
875 	}
876 
877 	gtk_notebook_set_current_page (GTK_NOTEBOOK (l->data), single_num);
878 }
879 
880 GList *
gedit_multi_notebook_get_all_tabs(GeditMultiNotebook * mnb)881 gedit_multi_notebook_get_all_tabs (GeditMultiNotebook *mnb)
882 {
883 	GList *nbs;
884 	GList *ret = NULL;
885 
886 	g_return_val_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb), NULL);
887 
888 	for (nbs = mnb->priv->notebooks; nbs != NULL; nbs = g_list_next (nbs))
889 	{
890 		GList *l, *children;
891 
892 		children = gtk_container_get_children (GTK_CONTAINER (nbs->data));
893 
894 		for (l = children; l != NULL; l = g_list_next (l))
895 		{
896 			ret = g_list_prepend (ret, l->data);
897 		}
898 
899 		g_list_free (children);
900 	}
901 
902 	ret = g_list_reverse (ret);
903 
904 	return ret;
905 }
906 
907 void
gedit_multi_notebook_close_tabs(GeditMultiNotebook * mnb,const GList * tabs)908 gedit_multi_notebook_close_tabs (GeditMultiNotebook *mnb,
909 				 const GList        *tabs)
910 {
911 	GList *l;
912 
913 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
914 
915 	for (l = (GList *)tabs; l != NULL; l = g_list_next (l))
916 	{
917 		GList *nbs;
918 
919 		for (nbs = mnb->priv->notebooks; nbs != NULL; nbs = g_list_next (nbs))
920 		{
921 			gint n;
922 
923 			n = gtk_notebook_page_num (GTK_NOTEBOOK (nbs->data),
924 			                           GTK_WIDGET (l->data));
925 
926 			if (n != -1)
927 			{
928 				gtk_container_remove (GTK_CONTAINER (nbs->data),
929 				                      GTK_WIDGET (l->data));
930 				break;
931 			}
932 		}
933 	}
934 }
935 
936 /**
937  * gedit_multi_notebook_close_all_tabs:
938  * @mnb: a #GeditMultiNotebook
939  *
940  * Closes all opened tabs.
941  */
942 void
gedit_multi_notebook_close_all_tabs(GeditMultiNotebook * mnb)943 gedit_multi_notebook_close_all_tabs (GeditMultiNotebook *mnb)
944 {
945 	GList *nbs, *l;
946 
947 	g_return_if_fail (GEDIT_MULTI_NOTEBOOK (mnb));
948 
949 	/* We copy the list because the main one is going to have the items
950 	   removed */
951 	nbs = g_list_copy (mnb->priv->notebooks);
952 
953 	for (l = nbs; l != NULL; l = g_list_next (l))
954 	{
955 		gedit_notebook_remove_all_tabs (GEDIT_NOTEBOOK (l->data));
956 	}
957 
958 	g_list_free (nbs);
959 }
960 
961 void
gedit_multi_notebook_add_new_notebook(GeditMultiNotebook * mnb)962 gedit_multi_notebook_add_new_notebook (GeditMultiNotebook *mnb)
963 {
964 	GtkWidget *notebook;
965 	GeditTab *tab;
966 
967 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
968 
969 	notebook = gedit_notebook_new ();
970 	add_notebook (mnb, notebook, FALSE);
971 
972 	tab = _gedit_tab_new ();
973 	gtk_widget_show (GTK_WIDGET (tab));
974 
975 	/* When gtk_notebook_insert_page is called the focus is set in
976 	   the notebook, we don't want this to happen until the page is added.
977 	   Also we don't want to call switch_page when we add the tab
978 	   but when we switch the notebook. */
979 	g_signal_handlers_block_by_func (notebook, notebook_set_focus, mnb);
980 	g_signal_handlers_block_by_func (notebook, notebook_switch_page, mnb);
981 
982 	gedit_notebook_add_tab (GEDIT_NOTEBOOK (notebook),
983 				tab,
984 				-1,
985 				TRUE);
986 
987 	g_signal_handlers_unblock_by_func (notebook, notebook_switch_page, mnb);
988 	g_signal_handlers_unblock_by_func (notebook, notebook_set_focus, mnb);
989 
990 	notebook_set_focus (GTK_CONTAINER (notebook), NULL, mnb);
991 }
992 
993 void
gedit_multi_notebook_add_new_notebook_with_tab(GeditMultiNotebook * mnb,GeditTab * tab)994 gedit_multi_notebook_add_new_notebook_with_tab (GeditMultiNotebook *mnb,
995                                                 GeditTab           *tab)
996 {
997 	GtkWidget *notebook;
998 	GeditNotebook *old_notebook;
999 
1000 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
1001 	g_return_if_fail (GEDIT_IS_TAB (tab));
1002 
1003 	notebook = gedit_notebook_new ();
1004 	add_notebook (mnb, notebook, FALSE);
1005 
1006 	old_notebook = gedit_multi_notebook_get_notebook_for_tab (mnb, tab);
1007 
1008 	/* When gtk_notebook_insert_page is called the focus is set in
1009 	   the notebook, we don't want this to happen until the page is added.
1010 	   Also we don't want to call switch_page when we add the tab
1011 	   but when we switch the notebook. */
1012 	g_signal_handlers_block_by_func (old_notebook, notebook_set_focus, mnb);
1013 	g_signal_handlers_block_by_func (old_notebook, notebook_switch_page, mnb);
1014 
1015 	gedit_notebook_move_tab (old_notebook,
1016 	                         GEDIT_NOTEBOOK (notebook),
1017 	                         tab,
1018 	                         -1);
1019 
1020 	g_signal_handlers_unblock_by_func (old_notebook, notebook_switch_page, mnb);
1021 	g_signal_handlers_unblock_by_func (old_notebook, notebook_set_focus, mnb);
1022 
1023 	notebook_set_focus (GTK_CONTAINER (notebook), NULL, mnb);
1024 }
1025 
1026 void
gedit_multi_notebook_remove_active_notebook(GeditMultiNotebook * mnb)1027 gedit_multi_notebook_remove_active_notebook (GeditMultiNotebook *mnb)
1028 {
1029 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
1030 
1031 	gedit_notebook_remove_all_tabs (GEDIT_NOTEBOOK (mnb->priv->active_notebook));
1032 }
1033 
1034 void
gedit_multi_notebook_previous_notebook(GeditMultiNotebook * mnb)1035 gedit_multi_notebook_previous_notebook (GeditMultiNotebook *mnb)
1036 {
1037 	GList *current;
1038 	GtkWidget *notebook;
1039 
1040 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
1041 
1042 	current = g_list_find (mnb->priv->notebooks,
1043 			       mnb->priv->active_notebook);
1044 
1045 	if (current->prev != NULL)
1046 		notebook = GTK_WIDGET (current->prev->data);
1047 	else
1048 		notebook = GTK_WIDGET (g_list_last (mnb->priv->notebooks)->data);
1049 
1050 	gtk_widget_grab_focus (notebook);
1051 }
1052 
1053 void
gedit_multi_notebook_next_notebook(GeditMultiNotebook * mnb)1054 gedit_multi_notebook_next_notebook (GeditMultiNotebook *mnb)
1055 {
1056 	GList *current;
1057 	GtkWidget *notebook;
1058 
1059 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
1060 
1061 	current = g_list_find (mnb->priv->notebooks,
1062 			       mnb->priv->active_notebook);
1063 
1064 	if (current->next != NULL)
1065 		notebook = GTK_WIDGET (current->next->data);
1066 	else
1067 		notebook = GTK_WIDGET (mnb->priv->notebooks->data);
1068 
1069 	gtk_widget_grab_focus (notebook);
1070 }
1071 
1072 void
gedit_multi_notebook_foreach_notebook(GeditMultiNotebook * mnb,GtkCallback callback,gpointer callback_data)1073 gedit_multi_notebook_foreach_notebook (GeditMultiNotebook *mnb,
1074 				       GtkCallback         callback,
1075 				       gpointer            callback_data)
1076 {
1077 	GList *l;
1078 
1079 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
1080 
1081 	for (l = mnb->priv->notebooks; l != NULL; l = g_list_next (l))
1082 	{
1083 		callback (GTK_WIDGET (l->data), callback_data);
1084 	}
1085 }
1086 
1087 void
gedit_multi_notebook_foreach_tab(GeditMultiNotebook * mnb,GtkCallback callback,gpointer callback_data)1088 gedit_multi_notebook_foreach_tab (GeditMultiNotebook *mnb,
1089 				  GtkCallback         callback,
1090 				  gpointer            callback_data)
1091 {
1092 	GList *nb;
1093 
1094 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
1095 
1096 	for (nb = mnb->priv->notebooks; nb != NULL; nb = g_list_next (nb))
1097 	{
1098 		GList *l, *children;
1099 
1100 		children = gtk_container_get_children (GTK_CONTAINER (nb->data));
1101 
1102 		for (l = children; l != NULL; l = g_list_next (l))
1103 		{
1104 			callback (GTK_WIDGET (l->data), callback_data);
1105 		}
1106 
1107 		g_list_free (children);
1108 	}
1109 }
1110 
1111 /* We only use this to hide tabs in fullscreen mode so for now
1112  * we do not have a real property etc.
1113  */
1114 void
_gedit_multi_notebook_set_show_tabs(GeditMultiNotebook * mnb,gboolean show)1115 _gedit_multi_notebook_set_show_tabs (GeditMultiNotebook *mnb,
1116 				     gboolean           show)
1117 {
1118 	g_return_if_fail (GEDIT_IS_MULTI_NOTEBOOK (mnb));
1119 
1120 	mnb->priv->show_tabs = show != FALSE;
1121 
1122 	update_tabs_visibility (mnb);
1123 }
1124 
1125 /* ex:set ts=8 noet: */
1126