]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/dope/server/common/button.c
Inital import
[l4.git] / l4 / pkg / dope / server / common / button.c
1 /*
2  * \brief   DOpE Button widget module
3  * \date    2002-11-13
4  * \author  Norman Feske <nf2@inf.tu-dresden.de>
5  */
6
7 /*
8  * Copyright (C) 2002-2004  Norman Feske  <nf2@os.inf.tu-dresden.de>
9  * Technische Universitaet Dresden, Operating Systems Research Group
10  *
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.
14  */
15
16 struct button;
17 #define WIDGET struct button
18
19 #include "dopestd.h"
20 #include "button.h"
21 #include "gfx_macros.h"
22 #include "widget_data.h"
23 #include "widget_help.h"
24 #include "fontman.h"
25 #include "script.h"
26 #include "widman.h"
27 #include "userstate.h"
28 #include "messenger.h"
29 #include "keycodes.h"
30
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;
37
38 struct button_data {
39         char  *text;
40         s16    style;
41         s16    font_id;
42         s16    tx, ty;                    /* text position inside the button */
43         s16    pad_x, pad_y;
44         u16    flags;
45         void (*click)  (WIDGET *);
46         void (*release)(WIDGET *);
47 };
48
49 #define BUTTON_FLAGS_FREE_W 0x01     /* allow all widths  */
50 #define BUTTON_FLAGS_FREE_H 0x02     /* allow all heights */
51
52 static GFX_CONTAINER *normal_img;
53 static GFX_CONTAINER *focus_img;
54 static GFX_CONTAINER *actwin_img;
55
56 extern int config_clackcommit;
57
58 int init_button(struct dope_services *d);
59
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)
64
65
66 /**********************************
67  *** FUNCTIONS FOR INTERNAL USE ***
68  **********************************/
69
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;
74 }
75
76
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);
82 }
83
84
85 static inline void draw_raised_frame(GFX_CONTAINER *d, s32 x, s32 y, s32 w, s32 h) {
86
87         /* outer frame */
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);
92
93         /* inner frame */
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);
100
101         /* spot */
102         gfx->draw_hline(d, x + 1, y + 1, 1, WHITE_SOLID);
103         gfx->draw_vline(d, x + 1, y + 1, 1, WHITE_SOLID);
104 }
105
106
107 static inline void draw_pressed_frame(GFX_CONTAINER *d, s32 x, s32 y, s32 w, s32 h) {
108
109         /* outer frame */
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);
114
115         /* inner frame */
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);
120 }
121
122
123 static inline void draw_kfocus_frame(GFX_CONTAINER *d, s32 x, s32 y, s32 w, s32 h) {
124
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);
129 }
130
131
132 /******************************
133  *** GENERAL WIDGET METHODS ***
134  ******************************/
135
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;
139
140         /* we hit the origin, acknowledge visibility request */
141         if (origin == b) return 1;
142
143         /* only draw if no origin is specified */
144         if (origin) return 0;
145
146         x += b->wd->x;
147         y += b->wd->y;
148
149         gfx->push_clipping(ds, x, y, w, h);
150
151         x += b->bd->pad_x;
152         y += b->bd->pad_y;
153         w -= b->bd->pad_x*2;
154         h -= b->bd->pad_y*2;
155
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);
159
160         if (b->wd->flags & WID_FLAGS_MFOCUS) {
161                 gfx->draw_img(ds, x, y, w, h, focus_img, 255);
162
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);
166                 }
167         } else {
168                 if (b->bd->style == 2) {
169                         gfx->draw_img(ds, x, y, w, h, actwin_img, 255);
170                 } else {
171                         gfx->draw_img(ds, x, y, w, h, normal_img, 255);
172                 }
173         }
174
175         if (b->wd->flags & WID_FLAGS_STATE) {
176                 draw_pressed_frame(ds, x, y, w, h);
177         } else {
178                 if (!(b->wd->flags & WID_FLAGS_MFOCUS)) {
179                         draw_raised_frame(ds, x, y, w, h);
180                 }
181         }
182
183         tx += x; ty += y;
184         if (b->wd->flags & WID_FLAGS_MFOCUS) {
185                 tx += 1; ty += 1;
186         }
187         if (b->wd->flags & WID_FLAGS_STATE) {
188                 tx += 2; ty += 2;
189         }
190         gfx->push_clipping(ds, x + 2, y + 2, w - 4, h - 4);
191
192         if (b->bd->text) {
193                 switch (b->bd->style) {
194                         case 1:
195                                 gfx->draw_string(ds, tx, ty, BLACK_SOLID, 0, b->bd->font_id, b->bd->text);
196                                 break;
197                         default:
198                                 gfx->draw_string(ds, tx, ty, WHITE_SOLID, 0, b->bd->font_id, b->bd->text);
199                                 break;
200                 }
201         }
202
203         gfx->pop_clipping(ds);
204         gfx->pop_clipping(ds);
205
206         return 1;
207 }
208
209
210 static void but_untouch_callback(BUTTON *b, int dx, int dy)
211 {
212   (void)dx;
213   (void)dy;
214   unsigned long clack_msg, commit_msg = 0;
215
216   if (!b->gen->get_state(b)) return;
217
218   clack_msg = b->gen->get_bind_msg(b, "clack");
219   if (clack_msg)
220     msg->send_action_event(b->gen->get_app_id(b), "clack", clack_msg);
221
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);
225 }
226
227
228 static void (*orig_handle_event) (BUTTON *b, EVENT *e, WIDGET *from);
229 static void but_handle_event(BUTTON *b, EVENT *e, WIDGET *from)
230 {
231   unsigned long click_msg, clack_msg, commit_msg = 0;
232   if (e->type == L4RE_EV_KEY)
233     switch (e->value)
234       {
235
236       case 1: // press
237
238         /* check for mouse button event */
239         if ((e->code  != DOPE_BTN_MOUSE) && (e->code != DOPE_KEY_SPACE))
240           break;
241
242         if (b->bd->click) b->bd->click(b);
243
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");
247
248         if (click_msg || clack_msg || commit_msg)
249           userstate->touch(b, NULL, but_untouch_callback);
250
251         if (click_msg)
252           msg->send_action_event(b->gen->get_app_id(b), "click", click_msg);
253
254         if (commit_msg && !config_clackcommit)
255           msg->send_action_event(b->gen->get_app_id(b), "commit", commit_msg);
256
257         if (click_msg || clack_msg || commit_msg || b->bd->click)
258           return;
259         break;
260
261       case 0: // RELEASE
262         /* check for mouse button event */
263         if (e->code  == DOPE_BTN_MOUSE && b->bd->release)
264           b->bd->release(b);
265         break;
266       }
267
268   if (b->bd->click)
269     return;
270
271   orig_handle_event(b, e, from);
272 }
273
274
275 /*** DETERMINE MIN/MAX SIZE OF A BUTTON ***
276  *
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.
281  */
282 static void but_calc_minmax(BUTTON *b) {
283
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++;
288         }
289
290         /* the min/max height of the button depends on its text */
291         if (b->bd->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;
296                 b->wd->max_w = 9999;
297                 b->wd->max_h = b->wd->min_h;
298         } else {
299                 b->wd->min_w = b->wd->min_h = 0;
300                 b->wd->max_w = b->wd->max_h = 9999;
301         }
302
303         /* do not constrain the min/max properties for free buttons */
304         if (b->bd->flags & BUTTON_FLAGS_FREE_W) {
305                 b->wd->min_w = 0;
306                 b->wd->max_w = 9999;
307         }
308         if (b->bd->flags & BUTTON_FLAGS_FREE_H) {
309                 b->wd->min_h = 0;
310                 b->wd->max_h = 9999;
311         }
312 }
313
314
315 /*** UPDATE POSITION AND SIZE OF BUTTON ***
316  *
317  * When size changes, the text position must be updated.
318  */
319 static void (*orig_updatepos) (BUTTON *b);
320 static void but_updatepos(BUTTON *b) {
321         update_text_pos(b);
322         orig_updatepos(b);
323 }
324
325
326 /*** FREE BUTTON WIDGET DATA ***/
327 static void but_free_data(BUTTON *b) {
328         if (b->bd->text) free(b->bd->text);
329 }
330
331
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);
337 }
338
339
340 /*** RETURN WIDGET TYPE IDENTIFIER ***/
341 static char *but_get_type(BUTTON *b) {
342     (void)b;
343         return "Button";
344 }
345
346
347 /*******************************
348  *** BUTTON SPECIFIC METHODS ***
349  *******************************/
350
351 static void but_set_text(BUTTON *b, char *new_txt) {
352         if (b->bd->text) {
353                 free(b->bd->text);
354         }
355         b->bd->text = dope_strdup(new_txt);
356
357         /*
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.
362          */
363         if (!(b->bd->flags & BUTTON_FLAGS_FREE_W)
364          || !(b->bd->flags & BUTTON_FLAGS_FREE_H))
365                 b->wd->update |= WID_UPDATE_MINMAX;
366         else
367                 b->wd->update |= WID_UPDATE_REFRESH;
368 }
369
370
371 static char *but_get_text(BUTTON *b) {
372         return b->bd->text;
373 }
374
375
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;
379 }
380
381
382 static s32 but_get_font(BUTTON *b) {
383         return b->bd->font_id;
384 }
385
386
387 static void but_set_style(BUTTON *b, s32 style) {
388         b->bd->style = style;
389         b->wd->update |= WID_UPDATE_REFRESH;
390 }
391
392
393 static s32 but_get_style(BUTTON *b) {
394         return b->bd->font_id;
395 }
396
397
398 static void but_set_click(BUTTON *b, void (*callback)(BUTTON *b)) {
399         b->bd->click = callback;
400 }
401
402
403 static void but_set_release(BUTTON *b, void (*callback)(BUTTON *b)) {
404         b->bd->release = callback;
405 }
406
407
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;
412 }
413
414
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;
419 }
420
421
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;
426 }
427
428
429 static int but_get_pad_x(BUTTON *b) {
430         return b->bd->pad_x;
431 }
432
433
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;
438 }
439
440
441 static int but_get_pad_y(BUTTON *b) {
442         return b->bd->pad_y;
443 }
444
445
446 static struct widget_methods gen_methods;
447 static struct button_methods but_methods = {
448         but_set_text,
449         but_get_text,
450         but_set_font,
451         but_get_font,
452         but_set_style,
453         but_get_style,
454         but_set_click,
455         but_set_release,
456         but_set_free_w,
457         but_set_free_h,
458         but_set_pad_x,
459         but_set_pad_y,
460 };
461
462
463
464 /*************************
465  *** SERVICE FUNCTIONS ***
466  *************************/
467
468 static BUTTON *create(void) {
469
470         BUTTON *new = ALLOC_WIDGET(struct button);
471         SET_WIDGET_DEFAULTS(new, struct button, &but_methods);
472
473         /* set button specific attributes */
474         new->bd->style  = 1;
475         new->bd->pad_x  = new->bd->pad_y = 2;
476         new->wd->flags |= WID_FLAGS_HIGHLIGHT | WID_FLAGS_SELECTABLE;
477
478         update_text_pos(new);
479         but_calc_minmax(new);
480         return new;
481 }
482
483
484
485 /****************************************
486  *** SERVICE STRUCTURE OF THIS MODULE ***
487  ****************************************/
488
489 static struct button_services services = {
490         create
491 };
492
493
494
495 /**************************
496  *** MODULE ENTRY POINT ***
497  **************************/
498
499 static void build_script_lang(void) {
500         void *widtype;
501
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);
507 }
508
509
510 int init_button(struct dope_services *d) {
511
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");
518
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);
522
523         /* define general widget functions */
524         widman->default_widget_methods(&gen_methods);
525
526         orig_updatepos    = gen_methods.updatepos;
527         orig_handle_event = gen_methods.handle_event;
528         orig_bind         = gen_methods.bind;
529
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;
537
538         build_script_lang();
539
540         d->register_module("Button 1.0", &services);
541         return 1;
542 }