| /* |
| * Copyright (c) 2002, Adam Dunkels. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * 3. All advertising materials mentioning features or use of this |
| * software must display the following acknowledgement: |
| * This product includes software developed by Adam Dunkels. |
| * 4. The name of the author may not be used to endorse or promote |
| * products derived from this software without specific prior |
| * written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
| * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * This file is part of the "ctk" console GUI toolkit for cc65 |
| * |
| * $Id: ctk.c,v 1.19 2003/06/30 20:44:57 adamdunkels Exp $ |
| * |
| */ |
| |
| #include "cc.h" |
| #include "ek.h" |
| #include "dispatcher.h" |
| #include "ctk.h" |
| #include "ctk-draw.h" |
| #include "ctk-conf.h" |
| #include "ctk-mouse.h" |
| |
| static unsigned char height, width; |
| |
| static unsigned char mode; |
| |
| static struct ctk_window desktop_window; |
| static struct ctk_window *windows; |
| static struct ctk_window *dialog; |
| |
| #if CTK_CONF_MENUS |
| static struct ctk_menus menus; |
| static struct ctk_menu *lastmenu; |
| static struct ctk_menu desktopmenu; |
| #endif /* CTK_CONF_MENUS */ |
| |
| #ifndef NULL |
| #define NULL (void *)0 |
| #endif /* NULL */ |
| |
| |
| #define REDRAW_NONE 0 |
| #define REDRAW_ALL 1 |
| #define REDRAW_FOCUS 2 |
| #define REDRAW_WIDGETS 4 |
| #define REDRAW_MENUS 8 |
| #define REDRAW_MENUPART 16 |
| |
| #define MAX_REDRAWWIDGETS 4 |
| static unsigned char redraw; |
| static struct ctk_widget *redraw_widgets[MAX_REDRAWWIDGETS]; |
| static unsigned char redraw_widgetptr; |
| static unsigned char maxnitems; |
| |
| static unsigned char iconx, icony; |
| #define ICONX_START (width - 5) |
| #define ICONY_START 0 |
| #define ICONX_DELTA -8 |
| #define ICONY_DELTA 5 |
| #define ICONY_MAX (height - 4) |
| |
| static void ctk_idle(void); |
| static DISPATCHER_SIGHANDLER(ctk_sighandler, s, data); |
| static struct dispatcher_proc p = |
| {DISPATCHER_PROC("CTK Contiki GUI", ctk_idle, ctk_sighandler, NULL)}; |
| static ek_id_t ctkid; |
| |
| ek_signal_t ctk_signal_keypress, |
| ctk_signal_timer, |
| ctk_signal_button_activate, |
| ctk_signal_widget_activate, |
| ctk_signal_button_hover, |
| ctk_signal_widget_select, |
| ctk_signal_hyperlink_activate, |
| ctk_signal_hyperlink_hover, |
| ctk_signal_menu_activate, |
| ctk_signal_window_close, |
| ctk_signal_pointer_move, |
| ctk_signal_pointer_button; |
| |
| #if CTK_CONF_SCREENSAVER |
| ek_signal_t ctk_signal_screensaver_start, |
| ctk_signal_screensaver_stop, |
| ctk_signal_screensaver_uninstall; |
| #endif /* CTK_CONF_SCREENSAVER */ |
| |
| |
| #if CTK_CONF_MOUSE_SUPPORT |
| unsigned short mouse_x, mouse_y, mouse_button; |
| #endif /* CTK_CONF_MOUSE_SUPPORT */ |
| |
| static unsigned short screensaver_timer; |
| unsigned short ctk_screensaver_timeout = (5*60); |
| /*#define SCREENSAVER_TIMEOUT (5*60)*/ |
| |
| #if CTK_CONF_MENUS |
| /*-----------------------------------------------------------------------------------*/ |
| /* make_desktopmenu(void) |
| * |
| * Creates the leftmost menu, "Desktop". Since the desktop menu |
| * contains the list of all open windows, this function will be called |
| * whenever a window is opened or closed. |
| */ |
| static void |
| make_desktopmenu(void) |
| { |
| struct ctk_window *w; |
| |
| desktopmenu.nitems = 0; |
| |
| if(windows == NULL) { |
| ctk_menuitem_add(&desktopmenu, "(No windows)"); |
| } else { |
| for(w = windows; w != NULL; w = w->next) { |
| ctk_menuitem_add(&desktopmenu, w->title); |
| } |
| } |
| } |
| #endif /* CTK_CONF_MENUS */ |
| /*-----------------------------------------------------------------------------------*/ |
| /* ctk_init(void) |
| * |
| * Initializes CTK. Must be called before any other CTK function. |
| */ |
| void |
| ctk_init(void) |
| { |
| ctkid = dispatcher_start(&p); |
| |
| windows = NULL; |
| dialog = NULL; |
| |
| #if CTK_CONF_MENUS |
| ctk_menu_new(&desktopmenu, "Desktop"); |
| make_desktopmenu(); |
| menus.menus = menus.desktopmenu = &desktopmenu; |
| #endif /* CTK_CONF_MENUS */ |
| |
| #if CTK_CONF_MOUSE_SUPPORT |
| ctk_mouse_init(); |
| ctk_mouse_show(); |
| #endif /* CTK_CONF_MOUSE_SUPPORT */ |
| |
| ctk_draw_init(); |
| |
| height = ctk_draw_height(); |
| width = ctk_draw_width(); |
| |
| desktop_window.active = NULL; |
| |
| ctk_signal_keypress = dispatcher_sigalloc(); |
| ctk_signal_timer = dispatcher_sigalloc(); |
| |
| ctk_signal_button_activate = |
| ctk_signal_widget_activate = dispatcher_sigalloc(); |
| |
| ctk_signal_button_hover = |
| ctk_signal_hyperlink_hover = |
| ctk_signal_widget_select = dispatcher_sigalloc(); |
| |
| ctk_signal_hyperlink_activate = dispatcher_sigalloc(); |
| |
| ctk_signal_menu_activate = dispatcher_sigalloc(); |
| ctk_signal_window_close = dispatcher_sigalloc(); |
| |
| ctk_signal_pointer_move = dispatcher_sigalloc(); |
| ctk_signal_pointer_button = dispatcher_sigalloc(); |
| |
| |
| #if CTK_CONF_SCREENSAVER |
| ctk_signal_screensaver_start = dispatcher_sigalloc(); |
| ctk_signal_screensaver_stop = dispatcher_sigalloc(); |
| ctk_signal_screensaver_uninstall = dispatcher_sigalloc(); |
| #endif /* CTK_CONF_SCREENSAVER */ |
| |
| dispatcher_listen(ctk_signal_timer); |
| dispatcher_timer(ctk_signal_timer, NULL, CLK_TCK); |
| |
| mode = CTK_MODE_NORMAL; |
| |
| iconx = ICONX_START; |
| icony = ICONY_START; |
| |
| redraw = REDRAW_ALL; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /* void ctk_mode_set() |
| */ |
| void |
| ctk_mode_set(unsigned char m) { |
| mode = m; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| unsigned char |
| ctk_mode_get(void) { |
| return mode; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_icon_add(CC_REGISTER_ARG struct ctk_widget *icon, |
| ek_id_t id) |
| { |
| #if CTK_CONF_ICONS |
| icon->x = iconx; |
| icon->y = icony; |
| icon->widget.icon.owner = id; |
| |
| icony += ICONY_DELTA; |
| if(icony >= ICONY_MAX) { |
| icony = ICONY_START; |
| iconx += ICONX_DELTA; |
| } |
| |
| ctk_widget_add(&desktop_window, icon); |
| #endif /* CTK_CONF_ICONS */ |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_dialog_open(struct ctk_window *d) |
| { |
| dialog = d; |
| redraw |= REDRAW_ALL; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_dialog_close(void) |
| { |
| dialog = NULL; |
| redraw |= REDRAW_ALL; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_window_open(CC_REGISTER_ARG struct ctk_window *w) |
| { |
| struct ctk_window *w2; |
| |
| /* Check if already open. */ |
| for(w2 = windows; w2 != w && w2 != NULL; w2 = w2->next); |
| if(w2 == NULL) { |
| /* Not open, so we add it at the head of the list of open |
| windows. */ |
| w->next = windows; |
| if(windows != NULL) { |
| windows->prev = w; |
| } |
| windows = w; |
| w->prev = NULL; |
| } else { |
| /* Window already open, so we move it to the front of the windows |
| list. */ |
| if(w != windows) { |
| if(w->next != NULL) { |
| w->next->prev = w->prev; |
| } |
| if(w->prev != NULL) { |
| w->prev->next = w->next; |
| } |
| w->next = windows; |
| windows->prev = w; |
| windows = w; |
| w->prev = NULL; |
| } |
| } |
| |
| #if CTK_CONF_MENUS |
| /* Recreate the Desktop menu's window entries.*/ |
| make_desktopmenu(); |
| #endif /* CTK_CONF_MENUS */ |
| |
| redraw |= REDRAW_ALL; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_window_close(struct ctk_window *w) |
| { |
| struct ctk_window *w2; |
| |
| if(w == NULL) { |
| return; |
| } |
| |
| /* Check if the window to be closed is the first window on the |
| list. */ |
| if(w == windows) { |
| windows = w->next; |
| if(windows != NULL) { |
| windows->prev = NULL; |
| } |
| w->next = w->prev = NULL; |
| } else { |
| /* Otherwise we step through the list until we find the window |
| before the one to be closed. We then redirect its ->next |
| pointer and its ->next->prev. */ |
| for(w2 = windows; w2 != NULL && w2->next != w; w2 = w2->next); |
| |
| if(w2 == NULL) { |
| /* The window wasn't open, so there is nothing more for us to |
| do. */ |
| return; |
| } |
| |
| if(w->next != NULL) { |
| w->next->prev = w->prev; |
| } |
| w2->next = w->next; |
| |
| w->next = w->prev = NULL; |
| } |
| |
| #if CTK_CONF_MENUS |
| /* Recreate the Desktop menu's window entries.*/ |
| make_desktopmenu(); |
| #endif /* CTK_CONF_MENUS */ |
| redraw |= REDRAW_ALL; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| make_windowbuttons(CC_REGISTER_ARG struct ctk_window *window) |
| { |
| #if CTK_CONF_WINDOWMOVE |
| CTK_BUTTON_NEW(&window->titlebutton, 0, -1, window->titlelen, window->title); |
| #else |
| CTK_LABEL_NEW(&window->titlebutton, 0, -1, window->titlelen, 1, window->title); |
| #endif /* CTK_CONF_WINDOWMOVE */ |
| CTK_WIDGET_ADD(window, &window->titlebutton); |
| |
| |
| #if CTK_CONF_WINDOWCLOSE |
| CTK_BUTTON_NEW(&window->closebutton, window->w - 3, -1, 1, "x"); |
| #else |
| CTK_LABEL_NEW(&window->closebutton, window->w - 4, -1, 3, 1, " "); |
| #endif /* CTK_CONF_WINDOWCLOSE */ |
| CTK_WIDGET_ADD(window, &window->closebutton); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_window_clear(struct ctk_window *window) |
| { |
| window->active = window->inactive = window->focused = NULL; |
| |
| make_windowbuttons(window); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_menu_add(struct ctk_menu *menu) |
| { |
| #if CTK_CONF_MENUS |
| struct ctk_menu *m; |
| |
| if(lastmenu == NULL) { |
| lastmenu = menu; |
| } |
| |
| for(m = menus.menus; m->next != NULL; m = m->next) { |
| if(m == menu) { |
| return; |
| } |
| } |
| m->next = menu; |
| menu->next = NULL; |
| #endif /* CTK_CONF_MENUS */ |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_menu_remove(struct ctk_menu *menu) |
| { |
| #if CTK_CONF_MENUS |
| struct ctk_menu *m; |
| |
| for(m = menus.menus; m->next != NULL; m = m->next) { |
| if(m->next == menu) { |
| m->next = menu->next; |
| return; |
| } |
| } |
| #endif /* CTK_CONF_MENUS */ |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| do_redraw_all(unsigned char clipy1, unsigned char clipy2) |
| { |
| struct ctk_window *w; |
| struct ctk_widget *widget; |
| |
| if(mode != CTK_MODE_NORMAL && |
| mode != CTK_MODE_WINDOWMOVE) { |
| return; |
| } |
| |
| ctk_draw_clear(clipy1, clipy2); |
| |
| /* Draw widgets in root window */ |
| for(widget = desktop_window.active; |
| widget != NULL; widget = widget->next) { |
| ctk_draw_widget(widget, CTK_FOCUS_WINDOW, clipy1, clipy2); |
| } |
| |
| /* Draw windows */ |
| if(windows != NULL) { |
| /* Find the last window.*/ |
| for(w = windows; w->next != NULL; w = w->next); |
| |
| /* Draw the windows from back to front. */ |
| for(; w != windows; w = w->prev) { |
| ctk_draw_clear_window(w, 0, clipy1, clipy2); |
| ctk_draw_window(w, 0, clipy1, clipy2); |
| } |
| /* Draw focused window */ |
| ctk_draw_clear_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2); |
| ctk_draw_window(windows, CTK_FOCUS_WINDOW, clipy1, clipy2); |
| } |
| |
| /* Draw dialog (if any) */ |
| if(dialog != NULL) { |
| ctk_draw_dialog(dialog); |
| } |
| |
| #if CTK_CONF_MENUS |
| ctk_draw_menus(&menus); |
| #endif /* CTK_CONF_MENUS */ |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_desktop_redraw(struct ctk_desktop *d) |
| { |
| if(DISPATCHER_CURRENT() == ctkid) { |
| if(mode == CTK_MODE_NORMAL || |
| mode == CTK_MODE_WINDOWMOVE) { |
| do_redraw_all(1, height); |
| } |
| } else { |
| redraw |= REDRAW_ALL; |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_window_redraw(struct ctk_window *w) |
| { |
| /* Only redraw the window if it is a dialog or if it is the foremost |
| window. */ |
| if(mode != CTK_MODE_NORMAL) { |
| return; |
| } |
| |
| if(w == dialog) { |
| ctk_draw_dialog(w); |
| } else if(dialog == NULL && |
| #if CTK_CONF_MENUS |
| menus.open == NULL && |
| #endif /* CTK_CONF_MENUS */ |
| windows == w) { |
| ctk_draw_window(w, CTK_FOCUS_WINDOW, |
| 0, height); |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| window_new(CC_REGISTER_ARG struct ctk_window *window, |
| unsigned char w, unsigned char h, |
| char *title) |
| { |
| |
| if(w >= width - 2) { |
| window->x = 0; |
| } else { |
| window->x = (width - w - 2) / 2; |
| } |
| if(h >= height - 3) { |
| window->y = 0; |
| } else { |
| window->y = (height - h - 1) / 2; |
| } |
| |
| window->w = w; |
| window->h = h; |
| window->title = title; |
| if(title != NULL) { |
| window->titlelen = strlen(title); |
| } else { |
| window->titlelen = 0; |
| } |
| window->next = window->prev = NULL; |
| window->owner = DISPATCHER_CURRENT(); |
| window->active = window->inactive = window->focused = NULL; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_window_new(struct ctk_window *window, |
| unsigned char w, unsigned char h, |
| char *title) |
| { |
| window_new(window, w, h, title); |
| |
| make_windowbuttons(window); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_dialog_new(CC_REGISTER_ARG struct ctk_window *window, |
| unsigned char w, unsigned char h) |
| { |
| window_new(window, w, h, NULL); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_menu_new(CC_REGISTER_ARG struct ctk_menu *menu, |
| char *title) |
| { |
| #if CTK_CONF_MENUS |
| menu->next = NULL; |
| menu->title = title; |
| menu->titlelen = strlen(title); |
| menu->active = 0; |
| menu->nitems = 0; |
| #endif /* CTK_CONF_MENUS */ |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| unsigned char |
| ctk_menuitem_add(CC_REGISTER_ARG struct ctk_menu *menu, |
| char *name) |
| { |
| #if CTK_CONF_MENUS |
| if(menu->nitems == CTK_CONF_MAXMENUITEMS) { |
| return 0; |
| } |
| menu->items[menu->nitems].title = name; |
| menu->items[menu->nitems].titlelen = strlen(name); |
| return menu->nitems++; |
| #else |
| return 0; |
| #endif /* CTK_CONF_MENUS */ |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| add_redrawwidget(struct ctk_widget *w) |
| { |
| static unsigned char i; |
| |
| if(redraw_widgetptr == MAX_REDRAWWIDGETS) { |
| redraw |= REDRAW_FOCUS; |
| } else { |
| redraw |= REDRAW_WIDGETS; |
| /* Check if it is in the queue already. If so, we don't add it |
| again. */ |
| for(i = 0; i < redraw_widgetptr; ++i) { |
| if(redraw_widgets[i] == w) { |
| return; |
| } |
| } |
| redraw_widgets[redraw_widgetptr++] = w; |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_widget_redraw(struct ctk_widget *widget) |
| { |
| struct ctk_window *window; |
| |
| if(mode != CTK_MODE_NORMAL || widget == NULL) { |
| return; |
| } |
| |
| /* If this function isn't called by CTK itself, we only queue the |
| redraw request. */ |
| if(DISPATCHER_CURRENT() != ctkid) { |
| redraw |= REDRAW_WIDGETS; |
| add_redrawwidget(widget); |
| } else { |
| |
| /* Only redraw widgets that are in the foremost window. If we |
| would allow redrawing widgets in non-focused windows, we would |
| have to redraw all the windows that cover the non-focused |
| window as well, which would lead to flickering. |
| |
| Also, we avoid drawing any widgets when the menus are active. |
| */ |
| |
| #if CTK_CONF_MENUS |
| if(menus.open == NULL) |
| #endif /* CTK_CONF_MENUS */ |
| { |
| window = widget->window; |
| if(window == dialog) { |
| ctk_draw_widget(widget, CTK_FOCUS_DIALOG, 0, height); |
| } else if(window == windows || |
| window == &desktop_window) { |
| ctk_draw_widget(widget, CTK_FOCUS_WINDOW, 0, height); |
| } |
| } |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| ctk_widget_add(CC_REGISTER_ARG struct ctk_window *window, |
| CC_REGISTER_ARG struct ctk_widget *widget) |
| { |
| if(widget->type == CTK_WIDGET_LABEL || |
| widget->type == CTK_WIDGET_SEPARATOR) { |
| widget->next = window->inactive; |
| window->inactive = widget; |
| widget->window = window; |
| } else { |
| widget->next = window->active; |
| window->active = widget; |
| widget->window = window; |
| /* if(window->focused == NULL) { |
| window->focused = widget; |
| }*/ |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| unsigned char |
| ctk_desktop_width(struct ctk_desktop *w) |
| { |
| return ctk_draw_width(); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| unsigned char |
| ctk_desktop_height(struct ctk_desktop *w) |
| { |
| return ctk_draw_height(); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| select_widget(struct ctk_widget *focus) |
| { |
| struct ctk_window *window; |
| |
| window = focus->window; |
| |
| if(focus != window->focused) { |
| window->focused = focus; |
| /* The operation changed the focus, so we emit a "hover" signal |
| for those widgets that support it. */ |
| |
| if(window->focused->type == CTK_WIDGET_HYPERLINK) { |
| dispatcher_emit(ctk_signal_hyperlink_hover, window->focused, |
| window->owner); |
| } else if(window->focused->type == CTK_WIDGET_BUTTON) { |
| dispatcher_emit(ctk_signal_button_hover, window->focused, |
| window->owner); |
| } |
| |
| add_redrawwidget(window->focused); |
| |
| dispatcher_emit(ctk_signal_widget_select, focus, |
| focus->window->owner); |
| |
| } |
| |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| #define UP 0 |
| #define DOWN 1 |
| #define LEFT 2 |
| #define RIGHT 3 |
| static void |
| switch_focus_widget(unsigned char direction) |
| { |
| register struct ctk_window *window; |
| register struct ctk_widget *focus; |
| struct ctk_widget *widget; |
| |
| |
| if(dialog != NULL) { |
| window = dialog; |
| } else { |
| window = windows; |
| } |
| |
| /* If there are no windows open, we move focus around between the |
| icons on the root window instead. */ |
| if(window == NULL) { |
| window = &desktop_window; |
| } |
| |
| focus = window->focused; |
| if(focus == NULL) { |
| focus = window->active; |
| } |
| add_redrawwidget(focus); |
| |
| if((direction & 1) == 0) { |
| /* Move focus "up" */ |
| focus = focus->next; |
| } else { |
| /* Move focus "down" */ |
| for(widget = window->active; |
| widget != NULL; widget = widget->next) { |
| if(widget->next == focus) { |
| break; |
| } |
| } |
| focus = widget; |
| if(focus == NULL) { |
| if(window->active != NULL) { |
| for(focus = window->active; |
| focus->next != NULL; focus = focus->next); |
| } |
| } |
| } |
| if(focus == NULL) { |
| focus = window->active; |
| } |
| |
| select_widget(focus); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| #if CTK_CONF_MENUS |
| static void |
| switch_open_menu(unsigned char rightleft) |
| { |
| struct ctk_menu *menu; |
| |
| if(rightleft == 0) { |
| /* Move right */ |
| for(menu = menus.menus; menu != NULL; menu = menu->next) { |
| if(menu->next == menus.open) { |
| break; |
| } |
| } |
| lastmenu = menus.open; |
| menus.open = menu; |
| if(menus.open == NULL) { |
| for(menu = menus.menus; |
| menu->next != NULL; menu = menu->next); |
| menus.open = menu; |
| } |
| } else { |
| /* Move to left */ |
| lastmenu = menus.open; |
| menus.open = menus.open->next; |
| if(menus.open == NULL) { |
| menus.open = menus.menus; |
| } |
| } |
| |
| menus.open->active = 0; |
| |
| /* if(menus.open->nitems > maxnitems) { |
| maxnitems = menus.open->nitems; |
| }*/ |
| |
| /* ctk_desktop_redraw();*/ |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| switch_menu_item(unsigned char updown) |
| { |
| register struct ctk_menu *m; |
| |
| m = menus.open; |
| |
| if(updown == 0) { |
| /* Move up */ |
| if(m->active == 0) { |
| m->active = m->nitems - 1; |
| } else { |
| --m->active; |
| if(m->items[m->active].title[0] == '-') { |
| --m->active; |
| } |
| } |
| } else { |
| /* Move down */ |
| if(m->active >= m->nitems - 1) { |
| m->active = 0; |
| } else { |
| ++m->active; |
| if(m->items[m->active].title[0] == '-') { |
| ++m->active; |
| } |
| } |
| } |
| |
| } |
| #endif /* CTK_CONF_MENUS */ |
| /*-----------------------------------------------------------------------------------*/ |
| static unsigned char |
| activate(CC_REGISTER_ARG struct ctk_widget *w) |
| { |
| static unsigned char len; |
| |
| if(w->type == CTK_WIDGET_BUTTON) { |
| if(w == (struct ctk_widget *)&windows->closebutton) { |
| #if CTK_CONF_WINDOWCLOSE |
| dispatcher_emit(ctk_signal_window_close, windows, w->window->owner); |
| ctk_window_close(windows); |
| return REDRAW_ALL; |
| #endif /* CTK_CONF_WINDOWCLOSE */ |
| } else if(w == (struct ctk_widget *)&windows->titlebutton) { |
| #if CTK_CONF_WINDOWCLOSE |
| mode = CTK_MODE_WINDOWMOVE; |
| #endif /* CTK_CONF_WINDOWCLOSE */ |
| } else { |
| dispatcher_emit(ctk_signal_widget_activate, w, |
| w->window->owner); |
| } |
| #if CTK_CONF_ICONS |
| } else if(w->type == CTK_WIDGET_ICON) { |
| dispatcher_emit(ctk_signal_widget_activate, w, |
| w->widget.icon.owner); |
| #endif /* CTK_CONF_ICONS */ |
| } else if(w->type == CTK_WIDGET_HYPERLINK) { |
| dispatcher_emit(ctk_signal_hyperlink_activate, w, |
| DISPATCHER_BROADCAST); |
| } else if(w->type == CTK_WIDGET_TEXTENTRY) { |
| if(w->widget.textentry.state == CTK_TEXTENTRY_NORMAL) { |
| w->widget.textentry.state = CTK_TEXTENTRY_EDIT; |
| len = strlen(w->widget.textentry.text); |
| if(w->widget.textentry.xpos > len) { |
| w->widget.textentry.xpos = len; |
| } |
| } else if(w->widget.textentry.state == CTK_TEXTENTRY_EDIT) { |
| w->widget.textentry.state = CTK_TEXTENTRY_NORMAL; |
| } |
| add_redrawwidget(w); |
| return REDRAW_WIDGETS; |
| } else { |
| dispatcher_emit(ctk_signal_widget_activate, w, |
| w->window->owner); |
| } |
| return REDRAW_NONE; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| textentry_input(ctk_arch_key_t c, |
| CC_REGISTER_ARG struct ctk_textentry *t) |
| { |
| static char *cptr, *cptr2; |
| static unsigned char len, txpos, typos, tlen; |
| |
| txpos = t->xpos; |
| typos = t->ypos; |
| tlen = t->len; |
| |
| cptr = &t->text[txpos + typos * tlen]; |
| |
| switch(c) { |
| case CH_CURS_LEFT: |
| if(txpos > 0) { |
| --txpos; |
| } |
| break; |
| |
| case CH_CURS_RIGHT: |
| if(txpos < tlen && |
| *cptr != 0) { |
| ++txpos; |
| } |
| break; |
| |
| case CH_CURS_UP: |
| #if CTK_CONF_TEXTENTRY_MULTILINE |
| if(t->h == 1) { |
| txpos = 0; |
| } else { |
| if(typos > 0) { |
| --typos; |
| } else { |
| t->state = CTK_TEXTENTRY_NORMAL; |
| } |
| } |
| #else |
| txpos = 0; |
| #endif /* CTK_CONF_TEXTENTRY_MULTILINE */ |
| break; |
| |
| case CH_CURS_DOWN: |
| #if CTK_CONF_TEXTENTRY_MULTILINE |
| if(t->h == 1) { |
| txpos = strlen(t->text); |
| } else { |
| if(typos < t->h - 1) { |
| ++typos; |
| } else { |
| t->state = CTK_TEXTENTRY_NORMAL; |
| } |
| } |
| #else |
| txpos = strlen(t->text); |
| #endif /* CTK_CONF_TEXTENTRY_MULTILINE */ |
| break; |
| |
| case CH_ENTER: |
| #if CTK_CONF_TEXTENTRY_MULTILINE |
| if(t->h == 1) { |
| t->state = CTK_TEXTENTRY_NORMAL; |
| } else { |
| if(typos < t->h - 1) { |
| ++typos; |
| txpos = 0; |
| } else { |
| t->state = CTK_TEXTENTRY_NORMAL; |
| } |
| } |
| #else |
| t->state = CTK_TEXTENTRY_NORMAL; |
| #endif /* CTK_CONF_TEXTENTRY_MULTILINE */ |
| break; |
| |
| default: |
| len = tlen - txpos - 1; |
| if(c == CH_DEL) { |
| if(txpos > 0 && len > 0) { |
| strncpy(cptr - 1, cptr, |
| len); |
| *(cptr + len - 1) = 0; |
| --txpos; |
| } |
| } else { |
| if(len > 0) { |
| cptr2 = cptr + len - 1; |
| while(cptr2 + 1 > cptr) { |
| *(cptr2 + 1) = *cptr2; |
| --cptr2; |
| } |
| |
| *cptr = c; |
| ++txpos; |
| } |
| } |
| break; |
| } |
| |
| t->xpos = txpos; |
| t->ypos = typos; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| #if CTK_CONF_MENUS |
| static unsigned char |
| activate_menu(void) |
| { |
| struct ctk_window *w; |
| |
| lastmenu = menus.open; |
| if(menus.open == &desktopmenu) { |
| for(w = windows; w != NULL; w = w->next) { |
| if(w->title == desktopmenu.items[desktopmenu.active].title) { |
| ctk_window_open(w); |
| menus.open = NULL; |
| return REDRAW_ALL; |
| } |
| } |
| } else { |
| dispatcher_emit(ctk_signal_menu_activate, menus.open, |
| DISPATCHER_BROADCAST); |
| } |
| menus.open = NULL; |
| return REDRAW_MENUPART; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static unsigned char |
| menus_input(ctk_arch_key_t c) |
| { |
| |
| if(menus.open->nitems > maxnitems) { |
| maxnitems = menus.open->nitems; |
| } |
| |
| |
| switch(c) { |
| case CH_CURS_RIGHT: |
| switch_open_menu(1); |
| |
| return REDRAW_MENUPART; |
| |
| case CH_CURS_DOWN: |
| switch_menu_item(1); |
| return REDRAW_MENUS; |
| |
| case CH_CURS_LEFT: |
| switch_open_menu(0); |
| return REDRAW_MENUPART; |
| |
| case CH_CURS_UP: |
| switch_menu_item(0); |
| return REDRAW_MENUS; |
| |
| case CH_ENTER: |
| return activate_menu(); |
| |
| case CTK_CONF_MENU_KEY: |
| lastmenu = menus.open; |
| menus.open = NULL; |
| return REDRAW_MENUPART; |
| } |
| |
| return REDRAW_NONE; |
| } |
| #endif /* CTK_CONF_MENUS */ |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| ctk_idle(void) |
| { |
| static ctk_arch_key_t c; |
| static unsigned char i; |
| register struct ctk_window *window; |
| register struct ctk_widget *widget; |
| #if CTK_CONF_MOUSE_SUPPORT |
| static unsigned char mxc, myc, mouse_button_changed, mouse_moved, |
| mouse_clicked; |
| static unsigned char menux; |
| struct ctk_menu *menu; |
| |
| #endif /* CTK_CONF_MOUSE_SUPPORT */ |
| |
| #if CTK_CONF_MENUS |
| if(menus.open != NULL) { |
| maxnitems = menus.open->nitems; |
| } else { |
| maxnitems = 0; |
| } |
| #endif /* CTK_CONF_MENUS */ |
| |
| #if CTK_CONF_MOUSE_SUPPORT |
| mouse_button_changed = mouse_moved = mouse_clicked = 0; |
| |
| /* See if there is any change in the buttons. */ |
| if(ctk_mouse_button() != mouse_button) { |
| mouse_button = ctk_mouse_button(); |
| mouse_button_changed = 1; |
| if(mouse_button == 0) { |
| mouse_clicked = 1; |
| } |
| } |
| |
| /* Check if the mouse pointer has moved. */ |
| if(ctk_mouse_x() != mouse_x || |
| ctk_mouse_y() != mouse_y) { |
| mouse_x = ctk_mouse_x(); |
| mouse_y = ctk_mouse_y(); |
| mouse_moved = 1; |
| } |
| |
| mxc = ctk_mouse_xtoc(mouse_x); |
| myc = ctk_mouse_ytoc(mouse_y); |
| #endif /* CTK_CONF_MOUSE_SUPPORT */ |
| |
| |
| #if CTK_CONF_SCREENSAVER |
| if(mode == CTK_MODE_SCREENSAVER) { |
| #if 0 |
| #ifdef CTK_SCREENSAVER_RUN |
| CTK_SCREENSAVER_RUN(); |
| #endif /* CTK_SCREENSAVER_RUN */ |
| #endif /* 0 */ |
| if(ctk_arch_keyavail() |
| #if CTK_CONF_MOUSE_SUPPORT |
| || mouse_moved || mouse_button_changed |
| #endif /* CTK_CONF_MOUSE_SUPPORT */ |
| ) { |
| dispatcher_emit(ctk_signal_screensaver_stop, NULL, DISPATCHER_BROADCAST); |
| mode = CTK_MODE_NORMAL; |
| /* ctk_draw_init(); |
| ctk_desktop_redraw();*/ |
| } |
| } else |
| #endif /* CTK_CONF_SCREENSAVER */ |
| if(mode == CTK_MODE_NORMAL) { |
| #if CTK_CONF_MOUSE_SUPPORT |
| /* If there is any change in the mouse conditions, find out in |
| which window the mouse pointer currently is in order to send |
| the correct signals, or bring a window to focus. */ |
| if(mouse_moved || mouse_button_changed) { |
| ctk_mouse_show(); |
| screensaver_timer = 0; |
| |
| if(myc == 0) { |
| /* Here we should do whatever needs to be done when the mouse |
| moves around and clicks in the menubar. */ |
| if(mouse_clicked) { |
| /* Find out which menu that the mouse pointer is in. Start |
| with the ->next menu after the desktop menu. We assume |
| that the menus start one character from the left screen |
| side and that the desktop menu is farthest to the |
| right. */ |
| menux = 1; |
| for(menu = menus.menus->next; menu != NULL; menu = menu->next) { |
| if(mxc >= menux && mxc <= menux + menu->titlelen) { |
| break; |
| } |
| menux += menu->titlelen; |
| } |
| |
| /* Also check desktop menu. */ |
| if(mxc >= width - 7 && |
| mxc <= width - 1) { |
| menu = &desktopmenu; |
| } |
| |
| menus.open = menu; |
| redraw |= REDRAW_MENUPART; |
| } |
| } else { |
| --myc; |
| |
| if(menus.open != NULL) { |
| /* Do whatever needs to be done when a menu is open. */ |
| |
| if(menus.open == &desktopmenu) { |
| menux = width - CTK_CONF_MENUWIDTH; |
| } else { |
| menux = 1; |
| for(menu = menus.menus->next; menu != menus.open; |
| menu = menu->next) { |
| menux += menu->titlelen; |
| } |
| } |
| |
| if(mxc >= menux && mxc <= menux + CTK_CONF_MENUWIDTH) { |
| if(myc <= menus.open->nitems) { |
| menus.open->active = myc; |
| } else { |
| menus.open->active = menus.open->nitems - 1; |
| } |
| } |
| |
| if(mouse_clicked) { |
| if(mxc >= menux && mxc <= menux + CTK_CONF_MENUWIDTH && |
| myc <= menus.open->nitems) { |
| redraw |= activate_menu(); |
| } else { |
| lastmenu = menus.open; |
| menus.open = NULL; |
| redraw |= REDRAW_MENUPART; |
| } |
| } else { |
| redraw |= REDRAW_MENUS; |
| } |
| } else { |
| |
| /* Walk through the windows from top to bottom to see in |
| which window the mouse pointer is. */ |
| if(dialog != NULL) { |
| window = dialog; |
| } else { |
| for(window = windows; window != NULL; window = window->next) { |
| /* Check if the mouse is within the window. */ |
| if(mxc >= window->x && |
| mxc <= window->x + window->w && |
| myc >= window->y && |
| myc <= window->y + window->h) { |
| break; |
| } |
| } |
| } |
| |
| |
| /* If we didn't find any window, and there are no windows |
| open, the mouse pointer will definately be within the |
| background desktop window. */ |
| if(window == NULL) { |
| window = &desktop_window; |
| } |
| |
| /* If the mouse pointer moves around outside of the |
| currently focused window (or dialog), we should not have |
| any focused widgets in the focused window so we make sure |
| that there are none. */ |
| if(windows != NULL && |
| window != windows && |
| windows->focused != NULL){ |
| add_redrawwidget(windows->focused); |
| windows->focused = NULL; |
| redraw |= REDRAW_WIDGETS; |
| } |
| |
| if(window != NULL) { |
| /* If the mouse was clicked outside of the current window, |
| we bring the clicked window to front. */ |
| if(dialog == NULL && |
| window != &desktop_window && |
| window != windows && |
| mouse_clicked) { |
| /* Bring window to front. */ |
| ctk_window_open(window); |
| redraw |= REDRAW_ALL; |
| } else { |
| |
| /* Find out which widget currently is under the mouse |
| pointer and give it focus, unless it already has |
| focus. */ |
| mxc = mxc - window->x - 1; |
| myc = myc - window->y - 1; |
| |
| /* See if the mouse pointer is on a widget. If so, it |
| should be selected and, if the button is clicked, |
| activated. */ |
| for(widget = window->active; widget != NULL; |
| widget = widget->next) { |
| if(mxc >= widget->x && |
| mxc <= widget->x + widget->w && |
| (myc == widget->y || |
| ((widget->type == CTK_WIDGET_BITMAP || |
| /*widget->type == CTK_WIDGET_TEXTMAP ||*/ |
| widget->type == CTK_WIDGET_ICON) && |
| (myc >= widget->y && |
| myc <= widget->y + ((struct ctk_bitmap *)widget)->h)))) { |
| break; |
| } |
| } |
| |
| |
| if(mouse_moved && |
| (window != &desktop_window || |
| windows == NULL)) { |
| dispatcher_emit(ctk_signal_pointer_move, NULL, |
| window->owner); |
| |
| if(window->focused != NULL && |
| widget != window->focused) { |
| add_redrawwidget(window->focused); |
| if(CTK_WIDGET_TYPE(window->focused) == |
| CTK_WIDGET_TEXTENTRY) { |
| ((struct ctk_textentry *)(window->focused))->state = |
| CTK_TEXTENTRY_NORMAL; |
| } |
| window->focused = NULL; |
| } |
| redraw |= REDRAW_WIDGETS; |
| if(widget != NULL) { |
| select_widget(widget); |
| } |
| } |
| |
| if(mouse_button_changed) { |
| dispatcher_emit(ctk_signal_pointer_button, |
| (ek_data_t)mouse_button, |
| window->owner); |
| if(mouse_clicked && widget != NULL) { |
| select_widget(widget); |
| redraw |= activate(widget); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| #endif /* CTK_CONF_MOUSE_SUPPORT */ |
| |
| while(ctk_arch_keyavail()) { |
| |
| ctk_mouse_hide(); |
| |
| screensaver_timer = 0; |
| |
| c = ctk_arch_getkey(); |
| |
| if(dialog != NULL) { |
| window = dialog; |
| } else if(windows != NULL) { |
| window = windows; |
| } else { |
| window = &desktop_window; |
| } |
| widget = window->focused; |
| |
| |
| if(widget != NULL && |
| widget->type == CTK_WIDGET_TEXTENTRY && |
| widget->widget.textentry.state == CTK_TEXTENTRY_EDIT) { |
| textentry_input(c, (struct ctk_textentry *)widget); |
| add_redrawwidget(widget); |
| #if CTK_CONF_MENUS |
| } else if(menus.open != NULL) { |
| redraw |= menus_input(c); |
| #endif /* CTK_CONF_MENUS */ |
| } else { |
| switch(c) { |
| case CH_CURS_RIGHT: |
| switch_focus_widget(RIGHT); |
| break; |
| case CH_CURS_DOWN: |
| switch_focus_widget(DOWN); |
| break; |
| case CH_CURS_LEFT: |
| switch_focus_widget(LEFT); |
| break; |
| case CH_CURS_UP: |
| switch_focus_widget(UP); |
| break; |
| case CH_ENTER: |
| if(widget != NULL) { |
| redraw |= activate(widget); |
| } |
| break; |
| #if CTK_CONF_MENUS |
| case CTK_CONF_MENU_KEY: |
| if(dialog == NULL) { |
| if(lastmenu == NULL) { |
| menus.open = menus.menus; |
| } else { |
| menus.open = lastmenu; |
| } |
| menus.open->active = 0; |
| redraw |= REDRAW_MENUS; |
| } |
| break; |
| #endif /* CTK_CONF_MENUS */ |
| case CTK_CONF_WINDOWSWITCH_KEY: |
| if(windows != NULL) { |
| for(window = windows; window->next != NULL; |
| window = window->next); |
| ctk_window_open(window); |
| ctk_desktop_redraw(NULL); |
| } |
| break; |
| default: |
| if(widget != NULL && |
| widget->type == CTK_WIDGET_TEXTENTRY) { |
| widget->widget.textentry.state = CTK_TEXTENTRY_EDIT; |
| textentry_input(c, (struct ctk_textentry *)widget); |
| add_redrawwidget(widget); |
| } else { |
| dispatcher_emit(ctk_signal_keypress, (void *)c, |
| window->owner); |
| } |
| break; |
| } |
| } |
| |
| if(redraw & REDRAW_WIDGETS) { |
| for(i = 0; i < redraw_widgetptr; ++i) { |
| ctk_widget_redraw(redraw_widgets[i]); |
| } |
| redraw &= ~REDRAW_WIDGETS; |
| redraw_widgetptr = 0; |
| } |
| } |
| #if CTK_CONF_WINDOWMOVE |
| } else if(mode == CTK_MODE_WINDOWMOVE) { |
| |
| redraw = 0; |
| |
| window = windows; |
| |
| #if CTK_CONF_MOUSE_SUPPORT |
| |
| /* If the mouse has moved, we move the window as well. */ |
| if(mouse_moved) { |
| |
| if(window->w + mxc + 2 >= width) { |
| window->x = width - 2 - window->w; |
| } else { |
| window->x = mxc; |
| } |
| |
| if(window->h + myc + 2 >= height) { |
| window->y = height - 2 - window->h; |
| } else { |
| window->y = myc; |
| } |
| if(window->y > 0) { |
| --window->y; |
| } |
| |
| redraw = REDRAW_ALL; |
| } |
| |
| /* Check if the mouse has been clicked, and stop moving the window |
| if so. */ |
| if(mouse_button_changed && |
| mouse_button == 0) { |
| mode = CTK_MODE_NORMAL; |
| redraw = REDRAW_ALL; |
| } |
| #endif /* CTK_CONF_MOUSE_SUPPORT */ |
| |
| while(mode == CTK_MODE_WINDOWMOVE && ctk_arch_keyavail()) { |
| |
| screensaver_timer = 0; |
| |
| c = ctk_arch_getkey(); |
| |
| switch(c) { |
| case CH_CURS_RIGHT: |
| ++window->x; |
| if(window->x + window->w + 1 >= width) { |
| --window->x; |
| } |
| redraw = REDRAW_ALL; |
| break; |
| case CH_CURS_LEFT: |
| if(window->x > 0) { |
| --window->x; |
| } |
| redraw = REDRAW_ALL; |
| break; |
| case CH_CURS_DOWN: |
| ++window->y; |
| if(window->y + window->h + 2 >= height) { |
| --window->y; |
| } |
| redraw = REDRAW_ALL; |
| break; |
| case CH_CURS_UP: |
| if(window->y > 0) { |
| --window->y; |
| } |
| redraw = REDRAW_ALL; |
| break; |
| case CH_ENTER: |
| case CH_ESC: |
| mode = CTK_MODE_NORMAL; |
| redraw = REDRAW_ALL; |
| break; |
| } |
| } |
| /* if(redraw & REDRAW_ALL) { |
| do_redraw_all(1, height); |
| } |
| redraw = 0;*/ |
| #endif /* CTK_CONF_WINDOWMOVE */ |
| } |
| |
| if(redraw & REDRAW_ALL) { |
| do_redraw_all(1, height); |
| #if CTK_CONF_MENUS |
| } else if(redraw & REDRAW_MENUPART) { |
| do_redraw_all(1, maxnitems + 1); |
| } else if(redraw & REDRAW_MENUS) { |
| ctk_draw_menus(&menus); |
| #endif /* CTK_CONF_MENUS */ |
| } else if(redraw & REDRAW_FOCUS) { |
| if(dialog != NULL) { |
| ctk_window_redraw(dialog); |
| } else if(windows != NULL) { |
| ctk_window_redraw(windows); |
| } else { |
| ctk_window_redraw(&desktop_window); |
| } |
| } else if(redraw & REDRAW_WIDGETS) { |
| for(i = 0; i < redraw_widgetptr; ++i) { |
| ctk_widget_redraw(redraw_widgets[i]); |
| } |
| } |
| redraw = 0; |
| redraw_widgetptr = 0; |
| |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static |
| DISPATCHER_SIGHANDLER(ctk_sighandler, s, data) |
| { |
| DISPATCHER_SIGHANDLER_ARGS(s, data); |
| |
| if(s == ctk_signal_timer) { |
| if(mode == CTK_MODE_NORMAL) { |
| ++screensaver_timer; |
| if(screensaver_timer == ctk_screensaver_timeout) { |
| #if CTK_CONF_SCREENSAVER |
| dispatcher_emit(ctk_signal_screensaver_start, NULL, |
| DISPATCHER_BROADCAST); |
| #ifdef CTK_SCREENSAVER_INIT |
| CTK_SCREENSAVER_INIT(); |
| #endif /* CTK_SCREENSAVER_INIT */ |
| mode = CTK_MODE_SCREENSAVER; |
| #endif /* CTK_CONF_SCREENSAVER */ |
| screensaver_timer = 0; |
| } |
| } |
| dispatcher_timer(ctk_signal_timer, data, CLK_TCK); |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| |