2 * GNT - The GLib Ncurses Toolkit
4 * GNT is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
40 static GntWidgetClass
*parent_class
= NULL
;
42 static GntWidget
* find_focusable_widget(GntBox
*box
);
45 add_to_focus(gpointer value
, gpointer data
)
47 GntBox
*box
= GNT_BOX(data
);
48 GntWidget
*w
= GNT_WIDGET(value
);
51 g_list_foreach(GNT_BOX(w
)->list
, add_to_focus
, box
);
52 else if (GNT_WIDGET_IS_FLAG_SET(w
, GNT_WIDGET_CAN_TAKE_FOCUS
))
53 box
->focus
= g_list_append(box
->focus
, w
);
57 get_title_thingies(GntBox
*box
, char *title
, int *p
, int *r
)
59 GntWidget
*widget
= GNT_WIDGET(box
);
61 char *end
= (char*)gnt_util_onscreen_width_to_pointer(title
, widget
->priv
.width
- 4, &len
);
64 *p
= (widget
->priv
.width
- len
) / 2;
66 *r
= (widget
->priv
.width
+ len
) / 2;
71 gnt_box_draw(GntWidget
*widget
)
73 GntBox
*box
= GNT_BOX(widget
);
75 if (box
->focus
== NULL
&& widget
->parent
== NULL
)
76 g_list_foreach(box
->list
, add_to_focus
, box
);
78 g_list_foreach(box
->list
, (GFunc
)gnt_widget_draw
, NULL
);
80 gnt_box_sync_children(box
);
82 if (box
->title
&& !GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_BORDER
))
85 char *title
= g_strdup(box
->title
);
87 get_title_thingies(box
, title
, &pos
, &right
);
89 if (gnt_widget_has_focus(widget
))
90 wbkgdset(widget
->window
, '\0' | gnt_color_pair(GNT_COLOR_TITLE
));
92 wbkgdset(widget
->window
, '\0' | gnt_color_pair(GNT_COLOR_TITLE_D
));
93 mvwaddch(widget
->window
, 0, pos
-1, ACS_RTEE
| gnt_color_pair(GNT_COLOR_NORMAL
));
94 mvwaddstr(widget
->window
, 0, pos
, title
);
95 mvwaddch(widget
->window
, 0, right
, ACS_LTEE
| gnt_color_pair(GNT_COLOR_NORMAL
));
103 reposition_children(GntWidget
*widget
)
106 GntBox
*box
= GNT_BOX(widget
);
107 int w
, h
, curx
, cury
, max
;
108 gboolean has_border
= FALSE
;
112 curx
= widget
->priv
.x
;
113 cury
= widget
->priv
.y
;
114 if (!(GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_NO_BORDER
))
121 for (iter
= box
->list
; iter
; iter
= iter
->next
)
123 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(iter
->data
), GNT_WIDGET_INVISIBLE
))
125 gnt_widget_set_position(GNT_WIDGET(iter
->data
), curx
, cury
);
126 gnt_widget_get_size(GNT_WIDGET(iter
->data
), &w
, &h
);
131 cury
+= h
+ box
->pad
;
140 curx
+= w
+ box
->pad
;
164 widget
->priv
.width
= max
;
165 widget
->priv
.height
= cury
- widget
->priv
.y
;
169 widget
->priv
.width
= curx
- widget
->priv
.x
;
170 widget
->priv
.height
= max
;
175 gnt_box_set_position(GntWidget
*widget
, int x
, int y
)
178 int changex
, changey
;
180 changex
= widget
->priv
.x
- x
;
181 changey
= widget
->priv
.y
- y
;
183 for (iter
= GNT_BOX(widget
)->list
; iter
; iter
= iter
->next
)
185 GntWidget
*w
= GNT_WIDGET(iter
->data
);
186 gnt_widget_set_position(w
, w
->priv
.x
- changex
,
187 w
->priv
.y
- changey
);
192 gnt_box_size_request(GntWidget
*widget
)
194 GntBox
*box
= GNT_BOX(widget
);
196 int maxw
= 0, maxh
= 0;
198 g_list_foreach(box
->list
, (GFunc
)gnt_widget_size_request
, NULL
);
200 for (iter
= box
->list
; iter
; iter
= iter
->next
)
203 gnt_widget_get_size(GNT_WIDGET(iter
->data
), &w
, &h
);
210 for (iter
= box
->list
; iter
; iter
= iter
->next
)
213 GntWidget
*wid
= GNT_WIDGET(iter
->data
);
215 gnt_widget_get_size(wid
, &w
, &h
);
217 if (box
->homogeneous
)
232 gnt_widget_confirm_size(wid
, w
, h
);
233 gnt_widget_set_size(wid
, w
, h
);
236 reposition_children(widget
);
240 gnt_box_map(GntWidget
*widget
)
242 if (widget
->priv
.width
== 0 || widget
->priv
.height
== 0)
244 gnt_widget_size_request(widget
);
245 find_focusable_widget(GNT_BOX(widget
));
250 /* Ensures that the current widget can take focus */
252 find_focusable_widget(GntBox
*box
)
254 /* XXX: Make sure the widget is visible? */
255 if (box
->focus
== NULL
&& GNT_WIDGET(box
)->parent
== NULL
)
256 g_list_foreach(box
->list
, add_to_focus
, box
);
258 if (box
->active
== NULL
&& box
->focus
)
259 box
->active
= box
->focus
->data
;
265 find_next_focus(GntBox
*box
)
267 gpointer last
= box
->active
;
270 GList
*iter
= g_list_find(box
->focus
, box
->active
);
271 if (iter
&& iter
->next
)
272 box
->active
= iter
->next
->data
;
274 box
->active
= box
->focus
->data
;
275 if (!GNT_WIDGET_IS_FLAG_SET(box
->active
, GNT_WIDGET_INVISIBLE
))
277 } while (box
->active
!= last
);
281 find_prev_focus(GntBox
*box
)
283 gpointer last
= box
->active
;
290 GList
*iter
= g_list_find(box
->focus
, box
->active
);
292 box
->active
= box
->focus
->data
;
293 else if (!iter
->prev
)
294 box
->active
= g_list_last(box
->focus
)->data
;
296 box
->active
= iter
->prev
->data
;
297 if (!GNT_WIDGET_IS_FLAG_SET(box
->active
, GNT_WIDGET_INVISIBLE
))
299 } while (box
->active
!= last
);
303 gnt_box_key_pressed(GntWidget
*widget
, const char *text
)
305 GntBox
*box
= GNT_BOX(widget
);
308 if (box
->active
== NULL
&& !find_focusable_widget(box
))
311 if (gnt_widget_key_pressed(box
->active
, text
))
318 if (strcmp(text
, GNT_KEY_LEFT
) == 0)
320 find_prev_focus(box
);
322 else if (strcmp(text
, GNT_KEY_RIGHT
) == 0)
324 find_next_focus(box
);
326 else if (strcmp(text
, GNT_KEY_BACK_TAB
) == 0)
328 find_prev_focus(box
);
331 else if (text
[0] == '\t')
333 find_next_focus(box
);
336 if (now
&& now
!= box
->active
)
338 gnt_widget_set_focus(now
, FALSE
);
339 gnt_widget_set_focus(box
->active
, TRUE
);
347 gnt_box_lost_focus(GntWidget
*widget
)
349 GntWidget
*w
= GNT_BOX(widget
)->active
;
351 gnt_widget_set_focus(w
, FALSE
);
352 gnt_widget_draw(widget
);
356 gnt_box_gained_focus(GntWidget
*widget
)
358 GntWidget
*w
= GNT_BOX(widget
)->active
;
360 gnt_widget_set_focus(w
, TRUE
);
361 gnt_widget_draw(widget
);
365 gnt_box_destroy(GntWidget
*w
)
367 GntBox
*box
= GNT_BOX(w
);
369 gnt_box_remove_all(box
);
370 gnt_screen_release(w
);
374 gnt_box_expose(GntWidget
*widget
, int x
, int y
, int width
, int height
)
376 WINDOW
*win
= newwin(height
, width
, widget
->priv
.y
+ y
, widget
->priv
.x
+ x
);
377 copywin(widget
->window
, win
, y
, x
, 0, 0, height
- 1, width
- 1, FALSE
);
383 gnt_box_confirm_size(GntWidget
*widget
, int width
, int height
)
386 GntBox
*box
= GNT_BOX(widget
);
387 int wchange
, hchange
;
392 wchange
= widget
->priv
.width
- width
;
393 hchange
= widget
->priv
.height
- height
;
395 if (wchange
== 0 && hchange
== 0)
396 return TRUE
; /* Quit playing games */
398 /* XXX: Right now, I am trying to just apply all the changes to
399 * just one widget. It should be possible to distribute the
400 * changes to all the widgets in the box. */
401 for (iter
= box
->list
; iter
; iter
= iter
->next
)
403 GntWidget
*wid
= iter
->data
;
406 gnt_widget_get_size(wid
, &w
, &h
);
408 if (gnt_widget_confirm_size(wid
, w
- wchange
, h
- hchange
))
412 for (i
= box
->list
; i
; i
= i
->next
)
415 if (i
== iter
) continue;
416 gnt_widget_get_size(GNT_WIDGET(i
->data
), &tw
, &th
);
419 if (!gnt_widget_confirm_size(i
->data
, tw
- wchange
, th
)) {
420 /* If we are decreasing the size and the widget is going
421 * to be too large to fit into the box, then do not allow
423 if (wchange
> 0 && tw
>= widget
->priv
.width
)
429 if (!gnt_widget_confirm_size(i
->data
, tw
, th
- hchange
)) {
430 if (hchange
> 0 && th
>= widget
->priv
.height
)
437 gnt_widget_set_size(wid
, w
- wchange
, h
- hchange
);
443 for (i
= box
->list
; i
; i
= i
->next
)
446 if (i
== iter
) continue;
447 gnt_widget_get_size(GNT_WIDGET(i
->data
), &tw
, &th
);
448 gnt_widget_set_size(i
->data
, tw
- wchange
, th
- hchange
);
451 g_object_set_data(G_OBJECT(box
), "size-queued", wid
);
460 gnt_box_size_changed(GntWidget
*widget
, int oldw
, int oldh
)
462 int wchange
, hchange
;
464 GntBox
*box
= GNT_BOX(widget
);
468 wchange
= widget
->priv
.width
- oldw
;
469 hchange
= widget
->priv
.height
- oldh
;
471 wid
= g_object_get_data(G_OBJECT(box
), "size-queued");
474 gnt_widget_get_size(wid
, &tw
, &th
);
475 gnt_widget_set_size(wid
, tw
+ wchange
, th
+ hchange
);
476 g_object_set_data(G_OBJECT(box
), "size-queued", NULL
);
484 for (i
= box
->list
; i
; i
= i
->next
)
488 gnt_widget_get_size(GNT_WIDGET(i
->data
), &tw
, &th
);
489 gnt_widget_set_size(i
->data
, tw
+ wchange
, th
+ hchange
);
493 reposition_children(widget
);
497 gnt_box_clicked(GntWidget
*widget
, GntMouseEvent event
, int cx
, int cy
)
500 for (iter
= GNT_BOX(widget
)->list
; iter
; iter
= iter
->next
) {
502 GntWidget
*wid
= iter
->data
;
504 gnt_widget_get_position(wid
, &x
, &y
);
505 gnt_widget_get_size(wid
, &w
, &h
);
507 if (cx
>= x
&& cx
< x
+ w
&& cy
>= y
&& cy
< y
+ h
) {
508 if (event
<= GNT_MIDDLE_MOUSE_DOWN
&&
509 GNT_WIDGET_IS_FLAG_SET(wid
, GNT_WIDGET_CAN_TAKE_FOCUS
)) {
510 while (widget
->parent
)
511 widget
= widget
->parent
;
512 gnt_box_give_focus_to_child(GNT_BOX(widget
), wid
);
514 return gnt_widget_clicked(wid
, event
, cx
, cy
);
521 gnt_box_set_property(GObject
*obj
, guint prop_id
, const GValue
*value
,
524 GntBox
*box
= GNT_BOX(obj
);
527 box
->vertical
= g_value_get_boolean(value
);
530 box
->homogeneous
= g_value_get_boolean(value
);
533 g_return_if_reached();
539 gnt_box_get_property(GObject
*obj
, guint prop_id
, GValue
*value
,
542 GntBox
*box
= GNT_BOX(obj
);
545 g_value_set_boolean(value
, box
->vertical
);
548 g_value_set_boolean(value
, box
->homogeneous
);
556 gnt_box_class_init(GntBoxClass
*klass
)
558 GObjectClass
*gclass
= G_OBJECT_CLASS(klass
);
559 parent_class
= GNT_WIDGET_CLASS(klass
);
560 parent_class
->destroy
= gnt_box_destroy
;
561 parent_class
->draw
= gnt_box_draw
;
562 parent_class
->expose
= gnt_box_expose
;
563 parent_class
->map
= gnt_box_map
;
564 parent_class
->size_request
= gnt_box_size_request
;
565 parent_class
->set_position
= gnt_box_set_position
;
566 parent_class
->key_pressed
= gnt_box_key_pressed
;
567 parent_class
->clicked
= gnt_box_clicked
;
568 parent_class
->lost_focus
= gnt_box_lost_focus
;
569 parent_class
->gained_focus
= gnt_box_gained_focus
;
570 parent_class
->confirm_size
= gnt_box_confirm_size
;
571 parent_class
->size_changed
= gnt_box_size_changed
;
573 gclass
->set_property
= gnt_box_set_property
;
574 gclass
->get_property
= gnt_box_get_property
;
575 g_object_class_install_property(gclass
,
577 g_param_spec_boolean("vertical", "Vertical",
578 "Whether the child widgets in the box should be stacked vertically.",
580 G_PARAM_READWRITE
|G_PARAM_STATIC_NAME
|G_PARAM_STATIC_NICK
|G_PARAM_STATIC_BLURB
583 g_object_class_install_property(gclass
,
585 g_param_spec_boolean("homogeneous", "Homogeneous",
586 "Whether the child widgets in the box should have the same size.",
588 G_PARAM_READWRITE
|G_PARAM_STATIC_NAME
|G_PARAM_STATIC_NICK
|G_PARAM_STATIC_BLURB
594 gnt_box_init(GTypeInstance
*instance
, gpointer
class)
596 GntWidget
*widget
= GNT_WIDGET(instance
);
597 GntBox
*box
= GNT_BOX(widget
);
598 /* Initially make both the height and width resizable.
599 * Update the flags as necessary when widgets are added to it. */
600 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_GROW_X
| GNT_WIDGET_GROW_Y
);
601 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
);
602 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_NO_BORDER
| GNT_WIDGET_NO_SHADOW
);
608 /******************************************************************************
610 *****************************************************************************/
612 gnt_box_get_gtype(void)
614 static GType type
= 0;
618 static const GTypeInfo info
= {
620 NULL
, /* base_init */
621 NULL
, /* base_finalize */
622 (GClassInitFunc
)gnt_box_class_init
,
623 NULL
, /* class_finalize */
624 NULL
, /* class_data */
627 gnt_box_init
, /* instance_init */
628 NULL
/* value_table */
631 type
= g_type_register_static(GNT_TYPE_WIDGET
,
639 GntWidget
*gnt_box_new(gboolean homo
, gboolean vert
)
641 GntWidget
*widget
= g_object_new(GNT_TYPE_BOX
, NULL
);
642 GntBox
*box
= GNT_BOX(widget
);
644 box
->homogeneous
= homo
;
645 box
->vertical
= vert
;
646 box
->alignment
= vert
? GNT_ALIGN_LEFT
: GNT_ALIGN_MID
;
651 void gnt_box_add_widget(GntBox
*b
, GntWidget
*widget
)
653 b
->list
= g_list_append(b
->list
, widget
);
654 widget
->parent
= GNT_WIDGET(b
);
657 void gnt_box_set_title(GntBox
*b
, const char *title
)
659 char *prev
= b
->title
;
660 GntWidget
*w
= GNT_WIDGET(b
);
661 b
->title
= g_strdup(title
);
662 if (w
->window
&& !GNT_WIDGET_IS_FLAG_SET(w
, GNT_WIDGET_NO_BORDER
)) {
663 /* Erase the old title */
665 get_title_thingies(b
, prev
, &pos
, &right
);
666 mvwhline(w
->window
, 0, pos
- 1, ACS_HLINE
| gnt_color_pair(GNT_COLOR_NORMAL
),
672 void gnt_box_set_pad(GntBox
*box
, int pad
)
675 /* XXX: Perhaps redraw if already showing? */
678 void gnt_box_set_toplevel(GntBox
*box
, gboolean set
)
680 GntWidget
*widget
= GNT_WIDGET(box
);
683 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_NO_BORDER
| GNT_WIDGET_NO_SHADOW
);
684 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
);
688 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_NO_BORDER
| GNT_WIDGET_NO_SHADOW
);
689 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
);
693 void gnt_box_sync_children(GntBox
*box
)
696 GntWidget
*widget
= GNT_WIDGET(box
);
699 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_BORDER
))
702 for (iter
= box
->list
; iter
; iter
= iter
->next
)
704 GntWidget
*w
= GNT_WIDGET(iter
->data
);
708 if (GNT_WIDGET_IS_FLAG_SET(w
, GNT_WIDGET_INVISIBLE
))
712 gnt_box_sync_children(GNT_BOX(w
));
714 gnt_widget_get_size(w
, &width
, &height
);
716 x
= w
->priv
.x
- widget
->priv
.x
;
717 y
= w
->priv
.y
- widget
->priv
.y
;
722 if (box
->alignment
== GNT_ALIGN_RIGHT
)
723 x
+= widget
->priv
.width
- width
;
724 else if (box
->alignment
== GNT_ALIGN_MID
)
725 x
+= (widget
->priv
.width
- width
)/2;
726 if (x
+ width
> widget
->priv
.width
- pos
)
727 x
-= x
+ width
- (widget
->priv
.width
- pos
);
732 if (box
->alignment
== GNT_ALIGN_BOTTOM
)
733 y
+= widget
->priv
.height
- height
;
734 else if (box
->alignment
== GNT_ALIGN_MID
)
735 y
+= (widget
->priv
.height
- height
)/2;
736 if (y
+ height
>= widget
->priv
.height
- pos
)
737 y
= widget
->priv
.height
- height
- pos
;
740 copywin(w
->window
, widget
->window
, 0, 0,
741 y
, x
, y
+ height
- 1, x
+ width
- 1, FALSE
);
742 gnt_widget_set_position(w
, x
+ widget
->priv
.x
, y
+ widget
->priv
.y
);
746 void gnt_box_set_alignment(GntBox
*box
, GntAlignment alignment
)
748 box
->alignment
= alignment
;
751 void gnt_box_remove(GntBox
*box
, GntWidget
*widget
)
753 box
->list
= g_list_remove(box
->list
, widget
);
754 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
)
755 && GNT_WIDGET(box
)->parent
== NULL
&& box
->focus
)
757 if (widget
== box
->active
)
759 find_next_focus(box
);
760 if (box
->active
== widget
) /* There's only one widget */
763 box
->focus
= g_list_remove(box
->focus
, widget
);
766 if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(box
), GNT_WIDGET_MAPPED
))
767 gnt_widget_draw(GNT_WIDGET(box
));
770 void gnt_box_remove_all(GntBox
*box
)
772 g_list_foreach(box
->list
, (GFunc
)gnt_widget_destroy
, NULL
);
773 g_list_free(box
->list
);
774 g_list_free(box
->focus
);
777 GNT_WIDGET(box
)->priv
.width
= 0;
778 GNT_WIDGET(box
)->priv
.height
= 0;
781 void gnt_box_readjust(GntBox
*box
)
787 if (GNT_WIDGET(box
)->parent
!= NULL
)
790 for (iter
= box
->list
; iter
; iter
= iter
->next
)
792 GntWidget
*w
= iter
->data
;
794 gnt_box_readjust(GNT_BOX(w
));
797 GNT_WIDGET_UNSET_FLAGS(w
, GNT_WIDGET_MAPPED
);
803 wid
= GNT_WIDGET(box
);
804 GNT_WIDGET_UNSET_FLAGS(wid
, GNT_WIDGET_MAPPED
);
806 wid
->priv
.height
= 0;
808 if (wid
->parent
== NULL
)
810 g_list_free(box
->focus
);
813 gnt_widget_size_request(wid
);
814 gnt_widget_get_size(wid
, &width
, &height
);
815 gnt_screen_resize_widget(wid
, width
, height
);
816 find_focusable_widget(box
);
820 void gnt_box_set_fill(GntBox
*box
, gboolean fill
)
825 void gnt_box_move_focus(GntBox
*box
, int dir
)
829 if (box
->active
== NULL
)
831 find_focusable_widget(box
);
838 find_next_focus(box
);
840 find_prev_focus(box
);
842 if (now
&& now
!= box
->active
)
844 gnt_widget_set_focus(now
, FALSE
);
845 gnt_widget_set_focus(box
->active
, TRUE
);
848 if (GNT_WIDGET(box
)->window
)
849 gnt_widget_draw(GNT_WIDGET(box
));
852 void gnt_box_give_focus_to_child(GntBox
*box
, GntWidget
*widget
)
857 while (GNT_WIDGET(box
)->parent
)
858 box
= GNT_BOX(GNT_WIDGET(box
)->parent
);
860 find
= g_list_find(box
->focus
, widget
);
863 box
->active
= widget
;
864 if (now
&& now
!= box
->active
)
866 gnt_widget_set_focus(now
, FALSE
);
867 gnt_widget_set_focus(box
->active
, TRUE
);
870 if (GNT_WIDGET(box
)->window
)
871 gnt_widget_draw(GNT_WIDGET(box
));