From d21447d096a320a08b3efb2b8768fad0dcdcfd64 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Thu, 5 May 2016 22:28:51 +0100 Subject: move frontends into sub directory --- frontends/framebuffer/gui.c | 2226 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2226 insertions(+) create mode 100644 frontends/framebuffer/gui.c (limited to 'frontends/framebuffer/gui.c') diff --git a/frontends/framebuffer/gui.c b/frontends/framebuffer/gui.c new file mode 100644 index 000000000..b0b98c546 --- /dev/null +++ b/frontends/framebuffer/gui.c @@ -0,0 +1,2226 @@ +/* + * Copyright 2008, 2014 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "utils/utils.h" +#include "utils/nsoption.h" +#include "utils/filepath.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "desktop/browser.h" +#include "desktop/textinput.h" +#include "desktop/browser_history.h" +#include "desktop/plotters.h" +#include "desktop/gui_window.h" +#include "desktop/gui_misc.h" +#include "desktop/netsurf.h" +#include "content/urldb.h" +#include "content/fetch.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/framebuffer.h" +#include "framebuffer/schedule.h" +#include "framebuffer/findfile.h" +#include "framebuffer/image_data.h" +#include "framebuffer/font.h" +#include "framebuffer/clipboard.h" +#include "framebuffer/fetch.h" +#include "framebuffer/bitmap.h" + + +#define NSFB_TOOLBAR_DEFAULT_LAYOUT "blfsrutc" + +fbtk_widget_t *fbtk; + +static bool fb_complete = false; + +struct gui_window *input_window = NULL; +struct gui_window *search_current_window; +struct gui_window *window_list = NULL; + +/* private data for browser user widget */ +struct browser_widget_s { + struct browser_window *bw; /**< The browser window connected to this gui window */ + int scrollx, scrolly; /**< scroll offsets. */ + + /* Pending window redraw state. */ + bool redraw_required; /**< flag indicating the foreground loop + * needs to redraw the browser widget. + */ + bbox_t redraw_box; /**< Area requiring redraw. */ + bool pan_required; /**< flag indicating the foreground loop + * needs to pan the window. + */ + int panx, pany; /**< Panning required. */ +}; + +static struct gui_drag { + enum state { + GUI_DRAG_NONE, + GUI_DRAG_PRESSED, + GUI_DRAG_DRAG + } state; + int button; + int x; + int y; + bool grabbed_pointer; +} gui_drag; + + +/** + * Cause an abnormal program termination. + * + * \note This never returns and is intended to terminate without any cleanup. + * + * \param error The message to display to the user. + */ +static void die(const char *error) +{ + fprintf(stderr, "%s\n", error); + exit(1); +} + + +/** + * Warn the user of an event. + * + * \param[in] message A warning looked up in the message translation table + * \param[in] detail Additional text to be displayed or NULL. + * \return NSERROR_OK on success or error code if there was a + * faliure displaying the message to the user. + */ +static nserror fb_warn_user(const char *warning, const char *detail) +{ + LOG("%s %s", warning, detail); + return NSERROR_OK; +} + +/* queue a redraw operation, co-ordinates are relative to the window */ +static void +fb_queue_redraw(struct fbtk_widget_s *widget, int x0, int y0, int x1, int y1) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(widget); + + bwidget->redraw_box.x0 = min(bwidget->redraw_box.x0, x0); + bwidget->redraw_box.y0 = min(bwidget->redraw_box.y0, y0); + bwidget->redraw_box.x1 = max(bwidget->redraw_box.x1, x1); + bwidget->redraw_box.y1 = max(bwidget->redraw_box.y1, y1); + + if (fbtk_clip_to_widget(widget, &bwidget->redraw_box)) { + bwidget->redraw_required = true; + fbtk_request_redraw(widget); + } else { + bwidget->redraw_box.y0 = bwidget->redraw_box.x0 = INT_MAX; + bwidget->redraw_box.y1 = bwidget->redraw_box.x1 = -(INT_MAX); + bwidget->redraw_required = false; + } +} + +/* queue a window scroll */ +static void +widget_scroll_y(struct gui_window *gw, int y, bool abs) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser); + int content_width, content_height; + int height; + + LOG("window scroll"); + if (abs) { + bwidget->pany = y - bwidget->scrolly; + } else { + bwidget->pany += y; + } + + browser_window_get_extents(gw->bw, true, + &content_width, &content_height); + + height = fbtk_get_height(gw->browser); + + /* dont pan off the top */ + if ((bwidget->scrolly + bwidget->pany) < 0) + bwidget->pany = -bwidget->scrolly; + + /* do not pan off the bottom of the content */ + if ((bwidget->scrolly + bwidget->pany) > (content_height - height)) + bwidget->pany = (content_height - height) - bwidget->scrolly; + + if (bwidget->pany == 0) + return; + + bwidget->pan_required = true; + + fbtk_request_redraw(gw->browser); + + fbtk_set_scroll_position(gw->vscroll, bwidget->scrolly + bwidget->pany); +} + +/* queue a window scroll */ +static void +widget_scroll_x(struct gui_window *gw, int x, bool abs) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser); + int content_width, content_height; + int width; + + if (abs) { + bwidget->panx = x - bwidget->scrollx; + } else { + bwidget->panx += x; + } + + browser_window_get_extents(gw->bw, true, + &content_width, &content_height); + + width = fbtk_get_width(gw->browser); + + /* dont pan off the left */ + if ((bwidget->scrollx + bwidget->panx) < 0) + bwidget->panx = - bwidget->scrollx; + + /* do not pan off the right of the content */ + if ((bwidget->scrollx + bwidget->panx) > (content_width - width)) + bwidget->panx = (content_width - width) - bwidget->scrollx; + + if (bwidget->panx == 0) + return; + + bwidget->pan_required = true; + + fbtk_request_redraw(gw->browser); + + fbtk_set_scroll_position(gw->hscroll, bwidget->scrollx + bwidget->panx); +} + +static void +fb_pan(fbtk_widget_t *widget, + struct browser_widget_s *bwidget, + struct browser_window *bw) +{ + int x; + int y; + int width; + int height; + nsfb_bbox_t srcbox; + nsfb_bbox_t dstbox; + + nsfb_t *nsfb = fbtk_get_nsfb(widget); + + height = fbtk_get_height(widget); + width = fbtk_get_width(widget); + + LOG("panning %d, %d", bwidget->panx, bwidget->pany); + + x = fbtk_get_absx(widget); + y = fbtk_get_absy(widget); + + /* if the pan exceeds the viewport size just redraw the whole area */ + if (bwidget->pany >= height || bwidget->pany <= -height || + bwidget->panx >= width || bwidget->panx <= -width) { + + bwidget->scrolly += bwidget->pany; + bwidget->scrollx += bwidget->panx; + fb_queue_redraw(widget, 0, 0, width, height); + + /* ensure we don't try to scroll again */ + bwidget->panx = 0; + bwidget->pany = 0; + bwidget->pan_required = false; + return; + } + + if (bwidget->pany < 0) { + /* pan up by less then viewport height */ + srcbox.x0 = x; + srcbox.y0 = y; + srcbox.x1 = srcbox.x0 + width; + srcbox.y1 = srcbox.y0 + height + bwidget->pany; + + dstbox.x0 = x; + dstbox.y0 = y - bwidget->pany; + dstbox.x1 = dstbox.x0 + width; + dstbox.y1 = dstbox.y0 + height + bwidget->pany; + + /* move part that remains visible up */ + nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox); + + /* redraw newly exposed area */ + bwidget->scrolly += bwidget->pany; + fb_queue_redraw(widget, 0, 0, width, - bwidget->pany); + + } else if (bwidget->pany > 0) { + /* pan down by less then viewport height */ + srcbox.x0 = x; + srcbox.y0 = y + bwidget->pany; + srcbox.x1 = srcbox.x0 + width; + srcbox.y1 = srcbox.y0 + height - bwidget->pany; + + dstbox.x0 = x; + dstbox.y0 = y; + dstbox.x1 = dstbox.x0 + width; + dstbox.y1 = dstbox.y0 + height - bwidget->pany; + + /* move part that remains visible down */ + nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox); + + /* redraw newly exposed area */ + bwidget->scrolly += bwidget->pany; + fb_queue_redraw(widget, 0, height - bwidget->pany, + width, height); + } + + if (bwidget->panx < 0) { + /* pan left by less then viewport width */ + srcbox.x0 = x; + srcbox.y0 = y; + srcbox.x1 = srcbox.x0 + width + bwidget->panx; + srcbox.y1 = srcbox.y0 + height; + + dstbox.x0 = x - bwidget->panx; + dstbox.y0 = y; + dstbox.x1 = dstbox.x0 + width + bwidget->panx; + dstbox.y1 = dstbox.y0 + height; + + /* move part that remains visible left */ + nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox); + + /* redraw newly exposed area */ + bwidget->scrollx += bwidget->panx; + fb_queue_redraw(widget, 0, 0, -bwidget->panx, height); + + } else if (bwidget->panx > 0) { + /* pan right by less then viewport width */ + srcbox.x0 = x + bwidget->panx; + srcbox.y0 = y; + srcbox.x1 = srcbox.x0 + width - bwidget->panx; + srcbox.y1 = srcbox.y0 + height; + + dstbox.x0 = x; + dstbox.y0 = y; + dstbox.x1 = dstbox.x0 + width - bwidget->panx; + dstbox.y1 = dstbox.y0 + height; + + /* move part that remains visible right */ + nsfb_plot_copy(nsfb, &srcbox, nsfb, &dstbox); + + /* redraw newly exposed area */ + bwidget->scrollx += bwidget->panx; + fb_queue_redraw(widget, width - bwidget->panx, 0, + width, height); + } + + bwidget->pan_required = false; + bwidget->panx = 0; + bwidget->pany = 0; +} + +static void +fb_redraw(fbtk_widget_t *widget, + struct browser_widget_s *bwidget, + struct browser_window *bw) +{ + int x; + int y; + int caret_x, caret_y, caret_h; + struct rect clip; + struct redraw_context ctx = { + .interactive = true, + .background_images = true, + .plot = &fb_plotters + }; + nsfb_t *nsfb = fbtk_get_nsfb(widget); + float scale = browser_window_get_scale(bw); + + x = fbtk_get_absx(widget); + y = fbtk_get_absy(widget); + + /* adjust clipping co-ordinates according to window location */ + bwidget->redraw_box.y0 += y; + bwidget->redraw_box.y1 += y; + bwidget->redraw_box.x0 += x; + bwidget->redraw_box.x1 += x; + + nsfb_claim(nsfb, &bwidget->redraw_box); + + /* redraw bounding box is relative to window */ + clip.x0 = bwidget->redraw_box.x0; + clip.y0 = bwidget->redraw_box.y0; + clip.x1 = bwidget->redraw_box.x1; + clip.y1 = bwidget->redraw_box.y1; + + browser_window_redraw(bw, + (x - bwidget->scrollx) / scale, + (y - bwidget->scrolly) / scale, + &clip, &ctx); + + if (fbtk_get_caret(widget, &caret_x, &caret_y, &caret_h)) { + /* This widget has caret, so render it */ + nsfb_bbox_t line; + nsfb_plot_pen_t pen; + + line.x0 = x - bwidget->scrollx + caret_x; + line.y0 = y - bwidget->scrolly + caret_y; + line.x1 = x - bwidget->scrollx + caret_x; + line.y1 = y - bwidget->scrolly + caret_y + caret_h; + + pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; + pen.stroke_width = 1; + pen.stroke_colour = 0xFF0000FF; + + nsfb_plot_line(nsfb, &line, &pen); + } + + nsfb_update(fbtk_get_nsfb(widget), &bwidget->redraw_box); + + bwidget->redraw_box.y0 = bwidget->redraw_box.x0 = INT_MAX; + bwidget->redraw_box.y1 = bwidget->redraw_box.x1 = INT_MIN; + bwidget->redraw_required = false; +} + +static int +fb_browser_window_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + struct browser_widget_s *bwidget; + + bwidget = fbtk_get_userpw(widget); + if (bwidget == NULL) { + LOG("browser widget from widget %p was null", widget); + return -1; + } + + if (bwidget->pan_required) { + fb_pan(widget, bwidget, gw->bw); + } + + if (bwidget->redraw_required) { + fb_redraw(widget, bwidget, gw->bw); + } else { + bwidget->redraw_box.x0 = 0; + bwidget->redraw_box.y0 = 0; + bwidget->redraw_box.x1 = fbtk_get_width(widget); + bwidget->redraw_box.y1 = fbtk_get_height(widget); + fb_redraw(widget, bwidget, gw->bw); + } + return 0; +} + +static int fb_browser_window_destroy(fbtk_widget_t *widget, + fbtk_callback_info *cbi) +{ + struct browser_widget_s *browser_widget; + + if (widget == NULL) { + return 0; + } + + /* Free private data */ + browser_widget = fbtk_get_userpw(widget); + free(browser_widget); + + return 0; +} + + +static const char *fename; +static int febpp; +static int fewidth; +static int feheight; +static const char *feurl; + +static bool +process_cmdline(int argc, char** argv) +{ + int opt; + int option_index; + static struct option long_options[] = { + {0, 0, 0, 0 } + }; /* no long options */ + + LOG("argc %d, argv %p", argc, argv); + + fename = "sdl"; + febpp = 32; + + fewidth = nsoption_int(window_width); + if (fewidth <= 0) { + fewidth = 800; + } + feheight = nsoption_int(window_height); + if (feheight <= 0) { + feheight = 600; + } + + if ((nsoption_charp(homepage_url) != NULL) && + (nsoption_charp(homepage_url)[0] != '\0')) { + feurl = nsoption_charp(homepage_url); + } else { + feurl = NETSURF_HOMEPAGE; + } + + while((opt = getopt_long(argc, argv, "f:b:w:h:", + long_options, &option_index)) != -1) { + switch (opt) { + case 'f': + fename = optarg; + break; + + case 'b': + febpp = atoi(optarg); + break; + + case 'w': + fewidth = atoi(optarg); + break; + + case 'h': + feheight = atoi(optarg); + break; + + default: + fprintf(stderr, + "Usage: %s [-f frontend] [-b bpp] url\n", + argv[0]); + return false; + } + } + + if (optind < argc) { + feurl = argv[optind]; + } + + return true; +} + +/** + * Set option defaults for framebuffer frontend + * + * @param defaults The option table to update. + * @return error status. + */ +static nserror set_defaults(struct nsoption_s *defaults) +{ + /* Set defaults for absent option strings */ + nsoption_setnull_charp(cookie_file, strdup("~/.netsurf/Cookies")); + nsoption_setnull_charp(cookie_jar, strdup("~/.netsurf/Cookies")); + + if (nsoption_charp(cookie_file) == NULL || + nsoption_charp(cookie_jar) == NULL) { + LOG("Failed initialising cookie options"); + return NSERROR_BAD_PARAMETER; + } + + /* set system colours for framebuffer ui */ + nsoption_set_colour(sys_colour_ActiveBorder, 0x00000000); + nsoption_set_colour(sys_colour_ActiveCaption, 0x00ddddcc); + nsoption_set_colour(sys_colour_AppWorkspace, 0x00eeeeee); + nsoption_set_colour(sys_colour_Background, 0x00aa0000); + nsoption_set_colour(sys_colour_ButtonFace, 0x00dddddd); + nsoption_set_colour(sys_colour_ButtonHighlight, 0x00cccccc); + nsoption_set_colour(sys_colour_ButtonShadow, 0x00bbbbbb); + nsoption_set_colour(sys_colour_ButtonText, 0x00000000); + nsoption_set_colour(sys_colour_CaptionText, 0x00000000); + nsoption_set_colour(sys_colour_GrayText, 0x00777777); + nsoption_set_colour(sys_colour_Highlight, 0x00ee0000); + nsoption_set_colour(sys_colour_HighlightText, 0x00000000); + nsoption_set_colour(sys_colour_InactiveBorder, 0x00000000); + nsoption_set_colour(sys_colour_InactiveCaption, 0x00ffffff); + nsoption_set_colour(sys_colour_InactiveCaptionText, 0x00cccccc); + nsoption_set_colour(sys_colour_InfoBackground, 0x00aaaaaa); + nsoption_set_colour(sys_colour_InfoText, 0x00000000); + nsoption_set_colour(sys_colour_Menu, 0x00aaaaaa); + nsoption_set_colour(sys_colour_MenuText, 0x00000000); + nsoption_set_colour(sys_colour_Scrollbar, 0x00aaaaaa); + nsoption_set_colour(sys_colour_ThreeDDarkShadow, 0x00555555); + nsoption_set_colour(sys_colour_ThreeDFace, 0x00dddddd); + nsoption_set_colour(sys_colour_ThreeDHighlight, 0x00aaaaaa); + nsoption_set_colour(sys_colour_ThreeDLightShadow, 0x00999999); + nsoption_set_colour(sys_colour_ThreeDShadow, 0x00777777); + nsoption_set_colour(sys_colour_Window, 0x00aaaaaa); + nsoption_set_colour(sys_colour_WindowFrame, 0x00000000); + nsoption_set_colour(sys_colour_WindowText, 0x00000000); + + return NSERROR_OK; +} + + +/** + * Ensures output logging stream is correctly configured + */ +static bool nslog_stream_configure(FILE *fptr) +{ + /* set log stream to be non-buffering */ + setbuf(fptr, NULL); + + return true; +} + +static void framebuffer_run(void) +{ + nsfb_event_t event; + int timeout; /* timeout in miliseconds */ + + while (fb_complete != true) { + /* run the scheduler and discover how long to wait for + * the next event. + */ + timeout = schedule_run(); + + /* if redraws are pending do not wait for event, + * return immediately + */ + if (fbtk_get_redraw_pending(fbtk)) + timeout = 0; + + if (fbtk_event(fbtk, &event, timeout)) { + if ((event.type == NSFB_EVENT_CONTROL) && + (event.value.controlcode == NSFB_CONTROL_QUIT)) + fb_complete = true; + } + + fbtk_redraw(fbtk); + } +} + +static void gui_quit(void) +{ + LOG("gui_quit"); + + urldb_save_cookies(nsoption_charp(cookie_jar)); + + framebuffer_finalise(); +} + +/* called back when click in browser window */ +static int +fb_browser_window_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + struct browser_widget_s *bwidget = fbtk_get_userpw(widget); + browser_mouse_state mouse; + float scale = browser_window_get_scale(gw->bw); + int x = (cbi->x + bwidget->scrollx) / scale; + int y = (cbi->y + bwidget->scrolly) / scale; + uint64_t time_now; + static struct { + enum { CLICK_SINGLE, CLICK_DOUBLE, CLICK_TRIPLE } type; + uint64_t time; + } last_click; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN && + cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + LOG("browser window clicked at %d,%d", cbi->x, cbi->y); + + switch (cbi->event->type) { + case NSFB_EVENT_KEY_DOWN: + switch (cbi->event->value.keycode) { + case NSFB_KEY_MOUSE_1: + browser_window_mouse_click(gw->bw, + BROWSER_MOUSE_PRESS_1, x, y); + gui_drag.state = GUI_DRAG_PRESSED; + gui_drag.button = 1; + gui_drag.x = x; + gui_drag.y = y; + break; + + case NSFB_KEY_MOUSE_3: + browser_window_mouse_click(gw->bw, + BROWSER_MOUSE_PRESS_2, x, y); + gui_drag.state = GUI_DRAG_PRESSED; + gui_drag.button = 2; + gui_drag.x = x; + gui_drag.y = y; + break; + + case NSFB_KEY_MOUSE_4: + /* scroll up */ + if (browser_window_scroll_at_point(gw->bw, x, y, + 0, -100) == false) + widget_scroll_y(gw, -100, false); + break; + + case NSFB_KEY_MOUSE_5: + /* scroll down */ + if (browser_window_scroll_at_point(gw->bw, x, y, + 0, 100) == false) + widget_scroll_y(gw, 100, false); + break; + + default: + break; + + } + + break; + case NSFB_EVENT_KEY_UP: + + mouse = 0; + nsu_getmonotonic_ms(&time_now); + + switch (cbi->event->value.keycode) { + case NSFB_KEY_MOUSE_1: + if (gui_drag.state == GUI_DRAG_DRAG) { + /* End of a drag, rather than click */ + + if (gui_drag.grabbed_pointer) { + /* need to ungrab pointer */ + fbtk_tgrab_pointer(widget); + gui_drag.grabbed_pointer = false; + } + + gui_drag.state = GUI_DRAG_NONE; + + /* Tell core */ + browser_window_mouse_track(gw->bw, 0, x, y); + break; + } + /* This is a click; + * clear PRESSED state and pass to core */ + gui_drag.state = GUI_DRAG_NONE; + mouse = BROWSER_MOUSE_CLICK_1; + break; + + case NSFB_KEY_MOUSE_3: + if (gui_drag.state == GUI_DRAG_DRAG) { + /* End of a drag, rather than click */ + gui_drag.state = GUI_DRAG_NONE; + + if (gui_drag.grabbed_pointer) { + /* need to ungrab pointer */ + fbtk_tgrab_pointer(widget); + gui_drag.grabbed_pointer = false; + } + + /* Tell core */ + browser_window_mouse_track(gw->bw, 0, x, y); + break; + } + /* This is a click; + * clear PRESSED state and pass to core */ + gui_drag.state = GUI_DRAG_NONE; + mouse = BROWSER_MOUSE_CLICK_2; + break; + + default: + break; + + } + + /* Determine if it's a double or triple click, allowing + * 0.5 seconds (500ms) between clicks + */ + if ((time_now < (last_click.time + 500)) && + (cbi->event->value.keycode != NSFB_KEY_MOUSE_4) && + (cbi->event->value.keycode != NSFB_KEY_MOUSE_5)) { + if (last_click.type == CLICK_SINGLE) { + /* Set double click */ + mouse |= BROWSER_MOUSE_DOUBLE_CLICK; + last_click.type = CLICK_DOUBLE; + + } else if (last_click.type == CLICK_DOUBLE) { + /* Set triple click */ + mouse |= BROWSER_MOUSE_TRIPLE_CLICK; + last_click.type = CLICK_TRIPLE; + } else { + /* Set normal click */ + last_click.type = CLICK_SINGLE; + } + } else { + last_click.type = CLICK_SINGLE; + } + + if (mouse) { + browser_window_mouse_click(gw->bw, mouse, x, y); + } + + last_click.time = time_now; + + break; + default: + break; + + } + return 1; +} + +/* called back when movement in browser window */ +static int +fb_browser_window_move(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + browser_mouse_state mouse = 0; + struct gui_window *gw = cbi->context; + struct browser_widget_s *bwidget = fbtk_get_userpw(widget); + float scale = browser_window_get_scale(gw->bw); + int x = (cbi->x + bwidget->scrollx) / scale; + int y = (cbi->y + bwidget->scrolly) / scale; + + if (gui_drag.state == GUI_DRAG_PRESSED && + (abs(x - gui_drag.x) > 5 || + abs(y - gui_drag.y) > 5)) { + /* Drag started */ + if (gui_drag.button == 1) { + browser_window_mouse_click(gw->bw, + BROWSER_MOUSE_DRAG_1, + gui_drag.x, gui_drag.y); + } else { + browser_window_mouse_click(gw->bw, + BROWSER_MOUSE_DRAG_2, + gui_drag.x, gui_drag.y); + } + gui_drag.grabbed_pointer = fbtk_tgrab_pointer(widget); + gui_drag.state = GUI_DRAG_DRAG; + } + + if (gui_drag.state == GUI_DRAG_DRAG) { + /* set up mouse state */ + mouse |= BROWSER_MOUSE_DRAG_ON; + + if (gui_drag.button == 1) + mouse |= BROWSER_MOUSE_HOLDING_1; + else + mouse |= BROWSER_MOUSE_HOLDING_2; + } + + browser_window_mouse_track(gw->bw, mouse, x, y); + + return 0; +} + + +static int +fb_browser_window_input(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + static fbtk_modifier_type modifier = FBTK_MOD_CLEAR; + int ucs4 = -1; + + LOG("got value %d", cbi->event->value.keycode); + + switch (cbi->event->type) { + case NSFB_EVENT_KEY_DOWN: + switch (cbi->event->value.keycode) { + + case NSFB_KEY_DELETE: + browser_window_key_press(gw->bw, NS_KEY_DELETE_RIGHT); + break; + + case NSFB_KEY_PAGEUP: + if (browser_window_key_press(gw->bw, + NS_KEY_PAGE_UP) == false) + widget_scroll_y(gw, -fbtk_get_height( + gw->browser), false); + break; + + case NSFB_KEY_PAGEDOWN: + if (browser_window_key_press(gw->bw, + NS_KEY_PAGE_DOWN) == false) + widget_scroll_y(gw, fbtk_get_height( + gw->browser), false); + break; + + case NSFB_KEY_RIGHT: + if (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL) { + /* CTRL held */ + if (browser_window_key_press(gw->bw, + NS_KEY_LINE_END) == false) + widget_scroll_x(gw, INT_MAX, true); + + } else if (modifier & FBTK_MOD_RSHIFT || + modifier & FBTK_MOD_LSHIFT) { + /* SHIFT held */ + if (browser_window_key_press(gw->bw, + NS_KEY_WORD_RIGHT) == false) + widget_scroll_x(gw, fbtk_get_width( + gw->browser), false); + + } else { + /* no modifier */ + if (browser_window_key_press(gw->bw, + NS_KEY_RIGHT) == false) + widget_scroll_x(gw, 100, false); + } + break; + + case NSFB_KEY_LEFT: + if (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL) { + /* CTRL held */ + if (browser_window_key_press(gw->bw, + NS_KEY_LINE_START) == false) + widget_scroll_x(gw, 0, true); + + } else if (modifier & FBTK_MOD_RSHIFT || + modifier & FBTK_MOD_LSHIFT) { + /* SHIFT held */ + if (browser_window_key_press(gw->bw, + NS_KEY_WORD_LEFT) == false) + widget_scroll_x(gw, -fbtk_get_width( + gw->browser), false); + + } else { + /* no modifier */ + if (browser_window_key_press(gw->bw, + NS_KEY_LEFT) == false) + widget_scroll_x(gw, -100, false); + } + break; + + case NSFB_KEY_UP: + if (browser_window_key_press(gw->bw, + NS_KEY_UP) == false) + widget_scroll_y(gw, -100, false); + break; + + case NSFB_KEY_DOWN: + if (browser_window_key_press(gw->bw, + NS_KEY_DOWN) == false) + widget_scroll_y(gw, 100, false); + break; + + case NSFB_KEY_RSHIFT: + modifier |= FBTK_MOD_RSHIFT; + break; + + case NSFB_KEY_LSHIFT: + modifier |= FBTK_MOD_LSHIFT; + break; + + case NSFB_KEY_RCTRL: + modifier |= FBTK_MOD_RCTRL; + break; + + case NSFB_KEY_LCTRL: + modifier |= FBTK_MOD_LCTRL; + break; + + case NSFB_KEY_y: + case NSFB_KEY_z: + if (cbi->event->value.keycode == NSFB_KEY_z && + (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL) && + (modifier & FBTK_MOD_RSHIFT || + modifier & FBTK_MOD_LSHIFT)) { + /* Z pressed with CTRL and SHIFT held */ + browser_window_key_press(gw->bw, NS_KEY_REDO); + break; + + } else if (cbi->event->value.keycode == NSFB_KEY_z && + (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL)) { + /* Z pressed with CTRL held */ + browser_window_key_press(gw->bw, NS_KEY_UNDO); + break; + + } else if (cbi->event->value.keycode == NSFB_KEY_y && + (modifier & FBTK_MOD_RCTRL || + modifier & FBTK_MOD_LCTRL)) { + /* Y pressed with CTRL held */ + browser_window_key_press(gw->bw, NS_KEY_REDO); + break; + } + /* Z or Y pressed but not undo or redo; + * Fall through to default handling */ + + default: + ucs4 = fbtk_keycode_to_ucs4(cbi->event->value.keycode, + modifier); + if (ucs4 != -1) + browser_window_key_press(gw->bw, ucs4); + break; + } + break; + + case NSFB_EVENT_KEY_UP: + switch (cbi->event->value.keycode) { + case NSFB_KEY_RSHIFT: + modifier &= ~FBTK_MOD_RSHIFT; + break; + + case NSFB_KEY_LSHIFT: + modifier &= ~FBTK_MOD_LSHIFT; + break; + + case NSFB_KEY_RCTRL: + modifier &= ~FBTK_MOD_RCTRL; + break; + + case NSFB_KEY_LCTRL: + modifier &= ~FBTK_MOD_LCTRL; + break; + + default: + break; + } + break; + + default: + break; + } + + return 0; +} + +static void +fb_update_back_forward(struct gui_window *gw) +{ + struct browser_window *bw = gw->bw; + + fbtk_set_bitmap(gw->back, + (browser_window_back_available(bw)) ? + &left_arrow : &left_arrow_g); + fbtk_set_bitmap(gw->forward, + (browser_window_forward_available(bw)) ? + &right_arrow : &right_arrow_g); +} + +/* left icon click routine */ +static int +fb_leftarrow_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + struct browser_window *bw = gw->bw; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + if (browser_window_back_available(bw)) + browser_window_history_back(bw, false); + + fb_update_back_forward(gw); + + return 1; +} + +/* right arrow icon click routine */ +static int +fb_rightarrow_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + struct browser_window *bw = gw->bw; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + if (browser_window_forward_available(bw)) + browser_window_history_forward(bw, false); + + fb_update_back_forward(gw); + return 1; + +} + +/* reload icon click routine */ +static int +fb_reload_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct browser_window *bw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + browser_window_reload(bw, true); + return 1; +} + +/* stop icon click routine */ +static int +fb_stop_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct browser_window *bw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + browser_window_stop(bw); + return 0; +} + +static int +fb_osk_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + map_osk(); + + return 0; +} + +/* close browser window icon click routine */ +static int +fb_close_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + fb_complete = true; + + return 0; +} + +static int +fb_scroll_callback(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + + switch (cbi->type) { + case FBTK_CBT_SCROLLY: + widget_scroll_y(gw, cbi->y, true); + break; + + case FBTK_CBT_SCROLLX: + widget_scroll_x(gw, cbi->x, true); + break; + + default: + break; + } + return 0; +} + +static int +fb_url_enter(void *pw, char *text) +{ + struct browser_window *bw = pw; + nsurl *url; + nserror error; + + error = nsurl_create(text, &url); + if (error != NSERROR_OK) { + fb_warn_user(messages_get_errorcode(error), 0); + } else { + browser_window_navigate(bw, url, NULL, BW_NAVIGATE_HISTORY, + NULL, NULL, NULL); + nsurl_unref(url); + } + + return 0; +} + +static int +fb_url_move(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + framebuffer_set_cursor(&caret_image); + return 0; +} + +static int +set_ptr_default_move(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + framebuffer_set_cursor(&pointer_image); + return 0; +} + +static int +fb_localhistory_btn_clik(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + struct gui_window *gw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + fb_localhistory_map(gw->localhistory); + + return 0; +} + + +/** Create a toolbar window and populate it with buttons. + * + * The toolbar layout uses a character to define buttons type and position: + * b - back + * l - local history + * f - forward + * s - stop + * r - refresh + * u - url bar expands to fit remaining space + * t - throbber/activity indicator + * c - close the current window + * + * The default layout is "blfsrut" there should be no more than a + * single url bar entry or behaviour will be undefined. + * + * @param gw Parent window + * @param toolbar_height The height in pixels of the toolbar + * @param padding The padding in pixels round each element of the toolbar + * @param frame_col Frame colour. + * @param toolbar_layout A string defining which buttons and controls + * should be added to the toolbar. May be empty + * string to disable the bar.. + * + */ +static fbtk_widget_t * +create_toolbar(struct gui_window *gw, + int toolbar_height, + int padding, + colour frame_col, + const char *toolbar_layout) +{ + fbtk_widget_t *toolbar; + fbtk_widget_t *widget; + + int xpos; /* The position of the next widget. */ + int xlhs = 0; /* extent of the left hand side widgets */ + int xdir = 1; /* the direction of movement + or - 1 */ + const char *itmtype; /* type of the next item */ + + if (toolbar_layout == NULL) { + toolbar_layout = NSFB_TOOLBAR_DEFAULT_LAYOUT; + } + + LOG("Using toolbar layout %s", toolbar_layout); + + itmtype = toolbar_layout; + + /* check for the toolbar being disabled */ + if ((*itmtype == 0) || (*itmtype == 'q')) { + return NULL; + } + + toolbar = fbtk_create_window(gw->window, 0, 0, 0, + toolbar_height, + frame_col); + + if (toolbar == NULL) { + return NULL; + } + + fbtk_set_handler(toolbar, + FBTK_CBT_POINTERENTER, + set_ptr_default_move, + NULL); + + + xpos = padding; + + /* loop proceeds creating widget on the left hand side until + * it runs out of layout or encounters a url bar declaration + * wherupon it works backwards from the end of the layout + * untill the space left is for the url bar + */ + while ((itmtype >= toolbar_layout) && + (*itmtype != 0) && + (xdir !=0)) { + + LOG("toolbar adding %c", *itmtype); + + + switch (*itmtype) { + + case 'b': /* back */ + widget = fbtk_create_button(toolbar, + (xdir == 1) ? xpos : + xpos - left_arrow.width, + padding, + left_arrow.width, + -padding, + frame_col, + &left_arrow, + fb_leftarrow_click, + gw); + gw->back = widget; /* keep reference */ + break; + + case 'l': /* local history */ + widget = fbtk_create_button(toolbar, + (xdir == 1) ? xpos : + xpos - history_image.width, + padding, + history_image.width, + -padding, + frame_col, + &history_image, + fb_localhistory_btn_clik, + gw); + gw->history = widget; + break; + + case 'f': /* forward */ + widget = fbtk_create_button(toolbar, + (xdir == 1)?xpos : + xpos - right_arrow.width, + padding, + right_arrow.width, + -padding, + frame_col, + &right_arrow, + fb_rightarrow_click, + gw); + gw->forward = widget; + break; + + case 'c': /* close the current window */ + widget = fbtk_create_button(toolbar, + (xdir == 1)?xpos : + xpos - stop_image_g.width, + padding, + stop_image_g.width, + -padding, + frame_col, + &stop_image_g, + fb_close_click, + gw->bw); + gw->close = widget; + break; + + case 's': /* stop */ + widget = fbtk_create_button(toolbar, + (xdir == 1)?xpos : + xpos - stop_image.width, + padding, + stop_image.width, + -padding, + frame_col, + &stop_image, + fb_stop_click, + gw->bw); + gw->stop = widget; + break; + + case 'r': /* reload */ + widget = fbtk_create_button(toolbar, + (xdir == 1)?xpos : + xpos - reload.width, + padding, + reload.width, + -padding, + frame_col, + &reload, + fb_reload_click, + gw->bw); + gw->reload = widget; + break; + + case 't': /* throbber/activity indicator */ + widget = fbtk_create_bitmap(toolbar, + (xdir == 1)?xpos : + xpos - throbber0.width, + padding, + throbber0.width, + -padding, + frame_col, + &throbber0); + gw->throbber = widget; + break; + + + case 'u': /* url bar*/ + if (xdir == -1) { + /* met the u going backwards add url + * now we know available extent + */ + + widget = fbtk_create_writable_text(toolbar, + xlhs, + padding, + xpos - xlhs, + -padding, + FB_COLOUR_WHITE, + FB_COLOUR_BLACK, + true, + fb_url_enter, + gw->bw); + + fbtk_set_handler(widget, + FBTK_CBT_POINTERENTER, + fb_url_move, gw->bw); + + gw->url = widget; /* keep reference */ + + /* toolbar is complete */ + xdir = 0; + break; + } + /* met url going forwards, note position and + * reverse direction + */ + itmtype = toolbar_layout + strlen(toolbar_layout); + xdir = -1; + xlhs = xpos; + xpos = (2 * fbtk_get_width(toolbar)); + widget = toolbar; + break; + + default: + widget = NULL; + xdir = 0; + LOG("Unknown element %c in toolbar layout", *itmtype); + break; + + } + + if (widget != NULL) { + xpos += (xdir * (fbtk_get_width(widget) + padding)); + } + + LOG("xpos is %d", xpos); + + itmtype += xdir; + } + + fbtk_set_mapping(toolbar, true); + + return toolbar; +} + + +/** Resize a toolbar. + * + * @param gw Parent window + * @param toolbar_height The height in pixels of the toolbar + * @param padding The padding in pixels round each element of the toolbar + * @param toolbar_layout A string defining which buttons and controls + * should be added to the toolbar. May be empty + * string to disable the bar. + */ +static void +resize_toolbar(struct gui_window *gw, + int toolbar_height, + int padding, + const char *toolbar_layout) +{ + fbtk_widget_t *widget; + + int xpos; /* The position of the next widget. */ + int xlhs = 0; /* extent of the left hand side widgets */ + int xdir = 1; /* the direction of movement + or - 1 */ + const char *itmtype; /* type of the next item */ + int x = 0, y = 0, w = 0, h = 0; + + if (gw->toolbar == NULL) { + return; + } + + if (toolbar_layout == NULL) { + toolbar_layout = NSFB_TOOLBAR_DEFAULT_LAYOUT; + } + + itmtype = toolbar_layout; + + if (*itmtype == 0) { + return; + } + + fbtk_set_pos_and_size(gw->toolbar, 0, 0, 0, toolbar_height); + + xpos = padding; + + /* loop proceeds creating widget on the left hand side until + * it runs out of layout or encounters a url bar declaration + * wherupon it works backwards from the end of the layout + * untill the space left is for the url bar + */ + while (itmtype >= toolbar_layout && xdir != 0) { + + switch (*itmtype) { + case 'b': /* back */ + widget = gw->back; + x = (xdir == 1) ? xpos : xpos - left_arrow.width; + y = padding; + w = left_arrow.width; + h = -padding; + break; + + case 'l': /* local history */ + widget = gw->history; + x = (xdir == 1) ? xpos : xpos - history_image.width; + y = padding; + w = history_image.width; + h = -padding; + break; + + case 'f': /* forward */ + widget = gw->forward; + x = (xdir == 1) ? xpos : xpos - right_arrow.width; + y = padding; + w = right_arrow.width; + h = -padding; + break; + + case 'c': /* close the current window */ + widget = gw->close; + x = (xdir == 1) ? xpos : xpos - stop_image_g.width; + y = padding; + w = stop_image_g.width; + h = -padding; + break; + + case 's': /* stop */ + widget = gw->stop; + x = (xdir == 1) ? xpos : xpos - stop_image.width; + y = padding; + w = stop_image.width; + h = -padding; + break; + + case 'r': /* reload */ + widget = gw->reload; + x = (xdir == 1) ? xpos : xpos - reload.width; + y = padding; + w = reload.width; + h = -padding; + break; + + case 't': /* throbber/activity indicator */ + widget = gw->throbber; + x = (xdir == 1) ? xpos : xpos - throbber0.width; + y = padding; + w = throbber0.width; + h = -padding; + break; + + + case 'u': /* url bar*/ + if (xdir == -1) { + /* met the u going backwards add url + * now we know available extent + */ + widget = gw->url; + x = xlhs; + y = padding; + w = xpos - xlhs; + h = -padding; + + /* toolbar is complete */ + xdir = 0; + break; + } + /* met url going forwards, note position and + * reverse direction + */ + itmtype = toolbar_layout + strlen(toolbar_layout); + xdir = -1; + xlhs = xpos; + w = fbtk_get_width(gw->toolbar); + xpos = 2 * w; + widget = gw->toolbar; + break; + + default: + widget = NULL; + break; + + } + + if (widget != NULL) { + if (widget != gw->toolbar) + fbtk_set_pos_and_size(widget, x, y, w, h); + xpos += xdir * (w + padding); + } + + itmtype += xdir; + } +} + +/** Routine called when "stripped of focus" event occours for browser widget. + * + * @param widget The widget reciving "stripped of focus" event. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +fb_browser_window_strip_focus(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + fbtk_set_caret(widget, false, 0, 0, 0, NULL); + + return 0; +} + +static void +create_browser_widget(struct gui_window *gw, int toolbar_height, int furniture_width) +{ + struct browser_widget_s *browser_widget; + browser_widget = calloc(1, sizeof(struct browser_widget_s)); + + gw->browser = fbtk_create_user(gw->window, + 0, + toolbar_height, + -furniture_width, + -furniture_width, + browser_widget); + + fbtk_set_handler(gw->browser, FBTK_CBT_REDRAW, fb_browser_window_redraw, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_DESTROY, fb_browser_window_destroy, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_INPUT, fb_browser_window_input, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_CLICK, fb_browser_window_click, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_STRIP_FOCUS, fb_browser_window_strip_focus, gw); + fbtk_set_handler(gw->browser, FBTK_CBT_POINTERMOVE, fb_browser_window_move, gw); +} + +static void +resize_browser_widget(struct gui_window *gw, int x, int y, + int width, int height) +{ + fbtk_set_pos_and_size(gw->browser, x, y, width, height); + browser_window_reformat(gw->bw, false, width, height); +} + +static void +create_normal_browser_window(struct gui_window *gw, int furniture_width) +{ + fbtk_widget_t *widget; + fbtk_widget_t *toolbar; + int statusbar_width = 0; + int toolbar_height = nsoption_int(fb_toolbar_size); + + LOG("Normal window"); + + gw->window = fbtk_create_window(fbtk, 0, 0, 0, 0, 0); + + statusbar_width = nsoption_int(toolbar_status_size) * + fbtk_get_width(gw->window) / 10000; + + /* toolbar */ + toolbar = create_toolbar(gw, + toolbar_height, + 2, + FB_FRAME_COLOUR, + nsoption_charp(fb_toolbar_layout)); + gw->toolbar = toolbar; + + /* set the actually created toolbar height */ + if (toolbar != NULL) { + toolbar_height = fbtk_get_height(toolbar); + } else { + toolbar_height = 0; + } + + /* status bar */ + gw->status = fbtk_create_text(gw->window, + 0, + fbtk_get_height(gw->window) - furniture_width, + statusbar_width, furniture_width, + FB_FRAME_COLOUR, FB_COLOUR_BLACK, + false); + fbtk_set_handler(gw->status, FBTK_CBT_POINTERENTER, set_ptr_default_move, NULL); + + LOG("status bar %p at %d,%d", gw->status, fbtk_get_absx(gw->status), fbtk_get_absy(gw->status)); + + /* create horizontal scrollbar */ + gw->hscroll = fbtk_create_hscroll(gw->window, + statusbar_width, + fbtk_get_height(gw->window) - furniture_width, + fbtk_get_width(gw->window) - statusbar_width - furniture_width, + furniture_width, + FB_SCROLL_COLOUR, + FB_FRAME_COLOUR, + fb_scroll_callback, + gw); + + /* fill bottom right area */ + + if (nsoption_bool(fb_osk) == true) { + widget = fbtk_create_text_button(gw->window, + fbtk_get_width(gw->window) - furniture_width, + fbtk_get_height(gw->window) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR, FB_COLOUR_BLACK, + fb_osk_click, + NULL); + widget = fbtk_create_button(gw->window, + fbtk_get_width(gw->window) - furniture_width, + fbtk_get_height(gw->window) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR, + &osk_image, + fb_osk_click, + NULL); + } else { + widget = fbtk_create_fill(gw->window, + fbtk_get_width(gw->window) - furniture_width, + fbtk_get_height(gw->window) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR); + + fbtk_set_handler(widget, FBTK_CBT_POINTERENTER, set_ptr_default_move, NULL); + } + + gw->bottom_right = widget; + + /* create vertical scrollbar */ + gw->vscroll = fbtk_create_vscroll(gw->window, + fbtk_get_width(gw->window) - furniture_width, + toolbar_height, + furniture_width, + fbtk_get_height(gw->window) - toolbar_height - furniture_width, + FB_SCROLL_COLOUR, + FB_FRAME_COLOUR, + fb_scroll_callback, + gw); + + /* browser widget */ + create_browser_widget(gw, toolbar_height, nsoption_int(fb_furniture_size)); + + /* Give browser_window's user widget input focus */ + fbtk_set_focus(gw->browser); +} + +static void +resize_normal_browser_window(struct gui_window *gw, int furniture_width) +{ + bool resized; + int width, height; + int statusbar_width; + int toolbar_height = fbtk_get_height(gw->toolbar); + + /* Resize the main window widget */ + resized = fbtk_set_pos_and_size(gw->window, 0, 0, 0, 0); + if (!resized) + return; + + width = fbtk_get_width(gw->window); + height = fbtk_get_height(gw->window); + statusbar_width = nsoption_int(toolbar_status_size) * width / 10000; + + resize_toolbar(gw, toolbar_height, 2, + nsoption_charp(fb_toolbar_layout)); + fbtk_set_pos_and_size(gw->status, + 0, height - furniture_width, + statusbar_width, furniture_width); + fbtk_reposition_hscroll(gw->hscroll, + statusbar_width, height - furniture_width, + width - statusbar_width - furniture_width, + furniture_width); + fbtk_set_pos_and_size(gw->bottom_right, + width - furniture_width, height - furniture_width, + furniture_width, furniture_width); + fbtk_reposition_vscroll(gw->vscroll, + width - furniture_width, + toolbar_height, furniture_width, + height - toolbar_height - furniture_width); + resize_browser_widget(gw, + 0, toolbar_height, + width - furniture_width, + height - furniture_width - toolbar_height); +} + +static void gui_window_add_to_window_list(struct gui_window *gw) +{ + gw->next = NULL; + gw->prev = NULL; + + if (window_list == NULL) { + window_list = gw; + } else { + window_list->prev = gw; + gw->next = window_list; + window_list = gw; + } +} + +static void gui_window_remove_from_window_list(struct gui_window *gw) +{ + struct gui_window *list; + + for (list = window_list; list != NULL; list = list->next) { + if (list != gw) + continue; + + if (list == window_list) { + window_list = list->next; + if (window_list != NULL) + window_list->prev = NULL; + } else { + list->prev->next = list->next; + if (list->next != NULL) { + list->next->prev = list->prev; + } + } + break; + } +} + + +static struct gui_window * +gui_window_create(struct browser_window *bw, + struct gui_window *existing, + gui_window_create_flags flags) +{ + struct gui_window *gw; + + gw = calloc(1, sizeof(struct gui_window)); + + if (gw == NULL) + return NULL; + + /* associate the gui window with the underlying browser window + */ + gw->bw = bw; + + create_normal_browser_window(gw, nsoption_int(fb_furniture_size)); + gw->localhistory = fb_create_localhistory(bw, fbtk, nsoption_int(fb_furniture_size)); + + /* map and request redraw of gui window */ + fbtk_set_mapping(gw->window, true); + + /* Add it to the window list */ + gui_window_add_to_window_list(gw); + + return gw; +} + +static void +gui_window_destroy(struct gui_window *gw) +{ + gui_window_remove_from_window_list(gw); + + fbtk_destroy_widget(gw->window); + + free(gw); +} + +static void +gui_window_redraw_window(struct gui_window *g) +{ + fb_queue_redraw(g->browser, 0, 0, fbtk_get_width(g->browser), fbtk_get_height(g->browser) ); +} + +static void +gui_window_update_box(struct gui_window *g, const struct rect *rect) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser); + fb_queue_redraw(g->browser, + rect->x0 - bwidget->scrollx, + rect->y0 - bwidget->scrolly, + rect->x1 - bwidget->scrollx, + rect->y1 - bwidget->scrolly); +} + +static bool +gui_window_get_scroll(struct gui_window *g, int *sx, int *sy) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser); + float scale = browser_window_get_scale(g->bw); + + *sx = bwidget->scrollx / scale; + *sy = bwidget->scrolly / scale; + + return true; +} + +static void +gui_window_set_scroll(struct gui_window *gw, int sx, int sy) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(gw->browser); + float scale = browser_window_get_scale(gw->bw); + + assert(bwidget); + + widget_scroll_x(gw, sx * scale, true); + widget_scroll_y(gw, sy * scale, true); +} + + +static void +gui_window_get_dimensions(struct gui_window *g, + int *width, + int *height, + bool scaled) +{ + float scale = browser_window_get_scale(g->bw); + + *width = fbtk_get_width(g->browser); + *height = fbtk_get_height(g->browser); + + if (scaled) { + *width /= scale; + *height /= scale; + } +} + +static void +gui_window_update_extent(struct gui_window *gw) +{ + int w, h; + browser_window_get_extents(gw->bw, true, &w, &h); + + fbtk_set_scroll_parameters(gw->hscroll, 0, w, + fbtk_get_width(gw->browser), 100); + + fbtk_set_scroll_parameters(gw->vscroll, 0, h, + fbtk_get_height(gw->browser), 100); +} + +static void +gui_window_set_status(struct gui_window *g, const char *text) +{ + fbtk_set_text(g->status, text); +} + +static void +gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape) +{ + switch (shape) { + case GUI_POINTER_POINT: + framebuffer_set_cursor(&hand_image); + break; + + case GUI_POINTER_CARET: + framebuffer_set_cursor(&caret_image); + break; + + case GUI_POINTER_MENU: + framebuffer_set_cursor(&menu_image); + break; + + case GUI_POINTER_PROGRESS: + framebuffer_set_cursor(&progress_image); + break; + + case GUI_POINTER_MOVE: + framebuffer_set_cursor(&move_image); + break; + + default: + framebuffer_set_cursor(&pointer_image); + break; + } +} + +static nserror +gui_window_set_url(struct gui_window *g, nsurl *url) +{ + fbtk_set_text(g->url, nsurl_access(url)); + return NSERROR_OK; +} + +static void +throbber_advance(void *pw) +{ + struct gui_window *g = pw; + struct fbtk_bitmap *image; + + switch (g->throbber_index) { + case 0: + image = &throbber1; + g->throbber_index = 1; + break; + + case 1: + image = &throbber2; + g->throbber_index = 2; + break; + + case 2: + image = &throbber3; + g->throbber_index = 3; + break; + + case 3: + image = &throbber4; + g->throbber_index = 4; + break; + + case 4: + image = &throbber5; + g->throbber_index = 5; + break; + + case 5: + image = &throbber6; + g->throbber_index = 6; + break; + + case 6: + image = &throbber7; + g->throbber_index = 7; + break; + + case 7: + image = &throbber8; + g->throbber_index = 0; + break; + + default: + return; + } + + if (g->throbber_index >= 0) { + fbtk_set_bitmap(g->throbber, image); + framebuffer_schedule(100, throbber_advance, g); + } +} + +static void +gui_window_start_throbber(struct gui_window *g) +{ + g->throbber_index = 0; + framebuffer_schedule(100, throbber_advance, g); +} + +static void +gui_window_stop_throbber(struct gui_window *gw) +{ + gw->throbber_index = -1; + fbtk_set_bitmap(gw->throbber, &throbber0); + + fb_update_back_forward(gw); + +} + +static void +gui_window_remove_caret_cb(fbtk_widget_t *widget) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(widget); + int c_x, c_y, c_h; + + if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) { + /* browser window already had caret: + * redraw its area to remove it first */ + fb_queue_redraw(widget, + c_x - bwidget->scrollx, + c_y - bwidget->scrolly, + c_x + 1 - bwidget->scrollx, + c_y + c_h - bwidget->scrolly); + } +} + +static void +gui_window_place_caret(struct gui_window *g, int x, int y, int height, + const struct rect *clip) +{ + struct browser_widget_s *bwidget = fbtk_get_userpw(g->browser); + + /* set new pos */ + fbtk_set_caret(g->browser, true, x, y, height, + gui_window_remove_caret_cb); + + /* redraw new caret pos */ + fb_queue_redraw(g->browser, + x - bwidget->scrollx, + y - bwidget->scrolly, + x + 1 - bwidget->scrollx, + y + height - bwidget->scrolly); +} + +static void +gui_window_remove_caret(struct gui_window *g) +{ + int c_x, c_y, c_h; + + if (fbtk_get_caret(g->browser, &c_x, &c_y, &c_h)) { + /* browser window owns the caret, so can remove it */ + fbtk_set_caret(g->browser, false, 0, 0, 0, NULL); + } +} + +static void framebuffer_window_reformat(struct gui_window *gw) +{ + /** @todo if we ever do zooming reformat should be implemented */ + LOG("window:%p", gw); + + /* + browser_window_reformat(gw->bw, false, width, height); + */ +} + +static struct gui_window_table framebuffer_window_table = { + .create = gui_window_create, + .destroy = gui_window_destroy, + .redraw = gui_window_redraw_window, + .update = gui_window_update_box, + .get_scroll = gui_window_get_scroll, + .set_scroll = gui_window_set_scroll, + .get_dimensions = gui_window_get_dimensions, + .update_extent = gui_window_update_extent, + .reformat = framebuffer_window_reformat, + + .set_url = gui_window_set_url, + .set_status = gui_window_set_status, + .set_pointer = gui_window_set_pointer, + .place_caret = gui_window_place_caret, + .remove_caret = gui_window_remove_caret, + .start_throbber = gui_window_start_throbber, + .stop_throbber = gui_window_stop_throbber, +}; + + +static struct gui_misc_table framebuffer_misc_table = { + .schedule = framebuffer_schedule, + .warning = fb_warn_user, + + .quit = gui_quit, +}; + +/** Entry point from OS. + * + * /param argc The number of arguments in the string vector. + * /param argv The argument string vector. + * /return The return code to the OS + */ +int +main(int argc, char** argv) +{ + struct browser_window *bw; + char *options; + char *messages; + nsurl *url; + nserror ret; + nsfb_t *nsfb; + struct netsurf_table framebuffer_table = { + .misc = &framebuffer_misc_table, + .window = &framebuffer_window_table, + .clipboard = framebuffer_clipboard_table, + .fetch = framebuffer_fetch_table, + .utf8 = framebuffer_utf8_table, + .bitmap = framebuffer_bitmap_table, + .layout = framebuffer_layout_table, + }; + + ret = netsurf_register(&framebuffer_table); + if (ret != NSERROR_OK) { + die("NetSurf operation table failed registration"); + } + + respaths = fb_init_resource(NETSURF_FB_RESPATH":"NETSURF_FB_FONTPATH); + + /* initialise logging. Not fatal if it fails but not much we + * can do about it either. + */ + nslog_init(nslog_stream_configure, &argc, argv); + + /* user options setup */ + ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default); + if (ret != NSERROR_OK) { + die("Options failed to initialise"); + } + options = filepath_find(respaths, "Choices"); + nsoption_read(options, nsoptions); + free(options); + nsoption_commandline(&argc, argv, nsoptions); + + /* message init */ + messages = filepath_find(respaths, "Messages"); + ret = messages_add_from_file(messages); + free(messages); + if (ret != NSERROR_OK) { + fprintf(stderr, "Message translations failed to load\n"); + } + + /* common initialisation */ + ret = netsurf_init(NULL); + if (ret != NSERROR_OK) { + die("NetSurf failed to initialise"); + } + + /* Override, since we have no support for non-core SELECT menu */ + nsoption_set_bool(core_select_menu, true); + + if (process_cmdline(argc,argv) != true) + die("unable to process command line.\n"); + + nsfb = framebuffer_initialise(fename, fewidth, feheight, febpp); + if (nsfb == NULL) + die("Unable to initialise framebuffer"); + + framebuffer_set_cursor(&pointer_image); + + if (fb_font_init() == false) + die("Unable to initialise the font system"); + + fbtk = fbtk_init(nsfb); + + fbtk_enable_oskb(fbtk); + + urldb_load_cookies(nsoption_charp(cookie_file)); + + /* create an initial browser window */ + + LOG("calling browser_window_create"); + + ret = nsurl_create(feurl, &url); + if (ret == NSERROR_OK) { + ret = browser_window_create(BW_CREATE_HISTORY, + url, + NULL, + NULL, + &bw); + nsurl_unref(url); + } + if (ret != NSERROR_OK) { + fb_warn_user(messages_get_errorcode(ret), 0); + } else { + framebuffer_run(); + + browser_window_destroy(bw); + } + + netsurf_exit(); + + if (fb_font_finalise() == false) + LOG("Font finalisation failed."); + + /* finalise options */ + nsoption_finalise(nsoptions, nsoptions_default); + + return 0; +} + +void gui_resize(fbtk_widget_t *root, int width, int height) +{ + struct gui_window *gw; + nsfb_t *nsfb = fbtk_get_nsfb(root); + + /* Enforce a minimum */ + if (width < 300) + width = 300; + if (height < 200) + height = 200; + + if (framebuffer_resize(nsfb, width, height, febpp) == false) { + return; + } + + fbtk_set_pos_and_size(root, 0, 0, width, height); + + fewidth = width; + feheight = height; + + for (gw = window_list; gw != NULL; gw = gw->next) { + resize_normal_browser_window(gw, + nsoption_int(fb_furniture_size)); + } + + fbtk_request_redraw(root); +} + + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ -- cgit v1.2.3