blob: 23738b58f8e3d8c08286f6274822b9948151be35 [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 *
adamdunkelsd07a5422003-04-05 12:22:35 +000035 * $Id: ctk.c,v 1.5 2003/04/05 12:22:35 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. */
278 for(w2 = windows; w2->next != w; w2 = w2->next);
279
280 if(w->next != NULL) {
281 w->next->prev = w->prev;
282 }
283 w2->next = w->next;
284
285 w->next = w->prev = NULL;
286 }
287
288#if CTK_CONF_MENUS
289 /* Recreate the Desktop menu's window entries.*/
290 make_desktopmenu();
291#endif /* CTK_CONF_MENUS */
292}
293/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +0000294static void
295make_windowbuttons(CC_REGISTER_ARG struct ctk_window *window)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000296{
297#if CTK_CONF_WINDOWMOVE
298 CTK_BUTTON_NEW(&window->titlebutton, 0, -1, window->titlelen, window->title);
299#else
300 CTK_LABEL_NEW(&window->titlebutton, 0, -1, window->titlelen, 1, window->title);
301#endif /* CTK_CONF_WINDOWMOVE */
302 CTK_WIDGET_ADD(window, &window->titlebutton);
303
304
305#if CTK_CONF_WINDOWCLOSE
306 CTK_BUTTON_NEW(&window->closebutton, window->w - 3, -1, 1, "x");
307#else
308 CTK_LABEL_NEW(&window->closebutton, window->w - 4, -1, 3, 1, " ");
309#endif /* CTK_CONF_WINDOWCLOSE */
310 CTK_WIDGET_ADD(window, &window->closebutton);
311}
312/*-----------------------------------------------------------------------------------*/
313void
314ctk_window_clear(struct ctk_window *window)
315{
316 window->active = window->inactive = window->focused = NULL;
317
318 make_windowbuttons(window);
319}
320/*-----------------------------------------------------------------------------------*/
321void
322ctk_menu_add(struct ctk_menu *menu)
323{
324#if CTK_CONF_MENUS
325 struct ctk_menu *m;
326
327 if(lastmenu == NULL) {
328 lastmenu = menu;
329 }
330
331 for(m = menus.menus; m->next != NULL; m = m->next) {
332 if(m == menu) {
333 return;
334 }
335 }
336 m->next = menu;
337 menu->next = NULL;
338#endif /* CTK_CONF_MENUS */
339}
340/*-----------------------------------------------------------------------------------*/
341void
342ctk_menu_remove(struct ctk_menu *menu)
343{
344#if CTK_CONF_MENUS
345 struct ctk_menu *m;
346
347 for(m = menus.menus; m->next != NULL; m = m->next) {
348 if(m->next == menu) {
349 m->next = menu->next;
350 return;
351 }
352 }
353#endif /* CTK_CONF_MENUS */
354}
355/*-----------------------------------------------------------------------------------*/
356static void
357do_redraw_all(unsigned char clipy1, unsigned char clipy2)
358{
359 struct ctk_window *w;
360 struct ctk_widget *widget;
361
362 if(mode != CTK_MODE_NORMAL &&
363 mode != CTK_MODE_WINDOWMOVE) {
364 return;
365 }
366
367 ctk_draw_clear(clipy1, clipy2);
368
369 /* Draw widgets in root window */
370 for(widget = desktop_window.active;
371 widget != NULL; widget = widget->next) {
372 ctk_draw_widget(widget, 0, clipy1, clipy2);
373 }
374
375 /* Draw windows */
376 if(windows != NULL) {
377 /* Find the last window.*/
378 for(w = windows; w->next != NULL; w = w->next);
379
380 /* Draw the windows from back to front. */
381 for(; w != windows; w = w->prev) {
adamdunkels9d3a0e52003-04-02 09:53:59 +0000382 ctk_draw_clear_window(w, 0, clipy1, clipy2);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000383 ctk_draw_window(w, 0, clipy1, clipy2);
384 }
385 /* Draw focused window */
adamdunkels9d3a0e52003-04-02 09:53:59 +0000386 ctk_draw_clear_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000387 ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
388 }
389
390 /* Draw dialog (if any) */
391 if(dialog != NULL) {
392 ctk_draw_dialog(dialog);
393 }
394
395#if CTK_CONF_MENUS
396 ctk_draw_menus(&menus);
397#endif /* CTK_CONF_MENUS */
398}
399/*-----------------------------------------------------------------------------------*/
400void
401ctk_redraw(void)
402{
403 if(DISPATCHER_CURRENT() == ctkid) {
404 if(mode == CTK_MODE_NORMAL ||
405 mode == CTK_MODE_WINDOWMOVE) {
406 do_redraw_all(1, height);
407 }
408 } else {
409 redraw |= REDRAW_ALL;
410 }
411}
412/*-----------------------------------------------------------------------------------*/
413void
414ctk_window_redraw(struct ctk_window *w)
415{
416 /* Only redraw the window if it is a dialog or if it is the foremost
417 window. */
418 if(mode != CTK_MODE_NORMAL) {
419 return;
420 }
421
422 if(w == dialog) {
423 ctk_draw_dialog(w);
424 } else if(dialog == NULL &&
425#if CTK_CONF_MENUS
426 menus.open == NULL &&
427#endif /* CTK_CONF_MENUS */
428 windows == w) {
429 ctk_draw_window(w, CTK_FOCUS_WINDOW,
430 0, height);
431 }
432}
433/*-----------------------------------------------------------------------------------*/
434static void
adamdunkelsd07a5422003-04-05 12:22:35 +0000435window_new(CC_REGISTER_ARG struct ctk_window *window,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000436 unsigned char w, unsigned char h,
437 char *title)
438{
439 window->x = window->y = 0;
440 window->w = w;
441 window->h = h;
442 window->title = title;
443 if(title != NULL) {
444 window->titlelen = strlen(title);
445 } else {
446 window->titlelen = 0;
447 }
448 window->next = window->prev = NULL;
449 window->owner = DISPATCHER_CURRENT();
450 window->active = window->inactive = window->focused = NULL;
451}
452/*-----------------------------------------------------------------------------------*/
453void
454ctk_window_new(struct ctk_window *window,
455 unsigned char w, unsigned char h,
456 char *title)
457{
458 window_new(window, w, h, title);
459
460 make_windowbuttons(window);
461}
462/*-----------------------------------------------------------------------------------*/
463void
adamdunkelsd07a5422003-04-05 12:22:35 +0000464ctk_dialog_new(CC_REGISTER_ARG struct ctk_window *window,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000465 unsigned char w, unsigned char h)
466{
467 window_new(window, w, h, NULL);
468
469 window->x = (width - w) / 2;
470 window->y = (height - h - 1) / 2;
471}
472/*-----------------------------------------------------------------------------------*/
473void
adamdunkelsd07a5422003-04-05 12:22:35 +0000474ctk_menu_new(CC_REGISTER_ARG struct ctk_menu *menu,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000475 char *title)
476{
477#if CTK_CONF_MENUS
478 menu->next = NULL;
479 menu->title = title;
480 menu->titlelen = strlen(title);
481 menu->active = 0;
482 menu->nitems = 0;
483#endif /* CTK_CONF_MENUS */
484}
485/*-----------------------------------------------------------------------------------*/
486unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000487ctk_menuitem_add(CC_REGISTER_ARG struct ctk_menu *menu,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000488 char *name)
489{
490#if CTK_CONF_MENUS
491 if(menu->nitems == CTK_CONF_MAXMENUITEMS) {
492 return 0;
493 }
494 menu->items[menu->nitems].title = name;
495 menu->items[menu->nitems].titlelen = strlen(name);
496 return menu->nitems++;
497#else
498 return 0;
499#endif /* CTK_CONF_MENUS */
500}
501/*-----------------------------------------------------------------------------------*/
502static void
503add_redrawwidget(struct ctk_widget *w)
504{
505 static unsigned char i;
506
507 if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
508 redraw |= REDRAW_FOCUS;
509 } else {
510 redraw |= REDRAW_WIDGETS;
511 /* Check if it is in the queue already. If so, we don't add it
512 again. */
513 for(i = 0; i < redraw_widgetptr; ++i) {
514 if(redraw_widgets[i] == w) {
515 return;
516 }
517 }
518 redraw_widgets[redraw_widgetptr++] = w;
519 }
520}
521/*-----------------------------------------------------------------------------------*/
522void
523ctk_widget_redraw(struct ctk_widget *widget)
524{
525 struct ctk_window *window;
526
527 if(mode != CTK_MODE_NORMAL) {
528 return;
529 }
530
531 /* If this function isn't called by CTK itself, we only queue the
532 redraw request. */
533 if(DISPATCHER_CURRENT() != ctkid) {
534 redraw |= REDRAW_WIDGETS;
535 add_redrawwidget(widget);
536 } else {
537
538 /* Only redraw widgets that are in the foremost window. If we
539 would allow redrawing widgets in non-focused windows, we would
540 have to redraw all the windows that cover the non-focused
541 window as well, which would lead to flickering.
542
543 Also, we avoid drawing any widgets when the menus are active.
544 */
545
546#if CTK_CONF_MENUS
547 if(menus.open == NULL)
548#endif /* CTK_CONF_MENUS */
549 {
550 window = widget->window;
551 if(window == dialog) {
552 ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
553 } else if(window == windows) {
adamdunkels9d3a0e52003-04-02 09:53:59 +0000554 ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000555 } else if(window == &desktop_window) {
556 ctk_draw_widget(widget, 0, 0, height);
557 }
558 }
559 }
560}
561/*-----------------------------------------------------------------------------------*/
562void
adamdunkelsd07a5422003-04-05 12:22:35 +0000563ctk_widget_add(CC_REGISTER_ARG struct ctk_window *window,
564 CC_REGISTER_ARG struct ctk_widget *widget)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000565{
566 if(widget->type == CTK_WIDGET_LABEL ||
567 widget->type == CTK_WIDGET_SEPARATOR) {
568 widget->next = window->inactive;
569 window->inactive = widget;
570 widget->window = window;
571 } else {
572 widget->next = window->active;
573 window->active = widget;
574 widget->window = window;
575 if(window->focused == NULL) {
576 window->focused = widget;
577 }
578 }
579}
580/*-----------------------------------------------------------------------------------*/
581#define UP 0
582#define DOWN 1
583#define LEFT 2
584#define RIGHT 3
585static void
586switch_focus_widget(unsigned char direction)
587{
588 register struct ctk_window *window;
589 register struct ctk_widget *focus;
590 struct ctk_widget *widget;
591
592
593 if(dialog != NULL) {
594 window = dialog;
595 } else {
596 window = windows;
597 }
598
599 /* If there are no windows open, we move focus around between the
600 icons on the root window instead. */
601 if(window == NULL) {
602 window = &desktop_window;
603 }
604
605 focus = window->focused;
606 add_redrawwidget(focus);
607
608 if((direction & 1) == 0) {
609 /* Move focus "up" */
610 focus = focus->next;
611 } else {
612 /* Move focus "down" */
613 for(widget = window->active;
614 widget != NULL; widget = widget->next) {
615 if(widget->next == focus) {
616 break;
617 }
618 }
619 focus = widget;
620 if(focus == NULL) {
621 if(window->active != NULL) {
622 for(focus = window->active;
623 focus->next != NULL; focus = focus->next);
624 }
625 }
626 }
627 if(focus == NULL) {
628 focus = window->active;
629 }
630
631 if(focus != window->focused) {
632 window->focused = focus;
633 /* The operation changed the focus, so we emit a "hover" signal
634 for those widgets that support it. */
635
636 if(window->focused->type == CTK_WIDGET_HYPERLINK) {
637 dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
638 window->owner);
639 } else if(window->focused->type == CTK_WIDGET_BUTTON) {
640 dispatcher_emit(ctk_signal_button_hover, window->focused,
641 window->owner);
642 }
643
644 add_redrawwidget(window->focused);
645 }
646}
647/*-----------------------------------------------------------------------------------*/
648#if CTK_CONF_MENUS
649static void
650switch_open_menu(unsigned char rightleft)
651{
652 struct ctk_menu *menu;
653
654 if(rightleft == 0) {
655 /* Move right */
656 for(menu = menus.menus; menu != NULL; menu = menu->next) {
657 if(menu->next == menus.open) {
658 break;
659 }
660 }
661 lastmenu = menus.open;
662 menus.open = menu;
663 if(menus.open == NULL) {
664 for(menu = menus.menus;
665 menu->next != NULL; menu = menu->next);
666 menus.open = menu;
667 }
668 } else {
669 /* Move to left */
670 lastmenu = menus.open;
671 menus.open = menus.open->next;
672 if(menus.open == NULL) {
673 menus.open = menus.menus;
674 }
675 }
676
677 menus.open->active = 0;
678 /* ctk_redraw();*/
679}
680/*-----------------------------------------------------------------------------------*/
681static void
682switch_menu_item(unsigned char updown)
683{
684 register struct ctk_menu *m;
685
686 m = menus.open;
687
688 if(updown == 0) {
689 /* Move up */
690 if(m->active == 0) {
691 m->active = m->nitems - 1;
692 } else {
693 --m->active;
694 if(m->items[m->active].title[0] == '-') {
695 --m->active;
696 }
697 }
698 } else {
699 /* Move down */
700 if(m->active >= m->nitems - 1) {
701 m->active = 0;
702 } else {
703 ++m->active;
704 if(m->items[m->active].title[0] == '-') {
705 ++m->active;
706 }
707 }
708 }
709
710}
711#endif /* CTK_CONF_MENUS */
712/*-----------------------------------------------------------------------------------*/
713static unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000714activate(CC_REGISTER_ARG struct ctk_widget *w)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000715{
716 static unsigned char len;
717
718 if(w->type == CTK_WIDGET_BUTTON) {
719 if(w == (struct ctk_widget *)&windows->closebutton) {
720#if CTK_CONF_WINDOWCLOSE
adamdunkels5cb690c2003-04-02 11:36:21 +0000721 dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000722 ctk_window_close(windows);
723 return REDRAW_ALL;
724#endif /* CTK_CONF_WINDOWCLOSE */
725 } else if(w == (struct ctk_widget *)&windows->titlebutton) {
726#if CTK_CONF_WINDOWCLOSE
727 mode = CTK_MODE_WINDOWMOVE;
728#endif /* CTK_CONF_WINDOWCLOSE */
729 } else {
730 dispatcher_emit(ctk_signal_button_activate, w,
731 w->window->owner);
732 }
733#if CTK_CONF_ICONS
734 } else if(w->type == CTK_WIDGET_ICON) {
735 dispatcher_emit(ctk_signal_button_activate, w,
736 w->widget.icon.owner);
737#endif /* CTK_CONF_ICONS */
738 } else if(w->type == CTK_WIDGET_HYPERLINK) {
739 dispatcher_emit(ctk_signal_hyperlink_activate, w,
740 DISPATCHER_BROADCAST);
741 } else if(w->type == CTK_WIDGET_TEXTENTRY) {
742 if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
743 w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
744 len = strlen(w->widget.textentry.text);
745 if(w->widget.textentry.xpos > len) {
746 w->widget.textentry.xpos = len;
747 }
748 } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
749 w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
750 }
751 add_redrawwidget(w);
752 return REDRAW_WIDGETS;
753 }
754 return REDRAW_NONE;
755}
756/*-----------------------------------------------------------------------------------*/
757static void
758textentry_input(ctk_arch_key_t c,
adamdunkelsd07a5422003-04-05 12:22:35 +0000759 CC_REGISTER_ARG struct ctk_textentry *t)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000760{
761 static char *cptr, *cptr2;
762 static unsigned char len, txpos, typos, tlen;
763
764 txpos = t->xpos;
765 typos = t->ypos;
766 tlen = t->len;
767
768 cptr = &t->text[txpos + typos * tlen];
769
770 switch(c) {
771 case CH_CURS_LEFT:
772 if(txpos > 0) {
773 --txpos;
774 }
775 break;
776
777 case CH_CURS_RIGHT:
778 if(txpos < tlen &&
779 *cptr != 0) {
780 ++txpos;
781 }
782 break;
783
784 case CH_CURS_UP:
785#if CTK_CONF_TEXTENTRY_MULTILINE
786 if(t->h == 1) {
787 txpos = 0;
788 } else {
789 if(typos > 0) {
790 --typos;
791 } else {
792 t->state = CTK_TEXTENTRY_NORMAL;
793 }
794 }
795#else
796 txpos = 0;
797#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
798 break;
799
800 case CH_CURS_DOWN:
801#if CTK_CONF_TEXTENTRY_MULTILINE
802 if(t->h == 1) {
803 txpos = strlen(t->text);
804 } else {
805 if(typos < t->h - 1) {
806 ++typos;
807 } else {
808 t->state = CTK_TEXTENTRY_NORMAL;
809 }
810 }
811#else
812 txpos = strlen(t->text);
813#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
814 break;
815
816 case CH_ENTER:
817#if CTK_CONF_TEXTENTRY_MULTILINE
818 if(t->h == 1) {
819 t->state = CTK_TEXTENTRY_NORMAL;
820 } else {
821 if(typos < t->h - 1) {
822 ++typos;
823 txpos = 0;
824 } else {
825 t->state = CTK_TEXTENTRY_NORMAL;
826 }
827 }
828#else
829 t->state = CTK_TEXTENTRY_NORMAL;
830#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
831 break;
832
833 default:
834 len = tlen - txpos - 1;
835 if(c == CH_DEL) {
836 if(txpos > 0 && len > 0) {
837 strncpy(cptr - 1, cptr,
838 len);
839 *(cptr + len - 1) = 0;
840 --txpos;
841 }
842 } else {
843 if(len > 0) {
844 cptr2 = cptr + len - 1;
845 while(cptr2 + 1 > cptr) {
846 *(cptr2 + 1) = *cptr2;
847 --cptr2;
848 }
849
850 *cptr = c;
851 ++txpos;
852 }
853 }
854 break;
855 }
856
857 t->xpos = txpos;
858 t->ypos = typos;
859}
860/*-----------------------------------------------------------------------------------*/
861#if CTK_CONF_MENUS
862static unsigned char
863menus_input(ctk_arch_key_t c)
864{
865 struct ctk_window *w;
866
867 if(menus.open->nitems > maxnitems) {
868 maxnitems = menus.open->nitems;
869 }
870
871
872 switch(c) {
873 case CH_CURS_RIGHT:
874 switch_open_menu(1);
875
876 return REDRAW_MENUPART;
877
878 case CH_CURS_DOWN:
879 switch_menu_item(1);
880 return REDRAW_MENUS;
881
882 case CH_CURS_LEFT:
883 switch_open_menu(0);
884 return REDRAW_MENUPART;
885
886 case CH_CURS_UP:
887 switch_menu_item(0);
888 return REDRAW_MENUS;
889
890 case CH_ENTER:
891 lastmenu = menus.open;
892 if(menus.open == &desktopmenu) {
893 for(w = windows; w != NULL; w = w->next) {
894 if(w->title == desktopmenu.items[desktopmenu.active].title) {
895 ctk_window_open(w);
896 menus.open = NULL;
897 return REDRAW_ALL;
898 }
899 }
900 } else {
901 dispatcher_emit(ctk_signal_menu_activate, menus.open,
902 DISPATCHER_BROADCAST);
903 }
904 menus.open = NULL;
905 return REDRAW_MENUPART;
906
adamdunkels88ed9c42003-03-28 12:10:09 +0000907 case CTK_CONF_MENU_KEY:
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000908 lastmenu = menus.open;
909 menus.open = NULL;
910 return REDRAW_MENUPART;
911 }
912}
913#endif /* CTK_CONF_MENUS */
914/*-----------------------------------------------------------------------------------*/
915static void
916idle(void)
917{
918 static ctk_arch_key_t c;
919 static unsigned char i;
920 register struct ctk_window *window;
921 register struct ctk_widget *widget;
922
923#if CTK_CONF_MENUS
924 if(menus.open != NULL) {
925 maxnitems = menus.open->nitems;
926 } else {
927 maxnitems = 0;
928 }
929#endif /* CTK_CONF_MENUS */
930
931 if(mode == CTK_MODE_SCREENSAVER) {
932#ifdef CTK_SCREENSAVER_RUN
933 CTK_SCREENSAVER_RUN();
934#endif /* CTK_SCREENSAVER_RUN */
935 if(ctk_arch_keyavail()) {
936 mode = CTK_MODE_NORMAL;
937 ctk_draw_init();
938 ctk_redraw();
939 }
940 } else if(mode == CTK_MODE_NORMAL) {
941 while(ctk_arch_keyavail()) {
942
943 screensaver_timer = 0;
944
945 c = ctk_arch_getkey();
946
947
948 if(dialog != NULL) {
949 window = dialog;
950 } else if(windows != NULL) {
951 window = windows;
952 } else {
953 window = &desktop_window;
954 }
955 widget = window->focused;
956
957
958 if(widget != NULL &&
959 widget->type == CTK_WIDGET_TEXTENTRY &&
960 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
961 textentry_input(c, (struct ctk_textentry *)widget);
962 add_redrawwidget(widget);
963#if CTK_CONF_MENUS
964 } else if(menus.open != NULL) {
965 redraw |= menus_input(c);
966#endif /* CTK_CONF_MENUS */
967 } else {
968 switch(c) {
969 case CH_CURS_RIGHT:
970 switch_focus_widget(RIGHT);
971 break;
972 case CH_CURS_DOWN:
973 switch_focus_widget(DOWN);
974 break;
975 case CH_CURS_LEFT:
976 switch_focus_widget(LEFT);
977 break;
978 case CH_CURS_UP:
979 switch_focus_widget(UP);
980 break;
981 case CH_ENTER:
982 redraw |= activate(widget);
983 break;
984#if CTK_CONF_MENUS
985 case CTK_CONF_MENU_KEY:
986 if(dialog == NULL) {
987 if(lastmenu == NULL) {
988 menus.open = menus.menus;
989 } else {
990 menus.open = lastmenu;
991 }
992 menus.open->active = 0;
993 redraw |= REDRAW_MENUS;
994 }
995 break;
996#endif /* CTK_CONF_MENUS */
997 case CTK_CONF_WINDOWSWITCH_KEY:
998 if(windows != NULL) {
999 for(window = windows; window->next != NULL;
1000 window = window->next);
1001 ctk_window_open(window);
1002 ctk_redraw();
1003 }
1004 break;
1005 default:
1006 if(widget->type == CTK_WIDGET_TEXTENTRY) {
1007 widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1008 textentry_input(c, (struct ctk_textentry *)widget);
1009 add_redrawwidget(widget);
1010 } else {
1011 dispatcher_emit(ctk_signal_keypress, (void *)c,
1012 window->owner);
1013 }
1014 break;
1015 }
1016 }
1017
1018 if(redraw & REDRAW_WIDGETS) {
1019 for(i = 0; i < redraw_widgetptr; ++i) {
1020 ctk_widget_redraw(redraw_widgets[i]);
1021 }
1022 redraw &= ~REDRAW_WIDGETS;
1023 redraw_widgetptr = 0;
1024 }
1025 }
1026 if(redraw & REDRAW_ALL) {
1027 do_redraw_all(1, height);
1028#if CTK_CONF_MENUS
1029 } else if(redraw & REDRAW_MENUPART) {
1030 do_redraw_all(1, maxnitems + 1);
1031 } else if(redraw & REDRAW_MENUS) {
1032 ctk_draw_menus(&menus);
1033#endif /* CTK_CONF_MENUS */
1034 } else if(redraw & REDRAW_FOCUS) {
1035 if(dialog != NULL) {
1036 ctk_window_redraw(dialog);
1037 } else if(windows != NULL) {
1038 ctk_window_redraw(windows);
1039 } else {
1040 ctk_window_redraw(&desktop_window);
1041 }
1042 } else if(redraw & REDRAW_WIDGETS) {
1043 for(i = 0; i < redraw_widgetptr; ++i) {
1044 ctk_widget_redraw(redraw_widgets[i]);
1045 }
1046 }
1047 redraw = 0;
1048 redraw_widgetptr = 0;
1049#if CTK_CONF_WINDOWMOVE
1050 } else if(mode == CTK_MODE_WINDOWMOVE) {
1051
1052 redraw = 0;
1053
1054 window = windows;
1055
1056 while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
1057
1058 screensaver_timer = 0;
1059
1060 c = ctk_arch_getkey();
1061
1062 switch(c) {
1063 case CH_CURS_RIGHT:
1064 ++window->x;
1065 if(window->x + window->w + 1 >= width) {
1066 --window->x;
1067 }
1068 redraw = REDRAW_ALL;
1069 break;
1070 case CH_CURS_LEFT:
1071 if(window->x > 0) {
1072 --window->x;
1073 }
1074 redraw = REDRAW_ALL;
1075 break;
1076 case CH_CURS_DOWN:
1077 ++window->y;
1078 if(window->y + window->h + 2 >= height) {
1079 --window->y;
1080 }
1081 redraw = REDRAW_ALL;
1082 break;
1083 case CH_CURS_UP:
1084 if(window->y > 0) {
1085 --window->y;
1086 }
1087 redraw = REDRAW_ALL;
1088 break;
1089 case CH_ENTER:
1090 case CH_ESC:
1091 mode = CTK_MODE_NORMAL;
1092 redraw = REDRAW_ALL;
1093 break;
1094 }
1095 }
1096 if(redraw & REDRAW_ALL) {
1097 do_redraw_all(1, height);
1098 }
1099 redraw = 0;
1100#endif /* CTK_CONF_WINDOWMOVE */
1101 }
1102}
1103/*-----------------------------------------------------------------------------------*/
1104static void
1105sighandler(ek_signal_t s, ek_data_t data)
1106{
1107 if(s == ctk_signal_timer) {
1108 if(mode == CTK_MODE_NORMAL) {
1109 ++screensaver_timer;
1110 if(screensaver_timer == SCREENSAVER_TIMEOUT) {
1111#ifdef CTK_SCREENSAVER_INIT
1112 CTK_SCREENSAVER_INIT();
1113#endif /* CTK_SCREENSAVER_INIT */
1114 mode = CTK_MODE_SCREENSAVER;
1115 screensaver_timer = 0;
1116 }
1117 }
1118 dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
1119 }
1120}
1121/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +00001122
1123/*-----------------------------------------------------------------------------------*/