1 /* file included in chanview.c */
5 GtkWidget
*outer
; /* outer box */
6 GtkWidget
*inner
; /* inner box */
7 GtkWidget
*b1
; /* button1 */
8 GtkWidget
*b2
; /* button2 */
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 *)
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.
35 cv_tabs_sizerequest (GtkWidget
*viewport
, GtkRequisition
*requisition
, chanview
*cv
)
38 requisition
->width
= 1;
40 requisition
->height
= 1;
44 cv_tabs_sizealloc (GtkWidget
*widget
, GtkAllocation
*allocation
, chanview
*cv
)
50 inner
= ((tabview
*)cv
)->inner
;
54 adj
= gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner
->parent
));
55 gdk_window_get_geometry (inner
->parent
->window
, 0, 0, 0, &viewport_size
, 0);
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
);
68 gtk_widget_show (((tabview
*)cv
)->b1
);
69 gtk_widget_show (((tabview
*)cv
)->b2
);
74 tab_search_offset (GtkWidget
*inner
, gint start_offset
,
75 gboolean forward
, gboolean vertical
)
83 boxes
= GTK_BOX (inner
)->children
;
84 if (!forward
&& boxes
)
85 boxes
= g_list_last (boxes
);
89 box
= ((GtkBoxChild
*)boxes
->data
)->widget
;
90 boxes
= (forward
? boxes
->next
: boxes
->prev
);
92 tabs
= GTK_BOX (box
)->children
;
94 tabs
= g_list_last (tabs
);
98 button
= ((GtkBoxChild
*)tabs
->data
)->widget
;
99 tabs
= (forward
? tabs
->next
: tabs
->prev
);
101 if (!GTK_IS_TOGGLE_BUTTON (button
))
104 found
= (vertical
? button
->allocation
.y
: button
->allocation
.x
);
105 if ((forward
&& found
> start_offset
) ||
106 (!forward
&& found
< start_offset
))
115 tab_scroll_left_up_clicked (GtkWidget
*widget
, chanview
*cv
)
123 inner
= ((tabview
*)cv
)->inner
;
127 adj
= gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner
->parent
));
128 gdk_window_get_geometry (inner
->parent
->window
, 0, 0, 0, &viewport_size
, 0);
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) */
157 tab_left_is_moving
= 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
162 tab_scroll_right_down_clicked (GtkWidget
*widget
, chanview
*cv
)
170 inner
= ((tabview
*)cv
)->inner
;
174 adj
= gtk_viewport_get_vadjustment (GTK_VIEWPORT (inner
->parent
));
175 gdk_window_get_geometry (inner
->parent
->window
, 0, 0, 0, &viewport_size
, 0);
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) */
204 tab_right_is_moving
= 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */
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
);
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 */
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
);
247 cv_tabs_init (chanview
*cv
)
249 GtkWidget
*box
, *hbox
= NULL
;
255 outer
= gtk_vbox_new (0, 0);
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
);
274 box
= gtk_vbox_new (FALSE
, 0);
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 */
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
,
295 ((tabview
*)cv
)->b1
= make_sbutton (cv
->vertical
?
296 GTK_ARROW_DOWN
: GTK_ARROW_RIGHT
,
297 tab_scroll_right_down_clicked
,
302 gtk_container_add (GTK_CONTAINER (hbox
), ((tabview
*)cv
)->b2
);
303 gtk_container_add (GTK_CONTAINER (hbox
), ((tabview
*)cv
)->b1
);
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
,
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
);
319 cv_tabs_postinit (chanview
*cv
)
324 tab_add_sorted (chanview
*cv
, GtkWidget
*box
, GtkWidget
*tab
, chan
*ch
)
333 gtk_box_pack_start (GTK_BOX (box
), tab
, 0, 0, 0);
334 gtk_widget_show (tab
);
339 * - move tab if renamed (dialogs) */
341 /* userdata, passed to mg_tabs_compare() */
344 list
= GTK_BOX (box
)->children
;
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
);
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 */
373 cv_tabs_prune (chanview
*cv
)
375 GList
*boxes
, *children
;
376 GtkWidget
*box
, *inner
;
380 inner
= ((tabview
*)cv
)->inner
;
381 boxes
= GTK_BOX (inner
)->children
;
388 /* check if the box is empty (except a vseperator) */
390 children
= GTK_BOX (box
)->children
;
393 if (!GTK_IS_SEPARATOR (((GtkBoxChild
*)children
->data
)->widget
))
398 children
= children
->next
;
402 gtk_widget_destroy (box
);
407 tab_add_real (chanview
*cv
, GtkWidget
*tab
, chan
*ch
)
409 GList
*boxes
, *children
;
410 GtkWidget
*sep
, *box
, *inner
;
414 inner
= ((tabview
*)cv
)->inner
;
415 /* see if a family for this tab already exists */
416 boxes
= GTK_BOX (inner
)->children
;
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
);
431 /* check if the box is empty (except a vseperator) */
433 children
= GTK_BOX (box
)->children
;
436 if (!GTK_IS_SEPARATOR (((GtkBoxChild
*)children
->data
)->widget
))
441 children
= children
->next
;
445 gtk_widget_destroy (box
);
448 /* create a new family box */
452 box
= gtk_vbox_new (FALSE
, 0);
453 sep
= gtk_hseparator_new ();
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
);
472 tab_ignore_cb (GtkWidget
*widget
, GdkEventCrossing
*event
, gpointer user_data
)
477 /* called when a tab is clicked (button down) */
480 tab_pressed_cb (GtkToggleButton
*tab
, chan
*ch
)
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
);
493 is_switching
= FALSE
;
495 gtk_toggle_button_set_active (tab
, TRUE
);
496 ignore_toggle
= FALSE
;
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 */
506 tab_toggled_cb (GtkToggleButton
*tab
, chan
*ch
)
511 /* activated a tab via keyboard */
512 tab_pressed_cb (tab
, ch
);
516 tab_click_cb (GtkWidget
*wid
, GdkEventButton
*event
, chan
*ch
)
518 return ch
->cv
->cb_contextmenu (ch
->cv
, ch
, ch
->tag
, ch
->userdata
, event
);
522 cv_tabs_add (chanview
*cv
, chan
*ch
, char *name
, GtkTreeIter
*parent
)
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
);
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
);
549 /* traverse all the family boxes of tabs
551 * A "group" is basically:
554 * `-GtkV/HBox (inner box)
555 * `- GtkBox (family box)
568 tab_group_for_each_tab (chanview
*cv
,
569 int (*callback
) (GtkWidget
*tab
, int num
, int usernum
),
578 innerbox
= (GtkBox
*) ((tabview
*)cv
)->inner
;
579 boxes
= innerbox
->children
;
584 tabs
= GTK_BOX (child
->widget
)->children
;
590 if (!GTK_IS_SEPARATOR (child
->widget
))
592 if (callback (child
->widget
, i
, usernum
) != -1)
606 tab_check_focus_cb (GtkWidget
*tab
, int num
, int unused
)
608 if (GTK_TOGGLE_BUTTON (tab
)->active
)
614 /* returns the currently focused tab number */
617 tab_group_get_cur_page (chanview
*cv
)
619 return tab_group_for_each_tab (cv
, tab_check_focus_cb
, 0);
623 cv_tabs_focus (chan
*ch
)
626 /* focus the new one (tab_pressed_cb defocuses the old one) */
627 tab_pressed_cb (GTK_TOGGLE_BUTTON (ch
->impl
), ch
);
631 tab_focus_num_cb (GtkWidget
*tab
, int num
, int want
)
635 cv_tabs_focus (g_object_get_data (G_OBJECT (tab
), "c"));
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 */
651 chanview_populate (cv
);
654 /* switch to the tab number specified */
657 cv_tabs_move_focus (chanview
*cv
, gboolean relative
, int num
)
664 i
= tab_group_get_cur_page (cv
) + num
;
665 /* make it wrap around at both ends */
670 tab_group_for_each_tab (cv
, tab_focus_num_cb
, i
);
674 tab_group_for_each_tab (cv
, tab_focus_num_cb
, num
);
678 cv_tabs_remove (chan
*ch
)
680 gtk_widget_destroy (ch
->impl
);
683 cv_tabs_prune (ch
->cv
);
687 cv_tabs_move (chan
*ch
, int delta
)
691 GtkWidget
*parent
= ((GtkWidget
*)ch
->impl
)->parent
;
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
)
704 pos
= (pos
- delta
) % i
;
705 gtk_box_reorder_child (GTK_BOX (parent
), ch
->impl
, pos
);
709 cv_tabs_move_family (chan
*ch
, int delta
)
713 GtkWidget
*box
= NULL
;
715 /* find position of tab's family */
717 for (list
= GTK_BOX (((tabview
*)ch
->cv
)->inner
)->children
; list
; list
= list
->next
)
719 GtkBoxChild
*child_entry
;
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
;
732 pos
= (pos
- delta
) % i
;
733 gtk_box_reorder_child (GTK_BOX (box
->parent
), box
, pos
);
737 cv_tabs_cleanup (chanview
*cv
)
740 gtk_widget_destroy (((tabview
*)cv
)->outer
);
744 cv_tabs_set_color (chan
*ch
, PangoAttrList
*list
)
746 gtk_label_set_attributes (GTK_LABEL (GTK_BIN (ch
->impl
)->child
), list
);
750 cv_tabs_rename (chan
*ch
, char *name
)
753 GtkWidget
*tab
= ch
->impl
;
755 attr
= gtk_label_get_attributes (GTK_LABEL (GTK_BIN (tab
)->child
));
757 pango_attr_list_ref (attr
);
759 gtk_button_set_label (GTK_BUTTON (tab
), name
);
760 gtk_widget_queue_resize (tab
->parent
->parent
->parent
);
764 gtk_label_set_attributes (GTK_LABEL (GTK_BIN (tab
)->child
), attr
);
765 pango_attr_list_unref (attr
);
770 cv_tabs_is_collapsed (chan
*ch
)
776 cv_tabs_get_parent (chan
*ch
)