1 /* HexChat
2  * Copyright (C) 1998-2010 Peter Zelezny.
3  * Copyright (C) 2009-2013 Berke Viktor.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 /* file included in chanview.c */
21 
22 typedef struct
23 {
24 	GtkWidget *outer;	/* outer box */
25 	GtkWidget *inner;	/* inner box */
26 	GtkWidget *b1;		/* button1 */
27 	GtkWidget *b2;		/* button2 */
28 } tabview;
29 
30 static void chanview_populate (chanview *cv);
31 
32 /* ignore "toggled" signal? */
33 static int ignore_toggle = FALSE;
34 static int tab_left_is_moving = 0;
35 static int tab_right_is_moving = 0;
36 
37 /* userdata for gobjects used here:
38  *
39  * tab (togglebuttons inside boxes):
40  *   "u" userdata passed to tab-focus callback function (sess)
41  *   "c" the tab's (chan *)
42  *
43  * box (family box)
44  *   "f" family
45  *
46  */
47 
48 /*
49  * GtkViewports request at least as much space as their children do.
50  * If we don't intervene here, the GtkViewport will be granted its
51  * request, even at the expense of resizing the top-level window.
52  */
53 static void
cv_tabs_sizerequest(GtkWidget * viewport,GtkRequisition * requisition,chanview * cv)54 cv_tabs_sizerequest (GtkWidget *viewport, GtkRequisition *requisition, chanview *cv)
55 {
56 	if (!cv->vertical)
57 		requisition->width = 1;
58 	else
59 		requisition->height = 1;
60 }
61 
62 static void
cv_tabs_sizealloc(GtkWidget * widget,GtkAllocation * allocation,chanview * cv)63 cv_tabs_sizealloc (GtkWidget *widget, GtkAllocation *allocation, chanview *cv)
64 {
65 	GdkWindow *parent_win;
66 	GtkAdjustment *adj;
67 	GtkWidget *inner;
68 	gint viewport_size;
69 
70 	inner = ((tabview *)cv)->inner;
71 	parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
72 
73 	if (cv->vertical)
74 	{
75 		adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (gtk_widget_get_parent (inner)));
76 		gdk_window_get_geometry (parent_win, 0, 0, 0, &viewport_size, 0);
77 	} else
78 	{
79 		adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (gtk_widget_get_parent (inner)));
80 		gdk_window_get_geometry (parent_win, 0, 0, &viewport_size, 0, 0);
81 	}
82 
83 	if (gtk_adjustment_get_upper (adj) <= viewport_size)
84 	{
85 		gtk_widget_hide (((tabview *)cv)->b1);
86 		gtk_widget_hide (((tabview *)cv)->b2);
87 	} else
88 	{
89 		gtk_widget_show (((tabview *)cv)->b1);
90 		gtk_widget_show (((tabview *)cv)->b2);
91 	}
92 }
93 
94 static gint
tab_search_offset(GtkWidget * inner,gint start_offset,gboolean forward,gboolean vertical)95 tab_search_offset (GtkWidget *inner, gint start_offset,
96 				   gboolean forward, gboolean vertical)
97 {
98 	GList *boxes;
99 	GList *tabs;
100 	GtkWidget *box;
101 	GtkWidget *button;
102 	GtkAllocation allocation;
103 	gint found;
104 
105 	boxes = gtk_container_get_children (GTK_CONTAINER (inner));
106 	if (!forward && boxes)
107 		boxes = g_list_last (boxes);
108 
109 	while (boxes)
110 	{
111 		box = (GtkWidget *)boxes->data;
112 		boxes = (forward ? boxes->next : boxes->prev);
113 
114 		tabs = gtk_container_get_children (GTK_CONTAINER (box));
115 		if (!forward && tabs)
116 			tabs = g_list_last (tabs);
117 
118 		while (tabs)
119 		{
120 			button = (GtkWidget *)tabs->data;
121 			tabs = (forward ? tabs->next : tabs->prev);
122 
123 			if (!GTK_IS_TOGGLE_BUTTON (button))
124 				continue;
125 
126 			gtk_widget_get_allocation (button, &allocation);
127 			found = (vertical ? allocation.y : allocation.x);
128 			if ((forward && found > start_offset) ||
129 				(!forward && found < start_offset))
130 				return found;
131 		}
132 	}
133 
134 	return 0;
135 }
136 
137 static void
tab_scroll_left_up_clicked(GtkWidget * widget,chanview * cv)138 tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
139 {
140 	GtkAdjustment *adj;
141 	gint viewport_size;
142 	gfloat new_value;
143 	GtkWidget *inner;
144 	GdkWindow *parent_win;
145 	gdouble i;
146 
147 	inner = ((tabview *)cv)->inner;
148 	parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
149 
150 	if (cv->vertical)
151 	{
152 		adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (gtk_widget_get_parent(inner)));
153 		gdk_window_get_geometry (parent_win, 0, 0, 0, &viewport_size, 0);
154 	} else
155 	{
156 		adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (gtk_widget_get_parent(inner)));
157 		gdk_window_get_geometry (parent_win, 0, 0, &viewport_size, 0, 0);
158 	}
159 
160 	new_value = tab_search_offset (inner, gtk_adjustment_get_value (adj), 0, cv->vertical);
161 
162 	if (new_value + viewport_size > gtk_adjustment_get_upper (adj))
163 		new_value = gtk_adjustment_get_upper (adj) - viewport_size;
164 
165 	if (!tab_left_is_moving)
166 	{
167 		tab_left_is_moving = 1;
168 
169 		for (i = gtk_adjustment_get_value (adj); ((i > new_value) && (tab_left_is_moving)); i -= 0.1)
170 		{
171 			gtk_adjustment_set_value (adj, i);
172 			while (g_main_context_pending (NULL))
173 				g_main_context_iteration (NULL, TRUE);
174 		}
175 
176 		gtk_adjustment_set_value (adj, new_value);
177 
178 		tab_left_is_moving = 0;		/* hSP: set to false in case we didnt get stopped (the normal case) */
179 	}
180 	else
181 	{
182 		tab_left_is_moving = 0;		/* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
183 	}
184 }
185 
186 static void
tab_scroll_right_down_clicked(GtkWidget * widget,chanview * cv)187 tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv)
188 {
189 	GtkAdjustment *adj;
190 	gint viewport_size;
191 	gfloat new_value;
192 	GtkWidget *inner;
193 	GdkWindow *parent_win;
194 	gdouble i;
195 
196 	inner = ((tabview *)cv)->inner;
197 	parent_win = gtk_widget_get_window (gtk_widget_get_parent (inner));
198 
199 	if (cv->vertical)
200 	{
201 		adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (gtk_widget_get_parent(inner)));
202 		gdk_window_get_geometry (parent_win, 0, 0, 0, &viewport_size, 0);
203 	} else
204 	{
205 		adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (gtk_widget_get_parent(inner)));
206 		gdk_window_get_geometry (parent_win, 0, 0, &viewport_size, 0, 0);
207 	}
208 
209 	new_value = tab_search_offset (inner, gtk_adjustment_get_value (adj), 1, cv->vertical);
210 
211 	if (new_value == 0 || new_value + viewport_size > gtk_adjustment_get_upper (adj))
212 		new_value = gtk_adjustment_get_upper (adj) - viewport_size;
213 
214 	if (!tab_right_is_moving)
215 	{
216 		tab_right_is_moving = 1;
217 
218 		for (i = gtk_adjustment_get_value (adj); ((i < new_value) && (tab_right_is_moving)); i += 0.1)
219 		{
220 			gtk_adjustment_set_value (adj, i);
221 			while (g_main_context_pending (NULL))
222 				g_main_context_iteration (NULL, TRUE);
223 		}
224 
225 		gtk_adjustment_set_value (adj, new_value);
226 
227 		tab_right_is_moving = 0;		/* hSP: set to false in case we didnt get stopped (the normal case) */
228 	}
229 	else
230 	{
231 		tab_right_is_moving = 0;		/* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
232 	}
233 }
234 
235 static gboolean
tab_scroll_cb(GtkWidget * widget,GdkEventScroll * event,gpointer cv)236 tab_scroll_cb (GtkWidget *widget, GdkEventScroll *event, gpointer cv)
237 {
238 	if (prefs.hex_gui_tab_scrollchans)
239 	{
240 		if (event->direction == GDK_SCROLL_DOWN)
241 			mg_switch_page (1, 1);
242 		else if (event->direction == GDK_SCROLL_UP)
243 			mg_switch_page (1, -1);
244 	}
245 	else
246 	{
247 		/* mouse wheel scrolling */
248 		if (event->direction == GDK_SCROLL_UP)
249 			tab_scroll_left_up_clicked (widget, cv);
250 		else if (event->direction == GDK_SCROLL_DOWN)
251 			tab_scroll_right_down_clicked (widget, cv);
252 	}
253 
254 	return FALSE;
255 }
256 
257 static void
cv_tabs_xclick_cb(GtkWidget * button,chanview * cv)258 cv_tabs_xclick_cb (GtkWidget *button, chanview *cv)
259 {
260 	cv->cb_xbutton (cv, cv->focused, cv->focused->tag, cv->focused->userdata);
261 }
262 
263 /* make a Scroll (arrow) button */
264 
265 static GtkWidget *
make_sbutton(GtkArrowType type,void * click_cb,void * userdata)266 make_sbutton (GtkArrowType type, void *click_cb, void *userdata)
267 {
268 	GtkWidget *button, *arrow;
269 
270 	button = gtk_button_new ();
271 	arrow = gtk_arrow_new (type, GTK_SHADOW_NONE);
272 	gtk_container_add (GTK_CONTAINER (button), arrow);
273 	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
274 	g_signal_connect (G_OBJECT (button), "clicked",
275 							G_CALLBACK (click_cb), userdata);
276 	g_signal_connect (G_OBJECT (button), "scroll_event",
277 							G_CALLBACK (tab_scroll_cb), userdata);
278 	gtk_widget_show (arrow);
279 
280 	return button;
281 }
282 
283 static void
cv_tabs_init(chanview * cv)284 cv_tabs_init (chanview *cv)
285 {
286 	GtkWidget *box, *hbox = NULL;
287 	GtkWidget *viewport;
288 	GtkWidget *outer;
289 	GtkWidget *button;
290 
291 	if (cv->vertical)
292 		outer = gtk_vbox_new (0, 0);
293 	else
294 		outer = gtk_hbox_new (0, 0);
295 	((tabview *)cv)->outer = outer;
296 	g_signal_connect (G_OBJECT (outer), "size_allocate",
297 							G_CALLBACK (cv_tabs_sizealloc), cv);
298 /*	gtk_container_set_border_width (GTK_CONTAINER (outer), 2);*/
299 	gtk_widget_show (outer);
300 
301 	viewport = gtk_viewport_new (0, 0);
302 	gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
303 	g_signal_connect (G_OBJECT (viewport), "size_request",
304 							G_CALLBACK (cv_tabs_sizerequest), cv);
305 	g_signal_connect (G_OBJECT (viewport), "scroll_event",
306 							G_CALLBACK (tab_scroll_cb), cv);
307 	gtk_box_pack_start (GTK_BOX (outer), viewport, 1, 1, 0);
308 	gtk_widget_show (viewport);
309 
310 	if (cv->vertical)
311 		box = gtk_vbox_new (FALSE, 0);
312 	else
313 		box = gtk_hbox_new (FALSE, 0);
314 	((tabview *)cv)->inner = box;
315 	gtk_container_add (GTK_CONTAINER (viewport), box);
316 	gtk_widget_show (box);
317 
318 	/* if vertical, the buttons can be side by side */
319 	if (cv->vertical)
320 	{
321 		hbox = gtk_hbox_new (FALSE, 0);
322 		gtk_box_pack_start (GTK_BOX (outer), hbox, 0, 0, 0);
323 		gtk_widget_show (hbox);
324 	}
325 
326 	/* make the Scroll buttons */
327 	((tabview *)cv)->b2 = make_sbutton (cv->vertical ?
328 													GTK_ARROW_UP : GTK_ARROW_LEFT,
329 													tab_scroll_left_up_clicked,
330 													cv);
331 
332 	((tabview *)cv)->b1 = make_sbutton (cv->vertical ?
333 													GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
334 													tab_scroll_right_down_clicked,
335 													cv);
336 
337 	if (hbox)
338 	{
339 		gtk_container_add (GTK_CONTAINER (hbox), ((tabview *)cv)->b2);
340 		gtk_container_add (GTK_CONTAINER (hbox), ((tabview *)cv)->b1);
341 	} else
342 	{
343 		gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b2, 0, 0, 0);
344 		gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b1, 0, 0, 0);
345 	}
346 
347 	button = gtkutil_button (outer, GTK_STOCK_CLOSE, NULL, cv_tabs_xclick_cb,
348 									 cv, 0);
349 	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
350 	gtk_widget_set_can_focus (button, FALSE);
351 
352 	gtk_container_add (GTK_CONTAINER (cv->box), outer);
353 }
354 
355 static void
cv_tabs_postinit(chanview * cv)356 cv_tabs_postinit (chanview *cv)
357 {
358 }
359 
360 static void
tab_add_sorted(chanview * cv,GtkWidget * box,GtkWidget * tab,chan * ch)361 tab_add_sorted (chanview *cv, GtkWidget *box, GtkWidget *tab, chan *ch)
362 {
363 	GList *list;
364 	GtkWidget *child;
365 	int i = 0;
366 	void *b;
367 
368 	if (!cv->sorted)
369 	{
370 		gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
371 		gtk_widget_show (tab);
372 		return;
373 	}
374 
375 	/* sorting TODO:
376     *   - move tab if renamed (dialogs) */
377 
378 	/* userdata, passed to mg_tabs_compare() */
379 	b = ch->userdata;
380 
381 	list = gtk_container_get_children (GTK_CONTAINER (box));
382 	while (list)
383 	{
384 		child = list->data;
385 		if (!GTK_IS_SEPARATOR (child))
386 		{
387 			void *a = g_object_get_data (G_OBJECT (child), "u");
388 
389 			if (ch->tag == 0 && cv->cb_compare (a, b) > 0)
390 			{
391 				gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
392 				gtk_box_reorder_child (GTK_BOX (box), tab, ++i);
393 				gtk_widget_show (tab);
394 				return;
395 			}
396 		}
397 		i++;
398 		list = list->next;
399 	}
400 
401 	/* append */
402 	gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
403 	gtk_box_reorder_child (GTK_BOX (box), tab, i);
404 	gtk_widget_show (tab);
405 }
406 
407 /* remove empty boxes and separators */
408 
409 static void
cv_tabs_prune(chanview * cv)410 cv_tabs_prune (chanview *cv)
411 {
412 	GList *boxes, *children;
413 	GtkWidget *box, *inner;
414 	GtkWidget *child;
415 	int empty;
416 
417 	inner = ((tabview *)cv)->inner;
418 	boxes = gtk_container_get_children (GTK_CONTAINER (inner));
419 	while (boxes)
420 	{
421 		child = boxes->data;
422 		box = child;
423 		boxes = boxes->next;
424 
425 		/* check if the box is empty (except a vseperator) */
426 		empty = TRUE;
427 		children = gtk_container_get_children (GTK_CONTAINER (box));
428 		while (children)
429 		{
430 			if (!GTK_IS_SEPARATOR ((GtkWidget *)children->data))
431 			{
432 				empty = FALSE;
433 				break;
434 			}
435 			children = children->next;
436 		}
437 
438 		if (empty)
439 			gtk_widget_destroy (box);
440 	}
441 }
442 
443 static void
tab_add_real(chanview * cv,GtkWidget * tab,chan * ch)444 tab_add_real (chanview *cv, GtkWidget *tab, chan *ch)
445 {
446 	GList *boxes, *children;
447 	GtkWidget *sep, *box, *inner;
448 	GtkWidget *child;
449 	int empty;
450 
451 	inner = ((tabview *)cv)->inner;
452 	/* see if a family for this tab already exists */
453 	boxes = gtk_container_get_children (GTK_CONTAINER (inner));
454 	while (boxes)
455 	{
456 		child = boxes->data;
457 		box = child;
458 
459 		if (g_object_get_data (G_OBJECT (box), "f") == ch->family)
460 		{
461 			tab_add_sorted (cv, box, tab, ch);
462 			gtk_widget_queue_resize (gtk_widget_get_parent(inner));
463 			return;
464 		}
465 
466 		boxes = boxes->next;
467 
468 		/* check if the box is empty (except a vseperator) */
469 		empty = TRUE;
470 		children = gtk_container_get_children (GTK_CONTAINER (box));
471 		while (children)
472 		{
473 			if (!GTK_IS_SEPARATOR ((GtkWidget *)children->data))
474 			{
475 				empty = FALSE;
476 				break;
477 			}
478 			children = children->next;
479 		}
480 
481 		if (empty)
482 			gtk_widget_destroy (box);
483 	}
484 
485 	/* create a new family box */
486 	if (cv->vertical)
487 	{
488 		/* vertical */
489 		box = gtk_vbox_new (FALSE, 0);
490 		sep = gtk_hseparator_new ();
491 	} else
492 	{
493 		/* horiz */
494 		box = gtk_hbox_new (FALSE, 0);
495 		sep = gtk_vseparator_new ();
496 	}
497 
498 	gtk_box_pack_end (GTK_BOX (box), sep, 0, 0, 4);
499 	gtk_widget_show (sep);
500 	gtk_box_pack_start (GTK_BOX (inner), box, 0, 0, 0);
501 	g_object_set_data (G_OBJECT (box), "f", ch->family);
502 	gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
503 	gtk_widget_show (tab);
504 	gtk_widget_show (box);
505 	gtk_widget_queue_resize (gtk_widget_get_parent(inner));
506 }
507 
508 static gboolean
tab_ignore_cb(GtkWidget * widget,GdkEventCrossing * event,gpointer user_data)509 tab_ignore_cb (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
510 {
511 	return TRUE;
512 }
513 
514 /* called when a tab is clicked (button down) */
515 
516 static void
tab_pressed_cb(GtkToggleButton * tab,chan * ch)517 tab_pressed_cb (GtkToggleButton *tab, chan *ch)
518 {
519 	chan *old_tab;
520 	int is_switching = TRUE;
521 	chanview *cv = ch->cv;
522 
523 	ignore_toggle = TRUE;
524 	/* de-activate the old tab */
525 	old_tab = cv->focused;
526 	if (old_tab && old_tab->impl)
527 	{
528 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (old_tab->impl), FALSE);
529 		if (old_tab == ch)
530 			is_switching = FALSE;
531 	}
532 	gtk_toggle_button_set_active (tab, TRUE);
533 	ignore_toggle = FALSE;
534 	cv->focused = ch;
535 
536 	if (/*tab->active*/is_switching)
537 		/* call the focus callback */
538 		cv->cb_focus (cv, ch, ch->tag, ch->userdata);
539 }
540 
541 /* called for keyboard tab toggles only */
542 static void
tab_toggled_cb(GtkToggleButton * tab,chan * ch)543 tab_toggled_cb (GtkToggleButton *tab, chan *ch)
544 {
545 	if (ignore_toggle)
546 		return;
547 
548 	/* activated a tab via keyboard */
549 	tab_pressed_cb (tab, ch);
550 }
551 
552 static gboolean
tab_click_cb(GtkWidget * wid,GdkEventButton * event,chan * ch)553 tab_click_cb (GtkWidget *wid, GdkEventButton *event, chan *ch)
554 {
555 	return ch->cv->cb_contextmenu (ch->cv, ch, ch->tag, ch->userdata, event);
556 }
557 
558 static void *
cv_tabs_add(chanview * cv,chan * ch,char * name,GtkTreeIter * parent)559 cv_tabs_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
560 {
561 	GtkWidget *but;
562 
563 	but = gtk_toggle_button_new_with_label (name);
564 	gtk_widget_set_name (but, "hexchat-tab");
565 	g_object_set_data (G_OBJECT (but), "c", ch);
566 	/* used to trap right-clicks */
567 	g_signal_connect (G_OBJECT (but), "button_press_event",
568 						 	G_CALLBACK (tab_click_cb), ch);
569 	/* avoid prelights */
570 	g_signal_connect (G_OBJECT (but), "enter_notify_event",
571 						 	G_CALLBACK (tab_ignore_cb), NULL);
572 	g_signal_connect (G_OBJECT (but), "leave_notify_event",
573 						 	G_CALLBACK (tab_ignore_cb), NULL);
574 	g_signal_connect (G_OBJECT (but), "pressed",
575 							G_CALLBACK (tab_pressed_cb), ch);
576 	/* for keyboard */
577 	g_signal_connect (G_OBJECT (but), "toggled",
578 						 	G_CALLBACK (tab_toggled_cb), ch);
579 	g_object_set_data (G_OBJECT (but), "u", ch->userdata);
580 
581 	tab_add_real (cv, but, ch);
582 
583 	return but;
584 }
585 
586 /* traverse all the family boxes of tabs
587  *
588  * A "group" is basically:
589  * GtkV/HBox
590  * `-GtkViewPort
591  *   `-GtkV/HBox (inner box)
592  *     `- GtkBox (family box)
593  *        `- GtkToggleButton
594  *        `- GtkToggleButton
595  *        `- ...
596  *     `- GtkBox
597  *        `- GtkToggleButton
598  *        `- GtkToggleButton
599  *        `- ...
600  *     `- ...
601  *
602  * */
603 
604 static int
tab_group_for_each_tab(chanview * cv,int (* callback)(GtkWidget * tab,int num,int usernum),int usernum)605 tab_group_for_each_tab (chanview *cv,
606 								int (*callback) (GtkWidget *tab, int num, int usernum),
607 								int usernum)
608 {
609 	GList *tabs;
610 	GList *boxes;
611 	GtkWidget *child;
612 	GtkBox *innerbox;
613 	int i;
614 
615 	innerbox = (GtkBox *) ((tabview *)cv)->inner;
616 	boxes = gtk_container_get_children (GTK_CONTAINER (innerbox));
617 	i = 0;
618 	while (boxes)
619 	{
620 		child = boxes->data;
621 		tabs = gtk_container_get_children (GTK_CONTAINER (child));
622 
623 		while (tabs)
624 		{
625 			child = tabs->data;
626 
627 			if (!GTK_IS_SEPARATOR (child))
628 			{
629 				if (callback (child, i, usernum) != -1)
630 					return i;
631 				i++;
632 			}
633 			tabs = tabs->next;
634 		}
635 
636 		boxes = boxes->next;
637 	}
638 
639 	return i;
640 }
641 
642 static int
tab_check_focus_cb(GtkWidget * tab,int num,int unused)643 tab_check_focus_cb (GtkWidget *tab, int num, int unused)
644 {
645 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (tab)))
646 		return num;
647 
648 	return -1;
649 }
650 
651 /* returns the currently focused tab number */
652 
653 static int
tab_group_get_cur_page(chanview * cv)654 tab_group_get_cur_page (chanview *cv)
655 {
656 	return tab_group_for_each_tab (cv, tab_check_focus_cb, 0);
657 }
658 
659 static void
cv_tabs_focus(chan * ch)660 cv_tabs_focus (chan *ch)
661 {
662 	if (ch->impl)
663 	/* focus the new one (tab_pressed_cb defocuses the old one) */
664 		tab_pressed_cb (GTK_TOGGLE_BUTTON (ch->impl), ch);
665 }
666 
667 static int
tab_focus_num_cb(GtkWidget * tab,int num,int want)668 tab_focus_num_cb (GtkWidget *tab, int num, int want)
669 {
670 	if (num == want)
671 	{
672 		cv_tabs_focus (g_object_get_data (G_OBJECT (tab), "c"));
673 		return 1;
674 	}
675 
676 	return -1;
677 }
678 
679 static void
cv_tabs_change_orientation(chanview * cv)680 cv_tabs_change_orientation (chanview *cv)
681 {
682 	/* cleanup the old one */
683 	if (cv->func_cleanup)
684 		cv->func_cleanup (cv);
685 
686 	/* now rebuild a new tabbar or tree */
687 	cv->func_init (cv);
688 	chanview_populate (cv);
689 }
690 
691 /* switch to the tab number specified */
692 
693 static void
cv_tabs_move_focus(chanview * cv,gboolean relative,int num)694 cv_tabs_move_focus (chanview *cv, gboolean relative, int num)
695 {
696 	int i, max;
697 
698 	if (relative)
699 	{
700 		max = cv->size;
701 		i = tab_group_get_cur_page (cv) + num;
702 		/* make it wrap around at both ends */
703 		if (i < 0)
704 			i = max - 1;
705 		if (i >= max)
706 			i = 0;
707 		tab_group_for_each_tab (cv, tab_focus_num_cb, i);
708 		return;
709 	}
710 
711 	tab_group_for_each_tab (cv, tab_focus_num_cb, num);
712 }
713 
714 static void
cv_tabs_remove(chan * ch)715 cv_tabs_remove (chan *ch)
716 {
717 	gtk_widget_destroy (ch->impl);
718 	ch->impl = NULL;
719 
720 	cv_tabs_prune (ch->cv);
721 }
722 
723 static void
cv_tabs_move(chan * ch,int delta)724 cv_tabs_move (chan *ch, int delta)
725 {
726 	int i = 0;
727 	int pos = 0;
728 	GList *list;
729 	GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET (ch->impl));
730 
731 	for (list = gtk_container_get_children (GTK_CONTAINER (parent)); list; list = list->next)
732 	{
733 		GtkWidget *child_entry;
734 
735 		child_entry = list->data;
736 		if (child_entry == ch->impl)
737 			pos = i;
738 
739 		/* keep separator at end to not throw off our count */
740 		if (GTK_IS_SEPARATOR (child_entry))
741 			gtk_box_reorder_child (GTK_BOX (parent), child_entry, -1);
742 		else
743 			i++;
744 	}
745 
746 	pos = (pos - delta) % i;
747 	gtk_box_reorder_child (GTK_BOX (parent), ch->impl, pos);
748 }
749 
750 static void
cv_tabs_move_family(chan * ch,int delta)751 cv_tabs_move_family (chan *ch, int delta)
752 {
753 	int i, pos = 0;
754 	GList *list;
755 	GtkWidget *box = NULL;
756 
757 	/* find position of tab's family */
758 	i = 0;
759 	for (list = gtk_container_get_children (GTK_CONTAINER (((tabview *)ch->cv)->inner)); list; list = list->next)
760 	{
761 		GtkWidget *child_entry;
762 		void *fam;
763 
764 		child_entry = list->data;
765 		fam = g_object_get_data (G_OBJECT (child_entry), "f");
766 		if (fam == ch->family)
767 		{
768 			box = child_entry;
769 			pos = i;
770 		}
771 		i++;
772 	}
773 
774 	pos = (pos - delta) % i;
775 	gtk_box_reorder_child (GTK_BOX (gtk_widget_get_parent(box)), box, pos);
776 }
777 
778 static void
cv_tabs_cleanup(chanview * cv)779 cv_tabs_cleanup (chanview *cv)
780 {
781 	if (cv->box)
782 		gtk_widget_destroy (((tabview *)cv)->outer);
783 }
784 
785 static void
cv_tabs_set_color(chan * ch,PangoAttrList * list)786 cv_tabs_set_color (chan *ch, PangoAttrList *list)
787 {
788 	gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (ch->impl))), list);
789 }
790 
791 static void
cv_tabs_rename(chan * ch,char * name)792 cv_tabs_rename (chan *ch, char *name)
793 {
794 	PangoAttrList *attr;
795 	GtkWidget *tab = ch->impl;
796 
797 	attr = gtk_label_get_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (tab))));
798 	if (attr)
799 		pango_attr_list_ref (attr);
800 
801 	gtk_button_set_label (GTK_BUTTON (tab), name);
802 	gtk_widget_queue_resize (gtk_widget_get_parent(gtk_widget_get_parent(gtk_widget_get_parent(tab))));
803 
804 	if (attr)
805 	{
806 		gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (tab))), attr);
807 		pango_attr_list_unref (attr);
808 	}
809 }
810 
811 static gboolean
cv_tabs_is_collapsed(chan * ch)812 cv_tabs_is_collapsed (chan *ch)
813 {
814 	return FALSE;
815 }
816 
817 static chan *
cv_tabs_get_parent(chan * ch)818 cv_tabs_get_parent (chan *ch)
819 {
820 	return NULL;
821 }
822