/* * Copyright 2010 Ole Loots * * 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 . * * Module Description: * * This File implements the NetSurf Browser window, or passed functionality to * the appropriate widget's. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "utils/log.h" #include "desktop/gui.h" #include "desktop/history_core.h" #include "desktop/netsurf.h" #include "desktop/browser.h" #include "desktop/browser_private.h" #include "desktop/mouse.h" #include "desktop/plotters.h" #include "desktop/textinput.h" #include "content/content.h" #include "content/hlcache.h" #include "content/urldb.h" #include "css/css.h" #include "atari/res/netsurf.rsh" #include "atari/gemtk/gemtk.h" #include "atari/ctxmenu.h" #include "atari/gui.h" #include "atari/rootwin.h" #include "atari/misc.h" #include "atari/plot/plot.h" #include "atari/toolbar.h" #include "atari/statusbar.h" #include "atari/search.h" #include "atari/osspec.h" #include "atari/encoding.h" #include "atari/redrawslots.h" #include "atari/toolbar.h" #include "atari/gemtk/gemtk.h" extern struct gui_window *input_window; extern EVMULT_OUT aes_event_out; extern GRECT desk_area; struct rootwin_data_s { struct s_gui_win_root *rootwin; }; /* -------------------------------------------------------------------------- */ /* Static module event handlers */ /* -------------------------------------------------------------------------- */ static void on_redraw(ROOTWIN *rootwin, short msg[8]); static void on_resized(ROOTWIN *rootwin); static void on_file_dropped(ROOTWIN *rootwin, short msg[8]); static short on_window_key_input(ROOTWIN * rootwin, unsigned short nkc); static bool on_content_mouse_click(ROOTWIN *rootwin); static bool on_content_mouse_move(ROOTWIN *rootwin, GRECT *content_area); static void toolbar_redraw_cb(GUIWIN *win, uint16_t msg, GRECT *clip); static bool redraw_active = false; static const struct redraw_context rootwin_rdrw_ctx = { .interactive = true, .background_images = true, .plot = &atari_plotters }; static short handle_event(GUIWIN *win, EVMULT_OUT *ev_out, short msg[8]) { short retval = 0; GRECT content_area; static bool prev_url = false; static short prev_x=0; static short prev_y=0; struct rootwin_data_s * data = gemtk_wm_get_user_data(win); struct gui_window *tmp; if ((ev_out->emo_events & MU_MESAG) != 0) { // handle message //printf("root win msg: %d\n", msg[0]); switch (msg[0]) { case WM_REDRAW: LOG(("WM_REDRAW")); on_redraw(data->rootwin, msg); break; case WM_REPOSED: case WM_SIZED: case WM_MOVED: case WM_FULLED: LOG(("WM_SIZED")); on_resized(data->rootwin); break; case WM_ICONIFY: // TODO: find next active gui window and schedule redraw for that. tmp = window_list; while(tmp != NULL){ if(tmp->root != data->rootwin){ gemtk_wm_send_msg(tmp->root->win, WM_TOPPED, 0, 0, 0, 0); break; } tmp = tmp->next; } break; case WM_TOPPED: case WM_NEWTOP: case WM_UNICONIFY: LOG(("WM_TOPPED")); gui_set_input_gui_window(data->rootwin->active_gui_window); //window_restore_active_gui_window(data->rootwin); // TODO: use something like "restore_active_gui_window_state()" break; case WM_CLOSED: // TODO: this needs to iterate through all gui windows and // check if the rootwin is this window... if (data->rootwin->active_gui_window != NULL) { LOG(("WM_CLOSED initiated destroy for bw %p", data->rootwin->active_gui_window->browser->bw)); browser_window_destroy( data->rootwin->active_gui_window->browser->bw); } break; case AP_DRAGDROP: on_file_dropped(data->rootwin, msg); break; case WM_TOOLBAR: toolbar_mouse_input(data->rootwin->toolbar, msg[4], msg[7]); break; default: break; } } if ((ev_out->emo_events & MU_KEYBD) != 0) { // handle key uint16_t nkc = gem_to_norm( (short)ev_out->emo_kmeta, (short)ev_out->emo_kreturn); retval = on_window_key_input(data->rootwin, nkc); } if ((ev_out->emo_events & MU_BUTTON) != 0) { window_get_grect(data->rootwin, BROWSER_AREA_CONTENT, &content_area); if (POINT_WITHIN(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y, content_area)) { on_content_mouse_click(data->rootwin); } } if ((ev_out->emo_events & (MU_M1)) != 0) { short ghandle = wind_find(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y); if (data->rootwin->aes_handle==ghandle) { // The window found at x,y is an gui_window // and it's the input window. window_get_grect(data->rootwin, BROWSER_AREA_CONTENT, &content_area); if (POINT_WITHIN(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y, content_area)) { on_content_mouse_move(data->rootwin, &content_area); } else { GRECT tb_area; window_get_grect(data->rootwin, BROWSER_AREA_URL_INPUT, &tb_area); if (POINT_WITHIN(ev_out->emo_mouse.p_x, ev_out->emo_mouse.p_y, tb_area)) { gem_set_cursor(&gem_cursors.ibeam); prev_url = true; } else { if(prev_url) { struct gui_window *gw; gw = window_get_active_gui_window(data->rootwin); gem_set_cursor(gw->cursor); prev_url = false; } } } } } return(retval); } /* -------------------------------------------------------------------------- */ /* Module public functions: */ /* -------------------------------------------------------------------------- */ int window_create(struct gui_window * gw, struct browser_window * bw, struct browser_window * clone, unsigned long inflags) { int err = 0; bool tb, sb; int flags; struct rootwin_data_s *data; struct gemtk_wm_scroll_info_s *slid; tb = (inflags & WIDGET_TOOLBAR); sb = (inflags & WIDGET_STATUSBAR); flags = CLOSER | MOVER | NAME | FULLER | SMALLER; if( inflags & WIDGET_SCROLL ) { flags |= (UPARROW | DNARROW | LFARROW | RTARROW | VSLIDE | HSLIDE); } if( inflags & WIDGET_RESIZE ) { flags |= ( SIZER ); } if( inflags & WIDGET_STATUSBAR ) { flags |= ( INFO ); } gw->root = malloc(sizeof(struct s_gui_win_root)); if (gw->root == NULL) return(-1); memset(gw->root, 0, sizeof(struct s_gui_win_root) ); gw->root->title = malloc(atari_sysinfo.aes_max_win_title_len+1); redraw_slots_init(&gw->root->redraw_slots, 8); gw->root->aes_handle = wind_create(flags, 40, 40, desk_area.g_w, desk_area.g_h); if(gw->root->aes_handle<0) { free(gw->root->title); free(gw->root); return( -1 ); } gw->root->win = gemtk_wm_add(gw->root->aes_handle, GEMTK_WM_FLAG_PREPROC_WM | GEMTK_WM_FLAG_RECV_PREPROC_WM, handle_event); data = malloc(sizeof(struct rootwin_data_s)); data->rootwin = gw->root; gemtk_wm_set_user_data(gw->root->win, (void*)data); slid = gemtk_wm_get_scroll_info(gw->root->win); slid->y_unit_px = 32; slid->x_unit_px = 32; /* create */ if(tb) { gw->root->toolbar = toolbar_create(gw->root); assert(gw->root->toolbar); gemtk_wm_set_toolbar(gw->root->win, gw->root->toolbar->form, 0, 0); gemtk_wm_set_toolbar_redraw_func(gw->root->win, toolbar_redraw_cb); } else { gw->root->toolbar = NULL; } /* create browser component: */ gw->browser = (struct s_browser *)malloc( sizeof(struct s_browser)); assert(gw->browser); gw->browser->bw = bw; if(clone) gw->browser->bw->scale = clone->scale; else gw->browser->bw->scale = 1; /* create statusbar component: */ if(sb) { gw->root->statusbar = sb_create( gw ); } else { gw->root->statusbar = NULL; } // Setup some window defaults: wind_set_str(gw->root->aes_handle, WF_NAME, (char*)"NetSurf"); wind_set(gw->root->aes_handle, WF_OPTS, 1, WO0_FULLREDRAW, 0, 0); wind_set(gw->root->aes_handle, WF_OPTS, 1, WO0_NOBLITW, 0, 0); wind_set(gw->root->aes_handle, WF_OPTS, 1, WO0_NOBLITH, 0, 0); if (inflags & WIN_TOP) { window_set_focus(gw->root, BROWSER, gw->browser); } return (err); } void window_unref_gui_window(ROOTWIN *rootwin, struct gui_window *gw) { struct gui_window *w; input_window = NULL; LOG(("window: %p, gui_window: %p", rootwin, gw)); w = window_list; // find the next active tab: while( w != NULL ) { if(w->root == rootwin && w != gw) { LOG(("activating next tab %p", w)); gui_set_input_gui_window(w); break; } w = w->next; } if(input_window == NULL) { // the last gui window for this rootwin was removed: redraw_slots_free(&rootwin->redraw_slots); window_destroy(rootwin); } } int window_destroy(ROOTWIN *rootwin) { int err = 0; struct gui_window *w; assert(rootwin != NULL); LOG(("%p", rootwin)); if (gemtk_wm_get_user_data(rootwin->win) != NULL) { free(gemtk_wm_get_user_data(rootwin->win)); } // make sure we do not destroy windows which have gui_windows attached: w = window_list; while( w != NULL ) { if(w->root == rootwin) { assert(rootwin == NULL); } w = w->next; } if (rootwin->toolbar) toolbar_destroy(rootwin->toolbar); if(rootwin->statusbar) sb_destroy(rootwin->statusbar); if(rootwin->title) free(rootwin->title); gemtk_wm_remove(rootwin->win); wind_close(rootwin->aes_handle); wind_delete(rootwin->aes_handle); free(rootwin); return(err); } void window_open(ROOTWIN *rootwin, struct gui_window *gw, GRECT pos) { GRECT br, g; rootwin->active_gui_window = gw; assert(rootwin->active_gui_window != NULL); wind_open(rootwin->aes_handle, pos.g_x, pos.g_y, pos.g_w, pos.g_h ); wind_set_str(rootwin->aes_handle, WF_NAME, (char *)""); rootwin->active_gui_window->browser->attached = true; if(rootwin->statusbar != NULL) { sb_attach(rootwin->statusbar, rootwin->active_gui_window); } gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_TOOLBAR, &g); toolbar_set_attached(rootwin->toolbar, true); toolbar_set_dimensions(rootwin->toolbar, &g); window_update_back_forward(rootwin); window_set_focus(rootwin, BROWSER, rootwin->active_gui_window->browser); } void window_restore_active_gui_window(ROOTWIN *rootwin) { GRECT tb_area; struct gui_window *gw; LOG(("")); assert(rootwin->active_gui_window); gw = rootwin->active_gui_window; window_set_icon(rootwin, gw->icon); window_set_stauts(rootwin, gw->status); window_set_title(rootwin, gw->title); if (gw->search != NULL) { // TODO: update search session (especially browser window) } toolbar_get_grect(rootwin->toolbar, 0, &tb_area); gemtk_wm_set_toolbar_size(rootwin->win, tb_area.g_h); window_update_back_forward(rootwin); toolbar_set_url(rootwin->toolbar, gw->url); } /* update back forward buttons (see tb_update_buttons (bug) ) */ void window_update_back_forward(struct s_gui_win_root *rootwin) { struct gui_window * active_gw = rootwin->active_gui_window; toolbar_update_buttons(rootwin->toolbar, active_gw->browser->bw, -1); } void window_set_stauts(struct s_gui_win_root *rootwin, char * text) { assert(rootwin != NULL); CMP_STATUSBAR sb = rootwin->statusbar; if( sb == NULL) return; if(text != NULL) sb_set_text(sb, text); else sb_set_text(sb, ""); } void window_set_title(struct s_gui_win_root * rootwin, char *title) { wind_set_str(rootwin->aes_handle, WF_NAME, title); } void window_scroll_by(ROOTWIN *root, int sx, int sy) { int units; GRECT content_area; struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(root->win); if(sx < 0) { sx = 0; } if(sy<0) { sy = 0; } int xunits = sx / slid->x_unit_px; int yunits = sy / slid->y_unit_px; gemtk_wm_scroll(root->win, GEMTK_WM_VSLIDER, yunits - slid->y_pos, false); gemtk_wm_scroll(root->win, GEMTK_WM_HSLIDER, xunits - slid->x_pos, false); gemtk_wm_update_slider(root->win, GEMTK_WM_VH_SLIDER); } /** * Set the dimensions of the scrollable content. * */ void window_set_content_size(ROOTWIN *rootwin, int width, int height) { GRECT area; struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(rootwin->win); window_get_grect(rootwin, BROWSER_AREA_CONTENT, &area); slid->x_units = (width/slid->x_unit_px); slid->y_units = (height/slid->y_unit_px); if(slid->x_units < slid->x_pos) slid->x_pos = 0; if(slid->y_units < slid->y_pos) slid->y_pos = 0; gemtk_wm_update_slider(rootwin->win, GEMTK_WM_VH_SLIDER); } /* set focus to an arbitary element */ void window_set_focus(struct s_gui_win_root *rootwin, enum focus_element_type type, void * element) { struct textarea * ta; assert(rootwin != NULL); if (rootwin->focus.type != type || rootwin->focus.element != element) { LOG(("Set focus: %p (%d)\n", element, type)); rootwin->focus.type = type; rootwin->focus.element = element; switch( type ) { case URL_WIDGET: // TODO: make something like: toolbar_text_select_all(); toolbar_key_input(rootwin->toolbar, (short)(NKF_CTRL | 'A') ); /* ta = toolbar_get_textarea(rootwin->toolbar, URL_INPUT_TEXT_AREA); textarea_keypress(ta, KEY_SELECT_ALL); */ break; case SEARCH_INPUT: gemtk_wm_set_toolbar_edit_obj(rootwin->win, TOOLBAR_TB_SRCH, 0); break; default: break; } } } /* check if the url widget has focus */ bool window_url_widget_has_focus(struct s_gui_win_root *rootwin) { assert(rootwin != NULL); if (rootwin->focus.type == URL_WIDGET) { return true; } return false; } /* check if an arbitary window widget / or frame has the focus */ bool window_widget_has_focus(struct s_gui_win_root *rootwin, enum focus_element_type t, void * element) { assert(rootwin != NULL); if( element == NULL ) { return((rootwin->focus.type == t)); } return((element == rootwin->focus.element && t == rootwin->focus.type)); } void window_set_icon(ROOTWIN *rootwin, struct bitmap * bmp ) { rootwin->icon = bmp; /* redraw window when it is iconyfied: */ if (rootwin->icon != NULL) { short info, dummy; if (gemtk_wm_get_state(rootwin->win) & GEMTK_WM_STATUS_ICONIFIED) { window_redraw_favicon(rootwin, NULL); } } } void window_set_active_gui_window(ROOTWIN *rootwin, struct gui_window *gw) { struct gui_window *old_gw = rootwin->active_gui_window; LOG(("")); if (rootwin->active_gui_window != NULL) { if(rootwin->active_gui_window == gw) { LOG(("nothing to do...")); return; } } // TODO: when the window isn't on top, initiate WM_TOPPED. rootwin->active_gui_window = gw; if (old_gw != NULL) { LOG(("restoring window...")); window_restore_active_gui_window(rootwin); } } struct gui_window * window_get_active_gui_window(ROOTWIN * rootwin) { return(rootwin->active_gui_window); } void window_get_scroll(ROOTWIN *rootwin, int *x, int *y) { struct gemtk_wm_scroll_info_s *slid; slid = gemtk_wm_get_scroll_info(rootwin->win); *x = slid->x_pos * slid->x_unit_px; *y = slid->y_pos * slid->y_unit_px; } void window_get_grect(ROOTWIN *rootwin, enum browser_area_e which, GRECT *d) { d->g_x = 0; d->g_y = 0; d->g_w = 0; d->g_h = 0; if (which == BROWSER_AREA_TOOLBAR) { // gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_TOOLBAR, d); toolbar_get_grect(rootwin->toolbar, 0, d); } else if (which == BROWSER_AREA_CONTENT) { GRECT tb_area; gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_WORK, d); toolbar_get_grect(rootwin->toolbar, 0, &tb_area); d->g_y += tb_area.g_h; d->g_h -= tb_area.g_h; } else if (which == BROWSER_AREA_URL_INPUT) { toolbar_get_grect(rootwin->toolbar, TOOLBAR_AREA_URL, d); } else if (which == BROWSER_AREA_SEARCH) { // todo: check if search is visible toolbar_get_grect(rootwin->toolbar, TOOLBAR_AREA_SEARCH, d); } else { } // sanitize the results if (d->g_h < 0) { d->g_h = 0; } if (d->g_w < 0) { d->g_w = 0; } //printf("window_get_grect %d:", which); //dbg_grect("", d); } void window_open_search(ROOTWIN *rootwin, bool reformat) { struct browser_window *bw; struct gui_window *gw; GRECT area; OBJECT *obj; LOG(("")); gw = rootwin->active_gui_window; bw = gw->browser->bw; obj = toolbar_get_form(rootwin->toolbar); if (gw->search == NULL) { gw->search = nsatari_search_session_create(obj, bw); } toolbar_set_visible(rootwin->toolbar, TOOLBAR_AREA_SEARCH, true); window_get_grect(rootwin, BROWSER_AREA_TOOLBAR, &area); gemtk_wm_set_toolbar_size(rootwin->win, area.g_h); window_get_grect(rootwin, BROWSER_AREA_SEARCH, &area); window_schedule_redraw_grect(rootwin, &area); window_process_redraws(rootwin); window_set_focus(rootwin, SEARCH_INPUT, NULL); window_get_grect(rootwin, BROWSER_AREA_CONTENT, &area); if (reformat) { browser_window_reformat(bw, false, area.g_w, area.g_h); } } void window_close_search(ROOTWIN *rootwin) { struct browser_window *bw; struct gui_window *gw; GRECT area; OBJECT *obj; gw = rootwin->active_gui_window; bw = gw->browser->bw; obj = gemtk_obj_get_tree(TOOLBAR); if (gw->search != NULL) { nsatari_search_session_destroy(gw->search); gw->search = NULL; } toolbar_set_visible(rootwin->toolbar, TOOLBAR_AREA_SEARCH, false); window_get_grect(rootwin, BROWSER_AREA_TOOLBAR, &area); gemtk_wm_set_toolbar_size(rootwin->win, area.g_h); window_get_grect(rootwin, BROWSER_AREA_CONTENT, &area); browser_window_reformat(bw, false, area.g_w, area.g_h); } /** * Redraw the favicon */ void window_redraw_favicon(ROOTWIN *rootwin, GRECT *clip) { GRECT work; assert(rootwin); //printf("window_redraw_favicon: root: %p, win: %p\n", rootwin, rootwin->win); gemtk_wm_clear(rootwin->win); gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_WORK, &work); if (clip == NULL) { clip = &work; } else { if(!rc_intersect(&work, clip)) { return; } } if (rootwin->icon == NULL) { //printf("window_redraw_favicon OBJCTREE\n"); OBJECT * tree = gemtk_obj_get_tree(ICONIFY); tree->ob_x = work.g_x; tree->ob_y = work.g_y; tree->ob_width = work.g_w; tree->ob_height = work.g_h; objc_draw(tree, 0, 8, clip->g_x, clip->g_y, clip->g_w, clip->g_h); } else { // TODO: consider the clipping rectangle printf("window_redraw_favicon image %p\n", rootwin->icon); struct rect work_clip = { 0,0,work.g_w,work.g_h }; int xoff=0; if (work.g_w > work.g_h) { xoff = ((work.g_w-work.g_h)/2); work.g_w = work.g_h; } plot_set_dimensions( work.g_x+xoff, work.g_y, work.g_w, work.g_h); plot_clip(&work_clip); atari_plotters.bitmap(0, 0, work.g_w, work.g_h, rootwin->icon, 0xffffff, 0); } } /*** * Schedule an redraw area, redraw requests during redraw are * not optimized (merged) into other areas, so that the redraw * functions can spot the change. * */ void window_schedule_redraw_grect(ROOTWIN *rootwin, GRECT *area) { GRECT work; //dbg_grect("window_schedule_redraw_grect input ", area); gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_WORK, &work); if(!rc_intersect(area, &work)) return; //dbg_grect("window_schedule_redraw_grect intersection ", &work); redraw_slot_schedule_grect(&rootwin->redraw_slots, &work, redraw_active); } static void window_redraw_content(ROOTWIN *rootwin, GRECT *content_area, GRECT *clip, struct gemtk_wm_scroll_info_s * slid, struct browser_window *bw) { struct rect redraw_area; GRECT content_area_rel; if(bw->window->browser->reformat_pending) { browser_window_reformat(bw, true, content_area->g_w, content_area->g_h); bw->window->browser->reformat_pending = false; //return; } //dbg_grect("browser redraw, content area", content_area); //dbg_grect("browser redraw, content clip", clip); plot_set_dimensions(content_area->g_x, content_area->g_y, content_area->g_w, content_area->g_h); /* first, we make the coords relative to the content area: */ content_area_rel.g_x = clip->g_x - content_area->g_x; content_area_rel.g_y = clip->g_y - content_area->g_y; content_area_rel.g_w = clip->g_w; content_area_rel.g_h = clip->g_h; if (content_area_rel.g_x < 0) { content_area_rel.g_w += content_area_rel.g_x; content_area_rel.g_x = 0; } if (content_area_rel.g_y < 0) { content_area_rel.g_h += content_area_rel.g_y; content_area_rel.g_y = 0; } //dbg_grect("browser redraw, relative plot coords:", &content_area_rel); redraw_area.x0 = content_area_rel.g_x; redraw_area.y0 = content_area_rel.g_y; redraw_area.x1 = content_area_rel.g_x + content_area_rel.g_w; redraw_area.y1 = content_area_rel.g_y + content_area_rel.g_h; plot_clip(&redraw_area); //dbg_rect("rdrw area", &redraw_area); browser_window_redraw( bw, -(slid->x_pos*slid->x_unit_px), -(slid->y_pos*slid->y_unit_px), &redraw_area, &rootwin_rdrw_ctx); } void window_place_caret(ROOTWIN *rootwin, short mode, int content_x, int content_y, int h, GRECT *work) { struct s_caret *caret = &rootwin->caret; VdiHdl vh = gemtk_wm_get_vdi_handle(rootwin->win); short pxy[8]; GRECT mywork, caret_pos; MFDB screen; int i, scroll_x, scroll_y; uint16_t *fd_addr; struct gemtk_wm_scroll_info_s *slid; short colors[2] = {G_BLACK, G_WHITE}; bool render_required = false; // avoid duplicate draw of the caret: if (mode == 1 &&(caret->state&CARET_STATE_VISIBLE)!=0) { if (caret->dimensions.g_x == content_x && caret->dimensions.g_y == content_y && caret->dimensions.g_h == h) { return; } } if(work == NULL) { window_get_grect(rootwin, BROWSER_AREA_CONTENT, &mywork); work = &mywork; } slid = gemtk_wm_get_scroll_info(rootwin->win); scroll_x = slid->x_pos * slid->x_unit_px; scroll_y = slid->y_pos * slid->y_unit_px; init_mfdb(0, 1, h, 0, &screen); // enable clipping: pxy[0] = work->g_x; pxy[1] = work->g_y; pxy[2] = pxy[0] + work->g_w - 1; pxy[3] = pxy[1] + work->g_h - 1; vs_clip(vh, 1, pxy); // when the caret is visible, undraw it: if (caret->symbol.fd_addr != NULL && (caret->state&CARET_STATE_VISIBLE)!=0) { caret_pos.g_x = work->g_x + (caret->dimensions.g_x - scroll_x); caret_pos.g_y = work->g_y + (caret->dimensions.g_y - scroll_y); caret_pos.g_w = caret->dimensions.g_w; caret_pos.g_h = caret->dimensions.g_h; if (rc_intersect(work, &caret_pos)) { pxy[0] = 0; pxy[1] = 0; pxy[2] = caret->dimensions.g_w-1; pxy[3] = caret->dimensions.g_h-1; pxy[4] = caret_pos.g_x; pxy[5] = caret_pos.g_y; pxy[6] = pxy[4] + caret_pos.g_w-1; pxy[7] = pxy[5] + caret_pos.g_h-1; vrt_cpyfm(vh, MD_XOR, pxy, &caret->symbol, &screen, colors); } } if (mode == 0) { // update state: caret->state &= ~CARET_STATE_VISIBLE; goto exit; } // when the caret isn't allocated, create it: if (caret->symbol.fd_addr == NULL) { caret->fd_size = init_mfdb(1, 16, h, MFDB_FLAG_ZEROMEM, &caret->symbol); render_required = true; } else { // the caret may need more memory: if (caret->dimensions.g_h < h) { caret->fd_size = init_mfdb(1, 16, h, MFDB_FLAG_NOALLOC, &caret->symbol); realloc(caret->symbol.fd_addr, caret->fd_size); render_required = true; } } // set new caret position: caret->dimensions.g_x = content_x; caret->dimensions.g_y = content_y; caret->dimensions.g_w = 1; caret->dimensions.g_h = h; // draw the caret into the mfdb buffer: if (render_required) { assert(caret->symbol.fd_nplanes == 1); assert(caret->symbol.fd_w == 16); // draw an vertical line into the mfdb buffer fd_addr = (uint16_t*)caret->symbol.fd_addr; for(i = 0; isymbol.fd_h; i++) { fd_addr[i] = 0xFFFF; } } // convert content coords to screen coords: caret_pos.g_x = work->g_x + (content_x - scroll_x); caret_pos.g_y = work->g_y + (content_y - scroll_y); caret_pos.g_w = caret->dimensions.g_w; caret_pos.g_h = caret->dimensions.g_h; if (rc_intersect(work, &caret_pos) && redraw_active == false) { pxy[0] = 0; pxy[1] = 0; pxy[2] = caret->dimensions.g_w-1; pxy[3] = caret->dimensions.g_h-1; pxy[4] = caret_pos.g_x; pxy[5] = caret_pos.g_y; pxy[6] = pxy[4] + caret_pos.g_w-1; pxy[7] = pxy[5] + caret_pos.g_h-1; //dbg_pxy("caret screen coords (md_repl)", &pxy[4]); // draw caret to screen coords: vrt_cpyfm(vh, /*MD_REPLACE*/ MD_XOR, pxy, &caret->symbol, &screen, colors); // update state: caret->state |= CARET_STATE_VISIBLE; } exit: // disable clipping: vs_clip(gemtk_wm_get_vdi_handle(rootwin->win), 0, pxy); } void window_process_redraws(ROOTWIN * rootwin) { GRECT work, visible_ro, tb_area, content_area; short i; short scroll_x=0, scroll_y=0; bool toolbar_rdrw_required; bool caret_rdrw_required = false; struct gemtk_wm_scroll_info_s *slid =NULL; int caret_h = 0; struct s_caret *caret = &rootwin->caret; redraw_active = true; toolbar_get_grect(rootwin->toolbar, 0, &tb_area); //gemtk_wm_set_toolbar_size(rootwin->win, tb_area.g_h); window_get_grect(rootwin, BROWSER_AREA_CONTENT, &content_area); //dbg_grect("content area", &content_area); //dbg_grect("window_process_redraws toolbar area", &tb_area); while(plot_lock() == false); if (((rootwin->caret.state & CARET_STATE_ENABLED)!=0) && rootwin->caret.dimensions.g_h > 0) { // hide caret: window_place_caret(rootwin, 0, -1, -1, -1, &content_area); } /* short pxy_clip[4]; pxy_clip[0] = tb_area.g_x; pxy_clip[0] = tb_area.g_y; pxy_clip[0] = pxy_clip[0] + tb_area.g_w + content_area.g_w - 1; pxy_clip[0] = pxy_clip[1] + tb_area.g_h + content_area.g_h - 1; vs_clip(gemtk_wm_get_vdi_handle(rootwin->win), 1, pxy_clip); //gemtk_wm_clear(rootwin->win); */ wind_get_grect(rootwin->aes_handle, WF_FIRSTXYWH, &visible_ro); while (visible_ro.g_w > 0 && visible_ro.g_h > 0) { // TODO: optimze the rectangle list - // remove rectangles which were completly inside the visible area. // that way we don't have to loop over again... for(i=0; iredraw_slots.areas_used; i++) { GRECT rdrw_area_ro = { rootwin->redraw_slots.areas[i].x0, rootwin->redraw_slots.areas[i].y0, rootwin->redraw_slots.areas[i].x1 - rootwin->redraw_slots.areas[i].x0, rootwin->redraw_slots.areas[i].y1 - rootwin->redraw_slots.areas[i].y0 }; rc_intersect(&visible_ro, &rdrw_area_ro); GRECT rdrw_area = rdrw_area_ro; if (rc_intersect(&tb_area, &rdrw_area)) { toolbar_redraw(rootwin->toolbar, &rdrw_area); } rdrw_area = rdrw_area_ro; if (rc_intersect(&content_area, &rdrw_area)) { if(slid == NULL) { slid = gemtk_wm_get_scroll_info(rootwin->win); scroll_x = slid->x_pos * slid->x_unit_px; scroll_y = slid->y_pos * slid->y_unit_px; } window_redraw_content(rootwin, &content_area, &rdrw_area, slid, rootwin->active_gui_window->browser->bw); if (((rootwin->caret.state & CARET_STATE_ENABLED)!=0)) { GRECT caret_pos; caret_pos.g_x = content_area.g_x + (caret->dimensions.g_x - scroll_x); caret_pos.g_y = content_area.g_y + (caret->dimensions.g_y - scroll_y); caret_pos.g_w = caret->dimensions.g_w; caret_pos.g_h = caret->dimensions.g_h; if (gemtk_rc_intersect_ro(&caret_pos, &content_area)) { caret_rdrw_required = true; } } } } wind_get_grect(rootwin->aes_handle, WF_NEXTXYWH, &visible_ro); } // disable clipping: //vs_clip(gemtk_wm_get_vdi_handle(rootwin->win), 0, pxy_clip); if (caret_rdrw_required && ((rootwin->caret.state & CARET_STATE_ENABLED)!=0)) { // force redraw of caret: caret_h = rootwin->caret.dimensions.g_h; rootwin->caret.dimensions.g_h = 0; redraw_active = false; window_place_caret(rootwin, 1, rootwin->caret.dimensions.g_x, rootwin->caret.dimensions.g_y, caret_h, &content_area); } rootwin->redraw_slots.areas_used = 0; redraw_active = false; plot_unlock(); } /* -------------------------------------------------------------------------- */ /* Event Handlers: */ /* -------------------------------------------------------------------------- */ static bool on_content_mouse_move(ROOTWIN *rootwin, GRECT *content_area) { int mx, my, sx, sy; struct gemtk_wm_scroll_info_s *slid; struct gui_window *gw; struct browser_window *bw; // make relative mouse coords: mx = aes_event_out.emo_mouse.p_x - content_area->g_x; my = aes_event_out.emo_mouse.p_y - content_area->g_y; slid = gemtk_wm_get_scroll_info(rootwin->win); gw = window_get_active_gui_window(rootwin); bw = gw->browser->bw; // calculate scroll pos. in pixel: sx = slid->x_pos * slid->x_unit_px; sy = slid->y_pos * slid->y_unit_px; browser_window_mouse_track(bw, 0, mx + sx, my + sy); } static bool on_content_mouse_click(ROOTWIN *rootwin) { short dummy, mbut, mx, my; GRECT cwork; browser_mouse_state bmstate = 0; struct gui_window *gw; struct gemtk_wm_scroll_info_s *slid; gw = window_get_active_gui_window(rootwin); if(input_window != gw) { gui_set_input_gui_window(gw); } window_set_focus(gw->root, BROWSER, (void*)gw->browser ); window_get_grect(gw->root, BROWSER_AREA_CONTENT, &cwork); /* convert screen coords to component coords: */ mx = aes_event_out.emo_mouse.p_x - cwork.g_x; my = aes_event_out.emo_mouse.p_y - cwork.g_y; //printf("content click at %d,%d\n", mx, my); /* Translate GEM key state to netsurf mouse modifier */ if ( aes_event_out.emo_kmeta & (K_RSHIFT | K_LSHIFT)) { bmstate |= BROWSER_MOUSE_MOD_1; } else { bmstate &= ~(BROWSER_MOUSE_MOD_1); } if ( (aes_event_out.emo_kmeta & K_CTRL) ) { bmstate |= BROWSER_MOUSE_MOD_2; } else { bmstate &= ~(BROWSER_MOUSE_MOD_2); } if ( (aes_event_out.emo_kmeta & K_ALT) ) { bmstate |= BROWSER_MOUSE_MOD_3; } else { bmstate &= ~(BROWSER_MOUSE_MOD_3); } /* convert component coords to scrolled content coords: */ slid = gemtk_wm_get_scroll_info(rootwin->win); int sx_origin = mx; int sy_origin = my; short rel_cur_x, rel_cur_y; short prev_x=sx_origin, prev_y=sy_origin; bool dragmode = false; /* Detect left mouse button state and compare with event state: */ graf_mkstate(&rel_cur_x, &rel_cur_y, &mbut, &dummy); if( (mbut & 1) && (aes_event_out.emo_mbutton & 1) ) { /* Mouse still pressed, report drag */ rel_cur_x = (rel_cur_x - cwork.g_x); rel_cur_y = (rel_cur_y - cwork.g_y); browser_window_mouse_click( gw->browser->bw, BROWSER_MOUSE_DRAG_ON|BROWSER_MOUSE_DRAG_1, rel_cur_x + slid->x_pos * slid->x_unit_px, rel_cur_y + slid->y_pos * slid->y_unit_px); do { // only consider movements of 5px or more as drag...: if( abs(prev_x-rel_cur_x) > 5 || abs(prev_y-rel_cur_y) > 5 ) { browser_window_mouse_track( gw->browser->bw, BROWSER_MOUSE_DRAG_ON|BROWSER_MOUSE_DRAG_1, rel_cur_x + slid->x_pos * slid->x_unit_px, rel_cur_y + slid->y_pos * slid->y_unit_px); prev_x = rel_cur_x; prev_y = rel_cur_y; dragmode = true; } else { if( dragmode == false ) { browser_window_mouse_track( gw->browser->bw,BROWSER_MOUSE_PRESS_1, rel_cur_x + slid->x_pos * slid->x_unit_px, rel_cur_y + slid->y_pos * slid->y_unit_px); } } // we may need to process scrolling: // TODO: this doesn't work, because gemtk schedules redraw via // AES window messages but we do not process them right here... if (rootwin->redraw_slots.areas_used > 0) { window_process_redraws(rootwin); } evnt_timer(150); graf_mkstate(&rel_cur_x, &rel_cur_y, &mbut, &dummy); rel_cur_x = (rel_cur_x - cwork.g_x); rel_cur_y = (rel_cur_y - cwork.g_y); } while( mbut & 1 ); browser_window_mouse_track(gw->browser->bw, 0, rel_cur_x + slid->x_pos * slid->x_unit_px, rel_cur_y + slid->y_pos * slid->y_unit_px); } else { /* Right button pressed? */ if ((aes_event_out.emo_mbutton & 2 ) ) { context_popup(gw, aes_event_out.emo_mouse.p_x, aes_event_out.emo_mouse.p_y); } else { browser_window_mouse_click(gw->browser->bw, bmstate|BROWSER_MOUSE_PRESS_1, sx_origin + slid->x_pos * slid->x_unit_px, sy_origin + slid->y_pos * slid->y_unit_px); browser_window_mouse_click(gw->browser->bw, bmstate|BROWSER_MOUSE_CLICK_1, sx_origin + slid->x_pos * slid->x_unit_px, sy_origin + slid->y_pos * slid->y_unit_px); } } if (rootwin->redraw_slots.areas_used > 0) { window_process_redraws(rootwin); } } /* Report keypress to browser component. parameter: - unsigned short nkc ( CFLIB normalised key code ) */ static bool on_content_keypress(struct browser_window *bw, unsigned short nkc) { bool r = false; unsigned char ascii = (nkc & 0xFF); long ucs4; long ik = nkc_to_input_key( nkc, &ucs4 ); // pass event to specific control? if (ik == 0) { if (ascii >= 9) { r = browser_window_key_press(bw, ucs4); } } else { r = browser_window_key_press(bw, ik); if (r == false) { GRECT g; GUIWIN * w = bw->window->root->win; window_get_grect(bw->window->root, BROWSER_AREA_CONTENT, &g); struct gemtk_wm_scroll_info_s *slid = gemtk_wm_get_scroll_info(w); switch( ik ) { case KEY_LINE_START: gemtk_wm_scroll(w, GEMTK_WM_HSLIDER, -(g.g_w/slid->x_unit_px), false); break; case KEY_LINE_END: gemtk_wm_scroll(w, GEMTK_WM_HSLIDER, (g.g_w/slid->x_unit_px), false); break; case KEY_PAGE_UP: gemtk_wm_scroll(w, GEMTK_WM_VSLIDER, (g.g_h/slid->y_unit_px), false); break; case KEY_PAGE_DOWN: gemtk_wm_scroll(w, GEMTK_WM_VSLIDER, (g.g_h/slid->y_unit_px), false); break; case KEY_RIGHT: gemtk_wm_scroll(w, GEMTK_WM_HSLIDER, -1, false); break; case KEY_LEFT: gemtk_wm_scroll(w, GEMTK_WM_HSLIDER, 1, false); break; case KEY_UP: gemtk_wm_scroll(w, GEMTK_WM_VSLIDER, -1, false); break; case KEY_DOWN: gemtk_wm_scroll(w, GEMTK_WM_VSLIDER, 1, false); break; default: break; } gemtk_wm_update_slider(w, GEMTK_WM_VSLIDER|GEMTK_WM_HSLIDER); } } return(r); } static short on_window_key_input(ROOTWIN *rootwin, unsigned short nkc) { bool done = false; struct gui_window * gw = window_get_active_gui_window(rootwin); struct gui_window * gw_tmp; if( gw == NULL ) return(false); if(window_url_widget_has_focus((void*)gw->root)) { /* make sure we report for the root window and report...: */ done = toolbar_key_input(gw->root->toolbar, nkc); } else { gw_tmp = window_list; /* search for active browser component: */ while( gw_tmp != NULL && done == false ) { /* todo: only handle when input_window == ontop */ if( window_widget_has_focus(input_window->root, BROWSER, (void*)gw_tmp->browser)) { done = on_content_keypress(gw_tmp->browser->bw, nkc); break; } else { gw_tmp = gw_tmp->next; } } } return((done==true) ? 1 : 0); } static void on_redraw(ROOTWIN *rootwin, short msg[8]) { short handle; GRECT clip = {msg[4], msg[5], msg[6], msg[7]}; //dbg_grect("on_redraw", &clip); if(gemtk_wm_get_state(rootwin->win) & GEMTK_WM_STATUS_ICONIFIED) { GRECT clip = {msg[4], msg[5], msg[6], msg[7]}; window_redraw_favicon(rootwin, &clip); } else { window_schedule_redraw_grect(rootwin, &clip); } } static void on_resized(ROOTWIN *rootwin) { GRECT g, work; OBJECT *toolbar; struct gui_window *gw; gw = window_get_active_gui_window(rootwin); //printf("resized...\n"); assert(gw != NULL); if(gw == NULL) return; wind_get_grect(rootwin->aes_handle, WF_CURRXYWH, &g); gemtk_wm_get_grect(rootwin->win, GEMTK_WM_AREA_WORK, &work); if (rootwin->loc.g_w != g.g_w || rootwin->loc.g_h != g.g_h) { /* resized */ toolbar_set_width(rootwin->toolbar, work.g_w); if ( gw->browser->bw->current_content != NULL ) { /* Reformat will happen when redraw is processed: */ // TODO: call reformat directly, this was introduced because // of bad AES knowledge, it's ok to call it directly here... //printf("reformat......\n"); rootwin->active_gui_window->browser->reformat_pending = true; } } if (rootwin->loc.g_x != g.g_x || rootwin->loc.g_y != g.g_y) { /* moved */ toolbar_set_origin(rootwin->toolbar, work.g_x, work.g_y); } rootwin->loc = g; } static void on_file_dropped(ROOTWIN *rootwin, short msg[8]) { char file[DD_NAMEMAX]; char name[DD_NAMEMAX]; char *buff=NULL; int dd_hdl; int dd_msg; /* pipe-handle */ long size; char ext[32]; short mx,my,bmstat,mkstat; struct gui_window *gw; graf_mkstate(&mx, &my, &bmstat, &mkstat); gw = window_get_active_gui_window(rootwin); if( gw == NULL ) return; if(gemtk_wm_get_state(rootwin->win) & GEMTK_WM_STATUS_ICONIFIED) return; dd_hdl = gemtk_dd_open( msg[7], DD_OK); if( dd_hdl<0) return; /* pipe not open */ memset( ext, 0, 32); strcpy( ext, "ARGS"); dd_msg = gemtk_dd_sexts( dd_hdl, ext); if( dd_msg<0) goto error; dd_msg = gemtk_dd_rtry( dd_hdl, (char*)&name[0], (char*)&file[0], (char*)&ext[0], &size); if( size+1 >= PATH_MAX ) goto error; if( !strncmp( ext, "ARGS", 4) && dd_msg > 0) { gemtk_dd_reply(dd_hdl, DD_OK); buff = (char*)malloc(sizeof(char)*(size+1)); if (buff != NULL) { if (Fread(dd_hdl, size, buff ) == size) buff[size] = 0; LOG(("file: %s, ext: %s, size: %d dropped at: %d,%d\n", (char*)buff, (char*)&ext, size, mx, my )); { GRECT content_area; struct browser_window * bw = gw->browser->bw; window_get_grect(rootwin, BROWSER_AREA_CONTENT, &content_area); mx = mx - content_area.g_x; my = my - content_area.g_y; if( (mx < 0 || mx > content_area.g_w) || (my < 0 || my > content_area.g_h) ) return; utf8_convert_ret ret; char *utf8_fn; int sx, sy; ret = utf8_from_local_encoding(buff, 0, &utf8_fn); if (ret != UTF8_CONVERT_OK) { free(buff); /* A bad encoding should never happen */ LOG(("utf8_from_local_encoding failed")); assert(ret != UTF8_CONVERT_BADENC); /* no memory */ return; } gui_window_get_scroll(gw, &sx, &sy); browser_window_drop_file_at_point( gw->browser->bw, mx+sx, my+sy, utf8_fn ); free(utf8_fn); free(buff); } } } error: gemtk_dd_close( dd_hdl); } static void toolbar_redraw_cb(GUIWIN *win, uint16_t msg, GRECT *clip) { struct rootwin_data_s * ud; if (msg != WM_REDRAW) { ud = gemtk_wm_get_user_data(win); assert(ud); toolbar_redraw(ud->rootwin->toolbar, clip); } }