/* * Copyright 2012 Ole Loots <ole@monochrom.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <stdint.h> #include <stdbool.h> #include <assert.h> #include <gem.h> #include <gemx.h> #include <cflib.h> #include "gemtk.h" #include "vaproto.h" //#define DEBUG_PRINT(x) printf x #define DEBUG_PRINT(x) struct gemtk_window_s { /** The AES handle of the window */ short handle; /** the generic event handler function for events passed to the client */ gemtk_wm_event_handler_f handler_func; /** The toolbar redraw function, if any */ gemtk_wm_redraw_f toolbar_redraw_func; /** window configuration */ uint32_t flags; /** window state */ uint32_t state; /** AES Tree used as toolbar */ OBJECT *toolbar; /** Current edit object selected in the toolbar, if any. */ short toolbar_edit_obj; /** Current selected object in the toolbar, if any. */ short toolbar_focus_obj; /** Describes the start of the toolbar tree (usually 0) */ short toolbar_idx; /** depending on the flag GEMTK_WM_FLAG_HAS_VTOOLBAR this defines the toolbar height or the toolbar width (GEMTK_WM_FLAG_HAS_VTOOLBAR means width). */ short toolbar_size; /** AES Object tree to be used for windowed dialogs. */ OBJECT *form; /** Current form edit object, if any. */ short form_edit_obj; /** Current form focus object, if any */ short form_focus_obj; /** Describes the start of the form tree */ short form_idx; /** Scroll state */ struct gemtk_wm_scroll_info_s scroll_info; /** Arbitary data set by the user */ void *user_data; /** linked list items */ struct gemtk_window_s *next, *prev; }; static GUIWIN * winlist; static VdiHdl v_vdi_h = -1; static short work_out[57]; static void move_rect(GUIWIN * win, GRECT *rect, int dx, int dy) { INT16 xy[ 8]; long dum = 0L; GRECT g; VdiHdl vh = gemtk_wm_get_vdi_handle(win); while(!wind_update(BEG_UPDATE)); graf_mouse(M_OFF, 0L); /* get intersection with screen area */ wind_get_grect(0, WF_CURRXYWH, &g); if(!rc_intersect(&g, rect)){ goto error; } xy[0] = rect->g_x; xy[1] = rect->g_y; xy[2] = xy[0] + rect->g_w-1; xy[3] = xy[1] + rect->g_h-1; xy[4] = xy[0] + dx; xy[5] = xy[1] + dy; xy[6] = xy[2] + dx; xy[7] = xy[3] + dy; vro_cpyfm(vh, S_ONLY, xy, (MFDB *)&dum, (MFDB *)&dum); error: graf_mouse(M_ON, 0L); wind_update(END_UPDATE); } /** * Handles common events. * returns 0 when the event was not handled, 1 otherwise. */ static short preproc_wm(GUIWIN * gw, EVMULT_OUT *ev_out, short msg[8]) { GRECT g, g_ro, g2; short retval = 1; int val = 1; struct gemtk_wm_scroll_info_s *slid; switch(msg[0]) { case WM_HSLID: gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &g); wind_set(gw->handle, WF_HSLIDE, msg[4], 0, 0, 0); slid = gemtk_wm_get_scroll_info(gw); val = (float)(slid->x_units-(g.g_w/slid->x_unit_px))/1000*(float)msg[4]; if(val != slid->x_pos) { if (val < slid->x_pos) { val = -(MAX(0, slid->x_pos-val)); } else { val = val-slid->x_pos; } gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, val, false); } break; case WM_VSLID: gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &g); wind_set(gw->handle, WF_VSLIDE, msg[4], 0, 0, 0); slid = gemtk_wm_get_scroll_info(gw); val = (float)(slid->y_units-(g.g_h/slid->y_unit_px))/1000*(float)msg[4]; if(val != slid->y_pos) { if (val < slid->y_pos) { val = -(slid->y_pos - val); } else { val = val -slid->y_pos; } gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, val, false); } break; case WM_ARROWED: if((gw->flags & GEMTK_WM_FLAG_CUSTOM_SCROLLING) == 0) { slid = gemtk_wm_get_scroll_info(gw); gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &g); g_ro = g; switch(msg[4]) { case WA_UPPAGE: /* scroll page up */ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, -(g.g_h/slid->y_unit_px), true); break; case WA_UPLINE: /* scroll line up */ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, -1, true); break; case WA_DNPAGE: /* scroll page down */ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, g.g_h/slid->y_unit_px, true); break; case WA_DNLINE: /* scroll line down */ gemtk_wm_scroll(gw, GEMTK_WM_VSLIDER, +1, true); break; case WA_LFPAGE: /* scroll page left */ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, -(g.g_w/slid->x_unit_px), true); break; case WA_LFLINE: /* scroll line left */ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, -1, true); break; case WA_RTPAGE: /* scroll page right */ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, (g.g_w/slid->x_unit_px), true); break; case WA_RTLINE: /* scroll line right */ gemtk_wm_scroll(gw, GEMTK_WM_HSLIDER, 1, true); break; default: break; } } break; case WM_TOPPED: wind_set(gw->handle, WF_TOP, 1, 0, 0, 0); break; case WM_MOVED: wind_get_grect(gw->handle, WF_CURRXYWH, &g); wind_set(gw->handle, WF_CURRXYWH, msg[4], msg[5], g.g_w, g.g_h); if (gw->form) { gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &g); slid = gemtk_wm_get_scroll_info(gw); gw->form[gw->form_idx].ob_x = g.g_x - (slid->x_pos * slid->x_unit_px); gw->form[gw->form_idx].ob_y = g.g_y - (slid->y_pos * slid->y_unit_px); } break; case WM_SIZED: case WM_REPOSED: wind_get_grect(gw->handle, WF_FULLXYWH, &g2); wind_get_grect(gw->handle, WF_CURRXYWH, &g); g.g_w = MIN(msg[6], g2.g_w); g.g_h = MIN(msg[7], g2.g_h); if(g2.g_w != g.g_w || g2.g_h != g.g_h) { wind_set(gw->handle, WF_CURRXYWH, g.g_x, g.g_y, g.g_w, g.g_h); if((gw->flags & GEMTK_WM_FLAG_CUSTOM_SCROLLING) == 0) { if(gemtk_wm_update_slider(gw, GEMTK_WM_VH_SLIDER)) { gemtk_wm_exec_redraw(gw, NULL); } } } break; case WM_FULLED: wind_get_grect(DESKTOP_HANDLE, WF_WORKXYWH, &g); wind_get_grect(gw->handle, WF_CURRXYWH, &g2); if(g.g_w == g2.g_w && g.g_h == g2.g_h) { wind_get_grect(gw->handle, WF_PREVXYWH, &g); } wind_set_grect(gw->handle, WF_CURRXYWH, &g); if((gw->flags & GEMTK_WM_FLAG_CUSTOM_SCROLLING) == 0) { if(gemtk_wm_update_slider(gw, GEMTK_WM_VH_SLIDER)) { gemtk_wm_exec_redraw(gw, NULL); } } break; case WM_ICONIFY: wind_set(gw->handle, WF_ICONIFY, msg[4], msg[5], msg[6], msg[7]); gw->state |= GEMTK_WM_STATUS_ICONIFIED; break; case WM_UNICONIFY: wind_set(gw->handle, WF_UNICONIFY, msg[4], msg[5], msg[6], msg[7]); gw->state &= ~(GEMTK_WM_STATUS_ICONIFIED); break; case WM_SHADED: gw->state |= GEMTK_WM_STATUS_SHADED; break; case WM_UNSHADED: gw->state &= ~(GEMTK_WM_STATUS_SHADED); break; case WM_REDRAW: if ((gw->flags & GEMTK_WM_FLAG_CUSTOM_TOOLBAR) == 0 && (gw->toolbar != NULL)) { g.g_x = msg[4]; g.g_y = msg[5]; g.g_w = msg[6]; g.g_h = msg[7]; if((gw->state & GEMTK_WM_STATUS_ICONIFIED) == 0){ gemtk_wm_toolbar_redraw(gw, WM_REDRAW, &g); } } if (gw->form != NULL) { g.g_x = msg[4]; g.g_y = msg[5]; g.g_w = msg[6]; g.g_h = msg[7]; gemtk_wm_form_redraw(gw, &g); } break; default: retval = 0; break; } return(retval); } /** * Preprocess mouse events */ static short preproc_mu_button(GUIWIN * gw, EVMULT_OUT *ev_out, short msg[8]) { short retval = 0, obj_idx = 0; DEBUG_PRINT(("preproc_mu_button\n")); // toolbar handling: if ((gw->flags & GEMTK_WM_FLAG_CUSTOM_TOOLBAR) == 0 && gw->toolbar != NULL) { GRECT tb_area; gemtk_wm_get_grect(gw, GEMTK_WM_AREA_TOOLBAR, &tb_area); if (POINT_WITHIN(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y, tb_area)) { gw->toolbar[gw->toolbar_idx].ob_x = tb_area.g_x; gw->toolbar[gw->toolbar_idx].ob_y = tb_area.g_y; obj_idx = objc_find(gw->toolbar, gw->toolbar_idx, 8, ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y); gw->toolbar_focus_obj = obj_idx; DEBUG_PRINT(("Toolbar index: %d\n", obj_idx)); if (obj_idx > -1 && (gw->toolbar[obj_idx].ob_state & OS_DISABLED)== 0 && ((gw->flags & GEMTK_WM_FLAG_CUSTOM_TOOLBAR) == 0)) { uint16_t type = (gw->toolbar[obj_idx].ob_type & 0xFF); uint16_t nextobj; DEBUG_PRINT(("toolbar item type: %d, toolbar_edit_obj: %d\n", type, gw->toolbar_edit_obj)); // Report mouse click to the tree: retval = form_wbutton(gw->toolbar, gw->toolbar_focus_obj, ev_out->emo_mclicks, &nextobj, gw->handle); if (nextobj == obj_idx && (type == G_FTEXT || type == G_FBOXTEXT)) { gw->toolbar_edit_obj = obj_idx; } else { gw->toolbar_edit_obj = -1; } } // send WM_TOOLBAR message short oldevents = ev_out->emo_events; short msg_out[8] = {WM_TOOLBAR, gl_apid, 0, gw->handle, obj_idx, ev_out->emo_mclicks, ev_out->emo_kmeta, ev_out->emo_mbutton }; ev_out->emo_events = MU_MESAG; // notify the window about toolbar click: gw->handler_func(gw, ev_out, msg_out); ev_out->emo_events = oldevents; retval = 1; } else { if (gw->toolbar_edit_obj != -1) { gw->toolbar_edit_obj = -1; } } } if (gw->form != NULL) { GRECT content_area; struct gemtk_wm_scroll_info_s *slid; DEBUG_PRINT(("preproc_mu_button: handling form click.\n")); gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &content_area); if (POINT_WITHIN(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y, content_area)) { slid = gemtk_wm_get_scroll_info(gw); // adjust form position (considering window and scroll position): gw->form[gw->form_idx].ob_x = content_area.g_x - (slid->x_pos * slid->x_unit_px); gw->form[gw->form_idx].ob_y = content_area.g_y - (slid->y_pos * slid->y_unit_px); obj_idx = objc_find(gw->form, gw->form_idx, 8, ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y); gw->form_focus_obj = obj_idx; DEBUG_PRINT(("Window Form click, obj: %d\n", gw->form_focus_obj)); if (obj_idx > -1 && (gw->form[obj_idx].ob_state & OS_DISABLED)== 0) { uint16_t type = (gw->form[obj_idx].ob_type & 0xFF); uint16_t nextobj; DEBUG_PRINT(("type: %d\n", type)); retval = form_wbutton(gw->form, gw->form_focus_obj, ev_out->emo_mclicks, &nextobj, gw->handle); if (nextobj == obj_idx && (type == G_FTEXT || type == G_FBOXTEXT)) { gw->form_edit_obj = obj_idx; } else { gw->form_edit_obj = -1; } short oldevents = ev_out->emo_events; short msg_out[8] = { GEMTK_WM_WM_FORM_CLICK, gl_apid, 0, gw->handle, gw->form_focus_obj, ev_out->emo_mclicks, ev_out->emo_kmeta, 0 }; ev_out->emo_events = MU_MESAG; // notify the window about form click: gw->handler_func(gw, ev_out, msg_out); ev_out->emo_events = oldevents; retval = 1; evnt_timer(150); } } else { gw->form_edit_obj = -1; } } return(retval); } /** * Preprocess keyboard events (for forms/toolbars) */ static short preproc_mu_keybd(GUIWIN * gw, EVMULT_OUT *ev_out, short msg[8]) { short retval = 0; if ((gw->toolbar != NULL) && (gw->toolbar_edit_obj > -1)) { short next_edit_obj = gw->toolbar_edit_obj; short next_char = -1; short edit_idx; short r; DEBUG_PRINT(("%s, gw: %p, toolbar_edit_obj: %d\n", __FUNCTION__, gw, gw->toolbar_edit_obj)); r = form_wkeybd(gw->toolbar, gw->toolbar_edit_obj, next_edit_obj, ev_out->emo_kreturn, &next_edit_obj, &next_char, gw->handle); if (next_edit_obj != gw->toolbar_edit_obj) { gemtk_wm_set_toolbar_edit_obj(gw, next_edit_obj, ev_out->emo_kreturn); } else { if (next_char > 13) { r = objc_wedit(gw->toolbar, gw->toolbar_edit_obj, ev_out->emo_kreturn, &edit_idx, EDCHAR, gw->handle); } } //retval = 1; /*gemtk_wm_send_msg(gw, GEMTK_WM_WM_FORM_KEY, gw->toolbar_edit_obj, ev_out->emo_kreturn, 0, 0);*/ } if((gw->form != NULL) && (gw->form_edit_obj > -1) ) { short next_edit_obj = gw->form_edit_obj; short next_char = -1; short edit_idx; short r; r = form_wkeybd(gw->form, gw->form_edit_obj, next_edit_obj, ev_out->emo_kreturn, &next_edit_obj, &next_char, gw->handle); if (next_edit_obj != gw->form_edit_obj) { if(gw->form_edit_obj != -1) { objc_wedit(gw->form, gw->form_edit_obj, ev_out->emo_kreturn, &edit_idx, EDEND, gw->handle); } gw->form_edit_obj = next_edit_obj; objc_wedit(gw->form, gw->form_edit_obj, ev_out->emo_kreturn, &edit_idx, EDINIT, gw->handle); } else { if(next_char > 13) r = objc_wedit(gw->form, gw->form_edit_obj, ev_out->emo_kreturn, &edit_idx, EDCHAR, gw->handle); } } return(retval); } /** * Default toolbar redraw function */ static void std_toolbar_redraw(GUIWIN *gw, uint16_t msg, GRECT *clip) { GRECT g, tb_area; gemtk_wm_get_grect(gw, GEMTK_WM_AREA_TOOLBAR, &tb_area); assert(gw->toolbar); assert(gw->toolbar_idx >= 0); // Update object position: gw->toolbar[gw->toolbar_idx].ob_x = tb_area.g_x; gw->toolbar[gw->toolbar_idx].ob_y = tb_area.g_y; gw->toolbar[gw->toolbar_idx].ob_width = tb_area.g_w; gw->toolbar[gw->toolbar_idx].ob_height = tb_area.g_h; wind_get_grect(gw->handle, WF_FIRSTXYWH, &g); while (g.g_h > 0 || g.g_w > 0) { if(rc_intersect(clip, &g)) { objc_draw(gw->toolbar, gw->toolbar_idx, 8, g.g_x, g.g_y, g.g_w, g.g_h); } wind_get_grect(gw->handle, WF_NEXTXYWH, &g); } } /** * Event Dispatcher function. The guiwin API doesn't own an event loop, * so you have to inform it for every event that you want it to handle. */ short gemtk_wm_dispatch_event(EVMULT_IN *ev_in, EVMULT_OUT *ev_out, short msg[8]) { GUIWIN *dest; short retval = 0; bool handler_called = false; if( (ev_out->emo_events & MU_MESAG) != 0 ) { DEBUG_PRINT(("gemtk_wm_handle_event_multi_fast: %d (%x)\n", msg[0], msg[0])); switch (msg[0]) { case WM_REDRAW: case WM_CLOSED: case WM_TOPPED: case WM_ARROWED: case WM_HSLID: case WM_VSLID: case WM_FULLED: case WM_SIZED: case WM_REPOSED: case WM_MOVED: case WM_NEWTOP: case WM_UNTOPPED: case WM_ONTOP: case WM_BOTTOM: case WM_ICONIFY: case WM_UNICONIFY: case WM_ALLICONIFY: case WM_TOOLBAR: case AP_DRAGDROP: case AP_TERM: case AP_TFAIL: dest = gemtk_wm_find(msg[3]); if (dest) { DEBUG_PRINT(("Found WM_ dest: %p (%d), flags: %d, cb: %p\n", dest, dest->handle, dest->flags, dest->handler_func)); if (dest->flags&GEMTK_WM_FLAG_PREPROC_WM) { retval = preproc_wm(dest, ev_out, msg); if(((retval == 0)||(dest->flags&GEMTK_WM_FLAG_RECV_PREPROC_WM))) { retval = dest->handler_func(dest, ev_out, msg); handler_called = true; } } else { if (dest->handler_func) { retval = dest->handler_func(dest, ev_out, msg); handler_called = true; } } } break; // TODO: check code with Thing! Desktop /* We receive VA_PROTOSTATUS but AV_START doesn't seem to cause an TeraDesk response. Check if something happens with Thing! Desktop. case VA_PROTOSTATUS: case VA_VIEWED: case AV_STARTED: gemtk_av_dispatch(msg); break; */ } } else { short h_aes; h_aes = wind_find(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y); if(h_aes > 0 && (ev_out->emo_events != MU_TIMER)) { dest = gemtk_wm_find(h_aes); if (dest == NULL || dest->handler_func == NULL) return(0); DEBUG_PRINT(("Found Event receiver GUIWIN: %p (%d), flags: %d, cb: %p\n", dest, dest->handle, dest->flags, dest->handler_func)); if ((ev_out->emo_events & MU_BUTTON) != 0) { DEBUG_PRINT(("gemtk_wm_handle_event_multi_fast: MU_BUTTON -> " "%d / %d\n", ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y)); retval = preproc_mu_button(dest, ev_out, msg); if(retval != 0) { handler_called = true; } } if ((ev_out->emo_events & MU_KEYBD)) { DEBUG_PRINT(("gemtk_wm_handle_event_multi_fast: MU_KEYBD -> %x\n", ev_out->emo_kreturn)); retval = preproc_mu_keybd(dest, ev_out, msg); if(retval != 0) { handler_called = true; } } if (handler_called==false) { retval = dest->handler_func(dest, ev_out, msg); } } } return(retval); } /** * Initialises the guiwin API */ short gemtk_wm_init(void) { if(v_vdi_h == -1) { short dummy; short work_in[12] = {Getrez()+2,1,1,1,1,1,1,1,1,1,2,1}; v_vdi_h=graf_handle(&dummy, &dummy, &dummy, &dummy); v_opnvwk(work_in, &v_vdi_h, work_out); } return(0); } void gemtk_wm_exit(void) { v_clsvwk(v_vdi_h); } /** * Adds and AES handle to the guiwin list and creates and GUIWIN management * structure. * * \param handle The AES handle * \param flags Creation flags, configures how the AES window is handled * \param cb event handler function for that window */ GUIWIN * gemtk_wm_add(short handle, uint32_t flags, gemtk_wm_event_handler_f cb) { GUIWIN *win = calloc(1, sizeof(GUIWIN)); assert(win!=NULL); DEBUG_PRINT(("gemtk_wm_add: %d, %p, cb: %p\n", handle, win, cb)); win->handle = handle; win->handler_func = cb; win->flags = flags; gemtk_wm_link(win); DEBUG_PRINT(("Added guiwin: %p, tb: %p\n", win, win->toolbar)); return(win); } /** * Returns an GUIWIN* for AES handle, when that AES window is managed by gemtk_wm */ GUIWIN *gemtk_wm_find(short handle) { GUIWIN *g; DEBUG_PRINT(("guiwin search handle: %d\n", handle)); for (g = winlist; g != NULL; g=g->next) { if(g->handle == handle) { DEBUG_PRINT(("guiwin found handle: %p\n", g)); return(g); } } return(NULL); } void gemtk_wm_dump_window_info(GUIWIN *win) { char title[255]; GRECT work_area; GRECT curr_area; GRECT gemtk_work_area; GRECT gemtk_toolbar_area; GRECT gemtk_free_area; short handle; struct gemtk_wm_scroll_info_s *slid; handle = gemtk_wm_get_handle(win); assert(handle); gemtk_wind_get_str(handle, WF_NAME, title, 255); wind_get_grect(handle, WF_WORKXYWH, &work_area); wind_get_grect(handle, WF_CURRXYWH, &curr_area); gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &gemtk_free_area); gemtk_wm_get_grect(win, GEMTK_WM_AREA_WORK, &gemtk_work_area); gemtk_wm_get_grect(win, GEMTK_WM_AREA_TOOLBAR, &gemtk_toolbar_area); slid = gemtk_wm_get_scroll_info(win); printf ("GEMTK Window: %p (AES handle: %d)\n", win, win->handle); printf ("Title: %s\n", title); GEMTK_DBG_GRECT ("WF_WORKXYWH: \n", &work_area) GEMTK_DBG_GRECT ("WF_CURRXYWH: \n", &curr_area) GEMTK_DBG_GRECT ("GEMTK_WM_AREA_CONTENT:\n", &gemtk_free_area) GEMTK_DBG_GRECT ("GEMTK_WM_AREA_WORK:\n", &gemtk_work_area) GEMTK_DBG_GRECT ("GEMTK_WM_AREA_TOOLBAR:\n", &gemtk_toolbar_area) printf ("Slider X pos: %d\n", slid->x_pos); printf ("Slider Y pos: %d\n", slid->y_pos); printf ("Slider X units: %d\n", slid->x_unit_px); printf ("Slider Y units: %d\n", slid->y_unit_px); #undef DBG_GRECT }; /** * Check's if the pointer is managed by the guiwin API. */ GUIWIN *gemtk_wm_validate_ptr(GUIWIN *win) { GUIWIN *g; for( g = winlist; g != NULL; g=g->next ) { DEBUG_PRINT(("guiwin gemtk_wm_validate_ptr check: %p\n", g)); if(g == win) { DEBUG_PRINT(("gemtk_wm_validate_ptr valid: %p\n", g)); return(g); } } return(NULL); } /** * Add the GUIWIN to the list of handled windows. */ GUIWIN *gemtk_wm_link(GUIWIN *win) { /* Make sure the window is not linked: */ GUIWIN *win_val = gemtk_wm_validate_ptr(win); if(win_val){ DEBUG_PRINT(("GUIWIN %p is already linked!\n", win)); return(NULL); } if (winlist == NULL) { winlist = win; win->next = NULL; win->prev = NULL; } else { GUIWIN *tmp = winlist; while( tmp->next != NULL ) { tmp = tmp->next; } tmp->next = win; win->prev = tmp; win->next = NULL; } return(win); } /** * Remove the GUIWIN from the list of handled windows. */ GUIWIN *gemtk_wm_unlink(GUIWIN *win) { GUIWIN * win_val; /* Make sure the window is linked: */ win_val = gemtk_wm_validate_ptr(win); if (win_val == NULL){ DEBUG_PRINT(("GUIWIN %p is not linked!\n", win)); return(NULL); } /* unlink the window: */ if(win->prev != NULL ) { win->prev->next = win->next; } else { winlist = win->next; } if (win->next != NULL) { win->next->prev = win->prev; } return(win); } /** * Remove an GUIWIN from the list of managed windows and free the GUIWIN. * Call this when the AES window is closed or deleted. */ short gemtk_wm_remove(GUIWIN *win) { gemtk_wm_unlink(win); DEBUG_PRINT(("guiwin free: %p\n", win)); free(win); return(0); } /** Calculate & get a well known area within the GUIWIN. * \param win The GUIWIN ptr. * \param mode Specifies the area to retrieve. * \param dest The calculated rectangle. */ void gemtk_wm_get_grect(GUIWIN *win, enum guwin_area_e mode, GRECT *dest) { assert(win != NULL); wind_get_grect(win->handle, WF_WORKXYWH, dest); if (mode == GEMTK_WM_AREA_CONTENT) { GRECT tb_area; gemtk_wm_get_grect(win, GEMTK_WM_AREA_TOOLBAR, &tb_area); if (win->flags & GEMTK_WM_FLAG_HAS_VTOOLBAR) { dest->g_x += tb_area.g_w; dest->g_w -= tb_area.g_w; } else { dest->g_y += tb_area.g_h; dest->g_h -= tb_area.g_h; } } else if (mode == GEMTK_WM_AREA_TOOLBAR) { if (win->toolbar) { if (win->flags & GEMTK_WM_FLAG_HAS_VTOOLBAR) { dest->g_w = win->toolbar_size; } else { dest->g_h = win->toolbar_size; } } else { dest->g_w = 0; dest->g_h = 0; } } } /** * Scroll the content area (GEMTK_WM_AREA_CONTENT) in the specified dimension * (GEMTK_WM_VSLIDER, GEMTK_WM_HSLIDER) * \param win The GUIWIN * \param orientation GEMTK_WM_VSLIDER or GEMTK_WM_HSLIDER * \param units the amout to scroll (pass negative values to scroll up) * \param refresh Sliders will be updated when this flag is set */ void gemtk_wm_scroll(GUIWIN *win, short orientation, int units, bool refresh) { struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(win); int oldpos = 0, newpos = 0, vis_units=0, pix = 0; int abs_pix = 0; GRECT *redraw=NULL, g, g_ro; gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &g); g_ro = g; if (orientation == GEMTK_WM_VSLIDER) { pix = units*slid->y_unit_px; abs_pix = abs(pix); oldpos = slid->y_pos; vis_units = g.g_h/slid->y_unit_px; newpos = slid->y_pos = MIN(slid->y_units-vis_units, MAX(0, slid->y_pos+units)); if(newpos < 0) { newpos = slid->y_pos = 0; } if(oldpos == newpos) return; if (units>=vis_units || gemtk_wm_has_intersection(win, &g_ro)) { // send complete redraw redraw = &g_ro; } else { // only adjust ypos when scrolling down: if(pix < 0 ) { // blit screen area: g.g_h -= abs_pix; move_rect(win, &g, 0, abs_pix); g.g_y = g_ro.g_y; g.g_h = abs_pix; redraw = &g; } else { // blit screen area: g.g_y += abs_pix; g.g_h -= abs_pix; move_rect(win, &g, 0, -abs_pix); g.g_y = g_ro.g_y + g_ro.g_h - abs_pix; g.g_h = abs_pix; redraw = &g; } } } else { pix = units*slid->x_unit_px; abs_pix = abs(pix); oldpos = slid->x_pos; vis_units = g.g_w/slid->x_unit_px; newpos = slid->x_pos = MIN(slid->x_units-vis_units, MAX(0, slid->x_pos+units)); if(newpos < 0) { newpos = slid->x_pos = 0; } if(oldpos == newpos) return; if (units>=vis_units || gemtk_wm_has_intersection(win, &g_ro)) { // send complete redraw redraw = &g_ro; } else { // only adjust ypos when scrolling down: if(pix < 0 ) { // blit screen area: g.g_w -= abs_pix; move_rect(win, &g, abs_pix, 0); g.g_x = g_ro.g_x; g.g_w = abs_pix; redraw = &g; } else { // blit screen area: g.g_x += abs_pix; g.g_w -= abs_pix; move_rect(win, &g, -abs_pix, 0); g.g_x = g_ro.g_x + g_ro.g_w - abs_pix; g.g_w = abs_pix; redraw = &g; } } } if (refresh) { gemtk_wm_update_slider(win, orientation); } if ((redraw != NULL) && (redraw->g_h > 0)) { gemtk_wm_exec_redraw(win, redraw); } } /** * Refresh the sliders of the window. * \param win the GUIWIN * \param mode bitmask, valid bits: GEMTK_WM_VSLIDER, GEMTK_WM_HSLIDER */ bool gemtk_wm_update_slider(GUIWIN *win, short mode) { GRECT viewport; struct gemtk_wm_scroll_info_s * slid; unsigned long size, pos; int old_x, old_y; short handle = gemtk_wm_get_handle(win); gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &viewport); slid = gemtk_wm_get_scroll_info(win); old_x = slid->x_pos; old_y = slid->y_pos; // TODO: check if the window has sliders of that direction...? if((mode & GEMTK_WM_VSLIDER) && (slid->y_unit_px > 0)) { if ( slid->y_units < (long)viewport.g_h/slid->y_unit_px) { size = 1000L; } else size = MAX( 50L, (unsigned long)viewport.g_h*1000L/ (unsigned long)(slid->y_unit_px*slid->y_units)); wind_set(handle, WF_VSLSIZE, (int)size, 0, 0, 0); if (slid->y_units > (long)viewport.g_h/slid->y_unit_px) { pos = (unsigned long)slid->y_pos *1000L/ (unsigned long)(slid->y_units-viewport.g_h/slid->y_unit_px); wind_set(handle, WF_VSLIDE, (int)pos, 0, 0, 0); } else if (slid->y_pos) { slid->y_pos = 0; wind_set(handle, WF_VSLIDE, 0, 0, 0, 0); } } if((mode & GEMTK_WM_HSLIDER) && (slid->x_unit_px > 0)) { if ( slid->x_units < (long)viewport.g_w/slid->x_unit_px) size = 1000L; else size = MAX( 50L, (unsigned long)viewport.g_w*1000L/ (unsigned long)(slid->x_unit_px*slid->x_units)); wind_set(handle, WF_HSLSIZE, (int)size, 0, 0, 0); if( slid->x_units > (long)viewport.g_w/slid->x_unit_px) { pos = (unsigned long)slid->x_pos*1000L/ (unsigned long)(slid->x_units-viewport.g_w/slid->x_unit_px); wind_set(handle, WF_HSLIDE, (int)pos, 0, 0, 0); } else if (slid->x_pos) { slid->x_pos = 0; wind_set(handle, WF_HSLIDE, 0, 0, 0, 0); } } if(old_x != slid->x_pos || old_y != slid->y_pos) { return(true); } return(false); } /** * Return the AES handle for the GUIWIN. */ short gemtk_wm_get_handle(GUIWIN *win) { return(win->handle); } /** * Return the VDI handle for an GUIWIN. */ VdiHdl gemtk_wm_get_vdi_handle(GUIWIN *win) { return(v_vdi_h); } /** * Returns the state bitmask of the window */ uint32_t gemtk_wm_get_state(GUIWIN *win) { return(win->state); } /** * Set and new event handler function. */ void gemtk_wm_set_event_handler(GUIWIN *win,gemtk_wm_event_handler_f cb) { win->handler_func = cb; } /** * Configure the window so that it shows an toolbar or at least reserves * an area to draw an toolbar. * \param win The GUIWIN * \param toolbar the AES form * \param idx index within the toolbar tree (0 in most cases...) * \param flags optional configuration flags */ //TODO: document flags void gemtk_wm_set_toolbar(GUIWIN *win, OBJECT *toolbar, short idx, uint32_t flags) { win->toolbar = toolbar; win->toolbar_idx = idx; win->toolbar_edit_obj = -1; if (flags & GEMTK_WM_FLAG_HAS_VTOOLBAR) { win->flags |= GEMTK_WM_FLAG_HAS_VTOOLBAR; win->toolbar_size = win->toolbar[idx].ob_width; } else { win->toolbar_size = win->toolbar[idx].ob_height; } gemtk_wm_set_toolbar_redraw_func(win, std_toolbar_redraw); } /** Update width/height of the toolbar region * \param win the GUIWIN * \param s the width or height, depending on the flag GEMTK_WM_FLAG_HAS_VTOOLBAR */ void gemtk_wm_set_toolbar_size(GUIWIN *win, uint16_t s) { win->toolbar_size = s; } short gemtk_wm_get_toolbar_edit_obj(GUIWIN *win) { return(win->toolbar_edit_obj); } /** Set the current active edit object */ void gemtk_wm_set_toolbar_edit_obj(GUIWIN *win, uint16_t obj, short kreturn) { short edit_idx; DEBUG_PRINT(("%s, win: %p, obj: %d, kret: %d\n", __FUNCTION__, win, obj, kreturn)); if (obj != win->toolbar_edit_obj) { DEBUG_PRINT(("%s, current edit obj: %d\n", __FUNCTION__, win->toolbar_edit_obj)); if(win->toolbar_edit_obj != -1) { objc_wedit(win->toolbar, win->toolbar_edit_obj, kreturn, &edit_idx, EDEND, win->handle); } win->toolbar_edit_obj = obj; objc_wedit(win->toolbar, win->toolbar_edit_obj, kreturn, &edit_idx, EDINIT, win->handle); } else { DEBUG_PRINT(("%s, nothing to do!\n", __FUNCTION__)); } } /** Set an custom toolbar redraw function which is called instead of * default drawing routine. * \param win the GUIWIN * \param func the custom redraw function */ void gemtk_wm_set_toolbar_redraw_func(GUIWIN *win, gemtk_wm_redraw_f func) { win->toolbar_redraw_func = func; } /** * Attach an arbitary pointer to the GUIWIN */ void gemtk_wm_set_user_data(GUIWIN *win, void *data) { win->user_data = data; } /** * Retrieve the user_data pointer attached to the GUIWIN. */ void *gemtk_wm_get_user_data(GUIWIN *win) { return(win->user_data); } /** Get the scroll management structure for a GUIWIN */ struct gemtk_wm_scroll_info_s *gemtk_wm_get_scroll_info(GUIWIN *win) { return(&win->scroll_info); } /** * Get the amount of content dimensions within the window * which is calculated by using the scroll_info attached to the GUIWIN. */ void gemtk_wm_set_scroll_grid(GUIWIN * win, short x, short y) { struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(win); assert(slid != NULL); slid->y_unit_px = x; slid->x_unit_px = y; } /** Set the size of the content measured in content units * \param win the GUIWIN * \param x horizontal size * \param y vertical size */ void gemtk_wm_set_content_units(GUIWIN * win, short x, short y) { struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(win); assert(slid != NULL); slid->x_units = x; slid->y_units = y; } /** Send an Message to a GUIWIN using AES message pipe * \param win the GUIWIN which shall receive the message * \param msg_type the WM_ message definition * \param a the 4th parameter to appl_write * \param b the 5th parameter to appl_write * \param c the 6th parameter to appl_write * \param d the 7th parameter to appl_write */ void gemtk_wm_send_msg(GUIWIN *win, short msg_type, short a, short b, short c, short d) { short msg[8]; msg[0] = msg_type; msg[1] = gl_apid; msg[2] = 0; msg[3] = (win != NULL) ? win->handle : 0; msg[4] = a; msg[5] = b; msg[6] = c; msg[7] = d; appl_write(gl_apid, 16, &msg); } /** Directly execute an Message to a GUIWIN using the internal dispatcher function. * This only works for managed windows which have the, GEMTK_WM_FLAG_PREPROC_WM flag set. This call does not send any AES messages. * \param win the GUIWIN which shall receive the message * \param msg_type the WM_ message definition * \param a the 4th parameter to appl_write * \param b the 5th parameter to appl_write * \param c the 6th parameter to appl_write * \param d the 7th parameter to appl_write */ short gemtk_wm_exec_msg(GUIWIN *win, short msg_type, short a, short b, short c, short d) { short msg[8], retval; EVMULT_OUT event_out; msg[0] = msg_type; msg[1] = gl_apid; msg[2] = 0; msg[3] = win->handle; msg[4] = a; msg[5] = b; msg[6] = c; msg[7] = d; event_out.emo_events = MU_MESAG; retval = preproc_wm(win, &event_out, msg); if (retval == 0 || (win->flags & GEMTK_WM_FLAG_PREPROC_WM) != 0){ retval = win->handler_func(win, &event_out, msg); } return(retval); } void gemtk_wm_exec_redraw(GUIWIN *win, GRECT *area) { GRECT work; if (area == NULL) { gemtk_wm_get_grect(win, GEMTK_WM_AREA_WORK, &work); if (work.g_w < 1 || work.g_h < 1) { if (win->toolbar != NULL) { gemtk_wm_get_grect(win, GEMTK_WM_AREA_TOOLBAR, &work); if (work.g_w < 1 || work.g_h < 1) { return; } } } area = &work; } gemtk_wm_exec_msg(win, WM_REDRAW, area->g_x, area->g_y, area->g_w, area->g_h); } /** Attach an AES FORM to the GUIWIN, similar feature like the toolbar */ void gemtk_wm_set_form(GUIWIN *win, OBJECT *tree, short index) { DEBUG_PRINT(("Setting form %p (%d) for window %p\n", tree, index, win)); win->form = tree; win->form_edit_obj = -1; win->form_focus_obj = -1; win->form_idx = index; } /** Checks if a GUIWIN is overlapped by other windows. */ bool gemtk_wm_has_intersection(GUIWIN *win, GRECT *work) { GRECT area, mywork; bool retval = true; if (work == NULL) { gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &mywork); work = &mywork; } wind_get_grect(win->handle, WF_FIRSTXYWH, &area); while (area.g_w) { //GRECT * ptr = &area; if (RC_WITHIN(work, &area)) { retval = false; } wind_get_grect(win->handle, WF_NEXTXYWH, &area); } return(retval); } /** Execute an toolbar redraw * \param msg specifies the AES message which initiated the redraw, or 0 when * the function was called without AES message context. */ void gemtk_wm_toolbar_redraw(GUIWIN *gw, uint16_t msg, GRECT *clip) { GRECT tb_area, tb_area_ro; gemtk_wm_get_grect(gw, GEMTK_WM_AREA_TOOLBAR, &tb_area_ro); if(clip == NULL) { clip = &tb_area_ro; } tb_area = tb_area_ro; if (rc_intersect(clip, &tb_area)) { gw->toolbar_redraw_func(gw, msg, &tb_area); } } /** Execute FORM redraw */ void gemtk_wm_form_redraw(GUIWIN *gw, GRECT *clip) { GRECT area, area_ro, g; struct gemtk_wm_scroll_info_s *slid; //int new_x, new_y, old_x, old_y; DEBUG_PRINT(("gemtk_wm_form_redraw\n")); // calculate form coordinates, include scrolling: gemtk_wm_get_grect(gw, GEMTK_WM_AREA_CONTENT, &area_ro); slid = gemtk_wm_get_scroll_info(gw); // Update form position: gw->form[gw->form_idx].ob_x = area_ro.g_x - (slid->x_pos * slid->x_unit_px); gw->form[gw->form_idx].ob_y = area_ro.g_y - (slid->y_pos * slid->y_unit_px); if(clip == NULL) { clip = &area_ro; } area = area_ro; /* Walk the AES rectangle list and redraw the visible areas of the window:*/ if(rc_intersect(clip, &area)) { wind_get_grect(gw->handle, WF_FIRSTXYWH, &g); while (g.g_h > 0 || g.g_w > 0) { if(rc_intersect(&area, &g)) { objc_draw(gw->form, gw->form_idx, 8, g.g_x, g.g_y, g.g_w, g.g_h); } wind_get_grect(gw->handle, WF_NEXTXYWH, &g); } } } /** Fill the content area with white color */ void gemtk_wm_clear(GUIWIN *win) { GRECT area, g; short pxy[4]; VdiHdl vh; vh = gemtk_wm_get_vdi_handle(win); if(win->state & GEMTK_WM_STATUS_ICONIFIED) { // also clear the toolbar area when iconified: gemtk_wm_get_grect(win, GEMTK_WM_AREA_WORK, &area); } else { gemtk_wm_get_grect(win, GEMTK_WM_AREA_CONTENT, &area); } vsf_interior(vh, FIS_SOLID); vsf_color(vh, 0); vswr_mode(vh, MD_REPLACE); wind_get_grect(win->handle, WF_FIRSTXYWH, &g); while (g.g_h > 0 || g.g_w > 0) { if(rc_intersect(&area, &g)) { pxy[0] = g.g_x; pxy[1] = g.g_y; pxy[2] = g.g_x+g.g_w-1; pxy[3] = g.g_y+g.g_h-1; v_bar(vh, pxy); } wind_get_grect(win->handle, WF_NEXTXYWH, &g); } }