/* * Copyright 2021 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 . */ /** * \file * Implementation of netsurf window (widget) for fltk. */ #include #include #include #include #include #include #include #include extern "C" { #include "utils/errors.h" #include "utils/nsoption.h" #include "utils/nsurl.h" #include "netsurf/types.h" #include "netsurf/mouse.h" #include "netsurf/window.h" #include "netsurf/plotters.h" #include "netsurf/content.h" #include "netsurf/browser_window.h" #include "netsurf/mouse.h" #include "desktop/browser_history.h" } #include "fltk/window.h" #include "fltk/plotters.h" extern bool nsfltk_done; struct gui_window { class NS_Window *window; }; class NS_Widget : public Fl_Widget { private: struct browser_window *m_bw; int m_xoffset; int m_yoffset; protected: void draw(); int handle(int event); public: NS_Widget(int X,int Y,int W,int H, struct browser_window *bw) : Fl_Widget(X,Y,W,H), m_bw(bw), m_xoffset(0), m_yoffset(0) {} bool get_scroll(int *sx, int *sy); nserror get_dimensions(int *width, int *height); nserror invalidate(const struct rect *rect); void vscroll_callback(Fl_Scrollbar *w); void hscroll_callback(Fl_Scrollbar *w); }; /** * handle events on the netsurf browsing widget */ int NS_Widget::handle(int event) { int state = BROWSER_MOUSE_HOVER; int button; switch (event) { case FL_PUSH: button = Fl::event_button(); if (button == FL_LEFT_MOUSE) { state |= BROWSER_MOUSE_PRESS_1; } browser_window_mouse_click(m_bw, (browser_mouse_state)state, Fl::event_x() - x() + m_xoffset, Fl::event_y() - y() + m_yoffset); return 1; case FL_RELEASE: button = Fl::event_button(); if (button == FL_LEFT_MOUSE) { state |= BROWSER_MOUSE_CLICK_1; } browser_window_mouse_click(m_bw, (browser_mouse_state)state, Fl::event_x() - x() + m_xoffset, Fl::event_y() - y() + m_yoffset); return 1; default: return Fl_Widget::handle(event); } } /** * redraw the netsurf browsing widget */ void NS_Widget::draw() { struct rect clip; fl_clip_box(x(), y(), w(), h(), clip.x0, clip.y0, clip.x1, clip.y1); /* clip box generates width/height so convert to absolute */ clip.x1 += clip.x0; clip.y1 += clip.y0; struct redraw_context ctx = { .interactive = true, .background_images = true, .plot = &nsfltk_plotters, .priv = NULL, }; browser_window_redraw(m_bw, x() - m_xoffset, y() - m_yoffset, &clip, &ctx); } /** * vertical scrollbar position has been changed */ void NS_Widget::vscroll_callback(Fl_Scrollbar *sb) { m_yoffset = sb->value(); damage(FL_DAMAGE_SCROLL); } /** * horizontal scrollbar position has been changed */ void NS_Widget::hscroll_callback(Fl_Scrollbar *sb) { m_xoffset = sb->value(); damage(FL_DAMAGE_SCROLL); } /** * get the current scroll offsets */ bool NS_Widget::get_scroll(int *sx, int *sy) { *sx = m_xoffset; *sy = m_yoffset; return true; } /** * get the viewable dimensions of browsing context */ nserror NS_Widget::get_dimensions(int *width, int *height) { *width = w(); *height = h(); return NSERROR_OK; } /** * mark an area of the browsing context as invalid */ nserror NS_Widget::invalidate(const struct rect *rect) { if (rect == NULL) { damage(FL_DAMAGE_ALL); } else { damage(FL_DAMAGE_ALL, rect->x0, rect->y0, rect->x1 - rect->x0, rect->y1 - rect->y0); } return NSERROR_OK; } /** * widget representing url bar */ class NS_URLBar : public Fl_Pack { private: struct browser_window *m_bw; Fl_Button *m_back_button; Fl_Button *m_forward_button; Fl_Input *m_input; void back_callback(Fl_Button *button); void forward_callback(Fl_Button *button); public: NS_URLBar(int X,int Y,int W,int H, struct browser_window *bw); nserror set_url(struct nsurl *url); /* static wrapper for fltk callbacks */ static void static_back_callback(Fl_Widget *w, void *f); static void static_forward_callback(Fl_Widget *w, void *f); }; NS_URLBar::NS_URLBar(int X,int Y,int W,int H, struct browser_window *bw) : Fl_Pack(X,Y,W,H), m_bw(bw) { type(Fl_Pack::HORIZONTAL); spacing(4); m_back_button = new Fl_Button(0,0,H,H, "B"); m_back_button->callback(static_back_callback, (void *)this); m_forward_button = new Fl_Button(0,0,H,H, "F"); m_forward_button->callback(static_forward_callback, (void *)this); m_input = new Fl_Input(0,0,W,H); end(); resizable(m_input); } nserror NS_URLBar::set_url(struct nsurl *url) { size_t idn_url_l; char *idn_url_s = NULL; if (nsurl_get_utf8(url, &idn_url_s, &idn_url_l) == NSERROR_OK) { m_input->value(idn_url_s, idn_url_l-1); free(idn_url_s); } else { m_input->value(nsurl_access(url)); } return NSERROR_OK; } void NS_URLBar::back_callback(Fl_Button *button) { browser_window_history_back(m_bw, false); } void NS_URLBar::forward_callback(Fl_Button *button) { browser_window_history_forward(m_bw, false); } void NS_URLBar::static_back_callback(Fl_Widget *w, void *f) { ((NS_URLBar *)f)->back_callback((Fl_Button *)w); } void NS_URLBar::static_forward_callback(Fl_Widget *w, void *f) { ((NS_URLBar *)f)->forward_callback((Fl_Button *)w); } /** * Class for netsurf window */ class NS_Window : public Fl_Double_Window { private: struct browser_window *m_bw; NS_URLBar *m_nsurlbar; NS_Widget *m_nswidget; Fl_Scrollbar *m_vscrollbar; Fl_Scrollbar *m_hscrollbar; Fl_Output *m_status; nserror set_scroll(const struct rect *rect); void set_status(const char *text); void set_title(const char *title); nserror set_extent(int w, int h); void close_callback(Fl_Widget *w); /* static wrapper for fltk callbacks */ static void static_close_callback(Fl_Widget *w, void *f); static void static_hscroll_callback(Fl_Widget *w, void *f); static void static_vscroll_callback(Fl_Widget *w, void *f); public: NS_Window(int W, int H, struct browser_window *bw); ~NS_Window() { nsfltk_done=true; } /* static wrappers to be able to call instance methods */ static nserror static_set_scroll(struct gui_window *gw, const struct rect *rect); static void static_set_status(struct gui_window *gw, const char *text); static void static_set_title(struct gui_window *gw, const char *title); static bool static_get_scroll(struct gui_window *gw, int *sx, int *sy); static nserror static_set_url(struct gui_window *gw, struct nsurl *url); static nserror static_event(struct gui_window *gw, enum gui_window_event event); static nserror static_invalidate(struct gui_window *gw, const struct rect *rect); static nserror static_get_dimensions(struct gui_window *gw, int *width, int *height); static struct gui_window *static_create(struct browser_window *bw, struct gui_window *existing, gui_window_create_flags flags); static void static_destroy(struct gui_window *gw); }; /** * netsurf window class constructor */ NS_Window::NS_Window(int W, int H, struct browser_window *bw) : Fl_Double_Window(W,H), m_bw(bw) { int scrlsize = Fl::scrollbar_size(); int splitx = (nsoption_int(toolbar_status_size) * W) / 10000; int urlbarh = scrlsize * 2; callback(static_close_callback, (void *)this); // url bar m_nsurlbar = new NS_URLBar(0, 0, W, urlbarh, bw); // browser drawing canvas widget m_nswidget = new NS_Widget(0, m_nsurlbar->h(), W - scrlsize, H - scrlsize - urlbarh, bw); // vertical scrollbar m_vscrollbar = new Fl_Scrollbar(m_nswidget->w(), m_nsurlbar->h(), scrlsize, m_nswidget->h()); m_vscrollbar->callback(static_vscroll_callback, (void *)m_nswidget); m_vscrollbar->value(0, m_nswidget->h(), 0, m_nswidget->h()); // horizontal scrollbar m_hscrollbar = new Fl_Scrollbar(splitx, m_nswidget->y() + m_nswidget->h(), m_nswidget->w() - splitx, scrlsize); m_hscrollbar->type(FL_HORIZONTAL); m_hscrollbar->callback(static_hscroll_callback, (void *)m_nswidget); m_hscrollbar->value(0, m_nswidget->w(), 0, m_nswidget->w()); // status text m_status = new Fl_Output(0, m_hscrollbar->y(), splitx, scrlsize); end(); resizable(m_nswidget); } /** * fltk window has been closed */ void NS_Window::close_callback(Fl_Widget *w) { browser_window_destroy(m_bw); } /** * set the status text */ void NS_Window::set_status(const char *text) { m_status->value(text); } /** * set the window title text */ void NS_Window::set_title(const char *text) { label(text); } /** * set the current position of the scroll bars */ nserror NS_Window::set_scroll(const struct rect *rect) { m_vscrollbar->value(rect->y0); m_hscrollbar->value(rect->x0); m_nswidget->vscroll_callback(m_vscrollbar); m_nswidget->hscroll_callback(m_hscrollbar); return NSERROR_OK; } /** * Set the extent of the underlying canvas */ nserror NS_Window::set_extent(int ew, int eh) { m_hscrollbar->value(0, w(), 0, ew); m_vscrollbar->value(0, h(), 0, eh); return NSERROR_OK; } /* static methods */ /** * static window close fltk callback which calls the instance */ void NS_Window::static_close_callback(Fl_Widget *w, void *f) { ((NS_Window *)f)->close_callback(w); } /** * static vertical scrollbar fltk callback which calls the instance */ void NS_Window::static_vscroll_callback(Fl_Widget *w, void *f) { ((NS_Widget *)f)->vscroll_callback((Fl_Scrollbar *)w); } /** * static horizontal scrollbar fltk callback which calls the instance */ void NS_Window::static_hscroll_callback(Fl_Widget *w, void *f) { ((NS_Widget *)f)->hscroll_callback((Fl_Scrollbar *)w); } /** * Set the status bar message of a browser window. * * \param g gui_window to update * \param text new status text */ void NS_Window::static_set_status(struct gui_window *gw, const char *text) { ((NS_Window *)gw->window)->set_status(text); } /** * Set the title of a window. * * \param gw The gui window to set title of. * \param title new window title */ void NS_Window::static_set_title(struct gui_window *gw, const char *title) { ((NS_Window *)gw->window)->set_title(title); } /** * Get the scroll position of a browser window. * * \param gw The gui window to obtain the scroll position from. * \param sx receives x ordinate of point at top-left of window * \param sy receives y ordinate of point at top-left of window * \return true iff successful */ bool NS_Window::static_get_scroll(struct gui_window *gw, int *sx, int *sy) { return gw->window->m_nswidget->get_scroll(sx, sy); } /** * Set the scroll position of a browser window. * * scrolls the viewport to ensure the specified rectangle of the * content is shown. * If the rectangle is of zero size i.e. x0 == x1 and y0 == y1 * the contents will be scrolled so the specified point in the * content is at the top of the viewport. * If the size of the rectangle is non zero the frontend may * add padding or centre the defined area or it may simply * align as in the zero size rectangle * * \param gw The gui window to scroll. * \param rect The rectangle to ensure is shown. * \return NSERROR_OK on success or appropriate error code. */ nserror NS_Window::static_set_scroll(struct gui_window *gw, const struct rect *rect) { return gw->window->set_scroll(rect); } /** * Set the navigation url. * * \param gw window to update. * \param url The url to use as icon. */ nserror NS_Window::static_set_url(struct gui_window *gw, struct nsurl *url) { return gw->window->m_nsurlbar->set_url(url); } /** * Miscellaneous event occurred for a window * * This is used to inform the frontend of window events which * require no additional parameters. * * \param gw The gui window the event occurred for * \param event Which event has occurred. * \return NSERROR_OK if the event was processed else error code. */ nserror NS_Window::static_event(struct gui_window *gw, enum gui_window_event event) { nserror res; switch (event) { case GW_EVENT_UPDATE_EXTENT: { int w, h; res = browser_window_get_extents(gw->window->m_bw, true, &w, &h); if (res == NSERROR_OK) { res = (gw->window)->set_extent(w, h); } break; } default: res = NSERROR_OK; break; } return res; } /** * Invalidate an area of a window. * * The specified area of the window should now be considered * out of date. If the area is NULL the entire window must be * invalidated. It is expected that the windowing system will * then subsequently cause redraw/expose operations as * necessary. * * \note the frontend should not attempt to actually start the * redraw operations as a result of this callback because the * core redraw functions may already be threaded. * * \param gw The gui window to invalidate. * \param rect area to redraw or NULL for the entire window area * \return NSERROR_OK on success or appropriate error code */ nserror NS_Window::static_invalidate(struct gui_window *gw, const struct rect *rect) { return gw->window->m_nswidget->invalidate(rect); } /** * Find the current dimensions of a browser window's content area. * * This is used to determine the actual available drawing size * in pixels. This allows contents that can be dynamically * reformatted, such as HTML, to better use the available * space. * * \param gw The gui window to measure content area of. * \param width receives width of window * \param height receives height of window * \return NSERROR_OK on success and width and height updated * else error code. */ nserror NS_Window::static_get_dimensions(struct gui_window *gw, int *width, int *height) { return gw->window->m_nswidget->get_dimensions(width, height); } /** * Create and open a gui window for a browsing context. * * The implementing front end must create a context suitable * for it to display a window referred to as the "gui window". * * The frontend will be expected to request the core redraw * areas of the gui window which have become invalidated * either from toolkit expose events or as a result of a * invalidate() call. * * Most core operations used by the frontend concerning browser * windows require passing the browser window context therefor * the gui window must include a reference to the browser * window passed here. * * If GW_CREATE_CLONE flag is set existing is non-NULL. * * \param bw The core browsing context associated with the gui window * \param existing An existing gui_window, may be NULL. * \param flags flags to control the gui window creation. * \return gui window, or NULL on error. */ struct gui_window * NS_Window::static_create(struct browser_window *bw, struct gui_window *existing, gui_window_create_flags flags) { struct gui_window *gw; gw = (struct gui_window *)calloc(1, sizeof(struct gui_window)); if (gw != NULL) { gw->window = new NS_Window(800,600, bw); gw->window->show(); } return gw; } /** * Destroy previously created gui window * * \param gw The gui window to destroy. */ void NS_Window::static_destroy(struct gui_window *gw) { Fl::delete_widget(gw->window); free(gw); } /** * window operations table for fltk frontend */ static struct gui_window_table window_table = { .create = NS_Window::static_create, .destroy = NS_Window::static_destroy, .invalidate = NS_Window::static_invalidate, .get_scroll = NS_Window::static_get_scroll, .set_scroll = NS_Window::static_set_scroll, .get_dimensions = NS_Window::static_get_dimensions, .event = NS_Window::static_event, .set_title = NS_Window::static_set_title, .set_url = NS_Window::static_set_url, .set_icon = NULL, .set_status = NS_Window::static_set_status, .set_pointer = NULL, .place_caret = NULL, .drag_start = NULL, .save_link = NULL, .create_form_select_menu = NULL, .file_gadget_open = NULL, .drag_save_object = NULL, .drag_save_selection = NULL, .console_log = NULL, }; struct gui_window_table *nsfltk_window_table = &window_table;