blob: 109b9c63646bda343ca15077167f69ef95244d2f [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 *
adamdunkelse0683312003-04-09 09:02:52 +000035 * $Id: ctk.c,v 1.8 2003/04/09 09:02:52 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
adamdunkelse0683312003-04-09 09:02:52 +0000104unsigned short mouse_last_x, mouse_last_y, mouse_last_button;
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/*-----------------------------------------------------------------------------------*/
adamdunkelse0683312003-04-09 09:02:52 +0000597static void
598focus_widget(struct ctk_widget *focus)
599{
600 struct ctk_window *window;
601
602 window = focus->window;
603
604 if(focus != window->focused) {
605 window->focused = focus;
606 /* The operation changed the focus, so we emit a "hover" signal
607 for those widgets that support it. */
608
609 if(window->focused->type == CTK_WIDGET_HYPERLINK) {
610 dispatcher_emit(ctk_signal_hyperlink_hover, window->focused,
611 window->owner);
612 } else if(window->focused->type == CTK_WIDGET_BUTTON) {
613 dispatcher_emit(ctk_signal_button_hover, window->focused,
614 window->owner);
615 }
616
617 add_redrawwidget(window->focused);
618 }
619
620}
621/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000622#define UP 0
623#define DOWN 1
624#define LEFT 2
625#define RIGHT 3
626static void
627switch_focus_widget(unsigned char direction)
628{
629 register struct ctk_window *window;
630 register struct ctk_widget *focus;
631 struct ctk_widget *widget;
632
633
634 if(dialog != NULL) {
635 window = dialog;
636 } else {
637 window = windows;
638 }
639
640 /* If there are no windows open, we move focus around between the
641 icons on the root window instead. */
642 if(window == NULL) {
643 window = &desktop_window;
644 }
645
646 focus = window->focused;
647 add_redrawwidget(focus);
648
649 if((direction & 1) == 0) {
650 /* Move focus "up" */
651 focus = focus->next;
652 } else {
653 /* Move focus "down" */
654 for(widget = window->active;
655 widget != NULL; widget = widget->next) {
656 if(widget->next == focus) {
657 break;
658 }
659 }
660 focus = widget;
661 if(focus == NULL) {
662 if(window->active != NULL) {
663 for(focus = window->active;
664 focus->next != NULL; focus = focus->next);
665 }
666 }
667 }
668 if(focus == NULL) {
669 focus = window->active;
670 }
adamdunkelse0683312003-04-09 09:02:52 +0000671
672 focus_widget(focus);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000673}
674/*-----------------------------------------------------------------------------------*/
675#if CTK_CONF_MENUS
676static void
677switch_open_menu(unsigned char rightleft)
678{
679 struct ctk_menu *menu;
680
681 if(rightleft == 0) {
682 /* Move right */
683 for(menu = menus.menus; menu != NULL; menu = menu->next) {
684 if(menu->next == menus.open) {
685 break;
686 }
687 }
688 lastmenu = menus.open;
689 menus.open = menu;
690 if(menus.open == NULL) {
691 for(menu = menus.menus;
692 menu->next != NULL; menu = menu->next);
693 menus.open = menu;
694 }
695 } else {
696 /* Move to left */
697 lastmenu = menus.open;
698 menus.open = menus.open->next;
699 if(menus.open == NULL) {
700 menus.open = menus.menus;
701 }
702 }
703
704 menus.open->active = 0;
705 /* ctk_redraw();*/
706}
707/*-----------------------------------------------------------------------------------*/
708static void
709switch_menu_item(unsigned char updown)
710{
711 register struct ctk_menu *m;
712
713 m = menus.open;
714
715 if(updown == 0) {
716 /* Move up */
717 if(m->active == 0) {
718 m->active = m->nitems - 1;
719 } else {
720 --m->active;
721 if(m->items[m->active].title[0] == '-') {
722 --m->active;
723 }
724 }
725 } else {
726 /* Move down */
727 if(m->active >= m->nitems - 1) {
728 m->active = 0;
729 } else {
730 ++m->active;
731 if(m->items[m->active].title[0] == '-') {
732 ++m->active;
733 }
734 }
735 }
736
737}
738#endif /* CTK_CONF_MENUS */
739/*-----------------------------------------------------------------------------------*/
740static unsigned char
adamdunkelsd07a5422003-04-05 12:22:35 +0000741activate(CC_REGISTER_ARG struct ctk_widget *w)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000742{
743 static unsigned char len;
744
745 if(w->type == CTK_WIDGET_BUTTON) {
746 if(w == (struct ctk_widget *)&windows->closebutton) {
747#if CTK_CONF_WINDOWCLOSE
adamdunkels5cb690c2003-04-02 11:36:21 +0000748 dispatcher_emit(ctk_signal_window_close, windows, w->window->owner);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000749 ctk_window_close(windows);
750 return REDRAW_ALL;
751#endif /* CTK_CONF_WINDOWCLOSE */
752 } else if(w == (struct ctk_widget *)&windows->titlebutton) {
753#if CTK_CONF_WINDOWCLOSE
754 mode = CTK_MODE_WINDOWMOVE;
755#endif /* CTK_CONF_WINDOWCLOSE */
756 } else {
757 dispatcher_emit(ctk_signal_button_activate, w,
758 w->window->owner);
759 }
760#if CTK_CONF_ICONS
761 } else if(w->type == CTK_WIDGET_ICON) {
762 dispatcher_emit(ctk_signal_button_activate, w,
763 w->widget.icon.owner);
764#endif /* CTK_CONF_ICONS */
765 } else if(w->type == CTK_WIDGET_HYPERLINK) {
766 dispatcher_emit(ctk_signal_hyperlink_activate, w,
767 DISPATCHER_BROADCAST);
768 } else if(w->type == CTK_WIDGET_TEXTENTRY) {
769 if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) {
770 w->widget.textentry.state = CTK_TEXTENTRY_EDIT;
771 len = strlen(w->widget.textentry.text);
772 if(w->widget.textentry.xpos > len) {
773 w->widget.textentry.xpos = len;
774 }
775 } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
776 w->widget.textentry.state = CTK_TEXTENTRY_NORMAL;
777 }
778 add_redrawwidget(w);
779 return REDRAW_WIDGETS;
780 }
781 return REDRAW_NONE;
782}
783/*-----------------------------------------------------------------------------------*/
784static void
785textentry_input(ctk_arch_key_t c,
adamdunkelsd07a5422003-04-05 12:22:35 +0000786 CC_REGISTER_ARG struct ctk_textentry *t)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000787{
788 static char *cptr, *cptr2;
789 static unsigned char len, txpos, typos, tlen;
790
791 txpos = t->xpos;
792 typos = t->ypos;
793 tlen = t->len;
794
795 cptr = &t->text[txpos + typos * tlen];
796
797 switch(c) {
798 case CH_CURS_LEFT:
799 if(txpos > 0) {
800 --txpos;
801 }
802 break;
803
804 case CH_CURS_RIGHT:
805 if(txpos < tlen &&
806 *cptr != 0) {
807 ++txpos;
808 }
809 break;
810
811 case CH_CURS_UP:
812#if CTK_CONF_TEXTENTRY_MULTILINE
813 if(t->h == 1) {
814 txpos = 0;
815 } else {
816 if(typos > 0) {
817 --typos;
818 } else {
819 t->state = CTK_TEXTENTRY_NORMAL;
820 }
821 }
822#else
823 txpos = 0;
824#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
825 break;
826
827 case CH_CURS_DOWN:
828#if CTK_CONF_TEXTENTRY_MULTILINE
829 if(t->h == 1) {
830 txpos = strlen(t->text);
831 } else {
832 if(typos < t->h - 1) {
833 ++typos;
834 } else {
835 t->state = CTK_TEXTENTRY_NORMAL;
836 }
837 }
838#else
839 txpos = strlen(t->text);
840#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
841 break;
842
843 case CH_ENTER:
844#if CTK_CONF_TEXTENTRY_MULTILINE
845 if(t->h == 1) {
846 t->state = CTK_TEXTENTRY_NORMAL;
847 } else {
848 if(typos < t->h - 1) {
849 ++typos;
850 txpos = 0;
851 } else {
852 t->state = CTK_TEXTENTRY_NORMAL;
853 }
854 }
855#else
856 t->state = CTK_TEXTENTRY_NORMAL;
857#endif /* CTK_CONF_TEXTENTRY_MULTILINE */
858 break;
859
860 default:
861 len = tlen - txpos - 1;
862 if(c == CH_DEL) {
863 if(txpos > 0 && len > 0) {
864 strncpy(cptr - 1, cptr,
865 len);
866 *(cptr + len - 1) = 0;
867 --txpos;
868 }
869 } else {
870 if(len > 0) {
871 cptr2 = cptr + len - 1;
872 while(cptr2 + 1 > cptr) {
873 *(cptr2 + 1) = *cptr2;
874 --cptr2;
875 }
876
877 *cptr = c;
878 ++txpos;
879 }
880 }
881 break;
882 }
883
884 t->xpos = txpos;
885 t->ypos = typos;
886}
887/*-----------------------------------------------------------------------------------*/
888#if CTK_CONF_MENUS
889static unsigned char
890menus_input(ctk_arch_key_t c)
891{
892 struct ctk_window *w;
893
894 if(menus.open->nitems > maxnitems) {
895 maxnitems = menus.open->nitems;
896 }
897
898
899 switch(c) {
900 case CH_CURS_RIGHT:
901 switch_open_menu(1);
902
903 return REDRAW_MENUPART;
904
905 case CH_CURS_DOWN:
906 switch_menu_item(1);
907 return REDRAW_MENUS;
908
909 case CH_CURS_LEFT:
910 switch_open_menu(0);
911 return REDRAW_MENUPART;
912
913 case CH_CURS_UP:
914 switch_menu_item(0);
915 return REDRAW_MENUS;
916
917 case CH_ENTER:
918 lastmenu = menus.open;
919 if(menus.open == &desktopmenu) {
920 for(w = windows; w != NULL; w = w->next) {
921 if(w->title == desktopmenu.items[desktopmenu.active].title) {
922 ctk_window_open(w);
923 menus.open = NULL;
924 return REDRAW_ALL;
925 }
926 }
927 } else {
928 dispatcher_emit(ctk_signal_menu_activate, menus.open,
929 DISPATCHER_BROADCAST);
930 }
931 menus.open = NULL;
932 return REDRAW_MENUPART;
933
adamdunkels88ed9c42003-03-28 12:10:09 +0000934 case CTK_CONF_MENU_KEY:
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000935 lastmenu = menus.open;
936 menus.open = NULL;
937 return REDRAW_MENUPART;
938 }
939}
940#endif /* CTK_CONF_MENUS */
941/*-----------------------------------------------------------------------------------*/
942static void
adamdunkelsc4902862003-04-09 00:30:45 +0000943ctk_idle(void)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000944{
945 static ctk_arch_key_t c;
adamdunkelse0683312003-04-09 09:02:52 +0000946 static unsigned char i, mxc, myc, mouse_clicked;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000947 register struct ctk_window *window;
adamdunkelsc4902862003-04-09 00:30:45 +0000948 register struct ctk_widget *widget;
949
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000950#if CTK_CONF_MENUS
951 if(menus.open != NULL) {
952 maxnitems = menus.open->nitems;
953 } else {
954 maxnitems = 0;
955 }
956#endif /* CTK_CONF_MENUS */
957
958 if(mode == CTK_MODE_SCREENSAVER) {
959#ifdef CTK_SCREENSAVER_RUN
960 CTK_SCREENSAVER_RUN();
961#endif /* CTK_SCREENSAVER_RUN */
962 if(ctk_arch_keyavail()) {
963 mode = CTK_MODE_NORMAL;
964 ctk_draw_init();
965 ctk_redraw();
966 }
adamdunkelsc4902862003-04-09 00:30:45 +0000967 } else if(mode == CTK_MODE_NORMAL) {
adamdunkelse0683312003-04-09 09:02:52 +0000968 if(dialog != NULL) {
969 window = dialog;
970 } else {
971 window = windows;
972 }
adamdunkelsc4902862003-04-09 00:30:45 +0000973
adamdunkelse0683312003-04-09 09:02:52 +0000974
975 /* If there has been a change in the mouse button(s), we send out
976 a signal. */
977 mouse_clicked = 0;
978 if(ctk_mouse_button() != mouse_last_button) {
979 mouse_last_button = ctk_mouse_button();
980 if(mouse_last_button == 0) {
981 dispatcher_emit(ctk_signal_pointer_up, NULL, window->owner);
982 } else {
983 dispatcher_emit(ctk_signal_pointer_down, (ek_data_t)mouse_last_button,
984 window->owner);
985 mouse_clicked = 1;
986 }
987 }
988
adamdunkelsc4902862003-04-09 00:30:45 +0000989 /* Check if the mouse pointer has moved, and if so we emit a
990 signal. */
991 if(ctk_mouse_x() != mouse_last_x ||
adamdunkelse0683312003-04-09 09:02:52 +0000992 ctk_mouse_y() != mouse_last_y ||
993 mouse_clicked != 0) {
adamdunkelsc4902862003-04-09 00:30:45 +0000994 mouse_last_x = ctk_mouse_x();
995 mouse_last_y = ctk_mouse_y();
996 dispatcher_emit(ctk_signal_pointer_move, NULL, window->owner);
997
998 /* Find out which widget currently is under the mouse pointer
999 and give it focus, unless it already has focus. */
1000 mxc = ctk_mouse_xtoc(mouse_last_x);
adamdunkelse0683312003-04-09 09:02:52 +00001001 myc = ctk_mouse_ytoc(mouse_last_y) - 1;
adamdunkelsc4902862003-04-09 00:30:45 +00001002
adamdunkelse0683312003-04-09 09:02:52 +00001003 /* Check if the mouse is in the current window. */
adamdunkelsc4902862003-04-09 00:30:45 +00001004 if(mxc >= window->x &&
1005 mxc <= window->x + window->w &&
1006 myc >= window->y &&
1007 myc <= window->y + window->h) {
1008
adamdunkelse0683312003-04-09 09:02:52 +00001009 mxc = mxc - window->x - 1;
1010 myc = myc - window->y - 1;
1011
adamdunkelsc4902862003-04-09 00:30:45 +00001012
1013 redraw |= REDRAW_WIDGETS;
1014 add_redrawwidget(window->focused);
1015 window->focused = NULL;
1016
1017 /* Now find the appropriate widget to assign focus to. */
1018 for(widget = window->active; widget != NULL; widget = widget->next) {
1019 if(mxc >= widget->x &&
1020 mxc <= widget->x + widget->w &&
adamdunkelse0683312003-04-09 09:02:52 +00001021 myc == widget->y /* &&
adamdunkelsc4902862003-04-09 00:30:45 +00001022 ((widget->type == CTK_WIDGET_BITMAP ||
1023 widget->type == CTK_WIDGET_TEXTENTRY ||
1024 widget->type == CTK_WIDGET_ICON) &&
1025 (myc <= widget->y + ((struct ctk_bitmap *)widget)->h))*/) {
adamdunkelse0683312003-04-09 09:02:52 +00001026 focus_widget(widget);
1027 if(mouse_clicked != 0) {
1028 redraw |= activate(widget);
1029 }
adamdunkelsc4902862003-04-09 00:30:45 +00001030 break;
1031 }
1032 }
adamdunkelse0683312003-04-09 09:02:52 +00001033 } else {
1034 redraw |= REDRAW_WIDGETS;
1035 add_redrawwidget(window->focused);
1036 window->focused = NULL;
adamdunkelsc4902862003-04-09 00:30:45 +00001037 }
1038 }
1039
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001040 while(ctk_arch_keyavail()) {
1041
1042 screensaver_timer = 0;
1043
1044 c = ctk_arch_getkey();
1045
1046
1047 if(dialog != NULL) {
1048 window = dialog;
1049 } else if(windows != NULL) {
1050 window = windows;
1051 } else {
1052 window = &desktop_window;
1053 }
1054 widget = window->focused;
1055
1056
1057 if(widget != NULL &&
1058 widget->type == CTK_WIDGET_TEXTENTRY &&
1059 widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) {
1060 textentry_input(c, (struct ctk_textentry *)widget);
1061 add_redrawwidget(widget);
1062#if CTK_CONF_MENUS
1063 } else if(menus.open != NULL) {
1064 redraw |= menus_input(c);
1065#endif /* CTK_CONF_MENUS */
1066 } else {
1067 switch(c) {
1068 case CH_CURS_RIGHT:
1069 switch_focus_widget(RIGHT);
1070 break;
1071 case CH_CURS_DOWN:
1072 switch_focus_widget(DOWN);
1073 break;
1074 case CH_CURS_LEFT:
1075 switch_focus_widget(LEFT);
1076 break;
1077 case CH_CURS_UP:
1078 switch_focus_widget(UP);
1079 break;
1080 case CH_ENTER:
1081 redraw |= activate(widget);
1082 break;
1083#if CTK_CONF_MENUS
1084 case CTK_CONF_MENU_KEY:
1085 if(dialog == NULL) {
1086 if(lastmenu == NULL) {
1087 menus.open = menus.menus;
1088 } else {
1089 menus.open = lastmenu;
1090 }
1091 menus.open->active = 0;
1092 redraw |= REDRAW_MENUS;
1093 }
1094 break;
1095#endif /* CTK_CONF_MENUS */
1096 case CTK_CONF_WINDOWSWITCH_KEY:
1097 if(windows != NULL) {
1098 for(window = windows; window->next != NULL;
1099 window = window->next);
1100 ctk_window_open(window);
1101 ctk_redraw();
1102 }
1103 break;
1104 default:
1105 if(widget->type == CTK_WIDGET_TEXTENTRY) {
1106 widget->widget.textentry.state = CTK_TEXTENTRY_EDIT;
1107 textentry_input(c, (struct ctk_textentry *)widget);
1108 add_redrawwidget(widget);
1109 } else {
1110 dispatcher_emit(ctk_signal_keypress, (void *)c,
1111 window->owner);
1112 }
1113 break;
1114 }
1115 }
1116
1117 if(redraw & REDRAW_WIDGETS) {
1118 for(i = 0; i < redraw_widgetptr; ++i) {
1119 ctk_widget_redraw(redraw_widgets[i]);
1120 }
1121 redraw &= ~REDRAW_WIDGETS;
1122 redraw_widgetptr = 0;
1123 }
1124 }
1125 if(redraw & REDRAW_ALL) {
1126 do_redraw_all(1, height);
1127#if CTK_CONF_MENUS
1128 } else if(redraw & REDRAW_MENUPART) {
1129 do_redraw_all(1, maxnitems + 1);
1130 } else if(redraw & REDRAW_MENUS) {
1131 ctk_draw_menus(&menus);
1132#endif /* CTK_CONF_MENUS */
1133 } else if(redraw & REDRAW_FOCUS) {
1134 if(dialog != NULL) {
1135 ctk_window_redraw(dialog);
1136 } else if(windows != NULL) {
1137 ctk_window_redraw(windows);
1138 } else {
1139 ctk_window_redraw(&desktop_window);
1140 }
1141 } else if(redraw & REDRAW_WIDGETS) {
1142 for(i = 0; i < redraw_widgetptr; ++i) {
1143 ctk_widget_redraw(redraw_widgets[i]);
1144 }
1145 }
1146 redraw = 0;
1147 redraw_widgetptr = 0;
1148#if CTK_CONF_WINDOWMOVE
1149 } else if(mode == CTK_MODE_WINDOWMOVE) {
1150
1151 redraw = 0;
1152
1153 window = windows;
1154
1155 while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) {
1156
1157 screensaver_timer = 0;
1158
1159 c = ctk_arch_getkey();
1160
1161 switch(c) {
1162 case CH_CURS_RIGHT:
1163 ++window->x;
1164 if(window->x + window->w + 1 >= width) {
1165 --window->x;
1166 }
1167 redraw = REDRAW_ALL;
1168 break;
1169 case CH_CURS_LEFT:
1170 if(window->x > 0) {
1171 --window->x;
1172 }
1173 redraw = REDRAW_ALL;
1174 break;
1175 case CH_CURS_DOWN:
1176 ++window->y;
1177 if(window->y + window->h + 2 >= height) {
1178 --window->y;
1179 }
1180 redraw = REDRAW_ALL;
1181 break;
1182 case CH_CURS_UP:
1183 if(window->y > 0) {
1184 --window->y;
1185 }
1186 redraw = REDRAW_ALL;
1187 break;
1188 case CH_ENTER:
1189 case CH_ESC:
1190 mode = CTK_MODE_NORMAL;
1191 redraw = REDRAW_ALL;
1192 break;
1193 }
1194 }
1195 if(redraw & REDRAW_ALL) {
1196 do_redraw_all(1, height);
1197 }
1198 redraw = 0;
1199#endif /* CTK_CONF_WINDOWMOVE */
1200 }
1201}
1202/*-----------------------------------------------------------------------------------*/
1203static void
adamdunkelsc4902862003-04-09 00:30:45 +00001204ctk_sighandler(ek_signal_t s, ek_data_t data)
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001205{
1206 if(s == ctk_signal_timer) {
1207 if(mode == CTK_MODE_NORMAL) {
1208 ++screensaver_timer;
1209 if(screensaver_timer == SCREENSAVER_TIMEOUT) {
1210#ifdef CTK_SCREENSAVER_INIT
1211 CTK_SCREENSAVER_INIT();
1212#endif /* CTK_SCREENSAVER_INIT */
1213 mode = CTK_MODE_SCREENSAVER;
1214 screensaver_timer = 0;
1215 }
1216 }
1217 dispatcher_timer(ctk_signal_timer, data, CLK_TCK);
1218 }
1219}
1220/*-----------------------------------------------------------------------------------*/
adamdunkelsd07a5422003-04-05 12:22:35 +00001221