diff options
author | Ole Loots <ole@monochrom.net> | 2013-01-08 23:36:09 +0100 |
---|---|---|
committer | Ole Loots <ole@monochrom.net> | 2013-01-08 23:36:09 +0100 |
commit | b96d7afc771caf891a256adc82bd9e8a2dad9b7c (patch) | |
tree | 0c015f5c122fb8355bd86ad7e961433bdd46e33f | |
parent | 13c71436ba6e81be6dc46e245bf1fff43e2e702c (diff) | |
parent | d1dabbb0e28edc5614940c08d594957faa956daf (diff) | |
download | netsurf-b96d7afc771caf891a256adc82bd9e8a2dad9b7c.tar.gz netsurf-b96d7afc771caf891a256adc82bd9e8a2dad9b7c.tar.bz2 |
Merge branch 'master' into mono/removing-windom-dependency
63 files changed, 4153 insertions, 3901 deletions
diff --git a/Makefile.sources.javascript b/Makefile.sources.javascript index 4633e9d0d..bcdd68501 100644 --- a/Makefile.sources.javascript +++ b/Makefile.sources.javascript @@ -20,6 +20,7 @@ JSAPI_BINDING_location := javascript/jsapi/location.bnd JSAPI_BINDING_htmlcollection := javascript/jsapi/htmlcollection.bnd JSAPI_BINDING_nodelist := javascript/jsapi/nodelist.bnd JSAPI_BINDING_text := javascript/jsapi/text.bnd +JSAPI_BINDING_comment := javascript/jsapi/comment.bnd JSAPI_BINDING_node := javascript/jsapi/node.bnd JSAPI_BINDING_event := javascript/jsapi/event.bnd diff --git a/amiga/arexx.c b/amiga/arexx.c index c3279ad3d..7048c0673 100755 --- a/amiga/arexx.c +++ b/amiga/arexx.c @@ -275,7 +275,8 @@ STATIC VOID rx_save(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unu if(!bw) return; - ami_update_pointer(bw->window->shared->win,GUI_POINTER_WAIT); + ami_set_pointer(bw->window->shared, GUI_POINTER_WAIT, false); + if(fh = FOpen((char *)cmd->ac_ArgList[0], MODE_NEWFILE, 0)) { if(source_data = content_get_source_data(bw->current_content, &source_size)) @@ -285,7 +286,7 @@ STATIC VOID rx_save(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unu SetComment((char *)cmd->ac_ArgList[0], nsurl_access(hlcache_handle_get_url(bw->current_content))); } - ami_update_pointer(bw->window->shared->win,GUI_POINTER_DEFAULT); + ami_reset_pointer(bw->window->shared); } STATIC VOID rx_quit(struct ARexxCmd *cmd, struct RexxMsg *rxm __attribute__((unused))) diff --git a/amiga/clipboard.c b/amiga/clipboard.c index b24c2da65..018ea2604 100755 --- a/amiga/clipboard.c +++ b/amiga/clipboard.c @@ -137,7 +137,7 @@ bool ami_clipboard_check_for_utf8(struct IFFHandle *iffh) { return utf8_chunk; } -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) +void gui_get_clipboard(char **buffer, size_t *length) { /* This and the other clipboard code is heavily based on the RKRM examples */ struct ContextNode *cn; @@ -193,7 +193,7 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) rlen, &clip); } - browser_window_paste_text(g->shared->bw,clip,rlen,true); + //browser_window_paste_text(g->shared->bw,clip,rlen,true); } if(rlen < 0) error = rlen; } @@ -202,7 +202,7 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) { while((rlen = ReadChunkBytes(iffh, readbuf, 1024)) > 0) { - browser_window_paste_text(g->shared->bw, readbuf, rlen, true); + //browser_window_paste_text(g->shared->bw, readbuf, rlen, true); } if(rlen < 0) error = rlen; } @@ -210,22 +210,24 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) CloseIFF(iffh); } -bool gui_empty_clipboard(void) +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { - /* Put a half-completed FTXT on the clipboard and leave it open for more additions */ - + char *text; struct CSet cset = {0}; - if(!(OpenIFF(iffh,IFFF_WRITE))) + if(buffer == NULL) return; + + if(!(OpenIFF(iffh, IFFF_WRITE))) { - if(!(PushChunk(iffh,ID_FTXT,ID_FORM,IFFSIZE_UNKNOWN))) + if(!(PushChunk(iffh, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN))) { if(nsoption_bool(utf8_clipboard)) { - if(!(PushChunk(iffh,0,ID_CSET,32))) + if(!(PushChunk(iffh, 0, ID_CSET, 32))) { cset.CodeSet = 106; // UTF-8 - WriteChunkBytes(iffh,&cset,32); + WriteChunkBytes(iffh, &cset, 32); PopChunk(iffh); } } @@ -233,82 +235,39 @@ bool gui_empty_clipboard(void) else { PopChunk(iffh); - return false; } - return true; - } - return false; -} - -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) -{ - /* This might crash or at least not work if gui_empty_clipboard isn't called first, - and gui_commit_clipboard after. - These only seem to be called from desktop/textinput.c in this specific order, if they - are added elsewhere this might need a rewrite. */ - - char *buffer; - - if(text == NULL) return true; - - if(!(PushChunk(iffh,0,ID_CHRS,IFFSIZE_UNKNOWN))) { - if(nsoption_bool(utf8_clipboard)) { - WriteChunkBytes(iffh,text,length); - } else { - if(utf8_to_local_encoding(text, length, &buffer) == UTF8_CONVERT_OK) { - char *p; - p = buffer; - - while(*p != '\0') { - if(*p == 0xa0) *p = 0x20; - p++; + if(!(PushChunk(iffh, 0, ID_CHRS, IFFSIZE_UNKNOWN))) { + if(nsoption_bool(utf8_clipboard)) { + WriteChunkBytes(iffh, buffer, length); + } else { + if(utf8_to_local_encoding(buffer, length, &text) == UTF8_CONVERT_OK) { + char *p; + + p = text; + + while(*p != '\0') { + if(*p == 0xa0) *p = 0x20; + p++; + } + WriteChunkBytes(iffh, text, strlen(text)); + ami_utf8_free(text); } - WriteChunkBytes(iffh, buffer, strlen(buffer)); - ami_utf8_free(buffer); } - } - if(space) WriteChunkBytes(iffh," ",1); - PopChunk(iffh); - } else { - PopChunk(iffh); - return false; - } + PopChunk(iffh); + } else { + PopChunk(iffh); + } - if(!(PushChunk(iffh, 0, ID_UTF8, IFFSIZE_UNKNOWN))) { - WriteChunkBytes(iffh, text, length); - if(space) WriteChunkBytes(iffh, " ", 1); - PopChunk(iffh); - } else { - PopChunk(iffh); - return false; + if(!(PushChunk(iffh, 0, ID_UTF8, IFFSIZE_UNKNOWN))) { + WriteChunkBytes(iffh, buffer, length); + PopChunk(iffh); + } else { + PopChunk(iffh); + } + CloseIFF(iffh); } - - return true; -} - -bool gui_commit_clipboard(void) -{ - if(iffh) CloseIFF(iffh); - - return true; -} - -bool gui_copy_to_clipboard(struct selection *s) -{ - bool success; - - if(s->defined == false) return false; - if(!gui_empty_clipboard()) return false; - - success = selection_copy_to_clipboard(s); - - /* commit regardless, otherwise we leave the clipboard in an unusable state */ - gui_commit_clipboard(); - - return success; } struct ami_text_selection *ami_selection_to_text(struct gui_window_2 *gwin) @@ -361,13 +320,14 @@ void ami_drag_selection(struct selection *s) if(ami_text_box_at_point(gwin, (ULONG *)&x, (ULONG *)&y)) { iffh = ami_clipboard_init_internal(1); - +#if 0 +/* TODO: fix this */ if(gui_copy_to_clipboard(s)) { browser_window_mouse_click(gwin->bw, BROWSER_MOUSE_PRESS_1, x, y); browser_window_key_press(gwin->bw, KEY_PASTE); } - +#endif ami_clipboard_free_internal(iffh); iffh = old_iffh; } @@ -407,11 +367,7 @@ void ami_drag_selection(struct selection *s) bool ami_easy_clipboard(char *text) { - if(!gui_empty_clipboard()) return false; - if(!gui_add_to_clipboard(text,strlen(text),false,plot_style_font)) - return false; - if(!gui_commit_clipboard()) return false; - + gui_set_clipboard(text, strlen(text), NULL, 0); return true; } diff --git a/amiga/download.c b/amiga/download.c index c0c88bb0b..c49bd8554 100644 --- a/amiga/download.c +++ b/amiga/download.c @@ -356,7 +356,8 @@ gui_window_save_link(struct gui_window *g, const char *url, const char *title) { strlcpy(fname, savereq->fr_Drawer, 1024); AddPart(fname,savereq->fr_File,1024); - ami_update_pointer(g->shared->win,GUI_POINTER_WAIT); + + ami_set_pointer(g->shared, GUI_POINTER_WAIT, false); if(ami_download_check_overwrite(fname, g->shared->win, 0)) { @@ -383,7 +384,7 @@ gui_window_save_link(struct gui_window *g, const char *url, const char *title) } FreeVec(linkname); } - ami_update_pointer(g->shared->win,GUI_POINTER_DEFAULT); + ami_reset_pointer(g->shared); } } diff --git a/amiga/drag.c b/amiga/drag.c index e19b27393..addc4b3cf 100644 --- a/amiga/drag.c +++ b/amiga/drag.c @@ -142,7 +142,7 @@ void ami_drag_save(struct Window *win) return; } - ami_update_pointer(win,GUI_POINTER_WAIT); + ami_update_pointer(win, GUI_POINTER_WAIT); switch(drag_save) { @@ -189,7 +189,8 @@ void ami_drag_save(struct Window *win) drag_save = 0; drag_save_data = NULL; - ami_update_pointer(win,GUI_POINTER_DEFAULT); + + ami_update_pointer(win, GUI_POINTER_DEFAULT); } void ami_drag_icon_show(struct Window *win, const char *type) diff --git a/amiga/gui.c b/amiga/gui.c index e1fc19d7f..43412704a 100755 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -1435,7 +1435,7 @@ void ami_handle_msg(void) { ami_context_menu_mouse_trap(gwin, FALSE); - if(!gwin->mouse_state) ami_update_pointer(gwin->win,GUI_POINTER_DEFAULT); + if(!gwin->mouse_state) ami_set_pointer(gwin, GUI_POINTER_DEFAULT, true); } break; @@ -3599,7 +3599,7 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, struct rect clip; int tile_x_scale = (int)(nsoption_int(redraw_tile_size_x) / gwin->bw->scale); int tile_y_scale = (int)(nsoption_int(redraw_tile_size_y) / gwin->bw->scale); - + browserglob.shared_pens = &gwin->shared_pens; if(top < 0) { @@ -3632,6 +3632,8 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, // printf("%ld %ld %ld %ld\n",left, top, width, height); + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); + for(y = top; y < (top + height); y += tile_y_scale) { clip.y0 = 0; clip.y1 = nsoption_int(redraw_tile_size_y); @@ -3667,6 +3669,8 @@ void ami_do_redraw_tiled(struct gui_window_2 *gwin, } } } + + ami_reset_pointer(gwin); } @@ -3738,87 +3742,85 @@ void gui_window_update_box(struct gui_window *g, const struct rect *rect) rect->x1, rect->y1); } -void ami_do_redraw(struct gui_window_2 *g) +void ami_do_redraw(struct gui_window_2 *gwin) { struct Region *reg = NULL; struct Rectangle rect; hlcache_handle *c; ULONG hcurrent,vcurrent,xoffset,yoffset,width=800,height=600,x0=0,y0=0; struct IBox *bbox; - ULONG oldh=g->oldh,oldv=g->oldv; + ULONG oldh = gwin->oldh, oldv=gwin->oldv; bool morescroll = false; struct RastPort *temprp; - if(browser_window_redraw_ready(g->bw) == false) return; + if(browser_window_redraw_ready(gwin->bw) == false) return; - GetAttr(SPACE_AreaBox, (Object *)g->objects[GID_BROWSER], (ULONG *)&bbox); - ami_get_hscroll_pos(g, (ULONG *)&hcurrent); - ami_get_vscroll_pos(g, (ULONG *)&vcurrent); + GetAttr(SPACE_AreaBox, (Object *)gwin->objects[GID_BROWSER], (ULONG *)&bbox); + ami_get_hscroll_pos(gwin, (ULONG *)&hcurrent); + ami_get_vscroll_pos(gwin, (ULONG *)&vcurrent); - g->bw->window->scrollx = hcurrent; - g->bw->window->scrolly = vcurrent; + gwin->bw->window->scrollx = hcurrent; + gwin->bw->window->scrolly = vcurrent; - c = g->bw->current_content; + c = gwin->bw->current_content; width=bbox->Width; height=bbox->Height; xoffset=bbox->Left; yoffset=bbox->Top; - if(g->bw->reformat_pending) + if(gwin->bw->reformat_pending) { - browser_window_reformat(g->bw,false,width,height); - g->bw->reformat_pending = false; - g->redraw_scroll = false; + browser_window_reformat(gwin->bw, false, width, height); + gwin->bw->reformat_pending = false; + gwin->redraw_scroll = false; } - if(g->redraw_scroll) + if(gwin->redraw_scroll) { if((abs(vcurrent-oldv) > height) || (abs(hcurrent-oldh) > width)) - g->redraw_scroll = false; - - if(g->new_content) g->redraw_scroll = false; + gwin->redraw_scroll = false; - //if(g->bw->scale != 1.0) g->redraw_scroll = false; + if(gwin->new_content) gwin->redraw_scroll = false; } - if(g->redraw_scroll) + if(gwin->redraw_scroll) { - g->bw->window->c_h_temp = g->bw->window->c_h; - gui_window_remove_caret(g->bw->window); + gwin->bw->window->c_h_temp = gwin->bw->window->c_h; + gui_window_remove_caret(gwin->bw->window); - ScrollWindowRaster(g->win,hcurrent-oldh,vcurrent-oldv, - xoffset,yoffset,xoffset+width-1,yoffset+height-1); + ScrollWindowRaster(gwin->win, hcurrent - oldh, vcurrent - oldv, + xoffset, yoffset, xoffset + width - 1, yoffset + height - 1); - g->bw->window->c_h = g->bw->window->c_h_temp; + gwin->bw->window->c_h = gwin->bw->window->c_h_temp; if(vcurrent>oldv) /* Going down */ { - ami_do_redraw_limits(g->bw->window, g->bw, - hcurrent, (height / g->bw->scale) + oldv - 1, - hcurrent + (width / g->bw->scale), - vcurrent + (height / g->bw->scale) + 1); + ami_do_redraw_limits(gwin->bw->window, gwin->bw, + hcurrent, (height / gwin->bw->scale) + oldv - 1, + hcurrent + (width / gwin->bw->scale), + vcurrent + (height / gwin->bw->scale) + 1); } else if(vcurrent<oldv) /* Going up */ { - ami_do_redraw_limits(g->bw->window, g->bw, + ami_do_redraw_limits(gwin->bw->window, gwin->bw, hcurrent, vcurrent, - hcurrent + (width / g->bw->scale), + hcurrent + (width / gwin->bw->scale), oldv); } if(hcurrent>oldh) /* Going right */ { - ami_do_redraw_limits(g->bw->window, g->bw, - (width / g->bw->scale) + oldh , vcurrent, - hcurrent + (width / g->bw->scale), - vcurrent + (height / g->bw->scale)); + ami_do_redraw_limits(gwin->bw->window, gwin->bw, + (width / gwin->bw->scale) + oldh , vcurrent, + hcurrent + (width / gwin->bw->scale), + vcurrent + (height / gwin->bw->scale)); } else if(hcurrent<oldh) /* Going left */ { - ami_do_redraw_limits(g->bw->window, g->bw, + ami_do_redraw_limits(gwin->bw->window, gwin->bw, hcurrent, vcurrent, - oldh, vcurrent + (height / g->bw->scale)); + oldh, vcurrent + (height / gwin->bw->scale)); } } else @@ -3834,34 +3836,38 @@ void ami_do_redraw(struct gui_window_2 *g) if(nsoption_bool(direct_render) == false) { - ami_do_redraw_tiled(g, hcurrent, vcurrent, width, height, hcurrent, vcurrent, bbox, &ctx); + ami_do_redraw_tiled(gwin, hcurrent, vcurrent, width, height, hcurrent, vcurrent, bbox, &ctx); } else { - browserglob.shared_pens = &g->shared_pens; + browserglob.shared_pens = &gwin->shared_pens; temprp = browserglob.rp; - browserglob.rp = g->win->RPort; + browserglob.rp = gwin->win->RPort; clip.x0 = bbox->Left; clip.y0 = bbox->Top; clip.x1 = bbox->Left + bbox->Width; clip.y1 = bbox->Top + bbox->Height; - if(browser_window_redraw(g->bw, clip.x0 - hcurrent, clip.y0 - vcurrent, &clip, &ctx)) + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); + + if(browser_window_redraw(gwin->bw, clip.x0 - hcurrent, clip.y0 - vcurrent, &clip, &ctx)) { ami_clearclipreg(&browserglob); browserglob.rp = temprp; } + + ami_reset_pointer(gwin); } } - ami_update_buttons(g); + ami_update_buttons(gwin); - g->oldh = hcurrent; - g->oldv = vcurrent; + gwin->oldh = hcurrent; + gwin->oldv = vcurrent; - g->redraw_scroll = false; - g->redraw_required = false; - g->new_content = false; + gwin->redraw_scroll = false; + gwin->redraw_required = false; + gwin->new_content = false; } void ami_refresh_window(struct gui_window_2 *gwin) diff --git a/amiga/gui.h b/amiga/gui.h index 1dff5d3c0..ff467977a 100755 --- a/amiga/gui.h +++ b/amiga/gui.h @@ -120,6 +120,7 @@ struct gui_window_2 { struct IBox *ptr_lock; struct AppWindow *appwin; struct MinList shared_pens; + gui_pointer_shape mouse_pointer; }; struct gui_window diff --git a/amiga/gui_options.c b/amiga/gui_options.c index 66e47d8ad..e54ad15a6 100755 --- a/amiga/gui_options.c +++ b/amiga/gui_options.c @@ -1502,10 +1502,7 @@ void ami_gui_opts_use(bool save) bool rescan_fonts = false; bool old_tab_always_show; - SetWindowPointer(gow->win, - WA_BusyPointer, TRUE, - WA_PointerDelay, TRUE, - TAG_DONE); + ami_update_pointer(gow->win, GUI_POINTER_WAIT); GetAttr(STRINGA_TextVal,gow->objects[GID_OPTS_HOMEPAGE],(ULONG *)&data); nsoption_set_charp(homepage_url, (char *)strdup((char *)data)); @@ -1863,9 +1860,7 @@ void ami_gui_opts_use(bool save) ami_menu_check_toggled = true; - SetWindowPointer(gow->win, - WA_Pointer, NULL, - TAG_DONE); + ami_update_pointer(gow->win, GUI_POINTER_DEFAULT); } void ami_gui_opts_close(void) diff --git a/amiga/menu.c b/amiga/menu.c index 58a4d5115..496400dfb 100755 --- a/amiga/menu.c +++ b/amiga/menu.c @@ -341,7 +341,6 @@ void ami_init_menulabs(struct gui_window_2 *gwin) gwin->menutype[41] = NM_ITEM; gwin->menulab[41] = ami_utf8_easy((char *)messages_get("EnableJS")); gwin->menu_hook[41].h_Entry = (HOOKFUNC)ami_menu_item_browser_enablejs; - gwin->menukey[41] = 'J'; gwin->menutype[42] = NM_ITEM; gwin->menulab[42] = NM_BARLABEL; @@ -754,9 +753,9 @@ static void ami_menu_item_project_print(struct Hook *hook, APTR window, struct I struct gui_window_2 *gwin; GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin); - ami_update_pointer(gwin->win,GUI_POINTER_WAIT); + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); ami_print_ui(gwin->bw->current_content); - ami_update_pointer(gwin->win,GUI_POINTER_DEFAULT); + ami_reset_pointer(gwin); } static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct IntuiMessage *msg) @@ -767,7 +766,7 @@ static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct I GetAttr(WINDOW_UserData, (Object *)window, (ULONG *)&gwin); - ami_update_pointer(gwin->win,GUI_POINTER_WAIT); + ami_set_pointer(gwin, GUI_POINTER_WAIT, false); temp = ASPrintf("%s|%s|%s", messages_get("OK"), messages_get("HelpCredits"), @@ -801,7 +800,7 @@ static void ami_menu_item_project_about(struct Hook *hook, APTR window, struct I else if(sel == 0) browser_window_create("about:licence", NULL, 0, true, false); - ami_update_pointer(gwin->win,GUI_POINTER_DEFAULT); + ami_reset_pointer(gwin); } static void ami_menu_item_project_quit(struct Hook *hook, APTR window, struct IntuiMessage *msg) diff --git a/amiga/search.c b/amiga/search.c index 6f8ebd414..1148f6d77 100755 --- a/amiga/search.c +++ b/amiga/search.c @@ -36,9 +36,10 @@ #include "amiga/os3support.h" #include "amiga/search.h" #include "amiga/object.h" +#include "amiga/theme.h" + #include <proto/intuition.h> #include <proto/exec.h> - #include <proto/window.h> #include <proto/layout.h> #include <proto/string.h> @@ -277,10 +278,10 @@ void ami_search_set_status(bool found, void *p) void ami_search_set_hourglass(bool active, void *p) { - SetWindowPointer(fwin->win, - WA_BusyPointer,active, - WA_PointerDelay,active, - TAG_DONE); + if(active) + ami_update_pointer(fwin->win, GUI_POINTER_WAIT); + else + ami_update_pointer(fwin->win, GUI_POINTER_DEFAULT); } /** diff --git a/amiga/theme.c b/amiga/theme.c index fce69eda3..65d2f51e1 100644 --- a/amiga/theme.c +++ b/amiga/theme.c @@ -48,7 +48,6 @@ struct bitmap *throbber_nsbm = NULL; ULONG throbber_frames,throbber_update_interval; static Object *mouseptrobj[AMI_LASTPOINTER+1]; static struct BitMap *mouseptrbm[AMI_LASTPOINTER+1]; -static int mouseptrcurrent=0; char *ptrs[AMI_LASTPOINTER+1] = { "ptr_default", @@ -174,12 +173,24 @@ void ami_get_theme_filename(char *filename, char *themestring, bool protocol) void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape) { - ami_update_pointer(g->shared->win,shape); + ami_set_pointer(g->shared, shape, true); +} + +void ami_set_pointer(struct gui_window_2 *gwin, gui_pointer_shape shape, bool update) +{ + if(gwin->mouse_pointer == shape) return; + ami_update_pointer(gwin->win, shape); + if(update == true) gwin->mouse_pointer = shape; +} + +/* reset the mouse pointer back to what NetSurf last set it as */ +void ami_reset_pointer(struct gui_window_2 *gwin) +{ + ami_update_pointer(gwin->win, gwin->mouse_pointer); } void ami_update_pointer(struct Window *win, gui_pointer_shape shape) { - if(mouseptrcurrent == shape) return; if(drag_save_data) return; if(nsoption_bool(use_os_pointers)) @@ -187,24 +198,24 @@ void ami_update_pointer(struct Window *win, gui_pointer_shape shape) switch(shape) { case GUI_POINTER_DEFAULT: - SetWindowPointer(win,TAG_DONE); + SetWindowPointer(win, TAG_DONE); break; case GUI_POINTER_WAIT: SetWindowPointer(win, - WA_BusyPointer,TRUE, - WA_PointerDelay,TRUE, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, TAG_DONE); break; default: if(mouseptrobj[shape]) { - SetWindowPointer(win,WA_Pointer,mouseptrobj[shape],TAG_DONE); + SetWindowPointer(win, WA_Pointer, mouseptrobj[shape], TAG_DONE); } else { - SetWindowPointer(win,TAG_DONE); + SetWindowPointer(win, TAG_DONE); } break; } @@ -213,34 +224,28 @@ void ami_update_pointer(struct Window *win, gui_pointer_shape shape) { if(mouseptrobj[shape]) { - SetWindowPointer(win,WA_Pointer,mouseptrobj[shape],TAG_DONE); + SetWindowPointer(win, WA_Pointer, mouseptrobj[shape], TAG_DONE); } else { if(shape == GUI_POINTER_WAIT) { SetWindowPointer(win, - WA_BusyPointer,TRUE, - WA_PointerDelay,TRUE, + WA_BusyPointer, TRUE, + WA_PointerDelay, TRUE, TAG_DONE); } else { - SetWindowPointer(win,TAG_DONE); + SetWindowPointer(win, TAG_DONE); } } } - - mouseptrcurrent = shape; } void gui_window_hide_pointer(struct gui_window *g) { - if(mouseptrcurrent != AMI_GUI_POINTER_BLANK) - { - SetWindowPointer(g->shared->win,WA_Pointer,mouseptrobj[AMI_GUI_POINTER_BLANK],TAG_DONE); - mouseptrcurrent = AMI_GUI_POINTER_BLANK; - } + ami_set_pointer(g->shared, AMI_GUI_POINTER_BLANK, true); } void ami_init_mouse_pointers(void) diff --git a/amiga/theme.h b/amiga/theme.h index ba1295d61..3c3931c12 100644 --- a/amiga/theme.h +++ b/amiga/theme.h @@ -35,5 +35,10 @@ void ami_update_throbber(struct gui_window_2 *g,bool redraw); void ami_init_mouse_pointers(void); void ami_mouse_pointers_free(void); -void ami_update_pointer(struct Window *win, gui_pointer_shape shape); +void ami_set_pointer(struct gui_window_2 *gwin, gui_pointer_shape shape, bool update); +void ami_reset_pointer(struct gui_window_2 *gwin); +/* Use the following ONLY if nothing other than the Intuition window pointer is available, + * and ALWAYS in preference to SetWindowPointer(), as it features more pointers and uses + * the correct ones specified in user preferences. */ +void ami_update_pointer(struct Window *win, gui_pointer_shape shape); #endif diff --git a/amiga/tree.c b/amiga/tree.c index 95a68dc20..b19cd203e 100755 --- a/amiga/tree.c +++ b/amiga/tree.c @@ -991,12 +991,12 @@ BOOL ami_tree_event(struct treeview_window *twin) { strlcpy(fname,savereq->fr_Drawer,1024); AddPart(fname,savereq->fr_File,1024); - ami_update_pointer(twin->win,GUI_POINTER_WAIT); + ami_update_pointer(twin->win, GUI_POINTER_WAIT); if(twin->type == AMI_TREE_HISTORY) history_global_export(fname); else if(twin->type == AMI_TREE_HOTLIST) hotlist_export(fname); - ami_update_pointer(twin->win,GUI_POINTER_DEFAULT); + ami_update_pointer(twin->win, GUI_POINTER_DEFAULT); } break; @@ -1237,9 +1237,9 @@ void ami_tree_redraw_request(int x, int y, int width, int height, void *data) }; if(!twin->win) return; -// if(tree_get_redraw(twin->tree) == false) return; ami_update_pointer(twin->win, GUI_POINTER_WAIT); + glob = &twin->globals; GetAttr(SPACE_AreaBox,twin->objects[GID_BROWSER],(ULONG *)&bbox); diff --git a/beos/fetch_rsrc.cpp b/beos/fetch_rsrc.cpp index f7c99d7c4..6f78aafa5 100644 --- a/beos/fetch_rsrc.cpp +++ b/beos/fetch_rsrc.cpp @@ -85,7 +85,7 @@ static bool fetch_rsrc_can_fetch(const nsurl *url) } static void *fetch_rsrc_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { diff --git a/cocoa/selection.m b/cocoa/selection.m index 6bba74d34..ef34b4e94 100644 --- a/cocoa/selection.m +++ b/cocoa/selection.m @@ -28,7 +28,6 @@ static NSMutableString *cocoa_clipboard_string; void gui_start_selection(struct gui_window *g) { - gui_empty_clipboard(); } void gui_clear_selection(struct gui_window *g) @@ -36,53 +35,73 @@ void gui_clear_selection(struct gui_window *g) } -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length) { NSPasteboard *pb = [NSPasteboard generalPasteboard]; NSString *string = [pb stringForType: NSStringPboardType]; + + *buffer = NULL; + *length = 0; + if (string) { const char *text = [string UTF8String]; - browser_window_paste_text( [(BrowserViewController *)g browser], text, strlen(text), true ); + *length = strlen(text); + + *buffer = malloc(*length); + + if (*buffer != NULL) { + memcpy(*buffer, text, *length); + } else { + *length = 0; + } } } -bool gui_empty_clipboard(void) +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { + /* Empty clipboard string */ if (nil == cocoa_clipboard_string) { cocoa_clipboard_string = [[NSMutableString alloc] init]; } else { [cocoa_clipboard_string setString: @""]; } - return true; -} -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) -{ + /* Add text to clipboard string */ if (nil == cocoa_clipboard_string) return false; - [cocoa_clipboard_string appendString: [[[NSString alloc] initWithBytes: text - length: length - encoding: NSUTF8StringEncoding] - autorelease]]; - if (space) [cocoa_clipboard_string appendString: @" "]; + [cocoa_clipboard_string appendString: [[[NSString alloc] + initWithBytes: buffer + length: length + encoding: NSUTF8StringEncoding] + autorelease]]; - return true; -} - -bool gui_commit_clipboard(void) -{ + /* Stick it on the pasteboard */ NSPasteboard *pb = [NSPasteboard generalPasteboard]; [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil]; bool result = [pb setString: cocoa_clipboard_string forType: NSStringPboardType]; - if (result) gui_empty_clipboard(); - return result; -} -bool gui_copy_to_clipboard(struct selection *s) -{ - if (selection_defined( s ) && selection_copy_to_clipboard( s )) - gui_commit_clipboard(); - return true; + if (result) { + /* Empty clipboard string */ + if (nil == cocoa_clipboard_string) { + cocoa_clipboard_string = [[NSMutableString alloc] init]; + } else { + [cocoa_clipboard_string setString: @""]; + } + } } diff --git a/content/content.h b/content/content.h index 649f54dfa..7781ba9b8 100644 --- a/content/content.h +++ b/content/content.h @@ -78,8 +78,7 @@ typedef enum { CONTENT_MSG_SCROLL, /**< Request to scroll content */ CONTENT_MSG_DRAGSAVE, /**< Allow drag saving of content */ CONTENT_MSG_SAVELINK, /**< Allow URL to be saved */ - CONTENT_MSG_POINTER, /**< Wants a specific mouse pointer set */ - CONTENT_MSG_PASTE /**< Inform that content wants clipboard paste */ + CONTENT_MSG_POINTER /**< Wants a specific mouse pointer set */ } content_msg; /** RFC5988 metadata link */ diff --git a/content/fetch.c b/content/fetch.c index 028fe9ee4..bfe4458cf 100644 --- a/content/fetch.c +++ b/content/fetch.c @@ -235,7 +235,8 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, fetch_callback callback, void *p, bool only_2xx, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, - bool verifiable, const char *headers[]) + bool verifiable, bool downgrade_tls, + const char *headers[]) { struct fetch *fetch; scheme_fetcher *fetcher = fetchers; @@ -321,8 +322,9 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, /* Got a scheme fetcher, try and set up the fetch */ fetch->fetcher_handle = fetch->ops->setup_fetch(fetch, url, - only_2xx, post_urlenc, - post_multipart, headers); + only_2xx, downgrade_tls, + post_urlenc, post_multipart, + headers); if (fetch->fetcher_handle == NULL) goto failed; diff --git a/content/fetch.h b/content/fetch.h index d7cdced1b..d23b3cd4b 100644 --- a/content/fetch.h +++ b/content/fetch.h @@ -43,7 +43,8 @@ typedef enum { FETCH_REDIRECT, FETCH_NOTMODIFIED, FETCH_AUTH, - FETCH_CERT_ERR + FETCH_CERT_ERR, + FETCH_SSL_ERR } fetch_msg_type; typedef struct fetch_msg { @@ -103,7 +104,7 @@ struct fetch * fetch_start(nsurl *url, nsurl *referer, fetch_callback callback, void *p, bool only_2xx, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, - bool verifiable, + bool verifiable, bool downgrade_tls, const char *headers[]); void fetch_abort(struct fetch *f); void fetch_poll(void); @@ -123,17 +124,17 @@ struct fetch_multipart_data *fetch_multipart_data_clone( /* API for fetchers themselves */ -typedef bool (*fetcher_initialise)(lwc_string *); -typedef bool (*fetcher_can_fetch)(const nsurl *); -typedef void* (*fetcher_setup_fetch)(struct fetch *, nsurl *, - bool, const char *, - const struct fetch_multipart_data *, - const char **); -typedef bool (*fetcher_start_fetch)(void *); -typedef void (*fetcher_abort_fetch)(void *); -typedef void (*fetcher_free_fetch)(void *); -typedef void (*fetcher_poll_fetcher)(lwc_string *); -typedef void (*fetcher_finalise)(lwc_string *); +typedef bool (*fetcher_initialise)(lwc_string *scheme); +typedef bool (*fetcher_can_fetch)(const nsurl *url); +typedef void *(*fetcher_setup_fetch)(struct fetch *parent_fetch, nsurl *url, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, + const struct fetch_multipart_data *post_multipart, + const char **headers); +typedef bool (*fetcher_start_fetch)(void *fetch); +typedef void (*fetcher_abort_fetch)(void *fetch); +typedef void (*fetcher_free_fetch)(void *fetch); +typedef void (*fetcher_poll_fetcher)(lwc_string *scheme); +typedef void (*fetcher_finalise)(lwc_string *scheme); /** Register a fetcher for a scheme * diff --git a/content/fetchers/about.c b/content/fetchers/about.c index 78f22df01..387e32a98 100644 --- a/content/fetchers/about.c +++ b/content/fetchers/about.c @@ -729,6 +729,7 @@ static void * fetch_about_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c index ff7ccbe30..668e05a46 100644 --- a/content/fetchers/curl.c +++ b/content/fetchers/curl.c @@ -77,6 +77,7 @@ struct curl_fetch_info { bool abort; /**< Abort requested. */ bool stopped; /**< Download stopped on purpose. */ bool only_2xx; /**< Only HTTP 2xx responses acceptable. */ + bool downgrade_tls; /**< Downgrade to TLS <= 1.0 */ nsurl *url; /**< URL of this fetch. */ lwc_string *host; /**< The hostname of this fetch. */ struct curl_slist *headers; /**< List of request headers. */ @@ -114,7 +115,7 @@ static bool fetch_curl_initialise(lwc_string *scheme); static void fetch_curl_finalise(lwc_string *scheme); static bool fetch_curl_can_fetch(const nsurl *url); static void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers); static bool fetch_curl_start(void *vfetch); @@ -348,7 +349,7 @@ bool fetch_curl_can_fetch(const nsurl *url) */ void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { @@ -370,6 +371,7 @@ void * fetch_curl_setup(struct fetch *parent_fetch, nsurl *url, fetch->abort = false; fetch->stopped = false; fetch->only_2xx = only_2xx; + fetch->downgrade_tls = downgrade_tls; fetch->headers = NULL; fetch->url = nsurl_ref(url); fetch->host = nsurl_get_component(url, NSURL_HOST); @@ -665,14 +667,28 @@ fetch_curl_set_options(struct curl_fetch_info *f) CURLcode fetch_curl_sslctxfun(CURL *curl_handle, void *_sslctx, void *parm) { + struct curl_fetch_info *f = (struct curl_fetch_info *) parm; SSL_CTX *sslctx = _sslctx; + long options = SSL_OP_ALL; + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, fetch_curl_verify_callback); SSL_CTX_set_cert_verify_callback(sslctx, fetch_curl_cert_verify_callback, parm); + + if (f->downgrade_tls) { +#ifdef SSL_OP_NO_TLSv1_1 + /* Disable TLS1.1, if the server can't cope with it */ + options |= SSL_OP_NO_TLSv1_1; +#endif + } + #ifdef SSL_OP_NO_TLSv1_2 /* Disable TLS1.2, as it causes some servers to stall. */ - SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2); + options |= SSL_OP_NO_TLSv1_2; #endif + + SSL_CTX_set_options(sslctx, options); + return CURLE_OK; } @@ -851,17 +867,16 @@ void fetch_curl_done(CURL *curl_handle, CURLcode result) else { finished = true; } - } else if (result == CURLE_WRITE_ERROR && f->stopped) + } else if (result == CURLE_WRITE_ERROR && f->stopped) { /* CURLE_WRITE_ERROR occurs when fetch_curl_data * returns 0, which we use to abort intentionally */ ; - else if (result == CURLE_SSL_PEER_CERTIFICATE || + } else if (result == CURLE_SSL_PEER_CERTIFICATE || result == CURLE_SSL_CACERT) { memcpy(certs, f->cert_data, sizeof(certs)); memset(f->cert_data, 0, sizeof(f->cert_data)); cert = true; - } - else { + } else { LOG(("Unknown cURL response code %d", result)); error = true; } @@ -955,8 +970,12 @@ void fetch_curl_done(CURL *curl_handle, CURLcode result) msg.data.cert_err.num_certs = i; fetch_send_callback(&msg, f->fetch_handle); } else if (error) { - msg.type = FETCH_ERROR; - msg.data.error = fetch_error_buffer; + if (result != CURLE_SSL_CONNECT_ERROR) { + msg.type = FETCH_ERROR; + msg.data.error = fetch_error_buffer; + } else { + msg.type = FETCH_SSL_ERR; + } fetch_send_callback(&msg, f->fetch_handle); } diff --git a/content/fetchers/data.c b/content/fetchers/data.c index 3f8989e8c..77d3c9f9c 100644 --- a/content/fetchers/data.c +++ b/content/fetchers/data.c @@ -81,7 +81,7 @@ static bool fetch_data_can_fetch(const nsurl *url) } static void *fetch_data_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, const char *post_urlenc, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) { diff --git a/content/fetchers/file.c b/content/fetchers/file.c index 4c02c0c60..b7b831d7b 100644 --- a/content/fetchers/file.c +++ b/content/fetchers/file.c @@ -128,6 +128,7 @@ static void * fetch_file_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/fetchers/resource.c b/content/fetchers/resource.c index 86d5498d5..1fc33f882 100644 --- a/content/fetchers/resource.c +++ b/content/fetchers/resource.c @@ -231,6 +231,7 @@ static void * fetch_resource_setup(struct fetch *fetchh, nsurl *url, bool only_2xx, + bool downgrade_tls, const char *post_urlenc, const struct fetch_multipart_data *post_multipart, const char **headers) diff --git a/content/llcache.c b/content/llcache.c index ed5cc6eda..8996acadd 100644 --- a/content/llcache.c +++ b/content/llcache.c @@ -84,6 +84,8 @@ typedef struct { bool tried_with_auth; /**< Whether we've tried with auth */ + bool tried_with_tls_downgrade; /**< Whether we've tried TLS <= 1.0 */ + bool outstanding_query; /**< Waiting for a query response */ } llcache_fetch_ctx; @@ -711,6 +713,7 @@ static nserror llcache_object_refetch(llcache_object *object) object->fetch.flags & LLCACHE_RETRIEVE_NO_ERROR_PAGES, urlenc, multipart, object->fetch.flags & LLCACHE_RETRIEVE_VERIFIABLE, + object->fetch.tried_with_tls_downgrade, (const char **) headers); /* Clean up cache-control headers */ @@ -843,14 +846,14 @@ static nserror llcache_object_add_to_list(llcache_object *object, } /** - * Determine if an object is still fresh + * Determine the remaining lifetime of a cache object using the * * \param object Object to consider * \return True if object is still fresh, false otherwise */ -static bool llcache_object_is_fresh(const llcache_object *object) +static int +llcache_object_rfc2616_remaining_lifetime(const llcache_cache_control *cd) { - const llcache_cache_control *cd = &object->cache; int current_age, freshness_lifetime; time_t now = time(NULL); @@ -870,24 +873,51 @@ static bool llcache_object_is_fresh(const llcache_object *object) freshness_lifetime = 0; #ifdef LLCACHE_TRACE - LOG(("%p: (%d > %d || %d != %d)", object, - freshness_lifetime, current_age, - object->fetch.state, LLCACHE_FETCH_COMPLETE)); + LOG(("%d:%d", freshness_lifetime, current_age)); +#endif + + if ((cd->no_cache == LLCACHE_VALIDATE_FRESH) && + (freshness_lifetime > current_age)) { + /* object was not forbidden from being returned from + * the cache unvalidated (i.e. the response contained + * a no-cache directive) + * + * The object current age is within the freshness lifetime. + */ + return freshness_lifetime - current_age; + } + + return 0; /* object has no remaining lifetime */ +} + +/** + * Determine if an object is still fresh + * + * \param object Object to consider + * \return True if object is still fresh, false otherwise + */ +static bool llcache_object_is_fresh(const llcache_object *object) +{ + int remaining_lifetime; + const llcache_cache_control *cd = &object->cache; + + remaining_lifetime = llcache_object_rfc2616_remaining_lifetime(cd); + +#ifdef LLCACHE_TRACE + LOG(("%p: (%d > 0 || %d != %d)", object, + remaining_lifetime, + object->fetch.state, LLCACHE_FETCH_COMPLETE)); #endif /* The object is fresh if: + * - it was not forbidden from being returned from the cache + * unvalidated. * - * it was not forbidden from being returned from the cache - * unvalidated (i.e. the response contained a no-cache directive) - * - * and: - * - * its current age is within the freshness lifetime - * or if we're still fetching the object + * - it has remaining lifetime or still being fetched. */ - return (cd->no_cache == LLCACHE_VALIDATE_FRESH && - (freshness_lifetime > current_age || - object->fetch.state != LLCACHE_FETCH_COMPLETE)); + return ((cd->no_cache == LLCACHE_VALIDATE_FRESH) && + ((remaining_lifetime > 0) || + (object->fetch.state != LLCACHE_FETCH_COMPLETE))); } /** @@ -1544,6 +1574,45 @@ static nserror llcache_fetch_cert_error(llcache_object *object, } /** + * Handle a TLS connection setup failure + * + * \param object Object being fetched + * \return NSERROR_OK on success, appropriate error otherwise + */ +static nserror llcache_fetch_ssl_error(llcache_object *object) +{ + nserror error = NSERROR_OK; + + /* Fetch has been stopped, and destroyed. Invalidate object's pointer */ + object->fetch.fetch = NULL; + + /* Invalidate cache-control data */ + llcache_invalidate_cache_control_data(object); + + if (object->fetch.tried_with_tls_downgrade == true) { + /* Have already tried to downgrade, so give up */ + llcache_event event; + + /* Mark object complete */ + object->fetch.state = LLCACHE_FETCH_COMPLETE; + + /* Inform client(s) that object fetch failed */ + event.type = LLCACHE_EVENT_ERROR; + /** \todo More appropriate error message */ + event.data.msg = messages_get("FetchFailed"); + + error = llcache_send_event_to_users(object, &event); + } else { + /* Flag that we've tried to downgrade, so that if the + * fetch fails again, we give up */ + object->fetch.tried_with_tls_downgrade = true; + error = llcache_object_refetch(object); + } + + return error; +} + +/** * Handler for fetch events * * \param msg Fetch event @@ -1705,6 +1774,17 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p) msg->data.cert_err.certs, msg->data.cert_err.num_certs); break; + case FETCH_SSL_ERR: + /* TLS connection setup failed */ + + /* Release candidate, if any */ + if (object->candidate != NULL) { + object->candidate->candidate_count--; + object->candidate = NULL; + } + + error = llcache_fetch_ssl_error(object); + break; } /* Deal with any errors reported by event handlers */ @@ -2086,6 +2166,7 @@ void llcache_clean(void) { llcache_object *object, *next; uint32_t llcache_size = 0; + int remaining_lifetime; #ifdef LLCACHE_TRACE LOG(("Attempting cache clean")); @@ -2122,17 +2203,25 @@ void llcache_clean(void) for (object = llcache->cached_objects; object != NULL; object = next) { next = object->next; - if ((object->users == NULL) && + remaining_lifetime = llcache_object_rfc2616_remaining_lifetime(&object->cache); + + if ((object->users == NULL) && (object->candidate_count == 0) && - (llcache_object_is_fresh(object) == false) && (object->fetch.fetch == NULL) && (object->fetch.outstanding_query == false)) { + + if (remaining_lifetime > 0) { + /* object is fresh */ + llcache_size += object->source_len + sizeof(*object); + } else { + /* object is not fresh */ #ifdef LLCACHE_TRACE - LOG(("Found victim %p", object)); + LOG(("Found stale cacheable object (%p) with no users or pending fetches", object)); #endif - llcache_object_remove_from_list(object, - &llcache->cached_objects); - llcache_object_destroy(object); + llcache_object_remove_from_list(object, + &llcache->cached_objects); + llcache_object_destroy(object); + } } else { llcache_size += object->source_len + sizeof(*object); } @@ -2146,11 +2235,10 @@ void llcache_clean(void) object = next) { next = object->next; - if (object->users == NULL && - object->candidate_count == 0 && - object->fetch.fetch == NULL && - object->fetch.outstanding_query == - false) { + if ((object->users == NULL) && + (object->candidate_count == 0) && + (object->fetch.fetch == NULL) && + (object->fetch.outstanding_query == false)) { #ifdef LLCACHE_TRACE LOG(("Found victim %p", object)); #endif diff --git a/css/select.c b/css/select.c index a52b8b144..a98ab06f5 100644 --- a/css/select.c +++ b/css/select.c @@ -2516,20 +2516,17 @@ node_presentational_hint_width(nscss_select_ctx *ctx, } if ((width == NULL) || - dom_string_lwc_isequal(width, + dom_string_caseless_lwc_isequal(width, corestring_lwc_text) || - dom_string_lwc_isequal(width, + dom_string_caseless_lwc_isequal(width, corestring_lwc_search) || - dom_string_lwc_isequal(width, + dom_string_caseless_lwc_isequal(width, corestring_lwc_file) || - dom_string_lwc_isequal(width, + dom_string_caseless_lwc_isequal(width, corestring_lwc_password)) { hint->data.length.unit = CSS_UNIT_EX; - } dom_string_unref(width); - - } return CSS_OK; @@ -2669,11 +2666,13 @@ node_presentational_hint_float(nscss_select_ctx *ctx, return CSS_PROPERTY_NOT_SET; } - if (dom_string_lwc_isequal(align, corestring_lwc_left)) { + if (dom_string_caseless_lwc_isequal(align, + corestring_lwc_left)) { hint->status = CSS_FLOAT_LEFT; dom_string_unref(align); return CSS_OK; - } else if (dom_string_lwc_isequal(align, corestring_lwc_right)) { + } else if (dom_string_caseless_lwc_isequal(align, + corestring_lwc_right)) { hint->status = CSS_FLOAT_RIGHT; dom_string_unref(align); return CSS_OK; diff --git a/desktop/browser.c b/desktop/browser.c index 6262f2453..6a1688192 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -1545,12 +1545,6 @@ nserror browser_window_callback(hlcache_handle *c, browser_window_set_pointer(bw, event->data.pointer); break; - case CONTENT_MSG_PASTE: - /* Content wants a clipboard paste */ - gui_paste_from_clipboard(bw->window, - event->data.paste.x, event->data.paste.y); - break; - default: assert(0); } diff --git a/desktop/gui.h b/desktop/gui.h index 48684c3c5..d04d7b405 100644 --- a/desktop/gui.h +++ b/desktop/gui.h @@ -119,12 +119,33 @@ void gui_drag_save_selection(struct selection *s, struct gui_window *g); void gui_start_selection(struct gui_window *g); void gui_clear_selection(struct gui_window *g); -void gui_paste_from_clipboard(struct gui_window *g, int x, int y); -bool gui_empty_clipboard(void); -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle); -bool gui_commit_clipboard(void); -bool gui_copy_to_clipboard(struct selection *s); + + +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length); + +typedef struct nsnsclipboard_styles { + size_t start; /**< Start of run */ + + plot_font_style_t style; /**< Style to give text run */ +} nsclipboard_styles; +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles); + + void gui_create_form_select_menu(struct browser_window *bw, struct form_control *control); diff --git a/desktop/options.h b/desktop/options.h index 53c025c18..17ba64f6c 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -108,7 +108,8 @@ extern struct ns_options nsoptions; free(nsoptions.OPTION); \ } \ nsoptions.OPTION = VALUE; \ - if (*nsoptions.OPTION == 0) { \ + if ((nsoptions.OPTION != NULL) && \ + (*nsoptions.OPTION == 0)) { \ free(nsoptions.OPTION); \ nsoptions.OPTION = NULL; \ } \ diff --git a/desktop/options_main.h b/desktop/options_main.h index 9ec4888bc..7b9e7314b 100644 --- a/desktop/options_main.h +++ b/desktop/options_main.h @@ -66,6 +66,8 @@ char *accept_charset; \ /** Preferred maximum size of memory cache / bytes. */ \ int memory_cache_size; \ + /** Preferred expiry size of disc cache / bytes. */ \ + int disc_cache_size; \ /** Preferred expiry age of disc cache / days. */ \ int disc_cache_age; \ /** Whether to block advertisements */ \ @@ -220,6 +222,7 @@ .accept_language = NULL, \ .accept_charset = NULL, \ .memory_cache_size = 12 * 1024 * 1024, \ + .disc_cache_size = 1024 * 1024 * 1024, \ .disc_cache_age = 28, \ .block_ads = false, \ .do_not_track = false, \ @@ -315,6 +318,7 @@ { "accept_language", OPTION_STRING, &nsoptions.accept_language }, \ { "accept_charset", OPTION_STRING, &nsoptions.accept_charset }, \ { "memory_cache_size", OPTION_INTEGER, &nsoptions.memory_cache_size }, \ + { "disc_cache_size", OPTION_INTEGER, &nsoptions.disc_cache_size }, \ { "disc_cache_age", OPTION_INTEGER, &nsoptions.disc_cache_age }, \ { "block_advertisements", OPTION_BOOL, &nsoptions.block_ads }, \ { "do_not_track", OPTION_BOOL, &nsoptions.do_not_track }, \ diff --git a/desktop/selection.c b/desktop/selection.c index f4f44e117..ae9df5ec6 100644 --- a/desktop/selection.c +++ b/desktop/selection.c @@ -77,6 +77,9 @@ struct selection_string { char *buffer; size_t buffer_len; size_t length; + + int n_styles; + nsclipboard_styles *styles; }; @@ -743,69 +746,6 @@ void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx) /** - * Selection traversal routine for appending text to the current contents - * of the clipboard. - * - * \param text pointer to text being added, or NULL for newline - * \param length length of text to be appended (bytes) - * \param box pointer to text box, or NULL if from textplain - * \param handle unused handle, we don't need one - * \param whitespace_text whitespace to place before text for formatting - * may be NULL - * \param whitespace_length length of whitespace_text - * \return true iff successful and traversal should continue - */ - -static bool selection_copy_clip_handler(const char *text, size_t length, - struct box *box, void *handle, const char *whitespace_text, - size_t whitespace_length) -{ - bool add_space = false; - plot_font_style_t style = *plot_style_font; - - /* add any whitespace which precedes the text from this box */ - if (whitespace_text != NULL && whitespace_length > 0) { - if (!gui_add_to_clipboard(whitespace_text, - whitespace_length, false, &style)) { - return false; - } - } - - if (box != NULL) { - /* HTML */ - add_space = (box->space != 0); - - if (box->style != NULL) { - /* Override default font style */ - font_plot_style_from_css(box->style, &style); - } else { - /* If there's no style, there must be no text */ - assert(box->text == NULL); - } - } - - /* add the text from this box */ - if (!gui_add_to_clipboard(text, length, add_space, &style)) - return false; - - return true; -} - - -/** - * Copy the selected contents to the clipboard - * - * \param s selection - * \return true iff successful, ie. cut operation can proceed without losing data - */ - -bool selection_copy_to_clipboard(struct selection *s) -{ - return selection_traverse(s, selection_copy_clip_handler, NULL); -} - - -/** * Append text to selection string. * * \param text text to be added @@ -816,11 +756,34 @@ bool selection_copy_to_clipboard(struct selection *s) */ static bool selection_string_append(const char *text, size_t length, bool space, - struct selection_string *sel_string) + plot_font_style_t *style, struct selection_string *sel_string) { size_t new_length = sel_string->length + length + (space ? 1 : 0) + 1; + if (style != NULL) { + /* Add text run style */ + nsclipboard_styles *new_styles; + + if (sel_string->n_styles == 0) + assert(sel_string->length == 0); + + new_styles = realloc(sel_string->styles, + (sel_string->n_styles + 1) * + sizeof(nsclipboard_styles)); + if (new_styles == NULL) + return false; + + sel_string->styles = new_styles; + + sel_string->styles[sel_string->n_styles].style = *style; + sel_string->styles[sel_string->n_styles].start = + sel_string->length; + + sel_string->n_styles++; + } + if (new_length > sel_string->buffer_len) { + /* Need to extend buffer */ size_t new_alloc = new_length + (new_length / 4); char *new_buff; @@ -832,12 +795,14 @@ static bool selection_string_append(const char *text, size_t length, bool space, sel_string->buffer_len = new_alloc; } + /* Copy text onto end of existing text in buffer */ memcpy(sel_string->buffer + sel_string->length, text, length); sel_string->length += length; if (space) sel_string->buffer[sel_string->length++] = ' '; + /* Ensure NULL termination */ sel_string->buffer[sel_string->length] = '\0'; return true; @@ -862,11 +827,13 @@ static bool selection_copy_handler(const char *text, size_t length, size_t whitespace_length) { bool add_space = false; + plot_font_style_t style; + plot_font_style_t *pstyle = NULL; /* add any whitespace which precedes the text from this box */ if (whitespace_text != NULL && whitespace_length > 0) { if (!selection_string_append(whitespace_text, - whitespace_length, false, handle)) { + whitespace_length, false, pstyle, handle)) { return false; } } @@ -874,10 +841,19 @@ static bool selection_copy_handler(const char *text, size_t length, if (box != NULL) { /* HTML */ add_space = (box->space != 0); + + if (box->style != NULL) { + /* Override default font style */ + font_plot_style_from_css(box->style, &style); + pstyle = &style; + } else { + /* If there's no style, there must be no text */ + assert(box->text == NULL); + } } /* add the text from this box */ - if (!selection_string_append(text, length, add_space, handle)) + if (!selection_string_append(text, length, add_space, pstyle, handle)) return false; return true; @@ -891,28 +867,69 @@ static bool selection_copy_handler(const char *text, size_t length, * \return string of selected text, or NULL. Ownership passed to caller. */ -char * selection_get_copy(struct selection *s) +char *selection_get_copy(struct selection *s) { struct selection_string sel_string = { .buffer = NULL, .buffer_len = 0, - .length = 0 + .length = 0, + + .n_styles = 0, + .styles = NULL }; if (s == NULL || !s->defined) return NULL; if (!selection_traverse(s, selection_copy_handler, &sel_string)) { - if (sel_string.buffer != NULL) { - free(sel_string.buffer); - } + free(sel_string.buffer); + free(sel_string.styles); return NULL; } + free(sel_string.styles); + return sel_string.buffer; } + +/** + * Copy the selected contents to the clipboard + * + * \param s selection + * \return true iff successful + */ +bool selection_copy_to_clipboard(struct selection *s) +{ + struct selection_string sel_string = { + .buffer = NULL, + .buffer_len = 0, + .length = 0, + + .n_styles = 0, + .styles = NULL + }; + + if (s == NULL || !s->defined) + return false; + + if (!selection_traverse(s, selection_copy_handler, &sel_string)) { + free(sel_string.buffer); + free(sel_string.styles); + return false; + } + + gui_set_clipboard(sel_string.buffer, sel_string.length, + sel_string.styles, sel_string.n_styles); + + free(sel_string.buffer); + free(sel_string.styles); + + return true; +} + + /** * Clears the current selection, optionally causing the screen to be updated. * diff --git a/desktop/textarea.c b/desktop/textarea.c index 05e3aaae5..35959c8ce 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -65,7 +65,7 @@ struct line_info { unsigned int b_length; /**< Byte length of line */ }; -struct text_area { +struct textarea { int scroll_x, scroll_y; /**< scroll offsets of the textarea * content @@ -106,165 +106,362 @@ struct text_area { void *data; /** < Callback data for both callback functions */ - int drag_start_char; /**< Character index at which the drag was - * started - */ + int drag_start_char; /**< Character index of drag start */ }; -static bool textarea_insert_text(struct text_area *ta, unsigned int index, - const char *text); -static bool textarea_replace_text(struct text_area *ta, unsigned int start, - unsigned int end, const char *text); -static bool textarea_reflow(struct text_area *ta, unsigned int line); -static unsigned int textarea_get_xy_offset(struct text_area *ta, int x, int y); -static bool textarea_set_caret_xy(struct text_area *ta, int x, int y); -static bool textarea_scroll_visible(struct text_area *ta); -static bool textarea_select(struct text_area *ta, int c_start, int c_end); -static bool textarea_select_fragment(struct text_area *ta); -static void textarea_normalise_text(struct text_area *ta, - unsigned int b_start, unsigned int b_len); /** - * Create a text area + * Normalises any line endings within the text, replacing CRLF or CR with + * LF as necessary. If the textarea is single line, then all linebreaks are + * converted into spaces. * - * \param x X coordinate of left border - * \param y Y coordinate of top border - * \param width width of the text area - * \param height width of the text area - * \param flags text area flags - * \param style font style - * \param redraw_start_callback will be called when textarea wants to redraw - * \param redraw_end_callback will be called when textarea finisjes redrawing - * \param data user specified data which will be passed to redraw callbacks - * \return Opaque handle for textarea or 0 on error + * \param ta Text area + * \param b_start Byte offset to start at + * \param b_len Byte length to check */ -struct text_area *textarea_create(int width, int height, - unsigned int flags, const plot_font_style_t *style, - textarea_redraw_request_callback redraw_request, void *data) +static void textarea_normalise_text(struct textarea *ta, + unsigned int b_start, unsigned int b_len) { - struct text_area *ret; + bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false; + unsigned int index; - if (redraw_request == NULL) { - LOG(("no callback provided")); - return NULL; + /* Remove CR characters. If it's a CRLF pair delete it ot replace it + * with LF otherwise. + */ + for (index = 0; index < b_len; index++) { + if (ta->text[b_start + index] == '\r') { + if (b_start + index + 1 <= ta->text_len && + ta->text[b_start + index + 1] == '\n') { + ta->text_len--; + ta->text_utf8_len--; + memmove(ta->text + b_start + index, + ta->text + b_start + index + 1, + ta->text_len - b_start - index); + } + else + ta->text[b_start + index] = '\n'; + } + /* Replace newlines with spaces if this is a single line + * textarea. + */ + if (!multi && (ta->text[b_start + index] == '\n')) + ta->text[b_start + index] = ' '; } - ret = malloc(sizeof(struct text_area)); - if (ret == NULL) { - LOG(("malloc failed")); - return NULL; - } +} - ret->redraw_request = redraw_request; - ret->data = data; - ret->vis_width = width; - ret->vis_height = height; - ret->scroll_x = 0; - ret->scroll_y = 0; - ret->drag_start_char = 0; +/** + * Selects a character range in the textarea and redraws it + * + * \param ta Text area + * \param c_start First character (inclusive) + * \param c_end Last character (exclusive) + * \return true on success false otherwise + */ +static bool textarea_select(struct textarea *ta, int c_start, int c_end) +{ + int swap; - ret->flags = flags; - ret->text = malloc(64); - if (ret->text == NULL) { - LOG(("malloc failed")); - free(ret); - return NULL; + /* Ensure start is the beginning of the selection */ + if (c_start > c_end) { + swap = c_start; + c_start = c_end; + c_end = swap; } - ret->text[0] = '\0'; - ret->text_alloc = 64; - ret->text_len = 1; - ret->text_utf8_len = 0; - ret->fstyle = *style; + ta->selection_start = c_start; + ta->selection_end = c_end; - ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2), FMUL(nscss_screen_dpi, INTTOFIX((style->size / FONT_SIZE_SCALE))))), F_72)); + ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); - ret->caret_pos.line = ret->caret_pos.char_off = 0; - ret->caret_x = MARGIN_LEFT; - ret->caret_y = 0; - ret->selection_start = -1; - ret->selection_end = -1; + return true; +} - ret->line_count = 0; - ret->lines = NULL; - return ret; +/** + * Selects a text fragment, relative to current caret position. + * + * \param ta Text area + * \return True on success, false otherwise + */ +static bool textarea_select_fragment(struct textarea * ta) +{ + int caret_pos, sel_start = 0, sel_end = 0, index; + size_t b_start, b_end; + + /* Fragment separators must be suitable for URLs and ordinary text */ + static const char *sep = " /:.\r\n"; + + caret_pos = textarea_get_caret(ta); + if (caret_pos < 0) { + return false; + } + + /* Compute byte offset of caret position */ + for (b_start = 0, index = 0; index < caret_pos; + b_start = utf8_next(ta->text, ta->text_len, + b_start), + index++) { + /* Cache the character offset of the last separator */ + if (strchr(sep, ta->text[b_start]) != NULL) { + /* Add one to start to skip over separator */ + sel_start = index + 1; + } + } + + /* Search for next separator, if any */ + for (b_end = b_start; b_end < ta->text_len; + b_end = utf8_next(ta->text, ta->text_len, + b_end), + index++) { + if (strchr(sep, ta->text[b_end]) != NULL) { + sel_end = index; + break; + } + } + + if (sel_start < sel_end) { + textarea_select(ta, sel_start, sel_end); + return true; + } + + return false; } /** - * Destroy a text area + * Scrolls a textarea to make the caret visible (doesn't perform a redraw) * - * \param ta Text area to destroy + * \param ta The text area to be scrolled + * \return true if textarea was scrolled false otherwise */ -void textarea_destroy(struct text_area *ta) +static bool textarea_scroll_visible(struct textarea *ta) { - free(ta->text); - free(ta->lines); - free(ta); + int x0, x1, y0, y1, x, y; + int index, b_off; + bool scrolled = false; + + if (ta->caret_pos.char_off == -1) + return false; + + x0 = MARGIN_LEFT; + x1 = ta->vis_width - MARGIN_RIGHT; + y0 = 0; + y1 = ta->vis_height; + + index = textarea_get_caret(ta); + + /* find byte offset of caret position */ + for (b_off = 0; index-- > 0; + b_off = utf8_next(ta->text, ta->text_len, b_off)) + ; /* do nothing */ + + nsfont.font_width(&ta->fstyle, + ta->text + ta->lines[ta->caret_pos.line].b_start, + b_off - ta->lines[ta->caret_pos.line].b_start, + &x); + + /* top-left of caret */ + x += MARGIN_LEFT - ta->scroll_x; + y = ta->line_height * ta->caret_pos.line - ta->scroll_y; + + /* check and change vertical scroll */ + if (y < y0) { + ta->scroll_y -= y0 - y; + scrolled = true; + } else if (y + ta->line_height > y1) { + ta->scroll_y += y + ta->line_height - y1; + scrolled = true; + } + + + /* check and change horizontal scroll */ + if (x < x0) { + ta->scroll_x -= x0 - x ; + scrolled = true; + } else if (x > x1 - 1) { + ta->scroll_x += x - (x1 - 1); + scrolled = true; + } + + return scrolled; } /** - * Set the text in a text area, discarding any current text + * Reflow a text area from the given line onwards * - * \param ta Text area - * \param text UTF-8 text to set text area's contents to - * \return true on success, false on memory exhaustion + * \param ta Text area to reflow + * \param line Line number to begin reflow on + * \return true on success false otherwise */ -bool textarea_set_text(struct text_area *ta, const char *text) +static bool textarea_reflow(struct textarea *ta, unsigned int line) { - unsigned int len = strlen(text) + 1; + char *text; + unsigned int len; + size_t b_off; + int x; + char *space; + unsigned int line_count = 0; - if (len >= ta->text_alloc) { - char *temp = realloc(ta->text, len + 64); - if (temp == NULL) { - LOG(("realloc failed")); + /** \todo pay attention to line parameter */ + /** \todo create horizontal scrollbar if needed */ + + ta->line_count = 0; + + if (ta->lines == NULL) { + ta->lines = + malloc(LINE_CHUNK_SIZE * sizeof(struct line_info)); + if (ta->lines == NULL) { + LOG(("malloc failed")); return false; } - ta->text = temp; - ta->text_alloc = len + 64; } - memcpy(ta->text, text, len); - ta->text_len = len; - ta->text_utf8_len = utf8_length(ta->text); + if (!(ta->flags & TEXTAREA_MULTILINE)) { + /* Single line */ + ta->lines[line_count].b_start = 0; + ta->lines[line_count++].b_length = ta->text_len - 1; - textarea_normalise_text(ta, 0, len); + ta->line_count = line_count; - return textarea_reflow(ta, 0); + return true; + } + + for (len = ta->text_len - 1, text = ta->text; len > 0; + len -= b_off, text += b_off) { + + nsfont.font_split(&ta->fstyle, text, len, + ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT, + &b_off, &x); + + if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) { + struct line_info *temp = realloc(ta->lines, + (line_count + LINE_CHUNK_SIZE) * + sizeof(struct line_info)); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + + ta->lines = temp; + } + + /* handle LF */ + for (space = text; space <= text + b_off; space++) { + if (*space == '\n') + break; + } + + if (space <= text + b_off) { + /* Found newline; use it */ + ta->lines[line_count].b_start = text - ta->text; + ta->lines[line_count++].b_length = space - text; + + b_off = space + 1 - text; + + if (len - b_off == 0) { + /* reached end of input => add last line */ + ta->lines[line_count].b_start = + text + b_off - ta->text; + ta->lines[line_count++].b_length = 0; + } + + continue; + } + + if (len - b_off > 0) { + /* find last space (if any) */ + for (space = text + b_off; space > text; space--) + if (*space == ' ') + break; + + if (space != text) + b_off = space + 1 - text; + } + + ta->lines[line_count].b_start = text - ta->text; + ta->lines[line_count++].b_length = b_off; + } + + ta->line_count = line_count; + + return true; } /** - * Extract the text from a text area + * get character offset from the beginning of the text for some coordinates * - * \param ta Text area - * \param buf Pointer to buffer to receive data, or NULL - * to read length required - * \param len Length (bytes) of buffer pointed to by buf, or 0 to read length - * \return Length (bytes) written/required or -1 on error + * \param ta Text area + * \param x X coordinate + * \param y Y coordinate + * \return character offset */ -int textarea_get_text(struct text_area *ta, char *buf, unsigned int len) +static unsigned int textarea_get_xy_offset(struct textarea *ta, int x, int y) { - if (buf == NULL && len == 0) { - /* want length */ - return ta->text_len; - } else if (buf == NULL) { - /* Can't write to NULL */ - return -1; - } + size_t b_off, temp; + unsigned int c_off; + int line; - if (len < ta->text_len) { - LOG(("buffer too small")); - return -1; - } + if (!ta->line_count) + return 0; - memcpy(buf, ta->text, ta->text_len); + x = x - MARGIN_LEFT + ta->scroll_x; + y = y + ta->scroll_y; - return ta->text_len; + if (x < 0) + x = 0; + + line = y / ta->line_height; + + if (line < 0) + line = 0; + if (ta->line_count - 1 < line) + line = ta->line_count - 1; + + nsfont.font_position_in_string(&ta->fstyle, + ta->text + ta->lines[line].b_start, + ta->lines[line].b_length, x, &b_off, &x); + + /* If the calculated byte offset corresponds with the number of bytes + * in the line, and the line has been soft-wrapped, then ensure the + * caret offset is before the trailing space character, rather than + * after it. Otherwise, the caret will be placed at the start of the + * following line, which is undesirable. + */ + if (b_off == (unsigned)ta->lines[line].b_length && + ta->text[ta->lines[line].b_start + + ta->lines[line].b_length - 1] == ' ') + b_off--; + + for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start; + temp = utf8_next(ta->text, ta->text_len, temp)) + c_off++; + + return c_off; +} + + +/** + * Set the caret's position + * + * \param ta Text area + * \param x X position of caret in a window relative to text area top left + * \param y Y position of caret in a window relative to text area top left + * \return true on success false otherwise + */ +static bool textarea_set_caret_xy(struct textarea *ta, int x, int y) +{ + unsigned int c_off; + + if (ta->flags & TEXTAREA_READONLY) + return true; + + c_off = textarea_get_xy_offset(ta, x, y); + return textarea_set_caret(ta, c_off); } @@ -276,7 +473,7 @@ int textarea_get_text(struct text_area *ta, char *buf, unsigned int len) * \param text UTF-8 text to insert * \return false on memory exhaustion, true otherwise */ -bool textarea_insert_text(struct text_area *ta, unsigned int index, +static bool textarea_insert_text(struct textarea *ta, unsigned int index, const char *text) { unsigned int b_len = strlen(text); @@ -330,7 +527,7 @@ bool textarea_insert_text(struct text_area *ta, unsigned int index, * \param text UTF-8 text to insert * \return false on memory exhaustion, true otherwise */ -bool textarea_replace_text(struct text_area *ta, unsigned int start, +static bool textarea_replace_text(struct textarea *ta, unsigned int start, unsigned int end, const char *text) { unsigned int b_len = strlen(text); @@ -391,15 +588,124 @@ bool textarea_replace_text(struct text_area *ta, unsigned int start, } -/** - * Set the caret's position - * - * \param ta Text area - * \param caret 0-based character index to place caret at, -1 removes - * the caret - * \return true on success false otherwise - */ -bool textarea_set_caret(struct text_area *ta, int caret) + + +/* exported interface, documented in textarea.h */ +struct textarea *textarea_create(int width, int height, + textarea_flags flags, const plot_font_style_t *style, + textarea_redraw_request_callback redraw_request, void *data) +{ + struct textarea *ret; + + if (redraw_request == NULL) { + LOG(("no callback provided")); + return NULL; + } + + ret = malloc(sizeof(struct textarea)); + if (ret == NULL) { + LOG(("malloc failed")); + return NULL; + } + + ret->redraw_request = redraw_request; + ret->data = data; + ret->vis_width = width; + ret->vis_height = height; + ret->scroll_x = 0; + ret->scroll_y = 0; + ret->drag_start_char = 0; + + + ret->flags = flags; + ret->text = malloc(64); + if (ret->text == NULL) { + LOG(("malloc failed")); + free(ret); + return NULL; + } + ret->text[0] = '\0'; + ret->text_alloc = 64; + ret->text_len = 1; + ret->text_utf8_len = 0; + + ret->fstyle = *style; + + ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2), + FMUL(nscss_screen_dpi, + INTTOFIX((style->size / FONT_SIZE_SCALE))))), F_72)); + + ret->caret_pos.line = ret->caret_pos.char_off = 0; + ret->caret_x = MARGIN_LEFT; + ret->caret_y = 0; + ret->selection_start = -1; + ret->selection_end = -1; + + ret->line_count = 0; + ret->lines = NULL; + + return ret; +} + + +/* exported interface, documented in textarea.h */ +void textarea_destroy(struct textarea *ta) +{ + free(ta->text); + free(ta->lines); + free(ta); +} + + +/* exported interface, documented in textarea.h */ +bool textarea_set_text(struct textarea *ta, const char *text) +{ + unsigned int len = strlen(text) + 1; + + if (len >= ta->text_alloc) { + char *temp = realloc(ta->text, len + 64); + if (temp == NULL) { + LOG(("realloc failed")); + return false; + } + ta->text = temp; + ta->text_alloc = len + 64; + } + + memcpy(ta->text, text, len); + ta->text_len = len; + ta->text_utf8_len = utf8_length(ta->text); + + textarea_normalise_text(ta, 0, len); + + return textarea_reflow(ta, 0); +} + + +/* exported interface, documented in textarea.h */ +int textarea_get_text(struct textarea *ta, char *buf, unsigned int len) +{ + if (buf == NULL && len == 0) { + /* want length */ + return ta->text_len; + } else if (buf == NULL) { + /* Can't write to NULL */ + return -1; + } + + if (len < ta->text_len) { + LOG(("buffer too small")); + return -1; + } + + memcpy(buf, ta->text, ta->text_len); + + return ta->text_len; +} + + +/* exported interface, documented in textarea.h */ +bool textarea_set_caret(struct textarea *ta, int caret) { unsigned int c_len; unsigned int b_off; @@ -500,7 +806,7 @@ bool textarea_set_caret(struct text_area *ta, int caret) ta->text + ta->lines[ta->caret_pos.line].b_start, b_off - ta->lines[ta->caret_pos.line].b_start, - &x); + &x); x += MARGIN_LEFT - ta->scroll_x; ta->caret_x = x; @@ -532,86 +838,8 @@ bool textarea_set_caret(struct text_area *ta, int caret) } -/** - * get character offset from the beginning of the text for some coordinates - * - * \param ta Text area - * \param x X coordinate - * \param y Y coordinate - * \return character offset - */ -unsigned int textarea_get_xy_offset(struct text_area *ta, int x, int y) -{ - size_t b_off, temp; - unsigned int c_off; - int line; - - if (!ta->line_count) - return 0; - - x = x - MARGIN_LEFT + ta->scroll_x; - y = y + ta->scroll_y; - - if (x < 0) - x = 0; - - line = y / ta->line_height; - - if (line < 0) - line = 0; - if (ta->line_count - 1 < line) - line = ta->line_count - 1; - - nsfont.font_position_in_string(&ta->fstyle, - ta->text + ta->lines[line].b_start, - ta->lines[line].b_length, x, &b_off, &x); - - /* If the calculated byte offset corresponds with the number of bytes - * in the line, and the line has been soft-wrapped, then ensure the - * caret offset is before the trailing space character, rather than - * after it. Otherwise, the caret will be placed at the start of the - * following line, which is undesirable. - */ - if (b_off == (unsigned)ta->lines[line].b_length && - ta->text[ta->lines[line].b_start + - ta->lines[line].b_length - 1] == ' ') - b_off--; - - for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start; - temp = utf8_next(ta->text, ta->text_len, temp)) - c_off++; - - return c_off; -} - - -/** - * Set the caret's position - * - * \param ta Text area - * \param x X position of caret in a window relative to text area top left - * \param y Y position of caret in a window relative to text area top left - * \return true on success false otherwise - */ -bool textarea_set_caret_xy(struct text_area *ta, int x, int y) -{ - unsigned int c_off; - - if (ta->flags & TEXTAREA_READONLY) - return true; - - c_off = textarea_get_xy_offset(ta, x, y); - return textarea_set_caret(ta, c_off); -} - - -/** - * Get the caret's position - * - * \param ta Text area - * \return 0-based character index of caret location, or -1 on error - */ -int textarea_get_caret(struct text_area *ta) +/* exported interface, documented in textarea.h */ +int textarea_get_caret(struct textarea *ta) { unsigned int c_off = 0, b_off; @@ -628,119 +856,9 @@ int textarea_get_caret(struct text_area *ta) return c_off + ta->caret_pos.char_off; } -/** - * Reflow a text area from the given line onwards - * - * \param ta Text area to reflow - * \param line Line number to begin reflow on - * \return true on success false otherwise - */ -bool textarea_reflow(struct text_area *ta, unsigned int line) -{ - char *text; - unsigned int len; - size_t b_off; - int x; - char *space; - unsigned int line_count = 0; - - /** \todo pay attention to line parameter */ - /** \todo create horizontal scrollbar if needed */ - - ta->line_count = 0; - - if (ta->lines == NULL) { - ta->lines = - malloc(LINE_CHUNK_SIZE * sizeof(struct line_info)); - if (ta->lines == NULL) { - LOG(("malloc failed")); - return false; - } - } - - if (!(ta->flags & TEXTAREA_MULTILINE)) { - /* Single line */ - ta->lines[line_count].b_start = 0; - ta->lines[line_count++].b_length = ta->text_len - 1; - - ta->line_count = line_count; - - return true; - } - - for (len = ta->text_len - 1, text = ta->text; len > 0; - len -= b_off, text += b_off) { - - nsfont.font_split(&ta->fstyle, text, len, - ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT, - &b_off, &x); - - if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) { - struct line_info *temp = realloc(ta->lines, - (line_count + LINE_CHUNK_SIZE) * - sizeof(struct line_info)); - if (temp == NULL) { - LOG(("realloc failed")); - return false; - } - - ta->lines = temp; - } - - /* handle LF */ - for (space = text; space <= text + b_off; space++) { - if (*space == '\n') - break; - } - - if (space <= text + b_off) { - /* Found newline; use it */ - ta->lines[line_count].b_start = text - ta->text; - ta->lines[line_count++].b_length = space - text; - - b_off = space + 1 - text; - - if (len - b_off == 0) { - /* reached end of input => add last line */ - ta->lines[line_count].b_start = - text + b_off - ta->text; - ta->lines[line_count++].b_length = 0; - } - - continue; - } - - if (len - b_off > 0) { - /* find last space (if any) */ - for (space = text + b_off; space > text; space--) - if (*space == ' ') - break; - - if (space != text) - b_off = space + 1 - text; - } - - ta->lines[line_count].b_start = text - ta->text; - ta->lines[line_count++].b_length = b_off; - } - - ta->line_count = line_count; - - return true; -} - -/** - * Handle redraw requests for text areas - * - * \param redraw Redraw request block - * \param x0 left X coordinate of redraw area - * \param y0 top Y coordinate of redraw area - * \param x1 right X coordinate of redraw area - * \param y1 bottom Y coordinate of redraw area - * \param ctx current redraw context - */ -void textarea_redraw(struct text_area *ta, int x, int y, +/* exported interface, documented in textarea.h */ +void textarea_redraw(struct textarea *ta, int x, int y, const struct rect *clip, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; @@ -927,14 +1045,9 @@ void textarea_redraw(struct text_area *ta, int x, int y, } } -/** - * Key press handling for text areas. - * - * \param ta The text area which got the keypress - * \param key The ucs4 character codepoint - * \return true if the keypress is dealt with, false otherwise. - */ -bool textarea_keypress(struct text_area *ta, uint32_t key) + +/* exported interface, documented in textarea.h */ +bool textarea_keypress(struct textarea *ta, uint32_t key) { char utf8[6]; unsigned int caret, caret_init, length, l_len, b_off, b_len; @@ -1252,122 +1365,45 @@ bool textarea_keypress(struct text_area *ta, uint32_t key) return true; } -/** - * Scrolls a textarea to make the caret visible (doesn't perform a redraw) - * - * \param ta The text area to be scrolled - * \return true if textarea was scrolled false otherwise - */ -bool textarea_scroll_visible(struct text_area *ta) -{ - int x0, x1, y0, y1, x, y; - int index, b_off; - bool scrolled = false; - if (ta->caret_pos.char_off == -1) - return false; - - x0 = MARGIN_LEFT; - x1 = ta->vis_width - MARGIN_RIGHT; - y0 = 0; - y1 = ta->vis_height; - - index = textarea_get_caret(ta); - - /* find byte offset of caret position */ - for (b_off = 0; index-- > 0; - b_off = utf8_next(ta->text, ta->text_len, b_off)) - ; /* do nothing */ - - nsfont.font_width(&ta->fstyle, - ta->text + ta->lines[ta->caret_pos.line].b_start, - b_off - ta->lines[ta->caret_pos.line].b_start, - &x); - - /* top-left of caret */ - x += MARGIN_LEFT - ta->scroll_x; - y = ta->line_height * ta->caret_pos.line - ta->scroll_y; - - /* check and change vertical scroll */ - if (y < y0) { - ta->scroll_y -= y0 - y; - scrolled = true; - } else if (y + ta->line_height > y1) { - ta->scroll_y += y + ta->line_height - y1; - scrolled = true; - } - - - /* check and change horizontal scroll */ - if (x < x0) { - ta->scroll_x -= x0 - x ; - scrolled = true; - } else if (x > x1 - 1) { - ta->scroll_x += x - (x1 - 1); - scrolled = true; - } - - return scrolled; -} - - -/** - * Handles all kinds of mouse action - * - * \param ta Text area - * \param mouse the mouse state at action moment - * \param x X coordinate - * \param y Y coordinate - * \return true if action was handled false otherwise - */ -bool textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse, +/* exported interface, documented in textarea.h */ +bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, int x, int y) { int c_start, c_end; /* mouse button pressed above the text area, move caret */ if (mouse & BROWSER_MOUSE_PRESS_1) { - if (!(ta->flags & TEXTAREA_READONLY)) + if (!(ta->flags & TEXTAREA_READONLY)) { textarea_set_caret_xy(ta, x, y); + ta->drag_start_char = textarea_get_xy_offset(ta, x, y); + } if (ta->selection_start != -1) { + /* remove selection */ ta->selection_start = ta->selection_end = -1; ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); } - } - else if (mouse & BROWSER_MOUSE_DOUBLE_CLICK) { + + } else if (mouse & BROWSER_MOUSE_DOUBLE_CLICK) { if (!(ta->flags & TEXTAREA_READONLY)) { textarea_set_caret_xy(ta, x, y); return textarea_select_fragment(ta); } - } - else if (mouse & BROWSER_MOUSE_DRAG_1) { - ta->drag_start_char = textarea_get_xy_offset(ta, x, y); - if (!(ta->flags & TEXTAREA_READONLY)) - return textarea_set_caret(ta, -1); - } - else if (mouse & BROWSER_MOUSE_HOLDING_1) { + + } else if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_HOLDING_1)) { c_start = ta->drag_start_char; c_end = textarea_get_xy_offset(ta, x, y); return textarea_select(ta, c_start, c_end); - } return true; } -/** - * Handles the end of a drag operation - * - * \param ta Text area - * \param mouse the mouse state at drag end moment - * \param x X coordinate - * \param y Y coordinate - * \return true if drag end was handled false otherwise - */ -bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse, +/* exported interface, documented in textarea.h */ +bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse, int x, int y) { int c_end; @@ -1376,141 +1412,9 @@ bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse, return textarea_select(ta, ta->drag_start_char, c_end); } -/** - * Selects a character range in the textarea and redraws it - * - * \param ta Text area - * \param c_start First character (inclusive) - * \param c_end Last character (exclusive) - * \return true on success false otherwise - */ -bool textarea_select(struct text_area *ta, int c_start, int c_end) -{ - int swap = -1; - - /* if start is after end they get swapped, start won't and end will - be selected this way */ - if (c_start > c_end) { - swap = c_start; - c_start = c_end; - c_end = swap; - } - - ta->selection_start = c_start; - ta->selection_end = c_end; - - if (!(ta->flags & TEXTAREA_READONLY)) { - if (swap == -1) - return textarea_set_caret(ta, c_end); - else - return textarea_set_caret(ta, c_start); - } - - ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height); - - return true; -} - -/** - * Selects a text fragment, relative to current caret position. - * - * \param ta Text area - * \return True on success, false otherwise - */ -static bool textarea_select_fragment(struct text_area * ta) -{ - int caret_pos, sel_start = 0, sel_end = 0, index; - size_t b_start, b_end; - - /* Fragment separators must be suitable for URLs and ordinary text */ - static const char *sep = " /:.\r\n"; - - caret_pos = textarea_get_caret(ta); - if (caret_pos < 0) { - return false; - } - - /* Compute byte offset of caret position */ - for (b_start = 0, index = 0; index < caret_pos; - b_start = utf8_next(ta->text, ta->text_len, - b_start), - index++) { - /* Cache the character offset of the last separator */ - if (strchr(sep, ta->text[b_start]) != NULL) { - /* Add one to start to skip over separator */ - sel_start = index + 1; - } - } - - /* Search for next separator, if any */ - for (b_end = b_start; b_end < ta->text_len; - b_end = utf8_next(ta->text, ta->text_len, - b_end), - index++) { - if (strchr(sep, ta->text[b_end]) != NULL) { - sel_end = index; - break; - } - } - - if (sel_start < sel_end) { - textarea_select(ta, sel_start, sel_end); - return true; - } - - return false; -} - - -/** - * Normalises any line endings within the text, replacing CRLF or CR with - * LF as necessary. If the textarea is single line, then all linebreaks are - * converted into spaces. - * - * \param ta Text area - * \param b_start Byte offset to start at - * \param b_len Byte length to check - */ -void textarea_normalise_text(struct text_area *ta, - unsigned int b_start, unsigned int b_len) -{ - bool multi = (ta->flags & TEXTAREA_MULTILINE) ? true:false; - unsigned int index; - - /* Remove CR characters. If it's a CRLF pair delete it ot replace it - * with LF otherwise. - */ - for (index = 0; index < b_len; index++) { - if (ta->text[b_start + index] == '\r') { - if (b_start + index + 1 <= ta->text_len && - ta->text[b_start + index + 1] == '\n') { - ta->text_len--; - ta->text_utf8_len--; - memmove(ta->text + b_start + index, - ta->text + b_start + index + 1, - ta->text_len - b_start - index); - } - else - ta->text[b_start + index] = '\n'; - } - /* Replace newlines with spaces if this is a single line - * textarea. - */ - if (!multi && (ta->text[b_start + index] == '\n')) - ta->text[b_start + index] = ' '; - } - -} - - -/** - * Gets the dimensions of a textarea - * - * \param width if not NULL, gets updated to the width of the textarea - * \param height if not NULL, gets updated to the height of the textarea - */ -void textarea_get_dimensions(struct text_area *ta, int *width, int *height) +/* exported interface, documented in textarea.h */ +void textarea_get_dimensions(struct textarea *ta, int *width, int *height) { if (width != NULL) *width = ta->vis_width; @@ -1518,14 +1422,9 @@ void textarea_get_dimensions(struct text_area *ta, int *width, int *height) *height = ta->vis_height; } -/** - * Set the dimensions of a textarea, causing a reflow and - * emitting a redraw request. - * - * \param width the new width of the textarea - * \param height the new height of the textarea - */ -void textarea_set_dimensions(struct text_area *ta, int width, int height) + +/* exported interface, documented in textarea.h */ +void textarea_set_dimensions(struct textarea *ta, int width, int height) { ta->vis_width = width; ta->vis_height = height; diff --git a/desktop/textarea.h b/desktop/textarea.h index 1cbe5fa43..e4fa2c7aa 100644 --- a/desktop/textarea.h +++ b/desktop/textarea.h @@ -30,30 +30,140 @@ #include "desktop/plot_style.h" /* Text area flags */ -#define TEXTAREA_MULTILINE 0x01 /**< Text area is multiline */ -#define TEXTAREA_READONLY 0x02 /**< Text area is read only */ +typedef enum textarea_flags { + TEXTAREA_DEFAULT = (1 << 0), + TEXTAREA_MULTILINE = (1 << 1), + TEXTAREA_READONLY = (1 << 2) +} textarea_flags; -struct text_area; + +struct textarea; typedef void(*textarea_redraw_request_callback)(void *data, int x, int y, int width, int height); -struct text_area *textarea_create(int width, int height, - unsigned int flags, const plot_font_style_t *style, +/** + * Create a text area + * + * \param width width of the text area + * \param height width of the text area + * \param flags text area flags + * \param style font style + * \param redraw_start_callback will be called when textarea wants to redraw + * \param redraw_end_callback will be called when textarea finisjes redrawing + * \param data user specified data which will be passed to redraw callbacks + * \return Opaque handle for textarea or 0 on error + */ +struct textarea *textarea_create(int width, int height, + textarea_flags flags, const plot_font_style_t *style, textarea_redraw_request_callback redraw_request, void *data); -void textarea_destroy(struct text_area *ta); -bool textarea_set_text(struct text_area *ta, const char *text); -int textarea_get_text(struct text_area *ta, char *buf, unsigned int len); -bool textarea_set_caret(struct text_area *ta, int caret); -int textarea_get_caret(struct text_area *ta); -void textarea_redraw(struct text_area *ta, int x, int y, + +/** + * Destroy a text area + * + * \param ta Text area to destroy + */ +void textarea_destroy(struct textarea *ta); + +/** + * Set the text in a text area, discarding any current text + * + * \param ta Text area + * \param text UTF-8 text to set text area's contents to + * \return true on success, false on memory exhaustion + */ +bool textarea_set_text(struct textarea *ta, const char *text); + +/** + * Extract the text from a text area + * + * \param ta Text area + * \param buf Pointer to buffer to receive data, or NULL + * to read length required + * \param len Length (bytes) of buffer pointed to by buf, or 0 to read length + * \return Length (bytes) written/required or -1 on error + */ +int textarea_get_text(struct textarea *ta, char *buf, unsigned int len); + +/** + * Set the caret's position + * + * \param ta Text area + * \param caret 0-based character index to place caret at, -1 removes + * the caret + * \return true on success false otherwise + */ +bool textarea_set_caret(struct textarea *ta, int caret); + +/** + * Get the caret's position + * + * \param ta Text area + * \return 0-based character index of caret location, or -1 on error + */ +int textarea_get_caret(struct textarea *ta); + +/** + * Handle redraw requests for text areas + * + * \param redraw Redraw request block + * \param x0 left X coordinate of redraw area + * \param y0 top Y coordinate of redraw area + * \param x1 right X coordinate of redraw area + * \param y1 bottom Y coordinate of redraw area + * \param ctx current redraw context + */ +void textarea_redraw(struct textarea *ta, int x, int y, const struct rect *clip, const struct redraw_context *ctx); -bool textarea_keypress(struct text_area *ta, uint32_t key); -bool textarea_mouse_action(struct text_area *ta, browser_mouse_state mouse, + +/** + * Key press handling for text areas. + * + * \param ta The text area which got the keypress + * \param key The ucs4 character codepoint + * \return true if the keypress is dealt with, false otherwise. + */ +bool textarea_keypress(struct textarea *ta, uint32_t key); + +/** + * Handles all kinds of mouse action + * + * \param ta Text area + * \param mouse the mouse state at action moment + * \param x X coordinate + * \param y Y coordinate + * \return true if action was handled false otherwise + */ +bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse, int x, int y); -bool textarea_drag_end(struct text_area *ta, browser_mouse_state mouse, + +/** + * Handles the end of a drag operation + * + * \param ta Text area + * \param mouse the mouse state at drag end moment + * \param x X coordinate + * \param y Y coordinate + * \return true if drag end was handled false otherwise + */ +bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse, int x, int y); -void textarea_get_dimensions(struct text_area *ta, int *width, int *height); -void textarea_set_dimensions(struct text_area *ta, int width, int height); + +/** + * Gets the dimensions of a textarea + * + * \param width if not NULL, gets updated to the width of the textarea + * \param height if not NULL, gets updated to the height of the textarea + */ +void textarea_get_dimensions(struct textarea *ta, int *width, int *height); + +/** + * Set the dimensions of a textarea, causing a reflow and + * emitting a redraw request. + * + * \param width the new width of the textarea + * \param height the new height of the textarea + */ +void textarea_set_dimensions(struct textarea *ta, int width, int height); #endif diff --git a/desktop/textinput.c b/desktop/textinput.c index 8efc71963..b4fda5eef 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -127,14 +127,10 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) assert(bw->window != NULL); - /* keys that take effect wherever the caret is positioned */ + /* safe keys that can be handled whether input claimed or not */ switch (key) { - case KEY_SELECT_ALL: - selection_select_all(bw->cur_sel); - return true; - case KEY_COPY_SELECTION: - gui_copy_to_clipboard(bw->cur_sel); + selection_copy_to_clipboard(bw->cur_sel); return true; case KEY_CLEAR_SELECTION: @@ -151,12 +147,20 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) return false; } - /* pass on to the appropriate field */ - if (!focus->caret_callback) - return false; + if (focus->caret_callback) { + /* Pass keypress onto anything that has claimed input focus */ + return focus->caret_callback(focus, key, + focus->caret_p1, focus->caret_p2); + } - return focus->caret_callback(focus, key, - focus->caret_p1, focus->caret_p2); + /* keys we can't handle here if cursor is in form */ + switch (key) { + case KEY_SELECT_ALL: + selection_select_all(bw->cur_sel); + return true; + } + + return false; } @@ -168,6 +172,8 @@ bool browser_window_key_press(struct browser_window *bw, uint32_t key) * \param utf8_len length (bytes) of text block * \param last true iff this is the last chunk (update screen too) * \return true iff successful + * + * TODO: Remove this function. */ bool browser_window_paste_text(struct browser_window *bw, const char *utf8, diff --git a/desktop/tree.c b/desktop/tree.c index 8517cc122..250bdd861 100644 --- a/desktop/tree.c +++ b/desktop/tree.c @@ -159,7 +159,7 @@ struct tree { int width; /* Tree width */ int height; /* Tree height */ unsigned int flags; /* Tree flags */ - struct text_area *textarea; /* Handle for UTF-8 textarea */ + struct textarea *textarea; /* Handle for UTF-8 textarea */ bool textarea_drag_start; /* whether the start of a mouse drag was in the textarea */ struct node_element *editing; /* Node element being edited */ @@ -2959,7 +2959,7 @@ void tree_start_edit(struct tree *tree, struct node_element *element) if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON) width -= NODE_INSTEP; - tree->textarea = textarea_create(width, height, 0, + tree->textarea = textarea_create(width, height, TEXTAREA_DEFAULT, &plot_fstyle, tree_textarea_redraw_request, tree); if (tree->textarea == NULL) { tree_stop_edit(tree, false); diff --git a/framebuffer/clipboard.c b/framebuffer/clipboard.c index 241e43415..bd9c89dca 100644 --- a/framebuffer/clipboard.c +++ b/framebuffer/clipboard.c @@ -37,117 +37,62 @@ static struct gui_clipboard { } gui_clipboard; + + /** - * Empty the clipboard, called prior to gui_add_to_clipboard and - * gui_commit_clipboard + * Core asks front end for clipboard contents. * - * \return true iff successful + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer */ - -bool gui_empty_clipboard(void) +void gui_get_clipboard(char **buffer, size_t *length) { - const size_t init_size = 1024; + *buffer = NULL; + *length = 0; - if (gui_clipboard.buffer_len == 0) { - gui_clipboard.buffer = malloc(init_size); - if (gui_clipboard.buffer == NULL) - return false; - - gui_clipboard.buffer_len = init_size; - } + if (gui_clipboard.length > 0) { + assert(gui_clipboard.buffer != NULL); + LOG(("Pasting %i bytes: \"%s\"\n", gui_clipboard.length, + gui_clipboard.buffer)); - gui_clipboard.length = 0; + *buffer = malloc(gui_clipboard.length); - return true; + if (*buffer != NULL) { + memcpy(*buffer, gui_clipboard.buffer, + gui_clipboard.length); + *length = gui_clipboard.length; + } + } } /** - * Add some text to the clipboard, optionally appending a trailing space. + * Core tells front end to put given text in clipboard * - * \param text text to be added - * \param length length of text in bytes - * \param space indicates whether a trailing space should be appended - * \param fstyle The font style - * \return true if successful + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array */ - -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { - size_t new_length = gui_clipboard.length + length + (space ? 1 : 0) + 1; - - if (new_length > gui_clipboard.buffer_len) { - size_t new_alloc = new_length + (new_length / 4); + if (gui_clipboard.buffer_len < length + 1) { + /* Make buffer big enough */ char *new_buff; - new_buff = realloc(gui_clipboard.buffer, new_alloc); + new_buff = realloc(gui_clipboard.buffer, length + 1); if (new_buff == NULL) - return false; + return; gui_clipboard.buffer = new_buff; - gui_clipboard.buffer_len = new_alloc; + gui_clipboard.buffer_len = length + 1; } - memcpy(gui_clipboard.buffer + gui_clipboard.length, text, length); - gui_clipboard.length += length; - - if (space) - gui_clipboard.buffer[gui_clipboard.length++] = ' '; + gui_clipboard.length = 0; + memcpy(gui_clipboard.buffer, buffer, length); + gui_clipboard.length = length; gui_clipboard.buffer[gui_clipboard.length] = '\0'; - - return true; -} - - -/** - * Commit the changes made by gui_empty_clipboard and gui_add_to_clipboard. - * - * \return true iff successful - */ - -bool gui_commit_clipboard(void) -{ - /* TODO: Stick the clipboard in some fbtk buffer? */ - return true; -} - - -/** - * Copy the selected contents to the clipboard - * - * \param s selection - * \return true iff successful, ie. cut operation can proceed without losing data - */ - -bool gui_copy_to_clipboard(struct selection *s) -{ - if (!gui_empty_clipboard()) - return false; - - selection_copy_to_clipboard(s); - - return gui_commit_clipboard(); -} - - -/** - * Request to paste the clipboard contents into a textarea/input field - * at a given position. - * - * \param g gui window - * \param x x ordinate at which to paste text - * \param y y ordinate at which to paste text - */ - -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) -{ - if (gui_clipboard.length > 0) { - LOG(("Pasting %i chars: \"%s\"\n", gui_clipboard.length, - gui_clipboard.buffer)); - browser_window_paste_text(g->bw, gui_clipboard.buffer, - gui_clipboard.length, true); - } } diff --git a/gtk/Makefile.target b/gtk/Makefile.target index 2d1eebf50..88f8eccde 100644 --- a/gtk/Makefile.target +++ b/gtk/Makefile.target @@ -112,7 +112,7 @@ S_GTK := font_pango.c bitmap.c gui.c schedule.c thumbnail.c plotters.c \ selection.c history.c window.c filetype.c download.c menu.c \ print.c search.c tabs.c theme.c toolbar.c \ compat.c cookies.c hotlist.c system_colour.c \ - $(addprefix dialogs/,options.c about.c source.c) + $(addprefix dialogs/,preferences.c about.c source.c) S_GTK := $(addprefix gtk/,$(S_GTK)) $(addprefix utils/,container.c) # code in utils/container.ch is non-universal it seems diff --git a/gtk/dialogs/options.c b/gtk/dialogs/options.c deleted file mode 100644 index 8bd5665a3..000000000 --- a/gtk/dialogs/options.c +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * Copyright 2006 Rob Kendrick <rjek@rjek.com> - * Copyright 2008 Mike Lester <element3260@gmail.com> - * Copyright 2009 Daniel Silverstone <dsilvers@netsurf-browser.org> - * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.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 <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <math.h> -#include <gtk/gtk.h> - -#include "desktop/browser_private.h" -#include "desktop/options.h" -#include "desktop/print.h" -#include "desktop/searchweb.h" - -#include "gtk/compat.h" -#include "gtk/gui.h" -#include "gtk/scaffolding.h" -#include "gtk/theme.h" -#include "gtk/dialogs/options.h" -#include "gtk/window.h" -#include "utils/log.h" -#include "utils/utils.h" -#include "utils/messages.h" - -GtkDialog *wndPreferences = NULL; -static GtkBuilder *gladeFile; - -static struct browser_window *current_browser; - -static void dialog_response_handler (GtkDialog *dlg, gint res_id); -static gboolean on_dialog_close (GtkDialog *dlg, gboolean stay_alive); -static void nsgtk_options_theme_combo(void); - -/* Declares both widget and callback */ -#define DECLARE(x) \ - static GtkWidget *x; \ - static gboolean on_##x##_changed(GtkWidget *widget, gpointer data) - -DECLARE(entryHomePageURL); -DECLARE(setCurrentPage); -DECLARE(setDefaultPage); -DECLARE(checkHideAdverts); -DECLARE(checkDisablePopups); -DECLARE(checkDisablePlugins); -DECLARE(spinHistoryAge); -DECLARE(checkHoverURLs); -DECLARE(checkDisplayRecentURLs); -//DECLARE(comboboxLanguage); -static GtkWidget *comboboxLanguage; -static gboolean on_comboboxLanguage_changed(GtkComboBox *combo, gpointer data); -DECLARE(checkSendReferer); -DECLARE(checkSendDNT); - -DECLARE(comboProxyType); -DECLARE(entryProxyHost); -DECLARE(spinProxyPort); -DECLARE(entryProxyUser); -DECLARE(entryProxyPassword); -DECLARE(spinMaxFetchers); -DECLARE(spinFetchesPerHost); -DECLARE(spinCachedConnections); - -DECLARE(checkEnableJavascript); - -DECLARE(checkResampleImages); -DECLARE(spinAnimationSpeed); -DECLARE(checkEnableAnimations); - -//DECLARE(fontSansSerif); -//DECLARE(fontSerif); -//DECLARE(fontMonospace); -//DECLARE(fontCursive); -//DECLARE(fontFantasy); -DECLARE(comboDefault); -DECLARE(spinDefaultSize); -//DECLARE(spinMinimumSize); -DECLARE(fontPreview); - -DECLARE(comboButtonType); - -DECLARE(spinMemoryCacheSize); -DECLARE(spinDiscCacheAge); - -DECLARE(checkClearDownloads); -DECLARE(checkRequestOverwrite); -DECLARE(fileChooserDownloads); -/* Tabs */ -DECLARE(checkShowSingleTab); -DECLARE(checkFocusNew); -DECLARE(checkNewBlank); -DECLARE(comboTabPosition); - -DECLARE(checkUrlSearch); -DECLARE(comboSearch); -DECLARE(combotheme); -DECLARE(buttonaddtheme); -DECLARE(sourceButtonTab); -static GtkWidget *sourceButtonWindow; - -DECLARE(spinMarginTop); -DECLARE(spinMarginBottom); -DECLARE(spinMarginLeft); -DECLARE(spinMarginRight); -DECLARE(spinExportScale); -DECLARE(checkSuppressImages); -DECLARE(checkRemoveBackgrounds); -DECLARE(checkFitPage); -DECLARE(checkCompressPDF); -DECLARE(checkPasswordPDF); -//DECLARE(setDefaultExportOptions); - -/* Used when the feature is not implemented yet */ -#define FIND_WIDGET(wname) \ - do { \ - (wname) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #wname)); \ - if ((wname) == NULL) \ - LOG(("Unable to find widget '%s'!", #wname)); \ - } while (0) - -/* Assigns widget and connects it to its callback function */ -#define CONNECT(wname, event) \ - do { \ - if ((wname) == NULL) \ - LOG(("Unable to find widget '%s'!", #wname)); \ - else \ - g_signal_connect(G_OBJECT(wname), event, \ - G_CALLBACK(on_##wname##_changed), NULL); \ - } while (0) - -/* exported interface documented in gtk/dialogs/options.h */ -GtkDialog* -nsgtk_options_init(struct browser_window *bw, GtkWindow *parent) -{ - GError *error = NULL; - GObject *dlgobject; - //GSList *group; - - gladeFile = gtk_builder_new(); - if (!gtk_builder_add_from_file(gladeFile, glade_file_location->options, &error)) { - g_warning("Couldn't load builder file: %s", error->message); - g_error_free(error); - return NULL; - } - - - dlgobject = gtk_builder_get_object(gladeFile, "dialogPreferences"); - if (dlgobject == NULL) { - LOG(("Unable to get object for preferences dialog")); - return NULL; - } - - current_browser = bw; - wndPreferences = GTK_DIALOG(dlgobject); - gtk_window_set_transient_for(GTK_WINDOW(wndPreferences), parent); - - /* set the widgets to reflect the current options */ - nsgtk_options_load(); - - FIND_WIDGET(sourceButtonTab); - FIND_WIDGET(sourceButtonWindow); - //group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(sourceButtonWindow)); - //gtk_radio_button_set_group(GTK_RADIO_BUTTON(sourceButtonTab), group); - - - /* Connect all widgets to their appropriate callbacks */ - CONNECT(entryHomePageURL, "focus-out-event"); - CONNECT(setCurrentPage, "clicked"); - CONNECT(setDefaultPage, "clicked"); - CONNECT(checkHideAdverts, "toggled"); - - CONNECT(checkDisablePopups, "toggled"); - CONNECT(checkDisablePlugins, "toggled"); - CONNECT(spinHistoryAge, "focus-out-event"); - CONNECT(checkHoverURLs, "toggled"); - - CONNECT(comboboxLanguage, "changed"); - - CONNECT(checkDisplayRecentURLs, "toggled"); - CONNECT(checkSendReferer, "toggled"); - CONNECT(checkSendDNT, "toggled"); - CONNECT(checkShowSingleTab, "toggled"); - - CONNECT(comboProxyType, "changed"); - CONNECT(entryProxyHost, "focus-out-event"); - CONNECT(spinProxyPort, "focus-out-event"); - CONNECT(entryProxyUser, "focus-out-event"); - CONNECT(entryProxyPassword, "focus-out-event"); - CONNECT(spinMaxFetchers, "value-changed"); - CONNECT(spinFetchesPerHost, "value-changed"); - CONNECT(spinCachedConnections, "value-changed"); - - CONNECT(checkEnableJavascript, "toggled"); - - CONNECT(checkResampleImages, "toggled"); - CONNECT(spinAnimationSpeed, "value-changed"); - CONNECT(checkEnableAnimations, "toggled"); - -/* CONNECT(fontSansSerif, "font-set"); - CONNECT(fontSerif, "font-set"); - CONNECT(fontMonospace, "font-set"); - CONNECT(fontCursive, "font-set"); - CONNECT(fontFantasy, "font-set"); - CONNECT(spinMinimumSize, "value-changed"); -*/ - CONNECT(comboDefault, "changed"); - CONNECT(spinDefaultSize, "value-changed"); - CONNECT(fontPreview, "clicked"); - - CONNECT(comboButtonType, "changed"); - - CONNECT(comboTabPosition, "changed"); - - CONNECT(spinMemoryCacheSize, "value-changed"); - CONNECT(spinDiscCacheAge, "value-changed"); - - CONNECT(checkClearDownloads, "toggled"); - CONNECT(checkRequestOverwrite, "toggled"); - CONNECT(fileChooserDownloads, "current-folder-changed"); - - CONNECT(checkFocusNew, "toggled"); - CONNECT(checkNewBlank, "toggled"); - CONNECT(checkUrlSearch, "toggled"); - CONNECT(comboSearch, "changed"); - - CONNECT(combotheme, "changed"); - CONNECT(buttonaddtheme, "clicked"); - CONNECT(sourceButtonTab, "toggled"); - - CONNECT(spinMarginTop, "value-changed"); - CONNECT(spinMarginBottom, "value-changed"); - CONNECT(spinMarginLeft, "value-changed"); - CONNECT(spinMarginRight, "value-changed"); - CONNECT(spinExportScale, "value-changed"); - CONNECT(checkSuppressImages, "toggled"); - CONNECT(checkRemoveBackgrounds, "toggled"); - CONNECT(checkFitPage, "toggled"); - CONNECT(checkCompressPDF, "toggled"); - CONNECT(checkPasswordPDF, "toggled"); -// CONNECT(setDefaultExportOptions, "clicked"); - - g_signal_connect(G_OBJECT(wndPreferences), "response", - G_CALLBACK (dialog_response_handler), NULL); - - g_signal_connect(G_OBJECT(wndPreferences), "delete-event", - G_CALLBACK (on_dialog_close), (gpointer)TRUE); - - g_signal_connect(G_OBJECT(wndPreferences), "destroy", - G_CALLBACK (on_dialog_close), (gpointer)FALSE); - - gtk_widget_show(GTK_WIDGET(wndPreferences)); - - return wndPreferences; -} - -#define SET_ENTRY(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_entry_set_text(GTK_ENTRY((widget)), (value)); \ - } while (0) - -#define SET_SPIN(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_spin_button_set_value(GTK_SPIN_BUTTON((widget)), (value)); \ - } while (0) - -#define SET_CHECK(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON((widget)), \ - (value)); \ - } while (0) - -#define SET_COMBO(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_combo_box_set_active(GTK_COMBO_BOX((widget)), (value)); \ - } while (0) - -#define SET_FONT(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_font_button_set_font_name(GTK_FONT_BUTTON((widget)), \ - (value)); \ - } while (0) - -#define SET_FILE_CHOOSER(widget, value) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(\ - (widget)), (value)); \ - } while (0) - -#define SET_BUTTON(widget) \ - do { \ - (widget) = GTK_WIDGET(gtk_builder_get_object(gladeFile, #widget)); \ - } while (0) - -static void set_proxy_widgets_sensitivity(int proxyval) -{ - switch (proxyval) { - case 0: /* no proxy */ - gtk_widget_set_sensitive(entryProxyHost, FALSE); - gtk_widget_set_sensitive(spinProxyPort, FALSE); - gtk_widget_set_sensitive(entryProxyUser, FALSE); - gtk_widget_set_sensitive(entryProxyPassword, FALSE); - break; - - case 1: /* proxy with no auth */ - gtk_widget_set_sensitive(entryProxyHost, TRUE); - gtk_widget_set_sensitive(spinProxyPort, TRUE); - gtk_widget_set_sensitive(entryProxyUser, FALSE); - gtk_widget_set_sensitive(entryProxyPassword, FALSE); - break; - - case 2: /* proxy with basic auth */ - gtk_widget_set_sensitive(entryProxyHost, TRUE); - gtk_widget_set_sensitive(spinProxyPort, TRUE); - gtk_widget_set_sensitive(entryProxyUser, TRUE); - gtk_widget_set_sensitive(entryProxyPassword, TRUE); - break; - - case 3: /* proxy with ntlm auth */ - gtk_widget_set_sensitive(entryProxyHost, TRUE); - gtk_widget_set_sensitive(spinProxyPort, TRUE); - gtk_widget_set_sensitive(entryProxyUser, TRUE); - gtk_widget_set_sensitive(entryProxyPassword, TRUE); - break; - - case 4: /* system proxy */ - gtk_widget_set_sensitive(entryProxyHost, FALSE); - gtk_widget_set_sensitive(spinProxyPort, FALSE); - gtk_widget_set_sensitive(entryProxyUser, FALSE); - gtk_widget_set_sensitive(entryProxyPassword, FALSE); - break; - - } -} - -void nsgtk_options_load(void) -{ - const char *default_accept_language = "en"; - const char *default_homepage_url = ""; - const char *default_http_proxy_host; - const char *default_http_proxy_auth_user; - const char *default_http_proxy_auth_pass; - - int active_language = 0; - GtkListStore *liststore; - GtkTreeIter iter; - - int proxytype = 0; - FILE *fp; - char buf[50]; - - /* Network - HTTP Proxy */ - default_http_proxy_host = nsoption_charp(http_proxy_host); - default_http_proxy_auth_user = nsoption_charp(http_proxy_auth_user); - default_http_proxy_auth_pass = nsoption_charp(http_proxy_auth_pass); - - if (nsoption_bool(http_proxy) == true) { - /* proxy type combo box starts with disabled, to allow - * for this the http_proxy option needs combining with - * the http_proxy_auth option - */ - proxytype = nsoption_int(http_proxy_auth) + 1; - if (default_http_proxy_host == NULL) { - /* set to use a proxy without a host, turn proxy off */ - proxytype = 0; - } else if (((proxytype == 2) || - (proxytype == 3)) && - ((default_http_proxy_auth_user == NULL) || - (default_http_proxy_auth_pass == NULL))) { - /* authentication selected with empty credentials, turn proxy off */ - proxytype = 0; - } - } - - if (default_http_proxy_host == NULL) { - default_http_proxy_host = ""; - } - - if (default_http_proxy_auth_user == NULL) { - default_http_proxy_auth_user = ""; - } - - if (default_http_proxy_auth_pass == NULL) { - default_http_proxy_auth_pass = ""; - } - - SET_COMBO(comboProxyType, proxytype); - SET_ENTRY(entryProxyHost, default_http_proxy_host); - SET_SPIN(spinProxyPort, nsoption_int(http_proxy_port)); - SET_ENTRY(entryProxyUser, default_http_proxy_auth_user); - SET_ENTRY(entryProxyPassword, default_http_proxy_auth_pass); - - set_proxy_widgets_sensitivity(proxytype); - - - /* accept language selection */ - if (nsoption_charp(accept_language) != NULL) { - default_accept_language = nsoption_charp(accept_language); - } - - /* Fill content language list store */ - liststore = GTK_LIST_STORE(gtk_builder_get_object(gladeFile, "liststore_content_language")); - if ((liststore != NULL) && - (languages_file_location != NULL) && - ((fp = fopen(languages_file_location, "r")) != NULL)) { - int combo_row_count = 0; - - gtk_list_store_clear(liststore); - active_language = -1; - - LOG(("Used %s for languages", languages_file_location)); - while (fgets(buf, sizeof(buf), fp)) { - /* Ignore blank lines */ - if (buf[0] == '\0') - continue; - - /* Remove trailing \n */ - buf[strlen(buf) - 1] = '\0'; - - gtk_list_store_append(liststore, &iter); - gtk_list_store_set(liststore, &iter, 0, buf, -1 ); - - if (strcmp(buf, default_accept_language) == 0) { - active_language = combo_row_count; - } - - combo_row_count++; - } - - if (active_language == -1) { - /* configured language was not in list, add it */ - gtk_list_store_append(liststore, &iter); - gtk_list_store_set(liststore, &iter, 0, default_accept_language, -1 ); - active_language = combo_row_count; - - } - - fclose(fp); - } else { - LOG(("Failed opening languages file")); - } - - SET_COMBO(comboboxLanguage, active_language); - - - /* Startup */ - if (nsoption_charp(homepage_url) != NULL) { - default_homepage_url = nsoption_charp(homepage_url); - } - - SET_ENTRY(entryHomePageURL, default_homepage_url); - SET_BUTTON(setCurrentPage); - SET_BUTTON(setDefaultPage); - - /* Theme */ - nsgtk_options_theme_combo(); - - SET_CHECK(checkHideAdverts, nsoption_bool(block_ads)); - - SET_CHECK(checkDisablePopups, nsoption_bool(disable_popups)); - SET_CHECK(checkDisablePlugins, nsoption_bool(disable_plugins)); - SET_SPIN(spinHistoryAge, nsoption_int(history_age)); - SET_CHECK(checkHoverURLs, nsoption_bool(hover_urls)); - - SET_CHECK(checkDisplayRecentURLs, nsoption_bool(url_suggestion)); - SET_CHECK(checkSendReferer, nsoption_bool(send_referer)); - SET_CHECK(checkSendDNT, nsoption_bool(do_not_track)); - SET_CHECK(checkShowSingleTab, nsoption_bool(show_single_tab)); - - SET_SPIN(spinMaxFetchers, nsoption_int(max_fetchers)); - SET_SPIN(spinFetchesPerHost, nsoption_int(max_fetchers_per_host)); - SET_SPIN(spinCachedConnections, nsoption_int(max_cached_fetch_handles)); - - SET_CHECK(checkEnableJavascript, nsoption_bool(enable_javascript)); - - SET_CHECK(checkResampleImages, nsoption_bool(render_resample)); - SET_SPIN(spinAnimationSpeed, nsoption_int(minimum_gif_delay) / 100.0); - SET_CHECK(checkEnableAnimations, nsoption_bool(animate_images)); - -/* SET_FONT(fontSansSerif, nsoption_charp(font_sans)); - SET_FONT(fontSerif, nsoption_charp(font_serif)); - SET_FONT(fontMonospace, nsoption_charp(font_mono)); - SET_FONT(fontCursive, nsoption_charp(font_cursive)); - SET_FONT(fontFantasy, nsoption_charp(font_fantasy)); - SET_SPIN(spinMinimumSize, nsoption_bool(font_min_size) / 10); -*/ - SET_COMBO(comboDefault, nsoption_int(font_default)); - SET_SPIN(spinDefaultSize, nsoption_int(font_size) / 10); - SET_BUTTON(fontPreview); - - SET_COMBO(comboButtonType, nsoption_int(button_type) -1); - - SET_COMBO(comboTabPosition, nsoption_int(position_tab)); - - SET_SPIN(spinMemoryCacheSize, nsoption_int(memory_cache_size) >> 20); - SET_SPIN(spinDiscCacheAge, nsoption_int(disc_cache_age)); - - SET_CHECK(checkClearDownloads, nsoption_bool(downloads_clear)); - SET_CHECK(checkRequestOverwrite, nsoption_bool(request_overwrite)); - SET_FILE_CHOOSER(fileChooserDownloads, nsoption_charp(downloads_directory)); - - SET_CHECK(checkFocusNew, nsoption_bool(focus_new)); - SET_CHECK(checkNewBlank, nsoption_bool(new_blank)); - SET_CHECK(checkUrlSearch, nsoption_bool(search_url_bar)); - SET_COMBO(comboSearch, nsoption_int(search_provider)); - - SET_BUTTON(buttonaddtheme); - SET_CHECK(sourceButtonTab, nsoption_bool(source_tab)); - - SET_SPIN(spinMarginTop, nsoption_int(margin_top)); - SET_SPIN(spinMarginBottom, nsoption_int(margin_bottom)); - SET_SPIN(spinMarginLeft, nsoption_int(margin_left)); - SET_SPIN(spinMarginRight, nsoption_int(margin_right)); - SET_SPIN(spinExportScale, nsoption_int(export_scale)); - SET_CHECK(checkSuppressImages, nsoption_bool(suppress_images)); - SET_CHECK(checkRemoveBackgrounds, nsoption_bool(remove_backgrounds)); - SET_CHECK(checkFitPage, nsoption_bool(enable_loosening)); - SET_CHECK(checkCompressPDF, nsoption_bool(enable_PDF_compression)); - SET_CHECK(checkPasswordPDF, nsoption_bool(enable_PDF_password)); -// SET_BUTTON(setDefaultExportOptions); -} - -static void dialog_response_handler(GtkDialog *dlg, gint res_id) -{ - switch (res_id) { - case GTK_RESPONSE_CLOSE: - on_dialog_close(dlg, TRUE); - } -} - -static gboolean on_dialog_close (GtkDialog *dlg, gboolean stay_alive) -{ - LOG(("Writing options to file")); - nsoption_write(options_file_location); - if ((stay_alive) && GTK_IS_WIDGET(dlg)) - gtk_widget_hide(GTK_WIDGET(dlg)); - else { - stay_alive = FALSE; - } - return stay_alive; -} - -static void nsgtk_options_theme_combo(void) { -/* populate theme combo from themelist file */ - GtkBox *box = GTK_BOX(gtk_builder_get_object(gladeFile, "themehbox")); - char buf[50]; - size_t len = SLEN("themelist") + strlen(res_dir_location) + 1; - char themefile[len]; - - combotheme = nsgtk_combo_box_text_new(); - - if ((combotheme == NULL) || (box == NULL)) { - warn_user(messages_get("NoMemory"), 0); - return; - } - snprintf(themefile, len, "%sthemelist", res_dir_location); - FILE *fp = fopen((const char *)themefile, "r"); - if (fp == NULL) { - LOG(("Failed opening themes file")); - warn_user("FileError", (const char *) themefile); - return; - } - while (fgets(buf, sizeof(buf), fp) != NULL) { - /* Ignore blank lines */ - if (buf[0] == '\0') - continue; - - /* Remove trailing \n */ - buf[strlen(buf) - 1] = '\0'; - - nsgtk_combo_box_text_append_text(combotheme, buf); - } - fclose(fp); - gtk_combo_box_set_active(GTK_COMBO_BOX(combotheme), - nsoption_int(current_theme)); - gtk_box_pack_start(box, combotheme, FALSE, TRUE, 0); - gtk_widget_show(combotheme); -} - -bool nsgtk_options_combo_theme_add(const char *themename) -{ - if (wndPreferences == NULL) - return false; - nsgtk_combo_box_text_append_text(combotheme, themename); - return true; -} - - -/* Defines the callback functions for all widgets and specifies - * nsgtk_reflow_all_windows only where necessary */ - -#define ENTRY_CHANGED(widget, option) \ -static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) \ -{ \ - if (!g_str_equal(gtk_entry_get_text(GTK_ENTRY((widget))), \ - nsoption_charp(option) ? nsoption_charp(option) : "")) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_charp(option, strdup(gtk_entry_get_text(GTK_ENTRY((widget))))); \ - } \ - return FALSE; \ -} - -#define CHECK_CHANGED(widget, option) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_bool(option, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON((widget)))); \ - do - -#define SPIN_CHANGED(widget, option) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_int(option, gtk_spin_button_get_value(GTK_SPIN_BUTTON((widget)))); \ - do - -#define COMBO_CHANGED(widget, option) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_int(option, gtk_combo_box_get_active(GTK_COMBO_BOX((widget)))); \ - do - -#define FONT_CHANGED(widget, option) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - nsoption_set_charp(option, strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON((widget))))); \ - do - -#define BUTTON_CLICKED(widget) \ - static gboolean on_##widget##_changed(GtkWidget *widget, gpointer data) { \ - LOG(("Signal emitted on '%s'", #widget)); \ - do - -#define END_HANDLER \ - while (0); \ - return FALSE; \ - } - -static gboolean on_comboboxLanguage_changed(GtkComboBox *combo, gpointer data) -{ - gchar *lang = NULL; - GtkTreeIter iter; - GtkTreeModel *model; - - /* Obtain currently selected item from combo box. - * If nothing is selected, do nothing. - */ - if (gtk_combo_box_get_active_iter(combo, &iter)) { - /* Obtain data model from combo box. */ - model = gtk_combo_box_get_model(combo); - - /* Obtain string from model. */ - gtk_tree_model_get(model, &iter, 0, &lang, -1); - } - - if (lang != NULL) { - nsoption_set_charp(accept_language, strdup(lang)); - g_free(lang); - } - - return FALSE; -} - -ENTRY_CHANGED(entryHomePageURL, homepage_url) - -BUTTON_CLICKED(setCurrentPage) -{ - const gchar *url; - url = nsurl_access(hlcache_handle_get_url(current_browser->current_content)); - gtk_entry_set_text(GTK_ENTRY(entryHomePageURL), url); - nsoption_set_charp(homepage_url, - strdup(gtk_entry_get_text(GTK_ENTRY(entryHomePageURL)))); -} -END_HANDLER - -BUTTON_CLICKED(setDefaultPage) -{ - gtk_entry_set_text(GTK_ENTRY(entryHomePageURL), NETSURF_HOMEPAGE); - nsoption_set_charp(homepage_url, - strdup(gtk_entry_get_text(GTK_ENTRY(entryHomePageURL)))); -} -END_HANDLER - -CHECK_CHANGED(checkHideAdverts, block_ads) -{ -} -END_HANDLER - -CHECK_CHANGED(checkDisplayRecentURLs, url_suggestion) -{ -} -END_HANDLER - -CHECK_CHANGED(checkSendReferer, send_referer) -{ -} -END_HANDLER - -CHECK_CHANGED(checkSendDNT, do_not_track) -{ -} -END_HANDLER - -CHECK_CHANGED(checkShowSingleTab, show_single_tab) -{ - nsgtk_reflow_all_windows(); -} -END_HANDLER - - -COMBO_CHANGED(comboProxyType, http_proxy_auth) -{ - LOG(("proxy auth: %d", nsoption_int(http_proxy_auth))); - - set_proxy_widgets_sensitivity(nsoption_int(http_proxy_auth)); - switch (nsoption_int(http_proxy_auth)) { - case 0: /* no proxy */ - nsoption_set_bool(http_proxy, false); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); - break; - - case 1: /* proxy with no auth */ - nsoption_set_bool(http_proxy, true); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); - break; - - case 2: /* proxy with basic auth */ - nsoption_set_bool(http_proxy, true); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC); - break; - - case 3: /* proxy with ntlm auth */ - nsoption_set_bool(http_proxy, true); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NTLM); - break; - - case 4: /* system proxy */ - nsoption_set_bool(http_proxy, true); - nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); - break; - - } -} -END_HANDLER - -ENTRY_CHANGED(entryProxyHost, http_proxy_host) - -SPIN_CHANGED(spinProxyPort, http_proxy_port) -{ -} -END_HANDLER - -ENTRY_CHANGED(entryProxyUser, http_proxy_auth_user) - -ENTRY_CHANGED(entryProxyPassword, http_proxy_auth_pass) - -SPIN_CHANGED(spinMaxFetchers, max_fetchers) -{ -} -END_HANDLER - -SPIN_CHANGED(spinFetchesPerHost, max_fetchers_per_host) -{ -} -END_HANDLER - -SPIN_CHANGED(spinCachedConnections, max_cached_fetch_handles) -{ -} -END_HANDLER - -CHECK_CHANGED(checkResampleImages, render_resample) -{ -} -END_HANDLER - -static gboolean on_spinAnimationSpeed_changed(GtkWidget *widget, gpointer data) -{ - LOG(("Signal emitted on '%s'", "spinAnimationSpeed")); - nsoption_set_int(minimum_gif_delay, - round(gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)) * 100.0)); - return FALSE; -} - -CHECK_CHANGED(checkEnableAnimations, animate_images) -{ -} -END_HANDLER - -CHECK_CHANGED(checkEnableJavascript, enable_javascript) -{ -} -END_HANDLER - -CHECK_CHANGED(checkDisablePopups, disable_popups) -{ -} -END_HANDLER - -CHECK_CHANGED(checkDisablePlugins, disable_plugins) -{ -} -END_HANDLER - -SPIN_CHANGED(spinHistoryAge, history_age) -{ -} -END_HANDLER - -CHECK_CHANGED(checkHoverURLs, hover_urls) -{ -} -END_HANDLER -/* -FONT_CHANGED(fontSansSerif, font_sans) -{ -} -END_HANDLER - -FONT_CHANGED(fontSerif, font_serif) -{ -} -END_HANDLER - -FONT_CHANGED(fontMonospace, font_mono) -{ -} -END_HANDLER - -FONT_CHANGED(fontCursive, font_cursive) -{ -} -END_HANDLER - -FONT_CHANGED(fontFantasy, font_fantasy) -{ -} -END_HANDLER -*/ -COMBO_CHANGED(comboDefault, font_default) -{ -} -END_HANDLER - -SPIN_CHANGED(spinDefaultSize, font_size) -{ - nsoption_set_int(font_size, nsoption_int(font_size) * 10); -} -END_HANDLER - -/*SPIN_CHANGED(spinMinimumSize, font_min_size) -{ - nsoption_set_int(font_min_size, nsoption_int(font_min_size) * 10); -} -END_HANDLER -*/ -BUTTON_CLICKED(fontPreview) -{ - nsgtk_reflow_all_windows(); -} -END_HANDLER - -COMBO_CHANGED(comboButtonType, button_type) -{ - nsgtk_scaffolding *current = scaf_list; - nsoption_set_int(button_type, nsoption_int(button_type) + 1); - - /* value of 0 is reserved for 'unset' */ - while (current) { - nsgtk_scaffolding_reset_offset(current); - switch(nsoption_int(button_type)) { - case 1: - gtk_toolbar_set_style( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_TOOLBAR_ICONS); - gtk_toolbar_set_icon_size( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_ICON_SIZE_SMALL_TOOLBAR); - break; - case 2: - gtk_toolbar_set_style( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_TOOLBAR_ICONS); - gtk_toolbar_set_icon_size( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_ICON_SIZE_LARGE_TOOLBAR); - break; - case 3: - gtk_toolbar_set_style( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_TOOLBAR_BOTH); - gtk_toolbar_set_icon_size( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_ICON_SIZE_LARGE_TOOLBAR); - break; - case 4: - gtk_toolbar_set_style( - GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), - GTK_TOOLBAR_TEXT); - default: - break; - } - current = nsgtk_scaffolding_iterate(current); - } -} -END_HANDLER - -COMBO_CHANGED(comboTabPosition, position_tab) -{ - nsgtk_scaffolding *current = scaf_list; - nsoption_set_int(button_type, nsoption_int(button_type) + 1); - - /* value of 0 is reserved for 'unset' */ - while (current) { - nsgtk_scaffolding_reset_offset(current); - - nsgtk_reflow_all_windows(); - - current = nsgtk_scaffolding_iterate(current); - } -} -END_HANDLER - -SPIN_CHANGED(spinMemoryCacheSize, memory_cache_size) -{ - nsoption_set_int(memory_cache_size, nsoption_int(memory_cache_size) << 20); -} -END_HANDLER - -SPIN_CHANGED(spinDiscCacheAge, disc_cache_age) -{ -} -END_HANDLER - -CHECK_CHANGED(checkClearDownloads, downloads_clear) -{ -} -END_HANDLER - -CHECK_CHANGED(checkRequestOverwrite, request_overwrite) -{ -} -END_HANDLER - -static gboolean on_fileChooserDownloads_changed(GtkWidget *widget, gpointer data) -{ - gchar *dir; - LOG(("Signal emitted on '%s'", "fileChooserDownloads")); - - dir = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER((widget))); - nsoption_set_charp(downloads_directory, strdup(dir)); - g_free(dir); - return FALSE; -} - -CHECK_CHANGED(checkFocusNew, focus_new) -{ -} -END_HANDLER - -CHECK_CHANGED(checkNewBlank, new_blank) -{ -} -END_HANDLER - -CHECK_CHANGED(checkUrlSearch, search_url_bar) -{ -} -END_HANDLER - -COMBO_CHANGED(comboSearch, search_provider) -{ - nsgtk_scaffolding *current = scaf_list; - char *name; - - /* refresh web search prefs from file */ - search_web_provider_details(nsoption_charp(search_provider)); - - /* retrieve ico */ - search_web_retrieve_ico(false); - - /* callback may handle changing gui */ - if (search_web_ico() != NULL) - gui_window_set_search_ico(search_web_ico()); - - /* set entry */ - name = search_web_provider_name(); - if (name == NULL) { - warn_user(messages_get("NoMemory"), 0); - continue; - } - char content[strlen(name) + SLEN("Search ") + 1]; - sprintf(content, "Search %s", name); - free(name); - while (current) { - nsgtk_scaffolding_set_websearch(current, content); - current = nsgtk_scaffolding_iterate(current); - } -} -END_HANDLER - -COMBO_CHANGED(combotheme, current_theme) -{ - nsgtk_scaffolding *current = scaf_list; - char *name; - if (nsoption_int(current_theme) != 0) { - if (nsgtk_theme_name() != NULL) - free(nsgtk_theme_name()); - name = nsgtk_combo_box_text_get_active_text(combotheme); - if (name != NULL) { - nsgtk_theme_set_name(name); - nsgtk_theme_prepare(); - /* possible name leak */ - } - } else if (nsgtk_theme_name() != NULL) { - free(nsgtk_theme_name()); - nsgtk_theme_set_name(NULL); - } - - while (current) { - nsgtk_theme_implement(current); - current = nsgtk_scaffolding_iterate(current); - } -} -END_HANDLER - -BUTTON_CLICKED(buttonaddtheme) -{ - char *filename, *directory; - size_t len; - GtkWidget *fc = gtk_file_chooser_dialog_new( - messages_get("gtkAddThemeTitle"), - GTK_WINDOW(wndPreferences), - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, - GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); - len = SLEN("themes") + strlen(res_dir_location) + 1; - char themesfolder[len]; - snprintf(themesfolder, len, "%sthemes", res_dir_location); - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc), - themesfolder); - gint res = gtk_dialog_run(GTK_DIALOG(fc)); - if (res == GTK_RESPONSE_ACCEPT) { - filename = gtk_file_chooser_get_current_folder( - GTK_FILE_CHOOSER(fc)); - if (strcmp(filename, themesfolder) != 0) { - directory = strrchr(filename, '/'); - *directory = '\0'; - if (strcmp(filename, themesfolder) != 0) { - warn_user(messages_get( - "gtkThemeFolderInstructions"), - 0); - gtk_widget_destroy(GTK_WIDGET(fc)); - if (filename != NULL) - g_free(filename); - return FALSE; - } else { - directory++; - } - } else { - if (filename != NULL) - g_free(filename); - filename = gtk_file_chooser_get_filename( - GTK_FILE_CHOOSER(fc)); - if (strcmp(filename, themesfolder) == 0) { - warn_user(messages_get("gtkThemeFolderSub"), - 0); - gtk_widget_destroy(GTK_WIDGET(fc)); - g_free(filename); - return FALSE; - } - directory = strrchr(filename, '/') + 1; - } - gtk_widget_destroy(GTK_WIDGET(fc)); - nsgtk_theme_add(directory); - if (filename != NULL) - g_free(filename); - } -} -END_HANDLER - -CHECK_CHANGED(sourceButtonTab, source_tab) -{ -} -END_HANDLER - -SPIN_CHANGED(spinMarginTop, margin_top) -{ -} -END_HANDLER - -SPIN_CHANGED(spinMarginBottom, margin_bottom) -{ -} -END_HANDLER - -SPIN_CHANGED(spinMarginLeft, margin_left) -{ -} -END_HANDLER - -SPIN_CHANGED(spinMarginRight, margin_right) -{ -} -END_HANDLER - -SPIN_CHANGED(spinExportScale, export_scale) -{ -} -END_HANDLER - -CHECK_CHANGED(checkSuppressImages, suppress_images) -{ -} -END_HANDLER - -CHECK_CHANGED(checkRemoveBackgrounds, remove_backgrounds) -{ -} -END_HANDLER - -CHECK_CHANGED(checkFitPage, enable_loosening) -{ -} -END_HANDLER - -CHECK_CHANGED(checkCompressPDF, enable_PDF_compression) -{ -} -END_HANDLER - -CHECK_CHANGED(checkPasswordPDF, enable_PDF_password) -{ -} -END_HANDLER - -/* -BUTTON_CLICKED(setDefaultExportOptions) -{ - nsoption_set_int(margin_top, DEFAULT_MARGIN_TOP_MM); - nsoption_set_int(margin_bottom, DEFAULT_MARGIN_BOTTOM_MM); - nsoption_set_int(margin_left, DEFAULT_MARGIN_LEFT_MM); - nsoption_set_int(margin_right, DEFAULT_MARGIN_RIGHT_MM); - nsoption_set_int(export_scale, DEFAULT_EXPORT_SCALE * 100); - nsoption_set_bool(suppress_images, false); - nsoption_set_bool(remove_backgrounds, false); - nsoption_set_bool(enable_loosening, true); - nsoption_set_bool(enable_PDF_compression, true); - nsoption_set_bool(enable_PDF_password, false); - - SET_SPIN(spinMarginTop, nsoption_int(margin_top)); - SET_SPIN(spinMarginBottom, nsoption_int(margin_bottom)); - SET_SPIN(spinMarginLeft, nsoption_int(margin_left)); - SET_SPIN(spinMarginRight, nsoption_int(margin_right)); - SET_SPIN(spinExportScale, nsoption_int(export_scale)); - SET_CHECK(checkSuppressImages, nsoption_bool(suppress_images)); - SET_CHECK(checkRemoveBackgrounds, nsoption_bool(remove_backgrounds)); - SET_CHECK(checkCompressPDF, nsoption_bool(enable_PDF_compression)); - SET_CHECK(checkPasswordPDF, nsoption_bool(enable_PDF_password)); - SET_CHECK(checkFitPage, nsoption_bool(enable_loosening)); -} -END_HANDLER -*/ diff --git a/gtk/dialogs/preferences.c b/gtk/dialogs/preferences.c new file mode 100644 index 000000000..2d787e6dc --- /dev/null +++ b/gtk/dialogs/preferences.c @@ -0,0 +1,1082 @@ +/* + * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org> + * + * 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 <math.h> + +#include "desktop/browser_private.h" +#include "desktop/options.h" +#include "desktop/searchweb.h" +#include "utils/log.h" +#include "utils/utils.h" +#include "utils/messages.h" + +#include "gtk/compat.h" +#include "gtk/window.h" +#include "gtk/gui.h" +#include "gtk/scaffolding.h" +#include "gtk/theme.h" +#include "gtk/dialogs/preferences.h" + +/* private prefs */ +struct ppref { + /** dialog handle created when window first accessed */ + GObject *dialog; + + struct browser_window *bw; + + /* widgets which are accessed from outside their own signal handlers */ + GtkEntry *entryHomePageURL; + GtkEntry *entryProxyHost; + GtkEntry *entryProxyUser; + GtkEntry *entryProxyPassword; + GtkSpinButton *spinProxyPort; + + /* dynamic list stores */ + GtkListStore *themes; + GtkListStore *content_language; +}; + +static struct ppref ppref; + + +/* Set netsurf option based on toggle button state + * + * This works for any widget which subclasses togglebutton (checkbox, + * radiobutton etc.) + */ +#define TOGGLEBUTTON_SIGNALS(WIDGET, OPTION) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \ + struct ppref *priv) \ +{ \ + nsoption_set_bool(OPTION, \ + gtk_toggle_button_get_active(togglebutton)); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \ + struct ppref *priv) \ +{ \ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), \ + nsoption_bool(OPTION)); \ +} + +#define SPINBUTTON_SIGNALS(WIDGET, OPTION, MULTIPLIER) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \ + struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \ + struct ppref *priv) \ +{ \ + nsoption_set_int(OPTION, \ + round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \ +{ \ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \ + ((gdouble)nsoption_int(OPTION)) / MULTIPLIER); \ +} + +#define ENTRY_SIGNALS(WIDGET, OPTION) \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv)\ +{ \ + nsoption_set_charp(OPTION, \ + strdup(gtk_entry_get_text(GTK_ENTRY(editable)))); \ +} \ + \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \ +G_MODULE_EXPORT void \ +nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \ +{ \ + const char *OPTION; \ + OPTION = nsoption_charp(OPTION); \ + if (OPTION != NULL) { \ + gtk_entry_set_text(GTK_ENTRY(widget), OPTION); \ + } \ +} + +/* GTK module requires these to be exported symbols so these all need + * forward declaring to avoid warnings + */ +G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTheme_changed(GtkComboBox *combo, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTheme_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_buttonAddTheme_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_sourceButtonWindow_toggled(GtkToggleButton *togglebutton, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_sourceButtonWindow_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid); +G_MODULE_EXPORT gboolean nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, struct ppref *priv); +G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv); + + +/********* PDF **********/ + +/* Appearance */ + +/* no images in output */ +TOGGLEBUTTON_SIGNALS(checkSuppressImages, suppress_images) + +/* no background images */ +TOGGLEBUTTON_SIGNALS(checkRemoveBackgrounds, remove_backgrounds) + +/* scale to fit page */ +TOGGLEBUTTON_SIGNALS(checkFitPage, enable_loosening) + +/* port */ +SPINBUTTON_SIGNALS(spinExportScale, export_scale, 1.0) + +/* Margins */ +SPINBUTTON_SIGNALS(spinMarginTop, margin_top, 1.0) +SPINBUTTON_SIGNALS(spinMarginBottom, margin_bottom, 1.0) +SPINBUTTON_SIGNALS(spinMarginLeft, margin_left, 1.0) +SPINBUTTON_SIGNALS(spinMarginRight, margin_right, 1.0) + + +/* Generation */ + +/* output is compressed */ +TOGGLEBUTTON_SIGNALS(checkCompressPDF, enable_PDF_compression) + +/* output has a password */ +TOGGLEBUTTON_SIGNALS(checkPasswordPDF, enable_PDF_password) + +/********* Network **********/ + +/* HTTP proxy */ +static void set_proxy_widgets_sensitivity(int proxyval, struct ppref *priv) +{ + gboolean host; + gboolean port; + gboolean user; + gboolean pass; + + switch (proxyval) { + case 0: /* no proxy */ + host = FALSE; + port = FALSE; + user = FALSE; + pass = FALSE; + break; + + case 1: /* proxy with no auth */ + host = TRUE; + port = TRUE; + user = FALSE; + pass = FALSE; + break; + + case 2: /* proxy with basic auth */ + host = TRUE; + port = TRUE; + user = TRUE; + pass = TRUE; + break; + + case 3: /* proxy with ntlm auth */ + host = TRUE; + port = TRUE; + user = TRUE; + pass = TRUE; + break; + + case 4: /* system proxy */ + host = FALSE; + port = FALSE; + user = FALSE; + pass = FALSE; + break; + + default: + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyHost), host); + gtk_widget_set_sensitive(GTK_WIDGET(priv->spinProxyPort), port); + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyUser), user); + gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyPassword), pass); + +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv) +{ + int proxy_sel; + proxy_sel = gtk_combo_box_get_active(combo); + + switch (proxy_sel) { + case 0: /* no proxy */ + nsoption_set_bool(http_proxy, false); + break; + + case 1: /* proxy with no auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); + break; + + case 2: /* proxy with basic auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC); + break; + + case 3: /* proxy with ntlm auth */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NTLM); + break; + + case 4: /* system proxy */ + nsoption_set_bool(http_proxy, true); + nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE); + break; + } + + set_proxy_widgets_sensitivity(proxy_sel, priv); +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv) +{ + int proxytype = 0; /* no proxy by default */ + + if (nsoption_bool(http_proxy) == true) { + /* proxy type combo box starts with disabled, to allow + * for this the http_proxy option needs combining with + * the http_proxy_auth option + */ + proxytype = nsoption_int(http_proxy_auth) + 1; + if (nsoption_charp(http_proxy_host) == NULL) { + /* set to use a proxy without a host, turn proxy off */ + proxytype = 0; + } else if (((proxytype == 2) || + (proxytype == 3)) && + ((nsoption_charp(http_proxy_auth_user) == NULL) || + (nsoption_charp(http_proxy_auth_pass) == NULL))) { + /* authentication selected with empty credentials, turn proxy off */ + proxytype = 0; + } + } + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), proxytype); + + set_proxy_widgets_sensitivity(proxytype, priv); +} + +/* host */ +ENTRY_SIGNALS(entryProxyHost, http_proxy_host) + +/* port */ +SPINBUTTON_SIGNALS(spinProxyPort, http_proxy_port, 1.0) + +/* user */ +ENTRY_SIGNALS(entryProxyUser, http_proxy_auth_user) + +/* password */ +ENTRY_SIGNALS(entryProxyPassword, http_proxy_auth_pass) + +/* Fetching */ + +/* maximum fetchers */ +SPINBUTTON_SIGNALS(spinMaxFetchers, max_fetchers, 1.0) + +/* fetches per host */ +SPINBUTTON_SIGNALS(spinFetchesPerHost, max_fetchers_per_host, 1.0) + +/* cached connections */ +SPINBUTTON_SIGNALS(spinCachedConnections, max_cached_fetch_handles, 1.0) + + +/********* Privacy **********/ + +/* General */ + +/* enable referral submission */ +TOGGLEBUTTON_SIGNALS(checkSendReferer, send_referer) + +/* send do not track */ +TOGGLEBUTTON_SIGNALS(checkSendDNT, do_not_track) + +/* History */ + +/* local history shows url tooltips */ +TOGGLEBUTTON_SIGNALS(checkHoverURLs, hover_urls) + +/* remember browsing history */ +SPINBUTTON_SIGNALS(spinHistoryAge, history_age, 1.0) + +/* Cache */ + +/* memory cache size */ +SPINBUTTON_SIGNALS(spinMemoryCacheSize, memory_cache_size, (1024*1024)) + +/* disc cache size */ +SPINBUTTON_SIGNALS(spinDiscCacheSize, disc_cache_size, (1024*1024)) + + +/* disc cache age */ +SPINBUTTON_SIGNALS(spinDiscCacheAge, disc_cache_age, 1.0) + + +/********* Content **********/ + +/* Control */ + + +/* prevent popups */ +TOGGLEBUTTON_SIGNALS(checkDisablePopups, disable_popups) + +/* hide adverts */ +TOGGLEBUTTON_SIGNALS(checkHideAdverts, block_ads) + +/* enable javascript */ +TOGGLEBUTTON_SIGNALS(checkEnableJavascript, enable_javascript) + +/* disable plugins */ +TOGGLEBUTTON_SIGNALS(checkDisablePlugins, disable_plugins) + +/* high quality image scaling */ +TOGGLEBUTTON_SIGNALS(checkResampleImages, render_resample) + +/* load and display of images */ +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, + struct ppref *priv) +{ + int img_sel; + /* get the row number for the selection */ + img_sel = gtk_combo_box_get_active(combo); + switch (img_sel) { + case 0: + /* background and foreground */ + nsoption_set_bool(foreground_images, true); + nsoption_set_bool(background_images, true); + break; + + case 1: + /* foreground only */ + nsoption_set_bool(foreground_images, true); + nsoption_set_bool(background_images, false); + break; + + case 2: + /* background only */ + nsoption_set_bool(foreground_images, false); + nsoption_set_bool(background_images, true); + break; + + case 3: + /* no images */ + nsoption_set_bool(foreground_images, false); + nsoption_set_bool(background_images, false); + break; + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, + struct ppref *priv) +{ + if (nsoption_bool(foreground_images)) { + if (nsoption_bool(background_images)) { + /* background and foreground */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); + } else { + /* foreground only */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 1); + } + } else { + if (nsoption_bool(background_images)) { + /* background only */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 2); + } else { + /* no images */ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 3); + } + } +} + +/* Animation */ + +/* enable animation */ +TOGGLEBUTTON_SIGNALS(checkEnableAnimations, animate_images) + +/* frame time */ +SPINBUTTON_SIGNALS(spinAnimationSpeed, minimum_gif_delay, 100.0) + +/* Fonts */ + +/* default font */ +G_MODULE_EXPORT void +nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv) +{ + int font_sel; + /* get the row number for the selection */ + font_sel = gtk_combo_box_get_active(combo); + if ((font_sel >= 0) && (font_sel <= 4)) { + nsoption_set_int(font_default, font_sel); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(font_default)); +} + +/* default font size */ +SPINBUTTON_SIGNALS(spinDefaultSize, font_size, 10.0) + +/* preview - actually reflow all views */ +G_MODULE_EXPORT void +nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv) +{ + nsgtk_reflow_all_windows(); +} + + +/* Language */ + +/* accept language */ +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, + struct ppref *priv) +{ + gchar *lang = NULL; + GtkTreeIter iter; + GtkTreeModel *model; + + /* Obtain currently selected item from combo box. + * If nothing is selected, do nothing. + */ + if (gtk_combo_box_get_active_iter(combo, &iter)) { + /* Obtain data model from combo box. */ + model = gtk_combo_box_get_model(combo); + + /* Obtain string from model. */ + gtk_tree_model_get(model, &iter, 0, &lang, -1); + } + + if (lang != NULL) { + nsoption_set_charp(accept_language, strdup(lang)); + g_free(lang); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, + struct ppref *priv) +{ + /* Fill content language list store */ + int active_language = 0; + GtkTreeIter iter; + FILE *fp; + char buf[50]; + const char *default_accept_language = "en"; + + if ((priv->content_language != NULL) && + (languages_file_location != NULL) && + ((fp = fopen(languages_file_location, "r")) != NULL)) { + int combo_row_count = 0; + + gtk_list_store_clear(priv->content_language); + active_language = -1; + + LOG(("Used %s for languages", languages_file_location)); + while (fgets(buf, sizeof(buf), fp)) { + /* Ignore blank lines */ + if (buf[0] == '\0') + continue; + + /* Remove trailing \n */ + buf[strlen(buf) - 1] = '\0'; + + gtk_list_store_append(priv->content_language, &iter); + gtk_list_store_set(priv->content_language, + &iter, 0, buf, -1 ); + + if (strcmp(buf, default_accept_language) == 0) { + active_language = combo_row_count; + } + + combo_row_count++; + } + + if (active_language == -1) { + /* configured language was not in list, add it */ + gtk_list_store_append(priv->content_language, &iter); + gtk_list_store_set(priv->content_language, + &iter, + 0, default_accept_language, -1 ); + active_language = combo_row_count; + } + + fclose(fp); + } else { + LOG(("Failed opening languages file")); + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), active_language); +} + + +/********* Apperance **********/ + +/* Themes */ + +/* select theme */ +G_MODULE_EXPORT void +nsgtk_preferences_comboTheme_changed(GtkComboBox *combo, struct ppref *priv) +{ + nsgtk_scaffolding *current = scaf_list; + int theme = 0; + gchar *name; + GtkTreeIter iter; + GtkTreeModel *model; + + /* Obtain currently selected item from combo box. + * If nothing is selected, do nothing. + */ + if (gtk_combo_box_get_active_iter(combo, &iter)) { + /* get the row number for the config */ + theme = gtk_combo_box_get_active(combo); + + nsoption_set_int(current_theme, theme); + + /* retrive the theme name if it is not the default */ + if (theme != 0) { + /* Obtain data model from combo box. */ + model = gtk_combo_box_get_model(combo); + + /* Obtain string from model. */ + gtk_tree_model_get(model, &iter, 0, &name, -1); + } else { + name = NULL; + } + + nsgtk_theme_set_name(name); + + if (name != NULL) { + g_free(name); + } + + while (current) { + nsgtk_theme_implement(current); + current = nsgtk_scaffolding_iterate(current); + } + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboTheme_realize(GtkWidget *widget, struct ppref *priv) +{ + /* Fill theme list store */ + FILE *fp; + GtkTreeIter iter; + char buf[50]; + int combo_row_count = 0; + int selected_theme = 0; + + if ((priv->themes != NULL) && + (themelist_file_location != NULL) && + ((fp = fopen(themelist_file_location, "r")) != NULL)) { + gtk_list_store_clear(priv->themes); + + LOG(("Used %s for themelist", themelist_file_location)); + + while (fgets(buf, sizeof(buf), fp)) { + /* Ignore blank lines */ + if (buf[0] == '\0') + continue; + + /* Remove trailing \n */ + buf[strlen(buf) - 1] = '\0'; + + gtk_list_store_append(priv->themes, &iter); + gtk_list_store_set(priv->themes, &iter, 0, buf, -1); + + combo_row_count++; + } + + fclose(fp); + } else { + LOG(("Failed opening themes file")); + } + + /* get configured theme and sanity check value */ + selected_theme = nsoption_int(current_theme); + if (selected_theme > combo_row_count) { + selected_theme = combo_row_count; + } + if (selected_theme < 0) { + selected_theme = 0; + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), selected_theme); +} + +/* add theme */ +G_MODULE_EXPORT void +nsgtk_preferences_buttonAddTheme_clicked(GtkButton *button, struct ppref *priv) +{ + char *filename, *directory; + size_t len; + GtkWidget *fc = gtk_file_chooser_dialog_new( + messages_get("gtkAddThemeTitle"), + GTK_WINDOW(priv->dialog), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + len = SLEN("themes") + strlen(res_dir_location) + 1; + char themesfolder[len]; + snprintf(themesfolder, len, "%sthemes", res_dir_location); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc), + themesfolder); + gint res = gtk_dialog_run(GTK_DIALOG(fc)); + if (res == GTK_RESPONSE_ACCEPT) { + filename = gtk_file_chooser_get_current_folder( + GTK_FILE_CHOOSER(fc)); + if (strcmp(filename, themesfolder) != 0) { + directory = strrchr(filename, '/'); + *directory = '\0'; + if (strcmp(filename, themesfolder) != 0) { + warn_user(messages_get( + "gtkThemeFolderInstructions"), + 0); + gtk_widget_destroy(GTK_WIDGET(fc)); + if (filename != NULL) + g_free(filename); + return; + } else { + directory++; + } + } else { + if (filename != NULL) + g_free(filename); + filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(fc)); + if (strcmp(filename, themesfolder) == 0) { + warn_user(messages_get("gtkThemeFolderSub"), + 0); + gtk_widget_destroy(GTK_WIDGET(fc)); + g_free(filename); + return; + } + directory = strrchr(filename, '/') + 1; + } + gtk_widget_destroy(GTK_WIDGET(fc)); + nsgtk_theme_add(directory); + if (filename != NULL) + g_free(filename); + } +} + +/* Tabs */ + +/* always show tab bar */ +G_MODULE_EXPORT void +nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, + struct ppref *priv) +{ + nsoption_set_bool(show_single_tab, + gtk_toggle_button_get_active(togglebutton)); + nsgtk_reflow_all_windows(); +} + +G_MODULE_EXPORT void +nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), + nsoption_bool(show_single_tab)); +} + +/* switch to newly opened tabs immediately */ +TOGGLEBUTTON_SIGNALS(checkFocusNew, focus_new) + +/* newly opened tabs are blank */ +TOGGLEBUTTON_SIGNALS(checkNewBlank, new_blank) + +/* tab position */ +G_MODULE_EXPORT void +nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, + struct ppref *priv) +{ + nsgtk_scaffolding *current = scaf_list; + + /* set the option */ + nsoption_set_int(position_tab, gtk_combo_box_get_active(widget)); + + /* update all notebooks in all scaffolds */ + while (current) { + nsgtk_scaffolding_reset_offset(current); + + nsgtk_reflow_all_windows(); + + current = nsgtk_scaffolding_iterate(current); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(position_tab)); +} + +/* Source */ + +/* source view opening */ +TOGGLEBUTTON_SIGNALS(sourceButtonTab, source_tab) + +G_MODULE_EXPORT void +nsgtk_preferences_sourceButtonWindow_toggled(GtkToggleButton *togglebutton, + struct ppref *priv) +{ + nsoption_set_bool(source_tab, + !gtk_toggle_button_get_active(togglebutton)); +} + +G_MODULE_EXPORT void +nsgtk_preferences_sourceButtonWindow_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), + !nsoption_bool(source_tab)); +} + + +/* URLbar */ + +/* show recently visited urls as you type */ +TOGGLEBUTTON_SIGNALS(checkDisplayRecentURLs, url_suggestion) + +/* Toolbar */ + +/* button position */ +G_MODULE_EXPORT void +nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, + struct ppref *priv) +{ + nsgtk_scaffolding *current = scaf_list; + nsoption_set_int(button_type, gtk_combo_box_get_active(widget) + 1); + + /* value of 0 is reserved for 'unset' */ + while (current) { + nsgtk_scaffolding_reset_offset(current); + switch(nsoption_int(button_type)) { + case 1: + gtk_toolbar_set_style( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_ICON_SIZE_SMALL_TOOLBAR); + break; + case 2: + gtk_toolbar_set_style( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_TOOLBAR_ICONS); + gtk_toolbar_set_icon_size( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + case 3: + gtk_toolbar_set_style( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_TOOLBAR_BOTH); + gtk_toolbar_set_icon_size( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_ICON_SIZE_LARGE_TOOLBAR); + break; + case 4: + gtk_toolbar_set_style( + GTK_TOOLBAR(nsgtk_scaffolding_toolbar(current)), + GTK_TOOLBAR_TEXT); + default: + break; + } + current = nsgtk_scaffolding_iterate(current); + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(button_type) - 1); +} + + + +/************ Main ************/ + +/* Startup */ + +/* entry HomePageURL widget */ +ENTRY_SIGNALS(entryHomePageURL, homepage_url) + +/* put current page into homepage url */ +G_MODULE_EXPORT void +nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv) +{ + const gchar *url; + + if (priv->bw != NULL) { + url = nsurl_access(hlcache_handle_get_url(priv->bw->current_content)); + } else { + url = "about:blank"; + } + + if (priv->entryHomePageURL != NULL) { + gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url); + nsoption_set_charp(homepage_url, strdup(url)); + } +} + +/* put default page into homepage */ +G_MODULE_EXPORT void +nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv) +{ + const gchar *url = NETSURF_HOMEPAGE; + + if (priv->entryHomePageURL != NULL) { + gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url); + nsoption_set_charp(homepage_url, strdup(url)); + } +} + +/* Search */ + +/* Url Search widget */ +TOGGLEBUTTON_SIGNALS(checkUrlSearch, search_url_bar) + +/* provider combo */ +G_MODULE_EXPORT void +nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv) +{ + nsgtk_scaffolding *current = scaf_list; + char *name; + int provider; + + provider = gtk_combo_box_get_active(widget); + + /* set the option */ + nsoption_set_int(search_provider, provider); + + /* refresh web search prefs from file */ + search_web_provider_details(provider); + + /* retrieve ico */ + search_web_retrieve_ico(false); + + /* callback may handle changing gui */ + if (search_web_ico() != NULL) { + gui_window_set_search_ico(search_web_ico()); + } + + /* set entry */ + name = search_web_provider_name(); + if (name != NULL) { + char content[strlen(name) + SLEN("Search ") + 1]; + + sprintf(content, "Search %s", name); + free(name); + while (current) { + nsgtk_scaffolding_set_websearch(current, content); + current = nsgtk_scaffolding_iterate(current); + } + } +} + +G_MODULE_EXPORT void +nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv) +{ + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + nsoption_int(search_provider)); +} + + +/* Downloads */ + +/* clear downloads */ +TOGGLEBUTTON_SIGNALS(checkClearDownloads, downloads_clear) + +/* request overwite */ +TOGGLEBUTTON_SIGNALS(checkRequestOverwrite, request_overwrite) + +/* download location + * + * note selection-changed is used instead of file-set as the returned + * filename when that signal are used is incorrect. Though this signal + * does update frequently often with the same data. + */ +G_MODULE_EXPORT void +nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, + struct ppref *priv) +{ + gchar *dir; + dir = gtk_file_chooser_get_filename(chooser); + nsoption_set_charp(downloads_directory, strdup(dir)); + g_free(dir); +} + +G_MODULE_EXPORT void +nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, + struct ppref *priv) +{ + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget), + nsoption_charp(downloads_directory)); +} + + +/************* Dialog window ***********/ + +/* dialog close and destroy events */ +G_MODULE_EXPORT void +nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid) +{ + if (resid == GTK_RESPONSE_CLOSE) { + nsoption_write(options_file_location); + gtk_widget_hide(GTK_WIDGET(dlg)); + } +} + +G_MODULE_EXPORT gboolean +nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, + struct ppref *priv) +{ + nsoption_write(options_file_location); + gtk_widget_hide(GTK_WIDGET(dlg)); + + /* delt with it by hiding window, no need to destory widget by + * default */ + return TRUE; +} + +G_MODULE_EXPORT void +nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv) +{ + nsoption_write(options_file_location); +} + + +/* exported interface documented in gtk/dialogs/preferences.h */ +GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent) +{ + GError *error = NULL; + GtkBuilder *preferences_builder; + struct ppref *priv = &ppref; + + priv->bw = bw; /* for setting "current" page */ + + /* memoised dialog creation */ + if (priv->dialog != NULL) { + gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent); + return GTK_WIDGET(priv->dialog); + } + + /* populate builder object */ + preferences_builder = gtk_builder_new(); + if (!gtk_builder_add_from_file(preferences_builder, + glade_file_location->options, + &error)) { + g_warning("Couldn't load builder file: %s", error->message); + g_error_free(error); + return NULL; + } + + + priv->dialog = gtk_builder_get_object(preferences_builder, + "dialogPreferences"); + if (priv->dialog == NULL) { + LOG(("Unable to get object for preferences dialog")); + /* release builder as were done with it */ + g_object_unref(G_OBJECT(preferences_builder)); + return NULL; + } + + /* need to explicitly obtain handles for some widgets enabling + * updates by other widget events + */ +#define GB(TYPE, NAME) GTK_##TYPE(gtk_builder_get_object(preferences_builder, #NAME)) + priv->entryHomePageURL = GB(ENTRY, entryHomePageURL); + priv->themes = GB(LIST_STORE, liststore_themes); + priv->content_language = GB(LIST_STORE, liststore_content_language); + priv->entryProxyHost = GB(ENTRY, entryProxyHost); + priv->spinProxyPort = GB(SPIN_BUTTON, spinProxyPort); + priv->entryProxyUser = GB(ENTRY, entryProxyUser); + priv->entryProxyPassword = GB(ENTRY, entryProxyPassword); +#undef GB + + /* connect all signals ready to use */ + gtk_builder_connect_signals(preferences_builder, priv); + + /* release builder as were done with it */ + g_object_unref(G_OBJECT(preferences_builder)); + + /* mark dialog as transient on parent */ + gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent); + + return GTK_WIDGET(priv->dialog); +} + +/* exported interface documented in gtk/dialogs/preferences.h */ +void nsgtk_preferences_theme_add(const char *themename) +{ + struct ppref *priv = &ppref; + GtkTreeIter iter; + + gtk_list_store_append(priv->themes, &iter); + gtk_list_store_set(priv->themes, &iter, 0, themename, -1 ); +} diff --git a/gtk/dialogs/options.h b/gtk/dialogs/preferences.h index 9f6602593..3ef33ca30 100644 --- a/gtk/dialogs/options.h +++ b/gtk/dialogs/preferences.h @@ -1,6 +1,5 @@ /* - * Copyright 2006 Rob Kendrick <rjek@rjek.com> - * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net> + * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -17,18 +16,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef NETSURF_GTK_OPTIONS_H -#define NETSURF_GTK_OPTIONS_H +#ifndef NETSURF_GTK_PREFERENCES_H +#define NETSURF_GTK_PREFERENCES_H #include <gtk/gtk.h> -extern GtkDialog *wndPreferences; +/** Initialise prefernces window + */ +GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent); -GtkDialog* nsgtk_options_init(struct browser_window *bw, GtkWindow *parent); - /** Init options and load window */ -void nsgtk_options_load(void); /** Load current options into window */ -void nsgtk_options_save(void); /** Save options from window */ -bool nsgtk_options_combo_theme_add(const char *themename); - /** add new theme name to combo */ +/** Theme added + */ +void nsgtk_preferences_theme_add(const char *themename); #endif @@ -59,7 +59,6 @@ #include "desktop/tree.h" #include "css/utils.h" #include "gtk/compat.h" -#include "gtk/dialogs/options.h" #include "gtk/completion.h" #include "gtk/cookies.h" #include "gtk/download.h" @@ -85,6 +84,7 @@ char *toolbar_indices_file_location; char *res_dir_location; char *print_options_file_location; char *languages_file_location; +char *themelist_file_location; GdkPixbuf *favicon_pixbuf; /* favicon default pixbuf */ @@ -364,6 +364,17 @@ static void gui_init(int argc, char** argv, char **respath) die("Unable to find resources.\n"); } + /* find the theme list file */ + themelist_file_location = filepath_find(respath, "themelist"); + if ((themelist_file_location != NULL) && + (strlen(themelist_file_location) < 10)) { + free(themelist_file_location); + themelist_file_location = NULL; + } + if (themelist_file_location == NULL) { + LOG(("Unable to find themelist - disabling")); + } + /* Obtain resources path location. * * Uses the directory the languages file was found in, @@ -1017,7 +1028,19 @@ uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *key) return gdk_keyval_to_unicode(key->keyval); case 'u': if (key->state & GDK_CONTROL_MASK) - return KEY_CLEAR_SELECTION; + return KEY_CUT_LINE; + return gdk_keyval_to_unicode(key->keyval); + case 'c': + if (key->state & GDK_CONTROL_MASK) + return KEY_COPY_SELECTION; + return gdk_keyval_to_unicode(key->keyval); + case 'v': + if (key->state & GDK_CONTROL_MASK) + return KEY_PASTE; + return gdk_keyval_to_unicode(key->keyval); + case 'x': + if (key->state & GDK_CONTROL_MASK) + return KEY_CUT_SELECTION; return gdk_keyval_to_unicode(key->keyval); case GDK_KEY(Escape): return KEY_ESCAPE; @@ -54,6 +54,7 @@ extern char *toolbar_indices_file_location; extern char *options_file_location; /**< location where user options are written */ extern char *res_dir_location; extern char *print_options_file_location; +extern char *themelist_file_location; extern GdkPixbuf *favicon_pixbuf; /* favicon default pixbuf */ diff --git a/gtk/res/options.gtk2.ui b/gtk/res/options.gtk2.ui index 5dd793062..85db837b8 100644 --- a/gtk/res/options.gtk2.ui +++ b/gtk/res/options.gtk2.ui @@ -9,6 +9,9 @@ <property name="destroy_with_parent">True</property> <property name="type_hint">dialog</property> <property name="has_separator">False</property> + <signal name="destroy" handler="nsgtk_preferences_dialogPreferences_destroy"/> + <signal name="response" handler="nsgtk_preferences_dialogPreferences_response"/> + <signal name="delete_event" handler="nsgtk_preferences_dialogPreferences_deleteevent"/> <child internal-child="vbox"> <object class="GtkVBox" id="vbox_dialog"> <property name="visible">True</property> @@ -59,6 +62,8 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="invisible_char">●</property> + <signal name="changed" handler="nsgtk_preferences_entryHomePageURL_changed"/> + <signal name="realize" handler="nsgtk_preferences_entryHomePageURL_realize"/> </object> <packing> <property name="pack_type">end</property> @@ -83,11 +88,13 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> + <signal name="clicked" handler="nsgtk_preferences_setCurrentPage_clicked"/> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">1</property> + <property name="pack_type">end</property> + <property name="position">2</property> </packing> </child> <child> @@ -96,17 +103,18 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> + <signal name="clicked" handler="nsgtk_preferences_setDefaultPage_clicked"/> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">2</property> + <property name="pack_type">end</property> + <property name="position">1</property> </packing> </child> </object> <packing> <property name="fill">False</property> - <property name="pack_type">end</property> <property name="position">1</property> </packing> </child> @@ -151,6 +159,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkUrlSearch_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkUrlSearch_toggled"/> </object> <packing> <property name="position">0</property> @@ -175,6 +185,8 @@ <object class="GtkComboBox" id="comboSearch"> <property name="visible">True</property> <property name="model">liststore_search_provider</property> + <signal name="changed" handler="nsgtk_preferences_comboSearch_changed"/> + <signal name="realize" handler="nsgtk_preferences_comboSearch_realize"/> <child> <object class="GtkCellRendererText" id="cellrenderertext1"/> <attributes> @@ -232,6 +244,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkClearDownloads_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkClearDownloads_toggled"/> </object> <packing> <property name="expand">False</property> @@ -245,6 +259,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkRequestOverwrite_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkRequestOverwrite_toggled"/> </object> <packing> <property name="expand">False</property> @@ -269,6 +285,8 @@ <object class="GtkFileChooserButton" id="fileChooserDownloads"> <property name="visible">True</property> <property name="action">select-folder</property> + <signal name="selection_changed" handler="nsgtk_preferences_fileChooserDownloads_selectionchanged"/> + <signal name="realize" handler="nsgtk_preferences_fileChooserDownloads_realize"/> </object> <packing> <property name="position">1</property> @@ -330,14 +348,29 @@ <property name="visible">True</property> <property name="spacing">12</property> <child> - <placeholder/> + <object class="GtkComboBox" id="comboTheme"> + <property name="visible">True</property> + <property name="model">liststore_themes</property> + <signal name="changed" handler="nsgtk_preferences_comboTheme_changed"/> + <signal name="realize" handler="nsgtk_preferences_comboTheme_realize"/> + <child> + <object class="GtkCellRendererText" id="cellrenderertext9"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> </child> <child> - <object class="GtkButton" id="buttonaddtheme"> + <object class="GtkButton" id="buttonAddTheme"> <property name="label" translatable="yes">Add Theme...</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> + <signal name="clicked" handler="nsgtk_preferences_buttonAddTheme_clicked"/> </object> <packing> <property name="expand">False</property> @@ -386,6 +419,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkShowSingleTab_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkShowSingleTab_toggled"/> </object> <packing> <property name="position">0</property> @@ -398,6 +433,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkFocusNew_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkFocusNew_toggled"/> </object> <packing> <property name="position">1</property> @@ -410,6 +447,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkNewBlank_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkNewBlank_toggled"/> </object> <packing> <property name="position">2</property> @@ -434,6 +473,8 @@ <object class="GtkComboBox" id="comboTabPosition"> <property name="visible">True</property> <property name="model">liststore_tab_position</property> + <signal name="changed" handler="nsgtk_preferences_comboTabPosition_changed"/> + <signal name="realize" handler="nsgtk_preferences_comboTabPosition_realize"/> <child> <object class="GtkCellRendererText" id="cellrenderertext2"/> <attributes> @@ -503,10 +544,11 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> - <property name="xalign">0.54000002145767212</property> <property name="active">True</property> <property name="draw_indicator">True</property> <property name="group">sourceButtonTab</property> + <signal name="realize" handler="nsgtk_preferences_sourceButtonWindow_realize"/> + <signal name="toggled" handler="nsgtk_preferences_sourceButtonWindow_toggled"/> </object> <packing> <property name="position">0</property> @@ -520,6 +562,8 @@ <property name="receives_default">False</property> <property name="active">True</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_sourceButtonTab_realize"/> + <signal name="toggled" handler="nsgtk_preferences_sourceButtonTab_toggled"/> </object> <packing> <property name="position">1</property> @@ -571,6 +615,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkDisplayRecentURLs_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkDisplayRecentURLs_toggled"/> </object> <packing> <property name="position">0</property> @@ -628,6 +674,8 @@ <object class="GtkComboBox" id="comboButtonType"> <property name="visible">True</property> <property name="model">liststore_toolbar_buttontype</property> + <signal name="changed" handler="nsgtk_preferences_comboButtonType_changed"/> + <signal name="realize" handler="nsgtk_preferences_comboButtonType_realize"/> <child> <object class="GtkCellRendererText" id="cellrenderertext3"/> <attributes> @@ -705,6 +753,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkDisablePopups_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkDisablePopups_toggled"/> </object> <packing> <property name="expand">False</property> @@ -719,6 +769,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkHideAdverts_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkHideAdverts_toggled"/> </object> <packing> <property name="expand">False</property> @@ -733,6 +785,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkEnableJavascript_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkEnableJavascript_toggled"/> </object> <packing> <property name="expand">False</property> @@ -747,6 +801,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkDisablePlugins_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkDisablePlugins_toggled"/> </object> <packing> <property name="expand">False</property> @@ -761,6 +817,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkResampleImages_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkResampleImages_toggled"/> </object> <packing> <property name="position">4</property> @@ -784,6 +842,8 @@ <object class="GtkComboBox" id="comboboxLoadImages"> <property name="visible">True</property> <property name="model">liststore_image_loading</property> + <signal name="changed" handler="nsgtk_preferences_comboboxLoadImages_changed"/> + <signal name="realize" handler="nsgtk_preferences_comboboxLoadImages_realize"/> <child> <object class="GtkCellRendererText" id="cellrenderertext4"/> <attributes> @@ -841,6 +901,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkEnableAnimations_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkEnableAnimations_toggled"/> </object> <packing> <property name="position">0</property> @@ -872,6 +934,8 @@ <property name="digits">1</property> <property name="numeric">True</property> <property name="update_policy">if-valid</property> + <signal name="value_changed" handler="nsgtk_preferences_spinAnimationSpeed_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinAnimationSpeed_realize"/> </object> <packing> <property name="expand">False</property> @@ -934,6 +998,8 @@ <object class="GtkComboBox" id="comboDefault"> <property name="visible">True</property> <property name="model">liststore_defaultfont</property> + <signal name="changed" handler="nsgtk_preferences_comboDefault_changed"/> + <signal name="realize" handler="nsgtk_preferences_comboDefault_realize"/> <child> <object class="GtkCellRendererText" id="cellrenderertext5"/> <attributes> @@ -970,12 +1036,15 @@ <property name="can_focus">True</property> <property name="has_tooltip">True</property> <property name="tooltip_text" translatable="yes">The base-line font size to use.</property> - <property name="max_length">3</property> + <property name="max_length">4</property> <property name="invisible_char">●</property> - <property name="width_chars">3</property> + <property name="width_chars">4</property> <property name="adjustment">adjustment_font_default_size</property> <property name="climb_rate">1</property> + <property name="digits">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinDefaultSize_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinDefaultSize_realize"/> </object> <packing> <property name="expand">False</property> @@ -996,6 +1065,7 @@ <property name="receives_default">True</property> <property name="image">image1</property> <property name="use_underline">True</property> + <signal name="clicked" handler="nsgtk_preferences_fontPreview_clicked"/> </object> <packing> <property name="expand">False</property> @@ -1055,6 +1125,8 @@ <property name="has_tooltip">True</property> <property name="tooltip_text" translatable="yes">set preferred language for web pages</property> <property name="model">liststore_content_language</property> + <signal name="changed" handler="nsgtk_preferences_comboboxLanguage_changed"/> + <signal name="realize" handler="nsgtk_preferences_comboboxLanguage_realize"/> <child> <object class="GtkCellRendererText" id="cellrenderertext7"> <property name="xalign">0</property> @@ -1119,6 +1191,68 @@ <property name="orientation">vertical</property> <property name="spacing">6</property> <child> + <object class="GtkFrame" id="frame_privacy_general"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkAlignment" id="alignment20"> + <property name="visible">True</property> + <property name="top_padding">6</property> + <property name="left_padding">12</property> + <property name="right_padding">12</property> + <child> + <object class="GtkVBox" id="vbox15"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="checkSendReferer"> + <property name="label" translatable="yes">Enable referral submission</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkSendReferer_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkSendReferer_toggled"/> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="checkSendDNT"> + <property name="label" translatable="yes">Enable sending "Do Not Track" request</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkSendDNT_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkSendDNT_toggled"/> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label61"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>General</b></property> + <property name="use_markup">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="padding">6</property> + <property name="position">0</property> + </packing> + </child> + <child> <object class="GtkFrame" id="frame_privacy_history"> <property name="visible">True</property> <property name="label_xalign">0</property> @@ -1141,6 +1275,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkHoverURLs_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkHoverURLs_toggled"/> </object> <packing> <property name="position">0</property> @@ -1170,6 +1306,9 @@ <property name="adjustment">adjustment_history_age</property> <property name="climb_rate">1</property> <property name="numeric">True</property> + <property name="update_policy">if-valid</property> + <signal name="value_changed" handler="nsgtk_preferences_spinHistoryAge_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinHistoryAge_realize"/> </object> <packing> <property name="expand">False</property> @@ -1206,7 +1345,7 @@ <packing> <property name="expand">False</property> <property name="padding">6</property> - <property name="position">0</property> + <property name="position">1</property> </packing> </child> <child> @@ -1275,6 +1414,8 @@ <property name="adjustment">adjustment_cache_memory_size</property> <property name="climb_rate">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinMemoryCacheSize_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinMemoryCacheSize_realize"/> </object> <packing> <property name="left_attach">1</property> @@ -1291,6 +1432,8 @@ <property name="adjustment">adjustment_cache_disc_size</property> <property name="climb_rate">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinDiscCacheSize_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinDiscCacheSize_realize"/> </object> <packing> <property name="left_attach">1</property> @@ -1309,6 +1452,8 @@ <property name="adjustment">adjustment_disc_cache_age</property> <property name="climb_rate">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinDiscCacheAge_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinDiscCacheAge_realize"/> </object> <packing> <property name="left_attach">1</property> @@ -1401,63 +1546,6 @@ <packing> <property name="expand">False</property> <property name="padding">6</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkFrame" id="frame_privacy_general"> - <property name="visible">True</property> - <property name="label_xalign">0</property> - <property name="shadow_type">none</property> - <child> - <object class="GtkAlignment" id="alignment20"> - <property name="visible">True</property> - <property name="top_padding">6</property> - <property name="left_padding">12</property> - <property name="right_padding">12</property> - <child> - <object class="GtkVBox" id="vbox15"> - <property name="visible">True</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> - <child> - <object class="GtkCheckButton" id="checkSendReferer"> - <property name="label" translatable="yes">Enable referral submission</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="checkSendDNT"> - <property name="label" translatable="yes">Enable sending "Do Not Track" request</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="position">1</property> - </packing> - </child> - </object> - </child> - </object> - </child> - <child type="label"> - <object class="GtkLabel" id="label61"> - <property name="visible">True</property> - <property name="label" translatable="yes"><b>General</b></property> - <property name="use_markup">True</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> <property name="position">2</property> </packing> </child> @@ -1551,6 +1639,8 @@ <property name="has_tooltip">True</property> <property name="tooltip_text" translatable="yes">The type of HTTP proxy server.</property> <property name="model">liststore_proxy_type</property> + <signal name="changed" handler="nsgtk_preferences_comboProxyType_changed"/> + <signal name="realize" handler="nsgtk_preferences_comboProxyType_realize"/> <child> <object class="GtkCellRendererText" id="cellrenderertext6"/> <attributes> @@ -1574,6 +1664,8 @@ <property name="has_tooltip">True</property> <property name="tooltip_text" translatable="yes">Host name of your proxy server.</property> <property name="invisible_char">●</property> + <signal name="changed" handler="nsgtk_preferences_entryProxyHost_changed"/> + <signal name="realize" handler="nsgtk_preferences_entryProxyHost_realize"/> </object> <packing> <property name="position">0</property> @@ -1602,6 +1694,8 @@ <property name="climb_rate">1</property> <property name="numeric">True</property> <property name="update_policy">if-valid</property> + <signal name="value_changed" handler="nsgtk_preferences_spinProxyPort_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinProxyPort_realize"/> </object> <packing> <property name="expand">False</property> @@ -1623,6 +1717,8 @@ <property name="has_tooltip">True</property> <property name="tooltip_text" translatable="yes">Username to access the proxy</property> <property name="invisible_char">●</property> + <signal name="changed" handler="nsgtk_preferences_entryProxyUser_changed"/> + <signal name="realize" handler="nsgtk_preferences_entryProxyUser_realize"/> </object> <packing> <property name="left_attach">1</property> @@ -1637,7 +1733,10 @@ <property name="can_focus">True</property> <property name="has_tooltip">True</property> <property name="tooltip_text" translatable="yes">Password to access the proxy</property> + <property name="visibility">False</property> <property name="invisible_char">●</property> + <signal name="changed" handler="nsgtk_preferences_entryProxyPassword_changed"/> + <signal name="realize" handler="nsgtk_preferences_entryProxyPassword_realize"/> </object> <packing> <property name="left_attach">1</property> @@ -1727,6 +1826,8 @@ <property name="adjustment">adjustment_fetching_max</property> <property name="climb_rate">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinMaxFetchers_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinMaxFetchers_realize"/> </object> <packing> <property name="left_attach">1</property> @@ -1745,6 +1846,8 @@ <property name="adjustment">adjustment_fetching_perhost</property> <property name="climb_rate">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinFetchesPerHost_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinFetchesPerHost_realize"/> </object> <packing> <property name="left_attach">1</property> @@ -1765,6 +1868,8 @@ <property name="adjustment">adjustment_fetching_cached</property> <property name="climb_rate">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinCachedConnections_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinCachedConnections_realize"/> </object> <packing> <property name="left_attach">1</property> @@ -1835,6 +1940,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkSuppressImages_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkSuppressImages_toggled"/> </object> <packing> <property name="position">0</property> @@ -1847,6 +1954,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkRemoveBackgrounds_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkRemoveBackgrounds_toggled"/> </object> <packing> <property name="position">1</property> @@ -1859,6 +1968,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkFitPage_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkFitPage_toggled"/> </object> <packing> <property name="position">2</property> @@ -1889,6 +2000,8 @@ <property name="adjustment">adjustment_pdf_scale</property> <property name="climb_rate">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinExportScale_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinExportScale_realize"/> </object> <packing> <property name="expand">False</property> @@ -1995,6 +2108,8 @@ <property name="climb_rate">1</property> <property name="digits">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinMarginTop_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinMarginTop_realize"/> </object> <packing> <property name="expand">False</property> @@ -2034,6 +2149,8 @@ <property name="climb_rate">1</property> <property name="digits">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinMarginLeft_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinMarginLeft_realize"/> </object> <packing> <property name="expand">False</property> @@ -2072,6 +2189,8 @@ <property name="climb_rate">1</property> <property name="digits">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinMarginBottom_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinMarginBottom_realize"/> </object> <packing> <property name="expand">False</property> @@ -2112,6 +2231,8 @@ <property name="climb_rate">1</property> <property name="digits">1</property> <property name="numeric">True</property> + <signal name="value_changed" handler="nsgtk_preferences_spinMarginRight_valuechanged"/> + <signal name="realize" handler="nsgtk_preferences_spinMarginRight_realize"/> </object> <packing> <property name="expand">False</property> @@ -2188,6 +2309,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkCompressPDF_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkCompressPDF_toggled"/> </object> <packing> <property name="position">0</property> @@ -2200,6 +2323,8 @@ <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="draw_indicator">True</property> + <signal name="realize" handler="nsgtk_preferences_checkPasswordPDF_realize"/> + <signal name="toggled" handler="nsgtk_preferences_checkPasswordPDF_toggled"/> </object> <packing> <property name="position">1</property> @@ -2481,9 +2606,9 @@ <object class="GtkAdjustment" id="adjustment_font_default_size"> <property name="value">16</property> <property name="lower">1</property> - <property name="upper">100</property> - <property name="step_increment">1</property> - <property name="page_increment">10</property> + <property name="upper">99.900000000000006</property> + <property name="step_increment">0.10000000000000001</property> + <property name="page_increment">2</property> </object> <object class="GtkAdjustment" id="adjustment_history_age"> <property name="value">28</property> @@ -2562,4 +2687,15 @@ </row> </data> </object> + <object class="GtkListStore" id="liststore_themes"> + <columns> + <!-- column-name Theme --> + <column type="gchararray"/> + </columns> + <data> + <row> + <col id="0" translatable="yes">Default</col> + </row> + </data> + </object> </interface> diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c index 91288a22e..287fed12b 100644 --- a/gtk/scaffolding.c +++ b/gtk/scaffolding.c @@ -53,7 +53,7 @@ #include "desktop/tree.h" #include "gtk/cookies.h" #include "gtk/completion.h" -#include "gtk/dialogs/options.h" +#include "gtk/dialogs/preferences.h" #include "gtk/dialogs/about.h" #include "gtk/dialogs/source.h" #include "gtk/bitmap.h" @@ -138,7 +138,6 @@ struct gtk_scaffolding { GtkBuilder *xml; struct gtk_history_window *history_window; - GtkDialog *preferences_dialog; int throb_frame; struct gui_window *top_level; @@ -972,21 +971,21 @@ MULTIHANDLER(copy) if (GTK_IS_EDITABLE (focused)) gtk_editable_copy_clipboard(GTK_EDITABLE(g->url_bar)); else - gui_copy_to_clipboard(browser_window_get_selection(bw)); + browser_window_key_press(bw, KEY_COPY_SELECTION); return TRUE; } MULTIHANDLER(paste) { - struct gui_window *gui = g->top_level; + struct browser_window *bw = nsgtk_get_browser_window(g->top_level); GtkWidget *focused = gtk_window_get_focus(g->window); /* If the url bar has focus, let gtk handle it */ if (GTK_IS_EDITABLE (focused)) gtk_editable_paste_clipboard (GTK_EDITABLE (focused)); else - gui_paste_from_clipboard(gui, 0, 0); + browser_window_key_press(bw, KEY_PASTE); return TRUE; } @@ -1027,10 +1026,12 @@ MULTIHANDLER(find) MULTIHANDLER(preferences) { struct browser_window *bw = nsgtk_get_browser_window(g->top_level); - if (g->preferences_dialog == NULL) - g->preferences_dialog = nsgtk_options_init(bw, g->window); - else - gtk_widget_show(GTK_WIDGET(g->preferences_dialog)); + GtkWidget* wndpreferences; + + wndpreferences = nsgtk_preferences(bw, g->window); + if (wndpreferences != NULL) { + gtk_widget_show(GTK_WIDGET(wndpreferences)); + } return TRUE; } @@ -1769,8 +1770,6 @@ nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel) g->menu_bar = nsgtk_menu_bar_create(GTK_MENU_SHELL(gtk_builder_get_object(g->xml, "menubar")), group); - g->preferences_dialog = NULL; - /* set this window's size and position to what's in the options, or * or some sensible default if they're not set yet. */ diff --git a/gtk/selection.c b/gtk/selection.c index fd0b7c84b..7d516e944 100644 --- a/gtk/selection.c +++ b/gtk/selection.c @@ -31,31 +31,10 @@ static GString *current_selection = NULL; static GtkClipboard *clipboard; -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) -{ - /* add the text from this box */ - current_selection = g_string_append_len(current_selection, - text, length); - if (space) g_string_append(current_selection, " "); - return true; -} -bool gui_copy_to_clipboard(struct selection *s) -{ - clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); - if (s->defined && selection_copy_to_clipboard(s)) - gui_commit_clipboard(); - return true; -} void gui_start_selection(struct gui_window *g) { - if (current_selection == NULL) - current_selection = g_string_new(NULL); - else - g_string_set_size(current_selection, 0); - gtk_widget_grab_focus(GTK_WIDGET(nsgtk_window_get_layout(g))); } @@ -63,34 +42,61 @@ void gui_clear_selection(struct gui_window *g) { } -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length) { - gchar *text; - clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); - text = gtk_clipboard_wait_for_text (clipboard); - /* clipboard_wait... converts the string to utf8 for us */ - if (text != NULL) - browser_window_paste_text(nsgtk_get_browser_window(g), - text, strlen(text), true); - g_free(text); + gchar *gtext; + + *buffer = NULL; + *length = 0; + + /* get clipboard contents from gtk */ + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gtext = gtk_clipboard_wait_for_text(clipboard); /* conv to utf-8 */ + + if (gtext == NULL) + return; + + *length = strlen(gtext); + *buffer = malloc(*length); + if (*buffer == NULL) { + *length = 0; + g_free(gtext); + return; + } + + memcpy(*buffer, gtext, *length); + + g_free(gtext); } -bool gui_empty_clipboard(void) + +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { + clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + if (!current_selection) current_selection = g_string_new(NULL); else g_string_set_size(current_selection, 0); - return true; -} + current_selection = g_string_append_len(current_selection, + buffer, length); -bool gui_commit_clipboard(void) -{ - clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text(clipboard, current_selection->str, -1); - gui_empty_clipboard(); - - return true; } diff --git a/gtk/theme.c b/gtk/theme.c index f16bd2d11..402433c32 100644 --- a/gtk/theme.c +++ b/gtk/theme.c @@ -32,7 +32,7 @@ #include "gtk/theme.h" #include "gtk/window.h" #include "desktop/options.h" -#include "gtk/dialogs/options.h" +#include "gtk/dialogs/preferences.h" #include "utils/container.h" #include "utils/log.h" #include "utils/messages.h" @@ -157,28 +157,32 @@ static bool nsgtk_theme_verify(const char *themename) void nsgtk_theme_init(void) { - size_t len; - if (nsoption_int(current_theme) == 0) { + int theme; + nsgtk_scaffolding *list = scaf_list; + FILE *fp; + char buf[50]; + int row_count = 0; + + theme = nsoption_int(current_theme); + + /* check if default theme is selected */ + if (theme == 0) { return; } - len = SLEN("themelist") + strlen(res_dir_location) + 1; - char themefile[len]; - snprintf(themefile, len, "%s%s", res_dir_location, "themelist"); - nsgtk_scaffolding *list = scaf_list; nsgtk_theme_verify(NULL); - FILE *fp = fopen(themefile, "r"); + fp = fopen(themelist_file_location, "r"); if (fp == NULL) return; - char buf[50]; - int row_count = 0; + while (fgets(buf, sizeof(buf), fp) != NULL) { if (buf[0] == '\0') continue; - if (row_count++ == nsoption_int(current_theme)) { - if (current_theme_name != NULL) + if (row_count++ == theme) { + if (current_theme_name != NULL) { free(current_theme_name); + } /* clear the '\n' ["\n\0"->"\0\0"] */ buf[strlen(buf) - 1] = '\0'; current_theme_name = strdup(buf); @@ -204,13 +208,25 @@ char *nsgtk_theme_name(void) } /** - * set static global current_theme_name from param; caller is responsible - * for the integrity of the global reference + * set static global current_theme_name from param */ -void nsgtk_theme_set_name(char *name) +void nsgtk_theme_set_name(const char *name) { - current_theme_name = name; + if ((name == NULL) && (current_theme_name == NULL)) { + return; /* setting it to the same thing */ + } else if ((name == NULL) && (current_theme_name != NULL)) { + free(current_theme_name); + current_theme_name = NULL; + } else if ((name != NULL) && (current_theme_name == NULL)) { + current_theme_name = strdup(name); + nsgtk_theme_prepare(); + } else if (strcmp(name, current_theme_name) != 0) { + /* disimilar new name */ + free(current_theme_name); + current_theme_name = strdup(name); + nsgtk_theme_prepare(); + } } /** @@ -260,9 +276,7 @@ void nsgtk_theme_add(const char *themename) gtk_widget_show_all(notification); /* update combo */ - if (wndPreferences != NULL) { - nsgtk_options_combo_theme_add(themename); - } + nsgtk_preferences_theme_add(themename); } diff --git a/gtk/theme.h b/gtk/theme.h index 117207037..4aedad23f 100644 --- a/gtk/theme.h +++ b/gtk/theme.h @@ -41,6 +41,6 @@ void nsgtk_theme_init(void); void nsgtk_theme_prepare(void); void nsgtk_theme_implement(struct gtk_scaffolding *g); char *nsgtk_theme_name(void); -void nsgtk_theme_set_name(char *name); +void nsgtk_theme_set_name(const char *name); #endif diff --git a/javascript/jsapi/comment.bnd b/javascript/jsapi/comment.bnd new file mode 100644 index 000000000..580f5cbed --- /dev/null +++ b/javascript/jsapi/comment.bnd @@ -0,0 +1,47 @@ +/* Binding to generate Comment interface + * + * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * Released under the terms of the MIT License, + * http://www.opensource.org/licenses/mit-license + */ + + +webidlfile "html.idl"; + +hdrcomment "Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>"; +hdrcomment "This file is part of NetSurf, http://www.netsurf-browser.org/"; +hdrcomment "Released under the terms of the MIT License,"; +hdrcomment " http://www.opensource.org/licenses/mit-license"; + +preamble %{ + +#include <dom/dom.h> + +#include "utils/config.h" +#include "utils/log.h" +#include "render/html_internal.h" +#include "javascript/jsapi.h" + +#include "comment.h" + +%} + +#include "dom.bnd" + +binding comment { + type js_libdom; /* the binding type */ + + interface Comment; /* Web IDL interface to generate */ + + private "dom_comment *" node; + private "struct html_content *" htmlc; +} + +api finalise %{ + if (private != NULL) { + dom_node_unref(private->node); + } +%} diff --git a/javascript/jsapi/dom.bnd b/javascript/jsapi/dom.bnd index e781b330c..3fc7f9ed1 100644 --- a/javascript/jsapi/dom.bnd +++ b/javascript/jsapi/dom.bnd @@ -10,6 +10,57 @@ webidlfile "dom.idl"; +preamble %{ +#include "comment.h" +#include "text.h" +#include "htmlelement.h" +%} + + +prologue %{ +/* CAUTION this expects all javascript Node objects private pointers + * to have private->node in the same place. + */ +static struct dom_node *jsnode_to_domnode(JSContext *cx, JSObject *jsnode) +{ + struct jsclass_private *jsnode_private; + + if (jsnode == NULL) { + return NULL; + } + + /* element */ + jsnode_private = JS_GetInstancePrivate(cx, + jsnode, + &JSClass_HTMLElement, + NULL); + if (jsnode_private != NULL) { + return (struct dom_node *)jsnode_private->node; + } + + /* text */ + jsnode_private = JS_GetInstancePrivate(cx, + jsnode, + &JSClass_Text, + NULL); + if (jsnode_private != NULL) { + return (struct dom_node *)jsnode_private->node; + } + + /* comment */ + jsnode_private = JS_GetInstancePrivate(cx, + jsnode, + &JSClass_Comment, + NULL); + if (jsnode_private != NULL) { + return (struct dom_node *)jsnode_private->node; + } + + return NULL; +} + +%} + /* interface Node members */ getter nodeType %{ @@ -74,34 +125,26 @@ getter textContent %{ } %} - +/* interface Node { Node appendChild(Node node); } */ operation appendChild %{ + struct dom_node *domnode; /* dom node from js input node */ struct dom_node *result = NULL; dom_exception exc; - - struct jsclass_private *node_private; dom_node_type node_type; - JSLOG("appending %p", node); - - /* CAUTION this expects all Node objects private pointers to - * have private->node in the same place - */ - /* text */ - node_private = JS_GetInstancePrivate(cx, node, &JSClass_Text, NULL); - if (node_private == NULL) { - /* element */ - node_private = JS_GetInstancePrivate(cx, node, &JSClass_HTMLElement, NULL); - } - - if (node_private == NULL) { - /* type error? */ + domnode = jsnode_to_domnode(cx, node); + if (domnode == NULL) { + /* should cause Error: NOT_FOUND_ERR: DOM Exception 8 */ + JSLOG("Error: NOT_FOUND_ERR: DOM Exception 8"); return JS_FALSE; } + JSLOG("appending js node %p (dom %p)", node, domnode); + /* append the found element */ - exc = dom_node_append_child(private->node, node_private->node, &result); + exc = dom_node_append_child(private->node, domnode, &result); if (exc != DOM_NO_ERR) { + JSLOG("Error: DOM Exception (libdom append child)"); return JS_FALSE; } diff --git a/javascript/jsapi/htmldocument.bnd b/javascript/jsapi/htmldocument.bnd index ddf408a9f..8d5c69eb5 100644 --- a/javascript/jsapi/htmldocument.bnd +++ b/javascript/jsapi/htmldocument.bnd @@ -8,8 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" - webidlfile "html.idl"; hdrcomment "Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>"; @@ -20,7 +18,7 @@ hdrcomment " http://www.opensource.org/licenses/mit-license"; preamble %{ #include <dom/dom.h> - + #include "utils/config.h" #include "utils/log.h" #include "utils/corestrings.h" @@ -38,6 +36,8 @@ preamble %{ %} +#include "dom.bnd" + binding document { type js_libdom; /* the binding type */ @@ -47,10 +47,10 @@ binding document { * context structure. */ private "dom_document *" node; - private "struct html_content *" htmlc; + private "struct html_content *" htmlc; /** location instantiated on first use */ - property unshared location; + property unshared location; /* events through a single interface */ property unshared type EventHandler; @@ -70,9 +70,9 @@ getter location %{ /* already created - return it */ return JS_TRUE; } - jsret = jsapi_new_Location(cx, - NULL, - NULL, + jsret = jsapi_new_Location(cx, + NULL, + NULL, llcache_handle_get_url(private->htmlc->base.llcache), private->htmlc); %} @@ -110,7 +110,7 @@ getter documentElement %{ /* document (html) element */ exc = dom_document_get_document_element(private->node, (void *)&element); - if (exc != DOM_NO_ERR) { + if (exc != DOM_NO_ERR) { return JS_FALSE; } @@ -122,11 +122,11 @@ getter documentElement %{ getter head %{ dom_node *element; dom_node *head; - dom_exception exc; + dom_exception exc; /* document (html) element */ exc = dom_document_get_document_element(private->node, &element); - if (exc != DOM_NO_ERR) { + if (exc != DOM_NO_ERR) { return JS_FALSE; } @@ -142,13 +142,13 @@ getter head %{ getter body %{ dom_node *element; dom_node *body; - dom_exception exc; + dom_exception exc; JSLOG("Getting your body"); /* document (html) element */ exc = dom_document_get_document_element(private->node, &element); - if (exc != DOM_NO_ERR) { + if (exc != DOM_NO_ERR) { return JS_FALSE; } @@ -167,58 +167,58 @@ getter body %{ operation getElementById %{ dom_string *elementId_dom; dom_element *element; - dom_exception exc; + dom_exception exc; exc = dom_string_create((unsigned char*)elementId, elementId_len, &elementId_dom); - if (exc != DOM_NO_ERR) { - return JS_FALSE; - } + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } exc = dom_document_get_element_by_id(private->node, elementId_dom, &element); - dom_string_unref(elementId_dom); - if (exc != DOM_NO_ERR) { - return JS_FALSE; - } - - if (element != NULL) { - jsret = jsapi_new_HTMLElement(cx, NULL, NULL, element, private->htmlc); - } + dom_string_unref(elementId_dom); + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + if (element != NULL) { + jsret = jsapi_new_HTMLElement(cx, NULL, NULL, element, private->htmlc); + } %} -/* +/* * * Dom 4 says this should return a htmlcollection, libdom currently - * returns DOM 3 spec of a nodelist + * returns DOM 3 spec of a nodelist */ operation getElementsByTagName %{ dom_string *localName_dom; - /* dom_html_collection *collection;*/ - dom_nodelist *nodelist; - dom_exception exc; + /* dom_html_collection *collection;*/ + dom_nodelist *nodelist; + dom_exception exc; exc = dom_string_create((uint8_t *)localName, localName_len, &localName_dom); - if (exc != DOM_NO_ERR) { - return JS_FALSE; - } - - exc = dom_document_get_elements_by_tag_name(private->node, localName_dom, /*&collection*/&nodelist); - dom_string_unref(localName_dom); - if (exc != DOM_NO_ERR) { - return JS_FALSE; - } - - if (/*collection*/nodelist != NULL) { - /*jsret = jsapi_new_HTMLCollection(cx, - NULL, - NULL, - collection, - private->htmlc);*/ - jsret = jsapi_new_NodeList(cx, - NULL, - NULL, - nodelist, - private->htmlc); - } + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + exc = dom_document_get_elements_by_tag_name(private->node, localName_dom, /*&collection*/&nodelist); + dom_string_unref(localName_dom); + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + if (/*collection*/nodelist != NULL) { + /*jsret = jsapi_new_HTMLCollection(cx, + NULL, + NULL, + collection, + private->htmlc);*/ + jsret = jsapi_new_NodeList(cx, + NULL, + NULL, + nodelist, + private->htmlc); + } %} @@ -228,10 +228,10 @@ operation write %{ } %} -/* in dom Document */ +/* interface Document (dom) { Text createTextNode(DOMString data); } */ operation createTextNode %{ dom_string *data_dom; - dom_exception exc; + dom_exception exc; dom_text *text; if (data != NULL) { @@ -255,10 +255,43 @@ operation createTextNode %{ %} +/* interface Document (dom) { Comment createComment(DOMString data); } */ +operation createComment %{ + dom_string *data_dom; + dom_exception exc; + dom_comment *comment; + + if (data != NULL) { + + JSLOG("Creating string \"%s\"", data); + exc = dom_string_create((unsigned char*)data, + data_len, + &data_dom); + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + JSLOG("Creating comment object for dom string \"%s\"", + dom_string_data(data_dom)); + exc = dom_document_create_comment(private->node, + data_dom, + &comment); + dom_string_unref(data_dom); + if (exc != DOM_NO_ERR) { + return JS_FALSE; + } + + jsret = jsapi_new_Comment(cx, NULL, NULL, comment, private->htmlc); + } + + JSLOG("returning jsobject %p", jsret); + +%} + /* in dom Document */ operation createElement %{ dom_string *localName_dom; - dom_exception exc; + dom_exception exc; dom_element *element; if (localName != NULL) { diff --git a/javascript/jsapi/htmlelement.bnd b/javascript/jsapi/htmlelement.bnd index 48ebbdb64..5e22f7e7d 100644 --- a/javascript/jsapi/htmlelement.bnd +++ b/javascript/jsapi/htmlelement.bnd @@ -8,8 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" - webidlfile "html.idl"; hdrcomment "Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>"; @@ -34,6 +32,8 @@ preamble %{ %} +#include "dom.bnd" + binding htmlelement { type js_libdom; /* the binding type */ diff --git a/javascript/jsapi/navigator.bnd b/javascript/jsapi/navigator.bnd index d040edec2..2fb0c2d0a 100644 --- a/javascript/jsapi/navigator.bnd +++ b/javascript/jsapi/navigator.bnd @@ -8,7 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" webidlfile "html.idl"; diff --git a/javascript/jsapi/text.bnd b/javascript/jsapi/text.bnd index 6b4352116..eb17a943e 100644 --- a/javascript/jsapi/text.bnd +++ b/javascript/jsapi/text.bnd @@ -8,7 +8,6 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" webidlfile "html.idl"; @@ -27,10 +26,11 @@ preamble %{ #include "javascript/jsapi.h" #include "text.h" -#include "htmlelement.h" %} +#include "dom.bnd" + binding text { type js_libdom; /* the binding type */ diff --git a/javascript/jsapi/window.bnd b/javascript/jsapi/window.bnd index 288b5b3d8..bba1eb7db 100644 --- a/javascript/jsapi/window.bnd +++ b/javascript/jsapi/window.bnd @@ -8,9 +8,9 @@ * http://www.opensource.org/licenses/mit-license */ -#include "dom.bnd" webidlfile "html.idl"; +webidlfile "dom.idl"; hdrcomment "Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>"; hdrcomment "This file is part of NetSurf, http://www.netsurf-browser.org/"; @@ -20,7 +20,7 @@ hdrcomment " http://www.opensource.org/licenses/mit-license"; preamble %{ #include <dom/dom.h> - + #include "utils/config.h" #include "utils/log.h" #include "utils/corestrings.h" @@ -35,6 +35,7 @@ preamble %{ #include "nodelist.h" #include "htmldocument.h" #include "text.h" +#include "comment.h" #include "htmlelement.h" #include "window.h" #include "location.h" @@ -107,7 +108,7 @@ api init %{ return NULL; /* Initialises all the user javascript classes to make their - * prototypes available. + * prototypes available. */ /** @todo should we be managing these prototype objects ourselves */ user_proto = jsapi_InitClass_Document(cx, prototype); @@ -150,6 +151,11 @@ api init %{ return NULL; } + user_proto = jsapi_InitClass_Comment(cx, prototype); + if (user_proto == NULL) { + return NULL; + } + user_proto = jsapi_InitClass_Node(cx, prototype); if (user_proto == NULL) { return NULL; @@ -166,16 +172,16 @@ api new %{ /* @todo sort out windows that are not globals */ assert(parent == NULL); - /* the window object is the global so its prototype *is* the instance */ - newobject = prototype; + /* the window object is the global so its prototype *is* the instance */ + newobject = prototype; /* instantiate the subclasses off the window global */ private->document = jsapi_new_Document(cx, - NULL, - newobject, - (dom_document *)dom_node_ref(htmlc->document), - htmlc); - if (private->document == NULL) { + NULL, + newobject, + (dom_document *)dom_node_ref(htmlc->document), + htmlc); + if (private->document == NULL) { free(private); return NULL; } @@ -212,7 +218,7 @@ operation prompt %{ /* boolean dispatchEvent(Event event); */ operation dispatchEvent %{ /* this implementation is unique to the window object as it is - * not a "real" dom node. + * not a "real" dom node. */ /* caution, this must match the struct generated from event.bnd */ @@ -242,7 +248,7 @@ operation dispatchEvent %{ jsret = JS_CallFunctionValue(cx, NULL, eventval, 1, event_argv, &event_rval); } } - } + } %} getter location %{ @@ -261,18 +267,18 @@ getter self %{ getter EventHandler %{ /* this implementation is unique to the window object as it is - * not a dom node. + * not a dom node. */ - JSLOG("propname[%d]=\"%s\"", + JSLOG("propname[%d]=\"%s\"", tinyid, jsclass_properties[tinyid].name); %} setter EventHandler %{ /* this implementation is unique to the window object as it is - * not a dom node. + * not a dom node. */ - JSLOG("propname[%d]=\"%s\"", - tinyid, + JSLOG("propname[%d]=\"%s\"", + tinyid, jsclass_properties[tinyid].name); %} diff --git a/monkey/browser.c b/monkey/browser.c index ddea08675..e6a57d00b 100644 --- a/monkey/browser.c +++ b/monkey/browser.c @@ -327,34 +327,28 @@ gui_clear_selection(struct gui_window *g) { } -void -gui_paste_from_clipboard(struct gui_window *g, int x, int y) -{ -} - -bool -gui_empty_clipboard(void) -{ - return true; -} - -bool -gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length) { - return true; } -bool -gui_commit_clipboard(void) -{ - return true; -} -bool -gui_copy_to_clipboard(struct selection *s) +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { - return true; } void diff --git a/render/html.c b/render/html.c index 19ea5a9d0..3e26928fd 100644 --- a/render/html.c +++ b/render/html.c @@ -1302,7 +1302,6 @@ html_object_callback(hlcache_handle *object, case CONTENT_MSG_SAVELINK: case CONTENT_MSG_POINTER: - case CONTENT_MSG_PASTE: /* These messages are for browser window layer. * we're not interested, so pass them on. */ content_broadcast(&c->base, event->type, event->data); diff --git a/render/html_redraw.c b/render/html_redraw.c index e7474f8c4..e305b7b08 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -53,125 +53,9 @@ #include "utils/utils.h" -static bool html_redraw_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_box_children(const html_content *html, struct box *box, - int x_parent, int y_parent, const struct rect *clip, - float scale, colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_text_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx); -static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, - int p_width, int p_height, const struct rect *clip, - float scale, const struct redraw_context *ctx); -static bool html_redraw_inline_borders(struct box *box, struct rect b, - const struct rect *clip, float scale, bool first, bool last, - const struct redraw_context *ctx); -static bool html_redraw_border_plot(const int side, const int *p, colour c, - enum css_border_style_e style, int thickness, bool rectangular, - const struct rect *clip, const struct redraw_context *ctx); -static bool html_redraw_checkbox(int x, int y, int width, int height, - bool selected, const struct redraw_context *ctx); -static bool html_redraw_radio(int x, int y, int width, int height, - bool selected, const struct redraw_context *ctx); -static bool html_redraw_file(int x, int y, int width, int height, - struct box *box, float scale, colour background_colour, - const struct redraw_context *ctx); -static bool html_redraw_background(int x, int y, struct box *box, float scale, - const struct rect *clip, colour *background_colour, - struct box *background, const struct redraw_context *ctx); -static bool html_redraw_inline_background(int x, int y, struct box *box, - float scale, const struct rect *clip, struct rect b, - bool first, bool last, colour *background_colour, - const struct redraw_context *ctx); -static bool html_redraw_text_decoration(struct box *box, - int x_parent, int y_parent, float scale, - colour background_colour, const struct redraw_context *ctx); -static bool html_redraw_text_decoration_inline(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx); -static bool html_redraw_text_decoration_block(struct box *box, int x, int y, - float scale, colour colour, float ratio, - const struct redraw_context *ctx); - -bool html_redraw_debug = false; - -/** - * Draw a CONTENT_HTML using the current set of plotters (plot). - * - * \param c content of type CONTENT_HTML - * \param data redraw data for this content redraw - * \param clip current clip region - * \param ctx current redraw context - * \return true if successful, false otherwise - * - * x, y, clip_[xy][01] are in target coordinates. - */ - -bool html_redraw(struct content *c, struct content_redraw_data *data, - const struct rect *clip, const struct redraw_context *ctx) -{ - html_content *html = (html_content *) c; - struct box *box; - bool result = true; - bool select, select_only; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = data->background_colour, - }; - box = html->layout; - assert(box); - /* The select menu needs special treating because, when opened, it - * reaches beyond its layout box. - */ - select = false; - select_only = false; - if (ctx->interactive && html->visible_select_menu != NULL) { - struct form_control *control = html->visible_select_menu; - select = true; - /* check if the redraw rectangle is completely inside of the - select menu */ - select_only = form_clip_inside_select_menu(control, - data->scale, clip); - } - - if (!select_only) { - /* clear to background colour */ - result = ctx->plot->clip(clip); - - if (html->background_colour != NS_TRANSPARENT) - pstyle_fill_bg.fill_colour = html->background_colour; - - result &= ctx->plot->rectangle(clip->x0, clip->y0, - clip->x1, clip->y1, - &pstyle_fill_bg); - - result &= html_redraw_box(html, box, data->x, data->y, clip, - data->scale, pstyle_fill_bg.fill_colour, ctx); - } - - if (select) { - int menu_x, menu_y; - box = html->visible_select_menu->box; - box_coords(box, &menu_x, &menu_y); - - menu_x -= box->border[LEFT].width; - menu_y += box->height + box->border[BOTTOM].width + - box->padding[BOTTOM] + box->padding[TOP]; - result &= form_redraw_select_menu(html->visible_select_menu, - data->x + menu_x, data->y + menu_y, - data->scale, clip, ctx); - } - - return result; - -} +bool html_redraw_debug = false; /** * Determine if a box has a background that needs drawing @@ -239,678 +123,6 @@ static struct box *html_redraw_find_bg_box(struct box *box) } /** - * Recursively draw a box. - * - * \param html html content - * \param box box to draw - * \param x_parent coordinate of parent box - * \param y_parent coordinate of parent box - * \param clip clip rectangle - * \param scale scale for redraw - * \param current_background_color background colour under this box - * \param ctx current redraw context - * \return true if successful, false otherwise - * - * x, y, clip_[xy][01] are in target coordinates. - */ - -bool html_redraw_box(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - int x, y; - int width, height; - int padding_left, padding_top, padding_width, padding_height; - int border_left, border_top, border_right, border_bottom; - struct rect r; - int x_scrolled, y_scrolled; - struct box *bg_box = NULL; - bool has_x_scroll, has_y_scroll; - css_computed_clip_rect css_rect; - - if (html_redraw_printing && (box->flags & PRINTED)) - return true; - - /* avoid trivial FP maths */ - if (scale == 1.0) { - x = x_parent + box->x; - y = y_parent + box->y; - width = box->width; - height = box->height; - padding_left = box->padding[LEFT]; - padding_top = box->padding[TOP]; - padding_width = padding_left + box->width + box->padding[RIGHT]; - padding_height = padding_top + box->height + - box->padding[BOTTOM]; - border_left = box->border[LEFT].width; - border_top = box->border[TOP].width; - border_right = box->border[RIGHT].width; - border_bottom = box->border[BOTTOM].width; - } else { - x = (x_parent + box->x) * scale; - y = (y_parent + box->y) * scale; - width = box->width * scale; - height = box->height * scale; - /* left and top padding values are normally zero, - * so avoid trivial FP maths */ - padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale - : 0; - padding_top = box->padding[TOP] ? box->padding[TOP] * scale - : 0; - padding_width = (box->padding[LEFT] + box->width + - box->padding[RIGHT]) * scale; - padding_height = (box->padding[TOP] + box->height + - box->padding[BOTTOM]) * scale; - border_left = box->border[LEFT].width * scale; - border_top = box->border[TOP].width * scale; - border_right = box->border[RIGHT].width * scale; - border_bottom = box->border[BOTTOM].width * scale; - } - - /* calculate rectangle covering this box and descendants */ - if (box->style && css_computed_overflow(box->style) != - CSS_OVERFLOW_VISIBLE) { - /* box contents clipped to box size */ - r.x0 = x - border_left; - r.y0 = y - border_top; - r.x1 = x + padding_width + border_right; - r.y1 = y + padding_height + border_bottom; - } else { - /* box contents can hang out of the box; use descendant box */ - if (scale == 1.0) { - r.x0 = x + box->descendant_x0; - r.y0 = y + box->descendant_y0; - r.x1 = x + box->descendant_x1 + 1; - r.y1 = y + box->descendant_y1 + 1; - } else { - r.x0 = x + box->descendant_x0 * scale; - r.y0 = y + box->descendant_y0 * scale; - r.x1 = x + box->descendant_x1 * scale + 1; - r.y1 = y + box->descendant_y1 * scale + 1; - } - if (!box->parent) { - /* root element */ - int margin_left, margin_right; - int margin_top, margin_bottom; - if (scale == 1.0) { - margin_left = box->margin[LEFT]; - margin_top = box->margin[TOP]; - margin_right = box->margin[RIGHT]; - margin_bottom = box->margin[BOTTOM]; - } else { - margin_left = box->margin[LEFT] * scale; - margin_top = box->margin[TOP] * scale; - margin_right = box->margin[RIGHT] * scale; - margin_bottom = box->margin[BOTTOM] * scale; - } - r.x0 = x - border_left - margin_left < r.x0 ? - x - border_left - margin_left : r.x0; - r.y0 = y - border_top - margin_top < r.y0 ? - y - border_top - margin_top : r.y0; - r.x1 = x + padding_width + border_right + - margin_right > r.x1 ? - x + padding_width + border_right + - margin_right : r.x1; - r.y1 = y + padding_height + border_bottom + - margin_bottom > r.y1 ? - y + padding_height + border_bottom + - margin_bottom : r.y1; - } - } - - /* return if the rectangle is completely outside the clip rectangle */ - if (clip->y1 < r.y0 || r.y1 < clip->y0 || - clip->x1 < r.x0 || r.x1 < clip->x0) - return true; - - /*if the rectangle is under the page bottom but it can fit in a page, - don't print it now*/ - if (html_redraw_printing) { - if (r.y1 > html_redraw_printing_border) { - if (r.y1 - r.y0 <= html_redraw_printing_border && - (box->type == BOX_TEXT || - box->type == BOX_TABLE_CELL - || box->object || box->gadget)) { - /*remember the highest of all points from the - not printed elements*/ - if (r.y0 < html_redraw_printing_top_cropped) - html_redraw_printing_top_cropped = r.y0; - return true; - } - } - else box->flags |= PRINTED; /*it won't be printed anymore*/ - } - - /* if visibility is hidden render children only */ - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) { - if ((plot->group_start) && (!plot->group_start("hidden box"))) - return false; - if (!html_redraw_box_children(html, box, x_parent, y_parent, - &r, scale, current_background_color, ctx)) - return false; - return ((!plot->group_end) || (plot->group_end())); - } - - if ((plot->group_start) && (!plot->group_start("vis box"))) - return false; - - - if (box->style != NULL && - css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE && - css_computed_clip(box->style, &css_rect) == - CSS_CLIP_RECT) { - /* We have an absolutly positioned box with a clip rect */ - if (css_rect.left_auto == false) - r.x0 = x - border_left + FIXTOINT(nscss_len2px( - css_rect.left, css_rect.lunit, - box->style)); - - if (css_rect.top_auto == false) - r.y0 = y - border_top + FIXTOINT(nscss_len2px( - css_rect.top, css_rect.tunit, - box->style)); - - if (css_rect.right_auto == false) - r.x1 = x - border_left + FIXTOINT(nscss_len2px( - css_rect.right, css_rect.runit, - box->style)); - - if (css_rect.bottom_auto == false) - r.y1 = y - border_top + FIXTOINT(nscss_len2px( - css_rect.bottom, css_rect.bunit, - box->style)); - - /* find intersection of clip rectangle and box */ - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - /* no point trying to draw 0-width/height boxes */ - if (r.x0 == r.x1 || r.y0 == r.y1) - /* not an error */ - return ((!plot->group_end) || (plot->group_end())); - /* clip to it */ - if (!plot->clip(&r)) - return false; - - } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->object) { - /* find intersection of clip rectangle and box */ - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - /* no point trying to draw 0-width/height boxes */ - if (r.x0 == r.x1 || r.y0 == r.y1) - /* not an error */ - return ((!plot->group_end) || (plot->group_end())); - /* clip to it */ - if (!plot->clip(&r)) - return false; - } else { - /* clip box is fine, clip to it */ - r = *clip; - if (!plot->clip(&r)) - return false; - } - - /* background colour and image for block level content and replaced - * inlines */ - - bg_box = html_redraw_find_bg_box(box); - - /* bg_box == NULL implies that this box should not have - * its background rendered. Otherwise filter out linebreaks, - * optimize away non-differing inlines, only plot background - * for BOX_TEXT it's in an inline */ - if (bg_box && bg_box->type != BOX_BR && - bg_box->type != BOX_TEXT && - bg_box->type != BOX_INLINE_END && - (bg_box->type != BOX_INLINE || bg_box->object || - bg_box->flags & IFRAME || box->flags & REPLACE_DIM)) { - /* find intersection of clip box and border edge */ - struct rect p; - p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left; - p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top; - p.x1 = x + padding_width + border_right < r.x1 ? - x + padding_width + border_right : r.x1; - p.y1 = y + padding_height + border_bottom < r.y1 ? - y + padding_height + border_bottom : r.y1; - if (!box->parent) { - /* Root element, special case: - * background covers margins too */ - int m_left, m_top, m_right, m_bottom; - if (scale == 1.0) { - m_left = box->margin[LEFT]; - m_top = box->margin[TOP]; - m_right = box->margin[RIGHT]; - m_bottom = box->margin[BOTTOM]; - } else { - m_left = box->margin[LEFT] * scale; - m_top = box->margin[TOP] * scale; - m_right = box->margin[RIGHT] * scale; - m_bottom = box->margin[BOTTOM] * scale; - } - p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left; - p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top; - p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1; - p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1; - } - /* valid clipping rectangles only */ - if ((p.x0 < p.x1) && (p.y0 < p.y1)) { - /* plot background */ - if (!html_redraw_background(x, y, box, scale, &p, - ¤t_background_color, bg_box, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) - return false; - } - } - - /* borders for block level content and replaced inlines */ - if (box->style && box->type != BOX_TEXT && - box->type != BOX_INLINE_END && - (box->type != BOX_INLINE || box->object || - box->flags & IFRAME || box->flags & REPLACE_DIM) && - (border_top || border_right || - border_bottom || border_left)) { - if (!html_redraw_borders(box, x_parent, y_parent, - padding_width, padding_height, &r, - scale, ctx)) - return false; - } - - /* backgrounds and borders for non-replaced inlines */ - if (box->style && box->type == BOX_INLINE && box->inline_end && - (html_redraw_box_has_background(box) || - border_top || border_right || - border_bottom || border_left)) { - /* inline backgrounds and borders span other boxes and may - * wrap onto separate lines */ - struct box *ib; - struct rect b; /* border edge rectangle */ - struct rect p; /* clipped rect */ - bool first = true; - int ib_x; - int ib_y = y; - int ib_p_width; - int ib_b_left, ib_b_right; - - b.x0 = x - border_left; - b.x1 = x + padding_width + border_right; - b.y0 = y - border_top; - b.y1 = y + padding_height + border_bottom; - - p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; - p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; - p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; - p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; - for (ib = box; ib; ib = ib->next) { - /* to get extents of rectangle(s) associated with - * inline, cycle though all boxes in inline, skipping - * over floats */ - if (ib->type == BOX_FLOAT_LEFT || - ib->type == BOX_FLOAT_RIGHT) - continue; - if (scale == 1.0) { - ib_x = x_parent + ib->x; - ib_y = y_parent + ib->y; - ib_p_width = ib->padding[LEFT] + ib->width + - ib->padding[RIGHT]; - ib_b_left = ib->border[LEFT].width; - ib_b_right = ib->border[RIGHT].width; - } else { - ib_x = (x_parent + ib->x) * scale; - ib_y = (y_parent + ib->y) * scale; - ib_p_width = (ib->padding[LEFT] + ib->width + - ib->padding[RIGHT]) * scale; - ib_b_left = ib->border[LEFT].width * scale; - ib_b_right = ib->border[RIGHT].width * scale; - } - - if ((ib->flags & NEW_LINE) && ib != box) { - /* inline element has wrapped, plot background - * and borders */ - if (!html_redraw_inline_background( - x, y, box, scale, &p, b, - first, false, - ¤t_background_color, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) - return false; - if (!html_redraw_inline_borders(box, b, &r, - scale, first, false, ctx)) - return false; - /* reset coords */ - b.x0 = ib_x - ib_b_left; - b.y0 = ib_y - border_top - padding_top; - b.y1 = ib_y + padding_height - padding_top + - border_bottom; - - p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; - p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; - p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; - - first = false; - } - - /* increase width for current box */ - b.x1 = ib_x + ib_p_width + ib_b_right; - p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; - - if (ib == box->inline_end) - /* reached end of BOX_INLINE span */ - break; - } - /* plot background and borders for last rectangle of - * the inline */ - if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b, - first, true, ¤t_background_color, ctx)) - return false; - /* restore previous graphics window */ - if (!plot->clip(&r)) - return false; - if (!html_redraw_inline_borders(box, b, &r, scale, first, true, - ctx)) - return false; - - } - - /* Debug outlines */ - if (html_redraw_debug) { - int margin_left, margin_right; - int margin_top, margin_bottom; - if (scale == 1.0) { - /* avoid trivial fp maths */ - margin_left = box->margin[LEFT]; - margin_top = box->margin[TOP]; - margin_right = box->margin[RIGHT]; - margin_bottom = box->margin[BOTTOM]; - } else { - margin_left = box->margin[LEFT] * scale; - margin_top = box->margin[TOP] * scale; - margin_right = box->margin[RIGHT] * scale; - margin_bottom = box->margin[BOTTOM] * scale; - } - /* Content edge -- blue */ - if (!plot->rectangle(x + padding_left, - y + padding_top, - x + padding_left + width, - y + padding_top + height, - plot_style_content_edge)) - return false; - /* Padding edge -- red */ - if (!plot->rectangle(x, y, - x + padding_width, y + padding_height, - plot_style_padding_edge)) - return false; - /* Margin edge -- yellow */ - if (!plot->rectangle( - x - border_left - margin_left, - y - border_top - margin_top, - x + padding_width + border_right + - margin_right, - y + padding_height + border_bottom + - margin_bottom, - plot_style_margin_edge)) - return false; - } - - /* clip to the padding edge for objects, or boxes with overflow hidden - * or scroll */ - if ((box->style && css_computed_overflow(box->style) != - CSS_OVERFLOW_VISIBLE) || box->object || - box->flags & IFRAME) { - r.x0 = x; - r.y0 = y; - r.x1 = x + padding_width; - r.y1 = y + padding_height; - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - if (r.x1 <= r.x0 || r.y1 <= r.y0) - return ((!plot->group_end) || (plot->group_end())); - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->object) { - if (!plot->clip(&r)) - return false; - } - } - - /* text decoration */ - if (box->type != BOX_TEXT && box->style && - css_computed_text_decoration(box->style) != - CSS_TEXT_DECORATION_NONE) - if (!html_redraw_text_decoration(box, x_parent, y_parent, - scale, current_background_color, ctx)) - return false; - - if (box->object && width != 0 && height != 0) { - struct content_redraw_data obj_data; - - x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale; - y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale; - - obj_data.x = x_scrolled + padding_left; - obj_data.y = y_scrolled + padding_top; - obj_data.width = width; - obj_data.height = height; - obj_data.background_colour = current_background_color; - obj_data.scale = scale; - obj_data.repeat_x = false; - obj_data.repeat_y = false; - - if (content_get_type(box->object) == CONTENT_HTML) { - obj_data.x /= scale; - obj_data.y /= scale; - } - - if (!content_redraw(box->object, &obj_data, &r, ctx)) { - /* Show image fail */ - /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */ - const char *obj = "\xef\xbf\xbc"; - int obj_width; - int obj_x = x + padding_left; - if (!plot->rectangle(x + padding_left, - y + padding_top, - x + padding_left + width - 1, - y + padding_top + height - 1, - plot_style_broken_object)) - return false; - if (!nsfont.font_width(plot_fstyle_broken_object, obj, - sizeof(obj) - 1, &obj_width)) - obj_x += 1; - else - obj_x += width / 2 - obj_width / 2; - - if (!plot->text(obj_x, y + padding_top + (int) - (height * 0.75), - obj, sizeof(obj) - 1, - plot_fstyle_broken_object)) - return false; - } - - - } else if (box->iframe) { - /* Offset is passed to browser window redraw unscaled */ - browser_window_redraw(box->iframe, - (x + padding_left) / scale, - (y + padding_top) / scale, &r, ctx); - - } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) { - if (!html_redraw_checkbox(x + padding_left, y + padding_top, - width, height, box->gadget->selected, ctx)) - return false; - - } else if (box->gadget && box->gadget->type == GADGET_RADIO) { - if (!html_redraw_radio(x + padding_left, y + padding_top, - width, height, box->gadget->selected, ctx)) - return false; - - } else if (box->gadget && box->gadget->type == GADGET_FILE) { - if (!html_redraw_file(x + padding_left, y + padding_top, - width, height, box, scale, - current_background_color, ctx)) - return false; - - } else if (box->text) { - if (!html_redraw_text_box(html, box, x, y, &r, scale, - current_background_color, ctx)) - return false; - - } else { - if (!html_redraw_box_children(html, box, x_parent, y_parent, &r, - scale, current_background_color, ctx)) - return false; - } - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) - if (!plot->clip(clip)) - return false; - - /* list marker */ - if (box->list_marker) - if (!html_redraw_box(html, box->list_marker, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, ctx)) - return false; - - /* scrollbars */ - if (((box->style && box->type != BOX_BR && - box->type != BOX_TABLE && box->type != BOX_INLINE && - (css_computed_overflow(box->style) == - CSS_OVERFLOW_SCROLL || - css_computed_overflow(box->style) == - CSS_OVERFLOW_AUTO)) || (box->object && - content_get_type(box->object) == CONTENT_HTML)) && - box->parent != NULL) { - - has_x_scroll = box_hscrollbar_present(box); - has_y_scroll = box_vscrollbar_present(box); - - if (!box_handle_scrollbars((struct content *)html, - box, has_x_scroll, has_y_scroll)) - return false; - - if (box->scroll_x != NULL) - scrollbar_redraw(box->scroll_x, - x_parent + box->x, - y_parent + box->y + box->padding[TOP] + - box->height + box->padding[BOTTOM] - - SCROLLBAR_WIDTH, clip, scale, ctx); - if (box->scroll_y != NULL) - scrollbar_redraw(box->scroll_y, - x_parent + box->x + box->padding[LEFT] + - box->width + box->padding[RIGHT] - - SCROLLBAR_WIDTH, - y_parent + box->y, clip, scale, ctx); - } - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) - if (!plot->clip(clip)) - return false; - - return ((!plot->group_end) || (plot->group_end())); -} - - -/** - * Draw the various children of a box. - * - * \param html html content - * \param box box to draw children of - * \param x_parent coordinate of parent box - * \param y_parent coordinate of parent box - * \param clip clip rectangle - * \param scale scale for redraw - * \param current_background_color background colour under this box - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -bool html_redraw_box_children(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx) -{ - struct box *c; - - for (c = box->children; c; c = c->next) { - - if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) - if (!html_redraw_box(html, c, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, - ctx)) - return false; - } - for (c = box->float_children; c; c = c->next_float) - if (!html_redraw_box(html, c, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, - ctx)) - return false; - - return true; -} - - -/** - * Redraw the text content of a box, possibly partially highlighted - * because the text has been selected, or matches a search operation. - * - * \param box box with text content - * \param x x co-ord of box - * \param y y co-ord of box - * \param clip current clip rectangle - * \param scale current scale setting (1.0 = 100%) - * \param current_background_color - * \param ctx current redraw context - * \return true iff successful and redraw should proceed - */ - -bool html_redraw_text_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx) -{ - bool excluded = (box->object != NULL); - plot_font_style_t fstyle; - - font_plot_style_from_css(box->style, &fstyle); - fstyle.background = current_background_color; - - if (!text_redraw(box->text, box->length, box->byte_offset, - box->space, &fstyle, x, y, - clip, box->height, scale, excluded, - (struct content *)html, &html->sel, - html->search, ctx)) - return false; - - return true; -} - -/** * Redraw a short text string, complete with highlighting * (for selection/search) * @@ -1084,6 +296,386 @@ bool text_redraw(const char *utf8_text, size_t utf8_len, return true; } +static plot_style_t plot_style_bdr = { + .stroke_type = PLOT_OP_TYPE_DASH, +}; +static plot_style_t plot_style_fillbdr = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_dark = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_light = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_ddark = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; +static plot_style_t plot_style_fillbdr_dlight = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; + +/** + * Draw one border. + * + * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT) + * \param p array of precomputed border vertices + * \param c colour for border + * \param style border line style + * \param thickness border thickness + * \param rectangular whether border is rectangular + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_border_plot(const int side, const int *p, colour c, + enum css_border_style_e style, int thickness, bool rectangular, + const struct rect *clip, const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + int z[8]; /* Vertices of border part */ + unsigned int light = side; + plot_style_t *plot_style_bdr_in; + plot_style_t *plot_style_bdr_out; + + if (c == NS_TRANSPARENT) + return true; + + plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH; + plot_style_bdr.stroke_colour = c; + plot_style_bdr.stroke_width = thickness; + plot_style_fillbdr.fill_colour = c; + plot_style_fillbdr_dark.fill_colour = darken_colour(c); + plot_style_fillbdr_light.fill_colour = lighten_colour(c); + plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c); + plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c); + + switch (style) { + case CSS_BORDER_STYLE_DOTTED: + plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT; + /* fall through */ + case CSS_BORDER_STYLE_DASHED: + if (!plot->line((p[0] + p[2]) / 2, + (p[1] + p[3]) / 2, + (p[4] + p[6]) / 2, + (p[5] + p[7]) / 2, + &plot_style_bdr)) + return false; + break; + + case CSS_BORDER_STYLE_SOLID: + /* fall through to default */ + default: + if (rectangular || thickness == 1) { + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + &plot_style_fillbdr)) + return false; + } + } else { + if (!plot->polygon(p, 4, &plot_style_fillbdr)) + return false; + } + break; + + case CSS_BORDER_STYLE_DOUBLE: + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] * 2 + p[2]) / 3; + z[3] = (p[1] * 2 + p[3]) / 3; + z[4] = (p[6] * 2 + p[4]) / 3; + z[5] = (p[7] * 2 + p[5]) / 3; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, &plot_style_fillbdr)) + return false; + z[0] = p[2]; + z[1] = p[3]; + z[2] = (p[2] * 2 + p[0]) / 3; + z[3] = (p[3] * 2 + p[1]) / 3; + z[4] = (p[4] * 2 + p[6]) / 3; + z[5] = (p[5] * 2 + p[7]) / 3; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, &plot_style_fillbdr)) + return false; + break; + + case CSS_BORDER_STYLE_GROOVE: + light = 3 - light; + /* fall through */ + case CSS_BORDER_STYLE_RIDGE: + /* choose correct colours for each part of the border line */ + if (light <= 1) { + plot_style_bdr_in = &plot_style_fillbdr_dark; + plot_style_bdr_out = &plot_style_fillbdr_light; + } else { + plot_style_bdr_in = &plot_style_fillbdr_light; + plot_style_bdr_out = &plot_style_fillbdr_dark; + } + + /* Render border */ + if ((rectangular || thickness == 2) && thickness != 1) { + /* Border made up from two parts and can be plotted + * with rectangles */ + int x0, y0, x1, y1; + + /* First part */ + if (side == TOP || side == RIGHT) { + x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; + x1 = p[6]; y1 = p[7]; + } else { + x0 = p[6]; y0 = p[7]; + x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } + + /* Second part */ + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; + } else { + x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; + x1 = p[2]; y1 = p[3]; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } else if (thickness == 1) { + /* Border made up from one part which can be plotted + * as a rectangle */ + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } + } else { + /* Border made up from two parts and can't be plotted + * with rectangles */ + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] + p[2]) / 2; + z[3] = (p[1] + p[3]) / 2; + z[4] = (p[6] + p[4]) / 2; + z[5] = (p[7] + p[5]) / 2; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, plot_style_bdr_in)) + return false; + z[0] = p[2]; + z[1] = p[3]; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, plot_style_bdr_out)) + return false; + } + break; + + case CSS_BORDER_STYLE_INSET: + light = (light + 2) % 4; + /* fall through */ + case CSS_BORDER_STYLE_OUTSET: + /* choose correct colours for each part of the border line */ + switch (light) { + case 0: + plot_style_bdr_in = &plot_style_fillbdr_light; + plot_style_bdr_out = &plot_style_fillbdr_dlight; + break; + case 1: + plot_style_bdr_in = &plot_style_fillbdr_ddark; + plot_style_bdr_out = &plot_style_fillbdr_dark; + break; + case 2: + plot_style_bdr_in = &plot_style_fillbdr_dark; + plot_style_bdr_out = &plot_style_fillbdr_ddark; + break; + case 3: + plot_style_bdr_in = &plot_style_fillbdr_dlight; + plot_style_bdr_out = &plot_style_fillbdr_light; + break; + default: + plot_style_bdr_in = &plot_style_fillbdr; + plot_style_bdr_out = &plot_style_fillbdr; + break; + } + + /* Render border */ + if ((rectangular || thickness == 2) && thickness != 1) { + /* Border made up from two parts and can be plotted + * with rectangles */ + int x0, y0, x1, y1; + + /* First part */ + if (side == TOP || side == RIGHT) { + x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; + x1 = p[6]; y1 = p[7]; + } else { + x0 = p[6]; y0 = p[7]; + x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } + + /* Second part */ + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; + } else { + x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; + x1 = p[2]; y1 = p[3]; + } + /* find intersection of clip rectangle and border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } else if (thickness == 1) { + /* Border made up from one part which can be plotted + * as a rectangle */ + int x0, y0, x1, y1; + if (side == TOP || side == RIGHT) { + x0 = p[2]; y0 = p[3]; + x1 = p[6]; y1 = p[7]; + x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? + x1 + p[4] - p[6] : x1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_in)) + return false; + } + } else { + x0 = p[6]; y0 = p[7]; + x1 = p[2]; y1 = p[3]; + y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? + y1 + p[1] - p[3] : y1; + /* find intersection of clip rectangle and + * border */ + x0 = (clip->x0 > x0) ? clip->x0 : x0; + y0 = (clip->y0 > y0) ? clip->y0 : y0; + x1 = (clip->x1 < x1) ? clip->x1 : x1; + y1 = (clip->y1 < y1) ? clip->y1 : y1; + if ((x0 < x1) && (y0 < y1)) { + /* valid clip rectangles only */ + if (!plot->rectangle(x0, y0, x1, y1, + plot_style_bdr_out)) + return false; + } + } + } else { + /* Border made up from two parts and can't be plotted + * with rectangles */ + z[0] = p[0]; + z[1] = p[1]; + z[2] = (p[0] + p[2]) / 2; + z[3] = (p[1] + p[3]) / 2; + z[4] = (p[6] + p[4]) / 2; + z[5] = (p[7] + p[5]) / 2; + z[6] = p[6]; + z[7] = p[7]; + if (!plot->polygon(z, 4, plot_style_bdr_in)) + return false; + z[0] = p[2]; + z[1] = p[3]; + z[6] = p[4]; + z[7] = p[5]; + if (!plot->polygon(z, 4, plot_style_bdr_out)) + return false; + } + break; + } + + return true; +} + /** * Draw borders for a box. @@ -1098,7 +690,7 @@ bool text_redraw(const char *utf8_text, size_t utf8_len, * \return true if successful, false otherwise */ -bool html_redraw_borders(struct box *box, int x_parent, int y_parent, +static bool html_redraw_borders(struct box *box, int x_parent, int y_parent, int p_width, int p_height, const struct rect *clip, float scale, const struct redraw_context *ctx) { @@ -1333,7 +925,7 @@ bool html_redraw_borders(struct box *box, int x_parent, int y_parent, * \return true if successful, false otherwise */ -bool html_redraw_inline_borders(struct box *box, struct rect b, +static bool html_redraw_inline_borders(struct box *box, struct rect b, const struct rect *clip, float scale, bool first, bool last, const struct redraw_context *ctx) { @@ -1528,386 +1120,6 @@ bool html_redraw_inline_borders(struct box *box, struct rect b, return true; } -static plot_style_t plot_style_bdr = { - .stroke_type = PLOT_OP_TYPE_DASH, -}; -static plot_style_t plot_style_fillbdr = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_dark = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_light = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_ddark = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_dlight = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; - -/** - * Draw one border. - * - * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT) - * \param p array of precomputed border vertices - * \param c colour for border - * \param style border line style - * \param thickness border thickness - * \param rectangular whether border is rectangular - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -bool html_redraw_border_plot(const int side, const int *p, colour c, - enum css_border_style_e style, int thickness, bool rectangular, - const struct rect *clip, const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - int z[8]; /* Vertices of border part */ - unsigned int light = side; - plot_style_t *plot_style_bdr_in; - plot_style_t *plot_style_bdr_out; - - if (c == NS_TRANSPARENT) - return true; - - plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH; - plot_style_bdr.stroke_colour = c; - plot_style_bdr.stroke_width = thickness; - plot_style_fillbdr.fill_colour = c; - plot_style_fillbdr_dark.fill_colour = darken_colour(c); - plot_style_fillbdr_light.fill_colour = lighten_colour(c); - plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c); - plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c); - - switch (style) { - case CSS_BORDER_STYLE_DOTTED: - plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT; - /* fall through */ - case CSS_BORDER_STYLE_DASHED: - if (!plot->line((p[0] + p[2]) / 2, - (p[1] + p[3]) / 2, - (p[4] + p[6]) / 2, - (p[5] + p[7]) / 2, - &plot_style_bdr)) - return false; - break; - - case CSS_BORDER_STYLE_SOLID: - /* fall through to default */ - default: - if (rectangular || thickness == 1) { - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - &plot_style_fillbdr)) - return false; - } - } else { - if (!plot->polygon(p, 4, &plot_style_fillbdr)) - return false; - } - break; - - case CSS_BORDER_STYLE_DOUBLE: - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] * 2 + p[2]) / 3; - z[3] = (p[1] * 2 + p[3]) / 3; - z[4] = (p[6] * 2 + p[4]) / 3; - z[5] = (p[7] * 2 + p[5]) / 3; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, &plot_style_fillbdr)) - return false; - z[0] = p[2]; - z[1] = p[3]; - z[2] = (p[2] * 2 + p[0]) / 3; - z[3] = (p[3] * 2 + p[1]) / 3; - z[4] = (p[4] * 2 + p[6]) / 3; - z[5] = (p[5] * 2 + p[7]) / 3; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, &plot_style_fillbdr)) - return false; - break; - - case CSS_BORDER_STYLE_GROOVE: - light = 3 - light; - /* fall through */ - case CSS_BORDER_STYLE_RIDGE: - /* choose correct colours for each part of the border line */ - if (light <= 1) { - plot_style_bdr_in = &plot_style_fillbdr_dark; - plot_style_bdr_out = &plot_style_fillbdr_light; - } else { - plot_style_bdr_in = &plot_style_fillbdr_light; - plot_style_bdr_out = &plot_style_fillbdr_dark; - } - - /* Render border */ - if ((rectangular || thickness == 2) && thickness != 1) { - /* Border made up from two parts and can be plotted - * with rectangles */ - int x0, y0, x1, y1; - - /* First part */ - if (side == TOP || side == RIGHT) { - x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; - x1 = p[6]; y1 = p[7]; - } else { - x0 = p[6]; y0 = p[7]; - x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; - } - - /* Second part */ - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; - } else { - x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; - x1 = p[2]; y1 = p[3]; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } - } else if (thickness == 1) { - /* Border made up from one part which can be plotted - * as a rectangle */ - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; - } - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } - } - } else { - /* Border made up from two parts and can't be plotted - * with rectangles */ - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] + p[2]) / 2; - z[3] = (p[1] + p[3]) / 2; - z[4] = (p[6] + p[4]) / 2; - z[5] = (p[7] + p[5]) / 2; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, plot_style_bdr_in)) - return false; - z[0] = p[2]; - z[1] = p[3]; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, plot_style_bdr_out)) - return false; - } - break; - - case CSS_BORDER_STYLE_INSET: - light = (light + 2) % 4; - /* fall through */ - case CSS_BORDER_STYLE_OUTSET: - /* choose correct colours for each part of the border line */ - switch (light) { - case 0: - plot_style_bdr_in = &plot_style_fillbdr_light; - plot_style_bdr_out = &plot_style_fillbdr_dlight; - break; - case 1: - plot_style_bdr_in = &plot_style_fillbdr_ddark; - plot_style_bdr_out = &plot_style_fillbdr_dark; - break; - case 2: - plot_style_bdr_in = &plot_style_fillbdr_dark; - plot_style_bdr_out = &plot_style_fillbdr_ddark; - break; - case 3: - plot_style_bdr_in = &plot_style_fillbdr_dlight; - plot_style_bdr_out = &plot_style_fillbdr_light; - break; - default: - plot_style_bdr_in = &plot_style_fillbdr; - plot_style_bdr_out = &plot_style_fillbdr; - break; - } - - /* Render border */ - if ((rectangular || thickness == 2) && thickness != 1) { - /* Border made up from two parts and can be plotted - * with rectangles */ - int x0, y0, x1, y1; - - /* First part */ - if (side == TOP || side == RIGHT) { - x0 = (p[0] + p[2]) / 2; y0 = (p[1] + p[3]) / 2; - x1 = p[6]; y1 = p[7]; - } else { - x0 = p[6]; y0 = p[7]; - x1 = (p[0] + p[2]) / 2; y1 = (p[1] + p[3]) / 2; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; - } - - /* Second part */ - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = (p[6] + p[4]) / 2; y1 = (p[7] + p[5]) / 2; - } else { - x0 = (p[6] + p[4]) / 2; y0 = (p[7] + p[5]) / 2; - x1 = p[2]; y1 = p[3]; - } - /* find intersection of clip rectangle and border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } - } else if (thickness == 1) { - /* Border made up from one part which can be plotted - * as a rectangle */ - int x0, y0, x1, y1; - if (side == TOP || side == RIGHT) { - x0 = p[2]; y0 = p[3]; - x1 = p[6]; y1 = p[7]; - x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - x1 + p[4] - p[6] : x1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_in)) - return false; - } - } else { - x0 = p[6]; y0 = p[7]; - x1 = p[2]; y1 = p[3]; - y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - y1 + p[1] - p[3] : y1; - /* find intersection of clip rectangle and - * border */ - x0 = (clip->x0 > x0) ? clip->x0 : x0; - y0 = (clip->y0 > y0) ? clip->y0 : y0; - x1 = (clip->x1 < x1) ? clip->x1 : x1; - y1 = (clip->y1 < y1) ? clip->y1 : y1; - if ((x0 < x1) && (y0 < y1)) { - /* valid clip rectangles only */ - if (!plot->rectangle(x0, y0, x1, y1, - plot_style_bdr_out)) - return false; - } - } - } else { - /* Border made up from two parts and can't be plotted - * with rectangles */ - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] + p[2]) / 2; - z[3] = (p[1] + p[3]) / 2; - z[4] = (p[6] + p[4]) / 2; - z[5] = (p[7] + p[5]) / 2; - z[6] = p[6]; - z[7] = p[7]; - if (!plot->polygon(z, 4, plot_style_bdr_in)) - return false; - z[0] = p[2]; - z[1] = p[3]; - z[6] = p[4]; - z[7] = p[5]; - if (!plot->polygon(z, 4, plot_style_bdr_out)) - return false; - } - break; - } - - return true; -} - /** * Plot a checkbox. @@ -1921,8 +1133,8 @@ bool html_redraw_border_plot(const int side, const int *p, colour c, * \return true if successful, false otherwise */ -bool html_redraw_checkbox(int x, int y, int width, int height, bool selected, - const struct redraw_context *ctx) +static bool html_redraw_checkbox(int x, int y, int width, int height, + bool selected, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; double z = width * 0.15; @@ -1977,8 +1189,8 @@ bool html_redraw_checkbox(int x, int y, int width, int height, bool selected, * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_radio(int x, int y, int width, int height, bool selected, - const struct redraw_context *ctx) +static bool html_redraw_radio(int x, int y, int width, int height, + bool selected, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; @@ -2034,7 +1246,7 @@ bool html_redraw_radio(int x, int y, int width, int height, bool selected, * \return true if successful, false otherwise */ -bool html_redraw_file(int x, int y, int width, int height, +static bool html_redraw_file(int x, int y, int width, int height, struct box *box, float scale, colour background_colour, const struct redraw_context *ctx) { @@ -2082,7 +1294,7 @@ bool html_redraw_file(int x, int y, int width, int height, * to ::box, using the background information contained within ::background. */ -bool html_redraw_background(int x, int y, struct box *box, float scale, +static bool html_redraw_background(int x, int y, struct box *box, float scale, const struct rect *clip, colour *background_colour, struct box *background, const struct redraw_context *ctx) { @@ -2302,9 +1514,10 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, * \return true if successful, false otherwise */ -bool html_redraw_inline_background(int x, int y, struct box *box, float scale, - const struct rect *clip, struct rect b, bool first, bool last, - colour *background_colour, const struct redraw_context *ctx) +static bool html_redraw_inline_background(int x, int y, struct box *box, + float scale, const struct rect *clip, struct rect b, + bool first, bool last, colour *background_colour, + const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; struct rect r = *clip; @@ -2433,6 +1646,91 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, /** + * Plot text decoration for an inline box. + * + * \param box box to plot decorations for, of type BOX_INLINE + * \param x x coordinate of parent of box + * \param y y coordinate of parent of box + * \param scale scale for redraw + * \param colour colour for decorations + * \param ratio position of line as a ratio of line height + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_text_decoration_inline(struct box *box, int x, int y, + float scale, colour colour, float ratio, + const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + struct box *c; + plot_style_t plot_style_box = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_colour = colour, + }; + + for (c = box->next; + c && c != box->inline_end; + c = c->next) { + if (c->type != BOX_TEXT) + continue; + if (!plot->line((x + c->x) * scale, + (y + c->y + c->height * ratio) * scale, + (x + c->x + c->width) * scale, + (y + c->y + c->height * ratio) * scale, + &plot_style_box)) + return false; + } + return true; +} + + +/** + * Plot text decoration for an non-inline box. + * + * \param box box to plot decorations for, of type other than BOX_INLINE + * \param x x coordinate of box + * \param y y coordinate of box + * \param scale scale for redraw + * \param colour colour for decorations + * \param ratio position of line as a ratio of line height + * \param ctx current redraw context + * \return true if successful, false otherwise + */ + +static bool html_redraw_text_decoration_block(struct box *box, int x, int y, + float scale, colour colour, float ratio, + const struct redraw_context *ctx) +{ + const struct plotter_table *plot = ctx->plot; + struct box *c; + plot_style_t plot_style_box = { + .stroke_type = PLOT_OP_TYPE_SOLID, + .stroke_colour = colour, + }; + + /* draw through text descendants */ + for (c = box->children; c; c = c->next) { + if (c->type == BOX_TEXT) { + if (!plot->line((x + c->x) * scale, + (y + c->y + c->height * ratio) * scale, + (x + c->x + c->width) * scale, + (y + c->y + c->height * ratio) * scale, + &plot_style_box)) + return false; + } else if (c->type == BOX_INLINE_CONTAINER || + c->type == BOX_BLOCK) { + if (!html_redraw_text_decoration_block(c, + x + c->x, y + c->y, + scale, colour, ratio, ctx)) + return false; + } + } + return true; +} + + +/** * Plot text decoration for a box. * * \param box box to plot decorations for @@ -2444,7 +1742,7 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, * \return true if successful, false otherwise */ -bool html_redraw_text_decoration(struct box *box, +static bool html_redraw_text_decoration(struct box *box, int x_parent, int y_parent, float scale, colour background_colour, const struct redraw_context *ctx) { @@ -2490,85 +1788,750 @@ bool html_redraw_text_decoration(struct box *box, /** - * Plot text decoration for an inline box. + * Redraw the text content of a box, possibly partially highlighted + * because the text has been selected, or matches a search operation. * - * \param box box to plot decorations for, of type BOX_INLINE - * \param x x coordinate of parent of box - * \param y y coordinate of parent of box - * \param scale scale for redraw - * \param colour colour for decorations - * \param ratio position of line as a ratio of line height - * \param ctx current redraw context + * \param box box with text content + * \param x x co-ord of box + * \param y y co-ord of box + * \param clip current clip rectangle + * \param scale current scale setting (1.0 = 100%) + * \param current_background_color + * \param ctx current redraw context + * \return true iff successful and redraw should proceed + */ + +static bool html_redraw_text_box(const html_content *html, struct box *box, + int x, int y, const struct rect *clip, float scale, + colour current_background_color, + const struct redraw_context *ctx) +{ + bool excluded = (box->object != NULL); + plot_font_style_t fstyle; + + font_plot_style_from_css(box->style, &fstyle); + fstyle.background = current_background_color; + + if (!text_redraw(box->text, box->length, box->byte_offset, + box->space, &fstyle, x, y, + clip, box->height, scale, excluded, + (struct content *)html, &html->sel, + html->search, ctx)) + return false; + + return true; +} + +bool html_redraw_box(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, float scale, + colour current_background_color, + const struct redraw_context *ctx); + +/** + * Draw the various children of a box. + * + * \param html html content + * \param box box to draw children of + * \param x_parent coordinate of parent box + * \param y_parent coordinate of parent box + * \param clip clip rectangle + * \param scale scale for redraw + * \param current_background_color background colour under this box + * \param ctx current redraw context * \return true if successful, false otherwise */ -bool html_redraw_text_decoration_inline(struct box *box, int x, int y, - float scale, colour colour, float ratio, +static bool html_redraw_box_children(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, float scale, + colour current_background_color, const struct redraw_context *ctx) { - const struct plotter_table *plot = ctx->plot; struct box *c; - plot_style_t plot_style_box = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = colour, - }; - for (c = box->next; - c && c != box->inline_end; - c = c->next) { - if (c->type != BOX_TEXT) - continue; - if (!plot->line((x + c->x) * scale, - (y + c->y + c->height * ratio) * scale, - (x + c->x + c->width) * scale, - (y + c->y + c->height * ratio) * scale, - &plot_style_box)) - return false; + for (c = box->children; c; c = c->next) { + + if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) + if (!html_redraw_box(html, c, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, + ctx)) + return false; } + for (c = box->float_children; c; c = c->next_float) + if (!html_redraw_box(html, c, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, + ctx)) + return false; + return true; } - /** - * Plot text decoration for an non-inline box. + * Recursively draw a box. * - * \param box box to plot decorations for, of type other than BOX_INLINE - * \param x x coordinate of box - * \param y y coordinate of box - * \param scale scale for redraw - * \param colour colour for decorations - * \param ratio position of line as a ratio of line height - * \param ctx current redraw context + * \param html html content + * \param box box to draw + * \param x_parent coordinate of parent box + * \param y_parent coordinate of parent box + * \param clip clip rectangle + * \param scale scale for redraw + * \param current_background_color background colour under this box + * \param ctx current redraw context * \return true if successful, false otherwise + * + * x, y, clip_[xy][01] are in target coordinates. */ -bool html_redraw_text_decoration_block(struct box *box, int x, int y, - float scale, colour colour, float ratio, +bool html_redraw_box(const html_content *html, struct box *box, + int x_parent, int y_parent, + const struct rect *clip, const float scale, + colour current_background_color, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; - struct box *c; - plot_style_t plot_style_box = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = colour, - }; + int x, y; + int width, height; + int padding_left, padding_top, padding_width, padding_height; + int border_left, border_top, border_right, border_bottom; + struct rect r; + int x_scrolled, y_scrolled; + struct box *bg_box = NULL; + bool has_x_scroll, has_y_scroll; + css_computed_clip_rect css_rect; - /* draw through text descendants */ - for (c = box->children; c; c = c->next) { - if (c->type == BOX_TEXT) { - if (!plot->line((x + c->x) * scale, - (y + c->y + c->height * ratio) * scale, - (x + c->x + c->width) * scale, - (y + c->y + c->height * ratio) * scale, - &plot_style_box)) + if (html_redraw_printing && (box->flags & PRINTED)) + return true; + + /* avoid trivial FP maths */ + if (scale == 1.0) { + x = x_parent + box->x; + y = y_parent + box->y; + width = box->width; + height = box->height; + padding_left = box->padding[LEFT]; + padding_top = box->padding[TOP]; + padding_width = padding_left + box->width + box->padding[RIGHT]; + padding_height = padding_top + box->height + + box->padding[BOTTOM]; + border_left = box->border[LEFT].width; + border_top = box->border[TOP].width; + border_right = box->border[RIGHT].width; + border_bottom = box->border[BOTTOM].width; + } else { + x = (x_parent + box->x) * scale; + y = (y_parent + box->y) * scale; + width = box->width * scale; + height = box->height * scale; + /* left and top padding values are normally zero, + * so avoid trivial FP maths */ + padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale + : 0; + padding_top = box->padding[TOP] ? box->padding[TOP] * scale + : 0; + padding_width = (box->padding[LEFT] + box->width + + box->padding[RIGHT]) * scale; + padding_height = (box->padding[TOP] + box->height + + box->padding[BOTTOM]) * scale; + border_left = box->border[LEFT].width * scale; + border_top = box->border[TOP].width * scale; + border_right = box->border[RIGHT].width * scale; + border_bottom = box->border[BOTTOM].width * scale; + } + + /* calculate rectangle covering this box and descendants */ + if (box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) { + /* box contents clipped to box size */ + r.x0 = x - border_left; + r.y0 = y - border_top; + r.x1 = x + padding_width + border_right; + r.y1 = y + padding_height + border_bottom; + } else { + /* box contents can hang out of the box; use descendant box */ + if (scale == 1.0) { + r.x0 = x + box->descendant_x0; + r.y0 = y + box->descendant_y0; + r.x1 = x + box->descendant_x1 + 1; + r.y1 = y + box->descendant_y1 + 1; + } else { + r.x0 = x + box->descendant_x0 * scale; + r.y0 = y + box->descendant_y0 * scale; + r.x1 = x + box->descendant_x1 * scale + 1; + r.y1 = y + box->descendant_y1 * scale + 1; + } + if (!box->parent) { + /* root element */ + int margin_left, margin_right; + int margin_top, margin_bottom; + if (scale == 1.0) { + margin_left = box->margin[LEFT]; + margin_top = box->margin[TOP]; + margin_right = box->margin[RIGHT]; + margin_bottom = box->margin[BOTTOM]; + } else { + margin_left = box->margin[LEFT] * scale; + margin_top = box->margin[TOP] * scale; + margin_right = box->margin[RIGHT] * scale; + margin_bottom = box->margin[BOTTOM] * scale; + } + r.x0 = x - border_left - margin_left < r.x0 ? + x - border_left - margin_left : r.x0; + r.y0 = y - border_top - margin_top < r.y0 ? + y - border_top - margin_top : r.y0; + r.x1 = x + padding_width + border_right + + margin_right > r.x1 ? + x + padding_width + border_right + + margin_right : r.x1; + r.y1 = y + padding_height + border_bottom + + margin_bottom > r.y1 ? + y + padding_height + border_bottom + + margin_bottom : r.y1; + } + } + + /* return if the rectangle is completely outside the clip rectangle */ + if (clip->y1 < r.y0 || r.y1 < clip->y0 || + clip->x1 < r.x0 || r.x1 < clip->x0) + return true; + + /*if the rectangle is under the page bottom but it can fit in a page, + don't print it now*/ + if (html_redraw_printing) { + if (r.y1 > html_redraw_printing_border) { + if (r.y1 - r.y0 <= html_redraw_printing_border && + (box->type == BOX_TEXT || + box->type == BOX_TABLE_CELL + || box->object || box->gadget)) { + /*remember the highest of all points from the + not printed elements*/ + if (r.y0 < html_redraw_printing_top_cropped) + html_redraw_printing_top_cropped = r.y0; + return true; + } + } + else box->flags |= PRINTED; /*it won't be printed anymore*/ + } + + /* if visibility is hidden render children only */ + if (box->style && css_computed_visibility(box->style) == + CSS_VISIBILITY_HIDDEN) { + if ((plot->group_start) && (!plot->group_start("hidden box"))) + return false; + if (!html_redraw_box_children(html, box, x_parent, y_parent, + &r, scale, current_background_color, ctx)) + return false; + return ((!plot->group_end) || (plot->group_end())); + } + + if ((plot->group_start) && (!plot->group_start("vis box"))) + return false; + + + if (box->style != NULL && + css_computed_position(box->style) == + CSS_POSITION_ABSOLUTE && + css_computed_clip(box->style, &css_rect) == + CSS_CLIP_RECT) { + /* We have an absolutly positioned box with a clip rect */ + if (css_rect.left_auto == false) + r.x0 = x - border_left + FIXTOINT(nscss_len2px( + css_rect.left, css_rect.lunit, + box->style)); + + if (css_rect.top_auto == false) + r.y0 = y - border_top + FIXTOINT(nscss_len2px( + css_rect.top, css_rect.tunit, + box->style)); + + if (css_rect.right_auto == false) + r.x1 = x - border_left + FIXTOINT(nscss_len2px( + css_rect.right, css_rect.runit, + box->style)); + + if (css_rect.bottom_auto == false) + r.y1 = y - border_top + FIXTOINT(nscss_len2px( + css_rect.bottom, css_rect.bunit, + box->style)); + + /* find intersection of clip rectangle and box */ + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + /* no point trying to draw 0-width/height boxes */ + if (r.x0 == r.x1 || r.y0 == r.y1) + /* not an error */ + return ((!plot->group_end) || (plot->group_end())); + /* clip to it */ + if (!plot->clip(&r)) + return false; + + } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->object) { + /* find intersection of clip rectangle and box */ + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + /* no point trying to draw 0-width/height boxes */ + if (r.x0 == r.x1 || r.y0 == r.y1) + /* not an error */ + return ((!plot->group_end) || (plot->group_end())); + /* clip to it */ + if (!plot->clip(&r)) + return false; + } else { + /* clip box is fine, clip to it */ + r = *clip; + if (!plot->clip(&r)) + return false; + } + + /* background colour and image for block level content and replaced + * inlines */ + + bg_box = html_redraw_find_bg_box(box); + + /* bg_box == NULL implies that this box should not have + * its background rendered. Otherwise filter out linebreaks, + * optimize away non-differing inlines, only plot background + * for BOX_TEXT it's in an inline */ + if (bg_box && bg_box->type != BOX_BR && + bg_box->type != BOX_TEXT && + bg_box->type != BOX_INLINE_END && + (bg_box->type != BOX_INLINE || bg_box->object || + bg_box->flags & IFRAME || box->flags & REPLACE_DIM)) { + /* find intersection of clip box and border edge */ + struct rect p; + p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left; + p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top; + p.x1 = x + padding_width + border_right < r.x1 ? + x + padding_width + border_right : r.x1; + p.y1 = y + padding_height + border_bottom < r.y1 ? + y + padding_height + border_bottom : r.y1; + if (!box->parent) { + /* Root element, special case: + * background covers margins too */ + int m_left, m_top, m_right, m_bottom; + if (scale == 1.0) { + m_left = box->margin[LEFT]; + m_top = box->margin[TOP]; + m_right = box->margin[RIGHT]; + m_bottom = box->margin[BOTTOM]; + } else { + m_left = box->margin[LEFT] * scale; + m_top = box->margin[TOP] * scale; + m_right = box->margin[RIGHT] * scale; + m_bottom = box->margin[BOTTOM] * scale; + } + p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left; + p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top; + p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1; + p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1; + } + /* valid clipping rectangles only */ + if ((p.x0 < p.x1) && (p.y0 < p.y1)) { + /* plot background */ + if (!html_redraw_background(x, y, box, scale, &p, + ¤t_background_color, bg_box, ctx)) return false; - } else if (c->type == BOX_INLINE_CONTAINER || - c->type == BOX_BLOCK) { - if (!html_redraw_text_decoration_block(c, - x + c->x, y + c->y, - scale, colour, ratio, ctx)) + /* restore previous graphics window */ + if (!plot->clip(&r)) return false; } } - return true; + + /* borders for block level content and replaced inlines */ + if (box->style && box->type != BOX_TEXT && + box->type != BOX_INLINE_END && + (box->type != BOX_INLINE || box->object || + box->flags & IFRAME || box->flags & REPLACE_DIM) && + (border_top || border_right || + border_bottom || border_left)) { + if (!html_redraw_borders(box, x_parent, y_parent, + padding_width, padding_height, &r, + scale, ctx)) + return false; + } + + /* backgrounds and borders for non-replaced inlines */ + if (box->style && box->type == BOX_INLINE && box->inline_end && + (html_redraw_box_has_background(box) || + border_top || border_right || + border_bottom || border_left)) { + /* inline backgrounds and borders span other boxes and may + * wrap onto separate lines */ + struct box *ib; + struct rect b; /* border edge rectangle */ + struct rect p; /* clipped rect */ + bool first = true; + int ib_x; + int ib_y = y; + int ib_p_width; + int ib_b_left, ib_b_right; + + b.x0 = x - border_left; + b.x1 = x + padding_width + border_right; + b.y0 = y - border_top; + b.y1 = y + padding_height + border_bottom; + + p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; + p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; + p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; + p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; + for (ib = box; ib; ib = ib->next) { + /* to get extents of rectangle(s) associated with + * inline, cycle though all boxes in inline, skipping + * over floats */ + if (ib->type == BOX_FLOAT_LEFT || + ib->type == BOX_FLOAT_RIGHT) + continue; + if (scale == 1.0) { + ib_x = x_parent + ib->x; + ib_y = y_parent + ib->y; + ib_p_width = ib->padding[LEFT] + ib->width + + ib->padding[RIGHT]; + ib_b_left = ib->border[LEFT].width; + ib_b_right = ib->border[RIGHT].width; + } else { + ib_x = (x_parent + ib->x) * scale; + ib_y = (y_parent + ib->y) * scale; + ib_p_width = (ib->padding[LEFT] + ib->width + + ib->padding[RIGHT]) * scale; + ib_b_left = ib->border[LEFT].width * scale; + ib_b_right = ib->border[RIGHT].width * scale; + } + + if ((ib->flags & NEW_LINE) && ib != box) { + /* inline element has wrapped, plot background + * and borders */ + if (!html_redraw_inline_background( + x, y, box, scale, &p, b, + first, false, + ¤t_background_color, ctx)) + return false; + /* restore previous graphics window */ + if (!plot->clip(&r)) + return false; + if (!html_redraw_inline_borders(box, b, &r, + scale, first, false, ctx)) + return false; + /* reset coords */ + b.x0 = ib_x - ib_b_left; + b.y0 = ib_y - border_top - padding_top; + b.y1 = ib_y + padding_height - padding_top + + border_bottom; + + p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; + p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; + p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; + + first = false; + } + + /* increase width for current box */ + b.x1 = ib_x + ib_p_width + ib_b_right; + p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; + + if (ib == box->inline_end) + /* reached end of BOX_INLINE span */ + break; + } + /* plot background and borders for last rectangle of + * the inline */ + if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b, + first, true, ¤t_background_color, ctx)) + return false; + /* restore previous graphics window */ + if (!plot->clip(&r)) + return false; + if (!html_redraw_inline_borders(box, b, &r, scale, first, true, + ctx)) + return false; + + } + + /* Debug outlines */ + if (html_redraw_debug) { + int margin_left, margin_right; + int margin_top, margin_bottom; + if (scale == 1.0) { + /* avoid trivial fp maths */ + margin_left = box->margin[LEFT]; + margin_top = box->margin[TOP]; + margin_right = box->margin[RIGHT]; + margin_bottom = box->margin[BOTTOM]; + } else { + margin_left = box->margin[LEFT] * scale; + margin_top = box->margin[TOP] * scale; + margin_right = box->margin[RIGHT] * scale; + margin_bottom = box->margin[BOTTOM] * scale; + } + /* Content edge -- blue */ + if (!plot->rectangle(x + padding_left, + y + padding_top, + x + padding_left + width, + y + padding_top + height, + plot_style_content_edge)) + return false; + /* Padding edge -- red */ + if (!plot->rectangle(x, y, + x + padding_width, y + padding_height, + plot_style_padding_edge)) + return false; + /* Margin edge -- yellow */ + if (!plot->rectangle( + x - border_left - margin_left, + y - border_top - margin_top, + x + padding_width + border_right + + margin_right, + y + padding_height + border_bottom + + margin_bottom, + plot_style_margin_edge)) + return false; + } + + /* clip to the padding edge for objects, or boxes with overflow hidden + * or scroll */ + if ((box->style && css_computed_overflow(box->style) != + CSS_OVERFLOW_VISIBLE) || box->object || + box->flags & IFRAME) { + r.x0 = x; + r.y0 = y; + r.x1 = x + padding_width; + r.y1 = y + padding_height; + if (r.x0 < clip->x0) r.x0 = clip->x0; + if (r.y0 < clip->y0) r.y0 = clip->y0; + if (clip->x1 < r.x1) r.x1 = clip->x1; + if (clip->y1 < r.y1) r.y1 = clip->y1; + if (r.x1 <= r.x0 || r.y1 <= r.y0) + return ((!plot->group_end) || (plot->group_end())); + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->object) { + if (!plot->clip(&r)) + return false; + } + } + + /* text decoration */ + if (box->type != BOX_TEXT && box->style && + css_computed_text_decoration(box->style) != + CSS_TEXT_DECORATION_NONE) + if (!html_redraw_text_decoration(box, x_parent, y_parent, + scale, current_background_color, ctx)) + return false; + + if (box->object && width != 0 && height != 0) { + struct content_redraw_data obj_data; + + x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale; + y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale; + + obj_data.x = x_scrolled + padding_left; + obj_data.y = y_scrolled + padding_top; + obj_data.width = width; + obj_data.height = height; + obj_data.background_colour = current_background_color; + obj_data.scale = scale; + obj_data.repeat_x = false; + obj_data.repeat_y = false; + + if (content_get_type(box->object) == CONTENT_HTML) { + obj_data.x /= scale; + obj_data.y /= scale; + } + + if (!content_redraw(box->object, &obj_data, &r, ctx)) { + /* Show image fail */ + /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */ + const char *obj = "\xef\xbf\xbc"; + int obj_width; + int obj_x = x + padding_left; + if (!plot->rectangle(x + padding_left, + y + padding_top, + x + padding_left + width - 1, + y + padding_top + height - 1, + plot_style_broken_object)) + return false; + if (!nsfont.font_width(plot_fstyle_broken_object, obj, + sizeof(obj) - 1, &obj_width)) + obj_x += 1; + else + obj_x += width / 2 - obj_width / 2; + + if (!plot->text(obj_x, y + padding_top + (int) + (height * 0.75), + obj, sizeof(obj) - 1, + plot_fstyle_broken_object)) + return false; + } + + + } else if (box->iframe) { + /* Offset is passed to browser window redraw unscaled */ + browser_window_redraw(box->iframe, + (x + padding_left) / scale, + (y + padding_top) / scale, &r, ctx); + + } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) { + if (!html_redraw_checkbox(x + padding_left, y + padding_top, + width, height, box->gadget->selected, ctx)) + return false; + + } else if (box->gadget && box->gadget->type == GADGET_RADIO) { + if (!html_redraw_radio(x + padding_left, y + padding_top, + width, height, box->gadget->selected, ctx)) + return false; + + } else if (box->gadget && box->gadget->type == GADGET_FILE) { + if (!html_redraw_file(x + padding_left, y + padding_top, + width, height, box, scale, + current_background_color, ctx)) + return false; + + } else if (box->text) { + if (!html_redraw_text_box(html, box, x, y, &r, scale, + current_background_color, ctx)) + return false; + + } else { + if (!html_redraw_box_children(html, box, x_parent, y_parent, &r, + scale, current_background_color, ctx)) + return false; + } + + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) + if (!plot->clip(clip)) + return false; + + /* list marker */ + if (box->list_marker) + if (!html_redraw_box(html, box->list_marker, + x_parent + box->x - + scrollbar_get_offset(box->scroll_x), + y_parent + box->y - + scrollbar_get_offset(box->scroll_y), + clip, scale, current_background_color, ctx)) + return false; + + /* scrollbars */ + if (((box->style && box->type != BOX_BR && + box->type != BOX_TABLE && box->type != BOX_INLINE && + (css_computed_overflow(box->style) == + CSS_OVERFLOW_SCROLL || + css_computed_overflow(box->style) == + CSS_OVERFLOW_AUTO)) || (box->object && + content_get_type(box->object) == CONTENT_HTML)) && + box->parent != NULL) { + + has_x_scroll = box_hscrollbar_present(box); + has_y_scroll = box_vscrollbar_present(box); + + if (!box_handle_scrollbars((struct content *)html, + box, has_x_scroll, has_y_scroll)) + return false; + + if (box->scroll_x != NULL) + scrollbar_redraw(box->scroll_x, + x_parent + box->x, + y_parent + box->y + box->padding[TOP] + + box->height + box->padding[BOTTOM] - + SCROLLBAR_WIDTH, clip, scale, ctx); + if (box->scroll_y != NULL) + scrollbar_redraw(box->scroll_y, + x_parent + box->x + box->padding[LEFT] + + box->width + box->padding[RIGHT] - + SCROLLBAR_WIDTH, + y_parent + box->y, clip, scale, ctx); + } + + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || + box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) + if (!plot->clip(clip)) + return false; + + return ((!plot->group_end) || (plot->group_end())); +} + +/** + * Draw a CONTENT_HTML using the current set of plotters (plot). + * + * \param c content of type CONTENT_HTML + * \param data redraw data for this content redraw + * \param clip current clip region + * \param ctx current redraw context + * \return true if successful, false otherwise + * + * x, y, clip_[xy][01] are in target coordinates. + */ + +bool html_redraw(struct content *c, struct content_redraw_data *data, + const struct rect *clip, const struct redraw_context *ctx) +{ + html_content *html = (html_content *) c; + struct box *box; + bool result = true; + bool select, select_only; + plot_style_t pstyle_fill_bg = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = data->background_colour, + }; + + box = html->layout; + assert(box); + + /* The select menu needs special treating because, when opened, it + * reaches beyond its layout box. + */ + select = false; + select_only = false; + if (ctx->interactive && html->visible_select_menu != NULL) { + struct form_control *control = html->visible_select_menu; + select = true; + /* check if the redraw rectangle is completely inside of the + select menu */ + select_only = form_clip_inside_select_menu(control, + data->scale, clip); + } + + if (!select_only) { + /* clear to background colour */ + result = ctx->plot->clip(clip); + + if (html->background_colour != NS_TRANSPARENT) + pstyle_fill_bg.fill_colour = html->background_colour; + + result &= ctx->plot->rectangle(clip->x0, clip->y0, + clip->x1, clip->y1, + &pstyle_fill_bg); + + result &= html_redraw_box(html, box, data->x, data->y, clip, + data->scale, pstyle_fill_bg.fill_colour, ctx); + } + + if (select) { + int menu_x, menu_y; + box = html->visible_select_menu->box; + box_coords(box, &menu_x, &menu_y); + + menu_x -= box->border[LEFT].width; + menu_y += box->height + box->border[BOTTOM].width + + box->padding[BOTTOM] + box->padding[TOP]; + result &= form_redraw_select_menu(html->visible_select_menu, + data->x + menu_x, data->y + menu_y, + data->scale, clip, ctx); + } + + return result; + } diff --git a/render/html_script.c b/render/html_script.c index 9edd08cf2..9c14e84ce 100644 --- a/render/html_script.c +++ b/render/html_script.c @@ -212,7 +212,7 @@ convert_script_defer_cb(hlcache_handle *script, /* Find script */ for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) { - if (s->type == HTML_SCRIPT_ASYNC && s->data.handle == script) + if (s->type == HTML_SCRIPT_DEFER && s->data.handle == script) break; } diff --git a/render/layout.c b/render/layout.c index 702cc03c6..331e1efdb 100644 --- a/render/layout.c +++ b/render/layout.c @@ -3045,11 +3045,15 @@ struct box *layout_minmax_line(struct box *first, &fixed, &frac); if (0 < fixed) max += fixed; - if (b->next && b->space == UNKNOWN_WIDTH) { - font_func->font_width(&fstyle, " ", 1, - &b->space); + + if (b->next) { + if (b->space == UNKNOWN_WIDTH) { + font_func->font_width(&fstyle, " ", 1, + &b->space); + } max += b->space; } + *line_has_height = true; continue; } @@ -3095,9 +3099,11 @@ struct box *layout_minmax_line(struct box *first, } } max += b->width; - if (b->next && b->space == UNKNOWN_WIDTH) { - font_func->font_width(&fstyle, " ", 1, - &b->space); + if (b->next) { + if (b->space == UNKNOWN_WIDTH) { + font_func->font_width(&fstyle, " ", 1, + &b->space); + } max += b->space; } diff --git a/render/textinput.c b/render/textinput.c index 15e89f20d..71b4e53e0 100644 --- a/render/textinput.c +++ b/render/textinput.c @@ -76,6 +76,13 @@ static bool textinput_input_paste_text(struct browser_window *bw, #define SPACE_LEN(b) ((b->space == 0) ? 0 : 1) +static struct textinput_buffer { + char *buffer; + size_t buffer_len; + size_t length; +} textinput_buffer; + + /** * Given the x,y co-ordinates of a point within a textarea, return the @@ -525,6 +532,69 @@ static struct box *textinput_line_below(struct box *text_box) /** + * Add some text to the buffer, optionally appending a trailing space. + * + * \param text text to be added + * \param length length of text in bytes + * \param space indicates whether a trailing space should be appended + * \param fstyle The font style + * \return true if successful + */ + +static bool textinput_add_to_buffer(const char *text, size_t length, bool space, + const plot_font_style_t *fstyle) +{ + size_t new_length = textinput_buffer.length + length + (space ? 1 : 0) + 1; + + if (new_length > textinput_buffer.buffer_len) { + size_t new_alloc = new_length + (new_length / 4); + char *new_buff; + + new_buff = realloc(textinput_buffer.buffer, new_alloc); + if (new_buff == NULL) + return false; + + textinput_buffer.buffer = new_buff; + textinput_buffer.buffer_len = new_alloc; + } + + memcpy(textinput_buffer.buffer + textinput_buffer.length, text, length); + textinput_buffer.length += length; + + if (space) + textinput_buffer.buffer[textinput_buffer.length++] = ' '; + + textinput_buffer.buffer[textinput_buffer.length] = '\0'; + + return true; +} + + +/** + * Empty the buffer, called prior to textinput_add_to_buffer sequence + * + * \return true iff successful + */ + +static bool textinput_empty_buffer(void) +{ + const size_t init_size = 1024; + + if (textinput_buffer.buffer_len == 0) { + textinput_buffer.buffer = malloc(init_size); + if (textinput_buffer.buffer == NULL) + return false; + + textinput_buffer.buffer_len = init_size; + } + + textinput_buffer.length = 0; + + return true; +} + + +/** * Cut a range of text from a text box, * possibly placing it on the global clipboard. * @@ -546,27 +616,26 @@ static bool textinput_textarea_cut(struct content *c, bool success = true; bool del = false; /* caller expects start_box to persist */ - if (clipboard && !gui_empty_clipboard()) + if (textinput_empty_buffer() == false) { return false; + } while (box && box != end_box) { /* read before deletion, in case the whole box goes */ struct box *next = box->next; if (box->type == BOX_BR) { - if (clipboard && !gui_add_to_clipboard("\n", 1, false, - plot_style_font)) { - gui_commit_clipboard(); + if (clipboard && !textinput_add_to_buffer("\n", 1, + false, plot_style_font)) { return false; } box_unlink_and_free(box); } else { /* append box text to clipboard and then delete it */ if (clipboard && - !gui_add_to_clipboard(box->text + start_idx, + !textinput_add_to_buffer(box->text + start_idx, box->length - start_idx, SPACE_LEN(box), plot_style_font)) { - gui_commit_clipboard(); return false; } @@ -575,7 +644,6 @@ static bool textinput_textarea_cut(struct content *c, start_idx, (box->length + SPACE_LEN(box)) - start_idx) && clipboard) { - gui_commit_clipboard(); return false; } } else { @@ -592,7 +660,7 @@ static bool textinput_textarea_cut(struct content *c, /* and the last box */ if (box) { - if (clipboard && !gui_add_to_clipboard(box->text + start_idx, + if (clipboard && !textinput_add_to_buffer(box->text + start_idx, end_idx - start_idx, end_idx > box->length, plot_style_font)) { success = false; @@ -608,10 +676,12 @@ static bool textinput_textarea_cut(struct content *c, } } - if (clipboard && !gui_commit_clipboard()) - success = false; + if (clipboard) { + gui_set_clipboard(textinput_buffer.buffer, + textinput_buffer.length, NULL, 0); + } - return success; + return true; } @@ -1310,14 +1380,18 @@ bool textinput_textarea_callback(struct browser_window *bw, uint32_t key, case KEY_PASTE: { - union content_msg_data msg_data; - msg_data.paste.x = box_x + inline_container->x + - text_box->x + pixel_offset; - msg_data.paste.y = box_y + inline_container->y + text_box->y; - content_broadcast(c, CONTENT_MSG_PASTE, msg_data); + char *buff; + size_t buff_len; + bool success; - /* screen updated and caret repositioned already */ - return true; + gui_get_clipboard(&buff, &buff_len); + if (utf8 == NULL) + return false; + + success = browser_window_paste_text(bw, buff, buff_len, true); + free(buff); + + return success; } case KEY_CUT_SELECTION: @@ -1804,7 +1878,6 @@ bool textinput_input_callback(struct browser_window *bw, uint32_t key, struct box *text_box = input->children->children; size_t box_offset = input->gadget->caret_box_offset; size_t end_offset; - int pixel_offset = input->gadget->caret_pixel_offset; int box_x, box_y; struct form* form = input->gadget->form; bool changed = false; @@ -1954,14 +2027,18 @@ bool textinput_input_callback(struct browser_window *bw, uint32_t key, case KEY_PASTE: { - union content_msg_data msg_data; - msg_data.paste.x = box_x + input->children->x + text_box->x + - pixel_offset; - msg_data.paste.y = box_y + input->children->y + text_box->y; - content_broadcast(c, CONTENT_MSG_PASTE, msg_data); + char *buff; + size_t buff_len; + bool success; - /* screen updated and caret repositioned already */ - return true; + gui_get_clipboard(&buff, &buff_len); + if (utf8 == NULL) + return false; + + success = browser_window_paste_text(bw, buff, buff_len, true); + free(buff); + + return success; } case KEY_CUT_SELECTION: diff --git a/riscos/scripts/Run b/riscos/scripts/Run index 5f59b7ffa..d3b9988e8 100644 --- a/riscos/scripts/Run +++ b/riscos/scripts/Run @@ -93,7 +93,7 @@ RMEnsure Iconv 0.11 Error NetSurf requires Iconv 0.11 or later. This can be down | Ensure CryptRandom RMEnsure CryptRandom 0.12 NetSurfRMLoad System:Modules.CryptRand -RMEnsure CryptRandom 0.12 Error NetSurf requires CryptRandom 0.12 or later. This can be downloaded form http://www.riscos.info/index.php/CryptRandom +RMEnsure CryptRandom 0.12 Error NetSurf requires CryptRandom 0.12 or later. This can be downloaded from http://www.riscos.info/index.php/CryptRandom | Disable SpecialFX, if present Set NetSurf$SpecialFX 1 diff --git a/riscos/textselection.c b/riscos/textselection.c index 96c3e2c90..d1d4340b7 100644 --- a/riscos/textselection.c +++ b/riscos/textselection.c @@ -184,33 +184,6 @@ void ro_gui_selection_drag_end(struct gui_window *g, wimp_dragged *drag) /** - * Empty the clipboard, called prior to gui_add_to_clipboard and - * gui_commit_clipboard - * - * \return true iff successful - */ - -bool gui_empty_clipboard(void) -{ - const int init_size = 1024; - - if (!clip_alloc) { - clipboard = malloc(init_size); - if (!clipboard) { - LOG(("out of memory")); - warn_user("NoMemory", 0); - return false; - } - clip_alloc = init_size; - } - - clip_length = 0; - - return true; -} - - -/** * Perform tasks after a selection has been cleared. * * \param g gui window @@ -224,63 +197,35 @@ void gui_clear_selection(struct gui_window *g) /** - * Add some text to the clipboard, optionally appending a trailing space. + * Core tells front end to put given text in clipboard * - * \param text text to be added - * \param length length of text in bytes - * \param space indicates whether a trailing space should be appended also - * \param fstyle font plot style for text - * \return true iff successful + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array */ - -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { - size_t new_length = clip_length + length + (space ? 1 : 0); - - if (new_length > clip_alloc) { - size_t new_alloc = clip_alloc + (clip_alloc / 4); - char *new_cb; - - if (new_alloc < new_length) new_alloc = new_length; - - new_cb = realloc(clipboard, new_alloc); - if (!new_cb) return false; - - clipboard = new_cb; - clip_alloc = new_alloc; - } + utf8_convert_ret res; + char *new_cb; - memcpy(clipboard + clip_length, text, length); - clip_length += length; - if (space) clipboard[clip_length++] = ' '; - - return true; -} + if (length == 0) + return; + /* Convert to local encoding */ + res = utf8_to_local_encoding(buffer, length, &new_cb); -/** - * Commit the changes made by gui_empty_clipboard and gui_add_to_clipboard. - * - * \return true iff successful - */ + if (res != UTF8_CONVERT_OK || new_cb == NULL) + return; -bool gui_commit_clipboard(void) -{ - if (clip_length) { - utf8_convert_ret res; - char *new_cb; - - res = utf8_to_local_encoding(clipboard, clip_length, &new_cb); - if (res == UTF8_CONVERT_OK) { - free(clipboard); - clipboard = new_cb; -/* \todo utf8_to_local_encoding should return the length! */ - clip_alloc = clip_length = strlen(new_cb); - } - } + /* Replace existing clipboard contents with converted text */ + free(clipboard); + clipboard = new_cb; + clip_alloc = clip_length = strlen(new_cb); if (!owns_clipboard) { + /* Tell RO we now own clipboard */ wimp_full_message_claim_entity msg; os_error *error; @@ -302,44 +247,20 @@ bool gui_commit_clipboard(void) } LOG(("clipboard now holds %zd bytes", clip_length)); - - return true; } - /** - * Copy the selected contents to the global clipboard, - * and claim ownership of the clipboard from other apps. + * Core asks front end for clipboard contents. * - * \param s selection - * \return true iff successful, ie. cut operation can proceed without losing data + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer */ - -bool gui_copy_to_clipboard(struct selection *s) +void gui_get_clipboard(char **buffer, size_t *length) { - if (!gui_empty_clipboard()) - return false; - - selection_copy_to_clipboard(s); - - return gui_commit_clipboard(); -} + *buffer = NULL; + *length = 0; - -/** - * Request to paste the clipboard contents into a textarea/input field - * at a given position. Note that the actual operation may take place - * straight away (local clipboard) or in a number of chunks at some - * later time (clipboard owned by another app). - * - * \param g gui window - * \param x x ordinate at which to paste text - * \param y y ordinate at which to paste text - */ - -void gui_paste_from_clipboard(struct gui_window *g, int x, int y) -{ if (owns_clipboard) { if (clip_length > 0) { char *utf8; @@ -349,14 +270,17 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) ret = utf8_from_local_encoding(clipboard, clip_length, &utf8); if (ret == UTF8_CONVERT_OK) { - browser_window_paste_text(g->bw, utf8, - strlen(utf8), true); - free(utf8); + *buffer = utf8; + *length = strlen(utf8); } } - } - else { - wimp_full_message_data_request msg; + } else { + /** TODO: Handle case when we don't own the clipboard */ + +/* http://www.starfighter.acornarcade.com/mysite/articles/SelectionModel.html + */ + +/* wimp_full_message_data_request msg; os_error *error; os_coord pos; @@ -381,7 +305,7 @@ void gui_paste_from_clipboard(struct gui_window *g, int x, int y) error->errnum, error->errmess)); warn_user("WimpError", error->errmess); } - } +*/ } } diff --git a/utils/corestrings.c b/utils/corestrings.c index 9fee96d6e..65666df66 100644 --- a/utils/corestrings.c +++ b/utils/corestrings.c @@ -638,7 +638,7 @@ nserror corestrings_init(void) CSS_DOM_STRING_INTERN(href); CSS_DOM_STRING_INTERN(hreflang); CSS_DOM_STRING_INTERN(hspace); - CSS_DOM_STRING_INTERN(http_equiv); + /* http-equiv: see below */ CSS_DOM_STRING_INTERN(id); CSS_DOM_STRING_INTERN(input); CSS_DOM_STRING_INTERN(invalid); diff --git a/windows/gui.c b/windows/gui.c index 78c2d1b10..e2f8f9afa 100644 --- a/windows/gui.c +++ b/windows/gui.c @@ -774,8 +774,7 @@ nsws_window_command(HWND hwnd, if (GetFocus() == gw->urlbar) { SendMessage(gw->urlbar, WM_COPY, 0, 0); } else if (gw->bw != NULL) { - gui_copy_to_clipboard( - browser_window_get_selection(gw->bw)); + browser_window_key_press(gw->bw, KEY_COPY_SELECTION); } break; @@ -791,7 +790,7 @@ nsws_window_command(HWND hwnd, if (GetFocus() == gw->urlbar) SendMessage(gw->urlbar, WM_PASTE, 0, 0); else - gui_paste_from_clipboard(gw, 0, 0); + browser_window_key_press(gw->bw, KEY_PASTE); break; } @@ -1727,8 +1726,15 @@ void gui_clear_selection(struct gui_window *w) { } -void gui_paste_from_clipboard(struct gui_window *w, int x, int y) +/** + * Core asks front end for clipboard contents. + * + * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core + * \param length Byte length of UTF-8 text in buffer + */ +void gui_get_clipboard(char **buffer, size_t *length) { + /* TODO: Implement this */ HANDLE clipboard_handle; char *content; @@ -1740,14 +1746,18 @@ void gui_paste_from_clipboard(struct gui_window *w, int x, int y) } } -bool gui_empty_clipboard(void) -{ - return false; -} - -bool gui_add_to_clipboard(const char *text, size_t length, bool space, - const plot_font_style_t *fstyle) +/** + * Core tells front end to put given text in clipboard + * + * \param buffer UTF-8 text, owned by core + * \param length Byte length of UTF-8 text in buffer + * \param styles Array of styles given to text runs, owned by core, or NULL + * \param n_styles Number of text run styles in array + */ +void gui_set_clipboard(const char *buffer, size_t length, + nsclipboard_styles styles[], int n_styles) { + /* TODO: Implement this */ HANDLE hnew; char *new, *original; HANDLE h = GetClipboardData(CF_TEXT); @@ -1759,7 +1769,7 @@ bool gui_add_to_clipboard(const char *text, size_t length, bool space, size_t len = strlen(original) + 1; hnew = GlobalAlloc(GHND, length + len); new = (char *)GlobalLock(hnew); - snprintf(new, length + len, "%s%s", original, text); + snprintf(new, length + len, "%s%s", original, buffer); if (h != NULL) { GlobalUnlock(h); @@ -1767,29 +1777,6 @@ bool gui_add_to_clipboard(const char *text, size_t length, bool space, } GlobalUnlock(hnew); SetClipboardData(CF_TEXT, hnew); - return true; -} - -bool gui_commit_clipboard(void) -{ - return false; -} - - -bool gui_copy_to_clipboard(struct selection *s) -{ - if (selection_defined(s)) { - /* TODO: Fix this, so it's not looking inside selection - * object */ - -/* OpenClipboard(s->bw->window->main); - EmptyClipboard(); - if (selection_copy_to_clipboard(s)) { - CloseClipboard(); - return true; - } -*/ } - return false; } |