2 * \brief DOpE Button widget module
4 * \author Norman Feske <nf2@inf.tu-dresden.de>
8 * Copyright (C) 2002-2004 Norman Feske <nf2@os.inf.tu-dresden.de>
9 * Technische Universitaet Dresden, Operating Systems Research Group
11 * This file is part of the DOpE package, which is distributed under
12 * the terms of the GNU General Public Licence 2. Please see the
13 * COPYING file for details.
17 #define WIDGET struct button
21 #include "gfx_macros.h"
22 #include "widget_data.h"
23 #include "widget_help.h"
27 #include "userstate.h"
28 #include "messenger.h"
31 static struct widman_services *widman;
32 static struct gfx_services *gfx;
33 static struct fontman_services *font;
34 static struct script_services *script;
35 static struct userstate_services *userstate;
36 static struct messenger_services *msg;
42 s16 tx, ty; /* text position inside the button */
45 void (*click) (WIDGET *);
46 void (*release)(WIDGET *);
49 #define BUTTON_FLAGS_FREE_W 0x01 /* allow all widths */
50 #define BUTTON_FLAGS_FREE_H 0x02 /* allow all heights */
52 static GFX_CONTAINER *normal_img;
53 static GFX_CONTAINER *focus_img;
54 static GFX_CONTAINER *actwin_img;
56 extern int config_clackcommit;
58 int init_button(struct dope_services *d);
60 #define BLACK_SOLID GFX_RGBA(0, 0, 0, 255)
61 #define BLACK_MIXED GFX_RGBA(0, 0, 0, 127)
62 #define WHITE_SOLID GFX_RGBA(255, 255, 255, 255)
63 #define WHITE_MIXED GFX_RGBA(255, 255, 255, 127)
66 /**********************************
67 *** FUNCTIONS FOR INTERNAL USE ***
68 **********************************/
70 static void update_text_pos(BUTTON *b) {
71 if (!b->bd->text) return;
72 b->bd->tx = (b->wd->w - 2*b->bd->pad_x - font->calc_str_width (b->bd->font_id, b->bd->text))>>1;
73 b->bd->ty = (b->wd->h - 2*b->bd->pad_y - font->calc_str_height(b->bd->font_id, b->bd->text))>>1;
77 static inline void draw_focus_frame(GFX_CONTAINER *d, s32 x, s32 y, s32 w, s32 h) {
78 gfx->draw_hline(d, x, y, w, WHITE_MIXED);
79 gfx->draw_vline(d, x, y, h, WHITE_MIXED);
80 gfx->draw_hline(d, x, y + h - 1, w, BLACK_SOLID);
81 gfx->draw_vline(d, x + w - 1, y, h, BLACK_SOLID);
85 static inline void draw_raised_frame(GFX_CONTAINER *d, s32 x, s32 y, s32 w, s32 h) {
88 gfx->draw_hline(d, x, y, w, WHITE_MIXED);
89 gfx->draw_vline(d, x, y, h, WHITE_MIXED);
90 gfx->draw_hline(d, x, y + h - 1, w, BLACK_SOLID);
91 gfx->draw_vline(d, x + w - 1, y, h, BLACK_SOLID);
94 gfx->draw_hline(d, x + 1, y + 1, w - 2, WHITE_MIXED);
95 gfx->draw_vline(d, x + 1, y + 1, h - 2, WHITE_MIXED);
96 gfx->draw_hline(d, x + 1, y + 1, w - 2, WHITE_MIXED);
97 gfx->draw_vline(d, x + 1, y + 1, h - 2, WHITE_MIXED);
98 gfx->draw_hline(d, x + 1, y + h - 2, w - 2, BLACK_MIXED);
99 gfx->draw_vline(d, x + w - 2, y + 1, h - 2, BLACK_MIXED);
102 gfx->draw_hline(d, x + 1, y + 1, 1, WHITE_SOLID);
103 gfx->draw_vline(d, x + 1, y + 1, 1, WHITE_SOLID);
107 static inline void draw_pressed_frame(GFX_CONTAINER *d, s32 x, s32 y, s32 w, s32 h) {
110 gfx->draw_hline(d, x, y, w, BLACK_SOLID);
111 gfx->draw_vline(d, x, y, h, BLACK_SOLID);
112 gfx->draw_hline(d, x, y + h - 1, w, WHITE_MIXED);
113 gfx->draw_vline(d, x + w - 1, y, h, WHITE_MIXED);
116 gfx->draw_hline(d, x + 1, y + 1, w - 2, BLACK_SOLID);
117 gfx->draw_vline(d, x + 1, y + 1, h - 2, BLACK_SOLID);
118 gfx->draw_hline(d, x + 1, y + h - 2, w - 2, WHITE_SOLID);
119 gfx->draw_vline(d, x + w - 2, y + 1, h - 2, WHITE_SOLID);
123 static inline void draw_kfocus_frame(GFX_CONTAINER *d, s32 x, s32 y, s32 w, s32 h) {
125 gfx->draw_hline(d, x + 1, y, w - 2, BLACK_SOLID);
126 gfx->draw_vline(d, x, y, h, BLACK_SOLID);
127 gfx->draw_hline(d, x + 1, y + h - 1, w - 2, BLACK_SOLID);
128 gfx->draw_vline(d, x + w - 1, y, h, BLACK_SOLID);
132 /******************************
133 *** GENERAL WIDGET METHODS ***
134 ******************************/
136 static int but_draw(BUTTON *b, struct gfx_ds *ds, long x, long y, WIDGET *origin) {
137 int tx = b->bd->tx, ty = b->bd->ty;
138 int w = b->wd->w, h = b->wd->h;
140 /* we hit the origin, acknowledge visibility request */
141 if (origin == b) return 1;
143 /* only draw if no origin is specified */
144 if (origin) return 0;
149 gfx->push_clipping(ds, x, y, w, h);
156 /* draw keyboard focus frame if button is currently selected */
157 if (b->wd->flags & WID_FLAGS_KFOCUS)
158 draw_kfocus_frame(ds, x - 1, y - 1, w + 2, h + 2);
160 if (b->wd->flags & WID_FLAGS_MFOCUS) {
161 gfx->draw_img(ds, x, y, w, h, focus_img, 255);
163 if (!(b->wd->flags & WID_FLAGS_STATE)) {
164 gfx->draw_img(ds, x, y, w, h, focus_img, 255);
165 draw_focus_frame(ds, x, y, w, h);
168 if (b->bd->style == 2) {
169 gfx->draw_img(ds, x, y, w, h, actwin_img, 255);
171 gfx->draw_img(ds, x, y, w, h, normal_img, 255);
175 if (b->wd->flags & WID_FLAGS_STATE) {
176 draw_pressed_frame(ds, x, y, w, h);
178 if (!(b->wd->flags & WID_FLAGS_MFOCUS)) {
179 draw_raised_frame(ds, x, y, w, h);
184 if (b->wd->flags & WID_FLAGS_MFOCUS) {
187 if (b->wd->flags & WID_FLAGS_STATE) {
190 gfx->push_clipping(ds, x + 2, y + 2, w - 4, h - 4);
193 switch (b->bd->style) {
195 gfx->draw_string(ds, tx, ty, BLACK_SOLID, 0, b->bd->font_id, b->bd->text);
198 gfx->draw_string(ds, tx, ty, WHITE_SOLID, 0, b->bd->font_id, b->bd->text);
203 gfx->pop_clipping(ds);
204 gfx->pop_clipping(ds);
210 static void but_untouch_callback(BUTTON *b, int dx, int dy)
214 unsigned long clack_msg, commit_msg = 0;
216 if (!b->gen->get_state(b)) return;
218 clack_msg = b->gen->get_bind_msg(b, "clack");
220 msg->send_action_event(b->gen->get_app_id(b), "clack", clack_msg);
222 commit_msg = b->gen->get_bind_msg(b, "commit");
223 if (commit_msg && config_clackcommit)
224 msg->send_action_event(b->gen->get_app_id(b), "commit", commit_msg);
228 static void (*orig_handle_event) (BUTTON *b, EVENT *e, WIDGET *from);
229 static void but_handle_event(BUTTON *b, EVENT *e, WIDGET *from)
231 unsigned long click_msg, clack_msg, commit_msg = 0;
232 if (e->type == L4RE_EV_KEY)
238 /* check for mouse button event */
239 if ((e->code != DOPE_BTN_MOUSE) && (e->code != DOPE_KEY_SPACE))
242 if (b->bd->click) b->bd->click(b);
244 click_msg = b->gen->get_bind_msg(b, "click");
245 clack_msg = b->gen->get_bind_msg(b, "clack");
246 commit_msg = b->gen->get_bind_msg(b, "commit");
248 if (click_msg || clack_msg || commit_msg)
249 userstate->touch(b, NULL, but_untouch_callback);
252 msg->send_action_event(b->gen->get_app_id(b), "click", click_msg);
254 if (commit_msg && !config_clackcommit)
255 msg->send_action_event(b->gen->get_app_id(b), "commit", commit_msg);
257 if (click_msg || clack_msg || commit_msg || b->bd->click)
262 /* check for mouse button event */
263 if (e->code == DOPE_BTN_MOUSE && b->bd->release)
271 orig_handle_event(b, e, from);
275 /*** DETERMINE MIN/MAX SIZE OF A BUTTON ***
277 * The height and the min width of a Button depend on the used font and
278 * the Button text. For the DOpE-internal use of Buttons for the window
279 * controls there exist the FREE_W and FREE_H flags. When set, the size
280 * of a Button is completely free.
282 static void but_calc_minmax(BUTTON *b) {
284 /* ensure that there is enough padding to draw the focus frame */
285 if (b->wd->flags & WID_FLAGS_TAKEFOCUS) {
286 if (b->bd->pad_x < 1) b->bd->pad_x++;
287 if (b->bd->pad_y < 1) b->bd->pad_y++;
290 /* the min/max height of the button depends on its text */
292 b->wd->min_w = font->calc_str_width (b->bd->font_id, b->bd->text)
293 + b->bd->pad_x * 2 + 6;
294 b->wd->min_h = font->calc_str_height(b->bd->font_id, b->bd->text)
295 + b->bd->pad_y * 2 + 6;
297 b->wd->max_h = b->wd->min_h;
299 b->wd->min_w = b->wd->min_h = 0;
300 b->wd->max_w = b->wd->max_h = 9999;
303 /* do not constrain the min/max properties for free buttons */
304 if (b->bd->flags & BUTTON_FLAGS_FREE_W) {
308 if (b->bd->flags & BUTTON_FLAGS_FREE_H) {
315 /*** UPDATE POSITION AND SIZE OF BUTTON ***
317 * When size changes, the text position must be updated.
319 static void (*orig_updatepos) (BUTTON *b);
320 static void but_updatepos(BUTTON *b) {
326 /*** FREE BUTTON WIDGET DATA ***/
327 static void but_free_data(BUTTON *b) {
328 if (b->bd->text) free(b->bd->text);
332 /*** CATCH BIND CALLS TO ENABLE THE KEYBOARD FOCUS ***/
333 static void (*orig_bind) (BUTTON *b, char *bind_ident, unsigned long message);
334 static void but_bind(BUTTON *b, char *bind_ident, unsigned long message) {
335 b->wd->flags |= WID_FLAGS_TAKEFOCUS;
336 orig_bind(b, bind_ident, message);
340 /*** RETURN WIDGET TYPE IDENTIFIER ***/
341 static char *but_get_type(BUTTON *b) {
347 /*******************************
348 *** BUTTON SPECIFIC METHODS ***
349 *******************************/
351 static void but_set_text(BUTTON *b, char *new_txt) {
355 b->bd->text = dope_strdup(new_txt);
358 * If a button's size is completely free, a change
359 * of the text has no effect on the layout of the
360 * parent. In this case, it is enough to do a
361 * refresh instead of giving a MINMAX notification.
363 if (!(b->bd->flags & BUTTON_FLAGS_FREE_W)
364 || !(b->bd->flags & BUTTON_FLAGS_FREE_H))
365 b->wd->update |= WID_UPDATE_MINMAX;
367 b->wd->update |= WID_UPDATE_REFRESH;
371 static char *but_get_text(BUTTON *b) {
376 static void but_set_font(BUTTON *b, s32 font_id) {
377 b->bd->font_id = font_id;
378 b->wd->update |= WID_UPDATE_MINMAX;
382 static s32 but_get_font(BUTTON *b) {
383 return b->bd->font_id;
387 static void but_set_style(BUTTON *b, s32 style) {
388 b->bd->style = style;
389 b->wd->update |= WID_UPDATE_REFRESH;
393 static s32 but_get_style(BUTTON *b) {
394 return b->bd->font_id;
398 static void but_set_click(BUTTON *b, void (*callback)(BUTTON *b)) {
399 b->bd->click = callback;
403 static void but_set_release(BUTTON *b, void (*callback)(BUTTON *b)) {
404 b->bd->release = callback;
408 static void but_set_free_w(BUTTON *b, int free_w_flag) {
409 if (free_w_flag) b->bd->flags |= BUTTON_FLAGS_FREE_W;
410 else b->bd->flags &= ~BUTTON_FLAGS_FREE_W;
411 b->wd->update |= WID_UPDATE_MINMAX;
415 static void but_set_free_h(BUTTON *b, int free_h_flag) {
416 if (free_h_flag) b->bd->flags |= BUTTON_FLAGS_FREE_H;
417 else b->bd->flags &= ~BUTTON_FLAGS_FREE_H;
418 b->wd->update |= WID_UPDATE_MINMAX;
422 static void but_set_pad_x(BUTTON *b, int pad_x) {
423 if (pad_x < 0) return;
424 b->bd->pad_x = pad_x;
425 b->wd->update |= WID_UPDATE_MINMAX;
429 static int but_get_pad_x(BUTTON *b) {
434 static void but_set_pad_y(BUTTON *b, int pad_y) {
435 if (pad_y < 0) return;
436 b->bd->pad_y = pad_y;
437 b->wd->update |= WID_UPDATE_MINMAX;
441 static int but_get_pad_y(BUTTON *b) {
446 static struct widget_methods gen_methods;
447 static struct button_methods but_methods = {
464 /*************************
465 *** SERVICE FUNCTIONS ***
466 *************************/
468 static BUTTON *create(void) {
470 BUTTON *new = ALLOC_WIDGET(struct button);
471 SET_WIDGET_DEFAULTS(new, struct button, &but_methods);
473 /* set button specific attributes */
475 new->bd->pad_x = new->bd->pad_y = 2;
476 new->wd->flags |= WID_FLAGS_HIGHLIGHT | WID_FLAGS_SELECTABLE;
478 update_text_pos(new);
479 but_calc_minmax(new);
485 /****************************************
486 *** SERVICE STRUCTURE OF THIS MODULE ***
487 ****************************************/
489 static struct button_services services = {
495 /**************************
496 *** MODULE ENTRY POINT ***
497 **************************/
499 static void build_script_lang(void) {
502 widtype = script->reg_widget_type("Button", (void *(*)(void))create);
503 script->reg_widget_attrib(widtype, "string text", but_get_text, but_set_text, gen_methods.update);
504 script->reg_widget_attrib(widtype, "long padx", but_get_pad_x, but_set_pad_x, gen_methods.update);
505 script->reg_widget_attrib(widtype, "long pady", but_get_pad_y, but_set_pad_y, gen_methods.update);
506 widman->build_script_lang(widtype, &gen_methods);
510 int init_button(struct dope_services *d) {
512 widman = d->get_module("WidgetManager 1.0");
513 gfx = d->get_module("Gfx 1.0");
514 font = d->get_module("FontManager 1.0");
515 script = d->get_module("Script 1.0");
516 userstate = d->get_module("UserState 1.0");
517 msg = d->get_module("Messenger 1.0");
519 normal_img = gen_range_img(gfx, 128, 128, 128, 90, 90, 110);
520 focus_img = gen_range_img(gfx, 128, 128, 128, 100, 100, 140);
521 actwin_img = gen_range_img(gfx, 128, 128, 85, 110, 100, 90);
523 /* define general widget functions */
524 widman->default_widget_methods(&gen_methods);
526 orig_updatepos = gen_methods.updatepos;
527 orig_handle_event = gen_methods.handle_event;
528 orig_bind = gen_methods.bind;
530 gen_methods.get_type = but_get_type;
531 gen_methods.draw = but_draw;
532 gen_methods.updatepos = but_updatepos;
533 gen_methods.handle_event = but_handle_event;
534 gen_methods.calc_minmax = but_calc_minmax;
535 gen_methods.free_data = but_free_data;
536 gen_methods.bind = but_bind;
540 d->register_module("Button 1.0", &services);