netsurf/atari/browser.c
Ole Loots aadc2541a5 Added comments.
svn path=/trunk/netsurf/; revision=13121
2011-11-05 01:46:41 +00:00

1076 lines
28 KiB
C
Executable File

/*
* Copyright 2010 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/>.
*
* Module Description:
*
* This WinDom compo
*
*
*/
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <windom.h>
#include "desktop/gui.h"
#include "desktop/netsurf.h"
#include "desktop/browser.h"
#include "desktop/mouse.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 "utils/log.h"
#include "utils/messages.h"
#include "atari/gui.h"
#include "atari/browser_win.h"
#include "atari/misc.h"
#include "atari/global_evnt.h"
#include "atari/res/netsurf.rsh"
#include "atari/browser.h"
#include "atari/plot/plotter.h"
#include "atari/plot.h"
#include "atari/font.h"
#include "atari/ctxmenu.h"
#include "cflib.h"
extern browser_mouse_state bmstate;
extern int mouse_click_time[3];
extern int mouse_hold_start[3];
extern GEM_PLOTTER plotter;
extern struct gui_window *input_window;
extern short last_drag_x;
extern short last_drag_y;
static void __CDECL browser_evnt_wdestroy( WINDOW * c, short buff[8], void * data);
COMPONENT *comp_widget_create( APPvar *app, WINDOW *win, int size, int flex );
/*
The component window interface needs frame API init,
keep track of frame API init:
*/
static bool frameinit = true;
/*
Create an browser component window (undcomented WinDom structure).
Currently, this area is the area which is used to display HTML content.
However, it could also contains other areas, these need to be handled within
"browser_get_rect" function.
*/
struct s_browser * browser_create
(
struct gui_window * gw,
struct browser_window *bw,
struct browser_window * clone,
int lt, int w, int flex
)
{
LGRECT cwork;
COMPONENT * scrollv, * scrollh, * drawable;
if( frameinit ) {
mt_FrameInit( &app );
frameinit = false;
}
CMP_BROWSER bnew = (CMP_BROWSER)malloc( sizeof(struct s_browser) );
if( bnew )
{
memset(bnew, 0, sizeof(struct s_browser) );
bnew->bw = bw;
bnew->attached = false;
if(clone)
bw->scale = clone->scale;
else
bw->scale = 1;
bnew->redraw.areas_used = 0;
bnew->compwin = mt_WindCreate( &app, VSLIDE|HSLIDE, 1, 1, app.w, app.h);
bnew->compwin->w_u = 1;
bnew->compwin->h_u = 1;
/* needs to be adjusted when content width is known: */
bnew->compwin->ypos_max = w;
bnew->compwin->xpos_max = w;
mt_WindSlider( &app, bnew->compwin, HSLIDER|VSLIDER);
bnew->comp = (COMPONENT*)comp_widget_create( (void*)&app, (WINDOW*)bnew->compwin, 1, 1 );
if( bnew->comp == NULL ) {
free(bnew);
return(NULL);
}
/* Attach events to the component: */
mt_EvntDataAdd( &app, bnew->compwin, WM_XBUTTON,
browser_evnt_mbutton, (void*)gw, EV_BOT );
mt_CompEvntDataAttach( &app, bnew->comp, WM_REDRAW,
browser_evnt_redraw, (void*)gw );
mt_EvntDataAttach( &app, bnew->compwin , WM_REDRAW, browser_evnt_redraw_x, NULL );
mt_EvntDataAttach( &app, bnew->compwin, WM_SLIDEXY,
browser_evnt_slider, gw );
mt_EvntDataAttach( &app, bnew->compwin, WM_ARROWED,
browser_evnt_arrowed, gw );
mt_CompEvntDataAttach( &app, bnew->comp, WM_DESTROY,
browser_evnt_destroy, (void*)bnew );
/* just stub, as an reminder: */
mt_EvntDataAttach( &app, bnew->compwin, WM_DESTROY,
browser_evnt_wdestroy, (void*)bnew );
/* Set the gui_window owner. */
/* it is an link to the netsurf window system */
mt_CompDataAttach( &app, bnew->comp, CDT_OWNER, gw );
bnew->scroll.requested.y = 0;
bnew->scroll.requested.x = 0;
bnew->scroll.current.x = 0;
bnew->scroll.current.y = 0;
}
return( bnew );
}
bool browser_destroy( struct s_browser * b )
{
LOG(("%s\n", b->bw->name ));
assert( b != NULL );
assert( b->comp != NULL );
assert( b->bw != NULL );
if( b->compwin != NULL ) {
/* TODO: Only do this when it's the last browser viewport within the root win: */
COMPONENT * old = b->comp;
WINDOW * oldwin = b->compwin;
b->comp = NULL;
b->compwin = NULL;
/* I'm not sure about the correct order of these 2 calls: */
WindDelete( oldwin );
mt_CompDelete(&app, old );
}
return( true );
}
/*
Query the browser component for widget rectangles.
*/
void browser_get_rect( struct gui_window * gw, enum browser_rect type, LGRECT * out)
{
GRECT work;
assert( out != NULL);
int slider_v_w = 20;
int slider_v_h = 22;
int slider_h_w = 20;
int slider_h_h = 22;
/* Query component for it's current size: */
WindGetGrect( gw->browser->compwin, WF_WORKXYWH, &work);
/* And extract the different widget dimensions: */
/* Redraw area of html content: */
if( type == BR_CONTENT ){
out->g_w = work.g_w;
out->g_h = work.g_h;
out->g_x = work.g_x;
out->g_y = work.g_y;
return;
}
/* Horizontal scroller: */
LGRECT cur;
mt_CompGetLGrect(&app, gw->browser->comp, WF_WORKXYWH, &cur);
if( type == BR_HSLIDER ){
out->g_x = cur.g_x;
out->g_y = cur.g_y + work.g_h;
out->g_h = cur.g_h - work.g_h;
out->g_w = cur.g_w;
}
/* Vertical scroller: */
if( type == BR_VSLIDER ){
out->g_x = cur.g_x + work.g_w;
out->g_y = cur.g_y;
out->g_w = cur.g_w - work.g_w;
out->g_h = work.g_h;
}
}
/* Report an resize to the COMPONENT interface */
void browser_update_rects(struct gui_window * gw )
{
short buff[8];
LGRECT cmprect;
mt_WindGetGrect( &app, gw->root->handle, WF_CURRXYWH, (GRECT*)&buff[4]);
buff[0] = CM_REFLOW;
buff[1] = _AESapid;
buff[2] = 0;
EvntExec(gw->root->handle, buff);
}
void browser_set_content_size(struct gui_window * gw, int w, int h)
{
CMP_BROWSER b = gw->browser;
LGRECT work;
browser_get_rect( gw, BR_CONTENT, &work );
b->compwin->ypos_max = h;
b->compwin->xpos_max = w;
if( w < work.g_w + b->scroll.current.x || w < work.g_h + b->scroll.current.y ) {
/* let the scroll routine detect invalid scroll values... */
browser_scroll(gw, WA_LFLINE, b->scroll.current.x, true );
browser_scroll(gw, WA_UPLINE, b->scroll.current.y, true );
/* force update of scrollbars: */
b->scroll.required = true;
}
}
static void __CDECL browser_evnt_wdestroy( WINDOW * c, short buff[8], void * data)
{
struct s_browser * b = (struct s_browser*)data;
LOG((""));
}
static void __CDECL browser_evnt_destroy( COMPONENT * c, long buff[8], void * data)
{
struct s_browser * b = (struct s_browser*)data;
struct gui_window * gw = b->bw->window;
LOG(("%s\n",gw->browser->bw->name));
assert( b != NULL );
assert( gw != NULL );
assert( b->comp == NULL );
free( b );
gw->browser = NULL;
LOG(("evnt_destroy done!"));
}
static void __CDECL browser_evnt_arrowed( WINDOW *win, short buff[8], void * data)
{
bool abs = false;
int value = BROWSER_SCROLL_SVAL;
struct gui_window * gw = data;
LGRECT cwork;
if( input_window == NULL || input_window != gw ) {
return;
}
browser_get_rect( gw, 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( gw, buff[4], value, abs );
}
void __CDECL browser_evnt_slider( WINDOW *win, short buff[8], void * data)
{
int dx = buff[4];
int dy = buff[5];
GRECT work, screen;
struct gui_window * gw = data;
if (!dx && !dy) return;
if( input_window == NULL || input_window != gw ) {
return;
}
/* update the sliders _before_ we call redraw (which might depend on the slider possitions) */
mt_WindSlider( &app, win, (dx?HSLIDER:0) | (dy?VSLIDER:0) );
if( dy > 0 )
browser_scroll( gw, WA_DNPAGE, abs(dy), false );
else if ( dy < 0)
browser_scroll( gw, WA_UPPAGE, abs(dy), false );
if( dx > 0 )
browser_scroll( gw, WA_RTPAGE, abs(dx), false );
else if( dx < 0 )
browser_scroll( gw, WA_LFPAGE, abs(dx), false );
}
/*
Mouse Button handler for browser component.
*/
static void __CDECL browser_evnt_mbutton( WINDOW * c, short buff[8], void * data)
{
long lbuff[8];
short mx, my, dummy, mbut;
uint32_t tnow = clock()*1000 / CLOCKS_PER_SEC;
LGRECT cwork;
struct gui_window * gw = data;
if( input_window != gw ) {
return;
}
window_set_focus( gw, BROWSER, (void*)gw->browser );
browser_get_rect( gw, BR_CONTENT, &cwork );
mx = evnt.mx - cwork.g_x;
my = evnt.my - cwork.g_y;
LOG(("mevent (%d) within %s at %d / %d\n", evnt.nb_click, gw->browser->bw->name, mx, my ));
/* Translate GEM key state to netsurf mouse modifier */
if( evnt.mkstate & (K_RSHIFT | K_LSHIFT) ){
bmstate |= BROWSER_MOUSE_MOD_1;
} else {
bmstate &= ~(BROWSER_MOUSE_MOD_1);
}
if( (evnt.mkstate & K_CTRL) ){
bmstate |= BROWSER_MOUSE_MOD_2;
} else {
bmstate &= ~(BROWSER_MOUSE_MOD_2);
}
if( (evnt.mkstate & K_ALT) ){
bmstate |= BROWSER_MOUSE_MOD_3;
} else {
bmstate &= ~(BROWSER_MOUSE_MOD_3);
}
int sx = (mx + gw->browser->scroll.current.x);
int sy = (my + gw->browser->scroll.current.y);
/* Detect left mouse button state and compare with event state: */
graf_mkstate(&dummy, &dummy, &mbut, &dummy);
if( (mbut & 1) && (evnt.mbut & 1) ) {
if( mouse_hold_start[0] == 0 ) {
mouse_hold_start[0] = tnow;
LOG(("Drag starts at %d,%d\n", sx, sy));
browser_window_mouse_click(gw->browser->bw,BROWSER_MOUSE_PRESS_1,sx,sy);
bmstate |= BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_DRAG_ON;
} else {
bmstate |= BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_ON;
}
if( (abs(mx-last_drag_x)>5) || (abs(mx-last_drag_y)>5) ){
browser_window_mouse_track(
gw->browser->bw,
bmstate,
sx, sy
);
last_drag_x = mx;
last_drag_y = my;
}
} else if( (evnt.mbut & 1) ) {
mouse_click_time[0] = tnow; /* clock in ms */
/* check if this event was during an drag op, only handle if it wasn't: */
if( mouse_hold_start[0] == 0 ) {
LOG(("Click within %s at %d / %d\n", gw->browser->bw->name, sx, sy ));
browser_window_mouse_click(gw->browser->bw,BROWSER_MOUSE_PRESS_1,sx,sy);
browser_window_mouse_click(gw->browser->bw,BROWSER_MOUSE_CLICK_1,sx,sy);
bmstate &= ~( BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_CLICK_1);
}
mouse_hold_start[0] = 0;
}
/* Right button pressed? */
if( (evnt.mbut & 2 ) ) {
context_popup( gw, evnt.mx, evnt.my );
}
}
/*
Report scroll event to the browser component.
*/
void browser_scroll( struct gui_window * gw, short mode, int value, bool abs )
{
LGRECT work;
int max_y_scroll;
int max_x_scroll;
int oldx = gw->browser->scroll.current.x;
int oldy = gw->browser->scroll.current.y;
struct s_browser * b = gw->browser;
LOG((""));
if( b->bw->current_content != NULL ) {
browser_get_rect( gw, BR_CONTENT, &work);
max_y_scroll = (content_get_height( b->bw->current_content ) - work.g_h );
max_x_scroll = (content_get_width( b->bw->current_content ) - work.g_w);
} else {
return;
}
switch( mode ) {
case WA_UPPAGE:
case WA_UPLINE:
if( max_y_scroll < 1 )
return;
if( abs == false )
b->scroll.requested.y -= value;
else
b->scroll.requested.y = value - b->scroll.current.y;
break;
case WA_DNPAGE:
case WA_DNLINE:
if( max_y_scroll < 1 )
return;
if( abs == false )
b->scroll.requested.y += value;
else
b->scroll.requested.y = value - b->scroll.current.y;
break;
case WA_LFPAGE:
case WA_LFLINE:
if( max_x_scroll < 1 )
return;
if( abs == false )
b->scroll.requested.x -= value;
else
b->scroll.requested.x = value - b->scroll.current.x;
break;
case WA_RTPAGE:
case WA_RTLINE:
if( max_x_scroll < 1 )
return;
if( abs == false )
b->scroll.requested.x += value;
else
b->scroll.requested.x = value - b->scroll.current.x;
break;
default: break;
}
if( b->scroll.current.y + b->scroll.requested.y < 0 ) {
b->scroll.requested.y = -b->scroll.current.y;
}
if( b->scroll.current.y + b->scroll.requested.y > max_y_scroll ) {
b->scroll.requested.y = max_y_scroll - b->scroll.current.y;
}
if( b->scroll.current.x + b->scroll.requested.x < 0 ) {
b->scroll.requested.x = -b->scroll.current.x;
}
if( b->scroll.current.x + b->scroll.requested.x > max_x_scroll ) {
b->scroll.requested.x = max_x_scroll - b->scroll.current.x;
}
if( oldy != b->scroll.current.y + b->scroll.requested.y ||
oldx != b->scroll.current.x + b->scroll.requested.x ) {
b->scroll.required = true;
}
}
/*
perform the requested scrolling.
gw -> the browser window to act upon.
bwrect -> the dimensions of the browser, so that this function
doesn't need to get it.
*/
static void browser_process_scroll( struct gui_window * gw, LGRECT bwrect )
{
struct s_browser * b = gw->browser;
GRECT src;
GRECT dst;
short h,w;
if( gw->browser->bw->current_content == NULL )
return;
h = (short) abs( b->scroll.requested.y );
w = (short) abs( b->scroll.requested.x );
/* if the request exceeds the browser size, redraw the whole area */
if ( b->scroll.requested.y > bwrect.g_h || b->scroll.requested.y < -bwrect.g_h ||
b->scroll.requested.x > bwrect.g_w || b->scroll.requested.x < -bwrect.g_w ) {
b->scroll.current.y += b->scroll.requested.y;
b->scroll.current.x += b->scroll.requested.x;
browser_schedule_redraw( gw, 0, 0, bwrect.g_w, bwrect.g_h);
/* don't scroll again: */
b->scroll.requested.y = 0;
b->scroll.requested.x = 0;
}
if( b->scroll.requested.y < 0 ) {
/* scroll up */
src.g_x = 0;
src.g_y = 0;
src.g_w = bwrect.g_w;
src.g_h = bwrect.g_h - h;
dst.g_x = 0;
dst.g_y = h;
dst.g_w = src.g_w;
dst.g_h = src.g_h;
plotter->copy_rect( plotter, src, dst );
b->scroll.current.y += b->scroll.requested.y;
browser_schedule_redraw( gw, 0, 0, bwrect.g_w, h ) ;
}
if( b->scroll.requested.y > 0 ) {
/* scroll down */
src.g_x = 0;
src.g_y = h;
src.g_w = bwrect.g_w;
src.g_h = bwrect.g_h - h;
dst.g_x = 0;
dst.g_y = 0;
dst.g_w = bwrect.g_w;
dst.g_h = bwrect.g_h - h;
plotter->copy_rect( plotter, src, dst );
b->scroll.current.y += b->scroll.requested.y;
browser_schedule_redraw( gw, 0, bwrect.g_h - h, bwrect.g_w, bwrect.g_h );
}
if( b->scroll.requested.x < 0 ) {
/* scroll to the left */
src.g_x = 0;
src.g_y = 0;
src.g_w = bwrect.g_w - w;
src.g_h = bwrect.g_h;
dst.g_x = w;
dst.g_y = 0;
dst.g_w = bwrect.g_w - w;
dst.g_h = bwrect.g_h;
plotter->copy_rect( plotter, src, dst );
b->scroll.current.x += b->scroll.requested.x;
browser_schedule_redraw( gw, 0, 0, w, bwrect.g_h );
}
if( b->scroll.requested.x > 0 ) {
/* scroll to the right */
src.g_x = w;
src.g_y = 0;
src.g_w = bwrect.g_w - w;
src.g_h = bwrect.g_h;
dst.g_x = 0;
dst.g_y = 0;
dst.g_w = bwrect.g_w - w;
dst.g_h = bwrect.g_h;
plotter->copy_rect( plotter, src, dst );
b->scroll.current.x += b->scroll.requested.x;
browser_schedule_redraw( gw, bwrect.g_w - w, 0, bwrect.g_w, bwrect.g_h );
}
b->scroll.requested.y = 0;
b->scroll.requested.x = 0;
gw->browser->compwin->xpos = b->scroll.current.x;
gw->browser->compwin->ypos = b->scroll.current.y;
mt_WindSlider( &app, gw->browser->compwin, HSLIDER|VSLIDER);
}
/*
Report keypress to browser component.
The browser component doesn't list for keyinput by itself.
parameter:
- gui_window ( compocnent owner ).
- unsigned short nkc ( CFLIB normalised key code )
*/
bool browser_input( struct gui_window * gw, unsigned short nkc )
{
LGRECT work;
bool r = false;
unsigned char ascii = (nkc & 0xFF);
nkc = (nkc & (NKF_CTRL|NKF_SHIFT|0xFF));
browser_get_rect(gw, BR_CONTENT, &work);
if( (nkc & NKF_CTRL) != 0 ) {
switch ( ascii ) {
case 'A':
r = browser_window_key_press(gw->browser->bw, KEY_SELECT_ALL);
break;
case 'C':
r = browser_window_key_press(gw->browser->bw, KEY_COPY_SELECTION);
break;
case 'X':
r = browser_window_key_press(gw->browser->bw, KEY_CUT_SELECTION);
break;
case 'V':
r = browser_window_key_press(gw->browser->bw, KEY_PASTE);
break;
default:
break;
}
}
if( (nkc & NKF_SHIFT) != 0 ) {
switch( ascii ) {
case NK_TAB:
r = browser_window_key_press(gw->browser->bw, KEY_SHIFT_TAB);
break;
case NK_LEFT:
if( browser_window_key_press(gw->browser->bw, KEY_LINE_START) == false) {
browser_scroll( gw, WA_LFPAGE, work.g_w, false );
r = true;
}
break;
case NK_RIGHT:
if( browser_window_key_press(gw->browser->bw, KEY_LINE_END) == false) {
browser_scroll( gw, WA_RTPAGE, work.g_w, false );
r = true;
}
break;
case NK_UP:
if ( browser_window_key_press(gw->browser->bw, KEY_PAGE_UP) ==false ){
browser_scroll( gw, WA_UPPAGE, work.g_h, false );
r = true;
}
break;
case NK_DOWN:
if (browser_window_key_press(gw->browser->bw, KEY_PAGE_DOWN) == false) {
browser_scroll( gw, WA_DNPAGE, work.g_h, false );
r = true;
}
break;
default:
break;
}
}
if( (nkc & (NKF_SHIFT|NKF_CTRL) ) == 0 ) {
switch( ascii ) {
case NK_BS:
r = browser_window_key_press(gw->browser->bw, KEY_DELETE_LEFT);
break;
case NK_DEL:
r = browser_window_key_press(gw->browser->bw, KEY_DELETE_RIGHT);
break;
case NK_TAB:
r = browser_window_key_press(gw->browser->bw, KEY_TAB);
break;
case NK_ENTER:
r = browser_window_key_press(gw->browser->bw, KEY_NL);
break;
case NK_RET:
r = browser_window_key_press(gw->browser->bw, KEY_CR);
break;
case NK_ESC:
r = browser_window_key_press(gw->browser->bw, KEY_ESCAPE);
break;
case NK_CLRHOME:
r = browser_window_key_press(gw->browser->bw, KEY_TEXT_START);
break;
case NK_RIGHT:
if (browser_window_key_press(gw->browser->bw, KEY_RIGHT) == false){
browser_scroll( gw, WA_RTLINE, 16, false );
r = true;
}
break;
case NK_LEFT:
if (browser_window_key_press(gw->browser->bw, KEY_LEFT) == false) {
browser_scroll( gw, WA_LFLINE, 16, false );
r = true;
}
break;
case NK_UP:
if (browser_window_key_press(gw->browser->bw, KEY_UP) == false) {
browser_scroll( gw, WA_UPLINE, 16, false);
r = true;
}
break;
case NK_DOWN:
if (browser_window_key_press(gw->browser->bw, KEY_DOWN) == false) {
browser_scroll( gw, WA_DNLINE, 16, false);
r = true;
}
break;
case NK_M_PGUP:
if ( browser_window_key_press(gw->browser->bw, KEY_PAGE_UP) ==false ) {
browser_scroll( gw, WA_UPPAGE, work.g_h, false );
r = true;
}
break;
case NK_M_PGDOWN:
if (browser_window_key_press(gw->browser->bw, KEY_PAGE_DOWN) == false) {
browser_scroll( gw, WA_DNPAGE, work.g_h, false );
r = true;
}
break;
default:
break;
}
}
if( r == false && ( (nkc & NKF_CTRL)==0) ) {
if (ascii >= 9 ) {
int ucs4 = atari_to_ucs4(ascii);
r = browser_window_key_press(gw->browser->bw, ucs4 );
}
}
return( r );
}
static void __CDECL browser_evnt_redraw_x( WINDOW * c, short buf[8], void * data)
{
/* just an stub to prevent wndclear */
/* Probably the browser redraw is better placed here? dunno! */
return;
}
/* determines if a browser window needs redraw */
bool browser_redraw_required( struct gui_window * gw)
{
bool ret = true;
CMP_BROWSER b = gw->browser;
if( b->bw->current_content == NULL )
return ( false );
ret = ( ((b->redraw.areas_used > 0) )
|| b->scroll.required
|| b->caret.redraw );
return( ret );
}
/* schedule a redraw of content */
/* coords are relative to the framebuffer */
void browser_schedule_redraw_rect(struct gui_window * gw, short x, short y, short w, short h)
{
if( x < 0 ){
w += x;
x = 0;
}
if( y < 0 ) {
h += y;
y = 0;
}
browser_schedule_redraw( gw, x, y, x+w, y+h );
}
static inline bool rect_intersect( struct rect * box1, struct rect * box2 )
{
if (box2->x1 < box1->x0)
return false;
if (box2->y1 < box1->y0)
return false;
if (box2->x0 > box1->x1)
return false;
if (box2->y0 > box1->y1)
return false;
return true;
}
/*
schedule a redraw of content, coords are relative to the framebuffer
*/
void browser_schedule_redraw(struct gui_window * gw, short x0, short y0, short x1, short y1)
{
assert( gw != NULL );
CMP_BROWSER b = gw->browser;
int i;
struct rect area;
LGRECT work;
if( y1 < 0 || x1 < 0 )
return;
browser_get_rect( gw, BR_CONTENT, &work);
if( x0 > work.g_w )
return;
if( y0 > work.g_h )
return;
area.x0 = x0;
area.y0 = y0;
area.x1 = x1;
area.y1 = y1;
for( i=0; i<b->redraw.areas_used; i++) {
if( b->redraw.areas[i].x0 <= x0
&& b->redraw.areas[i].x1 >= x1
&& b->redraw.areas[i].y0 <= y0
&& b->redraw.areas[i].y1 >= y1 ){
/* the area is already queued for redraw */
return;
} else {
if( rect_intersect(&b->redraw.areas[i], &area ) ){
b->redraw.areas[i].x0 = MIN(b->redraw.areas[i].x0, x0);
b->redraw.areas[i].y0 = MIN(b->redraw.areas[i].y0, y0);
b->redraw.areas[i].x1 = MAX(b->redraw.areas[i].x1, x1);
b->redraw.areas[i].y1 = MAX(b->redraw.areas[i].y1, y1);
return;
}
}
}
if( b->redraw.areas_used < MAX_REDRW_SLOTS ) {
b->redraw.areas[b->redraw.areas_used].x0 = x0;
b->redraw.areas[b->redraw.areas_used].x1 = x1;
b->redraw.areas[b->redraw.areas_used].y0 = y0;
b->redraw.areas[b->redraw.areas_used].y1 = y1;
b->redraw.areas_used++;
} else {
/*
we are out of available slots, merge box with last slot
this is dumb... but also a very rare case.
*/
b->redraw.areas[MAX_REDRW_SLOTS-1].x0 = MIN(b->redraw.areas[i].x0, x0);
b->redraw.areas[MAX_REDRW_SLOTS-1].y0 = MIN(b->redraw.areas[i].y0, y0);
b->redraw.areas[MAX_REDRW_SLOTS-1].x1 = MAX(b->redraw.areas[i].x1, x1);
b->redraw.areas[MAX_REDRW_SLOTS-1].y1 = MAX(b->redraw.areas[i].y1, y1);
}
done:
return;
}
static void browser_redraw_content( struct gui_window * gw, int xoff, int yoff )
{
LGRECT work;
CMP_BROWSER b = gw->browser;
struct redraw_context ctx = {
.interactive = true,
.plot = &atari_plotters
};
LOG(("%s : %d,%d - %d,%d\n", b->bw->name, b->redraw.area.x0,
b->redraw.area.y0, b->redraw.area.x1, b->redraw.area.y1
));
browser_window_redraw( b->bw, -b->scroll.current.x,
-b->scroll.current.y, &b->redraw.area, &ctx );
}
void browser_redraw_caret( struct gui_window * gw, GRECT * area )
{
GRECT caret;
struct s_browser * b = gw->browser;
if( b->caret.redraw == true ){
struct rect old_clip;
struct rect clip;
caret = b->caret.requested;
caret.g_x -= gw->browser->scroll.current.x;
caret.g_y -= gw->browser->scroll.current.y;
clip.x0 = caret.g_x - 1;
clip.y0 = caret.g_y - 1;
clip.x1 = caret.g_x + caret.g_w + 1;
clip.y1 = caret.g_y + caret.g_h + 1;
/* store old clip before adjusting it: */
plot_get_clip( &old_clip );
/* clip to cursor: */
plot_clip( &clip );
plot_rectangle( caret.g_x, caret.g_y,
caret.g_x+caret.g_w, caret.g_y+caret.g_h,
plot_style_caret );
/* restore old clip area: */
plot_clip( &old_clip );
b->caret.current.g_x = caret.g_x + gw->browser->scroll.current.x;
b->caret.current.g_y = caret.g_y + gw->browser->scroll.current.y;
b->caret.current.g_w = caret.g_w;
b->caret.current.g_h = caret.g_h;
}
}
void browser_redraw( struct gui_window * gw )
{
LGRECT bwrect;
struct s_browser * b = gw->browser;
short todo[4];
struct rect clip;
if( b->attached == false || b->bw->current_content == NULL ) {
return;
}
browser_get_rect(gw, BR_CONTENT, &bwrect);
plotter->resize(plotter, bwrect.g_w, bwrect.g_h);
plotter->move(plotter, bwrect.g_x, bwrect.g_y );
clip.x0 = 0;
clip.y0 = 0;
clip.x1 = bwrect.g_w;
clip.y1 = bwrect.g_h;
plotter->clip( plotter, &clip );
plotter->lock(plotter);
if( b->scroll.required == true && b->bw->current_content != NULL) {
browser_process_scroll( gw, bwrect );
b->scroll.required = false;
}
if ((b->redraw.areas_used > 0) && b->bw->current_content != NULL ) {
if( (plotter->flags & PLOT_FLAG_OFFSCREEN) == 0 ) {
int i;
GRECT area;
GRECT fbwork;
todo[0] = bwrect.g_x;
todo[1] = bwrect.g_y;
todo[2] = todo[0] + bwrect.g_w-1;
todo[3] = todo[1] + bwrect.g_h-1;
vs_clip(plotter->vdi_handle, 1, (short*)&todo[0]);
if( wind_get(gw->root->handle->handle, WF_FIRSTXYWH,
&todo[0], &todo[1], &todo[2], &todo[3] )!=0 ) {
while (todo[2] && todo[3]) {
/* convert screen to framebuffer coords: */
fbwork.g_x = todo[0] - bwrect.g_x;
fbwork.g_y = todo[1] - bwrect.g_y;
if( fbwork.g_x < 0 ){
fbwork.g_w = todo[2] + todo[0];
fbwork.g_x = 0;
} else {
fbwork.g_w = todo[2];
}
if( fbwork.g_y < 0 ){
fbwork.g_h = todo[3] + todo[1];
fbwork.g_y = 0;
} else {
fbwork.g_h = todo[3];
}
/* walk the redraw requests: */
for( i=0; i<b->redraw.areas_used; i++ ){
area.g_x = b->redraw.areas[i].x0;
area.g_y = b->redraw.areas[i].y0;
area.g_w = b->redraw.areas[i].x1 - b->redraw.areas[i].x0;
area.g_h = b->redraw.areas[i].y1 - b->redraw.areas[i].y0;
if (rc_intersect((GRECT *)&fbwork,(GRECT *)&area)) {
b->redraw.area.x0 = area.g_x;
b->redraw.area.y0 = area.g_y;
b->redraw.area.x1 = area.g_x + area.g_w;
b->redraw.area.y1 = area.g_y + area.g_h;
browser_redraw_content( gw, 0, 0 );
} else {
/*
the area should be kept scheduled for later redraw, but because this
is onscreen plotter, it doesn't make much sense anyway...
*/
}
}
if (wind_get(gw->root->handle->handle, WF_NEXTXYWH,
&todo[0], &todo[1], &todo[2], &todo[3])==0) {
break;
}
}
}
vs_clip(plotter->vdi_handle, 0, (short*)&todo);
} else {
/* its save to do a complete redraw :) */
}
b->redraw.areas_used = 0;
}
if( b->caret.redraw == true && b->bw->current_content != NULL ) {
GRECT area;
todo[0] = bwrect.g_x;
todo[1] = bwrect.g_y;
todo[2] = todo[0] + bwrect.g_w;
todo[3] = todo[1] + bwrect.g_h;
area.g_x = bwrect.g_x;
area.g_y = bwrect.g_y;
area.g_w = bwrect.g_w;
area.g_h = bwrect.g_h;
vs_clip(plotter->vdi_handle, 1, (short*)&todo[0]);
browser_redraw_caret( gw, &area );
vs_clip(plotter->vdi_handle, 0, (short*)&todo[0]);
b->caret.redraw = false;
}
plotter->unlock(plotter);
/* TODO: if we use offscreen bitmap, trigger content redraw here */
}
static void __CDECL browser_evnt_redraw( COMPONENT * c, long buff[8], void * data)
{
short pxy[8];
WINDOW * w;
struct gui_window * gw = (struct gui_window *) data;
CMP_BROWSER b = gw->browser;
LGRECT work, lclip, rwork;
// TODO: maybe implement something like validate_gw()
// to fetch spurious redraw events? the function should
// traverse all gui_windows and see if gw exists in the list
int xoff,yoff,width,heigth;
short cw, ch, cellw, cellh;
/* use that instead of browser_find_root() ? */
w = (WINDOW*)mt_CompGetPtr( &app, c, CF_WINDOW );
browser_get_rect( gw, BR_CONTENT, &work );
lclip = work;
if ( !rc_lintersect( (LGRECT*)&buff[4], &lclip ) ) return;
if( b->bw->current_content == NULL )
return;
/* convert redraw coords to framebuffer coords: */
lclip.g_x -= work.g_x;
lclip.g_y -= work.g_y;
if( lclip.g_x < 0 ) {
lclip.g_w = work.g_w + lclip.g_x;
lclip.g_x = 0;
}
if( lclip.g_y < 0 ) {
lclip.g_h = work.g_h + lclip.g_y;
lclip.g_y = 0;
}
if( lclip.g_h > 0 && lclip.g_w > 0 ) {
browser_schedule_redraw( gw, lclip.g_x, lclip.g_y,
lclip.g_x + lclip.g_w, lclip.g_y + lclip.g_h
);
}
return;
}