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