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 "gntinternal.h"
26 #include "gntwidget.h"
28 #include "gntmarshal.h"
52 static GObjectClass
*parent_class
= NULL
;
53 static guint signals
[SIGS
] = { 0 };
55 static void init_widget(GntWidget
*widget
);
58 gnt_widget_init(GTypeInstance
*instance
, gpointer
class)
60 GntWidget
*widget
= GNT_WIDGET(instance
);
61 widget
->priv
.name
= NULL
;
66 gnt_widget_map(GntWidget
*widget
)
68 /* Get some default size for the widget */
70 g_signal_emit(widget
, signals
[SIG_MAP
], 0);
71 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_MAPPED
);
75 gnt_widget_dispose(GObject
*obj
)
77 GntWidget
*self
= GNT_WIDGET(obj
);
78 g_signal_emit(self
, signals
[SIG_DESTROY
], 0);
79 parent_class
->dispose(obj
);
84 gnt_widget_focus_change(GntWidget
*widget
)
86 if (GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_MAPPED
)
87 gnt_widget_draw(widget
);
91 gnt_widget_dummy_confirm_size(GntWidget
*widget
, int width
, int height
)
94 if (width
< widget
->priv
.minw
|| height
< widget
->priv
.minh
)
96 shadow
= gnt_widget_has_shadow(widget
);
97 if (widget
->priv
.width
+ shadow
!= width
&& !GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_GROW_X
))
99 if (widget
->priv
.height
+ shadow
!= height
&& !GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_GROW_Y
))
105 context_menu(GntBindable
*bind
, GList
*null
)
107 gboolean ret
= FALSE
;
108 g_signal_emit(bind
, signals
[SIG_CONTEXT_MENU
], 0, &ret
);
113 gnt_widget_class_init(GntWidgetClass
*klass
)
115 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
117 parent_class
= g_type_class_peek_parent(klass
);
119 obj_class
->dispose
= gnt_widget_dispose
;
121 klass
->destroy
= gnt_widget_destroy
;
122 klass
->show
= gnt_widget_show
;
123 klass
->draw
= gnt_widget_draw
;
124 klass
->expose
= gnt_widget_expose
;
125 klass
->map
= gnt_widget_map
;
126 klass
->lost_focus
= gnt_widget_focus_change
;
127 klass
->gained_focus
= gnt_widget_focus_change
;
128 klass
->confirm_size
= gnt_widget_dummy_confirm_size
;
130 klass
->key_pressed
= NULL
;
131 klass
->activate
= NULL
;
132 klass
->clicked
= NULL
;
134 signals
[SIG_DESTROY
] =
135 g_signal_new("destroy",
136 G_TYPE_FROM_CLASS(klass
),
138 G_STRUCT_OFFSET(GntWidgetClass
, destroy
),
140 g_cclosure_marshal_VOID__VOID
,
142 signals
[SIG_GIVE_FOCUS
] =
143 g_signal_new("gained-focus",
144 G_TYPE_FROM_CLASS(klass
),
146 G_STRUCT_OFFSET(GntWidgetClass
, gained_focus
),
148 g_cclosure_marshal_VOID__VOID
,
150 signals
[SIG_LOST_FOCUS
] =
151 g_signal_new("lost-focus",
152 G_TYPE_FROM_CLASS(klass
),
154 G_STRUCT_OFFSET(GntWidgetClass
, lost_focus
),
156 g_cclosure_marshal_VOID__VOID
,
158 signals
[SIG_ACTIVATE
] =
159 g_signal_new("activate",
160 G_TYPE_FROM_CLASS(klass
),
162 G_STRUCT_OFFSET(GntWidgetClass
, activate
),
164 g_cclosure_marshal_VOID__VOID
,
168 G_TYPE_FROM_CLASS(klass
),
170 G_STRUCT_OFFSET(GntWidgetClass
, map
),
172 g_cclosure_marshal_VOID__VOID
,
176 G_TYPE_FROM_CLASS(klass
),
178 G_STRUCT_OFFSET(GntWidgetClass
, draw
),
180 g_cclosure_marshal_VOID__VOID
,
184 G_TYPE_FROM_CLASS(klass
),
186 G_STRUCT_OFFSET(GntWidgetClass
, hide
),
188 g_cclosure_marshal_VOID__VOID
,
190 signals
[SIG_EXPOSE
] =
191 g_signal_new("expose",
192 G_TYPE_FROM_CLASS(klass
),
194 G_STRUCT_OFFSET(GntWidgetClass
, expose
),
196 gnt_closure_marshal_VOID__INT_INT_INT_INT
,
197 G_TYPE_NONE
, 4, G_TYPE_INT
, G_TYPE_INT
, G_TYPE_INT
, G_TYPE_INT
);
198 signals
[SIG_POSITION
] =
199 g_signal_new("position-set",
200 G_TYPE_FROM_CLASS(klass
),
202 G_STRUCT_OFFSET(GntWidgetClass
, set_position
),
204 gnt_closure_marshal_VOID__INT_INT
,
205 G_TYPE_NONE
, 2, G_TYPE_INT
, G_TYPE_INT
);
206 signals
[SIG_SIZE_REQUEST
] =
207 g_signal_new("size_request",
208 G_TYPE_FROM_CLASS(klass
),
210 G_STRUCT_OFFSET(GntWidgetClass
, size_request
),
212 g_cclosure_marshal_VOID__VOID
,
214 signals
[SIG_SIZE_CHANGED
] =
215 g_signal_new("size_changed",
216 G_TYPE_FROM_CLASS(klass
),
218 G_STRUCT_OFFSET(GntWidgetClass
, size_changed
),
220 gnt_closure_marshal_VOID__INT_INT
,
221 G_TYPE_NONE
, 2, G_TYPE_INT
, G_TYPE_INT
);
222 signals
[SIG_CONFIRM_SIZE
] =
223 g_signal_new("confirm_size",
224 G_TYPE_FROM_CLASS(klass
),
226 G_STRUCT_OFFSET(GntWidgetClass
, confirm_size
),
228 gnt_closure_marshal_BOOLEAN__INT_INT
,
229 G_TYPE_BOOLEAN
, 2, G_TYPE_INT
, G_TYPE_INT
);
230 signals
[SIG_KEY_PRESSED
] =
231 g_signal_new("key_pressed",
232 G_TYPE_FROM_CLASS(klass
),
234 G_STRUCT_OFFSET(GntWidgetClass
, key_pressed
),
235 gnt_boolean_handled_accumulator
, NULL
,
236 gnt_closure_marshal_BOOLEAN__STRING
,
237 G_TYPE_BOOLEAN
, 1, G_TYPE_STRING
);
239 signals
[SIG_CLICKED
] =
240 g_signal_new("clicked",
241 G_TYPE_FROM_CLASS(klass
),
243 G_STRUCT_OFFSET(GntWidgetClass
, clicked
),
244 gnt_boolean_handled_accumulator
, NULL
,
245 gnt_closure_marshal_BOOLEAN__INT_INT_INT
,
246 G_TYPE_BOOLEAN
, 3, G_TYPE_INT
, G_TYPE_INT
, G_TYPE_INT
);
248 signals
[SIG_CONTEXT_MENU
] =
249 g_signal_new("context-menu",
250 G_TYPE_FROM_CLASS(klass
),
253 gnt_boolean_handled_accumulator
, NULL
,
254 gnt_closure_marshal_BOOLEAN__VOID
,
257 /* This is relevant for all widgets */
258 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass
), "context-menu", context_menu
,
259 GNT_KEY_POPUP
, NULL
);
260 gnt_bindable_register_binding(GNT_BINDABLE_CLASS(klass
), "context-menu", GNT_KEY_F11
, NULL
);
261 gnt_bindable_register_binding(GNT_BINDABLE_CLASS(klass
), "context-menu", GNT_KEY_CTRL_X
, NULL
);
263 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass
), GNT_BINDABLE_CLASS(klass
));
267 /******************************************************************************
269 *****************************************************************************/
271 gnt_widget_get_gtype(void)
273 static GType type
= 0;
276 static const GTypeInfo info
= {
277 sizeof(GntWidgetClass
),
278 NULL
, /* base_init */
279 NULL
, /* base_finalize */
280 (GClassInitFunc
)gnt_widget_class_init
,
282 NULL
, /* class_data */
285 gnt_widget_init
, /* instance_init */
286 NULL
/* value_table */
289 type
= g_type_register_static(GNT_TYPE_BINDABLE
,
291 &info
, G_TYPE_FLAG_ABSTRACT
);
297 void gnt_widget_set_take_focus(GntWidget
*widget
, gboolean can
)
300 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
);
302 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
);
306 * gnt_widget_destroy:
307 * @obj: The #GntWidget instance.
309 * Emits the "destroy" signal notifying all reference holders that they
310 * should release @obj.
313 gnt_widget_destroy(GntWidget
*obj
)
315 g_return_if_fail(GNT_IS_WIDGET(obj
));
317 if(!(GNT_WIDGET_FLAGS(obj
) & GNT_WIDGET_DESTROYING
)) {
318 GNT_WIDGET_SET_FLAGS(obj
, GNT_WIDGET_DESTROYING
);
319 gnt_widget_hide(obj
);
321 g_object_run_dispose(G_OBJECT(obj
));
327 gnt_widget_show(GntWidget
*widget
)
329 gnt_widget_draw(widget
);
330 gnt_screen_occupy(widget
);
334 gnt_widget_draw(GntWidget
*widget
)
336 /* Draw the widget */
337 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_DRAWING
))
340 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_DRAWING
);
341 if (!(GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_MAPPED
)) {
342 gnt_widget_map(widget
);
345 if (widget
->window
== NULL
)
348 int x
, y
, maxx
, maxy
, w
, h
;
350 gboolean shadow
= TRUE
;
352 if (!gnt_widget_has_shadow(widget
))
357 w
= oldw
= widget
->priv
.width
+ shadow
;
358 h
= oldh
= widget
->priv
.height
+ shadow
;
360 getmaxyx(stdscr
, maxy
, maxx
);
361 maxy
-= 1; /* room for the taskbar */
366 x
= MAX(0, maxx
- w
);
368 y
= MAX(0, maxy
- h
);
375 if (w
!= oldw
|| h
!= oldh
) {
376 widget
->priv
.width
= w
- shadow
;
377 widget
->priv
.height
= h
- shadow
;
378 g_signal_emit(widget
, signals
[SIG_SIZE_CHANGED
], 0, oldw
, oldh
);
381 widget
->window
= newpad(widget
->priv
.height
+ 20, widget
->priv
.width
+ 20); /* XXX: */
386 g_signal_emit(widget
, signals
[SIG_DRAW
], 0);
387 gnt_widget_queue_update(widget
);
388 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_DRAWING
);
392 gnt_widget_key_pressed(GntWidget
*widget
, const char *keys
)
395 if (!GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_CAN_TAKE_FOCUS
))
398 if (!GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_DISABLE_ACTIONS
) &&
399 gnt_bindable_perform_action_key(GNT_BINDABLE(widget
), keys
))
402 keys
= gnt_bindable_remap_keys(GNT_BINDABLE(widget
), keys
);
403 g_signal_emit(widget
, signals
[SIG_KEY_PRESSED
], 0, keys
, &ret
);
408 gnt_widget_clicked(GntWidget
*widget
, GntMouseEvent event
, int x
, int y
)
411 g_signal_emit(widget
, signals
[SIG_CLICKED
], 0, event
, x
, y
, &ret
);
412 if (!ret
&& event
== GNT_RIGHT_MOUSE_DOWN
)
413 ret
= gnt_bindable_perform_action_named(GNT_BINDABLE(widget
), "context-menu", NULL
);
418 gnt_widget_expose(GntWidget
*widget
, int x
, int y
, int width
, int height
)
420 g_signal_emit(widget
, signals
[SIG_EXPOSE
], 0, x
, y
, width
, height
);
424 gnt_widget_hide(GntWidget
*widget
)
426 g_signal_emit(widget
, signals
[SIG_HIDE
], 0);
427 wbkgdset(widget
->window
, '\0' | gnt_color_pair(GNT_COLOR_NORMAL
));
429 /* XXX: I have no clue why, but this seemed to be necessary. */
430 if (gnt_widget_has_shadow(widget
))
431 mvwvline(widget
->window
, 1, widget
->priv
.width
, ' ', widget
->priv
.height
);
433 gnt_screen_release(widget
);
434 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_INVISIBLE
);
435 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_MAPPED
);
439 gnt_widget_set_position(GntWidget
*wid
, int x
, int y
)
441 g_signal_emit(wid
, signals
[SIG_POSITION
], 0, x
, y
);
442 /* XXX: Need to install properties for these and g_object_notify */
448 gnt_widget_get_position(GntWidget
*wid
, int *x
, int *y
)
457 gnt_widget_size_request(GntWidget
*widget
)
459 g_signal_emit(widget
, signals
[SIG_SIZE_REQUEST
], 0);
463 gnt_widget_get_size(GntWidget
*wid
, int *width
, int *height
)
465 gboolean shadow
= TRUE
;
466 if (!gnt_widget_has_shadow(wid
))
470 *width
= wid
->priv
.width
+ shadow
;
472 *height
= wid
->priv
.height
+ shadow
;
476 init_widget(GntWidget
*widget
)
478 gboolean shadow
= TRUE
;
480 if (!gnt_widget_has_shadow(widget
))
483 wbkgd(widget
->window
, gnt_color_pair(GNT_COLOR_NORMAL
));
484 werase(widget
->window
);
486 if (!(GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_NO_BORDER
))
488 /* - This is ugly. */
489 /* - What's your point? */
490 mvwvline(widget
->window
, 0, 0, ACS_VLINE
| gnt_color_pair(GNT_COLOR_NORMAL
), widget
->priv
.height
);
491 mvwvline(widget
->window
, 0, widget
->priv
.width
- 1,
492 ACS_VLINE
| gnt_color_pair(GNT_COLOR_NORMAL
), widget
->priv
.height
);
493 mvwhline(widget
->window
, widget
->priv
.height
- 1, 0,
494 ACS_HLINE
| gnt_color_pair(GNT_COLOR_NORMAL
), widget
->priv
.width
);
495 mvwhline(widget
->window
, 0, 0, ACS_HLINE
| gnt_color_pair(GNT_COLOR_NORMAL
), widget
->priv
.width
);
496 mvwaddch(widget
->window
, 0, 0, ACS_ULCORNER
| gnt_color_pair(GNT_COLOR_NORMAL
));
497 mvwaddch(widget
->window
, 0, widget
->priv
.width
- 1,
498 ACS_URCORNER
| gnt_color_pair(GNT_COLOR_NORMAL
));
499 mvwaddch(widget
->window
, widget
->priv
.height
- 1, 0,
500 ACS_LLCORNER
| gnt_color_pair(GNT_COLOR_NORMAL
));
501 mvwaddch(widget
->window
, widget
->priv
.height
- 1, widget
->priv
.width
- 1,
502 ACS_LRCORNER
| gnt_color_pair(GNT_COLOR_NORMAL
));
507 wbkgdset(widget
->window
, '\0' | gnt_color_pair(GNT_COLOR_SHADOW
));
508 mvwvline(widget
->window
, 1, widget
->priv
.width
, ' ', widget
->priv
.height
);
509 mvwhline(widget
->window
, widget
->priv
.height
, 1, ' ', widget
->priv
.width
);
514 gnt_widget_set_size(GntWidget
*widget
, int width
, int height
)
518 if (gnt_widget_has_shadow(widget
))
524 width
= widget
->priv
.width
;
526 height
= widget
->priv
.height
;
528 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_MAPPED
))
530 ret
= gnt_widget_confirm_size(widget
, width
, height
);
535 gboolean shadow
= TRUE
;
538 if (!gnt_widget_has_shadow(widget
))
541 oldw
= widget
->priv
.width
;
542 oldh
= widget
->priv
.height
;
544 widget
->priv
.width
= width
;
545 widget
->priv
.height
= height
;
546 if (width
+ shadow
>= getmaxx(widget
->window
) || height
+ shadow
>= getmaxy(widget
->window
)) {
547 delwin(widget
->window
);
548 widget
->window
= newpad(height
+ 20, width
+ 20);
551 g_signal_emit(widget
, signals
[SIG_SIZE_CHANGED
], 0, oldw
, oldh
);
557 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_MAPPED
))
560 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_MAPPED
);
567 gnt_widget_set_focus(GntWidget
*widget
, gboolean set
)
569 if (!(GNT_WIDGET_FLAGS(widget
) & GNT_WIDGET_CAN_TAKE_FOCUS
))
572 if (set
&& !GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_HAS_FOCUS
))
574 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_HAS_FOCUS
);
575 g_signal_emit(widget
, signals
[SIG_GIVE_FOCUS
], 0);
577 else if (!set
&& GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_HAS_FOCUS
))
579 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_HAS_FOCUS
);
580 g_signal_emit(widget
, signals
[SIG_LOST_FOCUS
], 0);
588 void gnt_widget_set_name(GntWidget
*widget
, const char *name
)
590 g_free(widget
->priv
.name
);
591 widget
->priv
.name
= g_strdup(name
);
594 const char *gnt_widget_get_name(GntWidget
*widget
)
596 return widget
->priv
.name
;
599 void gnt_widget_activate(GntWidget
*widget
)
601 g_signal_emit(widget
, signals
[SIG_ACTIVATE
], 0);
605 update_queue_callback(gpointer data
)
607 GntWidget
*widget
= GNT_WIDGET(data
);
609 if (!g_object_get_data(G_OBJECT(widget
), "gnt:queue_update"))
611 if (GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_MAPPED
))
612 gnt_screen_update(widget
);
613 g_object_set_data(G_OBJECT(widget
), "gnt:queue_update", NULL
);
617 void gnt_widget_queue_update(GntWidget
*widget
)
619 if (widget
->window
== NULL
)
621 while (widget
->parent
)
622 widget
= widget
->parent
;
624 if (!g_object_get_data(G_OBJECT(widget
), "gnt:queue_update"))
626 int id
= g_timeout_add(0, update_queue_callback
, widget
);
627 g_object_set_data_full(G_OBJECT(widget
), "gnt:queue_update", GINT_TO_POINTER(id
),
628 (GDestroyNotify
)g_source_remove
);
632 gboolean
gnt_widget_confirm_size(GntWidget
*widget
, int width
, int height
)
634 gboolean ret
= FALSE
;
635 g_signal_emit(widget
, signals
[SIG_CONFIRM_SIZE
], 0, width
, height
, &ret
);
639 void gnt_widget_set_visible(GntWidget
*widget
, gboolean set
)
642 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_INVISIBLE
);
644 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_INVISIBLE
);
647 gboolean
gnt_widget_has_shadow(GntWidget
*widget
)
649 return (!GNT_WIDGET_IS_FLAG_SET(widget
, GNT_WIDGET_NO_SHADOW
) &&
650 gnt_style_get_bool(GNT_STYLE_SHADOW
, FALSE
));