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
23 /* Stuff brutally ripped from Gflib */
25 #include "gntwidget.h"
27 #include "gntmarshal.h"
51 static GObjectClass
*parent_class
= NULL
;
52 static guint signals
[SIGS
] = { 0 };
54 static void init_widget(GntWidget
*widget
);
57 gnt_widget_init(GTypeInstance
*instance
, gpointer
class)
59 GntWidget
*widget
= GNT_WIDGET(instance
);
60 widget
->priv
.name
= NULL
;
65 gnt_widget_map(GntWidget
*widget
)
67 /* Get some default size for the widget */
69 g_signal_emit(widget
, signals
[SIG_MAP
], 0);
70 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_MAPPED
);
74 gnt_widget_dispose(GObject
*obj
)
76 GntWidget
*self
= GNT_WIDGET(obj
);
77 g_signal_emit(self
, signals
[SIG_DESTROY
], 0);
78 parent_class
->dispose(obj
);
83 gnt_widget_focus_change(GntWidget
*widget
)
85 if (GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_MAPPED
)
86 gnt_widget_draw(widget
);
90 gnt_widget_dummy_confirm_size(GntWidget
*widget
, int width
, int height
)
93 if (width
< widget
->priv
.minw
|| height
< widget
->priv
.minh
)
95 shadow
= gnt_widget_has_shadow(widget
);
96 if (widget
->priv
.width
+ shadow
!= width
&& !GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_GROW_X
))
98 if (widget
->priv
.height
+ shadow
!= height
&& !GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_GROW_Y
))
104 context_menu(GntBindable
*bind
, GList
*null
)
106 gboolean ret
= FALSE
;
107 g_signal_emit(bind
, signals
[SIG_CONTEXT_MENU
], 0, &ret
);
112 gnt_widget_class_init(GntWidgetClass
*klass
)
114 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
116 parent_class
= g_type_class_peek_parent(klass
);
118 obj_class
->dispose
= gnt_widget_dispose
;
120 klass
->destroy
= gnt_widget_destroy
;
121 klass
->show
= gnt_widget_show
;
122 klass
->draw
= gnt_widget_draw
;
123 klass
->expose
= gnt_widget_expose
;
124 klass
->map
= gnt_widget_map
;
125 klass
->lost_focus
= gnt_widget_focus_change
;
126 klass
->gained_focus
= gnt_widget_focus_change
;
127 klass
->confirm_size
= gnt_widget_dummy_confirm_size
;
129 klass
->key_pressed
= NULL
;
130 klass
->activate
= NULL
;
131 klass
->clicked
= NULL
;
133 signals
[SIG_DESTROY
] =
134 g_signal_new("destroy",
135 G_TYPE_FROM_CLASS(klass
),
137 G_STRUCT_OFFSET(GntWidgetClass
, destroy
),
139 g_cclosure_marshal_VOID__VOID
,
141 signals
[SIG_GIVE_FOCUS
] =
142 g_signal_new("gained-focus",
143 G_TYPE_FROM_CLASS(klass
),
145 G_STRUCT_OFFSET(GntWidgetClass
, gained_focus
),
147 g_cclosure_marshal_VOID__VOID
,
149 signals
[SIG_LOST_FOCUS
] =
150 g_signal_new("lost-focus",
151 G_TYPE_FROM_CLASS(klass
),
153 G_STRUCT_OFFSET(GntWidgetClass
, lost_focus
),
155 g_cclosure_marshal_VOID__VOID
,
157 signals
[SIG_ACTIVATE
] =
158 g_signal_new("activate",
159 G_TYPE_FROM_CLASS(klass
),
161 G_STRUCT_OFFSET(GntWidgetClass
, activate
),
163 g_cclosure_marshal_VOID__VOID
,
167 G_TYPE_FROM_CLASS(klass
),
169 G_STRUCT_OFFSET(GntWidgetClass
, map
),
171 g_cclosure_marshal_VOID__VOID
,
175 G_TYPE_FROM_CLASS(klass
),
177 G_STRUCT_OFFSET(GntWidgetClass
, draw
),
179 g_cclosure_marshal_VOID__VOID
,
183 G_TYPE_FROM_CLASS(klass
),
185 G_STRUCT_OFFSET(GntWidgetClass
, hide
),
187 g_cclosure_marshal_VOID__VOID
,
189 signals
[SIG_EXPOSE
] =
190 g_signal_new("expose",
191 G_TYPE_FROM_CLASS(klass
),
193 G_STRUCT_OFFSET(GntWidgetClass
, expose
),
195 gnt_closure_marshal_VOID__INT_INT_INT_INT
,
196 G_TYPE_NONE
, 4, G_TYPE_INT
, G_TYPE_INT
, G_TYPE_INT
, G_TYPE_INT
);
197 signals
[SIG_POSITION
] =
198 g_signal_new("position-set",
199 G_TYPE_FROM_CLASS(klass
),
201 G_STRUCT_OFFSET(GntWidgetClass
, set_position
),
203 gnt_closure_marshal_VOID__INT_INT
,
204 G_TYPE_NONE
, 2, G_TYPE_INT
, G_TYPE_INT
);
205 signals
[SIG_SIZE_REQUEST
] =
206 g_signal_new("size_request",
207 G_TYPE_FROM_CLASS(klass
),
209 G_STRUCT_OFFSET(GntWidgetClass
, size_request
),
211 g_cclosure_marshal_VOID__VOID
,
213 signals
[SIG_SIZE_CHANGED
] =
214 g_signal_new("size_changed",
215 G_TYPE_FROM_CLASS(klass
),
217 G_STRUCT_OFFSET(GntWidgetClass
, size_changed
),
219 gnt_closure_marshal_VOID__INT_INT
,
220 G_TYPE_NONE
, 2, G_TYPE_INT
, G_TYPE_INT
);
221 signals
[SIG_CONFIRM_SIZE
] =
222 g_signal_new("confirm_size",
223 G_TYPE_FROM_CLASS(klass
),
225 G_STRUCT_OFFSET(GntWidgetClass
, confirm_size
),
227 gnt_closure_marshal_BOOLEAN__INT_INT
,
228 G_TYPE_BOOLEAN
, 2, G_TYPE_INT
, G_TYPE_INT
);
229 signals
[SIG_KEY_PRESSED
] =
230 g_signal_new("key_pressed",
231 G_TYPE_FROM_CLASS(klass
),
233 G_STRUCT_OFFSET(GntWidgetClass
, key_pressed
),
234 gnt_boolean_handled_accumulator
, NULL
,
235 gnt_closure_marshal_BOOLEAN__STRING
,
236 G_TYPE_BOOLEAN
, 1, G_TYPE_STRING
);
238 signals
[SIG_CLICKED
] =
239 g_signal_new("clicked",
240 G_TYPE_FROM_CLASS(klass
),
242 G_STRUCT_OFFSET(GntWidgetClass
, clicked
),
243 gnt_boolean_handled_accumulator
, NULL
,
244 gnt_closure_marshal_BOOLEAN__INT_INT_INT
,
245 G_TYPE_BOOLEAN
, 3, G_TYPE_INT
, G_TYPE_INT
, G_TYPE_INT
);
247 signals
[SIG_CONTEXT_MENU
] =
248 g_signal_new("context-menu",
249 G_TYPE_FROM_CLASS(klass
),
252 gnt_boolean_handled_accumulator
, NULL
,
253 gnt_closure_marshal_BOOLEAN__VOID
,
256 /* This is relevant for all widgets */
257 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass
), "context-menu", context_menu
,
258 GNT_KEY_POPUP
, NULL
);
259 gnt_bindable_register_binding(GNT_BINDABLE_CLASS(klass
), "context-menu", GNT_KEY_F11
, NULL
);
261 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass
), GNT_BINDABLE_CLASS(klass
));
265 /******************************************************************************
267 *****************************************************************************/
269 gnt_widget_get_gtype(void)
271 static GType type
= 0;
274 static const GTypeInfo info
= {
275 sizeof(GntWidgetClass
),
276 NULL
, /* base_init */
277 NULL
, /* base_finalize */
278 (GClassInitFunc
)gnt_widget_class_init
,
280 NULL
, /* class_data */
283 gnt_widget_init
, /* instance_init */
284 NULL
/* value_table */
287 type
= g_type_register_static(GNT_TYPE_BINDABLE
,
289 &info
, G_TYPE_FLAG_ABSTRACT
);
295 void gnt_widget_set_take_focus(GntWidget
*widget
, gboolean can
)
298 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
);
300 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
);
304 * gnt_widget_destroy:
305 * @obj: The #GntWidget instance.
307 * Emits the "destroy" signal notifying all reference holders that they
308 * should release @obj.
311 gnt_widget_destroy(GntWidget
*obj
)
313 g_return_if_fail(GNT_IS_WIDGET(obj
));
315 if(!(GNT_WIDGET_FLAGS(obj
) & GNT_WIDGET_DESTROYING
)) {
316 GNT_WIDGET_SET_FLAGS(obj
, GNT_WIDGET_DESTROYING
);
317 gnt_widget_hide(obj
);
319 g_object_run_dispose(G_OBJECT(obj
));
325 gnt_widget_show(GntWidget
*widget
)
327 gnt_widget_draw(widget
);
328 gnt_screen_occupy(widget
);
332 gnt_widget_draw(GntWidget
*widget
)
334 /* Draw the widget */
335 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_DRAWING
))
338 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_DRAWING
);
339 if (!(GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_MAPPED
)) {
340 gnt_widget_map(widget
);
343 if (widget
->window
== NULL
)
346 int x
, y
, maxx
, maxy
, w
, h
;
348 gboolean shadow
= TRUE
;
350 if (!gnt_widget_has_shadow(widget
))
355 w
= oldw
= widget
->priv
.width
+ shadow
;
356 h
= oldh
= widget
->priv
.height
+ shadow
;
358 getmaxyx(stdscr
, maxy
, maxx
);
359 maxy
-= 1; /* room for the taskbar */
364 x
= MAX(0, maxx
- w
);
366 y
= MAX(0, maxy
- h
);
373 if (w
!= oldw
|| h
!= oldh
) {
374 widget
->priv
.width
= w
- shadow
;
375 widget
->priv
.height
= h
- shadow
;
376 g_signal_emit(widget
, signals
[SIG_SIZE_CHANGED
], 0, oldw
, oldh
);
379 widget
->window
= newpad(widget
->priv
.height
+ 20, widget
->priv
.width
+ 20); /* XXX: */
384 g_signal_emit(widget
, signals
[SIG_DRAW
], 0);
385 gnt_widget_queue_update(widget
);
386 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_DRAWING
);
390 gnt_widget_key_pressed(GntWidget
*widget
, const char *keys
)
393 if (!GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
))
396 if (!GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_DISABLE_ACTIONS
) &&
397 gnt_bindable_perform_action_key(GNT_BINDABLE(widget
), keys
))
400 keys
= gnt_bindable_remap_keys(GNT_BINDABLE(widget
), keys
);
401 g_signal_emit(widget
, signals
[SIG_KEY_PRESSED
], 0, keys
, &ret
);
406 gnt_widget_clicked(GntWidget
*widget
, GntMouseEvent event
, int x
, int y
)
409 g_signal_emit(widget
, signals
[SIG_CLICKED
], 0, event
, x
, y
, &ret
);
414 gnt_widget_expose(GntWidget
*widget
, int x
, int y
, int width
, int height
)
416 g_signal_emit(widget
, signals
[SIG_EXPOSE
], 0, x
, y
, width
, height
);
420 gnt_widget_hide(GntWidget
*widget
)
422 g_signal_emit(widget
, signals
[SIG_HIDE
], 0);
423 wbkgdset(widget
->window
, '\0' | gnt_color_pair(GNT_COLOR_NORMAL
));
425 /* XXX: I have no clue why, but this seemed to be necessary. */
426 if (gnt_widget_has_shadow(widget
))
427 mvwvline(widget
->window
, 1, widget
->priv
.width
, ' ', widget
->priv
.height
);
429 gnt_screen_release(widget
);
430 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_INVISIBLE
);
431 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_MAPPED
);
435 gnt_widget_set_position(GntWidget
*wid
, int x
, int y
)
437 g_signal_emit(wid
, signals
[SIG_POSITION
], 0, x
, y
);
438 /* XXX: Need to install properties for these and g_object_notify */
444 gnt_widget_get_position(GntWidget
*wid
, int *x
, int *y
)
453 gnt_widget_size_request(GntWidget
*widget
)
455 g_signal_emit(widget
, signals
[SIG_SIZE_REQUEST
], 0);
459 gnt_widget_get_size(GntWidget
*wid
, int *width
, int *height
)
461 gboolean shadow
= TRUE
;
462 if (!gnt_widget_has_shadow(wid
))
466 *width
= wid
->priv
.width
+ shadow
;
468 *height
= wid
->priv
.height
+ shadow
;
473 init_widget(GntWidget
*widget
)
475 gboolean shadow
= TRUE
;
477 if (!gnt_widget_has_shadow(widget
))
480 wbkgd(widget
->window
, gnt_color_pair(GNT_COLOR_NORMAL
));
481 werase(widget
->window
);
483 if (!(GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_NO_BORDER
))
485 /* - This is ugly. */
486 /* - What's your point? */
487 mvwvline(widget
->window
, 0, 0, ACS_VLINE
| gnt_color_pair(GNT_COLOR_NORMAL
), widget
->priv
.height
);
488 mvwvline(widget
->window
, 0, widget
->priv
.width
- 1,
489 ACS_VLINE
| gnt_color_pair(GNT_COLOR_NORMAL
), widget
->priv
.height
);
490 mvwhline(widget
->window
, widget
->priv
.height
- 1, 0,
491 ACS_HLINE
| gnt_color_pair(GNT_COLOR_NORMAL
), widget
->priv
.width
);
492 mvwhline(widget
->window
, 0, 0, ACS_HLINE
| gnt_color_pair(GNT_COLOR_NORMAL
), widget
->priv
.width
);
493 mvwaddch(widget
->window
, 0, 0, ACS_ULCORNER
| gnt_color_pair(GNT_COLOR_NORMAL
));
494 mvwaddch(widget
->window
, 0, widget
->priv
.width
- 1,
495 ACS_URCORNER
| gnt_color_pair(GNT_COLOR_NORMAL
));
496 mvwaddch(widget
->window
, widget
->priv
.height
- 1, 0,
497 ACS_LLCORNER
| gnt_color_pair(GNT_COLOR_NORMAL
));
498 mvwaddch(widget
->window
, widget
->priv
.height
- 1, widget
->priv
.width
- 1,
499 ACS_LRCORNER
| gnt_color_pair(GNT_COLOR_NORMAL
));
504 wbkgdset(widget
->window
, '\0' | gnt_color_pair(GNT_COLOR_SHADOW
));
505 mvwvline(widget
->window
, 1, widget
->priv
.width
, ' ', widget
->priv
.height
);
506 mvwhline(widget
->window
, widget
->priv
.height
, 1, ' ', widget
->priv
.width
);
511 gnt_widget_set_size(GntWidget
*widget
, int width
, int height
)
515 if (gnt_widget_has_shadow(widget
))
521 width
= widget
->priv
.width
;
523 height
= widget
->priv
.height
;
525 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_MAPPED
))
527 ret
= gnt_widget_confirm_size(widget
, width
, height
);
532 gboolean shadow
= TRUE
;
535 if (!gnt_widget_has_shadow(widget
))
538 oldw
= widget
->priv
.width
;
539 oldh
= widget
->priv
.height
;
541 widget
->priv
.width
= width
;
542 widget
->priv
.height
= height
;
543 if (width
+ shadow
>= getmaxx(widget
->window
) || height
+ shadow
>= getmaxy(widget
->window
)) {
544 delwin(widget
->window
);
545 widget
->window
= newpad(height
+ 20, width
+ 20);
548 g_signal_emit(widget
, signals
[SIG_SIZE_CHANGED
], 0, oldw
, oldh
);
554 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_MAPPED
))
557 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_MAPPED
);
564 gnt_widget_set_focus(GntWidget
*widget
, gboolean set
)
566 if (!(GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_CAN_TAKE_FOCUS
))
569 if (set
&& !GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_HAS_FOCUS
))
571 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_HAS_FOCUS
);
572 g_signal_emit(widget
, signals
[SIG_GIVE_FOCUS
], 0);
574 else if (!set
&& GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_HAS_FOCUS
))
576 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_HAS_FOCUS
);
577 g_signal_emit(widget
, signals
[SIG_LOST_FOCUS
], 0);
585 void gnt_widget_set_name(GntWidget
*widget
, const char *name
)
587 g_free(widget
->priv
.name
);
588 widget
->priv
.name
= g_strdup(name
);
591 const char *gnt_widget_get_name(GntWidget
*widget
)
593 return widget
->priv
.name
;
596 void gnt_widget_activate(GntWidget
*widget
)
598 g_signal_emit(widget
, signals
[SIG_ACTIVATE
], 0);
602 update_queue_callback(gpointer data
)
604 GntWidget
*widget
= GNT_WIDGET(data
);
606 if (!g_object_get_data(G_OBJECT(widget
), "gnt:queue_update"))
608 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_MAPPED
))
609 gnt_screen_update(widget
);
610 g_object_set_data(G_OBJECT(widget
), "gnt:queue_update", NULL
);
614 void gnt_widget_queue_update(GntWidget
*widget
)
616 if (widget
->window
== NULL
)
618 while (widget
->parent
)
619 widget
= widget
->parent
;
621 if (!g_object_get_data(G_OBJECT(widget
), "gnt:queue_update"))
623 int id
= g_timeout_add(0, update_queue_callback
, widget
);
624 g_object_set_data_full(G_OBJECT(widget
), "gnt:queue_update", GINT_TO_POINTER(id
),
625 (GDestroyNotify
)g_source_remove
);
629 gboolean
gnt_widget_confirm_size(GntWidget
*widget
, int width
, int height
)
631 gboolean ret
= FALSE
;
632 g_signal_emit(widget
, signals
[SIG_CONFIRM_SIZE
], 0, width
, height
, &ret
);
636 void gnt_widget_set_visible(GntWidget
*widget
, gboolean set
)
639 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_INVISIBLE
);
641 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_INVISIBLE
);
644 gboolean
gnt_widget_has_shadow(GntWidget
*widget
)
646 return (!GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_SHADOW
) &&
647 gnt_style_get_bool(GNT_STYLE_SHADOW
, FALSE
));