remove dbus
[rofl0r-ixchat.git] / src / fe-gtk / chanview-tabs.c
blob8e3da8e601c0377a0883260e434b59ce76bdf912
1 /* file included in chanview.c */
3 typedef struct
5 GtkWidget *outer; /* outer box */
6 GtkWidget *inner; /* inner box */
7 GtkWidget *b1; /* button1 */
8 GtkWidget *b2; /* button2 */
9 } tabview;
11 static void chanview_populate (chanview *cv);
13 /* ignore "toggled" signal? */
14 static int ignore_toggle = FALSE;
15 static int tab_left_is_moving = 0;
16 static int tab_right_is_moving = 0;
18 /* userdata for gobjects used here:
20 * tab (togglebuttons inside boxes):
21 * "u" userdata passed to tab-focus callback function (sess)
22 * "c" the tab's (chan *)
24 * box (family box)
25 * "f" family
30 * GtkViewports request at least as much space as their children do.
31 * If we don't intervene here, the GtkViewport will be granted its
32 * request, even at the expense of resizing the top-level window.
34 static void
35 cv_tabs_sizerequest (GtkWidget *viewport, GtkRequisition *requisition, chanview *cv)
37 if (!cv->vertical)
38 requisition->width = 1;
39 else
40 requisition->height = 1;
43 static void
44 cv_tabs_sizealloc (GtkWidget *widget, GtkAllocation *allocation, chanview *cv)
46 GtkAdjustment *adj;
47 GtkWidget *inner;
48 gint viewport_size;
50 inner = ((tabview *)cv)->inner;
52 if (cv->vertical)
54 adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
55 gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
56 } else
58 adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
59 gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
62 if (adj->upper <= viewport_size)
64 gtk_widget_hide (((tabview *)cv)->b1);
65 gtk_widget_hide (((tabview *)cv)->b2);
66 } else
68 gtk_widget_show (((tabview *)cv)->b1);
69 gtk_widget_show (((tabview *)cv)->b2);
73 static gint
74 tab_search_offset (GtkWidget *inner, gint start_offset,
75 gboolean forward, gboolean vertical)
77 GList *boxes;
78 GList *tabs;
79 GtkWidget *box;
80 GtkWidget *button;
81 gint found;
83 boxes = GTK_BOX (inner)->children;
84 if (!forward && boxes)
85 boxes = g_list_last (boxes);
87 while (boxes)
89 box = ((GtkBoxChild *)boxes->data)->widget;
90 boxes = (forward ? boxes->next : boxes->prev);
92 tabs = GTK_BOX (box)->children;
93 if (!forward && tabs)
94 tabs = g_list_last (tabs);
96 while (tabs)
98 button = ((GtkBoxChild *)tabs->data)->widget;
99 tabs = (forward ? tabs->next : tabs->prev);
101 if (!GTK_IS_TOGGLE_BUTTON (button))
102 continue;
104 found = (vertical ? button->allocation.y : button->allocation.x);
105 if ((forward && found > start_offset) ||
106 (!forward && found < start_offset))
107 return found;
111 return 0;
114 static void
115 tab_scroll_left_up_clicked (GtkWidget *widget, chanview *cv)
117 GtkAdjustment *adj;
118 gint viewport_size;
119 gfloat new_value;
120 GtkWidget *inner;
121 gfloat i;
123 inner = ((tabview *)cv)->inner;
125 if (cv->vertical)
127 adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
128 gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
129 } else
131 adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
132 gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
135 new_value = tab_search_offset (inner, adj->value, 0, cv->vertical);
137 if (new_value + viewport_size > adj->upper)
138 new_value = adj->upper - viewport_size;
140 if (!tab_left_is_moving)
142 tab_left_is_moving = 1;
144 for (i = adj->value; ((i > new_value) && (tab_left_is_moving)); i -= 0.1)
146 gtk_adjustment_set_value (adj, i);
147 while (g_main_pending ())
148 g_main_iteration (TRUE);
151 gtk_adjustment_set_value (adj, new_value);
153 tab_left_is_moving = 0; /* hSP: set to false in case we didnt get stopped (the normal case) */
155 else
157 tab_left_is_moving = 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
161 static void
162 tab_scroll_right_down_clicked (GtkWidget *widget, chanview *cv)
164 GtkAdjustment *adj;
165 gint viewport_size;
166 gfloat new_value;
167 GtkWidget *inner;
168 gfloat i;
170 inner = ((tabview *)cv)->inner;
172 if (cv->vertical)
174 adj = gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner->parent));
175 gdk_window_get_geometry (inner->parent->window, 0, 0, 0, &viewport_size, 0);
176 } else
178 adj = gtk_viewport_get_hadjustment (GTK_VIEWPORT (inner->parent));
179 gdk_window_get_geometry (inner->parent->window, 0, 0, &viewport_size, 0, 0);
182 new_value = tab_search_offset (inner, adj->value, 1, cv->vertical);
184 if (new_value == 0 || new_value + viewport_size > adj->upper)
185 new_value = adj->upper - viewport_size;
187 if (!tab_right_is_moving)
189 tab_right_is_moving = 1;
191 for (i = adj->value; ((i < new_value) && (tab_right_is_moving)); i += 0.1)
193 gtk_adjustment_set_value (adj, i);
194 while (g_main_pending ())
195 g_main_iteration (TRUE);
198 gtk_adjustment_set_value (adj, new_value);
200 tab_right_is_moving = 0; /* hSP: set to false in case we didnt get stopped (the normal case) */
202 else
204 tab_right_is_moving = 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
208 static gboolean
209 tab_scroll_cb (GtkWidget *widget, GdkEventScroll *event, gpointer cv)
211 /* mouse wheel scrolling */
212 if (event->direction == GDK_SCROLL_UP)
213 tab_scroll_left_up_clicked (widget, cv);
214 else if (event->direction == GDK_SCROLL_DOWN)
215 tab_scroll_right_down_clicked (widget, cv);
217 return FALSE;
220 static void
221 cv_tabs_xclick_cb (GtkWidget *button, chanview *cv)
223 cv->cb_xbutton (cv, cv->focused, cv->focused->tag, cv->focused->userdata);
226 /* make a Scroll (arrow) button */
228 static GtkWidget *
229 make_sbutton (GtkArrowType type, void *click_cb, void *userdata)
231 GtkWidget *button, *arrow;
233 button = gtk_button_new ();
234 arrow = gtk_arrow_new (type, GTK_SHADOW_NONE);
235 gtk_container_add (GTK_CONTAINER (button), arrow);
236 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
237 g_signal_connect (G_OBJECT (button), "clicked",
238 G_CALLBACK (click_cb), userdata);
239 g_signal_connect (G_OBJECT (button), "scroll_event",
240 G_CALLBACK (tab_scroll_cb), userdata);
241 gtk_widget_show (arrow);
243 return button;
246 static void
247 cv_tabs_init (chanview *cv)
249 GtkWidget *box, *hbox = NULL;
250 GtkWidget *viewport;
251 GtkWidget *outer;
252 GtkWidget *button;
254 if (cv->vertical)
255 outer = gtk_vbox_new (0, 0);
256 else
257 outer = gtk_hbox_new (0, 0);
258 ((tabview *)cv)->outer = outer;
259 g_signal_connect (G_OBJECT (outer), "size_allocate",
260 G_CALLBACK (cv_tabs_sizealloc), cv);
261 /* gtk_container_set_border_width (GTK_CONTAINER (outer), 2);*/
262 gtk_widget_show (outer);
264 viewport = gtk_viewport_new (0, 0);
265 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
266 g_signal_connect (G_OBJECT (viewport), "size_request",
267 G_CALLBACK (cv_tabs_sizerequest), cv);
268 g_signal_connect (G_OBJECT (viewport), "scroll_event",
269 G_CALLBACK (tab_scroll_cb), cv);
270 gtk_box_pack_start (GTK_BOX (outer), viewport, 1, 1, 0);
271 gtk_widget_show (viewport);
273 if (cv->vertical)
274 box = gtk_vbox_new (FALSE, 0);
275 else
276 box = gtk_hbox_new (FALSE, 0);
277 ((tabview *)cv)->inner = box;
278 gtk_container_add (GTK_CONTAINER (viewport), box);
279 gtk_widget_show (box);
281 /* if vertical, the buttons can be side by side */
282 if (cv->vertical)
284 hbox = gtk_hbox_new (FALSE, 0);
285 gtk_box_pack_start (GTK_BOX (outer), hbox, 0, 0, 0);
286 gtk_widget_show (hbox);
289 /* make the Scroll buttons */
290 ((tabview *)cv)->b2 = make_sbutton (cv->vertical ?
291 GTK_ARROW_UP : GTK_ARROW_LEFT,
292 tab_scroll_left_up_clicked,
293 cv);
295 ((tabview *)cv)->b1 = make_sbutton (cv->vertical ?
296 GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
297 tab_scroll_right_down_clicked,
298 cv);
300 if (hbox)
302 gtk_container_add (GTK_CONTAINER (hbox), ((tabview *)cv)->b2);
303 gtk_container_add (GTK_CONTAINER (hbox), ((tabview *)cv)->b1);
304 } else
306 gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b2, 0, 0, 0);
307 gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b1, 0, 0, 0);
310 button = gtkutil_button (outer, GTK_STOCK_CLOSE, NULL, cv_tabs_xclick_cb,
311 cv, 0);
312 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
313 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
315 gtk_container_add (GTK_CONTAINER (cv->box), outer);
318 static void
319 cv_tabs_postinit (chanview *cv)
323 static void
324 tab_add_sorted (chanview *cv, GtkWidget *box, GtkWidget *tab, chan *ch)
326 GList *list;
327 GtkBoxChild *child;
328 int i = 0;
329 void *b;
331 if (!cv->sorted)
333 gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
334 gtk_widget_show (tab);
335 return;
338 /* sorting TODO:
339 * - move tab if renamed (dialogs) */
341 /* userdata, passed to mg_tabs_compare() */
342 b = ch->userdata;
344 list = GTK_BOX (box)->children;
345 while (list)
347 child = list->data;
348 if (!GTK_IS_SEPARATOR (child->widget))
350 void *a = g_object_get_data (G_OBJECT (child->widget), "u");
352 if (ch->tag == 0 && cv->cb_compare (a, b) > 0)
354 gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
355 gtk_box_reorder_child (GTK_BOX (box), tab, i);
356 gtk_widget_show (tab);
357 return;
360 i++;
361 list = list->next;
364 /* append */
365 gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
366 gtk_box_reorder_child (GTK_BOX (box), tab, i);
367 gtk_widget_show (tab);
370 /* remove empty boxes and separators */
372 static void
373 cv_tabs_prune (chanview *cv)
375 GList *boxes, *children;
376 GtkWidget *box, *inner;
377 GtkBoxChild *child;
378 int empty;
380 inner = ((tabview *)cv)->inner;
381 boxes = GTK_BOX (inner)->children;
382 while (boxes)
384 child = boxes->data;
385 box = child->widget;
386 boxes = boxes->next;
388 /* check if the box is empty (except a vseperator) */
389 empty = TRUE;
390 children = GTK_BOX (box)->children;
391 while (children)
393 if (!GTK_IS_SEPARATOR (((GtkBoxChild *)children->data)->widget))
395 empty = FALSE;
396 break;
398 children = children->next;
401 if (empty)
402 gtk_widget_destroy (box);
406 static void
407 tab_add_real (chanview *cv, GtkWidget *tab, chan *ch)
409 GList *boxes, *children;
410 GtkWidget *sep, *box, *inner;
411 GtkBoxChild *child;
412 int empty;
414 inner = ((tabview *)cv)->inner;
415 /* see if a family for this tab already exists */
416 boxes = GTK_BOX (inner)->children;
417 while (boxes)
419 child = boxes->data;
420 box = child->widget;
422 if (g_object_get_data (G_OBJECT (box), "f") == ch->family)
424 tab_add_sorted (cv, box, tab, ch);
425 gtk_widget_queue_resize (inner->parent);
426 return;
429 boxes = boxes->next;
431 /* check if the box is empty (except a vseperator) */
432 empty = TRUE;
433 children = GTK_BOX (box)->children;
434 while (children)
436 if (!GTK_IS_SEPARATOR (((GtkBoxChild *)children->data)->widget))
438 empty = FALSE;
439 break;
441 children = children->next;
444 if (empty)
445 gtk_widget_destroy (box);
448 /* create a new family box */
449 if (cv->vertical)
451 /* vertical */
452 box = gtk_vbox_new (FALSE, 0);
453 sep = gtk_hseparator_new ();
454 } else
456 /* horiz */
457 box = gtk_hbox_new (FALSE, 0);
458 sep = gtk_vseparator_new ();
461 gtk_box_pack_end (GTK_BOX (box), sep, 0, 0, 4);
462 gtk_widget_show (sep);
463 gtk_box_pack_start (GTK_BOX (inner), box, 0, 0, 0);
464 g_object_set_data (G_OBJECT (box), "f", ch->family);
465 gtk_box_pack_start (GTK_BOX (box), tab, 0, 0, 0);
466 gtk_widget_show (tab);
467 gtk_widget_show (box);
468 gtk_widget_queue_resize (inner->parent);
471 static gboolean
472 tab_ignore_cb (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
474 return TRUE;
477 /* called when a tab is clicked (button down) */
479 static void
480 tab_pressed_cb (GtkToggleButton *tab, chan *ch)
482 chan *old_tab;
483 int is_switching = TRUE;
484 chanview *cv = ch->cv;
486 ignore_toggle = TRUE;
487 /* de-activate the old tab */
488 old_tab = cv->focused;
489 if (old_tab && old_tab->impl)
491 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (old_tab->impl), FALSE);
492 if (old_tab == ch)
493 is_switching = FALSE;
495 gtk_toggle_button_set_active (tab, TRUE);
496 ignore_toggle = FALSE;
497 cv->focused = ch;
499 if (/*tab->active*/is_switching)
500 /* call the focus callback */
501 cv->cb_focus (cv, ch, ch->tag, ch->userdata);
504 /* called for keyboard tab toggles only */
505 static void
506 tab_toggled_cb (GtkToggleButton *tab, chan *ch)
508 if (ignore_toggle)
509 return;
511 /* activated a tab via keyboard */
512 tab_pressed_cb (tab, ch);
515 static gboolean
516 tab_click_cb (GtkWidget *wid, GdkEventButton *event, chan *ch)
518 return ch->cv->cb_contextmenu (ch->cv, ch, ch->tag, ch->userdata, event);
521 static void *
522 cv_tabs_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
524 GtkWidget *but;
526 but = gtk_toggle_button_new_with_label (name);
527 gtk_widget_set_name (but, "xchat-tab");
528 g_object_set_data (G_OBJECT (but), "c", ch);
529 /* used to trap right-clicks */
530 g_signal_connect (G_OBJECT (but), "button_press_event",
531 G_CALLBACK (tab_click_cb), ch);
532 /* avoid prelights */
533 g_signal_connect (G_OBJECT (but), "enter_notify_event",
534 G_CALLBACK (tab_ignore_cb), NULL);
535 g_signal_connect (G_OBJECT (but), "leave_notify_event",
536 G_CALLBACK (tab_ignore_cb), NULL);
537 g_signal_connect (G_OBJECT (but), "pressed",
538 G_CALLBACK (tab_pressed_cb), ch);
539 /* for keyboard */
540 g_signal_connect (G_OBJECT (but), "toggled",
541 G_CALLBACK (tab_toggled_cb), ch);
542 g_object_set_data (G_OBJECT (but), "u", ch->userdata);
544 tab_add_real (cv, but, ch);
546 return but;
549 /* traverse all the family boxes of tabs
551 * A "group" is basically:
552 * GtkV/HBox
553 * `-GtkViewPort
554 * `-GtkV/HBox (inner box)
555 * `- GtkBox (family box)
556 * `- GtkToggleButton
557 * `- GtkToggleButton
558 * `- ...
559 * `- GtkBox
560 * `- GtkToggleButton
561 * `- GtkToggleButton
562 * `- ...
563 * `- ...
565 * */
567 static int
568 tab_group_for_each_tab (chanview *cv,
569 int (*callback) (GtkWidget *tab, int num, int usernum),
570 int usernum)
572 GList *tabs;
573 GList *boxes;
574 GtkBoxChild *child;
575 GtkBox *innerbox;
576 int i;
578 innerbox = (GtkBox *) ((tabview *)cv)->inner;
579 boxes = innerbox->children;
580 i = 0;
581 while (boxes)
583 child = boxes->data;
584 tabs = GTK_BOX (child->widget)->children;
586 while (tabs)
588 child = tabs->data;
590 if (!GTK_IS_SEPARATOR (child->widget))
592 if (callback (child->widget, i, usernum) != -1)
593 return i;
594 i++;
596 tabs = tabs->next;
599 boxes = boxes->next;
602 return i;
605 static int
606 tab_check_focus_cb (GtkWidget *tab, int num, int unused)
608 if (GTK_TOGGLE_BUTTON (tab)->active)
609 return num;
611 return -1;
614 /* returns the currently focused tab number */
616 static int
617 tab_group_get_cur_page (chanview *cv)
619 return tab_group_for_each_tab (cv, tab_check_focus_cb, 0);
622 static void
623 cv_tabs_focus (chan *ch)
625 if (ch->impl)
626 /* focus the new one (tab_pressed_cb defocuses the old one) */
627 tab_pressed_cb (GTK_TOGGLE_BUTTON (ch->impl), ch);
630 static int
631 tab_focus_num_cb (GtkWidget *tab, int num, int want)
633 if (num == want)
635 cv_tabs_focus (g_object_get_data (G_OBJECT (tab), "c"));
636 return 1;
639 return -1;
642 static void
643 cv_tabs_change_orientation (chanview *cv)
645 /* cleanup the old one */
646 if (cv->func_cleanup)
647 cv->func_cleanup (cv);
649 /* now rebuild a new tabbar or tree */
650 cv->func_init (cv);
651 chanview_populate (cv);
654 /* switch to the tab number specified */
656 static void
657 cv_tabs_move_focus (chanview *cv, gboolean relative, int num)
659 int i, max;
661 if (relative)
663 max = cv->size;
664 i = tab_group_get_cur_page (cv) + num;
665 /* make it wrap around at both ends */
666 if (i < 0)
667 i = max - 1;
668 if (i >= max)
669 i = 0;
670 tab_group_for_each_tab (cv, tab_focus_num_cb, i);
671 return;
674 tab_group_for_each_tab (cv, tab_focus_num_cb, num);
677 static void
678 cv_tabs_remove (chan *ch)
680 gtk_widget_destroy (ch->impl);
681 ch->impl = NULL;
683 cv_tabs_prune (ch->cv);
686 static void
687 cv_tabs_move (chan *ch, int delta)
689 int i, pos = 0;
690 GList *list;
691 GtkWidget *parent = ((GtkWidget *)ch->impl)->parent;
693 i = 0;
694 for (list = GTK_BOX (parent)->children; list; list = list->next)
696 GtkBoxChild *child_entry;
698 child_entry = list->data;
699 if (child_entry->widget == ch->impl)
700 pos = i;
701 i++;
704 pos = (pos - delta) % i;
705 gtk_box_reorder_child (GTK_BOX (parent), ch->impl, pos);
708 static void
709 cv_tabs_move_family (chan *ch, int delta)
711 int i, pos = 0;
712 GList *list;
713 GtkWidget *box = NULL;
715 /* find position of tab's family */
716 i = 0;
717 for (list = GTK_BOX (((tabview *)ch->cv)->inner)->children; list; list = list->next)
719 GtkBoxChild *child_entry;
720 void *fam;
722 child_entry = list->data;
723 fam = g_object_get_data (G_OBJECT (child_entry->widget), "f");
724 if (fam == ch->family)
726 box = child_entry->widget;
727 pos = i;
729 i++;
732 pos = (pos - delta) % i;
733 gtk_box_reorder_child (GTK_BOX (box->parent), box, pos);
736 static void
737 cv_tabs_cleanup (chanview *cv)
739 if (cv->box)
740 gtk_widget_destroy (((tabview *)cv)->outer);
743 static void
744 cv_tabs_set_color (chan *ch, PangoAttrList *list)
746 gtk_label_set_attributes (GTK_LABEL (GTK_BIN (ch->impl)->child), list);
749 static void
750 cv_tabs_rename (chan *ch, char *name)
752 PangoAttrList *attr;
753 GtkWidget *tab = ch->impl;
755 attr = gtk_label_get_attributes (GTK_LABEL (GTK_BIN (tab)->child));
756 if (attr)
757 pango_attr_list_ref (attr);
759 gtk_button_set_label (GTK_BUTTON (tab), name);
760 gtk_widget_queue_resize (tab->parent->parent->parent);
762 if (attr)
764 gtk_label_set_attributes (GTK_LABEL (GTK_BIN (tab)->child), attr);
765 pango_attr_list_unref (attr);
769 static gboolean
770 cv_tabs_is_collapsed (chan *ch)
772 return FALSE;
775 static chan *
776 cv_tabs_get_parent (chan *ch)
778 return NULL;