summaryrefslogtreecommitdiff
path: root/dw/fltkplatform.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dw/fltkplatform.cc')
-rw-r--r--dw/fltkplatform.cc739
1 files changed, 739 insertions, 0 deletions
diff --git a/dw/fltkplatform.cc b/dw/fltkplatform.cc
new file mode 100644
index 0000000..a244765
--- /dev/null
+++ b/dw/fltkplatform.cc
@@ -0,0 +1,739 @@
+/*
+ * RTFL (originally part of dillo)
+ *
+ * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program 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; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+
+#include "../lout/msg.h"
+#include "../lout/debug.hh"
+#include "fltkcore.hh"
+
+#include <FL/fl_draw.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Tooltip.H>
+#include <FL/Fl_Menu_Window.H>
+#include <FL/Fl_Paged_Device.H>
+
+/*
+ * Local data
+ */
+
+/* Tooltips */
+static Fl_Menu_Window *tt_window = NULL;
+static int in_tooltip = 0, req_tooltip = 0;
+
+namespace dw {
+namespace fltk {
+
+using namespace lout;
+
+/**
+ * \todo Distinction between italics and oblique would be nice.
+ */
+
+container::typed::HashTable <dw::core::style::FontAttrs,
+ FltkFont> *FltkFont::fontsTable =
+ new container::typed::HashTable <dw::core::style::FontAttrs,
+ FltkFont> (false, false);
+
+container::typed::HashTable <lout::object::ConstString,
+ FltkFont::FontFamily> *FltkFont::systemFonts =
+ NULL;
+
+FltkFont::FontFamily FltkFont::standardFontFamily (FL_HELVETICA,
+ FL_HELVETICA_BOLD,
+ FL_HELVETICA_ITALIC,
+ FL_HELVETICA_BOLD_ITALIC);
+
+FltkFont::FontFamily::FontFamily (Fl_Font fontNormal, Fl_Font fontBold,
+ Fl_Font fontItalic, Fl_Font fontBoldItalic)
+{
+ font[0] = fontNormal;
+ font[1] = fontBold;
+ font[2] = fontItalic;
+ font[3] = fontBoldItalic;
+}
+
+void FltkFont::FontFamily::set (Fl_Font f, int attrs)
+{
+ int idx = 0;
+ if (attrs & FL_BOLD)
+ idx += 1;
+ if (attrs & FL_ITALIC)
+ idx += 2;
+ font[idx] = f;
+}
+
+Fl_Font FltkFont::FontFamily::get (int attrs)
+{
+ int idx = 0;
+ if (attrs & FL_BOLD)
+ idx += 1;
+ if (attrs & FL_ITALIC)
+ idx += 2;
+
+ // should the desired font style not exist, we
+ // return the normal font of the fontFamily
+ return font[idx] >= 0 ? font[idx] : font[0];
+}
+
+
+
+FltkFont::FltkFont (core::style::FontAttrs *attrs)
+{
+ if (!systemFonts)
+ initSystemFonts ();
+
+ copyAttrs (attrs);
+
+ int fa = 0;
+ if (weight >= 500)
+ fa |= FL_BOLD;
+ if (style != core::style::FONT_STYLE_NORMAL)
+ fa |= FL_ITALIC;
+
+ object::ConstString nameString (name);
+ FontFamily *family = systemFonts->get (&nameString);
+ if (!family)
+ family = &standardFontFamily;
+
+ font = family->get (fa);
+
+ fl_font(font, size);
+ // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in
+ // 1.3.0 (STR #2688).
+ spaceWidth = misc::max(0, (int)fl_width(" ") + letterSpacing);
+ int xx, xy, xw, xh;
+ fl_text_extents("x", xx, xy, xw, xh);
+ xHeight = xh;
+ descent = fl_descent();
+ ascent = fl_height() - descent;
+}
+
+FltkFont::~FltkFont ()
+{
+ fontsTable->remove (this);
+}
+
+static void strstrip(char *big, const char *little)
+{
+ if (strlen(big) >= strlen(little) &&
+ misc::AsciiStrcasecmp(big + strlen(big) - strlen(little), little) == 0)
+ *(big + strlen(big) - strlen(little)) = '\0';
+}
+
+void FltkFont::initSystemFonts ()
+{
+ systemFonts = new container::typed::HashTable
+ <lout::object::ConstString, FontFamily> (true, true);
+
+ int k = Fl::set_fonts ("-*-iso10646-1");
+ for (int i = 0; i < k; i++) {
+ int t;
+ char *name = strdup (Fl::get_font_name ((Fl_Font) i, &t));
+
+ // normalize font family names (strip off "bold", "italic")
+ if (t & FL_ITALIC)
+ strstrip(name, " italic");
+ if (t & FL_BOLD)
+ strstrip(name, " bold");
+
+ _MSG("Found font: %s%s%s\n", name, t & FL_BOLD ? " bold" : "",
+ t & FL_ITALIC ? " italic" : "");
+
+ object::String *familyName = new object::String(name);
+ free (name);
+ FontFamily *family = systemFonts->get (familyName);
+
+ if (family) {
+ family->set ((Fl_Font) i, t);
+ delete familyName;
+ } else {
+ // set first font of family also as normal font in case there
+ // is no normal (non-bold, non-italic) font
+ family = new FontFamily ((Fl_Font) i, -1, -1, -1);
+ family->set ((Fl_Font) i, t);
+ systemFonts->put (familyName, family);
+ }
+ }
+}
+
+bool
+FltkFont::fontExists (const char *name)
+{
+ if (!systemFonts)
+ initSystemFonts ();
+ object::ConstString familyName (name);
+ return systemFonts->get (&familyName) != NULL;
+}
+
+Fl_Font
+FltkFont::get (const char *name, int attrs)
+{
+ if (!systemFonts)
+ initSystemFonts ();
+ object::ConstString familyName (name);
+ FontFamily *family = systemFonts->get (&familyName);
+ if (family)
+ return family->get (attrs);
+ else
+ return FL_HELVETICA;
+}
+
+bool
+FltkPlatform::fontExists (const char *name)
+{
+ return FltkFont::fontExists (name);
+}
+
+FltkFont*
+FltkFont::create (core::style::FontAttrs *attrs)
+{
+ FltkFont *font = fontsTable->get (attrs);
+
+ if (font == NULL) {
+ font = new FltkFont (attrs);
+ fontsTable->put (font, font);
+ }
+
+ return font;
+}
+
+container::typed::HashTable <dw::core::style::ColorAttrs,
+ FltkColor>
+ *FltkColor::colorsTable =
+ new container::typed::HashTable <dw::core::style::ColorAttrs,
+ FltkColor> (false, false);
+
+FltkColor::FltkColor (int color): Color (color)
+{
+ this->color = color;
+
+ if (!(colors[SHADING_NORMAL] = shadeColor (color, SHADING_NORMAL) << 8))
+ colors[SHADING_NORMAL] = FL_BLACK;
+ if (!(colors[SHADING_INVERSE] = shadeColor (color, SHADING_INVERSE) << 8))
+ colors[SHADING_INVERSE] = FL_BLACK;
+ if (!(colors[SHADING_DARK] = shadeColor (color, SHADING_DARK) << 8))
+ colors[SHADING_DARK] = FL_BLACK;
+ if (!(colors[SHADING_LIGHT] = shadeColor (color, SHADING_LIGHT) << 8))
+ colors[SHADING_LIGHT] = FL_BLACK;
+}
+
+FltkColor::~FltkColor ()
+{
+ colorsTable->remove (this);
+}
+
+FltkColor * FltkColor::create (int col)
+{
+ ColorAttrs attrs(col);
+ FltkColor *color = colorsTable->get (&attrs);
+
+ if (color == NULL) {
+ color = new FltkColor (col);
+ colorsTable->put (color, color);
+ }
+
+ return color;
+}
+
+FltkTooltip::FltkTooltip (const char *text) : Tooltip(text)
+{
+}
+
+FltkTooltip::~FltkTooltip ()
+{
+ if (in_tooltip || req_tooltip)
+ cancel(); /* cancel tooltip window */
+}
+
+FltkTooltip *FltkTooltip::create (const char *text)
+{
+ return new FltkTooltip(text);
+}
+
+/*
+ * Tooltip callback: used to delay it a bit
+ * INVARIANT: Only one instance of this function is requested.
+ */
+static void tooltip_tcb(void *data)
+{
+ req_tooltip = 2;
+ ((FltkTooltip *)data)->onEnter();
+ req_tooltip = 0;
+}
+
+void FltkTooltip::onEnter()
+{
+ _MSG("FltkTooltip::onEnter\n");
+ if (!str || !*str)
+ return;
+ if (req_tooltip == 0) {
+ Fl::remove_timeout(tooltip_tcb);
+ Fl::add_timeout(1.0, tooltip_tcb, this);
+ req_tooltip = 1;
+ return;
+ }
+
+ if (!tt_window) {
+ tt_window = new Fl_Menu_Window(0,0,100,24);
+ tt_window->set_override();
+ tt_window->box(FL_NO_BOX);
+ Fl_Box *b = new Fl_Box(0,0,100,24);
+ b->box(FL_BORDER_BOX);
+ b->color(fl_color_cube(FL_NUM_RED-1, FL_NUM_GREEN-1, FL_NUM_BLUE-2));
+ b->labelcolor(FL_BLACK);
+ b->labelfont(FL_HELVETICA);
+ b->labelsize(14);
+ b->align(FL_ALIGN_WRAP|FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+ tt_window->resizable(b);
+ tt_window->end();
+ }
+
+ /* prepare tooltip window */
+ int x, y;
+ Fl_Box *box = (Fl_Box*)tt_window->child(0);
+ box->label(str);
+ Fl::get_mouse(x,y); y += 6;
+ /* calculate window size */
+ int ww, hh;
+ ww = 800; // max width;
+ box->measure_label(ww, hh);
+ ww += 6 + 2 * Fl::box_dx(box->box());
+ hh += 6 + 2 * Fl::box_dy(box->box());
+ tt_window->resize(x,y,ww,hh);
+ tt_window->show();
+ in_tooltip = 1;
+}
+
+/*
+ * Leaving the widget cancels the tooltip
+ */
+void FltkTooltip::onLeave()
+{
+ _MSG(" FltkTooltip::onLeave in_tooltip=%d\n", in_tooltip);
+ cancel();
+}
+
+void FltkPlatform::cancelTooltip()
+{
+ FltkTooltip::cancel();
+}
+
+/*
+ * Remove a shown tooltip or cancel a pending one
+ */
+void FltkTooltip::cancel()
+{
+ if (req_tooltip) {
+ Fl::remove_timeout(tooltip_tcb);
+ req_tooltip = 0;
+ }
+ if (!in_tooltip) return;
+ in_tooltip = 0;
+ tt_window->hide();
+
+ /* WORKAROUND: (Black magic here)
+ * Hiding a tooltip with the keyboard or mousewheel doesn't work.
+ * The code below "fixes" the problem */
+ Fl_Widget *widget = Fl::belowmouse();
+ if (widget && widget->window()) {
+ widget->window()->damage(FL_DAMAGE_EXPOSE,0,0,1,1);
+ }
+}
+
+void FltkTooltip::onMotion()
+{
+}
+
+void FltkView::addFltkWidget (Fl_Widget *widget,
+ core::Allocation *allocation)
+{
+}
+
+void FltkView::removeFltkWidget (Fl_Widget *widget)
+{
+}
+
+void FltkView::allocateFltkWidget (Fl_Widget *widget,
+ core::Allocation *allocation)
+{
+}
+
+void FltkView::drawFltkWidget (Fl_Widget *widget, core::Rectangle *area)
+{
+}
+
+
+core::ui::LabelButtonResource *
+FltkPlatform::FltkResourceFactory::createLabelButtonResource (const char
+ *label)
+{
+ return new ui::FltkLabelButtonResource (platform, label);
+}
+
+core::ui::ComplexButtonResource *
+FltkPlatform::FltkResourceFactory::createComplexButtonResource (core::Widget
+ *widget,
+ bool relief)
+{
+ // Not needed within RTFL.
+ lout::misc::assertNotReached ();
+ return NULL;
+}
+
+core::ui::ListResource *
+FltkPlatform::FltkResourceFactory::createListResource (core::ui
+ ::ListResource
+ ::SelectionMode
+ selectionMode, int rows)
+{
+ return new ui::FltkListResource (platform, selectionMode, rows);
+}
+
+core::ui::OptionMenuResource *
+FltkPlatform::FltkResourceFactory::createOptionMenuResource ()
+{
+ return new ui::FltkOptionMenuResource (platform);
+}
+
+core::ui::EntryResource *
+FltkPlatform::FltkResourceFactory::createEntryResource (int size,
+ bool password,
+ const char *label)
+{
+ return new ui::FltkEntryResource (platform, size, password, label);
+}
+
+core::ui::MultiLineTextResource *
+FltkPlatform::FltkResourceFactory::createMultiLineTextResource (int cols,
+ int rows)
+{
+ return new ui::FltkMultiLineTextResource (platform, cols, rows);
+}
+
+core::ui::CheckButtonResource *
+FltkPlatform::FltkResourceFactory::createCheckButtonResource (bool activated)
+{
+ return new ui::FltkCheckButtonResource (platform, activated);
+}
+
+core::ui::RadioButtonResource
+*FltkPlatform::FltkResourceFactory::createRadioButtonResource
+(core::ui::RadioButtonResource *groupedWith, bool activated)
+{
+ return
+ new ui::FltkRadioButtonResource (platform,
+ (ui::FltkRadioButtonResource*)
+ groupedWith,
+ activated);
+}
+
+// ----------------------------------------------------------------------
+
+FltkPlatform::FltkPlatform ()
+{
+ DBG_OBJ_CREATE ("dw::fltk::FltkPlatform");
+
+ layout = NULL;
+ idleQueue = new container::typed::List <IdleFunc> (true);
+ idleFuncRunning = false;
+ idleFuncId = 0;
+
+ view = NULL;
+ resources = new container::typed::List <ui::FltkResource> (false);
+
+ resourceFactory.setPlatform (this);
+}
+
+FltkPlatform::~FltkPlatform ()
+{
+ if (idleFuncRunning)
+ Fl::remove_idle (generalStaticIdle, (void*)this);
+ delete idleQueue;
+ delete resources;
+
+ DBG_OBJ_DELETE ();
+}
+
+void FltkPlatform::setLayout (core::Layout *layout)
+{
+ this->layout = layout;
+ DBG_OBJ_ASSOC_CHILD (layout);
+}
+
+
+void FltkPlatform::attachView (core::View *view)
+{
+ if (this->view)
+ MSG_ERR("FltkPlatform::attachView: multiple views!\n");
+ this->view = (FltkView*)view;
+
+ for (container::typed::Iterator <ui::FltkResource> it =
+ resources->iterator (); it.hasNext (); ) {
+ ui::FltkResource *resource = it.getNext ();
+ resource->attachView (this->view);
+ }
+}
+
+
+void FltkPlatform::detachView (core::View *view)
+{
+ if (this->view != view)
+ MSG_ERR("FltkPlatform::detachView: this->view: %p view: %p\n",
+ this->view, view);
+
+ for (container::typed::Iterator <ui::FltkResource> it =
+ resources->iterator (); it.hasNext (); ) {
+ ui::FltkResource *resource = it.getNext ();
+ resource->detachView ((FltkView*)view);
+ }
+ this->view = NULL;
+}
+
+
+int FltkPlatform::textWidth (core::style::Font *font, const char *text,
+ int len)
+{
+ char chbuf[4];
+ int c, cu;
+ int width = 0;
+ FltkFont *ff = (FltkFont*) font;
+ int curr = 0, next = 0, nb;
+
+ if (font->fontVariant == core::style::FONT_VARIANT_SMALL_CAPS) {
+ int sc_fontsize = lout::misc::roundInt(ff->size * 0.78);
+ for (curr = 0; next < len; curr = next) {
+ next = nextGlyph(text, curr);
+ c = fl_utf8decode(text + curr, text + next, &nb);
+ if ((cu = fl_toupper(c)) == c) {
+ /* already uppercase, just draw the character */
+ fl_font(ff->font, ff->size);
+ if (fl_nonspacing(cu) == 0) {
+ width += font->letterSpacing;
+ width += (int)fl_width(text + curr, next - curr);
+ }
+ } else {
+ /* make utf8 string for converted char */
+ nb = fl_utf8encode(cu, chbuf);
+ fl_font(ff->font, sc_fontsize);
+ if (fl_nonspacing(cu) == 0) {
+ width += font->letterSpacing;
+ width += (int)fl_width(chbuf, nb);
+ }
+ }
+ }
+ } else {
+ fl_font (ff->font, ff->size);
+ width = (int) fl_width (text, len);
+
+ if (font->letterSpacing) {
+ int curr = 0, next = 0;
+
+ while (next < len) {
+ next = nextGlyph(text, curr);
+ c = fl_utf8decode(text + curr, text + next, &nb);
+ if (fl_nonspacing(c) == 0)
+ width += font->letterSpacing;
+ curr = next;
+ }
+ }
+ }
+
+ return width;
+}
+
+char *FltkPlatform::textToUpper (const char *text, int len)
+{
+ char *newstr = NULL;
+
+ if (len > 0) {
+ int newlen;
+
+ newstr = (char*) malloc(3 * len + 1);
+ newlen = fl_utf_toupper((const unsigned char*)text, len, newstr);
+ assert(newlen <= 3 * len);
+ newstr[newlen] = '\0';
+ }
+ return newstr;
+}
+
+char *FltkPlatform::textToLower (const char *text, int len)
+{
+ char *newstr = NULL;
+
+ if (len > 0) {
+ int newlen;
+
+ newstr = (char*) malloc(3 * len + 1);
+ newlen = fl_utf_tolower((const unsigned char*)text, len, newstr);
+ assert(newlen <= 3 * len);
+ newstr[newlen] = '\0';
+ }
+ return newstr;
+}
+
+int FltkPlatform::nextGlyph (const char *text, int idx)
+{
+ return fl_utf8fwd (&text[idx + 1], text, &text[strlen (text)]) - text;
+}
+
+int FltkPlatform::prevGlyph (const char *text, int idx)
+{
+ return fl_utf8back (&text[idx - 1], text, &text[strlen (text)]) - text;
+}
+
+float FltkPlatform::dpiX ()
+{
+ float horizontal, vertical;
+
+ Fl::screen_dpi(horizontal, vertical);
+ return horizontal;
+}
+
+float FltkPlatform::dpiY ()
+{
+ float horizontal, vertical;
+
+ Fl::screen_dpi(horizontal, vertical);
+ return vertical;
+}
+
+void FltkPlatform::generalStaticIdle (void *data)
+{
+ ((FltkPlatform*)data)->generalIdle();
+}
+
+void FltkPlatform::generalIdle ()
+{
+ IdleFunc *idleFunc;
+
+ if (!idleQueue->isEmpty ()) {
+ /* Execute the first function in the list. */
+ idleFunc = idleQueue->getFirst ();
+ (layout->*(idleFunc->func)) ();
+
+ /* Remove this function. */
+ idleQueue->removeRef(idleFunc);
+ }
+
+ if (idleQueue->isEmpty()) {
+ idleFuncRunning = false;
+ Fl::remove_idle (generalStaticIdle, (void*)this);
+ }
+}
+
+/**
+ * \todo Incomplete comments.
+ */
+int FltkPlatform::addIdle (void (core::Layout::*func) ())
+{
+ /*
+ * Since ... (todo) we have to wrap around fltk_add_idle. There is only one
+ * idle function, the passed idle function is put into a queue.
+ */
+ if (!idleFuncRunning) {
+ Fl::add_idle (generalStaticIdle, (void*)this);
+ idleFuncRunning = true;
+ }
+
+ idleFuncId++;
+
+ IdleFunc *idleFunc = new IdleFunc();
+ idleFunc->id = idleFuncId;
+ idleFunc->func = func;
+ idleQueue->append (idleFunc);
+
+ return idleFuncId;
+}
+
+void FltkPlatform::removeIdle (int idleId)
+{
+ bool found;
+ container::typed::Iterator <IdleFunc> it;
+ IdleFunc *idleFunc;
+
+ for (found = false, it = idleQueue->iterator(); !found && it.hasNext(); ) {
+ idleFunc = it.getNext();
+ if (idleFunc->id == idleId) {
+ idleQueue->removeRef (idleFunc);
+ found = true;
+ }
+ }
+
+ if (idleFuncRunning && idleQueue->isEmpty())
+ Fl::remove_idle (generalStaticIdle, (void*)this);
+}
+
+core::style::Font *FltkPlatform::createFont (core::style::FontAttrs
+ *attrs,
+ bool tryEverything)
+{
+ return FltkFont::create (attrs);
+}
+
+core::style::Color *FltkPlatform::createColor (int color)
+{
+ return FltkColor::create (color);
+}
+
+core::style::Tooltip *FltkPlatform::createTooltip (const char *text)
+{
+ return FltkTooltip::create (text);
+}
+
+void FltkPlatform::copySelection(const char *text)
+{
+ Fl::copy(text, strlen(text), 0);
+}
+
+core::Imgbuf *FltkPlatform::createImgbuf (core::Imgbuf::Type type,
+ int width, int height, double gamma)
+{
+ return new FltkImgbuf (type, width, height, gamma);
+}
+
+core::ui::ResourceFactory *FltkPlatform::getResourceFactory ()
+{
+ return &resourceFactory;
+}
+
+
+void FltkPlatform::attachResource (ui::FltkResource *resource)
+{
+ resources->append (resource);
+ resource->attachView (view);
+}
+
+void FltkPlatform::detachResource (ui::FltkResource *resource)
+{
+ resources->removeRef (resource);
+}
+
+} // namespace fltk
+} // namespace dw