blob: 9ee32ebfd3565796ca45e075fa9a0471b59fb1e6 [file] [log] [blame]
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001/*
2 * Copyright (c) 2002, Adam Dunkels.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution.
14 * 3. All advertising materials mentioning features or use of this
15 * software must display the following acknowledgement:
16 * This product includes software developed by Adam Dunkels.
17 * 4. The name of the author may not be used to endorse or promote
18 * products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * This file is part of the "ctk" console GUI toolkit for cc65
34 *
adamdunkels3cf116a2003-04-08 19:28:15 +000035 * $Id: ctk.c,v 1.6 2003/04/08 19:28:15 adamdunkels Exp $
adamdunkelsca9ddcb2003-03-19 14:13:31 +000036 *
37 */
38
adamdunkelsd07a5422003-04-05 12:22:35 +000039#include "cc.h"
adamdunkelsca9ddcb2003-03-19 14:13:31 +000040#include "ek.h"
41#include "dispatcher.h"
42#include "ctk.h"
43#include "ctk-draw.h"
44#include "ctk-conf.h"
45
46static unsigned char height, width;
47
48static unsigned char mode;
49
50static struct ctk_window desktop_window;
51static struct ctk_window *windows;
52static struct ctk_window *dialog;
53
54#if CTK_CONF_MENUS
55static struct ctk_menus menus;
56static struct ctk_menu *lastmenu;
57static struct ctk_menu desktopmenu;
58#endif /* CTK_CONF_MENUS */
59
60#ifndef NULL
61#define NULL (void *)0
62#endif /* NULL */
63
64
65#define REDRAW_NONE 0
66#define REDRAW_ALL 1
67#define REDRAW_FOCUS 2
68#define REDRAW_WIDGETS 4
69#define REDRAW_MENUS 8
70#define REDRAW_MENUPART 16
71
72#define MAX_REDRAWWIDGETS 4
73static unsigned char redraw;
74static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS];
75static unsigned char redraw_widgetptr;
76static unsigned char maxnitems;
77
78static unsigned char iconx, icony;
79#define ICONX_START (width - 5)
80#define ICONY_START 0
81#define ICONX_DELTA -8
82#define ICONY_DELTA 5
83#define ICONY_MAX (height - 4)
84
85static void idle(void);
86static void sighandler(ek_signal_t s, ek_data_t data);
87static struct dispatcher_proc p =
88 {DISPATCHER_PROC("CTK Contiki GUI", idle, sighandler, NULL)};
89static ek_id_t ctkid;
90
91
92ek_signal_t ctk_signal_keypress,
93 ctk_signal_timer,
94 ctk_signal_button_activate,
95 ctk_signal_button_hover,
96 ctk_signal_hyperlink_activate,
97 ctk_signal_hyperlink_hover,
98 ctk_signal_menu_activate,
99 ctk_signal_window_close;
100
101
102static unsigned short screensaver_timer;
103#define SCREENSAVER_TIMEOUT (5*60)
104
105#if CTK_CONF_MENUS
106/*-----------------------------------------------------------------------------------*/
107/* make_desktopmenu(void)
108 *
109 * Creates the leftmost menu, "Desktop". Since the desktop menu
110 * contains the list of all open windows, this function will be called
111 * whenever a window is opened or closed.
112 */
113static void
114make_desktopmenu(void)
115{
116 struct ctk_window *w;
117
118 desktopmenu.nitems = 0;
119
120 if(windows == NULL) {
121 ctk_menuitem_add(&desktopmenu, "(No windows)");
122 } else {
123 for(w = windows; w != NULL; w = w->next) {
124 ctk_menuitem_add(&desktopmenu, w->title);
125 }
126 }
127}
128#endif /* CTK_CONF_MENUS */
129/*-----------------------------------------------------------------------------------*/
130/* ctk_init(void)
131 *
132 * Initializes CTK. Must be called before any other CTK function.
133 */
134void
135ctk_init(void)
136{
137 ctkid = dispatcher_start(&p);
138
139 windows = NULL;
140 dialog = NULL;
141
142#if CTK_CONF_MENUS
143 ctk_menu_new(&desktopmenu, "Desktop");
144 make_desktopmenu();
145 menus.menus = menus.desktopmenu = &desktopmenu;
146#endif /* CTK_CONF_MENUS */
147
148 ctk_draw_init();
149
150 height = ctk_draw_height();
151 width = ctk_draw_width();
152
153 desktop_window.active = NULL;
154
155
156 ctk_signal_keypress = dispatcher_sigalloc();
157 ctk_signal_timer = dispatcher_sigalloc();
158 ctk_signal_button_activate = dispatcher_sigalloc();
159 ctk_signal_button_hover = dispatcher_sigalloc();
160 ctk_signal_hyperlink_activate = dispatcher_sigalloc();
161 ctk_signal_hyperlink_hover = dispatcher_sigalloc();
162 ctk_signal_menu_activate = dispatcher_sigalloc();
163 ctk_signal_window_close = dispatcher_sigalloc();
164
165 dispatcher_listen(ctk_signal_timer);
166 dispatcher_timer(ctk_signal_timer, NULL, CLK_TCK);
167
168 mode = CTK_MODE_NORMAL;
169
170 iconx = ICONX_START;
171 icony = ICONY_START;
172
173}
174/*-----------------------------------------------------------------------------------*/
175/* void ctk_mode_set()
176 */
177void
178ctk_mode_set(unsigned char m) {
179 mode = m;
180}
181/*-----------------------------------------------------------------------------------*/
182unsigned char
183ctk_mode_get(void) {
184 return mode;
185}
186/*-----------------------------------------------------------------------------------*/
187void
adamdunkelsd07a5422003-04-05 12:22:35 +0000188ctk_icon_add(CC_REGISTER_ARG struct ctk_widget *icon,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000189 ek_id_t id)
190{
191#if CTK_CONF_ICONS
192 icon->x = iconx;
193 icon->y = icony;
194 icon->widget.icon.owner = id;
195
196 icony += ICONY_DELTA;
197 if(icony >= ICONY_MAX) {
198 icony = ICONY_START;
199 iconx += ICONX_DELTA;
200 }
201
202 ctk_widget_add(&desktop_window, icon);
203#endif /* CTK_CONF_ICONS */
204}
205/*-----------------------------------------------------------------------------------*/
206void
207ctk_dialog_open(struct ctk_window *d)
208{
209 dialog = d;
210}
211/*-----------------------------------------------------------------------------------*/
212void
213ctk_dialog_close(void)
214{
215 dialog = NULL;
216}
217/*-----------------------------------------------------------------------------------*/
218void
adamdunkelsd07a5422003-04-05 12:22:35 +0000219ctk_window_open(CC_REGISTER_ARG struct ctk_window *w)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000220{
221 struct ctk_window *w2;
222
223 /* Check if already open. */
224 for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next);
225 if(w2 == NULL) {
226 /* Not open, so we add it at the head of the list of open
227 windows. */
228 w->next = windows;
229 if(windows != NULL) {
230 windows->prev = w;
231 }
232 windows = w;
233 w->prev = NULL;
234 } else {
235 /* Window already open, so we move it to the front of the windows
236 list. */
237 if(w != windows) {
238 if(w->next != NULL) {
239 w->next->prev = w->prev;
240 }
241 if(w->prev != NULL) {
242 w->prev->next = w->next;
243 }
244 w->next = windows;
245 windows->prev = w;
246 windows = w;
247 w->prev = NULL;
248 }
249 }
250
251#if CTK_CONF_MENUS
252 /* Recreate the Desktop menu's window entries.*/
253 make_desktopmenu();
254#endif /* CTK_CONF_MENUS */
255}
256/*-----------------------------------------------------------------------------------*/
257void
258ctk_window_close(struct ctk_window *w)
259{
260 struct ctk_window *w2;
261
262 if(w == NULL) {
263 return;
264 }
265
266 /* Check if the window to be closed is the first window on the
267 list. */
268 if(w == windows) {
269 windows = w->next;
270 if(windows != NULL) {
271 windows->prev = NULL;
272 }
273 w->next = w->prev = NULL;
274 } else {
275 /* Otherwise we step through the list until we find the window
276 before the one to be closed. We then redirect its ->next
277 pointer and its ->next->prev. */
adamdunkels3cf116a2003-04-08 19:28:15 +0000278 for(w2 = windows; w2 != NULL && w2->next != w; w2 = w2->next);
279
280 if(w2 == NULL) {
281 /* The window wasn't open, so there is nothing more for us to
282 do. */
283 return;
284 }
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000285
286 if(w->next != NULL) {
287 w->next->prev = w->prev;
288 }
289 w2->next = w->next;
290
291 w->next = w->prev = NULL;
292 }
293
294#if CTK_CONF_MENUS
295 /* Recreate the Desktop menu's window entries.*/
296 make_desktopmenu();
297#endif /* CTK_CONF_MENUS */
298}
299/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +0000300static void
301make_windowbuttons(CC_REGISTER_ARG struct ctk_window *window)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000302{
303#if CTK_CONF_WINDOWMOVE
304 CTK_BUTTON_NEW(&window->titlebutton, 0, -1, window->titlelen, window->title);
305#else
306 CTK_LABEL_NEW(&window->titlebutton, 0, -1, window->titlelen, 1, window->title);
307#endif /* CTK_CONF_WINDOWMOVE */
308 CTK_WIDGET_ADD(window, &window->titlebutton);
309
310
311#if CTK_CONF_WINDOWCLOSE
312 CTK_BUTTON_NEW(&window->closebutton, window->w - 3, -1, 1, "x");
313#else
314 CTK_LABEL_NEW(&window->closebutton, window->w - 4, -1, 3, 1, " ");
315#endif /* CTK_CONF_WINDOWCLOSE */
316 CTK_WIDGET_ADD(window, &window->closebutton);
317}
318/*-----------------------------------------------------------------------------------*/
319void
320ctk_window_clear(struct ctk_window *window)
321{
322 window->active = window->inactive = window->focused = NULL;
323
324 make_windowbuttons(window);
325}
326/*-----------------------------------------------------------------------------------*/
327void
328ctk_menu_add(struct ctk_menu *menu)
329{
330#if CTK_CONF_MENUS
331 struct ctk_menu *m;
332
333 if(lastmenu == NULL) {
334 lastmenu = menu;
335 }
336
337 for(m = menus.menus; m->next != NULL; m = m->next) {
338 if(m == menu) {
339 return;
340 }
341 }
342 m->next = menu;
343 menu->next = NULL;
344#endif /* CTK_CONF_MENUS */
345}
346/*-----------------------------------------------------------------------------------*/
347void
348ctk_menu_remove(struct ctk_menu *menu)
349{
350#if CTK_CONF_MENUS
351 struct ctk_menu *m;
352
353 for(m = menus.menus; m->next != NULL; m = m->next) {
354 if(m->next == menu) {
355 m->next = menu->next;
356 return;
357 }
358 }
359#endif /* CTK_CONF_MENUS */
360}
361/*-----------------------------------------------------------------------------------*/
362static void
363do_redraw_all(unsigned char clipy1, unsigned char clipy2)
364{
365 struct ctk_window *w;
366 struct ctk_widget *widget;
367
368 if(mode != CTK_MODE_NORMAL &&
369 mode != CTK_MODE_WINDOWMOVE) {
370 return;
371 }
372
373 ctk_draw_clear(clipy1, clipy2);
374
375 /* Draw widgets in root window */
376 for(widget = desktop_window.active;
377 widget != NULL; widget = widget->next) {
378 ctk_draw_widget(widget, 0, clipy1, clipy2);
379 }
380
381 /* Draw windows */
382 if(windows != NULL) {
383 /* Find the last window.*/
384 for(w = windows; w->next != NULL; w = w->next);
385
386 /* Draw the windows from back to front. */
387 for(; w != windows; w = w->prev) {
adamdunkels9d3a0e52003-04-02 09:53:59 +0000388 ctk_draw_clear_window(w, 0, clipy1, clipy2);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000389 ctk_draw_window(w, 0, clipy1, clipy2);
390 }
391 /* Draw focused window */
adamdunkels9d3a0e52003-04-02 09:53:59 +0000392 ctk_draw_clear_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000393 ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
394 }
395
396 /* Draw dialog (if any) */
397 if(dialog != NULL) {
398 ctk_draw_dialog(dialog);
399 }
400
401#if CTK_CONF_MENUS
402 ctk_draw_menus(&menus);
403#endif /* CTK_CONF_MENUS */
404}
405/*-----------------------------------------------------------------------------------*/
406void
407ctk_redraw(void)
408{
409 if(DISPATCHER_CURRENT() == ctkid) {
410 if(mode == CTK_MODE_NORMAL ||
411 mode == CTK_MODE_WINDOWMOVE) {
412 do_redraw_all(1, height);
413 }
414 } else {
415 redraw |= REDRAW_ALL;
416 }
417}
418/*-----------------------------------------------------------------------------------*/
419void
420ctk_window_redraw(struct ctk_window *w)
421{
422 /* Only redraw the window if it is a dialog or if it is the foremost
423 window. */
424 if(mode != CTK_MODE_NORMAL) {
425 return;
426 }
427
428 if(w == dialog) {
429 ctk_draw_dialog(w);
430 } else if(dialog == NULL &&
431#if CTK_CONF_MENUS
432 menus.open == NULL &&
433#endif /* CTK_CONF_MENUS */
434 windows == w) {
435 ctk_draw_window(w, CTK_FOCUS_WINDOW,
436 0, height);
437 }
438}
439/*-----------------------------------------------------------------------------------*/
440static void
adamdunkelsd07a5422003-04-05 12:22:35 +0000441window_new(CC_REGISTER_ARG struct ctk_window *window,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000442 unsigned char w, unsigned char h,
443 char *title)
444{
445 window->x = window->y = 0;
446 window->w = w;
447 window->h = h;
448 window->title = title;
449 if(title != NULL) {
450 window->titlelen = strlen(title);
451 } else {
452 window->titlelen = 0;
453 }
454 window->next = window->prev = NULL;
455 window->owner = DISPATCHER_CURRENT();
456 window->active = window->inactive = window->focused = NULL;
457}
458/*-----------------------------------------------------------------------------------*/
459void
460ctk_window_new(struct ctk_window *window,
461 unsigned char w, unsigned char h,
462 char *title)
463{
464 window_new(window, w, h, title);
465
466 make_windowbuttons(window);
467}
468/*-----------------------------------------------------------------------------------*/
469void
adamdunkelsd07a5422003-04-05 12:22:35 +0000470ctk_dialog_new(CC_REGISTER_ARG struct ctk_window *window,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000471 unsigned char w, unsigned char h)
472{
473 window_new(window, w, h, NULL);
474
475 window->x = (width - w) / 2;
476 window->y = (height - h - 1) / 2;
477}
478/*-----------------------------------------------------------------------------------*/
479void
adamdunkelsd07a5422003-04-05 12:22:35 +0000480ctk_menu_new(CC_REGISTER_ARG struct ctk_menu *menu,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000481 char *title)
482{
483#if CTK_CONF_MENUS
484 menu->next = NULL;
485 menu->title = title;
486 menu->titlelen = strlen(title);
487 menu->active = 0;
488 menu->nitems = 0;
489#endif /* CTK_CONF_MENUS */
490}
491/*-----------------------------------------------------------------------------------*/
492unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000493ctk_menuitem_add(CC_REGISTER_ARG struct ctk_menu *menu,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000494 char *name)
495{
496#if CTK_CONF_MENUS
497 if(menu->nitems == CTK_CONF_MAXMENUITEMS) {
498 return 0;
499 }
500 menu->items[menu->nitems].title = name;
501 menu->items[menu->nitems].titlelen = strlen(name);
502 return menu->nitems++;
503#else
504 return 0;
505#endif /* CTK_CONF_MENUS */
506}
507/*-----------------------------------------------------------------------------------*/
508static void
509add_redrawwidget(struct ctk_widget *w)
510{
511 static unsigned char i;
512
513 if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
514 redraw |= REDRAW_FOCUS;
515 } else {
516 redraw |= REDRAW_WIDGETS;
517 /* Check if it is in the queue already. If so, we don't add it
518 again. */
519 for(i = 0; i < redraw_widgetptr; ++i) {
520 if(redraw_widgets[i] == w) {
521 return;
522 }
523 }
524 redraw_widgets[redraw_widgetptr++] = w;
525 }
526}
527/*-----------------------------------------------------------------------------------*/
528void
529ctk_widget_redraw(struct ctk_widget *widget)
530{
531 struct ctk_window *window;
532
533 if(mode != CTK_MODE_NORMAL) {
534 return;
535 }
536
537 /* If this function isn't called by CTK itself, we only queue the
538 redraw request. */
539 if(DISPATCHER_CURRENT() != ctkid) {
540 redraw |= REDRAW_WIDGETS;
541 add_redrawwidget(widget);
542 } else {
543
544 /* Only redraw widgets that are in the foremost window. If we
545 would allow redrawing widgets in non-focused windows, we would
546 have to redraw all the windows that cover the non-focused
547 window as well, which would lead to flickering.
548
549 Also, we avoid drawing any widgets when the menus are active.
550 */
551
552#if CTK_CONF_MENUS
553 if(menus.open == NULL)
554#endif /* CTK_CONF_MENUS */
555 {
556 window = widget->window;
557 if(window == dialog) {
558 ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
559 } else if(window == windows) {
adamdunkels9d3a0e52003-04-02 09:53:59 +0000560 ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000561 } else if(window == &desktop_window) {
562 ctk_draw_widget(widget, 0, 0, height);
563 }
564 }
565 }
566}
567/*-----------------------------------------------------------------------------------*/
568void
adamdunkelsd07a5422003-04-05 12:22:35 +0000569ctk_widget_add(CC_REGISTER_ARG struct ctk_window *window,
570 CC_REGISTER_ARG struct ctk_widget *widget)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000571{
572 if(widget->type == CTK_WIDGET_LABEL ||
573 widget->type == CTK_WIDGET_SEPARATOR) {
574 widget->next = window->inactive;
575 window->inactive = widget;
576 widget->window = window;
577 } else {
578 widget->next = window->active;
579 window->active = widget;
580 widget->window = window;
581 if(window->focused == NULL) {
582 window->focused = widget;
583 }
584 }
585}
586/*-----------------------------------------------------------------------------------*/
587#define UP 0
588#define DOWN 1
589#define LEFT 2
590#define RIGHT 3
591static void
592switch_focus_widget(unsigned char direction)
593{
594 register struct ctk_window *window;
595 register struct ctk_widget *focus;
596 struct ctk_widget *widget;
597
598
599 if(dialog != NULL) {
600 window = dialog;
601 } else {
602 window = windows;
603 }
604
605 /* If there are no windows open, we move focus around between the
606 icons on the root window instead. */
607 if(window == NULL) {
608 window = &desktop_window;
609 }
610
611 focus = window->focused;
612 add_redrawwidget(focus);
613
614 if((direction & 1) == 0) {
615 /* Move focus "up" */
616 focus = focus->next;
617 } else {
618 /* Move focus "down" */
619 for(widget = window->active;
620 widget != NULL; widget = widget->next) {
621 if(widget->next == focus) {
622 break;
623 }
624 }
625 focus = widget;
626 if(focus == NULL) {
627 if(window->active != NULL) {
628 for(focus = window->active;
629 focus->next != NULL; focus = focus->next);
630 }
631 }
632 }
633 if(focus == NULL) {
634 focus = window->active;
635 }
636
637 if(focus != window->focused) {
638 window->focused = focus;
639 /* The operation changed the focus, so we emit a "hover" signal
640 for those widgets that support it. */
641
642 if(window->focused->type == CTK_WIDGET_HYPERLINK) {
643 dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
644 window->owner);
645 } else if(window->focused->type == CTK_WIDGET_BUTTON) {
646 dispatcher_emit(ctk_signal_button_hover, window->focused,
647 window->owner);
648 }
649
650 add_redrawwidget(window->focused);
651 }
652}
653/*-----------------------------------------------------------------------------------*/
654#if CTK_CONF_MENUS
655static void
656switch_open_menu(unsigned char rightleft)
657{
658 struct ctk_menu *menu;
659
660 if(rightleft == 0) {
661 /* Move right */
662 for(menu = menus.menus; menu != NULL; menu = menu->next) {
663 if(menu->next == menus.open) {
664 break;
665 }
666 }
667 lastmenu = menus.open;
668 menus.open = menu;
669 if(menus.open == NULL) {
670 for(menu = menus.menus;
671 menu->next != NULL; menu = menu->next);
672 menus.open = menu;
673 }
674 } else {
675 /* Move to left */
676 lastmenu = menus.open;
677 menus.open = menus.open->next;
678 if(menus.open == NULL) {
679 menus.open = menus.menus;
680 }
681 }
682
683 menus.open->active = 0;
684 /* ctk_redraw();*/
685}
686/*-----------------------------------------------------------------------------------*/
687static void
688switch_menu_item(unsigned char updown)
689{
690 register struct ctk_menu *m;
691
692 m = menus.open;
693
694 if(updown == 0) {
695 /* Move up */
696 if(m->active == 0) {
697 m->active = m->nitems - 1;
698 } else {
699 --m->active;
700 if(m->items[m->active].title[0] == '-') {
701 --m->active;
702 }
703 }
704 } else {
705 /* Move down */
706 if(m->active >= m->nitems - 1) {
707 m->active = 0;
708 } else {
709 ++m->active;
710 if(m->items[m->active].title[0] == '-') {
711 ++m->active;
712 }
713 }
714 }
715
716}
717#endif /* CTK_CONF_MENUS */
718/*-----------------------------------------------------------------------------------*/
719static unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000720activate(CC_REGISTER_ARG struct ctk_widget *w)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000721{
722 static unsigned char len;
723
724 if(w->type == CTK_WIDGET_BUTTON) {
725 if(w == (struct ctk_widget *)&windows->closebutton) {
726#if CTK_CONF_WINDOWCLOSE
adamdunkels5cb690c2003-04-02 11:36:21 +0000727 dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000728 ctk_window_close(windows);
729 return REDRAW_ALL;
730#endif /* CTK_CONF_WINDOWCLOSE */
731 } else if(w == (struct ctk_widget *)&windows->titlebutton) {
732#if CTK_CONF_WINDOWCLOSE
733 mode = CTK_MODE_WINDOWMOVE;
734#endif /* CTK_CONF_WINDOWCLOSE */
735 } else {
736 dispatcher_emit(ctk_signal_button_activate, w,
737 w->window->owner);
738 }
739#if CTK_CONF_ICONS
740 } else if(w->type == CTK_WIDGET_ICON) {
741 dispatcher_emit(ctk_signal_button_activate, w,
742 w->widget.icon.owner);
743#endif /* CTK_CONF_ICONS */
744 } else if(w->type == CTK_WIDGET_HYPERLINK) {
745 dispatcher_emit(ctk_signal_hyperlink_activate, w,
746 DISPATCHER_BROADCAST);
747 } else if(w->type == CTK_WIDGET_TEXTENTRY) {
748 if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
749 w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
750 len = strlen(w->widget.textentry.text);
751 if(w->widget.textentry.xpos > len) {
752 w->widget.textentry.xpos = len;
753 }
754 } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
755 w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
756 }
757 add_redrawwidget(w);
758 return REDRAW_WIDGETS;
759 }
760 return REDRAW_NONE;
761}
762/*-----------------------------------------------------------------------------------*/
763static void
764textentry_input(ctk_arch_key_t c,
adamdunkelsd07a5422003-04-05 12:22:35 +0000765 CC_REGISTER_ARG struct ctk_textentry *t)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000766{
767 static char *cptr, *cptr2;
768 static unsigned char len, txpos, typos, tlen;
769
770 txpos = t->xpos;
771 typos = t->ypos;
772 tlen = t->len;
773
774 cptr = &t->text[txpos + typos * tlen];
775
776 switch(c) {
777 case CH_CURS_LEFT:
778 if(txpos > 0) {
779 --txpos;
780 }
781 break;
782
783 case CH_CURS_RIGHT:
784 if(txpos < tlen &&
785 *cptr != 0) {
786 ++txpos;
787 }
788 break;
789
790 case CH_CURS_UP:
791#if CTK_CONF_TEXTENTRY_MULTILINE
792 if(t->h == 1) {
793 txpos = 0;
794 } else {
795 if(typos > 0) {
796 --typos;
797 } else {
798 t->state = CTK_TEXTENTRY_NORMAL;
799 }
800 }
801#else
802 txpos = 0;
803#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
804 break;
805
806 case CH_CURS_DOWN:
807#if CTK_CONF_TEXTENTRY_MULTILINE
808 if(t->h == 1) {
809 txpos = strlen(t->text);
810 } else {
811 if(typos < t->h - 1) {
812 ++typos;
813 } else {
814 t->state = CTK_TEXTENTRY_NORMAL;
815 }
816 }
817#else
818 txpos = strlen(t->text);
819#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
820 break;
821
822 case CH_ENTER:
823#if CTK_CONF_TEXTENTRY_MULTILINE
824 if(t->h == 1) {
825 t->state = CTK_TEXTENTRY_NORMAL;
826 } else {
827 if(typos < t->h - 1) {
828 ++typos;
829 txpos = 0;
830 } else {
831 t->state = CTK_TEXTENTRY_NORMAL;
832 }
833 }
834#else
835 t->state = CTK_TEXTENTRY_NORMAL;
836#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
837 break;
838
839 default:
840 len = tlen - txpos - 1;
841 if(c == CH_DEL) {
842 if(txpos > 0 && len > 0) {
843 strncpy(cptr - 1, cptr,
844 len);
845 *(cptr + len - 1) = 0;
846 --txpos;
847 }
848 } else {
849 if(len > 0) {
850 cptr2 = cptr + len - 1;
851 while(cptr2 + 1 > cptr) {
852 *(cptr2 + 1) = *cptr2;
853 --cptr2;
854 }
855
856 *cptr = c;
857 ++txpos;
858 }
859 }
860 break;
861 }
862
863 t->xpos = txpos;
864 t->ypos = typos;
865}
866/*-----------------------------------------------------------------------------------*/
867#if CTK_CONF_MENUS
868static unsigned char
869menus_input(ctk_arch_key_t c)
870{
871 struct ctk_window *w;
872
873 if(menus.open->nitems > maxnitems) {
874 maxnitems = menus.open->nitems;
875 }
876
877
878 switch(c) {
879 case CH_CURS_RIGHT:
880 switch_open_menu(1);
881
882 return REDRAW_MENUPART;
883
884 case CH_CURS_DOWN:
885 switch_menu_item(1);
886 return REDRAW_MENUS;
887
888 case CH_CURS_LEFT:
889 switch_open_menu(0);
890 return REDRAW_MENUPART;
891
892 case CH_CURS_UP:
893 switch_menu_item(0);
894 return REDRAW_MENUS;
895
896 case CH_ENTER:
897 lastmenu = menus.open;
898 if(menus.open == &desktopmenu) {
899 for(w = windows; w != NULL; w = w->next) {
900 if(w->title == desktopmenu.items[desktopmenu.active].title) {
901 ctk_window_open(w);
902 menus.open = NULL;
903 return REDRAW_ALL;
904 }
905 }
906 } else {
907 dispatcher_emit(ctk_signal_menu_activate, menus.open,
908 DISPATCHER_BROADCAST);
909 }
910 menus.open = NULL;
911 return REDRAW_MENUPART;
912
adamdunkels88ed9c42003-03-28 12:10:09 +0000913 case CTK_CONF_MENU_KEY:
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000914 lastmenu = menus.open;
915 menus.open = NULL;
916 return REDRAW_MENUPART;
917 }
918}
919#endif /* CTK_CONF_MENUS */
920/*-----------------------------------------------------------------------------------*/
921static void
922idle(void)
923{
924 static ctk_arch_key_t c;
925 static unsigned char i;
926 register struct ctk_window *window;
927 register struct ctk_widget *widget;
928
929#if CTK_CONF_MENUS
930 if(menus.open != NULL) {
931 maxnitems = menus.open->nitems;
932 } else {
933 maxnitems = 0;
934 }
935#endif /* CTK_CONF_MENUS */
936
937 if(mode == CTK_MODE_SCREENSAVER) {
938#ifdef CTK_SCREENSAVER_RUN
939 CTK_SCREENSAVER_RUN();
940#endif /* CTK_SCREENSAVER_RUN */
941 if(ctk_arch_keyavail()) {
942 mode = CTK_MODE_NORMAL;
943 ctk_draw_init();
944 ctk_redraw();
945 }
946 } else if(mode == CTK_MODE_NORMAL) {
947 while(ctk_arch_keyavail()) {
948
949 screensaver_timer = 0;
950
951 c = ctk_arch_getkey();
952
953
954 if(dialog != NULL) {
955 window = dialog;
956 } else if(windows != NULL) {
957 window = windows;
958 } else {
959 window = &desktop_window;
960 }
961 widget = window->focused;
962
963
964 if(widget != NULL &&
965 widget->type == CTK_WIDGET_TEXTENTRY &&
966 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
967 textentry_input(c, (struct ctk_textentry *)widget);
968 add_redrawwidget(widget);
969#if CTK_CONF_MENUS
970 } else if(menus.open != NULL) {
971 redraw |= menus_input(c);
972#endif /* CTK_CONF_MENUS */
973 } else {
974 switch(c) {
975 case CH_CURS_RIGHT:
976 switch_focus_widget(RIGHT);
977 break;
978 case CH_CURS_DOWN:
979 switch_focus_widget(DOWN);
980 break;
981 case CH_CURS_LEFT:
982 switch_focus_widget(LEFT);
983 break;
984 case CH_CURS_UP:
985 switch_focus_widget(UP);
986 break;
987 case CH_ENTER:
988 redraw |= activate(widget);
989 break;
990#if CTK_CONF_MENUS
991 case CTK_CONF_MENU_KEY:
992 if(dialog == NULL) {
993 if(lastmenu == NULL) {
994 menus.open = menus.menus;
995 } else {
996 menus.open = lastmenu;
997 }
998 menus.open->active = 0;
999 redraw |= REDRAW_MENUS;
1000 }
1001 break;
1002#endif /* CTK_CONF_MENUS */
1003 case CTK_CONF_WINDOWSWITCH_KEY:
1004 if(windows != NULL) {
1005 for(window = windows; window->next != NULL;
1006 window = window->next);
1007 ctk_window_open(window);
1008 ctk_redraw();
1009 }
1010 break;
1011 default:
1012 if(widget->type == CTK_WIDGET_TEXTENTRY) {
1013 widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1014 textentry_input(c, (struct ctk_textentry *)widget);
1015 add_redrawwidget(widget);
1016 } else {
1017 dispatcher_emit(ctk_signal_keypress, (void *)c,
1018 window->owner);
1019 }
1020 break;
1021 }
1022 }
1023
1024 if(redraw & REDRAW_WIDGETS) {
1025 for(i = 0; i < redraw_widgetptr; ++i) {
1026 ctk_widget_redraw(redraw_widgets[i]);
1027 }
1028 redraw &= ~REDRAW_WIDGETS;
1029 redraw_widgetptr = 0;
1030 }
1031 }
1032 if(redraw & REDRAW_ALL) {
1033 do_redraw_all(1, height);
1034#if CTK_CONF_MENUS
1035 } else if(redraw & REDRAW_MENUPART) {
1036 do_redraw_all(1, maxnitems + 1);
1037 } else if(redraw & REDRAW_MENUS) {
1038 ctk_draw_menus(&menus);
1039#endif /* CTK_CONF_MENUS */
1040 } else if(redraw & REDRAW_FOCUS) {
1041 if(dialog != NULL) {
1042 ctk_window_redraw(dialog);
1043 } else if(windows != NULL) {
1044 ctk_window_redraw(windows);
1045 } else {
1046 ctk_window_redraw(&desktop_window);
1047 }
1048 } else if(redraw & REDRAW_WIDGETS) {
1049 for(i = 0; i < redraw_widgetptr; ++i) {
1050 ctk_widget_redraw(redraw_widgets[i]);
1051 }
1052 }
1053 redraw = 0;
1054 redraw_widgetptr = 0;
1055#if CTK_CONF_WINDOWMOVE
1056 } else if(mode == CTK_MODE_WINDOWMOVE) {
1057
1058 redraw = 0;
1059
1060 window = windows;
1061
1062 while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
1063
1064 screensaver_timer = 0;
1065
1066 c = ctk_arch_getkey();
1067
1068 switch(c) {
1069 case CH_CURS_RIGHT:
1070 ++window->x;
1071 if(window->x + window->w + 1 >= width) {
1072 --window->x;
1073 }
1074 redraw = REDRAW_ALL;
1075 break;
1076 case CH_CURS_LEFT:
1077 if(window->x > 0) {
1078 --window->x;
1079 }
1080 redraw = REDRAW_ALL;
1081 break;
1082 case CH_CURS_DOWN:
1083 ++window->y;
1084 if(window->y + window->h + 2 >= height) {
1085 --window->y;
1086 }
1087 redraw = REDRAW_ALL;
1088 break;
1089 case CH_CURS_UP:
1090 if(window->y > 0) {
1091 --window->y;
1092 }
1093 redraw = REDRAW_ALL;
1094 break;
1095 case CH_ENTER:
1096 case CH_ESC:
1097 mode = CTK_MODE_NORMAL;
1098 redraw = REDRAW_ALL;
1099 break;
1100 }
1101 }
1102 if(redraw & REDRAW_ALL) {
1103 do_redraw_all(1, height);
1104 }
1105 redraw = 0;
1106#endif /* CTK_CONF_WINDOWMOVE */
1107 }
1108}
1109/*-----------------------------------------------------------------------------------*/
1110static void
1111sighandler(ek_signal_t s, ek_data_t data)
1112{
1113 if(s == ctk_signal_timer) {
1114 if(mode == CTK_MODE_NORMAL) {
1115 ++screensaver_timer;
1116 if(screensaver_timer == SCREENSAVER_TIMEOUT) {
1117#ifdef CTK_SCREENSAVER_INIT
1118 CTK_SCREENSAVER_INIT();
1119#endif /* CTK_SCREENSAVER_INIT */
1120 mode = CTK_MODE_SCREENSAVER;
1121 screensaver_timer = 0;
1122 }
1123 }
1124 dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
1125 }
1126}
1127/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +00001128
1129/*-----------------------------------------------------------------------------------*/