summaryrefslogtreecommitdiff
path: root/frontends/kolibrios/fb/gui.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/kolibrios/fb/gui.c')
-rw-r--r--frontends/kolibrios/fb/gui.c2226
1 files changed, 2226 insertions, 0 deletions
diff --git a/frontends/kolibrios/fb/gui.c b/frontends/kolibrios/fb/gui.c
new file mode 100644
index 000000000..4d4c7334f
--- /dev/null
+++ b/frontends/kolibrios/fb/gui.c
@@ -0,0 +1,2226 @@
+/*
+ * Copyright 2008, 2014 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 <limits.h>
+#include <getopt.h>
+#include <assert.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <nsutils/time.h>
+
+#include <libnsfb.h>
+#include <libnsfb_plot.h>
+#include <libnsfb_event.h>
+
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "utils/filepath.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "netsurf/browser_window.h"
+#include "netsurf/keypress.h"
+#include "desktop/browser_history.h"
+#include "netsurf/plotters.h"
+#include "netsurf/window.h"
+#include "netsurf/misc.h"
+#include "netsurf/netsurf.h"
+#include "netsurf/cookie_db.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] warning 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:
+ */