blob: 13d3902d1237a88c217e37548774c1abc56f353b [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 *
adamdunkels19a787c2003-04-09 09:22:24 +000035 * $Id: ctk.c,v 1.9 2003/04/09 09:22:24 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"
adamdunkelsc4902862003-04-09 00:30:45 +000045#include "ctk-mouse.h"
adamdunkelsca9ddcb2003-03-19 14:13:31 +000046
47static unsigned char height, width;
48
49static unsigned char mode;
50
51static struct ctk_window desktop_window;
52static struct ctk_window *windows;
53static struct ctk_window *dialog;
54
55#if CTK_CONF_MENUS
56static struct ctk_menus menus;
57static struct ctk_menu *lastmenu;
58static struct ctk_menu desktopmenu;
59#endif /* CTK_CONF_MENUS */
60
61#ifndef NULL
62#define NULL (void *)0
63#endif /* NULL */
64
65
66#define REDRAW_NONE 0
67#define REDRAW_ALL 1
68#define REDRAW_FOCUS 2
69#define REDRAW_WIDGETS 4
70#define REDRAW_MENUS 8
71#define REDRAW_MENUPART 16
72
73#define MAX_REDRAWWIDGETS 4
74static unsigned char redraw;
75static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS];
76static unsigned char redraw_widgetptr;
77static unsigned char maxnitems;
78
79static unsigned char iconx, icony;
80#define ICONX_START (width - 5)
81#define ICONY_START 0
82#define ICONX_DELTA -8
83#define ICONY_DELTA 5
84#define ICONY_MAX (height - 4)
85
adamdunkelsc4902862003-04-09 00:30:45 +000086static void ctk_idle(void);
87static void ctk_sighandler(ek_signal_t s, ek_data_t data);
adamdunkelsca9ddcb2003-03-19 14:13:31 +000088static struct dispatcher_proc p =
adamdunkelsc4902862003-04-09 00:30:45 +000089 {DISPATCHER_PROC("CTK Contiki GUI", ctk_idle, ctk_sighandler, NULL)};
adamdunkelsca9ddcb2003-03-19 14:13:31 +000090static ek_id_t ctkid;
91
adamdunkelsca9ddcb2003-03-19 14:13:31 +000092ek_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,
adamdunkelsc4902862003-04-09 00:30:45 +000099 ctk_signal_window_close,
100 ctk_signal_pointer_move,
101 ctk_signal_pointer_down,
102 ctk_signal_pointer_up;
103
adamdunkels19a787c2003-04-09 09:22:24 +0000104#if CTK_CONF_MOUSE_SUPPORT
adamdunkelse0683312003-04-09 09:02:52 +0000105unsigned short mouse_last_x, mouse_last_y, mouse_last_button;
adamdunkels19a787c2003-04-09 09:22:24 +0000106#endif /* CTK_CONF_MOUSE_SUPPORT */
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000107
108static unsigned short screensaver_timer;
109#define SCREENSAVER_TIMEOUT (5*60)
110
111#if CTK_CONF_MENUS
112/*-----------------------------------------------------------------------------------*/
113/* make_desktopmenu(void)
114 *
115 * Creates the leftmost menu, "Desktop". Since the desktop menu
116 * contains the list of all open windows, this function will be called
117 * whenever a window is opened or closed.
118 */
119static void
120make_desktopmenu(void)
121{
122 struct ctk_window *w;
123
124 desktopmenu.nitems = 0;
125
126 if(windows == NULL) {
127 ctk_menuitem_add(&desktopmenu, "(No windows)");
128 } else {
129 for(w = windows; w != NULL; w = w->next) {
130 ctk_menuitem_add(&desktopmenu, w->title);
131 }
132 }
133}
134#endif /* CTK_CONF_MENUS */
135/*-----------------------------------------------------------------------------------*/
136/* ctk_init(void)
137 *
138 * Initializes CTK. Must be called before any other CTK function.
139 */
140void
141ctk_init(void)
142{
143 ctkid = dispatcher_start(&p);
144
145 windows = NULL;
146 dialog = NULL;
147
148#if CTK_CONF_MENUS
149 ctk_menu_new(&desktopmenu, "Desktop");
150 make_desktopmenu();
151 menus.menus = menus.desktopmenu = &desktopmenu;
152#endif /* CTK_CONF_MENUS */
153
adamdunkelsc4902862003-04-09 00:30:45 +0000154 ctk_mouse_init();
155
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000156 ctk_draw_init();
157
158 height = ctk_draw_height();
159 width = ctk_draw_width();
160
161 desktop_window.active = NULL;
162
163
164 ctk_signal_keypress = dispatcher_sigalloc();
165 ctk_signal_timer = dispatcher_sigalloc();
166 ctk_signal_button_activate = dispatcher_sigalloc();
167 ctk_signal_button_hover = dispatcher_sigalloc();
168 ctk_signal_hyperlink_activate = dispatcher_sigalloc();
169 ctk_signal_hyperlink_hover = dispatcher_sigalloc();
170 ctk_signal_menu_activate = dispatcher_sigalloc();
171 ctk_signal_window_close = dispatcher_sigalloc();
adamdunkelsc4902862003-04-09 00:30:45 +0000172
173 ctk_signal_pointer_move = dispatcher_sigalloc();
174 ctk_signal_pointer_down = dispatcher_sigalloc();
175 ctk_signal_pointer_up = dispatcher_sigalloc();
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000176
177 dispatcher_listen(ctk_signal_timer);
178 dispatcher_timer(ctk_signal_timer, NULL, CLK_TCK);
179
180 mode = CTK_MODE_NORMAL;
181
182 iconx = ICONX_START;
183 icony = ICONY_START;
184
185}
186/*-----------------------------------------------------------------------------------*/
187/* void ctk_mode_set()
188 */
189void
190ctk_mode_set(unsigned char m) {
191 mode = m;
192}
193/*-----------------------------------------------------------------------------------*/
194unsigned char
195ctk_mode_get(void) {
196 return mode;
197}
198/*-----------------------------------------------------------------------------------*/
199void
adamdunkelsd07a5422003-04-05 12:22:35 +0000200ctk_icon_add(CC_REGISTER_ARG struct ctk_widget *icon,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000201 ek_id_t id)
202{
203#if CTK_CONF_ICONS
204 icon->x = iconx;
205 icon->y = icony;
206 icon->widget.icon.owner = id;
207
208 icony += ICONY_DELTA;
209 if(icony >= ICONY_MAX) {
210 icony = ICONY_START;
211 iconx += ICONX_DELTA;
212 }
213
214 ctk_widget_add(&desktop_window, icon);
215#endif /* CTK_CONF_ICONS */
216}
217/*-----------------------------------------------------------------------------------*/
218void
219ctk_dialog_open(struct ctk_window *d)
220{
221 dialog = d;
222}
223/*-----------------------------------------------------------------------------------*/
224void
225ctk_dialog_close(void)
226{
227 dialog = NULL;
228}
229/*-----------------------------------------------------------------------------------*/
230void
adamdunkelsd07a5422003-04-05 12:22:35 +0000231ctk_window_open(CC_REGISTER_ARG struct ctk_window *w)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000232{
233 struct ctk_window *w2;
234
235 /* Check if already open. */
236 for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next);
237 if(w2 == NULL) {
238 /* Not open, so we add it at the head of the list of open
239 windows. */
240 w->next = windows;
241 if(windows != NULL) {
242 windows->prev = w;
243 }
244 windows = w;
245 w->prev = NULL;
246 } else {
247 /* Window already open, so we move it to the front of the windows
248 list. */
249 if(w != windows) {
250 if(w->next != NULL) {
251 w->next->prev = w->prev;
252 }
253 if(w->prev != NULL) {
254 w->prev->next = w->next;
255 }
256 w->next = windows;
257 windows->prev = w;
258 windows = w;
259 w->prev = NULL;
260 }
261 }
262
263#if CTK_CONF_MENUS
264 /* Recreate the Desktop menu's window entries.*/
265 make_desktopmenu();
266#endif /* CTK_CONF_MENUS */
267}
268/*-----------------------------------------------------------------------------------*/
269void
270ctk_window_close(struct ctk_window *w)
271{
272 struct ctk_window *w2;
273
274 if(w == NULL) {
275 return;
276 }
277
278 /* Check if the window to be closed is the first window on the
279 list. */
280 if(w == windows) {
281 windows = w->next;
282 if(windows != NULL) {
283 windows->prev = NULL;
284 }
285 w->next = w->prev = NULL;
286 } else {
287 /* Otherwise we step through the list until we find the window
288 before the one to be closed. We then redirect its ->next
289 pointer and its ->next->prev. */
adamdunkels3cf116a2003-04-08 19:28:15 +0000290 for(w2 = windows; w2 != NULL && w2->next != w; w2 = w2->next);
291
292 if(w2 == NULL) {
293 /* The window wasn't open, so there is nothing more for us to
294 do. */
295 return;
296 }
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000297
298 if(w->next != NULL) {
299 w->next->prev = w->prev;
300 }
301 w2->next = w->next;
302
303 w->next = w->prev = NULL;
304 }
305
306#if CTK_CONF_MENUS
307 /* Recreate the Desktop menu's window entries.*/
308 make_desktopmenu();
309#endif /* CTK_CONF_MENUS */
310}
311/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +0000312static void
313make_windowbuttons(CC_REGISTER_ARG struct ctk_window *window)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000314{
315#if CTK_CONF_WINDOWMOVE
316 CTK_BUTTON_NEW(&window->titlebutton, 0, -1, window->titlelen, window->title);
317#else
318 CTK_LABEL_NEW(&window->titlebutton, 0, -1, window->titlelen, 1, window->title);
319#endif /* CTK_CONF_WINDOWMOVE */
320 CTK_WIDGET_ADD(window, &window->titlebutton);
321
322
323#if CTK_CONF_WINDOWCLOSE
324 CTK_BUTTON_NEW(&window->closebutton, window->w - 3, -1, 1, "x");
325#else
326 CTK_LABEL_NEW(&window->closebutton, window->w - 4, -1, 3, 1, " ");
327#endif /* CTK_CONF_WINDOWCLOSE */
328 CTK_WIDGET_ADD(window, &window->closebutton);
329}
330/*-----------------------------------------------------------------------------------*/
331void
332ctk_window_clear(struct ctk_window *window)
333{
334 window->active = window->inactive = window->focused = NULL;
335
336 make_windowbuttons(window);
337}
338/*-----------------------------------------------------------------------------------*/
339void
340ctk_menu_add(struct ctk_menu *menu)
341{
342#if CTK_CONF_MENUS
343 struct ctk_menu *m;
344
345 if(lastmenu == NULL) {
346 lastmenu = menu;
347 }
348
349 for(m = menus.menus; m->next != NULL; m = m->next) {
350 if(m == menu) {
351 return;
352 }
353 }
354 m->next = menu;
355 menu->next = NULL;
356#endif /* CTK_CONF_MENUS */
357}
358/*-----------------------------------------------------------------------------------*/
359void
360ctk_menu_remove(struct ctk_menu *menu)
361{
362#if CTK_CONF_MENUS
363 struct ctk_menu *m;
364
365 for(m = menus.menus; m->next != NULL; m = m->next) {
366 if(m->next == menu) {
367 m->next = menu->next;
368 return;
369 }
370 }
371#endif /* CTK_CONF_MENUS */
372}
373/*-----------------------------------------------------------------------------------*/
374static void
375do_redraw_all(unsigned char clipy1, unsigned char clipy2)
376{
377 struct ctk_window *w;
378 struct ctk_widget *widget;
379
380 if(mode != CTK_MODE_NORMAL &&
381 mode != CTK_MODE_WINDOWMOVE) {
382 return;
383 }
384
385 ctk_draw_clear(clipy1, clipy2);
386
387 /* Draw widgets in root window */
388 for(widget = desktop_window.active;
389 widget != NULL; widget = widget->next) {
390 ctk_draw_widget(widget, 0, clipy1, clipy2);
391 }
392
393 /* Draw windows */
394 if(windows != NULL) {
395 /* Find the last window.*/
396 for(w = windows; w->next != NULL; w = w->next);
397
398 /* Draw the windows from back to front. */
399 for(; w != windows; w = w->prev) {
adamdunkels9d3a0e52003-04-02 09:53:59 +0000400 ctk_draw_clear_window(w, 0, clipy1, clipy2);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000401 ctk_draw_window(w, 0, clipy1, clipy2);
402 }
403 /* Draw focused window */
adamdunkels9d3a0e52003-04-02 09:53:59 +0000404 ctk_draw_clear_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000405 ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2);
406 }
407
408 /* Draw dialog (if any) */
409 if(dialog != NULL) {
410 ctk_draw_dialog(dialog);
411 }
412
413#if CTK_CONF_MENUS
414 ctk_draw_menus(&menus);
415#endif /* CTK_CONF_MENUS */
416}
417/*-----------------------------------------------------------------------------------*/
418void
419ctk_redraw(void)
420{
421 if(DISPATCHER_CURRENT() == ctkid) {
422 if(mode == CTK_MODE_NORMAL ||
423 mode == CTK_MODE_WINDOWMOVE) {
424 do_redraw_all(1, height);
425 }
426 } else {
427 redraw |= REDRAW_ALL;
428 }
429}
430/*-----------------------------------------------------------------------------------*/
431void
432ctk_window_redraw(struct ctk_window *w)
433{
434 /* Only redraw the window if it is a dialog or if it is the foremost
435 window. */
436 if(mode != CTK_MODE_NORMAL) {
437 return;
438 }
439
440 if(w == dialog) {
441 ctk_draw_dialog(w);
442 } else if(dialog == NULL &&
443#if CTK_CONF_MENUS
444 menus.open == NULL &&
445#endif /* CTK_CONF_MENUS */
446 windows == w) {
447 ctk_draw_window(w, CTK_FOCUS_WINDOW,
448 0, height);
449 }
450}
451/*-----------------------------------------------------------------------------------*/
452static void
adamdunkelsd07a5422003-04-05 12:22:35 +0000453window_new(CC_REGISTER_ARG struct ctk_window *window,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000454 unsigned char w, unsigned char h,
455 char *title)
456{
457 window->x = window->y = 0;
458 window->w = w;
459 window->h = h;
460 window->title = title;
461 if(title != NULL) {
462 window->titlelen = strlen(title);
463 } else {
464 window->titlelen = 0;
465 }
466 window->next = window->prev = NULL;
467 window->owner = DISPATCHER_CURRENT();
468 window->active = window->inactive = window->focused = NULL;
469}
470/*-----------------------------------------------------------------------------------*/
471void
472ctk_window_new(struct ctk_window *window,
473 unsigned char w, unsigned char h,
474 char *title)
475{
476 window_new(window, w, h, title);
477
478 make_windowbuttons(window);
479}
480/*-----------------------------------------------------------------------------------*/
481void
adamdunkelsd07a5422003-04-05 12:22:35 +0000482ctk_dialog_new(CC_REGISTER_ARG struct ctk_window *window,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000483 unsigned char w, unsigned char h)
484{
485 window_new(window, w, h, NULL);
486
487 window->x = (width - w) / 2;
488 window->y = (height - h - 1) / 2;
489}
490/*-----------------------------------------------------------------------------------*/
491void
adamdunkelsd07a5422003-04-05 12:22:35 +0000492ctk_menu_new(CC_REGISTER_ARG struct ctk_menu *menu,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000493 char *title)
494{
495#if CTK_CONF_MENUS
496 menu->next = NULL;
497 menu->title = title;
498 menu->titlelen = strlen(title);
499 menu->active = 0;
500 menu->nitems = 0;
501#endif /* CTK_CONF_MENUS */
502}
503/*-----------------------------------------------------------------------------------*/
504unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000505ctk_menuitem_add(CC_REGISTER_ARG struct ctk_menu *menu,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000506 char *name)
507{
508#if CTK_CONF_MENUS
509 if(menu->nitems == CTK_CONF_MAXMENUITEMS) {
510 return 0;
511 }
512 menu->items[menu->nitems].title = name;
513 menu->items[menu->nitems].titlelen = strlen(name);
514 return menu->nitems++;
515#else
516 return 0;
517#endif /* CTK_CONF_MENUS */
518}
519/*-----------------------------------------------------------------------------------*/
520static void
521add_redrawwidget(struct ctk_widget *w)
522{
523 static unsigned char i;
524
525 if(redraw_widgetptr == MAX_REDRAWWIDGETS) {
526 redraw |= REDRAW_FOCUS;
527 } else {
528 redraw |= REDRAW_WIDGETS;
529 /* Check if it is in the queue already. If so, we don't add it
530 again. */
531 for(i = 0; i < redraw_widgetptr; ++i) {
532 if(redraw_widgets[i] == w) {
533 return;
534 }
535 }
536 redraw_widgets[redraw_widgetptr++] = w;
537 }
538}
539/*-----------------------------------------------------------------------------------*/
540void
541ctk_widget_redraw(struct ctk_widget *widget)
542{
543 struct ctk_window *window;
544
545 if(mode != CTK_MODE_NORMAL) {
546 return;
547 }
548
549 /* If this function isn't called by CTK itself, we only queue the
550 redraw request. */
551 if(DISPATCHER_CURRENT() != ctkid) {
552 redraw |= REDRAW_WIDGETS;
553 add_redrawwidget(widget);
554 } else {
555
556 /* Only redraw widgets that are in the foremost window. If we
557 would allow redrawing widgets in non-focused windows, we would
558 have to redraw all the windows that cover the non-focused
559 window as well, which would lead to flickering.
560
561 Also, we avoid drawing any widgets when the menus are active.
562 */
563
564#if CTK_CONF_MENUS
565 if(menus.open == NULL)
566#endif /* CTK_CONF_MENUS */
567 {
568 window = widget->window;
569 if(window == dialog) {
570 ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height);
571 } else if(window == windows) {
adamdunkels9d3a0e52003-04-02 09:53:59 +0000572 ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000573 } else if(window == &desktop_window) {
574 ctk_draw_widget(widget, 0, 0, height);
575 }
576 }
577 }
578}
579/*-----------------------------------------------------------------------------------*/
580void
adamdunkelsd07a5422003-04-05 12:22:35 +0000581ctk_widget_add(CC_REGISTER_ARG struct ctk_window *window,
582 CC_REGISTER_ARG struct ctk_widget *widget)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000583{
584 if(widget->type == CTK_WIDGET_LABEL ||
585 widget->type == CTK_WIDGET_SEPARATOR) {
586 widget->next = window->inactive;
587 window->inactive = widget;
588 widget->window = window;
589 } else {
590 widget->next = window->active;
591 window->active = widget;
592 widget->window = window;
593 if(window->focused == NULL) {
594 window->focused = widget;
595 }
596 }
597}
598/*-----------------------------------------------------------------------------------*/
adamdunkelse0683312003-04-09 09:02:52 +0000599static void
600focus_widget(struct ctk_widget *focus)
601{
602 struct ctk_window *window;
603
604 window = focus->window;
605
606 if(focus != window->focused) {
607 window->focused = focus;
608 /* The operation changed the focus, so we emit a "hover" signal
609 for those widgets that support it. */
610
611 if(window->focused->type == CTK_WIDGET_HYPERLINK) {
612 dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
613 window->owner);
614 } else if(window->focused->type == CTK_WIDGET_BUTTON) {
615 dispatcher_emit(ctk_signal_button_hover, window->focused,
616 window->owner);
617 }
618
619 add_redrawwidget(window->focused);
620 }
621
622}
623/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000624#define UP 0
625#define DOWN 1
626#define LEFT 2
627#define RIGHT 3
628static void
629switch_focus_widget(unsigned char direction)
630{
631 register struct ctk_window *window;
632 register struct ctk_widget *focus;
633 struct ctk_widget *widget;
634
635
636 if(dialog != NULL) {
637 window = dialog;
638 } else {
639 window = windows;
640 }
641
642 /* If there are no windows open, we move focus around between the
643 icons on the root window instead. */
644 if(window == NULL) {
645 window = &desktop_window;
646 }
647
648 focus = window->focused;
649 add_redrawwidget(focus);
650
651 if((direction & 1) == 0) {
652 /* Move focus "up" */
653 focus = focus->next;
654 } else {
655 /* Move focus "down" */
656 for(widget = window->active;
657 widget != NULL; widget = widget->next) {
658 if(widget->next == focus) {
659 break;
660 }
661 }
662 focus = widget;
663 if(focus == NULL) {
664 if(window->active != NULL) {
665 for(focus = window->active;
666 focus->next != NULL; focus = focus->next);
667 }
668 }
669 }
670 if(focus == NULL) {
671 focus = window->active;
672 }
adamdunkelse0683312003-04-09 09:02:52 +0000673
674 focus_widget(focus);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000675}
676/*-----------------------------------------------------------------------------------*/
677#if CTK_CONF_MENUS
678static void
679switch_open_menu(unsigned char rightleft)
680{
681 struct ctk_menu *menu;
682
683 if(rightleft == 0) {
684 /* Move right */
685 for(menu = menus.menus; menu != NULL; menu = menu->next) {
686 if(menu->next == menus.open) {
687 break;
688 }
689 }
690 lastmenu = menus.open;
691 menus.open = menu;
692 if(menus.open == NULL) {
693 for(menu = menus.menus;
694 menu->next != NULL; menu = menu->next);
695 menus.open = menu;
696 }
697 } else {
698 /* Move to left */
699 lastmenu = menus.open;
700 menus.open = menus.open->next;
701 if(menus.open == NULL) {
702 menus.open = menus.menus;
703 }
704 }
705
706 menus.open->active = 0;
707 /* ctk_redraw();*/
708}
709/*-----------------------------------------------------------------------------------*/
710static void
711switch_menu_item(unsigned char updown)
712{
713 register struct ctk_menu *m;
714
715 m = menus.open;
716
717 if(updown == 0) {
718 /* Move up */
719 if(m->active == 0) {
720 m->active = m->nitems - 1;
721 } else {
722 --m->active;
723 if(m->items[m->active].title[0] == '-') {
724 --m->active;
725 }
726 }
727 } else {
728 /* Move down */
729 if(m->active >= m->nitems - 1) {
730 m->active = 0;
731 } else {
732 ++m->active;
733 if(m->items[m->active].title[0] == '-') {
734 ++m->active;
735 }
736 }
737 }
738
739}
740#endif /* CTK_CONF_MENUS */
741/*-----------------------------------------------------------------------------------*/
742static unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000743activate(CC_REGISTER_ARG struct ctk_widget *w)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000744{
745 static unsigned char len;
746
747 if(w->type == CTK_WIDGET_BUTTON) {
748 if(w == (struct ctk_widget *)&windows->closebutton) {
749#if CTK_CONF_WINDOWCLOSE
adamdunkels5cb690c2003-04-02 11:36:21 +0000750 dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000751 ctk_window_close(windows);
752 return REDRAW_ALL;
753#endif /* CTK_CONF_WINDOWCLOSE */
754 } else if(w == (struct ctk_widget *)&windows->titlebutton) {
755#if CTK_CONF_WINDOWCLOSE
756 mode = CTK_MODE_WINDOWMOVE;
757#endif /* CTK_CONF_WINDOWCLOSE */
758 } else {
759 dispatcher_emit(ctk_signal_button_activate, w,
760 w->window->owner);
761 }
762#if CTK_CONF_ICONS
763 } else if(w->type == CTK_WIDGET_ICON) {
764 dispatcher_emit(ctk_signal_button_activate, w,
765 w->widget.icon.owner);
766#endif /* CTK_CONF_ICONS */
767 } else if(w->type == CTK_WIDGET_HYPERLINK) {
768 dispatcher_emit(ctk_signal_hyperlink_activate, w,
769 DISPATCHER_BROADCAST);
770 } else if(w->type == CTK_WIDGET_TEXTENTRY) {
771 if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
772 w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
773 len = strlen(w->widget.textentry.text);
774 if(w->widget.textentry.xpos > len) {
775 w->widget.textentry.xpos = len;
776 }
777 } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
778 w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
779 }
780 add_redrawwidget(w);
781 return REDRAW_WIDGETS;
782 }
783 return REDRAW_NONE;
784}
785/*-----------------------------------------------------------------------------------*/
786static void
787textentry_input(ctk_arch_key_t c,
adamdunkelsd07a5422003-04-05 12:22:35 +0000788 CC_REGISTER_ARG struct ctk_textentry *t)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000789{
790 static char *cptr, *cptr2;
791 static unsigned char len, txpos, typos, tlen;
792
793 txpos = t->xpos;
794 typos = t->ypos;
795 tlen = t->len;
796
797 cptr = &t->text[txpos + typos * tlen];
798
799 switch(c) {
800 case CH_CURS_LEFT:
801 if(txpos > 0) {
802 --txpos;
803 }
804 break;
805
806 case CH_CURS_RIGHT:
807 if(txpos < tlen &&
808 *cptr != 0) {
809 ++txpos;
810 }
811 break;
812
813 case CH_CURS_UP:
814#if CTK_CONF_TEXTENTRY_MULTILINE
815 if(t->h == 1) {
816 txpos = 0;
817 } else {
818 if(typos > 0) {
819 --typos;
820 } else {
821 t->state = CTK_TEXTENTRY_NORMAL;
822 }
823 }
824#else
825 txpos = 0;
826#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
827 break;
828
829 case CH_CURS_DOWN:
830#if CTK_CONF_TEXTENTRY_MULTILINE
831 if(t->h == 1) {
832 txpos = strlen(t->text);
833 } else {
834 if(typos < t->h - 1) {
835 ++typos;
836 } else {
837 t->state = CTK_TEXTENTRY_NORMAL;
838 }
839 }
840#else
841 txpos = strlen(t->text);
842#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
843 break;
844
845 case CH_ENTER:
846#if CTK_CONF_TEXTENTRY_MULTILINE
847 if(t->h == 1) {
848 t->state = CTK_TEXTENTRY_NORMAL;
849 } else {
850 if(typos < t->h - 1) {
851 ++typos;
852 txpos = 0;
853 } else {
854 t->state = CTK_TEXTENTRY_NORMAL;
855 }
856 }
857#else
858 t->state = CTK_TEXTENTRY_NORMAL;
859#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
860 break;
861
862 default:
863 len = tlen - txpos - 1;
864 if(c == CH_DEL) {
865 if(txpos > 0 && len > 0) {
866 strncpy(cptr - 1, cptr,
867 len);
868 *(cptr + len - 1) = 0;
869 --txpos;
870 }
871 } else {
872 if(len > 0) {
873 cptr2 = cptr + len - 1;
874 while(cptr2 + 1 > cptr) {
875 *(cptr2 + 1) = *cptr2;
876 --cptr2;
877 }
878
879 *cptr = c;
880 ++txpos;
881 }
882 }
883 break;
884 }
885
886 t->xpos = txpos;
887 t->ypos = typos;
888}
889/*-----------------------------------------------------------------------------------*/
890#if CTK_CONF_MENUS
891static unsigned char
892menus_input(ctk_arch_key_t c)
893{
894 struct ctk_window *w;
895
896 if(menus.open->nitems > maxnitems) {
897 maxnitems = menus.open->nitems;
898 }
899
900
901 switch(c) {
902 case CH_CURS_RIGHT:
903 switch_open_menu(1);
904
905 return REDRAW_MENUPART;
906
907 case CH_CURS_DOWN:
908 switch_menu_item(1);
909 return REDRAW_MENUS;
910
911 case CH_CURS_LEFT:
912 switch_open_menu(0);
913 return REDRAW_MENUPART;
914
915 case CH_CURS_UP:
916 switch_menu_item(0);
917 return REDRAW_MENUS;
918
919 case CH_ENTER:
920 lastmenu = menus.open;
921 if(menus.open == &desktopmenu) {
922 for(w = windows; w != NULL; w = w->next) {
923 if(w->title == desktopmenu.items[desktopmenu.active].title) {
924 ctk_window_open(w);
925 menus.open = NULL;
926 return REDRAW_ALL;
927 }
928 }
929 } else {
930 dispatcher_emit(ctk_signal_menu_activate, menus.open,
931 DISPATCHER_BROADCAST);
932 }
933 menus.open = NULL;
934 return REDRAW_MENUPART;
935
adamdunkels88ed9c42003-03-28 12:10:09 +0000936 case CTK_CONF_MENU_KEY:
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000937 lastmenu = menus.open;
938 menus.open = NULL;
939 return REDRAW_MENUPART;
940 }
941}
942#endif /* CTK_CONF_MENUS */
943/*-----------------------------------------------------------------------------------*/
944static void
adamdunkelsc4902862003-04-09 00:30:45 +0000945ctk_idle(void)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000946{
947 static ctk_arch_key_t c;
adamdunkels19a787c2003-04-09 09:22:24 +0000948 static unsigned char i;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000949 register struct ctk_window *window;
adamdunkels19a787c2003-04-09 09:22:24 +0000950 register struct ctk_widget *widget;
951#if CTK_CONF_MOUSE_SUPPORT
952 static unsigned char mxc, myc, mouse_clicked;
953#endif /* CTK_CONF_MOUSE_SUPPORT */
adamdunkelsc4902862003-04-09 00:30:45 +0000954
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000955#if CTK_CONF_MENUS
956 if(menus.open != NULL) {
957 maxnitems = menus.open->nitems;
958 } else {
959 maxnitems = 0;
960 }
961#endif /* CTK_CONF_MENUS */
962
963 if(mode == CTK_MODE_SCREENSAVER) {
964#ifdef CTK_SCREENSAVER_RUN
965 CTK_SCREENSAVER_RUN();
966#endif /* CTK_SCREENSAVER_RUN */
967 if(ctk_arch_keyavail()) {
968 mode = CTK_MODE_NORMAL;
969 ctk_draw_init();
970 ctk_redraw();
971 }
adamdunkelsc4902862003-04-09 00:30:45 +0000972 } else if(mode == CTK_MODE_NORMAL) {
adamdunkels19a787c2003-04-09 09:22:24 +0000973#if CTK_CONF_MOUSE_SUPPORT
adamdunkelse0683312003-04-09 09:02:52 +0000974 if(dialog != NULL) {
975 window = dialog;
976 } else {
977 window = windows;
978 }
adamdunkelsc4902862003-04-09 00:30:45 +0000979
adamdunkelse0683312003-04-09 09:02:52 +0000980 /* If there has been a change in the mouse button(s), we send out
981 a signal. */
982 mouse_clicked = 0;
983 if(ctk_mouse_button() != mouse_last_button) {
984 mouse_last_button = ctk_mouse_button();
985 if(mouse_last_button == 0) {
986 dispatcher_emit(ctk_signal_pointer_up, NULL, window->owner);
987 } else {
988 dispatcher_emit(ctk_signal_pointer_down, (ek_data_t)mouse_last_button,
989 window->owner);
990 mouse_clicked = 1;
991 }
992 }
993
adamdunkelsc4902862003-04-09 00:30:45 +0000994 /* Check if the mouse pointer has moved, and if so we emit a
995 signal. */
996 if(ctk_mouse_x() != mouse_last_x ||
adamdunkelse0683312003-04-09 09:02:52 +0000997 ctk_mouse_y() != mouse_last_y ||
998 mouse_clicked != 0) {
adamdunkelsc4902862003-04-09 00:30:45 +0000999 mouse_last_x = ctk_mouse_x();
1000 mouse_last_y = ctk_mouse_y();
1001 dispatcher_emit(ctk_signal_pointer_move, NULL, window->owner);
1002
1003 /* Find out which widget currently is under the mouse pointer
1004 and give it focus, unless it already has focus. */
1005 mxc = ctk_mouse_xtoc(mouse_last_x);
adamdunkelse0683312003-04-09 09:02:52 +00001006 myc = ctk_mouse_ytoc(mouse_last_y) - 1;
adamdunkelsc4902862003-04-09 00:30:45 +00001007
adamdunkelse0683312003-04-09 09:02:52 +00001008 /* Check if the mouse is in the current window. */
adamdunkelsc4902862003-04-09 00:30:45 +00001009 if(mxc >= window->x &&
1010 mxc <= window->x + window->w &&
1011 myc >= window->y &&
1012 myc <= window->y + window->h) {
1013
adamdunkelse0683312003-04-09 09:02:52 +00001014 mxc = mxc - window->x - 1;
1015 myc = myc - window->y - 1;
1016
adamdunkelsc4902862003-04-09 00:30:45 +00001017
1018 redraw |= REDRAW_WIDGETS;
1019 add_redrawwidget(window->focused);
1020 window->focused = NULL;
1021
1022 /* Now find the appropriate widget to assign focus to. */
1023 for(widget = window->active; widget != NULL; widget = widget->next) {
1024 if(mxc >= widget->x &&
1025 mxc <= widget->x + widget->w &&
adamdunkelse0683312003-04-09 09:02:52 +00001026 myc == widget->y /* &&
adamdunkelsc4902862003-04-09 00:30:45 +00001027 ((widget->type == CTK_WIDGET_BITMAP ||
1028 widget->type == CTK_WIDGET_TEXTENTRY ||
1029 widget->type == CTK_WIDGET_ICON) &&
1030 (myc <= widget->y + ((struct ctk_bitmap *)widget)->h))*/) {
adamdunkelse0683312003-04-09 09:02:52 +00001031 focus_widget(widget);
1032 if(mouse_clicked != 0) {
1033 redraw |= activate(widget);
1034 }
adamdunkelsc4902862003-04-09 00:30:45 +00001035 break;
1036 }
1037 }
adamdunkelse0683312003-04-09 09:02:52 +00001038 } else {
1039 redraw |= REDRAW_WIDGETS;
1040 add_redrawwidget(window->focused);
1041 window->focused = NULL;
adamdunkelsc4902862003-04-09 00:30:45 +00001042 }
1043 }
adamdunkels19a787c2003-04-09 09:22:24 +00001044#endif /* CTK_CONF_MOUSE_SUPPORT */
adamdunkelsc4902862003-04-09 00:30:45 +00001045
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001046 while(ctk_arch_keyavail()) {
1047
1048 screensaver_timer = 0;
1049
1050 c = ctk_arch_getkey();
1051
1052
1053 if(dialog != NULL) {
1054 window = dialog;
1055 } else if(windows != NULL) {
1056 window = windows;
1057 } else {
1058 window = &desktop_window;
1059 }
1060 widget = window->focused;
1061
1062
1063 if(widget != NULL &&
1064 widget->type == CTK_WIDGET_TEXTENTRY &&
1065 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
1066 textentry_input(c, (struct ctk_textentry *)widget);
1067 add_redrawwidget(widget);
1068#if CTK_CONF_MENUS
1069 } else if(menus.open != NULL) {
1070 redraw |= menus_input(c);
1071#endif /* CTK_CONF_MENUS */
1072 } else {
1073 switch(c) {
1074 case CH_CURS_RIGHT:
1075 switch_focus_widget(RIGHT);
1076 break;
1077 case CH_CURS_DOWN:
1078 switch_focus_widget(DOWN);
1079 break;
1080 case CH_CURS_LEFT:
1081 switch_focus_widget(LEFT);
1082 break;
1083 case CH_CURS_UP:
1084 switch_focus_widget(UP);
1085 break;
1086 case CH_ENTER:
1087 redraw |= activate(widget);
1088 break;
1089#if CTK_CONF_MENUS
1090 case CTK_CONF_MENU_KEY:
1091 if(dialog == NULL) {
1092 if(lastmenu == NULL) {
1093 menus.open = menus.menus;
1094 } else {
1095 menus.open = lastmenu;
1096 }
1097 menus.open->active = 0;
1098 redraw |= REDRAW_MENUS;
1099 }
1100 break;
1101#endif /* CTK_CONF_MENUS */
1102 case CTK_CONF_WINDOWSWITCH_KEY:
1103 if(windows != NULL) {
1104 for(window = windows; window->next != NULL;
1105 window = window->next);
1106 ctk_window_open(window);
1107 ctk_redraw();
1108 }
1109 break;
1110 default:
1111 if(widget->type == CTK_WIDGET_TEXTENTRY) {
1112 widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1113 textentry_input(c, (struct ctk_textentry *)widget);
1114 add_redrawwidget(widget);
1115 } else {
1116 dispatcher_emit(ctk_signal_keypress, (void *)c,
1117 window->owner);
1118 }
1119 break;
1120 }
1121 }
1122
1123 if(redraw & REDRAW_WIDGETS) {
1124 for(i = 0; i < redraw_widgetptr; ++i) {
1125 ctk_widget_redraw(redraw_widgets[i]);
1126 }
1127 redraw &= ~REDRAW_WIDGETS;
1128 redraw_widgetptr = 0;
1129 }
1130 }
1131 if(redraw & REDRAW_ALL) {
1132 do_redraw_all(1, height);
1133#if CTK_CONF_MENUS
1134 } else if(redraw & REDRAW_MENUPART) {
1135 do_redraw_all(1, maxnitems + 1);
1136 } else if(redraw & REDRAW_MENUS) {
1137 ctk_draw_menus(&menus);
1138#endif /* CTK_CONF_MENUS */
1139 } else if(redraw & REDRAW_FOCUS) {
1140 if(dialog != NULL) {
1141 ctk_window_redraw(dialog);
1142 } else if(windows != NULL) {
1143 ctk_window_redraw(windows);
1144 } else {
1145 ctk_window_redraw(&desktop_window);
1146 }
1147 } else if(redraw & REDRAW_WIDGETS) {
1148 for(i = 0; i < redraw_widgetptr; ++i) {
1149 ctk_widget_redraw(redraw_widgets[i]);
1150 }
1151 }
1152 redraw = 0;
1153 redraw_widgetptr = 0;
1154#if CTK_CONF_WINDOWMOVE
1155 } else if(mode == CTK_MODE_WINDOWMOVE) {
1156
1157 redraw = 0;
1158
1159 window = windows;
1160
1161 while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
1162
1163 screensaver_timer = 0;
1164
1165 c = ctk_arch_getkey();
1166
1167 switch(c) {
1168 case CH_CURS_RIGHT:
1169 ++window->x;
1170 if(window->x + window->w + 1 >= width) {
1171 --window->x;
1172 }
1173 redraw = REDRAW_ALL;
1174 break;
1175 case CH_CURS_LEFT:
1176 if(window->x > 0) {
1177 --window->x;
1178 }
1179 redraw = REDRAW_ALL;
1180 break;
1181 case CH_CURS_DOWN:
1182 ++window->y;
1183 if(window->y + window->h + 2 >= height) {
1184 --window->y;
1185 }
1186 redraw = REDRAW_ALL;
1187 break;
1188 case CH_CURS_UP:
1189 if(window->y > 0) {
1190 --window->y;
1191 }
1192 redraw = REDRAW_ALL;
1193 break;
1194 case CH_ENTER:
1195 case CH_ESC:
1196 mode = CTK_MODE_NORMAL;
1197 redraw = REDRAW_ALL;
1198 break;
1199 }
1200 }
1201 if(redraw & REDRAW_ALL) {
1202 do_redraw_all(1, height);
1203 }
1204 redraw = 0;
1205#endif /* CTK_CONF_WINDOWMOVE */
1206 }
1207}
1208/*-----------------------------------------------------------------------------------*/
1209static void
adamdunkelsc4902862003-04-09 00:30:45 +00001210ctk_sighandler(ek_signal_t s, ek_data_t data)
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001211{
1212 if(s == ctk_signal_timer) {
1213 if(mode == CTK_MODE_NORMAL) {
1214 ++screensaver_timer;
1215 if(screensaver_timer == SCREENSAVER_TIMEOUT) {
1216#ifdef CTK_SCREENSAVER_INIT
1217 CTK_SCREENSAVER_INIT();
1218#endif /* CTK_SCREENSAVER_INIT */
1219 mode = CTK_MODE_SCREENSAVER;
1220 screensaver_timer = 0;
1221 }
1222 }
1223 dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
1224 }
1225}
1226/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +00001227