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/fbtk/bitmap.c | 136 ++++++ frontends/framebuffer/fbtk/event.c | 349 +++++++++++++++ frontends/framebuffer/fbtk/fbtk.c | 830 ++++++++++++++++++++++++++++++++++++ frontends/framebuffer/fbtk/fill.c | 81 ++++ frontends/framebuffer/fbtk/osk.c | 199 +++++++++ frontends/framebuffer/fbtk/scroll.c | 589 +++++++++++++++++++++++++ frontends/framebuffer/fbtk/text.c | 640 +++++++++++++++++++++++++++ frontends/framebuffer/fbtk/user.c | 64 +++ frontends/framebuffer/fbtk/widget.h | 259 +++++++++++ frontends/framebuffer/fbtk/window.c | 91 ++++ 10 files changed, 3238 insertions(+) create mode 100644 frontends/framebuffer/fbtk/bitmap.c create mode 100644 frontends/framebuffer/fbtk/event.c create mode 100644 frontends/framebuffer/fbtk/fbtk.c create mode 100644 frontends/framebuffer/fbtk/fill.c create mode 100644 frontends/framebuffer/fbtk/osk.c create mode 100644 frontends/framebuffer/fbtk/scroll.c create mode 100644 frontends/framebuffer/fbtk/text.c create mode 100644 frontends/framebuffer/fbtk/user.c create mode 100644 frontends/framebuffer/fbtk/widget.h create mode 100644 frontends/framebuffer/fbtk/window.c (limited to 'frontends/framebuffer/fbtk') diff --git a/frontends/framebuffer/fbtk/bitmap.c b/frontends/framebuffer/fbtk/bitmap.c new file mode 100644 index 000000000..1f147be00 --- /dev/null +++ b/frontends/framebuffer/fbtk/bitmap.c @@ -0,0 +1,136 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit bitmaped image widget + * + * 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 "desktop/browser.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +static int +fb_redraw_bitmap(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + nsfb_t *nsfb; + + nsfb = fbtk_get_nsfb(widget); + + fbtk_get_bbox(widget, &bbox); + + rect = bbox; + + nsfb_claim(nsfb, &bbox); + + /* clear background */ + if ((widget->bg & 0xFF000000) != 0) { + /* transparent polygon filling isnt working so fake it */ + nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg); + } + + /* plot the image */ + nsfb_plot_bitmap(nsfb, + &rect, + (nsfb_colour_t *)widget->u.bitmap.bitmap->pixdata, + widget->u.bitmap.bitmap->width, + widget->u.bitmap.bitmap->height, + widget->u.bitmap.bitmap->width, + !widget->u.bitmap.bitmap->opaque); + + nsfb_update(nsfb, &bbox); + + return 0; +} + +/* exported function documented in fbtk.h */ +void +fbtk_set_bitmap(fbtk_widget_t *widget, struct fbtk_bitmap *image) +{ + if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_BITMAP)) + return; + + widget->u.bitmap.bitmap = image; + + fbtk_request_redraw(widget); +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_bitmap(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour c, + struct fbtk_bitmap *image) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_BITMAP, x, y, width, height); + + neww->bg = c; + neww->mapped = true; + neww->u.bitmap.bitmap = image; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_bitmap, NULL); + + return neww; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_button(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour c, + struct fbtk_bitmap *image, + fbtk_callback click, + void *pw) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_BITMAP, x, y, width, height); + + neww->bg = c; + neww->mapped = true; + neww->u.bitmap.bitmap = image; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_bitmap, NULL); + fbtk_set_handler(neww, FBTK_CBT_CLICK, click, pw); + fbtk_set_handler(neww, FBTK_CBT_POINTERENTER, fbtk_set_ptr, &hand_image); + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/event.c b/frontends/framebuffer/fbtk/event.c new file mode 100644 index 000000000..c0894921e --- /dev/null +++ b/frontends/framebuffer/fbtk/event.c @@ -0,0 +1,349 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit event processing. + * + * 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 "utils/utils.h" +#include "utils/log.h" +#include "desktop/browser.h" +#include "desktop/textinput.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +/* exported function documented in fbtk.h */ +void +fbtk_input(fbtk_widget_t *root, nsfb_event_t *event) +{ + fbtk_widget_t *input; + + root = fbtk_get_root_widget(root); + + /* obtain widget with input focus */ + input = root->u.root.input; + if (input == NULL) { + LOG("No widget has input focus."); + return; /* no widget with input */ + } + + fbtk_post_callback(input, FBTK_CBT_INPUT, event); +} + +/* exported function documented in fbtk.h */ +void +fbtk_click(fbtk_widget_t *widget, nsfb_event_t *event) +{ + fbtk_widget_t *root; + fbtk_widget_t *clicked; + nsfb_bbox_t cloc; + int x, y; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + nsfb_cursor_loc_get(root->u.root.fb, &cloc); + + clicked = fbtk_get_widget_at(root, cloc.x0, cloc.y0); + + if (clicked == NULL) + return; + + if (fbtk_get_handler(clicked, FBTK_CBT_INPUT) != NULL) { + fbtk_set_focus(clicked); + } + + x = fbtk_get_absx(clicked); + y = fbtk_get_absy(clicked); + + LOG("clicked %p at %d,%d", clicked, x, y); + + /* post the click */ + fbtk_post_callback(clicked, FBTK_CBT_CLICK, event, cloc.x0 - x, cloc.y0 - y); +} + +/* exported function documented in fbtk.h */ +bool +fbtk_tgrab_pointer(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + if (root->u.root.grabbed == widget) { + /* release pointer grab */ + root->u.root.grabbed = NULL; + return true; + } else if (root->u.root.grabbed == NULL) { + /* set pointer grab */ + root->u.root.grabbed = widget; + return true; + } + /* pointer was already grabbed */ + return false; +} + +/* exported function documented in fbtk.h */ +void +fbtk_warp_pointer(fbtk_widget_t *widget, int x, int y, bool relative) +{ + fbtk_widget_t *root; + fbtk_widget_t *moved; + nsfb_bbox_t cloc; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + if (relative) { + nsfb_cursor_loc_get(root->u.root.fb, &cloc); + cloc.x0 += x; + cloc.y0 += y; + } else { + cloc.x0 = x; + cloc.y0 = y; + } + + /* ensure cursor location lies within the root widget */ + if (cloc.x0 < root->x) + cloc.x0 = root->x; + if (cloc.x0 >= (root->x + root->width)) + cloc.x0 = (root->x + root->width) - 1; + if (cloc.y0 < root->y) + cloc.y0 = root->y; + if (cloc.y0 >= (root->y + root->height)) + cloc.y0 = (root->y + root->height) - 1; + + if (root->u.root.grabbed == NULL) { + /* update the pointer cursor */ + nsfb_cursor_loc_set(root->u.root.fb, &cloc); + + moved = fbtk_get_widget_at(root, cloc.x0, cloc.y0); + + x = fbtk_get_absx(moved); + y = fbtk_get_absy(moved); + + /* post enter and leaving messages */ + if (moved != root->u.root.prev) { + fbtk_post_callback(root->u.root.prev, FBTK_CBT_POINTERLEAVE); + root->u.root.prev = moved; + fbtk_post_callback(root->u.root.prev, FBTK_CBT_POINTERENTER); + } + } else { + /* pointer movement has been grabbed by a widget */ + moved = root->u.root.grabbed; + + /* ensure pointer remains within widget boundary */ + x = fbtk_get_absx(moved); + y = fbtk_get_absy(moved); + + if (cloc.x0 < x) + cloc.x0 = x; + if (cloc.y0 < y) + cloc.y0 = y; + if (cloc.x0 > (x + moved->width)) + cloc.x0 = (x + moved->width); + if (cloc.y0 > (y + moved->height)) + cloc.y0 = (y + moved->height); + + /* update the pointer cursor */ + nsfb_cursor_loc_set(root->u.root.fb, &cloc); + } + + /* post the movement */ + fbtk_post_callback(moved, FBTK_CBT_POINTERMOVE, cloc.x0 - x, cloc.y0 - y); + +} + +/* exported function documented in fbtk.h */ +bool +fbtk_event(fbtk_widget_t *root, nsfb_event_t *event, int timeout) +{ + nsfb_bbox_t cloc; + bool unused = false; /* is the event available */ + bool move_pointer = false; /* whether pointer move events occured */ + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(root); + + do { + if (nsfb_event(root->u.root.fb, event, timeout) == false) { + if (move_pointer) + fbtk_warp_pointer(root, cloc.x0, cloc.y0, + false); + return false; + } + + if (move_pointer && event->type != NSFB_EVENT_MOVE_RELATIVE && + event->type != NSFB_EVENT_MOVE_ABSOLUTE) { + /* Flush the movements */ + fbtk_warp_pointer(root, cloc.x0, cloc.y0, false); + + } else if (!move_pointer && + event->type == NSFB_EVENT_MOVE_RELATIVE) { + /* Get current pointer coords */ + nsfb_cursor_loc_get(root->u.root.fb, &cloc); + } + + switch (event->type) { + case NSFB_EVENT_KEY_DOWN: + case NSFB_EVENT_KEY_UP: + if ((event->value.keycode >= NSFB_KEY_MOUSE_1) && + (event->value.keycode <= NSFB_KEY_MOUSE_5)) { + fbtk_click(root, event); + } else { + fbtk_input(root, event); + } + break; + + case NSFB_EVENT_CONTROL: + unused = true; + break; + + case NSFB_EVENT_MOVE_RELATIVE: + /* Consecutive move events are consolidated into a + * single pointer warp */ + move_pointer = true; + cloc.x0 += event->value.vector.x; + cloc.y0 += event->value.vector.y; + timeout = 0; + break; + + case NSFB_EVENT_MOVE_ABSOLUTE: + /* Consecutive move events are consolidated into a + * single pointer warp */ + move_pointer = true; + cloc.x0 = event->value.vector.x; + cloc.y0 = event->value.vector.y; + timeout = 0; + break; + + case NSFB_EVENT_RESIZE: + /* Try to resize framebuffer */ + gui_resize(root, + event->value.resize.w, + event->value.resize.h); + break; + + default: + break; + } + } while (event->type == NSFB_EVENT_MOVE_RELATIVE || + event->type == NSFB_EVENT_MOVE_ABSOLUTE); + return unused; +} + +static int keymap[] = { + /* 0 1 2 3 4 5 6 7 8 9 */ + -1, -1, -1, -1, -1, -1, -1, -1, 8, 9, /* 0 - 9 */ + -1, -1, -1, 13, -1, -1, -1, -1, -1, -1, /* 10 - 19 */ + -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, /* 20 - 29 */ + -1, -1, ' ', '!', '"', '#', '$', -1, '&','\'', /* 30 - 39 */ + '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', /* 40 - 49 */ + '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', /* 50 - 59 */ + '<', '=', '>', '?', '@', -1, -1, -1, -1, -1, /* 60 - 69 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */ + -1, '[','\\', ']', '~', '_', '`', 'a', 'b', 'c', /* 90 - 99 */ + 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', /* 100 - 109 */ + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 110 - 119 */ + 'x', 'y', 'z', -1, -1, -1, -1, -1, -1, -1, /* 120 - 129 */ +}; + +static int sh_keymap[] = { + /* 0 1 2 3 4 5 6 7 8 9 */ + -1, -1, -1, -1, -1, -1, -1, -1, 8, 9, /* 0 - 9 */ + -1, -1, -1, 13, -1, -1, -1, -1, -1, -1, /* 10 - 19 */ + -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, /* 20 - 29 */ + -1, -1, ' ', '!', '"', '~', '$', -1, '&', '@', /* 30 - 39 */ + '(', ')', '*', '+', '<', '_', '>', '?', ')', '!', /* 40 - 49 */ + '"', 243, '$', '%', '^', '&', '*', '(', ';', ':', /* 50 - 59 */ + '<', '+', '>', '?', '@', -1, -1, -1, -1, -1, /* 60 - 69 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */ + -1, '{', '|', '}', '~', '_', 254, 'A', 'B', 'C', /* 90 - 99 */ + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', /* 100 - 109 */ + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 110 - 119 */ + 'X', 'Y', 'Z', -1, -1, -1, -1, -1, -1, -1, /* 120 - 129 */ +}; + + +/* exported function documented in fbtk.h */ +int +fbtk_keycode_to_ucs4(int code, fbtk_modifier_type mods) +{ + int ucs4 = -1; + + if (mods & FBTK_MOD_LSHIFT || mods & FBTK_MOD_RSHIFT) { + if ((code >= 0) && (code < (int) NOF_ELEMENTS(sh_keymap))) + ucs4 = sh_keymap[code]; + + } else if (mods == FBTK_MOD_CLEAR) { + if ((code >= 0) && (code < (int) NOF_ELEMENTS(keymap))) + ucs4 = keymap[code]; + + } else if (mods & FBTK_MOD_LCTRL || mods & FBTK_MOD_RCTRL) { + switch (code) { + case NSFB_KEY_a: + ucs4 = NS_KEY_SELECT_ALL; + break; + + case NSFB_KEY_c: + ucs4 = NS_KEY_COPY_SELECTION; + break; + + case NSFB_KEY_u: + ucs4 = NS_KEY_DELETE_LINE; + break; + + case NSFB_KEY_v: + ucs4 = NS_KEY_PASTE; + break; + + case NSFB_KEY_x: + ucs4 = NS_KEY_CUT_SELECTION; + break; + + case NSFB_KEY_z: + ucs4 = NS_KEY_CLEAR_SELECTION; + break; + default: + break; + } + } + return ucs4; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/fbtk.c b/frontends/framebuffer/fbtk/fbtk.c new file mode 100644 index 000000000..db1c1f1a9 --- /dev/null +++ b/frontends/framebuffer/fbtk/fbtk.c @@ -0,0 +1,830 @@ +/* + * Copyright 2008,2010 Vincent Sanders + * + * Framebuffer windowing toolkit core. + * + * 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 + +#include "utils/utils.h" +#include "utils/log.h" +#include "desktop/browser.h" +#include "desktop/plotters.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +#ifdef FBTK_LOGGING + +/* tree dump debug, also example of depth first tree walk */ +static void +dump_tk_tree(fbtk_widget_t *widget) +{ + widget = fbtk_get_root_widget(widget); + int indent = 0; + + while (widget != NULL) { + LOG("%*s%p", indent, "", widget); + if (widget->first_child != NULL) { + widget = widget->first_child; + indent += 6; + } else if (widget->next != NULL) { + widget = widget->next; + } else { + while ((widget->parent != NULL) && + (widget->parent->next == NULL)) { + widget = widget->parent; + indent -= 6; + } + if (widget->parent != NULL) { + indent -= 6; + widget = widget->parent->next; + } else { + widget = NULL; + } + } + } +} + +#endif + +/* exported function documented in fbtk.h */ +void +fbtk_request_redraw(fbtk_widget_t *widget) +{ + fbtk_widget_t *cwidget; + fbtk_widget_t *pwidget; + + assert(widget != NULL); + + /* if widget not mapped do not try to redraw it */ + pwidget = widget; + while (pwidget != NULL) { + if (pwidget->mapped == false) + return; + pwidget = pwidget->parent; + } + + widget->redraw.needed = true; + widget->redraw.x = 0; + widget->redraw.y = 0; + widget->redraw.width = widget->width; + widget->redraw.height = widget->height; + +#ifdef FBTK_LOGGING + LOG("redrawing %p %d,%d %d,%d", widget, widget->redraw.x, widget->redraw.y, widget->redraw.width, widget->redraw.height); +#endif + + cwidget = widget->last_child; + while (cwidget != NULL) { + fbtk_request_redraw(cwidget); + cwidget = cwidget->prev; + } + + while (widget->parent != NULL) { + widget = widget->parent; + widget->redraw.child = true; + } +} + + + +/* exported function documented in fbtk.h */ +int +fbtk_set_mapping(fbtk_widget_t *widget, bool map) +{ + LOG("setting mapping on %p to %d", widget, map); + widget->mapped = map; + if (map) { + fbtk_request_redraw(widget); + } else { + fbtk_request_redraw(widget->parent); + } + return 0; +} + +/** swap the widget given with the next sibling. + * + * Swap a sibling widget with the next deepest in the hierachy + */ +static void +swap_siblings(fbtk_widget_t *lw) +{ + fbtk_widget_t *rw = lw->next; /* the widget to swap lw with */ + fbtk_widget_t *before; + fbtk_widget_t *after; + + assert(rw != NULL); + + LOG("Swapping %p with %p", lw, rw); + before = lw->prev; + after = rw->next; + + if (before == NULL) { + /* left widget is currently the first child */ + lw->parent->first_child = rw; + } else { + before->next = rw; + } + rw->prev = before; + rw->next = lw; + + if (after == NULL) { + /* right widget is currently the last child */ + rw->parent->last_child = lw; + } else { + after->prev = lw; + } + lw->next = after; + lw->prev = rw; +} + + + +/* exported function documented in fbtk.h */ +int +fbtk_set_zorder(fbtk_widget_t *widget, int z) +{ + while (z != 0) { + if (z < 0) { + if (widget->prev == NULL) + break; /* cannot go any shallower */ + + /* swap with previous entry */ + swap_siblings(widget->prev); + + z++; + } else { + if (widget->next == NULL) + break; /* cannot go any deeper */ + + /* swap with subsequent entry */ + swap_siblings(widget); + + z--; + } + } + + return z; +} + + +/* exported function documented in fbtk.h */ +bool +fbtk_set_pos_and_size(fbtk_widget_t *widget, + int x, int y, + int width, int height) +{ + if (widget->parent != NULL) { + fbtk_widget_t *parent = widget->parent; + + /* make new window fit inside parent */ + if (width == 0) { + width = parent->width - x; + } else if (width < 0) { + width = parent->width + width - x; + } + if ((width + x) > parent->width) { + width = parent->width - x; + } + + if (height == 0) { + height = parent->height - y; + } else if (height < 0) { + height = parent->height + height - y; + } + if ((height + y) > parent->height) { + height = parent->height - y; + } + } + + if ((widget->x != x) || + (widget->y != y) || + (widget->width != width) || + (widget->height != height)) { + widget->x = x; + widget->y = y; + widget->width = width; + widget->height = height; + return true; + } + return false; +} + + +/* exported function docuemnted in fbtk.h */ +void +fbtk_set_caret(fbtk_widget_t *widget, bool set, + int x, int y, int height, + void (*remove_caret)(fbtk_widget_t *widget)) +{ + fbtk_widget_t *root; + + assert(widget != NULL); + root = fbtk_get_root_widget(widget); + + if (root->u.root.caret.owner != NULL && + root->u.root.caret.remove_cb != NULL) + root->u.root.caret.remove_cb(widget); + + if (set) { + assert(remove_caret != NULL); + + root->u.root.caret.owner = widget; + root->u.root.caret.x = x; + root->u.root.caret.y = y; + root->u.root.caret.height = height; + root->u.root.caret.remove_cb = remove_caret; + + } else { + root->u.root.caret.owner = NULL; + root->u.root.caret.remove_cb = NULL; + } +} + +/* exported function documented in fbtk.h */ +int +fbtk_destroy_widget(fbtk_widget_t *widget) +{ + fbtk_widget_t *parent; + int ret = 0; + + ret = fbtk_post_callback(widget, FBTK_CBT_DESTROY); + + while (widget->first_child != NULL) { + fbtk_destroy_widget(widget->first_child); + } + + parent = widget->parent; + if (parent != NULL) { + + /* unlink from siblings */ + if (widget->prev != NULL) { + widget->prev->next = widget->next; + } else { + /* must be the first widget, unlink from parent */ + parent->first_child = widget->next; + } + if (widget->next != NULL) { + widget->next->prev = widget->prev; + } else { + /* must be the last widget, unlink from parent */ + parent->last_child = widget->prev; + } + + free(widget); + } + + return ret; +} + +/* region coverage flags. */ +enum { + POINT_LEFTOF_REGION = 1, + POINT_RIGHTOF_REGION = 2, + POINT_ABOVE_REGION = 4, + POINT_BELOW_REGION = 8, +}; + +/* Computes where a point lies in respect to an area. */ +#define REGION(x,y,cx1,cx2,cy1,cy2) \ + (( (y) > (cy2) ? POINT_BELOW_REGION : 0) | \ + ( (y) < (cy1) ? POINT_ABOVE_REGION : 0) | \ + ( (x) > (cx2) ? POINT_RIGHTOF_REGION : 0) | \ + ( (x) < (cx1) ? POINT_LEFTOF_REGION : 0) ) + +/* swap two integers */ +#define SWAP(a, b) do { int t; t=(a); (a)=(b); (b)=t; } while(0) + +/* exported function documented in fbtk.h */ +bool +fbtk_clip_rect(const bbox_t * restrict clip, bbox_t * restrict box) +{ + uint8_t region1; + uint8_t region2; + + /* ensure co-ordinates are in ascending order */ + if (box->x1 < box->x0) + SWAP(box->x0, box->x1); + if (box->y1 < box->y0) + SWAP(box->y0, box->y1); + + region1 = REGION(box->x0, box->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + region2 = REGION(box->x1, box->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1); + + /* area lies entirely outside the clipping rectangle */ + if ((region1 | region2) && (region1 & region2)) + return false; + + if (box->x0 < clip->x0) + box->x0 = clip->x0; + if (box->x0 > clip->x1) + box->x0 = clip->x1; + + if (box->x1 < clip->x0) + box->x1 = clip->x0; + if (box->x1 > clip->x1) + box->x1 = clip->x1; + + if (box->y0 < clip->y0) + box->y0 = clip->y0; + if (box->y0 > clip->y1) + box->y0 = clip->y1; + + if (box->y1 < clip->y0) + box->y1 = clip->y0; + if (box->y1 > clip->y1) + box->y1 = clip->y1; + + return true; +} + +/* exported function documented in fbtk.h */ +bool +fbtk_clip_to_widget(fbtk_widget_t *widget, bbox_t * restrict box) +{ + bbox_t wbox; + wbox.x0 = 0; + wbox.y0 = 0; + wbox.x1 = widget->width; + wbox.y1 = widget->height; + return fbtk_clip_rect(&wbox, box); +} + + + +/* internally exported function documented in widget.h */ +int +fbtk_set_ptr(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + fbtk_widget_t *root = fbtk_get_root_widget(widget); + struct fbtk_bitmap *bm = cbi->context; + + nsfb_cursor_set(root->u.root.fb, + (nsfb_colour_t *)bm->pixdata, + bm->width, + bm->height, + bm->width, + bm->hot_x, + bm->hot_y); + + return 0; +} + + + +/* internally exported function documented in widget.h */ +fbtk_widget_t * +fbtk_get_root_widget(fbtk_widget_t *widget) +{ + while (widget->parent != NULL) + widget = widget->parent; + + /* check root widget was found */ + if (widget->type != FB_WIDGET_TYPE_ROOT) { + LOG("Widget with null parent that is not the root widget!"); + return NULL; + } + + return widget; +} + + +/* exported function documented in fbtk.h */ +int +fbtk_get_absx(fbtk_widget_t *widget) +{ + int x = widget->x; + + while (widget->parent != NULL) { + widget = widget->parent; + x += widget->x; + } + + return x; +} + +/* exported function documented in fbtk.h */ +int +fbtk_get_absy(fbtk_widget_t *widget) +{ + int y = widget->y; + + while (widget->parent != NULL) { + widget = widget->parent; + y += widget->y; + } + + return y; +} + +/* exported function documented in fbtk.h */ +int +fbtk_get_height(fbtk_widget_t *widget) +{ + return widget->height; +} + +/* exported function documented in fbtk.h */ +int +fbtk_get_width(fbtk_widget_t *widget) +{ + return widget->width; +} + +/* exported function documented in fbtk.h */ +bool +fbtk_get_bbox(fbtk_widget_t *widget, nsfb_bbox_t *bbox) +{ + bbox->x0 = widget->x; + bbox->y0 = widget->y; + bbox->x1 = widget->x + widget->width; + bbox->y1 = widget->y + widget->height; + + widget = widget->parent; + while (widget != NULL) { + bbox->x0 += widget->x; + bbox->y0 += widget->y; + bbox->x1 += widget->x; + bbox->y1 += widget->y; + widget = widget->parent; + } + + return true; +} + +bool +fbtk_get_caret(fbtk_widget_t *widget, int *x, int *y, int *height) +{ + fbtk_widget_t *root = fbtk_get_root_widget(widget); + + if (root->u.root.caret.owner == widget) { + *x = root->u.root.caret.x; + *y = root->u.root.caret.y; + *height = root->u.root.caret.height; + + return true; + + } else { + *x = 0; + *y = 0; + *height = 0; + + return false; + } +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_get_widget_at(fbtk_widget_t *nwid, int x, int y) +{ + fbtk_widget_t *widget = NULL; /* found widget */ + + /* require the root widget to start */ + nwid = fbtk_get_root_widget(nwid); + + while (nwid != NULL) { + if ((nwid->mapped) && + (x >= nwid->x) && + (y >= nwid->y) && + (x < (nwid->x + nwid->width)) && + (y < (nwid->y + nwid->height))) { + widget = nwid; + x -= nwid->x; + y -= nwid->y; + nwid = nwid->first_child; + } else { + nwid = nwid->next; + } + } + + return widget; +} + + + + +/* internally exported function documented in widget.h */ +fbtk_widget_t * +fbtk_widget_new(fbtk_widget_t *parent, + enum fbtk_widgettype_e type, + int x, + int y, + int width, + int height) +{ + fbtk_widget_t *neww; /* new widget */ + + if (parent == NULL) + return NULL; + + neww = calloc(1, sizeof(fbtk_widget_t)); + if (neww == NULL) + return NULL; + +#ifdef FBTK_LOGGING + LOG("creating %p %d,%d %d,%d", neww, x, y, width, height); +#endif + + /* make new window fit inside parent */ + if (width == 0) { + width = parent->width - x; + } else if (width < 0) { + width = parent->width + width - x; + } + if ((width + x) > parent->width) { + width = parent->width - x; + } + + if (height == 0) { + height = parent->height - y; + } else if (height < 0) { + height = parent->height + height - y; + } + if ((height + y) > parent->height) { + height = parent->height - y; + } + +#ifdef FBTK_LOGGING + LOG("using %p %d,%d %d,%d", neww, x, y, width, height); +#endif + /* set values */ + neww->type = type; + neww->x = x; + neww->y = y; + neww->width = width; + neww->height = height; + + /* insert into widget heiarchy */ + + neww->parent = parent; + + if (parent->first_child == NULL) { + /* no child widgets yet */ + parent->last_child = neww; + } else { + /* add new widget to front of sibling chain */ + neww->next = parent->first_child; + neww->next->prev = neww; + } + parent->first_child = neww; + + return neww; +} + +/* exported function documented in fbtk.h */ +bool +fbtk_get_redraw_pending(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + return root->redraw.needed | root->redraw.child; +} + +/** Perform a depth-first tree-walk, calling the redraw callback of the widgets in turn. + * + * This function makes no decisions of its own and simply walks the + * widget tree depth first calling widgets redraw callbacks if flagged + * to do so. + * The tree search is optimised with a flag to indicate wether the + * children of a node should be considered. + */ +static int +do_redraw(nsfb_t *nsfb, fbtk_widget_t *widget) +{ + nsfb_bbox_t plot_ctx; + fbtk_widget_t *cwidget; /* child widget */ + + /* check if the widget requires redrawing */ + if (widget->redraw.needed == true) { + plot_ctx.x0 = fbtk_get_absx(widget) + widget->redraw.x; + plot_ctx.y0 = fbtk_get_absy(widget) + widget->redraw.y; + plot_ctx.x1 = plot_ctx.x0 + widget->redraw.width; + plot_ctx.y1 = plot_ctx.y0 + widget->redraw.height; + +#ifdef FBTK_LOGGING + LOG("clipping %p %d,%d %d,%d", widget, plot_ctx.x0, plot_ctx.y0, plot_ctx.x1, plot_ctx.y1); +#endif + if (nsfb_plot_set_clip(nsfb, &plot_ctx) == true) { + fbtk_post_callback(widget, FBTK_CBT_REDRAW); + } + widget->redraw.needed = false; + } + + /* walk the widgets children if child flag is set */ + if (widget->redraw.child) { + cwidget = widget->last_child; + while (cwidget != NULL) { + do_redraw(nsfb, cwidget); + cwidget = cwidget->prev; + } + widget->redraw.child = false; + } + + return 1; +} + +/* exported function documented in fbtk.h */ +int +fbtk_redraw(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + return do_redraw(root->u.root.fb, root); +} + +/* exported function documented in fbtk.h */ +fbtk_callback +fbtk_get_handler(fbtk_widget_t *widget, fbtk_callback_type cbt) +{ + if ((cbt <= FBTK_CBT_START) || (cbt >= FBTK_CBT_END)) { + /* type out of range, no way to report error so return NULL */ + return NULL; + } + + return widget->callback[cbt]; +} + +/* exported function documented in fbtk.h */ +fbtk_callback +fbtk_set_handler(fbtk_widget_t *widget, + fbtk_callback_type cbt, + fbtk_callback cb, + void *context) +{ + fbtk_callback prevcb; + + if ((cbt <= FBTK_CBT_START) || (cbt >= FBTK_CBT_END)) { + /* type out of range, no way to report error so return NULL */ + return NULL; + } + + prevcb = widget->callback[cbt]; + + widget->callback[cbt] = cb; + widget->callback_context[cbt] = context; + + return prevcb; +} + +/* exported function docuemnted in fbtk.h */ +int +fbtk_post_callback(fbtk_widget_t *widget, fbtk_callback_type cbt, ...) +{ + fbtk_callback_info cbi; + int ret = 0; + va_list ap; + + if (widget == NULL) + return -1; + /* if the widget is not mapped do not attempt to post any + * events to it + */ + if (widget->mapped == false) + return ret; + + if (widget->callback[cbt] != NULL) { + cbi.type = cbt; + cbi.context = widget->callback_context[cbt]; + + va_start(ap, cbt); + + switch (cbt) { + case FBTK_CBT_SCROLLX: + cbi.x = va_arg(ap,int); + break; + + case FBTK_CBT_SCROLLY: + cbi.y = va_arg(ap,int); + break; + + case FBTK_CBT_CLICK: + cbi.event = va_arg(ap, void *); + cbi.x = va_arg(ap, int); + cbi.y = va_arg(ap, int); + break; + + case FBTK_CBT_INPUT: + cbi.event = va_arg(ap, void *); + break; + + case FBTK_CBT_POINTERMOVE: + cbi.x = va_arg(ap, int); + cbi.y = va_arg(ap, int); + break; + + case FBTK_CBT_REDRAW: + break; + + case FBTK_CBT_USER: + break; + + case FBTK_CBT_STRIP_FOCUS: + break; + + default: + break; + } + va_end(ap); + + ret = (widget->callback[cbt])(widget, &cbi); + } + + return ret; +} + +/* exported function docuemnted in fbtk.h */ +void +fbtk_set_focus(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + if (root->u.root.input != NULL && + root->u.root.input != widget) { + /* inform previous holder of focus that it's being stripped + * of focus */ + fbtk_post_callback(root->u.root.input, FBTK_CBT_STRIP_FOCUS); + } + + root->u.root.input = widget; +} + + + +/* exported function docuemnted in fbtk.h */ +nsfb_t * +fbtk_get_nsfb(fbtk_widget_t *widget) +{ + fbtk_widget_t *root; + + /* ensure we have the root widget */ + root = fbtk_get_root_widget(widget); + + return root->u.root.fb; +} + +/* exported function docuemnted in fbtk.h */ +fbtk_widget_t * +fbtk_init(nsfb_t *fb) +{ + fbtk_widget_t *root; + + /* create and configure root widget */ + root = calloc(1, sizeof(fbtk_widget_t)); + if (root == NULL) + return NULL; + + root->type = FB_WIDGET_TYPE_ROOT; + root->u.root.fb = fb; + root->u.root.caret.owner = NULL; + + nsfb_get_geometry(fb, &root->width, &root->height, NULL); + + root->mapped = true; + + return root; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/fill.c b/frontends/framebuffer/fbtk/fill.c new file mode 100644 index 000000000..07397b2df --- /dev/null +++ b/frontends/framebuffer/fbtk/fill.c @@ -0,0 +1,81 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit filled area widget + * + * 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 "desktop/browser.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" + +#include "widget.h" + +static int +fb_redraw_fill(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + nsfb_bbox_t bbox; + nsfb_t *nsfb; + + nsfb = fbtk_get_nsfb(widget); + + fbtk_get_bbox(widget, &bbox); + + nsfb_claim(nsfb, &bbox); + + /* clear background */ + if ((widget->bg & 0xFF000000) != 0) { + /* transparent polygon filling isnt working so fake it */ + nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg); + } + + nsfb_update(nsfb, &bbox); + + return 0; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_fill(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour c) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_FILL, x, y, width, height); + neww->bg = c; + neww->mapped = true; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_fill, NULL); + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/osk.c b/frontends/framebuffer/fbtk/osk.c new file mode 100644 index 000000000..1d57f157f --- /dev/null +++ b/frontends/framebuffer/fbtk/osk.c @@ -0,0 +1,199 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit on screen keyboard. + * + * 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 "utils/log.h" +#include "desktop/browser.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +struct kbd_button_s { + int x; + int y; + int w; + int h; + const char *t; + enum nsfb_key_code_e keycode; +}; + +#define KEYCOUNT 58 + +static struct kbd_button_s kbdbase[KEYCOUNT] = { + { 0, 0, 20, 15, "`", NSFB_KEY_BACKQUOTE}, + { 20, 0, 20, 15, "1", NSFB_KEY_1}, + { 40, 0, 20, 15, "2", NSFB_KEY_2}, + { 60, 0, 20, 15, "3", NSFB_KEY_3}, + { 80, 0, 20, 15, "4", NSFB_KEY_4}, + { 100, 0, 20, 15, "5", NSFB_KEY_5}, + { 120, 0, 20, 15, "6", NSFB_KEY_6}, + { 140, 0, 20, 15, "7", NSFB_KEY_7}, + { 160, 0, 20, 15, "8", NSFB_KEY_8}, + { 180, 0, 20, 15, "9", NSFB_KEY_9}, + { 200, 0, 20, 15, "0", NSFB_KEY_0}, + { 220, 0, 20, 15, "-", NSFB_KEY_MINUS}, + { 240, 0, 20, 15, "=", NSFB_KEY_EQUALS}, + { 260, 0, 40, 15, "\xe2\x8c\xab", NSFB_KEY_BACKSPACE}, + { 0, 15, 30, 15, "\xe2\x86\xb9", NSFB_KEY_TAB}, + { 30, 15, 20, 15, "q", NSFB_KEY_q}, + { 50, 15, 20, 15, "w", NSFB_KEY_w}, + { 70, 15, 20, 15, "e", NSFB_KEY_e}, + { 90, 15, 20, 15, "r", NSFB_KEY_r}, + { 110, 15, 20, 15, "t", NSFB_KEY_t}, + { 130, 15, 20, 15, "y", NSFB_KEY_y}, + { 150, 15, 20, 15, "u", NSFB_KEY_u}, + { 170, 15, 20, 15, "i", NSFB_KEY_i}, + { 190, 15, 20, 15, "o", NSFB_KEY_o}, + { 210, 15, 20, 15, "p", NSFB_KEY_p}, + { 230, 15, 20, 15, "[", NSFB_KEY_LEFTBRACKET}, + { 250, 15, 20, 15, "]", NSFB_KEY_RIGHTBRACKET}, + { 275, 15, 25, 30, "\xe2\x8f\x8e", NSFB_KEY_RETURN}, + { 35, 30, 20, 15, "a", NSFB_KEY_a}, + { 55, 30, 20, 15, "s", NSFB_KEY_s}, + { 75, 30, 20, 15, "d", NSFB_KEY_d}, + { 95, 30, 20, 15, "f", NSFB_KEY_f}, + { 115, 30, 20, 15, "g", NSFB_KEY_g}, + { 135, 30, 20, 15, "h", NSFB_KEY_h}, + { 155, 30, 20, 15, "j", NSFB_KEY_j}, + { 175, 30, 20, 15, "k", NSFB_KEY_k}, + { 195, 30, 20, 15, "l", NSFB_KEY_l}, + { 215, 30, 20, 15, ";", NSFB_KEY_SEMICOLON}, + { 235, 30, 20, 15, "'", NSFB_KEY_l}, + { 255, 30, 20, 15, "#", NSFB_KEY_HASH}, + { 0, 45, 25, 15, "\xe2\x87\xa7", NSFB_KEY_LSHIFT}, + { 25, 45, 20, 15, "\\", NSFB_KEY_SLASH}, + { 45, 45, 20, 15, "z", NSFB_KEY_z}, + { 65, 45, 20, 15, "x", NSFB_KEY_x}, + { 85, 45, 20, 15, "c", NSFB_KEY_c}, + { 105, 45, 20, 15, "v", NSFB_KEY_v}, + { 125, 45, 20, 15, "b", NSFB_KEY_b}, + { 145, 45, 20, 15, "n", NSFB_KEY_n}, + { 165, 45, 20, 15, "m", NSFB_KEY_m}, + { 185, 45, 20, 15, ",", NSFB_KEY_COMMA}, + { 205, 45, 20, 15, ".", NSFB_KEY_PERIOD}, + { 225, 45, 20, 15, "/", NSFB_KEY_BACKSLASH}, + { 245, 45, 55, 15, "\xe2\x87\xa7", NSFB_KEY_RSHIFT}, + { 40, 67, 185, 15, "", NSFB_KEY_SPACE}, + { 250, 60, 20, 15, "\xe2\x96\xb2", NSFB_KEY_UP}, + { 230, 67, 20, 15, "\xe2\x97\x80", NSFB_KEY_LEFT}, + { 270, 67, 20, 15, "\xe2\x96\xb6", NSFB_KEY_RIGHT}, + { 250, 75, 20, 15, "\xe2\x96\xbc", NSFB_KEY_DOWN}, +}; + +static fbtk_widget_t *osk; + +static int +osk_close(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + if (cbi->event->type != NSFB_EVENT_KEY_UP) + return 0; + + fbtk_set_mapping(osk, false); + + return 0; +} + +static int +osk_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + nsfb_event_t event; + struct kbd_button_s *kbd_button = cbi->context; + + event.type = cbi->event->type; + event.value.keycode = kbd_button->keycode; + fbtk_input(widget, &event); + + return 0; +} + +/* exported function documented in fbtk.h */ +void +fbtk_enable_oskb(fbtk_widget_t *fbtk) +{ + fbtk_widget_t *widget; + int kloop; + int maxx = 0; + int maxy = 0; + int ww; + int wh; + fbtk_widget_t *root = fbtk_get_root_widget(fbtk); + int furniture_width = 18; + + for (kloop=0; kloop < KEYCOUNT; kloop++) { + if ((kbdbase[kloop].x + kbdbase[kloop].w) > maxx) + maxx=kbdbase[kloop].x + kbdbase[kloop].w; + if ((kbdbase[kloop].y + kbdbase[kloop].h) > maxy) + maxy=kbdbase[kloop].y + kbdbase[kloop].h; + } + + ww = fbtk_get_width(root); + + /* scale window height apropriately */ + wh = (maxy * ww) / maxx; + + osk = fbtk_create_window(root, 0, fbtk_get_height(root) - wh, 0, wh, 0xff202020); + + for (kloop=0; kloop < KEYCOUNT; kloop++) { + widget = fbtk_create_text_button(osk, + (kbdbase[kloop].x * ww) / maxx, + (kbdbase[kloop].y * ww) / maxx, + (kbdbase[kloop].w * ww) / maxx, + (kbdbase[kloop].h *ww) / maxx, + FB_FRAME_COLOUR, + FB_COLOUR_BLACK, + osk_click, + &kbdbase[kloop]); + fbtk_set_text(widget, kbdbase[kloop].t); + } + + widget = fbtk_create_button(osk, + fbtk_get_width(osk) - furniture_width, + fbtk_get_height(osk) - furniture_width, + furniture_width, + furniture_width, + FB_FRAME_COLOUR, + &osk_image, + osk_close, + NULL); +} + +/* exported function documented in fbtk.h */ +void +map_osk(void) +{ + fbtk_set_zorder(osk, INT_MIN); + fbtk_set_mapping(osk, true); +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/scroll.c b/frontends/framebuffer/fbtk/scroll.c new file mode 100644 index 000000000..c86c8a6de --- /dev/null +++ b/frontends/framebuffer/fbtk/scroll.c @@ -0,0 +1,589 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit scrollbar widgets + * + * 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 "utils/log.h" +#include "desktop/browser.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +/* Vertical scroll widget */ + +static int +vscroll_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int vscroll; + int vpos; + + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + fbtk_widget_t *root = fbtk_get_root_widget(widget); + + fbtk_get_bbox(widget, &bbox); + + nsfb_claim(root->u.root.fb, &bbox); + + rect = bbox; + + /* background */ + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + + /* scroll well */ + rect.x0 = bbox.x0 + 2; + rect.y0 = bbox.y0 + 1; + rect.x1 = bbox.x1 - 3; + rect.y1 = bbox.y1 - 2; + + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg); + nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF999999, false, false); + + /* scroll bar */ + if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { + vscroll = ((widget->height - 4) * widget->u.scroll.thumb) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + vpos = ((widget->height - 4) * widget->u.scroll.position) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + } else { + vscroll = (widget->height - 4); + vpos = 0; + } + + rect.x0 = bbox.x0 + 5; + rect.y0 = bbox.y0 + 3 + vpos; + rect.x1 = bbox.x0 + widget->width - 5; + rect.y1 = bbox.y0 + vscroll + vpos; + + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + + nsfb_update(root->u.root.fb, &bbox); + + return 0; +} + +static int +vscroll_drag(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + newpos = ((widget->u.scroll.drag_position + + (cbi->y - widget->u.scroll.drag)) * + (widget->u.scroll.maximum - widget->u.scroll.minimum)) / + (widget->height - 4); + + if (newpos < scrollw->u.scroll.minimum) + newpos = scrollw->u.scroll.minimum; + + if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb )) + newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb); + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(widget, FBTK_CBT_SCROLLY, newpos); +} + +static int +vscrollu_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) + return 0; + + newpos = scrollw->u.scroll.position - scrollw->u.scroll.page; + if (newpos < scrollw->u.scroll.minimum) + newpos = scrollw->u.scroll.minimum; + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLY, newpos); +} + +static int +vscrolld_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) + return 0; + + newpos = scrollw->u.scroll.position + scrollw->u.scroll.page; + if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb )) + newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb); + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLY, newpos); +} + +static int +vscrollarea_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int vscroll; + int vpos; + int newpos; + int ret = 0; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) { + /* end all drags, just in case */ + if (fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, NULL, NULL) != NULL) + fbtk_tgrab_pointer(widget); + return 0; + } + + switch (cbi->event->value.keycode) { + + case NSFB_KEY_MOUSE_4: + /* scroll up */ + newpos = widget->u.scroll.position - widget->u.scroll.page; + if (newpos < widget->u.scroll.minimum) + newpos = widget->u.scroll.minimum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos); + break; + + case NSFB_KEY_MOUSE_5: + /* scroll down */ + newpos = widget->u.scroll.position + widget->u.scroll.page; + if (newpos > widget->u.scroll.maximum) + newpos = widget->u.scroll.maximum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos); + break; + + default: + + if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { + vscroll = ((widget->height - 4) * widget->u.scroll.thumb) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + vpos = ((widget->height - 4) * widget->u.scroll.position) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + } else { + vscroll = (widget->height - 4); + vpos = 0; + } + + if (cbi->y < vpos) { + /* above bar */ + newpos = widget->u.scroll.position - widget->u.scroll.thumb; + if (newpos < widget->u.scroll.minimum) + newpos = widget->u.scroll.minimum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos); + } else if (cbi->y > (vpos + vscroll)) { + /* below bar */ + newpos = widget->u.scroll.position + widget->u.scroll.thumb; + if (newpos > widget->u.scroll.maximum) + newpos = widget->u.scroll.maximum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLY, newpos); + } else { + /* on bar - start drag */ + widget->u.scroll.drag = cbi->y; + widget->u.scroll.drag_position = vpos; + fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, vscroll_drag, widget); + fbtk_tgrab_pointer(widget); + } + } + return ret; +} + + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_vscroll(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour fg, + colour bg, + fbtk_callback callback, + void *context) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, + FB_WIDGET_TYPE_VSCROLL, + x, + y + scrollu.height, + width, + height - scrollu.height - scrolld.height); + + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, vscroll_redraw, NULL); + + fbtk_set_handler(neww, FBTK_CBT_CLICK, vscrollarea_click, neww); + + fbtk_set_handler(neww, FBTK_CBT_SCROLLY, callback, context); + + neww->u.scroll.btnul = fbtk_create_button(parent, + x, + y, + width, + scrollu.height, + fg, + &scrollu, + vscrollu_click, + neww); + + neww->u.scroll.btndr = fbtk_create_button(parent, + x, + y + height - scrolld.height, + width, + scrolld.height, + fg, + &scrolld, + vscrolld_click, + neww); + + + return neww; +} + + +/* exported function documented in fbtk.h */ +void +fbtk_reposition_vscroll(fbtk_widget_t *vscroll, + int x, + int y, + int width, + int height) +{ + assert(vscroll->type == FB_WIDGET_TYPE_VSCROLL); + + fbtk_set_pos_and_size(vscroll, x, y + scrollu.height, + width, height - scrollu.height - scrolld.height); + fbtk_set_pos_and_size(vscroll->u.scroll.btnul, + x, y, width, scrollu.height); + fbtk_set_pos_and_size(vscroll->u.scroll.btndr, + x, y + height - scrolld.height, + width, scrolld.height); +} + +/* Horizontal scroll widget */ + +static int +hscroll_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int hscroll; + int hpos; + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + fbtk_widget_t *root = fbtk_get_root_widget(widget); + + fbtk_get_bbox(widget, &bbox); + + nsfb_claim(root->u.root.fb, &bbox); + + rect = bbox; + + /* background */ + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + + /* scroll well */ + rect.x0 = bbox.x0 + 1; + rect.y0 = bbox.y0 + 2; + rect.x1 = bbox.x1 - 2; + rect.y1 = bbox.y1 - 3; + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg); + + /* scroll well outline */ + nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF999999, false, false); + + if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { + hscroll = ((widget->width - 4) * widget->u.scroll.thumb) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + hpos = ((widget->width - 4) * widget->u.scroll.position) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + } else { + hscroll = (widget->width - 4); + hpos = 0; + } + + LOG("hscroll %d", hscroll); + + rect.x0 = bbox.x0 + 3 + hpos; + rect.y0 = bbox.y0 + 5; + rect.x1 = bbox.x0 + hscroll + hpos; + rect.y1 = bbox.y0 + widget->height - 5; + + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + + nsfb_update(root->u.root.fb, &bbox); + + return 0; +} + +static int +hscrolll_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) + return 0; + + newpos = scrollw->u.scroll.position - scrollw->u.scroll.page; + if (newpos < scrollw->u.scroll.minimum) + newpos = scrollw->u.scroll.minimum; + + if (newpos == scrollw->u.scroll.position) { + LOG("horiz scroll was the same %d", newpos); + return 0; + } + + return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLX, newpos); +} + +static int +hscrollr_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) + return 0; + + newpos = scrollw->u.scroll.position + scrollw->u.scroll.page; + if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb )) + newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb); + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(scrollw, FBTK_CBT_SCROLLX, newpos); +} + +static int +hscroll_drag(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int newpos; + fbtk_widget_t *scrollw = cbi->context; + + newpos = ((widget->u.scroll.drag_position + + (cbi->x - widget->u.scroll.drag)) * + (widget->u.scroll.maximum - widget->u.scroll.minimum)) / + (widget->width - 4); + + if (newpos < scrollw->u.scroll.minimum) + newpos = scrollw->u.scroll.minimum; + + if (newpos > (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb )) + newpos = (scrollw->u.scroll.maximum - scrollw->u.scroll.thumb); + + if (newpos == scrollw->u.scroll.position) + return 0; + + return fbtk_post_callback(widget, FBTK_CBT_SCROLLX, newpos); +} + +static int +hscrollarea_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int hscroll; + int hpos; + int newpos; + int ret = 0; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) { + /* end all drags, just in case */ + if (fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, NULL, NULL) != NULL) + fbtk_tgrab_pointer(widget); + return 0; + } + + if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { + hscroll = ((widget->width - 4) * widget->u.scroll.thumb) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + hpos = ((widget->width - 4) * widget->u.scroll.position) / + (widget->u.scroll.maximum - widget->u.scroll.minimum) ; + } else { + hscroll = (widget->width - 4); + hpos = 0; + } + + if (cbi->x < hpos) { + /* left of bar */ + newpos = widget->u.scroll.position - widget->u.scroll.page; + if (newpos < widget->u.scroll.minimum) + newpos = widget->u.scroll.minimum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLX, newpos); + } else if (cbi->x > (hpos + hscroll)) { + /* right of bar */ + newpos = widget->u.scroll.position + widget->u.scroll.page; + if (newpos > widget->u.scroll.maximum) + newpos = widget->u.scroll.maximum; + ret = fbtk_post_callback(cbi->context, FBTK_CBT_SCROLLX, newpos); + } else { + /* on bar - start drag */ + widget->u.scroll.drag = cbi->x; + widget->u.scroll.drag_position = hpos; + fbtk_set_handler(widget, FBTK_CBT_POINTERMOVE, hscroll_drag, widget); + fbtk_tgrab_pointer(widget); + } + return ret; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_hscroll(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour fg, + colour bg, + fbtk_callback callback, + void *context) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, + FB_WIDGET_TYPE_HSCROLL, + x + scrolll.width, + y, + width - scrolll.width - scrollr.width, + height); + + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, hscroll_redraw, NULL); + fbtk_set_handler(neww, FBTK_CBT_CLICK, hscrollarea_click, neww); + fbtk_set_handler(neww, FBTK_CBT_SCROLLX, callback, context); + + neww->u.scroll.btnul = fbtk_create_button(parent, + x, + y, + scrolll.width, + height, + fg, + &scrolll, + hscrolll_click, + neww); + + neww->u.scroll.btndr = fbtk_create_button(parent, + x + width - scrollr.width, + y, + scrollr.width, + height, + fg, + &scrollr, + hscrollr_click, + neww); + + return neww; +} + +/* exported function documented in fbtk.h */ +void +fbtk_reposition_hscroll(fbtk_widget_t *scrollh, + int x, + int y, + int width, + int height) +{ + assert(scrollh->type == FB_WIDGET_TYPE_HSCROLL); + + fbtk_set_pos_and_size(scrollh, x + scrolll.width, y, + width - scrolll.width - scrollr.width, height); + fbtk_set_pos_and_size(scrollh->u.scroll.btnul, + x, y, scrolll.width, height); + fbtk_set_pos_and_size(scrollh->u.scroll.btndr, + x + width - scrollr.width, y, + scrollr.width, height); +} + + +/* exported function documented in fbtk.h */ +bool +fbtk_set_scroll_parameters(fbtk_widget_t *widget, + int min, + int max, + int thumb, + int page) +{ + if (widget == NULL) + return false; + + if ((widget->type != FB_WIDGET_TYPE_HSCROLL) && + (widget->type != FB_WIDGET_TYPE_VSCROLL)) + return false; + + widget->u.scroll.minimum = min; + widget->u.scroll.maximum = max; + widget->u.scroll.thumb = thumb; + widget->u.scroll.page = page; + + if (widget->u.scroll.position > max) + widget->u.scroll.position = max; + if (widget->u.scroll.position < min) + widget->u.scroll.position = min; + + fbtk_request_redraw(widget); + + return true; +} + +/* exported function documented in fbtk.h */ +bool +fbtk_set_scroll_position(fbtk_widget_t *widget, int position) +{ + if (widget == NULL) + return false; + + if ((widget->type != FB_WIDGET_TYPE_HSCROLL) && + (widget->type != FB_WIDGET_TYPE_VSCROLL)) + return false; + + if ((position < widget->u.scroll.minimum) || + (position > widget->u.scroll.maximum)) + return false; + + widget->u.scroll.position = position; + + fbtk_request_redraw(widget); + + return true; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/text.c b/frontends/framebuffer/fbtk/text.c new file mode 100644 index 000000000..258e9dff9 --- /dev/null +++ b/frontends/framebuffer/fbtk/text.c @@ -0,0 +1,640 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit scrollbar widgets. + * + * 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 "utils/log.h" +#include "desktop/browser.h" +#include "desktop/plotters.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" +#include "framebuffer/font.h" +#include "framebuffer/framebuffer.h" +#include "framebuffer/image_data.h" + +#include "widget.h" + +//#define TEXT_WIDGET_BORDER 3 /**< The pixel border round a text widget. */ + +/* Lighten a colour by taking seven eights of each channel's intensity + * and adding a full eighth + */ +#define brighten_colour(c1) \ + (((((7 * ((c1 >> 16) & 0xff)) >> 3) + 32) << 16) | \ + ((((7 * ((c1 >> 8) & 0xff)) >> 3) + 32) << 8) | \ + ((((7 * (c1 & 0xff)) >> 3) + 32) << 0)) + +/* Convert pixels to points, assuming a DPI of 90 */ +#define px_to_pt(x) (((x) * 72) / FBTK_DPI) + +/* Get a font style for a text input */ +static inline void +fb_text_font_style(fbtk_widget_t *widget, int *font_height, int *padding, + plot_font_style_t *font_style) +{ + if (widget->u.text.outline) + *padding = 1; + else + *padding = 0; + +#ifdef FB_USE_FREETYPE + *padding += widget->height / 6; + *font_height = widget->height - *padding - *padding; +#else + *font_height = FB_FONT_HEIGHT; + *padding = (widget->height - *padding - *font_height) / 2; +#endif + + font_style->family = PLOT_FONT_FAMILY_SANS_SERIF; + font_style->size = px_to_pt(*font_height * FONT_SIZE_SCALE); + font_style->weight = 400; + font_style->flags = FONTF_NONE; + font_style->background = widget->bg; + font_style->foreground = widget->fg; +} + +/** Text redraw callback. + * + * Called when a text widget requires redrawing. + * + * @param widget The widget to be redrawn. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +fb_redraw_text(fbtk_widget_t *widget, fbtk_callback_info *cbi ) +{ + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + fbtk_widget_t *root; + plot_font_style_t font_style; + int caret_x, caret_y, caret_h; + int fh; + int padding; + int scroll = 0; + bool caret = false; + + fb_text_font_style(widget, &fh, &padding, &font_style); + + if (fbtk_get_caret(widget, &caret_x, &caret_y, &caret_h)) { + caret = true; + } + + root = fbtk_get_root_widget(widget); + + fbtk_get_bbox(widget, &bbox); + + rect = bbox; + + nsfb_claim(root->u.root.fb, &bbox); + + /* clear background */ + if ((widget->bg & 0xFF000000) != 0) { + /* transparent polygon filling isnt working so fake it */ + nsfb_plot_rectangle_fill(root->u.root.fb, &bbox, widget->bg); + } + + /* widget can have a single pixel outline border */ + if (widget->u.text.outline) { + rect.x1--; + rect.y1--; + nsfb_plot_rectangle(root->u.root.fb, &rect, 1, + 0x00000000, false, false); + } + + if (widget->u.text.text != NULL) { + int x = bbox.x0 + padding; + int y = bbox.y0 + ((fh * 3 + 2) / 4) + padding; + +#ifdef FB_USE_FREETYPE + /* Freetype renders text higher */ + y += 1; +#endif + + if (caret && widget->width - padding - padding < caret_x) { + scroll = (widget->width - padding - padding) - caret_x; + x += scroll; + } + + /* Call the fb text plotting, baseline is 3/4 down the font */ + fb_plotters.text(x, y, widget->u.text.text, + widget->u.text.len, &font_style); + } + + if (caret) { + /* This widget has caret, so render it */ + nsfb_t *nsfb = fbtk_get_nsfb(widget); + nsfb_bbox_t line; + nsfb_plot_pen_t pen; + + line.x0 = bbox.x0 + caret_x + scroll; + line.y0 = bbox.y0 + caret_y; + line.x1 = bbox.x0 + caret_x + scroll; + line.y1 = bbox.y0 + 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(root->u.root.fb, &bbox); + + return 0; +} + +/** Text destroy callback. + * + * Called when a text widget is destroyed. + * + * @param widget The widget being destroyed. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int fb_destroy_text(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_TEXT)) { + return 0; + } + + if (widget->u.text.text != NULL) { + free(widget->u.text.text); + } + + return 0; +} + +/** Text button redraw callback. + * + * Called when a text widget requires redrawing. + * + * @param widget The widget to be redrawn. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +fb_redraw_text_button(fbtk_widget_t *widget, fbtk_callback_info *cbi ) +{ + nsfb_bbox_t bbox; + nsfb_bbox_t rect; + nsfb_bbox_t line; + nsfb_plot_pen_t pen; + plot_font_style_t font_style; + int fh; + int border; + fbtk_widget_t *root = fbtk_get_root_widget(widget); + + fb_text_font_style(widget, &fh, &border, &font_style); + + pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; + pen.stroke_width = 1; + pen.stroke_colour = brighten_colour(widget->bg); + + fbtk_get_bbox(widget, &bbox); + + rect = bbox; + rect.x1--; + rect.y1--; + + nsfb_claim(root->u.root.fb, &bbox); + + /* clear background */ + if ((widget->bg & 0xFF000000) != 0) { + /* transparent polygon filling isnt working so fake it */ + nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); + } + + if (widget->u.text.outline) { + line.x0 = rect.x0; + line.y0 = rect.y0; + line.x1 = rect.x0; + line.y1 = rect.y1; + nsfb_plot_line(root->u.root.fb, &line, &pen); + line.x0 = rect.x0; + line.y0 = rect.y0; + line.x1 = rect.x1; + line.y1 = rect.y0; + nsfb_plot_line(root->u.root.fb, &line, &pen); + pen.stroke_colour = darken_colour(widget->bg); + line.x0 = rect.x0; + line.y0 = rect.y1; + line.x1 = rect.x1; + line.y1 = rect.y1; + nsfb_plot_line(root->u.root.fb, &line, &pen); + line.x0 = rect.x1; + line.y0 = rect.y0; + line.x1 = rect.x1; + line.y1 = rect.y1; + nsfb_plot_line(root->u.root.fb, &line, &pen); + } + + if (widget->u.text.text != NULL) { + /* Call the fb text plotting, baseline is 3/4 down the font */ + fb_plotters.text(bbox.x0 + border, + bbox.y0 + ((fh * 3) / 4) + border, + widget->u.text.text, + widget->u.text.len, + &font_style); + } + + nsfb_update(root->u.root.fb, &bbox); + + return 0; +} + +static void +fb_text_input_remove_caret_cb(fbtk_widget_t *widget) +{ + int c_x, c_y, c_h; + + if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) { + fbtk_request_redraw(widget); + } +} + +/** Routine called when text events occour in writeable widget. + * + * @param widget The widget reciving input events. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +text_input(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + int value; + static fbtk_modifier_type modifier = FBTK_MOD_CLEAR; + char *temp; + plot_font_style_t font_style; + int fh; + int border; + bool caret_moved = false; + + fb_text_font_style(widget, &fh, &border, &font_style); + + if (cbi->event == NULL) { + /* gain focus */ + if (widget->u.text.text == NULL) + widget->u.text.text = calloc(1,1); + + return 0; + } + + value = cbi->event->value.keycode; + + if (cbi->event->type != NSFB_EVENT_KEY_DOWN) { + switch (value) { + 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; + } + return 0; + } + + switch (value) { + case NSFB_KEY_BACKSPACE: + if (widget->u.text.idx <= 0) + break; + memmove(widget->u.text.text + widget->u.text.idx - 1, + widget->u.text.text + widget->u.text.idx, + widget->u.text.len - widget->u.text.idx); + widget->u.text.idx--; + widget->u.text.len--; + widget->u.text.text[widget->u.text.len] = 0; + + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.len, &widget->u.text.width); + + caret_moved = true; + break; + + case NSFB_KEY_RETURN: + widget->u.text.enter(widget->u.text.pw, widget->u.text.text); + break; + + case NSFB_KEY_RIGHT: + if (widget->u.text.idx < widget->u.text.len) { + if (modifier == FBTK_MOD_CLEAR) + widget->u.text.idx++; + else + widget->u.text.idx = widget->u.text.len; + + caret_moved = true; + } + break; + + case NSFB_KEY_LEFT: + if (widget->u.text.idx > 0) { + if (modifier == FBTK_MOD_CLEAR) + widget->u.text.idx--; + else + widget->u.text.idx = 0; + + caret_moved = true; + } + break; + + case NSFB_KEY_PAGEUP: + case NSFB_KEY_PAGEDOWN: + case NSFB_KEY_UP: + case NSFB_KEY_DOWN: + /* Not handling any of these correctly yet, but avoid putting + * charcters in the text widget when they're pressed. */ + 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; + + default: + if (modifier & FBTK_MOD_LCTRL || modifier & FBTK_MOD_RCTRL) { + /* CTRL pressed, don't enter any text */ + if (value == NSFB_KEY_u) { + /* CTRL+U: clear writable */ + widget->u.text.idx = 0; + widget->u.text.len = 0; + widget->u.text.text[widget->u.text.len] = '\0'; + widget->u.text.width = 0; + caret_moved = true; + } + break; + } + + /* allow for new character and null */ + temp = realloc(widget->u.text.text, widget->u.text.len + 2); + if (temp == NULL) { + break; + } + + widget->u.text.text = temp; + memmove(widget->u.text.text + widget->u.text.idx + 1, + widget->u.text.text + widget->u.text.idx, + widget->u.text.len - widget->u.text.idx); + widget->u.text.text[widget->u.text.idx] = + fbtk_keycode_to_ucs4(value, modifier); + widget->u.text.idx++; + widget->u.text.len++; + widget->u.text.text[widget->u.text.len] = '\0'; + + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.len, &widget->u.text.width); + caret_moved = true; + break; + } + + if (caret_moved) { + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.idx, &widget->u.text.idx_offset); + fbtk_set_caret(widget, true, + widget->u.text.idx_offset + border, + border, + widget->height - border - border, + fb_text_input_remove_caret_cb); + } + + fbtk_request_redraw(widget); + + return 0; +} + +/** Routine called when click events occour in writeable widget. + * + * @param widget The widget reciving click events. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +text_input_click(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + plot_font_style_t font_style; + int fh; + int border; + size_t idx; + + fb_text_font_style(widget, &fh, &border, &font_style); + + widget->u.text.idx = widget->u.text.len; + + fb_font_position(&font_style, widget->u.text.text, + widget->u.text.len, cbi->x - border, + &idx, + &widget->u.text.idx_offset); + widget->u.text.idx = idx; + fbtk_set_caret(widget, true, + widget->u.text.idx_offset + border, + border, + widget->height - border - border, + fb_text_input_remove_caret_cb); + + fbtk_request_redraw(widget); + + return 0; +} + +/** Routine called when "stripped of focus" event occours for writeable widget. + * + * @param widget The widget reciving "stripped of focus" event. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +text_input_strip_focus(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + fbtk_set_caret(widget, false, 0, 0, 0, NULL); + + return 0; +} + +/* exported function documented in fbtk.h */ +void +fbtk_writable_text(fbtk_widget_t *widget, fbtk_enter_t enter, void *pw) +{ + widget->u.text.enter = enter; + widget->u.text.pw = pw; + + fbtk_set_handler(widget, FBTK_CBT_INPUT, text_input, widget); +} + +/* exported function documented in fbtk.h */ +void +fbtk_set_text(fbtk_widget_t *widget, const char *text) +{ + plot_font_style_t font_style; + int c_x, c_y, c_h; + int fh; + int border; + + if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_TEXT)) + return; + if (widget->u.text.text != NULL) { + if (strcmp(widget->u.text.text, text) == 0) + return; /* text is being set to the same thing */ + free(widget->u.text.text); + } + widget->u.text.text = strdup(text); + widget->u.text.len = strlen(text); + widget->u.text.idx = widget->u.text.len; + + + fb_text_font_style(widget, &fh, &border, &font_style); + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.len, &widget->u.text.width); + fb_font_width(&font_style, widget->u.text.text, + widget->u.text.idx, &widget->u.text.idx_offset); + + if (fbtk_get_caret(widget, &c_x, &c_y, &c_h)) { + /* Widget has caret; move it to end of new string */ + fbtk_set_caret(widget, true, + widget->u.text.idx_offset + border, + border, + widget->height - border - border, + fb_text_input_remove_caret_cb); + } + + fbtk_request_redraw(widget); +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_text(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour bg, + colour fg, + bool outline) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height); + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + neww->u.text.outline = outline; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text, NULL); + fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL); + + return neww; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_writable_text(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour bg, + colour fg, + bool outline, + fbtk_enter_t enter, + void *pw) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height); + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + + neww->u.text.outline = outline; + neww->u.text.enter = enter; + neww->u.text.pw = pw; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text, NULL); + fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL); + fbtk_set_handler(neww, FBTK_CBT_CLICK, text_input_click, pw); + fbtk_set_handler(neww, FBTK_CBT_STRIP_FOCUS, text_input_strip_focus, NULL); + fbtk_set_handler(neww, FBTK_CBT_INPUT, text_input, neww); + + return neww; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_text_button(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour bg, + colour fg, + fbtk_callback click, + void *pw) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_TEXT, x, y, width, height); + neww->fg = fg; + neww->bg = bg; + neww->mapped = true; + + neww->u.text.outline = true; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_text_button, NULL); + fbtk_set_handler(neww, FBTK_CBT_DESTROY, fb_destroy_text, NULL); + fbtk_set_handler(neww, FBTK_CBT_CLICK, click, pw); + fbtk_set_handler(neww, FBTK_CBT_POINTERENTER, fbtk_set_ptr, &hand_image); + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/user.c b/frontends/framebuffer/fbtk/user.c new file mode 100644 index 000000000..2b9cc8768 --- /dev/null +++ b/frontends/framebuffer/fbtk/user.c @@ -0,0 +1,64 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit user widget. + * + * 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 "desktop/plot_style.h" +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" + +#include "widget.h" + +/* exported function documented in fbtk.h */ +void * +fbtk_get_userpw(fbtk_widget_t *widget) +{ + if ((widget == NULL) || + (widget->type != FB_WIDGET_TYPE_USER)) + return NULL; + + return widget->u.user.pw; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_user(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + void *pw) +{ + fbtk_widget_t *neww; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_USER, x, y, width, height); + neww->u.user.pw = pw; + neww->mapped = true; + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/widget.h b/frontends/framebuffer/fbtk/widget.h new file mode 100644 index 000000000..5622723ee --- /dev/null +++ b/frontends/framebuffer/fbtk/widget.h @@ -0,0 +1,259 @@ +/* + * Copyright 2010 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 . + */ + +#ifndef NETSURF_FB_FBTK_WIDGET_H +#define NETSURF_FB_FBTK_WIDGET_H + +#include + +enum fbtk_widgettype_e { + FB_WIDGET_TYPE_ROOT = 0, + FB_WIDGET_TYPE_WINDOW, + FB_WIDGET_TYPE_BITMAP, + FB_WIDGET_TYPE_FILL, + FB_WIDGET_TYPE_TEXT, + FB_WIDGET_TYPE_HSCROLL, + FB_WIDGET_TYPE_VSCROLL, + FB_WIDGET_TYPE_USER, +}; + + +/** Widget description. + * + * A widget is an entry in a tree structure which represents a + * rectangular area with co-ordinates relative to its parent widget. + * This area has a distinct set of callback operations for handling + * events which occour within its boundries. A widget may have an + * arbitrary number of child widgets. The order within the tree + * determines a widgets z order. + * + * --- + * A + * | + * +----------+ + * +--->| Button 3 | + * | +----------+ + * | | A + * | V | + * | +----------+ + * | | Button 2 | + * | +----------+ + * | | A + * | V | + * | +----------+ + * | | Button 1 | + * | +----------+ + * | | A + * | V | + * --- | +----------+ + * A | +->| Filled | + * | | | +----------+ + * +----------+ | | | + * +---->| |-+ | V + * | | Window 1 | | --- --- + * | | |---+ A + * | +----------+ | + * | | A +----------+ --- + * | | | +--->| Button 2 | A + * | | | | +----------+ | + * | | | | | A +-------------+ + * | | | | | | +--->| Button Up | + * | | | | | | | +-------------+ + * | | | | | | | | A + * | | | | | | | V | + * | | | | | | | +-------------+ + * | | | | | | | | Button Down | + * | | | | | | | +-------------+ + * | | | | | | | | A + * | | | | | | | V | + * | | | | | | | +-------------+ + * | | | | | | | +->| Scroller | + * | | | | V | | | +-------------+ + * | | | | +----------+ | | | + * | | | | | |-+ | V + * | | | | | V Scroll | | --- + * | | | | | |---+ + * | | | | +----------+ + * | | | | | A + * | | | | V | + * | | | | +----------+ + * | | | | +->| Button 1 | + * | | | | | +----------+ + * | +----------+ | | | + * | | |-+ | V + * | | Window 2 | | --- + * | | |---+ + * | +----------+ + * | | A + * | V | + * | +------------+ + * --- | | Background | + * A | +->| Bitmap | + * | | | +------------+ + * +------+ | | | + * | |-+ | V + * | Root | | --- + * | |---+ + * +------+ + * | + * V + * --- + * + * Every widget is contained within this generic wrapper. The + * integrated union provides for data specific to a widget type. + */ +struct fbtk_widget_s { + struct fbtk_widget_s *next; /* next lower z ordered widget in tree */ + struct fbtk_widget_s *prev; /* next higher z ordered widget in tree */ + + struct fbtk_widget_s *parent; /* parent widget */ + + struct fbtk_widget_s *first_child; /* first child widget */ + struct fbtk_widget_s *last_child; /* last child widget */ + + /* flags */ + bool mapped; /**< The widget is mapped/visible . */ + + /* Generic properties */ + int x; + int y; + int width; + int height; + colour bg; + colour fg; + + /* event callback handlers */ + fbtk_callback callback[FBTK_CBT_END]; + void *callback_context[FBTK_CBT_END]; + + /* widget redraw */ + struct { + bool child; /* A child of this widget requires redrawing */ + bool needed; /* the widget requires redrawing */ + int x; + int y; + int width; + int height; + } redraw; + + enum fbtk_widgettype_e type; /**< The type of the widget */ + + + union { + /* toolkit base handle */ + struct { + nsfb_t *fb; + struct fbtk_widget_s *prev; /* previous widget pointer wasin */ + struct fbtk_widget_s *grabbed; /* widget that has grabbed pointer movement. */ + struct fbtk_widget_s *input; + + /* caret */ + struct { + struct fbtk_widget_s *owner; /* widget / NULL */ + int x; /* relative to owner */ + int y; /* relative to owner */ + int height; + void (*remove_cb)(fbtk_widget_t *widget); + } caret; + } root; + + /* bitmap */ + struct { + struct fbtk_bitmap *bitmap; + } bitmap; + + /* text */ + struct { + char* text; + bool outline; + fbtk_enter_t enter; + void *pw; + int idx; /* caret pos in text */ + int len; /* text length */ + int width; /* text width in px */ + int idx_offset; /* caret pos in pixels */ + } text; + + /* application driven widget */ + struct { + void *pw; /* private data for user widget */ + } user; + + struct { + int minimum; /* lowest value of scrollbar */ + int maximum; /* highest value of scrollbar */ + int thumb; /* size of bar representing a page */ + int page; /* amount to page document */ + int position; /* position of bar */ + int drag; /* offset to start of drag */ + int drag_position; /* indicator bar pos at drag start */ + struct fbtk_widget_s *btnul; /* scroll button up/left */ + struct fbtk_widget_s *btndr; /* scroll button down/right*/ + } scroll; + + } u; +}; + + +/* These functions are not considered part of the public API but are + * not static as they are used by the higher level widget provision + * routines + */ + + +/** creates a new widget and insert it into to hierachy. + * + * The widget is set to defaults of false, 0 or NULL. + * + * @param parent The parent widget. The new widget will be added with + * the shallowest z order relative to its siblings. + * @param type The type of the widget. + * @param x The x co-ordinate relative to the parent widget. + * @param y The y co-ordinate relative to the parent widget. + * @param width the widgets width. This will be clipped to the parent, if + * the value is 0 the largest extent which can fit within the parent + * is used, if the value is negative the largest value that will fit + * within the parent less the value given will be used. + * @param height the widgets width. This will be clipped to the parent, if + * the value is 0 the largest extent which can fit within the parent + * is used, if the value is negative the largest value that will fit + * within the parent less the value given will be used. + */ +fbtk_widget_t *fbtk_widget_new(fbtk_widget_t *parent, enum fbtk_widgettype_e type, int x, int y, int width, int height); + +/** find the root widget from any widget in the toolkit hierarchy. + * + * @param widget Any widget. + * @return The root widget or NULL if \a widget was not valid. + */ +fbtk_widget_t *fbtk_get_root_widget(fbtk_widget_t *widget); + +/** set pointer to bitmap in context. + * + * widget helper callback to set cursor image to the bitmap passed in + * the callbacks private data. + */ +int fbtk_set_ptr(fbtk_widget_t *widget, fbtk_callback_info *cbi); + +#endif + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ diff --git a/frontends/framebuffer/fbtk/window.c b/frontends/framebuffer/fbtk/window.c new file mode 100644 index 000000000..787dac720 --- /dev/null +++ b/frontends/framebuffer/fbtk/window.c @@ -0,0 +1,91 @@ +/* + * Copyright 2010 Vincent Sanders + * + * Framebuffer windowing toolkit window widget. + * + * 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 "desktop/browser.h" + +#include "framebuffer/gui.h" +#include "framebuffer/fbtk.h" + +#include "widget.h" + +/** Window redraw callback. + * + * Called when a window requires redrawing. + * + * @param widget The widget to be redrawn. + * @param cbi The callback parameters. + * @return The callback result. + */ +static int +fb_redraw_window(fbtk_widget_t *widget, fbtk_callback_info *cbi) +{ + nsfb_bbox_t bbox; + nsfb_t *nsfb; + + if ((widget->bg & 0xFF000000) == 0) + return 0; + + nsfb = fbtk_get_nsfb(widget); + + fbtk_get_bbox(widget, &bbox); + + nsfb_claim(nsfb, &bbox); + + nsfb_plot_rectangle_fill(nsfb, &bbox, widget->bg); + + nsfb_update(nsfb, &bbox); + + return 0; +} + +/* exported function documented in fbtk.h */ +fbtk_widget_t * +fbtk_create_window(fbtk_widget_t *parent, + int x, + int y, + int width, + int height, + colour bg) +{ + fbtk_widget_t *neww; + + if (parent == NULL) + return NULL; + + neww = fbtk_widget_new(parent, FB_WIDGET_TYPE_WINDOW, x, y, width, height); + + neww->bg = bg; + + fbtk_set_handler(neww, FBTK_CBT_REDRAW, fb_redraw_window, NULL); + + return neww; +} + +/* + * Local Variables: + * c-basic-offset:8 + * End: + */ -- cgit v1.2.3