/* * 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 . */ #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/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 "render/box.h" #include "render/form.h" #include "atari/gui.h" #include "atari/browser_win.h" #include "atari/browser.h" #include "atari/misc.h" #include "atari/global_evnt.h" #include "atari/res/netsurf.rsh" #include "atari/browser.h" #include "atari/toolbar.h" #include "atari/statusbar.h" #include "atari/plot/plotter.h" #include "atari/dragdrop.h" #include "atari/search.h" #include "atari/osspec.h" bool cfg_rt_resize = false; bool cfg_rt_move = false; extern void * h_gem_rsrc; extern struct gui_window *input_window; extern GEM_PLOTTER plotter; extern int mouse_click_time[3]; extern int mouse_hold_start[3]; extern browser_mouse_state bmstate; extern short last_drag_x; extern short last_drag_y; void __CDECL std_szd( WINDOW * win, short buff[8], void * ); void __CDECL std_mvd( WINDOW * win, short buff[8], void * ); /* -------------------------------------------------------------------------- */ /* Module public functions: */ /* -------------------------------------------------------------------------- */ static void __CDECL evnt_window_arrowed( WINDOW *win, short buff[8], void *data ) { bool abs = false; LGRECT cwork; int value = BROWSER_SCROLL_SVAL; if( input_window == NULL ) { return; } browser_get_rect( input_window, BR_CONTENT, &cwork ); switch( buff[4] ) { case WA_UPPAGE: case WA_DNPAGE: value = cwork.g_h; break; case WA_LFPAGE: case WA_RTPAGE: value = cwork.g_w; break; default: break; } browser_scroll( input_window, buff[4], value, abs ); } /* this gets called at end of gui poll to track the mouse state and finally checks for released buttons. */ static void window_track_mouse_state( LGRECT * bwrect, bool within, short mx, short my, short mbut, short mkstate ){ int i = 0; int nx, ny; struct gui_window * gw = input_window; if( !gw ) { bmstate = 0; mouse_hold_start[0] = 0; mouse_hold_start[1] = 0; return; } /* todo: creat function find_browser_window( mx, my ) */ nx = (mx - bwrect->g_x + gw->browser->scroll.current.x); ny = (my - bwrect->g_y + gw->browser->scroll.current.y); if( mkstate & (K_RSHIFT | K_LSHIFT) ){ bmstate |= BROWSER_MOUSE_MOD_1; } else { bmstate &= ~(BROWSER_MOUSE_MOD_1); } if( (mkstate & K_CTRL) ){ bmstate |= BROWSER_MOUSE_MOD_2; } else { bmstate &= ~(BROWSER_MOUSE_MOD_2); } if( (mkstate & K_ALT) ){ bmstate |= BROWSER_MOUSE_MOD_3; } else { bmstate &= ~(BROWSER_MOUSE_MOD_3); } if( !(mbut&1) && !(mbut&2) ) { if(bmstate & BROWSER_MOUSE_DRAG_ON ) bmstate &= ~( BROWSER_MOUSE_DRAG_ON ); } /* todo: if we need right button click, increase loop count */ for( i = 1; i<2; i++ ) { if( !(mbut & i) ) { if( mouse_hold_start[i-1] > 0 ) { mouse_hold_start[i-1] = 0; /* TODO: not just use the input window browser, find the right one by component! */ if( i==1 ) { LOG(("Drag for %d ended", i)); bmstate &= ~( BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_DRAG_1 ) ; if( within ) { browser_window_mouse_drag_end( gw->browser->bw, 0, nx, ny ); } } if( i==2 ) { bmstate &= ~( BROWSER_MOUSE_HOLDING_2 | BROWSER_MOUSE_DRAG_2 ) ; LOG(("Drag for %d ended", i)); if( within ) { browser_window_mouse_drag_end( gw->browser->bw, 0, nx, ny ); } } } } } } static void __CDECL evnt_window_m1( WINDOW * win, short buff[8]) { struct gui_window * gw = input_window; static bool prev_url = false; static bool prev_sb = false; short mx, my, mbut, mkstate; bool a = false; /* flags if mouse is within controls or browser canvas */ bool within = false; LGRECT urlbox, bwbox, sbbox; int nx, ny; /* relative mouse position */ if( gw == NULL) return; graf_mkstate(&mx, &my, &mbut, &mkstate); browser_get_rect( gw, BR_CONTENT, &bwbox ); if( gw->root->toolbar ) mt_CompGetLGrect(&app, gw->root->toolbar->url.comp, WF_WORKXYWH, &urlbox); if( gw->root->statusbar ) mt_CompGetLGrect(&app, gw->root->statusbar->comp, WF_WORKXYWH, &sbbox); if( mx > bwbox.g_x && mx < bwbox.g_x + bwbox.g_w && my > bwbox.g_y && my < bwbox.g_y + bwbox.g_h ){ within = true; } if( evnt.m1_flag == MO_LEAVE ) { if( MOUSE_IS_DRAGGING() ){ window_track_mouse_state( &bwbox, within, mx, my, mbut, mkstate ); } if( gw->root->toolbar && within == false ) { if( (mx > urlbox.g_x && mx < urlbox.g_x + urlbox.g_w ) && (my > urlbox.g_y && my < + urlbox.g_y + urlbox.g_h )) { gem_set_cursor( &gem_cursors.ibeam ); prev_url = a = true; } } if( gw->root->statusbar && within == false /* && a == false */ ) { if( mx >= sbbox.g_x + (sbbox.g_w-MOVER_WH) && mx <= sbbox.g_x + sbbox.g_w && my >= sbbox.g_y + (sbbox.g_h-MOVER_WH) && my <= sbbox.g_y + sbbox.g_h ) { /* mouse within sizer box ( bottom right ) */ prev_sb = a = true; gem_set_cursor( &gem_cursors.sizenwse ); } } if( !a ) { if( prev_url || prev_sb ) { gem_set_cursor( &gem_cursors.arrow ); prev_url = false; prev_sb = false; } /* report mouse move in the browser window */ if( within ){ nx = mx - bwbox.g_x; ny = my - bwbox.g_y; if( ( abs(mx-last_drag_x)>5 || abs(mx-last_drag_y)>5 ) || !MOUSE_IS_DRAGGING() ){ browser_window_mouse_track( input_window->browser->bw, bmstate, nx + gw->browser->scroll.current.x, ny + gw->browser->scroll.current.y ); if( MOUSE_IS_DRAGGING() ){ last_drag_x = mx; last_drag_y = my; } } } } } else { /* set input window? */ } } int window_create( struct gui_window * gw, struct browser_window * bw, unsigned long inflags) { short buff[8]; OBJECT * tbtree; int err = 0; bool tb, sb; tb = (inflags & WIDGET_TOOLBAR ); sb = (inflags & WIDGET_STATUSBAR); short w,h, wx, wy, wh, ww; int flags = CLOSER | MOVER | NAME | FULLER | SMALLER ; gw->parent = NULL; 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); gw->root->handle = WindCreate( flags,40, 40, app.w, app.h ); if( gw->root->handle == NULL ) { free( gw->root->title ); free( gw->root ); return( -1 ); } gw->root->cmproot = mt_CompCreate(&app, CLT_VERTICAL, 1, 1); WindSetPtr( gw->root->handle, WF_COMPONENT, gw->root->cmproot, NULL); if( tb ) { gw->root->toolbar = tb_create( gw ); assert( gw->root->toolbar ); mt_CompAttach( &app, gw->root->cmproot, gw->root->toolbar->comp ); } else { gw->root->toolbar = NULL; } gw->browser = browser_create( gw, bw, NULL, BT_ROOT, CLT_HORIZONTAL, 1, 1 ); mt_CompAttach( &app, gw->root->cmproot, gw->browser->comp ); if( sb ) { gw->root->statusbar = sb_create( gw ); mt_CompAttach( &app, gw->root->cmproot, gw->root->statusbar->comp ); } else { gw->root->statusbar = NULL; } WindSetStr(gw->root->handle, WF_ICONTITLE, (char*)"NetSurf"); /* Event Handlers: */ EvntDataAttach( gw->root->handle, WM_CLOSED, evnt_window_close, NULL ); /* capture resize/move events so we can handle that manually */ if( !cfg_rt_resize ) { EvntAttach( gw->root->handle, WM_SIZED, evnt_window_resize); } else { EvntAdd( gw->root->handle, WM_SIZED, evnt_window_rt_resize, EV_BOT ); } if( !cfg_rt_move ) { EvntAttach( gw->root->handle, WM_MOVED, evnt_window_move ); } else { EvntAdd( gw->root->handle, WM_MOVED, evnt_window_rt_resize, EV_BOT ); } EvntAttach( gw->root->handle, WM_FORCE_MOVE, evnt_window_rt_resize ); EvntDataAttach( gw->root->handle, AP_DRAGDROP, evnt_window_dd, gw ); EvntDataAdd( gw->root->handle, WM_DESTROY,evnt_window_destroy, NULL, EV_TOP ); EvntDataAdd( gw->root->handle, WM_ARROWED,evnt_window_arrowed, NULL, EV_TOP ); EvntDataAdd( gw->root->handle, WM_NEWTOP, evnt_window_newtop, &evnt_data, EV_BOT); EvntDataAdd( gw->root->handle, WM_TOPPED, evnt_window_newtop, &evnt_data, EV_BOT); EvntDataAttach( gw->root->handle, WM_ICONDRAW, evnt_window_icondraw, gw); EvntAttach( gw->root->handle, WM_XM1, evnt_window_m1 ); /* OBJECT * tbut; RsrcGaddr( h_gem_rsrc, R_TREE, FAVICO , &tbut ); window_set_icon(gw, &tbut[]); */ /* TODO: check if window is openend as "foreground" window... */ window_set_focus( gw, BROWSER, gw->browser); return (err); } int window_destroy( struct gui_window * gw) { short buff[8]; int err = 0; if( gw->browser->type != BT_ROOT ) { return(0); } /* test this with frames: */ /* assert( gw->parent == NULL); */ search_destroy( gw ); if( input_window == gw ) input_window = NULL; if( gw->root ) { if( gw->root->toolbar ) tb_destroy( gw->root->toolbar ); if( gw->root->statusbar ) sb_destroy( gw->root->statusbar ); } search_destroy( gw ); LOG(("Freeing browser window")); if( gw->browser ) browser_destroy( gw->browser ); /* destroy the icon: */ /*window_set_icon(gw, NULL, false );*/ /* needed? */ /*listRemove( (LINKABLE*)gw->root->cmproot ); */ LOG(("Freeing root window")); if( gw->root ) { /* TODO: check if no other browser is bound to this root window! */ if( gw->root->title ) free( gw->root->title ); if( gw->root->cmproot ) mt_CompDelete( &app, gw->root->cmproot ); ApplWrite( _AESapid, WM_DESTROY, gw->root->handle->handle, 0, 0, 0, 0); EvntWindom( MU_MESAG ); gw->root->handle = NULL; free( gw->root ); gw->root = NULL; } return( err ); } void window_open( struct gui_window * gw) { LGRECT br; WindOpen(gw->root->handle, 20, 20, app.w/2, app.h/2 ); WindSetStr( gw->root->handle, WF_NAME, (char *)"" ); /* apply focus to the root frame: */ long lfbuff[8] = { CM_GETFOCUS }; mt_CompEvntExec( gl_appvar, gw->browser->comp, lfbuff ); /* recompute the nested component sizes and positions: */ browser_update_rects( gw ); browser_get_rect( gw, BR_CONTENT, &br ); plotter->move( plotter, br.g_x, br.g_y ); plotter->resize( plotter, br.g_w, br.g_h ); gw->browser->attached = true; if( gw->root->statusbar != NULL ){ gw->root->statusbar->attached = true; } snd_rdw( gw->root->handle ); } /* TODO void window_set_icon(struct gui_window * gw, void * data, bool is_rsc ) { #define CDT_ICON_TYPE_OBJECT 1UL #define CDT_ICON_TYPE_BITMAP 2UL void * prev_type; void * ico = DataSearch(&app, gw->root->handle, CDT_ICON ); if(ico != NULL) { prev_type = DataSearch(&app, gw->root->handle, CDT_ICON_TYPE ); if( prev_type == (void*)CDT_ICON_TYPE_OBJECT ){ mt_ObjcFree( &app, (OBJECT*)ico ); } if( prev_type == (void*)CDT_ICON_TYPE_BITMAP ){ bitmap_destroy(ico); } } if( data != NULL ) { DataAttach( &app, gw->root->handle, CDT_ICON, data); if(is_rsc) { DataAttach( &app, gw->root->handle, CDT_ICON_TYPE, CDT_ICON_TYPE_OBJECT); } else { DataAttach( &app, gw->root->handle, CDT_ICON_TYPE, CDT_ICON_TYPE_BITMAP); } } #undef CDT_ICON_TYPE_OBJECT #undef CDT_ICON_TYPE_BITMAP } */ /* update back forward buttons (see tb_update_buttons (bug) ) */ void window_update_back_forward( struct gui_window * gw) { tb_update_buttons( gw ); } static void window_redraw_controls(struct gui_window *gw, uint32_t flags) { LGRECT rect; /* redraw sliders manually, dunno why this is needed (mt_WindSlider should do the job anytime)!*/ browser_get_rect( gw, BR_VSLIDER, &rect); ApplWrite( _AESapid, WM_REDRAW, gw->root->handle->handle, rect.g_x, rect.g_y, rect.g_w, rect.g_h ); browser_get_rect( gw, BR_HSLIDER, &rect); ApplWrite( _AESapid, WM_REDRAW, gw->root->handle->handle, rect.g_x, rect.g_y, rect.g_w, rect.g_h ); /* send redraw to toolbar & statusbar & scrollbars: */ mt_CompGetLGrect(&app, gw->root->toolbar->comp, WF_WORKXYWH, &rect); ApplWrite( _AESapid, WM_REDRAW, gw->root->handle->handle, rect.g_x, rect.g_y, rect.g_w, rect.g_h ); mt_CompGetLGrect(&app, gw->root->statusbar->comp, WF_WORKXYWH, &rect); ApplWrite( _AESapid, WM_REDRAW, gw->root->handle->handle, rect.g_x, rect.g_y, rect.g_w, rect.g_h ); } void window_set_stauts( struct gui_window * gw , char * text ) { if( gw->root == NULL ) return; CMP_STATUSBAR sb = gw->root->statusbar; if( sb == NULL || gw->browser->attached == false ) return; sb_set_text( sb, text ); } /* set focus to an arbitary element */ void window_set_focus( struct gui_window * gw, enum focus_element_type type, void * element ) { if( gw->root->focus.type != type || gw->root->focus.element != element ) { LOG(("Set focus: %p (%d)\n", element, type)); gw->root->focus.type = type; gw->root->focus.element = element; } } /* check if the url widget has focus */ bool window_url_widget_has_focus( struct gui_window * gw ) { assert( gw ); assert( gw->root ); if( gw->root->focus.type == URL_WIDGET && gw->root->focus.element != NULL ) { assert( ( &gw->root->toolbar->url == (struct s_url_widget*)gw->root->focus.element ) ); assert( GUIWIN_VISIBLE(gw) ); return true; } return false; } /* check if an arbitary window widget / or frame has the focus */ bool window_widget_has_focus( struct gui_window * gw, enum focus_element_type t, void * element ) { if( gw == NULL ) return( false ); if( element == NULL ){ assert( 1 != 0 ); return( (gw->root->focus.type == t ) ); } assert( gw->root != NULL ); return( ( element == gw->root->focus.element && t == gw->root->focus.type) ); } static void __CDECL evnt_window_dd( WINDOW *win, short wbuff[8], void * data ) { struct gui_window * gw = (struct gui_window *)data; 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; graf_mkstate(&mx, &my, &bmstat, &mkstat); if( gw == NULL ) return; if( (win->status & WS_ICONIFY)) return; dd_hdl = ddopen( wbuff[7], DD_OK); if( dd_hdl<0) return; /* pipe not open */ memset( ext, 0, 32); strcpy( ext, "ARGS"); dd_msg = ddsexts( dd_hdl, ext); if( dd_msg<0) goto error; dd_msg = ddrtry( 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) { ddreply(dd_hdl, DD_OK); buff = (char*)alloca(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 )); { int posx, posy; struct box *box; struct box *file_box = 0; hlcache_handle *h; int box_x, box_y; LGRECT bwrect; struct browser_window * bw = gw->browser->bw; h = bw->current_content; if (!bw->current_content || content_get_type(h) != CONTENT_HTML) return; browser_get_rect( gw, BR_CONTENT, &bwrect ); mx = mx - bwrect.g_x; my = my - bwrect.g_y; if( (mx < 0 || mx > bwrect.g_w) || (my < 0 || my > bwrect.g_h) ) return; box = html_get_box_tree(h); box_x = box->margin[LEFT]; box_y = box->margin[TOP]; while ((box = box_at_point(box, mx+gw->browser->scroll.current.x, my+gw->browser->scroll.current.y, &box_x, &box_y, &h))) { if (box->style && css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) continue; if (box->gadget) { switch (box->gadget->type) { case GADGET_FILE: file_box = box; break; /* TODO: handle these case GADGET_TEXTBOX: case GADGET_TEXTAREA: case GADGET_PASSWORD: text_box = box; break; */ default: break; } } } /* end While */ if ( !file_box ) return; if (file_box) { utf8_convert_ret ret; char *utf8_fn; ret = local_encoding_to_utf8( buff, 0, &utf8_fn); if (ret != UTF8_CONVERT_OK) { /* A bad encoding should never happen */ LOG(("utf8_from_local_encoding failed")); assert(ret != UTF8_CONVERT_BADENC); /* Load was for us - just no memory */ return; } /* Found: update form input. */ free(file_box->gadget->value); file_box->gadget->value = utf8_fn; /* Redraw box. */ box_coords(file_box, &posx, &posy); browser_schedule_redraw(bw->window, posx - gw->browser->scroll.current.x, posy - gw->browser->scroll.current.y, posx - gw->browser->scroll.current.x + file_box->width, posy - gw->browser->scroll.current.y + file_box->height); } } } } error: ddclose( dd_hdl); } /* -------------------------------------------------------------------------- */ /* Non Public Modul event handlers: */ /* -------------------------------------------------------------------------- */ static void __CDECL evnt_window_destroy( WINDOW *win, short buff[8], void *data ) { LOG(("%s\n", __FUNCTION__ )); if( data ) free( data ); } static void __CDECL evnt_window_close( WINDOW *win, short buff[8], void *data ) { struct gui_window * gw = find_root_gui_window( win ); if( gw != NULL ) { browser_window_destroy( gw->browser->bw ); } } static void __CDECL evnt_window_newtop( WINDOW *win, short buff[8], void *data ) { input_window = find_root_gui_window( win ); LOG(("newtop: iw: %p, win: %p", input_window, win )); assert( input_window != NULL ); window_redraw_controls(input_window, 0); } static void __CDECL evnt_window_shaded( WINDOW *win, short buff[8], void *data ) { if(buff[0] == WM_SHADED){ LOG(("WM_SHADED, vis: %d, state: %d", GEMWIN_VISIBLE(win), win->status )); } if(buff[0] == WM_UNSHADED){ } } static void __CDECL evnt_window_icondraw( WINDOW *win, short buff[8], void * data ) { short x,y,w,h; struct gui_window * gw = (struct gui_window*)data; bool has_favicon = false; WindClear( win); WindGet( win, WF_WORKXYWH, &x, &y, &w, &h); if( has_favicon == false ) { OBJECT * tree; RsrcGaddr( h_gem_rsrc, R_TREE, ICONIFY , &tree ); tree->ob_x = x; tree->ob_y = y; tree->ob_width = w; tree->ob_height = h; mt_objc_draw( tree, 0, 8, buff[4], buff[5], buff[6], buff[7], app.aes_global ); } } static void __CDECL evnt_window_move( WINDOW *win, short buff[8] ) { short mx,my, mb, ks; short wx, wy, wh, ww, nx, ny; short r; short xoff, yoff; if( cfg_rt_move ) { std_mvd( win, buff, &app ); evnt_window_rt_resize( win, buff ); } else { wind_get( win->handle, WF_CURRXYWH, &wx, &wy, &ww, &wh ); if( graf_dragbox( ww, wh, wx, wy, app.x-ww, app.y, app.w+ww, app.h+wh, &nx, &ny )){ buff[4] = nx; buff[5] = ny; buff[6] = ww; buff[7] = wh; std_mvd( win, buff, &app ); evnt_window_rt_resize( win, buff ); } } } void __CDECL evnt_window_resize( WINDOW *win, short buff[8] ) { short mx,my, mb, ks; short wx, wy, wh, ww, nw, nh; short r; graf_mkstate( &mx, &my, &mb, &ks ); if( cfg_rt_resize ) { std_szd( win, buff, &app ); evnt_window_rt_resize( win, buff ); } else { wind_get( win->handle, WF_CURRXYWH, &wx, &wy, &ww, &wh ); r = graf_rubberbox(wx, wy, 20, 20, &nw, &nh); if( nw < 40 && nw < 40 ) return; buff[4] = wx; buff[5] = wy; buff[6] = nw; buff[7] = nh; std_szd( win, buff, &app ); evnt_window_rt_resize( win, buff ); } } /* perform the actual resize */ static void __CDECL evnt_window_rt_resize( WINDOW *win, short buff[8] ) { short x,y,w,h; struct gui_window * gw; LGRECT rect; bool resized; bool moved; if(buff[0] == WM_FORCE_MOVE ) { std_mvd(win, buff, &app); std_szd(win, buff, &app); } wind_get( win->handle, WF_WORKXYWH, &x, &y, &w, &h ); gw = find_root_gui_window( win ); assert( gw != NULL ); if(gw->root->loc.g_x != x || gw->root->loc.g_y != y ){ moved = true; gw->root->loc.g_x = x; gw->root->loc.g_y = y; browser_update_rects( gw ); } if(gw->root->loc.g_w != w || gw->root->loc.g_h != h ){ resized = true; /* report resize to component interface: */ browser_update_rects( gw ); browser_get_rect( gw, BR_CONTENT, &rect ); if( gw->browser->bw->current_content != NULL ) browser_window_reformat(gw->browser->bw, false, rect.g_w, rect.g_h ); gw->root->toolbar->url.scrollx = 0; window_redraw_controls(gw, 0); /* TODO: recalculate scroll position, istead of zeroing? */ } }