Initial import of Dillo

This commit is contained in:
2025-02-28 13:34:30 -05:00
parent bd4e3eebd8
commit 20fea64cb5
496 changed files with 156174 additions and 0 deletions

109
dw/Makefile.am Normal file
View File

@ -0,0 +1,109 @@
AM_CPPFLAGS = \
-I$(top_srcdir) \
-DDILLO_LIBDIR='"$(pkglibdir)/"' \
-DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/dw"'
noinst_LIBRARIES = \
libDw-core.a \
libDw-fltk.a \
libDw-widgets.a
libDw_core_a_SOURCES = \
core.hh \
events.hh \
findtext.cc \
findtext.hh \
imgbuf.hh \
imgrenderer.hh \
imgrenderer.cc \
iterator.cc \
iterator.hh \
layout.cc \
layout.hh \
platform.hh \
selection.hh \
selection.cc \
stackingcontextmgr.hh \
stackingcontextmgr.cc \
style.cc \
style.hh \
tools.cc \
tools.hh \
types.cc \
types.hh \
ui.cc \
ui.hh \
view.hh \
widget.cc \
widget.hh
libDw_fltk_a_SOURCES = \
fltkcomplexbutton.cc \
fltkcomplexbutton.hh \
fltkcore.hh \
fltkflatview.cc \
fltkflatview.hh \
fltkimgbuf.cc \
fltkimgbuf.hh \
fltkmisc.cc \
fltkmisc.hh \
fltkplatform.cc \
fltkplatform.hh \
fltkpreview.hh \
fltkpreview.cc \
fltkui.cc \
fltkui.hh \
fltkviewbase.cc \
fltkviewbase.hh \
fltkviewport.cc \
fltkviewport.hh
libDw_fltk_a_CXXFLAGS = @LIBFLTK_CXXFLAGS@
libDw_widgets_a_SOURCES = \
alignedtablecell.cc \
alignedtablecell.hh \
alignedtextblock.cc \
alignedtextblock.hh \
bullet.cc \
bullet.hh \
hyphenator.cc \
hyphenator.hh \
image.cc \
image.hh \
listitem.cc \
listitem.hh \
oofawarewidget.cc \
oofawarewidget_iterator.cc \
oofawarewidget.hh \
ooffloatsmgr.cc \
ooffloatsmgr.hh \
oofposabslikemgr.cc \
oofposabslikemgr.hh \
oofposabsmgr.cc \
oofposabsmgr.hh \
oofposfixedmgr.cc \
oofposfixedmgr.hh \
oofpositionedmgr.cc \
oofpositionedmgr.hh \
oofposrelmgr.cc \
oofposrelmgr.hh \
outofflowmgr.cc \
outofflowmgr.hh \
regardingborder.cc \
regardingborder.hh \
ruler.cc \
ruler.hh \
simpletablecell.cc \
simpletablecell.hh \
table.cc \
table_iterator.cc \
table.hh \
tablecell.cc \
tablecell.hh \
textblock.cc \
textblock_iterator.cc \
textblock_linebreaking.cc \
textblock.hh
EXTRA_DIST = preview.xbm

218
dw/alignedtablecell.cc Normal file
View File

@ -0,0 +1,218 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "alignedtablecell.hh"
#include "table.hh"
#include "tablecell.hh"
#include "../lout/debug.hh"
#include <stdio.h>
using namespace lout;
namespace dw {
int AlignedTableCell::CLASS_ID = -1;
AlignedTableCell::AlignedTableCell (AlignedTableCell *ref, bool limitTextWidth):
AlignedTextblock (limitTextWidth)
{
DBG_OBJ_CREATE ("dw::AlignedTableCell");
registerName ("dw::AlignedTableCell", &CLASS_ID);
/** \bug ignoreLine1OffsetSometimes does not work? */
//ignoreLine1OffsetSometimes = true;
charWordIndex = -1;
setRefTextblock (ref);
setButtonSensitive(true);
}
AlignedTableCell::~AlignedTableCell()
{
DBG_OBJ_DELETE ();
}
bool AlignedTableCell::getAdjustMinWidth ()
{
return tablecell::getAdjustMinWidth ();
}
bool AlignedTableCell::isBlockLevel ()
{
return tablecell::isBlockLevel ();
}
bool AlignedTableCell::usesMaxGeneratorWidth ()
{
return tablecell::usesMaxGeneratorWidth ();
}
int AlignedTableCell::getAvailWidthOfChild (Widget *child, bool forceValue)
{
DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::getAvailWidthOfChild",
"%p, %s", child, forceValue ? "true" : "false");
int width = tablecell::correctAvailWidthOfChild
(this, child, Textblock::getAvailWidthOfChild (child, forceValue),
forceValue);
DBG_OBJ_LEAVE ();
return width;
}
int AlignedTableCell::getAvailHeightOfChild (Widget *child, bool forceValue)
{
DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::getAvailHeightOfChild",
"%p, %s", child, forceValue ? "true" : "false");
int height = tablecell::correctAvailHeightOfChild
(this, child, Textblock::getAvailHeightOfChild (child, forceValue),
forceValue);
DBG_OBJ_LEAVE ();
return height;
}
void AlignedTableCell::correctRequisitionOfChild (Widget *child,
core::Requisition
*requisition,
void (*splitHeightFun) (int,
int*,
int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight)
{
DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::correctRequisitionOfChild",
"%p, %d * (%d + %d), ..., %s, %s", child, requisition->width,
requisition->ascent, requisition->descent,
misc::boolToStr (allowDecreaseWidth),
misc::boolToStr (allowDecreaseHeight));
AlignedTextblock::correctRequisitionOfChild (child, requisition,
splitHeightFun,
allowDecreaseWidth,
allowDecreaseHeight);
tablecell::correctCorrectedRequisitionOfChild (this, child, requisition,
splitHeightFun,
allowDecreaseWidth,
allowDecreaseHeight);
DBG_OBJ_LEAVE ();
}
void AlignedTableCell::correctExtremesOfChild (Widget *child,
core::Extremes *extremes,
bool useAdjustmentWidth)
{
DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::correctExtremesOfChild",
"%p, %d (%d) / %d (%d)",
child, extremes->minWidth, extremes->minWidthIntrinsic,
extremes->maxWidth, extremes->maxWidthIntrinsic);
AlignedTextblock::correctExtremesOfChild (child, extremes,
useAdjustmentWidth);
tablecell::correctCorrectedExtremesOfChild (this, child, extremes,
useAdjustmentWidth);
DBG_OBJ_LEAVE ();
}
int AlignedTableCell::applyPerWidth (int containerWidth,
core::style::Length perWidth)
{
return tablecell::applyPerWidth (this, containerWidth, perWidth);
}
int AlignedTableCell::applyPerHeight (int containerHeight,
core::style::Length perHeight)
{
return tablecell::applyPerHeight (this, containerHeight, perHeight);
}
bool AlignedTableCell::adjustExtraSpaceWhenCorrectingRequisitionByOOF ()
{
return tablecell::adjustExtraSpaceWhenCorrectingRequisitionByOOF ();
}
int AlignedTableCell::wordWrap(int wordIndex, bool wrapAll)
{
Textblock::Word *word;
const char *p;
int ret = Textblock::wordWrap (wordIndex, wrapAll);
if (charWordIndex == -1) {
word = words->getRef (wordIndex);
if (word->content.type == core::Content::TEXT) {
if ((p = strchr (word->content.text,
word->style->textAlignChar))) {
charWordIndex = wordIndex;
charWordPos = p - word->content.text + 1;
} else if (word->style->textAlignChar == ' ' &&
word->content.space) {
charWordIndex = wordIndex + 1;
charWordPos = 0;
}
}
}
if (wordIndex == charWordIndex)
updateValue ();
return ret;
}
int AlignedTableCell::getValue ()
{
Textblock::Word *word;
int i, wordIndex;
int w;
if (charWordIndex == -1)
wordIndex = words->size () -1;
else
wordIndex = charWordIndex;
w = 0;
for (i = 0; i < wordIndex; i++) {
word = words->getRef (i);
w += word->size.width + word->origSpace;
}
if (charWordIndex == -1) {
if (words->size () > 0) {
word = words->getRef (words->size () - 1);
w += word->size.width;
}
} else {
word = words->getRef (charWordIndex);
w += layout->textWidth (word->style->font, word->content.text,
charWordPos);
}
return w;
}
void AlignedTableCell::setMaxValue (int maxValue, int value)
{
line1Offset = maxValue - value;
queueResize (makeParentRefInFlow (0), true);
}
} // namespace dw

51
dw/alignedtablecell.hh Normal file
View File

@ -0,0 +1,51 @@
#ifndef __DW_ALIGNEDTABLECELL_HH__
#define __DW_ALIGNEDTABLECELL_HH__
#include "core.hh"
#include "alignedtextblock.hh"
namespace dw {
class AlignedTableCell: public AlignedTextblock
{
private:
int charWordIndex, charWordPos;
protected:
int getAvailWidthOfChild (Widget *child, bool forceValue);
int getAvailHeightOfChild (Widget *child, bool forceValue);
void correctRequisitionOfChild (Widget *child,
core::Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight);
void correctExtremesOfChild (Widget *child, core::Extremes *extremes,
bool useAdjustmentWidth);
bool getAdjustMinWidth ();
bool adjustExtraSpaceWhenCorrectingRequisitionByOOF ();
int wordWrap (int wordIndex, bool wrapAll);
int getValue ();
void setMaxValue (int maxValue, int value);
public:
static int CLASS_ID;
AlignedTableCell(AlignedTableCell *ref, bool limitTextWidth);
~AlignedTableCell();
int applyPerWidth (int containerWidth, core::style::Length perWidth);
int applyPerHeight (int containerHeight, core::style::Length perHeight);
bool isBlockLevel ();
bool usesMaxGeneratorWidth ();
};
} // namespace dw
#endif // __DW_ALIGNEDTABLECELL_HH__

107
dw/alignedtextblock.cc Normal file
View File

@ -0,0 +1,107 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "alignedtextblock.hh"
#include "../lout/debug.hh"
#include <stdio.h>
namespace dw {
AlignedTextblock::List::List ()
{
textblocks = new lout::misc::SimpleVector <AlignedTextblock*> (4);
values = new lout::misc::SimpleVector <int> (4);
maxValue = 0;
refCount = 0;
}
AlignedTextblock::List::~List ()
{
delete textblocks;
delete values;
}
int AlignedTextblock::List::add(AlignedTextblock *textblock)
{
textblocks->increase ();
values->increase ();
textblocks->set (textblocks->size () - 1, textblock);
refCount++;
return textblocks->size () - 1;
}
void AlignedTextblock::List::unref(int pos)
{
assert (textblocks->get (pos) != NULL);
textblocks->set (pos, NULL);
refCount--;
if (refCount == 0)
delete this;
}
int AlignedTextblock::CLASS_ID = -1;
AlignedTextblock::AlignedTextblock (bool limitTextWidth):
Textblock (limitTextWidth)
{
DBG_OBJ_CREATE ("dw::AlignedTextblock");
registerName ("dw::AlignedTextblock", &CLASS_ID);
}
void AlignedTextblock::setRefTextblock (AlignedTextblock *ref)
{
if (ref == NULL)
list = new List();
else
list = ref->list;
listPos = list->add (this);
updateValue ();
}
AlignedTextblock::~AlignedTextblock()
{
list->unref (listPos);
DBG_OBJ_DELETE ();
}
void AlignedTextblock::updateValue ()
{
if (list) {
list->setValue (listPos, getValue ());
if (list->getValue (listPos) > list->getMaxValue ()) {
// New value greater than current maximum -> apply it to others.
list->setMaxValue (list->getValue (listPos));
for (int i = 0; i < list->size (); i++)
if (list->getTextblock (i))
list->getTextblock (i)->setMaxValue (list->getMaxValue (),
list->getValue (i));
} else {
/* No change, apply old max_value only to this page. */
setMaxValue (list->getMaxValue (), list->getValue (listPos));
}
}
}
} // namespace dw

61
dw/alignedtextblock.hh Normal file
View File

@ -0,0 +1,61 @@
#ifndef __DW_ALIGNEDTEXTBLOCK_HH__
#define __DW_ALIGNEDTEXTBLOCK_HH__
#include "core.hh"
#include "textblock.hh"
namespace dw {
/**
* \brief Base widget for all textblocks (sub classes of dw::Textblock), which
* are positioned vertically and aligned horizontally.
*/
class AlignedTextblock: public Textblock
{
private:
class List
{
private:
lout::misc::SimpleVector <AlignedTextblock*> *textblocks;
lout::misc::SimpleVector <int> *values;
int maxValue, refCount;
~List ();
public:
List ();
inline int add (AlignedTextblock *textblock);
void unref (int pos);
inline int getMaxValue () { return maxValue; }
inline void setMaxValue (int maxValue) { this->maxValue = maxValue; }
inline int size () { return textblocks->size (); }
inline AlignedTextblock *getTextblock (int pos) {
return textblocks->get (pos); }
inline int getValue (int pos) {return values->get (pos); }
inline void setValue (int pos, int value) {
return values->set (pos, value); }
};
List *list;
int listPos;
protected:
AlignedTextblock(bool limitTextWidth);
virtual int getValue () = 0;
virtual void setMaxValue (int maxValue, int value) = 0;
void setRefTextblock (AlignedTextblock *ref);
void updateValue ();
public:
static int CLASS_ID;
~AlignedTextblock();
};
} // namespace dw
#endif // __DW_ALIGNEDTEXTBLOCK_HH__

92
dw/bullet.cc Normal file
View File

@ -0,0 +1,92 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "bullet.hh"
#include <stdio.h>
namespace dw {
Bullet::Bullet ()
{
DBG_OBJ_CREATE ("dw::Bullet");
}
Bullet::~Bullet ()
{
DBG_OBJ_DELETE ();
}
void Bullet::sizeRequestSimpl (core::Requisition *requisition)
{
requisition->width = lout::misc::max (getStyle()->font->xHeight * 4 / 5, 1);
requisition->ascent = lout::misc::max (getStyle()->font->xHeight, 1);
requisition->descent = 0;
}
void Bullet::getExtremesSimpl (core::Extremes *extremes)
{
extremes->minWidth = extremes->maxWidth = extremes->adjustmentWidth =
extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic =
lout::misc::max (getStyle()->font->xHeight * 4 / 5, 1);
}
void Bullet::containerSizeChangedForChildren ()
{
DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
// Nothing to do.
DBG_OBJ_LEAVE ();
}
void Bullet::draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context)
{
int x, y, l;
bool filled = true;
l = lout::misc::min (allocation.width, allocation.ascent);
x = allocation.x;
y = allocation.y + allocation.ascent - getStyle()->font->xHeight;
switch (getStyle()->listStyleType) {
case core::style::LIST_STYLE_TYPE_SQUARE:
view->drawRectangle (getStyle()->color,
core::style::Color::SHADING_NORMAL,
false, x, y, l, l);
break;
case core::style::LIST_STYLE_TYPE_CIRCLE:
filled = false;
// Fall Through
case core::style::LIST_STYLE_TYPE_DISC:
default:
view->drawArc (getStyle()->color, core::style::Color::SHADING_NORMAL,
filled, x + l/2, y + l/2, l, l, 0, 360);
}
}
core::Iterator *Bullet::iterator (core::Content::Type mask, bool atEnd)
{
//return new core::TextIterator (this, mask, atEnd, "*");
/** \bug Not implemented. */
return new core::EmptyIterator (this, mask, atEnd);
}
} // namespace dw

31
dw/bullet.hh Normal file
View File

@ -0,0 +1,31 @@
#ifndef __BULLET_HH__
#define __BULLET_HH__
#include "core.hh"
namespace dw {
/**
* \brief Displays different kind of bullets.
*
* Perhaps, in the future, Unicode characters are used for bullets, so this
* widget is not used anymore.
*/
class Bullet: public core::Widget
{
protected:
void sizeRequestSimpl (core::Requisition *requisition);
void getExtremesSimpl (core::Extremes *extremes);
void containerSizeChangedForChildren ();
void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
core::Iterator *iterator (core::Content::Type mask, bool atEnd);
public:
Bullet ();
~Bullet ();
};
} // namespace dw
#endif // __BULLET_HH__

66
dw/core.hh Normal file
View File

@ -0,0 +1,66 @@
#ifndef __DW_CORE_HH__
#define __DW_CORE_HH__
#define __INCLUDED_FROM_DW_CORE_HH__
/**
* \brief Dw is in this namespace, or sub namespaces of this one.
*
* The core can be found in dw::core, widgets are defined directly here.
*
* \sa \ref dw-overview
*/
namespace dw {
/** Used (temporally) for code related to positioned elements. */
enum { IMPL_POS = false };
/**
* \brief The core of Dw is defined in this namespace.
*
* \sa \ref dw-overview
*/
namespace core {
typedef unsigned char byte;
class Layout;
class View;
class Widget;
class Iterator;
class StackingContextMgr;
// Nothing yet to free.
inline void freeall () { }
namespace ui {
class ResourceFactory;
} // namespace ui
} // namespace core
} // namespace dw
#include "../lout/object.hh"
#include "../lout/container.hh"
#include "../lout/signal.hh"
#include "tools.hh"
#include "types.hh"
#include "events.hh"
#include "imgbuf.hh"
#include "imgrenderer.hh"
#include "style.hh"
#include "view.hh"
#include "platform.hh"
#include "iterator.hh"
#include "findtext.hh"
#include "selection.hh"
#include "layout.hh"
#include "widget.hh"
#include "stackingcontextmgr.hh"
#include "ui.hh"
#undef __INCLUDED_FROM_DW_CORE_HH__
#endif // __DW_CORE_HH__

83
dw/events.hh Normal file
View File

@ -0,0 +1,83 @@
#ifndef __DW_EVENTS_HH__
#define __DW_EVENTS_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
/**
* \brief Platform independent representation.
*/
enum ButtonState
{
/* We won't use more than these ones. */
SHIFT_MASK = 1 << 0,
CONTROL_MASK = 1 << 1,
META_MASK = 1 << 2,
BUTTON1_MASK = 1 << 3,
BUTTON2_MASK = 1 << 4,
BUTTON3_MASK = 1 << 5
};
/**
* \brief Base class for all events.
*
* The dw::core::Event hierarchy describes events in a platform independent
* way.
*/
class Event: public lout::object::Object
{
public:
};
/**
* \brief Base class for all mouse events.
*/
class MouseEvent: public Event
{
public:
ButtonState state;
};
/**
* \brief Base class for all mouse events related to a specific position.
*/
class MousePositionEvent: public MouseEvent
{
public:
int xCanvas, yCanvas, xWidget, yWidget;
};
/**
* \brief Represents a button press or release event.
*/
class EventButton: public MousePositionEvent
{
public:
int numPressed; /* 1 for simple click, 2 for double click, etc. */
int button;
};
/**
* \brief Represents a mouse motion event.
*/
class EventMotion: public MousePositionEvent
{
};
/**
* \brief Represents a enter or leave notify event.
*/
class EventCrossing: public MouseEvent
{
public:
Widget *lastWidget, *currentWidget;
};
} // namespace core
} // namespace dw
#endif // __DW_EVENTS_HH__

298
dw/findtext.cc Normal file
View File

@ -0,0 +1,298 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "core.hh"
#include "dlib/dlib.h"
#include "../lout/debug.hh"
#include "../lout/msg.h"
namespace dw {
namespace core {
FindtextState::FindtextState ()
{
DBG_OBJ_CREATE ("dw::core::FindtextState");
key = NULL;
nexttab = NULL;
widget = NULL;
iterator = NULL;
hlIterator = NULL;
}
FindtextState::~FindtextState ()
{
if (key)
free(key);
if (nexttab)
delete[] nexttab;
if (iterator)
delete iterator;
if (hlIterator)
delete hlIterator;
DBG_OBJ_DELETE ();
}
void FindtextState::setWidget (Widget *widget)
{
this->widget = widget;
// A widget change will restart the search.
if (key)
free(key);
key = NULL;
if (nexttab)
delete[] nexttab;
nexttab = NULL;
if (iterator)
delete iterator;
iterator = NULL;
if (hlIterator)
delete hlIterator;
hlIterator = NULL;
}
FindtextState::Result FindtextState::search (const char *key, bool caseSens,
bool backwards)
{
if (!widget || *key == 0) // empty keys are not found
return NOT_FOUND;
bool wasHighlighted = unhighlight ();
bool newKey;
// If the key (or the widget) changes (including case sensitivity),
// the search is started from the beginning.
if (this->key == NULL || this->caseSens != caseSens ||
strcmp (this->key, key) != 0) {
newKey = true;
if (this->key)
free(this->key);
this->key = dStrdup (key);
this->caseSens = caseSens;
if (nexttab)
delete[] nexttab;
nexttab = createNexttab (key, caseSens, backwards);
if (iterator)
delete iterator;
iterator = new CharIterator (widget, true);
if (backwards) {
/* Go to end */
while (iterator->next () ) ;
iterator->prev (); //We don't want to be at CharIterator::END.
} else {
iterator->next ();
}
} else
newKey = false;
bool firstTrial = !wasHighlighted || newKey;
if (search0 (backwards, firstTrial)) {
// Highlighting is done with a clone.
hlIterator = iterator->cloneCharIterator ();
for (int i = 0; key[i]; i++)
hlIterator->next ();
CharIterator::highlight (iterator, hlIterator, HIGHLIGHT_FINDTEXT);
CharIterator::scrollTo (iterator, hlIterator,
HPOS_INTO_VIEW, VPOS_CENTER);
// The search will continue from the word after the found position.
iterator->next ();
return SUCCESS;
} else {
if (firstTrial) {
return NOT_FOUND;
} else {
// Nothing found anymore, reset the state for the next trial.
delete iterator;
iterator = new CharIterator (widget, true);
if (backwards) {
/* Go to end */
while (iterator->next ()) ;
iterator->prev (); //We don't want to be at CharIterator::END.
} else {
iterator->next ();
}
// We expect a success.
Result result2 = search (key, caseSens, backwards);
assert (result2 == SUCCESS);
return RESTART;
}
}
}
/**
* \brief This method is called when the user closes the "find text" dialog.
*/
void FindtextState::resetSearch ()
{
unhighlight ();
if (key)
free(key);
key = NULL;
}
/*
* Return a new string: with the reverse of the original.
*/
const char* FindtextState::rev(const char *str)
{
if (!str)
return NULL;
int len = strlen(str);
char *nstr = new char[len+1];
for (int i = 0; i < len; ++i)
nstr[i] = str[len-1 -i];
nstr[len] = 0;
return nstr;
}
int *FindtextState::createNexttab (const char *needle, bool caseSens,
bool backwards)
{
const char* key;
key = (backwards) ? rev(needle) : needle;
int i = 0;
int j = -1;
int l = strlen (key);
int *nexttab = new int[l + 1]; // + 1 is necessary for l == 1 case
nexttab[0] = -1;
do {
if (j == -1 || charsEqual (key[i], key[j], caseSens)) {
i++;
j++;
nexttab[i] = j;
//_MSG ("nexttab[%d] = %d\n", i, j);
} else
j = nexttab[j];
} while (i < l - 1);
if (backwards)
delete [] key;
return nexttab;
}
/**
* \brief Unhighlight, and return whether a region was highlighted.
*/
bool FindtextState::unhighlight ()
{
if (hlIterator) {
CharIterator *start = hlIterator->cloneCharIterator ();
for (int i = 0; key[i]; i++)
start->prev ();
CharIterator::unhighlight (start, hlIterator, HIGHLIGHT_FINDTEXT);
delete start;
delete hlIterator;
hlIterator = NULL;
return true;
} else
return false;
}
bool FindtextState::search0 (bool backwards, bool firstTrial)
{
if (iterator->getChar () == CharIterator::END)
return false;
bool ret = false;
const char* searchKey = (backwards) ? rev(key) : key;
int j = 0;
bool nextit = true;
int l = strlen (key);
if (backwards && !firstTrial) {
_MSG("Having to do.");
/* Position correctly */
/* In order to achieve good results (i.e: find a word that ends within
* the previously searched word's limit) we have to position the
* iterator in the semilast character of the previously searched word.
*
* Since we know that if a word was found before it was exactly the
* same word as the one we are searching for now, we can apply the
* following expression:
*
* Where l=length of the key and n=num of positions to move:
*
* n = l - 3
*
* If n is negative, we have to move backwards, but if it is
* positive, we have to move forward. So, when l>=4, we start moving
* the iterator forward. */
if (l==1) {
iterator->prev();
iterator->prev();
} else if (l==2) {
iterator->prev();
} else if (l>=4) {
for (int i=0; i<l-3; i++) {
iterator->next();
}
}
} else if (backwards && l==1) {
/* Particular case where we can't find the last character */
iterator->next();
}
do {
if (j == -1 || charsEqual (iterator->getChar(),searchKey[j],caseSens)) {
j++;
nextit = backwards ? iterator->prev () : iterator->next ();
} else
j = nexttab[j];
} while (nextit && j < l);
if (j >= l) {
if (backwards) {
//This is the location of the key
iterator->next();
} else {
// Go back to where the key was found.
for (int i = 0; i < l; i++)
iterator->prev ();
}
ret = true;
}
if (backwards)
delete [] searchKey;
return ret;
}
} // namespace core
} // namespace dw

84
dw/findtext.hh Normal file
View File

@ -0,0 +1,84 @@
#ifndef __DW_FINDTEXT_STATE_H__
#define __DW_FINDTEXT_STATE_H__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
#include <ctype.h>
namespace dw {
namespace core {
class FindtextState
{
public:
typedef enum {
/** \brief The next occurrence of the pattern has been found. */
SUCCESS,
/**
* \brief There is no further occurrence of the pattern, instead, the
* first occurrence has been selected.
*/
RESTART,
/** \brief The pattern does not at all occur in the text. */
NOT_FOUND
} Result;
private:
/**
* \brief The key used for the last search.
*
* If dw::core::Findtext::search is called with the same key, the search
* is continued, otherwise it is restarted.
*/
char *key;
/** \brief Whether the last search was case sensitive. */
bool caseSens;
/** \brief The table used for KMP search. */
int *nexttab;
/** \brief The top of the widget tree, in which the search is done.
*
* From this, the iterator will be constructed. Set by
* dw::core::Findtext::widget
*/
Widget *widget;
/** \brief The position from where the next search will start. */
CharIterator *iterator;
/**
* \brief The position from where the characters are highlighted.
*
* NULL, when no text is highlighted.
*/
CharIterator *hlIterator;
static const char* rev(const char* _str); /* reverse a C string */
static int *createNexttab (const char *needle,bool caseSens,bool backwards);
bool unhighlight ();
bool search0 (bool backwards, bool firstTrial);
inline static bool charsEqual (char c1, char c2, bool caseSens)
{ return caseSens ? c1 == c2 : tolower (c1) == tolower (c2) ||
(isspace (c1) && isspace (c2)); }
public:
FindtextState ();
~FindtextState ();
void setWidget (Widget *widget);
Result search (const char *key, bool caseSens, bool backwards);
void resetSearch ();
};
} // namespace core
} // namespace dw
#endif // __DW_FINDTEXT_STATE_H__

136
dw/fltkcomplexbutton.cc Normal file
View File

@ -0,0 +1,136 @@
// fltkcomplexbutton.cc is derived from src/Fl_Button.cxx from FLTK's 1.3
// branch at http://fltk.org in early 2011.
// src/Fl_Button.cxx is Copyright 1998-2010 by Bill Spitzak and others.
/*
* 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.
*
* 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 <FL/Fl.H>
#include <FL/Fl_Window.H>
#include "fltkcomplexbutton.hh"
using namespace dw::fltk::ui;
/**
Sets the current value of the button.
A non-zero value sets the button to 1 (ON), and zero sets it to 0 (OFF).
\param[in] v button value.
*/
int ComplexButton::value(int v) {
v = v ? 1 : 0;
oldval = v;
clear_changed();
if (value_ != v) {
value_ = v;
if (box()) redraw();
return 1;
} else {
return 0;
}
}
void ComplexButton::draw() {
Fl_Color col = value() ? selection_color() : color();
draw_box(value() ? (down_box()?down_box():fl_down(box())) : box(), col);
// ComplexButton is a Group; draw its children
for (int i = children () - 1; i >= 0; i--) {
// set absolute coordinates for fltk-1.3 --jcid
child (i)->position(x()+(w()-child(i)->w())/2,y()+(h()-child(i)->h())/2);
draw_child (*child (i));
}
if (Fl::focus() == this) draw_focus();
}
int ComplexButton::handle(int event) {
int newval;
switch (event) {
case FL_ENTER: /* FALLTHROUGH */
case FL_LEAVE:
return 1;
case FL_PUSH:
if (Fl::visible_focus() && handle(FL_FOCUS)) Fl::focus(this);
/* fallthrough */
case FL_DRAG:
if (Fl::event_inside(this)) {
newval = !oldval;
} else
{
clear_changed();
newval = oldval;
}
if (newval != value_) {
value_ = newval;
set_changed();
redraw();
}
return 1;
case FL_RELEASE:
if (value_ == oldval) {
return 1;
}
set_changed();
value(oldval);
set_changed();
if (when() & FL_WHEN_RELEASE) do_callback();
return 1;
case FL_FOCUS : /* FALLTHROUGH */
case FL_UNFOCUS :
if (Fl::visible_focus()) {
redraw();
return 1;
} else return 0;
case FL_KEYBOARD :
if (Fl::focus() == this &&
(Fl::event_key() == ' ' || Fl::event_key() == FL_Enter) &&
!(Fl::event_state() & (FL_SHIFT | FL_CTRL | FL_ALT | FL_META))) {
value(1);
set_changed();
if (when() & FL_WHEN_RELEASE) do_callback();
return 1;
} else return 0;
case FL_KEYUP:
if (Fl::focus() == this &&
(Fl::event_key() == ' ' || Fl::event_key() == FL_Enter)) {
value(0);
return 1;
}
/* fallthrough */
default:
return 0;
}
}
/**
The constructor creates the button using the given position, size and label.
\param[in] X, Y, W, H position and size of the widget
\param[in] L widget label, default is no label
*/
ComplexButton::ComplexButton(int X, int Y, int W, int H, const char *L)
: Fl_Group(X,Y,W,H,L) {
Fl_Group::current(0);
box(FL_UP_BOX);
down_box(FL_NO_BOX);
value_ = oldval = 0;
}
ComplexButton::~ComplexButton() {
/*
* The Fl_Group destructor clear()s the children, but layout expects
* the flat view to be around until it deletes it.
*/
remove(0);
}

72
dw/fltkcomplexbutton.hh Normal file
View File

@ -0,0 +1,72 @@
// fltkcomplexbutton.hh is derived from FL/Fl_Button.H from FLTK's 1.3 branch
// at http://fltk.org in early 2011.
// FL/Fl_Button.H is Copyright 1998-2010 by Bill Spitzak and others.
/*
* 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.
*
* 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/>.
*/
#ifndef __FLTK_COMPLEX_BUTTON_HH__
#define __FLTK_COMPLEX_BUTTON_HH__
#include <FL/Fl_Group.H>
namespace dw {
namespace fltk {
namespace ui {
class ComplexButton : public Fl_Group {
char value_;
char oldval;
uchar down_box_;
protected:
virtual void draw();
public:
virtual int handle(int);
ComplexButton(int X, int Y, int W, int H, const char *L = 0);
~ComplexButton();
int value(int v);
/**
Returns the current value of the button (0 or 1).
*/
char value() const {return value_;}
/**
Returns the current down box type, which is drawn when value() is non-zero.
\retval Fl_Boxtype
*/
Fl_Boxtype down_box() const {return (Fl_Boxtype)down_box_;}
/**
Sets the down box type. The default value of 0 causes FLTK to figure out
the correct matching down version of box().
\param[in] b down box type
*/
void down_box(Fl_Boxtype b) {down_box_ = b;}
};
} // namespace ui
} // namespace fltk
} // namespace dw
#endif
//
//

36
dw/fltkcore.hh Normal file
View File

@ -0,0 +1,36 @@
#ifndef __DW_FLTK_CORE_HH__
#define __DW_FLTK_CORE_HH__
#define __INCLUDED_FROM_DW_FLTK_CORE_HH__
namespace dw {
namespace fltk {
namespace ui {
class FltkResource;
} // namespace ui
} // namespace fltk
} // namespace dw
#include <FL/Fl_Widget.H>
#include "core.hh"
#include "fltkimgbuf.hh"
#include "fltkplatform.hh"
#include "fltkui.hh"
namespace dw {
namespace fltk {
inline void freeall ()
{
FltkImgbuf::freeall ();
}
} // namespace fltk
} // namespace dw
#undef __INCLUDED_FROM_DW_FLTK_CORE_HH__
#endif // __DW_FLTK_CORE_HH__

110
dw/fltkflatview.cc Normal file
View File

@ -0,0 +1,110 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "fltkflatview.hh"
#include "../lout/debug.hh"
#include <stdio.h>
using namespace lout::container::typed;
namespace dw {
namespace fltk {
FltkFlatView::FltkFlatView (int x, int y, int w, int h, const char *label):
FltkWidgetView (x, y, w, h, label)
{
DBG_OBJ_CREATE ("dw::fltk::FltkFlatView");
}
FltkFlatView::~FltkFlatView ()
{
}
void FltkFlatView::setCanvasSize (int width, int ascent, int descent)
{
/**
* \bug It has to be clarified, who is responsible for setting the
* FLTK widget size. In the only used context (complex buttons),
* it is done elsewhere.
*/
#if 0
FltkWidgetView::setCanvasSize (width, ascent, descent);
w (width);
h (ascent + descent);
#endif
}
bool FltkFlatView::usesViewport ()
{
return false;
}
int FltkFlatView::getHScrollbarThickness ()
{
return 0;
}
int FltkFlatView::getVScrollbarThickness ()
{
return 0;
}
int FltkFlatView::getScrollbarOnLeft ()
{
return 0;
}
void FltkFlatView::scrollTo (int x, int y)
{
}
void FltkFlatView::setViewportSize (int width, int height,
int hScrollbarThickness,
int vScrollbarThickness)
{
}
int FltkFlatView::translateViewXToCanvasX (int X)
{
return X - x ();
}
int FltkFlatView::translateViewYToCanvasY (int Y)
{
return Y - y ();
}
int FltkFlatView::translateCanvasXToViewX (int X)
{
return X + x ();
}
int FltkFlatView::translateCanvasYToViewY (int Y)
{
return Y + y ();
}
} // namespace fltk
} // namespace dw

38
dw/fltkflatview.hh Normal file
View File

@ -0,0 +1,38 @@
#ifndef __DW_FLTKFLATVIEW_HH__
#define __DW_FLTKFLATVIEW_HH__
#include "core.hh"
#include "fltkcore.hh"
#include "fltkviewbase.hh"
namespace dw {
namespace fltk {
class FltkFlatView: public FltkWidgetView
{
protected:
int translateViewXToCanvasX (int x);
int translateViewYToCanvasY (int y);
int translateCanvasXToViewX (int x);
int translateCanvasYToViewY (int y);
public:
FltkFlatView (int x, int y, int w, int h, const char *label = 0);
~FltkFlatView ();
void setCanvasSize (int width, int ascent, int descent);
bool usesViewport ();
int getHScrollbarThickness ();
int getVScrollbarThickness ();
int getScrollbarOnLeft ();
void scrollTo (int x, int y);
void setViewportSize (int width, int height,
int hScrollbarThickness, int vScrollbarThickness);
};
} // namespace fltk
} // namespace dw
#endif // __DW_FLTKFLATVIEW_HH__

614
dw/fltkimgbuf.cc Normal file
View File

@ -0,0 +1,614 @@
/*
* Dillo Widget
*
* Copyright 2005-2007, 2012-2013 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.
*
* 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 "fltkcore.hh"
#include "../lout/msg.h"
#include "../lout/misc.hh"
#include <FL/fl_draw.H>
#include <math.h>
#define IMAGE_MAX_AREA (6000 * 6000)
#define MAX_WIDTH 0x8000
#define MAX_HEIGHT 0x8000
namespace dw {
namespace fltk {
using namespace lout::container::typed;
const enum ScaleMode { SIMPLE, BEAUTIFUL, BEAUTIFUL_GAMMA }
scaleMode = BEAUTIFUL_GAMMA;
Vector <FltkImgbuf::GammaCorrectionTable> *FltkImgbuf::gammaCorrectionTables
= new Vector <FltkImgbuf::GammaCorrectionTable> (true, 2);
uchar *FltkImgbuf::findGammaCorrectionTable (double gamma)
{
// Since the number of possible keys is low, a linear search is
// sufficiently fast.
for (int i = 0; i < gammaCorrectionTables->size(); i++) {
GammaCorrectionTable *gct = gammaCorrectionTables->get(i);
if (gct->gamma == gamma)
return gct->map;
}
_MSG("Creating new table for gamma = %g\n", gamma);
GammaCorrectionTable *gct = new GammaCorrectionTable();
gct->gamma = gamma;
for (int i = 0; i < 256; i++)
gct->map[i] = 255 * pow((double)i / 255, gamma);
gammaCorrectionTables->put (gct);
return gct->map;
}
bool FltkImgbuf::excessiveImageDimensions (int width, int height)
{
return width <= 0 || height <= 0 ||
width > IMAGE_MAX_AREA / height;
}
void FltkImgbuf::freeall ()
{
_MSG("Deleting gammaCorrectionTables\n");
delete gammaCorrectionTables;
gammaCorrectionTables = NULL;
}
FltkImgbuf::FltkImgbuf (Type type, int width, int height, double gamma)
{
DBG_OBJ_CREATE ("dw::fltk::FltkImgbuf");
_MSG ("FltkImgbuf::FltkImgbuf: new root %p\n", this);
init (type, width, height, gamma, NULL);
}
FltkImgbuf::FltkImgbuf (Type type, int width, int height, double gamma,
FltkImgbuf *root)
{
DBG_OBJ_CREATE ("dw::fltk::FltkImgbuf");
_MSG ("FltkImgbuf::FltkImgbuf: new scaled %p, root is %p\n", this, root);
init (type, width, height, gamma, root);
}
void FltkImgbuf::init (Type type, int width, int height, double gamma,
FltkImgbuf *root)
{
if (excessiveImageDimensions (width, height)) {
// Excessive image sizes which would cause crashes due to too
// big allocations for the image buffer (for root buffers, when
// the image was specially prepared). In this case we use a 1 x
// 1 size.
MSG("FltkImgbuf::init: suspicious image size request %d x %d\n",
width, height);
init (type, 1, 1, gamma, root);
} else if (width > MAX_WIDTH) {
// Too large dimensions cause dangerous overflow errors, so we
// limit dimensions to harmless values.
//
// Example: 65535 * 65536 / 65536 (see scaling below) results in
// the negative value -1.
MSG("FltkImgbuf::init: cannot handle large width %d\n", width);
init (type, MAX_WIDTH, height, gamma, root);
} else if (height > MAX_HEIGHT) {
MSG("FltkImgbuf::init: cannot handle large height %d\n", height);
init (type, width, MAX_HEIGHT, gamma, root);
} else if (gamma <= 0) {
MSG("FltkImgbuf::init: non-positive gamma %g\n", gamma);
init (type, width, height, 1, root);
} else {
this->root = root;
this->type = type;
this->width = width;
this->height = height;
this->gamma = gamma;
DBG_OBJ_SET_NUM ("width", width);
DBG_OBJ_SET_NUM ("height", height);
// TODO: Maybe this is only for root buffers
switch (type) {
case RGBA: bpp = 4; break;
case RGB: bpp = 3; break;
default: bpp = 1; break;
}
_MSG("FltkImgbuf::init this=%p width=%d height=%d bpp=%d gamma=%g\n",
this, width, height, bpp, gamma);
rawdata = new uchar[bpp * width * height];
// Set light-gray as interim background color.
memset(rawdata, 222, width*height*bpp);
refCount = 1;
deleteOnUnref = true;
copiedRows = new lout::misc::BitSet (height);
DBG_IF_RTFL {
lout::misc::StringBuffer sb;
copiedRows->intoStringBuffer (&sb);
DBG_OBJ_SET_SYM ("copiedRows", sb.getChars ());
}
// The list is only used for root buffers.
if (isRoot())
scaledBuffers = new lout::container::typed::List <FltkImgbuf> (true);
else
scaledBuffers = NULL;
if (!isRoot()) {
// Scaling
for (int row = 0; row < root->height; row++) {
if (root->copiedRows->get (row))
scaleRow (row, root->rawdata + row*root->width*root->bpp);
}
}
}
}
FltkImgbuf::~FltkImgbuf ()
{
_MSG ("FltkImgbuf::~FltkImgbuf\n");
if (!isRoot())
root->detachScaledBuf (this);
delete[] rawdata;
delete copiedRows;
if (scaledBuffers)
delete scaledBuffers;
DBG_OBJ_DELETE ();
}
/**
* \brief This method is called for the root buffer, when a scaled buffer
* removed.
*/
void FltkImgbuf::detachScaledBuf (FltkImgbuf *scaledBuf)
{
scaledBuffers->detachRef (scaledBuf);
_MSG("FltkImgbuf[root %p]: scaled buffer %p is detached, %d left\n",
this, scaledBuf, scaledBuffers->size ());
if (refCount == 0 && scaledBuffers->isEmpty () && deleteOnUnref)
// If the root buffer is not used anymore, but this is the last scaled
// buffer.
// See also: FltkImgbuf::unref().
delete this;
}
void FltkImgbuf::setCMap (int *colors, int num_colors)
{
}
inline void FltkImgbuf::scaleRow (int row, const core::byte *data)
{
if (row < root->height) {
if (scaleMode == SIMPLE)
scaleRowSimple (row, data);
else
scaleRowBeautiful (row, data);
}
}
inline void FltkImgbuf::scaleRowSimple (int row, const core::byte *data)
{
int sr1 = scaledY (row);
int sr2 = scaledY (row + 1);
for (int sr = sr1; sr < sr2; sr++) {
// Avoid multiple passes.
if (copiedRows->get(sr)) continue;
copiedRows->set (sr, true);
DBG_IF_RTFL {
lout::misc::StringBuffer sb;
copiedRows->intoStringBuffer (&sb);
DBG_OBJ_SET_SYM ("copiedRows", sb.getChars ());
}
if (sr == sr1) {
for (int px = 0; px < root->width; px++) {
int px1 = px * width / root->width;
int px2 = (px+1) * width / root->width;
for (int sp = px1; sp < px2; sp++) {
memcpy(rawdata + (sr*width + sp)*bpp, data + px*bpp, bpp);
}
}
} else {
memcpy(rawdata + sr*width*bpp, rawdata + sr1*width*bpp, width*bpp);
}
}
}
inline void FltkImgbuf::scaleRowBeautiful (int row, const core::byte *data)
{
int sr1 = scaledY (row);
int sr2 = scaledY (row + 1);
bool allRootRows = false;
// Don't rescale rows!
if (copiedRows->get(sr1)) return;
if (height > root->height) {
scaleBuffer (data, root->width, 1,
rawdata + sr1 * width * bpp, width, sr2 - sr1,
bpp, gamma);
// Mark scaled rows done
for (int sr = sr1; sr < sr2 || sr == sr1; sr++)
copiedRows->set (sr, true);
DBG_IF_RTFL {
lout::misc::StringBuffer sb;
copiedRows->intoStringBuffer (&sb);
DBG_OBJ_SET_SYM ("copiedRows", sb.getChars ());
}
} else {
assert (sr1 == sr2 || sr1 + 1 == sr2);
int row1 = backscaledY(sr1), row2 = backscaledY(sr1 + 1);
// Check all the necessary root lines already arrived,
// a larger area than a single row may be accessed here.
for (int r=row1; (allRootRows=root->copiedRows->get(r)) && ++r < row2; );
if (allRootRows) {
scaleBuffer (root->rawdata + row1 * root->width * bpp,
root->width, row2 - row1,
rawdata + sr1 * width * bpp, width, 1,
bpp, gamma);
// Mark scaled row done
copiedRows->set (sr1, true);
DBG_IF_RTFL {
lout::misc::StringBuffer sb;
copiedRows->intoStringBuffer (&sb);
DBG_OBJ_SET_SYM ("copiedRows", sb.getChars ());
}
}
}
}
/**
* General method to scale an image buffer. Used to scale single lines
* in scaleRowBeautiful.
*
* The algorithm is rather simple. If the scaled buffer is smaller
* (both width and height) than the original buffer, each pixel in the
* scaled buffer is assigned a rectangle of pixels in the original
* buffer; the resulting pixel value (red, green, blue) is simply the
* average of all pixel values. This is pretty fast and leads to
* rather good results.
*
* Nothing special (like interpolation) is done when scaling up.
*
* If scaleMode is set to BEAUTIFUL_GAMMA, gamma correction is
* considered, see <http://www.4p8.com/eric.brasseur/gamma.html>.
*
* TODO Could be optimized as in scaleRowSimple: when the destination
* image is larger, calculate only one row/column, and copy it to the
* other rows/columns.
*/
inline void FltkImgbuf::scaleBuffer (const core::byte *src, int srcWidth,
int srcHeight, core::byte *dest,
int destWidth, int destHeight, int bpp,
double gamma)
{
uchar *gammaMap1, *gammaMap2;
if (scaleMode == BEAUTIFUL_GAMMA) {
gammaMap1 = findGammaCorrectionTable (gamma);
gammaMap2 = findGammaCorrectionTable (1 / gamma);
}
int *v = new int[bpp];
for(int x = 0; x < destWidth; x++) {
for(int y = 0; y < destHeight; y++) {
int xo1 = x * srcWidth / destWidth;
int xo2 = lout::misc::max ((x + 1) * srcWidth / destWidth, xo1 + 1);
int yo1 = y * srcHeight / destHeight;
int yo2 = lout::misc::max ((y + 1) * srcHeight / destHeight, yo1 + 1);
int n = (xo2 - xo1) * (yo2 - yo1);
for(int i = 0; i < bpp; i++)
v[i] = 0;
for(int xo = xo1; xo < xo2; xo++)
for(int yo = yo1; yo < yo2; yo++) {
const core::byte *ps = src + bpp * (yo * srcWidth + xo);
for(int i = 0; i < bpp; i++)
v[i] +=
(scaleMode == BEAUTIFUL_GAMMA ? gammaMap2[ps[i]] : ps[i]);
}
core::byte *pd = dest + bpp * (y * destWidth + x);
for(int i = 0; i < bpp; i++)
pd[i] =
scaleMode == BEAUTIFUL_GAMMA ? gammaMap1[v[i] / n] : v[i] / n;
}
}
delete[] v;
}
void FltkImgbuf::copyRow (int row, const core::byte *data)
{
assert (isRoot());
if (row < height) {
// Flag the row done and copy its data.
copiedRows->set (row, true);
DBG_IF_RTFL {
lout::misc::StringBuffer sb;
copiedRows->intoStringBuffer (&sb);
DBG_OBJ_SET_SYM ("copiedRows", sb.getChars ());
}
memcpy(rawdata + row * width * bpp, data, width * bpp);
// Update all the scaled buffers of this root image.
for (Iterator <FltkImgbuf> it = scaledBuffers->iterator();
it.hasNext(); ) {
FltkImgbuf *sb = it.getNext ();
sb->scaleRow (row, data);
}
}
}
void FltkImgbuf::newScan ()
{
if (isRoot()) {
for (Iterator<FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext();){
FltkImgbuf *sb = it.getNext ();
sb->copiedRows->clear();
DBG_IF_RTFL {
lout::misc::StringBuffer sb;
copiedRows->intoStringBuffer (&sb);
DBG_OBJ_SET_SYM ("copiedRows", sb.getChars ());
}
}
}
}
core::Imgbuf* FltkImgbuf::getScaledBuf (int width, int height)
{
if (!isRoot())
return root->getScaledBuf (width, height);
if (width > MAX_WIDTH) {
// Similar to init.
MSG("FltkImgbuf::getScaledBuf: cannot handle large width %d\n", width);
return getScaledBuf (MAX_WIDTH, height);
}
if (height > MAX_HEIGHT) {
MSG("FltkImgbuf::getScaledBuf: cannot handle large height %d\n", height);
return getScaledBuf (width, MAX_HEIGHT);
}
if (width == this->width && height == this->height) {
ref ();
return this;
}
for (Iterator <FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext(); ) {
FltkImgbuf *sb = it.getNext ();
if (sb->width == width && sb->height == height) {
sb->ref ();
return sb;
}
}
// Check for excessive image sizes which would cause crashes due to
// too big allocations for the image buffer. In this case we return
// a pointer to the unscaled image buffer.
if (excessiveImageDimensions (width, height)) {
MSG("FltkImgbuf::getScaledBuf: suspicious image size request %d x %d\n",
width, height);
ref ();
return this;
}
// This size is not yet used, so a new buffer has to be created.
FltkImgbuf *sb = new FltkImgbuf (type, width, height, gamma, this);
scaledBuffers->append (sb);
DBG_OBJ_ASSOC_CHILD (sb);
return sb;
}
void FltkImgbuf::getRowArea (int row, dw::core::Rectangle *area)
{
// TODO: May have to be adjusted.
if (isRoot()) {
/* root buffer */
area->x = 0;
area->y = row;
area->width = width;
area->height = 1;
_MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n",
area->x, area->y, area->width, area->height);
} else {
if (row > root->height)
area->x = area->y = area->width = area->height = 0;
else {
// scaled buffer
int sr1 = scaledY (row);
int sr2 = scaledY (row + 1);
area->x = 0;
area->y = sr1;
area->width = width;
area->height = sr2 - sr1;
_MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n",
area->x, area->y, area->width, area->height);
}
}
}
int FltkImgbuf::getRootWidth ()
{
return root ? root->width : width;
}
int FltkImgbuf::getRootHeight ()
{
return root ? root->height : height;
}
core::Imgbuf *FltkImgbuf::createSimilarBuf (int width, int height)
{
return new FltkImgbuf (type, width, height, gamma);
}
void FltkImgbuf::copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot,
int xSrc, int ySrc, int widthSrc, int heightSrc)
{
FltkImgbuf *fDest = (FltkImgbuf*)dest;
assert (bpp == fDest->bpp);
int xSrc2 = lout::misc::min (xSrc + widthSrc, fDest->width - xDestRoot);
int ySrc2 = lout::misc::min (ySrc + heightSrc, fDest->height - yDestRoot);
//printf ("copying from (%d, %d), %d x %d to (%d, %d) (root) => "
// "xSrc2 = %d, ySrc2 = %d\n",
// xSrc, ySrc, widthSrc, heightSrc, xDestRoot, yDestRoot,
// xSrc2, ySrc2);
for (int x = xSrc; x < xSrc2; x++)
for (int y = ySrc; y < ySrc2; y++) {
int iSrc = x + width * y;
int iDest = xDestRoot + x + fDest->width * (yDestRoot + y);
//printf (" (%d, %d): %d -> %d\n", x, y, iSrc, iDest);
for (int b = 0; b < bpp; b++)
fDest->rawdata[bpp * iDest + b] = rawdata[bpp * iSrc + b];
}
}
void FltkImgbuf::ref ()
{
refCount++;
//if (root)
// MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n",
// this, root, refCount);
//else
// MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount);
}
void FltkImgbuf::unref ()
{
//if (root)
// MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n",
// this, root, refCount - 1);
//else
// MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount - 1);
if (--refCount == 0) {
if (isRoot ()) {
// Root buffer, it must be ensured that no scaled buffers are left.
// See also FltkImgbuf::detachScaledBuf().
if (scaledBuffers->isEmpty () && deleteOnUnref) {
delete this;
} else {
_MSG("FltkImgbuf[root %p]: not deleted. numScaled=%d\n",
this, scaledBuffers->size ());
}
} else
// Scaled buffer buffer, simply delete it.
delete this;
}
}
bool FltkImgbuf::lastReference ()
{
return refCount == 1 &&
(scaledBuffers == NULL || scaledBuffers->isEmpty ());
}
void FltkImgbuf::setDeleteOnUnref (bool deleteOnUnref)
{
assert (isRoot ());
this->deleteOnUnref = deleteOnUnref;
}
bool FltkImgbuf::isReferred ()
{
return refCount != 0 ||
(scaledBuffers != NULL && !scaledBuffers->isEmpty ());
}
int FltkImgbuf::scaledY(int ySrc)
{
// TODO: May have to be adjusted.
assert (root != NULL);
return ySrc * height / root->height;
}
int FltkImgbuf::backscaledY(int yScaled)
{
assert (root != NULL);
// Notice that rounding errors because of integers do not play a
// role. This method cannot be the exact inverse of scaledY, since
// scaleY is not bijective, and so not invertible. Instead, both
// values always return the smallest value.
return yScaled * root->height / height;
}
void FltkImgbuf::draw (Fl_Widget *target, int xRoot, int yRoot,
int x, int y, int width, int height)
{
// TODO: Clarify the question, whether "target" is the current widget
// (and so has not to be passed at all).
_MSG("::draw: xRoot=%d x=%d yRoot=%d y=%d width=%d height=%d\n"
" this->width=%d this->height=%d\n",
xRoot, x, yRoot, y, width, height, this->width, this->height);
if (x > this->width || y > this->height) {
return;
}
if (x + width > this->width) {
width = this->width - x;
}
if (y + height > this->height) {
height = this->height - y;
}
fl_draw_image(rawdata+bpp*(y*this->width + x), xRoot + x, yRoot + y, width,
height, bpp, this->width * bpp);
}
} // namespace fltk
} // namespace dw

93
dw/fltkimgbuf.hh Normal file
View File

@ -0,0 +1,93 @@
#ifndef __DW_FLTKIMGBUF_HH__
#define __DW_FLTKIMGBUF_HH__
#ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__
# error Do not include this file directly, use "fltkcore.hh" instead.
#endif
namespace dw {
namespace fltk {
class FltkImgbuf: public core::Imgbuf
{
private:
class GammaCorrectionTable: public lout::object::Object
{
public:
double gamma;
uchar map[256];
};
FltkImgbuf *root;
int refCount;
bool deleteOnUnref;
lout::container::typed::List <FltkImgbuf> *scaledBuffers;
int width, height;
Type type;
double gamma;
//{
int bpp;
uchar *rawdata;
//}
// This is just for testing drawing, it has to be replaced by
// the image buffer.
lout::misc::BitSet *copiedRows;
static lout::container::typed::Vector <GammaCorrectionTable>
*gammaCorrectionTables;
static uchar *findGammaCorrectionTable (double gamma);
static bool excessiveImageDimensions (int width, int height);
FltkImgbuf (Type type, int width, int height, double gamma,
FltkImgbuf *root);
void init (Type type, int width, int height, double gamma, FltkImgbuf *root);
int scaledY(int ySrc);
int backscaledY(int yScaled);
int isRoot() { return (root == NULL); }
void detachScaledBuf (FltkImgbuf *scaledBuf);
protected:
~FltkImgbuf ();
public:
FltkImgbuf (Type type, int width, int height, double gamma);
static void freeall ();
void setCMap (int *colors, int num_colors);
inline void scaleRow (int row, const core::byte *data);
inline void scaleRowSimple (int row, const core::byte *data);
inline void scaleRowBeautiful (int row, const core::byte *data);
inline static void scaleBuffer (const core::byte *src, int srcWidth,
int srcHeight, core::byte *dest,
int destWidth, int destHeight, int bpp,
double gamma);
void newScan ();
void copyRow (int row, const core::byte *data);
core::Imgbuf* getScaledBuf (int width, int height);
void getRowArea (int row, dw::core::Rectangle *area);
int getRootWidth ();
int getRootHeight ();
core::Imgbuf *createSimilarBuf (int width, int height);
void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot,
int xSrc, int ySrc, int widthSrc, int heightSrc);
void ref ();
void unref ();
bool lastReference ();
void setDeleteOnUnref (bool deleteOnUnref);
bool isReferred ();
void draw (Fl_Widget *target, int xRoot, int yRoot,
int x, int y, int width, int height);
};
} // namespace fltk
} // namespace dw
#endif // __DW_FLTK_IMGBUF_HH__

48
dw/fltkmisc.cc Normal file
View File

@ -0,0 +1,48 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "../lout/msg.h"
#include "fltkmisc.hh"
#include <FL/Fl.H>
#include <stdio.h>
namespace dw {
namespace fltk {
namespace misc {
int screenWidth ()
{
return Fl::w ();
}
int screenHeight ()
{
return Fl::h ();
}
void warpPointer (int x, int y)
{
MSG_ERR("no warpPointer mechanism available.\n");
}
} // namespace misc
} // namespace fltk
} // namespace dw

22
dw/fltkmisc.hh Normal file
View File

@ -0,0 +1,22 @@
#ifndef __FLTKMISC_HH__
#define __FLTKMISC_HH__
namespace dw {
namespace fltk {
/**
* \brief Miscellaneous FLTK stuff.
*/
namespace misc {
int screenWidth ();
int screenHeight ();
void warpPointer (int x, int y);
} // namespace misc
} // namespace fltk
} // namespace dw
#endif // __FLTKMISC_HH__

732
dw/fltkplatform.cc Normal file
View File

@ -0,0 +1,732 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "dlib/dlib.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;
zeroWidth = (int) fl_width("0");
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 = dStrdup (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)
{
return new ui::FltkComplexButtonResource (platform, widget, relief);
}
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,
const char *placeholder)
{
return new ui::FltkEntryResource (platform, size, password, label,
placeholder);
}
core::ui::MultiLineTextResource *
FltkPlatform::FltkResourceFactory::createMultiLineTextResource (int cols,
int rows,
const char *placeholder)
{
return new ui::FltkMultiLineTextResource (platform, cols, rows,placeholder);
}
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",
(void *) this->view, (void *) 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

188
dw/fltkplatform.hh Normal file
View File

@ -0,0 +1,188 @@
#ifndef __DW_FLTKPLATFORM_HH__
#define __DW_FLTKPLATFORM_HH__
#ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__
# error Do not include this file directly, use "fltkcore.hh" instead.
#endif
namespace dw {
/**
* \brief This namespace contains FLTK implementations of Dw interfaces.
*/
namespace fltk {
class FltkFont: public core::style::Font
{
class FontFamily: public lout::object::Object {
Fl_Font font[4];
public:
FontFamily (Fl_Font fontNormal, Fl_Font fontBold,
Fl_Font fontItalic, Fl_Font fontBoldItalic);
void set (Fl_Font, int attrs);
Fl_Font get (int attrs);
};
static FontFamily standardFontFamily;
static lout::container::typed::HashTable <lout::object::ConstString,
FontFamily> *systemFonts;
static lout::container::typed::HashTable <dw::core::style::FontAttrs,
FltkFont> *fontsTable;
FltkFont (core::style::FontAttrs *attrs);
~FltkFont ();
static void initSystemFonts ();
public:
Fl_Font font;
static FltkFont *create (core::style::FontAttrs *attrs);
static bool fontExists (const char *name);
static Fl_Font get (const char *name, int attrs);
};
class FltkColor: public core::style::Color
{
static lout::container::typed::HashTable <dw::core::style::ColorAttrs,
FltkColor> *colorsTable;
FltkColor (int color);
~FltkColor ();
public:
int colors[SHADING_NUM];
static FltkColor *create(int color);
};
class FltkTooltip: public core::style::Tooltip
{
private:
FltkTooltip (const char *text);
~FltkTooltip ();
public:
static FltkTooltip *create(const char *text);
static void cancel();
void onEnter();
void onLeave();
void onMotion();
};
/**
* \brief This interface adds some more methods for all flkt-based views.
*/
class FltkView: public core::View
{
public:
virtual bool usesFltkWidgets () = 0;
virtual void addFltkWidget (Fl_Widget *widget,
core::Allocation *allocation);
virtual void removeFltkWidget (Fl_Widget *widget);
virtual void allocateFltkWidget (Fl_Widget *widget,
core::Allocation *allocation);
virtual void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area);
};
class FltkPlatform: public core::Platform
{
private:
class FltkResourceFactory: public core::ui::ResourceFactory
{
private:
FltkPlatform *platform;
public:
inline void setPlatform (FltkPlatform *platform) {
this->platform = platform; }
core::ui::LabelButtonResource *createLabelButtonResource (const char
*label);
core::ui::ComplexButtonResource *
createComplexButtonResource (core::Widget *widget, bool relief);
core::ui::ListResource *
createListResource (core::ui::ListResource::SelectionMode selectionMode,
int rows);
core::ui::OptionMenuResource *createOptionMenuResource ();
core::ui::EntryResource *createEntryResource (int size, bool password,
const char *label,
const char *placeholder);
core::ui::MultiLineTextResource *createMultiLineTextResource (int cols,
int rows,
const char *placeholder);
core::ui::CheckButtonResource *createCheckButtonResource (bool
activated);
core::ui::RadioButtonResource *
createRadioButtonResource (core::ui::RadioButtonResource
*groupedWith, bool activated);
};
FltkResourceFactory resourceFactory;
class IdleFunc: public lout::object::Object
{
public:
int id;
void (core::Layout::*func) ();
};
core::Layout *layout;
lout::container::typed::List <IdleFunc> *idleQueue;
bool idleFuncRunning;
int idleFuncId;
static void generalStaticIdle(void *data);
void generalIdle();
FltkView *view;
lout::container::typed::List <ui::FltkResource> *resources;
public:
FltkPlatform ();
~FltkPlatform ();
void setLayout (core::Layout *layout);
void attachView (core::View *view);
void detachView (core::View *view);
int textWidth (core::style::Font *font, const char *text, int len);
char *textToUpper (const char *text, int len);
char *textToLower (const char *text, int len);
int nextGlyph (const char *text, int idx);
int prevGlyph (const char *text, int idx);
float dpiX ();
float dpiY ();
int addIdle (void (core::Layout::*func) ());
void removeIdle (int idleId);
core::style::Font *createFont (core::style::FontAttrs *attrs,
bool tryEverything);
bool fontExists (const char *name);
core::style::Color *createColor (int color);
core::style::Tooltip *createTooltip (const char *text);
void cancelTooltip();
core::Imgbuf *createImgbuf (core::Imgbuf::Type type, int width, int height,
double gamma);
void copySelection(const char *text);
core::ui::ResourceFactory *getResourceFactory ();
void attachResource (ui::FltkResource *resource);
void detachResource (ui::FltkResource *resource);
};
} // namespace fltk
} // namespace dw
#endif // __DW_FLTKPLATFORM_HH__

311
dw/fltkpreview.cc Normal file
View File

@ -0,0 +1,311 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "../lout/msg.h"
#include "fltkpreview.hh"
#include "fltkmisc.hh"
#include <FL/Fl.H>
#include <FL/Fl_Bitmap.H>
#include <FL/fl_draw.H>
#include <stdio.h>
#include "preview.xbm"
namespace dw {
namespace fltk {
FltkPreview::FltkPreview (int x, int y, int w, int h,
dw::core::Layout *layout, const char *label):
FltkViewBase (x, y, w, h, label)
{
layout->attachView (this);
scrollX = 0;
scrollY = 0;
scrollWidth = 1;
scrollHeight = 1;
}
FltkPreview::~FltkPreview ()
{
}
int FltkPreview::handle (int event)
{
return FltkViewBase::handle (event);
}
int FltkPreview::translateViewXToCanvasX (int x)
{
return x * canvasWidth / w ();
}
int FltkPreview::translateViewYToCanvasY (int y)
{
return y * canvasHeight / h ();
}
int FltkPreview::translateCanvasXToViewX (int x)
{
return x * w () / canvasWidth;
}
int FltkPreview::translateCanvasYToViewY (int y)
{
return y * h () / canvasHeight;
}
void FltkPreview::setCanvasSize (int width, int ascent, int descent)
{
FltkViewBase::setCanvasSize (width, ascent, descent);
if (parent() && parent()->visible ())
((FltkPreviewWindow*)parent())->reallocate ();
}
bool FltkPreview::usesViewport ()
{
return true;
}
int FltkPreview::getHScrollbarThickness ()
{
return 0;
}
int FltkPreview::getVScrollbarThickness ()
{
return 0;
}
int FltkPreview::getScrollbarOnLeft ()
{
return 0;
}
void FltkPreview::scrollTo (int x, int y)
{
scrollX = x;
scrollY = y;
}
void FltkPreview::scroll (dw::core::ScrollCommand cmd)
{
MSG_ERR("FltkPreview::scroll not implemented\n");
}
void FltkPreview::setViewportSize (int width, int height,
int hScrollbarThickness,
int vScrollbarThickness)
{
scrollWidth = width - vScrollbarThickness;
scrollHeight = height - hScrollbarThickness;
}
void FltkPreview::drawText (core::style::Font *font,
core::style::Color *color,
core::style::Color::Shading shading,
int x, int y, const char *text, int len)
{
/*
* We must call setfont() before calling getwidth() (or anything
* else that measures text).
*/
FltkFont *ff = (FltkFont*)font;
Fl::set_font(ff->font, translateCanvasXToViewX (ff->size));
#if 0
/**
* \todo Normally, this should already be known, maybe it
* should be passed?
*/
int width = (int)getwidth (text, len);
int height = font->ascent; // No descent, this would look to "bold".
int x1 = translateCanvasXToViewX (x);
int y1 = translateCanvasYToViewY (y);
int x2 = translateCanvasXToViewX (x + width);
int y2 = translateCanvasYToViewY (y + height);
Rectangle rect (x1, y1, x2 - x1, y2 - y1);
setcolor(((FltkColor*)color)->colors[shading]);
fillrect (rect);
#endif
fl_color(((FltkColor*)color)->colors[shading]);
fl_draw(text, len, translateCanvasXToViewX (x), translateCanvasYToViewY(y));
}
void FltkPreview::drawSimpleWrappedText (core::style::Font *font,
core::style::Color *color,
core::style::Color::Shading shading,
int x, int y, int w, int h,
const char *text)
{
}
void FltkPreview::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
int x, int y, int width, int height)
{
}
bool FltkPreview::usesFltkWidgets ()
{
return false;
}
void FltkPreview::drawFltkWidget (Fl_Widget *widget,
core::Rectangle *area)
{
}
// ----------------------------------------------------------------------
FltkPreviewWindow::FltkPreviewWindow (dw::core::Layout *layout):
Fl_Menu_Window (1, 1)
{
box (FL_EMBOSSED_BOX);
begin ();
preview = new FltkPreview (BORDER_WIDTH, BORDER_WIDTH, 1, 1, layout);
end ();
hide ();
}
FltkPreviewWindow::~FltkPreviewWindow ()
{
}
void FltkPreviewWindow::showWindow ()
{
reallocate ();
show ();
}
void FltkPreviewWindow::reallocate ()
{
int maxWidth = misc::screenWidth () / 2;
int maxHeight = misc::screenHeight () * 4 / 5;
int mx, my, width, height;
bool warp = false;
if (preview->canvasHeight * maxWidth > maxHeight * preview->canvasWidth) {
// Expand to maximal height (most likely case).
width = preview->canvasWidth * maxHeight / preview->canvasHeight;
height = maxHeight;
} else {
// Expand to maximal width.
width = maxWidth;
height = preview->canvasHeight * maxWidth / preview->canvasWidth;
}
Fl::get_mouse(mx, my);
posX = mx - preview->translateCanvasXToViewX (preview->scrollX
+ preview->scrollWidth / 2);
posY = my - preview->translateCanvasYToViewY (preview->scrollY
+ preview->scrollHeight / 2);
if (posX < 0) {
mx -= posX;
posX = 0;
warp = true;
} else if (posX + width > misc::screenWidth ()) {
mx -= (posX - (misc::screenWidth () - width));
posX = misc::screenWidth () - width;
warp = true;
}
if (posY < 0) {
my -= posY;
posY = 0;
warp = true;
} else if (posY + height > misc::screenHeight ()) {
my -= (posY - (misc::screenHeight () - height));
posY = misc::screenHeight () - height;
warp = true;
}
if (warp)
misc::warpPointer (mx, my);
resize (posX, posY, width, height);
preview->size(w () - 2 * BORDER_WIDTH, h () - 2 * BORDER_WIDTH);
}
void FltkPreviewWindow::hideWindow ()
{
Fl_Window::hide ();
}
void FltkPreviewWindow::scrollTo (int mouseX, int mouseY)
{
preview->scrollX =
preview->translateViewXToCanvasX (mouseX - posX - BORDER_WIDTH)
- preview->scrollWidth / 2;
preview->scrollY =
preview->translateViewYToCanvasY (mouseY - posY - BORDER_WIDTH)
- preview->scrollHeight / 2;
preview->theLayout->scrollPosChanged (preview,
preview->scrollX, preview->scrollY);
}
// ----------------------------------------------------------------------
FltkPreviewButton::FltkPreviewButton (int x, int y, int w, int h,
dw::core::Layout *layout,
const char *label):
Fl_Button (x, y, w, h, label)
{
image (new Fl_Bitmap (preview_bits, preview_width, preview_height));
window = new FltkPreviewWindow (layout);
}
FltkPreviewButton::~FltkPreviewButton ()
{
}
int FltkPreviewButton::handle (int event)
{
/** \bug Some parts are missing. */
switch (event) {
case FL_PUSH:
window->showWindow ();
return Fl_Button::handle (event);
case FL_DRAG:
if (window->visible ()) {
window->scrollTo (Fl::event_x_root (), Fl::event_y_root ());
return 1;
}
return Fl_Button::handle (event);
case FL_RELEASE:
window->hideWindow ();
return Fl_Button::handle (event);
default:
return Fl_Button::handle (event);
}
}
} // namespace fltk
} // namespace dw

96
dw/fltkpreview.hh Normal file
View File

@ -0,0 +1,96 @@
#ifndef __FlTKPREVIEW_HH__
#define __FlTKPREVIEW_HH__
#include <FL/Fl_Button.H>
#include <FL/Fl_Menu_Window.H>
#include "fltkviewbase.hh"
namespace dw {
namespace fltk {
class FltkPreview: public FltkViewBase
{
friend class FltkPreviewWindow;
private:
int scrollX, scrollY, scrollWidth, scrollHeight;
protected:
int translateViewXToCanvasX (int x);
int translateViewYToCanvasY (int y);
int translateCanvasXToViewX (int x);
int translateCanvasYToViewY (int y);
public:
FltkPreview (int x, int y, int w, int h, dw::core::Layout *layout,
const char *label = 0);
~FltkPreview ();
int handle (int event);
void setCanvasSize (int width, int ascent, int descent);
bool usesViewport ();
int getHScrollbarThickness ();
int getVScrollbarThickness ();
int getScrollbarOnLeft ();
void scrollTo (int x, int y);
void scroll (dw::core::ScrollCommand cmd);
void setViewportSize (int width, int height,
int hScrollbarThickness, int vScrollbarThickness);
void drawText (core::style::Font *font,
core::style::Color *color,
core::style::Color::Shading shading,
int x, int y, const char *text, int len);
void drawSimpleWrappedText (core::style::Font *font,
core::style::Color *color,
core::style::Color::Shading shading,
int x, int y, int w, int h,
const char *text);
void drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
int x, int y, int width, int height);
bool usesFltkWidgets ();
void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area);
};
class FltkPreviewWindow: public Fl_Menu_Window
{
private:
enum { BORDER_WIDTH = 2 };
FltkPreview *preview;
int posX, posY;
public:
FltkPreviewWindow (dw::core::Layout *layout);
~FltkPreviewWindow ();
void reallocate ();
void showWindow ();
void hideWindow ();
void scrollTo (int mouseX, int mouseY);
};
class FltkPreviewButton: public Fl_Button
{
private:
FltkPreviewWindow *window;
public:
FltkPreviewButton (int x, int y, int w, int h,
dw::core::Layout *layout, const char *label = 0);
~FltkPreviewButton ();
int handle (int event);
};
} // namespace fltk
} // namespace dw
#endif // __FlTKPREVIEW_HH__

1778
dw/fltkui.cc Normal file

File diff suppressed because it is too large Load Diff

539
dw/fltkui.hh Normal file
View File

@ -0,0 +1,539 @@
#ifndef __DW_FLTK_UI_HH__
#define __DW_FLTK_UI_HH__
#ifndef __INCLUDED_FROM_DW_FLTK_CORE_HH__
# error Do not include this file directly, use "fltkcore.hh" instead.
#endif
#include <FL/Fl_Button.H>
#include <FL/Fl_Menu.H>
#include <FL/Fl_Text_Buffer.H>
namespace dw {
namespace fltk {
/**
* \brief FLTK implementation of dw::core::ui.
*
* <div style="border: 2px solid #ff0000; margin-top: 0.5em;
* margin-bottom: 0.5em; padding: 0.5em 1em;
* background-color: #ffefe0"><b>Update:</b> The complicated design
* results from my insufficient knowledge of C++ some years ago; since
* then, I've learned how to deal with "diamond inheritance", as the
* (ideal, not actually implemented) design in the first diagram
* shows. It should be possible to implement this ideal design in a
* straightforward way, and so get rid of templates. --SG</div>
*
* The design should be like this:
*
* \dot
* digraph G {
* node [shape=record, fontname=Helvetica, fontsize=10];
* edge [arrowhead="none", arrowtail="empty", dir="both",
* labelfontname=Helvetica, labelfontsize=10, color="#404040",
* labelfontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
*
* subgraph cluster_core {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core::ui";
*
* Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
* LabelButtonResource [color="#a0a0a0",
* URL="\ref dw::core::ui::LabelButtonResource"];
* EntryResource [color="#a0a0a0",
* URL="\ref dw::core::ui::EntryResource"];
* }
*
* subgraph cluster_fltk {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::fltk::ui";
*
* FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"];
* FltkLabelButtonResource
* [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
* FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
* }
*
* Resource -> LabelButtonResource;
* Resource -> EntryResource;
* FltkResource -> FltkLabelButtonResource;
* FltkResource -> FltkEntryResource;
* Resource -> FltkResource;
* LabelButtonResource -> FltkLabelButtonResource;
* EntryResource -> FltkEntryResource;
* }
* \enddot
*
* <center>[\ref uml-legend "legend"]</center>
*
* where dw::fltk::ui::FltkResource provides some base functionality for all
* conctrete FLTK implementations of sub-interfaces of dw::core::ui::Resource.
* However, this is not directly possible in C++, since the base class
* dw::core::ui::Resource is ambiguous for
* dw::fltk::ui::FltkLabelButtonResource.
*
* To solve this, we have to remove the dependency between
* dw::fltk::ui::FltkResource and dw::core::ui::Resource, instead, the part
* of dw::core::ui::Resource, which is implemented in
* dw::fltk::ui::FltkResource, must be explicitly delegated from
* dw::fltk::ui::FltkLabelButtonResourceto dw::fltk::ui::FltkResource:
*
* \dot
* digraph G {
* node [shape=record, fontname=Helvetica, fontsize=10];
* edge [arrowhead="none", arrowtail="empty", dir="both",
* labelfontname=Helvetica, labelfontsize=10, color="#404040",
* labelfontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
*
* subgraph cluster_core {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core::ui";
*
* Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
* LabelButtonResource [color="#a0a0a0",
* URL="\ref dw::core::ui::LabelButtonResource"];
* EntryResource [color="#a0a0a0",
* URL="\ref dw::core::ui::EntryResource"];
* }
*
* subgraph cluster_fltk {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::fltk::ui";
*
* FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"];
* FltkLabelButtonResource
* [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
* FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
* }
*
* Resource -> LabelButtonResource;
* Resource -> EntryResource;
* FltkResource -> FltkLabelButtonResource;
* FltkResource -> FltkEntryResource;
* LabelButtonResource -> FltkLabelButtonResource;
* EntryResource -> FltkEntryResource;
* }
* \enddot
*
* <center>[\ref uml-legend "legend"]</center>
*
* To make this a bit simpler, we use templates:
*
* \dot
* digraph G {
* node [shape=record, fontname=Helvetica, fontsize=10];
* edge [arrowhead="none", arrowtail="empty", dir="both",
* labelfontname=Helvetica, labelfontsize=10, color="#404040",
* labelfontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
*
* subgraph cluster_core {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core::ui";
*
* Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
* LabelButtonResource [color="#a0a0a0",
* URL="\ref dw::core::ui::LabelButtonResource"];
* EntryResource [color="#a0a0a0",
* URL="\ref dw::core::ui::EntryResource"];
* }
*
* subgraph cluster_fltk {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::fltk::ui";
*
* FltkResource [color="#a0a0a0", URL="\ref dw::fltk::ui::FltkResource"];
* FltkSpecificResource [color="#a0a0a0",
* fillcolor="#ffffc0", style="filled"
* URL="\ref dw::fltk::ui::FltkSpecificResource"];
* FltkSpecificResource_button [color="#a0a0a0",
* label="FltkSpecificResource \<LabelButtonResource\>"];
* FltkSpecificResource_entry [color="#a0a0a0",
* label="FltkSpecificResource \<EntryResource\>"];
* FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
* FltkLabelButtonResource
* [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
* }
*
* Resource -> LabelButtonResource;
* Resource -> EntryResource;
* FltkResource -> FltkSpecificResource;
* FltkSpecificResource -> FltkSpecificResource_button [arrowhead="open",
* arrowtail="none",
* dir="both",
* style="dashed",
* color="#808000"];
* FltkSpecificResource -> FltkSpecificResource_entry [arrowhead="open",
* arrowtail="none",
* dir="both",
* style="dashed",
* color="#808000"];
* LabelButtonResource -> FltkSpecificResource_button;
* EntryResource -> FltkSpecificResource_entry;
* FltkSpecificResource_button -> FltkLabelButtonResource;
* FltkSpecificResource_entry -> FltkEntryResource;
* }
* \enddot
*
* <center>[\ref uml-legend "legend"]</center>
*/
namespace ui {
/**
* ...
*/
class FltkResource: public lout::object::Object
{
private:
bool enabled;
protected:
FltkView *view;
Fl_Widget *widget;
core::Allocation allocation;
FltkPlatform *platform;
core::style::Style *style;
FltkResource (FltkPlatform *platform);
void init (FltkPlatform *platform);
virtual Fl_Widget *createNewWidget (core::Allocation *allocation) = 0;
virtual void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
void setDisplayed (bool displayed);
bool displayed();
public:
~FltkResource ();
virtual void attachView (FltkView *view);
virtual void detachView (FltkView *view);
void sizeAllocate (core::Allocation *allocation);
void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
void setStyle (core::style::Style *style);
bool isEnabled ();
void setEnabled (bool enabled);
};
template <class I> class FltkSpecificResource: public I, public FltkResource
{
public:
FltkSpecificResource (FltkPlatform *platform);
~FltkSpecificResource ();
void sizeAllocate (core::Allocation *allocation);
void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
void setStyle (core::style::Style *style);
bool isEnabled ();
void setEnabled (bool enabled);
};
class FltkLabelButtonResource:
public FltkSpecificResource <dw::core::ui::LabelButtonResource>
{
private:
const char *label;
static void widgetCallback (Fl_Widget *widget, void *data);
protected:
Fl_Widget *createNewWidget (core::Allocation *allocation);
public:
FltkLabelButtonResource (FltkPlatform *platform, const char *label);
~FltkLabelButtonResource ();
void sizeRequest (core::Requisition *requisition);
const char *getLabel ();
void setLabel (const char *label);
};
class FltkComplexButtonResource:
public FltkSpecificResource <dw::core::ui::ComplexButtonResource>
{
private:
bool relief;
static void widgetCallback (Fl_Widget *widget, void *data);
protected:
FltkView *topView, *flatView;
void attachView (FltkView *view);
void detachView (FltkView *view);
void sizeAllocate (core::Allocation *allocation);
dw::core::Platform *createPlatform ();
void setLayout (dw::core::Layout *layout);
int reliefXThickness ();
int reliefYThickness ();
Fl_Widget *createNewWidget (core::Allocation *allocation);
public:
FltkComplexButtonResource (FltkPlatform *platform, dw::core::Widget *widget,
bool relief);
~FltkComplexButtonResource ();
};
/**
* \bug Maximal length not supported yet.
* \todo Text values are not synchronized (not needed in dillo).
*/
class FltkEntryResource:
public FltkSpecificResource <dw::core::ui::EntryResource>
{
private:
int size;
bool password;
const char *initText;
char *label;
int label_w;
char *placeholder;
bool editable;
static void widgetCallback (Fl_Widget *widget, void *data);
void setDisplayed (bool displayed);
protected:
Fl_Widget *createNewWidget (core::Allocation *allocation);
void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
public:
FltkEntryResource (FltkPlatform *platform, int size, bool password,
const char *label, const char *placeholder);
~FltkEntryResource ();
void sizeRequest (core::Requisition *requisition);
void sizeAllocate (core::Allocation *allocation);
const char *getText ();
void setText (const char *text);
bool isEditable ();
void setEditable (bool editable);
void setMaxLength (int maxlen);
};
class FltkMultiLineTextResource:
public FltkSpecificResource <dw::core::ui::MultiLineTextResource>
{
private:
bool editable;
int numCols, numRows;
char *placeholder;
protected:
Fl_Widget *createNewWidget (core::Allocation *allocation);
void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
public:
FltkMultiLineTextResource (FltkPlatform *platform, int cols, int rows,
const char *placeholder);
~FltkMultiLineTextResource ();
void sizeRequest (core::Requisition *requisition);
const char *getText ();
void setText (const char *text);
bool isEditable ();
void setEditable (bool editable);
};
template <class I> class FltkToggleButtonResource:
public FltkSpecificResource <I>
{
private:
bool initActivated;
protected:
virtual Fl_Button *createNewButton (core::Allocation *allocation) = 0;
Fl_Widget *createNewWidget (core::Allocation *allocation);
void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
public:
FltkToggleButtonResource (FltkPlatform *platform,
bool activated);
~FltkToggleButtonResource ();
void sizeRequest (core::Requisition *requisition);
bool isActivated ();
void setActivated (bool activated);
};
class FltkCheckButtonResource:
public FltkToggleButtonResource <dw::core::ui::CheckButtonResource>
{
protected:
Fl_Button *createNewButton (core::Allocation *allocation);
public:
FltkCheckButtonResource (FltkPlatform *platform,
bool activated);
~FltkCheckButtonResource ();
};
class FltkRadioButtonResource:
public FltkToggleButtonResource <dw::core::ui::RadioButtonResource>
{
private:
class Group
{
private:
class FltkGroupIterator:
public dw::core::ui::RadioButtonResource::GroupIterator
{
private:
lout::container::typed::Iterator <FltkRadioButtonResource> it;
public:
inline FltkGroupIterator (lout::container::typed::List
<FltkRadioButtonResource>
*list)
{ it = list->iterator (); }
bool hasNext ();
dw::core::ui::RadioButtonResource *getNext ();
void unref ();
};
lout::container::typed::List <FltkRadioButtonResource> *list;
protected:
~Group ();
public:
Group (FltkRadioButtonResource *radioButtonResource);
inline lout::container::typed::Iterator <FltkRadioButtonResource>
iterator ()
{
return list->iterator ();
}
inline dw::core::ui::RadioButtonResource::GroupIterator
*groupIterator ()
{
return new FltkGroupIterator (list);
}
void connect (FltkRadioButtonResource *radioButtonResource);
void unconnect (FltkRadioButtonResource *radioButtonResource);
};
Group *group;
static void widgetCallback (Fl_Widget *widget, void *data);
void buttonClicked ();
protected:
Fl_Button *createNewButton (core::Allocation *allocation);
public:
FltkRadioButtonResource (FltkPlatform *platform,
FltkRadioButtonResource *groupedWith,
bool activated);
~FltkRadioButtonResource ();
GroupIterator *groupIterator ();
};
template <class I> class FltkSelectionResource:
public FltkSpecificResource <I>
{
protected:
virtual bool setSelectedItems() { return false; }
virtual void addItem (const char *str, bool enabled, bool selected) = 0;
virtual void setItem (int index, bool selected) = 0;
virtual void pushGroup (const char *name, bool enabled) = 0;
virtual void popGroup () = 0;
public:
FltkSelectionResource (FltkPlatform *platform) :
FltkSpecificResource<I> (platform) {};
dw::core::Iterator *iterator (dw::core::Content::Type mask, bool atEnd);
};
class FltkOptionMenuResource:
public FltkSelectionResource <dw::core::ui::OptionMenuResource>
{
protected:
Fl_Widget *createNewWidget (core::Allocation *allocation);
virtual bool setSelectedItems() { return true; }
void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
int getNumberOfItems();
int getMaxItemWidth ();
private:
static void widgetCallback (Fl_Widget *widget, void *data);
void enlargeMenu();
Fl_Menu_Item *newItem();
Fl_Menu_Item *menu;
int itemsAllocated, itemsUsed;
public:
FltkOptionMenuResource (FltkPlatform *platform);
~FltkOptionMenuResource ();
void addItem (const char *str, bool enabled, bool selected);
void setItem (int index, bool selected);
void pushGroup (const char *name, bool enabled);
void popGroup ();
void sizeRequest (core::Requisition *requisition);
bool isSelected (int index);
};
class FltkListResource:
public FltkSelectionResource <dw::core::ui::ListResource>
{
protected:
Fl_Widget *createNewWidget (core::Allocation *allocation);
void setWidgetStyle (Fl_Widget *widget, core::style::Style *style);
int getNumberOfItems();
int getMaxItemWidth ();
private:
static void widgetCallback (Fl_Widget *widget, void *data);
void *newItem (const char *str, bool enabled, bool selected);
int currDepth;
int colWidths[4];
int showRows;
ListResource::SelectionMode mode;
public:
FltkListResource (FltkPlatform *platform,
core::ui::ListResource::SelectionMode selectionMode,
int rows);
~FltkListResource ();
void addItem (const char *str, bool enabled, bool selected);
void setItem (int index, bool selected);
void pushGroup (const char *name, bool enabled);
void popGroup ();
void sizeRequest (core::Requisition *requisition);
bool isSelected (int index);
};
} // namespace ui
} // namespace fltk
} // namespace dw
#endif // __DW_FLTK_UI_HH__

736
dw/fltkviewbase.cc Normal file
View File

@ -0,0 +1,736 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "fltkviewport.hh"
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <stdio.h>
#include "../lout/msg.h"
using namespace lout::object;
using namespace lout::container::typed;
namespace dw {
namespace fltk {
FltkViewBase::BackBuffer::BackBuffer ()
{
w = 0;
h = 0;
created = false;
}
FltkViewBase::BackBuffer::~BackBuffer ()
{
if (created)
fl_delete_offscreen (offscreen);
}
void FltkViewBase::BackBuffer::setSize (int w, int h)
{
if (!created || w > this->w || h > this->h) {
this->w = w;
this->h = h;
if (created)
fl_delete_offscreen (offscreen);
offscreen = fl_create_offscreen (w, h);
created = true;
}
}
FltkViewBase::BackBuffer *FltkViewBase::backBuffer;
bool FltkViewBase::backBufferInUse;
FltkViewBase::FltkViewBase (int x, int y, int w, int h, const char *label):
Fl_Group (x, y, w, h, label)
{
Fl_Group::current(0);
canvasWidth = 1;
canvasHeight = 1;
bgColor = FL_WHITE;
mouse_x = mouse_y = 0;
focused_child = NULL;
exposeArea = NULL;
if (backBuffer == NULL) {
backBuffer = new BackBuffer ();
}
box(FL_NO_BOX);
resizable(NULL);
}
FltkViewBase::~FltkViewBase ()
{
cancelQueueDraw ();
}
void FltkViewBase::setBufferedDrawing (bool b) {
if (b && backBuffer == NULL) {
backBuffer = new BackBuffer ();
} else if (!b && backBuffer != NULL) {
delete backBuffer;
backBuffer = NULL;
}
}
void FltkViewBase::draw ()
{
int d = damage ();
if ((d & FL_DAMAGE_USER1) && !(d & FL_DAMAGE_EXPOSE)) {
lout::container::typed::Iterator <core::Rectangle> it;
for (it = drawRegion.rectangles (); it.hasNext (); ) {
draw (it.getNext (), DRAW_BUFFERED);
}
drawRegion.clear ();
d &= ~FL_DAMAGE_USER1;
}
if (d & FL_DAMAGE_CHILD) {
drawChildWidgets ();
d &= ~FL_DAMAGE_CHILD;
}
if (d) {
dw::core::Rectangle rect (
translateViewXToCanvasX (x ()),
translateViewYToCanvasY (y ()),
w (),
h ());
if (d == FL_DAMAGE_SCROLL) {
// a clipping rectangle has already been set by fltk::scrollrect ()
draw (&rect, DRAW_PLAIN);
} else {
draw (&rect, DRAW_CLIPPED);
drawRegion.clear ();
}
}
}
void FltkViewBase::draw (const core::Rectangle *rect,
DrawType type)
{
int X = translateCanvasXToViewX (rect->x);
int Y = translateCanvasYToViewY (rect->y);
int W, H;
// fl_clip_box() can't handle values greater than SHRT_MAX!
if (X > x () + w () || Y > y () + h ())
return;
W = X + rect->width > x () + w () ? x () + w () - X : rect->width;
H = Y + rect->height > y () + h () ? y () + h () - Y : rect->height;
fl_clip_box(X, Y, W, H, X, Y, W, H);
core::Rectangle r (translateViewXToCanvasX (X),
translateViewYToCanvasY (Y), W, H);
if (r.isEmpty ())
return;
exposeArea = &r;
if (type == DRAW_BUFFERED && backBuffer && !backBufferInUse) {
backBufferInUse = true;
backBuffer->setSize (X + W, Y + H); // would be nicer to use (W, H)...
fl_begin_offscreen (backBuffer->offscreen);
fl_push_matrix ();
fl_color (bgColor);
fl_rectf (X, Y, W, H);
theLayout->expose (this, &r);
fl_pop_matrix ();
fl_end_offscreen ();
fl_copy_offscreen (X, Y, W, H, backBuffer->offscreen, X, Y);
backBufferInUse = false;
} else if (type == DRAW_BUFFERED || type == DRAW_CLIPPED) {
// if type == DRAW_BUFFERED but we do not have backBuffer available
// we fall back to clipped drawing
fl_push_clip (X, Y, W, H);
fl_color (bgColor);
fl_rectf (X, Y, W, H);
theLayout->expose (this, &r);
fl_pop_clip ();
} else {
fl_color (bgColor);
fl_rectf (X, Y, W, H);
theLayout->expose (this, &r);
}
// DEBUG:
//fl_color(FL_RED);
//fl_rect(X, Y, W, H);
exposeArea = NULL;
}
void FltkViewBase::drawChildWidgets () {
for (int i = children () - 1; i >= 0; i--) {
Fl_Widget& w = *child(i);
#if 0
PORT1.3
if (w.damage() & DAMAGE_CHILD_LABEL) {
draw_outside_label(w);
w.set_damage(w.damage() & ~DAMAGE_CHILD_LABEL);
}
#endif
update_child(w);
}
}
core::ButtonState getDwButtonState ()
{
int s1 = Fl::event_state ();
int s2 = (core::ButtonState)0;
if (s1 & FL_SHIFT) s2 |= core::SHIFT_MASK;
if (s1 & FL_CTRL) s2 |= core::CONTROL_MASK;
if (s1 & FL_ALT) s2 |= core::META_MASK;
if (s1 & FL_BUTTON1) s2 |= core::BUTTON1_MASK;
if (s1 & FL_BUTTON2) s2 |= core::BUTTON2_MASK;
if (s1 & FL_BUTTON3) s2 |= core::BUTTON3_MASK;
return (core::ButtonState)s2;
}
/*
* We handle Tab to determine which FLTK widget should get focus.
*
* Presumably a proper solution that allows focusing links, etc., would live
* in Textblock and use iterators.
*/
int FltkViewBase::manageTabToFocus()
{
int i, ret = 0;
Fl_Widget *old_child = NULL;
if (this == Fl::focus()) {
// if we have focus, give it to a child. Go forward typically,
// or backward with Shift pressed.
if (!(Fl::event_state() & FL_SHIFT)) {
for (i = 0; i < children(); i++) {
if (child(i)->take_focus()) {
ret = 1;
break;
}
}
} else {
for (i = children() - 1; i >= 0; i--) {
if (child(i)->take_focus()) {
ret = 1;
break;
}
}
}
} else {
// tabbing between children
old_child = Fl::focus();
if (!(ret = Fl_Group::handle (FL_KEYBOARD))) {
// group didn't have any more children to focus.
Fl::focus(this);
return 1;
} else {
// which one did it focus? (Note i == children() if not found)
i = find(Fl::focus());
}
}
if (ret) {
if (i >= 0 && i < children()) {
Fl_Widget *c = child(i);
int canvasX = translateViewXToCanvasX(c->x()),
canvasY = translateViewYToCanvasY(c->y());
theLayout->scrollTo(core::HPOS_INTO_VIEW, core::VPOS_INTO_VIEW,
canvasX, canvasY, c->w(), c->h());
// Draw the children who gained and lost focus. Otherwise a
// widget that had been only partly visible still shows its old
// appearance in the previously-visible portion.
core::Rectangle r(canvasX, canvasY, c->w(), c->h());
queueDraw(&r);
if (old_child) {
r.x = translateViewXToCanvasX(old_child->x());
r.y = translateViewYToCanvasY(old_child->y());
r.width = old_child->w();
r.height = old_child->h();
queueDraw(&r);
}
}
}
return ret;
}
int FltkViewBase::handle (int event)
{
bool processed;
/**
* \todo Consider, whether this from the FLTK documentation has any
* impacts: "To receive fltk::RELEASE events you must return non-zero
* when passed a fltk::PUSH event. "
*/
switch(event) {
case FL_PUSH:
/* Hide the tooltip */
theLayout->cancelTooltip();
processed =
theLayout->buttonPress (this, Fl::event_clicks () + 1,
translateViewXToCanvasX (Fl::event_x ()),
translateViewYToCanvasY (Fl::event_y ()),
getDwButtonState (), Fl::event_button ());
_MSG("PUSH => %s\n", processed ? "true" : "false");
if (processed) {
/* pressed dw content; give focus to the view */
if (Fl::event_button() != FL_RIGHT_MOUSE)
Fl::focus(this);
return true;
}
break;
case FL_RELEASE:
processed =
theLayout->buttonRelease (this, Fl::event_clicks () + 1,
translateViewXToCanvasX (Fl::event_x ()),
translateViewYToCanvasY (Fl::event_y ()),
getDwButtonState (), Fl::event_button ());
_MSG("RELEASE => %s\n", processed ? "true" : "false");
if (processed)
return true;
break;
case FL_MOVE:
mouse_x = Fl::event_x();
mouse_y = Fl::event_y();
processed =
theLayout->motionNotify (this,
translateViewXToCanvasX (mouse_x),
translateViewYToCanvasY (mouse_y),
getDwButtonState ());
_MSG("MOVE => %s\n", processed ? "true" : "false");
if (processed)
return true;
break;
case FL_DRAG:
processed =
theLayout->motionNotify (this,
translateViewXToCanvasX (Fl::event_x ()),
translateViewYToCanvasY (Fl::event_y ()),
getDwButtonState ());
_MSG("DRAG => %s\n", processed ? "true" : "false");
if (processed)
return true;
break;
case FL_ENTER:
theLayout->enterNotify (this,
translateViewXToCanvasX (Fl::event_x ()),
translateViewYToCanvasY (Fl::event_y ()),
getDwButtonState ());
break;
case FL_HIDE:
/* WORKAROUND: strangely, the tooltip window is not automatically hidden
* with its parent. Here we fake a LEAVE to achieve it. */
case FL_LEAVE:
theLayout->leaveNotify (this, getDwButtonState ());
break;
case FL_FOCUS:
if (focused_child && find(focused_child) < children()) {
/* strangely, find() == children() if the child is not found */
focused_child->take_focus();
}
return 1;
case FL_UNFOCUS:
// FLTK delivers UNFOCUS to the previously focused widget
if (find(Fl::focus()) < children())
focused_child = Fl::focus(); // remember the focused child!
else if (Fl::focus() == this)
focused_child = NULL; // no focused child this time
return 0;
case FL_KEYBOARD:
if (Fl::event_key() == FL_Tab)
return manageTabToFocus();
break;
default:
break;
}
return Fl_Group::handle (event);
}
// ----------------------------------------------------------------------
void FltkViewBase::setLayout (core::Layout *layout)
{
theLayout = layout;
if (usesViewport())
theLayout->viewportSizeChanged(this, w(), h());
}
void FltkViewBase::setCanvasSize (int width, int ascent, int descent)
{
canvasWidth = width;
canvasHeight = ascent + descent;
}
void FltkViewBase::setCursor (core::style::Cursor cursor)
{
static Fl_Cursor mapDwToFltk[] = {
FL_CURSOR_CROSS,
FL_CURSOR_DEFAULT,
FL_CURSOR_HAND,
FL_CURSOR_MOVE,
FL_CURSOR_WE,
FL_CURSOR_NESW,
FL_CURSOR_NWSE,
FL_CURSOR_NS,
FL_CURSOR_NWSE,
FL_CURSOR_NESW,
FL_CURSOR_NS,
FL_CURSOR_WE,
FL_CURSOR_INSERT,
FL_CURSOR_WAIT,
FL_CURSOR_HELP
};
fl_cursor (mapDwToFltk[cursor]);
}
void FltkViewBase::setBgColor (core::style::Color *color)
{
bgColor = color ?
((FltkColor*)color)->colors[dw::core::style::Color::SHADING_NORMAL] :
FL_WHITE;
}
void FltkViewBase::startDrawing (core::Rectangle *area)
{
}
void FltkViewBase::finishDrawing (core::Rectangle *area)
{
}
void FltkViewBase::queueDraw (core::Rectangle *area)
{
drawRegion.addRectangle (area);
damage (FL_DAMAGE_USER1); // USER1 for buffered draw
}
void FltkViewBase::queueDrawTotal ()
{
damage (FL_DAMAGE_EXPOSE);
}
void FltkViewBase::cancelQueueDraw ()
{
}
void FltkViewBase::drawPoint (core::style::Color *color,
core::style::Color::Shading shading,
int x, int y)
{
}
void FltkViewBase::drawLine (core::style::Color *color,
core::style::Color::Shading shading,
int x1, int y1, int x2, int y2)
{
fl_color(((FltkColor*)color)->colors[shading]);
// we clip with a large border (5000px), as clipping causes artefacts
// with non-solid line styles.
// However it's still better than no clipping at all.
clipPoint (&x1, &y1, 5000);
clipPoint (&x2, &y2, 5000);
fl_line (translateCanvasXToViewX (x1),
translateCanvasYToViewY (y1),
translateCanvasXToViewX (x2),
translateCanvasYToViewY (y2));
}
void FltkViewBase::drawTypedLine (core::style::Color *color,
core::style::Color::Shading shading,
core::style::LineType type, int width,
int x1, int y1, int x2, int y2)
{
char dashes[3], w, ng, d, gap, len;
const int f = 2;
w = (width == 1) ? 0 : width;
if (type == core::style::LINE_DOTTED) {
/* customized drawing for dotted lines */
len = (x2 == x1) ? y2 - y1 + 1 : (y2 == y1) ? x2 - x1 + 1 : 0;
ng = len / f*width;
d = len % f*width;
gap = ng ? d/ng + (w > 3 ? 2 : 0) : 0;
dashes[0] = 1; dashes[1] = f*width-gap; dashes[2] = 0;
fl_line_style(FL_DASH + FL_CAP_ROUND, w, dashes);
/* These formulas also work, but ain't pretty ;)
* fl_line_style(FL_DOT + FL_CAP_ROUND, w);
* dashes[0] = 1; dashes[1] = 3*width-2; dashes[2] = 0;
*/
} else if (type == core::style::LINE_DASHED) {
fl_line_style(FL_DASH + FL_CAP_ROUND, w);
}
fl_color(((FltkColor*)color)->colors[shading]);
drawLine (color, shading, x1, y1, x2, y2);
if (type != core::style::LINE_NORMAL)
fl_line_style(FL_SOLID);
}
void FltkViewBase::drawRectangle (core::style::Color *color,
core::style::Color::Shading shading,
bool filled,
int X, int Y, int width, int height)
{
fl_color(((FltkColor*)color)->colors[shading]);
if (width < 0) {
X += width;
width = -width;
}
if (height < 0) {
Y += height;
height = -height;
}
int x1 = X;
int y1 = Y;
int x2 = X + width;
int y2 = Y + height;
// We only support rectangles with line width 1px, so we clip with
// a rectangle 1px wider and higher than what we actually expose.
// This is only really necessary for non-filled rectangles.
clipPoint (&x1, &y1, 1);
clipPoint (&x2, &y2, 1);
x1 = translateCanvasXToViewX (x1);
y1 = translateCanvasYToViewY (y1);
x2 = translateCanvasXToViewX (x2);
y2 = translateCanvasYToViewY (y2);
if (filled)
fl_rectf (x1, y1, x2 - x1, y2 - y1);
else
fl_rect (x1, y1, x2 - x1, y2 - y1);
}
void FltkViewBase::drawArc (core::style::Color *color,
core::style::Color::Shading shading, bool filled,
int centerX, int centerY, int width, int height,
int angle1, int angle2)
{
fl_color(((FltkColor*)color)->colors[shading]);
int x = translateCanvasXToViewX (centerX) - width / 2;
int y = translateCanvasYToViewY (centerY) - height / 2;
fl_arc(x, y, width, height, angle1, angle2);
if (filled) {
// WORKAROUND: We call both fl_arc and fl_pie due to a FLTK bug
// (STR #2703) that was present in 1.3.0.
fl_pie(x, y, width, height, angle1, angle2);
}
}
void FltkViewBase::drawPolygon (core::style::Color *color,
core::style::Color::Shading shading,
bool filled, bool convex, core::Point *points,
int npoints)
{
if (npoints > 0) {
fl_color(((FltkColor*)color)->colors[shading]);
if (filled) {
if (convex)
fl_begin_polygon();
else
fl_begin_complex_polygon();
} else
fl_begin_loop();
for (int i = 0; i < npoints; i++) {
fl_vertex(translateCanvasXToViewX(points[i].x),
translateCanvasYToViewY(points[i].y));
}
if (filled) {
if (convex)
fl_end_polygon();
else
fl_end_complex_polygon();
} else
fl_end_loop();
}
}
core::View *FltkViewBase::getClippingView (int x, int y, int width, int height)
{
fl_push_clip (translateCanvasXToViewX (x), translateCanvasYToViewY (y),
width, height);
return this;
}
void FltkViewBase::mergeClippingView (core::View *clippingView)
{
fl_pop_clip ();
}
// ----------------------------------------------------------------------
FltkWidgetView::FltkWidgetView (int x, int y, int w, int h,
const char *label):
FltkViewBase (x, y, w, h, label)
{
}
FltkWidgetView::~FltkWidgetView ()
{
}
void FltkWidgetView::drawText (core::style::Font *font,
core::style::Color *color,
core::style::Color::Shading shading,
int X, int Y, const char *text, int len)
{
//printf ("drawText (..., %d, %d, '", X, Y);
//for (int i = 0; i < len; i++)
// putchar (text[i]);
//printf ("'\n");
FltkFont *ff = (FltkFont*)font;
fl_font(ff->font, ff->size);
fl_color(((FltkColor*)color)->colors[shading]);
if (!font->letterSpacing && !font->fontVariant) {
fl_draw(text, len,
translateCanvasXToViewX (X), translateCanvasYToViewY (Y));
} else {
/* Nonzero letter spacing adjustment, draw each glyph individually */
int viewX = translateCanvasXToViewX (X),
viewY = translateCanvasYToViewY (Y);
int curr = 0, next = 0, nb;
char chbuf[4];
int c, cu, width;
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 = theLayout->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);
width = (int)fl_width(text + curr, next - curr);
if (curr && width)
viewX += font->letterSpacing;
fl_draw(text + curr, next - curr, viewX, viewY);
viewX += width;
} else {
/* make utf8 string for converted char */
nb = fl_utf8encode(cu, chbuf);
fl_font(ff->font, sc_fontsize);
width = (int)fl_width(chbuf, nb);
if (curr && width)
viewX += font->letterSpacing;
fl_draw(chbuf, nb, viewX, viewY);
viewX += width;
}
}
} else {
while (next < len) {
next = theLayout->nextGlyph(text, curr);
width = (int)fl_width(text + curr, next - curr);
if (curr && width)
viewX += font->letterSpacing;
fl_draw(text + curr, next - curr, viewX, viewY);
viewX += width;
curr = next;
}
}
}
}
/*
* "simple" in that it ignores letter-spacing, etc. This was added for image
* alt text where none of that matters.
*/
void FltkWidgetView::drawSimpleWrappedText (core::style::Font *font,
core::style::Color *color,
core::style::Color::Shading shading,
int X, int Y, int W, int H,
const char *text)
{
FltkFont *ff = (FltkFont*)font;
fl_font(ff->font, ff->size);
fl_color(((FltkColor*)color)->colors[shading]);
fl_draw(text,
translateCanvasXToViewX (X), translateCanvasYToViewY (Y),
W, H, FL_ALIGN_TOP|FL_ALIGN_LEFT|FL_ALIGN_WRAP, NULL, 0);
}
void FltkWidgetView::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
int X, int Y, int width, int height)
{
((FltkImgbuf*)imgbuf)->draw (this,
translateCanvasXToViewX (xRoot),
translateCanvasYToViewY (yRoot),
X, Y, width, height);
}
bool FltkWidgetView::usesFltkWidgets ()
{
return true;
}
void FltkWidgetView::addFltkWidget (Fl_Widget *widget,
core::Allocation *allocation)
{
allocateFltkWidget (widget, allocation);
add (widget);
}
void FltkWidgetView::removeFltkWidget (Fl_Widget *widget)
{
remove (widget);
}
void FltkWidgetView::allocateFltkWidget (Fl_Widget *widget,
core::Allocation *allocation)
{
widget->resize (translateCanvasXToViewX (allocation->x),
translateCanvasYToViewY (allocation->y),
allocation->width,
allocation->ascent + allocation->descent);
}
void FltkWidgetView::drawFltkWidget (Fl_Widget *widget,
core::Rectangle *area)
{
draw_child (*widget);
draw_outside_label(*widget);
}
} // namespace fltk
} // namespace dw

141
dw/fltkviewbase.hh Normal file
View File

@ -0,0 +1,141 @@
#ifndef __DW_FLTKVIEWBASE_HH__
#define __DW_FLTKVIEWBASE_HH__
#include <time.h> // for time_t
#include <sys/time.h> // for time_t in FreeBSD
#include <FL/Fl_Group.H>
#include <FL/x.H>
#include "fltkcore.hh"
namespace dw {
namespace fltk {
class FltkViewBase: public FltkView, public Fl_Group
{
private:
class BackBuffer {
private:
int w;
int h;
bool created;
public:
Fl_Offscreen offscreen;
BackBuffer ();
~BackBuffer ();
void setSize(int w, int h);
};
typedef enum { DRAW_PLAIN, DRAW_CLIPPED, DRAW_BUFFERED } DrawType;
int bgColor;
core::Region drawRegion;
core::Rectangle *exposeArea;
static BackBuffer *backBuffer;
static bool backBufferInUse;
void draw (const core::Rectangle *rect, DrawType type);
void drawChildWidgets ();
int manageTabToFocus();
inline void clipPoint (int *x, int *y, int border) {
if (exposeArea) {
if (*x < exposeArea->x - border)
*x = exposeArea->x - border;
if (*x > exposeArea->x + exposeArea->width + border)
*x = exposeArea->x + exposeArea->width + border;
if (*y < exposeArea->y - border)
*y = exposeArea->y - border;
if (*y > exposeArea->y + exposeArea->height + border)
*y = exposeArea->y + exposeArea->height + border;
}
}
protected:
core::Layout *theLayout;
int canvasWidth, canvasHeight;
int mouse_x, mouse_y;
Fl_Widget *focused_child;
virtual int translateViewXToCanvasX (int x) = 0;
virtual int translateViewYToCanvasY (int y) = 0;
virtual int translateCanvasXToViewX (int x) = 0;
virtual int translateCanvasYToViewY (int y) = 0;
public:
FltkViewBase (int x, int y, int w, int h, const char *label = 0);
~FltkViewBase ();
void draw();
int handle (int event);
void setLayout (core::Layout *layout);
void setCanvasSize (int width, int ascent, int descent);
void setCursor (core::style::Cursor cursor);
void setBgColor (core::style::Color *color);
void startDrawing (core::Rectangle *area);
void finishDrawing (core::Rectangle *area);
void queueDraw (core::Rectangle *area);
void queueDrawTotal ();
void cancelQueueDraw ();
void drawPoint (core::style::Color *color,
core::style::Color::Shading shading,
int x, int y);
void drawLine (core::style::Color *color,
core::style::Color::Shading shading,
int x1, int y1, int x2, int y2);
void drawTypedLine (core::style::Color *color,
core::style::Color::Shading shading,
core::style::LineType type, int width,
int x1, int y1, int x2, int y2);
void drawRectangle (core::style::Color *color,
core::style::Color::Shading shading, bool filled,
int x, int y, int width, int height);
void drawArc (core::style::Color *color,
core::style::Color::Shading shading, bool filled,
int centerX, int centerY, int width, int height,
int angle1, int angle2);
void drawPolygon (core::style::Color *color,
core::style::Color::Shading shading,
bool filled, bool convex,
core::Point *points, int npoints);
core::View *getClippingView (int x, int y, int width, int height);
void mergeClippingView (core::View *clippingView);
void setBufferedDrawing (bool b);
};
class FltkWidgetView: public FltkViewBase
{
public:
FltkWidgetView (int x, int y, int w, int h, const char *label = 0);
~FltkWidgetView ();
void drawText (core::style::Font *font,
core::style::Color *color,
core::style::Color::Shading shading,
int x, int y, const char *text, int len);
void drawSimpleWrappedText (core::style::Font *font,
core::style::Color *color,
core::style::Color::Shading shading,
int x, int y, int w, int h,
const char *text);
void drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
int x, int y, int width, int height);
bool usesFltkWidgets ();
void addFltkWidget (Fl_Widget *widget, core::Allocation *allocation);
void removeFltkWidget (Fl_Widget *widget);
void allocateFltkWidget (Fl_Widget *widget,
core::Allocation *allocation);
void drawFltkWidget (Fl_Widget *widget, core::Rectangle *area);
};
} // namespace fltk
} // namespace dw
#endif // __DW_FLTKVIEWBASE_HH__

679
dw/fltkviewport.cc Normal file
View File

@ -0,0 +1,679 @@
/*
* Dillo Widget
*
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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.
*
* 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 "fltkviewport.hh"
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <FL/names.h>
#include <stdio.h>
#include "../lout/msg.h"
#include "../lout/debug.hh"
using namespace lout;
using namespace lout::object;
using namespace lout::container::typed;
namespace dw {
namespace fltk {
/*
* Lets SHIFT+{Left,Right} go to the parent
*/
class CustScrollbar : public Fl_Scrollbar
{
public:
CustScrollbar(int x, int y, int w, int h) : Fl_Scrollbar(x,y,w,h) {};
int handle(int e) {
if (e == FL_SHORTCUT && Fl::event_state() == FL_SHIFT &&
(Fl::event_key() == FL_Left || Fl::event_key() == FL_Right))
return 0;
return Fl_Scrollbar::handle(e);
}
};
FltkViewport::FltkViewport (int X, int Y, int W, int H, const char *label):
FltkWidgetView (X, Y, W, H, label)
{
DBG_OBJ_CREATE ("dw::fltk::FltkViewport");
hscrollbar = new CustScrollbar (x (), y (), 1, 1);
hscrollbar->type(FL_HORIZONTAL);
hscrollbar->callback (hscrollbarCallback, this);
hscrollbar->hide();
add (hscrollbar);
vscrollbar = new Fl_Scrollbar (x (), y(), 1, 1);
vscrollbar->type(FL_VERTICAL);
vscrollbar->callback (vscrollbarCallback, this);
vscrollbar->hide();
add (vscrollbar);
hasDragScroll = 1;
scrollX = scrollY = scrollDX = scrollDY = 0;
horScrolling = verScrolling = dragScrolling = 0;
scrollbarPageMode = false;
pageOverlap = 50;
pageScrolling = core::NONE_CMD;
pageScrollDelay = 0.300;
pageScrollInterval = 0.100;
gadgetOrientation[0] = GADGET_HORIZONTAL;
gadgetOrientation[1] = GADGET_HORIZONTAL;
gadgetOrientation[2] = GADGET_VERTICAL;
gadgetOrientation[3] = GADGET_HORIZONTAL;
gadgets =
new container::typed::List <object::TypedPointer < Fl_Widget> >
(true);
}
FltkViewport::~FltkViewport ()
{
delete gadgets;
DBG_OBJ_DELETE ();
}
void FltkViewport::adjustScrollbarsAndGadgetsAllocation ()
{
int hdiff = 0, vdiff = 0;
int visibility = 0;
_MSG(" >>FltkViewport::adjustScrollbarsAndGadgetsAllocation\n");
if (hscrollbar->visible ())
visibility |= 1;
if (vscrollbar->visible ())
visibility |= 2;
if (gadgets->size () > 0) {
switch (gadgetOrientation [visibility]) {
case GADGET_VERTICAL:
hdiff = SCROLLBAR_THICKNESS;
vdiff = SCROLLBAR_THICKNESS * gadgets->size ();
break;
case GADGET_HORIZONTAL:
hdiff = SCROLLBAR_THICKNESS * gadgets->size ();
vdiff = SCROLLBAR_THICKNESS;
break;
}
} else {
hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
}
if (scrollbarOnLeft) {
hscrollbar->resize(x () + hdiff, y () + h () - SCROLLBAR_THICKNESS,
w () - hdiff, SCROLLBAR_THICKNESS);
vscrollbar->resize(x (), y (),
SCROLLBAR_THICKNESS, h () - vdiff);
} else {
hscrollbar->resize(x (), y () + h () - SCROLLBAR_THICKNESS,
w () - hdiff, SCROLLBAR_THICKNESS);
vscrollbar->resize(x () + w () - SCROLLBAR_THICKNESS, y (),
SCROLLBAR_THICKNESS, h () - vdiff);
}
//int X = x () + w () - SCROLLBAR_THICKNESS;
//int Y = y () + h () - SCROLLBAR_THICKNESS;
for (Iterator <TypedPointer < Fl_Widget> > it = gadgets->iterator ();
it.hasNext (); ) {
Fl_Widget *widget = it.getNext()->getTypedValue ();
widget->resize(x (), y (), SCROLLBAR_THICKNESS, SCROLLBAR_THICKNESS);
/* FIXME: This has no effect */
#if 0
switch (gadgetOrientation [visibility]) {
case GADGET_VERTICAL:
Y -= SCROLLBAR_THICKNESS;
break;
case GADGET_HORIZONTAL:
X -= SCROLLBAR_THICKNESS;
break;
}
#endif
}
adjustScrollbarValues();
}
void FltkViewport::adjustScrollbarValues ()
{
hscrollbar->value (scrollX, hscrollbar->w (), 0, canvasWidth);
vscrollbar->value (scrollY, vscrollbar->h (), 0, canvasHeight);
}
void FltkViewport::hscrollbarChanged ()
{
scroll (hscrollbar->value () - scrollX, 0);
}
void FltkViewport::vscrollbarChanged ()
{
scroll (0, vscrollbar->value () - scrollY);
}
void FltkViewport::vscrollbarCallback (Fl_Widget *vscrollbar,void *viewportPtr)
{
((FltkViewport*)viewportPtr)->vscrollbarChanged ();
}
void FltkViewport::hscrollbarCallback (Fl_Widget *hscrollbar,void *viewportPtr)
{
((FltkViewport*)viewportPtr)->hscrollbarChanged ();
}
// ----------------------------------------------------------------------
void FltkViewport::resize(int X, int Y, int W, int H)
{
bool dimension_changed = W != w() || H != h();
Fl_Group::resize(X, Y, W, H);
if (dimension_changed) {
theLayout->viewportSizeChanged (this, W, H);
adjustScrollbarsAndGadgetsAllocation ();
}
}
void FltkViewport::draw_area (void *data, int x, int y, int w, int h)
{
FltkViewport *vp = (FltkViewport*) data;
fl_push_clip(x, y, w, h);
vp->FltkWidgetView::draw ();
for (Iterator <TypedPointer < Fl_Widget> > it = vp->gadgets->iterator();
it.hasNext (); ) {
Fl_Widget *widget = it.getNext()->getTypedValue ();
vp->draw_child (*widget);
}
fl_pop_clip();
}
/*
* Draw the viewport.
*
* + Damage flags come in different ways, draw() should cope with them all.
* + Damage flags are alive for visible and hidden widgets.
* + FL_DAMAGE_CHILD can flag scroll bars or embedded FLTK widgets.
*/
void FltkViewport::draw ()
{
const int d = damage(),
vis_vs = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0,
vis_hs = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0,
draw = d & (FL_DAMAGE_ALL | FL_DAMAGE_EXPOSE),
draw_vs = vis_vs && vscrollbar->damage (),
draw_hs = vis_hs && hscrollbar->damage ();
_MSG("FltkViewport::draw d=%d => ", d);
// scrollbars
if (draw || draw_vs)
draw_child (*vscrollbar);
if (draw || draw_hs)
draw_child (*hscrollbar);
if (draw && vis_vs && vis_hs) {
fl_color(FL_BACKGROUND_COLOR);
if (scrollbarOnLeft) {
fl_rectf(x(), y()+h()-vis_hs, vis_vs, vis_hs);
} else {
fl_rectf(x()+w()-vis_vs, y()+h()-vis_hs, vis_vs, vis_hs);
}
}
// main area
if (d == FL_DAMAGE_CHILD && (draw_vs || draw_hs)) {
_MSG("none\n");
} else if (d == (FL_DAMAGE_SCROLL | FL_DAMAGE_CHILD)) {
int x = this->x();
if (scrollbarOnLeft)
x += vis_vs;
fl_scroll(x, y(), w() - vis_vs, h() - vis_hs,
-scrollDX, -scrollDY, draw_area, this);
_MSG("fl_scroll()\n");
} else {
int x = this->x();
if (scrollbarOnLeft)
x += vis_vs;
draw_area(this, x, y(), w() - vis_vs, h() - vis_hs);
_MSG("draw_area()\n");
}
scrollDX = 0;
scrollDY = 0;
}
int FltkViewport::handle (int event)
{
int ret = 0;
_MSG("FltkViewport::handle %s\n", fl_eventnames[event]);
switch(event) {
case FL_KEYBOARD:
/* When the viewport has focus (and not one of its children), FLTK
* sends the event here. Returning zero tells FLTK to resend the
* event as SHORTCUT, which we finally route to the parent. */
/* As we don't know the exact keybindings set by the user, we ask for
* all of them (except for the minimum needed to keep form navigation).*/
if (Fl::event_key() != FL_Tab || Fl::event_ctrl())
return 0;
break;
case FL_SHORTCUT:
/* send it to the parent (UI) */
return 0;
case FL_FOCUS:
/** \bug Draw focus box. */
break;
case FL_UNFOCUS:
/** \bug Undraw focus box. */
break;
case FL_PUSH:
if (vscrollbar->visible() && Fl::event_inside(vscrollbar)) {
if (scrollbarPageMode ^ (bool) Fl::event_shift()) {
/* Check top and bottom actions first */
int yclick = Fl::event_y();
int ytop = y() + SCROLLBAR_THICKNESS;
int ybottom = y() + h() - SCROLLBAR_THICKNESS;
if (hscrollbar->visible())
ybottom -= SCROLLBAR_THICKNESS;
if (yclick <= ytop) {
scroll(core::TOP_CMD);
return 1;
} else if (yclick >= ybottom) {
scroll(core::BOTTOM_CMD);
return 1;
}
if (Fl::event_button() == FL_LEFT_MOUSE)
pageScrolling = core::SCREEN_DOWN_CMD;
else if (Fl::event_button() == FL_RIGHT_MOUSE)
pageScrolling = core::SCREEN_UP_CMD;
else
pageScrolling = core::NONE_CMD;
if (pageScrolling != core::NONE_CMD) {
scroll(pageScrolling);
/* Repeat until released */
if (!Fl::has_timeout(repeatPageScroll, this))
Fl::add_timeout(pageScrollDelay, repeatPageScroll, this);
return 1;
}
}
if (vscrollbar->handle(event)) {
verScrolling = 1;
}
} else if (hscrollbar->visible() && Fl::event_inside(hscrollbar)) {
if (hscrollbar->handle(event))
horScrolling = 1;
} else if (FltkWidgetView::handle(event) == 0 &&
Fl::event_button() == FL_MIDDLE_MOUSE) {
if (!hasDragScroll) {
/* let the parent widget handle it... */
return 0;
} else {
/* receive FL_DRAG and FL_RELEASE */
dragScrolling = 1;
dragX = Fl::event_x();
dragY = Fl::event_y();
setCursor (core::style::CURSOR_MOVE);
}
}
return 1;
break;
case FL_DRAG:
if (Fl::event_inside(this))
Fl::remove_timeout(selectionScroll);
if (dragScrolling) {
scroll(dragX - Fl::event_x(), dragY - Fl::event_y());
dragX = Fl::event_x();
dragY = Fl::event_y();
return 1;
} else if (verScrolling) {
vscrollbar->handle(event);
return 1;
} else if (horScrolling) {
hscrollbar->handle(event);
return 1;
} else if (!Fl::event_inside(this)) {
mouse_x = Fl::event_x();
mouse_y = Fl::event_y();
if (!Fl::has_timeout(selectionScroll, this))
Fl::add_timeout(0.025, selectionScroll, this);
}
break;
case FL_MOUSEWHEEL:
if ((vscrollbar->visible() && Fl::event_inside(vscrollbar)) ||
Fl::event_shift()) {
if (Fl::event_dy() > 0) {
scroll(core::SCREEN_DOWN_CMD);
return 1;
} else if (Fl::event_dy() < 0) {
scroll(core::SCREEN_UP_CMD);
return 1;
}
}
return (Fl::event_dx() ? hscrollbar : vscrollbar)->handle(event);
break;
case FL_RELEASE:
Fl::remove_timeout(repeatPageScroll);
Fl::remove_timeout(selectionScroll);
if (Fl::event_button() == FL_MIDDLE_MOUSE) {
setCursor (core::style::CURSOR_DEFAULT);
} else if (verScrolling) {
ret = vscrollbar->handle(event);
} else if (horScrolling) {
ret = hscrollbar->handle(event);
}
horScrolling = verScrolling = dragScrolling = 0;
break;
case FL_ENTER:
if (vscrollbar->visible() && Fl::event_inside(vscrollbar))
return vscrollbar->handle(event);
if (hscrollbar->visible() && Fl::event_inside(hscrollbar))
return hscrollbar->handle(event);
/* could be the result of, e.g., closing another window. */
mouse_x = Fl::event_x();
mouse_y = Fl::event_y();
positionChanged();
break;
case FL_MOVE:
/* Use LEAVE in order not to be over a link, etc., anymore. */
if (vscrollbar->visible() && Fl::event_inside(vscrollbar)) {
(void)FltkWidgetView::handle(FL_LEAVE);
return vscrollbar->handle(event);
}
if (hscrollbar->visible() && Fl::event_inside(hscrollbar)) {
(void)FltkWidgetView::handle(FL_LEAVE);
return hscrollbar->handle(event);
}
break;
case FL_LEAVE:
mouse_x = mouse_y = -1;
break;
}
return ret ? ret : FltkWidgetView::handle (event);
}
// ----------------------------------------------------------------------
void FltkViewport::setCanvasSize (int width, int ascent, int descent)
{
FltkWidgetView::setCanvasSize (width, ascent, descent);
adjustScrollbarValues ();
}
/*
* This is used to simulate mouse motion (e.g., when scrolling).
*/
void FltkViewport::positionChanged ()
{
if (!dragScrolling && mouse_x >= x() && mouse_x < x()+w() && mouse_y >= y()
&& mouse_y < y()+h())
(void)theLayout->motionNotify (this,
translateViewXToCanvasX (mouse_x),
translateViewYToCanvasY (mouse_y),
(core::ButtonState)0);
}
/*
* For scrollbars, this currently sets the same step to both vertical and
* horizontal. It may be differentiated if necessary.
*/
void FltkViewport::setScrollStep(int step)
{
vscrollbar->linesize(step);
hscrollbar->linesize(step);
}
void FltkViewport::setPageOverlap(int overlap)
{
pageOverlap = overlap;
}
void FltkViewport::setScrollbarPageMode(bool enable)
{
scrollbarPageMode = enable;
}
bool FltkViewport::usesViewport ()
{
return true;
}
int FltkViewport::getHScrollbarThickness ()
{
return SCROLLBAR_THICKNESS;
}
int FltkViewport::getVScrollbarThickness ()
{
return SCROLLBAR_THICKNESS;
}
void FltkViewport::scrollTo (int x, int y)
{
int hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
int vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
x = misc::min (x, canvasWidth - w() + hdiff);
x = misc::max (x, 0);
y = misc::min (y, canvasHeight - h() + vdiff);
y = misc::max (y, 0);
if (x == scrollX && y == scrollY) {
return;
}
/* multiple calls to scroll can happen before a redraw occurs.
* scrollDX and scrollDY can therefore be non-zero here.
*/
updateCanvasWidgets (x - scrollX, y - scrollY);
scrollDX += x - scrollX;
scrollDY += y - scrollY;
scrollX = x;
scrollY = y;
adjustScrollbarValues ();
damage(FL_DAMAGE_SCROLL);
theLayout->scrollPosChanged (this, scrollX, scrollY);
positionChanged();
}
void FltkViewport::scroll (int dx, int dy)
{
scrollTo (scrollX + dx, scrollY + dy);
}
void FltkViewport::scroll (core::ScrollCommand cmd)
{
int hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
int vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
if (cmd == core::SCREEN_UP_CMD) {
scroll (0, -h () + pageOverlap + vdiff);
} else if (cmd == core::SCREEN_DOWN_CMD) {
scroll (0, h () - pageOverlap - vdiff);
} else if (cmd == core::SCREEN_LEFT_CMD) {
scroll (-w() + pageOverlap + hdiff, 0);
} else if (cmd == core::SCREEN_RIGHT_CMD) {
scroll (w() - pageOverlap - hdiff, 0);
} else if (cmd == core::LINE_UP_CMD) {
scroll (0, -vscrollbar->linesize ());
} else if (cmd == core::LINE_DOWN_CMD) {
scroll (0, vscrollbar->linesize ());
} else if (cmd == core::LEFT_CMD) {
scroll (-hscrollbar->linesize (), 0);
} else if (cmd == core::RIGHT_CMD) {
scroll (hscrollbar->linesize (), 0);
} else if (cmd == core::TOP_CMD) {
scrollTo (scrollX, 0);
} else if (cmd == core::BOTTOM_CMD) {
scrollTo (scrollX, canvasHeight); /* gets adjusted in scrollTo () */
}
}
/*
* Scrolling in response to selection where the cursor is outside the view.
*/
void FltkViewport::selectionScroll ()
{
int distance;
int dx = 0, dy = 0;
if ((distance = x() - mouse_x) > 0)
dx = -distance * hscrollbar->linesize () / 48 - 1;
else if ((distance = mouse_x - (x() + w())) > 0)
dx = distance * hscrollbar->linesize () / 48 + 1;
if ((distance = y() - mouse_y) > 0)
dy = -distance * vscrollbar->linesize () / 48 - 1;
else if ((distance = mouse_y - (y() + h())) > 0)
dy = distance * vscrollbar->linesize () / 48 + 1;
scroll (dx, dy);
}
void FltkViewport::selectionScroll (void *data)
{
((FltkViewport *)data)->selectionScroll ();
Fl::repeat_timeout(0.025, selectionScroll, data);
}
void FltkViewport::repeatPageScroll ()
{
scroll(pageScrolling);
Fl::repeat_timeout(pageScrollInterval, repeatPageScroll, this);
}
void FltkViewport::repeatPageScroll (void *data)
{
((FltkViewport *)data)->repeatPageScroll ();
}
void FltkViewport::setScrollbarOnLeft (bool enable)
{
scrollbarOnLeft = enable ? 1 : 0;
adjustScrollbarsAndGadgetsAllocation();
damage(FL_DAMAGE_ALL);
}
void FltkViewport::setViewportSize (int width, int height,
int hScrollbarThickness,
int vScrollbarThickness)
{
int adjustReq =
(hscrollbar->visible() ? !hScrollbarThickness : hScrollbarThickness) ||
(vscrollbar->visible() ? !vScrollbarThickness : vScrollbarThickness);
_MSG("FltkViewport::setViewportSize old_w,old_h=%dx%d -> w,h=%dx%d\n"
"\t hThick=%d hVis=%d, vThick=%d vVis=%d, adjustReq=%d\n",
w(),h(),width,height,
hScrollbarThickness,hscrollbar->visible(),
vScrollbarThickness,vscrollbar->visible(), adjustReq);
(hScrollbarThickness > 0) ? hscrollbar->show () : hscrollbar->hide ();
(vScrollbarThickness > 0) ? vscrollbar->show () : vscrollbar->hide ();
/* If no scrollbar, go to the beginning */
scroll(hScrollbarThickness ? 0 : -scrollX,
vScrollbarThickness ? 0 : -scrollY);
/* Adjust when scrollbar visibility changes */
if (adjustReq)
adjustScrollbarsAndGadgetsAllocation ();
}
void FltkViewport::updateCanvasWidgets (int dx, int dy)
{
// scroll all child widgets except scroll bars
for (int i = children () - 1; i > 0; i--) {
Fl_Widget *widget = child (i);
if (widget == hscrollbar || widget == vscrollbar)
continue;
widget->position(widget->x () - dx, widget->y () - dy);
}
}
int FltkViewport::translateViewXToCanvasX (int X)
{
return X - x () + scrollX;
}
int FltkViewport::translateViewYToCanvasY (int Y)
{
return Y - y () + scrollY;
}
int FltkViewport::translateCanvasXToViewX (int X)
{
return X + x () - scrollX;
}
int FltkViewport::translateCanvasYToViewY (int Y)
{
return Y + y () - scrollY;
}
// ----------------------------------------------------------------------
void FltkViewport::setGadgetOrientation (bool hscrollbarVisible,
bool vscrollbarVisible,
FltkViewport::GadgetOrientation
gadgetOrientation)
{
this->gadgetOrientation[(hscrollbarVisible ? 0 : 1) |
(vscrollbarVisible ? 0 : 2)] = gadgetOrientation;
adjustScrollbarsAndGadgetsAllocation ();
}
void FltkViewport::addGadget (Fl_Widget *gadget)
{
/** \bug Reparent? */
gadgets->append (new TypedPointer < Fl_Widget> (gadget));
adjustScrollbarsAndGadgetsAllocation ();
}
} // namespace fltk
} // namespace dw

117
dw/fltkviewport.hh Normal file
View File

@ -0,0 +1,117 @@
/*
* Dillo Widget
*
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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.
*
* 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/>.
*/
#ifndef __DW_FLTKVIEWPORT_HH__
#define __DW_FLTKVIEWPORT_HH__
#include <FL/Fl_Group.H>
#include <FL/Fl_Scrollbar.H>
#include "core.hh"
#include "fltkcore.hh"
#include "fltkviewbase.hh"
namespace dw {
namespace fltk {
class FltkViewport: public FltkWidgetView
{
public:
enum GadgetOrientation { GADGET_VERTICAL, GADGET_HORIZONTAL };
private:
enum { SCROLLBAR_THICKNESS = 15 };
int scrollX, scrollY;
int scrollDX, scrollDY;
int scrollbarOnLeft;
int hasDragScroll, dragScrolling, dragX, dragY;
int horScrolling, verScrolling;
bool scrollbarPageMode;
int pageOverlap;
enum dw::core::ScrollCommand pageScrolling;
float pageScrollInterval;
float pageScrollDelay;
Fl_Scrollbar *vscrollbar, *hscrollbar;
GadgetOrientation gadgetOrientation[4];
lout::container::typed::List <lout::object::TypedPointer < Fl_Widget> >
*gadgets;
void adjustScrollbarsAndGadgetsAllocation ();
void adjustScrollbarValues ();
void hscrollbarChanged ();
void vscrollbarChanged ();
void positionChanged ();
static void hscrollbarCallback (Fl_Widget *hscrollbar, void *viewportPtr);
static void vscrollbarCallback (Fl_Widget *vscrollbar, void *viewportPtr);
void selectionScroll();
static void selectionScroll(void *vport);
void repeatPageScroll ();
static void repeatPageScroll (void *data);
void updateCanvasWidgets (int oldScrollX, int oldScrollY);
static void draw_area (void *data, int x, int y, int w, int h);
protected:
int translateViewXToCanvasX (int x);
int translateViewYToCanvasY (int y);
int translateCanvasXToViewX (int x);
int translateCanvasYToViewY (int y);
public:
FltkViewport (int x, int y, int w, int h, const char *label = 0);
~FltkViewport ();
void resize(int x, int y, int w, int h);
void draw ();
int handle (int event);
void setCanvasSize (int width, int ascent, int descent);
bool usesViewport ();
int getHScrollbarThickness ();
int getVScrollbarThickness ();
int getScrollbarOnLeft () {return scrollbarOnLeft; };
void scroll(int dx, int dy);
void scroll(dw::core::ScrollCommand cmd);
void scrollTo (int x, int y);
void setViewportSize (int width, int height,
int hScrollbarThickness, int vScrollbarThickness);
void setScrollStep(int step);
void setGadgetOrientation (bool hscrollbarVisible, bool vscrollbarVisible,
GadgetOrientation gadgetOrientation);
void setDragScroll (bool enable) { hasDragScroll = enable ? 1 : 0; }
void addGadget (Fl_Widget *gadget);
void setScrollbarOnLeft (bool enable);
void setScrollbarPageMode(bool enable);
void setPageOverlap(int overlap);
};
} // namespace fltk
} // namespace dw
#endif // __DW_FLTKVIEWPORT_HH__

603
dw/hyphenator.cc Normal file
View File

@ -0,0 +1,603 @@
/*
* Dillo Widget
*
* Copyright 2012-2013 Sebastian Geerken <sgeerken@dillo.org>,
* Johannes Hofmann <Johannes.Hofmann@gmx.de>
*
* 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.
*
* 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 "hyphenator.hh"
#include "dlib/dlib.h"
#include "../lout/misc.hh"
#include "../lout/unicode.hh"
#include <limits.h>
#include <stdio.h>
#include <string.h>
#define LEN 1000
/*
* This is (or at least began as) a direct translation of Ned Batchelder's
* public-domain Python implementation at
* http://nedbatchelder.com/code/modules/hyphenate.py
*/
using namespace lout::object;
using namespace lout::container::typed;
using namespace lout::misc;
using namespace lout::unicode;
namespace dw {
HashTable <String, Hyphenator> *Hyphenator::hyphenators =
new HashTable <String, Hyphenator> (true, true);
Hyphenator::Hyphenator (const char *patFile, const char *excFile, int pack)
{
trie = NULL; // As long we are not sure whether a pattern file can be read.
int bufLen = strlen (patFile) + 5 + 1;
char *buf = new char[bufLen];
snprintf(buf, bufLen, "%s.trie", patFile);
FILE *trieF = fopen (buf, "r");
delete[] buf;
if (trieF) {
trie = new Trie ();
if (trie->load (trieF) != 0) {
delete trie;
trie = NULL;
}
fclose (trieF);
}
if (trie == NULL) {
TrieBuilder trieBuilder(pack);
FILE *patF = fopen (patFile, "r");
if (patF) {
while (!feof (patF)) {
char buf[LEN + 1];
char *s = fgets (buf, LEN, patF);
if (s && s[0] != '%') { // ignore lines starting with '%' as comment
// TODO Better exit with an error, when the line is too long.
int l = strlen (s);
if (s[l - 1] == '\n')
s[l - 1] = 0;
insertPattern (&trieBuilder, s);
}
}
trie = trieBuilder.createTrie ();
fclose (patF);
}
}
exceptions = NULL; // Again, only instantiated when needed.
FILE *excF = fopen (excFile, "r");
if (excF) {
exceptions = new HashTable <ConstString, Vector <Integer> > (true, true);
while (!feof (excF)) {
char buf[LEN + 1];
char *s = fgets (buf, LEN, excF);
if (s && s[0] != '%') { // ignore lines starting with '%' as comment
// TODO Better exit with an error, when the line is too long.
int l = strlen (s);
if (s[l - 1] == '\n')
s[l - 1] = 0;
insertException (s);
}
}
fclose (excF);
}
}
Hyphenator::~Hyphenator ()
{
delete trie;
delete exceptions;
}
Hyphenator *Hyphenator::getHyphenator (const char *lang)
{
String *langString = new String (lang);
Hyphenator *hyphenator = hyphenators->get (langString);
if (hyphenator)
delete langString;
else {
int patFileLen = strlen (DILLO_LIBDIR) + 13 + strlen (lang) + 4 + 1;
char *patFile = new char[patFileLen];
snprintf (patFile, patFileLen, "%s/hyphenation/%s.pat",
DILLO_LIBDIR, lang);
int excFileLen = strlen (DILLO_LIBDIR) + 13 + strlen (lang) + 4 + 1;
char *excFile = new char[excFileLen];
snprintf (excFile, excFileLen, "%s/hyphenation/%s.exc",
DILLO_LIBDIR, lang);
//printf ("Loading hyphenation patterns for language '%s' from '%s' and "
// "exceptions from '%s' ...\n", lang, patFile, excFile);
hyphenator = new Hyphenator (patFile, excFile);
hyphenators->put (langString, hyphenator);
delete[] patFile;
delete[] excFile;
}
//lout::misc::StringBuffer sb;
//hyphenators->intoStringBuffer (&sb);
//printf ("%s\n", sb.getChars ());
return hyphenator;
}
void Hyphenator::insertPattern (TrieBuilder *trieBuilder, char *s)
{
// Convert the a pattern like 'a1bc3d4' into a string of chars 'abcd'
// and a list of points [ 0, 1, 0, 3, 4 ].
int l = strlen (s);
char *chars = new char[l + 1];
SimpleVector<char> points (1);
// TODO numbers consisting of multiple digits?
// TODO Encoding: This implementation works exactly like the Python
// implementation, based on UTF-8. Does this always work?
int numChars = 0;
for (int i = 0; s[i]; i++) {
if (s[i] >= '0' && s[i] <= '9') {
points.setSize(numChars + 1, '0');
points.set(numChars, s[i]);
} else {
chars[numChars++] = s[i];
}
}
chars[numChars] = 0;
points.setSize(numChars + 2, '0');
points.set(numChars + 1, '\0');
// Insert the pattern into the tree. Each character finds a dict
// another level down in the tree, and leaf nodes have the list of
// points.
//printf("insertPattern %s\n", chars);
trieBuilder->insert (chars, points.getArray ());
delete[] chars;
}
void Hyphenator::insertException (char *s)
{
Vector<Integer> *breaks = new Vector<Integer> (1, true);
int len = strlen (s);
for (int i = 0; i < len - 1; i++)
if((unsigned char)s[i] == 0xc2 && (unsigned char)s[i + 1] == 0xad)
breaks->put (new Integer (i - 2 * breaks->size()));
char *noHyphens = new char[len - 2 * breaks->size() + 1];
int j = 0;
for (int i = 0; i < len; ) {
if(i < len - 1 &&
(unsigned char)s[i] == 0xc2 && (unsigned char)s[i + 1] == 0xad)
i += 2;
else
noHyphens[j++] = s[i++];
}
noHyphens[j] = 0;
exceptions->put (new String (noHyphens), breaks);
delete[] noHyphens;
}
/**
* Simple test to avoid much costs. Passing it does not mean that the word
* can be hyphenated.
*/
bool Hyphenator::isHyphenationCandidate (const char *word)
{
// Short words aren't hyphenated.
return (strlen (word) > 4); // TODO UTF-8?
}
/**
* Test whether the character on which "s" points (UTF-8) is an actual
* part of the word. Other characters at the beginning and end are
* ignored.
*
* TODO Currently only suitable for English and German.
* TODO Only lowercase. (Uppercase not needed.)
*/
bool Hyphenator::isCharPartOfActualWord (char *s)
{
return isAlpha (decodeUtf8 (s));
}
/**
* Given a word, returns a list of the possible hyphenation points.
*/
int *Hyphenator::hyphenateWord(core::Platform *platform,
const char *word, int *numBreaks)
{
if ((trie == NULL && exceptions == NULL) || !isHyphenationCandidate (word)) {
*numBreaks = 0;
return NULL;
}
char *wordLc = platform->textToLower (word, strlen (word));
int start = 0;
SimpleVector <int> breakPos (1);
// Split the original word up, ignore anything but characters, and
// collect all break points, so that they fit to the original
// word. (The latter is what the offset in the call of
// hyphenateSingleWord() is for.)
while (true) {
while (wordLc[start] && !isCharPartOfActualWord (wordLc + start))
start = platform->nextGlyph (wordLc, start);
if (wordLc[start] == 0)
break;
int end = start, i = end;
while (wordLc[i]) {
if (!isCharPartOfActualWord (wordLc + i))
break;
else
end = i;
i = platform->nextGlyph (wordLc, i);
}
end = platform->nextGlyph (wordLc, end);
int nextStart;
if (wordLc[end]) {
nextStart = platform->nextGlyph (wordLc, end);
wordLc[end] = 0;
} else
nextStart = end;
hyphenateSingleWord (platform, wordLc + start, start, &breakPos);
start = nextStart;
}
free (wordLc);
*numBreaks = breakPos.size ();
if (*numBreaks == 0)
return NULL;
else {
return breakPos.detachArray ();
}
}
/**
* Hyphenate a single word, which only consists of lowercase
* characters. Store break positions + "offset" in "breakPos".
*/
void Hyphenator::hyphenateSingleWord(core::Platform *platform,
char *wordLc, int offset,
SimpleVector <int> *breakPos)
{
// If the word is an exception, get the stored points.
Vector <Integer> *exceptionalBreaks;
ConstString key (wordLc);
if (exceptions != NULL && (exceptionalBreaks = exceptions->get (&key))) {
for (int i = 0; i < exceptionalBreaks->size(); i++) {
breakPos->increase ();
breakPos->set (breakPos->size() - 1,
exceptionalBreaks->get(i)->getValue() + offset);
}
return;
}
// trie == NULL means that there is no pattern file.
if (trie == NULL)
return;
char *work = new char[strlen (wordLc) + 3];
strcpy (work, ".");
strcat (work, wordLc);
strcat (work, ".");
int l = strlen (work);
SimpleVector <int> points (l + 1);
points.setSize (l + 1, 0);
for (int i = 0; i < l; i++) {
int state = trie->root;
for (int j = i; j < l && trie->validState (state); j++) {
const char *p = trie->getData((unsigned char) work[j], &state);
if (p) {
for (int k = 0; p[k]; k++)
points.set(i + k,
lout::misc::max (points.get (i + k), p[k] - '0'));
}
}
}
delete[] work;
// No hyphens in the first two chars or the last two.
// Characters are not bytes, so UTF-8 characters must be counted.
const char *s = nextUtf8Char (wordLc);
if (s != NULL && (s = nextUtf8Char (s)) != NULL) {
// First two characters.
int bytesStart = s - wordLc;
for (int i = 0; i < bytesStart; i++)
points.set (i + 1, 0);
// Last two characters: instead of iterating back from the end,
// we simply iterate from the start to the end and count the
// characters.
int lenBytes = strlen (wordLc);
int lenUtf8 = numUtf8Chars (wordLc);
int bytesEnd = 0;
s = wordLc;
for (int i = 0; s; s = nextUtf8Char (s), i++) {
if (i == lenUtf8 - 2)
bytesEnd = lenBytes - (s - wordLc);
}
for (int i = 0; i < bytesEnd; i++)
points.set (points.size() - 2 - i, 0);
}
// Examine the points to build the break point list.
int n = lout::misc::min ((int)strlen (wordLc), points.size () - 2);
for (int i = 0; i < n; i++) {
if (points.get(i + 2) % 2) {
breakPos->increase ();
breakPos->set (breakPos->size() - 1, i + 1 + offset);
}
}
}
Trie::TrieNode TrieBuilder::trieNodeNull = {'\0', 0, NULL};
TrieBuilder::TrieBuilder (int pack)
{
this->pack = pack;
dataList = new SimpleVector <DataEntry> (10000);
stateStack = new SimpleVector <StackEntry> (10);
tree = new SimpleVector <Trie::TrieNode> (20000);
dataZone = new ZoneAllocator (1024);
stateStackPush(0);
}
TrieBuilder::~TrieBuilder ()
{
delete dataList;
delete stateStack;
delete tree;
delete dataZone;
}
void TrieBuilder::insert (const char *key, const char *value)
{
dataList->increase ();
dataList->getLastRef ()->key = (unsigned char *) dStrdup(key);
dataList->getLastRef ()->value = dataZone->strdup (value);
}
int TrieBuilder::keyCompare (const void *p1, const void *p2)
{
DataEntry *pd1 = (DataEntry *) p1;
DataEntry *pd2 = (DataEntry *) p2;
return strcmp ((char *) pd1->key, (char *) pd2->key);
}
int TrieBuilder::insertState (StackEntry *state, bool root)
{
int i, j;
if (state->count == 0)
return 0;
if (root) {
i = 0; // we reserve slot 0 for the root state
} else {
/* The bigger pack is the more slots we check and the smaller
* the trie will be, but CPU consumption also increases.
* Reasonable values for pack seemt to be between 256 and 1024.
*/
i = tree->size () - pack + 2 * state->count;
if (i < 256) // reserve first 256 entries for the root state
i = 256;
}
for (;; i++) {
if (i + 256 > tree->size ())
tree->setSize (i + 256, trieNodeNull);
for (j = 1; j < 256; j++) {
Trie::TrieNode *tn = tree->getRef(i + j);
if (tn->c == j || ((state->next[j] || state->data[j]) && tn->c != 0))
break;
}
if (j == 256) // found a suitable slot
break;
}
for (int j = 1; j < 256; j++) {
Trie::TrieNode *tn = tree->getRef(i + j);
if (state->next[j] || state->data[j]) {
tn->c = j;
tn->next = state->next[j];
tn->data = state->data[j];
}
}
assert (root || i >= 256);
assert (!root || i == 0);
return i;
}
void TrieBuilder::stateStackPush (unsigned char c)
{
stateStack->increase ();
StackEntry *e = stateStack->getLastRef ();
memset (e, 0, sizeof (StackEntry));
e->c = c;
}
int TrieBuilder::stateStackPop ()
{
int next = insertState (stateStack->getLastRef (), stateStack->size () == 1);
unsigned char c = stateStack->getLastRef ()->c;
const char *data = stateStack->getLastRef ()->data1;
stateStack->setSize (stateStack->size () - 1);
if (stateStack->size () > 0) {
assert (stateStack->getLastRef ()->next[c] == 0);
assert (stateStack->getLastRef ()->data[c] == NULL);
stateStack->getLastRef ()->next[c] = next;
stateStack->getLastRef ()->data[c] = data;
stateStack->getLastRef ()->count++;
}
return next;
}
Trie *TrieBuilder::createTrie ()
{
// we need to sort the patterns as byte strings not as unicode
qsort (dataList->getArray (), dataList->size (),
sizeof (DataEntry), keyCompare);
for (int i = 0; i < dataList->size (); i++) {
insertSorted (dataList->getRef (i)->key, dataList->getRef (i)->value);
free (dataList->getRef (i)->key);
}
while (stateStack->size ())
stateStackPop ();
int size = tree->size ();
Trie *trie = new Trie(tree->detachArray(), size, true, dataZone);
dataZone = NULL;
return trie;
}
void TrieBuilder::insertSorted (unsigned char *s, const char *data)
{
int len = strlen((char*)s);
for (int i = 0; i < len; i++) {
if (stateStack->size () > i + 1 &&
stateStack->getRef (i + 1)->c != s[i]) {
for (int j = stateStack->size () - 1; j >= i + 1; j--)
stateStackPop();
}
if (i + 1 >= stateStack->size ())
stateStackPush(s[i]);
}
while (stateStack->size () > len + 1)
stateStackPop();
assert (stateStack->size () == len + 1);
stateStack->getLastRef ()->data1 = data;
}
Trie::Trie (TrieNode *array, int size, bool freeArray, ZoneAllocator *dataZone)
{
this->array = array;
this->size = size;
this->freeArray = freeArray;
this->dataZone = dataZone;
}
Trie::~Trie ()
{
delete dataZone;
if (freeArray)
free(array);
}
void Trie::save (FILE *file)
{
for (int i = 0; i < size; i++) {
Trie::TrieNode *tn = &array[i];
if (tn->data)
fprintf(file, "%u, %u, %s\n", tn->c, tn->next, tn->data);
else
fprintf(file, "%u, %u\n", tn->c, tn->next);
}
}
int Trie::load (FILE *file)
{
int next, c, maxNext = 0;
SimpleVector <TrieNode> tree (100);
dataZone = new ZoneAllocator (1024);
while (!feof (file)) {
char buf[LEN + 1];
char *s = fgets (buf, LEN, file);
if (!s)
continue;
char data[LEN + 1];
int n = sscanf (s, "%d, %d, %s", &c, &next, data);
if (n >= 2 && c >= 0 && c < 256 && next >= 0) {
tree.increase ();
tree.getLastRef ()->c = c;
tree.getLastRef ()->next = next;
if (n >= 3)
tree.getLastRef ()->data = dataZone->strdup (data);
else
tree.getLastRef ()->data = NULL;
if (next > maxNext)
maxNext = next;
} else {
goto error;
}
}
if (maxNext >= tree.size ())
goto error;
size = tree.size ();
array = tree.detachArray ();
freeArray = true;
return 0;
error:
delete dataZone;
dataZone = NULL;
return 1;
}
} // namespace dw

115
dw/hyphenator.hh Normal file
View File

@ -0,0 +1,115 @@
#ifndef __DW_HYPHENATOR_HH__
#define __DW_HYPHENATOR_HH__
#include "../lout/object.hh"
#include "../lout/container.hh"
#include "../dw/core.hh"
namespace dw {
class Trie {
public:
struct TrieNode {
unsigned char c;
uint16_t next;
const char *data;
};
private:
TrieNode *array;
int size;
bool freeArray;
lout::misc::ZoneAllocator *dataZone;
public:
Trie (TrieNode *array = NULL, int size = 0, bool freeArray = false,
lout::misc::ZoneAllocator *dataZone = NULL);
~Trie ();
static const int root = 0;
inline bool validState (int state) { return state >= 0 && state < size; };
inline const char *getData (unsigned char c, int *state)
{
if (!validState (*state))
return NULL;
TrieNode *tn = array + *state + c;
if (tn->c == c) {
*state = tn->next > 0 ? tn->next : -1;
return tn->data;
} else {
*state = -1;
return NULL;
}
};
void save (FILE *file);
int load (FILE *file);
};
class TrieBuilder {
private:
struct StackEntry {
unsigned char c;
int count;
int next[256];
const char *data[256];
const char *data1;
};
struct DataEntry {
unsigned char *key;
const char *value;
};
int pack;
static Trie::TrieNode trieNodeNull;
lout::misc::SimpleVector <Trie::TrieNode> *tree;
lout::misc::SimpleVector <DataEntry> *dataList;
lout::misc::SimpleVector <StackEntry> *stateStack;
lout::misc::ZoneAllocator *dataZone;
static int keyCompare (const void *p1, const void *p2);
void stateStackPush (unsigned char c);
int stateStackPop ();
int insertState (StackEntry *state, bool root);
void insertSorted (unsigned char *key, const char *value);
public:
TrieBuilder (int pack);
~TrieBuilder ();
void insert (const char *key, const char *value);
Trie *createTrie();
};
class Hyphenator: public lout::object::Object
{
static lout::container::typed::HashTable
<lout::object::String, Hyphenator> *hyphenators;
Trie *trie;
lout::container::typed::HashTable <lout::object::ConstString,
lout::container::typed::Vector
<lout::object::Integer> > *exceptions;
void insertPattern (TrieBuilder *trieBuilder, char *s);
void insertException (char *s);
void hyphenateSingleWord(core::Platform *platform, char *wordLc, int offset,
lout::misc::SimpleVector <int> *breakPos);
bool isCharPartOfActualWord (char *s);
public:
Hyphenator (const char *patFile, const char *excFile, int pack = 256);
~Hyphenator();
static Hyphenator *getHyphenator (const char *language);
static bool isHyphenationCandidate (const char *word);
int *hyphenateWord(core::Platform *platform, const char *word, int *numBreaks);
void saveTrie (FILE *fp) { trie->save (fp); };
};
} // namespace dw
#endif // __DW_HYPHENATOR_HH__

567
dw/image.cc Normal file
View File

@ -0,0 +1,567 @@
/*
* Dillo Widget
*
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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.
*
* 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/>.
*/
//#define DEBUG_LEVEL 1
#include "image.hh"
#include "dlib/dlib.h"
#include "../lout/msg.h"
#include "../lout/misc.hh"
#include "../lout/debug.hh"
namespace dw {
using namespace lout;
ImageMapsList::ImageMap::ImageMap ()
{
shapesAndLinks = new container::typed::List <ShapeAndLink> (true);
defaultLink = -1;
}
ImageMapsList::ImageMap::~ImageMap ()
{
delete shapesAndLinks;
}
void ImageMapsList::ImageMap::draw (core::View *view,core::style::Style *style,
int x, int y)
{
container::typed::Iterator <ShapeAndLink> it;
for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
ShapeAndLink *shapeAndLink = it.getNext ();
shapeAndLink->shape->draw(view, style, x, y);
}
}
void ImageMapsList::ImageMap::add (core::Shape *shape, int link) {
ShapeAndLink *shapeAndLink = new ShapeAndLink ();
shapeAndLink->shape = shape;
shapeAndLink->link = link;
shapesAndLinks->append (shapeAndLink);
}
int ImageMapsList::ImageMap::link (int x, int y) {
container::typed::Iterator <ShapeAndLink> it;
int link = defaultLink;
for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
ShapeAndLink *shapeAndLink = it.getNext ();
if (shapeAndLink->shape->isPointWithin (x, y)) {
link = shapeAndLink->link;
break;
}
}
return link;
}
ImageMapsList::ImageMapsList ()
{
imageMaps = new container::typed::HashTable <object::Object, ImageMap>
(true, true);
currentMap = NULL;
}
ImageMapsList::~ImageMapsList ()
{
delete imageMaps;
}
/**
* \brief Start a new map and make it the current one.
*
* This has to be called before dw::ImageMapsList::addShapeToCurrentMap.
* "key" is owned by the image map list, so a copy should be passed, when
* necessary.
*/
void ImageMapsList::startNewMap (object::Object *key)
{
currentMap = new ImageMap ();
imageMaps->put (key, currentMap);
}
/**
* \brief Add a shape to the current map-
*
* "shape" is owned by the image map list, so a copy should be passed, when
* necessary.
*/
void ImageMapsList::addShapeToCurrentMap (core::Shape *shape, int link)
{
currentMap->add (shape, link);
}
/**
* \brief Set default link for current map-
*/
void ImageMapsList::setCurrentMapDefaultLink (int link)
{
currentMap->setDefaultLink (link);
}
void ImageMapsList::drawMap (lout::object::Object *key, core::View *view,
core::style::Style *style, int x, int y)
{
ImageMap *map = imageMaps->get (key);
if (map)
map->draw(view, style, x, y);
}
int ImageMapsList::link (object::Object *key, int x, int y)
{
int link = -1;
ImageMap *map = imageMaps->get (key);
if (map)
link = map->link (x, y);
return link;
}
// ----------------------------------------------------------------------
int Image::CLASS_ID = -1;
Image::Image(const char *altText)
{
DBG_OBJ_CREATE ("dw::Image");
registerName ("dw::Image", &CLASS_ID);
this->altText = altText ? dStrdup (altText) : NULL;
altTextWidth = -1; // not yet calculated
buffer = NULL;
bufWidth = bufHeight = -1;
clicking = false;
currLink = -1;
mapList = NULL;
mapKey = NULL;
isMap = false;
DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
}
Image::~Image()
{
if (altText)
free(altText);
if (buffer)
buffer->unref ();
if (mapKey)
delete mapKey;
DBG_OBJ_DELETE ();
}
void Image::sizeRequestSimpl (core::Requisition *requisition)
{
DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");
DEBUG_MSG(1, "-- Image::sizeRequestSimpl() begins\n");
DEBUG_MSG(1, "Image::sizeRequestImpl border: w=%d h=%d\n",
boxDiffWidth(), boxDiffHeight());
/* First set a naive size based on the image properties if given */
if (buffer) {
requisition->width = buffer->getRootWidth ();
requisition->ascent = buffer->getRootHeight ();
requisition->descent = 0;
} else {
if (altText && altText[0]) {
if (altTextWidth == -1)
altTextWidth =
layout->textWidth (getStyle()->font, altText, strlen (altText));
requisition->width = altTextWidth;
requisition->ascent = getStyle()->font->ascent;
requisition->descent = getStyle()->font->descent;
} else {
requisition->width = 0;
requisition->ascent = 0;
requisition->descent = 0;
}
}
requisition->width += boxDiffWidth ();
requisition->ascent += boxOffsetY ();
requisition->descent += boxRestHeight ();
DEBUG_MSG(1, "Image: initial requisition (with border): w=%d, h=%d\n",
requisition->width, requisition->ascent + requisition->descent);
/* Then correct the size if needed, so it fits within the available space in
* the container widget. The correctRequisition() method will take into the
* account the preferred aspect ratio. */
correctRequisition (requisition, core::splitHeightPreserveDescent, true,
true);
DEBUG_MSG(1, "Image: corrected requisition: w=%d, h=%d\n",
requisition->width, requisition->ascent + requisition->descent);
DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
requisition->width, requisition->ascent, requisition->descent);
DBG_OBJ_LEAVE ();
}
void Image::getExtremesSimpl (core::Extremes *extremes)
{
int contentWidth;
if (buffer)
contentWidth = buffer->getRootWidth ();
else {
if (altText && altText[0]) {
if (altTextWidth == -1)
altTextWidth =
layout->textWidth (getStyle()->font, altText, strlen (altText));
contentWidth = altTextWidth;
} else
contentWidth = 0;
}
int width = contentWidth + boxDiffWidth ();
// With percentage width, the image may be narrower than the buffer.
extremes->minWidth =
core::style::isPerLength (getStyle()->width) ? boxDiffWidth () : width;
// (We ignore the same effect for the maximal width.)
extremes->maxWidth = width;
extremes->minWidthIntrinsic = extremes->minWidth;
extremes->maxWidthIntrinsic = extremes->maxWidth;
correctExtremes (extremes, false);
extremes->adjustmentWidth =
misc::min (extremes->minWidthIntrinsic, extremes->minWidth);
}
void Image::sizeAllocateImpl (core::Allocation *allocation)
{
DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
allocation->x, allocation->y, allocation->width,
allocation->ascent, allocation->descent);
DEBUG_MSG(1, "Image::sizeAllocateImpl x=%d y=%d w=%d h=(%d + %d)\n",
allocation->x, allocation->y, allocation->width,
allocation->ascent, allocation->descent);
DEBUG_MSG(1, "Image::sizeAllocateImpl border: w=%d h=%d\n",
boxDiffWidth(), boxDiffHeight());
int newBufWidth = allocation->width - boxDiffWidth ();
int newBufHeight =
allocation->ascent + allocation->descent - boxDiffHeight ();
if (buffer && newBufWidth > 0 && newBufHeight > 0 &&
// Save some time when size did not change:
(newBufWidth != bufWidth || newBufHeight != bufHeight)) {
DBG_OBJ_MSG ("resize", 1, "replacing buffer");
DEBUG_MSG(1, "Image::sizeAllocateImpl new buffer size: w=%d h=%d\n",
newBufWidth, newBufHeight);
core::Imgbuf *oldBuffer = buffer;
buffer = oldBuffer->getScaledBuf (newBufWidth, newBufHeight);
oldBuffer->unref ();
bufWidth = newBufWidth;
bufHeight = newBufHeight;
DBG_OBJ_ASSOC_CHILD (this->buffer);
DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
}
DEBUG_MSG(1, "Image::sizeAllocateImpl x=%d y=%d w=%d h=(%d + %d)\n",
allocation->x, allocation->y, allocation->width,
allocation->ascent, allocation->descent);
DBG_OBJ_LEAVE ();
}
void Image::containerSizeChangedForChildren ()
{
DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
// Nothing to do.
DBG_OBJ_LEAVE ();
}
void Image::enterNotifyImpl (core::EventCrossing *event)
{
// BUG: this is wrong for image maps, but the cursor position is unknown.
currLink = getStyle()->x_link;
if (currLink != -1) {
(void) layout->emitLinkEnter (this, currLink, -1, -1, -1);
}
Widget::enterNotifyImpl(event);
}
void Image::leaveNotifyImpl (core::EventCrossing *event)
{
clicking = false;
if (currLink != -1) {
currLink = -1;
(void) layout->emitLinkEnter (this, -1, -1, -1, -1);
}
Widget::leaveNotifyImpl(event);
}
/*
* Return the coordinate relative to the contents.
* If the event occurred in the surrounding box, return the value at the
* edge of the contents instead.
*/
int Image::contentX (core::MousePositionEvent *event)
{
int ret = event->xWidget - boxOffsetX();
ret = misc::min(getContentWidth(), misc::max(ret, 0));
return ret;
}
int Image::contentY (core::MousePositionEvent *event)
{
int ret = event->yWidget - boxOffsetY();
ret = misc::min(getContentHeight(), misc::max(ret, 0));
return ret;
}
bool Image::motionNotifyImpl (core::EventMotion *event)
{
if (mapList || isMap) {
int x = contentX(event);
int y = contentY(event);
if (mapList) {
/* client-side image map */
int newLink = mapList->link (mapKey, x, y);
if (newLink != currLink) {
currLink = newLink;
clicking = false;
/* \todo Using MAP/AREA styles would probably be best */
setCursor(newLink == -1 ? getStyle()->cursor :
core::style::CURSOR_POINTER);
(void) layout->emitLinkEnter (this, newLink, -1, -1, -1);
}
} else if (isMap && currLink != -1) {
/* server-side image map */
(void) layout->emitLinkEnter (this, currLink, -1, x, y);
}
}
return true;
}
bool Image::buttonPressImpl (core::EventButton *event)
{
bool ret = false;
currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
getStyle()->x_link;
if (event->button == 3){
(void)layout->emitLinkPress(this, currLink, getStyle()->x_img, -1, -1,
event);
ret = true;
} else if (event->button == 1 || currLink != -1){
clicking = true;
ret = true;
}
return ret;
}
bool Image::buttonReleaseImpl (core::EventButton *event)
{
currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
getStyle()->x_link;
if (clicking) {
int x = isMap ? contentX(event) : -1;
int y = isMap ? contentY(event) : -1;
clicking = false;
layout->emitLinkClick (this, currLink, getStyle()->x_img, x, y, event);
return true;
}
return false;
}
void Image::draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context)
{
int dx, dy;
core::Rectangle content, intersection;
drawWidgetBox (view, area, false);
if (buffer) {
dx = boxOffsetX ();
dy = boxOffsetY ();
content.x = dx;
content.y = dy;
content.width = getContentWidth ();
content.height = getContentHeight ();
if (area->intersectsWith (&content, &intersection))
view->drawImage (buffer,
allocation.x + dx, allocation.y + dy,
intersection.x - dx, intersection.y - dy,
intersection.width, intersection.height);
} else {
core::View *clippingView;
if (altText && altText[0]) {
core::View *usedView = view;
clippingView = NULL;
if (altTextWidth == -1)
altTextWidth =
layout->textWidth (getStyle()->font, altText, strlen (altText));
if ((getContentWidth() < altTextWidth) ||
(getContentHeight() <
getStyle()->font->ascent + getStyle()->font->descent)) {
clippingView = usedView =
view->getClippingView (allocation.x + boxOffsetX (),
allocation.y + boxOffsetY (),
getContentWidth(),
getContentHeight());
}
usedView->drawSimpleWrappedText (getStyle()->font, getStyle()->color,
core::style::Color::SHADING_NORMAL,
allocation.x + boxOffsetX (),
allocation.y + boxOffsetY (),
getContentWidth(), getContentHeight(), altText);
if (clippingView)
view->mergeClippingView (clippingView);
}
if (mapKey) {
clippingView = view->getClippingView (allocation.x + boxOffsetX (),
allocation.y + boxOffsetY (),
getContentWidth(),
getContentHeight());
mapList->drawMap(mapKey, clippingView, getStyle(),
allocation.x + boxOffsetX (),
allocation.y + boxOffsetY ());
view->mergeClippingView (clippingView);
}
}
/** TODO: draw selection */
}
core::Iterator *Image::iterator (core::Content::Type mask, bool atEnd)
{
//return new core::TextIterator (this, mask, atEnd, altText);
/** \bug Not implemented. */
return new core::EmptyIterator (this, mask, atEnd);
}
void Image::setBuffer (core::Imgbuf *buffer, bool resize)
{
core::Imgbuf *oldBuf = this->buffer;
if (wasAllocated () && needsResize () &&
getContentWidth () > 0 && getContentHeight () > 0) {
// Don't create a new buffer for the transition from alt text to img,
// and only scale when both dimensions are known.
bufWidth = getContentWidth ();
bufHeight = getContentHeight ();
this->buffer = buffer->getScaledBuf (bufWidth, bufHeight);
} else {
this->buffer = buffer;
bufWidth = buffer->getRootWidth ();
bufHeight = buffer->getRootHeight ();
buffer->ref ();
}
queueResize (0, true);
if (bufWidth)
this->ratio = (float) bufWidth / (float) bufHeight;
DBG_OBJ_ASSOC_CHILD (this->buffer);
DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
if (oldBuf)
oldBuf->unref ();
}
void Image::drawRow (int row)
{
core::Rectangle area;
assert (buffer != NULL);
buffer->getRowArea (row, &area);
if (area.width && area.height)
queueDrawArea (area.x + boxOffsetX (), area.y + boxOffsetY (), area.width,
area.height);
}
void Image::finish ()
{
// Nothing to do; images are always drawn line by line.
}
void Image::fatal ()
{
// Could display an error.
}
/**
* \brief Sets image as server side image map.
*/
void Image::setIsMap ()
{
isMap = true;
}
/**
* \brief Sets image as client side image map.
*
* "list" is not owned by the image, the caller has to free it. "key"
* is owned by the image, if it is used by the caller afterwards, a copy
* should be passed.
*/
void Image::setUseMap (ImageMapsList *list, object::Object *key)
{
mapList = list;
if (mapKey && mapKey != key)
delete mapKey;
mapKey = key;
}
} // namespace dw

184
dw/image.hh Normal file
View File

@ -0,0 +1,184 @@
#ifndef __DW_IMAGE_HH__
#define __DW_IMAGE_HH__
#include "core.hh"
namespace dw {
/**
* \brief Represents a list of client-side image maps.
*
* All image maps of a HTML page (in the future, also image maps from
* different HTML pages) are stored in a list, which is passed to the
* image, so that it is possible to deal with maps, which are defined
* after the image within the HTML page.
*
* Maps are referred by instances of object::Object. These keys are
* typically URLs, so the type representing URLS should be derived from
* object::Object.
*
* \todo Some methods within the key class have to be implemented, this
* is not clear at this time.
*/
class ImageMapsList
{
private:
class ImageMap: public lout::object::Object {
private:
class ShapeAndLink: public lout::object::Object {
public:
core::Shape *shape;
int link;
~ShapeAndLink () { if (shape) delete shape; };
};
lout::container::typed::List <ShapeAndLink> *shapesAndLinks;
int defaultLink;
public:
ImageMap ();
~ImageMap ();
void draw (core::View *view, core::style::Style *style, int x, int y);
void add (core::Shape *shape, int link);
void setDefaultLink (int link) { defaultLink = link; };
int link (int x, int y);
};
lout::container::typed::HashTable <lout::object::Object, ImageMap>
*imageMaps;
ImageMap *currentMap;
public:
ImageMapsList ();
~ImageMapsList ();
void startNewMap (lout::object::Object *key);
void addShapeToCurrentMap (core::Shape *shape, int link);
void setCurrentMapDefaultLink (int link);
void drawMap(lout::object::Object *key, core::View *view,
core::style::Style *style, int x, int y);
int link (lout::object::Object *key, int x, int y);
};
/**
* \brief Displays an instance of dw::core::Imgbuf.
*
* The dw::core::Imgbuf is automatically scaled, when needed, but dw::Image
* does not keep a reference on the root buffer.
*
*
* <h3>Signals</h3>
*
* For image maps, dw::Image uses the signals defined in
* dw::core::Layout::LinkReceiver. For client side image maps, -1 is
* passed for the coordinates, for server side image maps, the respective
* coordinates are used. See section "Image Maps" below.
*
*
* <h3>%Image Maps</h3>
*
* <h4>Client Side %Image Maps</h4>
*
* You must first create a list of image maps (dw::ImageMapList), which can
* be used for multiple images. The caller is responsible for freeing the
* dw::ImageMapList.
*
* Adding a map is done by dw::ImageMapsList::startNewMap. The key is an
* instance of a sub class of object::Object. In the context of HTML, this is
* a URL, which defines this map globally, by combining the URL of the
* document, this map is defined in, with the value of the attribute "name" of
* the \<MAP\> element, as a fragment.
*
* dw::ImageMapsList::addShapeToCurrentMap adds a shape to the current
* map. The \em link argument is a number, which is later passed to
* the dw::core::Layout::LinkReceiver.
*
* This map list is then, together with the key for the image, passed to
* dw::Image::setUseMap. For HTML, a URL with the value of the "ismap"
* attribute of \<IMG\> should be used.
*
* dw::Image will search the correct map, when needed. If it is not found
* at this time, but later defined, it will be found and used later. This is
* the case, when an HTML \<MAP\> is defined below the \<IMG\> in the
* document.
*
* Currently, only maps defined in the same document as the image may be
* used, since the dw::ImageMapsList is stored in the HTML link block, and
* contains only the image maps defined in the document.
*
* <h4>Server Side %Image Maps</h4>
*
* To use images for server side image maps, you must call
* dw::Image::setIsMap, and the dw::Image::style must contain a valid link
* (dw::core::style::Style::x_link). After this, motions and clicks are
* delegated to dw::core::Layout::LinkReceiver.
*
* \sa\ref dw-images-and-backgrounds
*/
class Image: public core::Widget, public core::ImgRenderer
{
private:
char *altText;
core::Imgbuf *buffer;
int bufWidth, bufHeight;
int altTextWidth;
bool clicking;
int currLink;
ImageMapsList *mapList;
Object *mapKey;
bool isMap;
protected:
void sizeRequestSimpl (core::Requisition *requisition);
void getExtremesSimpl (core::Extremes *extremes);
void sizeAllocateImpl (core::Allocation *allocation);
void containerSizeChangedForChildren ();
void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
bool buttonPressImpl (core::EventButton *event);
bool buttonReleaseImpl (core::EventButton *event);
void enterNotifyImpl (core::EventCrossing *event);
void leaveNotifyImpl (core::EventCrossing *event);
bool motionNotifyImpl (core::EventMotion *event);
int contentX (core::MousePositionEvent *event);
int contentY (core::MousePositionEvent *event);
//core::Iterator *iterator (Content::Type mask, bool atEnd);
public:
static int CLASS_ID;
Image(const char *altText);
~Image();
// For images, the minimal width is not well defined, and
// correction of the size makes not much sense.
virtual bool getAdjustMinWidth () { return false; }
core::Iterator *iterator (core::Content::Type mask, bool atEnd);
inline core::Imgbuf *getBuffer () { return buffer; }
void setBuffer (core::Imgbuf *buffer, bool resize = false);
void drawRow (int row);
void finish ();
void fatal ();
void setIsMap ();
void setUseMap (ImageMapsList *list, Object *key);
/* This is a hack for the perhaps frivolous feature of drawing image map
* shapes when there is no image to display. If the map is defined after
* an image using an image map, and the actual image data has not been
* loaded, tell the image to redraw.
*/
void forceMapRedraw () { if (mapKey && ! buffer) queueDraw (); };
};
} // namespace dw
#endif // __DW_IMAGE_HH__

233
dw/imgbuf.hh Normal file
View File

@ -0,0 +1,233 @@
#ifndef __DW_IMGBUF_HH__
#define __DW_IMGBUF_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
#include "../lout/debug.hh"
namespace dw {
namespace core {
/**
* \brief The platform independent interface for image buffers.
*
* %Image buffers depend on the platform (see \ref dw-images-and-backgrounds),
* but have this general, platform independent interface. The purpose of
* an image buffer is
*
* <ol>
* <li> storing the image data,
* <li> handling scaled versions of this buffer, and
* <li> drawing.
* </ol>
*
* The latter must be done independently from the window.
*
* <h3>Creating</h3>
*
* %Image buffers are created by calling dw::core::Platform::createImgbuf.
*
* <h3>Storing %Image Data</h3>
*
* dw::core::Imgbuf supports five image types, which are listed in the table
* below. The representation defines, how the colors are stored within
* the data, which is passed to dw::core::Imgbuf::copyRow.
*
* <table>
* <tr><th>Type (dw::core::Imgbuf::Type) <th>Bytes per
* Pixel <th>Representation
* <tr><td>dw::core::Imgbuf::RGB <td>3 <td>red, green, blue
* <tr><td>dw::core::Imgbuf::RGBA <td>4 <td>red, green, blue, alpha
* <tr><td>dw::core::Imgbuf::GRAY <td>1 <td>gray value
* <tr><td>dw::core::Imgbuf::INDEXED <td>1 <td>index to colormap
* <tr><td>dw::core::Imgbuf::INDEXED_ALPHA <td>1 <td>index to colormap
* </table>
*
* The last two types need a colormap, which is set by
* dw::core::Imgbuf::setCMap, which must be called before
* dw::core::Imgbuf::copyRow. This function expects the colors as 32 bit
* unsigned integers, which have the format 0xrrbbgg (for indexed
* images), or 0xaarrggbb (for indexed alpha), respectively.
*
*
* <h3>Scaling</h3>
*
* The buffer with the original size, which was created by
* dw::core::Platform::createImgbuf, is called root buffer. Imgbuf provides
* the ability to scale buffers. Generally, both root buffers, as well as
* scaled buffers, may be shared, memory management is done by reference
* counters.
*
* Via dw::core::Imgbuf::getScaledBuf, you can retrieve a scaled buffer.
* Generally, something like this must work always, in an efficient way:
*
* \code
* dw::core::Imgbuf *curBuf, *oldBuf;
* int width, height,
* // ...
* oldBuf = curBuf;
* curBuf = oldBuf->getScaledBuf(oldBuf, width, height);
* oldBuf->unref();
* \endcode
*
* \em oldBuf may both be a root buffer, or a scaled buffer.
*
* The root buffer keeps a list of all children, and all methods
* operating on the image data (dw::core::Imgbuf::copyRow and
* dw::core::Imgbuf::setCMap) are delegated to the scaled buffers, when
* processed, and inherited, when a new scaled buffer is created. This
* means, that they must only be performed for the root buffer.
*
* A possible implementation could be (dw::fltk::FltkImgbuf does it this way):
*
* <ul>
* <li> If the method is called with an already scaled image buffer, this is
* delegated to the root buffer.
*
* <li> If the given size is the original size, the root buffer is
* returned, with an increased reference counter.
*
* <li> Otherwise, if this buffer has already been scaled to the given
* size, return this scaled buffer, with an increased reference
* counter.
*
* <li> Otherwise, return a new scaled buffer with reference counter 1.
* </ul>
*
* Special care is to be taken, when the root buffer is not used anymore,
* i.e. after dw::core::Imgbuf::unref the reference counter is 0, but there
* are still scaled buffers. Since all methods operating on the image data
* (dw::core::Imgbuf::copyRow and dw::core::Imgbuf::setCMap) are called for
* the root buffer, the root buffer is still needed, and so must not be
* deleted at this point. This is, how dw::fltk::FltkImgbuf solves this
* problem:
*
* <ul>
* <li> dw::fltk::FltkImgbuf::unref does, for root buffers, check, not only
* whether dw::fltk::FltkImgbuf::refCount is 0, but also, whether
* there are children left. When the latter is the case, the buffer
* is not deleted.
*
* <li> There is an additional check in dw::fltk::FltkImgbuf::detachScaledBuf,
* which deals with the case, that dw::fltk::FltkImgbuf::refCount is 0,
* and the last scaled buffer is removed.
* </ul>
*
* In the following example:
*
* \code
* dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
* dw::core::Layout *layout = new dw::core::Layout (platform);
*
* dw::core::Imgbuf *rootbuf =
* layout->createImgbuf (dw::core::Imgbuf::RGB, 100, 100);
* dw::core::Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50);
* rootbuf->unref ();
* scaledbuf->unref ();
* \endcode
*
* the root buffer is not deleted, when dw::core::Imgbuf::unref is called,
* since a scaled buffer is left. After calling dw::core::Imgbuf::unref for
* the scaled buffer, it is deleted, and after it, the root buffer.
*
* <h3>Drawing</h3>
*
* dw::core::Imgbuf provides no methods for drawing, instead, this is
* done by the views (implementation of dw::core::View).
*
* There are two situations, when drawing is necessary:
*
* <ol>
* <li> To react on expose events, the function dw::core::View::drawImage
* should be used, with the following parameters:
* <ul>
* <li> of course, the image buffer,
* <li> where the root of the image would be displayed (as \em xRoot
* and \em yRoot), and
* <li> the region within the image, which should be displayed (\em x,
* \em y, \em width, \em height).
* </ul>
*
* <li> When a row has been copied, it has to be drawn. To determine the
* area, which has to be drawn, the dw::core::Imgbuf::getRowArea
* should be used. The result can then passed
* to dw::core::View::drawImage.
* </ol>
*
* \sa \ref dw-images-and-backgrounds
*/
class Imgbuf: public lout::object::Object, public lout::signal::ObservedObject
{
public:
enum Type { RGB, RGBA, GRAY, INDEXED, INDEXED_ALPHA };
inline Imgbuf () {
DBG_OBJ_CREATE ("dw::core::Imgbuf");
DBG_OBJ_BASECLASS (lout::object::Object);
DBG_OBJ_BASECLASS (lout::signal::ObservedObject);
}
inline ~Imgbuf () {
DBG_OBJ_DELETE ();
}
/*
* Methods called from the image decoding
*/
virtual void setCMap (int *colors, int num_colors) = 0;
virtual void copyRow (int row, const byte *data) = 0;
virtual void newScan () = 0;
/*
* Methods called from dw::Image
*/
virtual Imgbuf* getScaledBuf (int width, int height) = 0;
virtual void getRowArea (int row, dw::core::Rectangle *area) = 0;
virtual int getRootWidth () = 0;
virtual int getRootHeight () = 0;
/**
* Creates an image buffer with same parameters (type, gamma etc.)
* except size.
*/
virtual Imgbuf *createSimilarBuf (int width, int height) = 0;
/**
* Copies another image buffer into this image buffer.
*/
virtual void copyTo (Imgbuf *dest, int xDestRoot, int yDestRoot,
int xSrc, int ySrc, int widthSrc, int heightSrc) = 0;
/*
* Reference counting.
*/
virtual void ref () = 0;
virtual void unref () = 0;
/**
* \todo Comment
*/
virtual bool lastReference () = 0;
/**
* \todo Comment
*/
virtual void setDeleteOnUnref (bool deleteOnUnref) = 0;
/**
* \todo Comment
*/
virtual bool isReferred () = 0;
};
} // namespace core
} // namespace dw
#endif // __DW_IMGBUF_HH__

67
dw/imgrenderer.cc Normal file
View File

@ -0,0 +1,67 @@
/*
* Dillo Widget
*
* Copyright 2013 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.
*
* 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 "core.hh"
namespace dw {
namespace core {
using namespace lout::container;
using namespace lout::object;
void ImgRendererDist::setBuffer (core::Imgbuf *buffer, bool resize)
{
for (typed::Iterator <TypedPointer <ImgRenderer> > it =
children->iterator (); it.hasNext (); ) {
TypedPointer <ImgRenderer> *tp = it.getNext ();
tp->getTypedValue()->setBuffer (buffer, resize);
}
}
void ImgRendererDist::drawRow (int row)
{
for (typed::Iterator <TypedPointer <ImgRenderer> > it =
children->iterator (); it.hasNext (); ) {
TypedPointer <ImgRenderer> *tp = it.getNext ();
tp->getTypedValue()->drawRow (row);
}
}
void ImgRendererDist::finish ()
{
for (typed::Iterator <TypedPointer <ImgRenderer> > it =
children->iterator (); it.hasNext (); ) {
TypedPointer <ImgRenderer> *tp = it.getNext ();
tp->getTypedValue()->finish ();
}
}
void ImgRendererDist::fatal ()
{
for (typed::Iterator <TypedPointer <ImgRenderer> > it =
children->iterator (); it.hasNext (); ) {
TypedPointer <ImgRenderer> *tp = it.getNext ();
tp->getTypedValue()->fatal ();
}
}
} // namespace core
} // namespace dw

87
dw/imgrenderer.hh Normal file
View File

@ -0,0 +1,87 @@
#ifndef __DW_IMGRENDERER_HH__
#define __DW_IMGRENDERER_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
/**
* \brief ...
*
* \sa \ref dw-images-and-backgrounds
*/
class ImgRenderer
{
public:
virtual ~ImgRenderer () { }
/**
* \brief Called, when an image buffer is attached.
*
* This is typically the case when all meta data (size, depth) has been read.
*/
virtual void setBuffer (core::Imgbuf *buffer, bool resize = false) = 0;
/**
* \brief Called, when data from a row is available and has been copied into
* the image buffer.
*
* The implementation will typically queue the respective area for drawing.
*/
virtual void drawRow (int row) = 0;
/**
* \brief Called, when all image data has been retrieved.
*
* The implementation may use this instead of "drawRow" for drawing, to
* limit the number of draws.
*/
virtual void finish () = 0;
/**
* \brief Called, when there are problems with the retrieval of image data.
*
* The implementation may use this to indicate an error.
*/
virtual void fatal () = 0;
};
/**
* \brief Implementation of ImgRenderer, which distributes all calls
* to a set of other implementations of ImgRenderer.
*
* The order of the call children is not defined, especially not
* identical to the order in which they have been added.
*/
class ImgRendererDist: public ImgRenderer
{
lout::container::typed::HashSet <lout::object::TypedPointer <ImgRenderer> >
*children;
public:
inline ImgRendererDist ()
{ children = new lout::container::typed::HashSet
<lout::object::TypedPointer <ImgRenderer> > (true); }
~ImgRendererDist () { delete children; }
void setBuffer (core::Imgbuf *buffer, bool resize);
void drawRow (int row);
void finish ();
void fatal ();
void put (ImgRenderer *child)
{ children->put (new lout::object::TypedPointer <ImgRenderer> (child)); }
void remove (ImgRenderer *child)
{ lout::object::TypedPointer <ImgRenderer> tp (child);
children->remove (&tp); }
};
} // namespace core
} // namespace dw
#endif // __DW_IMGRENDERER_HH__

902
dw/iterator.cc Normal file
View File

@ -0,0 +1,902 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "core.hh"
#include <limits.h>
using namespace lout;
namespace dw {
namespace core {
// --------------
// Iterator
// --------------
Iterator::Iterator(Widget *widget, Content::Type mask, bool atEnd)
{
this->widget = widget;
this->mask = mask;
}
Iterator::Iterator(Iterator &it): object::Comparable ()
{
widget = it.widget;
content = it.content;
}
Iterator::~Iterator()
{
}
bool Iterator::equals (Object *other)
{
Iterator *otherIt = (Iterator*)other;
return
this == otherIt ||
(getWidget() == otherIt->getWidget() && compareTo(otherIt) == 0);
}
void Iterator::intoStringBuffer(misc::StringBuffer *sb)
{
sb->append ("{ widget = ");
//widget->intoStringBuffer (sb);
sb->appendPointer (widget);
sb->append (" (");
sb->append (widget->getClassName());
sb->append (")>");
sb->append (", mask = ");
Content::maskIntoStringBuffer (mask, sb);
sb->append (", content = ");
Content::intoStringBuffer (&content, sb);
sb->append (" }");
}
/**
* \brief Delete the iterator.
*
* The destructor is hidden, implementations may use optimizations for
* the allocation. (Will soon be the case for dw::core::EmptyIteratorFactory.)
*/
void Iterator::unref ()
{
delete this;
}
/**
* \brief Scrolls the viewport, so that the region between \em it1 and
* \em it2 is seen, according to \em hpos and \em vpos.
*
* The parameters \em start and \em end have the same meaning as in
* dw::core::Iterator::getAllocation, \em start refers
* to \em it1, while \em end rerers to \em it2.
*
* If \em it1 and \em it2 point to the same location (see code), only
* \em it1 is regarded, and both belowstart and belowend refer to it.
*/
void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end,
HPosition hpos, VPosition vpos)
{
Allocation alloc1, alloc2, alloc;
int x1, x2, y1, y2;
DeepIterator *eit1, *eit2, *eit3;
int curStart, curEnd, cmp;
bool atStart;
if (it1->equals(it2)) {
it1->getAllocation (start, end, &alloc);
it1->getWidget()->getLayout()->scrollTo (hpos, vpos, alloc.x, alloc.y,
alloc.width,
alloc.ascent + alloc.descent);
} else {
// First, determine the rectangle all iterators from it1 and it2
// allocate, i.e. the smallest rectangle containing all allocations of
// these iterators.
eit1 = new DeepIterator (it1);
eit2 = new DeepIterator (it2);
x1 = INT_MAX;
x2 = INT_MIN;
y1 = INT_MAX;
y2 = INT_MIN;
for (atStart = true, eit3 = (DeepIterator*)eit1->clone ();
(cmp = eit3->compareTo (eit2)) <= 0;
eit3->next (), atStart = false) {
if (atStart)
curStart = start;
else
curStart = 0;
if (cmp == 0)
curEnd = end;
else
curEnd = INT_MAX;
eit3->getAllocation (curStart, curEnd, &alloc);
x1 = misc::min (x1, alloc.x);
x2 = misc::max (x2, alloc.x + alloc.width);
y1 = misc::min (y1, alloc.y);
y2 = misc::max (y2, alloc.y + alloc.ascent + alloc.descent);
}
delete eit3;
delete eit2;
delete eit1;
it1->getAllocation (start, INT_MAX, &alloc1);
it2->getAllocation (0, end, &alloc2);
if (alloc1.x > alloc2.x) {
//
// This is due to a line break within the region. When the line is
// longer than the viewport, and the region is actually quite short,
// the user would not see anything of the region, as in this figure
// (with region marked as "#"):
//
// +----------+ ,-- alloc1
// | | V
// | | ### ###
// ### ### | |
// ^ | | <-- viewport
// | +----------+
// `-- alloc2
// |----------------------------|
// width
//
// Therefore, we make the region smaller, so that the region will be
// displayed like this:
//
// ,-- alloc1
// +----|-----+
// | V |
// | ### ###|
// ### ### | |
// ^ | | <-- viewport
// `-- alloc2 +----------+
// |----------|
// width
//
/** \todo Changes in the viewport size, until the idle function is
* called, are not regarded. */
if (it1->getWidget()->getLayout()->getUsesViewport() &&
x2 - x1 > it1->getWidget()->getLayout()->getWidthViewport()) {
x1 = x2 - it1->getWidget()->getLayout()->getWidthViewport();
x2 = x1 + it1->getWidget()->getLayout()->getWidthViewport();
}
}
if (alloc1.y > alloc2.y) {
// This is similar to the case above, e.g. if the region ends in
// another table column.
if (it1->getWidget()->getLayout()->getUsesViewport() &&
y2 - y1 > it1->getWidget()->getLayout()->getHeightViewport()) {
y1 = y2 - it1->getWidget()->getLayout()->getHeightViewport();
y2 = y1 + it1->getWidget()->getLayout()->getHeightViewport();
}
}
it1->getWidget()->getLayout()->scrollTo (hpos, vpos,
x1, y1, x2 - x1, y2 - y1);
}
}
// -------------------
// EmptyIterator
// -------------------
EmptyIterator::EmptyIterator (Widget *widget, Content::Type mask, bool atEnd):
Iterator (widget, mask, atEnd)
{
this->content.type = (atEnd ? Content::END : Content::START);
}
EmptyIterator::EmptyIterator (EmptyIterator &it): Iterator (it)
{
}
object::Object *EmptyIterator::clone ()
{
return new EmptyIterator (*this);
}
int EmptyIterator::compareTo (object::Comparable *other)
{
EmptyIterator *otherIt = (EmptyIterator*)other;
if (content.type == otherIt->content.type)
return 0;
else if (content.type == Content::START)
return -1;
else
return +1;
}
bool EmptyIterator::next ()
{
content.type = Content::END;
return false;
}
bool EmptyIterator::prev ()
{
content.type = Content::START;
return false;
}
void EmptyIterator::highlight (int start, int end, HighlightLayer layer)
{
}
void EmptyIterator::unhighlight (int direction, HighlightLayer layer)
{
}
void EmptyIterator::getAllocation (int start, int end, Allocation *allocation)
{
}
// ------------------
// TextIterator
// ------------------
TextIterator::TextIterator (Widget *widget, Content::Type mask, bool atEnd,
const char *text): Iterator (widget, mask, atEnd)
{
this->content.type = (atEnd ? Content::END : Content::START);
this->text = (mask & Content::TEXT) ? text : NULL;
}
TextIterator::TextIterator (TextIterator &it): Iterator (it)
{
text = it.text;
}
int TextIterator::compareTo (object::Comparable *other)
{
TextIterator *otherIt = (TextIterator*)other;
if (content.type == otherIt->content.type)
return 0;
switch (content.type) {
case Content::START:
return -1;
case Content::TEXT:
if (otherIt->content.type == Content::START)
return +1;
else
return -1;
case Content::END:
return +1;
default:
misc::assertNotReached();
return 0;
}
}
bool TextIterator::next ()
{
if (content.type == Content::START && text != NULL) {
content.type = Content::TEXT;
content.text = text;
return true;
} else {
content.type = Content::END;
return false;
}
}
bool TextIterator::prev ()
{
if (content.type == Content::END && text != NULL) {
content.type = Content::TEXT;
content.text = text;
return true;
} else {
content.type = Content::START;
return false;
}
}
void TextIterator::getAllocation (int start, int end, Allocation *allocation)
{
// Return the allocation of the widget.
*allocation = *(getWidget()->getAllocation ());
}
// ------------------
// DeepIterator
// ------------------
DeepIterator::Stack::~Stack ()
{
for (int i = 0; i < size (); i++)
get(i)->unref ();
}
/*
* The following two methods are used by dw::core::DeepIterator::DeepIterator,
* when the passed dw::core::Iterator points to a widget. Since a
* dw::core::DeepIterator never returns a widget, the dw::core::Iterator has
* to be corrected, by searching for the next content downwards (within the
* widget pointed to), forwards, and backwards (in the traversed tree).
*/
/*
* Search downwards. If fromEnd is true, start search at the end,
* otherwise at the beginning.
*/
Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask,
bool fromEnd)
{
Iterator *it2, *it3;
//DEBUG_MSG (1, "%*smoving down (%swards) from %s\n",
// indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
assert (it->getContent()->type & Content::ANY_WIDGET);
it2 = it->getContent()->getWidget()->iterator (mask, fromEnd);
if (it2 == NULL) {
// Moving downwards failed.
//DEBUG_MSG (1, "%*smoving down failed\n", indent, "");
return NULL;
}
while (fromEnd ? it2->prev () : it2->next ()) {
//DEBUG_MSG (1, "%*sexamining %s\n",
// indent, "", a_Dw_iterator_text (it2));
if (it2->getContent()->type & Content::ANY_WIDGET) {
// Another widget. Search in it downwards.
it3 = searchDownward (it2, mask, fromEnd);
if (it3 != NULL) {
it2->unref ();
return it3;
}
// Else continue in this widget.
} else {
// Success!
//DEBUG_MSG (1, "%*smoving down succeeded: %s\n",
// indent, "", a_Dw_iterator_text (it2));
return it2;
}
}
// Nothing found.
it2->unref ();
//DEBUG_MSG (1, "%*smoving down failed (nothing found)\n", indent, "");
return NULL;
}
/*
* Search sidewards. fromEnd specifies the direction, false means forwards,
* true means backwards.
*/
Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask,
bool fromEnd)
{
Iterator *it2, *it3;
//DEBUG_MSG (1, "%*smoving %swards from %s\n",
// indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it));
assert (it->getContent()->type & Content::ANY_WIDGET);
it2 = it->cloneIterator ();
while (fromEnd ? it2->prev () : it2->next ()) {
if (it2->getContent()->type & Content::ANY_WIDGET) {
// Search downwards in this widget.
it3 = searchDownward (it2, mask, fromEnd);
if (it3 != NULL) {
it2->unref ();
//DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
// indent, "", from_end ? "back" : "for",
// a_Dw_iterator_text (it3));
return it3;
}
// Else continue in this widget.
} else {
// Success!
// DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
// indent, "", from_end ? "back" : "for",
// a_Dw_iterator_text (it2));
return it2;
}
}
/* Nothing found, go upwards in the tree (if possible). */
it2->unref ();
Widget *respParent = getRespectiveParent (it->getWidget(), it->getMask());
if (respParent) {
it2 = respParent->iterator (mask, false);
while (true) {
if (!it2->next ())
misc::assertNotReached ();
if (it2->getContent()->type & Content::ANY_WIDGET &&
it2->getContent()->getWidget () == it->getWidget ()) {
it3 = searchSideward (it2, mask, fromEnd);
it2->unref ();
//DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n",
// indent, "", from_end ? "back" : "for",
// a_Dw_iterator_text (it3));
return it3;
}
}
}
// Nothing found at all.
// DEBUG_MSG (1, "%*smoving %swards failed (nothing found)\n",
// indent, "", from_end ? "back" : "for");
return NULL;
}
Widget *DeepIterator::getRespectiveParent (Widget *widget, Content::Type mask)
{
// Return, depending on which is requested indirectly (follow
// references or containments) the parent (container) or the
// generator. At this point, the type of the parent/generator is
// not known (since the parent/generator is not known), so we have
// to examine the mask. This is the reason why only one of
// WIDGET_OOF_CONT and WIDGET_OOF_REF is allowed.
return (mask & Content::WIDGET_OOF_REF) ?
widget->getGenerator() : widget->getParent();
}
int DeepIterator::getRespectiveLevel (Widget *widget, Content::Type mask)
{
// Similar to getRespectiveParent.
return (mask & Content::WIDGET_OOF_REF) ?
widget->getGeneratorLevel() : widget->getLevel();
}
/**
* \brief Create a new deep iterator from an existing dw::core::Iterator.
*
* The content of the return value will be the content of \em it. If within
* the widget tree, there is no non-widget content, the resulting deep
* iterator is empty (denoted by dw::core::DeepIterator::stack == NULL).
*
* Notes:
*
* <ol>
* <li> The mask of \em i" must include DW_CONTENT_WIDGET, but
* dw::core::DeepIterator::next will never return widgets.
* </ol>
*/
DeepIterator::DeepIterator (Iterator *it)
{
//printf ("Starting creating DeepIterator %p ...\n", this);
//printf ("Initial iterator: ");
//it->print ();
//printf ("\n");
// Widgets out of flow are either followed widtin containers, or
// generators. Both (and also nothing at all) is not allowed. See
// also comment in getRespectiveParent.
int oofMask =
it->getMask() & (Content::WIDGET_OOF_CONT | Content::WIDGET_OOF_REF);
assert (oofMask == Content::WIDGET_OOF_CONT ||
oofMask == Content::WIDGET_OOF_REF);
//DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it));
// Clone input iterator, so the iterator passed as parameter
// remains untouched.
it = it->cloneIterator ();
this->mask = it->getMask ();
hasContents = true;
// If it points to a widget, find a near non-widget content,
// since an DeepIterator should never return widgets.
if (it->getContent()->type & Content::ANY_WIDGET) {
Iterator *it2;
// The second argument of searchDownward is actually a matter of
// taste :-)
if ((it2 = searchDownward (it, mask, false)) ||
(it2 = searchSideward (it, mask, false)) ||
(it2 = searchSideward (it, mask, true))) {
it->unref ();
it = it2;
} else {
// This may happen, when a page does not contain any non-widget
// content.
//DEBUG_MSG (1, "a_Dw_ext_iterator_new got totally helpless!\n");
it->unref ();
hasContents = false;
}
}
//DEBUG_MSG (1, " => %s\n", a_Dw_iterator_text (it));
if (hasContents) {
// If this widget has parents, we must construct appropriate iterators.
//
// \todo There may be a faster way instead of iterating through the
// parent widgets.
//printf ("Starting with: ");
//it->print ();
//printf ("\n");
// Construct the iterators.
int thisLevel = getRespectiveLevel (it->getWidget()), level;
Widget *w;
for (w = it->getWidget (), level = thisLevel;
getRespectiveParent (w) != NULL;
w = getRespectiveParent (w), level--) {
Iterator *it = getRespectiveParent(w)->iterator (mask, false);
//printf (" parent: %s %p\n", w->getClassName (), w);
stack.put (it, level - 1);
while (true) {
//printf (" ");
//it->print ();
//printf ("\n");
bool hasNext = it->next();
assert (hasNext);
if (it->getContent()->type & Content::ANY_WIDGET &&
it->getContent()->getWidget () == w)
break;
}
//printf (" %d: ", level - 1);
//it->print ();
//printf ("\n");
}
stack.put (it, thisLevel);
content = *(it->getContent());
}
//printf ("... done creating DeepIterator %p.\n", this);
}
DeepIterator::~DeepIterator ()
{
//printf ("Deleting DeepIterator %p ...\n", this);
}
object::Object *DeepIterator::clone ()
{
DeepIterator *it = new DeepIterator ();
for (int i = 0; i < stack.size (); i++)
it->stack.put (stack.get(i)->cloneIterator (), i);
it->mask = mask;
it->content = content;
it->hasContents = hasContents;
return it;
}
int DeepIterator::compareTo (object::Comparable *other)
{
DeepIterator *otherDeepIterator = (DeepIterator*)other;
//printf ("Compare: %s\n", stack.toString ());
//printf (" to: %s\n", otherDeepIterator->stack.toString ());
// Search the highest level, where the widgets are the same.
int level = 0;
// The Comparable interface does not define "uncomparable". Deep
// iterators are only comparable if they belong to the same widget
// tree, so have the same widget at the bottom at the
// stack. If this is not the case, we abort.
assert (stack.size() > 0);
assert (otherDeepIterator->stack.size() > 0);
//printf ("Equal? The %s %p (of %p) and the %s %p (of %p)?\n",
// stack.get(0)->getWidget()->getClassName(),
// stack.get(0)->getWidget(), this,
// otherDeepIterator->stack.get(0)->getWidget()->getClassName(),
// otherDeepIterator->stack.get(0)->getWidget(), otherDeepIterator);
assert (stack.get(0)->getWidget()
== otherDeepIterator->stack.get(level)->getWidget());
while (stack.get(level)->getWidget ()
== otherDeepIterator->stack.get(level)->getWidget ()) {
if (level == stack.size() - 1 ||
level == otherDeepIterator->stack.size() - 1)
break;
level++;
}
//printf (" => level = %d (temorally)\n", level);
while (stack.get(level)->getWidget ()
!= otherDeepIterator->stack.get(level)->getWidget ())
level--;
//printf (" => level = %d (finally)\n", level);
return stack.get(level)->compareTo (otherDeepIterator->stack.get(level));
}
DeepIterator *DeepIterator::createVariant(Iterator *it)
{
/** \todo Not yet implemented, and actually not yet needed very much. */
return new DeepIterator (it);
}
bool DeepIterator::isEmpty () {
return !hasContents;
}
/**
* \brief Move iterator forward and store content it.
*
* Returns true on success.
*/
bool DeepIterator::next ()
{
Iterator *it = stack.getTop ();
if (it->next ()) {
if (it->getContent()->type & Content::ANY_WIDGET) {
// Widget: new iterator on stack, to search in this widget.
stack.push (it->getContent()->getWidget()->iterator (mask, false));
return next ();
} else {
// Simply return the content of the iterartor.
content = *(it->getContent ());
return true;
}
} else {
// No more data in the top-most widget.
if (stack.size () > 1) {
// Pop iterator from stack, and move to next item in the old one.
stack.pop ();
return next ();
} else {
// Stack is empty.
content.type = Content::END;
return false;
}
}
}
/**
* \brief Move iterator backward and store content it.
*
* Returns true on success.
*/
bool DeepIterator::prev ()
{
Iterator *it = stack.getTop ();
if (it->prev ()) {
if (it->getContent()->type & Content::ANY_WIDGET) {
// Widget: new iterator on stack, to search in this widget.
stack.push (it->getContent()->getWidget()->iterator (mask, true));
return prev ();
} else {
// Simply return the content of the iterartor.
content = *(it->getContent ());
return true;
}
} else {
// No more data in the top-most widget.
if (stack.size () > 1) {
// Pop iterator from stack, and move to next item in the old one.
stack.pop ();
return prev ();
} else {
// Stack is empty.
content.type = Content::START;
return false;
}
}
}
// -----------------
// CharIterator
// -----------------
CharIterator::CharIterator ()
{
it = NULL;
}
/**
* \brief ...
*
* If followReferences is true, only the reference are followed, when
* the container and generator for a widget is different. If false,
* only the container is followed.
*/
CharIterator::CharIterator (Widget *widget, bool followReferences)
{
Iterator *i =
widget->iterator (Content::maskForSelection (followReferences), false);
it = new DeepIterator (i);
i->unref ();
ch = START;
}
CharIterator::~CharIterator ()
{
if (it)
delete it;
}
object::Object *CharIterator::clone()
{
CharIterator *cloned = new CharIterator ();
cloned->it = it->cloneDeepIterator ();
cloned->ch = ch;
cloned->pos = pos;
return cloned;
}
int CharIterator::compareTo(object::Comparable *other)
{
CharIterator *otherIt = (CharIterator*)other;
int c = it->compareTo(otherIt->it);
if (c != 0)
return c;
else
return pos - otherIt->pos;
}
bool CharIterator::next ()
{
if (ch == START || it->getContent()->type == Content::BREAK ||
(it->getContent()->type == Content::TEXT &&
it->getContent()->text[pos] == 0)) {
if (it->next()) {
if (it->getContent()->type == Content::BREAK)
ch = '\n';
else { // if (it->getContent()->type == Content::TEXT)
pos = 0;
ch = it->getContent()->text[pos];
if (ch == 0)
// should not happen, actually
return next ();
}
return true;
}
else {
ch = END;
return false;
}
} else if (ch == END)
return false;
else {
// at this point, it->getContent()->type == Content::TEXT
pos++;
ch = it->getContent()->text[pos];
if (ch == 0) {
if (it->getContent()->space) {
ch = ' ';
} else {
return next ();
}
}
return true;
}
}
bool CharIterator::prev ()
{
if (ch == END || it->getContent()->type == Content::BREAK ||
(it->getContent()->type == Content::TEXT && pos == 0)) {
if (it->prev()) {
if (it->getContent()->type == Content::BREAK)
ch = '\n';
else { // if (it->getContent()->type == Content::TEXT)
if (it->getContent()->text[0] == 0)
return prev ();
else {
pos = strlen (it->getContent()->text);
if (it->getContent()->space) {
ch = ' ';
} else {
pos--;
ch = it->getContent()->text[pos];
}
}
}
return true;
}
else {
ch = START;
return false;
}
} else if (ch == START)
return false;
else {
// at this point, it->getContent()->type == Content::TEXT
pos--;
ch = it->getContent()->text[pos];
return true;
}
}
void CharIterator::highlight (CharIterator *it1, CharIterator *it2,
HighlightLayer layer)
{
if (it2->getChar () == CharIterator::END)
it2->prev ();
if (it1->it->compareTo (it2->it) == 0)
// Only one content => highlight part of it.
it1->it->highlight (it1->pos, it2->pos, layer);
else {
DeepIterator *it = it1->it->cloneDeepIterator ();
int c;
bool start;
for (start = true;
(c = it->compareTo (it2->it)) <= 0;
it->next (), start = false) {
int endOfWord =
it->getContent()->type == Content::TEXT ?
strlen (it->getContent()->text) : 1;
if (start) // first iteration
it->highlight (it1->pos, endOfWord, layer);
else if (c == 0) // last iteration
it->highlight (0, it2->pos, layer);
else
it->highlight (0, endOfWord, layer);
}
delete it;
}
}
void CharIterator::unhighlight (CharIterator *it1, CharIterator *it2,
HighlightLayer layer)
{
if (it1->it->compareTo (it2->it) == 0)
// Only one content => unhighlight it (only for efficiency).
it1->it->unhighlight (0, layer);
else {
DeepIterator *it = it1->it->cloneDeepIterator ();
for (; it->compareTo (it2->it) <= 0; it->next ())
it->unhighlight (-1, layer);
delete it;
}
}
} // namespace core
} // namespace dw

269
dw/iterator.hh Normal file
View File

@ -0,0 +1,269 @@
#ifndef __ITERATOR_HH__
#define __ITERATOR_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
/**
* \brief Iterators are used to iterate through the contents of a widget.
*
* When using iterators, you should care about the results of
* dw::core::Widget::hasContents.
*
* \sa dw::core::Widget::iterator
*/
class Iterator: public lout::object::Comparable
{
protected:
Iterator(Widget *widget, Content::Type mask, bool atEnd);
Iterator(Iterator &it);
~Iterator();
Content content;
private:
Widget *widget;
Content::Type mask;
public:
bool equals (Object *other);
void intoStringBuffer(lout::misc::StringBuffer *sb);
inline Widget *getWidget () { return widget; }
inline Content *getContent () { return &content; }
inline Content::Type getMask () { return mask; }
virtual void unref ();
/**
* \brief Move iterator forward and store content it.
*
* Returns true on success.
*/
virtual bool next () = 0;
/**
* \brief Move iterator backward and store content it.
*
* Returns true on success.
*/
virtual bool prev () = 0;
/**
* \brief Extend highlighted region to contain part of the current content.
*
* For text, start and end define the
* characters, otherwise, the shape is defined as [0, 1], i.e. for
* highlighting a whole dw::core::Content, pass 0 and >= 1.
* To unhighlight see also dw::core::Iterator::unhighlight.
*/
virtual void highlight (int start, int end, HighlightLayer layer) = 0;
/**
* \brief Shrink highlighted region to no longer contain the
* current content.
*
* The direction parameter indicates whether the highlighted region should
* be reduced from the start (direction > 0) or from the end
* (direction < 0). If direction is 0 all content is unhighlighted.
*/
virtual void unhighlight (int direction, HighlightLayer layer) = 0;
/**
* \brief Return the shape, which a part of the item, the iterator points
* on, allocates.
*
* The parameters start and end have the same meaning as in
* DwIterator::highlight().
*/
virtual void getAllocation (int start, int end, Allocation *allocation) = 0;
inline Iterator *cloneIterator () { return (Iterator*)clone(); }
static void scrollTo (Iterator *it1, Iterator *it2, int start, int end,
HPosition hpos, VPosition vpos);
};
/**
* \brief This implementation of dw::core::Iterator can be used by widgets
* with no contents.
*/
class EmptyIterator: public Iterator
{
private:
EmptyIterator (EmptyIterator &it);
public:
EmptyIterator (Widget *widget, Content::Type mask, bool atEnd);
lout::object::Object *clone();
int compareTo(lout::object::Comparable *other);
bool next ();
bool prev ();
void highlight (int start, int end, HighlightLayer layer);
void unhighlight (int direction, HighlightLayer layer);
void getAllocation (int start, int end, Allocation *allocation);
};
/**
* \brief This implementation of dw::core::Iterator can be used by widgets
* having one text word as contents
*/
class TextIterator: public Iterator
{
private:
/** May be NULL, in this case, the next is skipped. */
const char *text;
TextIterator (TextIterator &it);
public:
TextIterator (Widget *widget, Content::Type mask, bool atEnd,
const char *text);
int compareTo(lout::object::Comparable *other);
bool next ();
bool prev ();
void getAllocation (int start, int end, Allocation *allocation);
};
/**
* \brief A stack of iterators, to iterate recursively through a widget tree.
*
* This class is similar to dw::core::Iterator, but not
* created by a widget, but explicitly from another iterator. Deep
* iterators do not have the limitation, that iteration is only done within
* a widget, instead, child widgets are iterated through recursively.
*/
class DeepIterator: public lout::object::Comparable
{
private:
class Stack: public lout::container::typed::Vector<Iterator>
{
public:
inline Stack (): lout::container::typed::Vector<Iterator> (4, false) { }
~Stack ();
inline Iterator *getTop () { return get (size () - 1); }
inline void push (Iterator *it) { put(it, -1); }
inline void pop() { getTop()->unref (); remove (size () - 1); }
};
Stack stack;
static Iterator *searchDownward (Iterator *it, Content::Type mask,
bool fromEnd);
static Iterator *searchSideward (Iterator *it, Content::Type mask,
bool fromEnd);
Content::Type mask;
Content content;
bool hasContents;
inline DeepIterator () { }
static Widget *getRespectiveParent (Widget *widget, Content::Type mask);
inline Widget *getRespectiveParent (Widget *widget) {
return getRespectiveParent (widget, mask);
}
static int getRespectiveLevel (Widget *widget, Content::Type mask);
inline int getRespectiveLevel (Widget *widget) {
return getRespectiveLevel (widget, mask);
}
public:
DeepIterator(Iterator *it);
~DeepIterator();
lout::object::Object *clone ();
DeepIterator *createVariant(Iterator *it);
inline Iterator *getTopIterator () { return stack.getTop(); }
inline Content *getContent () { return &content; }
bool isEmpty ();
bool next ();
bool prev ();
inline DeepIterator *cloneDeepIterator() { return (DeepIterator*)clone(); }
int compareTo(lout::object::Comparable *other);
/**
* \brief Highlight a part of the current content.
*
* Unhighlight the current content by passing -1 as start (see also
* (dw::core::Iterator::unhighlight). For text, start and end define the
* characters, otherwise, the shape is defined as [0, 1], i.e. for
* highlighting a whole dw::core::Content, pass 0 and >= 1.
*/
inline void highlight (int start, int end, HighlightLayer layer)
{ stack.getTop()->highlight (start, end, layer); }
/**
* \brief Return the shape, which a part of the item, the iterator points
* on, allocates.
*
* The parameters start and end have the same meaning as in
* DwIterator::highlight().
*/
inline void getAllocation (int start, int end, Allocation *allocation)
{ stack.getTop()->getAllocation (start, end, allocation); }
inline void unhighlight (int direction, HighlightLayer layer)
{ stack.getTop()->unhighlight (direction, layer); }
inline static void scrollTo (DeepIterator *it1, DeepIterator *it2,
int start, int end,
HPosition hpos, VPosition vpos)
{ Iterator::scrollTo(it1->stack.getTop(), it2->stack.getTop(),
start, end, hpos, vpos); }
};
class CharIterator: public lout::object::Comparable
{
public:
// START and END must not clash with any char value
// neither for signed nor unsigned char.
enum { START = 257, END = 258 };
private:
DeepIterator *it;
int pos, ch;
CharIterator ();
public:
CharIterator (Widget *widget, bool followReferences);
~CharIterator ();
lout::object::Object *clone();
int compareTo(lout::object::Comparable *other);
bool next ();
bool prev ();
inline int getChar() { return ch; }
inline CharIterator *cloneCharIterator() { return (CharIterator*)clone(); }
static void highlight (CharIterator *it1, CharIterator *it2,
HighlightLayer layer);
static void unhighlight (CharIterator *it1, CharIterator *it2,
HighlightLayer layer);
inline static void scrollTo (CharIterator *it1, CharIterator *it2,
HPosition hpos, VPosition vpos)
{ DeepIterator::scrollTo(it1->it, it2->it, it1->pos, it2->pos,
hpos, vpos); }
};
} // namespace core
} // namespace dw
#endif // __ITERATOR_HH__

1357
dw/layout.cc Normal file

File diff suppressed because it is too large Load Diff

451
dw/layout.hh Normal file
View File

@ -0,0 +1,451 @@
#ifndef __DW_LAYOUT_HH__
#define __DW_LAYOUT_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
/**
* \brief The central class for managing and drawing a widget tree.
*
* \sa\ref dw-overview, \ref dw-layout-widgets, \ref dw-layout-views
*/
class Layout: public lout::object::Object
{
friend class Widget;
private:
class LayoutImgRenderer: public style::StyleImage::ExternalImgRenderer
{
Layout *layout;
public:
LayoutImgRenderer (Layout *layout) { this->layout = layout; }
bool readyToDraw ();
void getBgArea (int *x, int *y, int *width, int *height);
void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef);
style::StyleImage *getBackgroundImage ();
style::BackgroundRepeat getBackgroundRepeat ();
style::BackgroundAttachment getBackgroundAttachment ();
style::Length getBackgroundPositionX ();
style::Length getBackgroundPositionY ();
void draw (int x, int y, int width, int height);
};
LayoutImgRenderer *layoutImgRenderer;
public:
/**
* \brief Receiver interface different signals.
*
* May be extended.
*/
class Receiver: public lout::signal::Receiver
{
public:
virtual void resizeQueued (bool extremesChanged);
virtual void canvasSizeChanged (int width, int ascent, int descent);
};
class LinkReceiver: public lout::signal::Receiver
{
public:
/**
* \brief Called, when a link is entered, left, or the position has
* changed.
*
* When a link is entered, this method is called with the respective
* arguments. When a link is left, this method is called with all
* three arguments (\em link, \em x, \em y) set to -1.
*
* When coordinates are supported, a change of the coordinates also
* causes emitting this signal.
*/
virtual bool enter (Widget *widget, int link, int img, int x, int y);
/**
* \brief Called, when the user has pressed the mouse button on a
* link (but not yet released).
*
* The causing event is passed as \em event.
*/
virtual bool press (Widget *widget, int link, int img, int x, int y,
EventButton *event);
/**
* \brief Called, when the user has released the mouse button on a
* link.
*
* The causing event is passed as \em event.
*/
virtual bool release (Widget *widget, int link, int img, int x, int y,
EventButton *event);
/**
* \brief Called, when the user has clicked on a link.
*
* For mouse interaction, this is equivalent to "press" and "release"
* on the same link. In this case, \em event contains the "release"
* event.
*
*
* When activating links via keyboard is supported, only a "clicked"
* signal will be emitted, and \em event will be NULL.
*/
virtual bool click (Widget *widget, int link, int img, int x, int y,
EventButton *event);
};
class LinkEmitter: public lout::signal::Emitter
{
private:
enum { ENTER, PRESS, RELEASE, CLICK };
protected:
bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
int argc, lout::object::Object **argv);
public:
inline void connectLink (LinkReceiver *receiver) { connect (receiver); }
bool emitEnter (Widget *widget, int link, int img, int x, int y);
bool emitPress (Widget *widget, int link, int img, int x, int y,
EventButton *event);
bool emitRelease (Widget *widget, int link, int img, int x, int y,
EventButton *event);
bool emitClick (Widget *widget, int link, int img, int x, int y,
EventButton *event);
};
LinkEmitter linkEmitter;
private:
class Emitter: public lout::signal::Emitter
{
private:
enum { RESIZE_QUEUED, CANVAS_SIZE_CHANGED };
protected:
bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
int argc, lout::object::Object **argv);
public:
inline void connectLayout (Receiver *receiver) { connect (receiver); }
void emitResizeQueued (bool extremesChanged);
void emitCanvasSizeChanged (int width, int ascent, int descent);
};
Emitter emitter;
class Anchor: public lout::object::Object
{
public:
char *name;
Widget *widget;
int y;
~Anchor ();
};
Platform *platform;
View *view;
Widget *topLevel, *widgetAtPoint;
lout::container::typed::Vector<Widget> *queueResizeList;
/* The state, which must be projected into the view. */
style::Color *bgColor;
style::StyleImage *bgImage;
style::BackgroundRepeat bgRepeat;
style::BackgroundAttachment bgAttachment;
style::Length bgPositionX, bgPositionY;
style::Cursor cursor;
int canvasWidth, canvasAscent, canvasDescent;
bool usesViewport, drawAfterScrollReq;
int scrollX, scrollY, viewportWidth, viewportHeight;
bool canvasHeightGreater;
int hScrollbarThickness, vScrollbarThickness;
HPosition scrollTargetHpos;
VPosition scrollTargetVpos;
int scrollTargetX, scrollTargetY, scrollTargetWidth, scrollTargetHeight;
char *requestedAnchor;
int scrollIdleId, resizeIdleId;
bool scrollIdleNotInterrupted;
/* Anchors of the widget tree */
lout::container::typed::HashTable <lout::object::String, Anchor>
*anchorsTable;
SelectionState selectionState;
FindtextState findtextState;
enum ButtonEventType { BUTTON_PRESS, BUTTON_RELEASE, MOTION_NOTIFY };
void detachWidget (Widget *widget);
Widget *getWidgetAtPoint (int x, int y);
void moveToWidget (Widget *newWidgetAtPoint, ButtonState state);
/**
* \brief Emit the necessary crossing events, when the mouse pointer has
* moved to position (\em x, \em );
*/
void moveToWidgetAtPoint (int x, int y, ButtonState state)
{ moveToWidget (getWidgetAtPoint (x, y), state); }
/**
* \brief Emit the necessary crossing events, when the mouse pointer
* has moved out of the view.
*/
void moveOutOfView (ButtonState state) { moveToWidget (NULL, state); }
bool processMouseEvent (MousePositionEvent *event, ButtonEventType type);
bool buttonEvent (ButtonEventType type, View *view,
int numPressed, int x, int y, ButtonState state,
int button);
void resizeIdle ();
void setSizeHints ();
void draw (View *view, Rectangle *area);
void scrollTo0(HPosition hpos, VPosition vpos,
int x, int y, int width, int height,
bool scrollingInterrupted);
void scrollIdle ();
void adjustScrollPos ();
static bool calcScrollInto (int targetValue, int requestedSize,
int *value, int viewportSize);
int currHScrollbarThickness();
int currVScrollbarThickness();
void updateAnchor ();
/* Widget */
char *addAnchor (Widget *widget, const char* name);
char *addAnchor (Widget *widget, const char* name, int y);
void changeAnchor (Widget *widget, char* name, int y);
void removeAnchor (Widget *widget, char* name);
void setCursor (style::Cursor cursor);
void updateCursor ();
void queueDraw (int x, int y, int width, int height);
void queueDrawExcept (int x, int y, int width, int height,
int ex, int ey, int ewidth, int eheight);
void queueResize (bool extremesChanged);
void removeWidget ();
/* For tests regarding the respective Layout and (mostly) Widget
methods. Accessed by respective methods (enter..., leave...,
...Entered) defined here and in Widget. */
int resizeIdleCounter, queueResizeCounter, sizeAllocateCounter,
sizeRequestCounter, getExtremesCounter, resizeCounter;
bool resizeLimit;
void enterResizeIdle () { resizeIdleCounter++; }
void leaveResizeIdle () { resizeIdleCounter--; }
public:
Layout (Platform *platform, bool limit=true);
~Layout ();
inline void connectLink (LinkReceiver *receiver)
{ linkEmitter.connectLink (receiver); }
inline bool emitLinkEnter (Widget *w, int link, int img, int x, int y)
{ return linkEmitter.emitEnter (w, link, img, x, y); }
inline bool emitLinkPress (Widget *w, int link, int img,
int x, int y, EventButton *event)
{ return linkEmitter.emitPress (w, link, img, x, y, event); }
inline bool emitLinkRelease (Widget *w, int link, int img,
int x, int y, EventButton *event)
{ return linkEmitter.emitRelease (w, link, img, x, y, event); }
inline bool emitLinkClick (Widget *w, int link, int img,
int x, int y, EventButton *event)
{ return linkEmitter.emitClick (w, link, img, x, y, event); }
lout::misc::ZoneAllocator *textZone;
void addWidget (Widget *widget);
void setWidget (Widget *widget);
void attachView (View *view);
void detachView (View *view);
inline bool getUsesViewport () { return usesViewport; }
inline int getWidthViewport () { return viewportWidth; }
inline int getHeightViewport () { return viewportHeight; }
inline int getScrollPosX () { return scrollX; }
inline int getScrollPosY () { return scrollY; }
/* public */
void scrollTo (HPosition hpos, VPosition vpos,
int x, int y, int width, int height);
void scroll (ScrollCommand);
void setAnchor (const char *anchor);
/* View */
inline void expose (View *view, Rectangle *area) {
DBG_OBJ_ENTER ("draw", 0, "expose", "%d, %d, %d * %d",
area->x, area->y, area->width, area->height);
draw (view, area);
DBG_OBJ_LEAVE ();
}
/**
* \brief This function is called by a view, to delegate a button press
* event.
*
* \em numPressed is 1 for simple presses, 2 for double presses etc. (more
* that 2 is never needed), \em x and \em y the world coordinates, and
* \em button the number of the button pressed.
*/
inline bool buttonPress (View *view, int numPressed, int x, int y,
ButtonState state, int button)
{
return buttonEvent (BUTTON_PRESS, view, numPressed, x, y, state, button);
}
void containerSizeChanged ();
/**
* \brief This function is called by a view, to delegate a button press
* event.
*
* Arguments are similar to dw::core::Layout::buttonPress.
*/
inline bool buttonRelease (View *view, int numPressed, int x, int y,
ButtonState state, int button)
{
return buttonEvent (BUTTON_RELEASE, view, numPressed, x, y, state,
button);
}
bool motionNotify (View *view, int x, int y, ButtonState state);
void enterNotify (View *view, int x, int y, ButtonState state);
void leaveNotify (View *view, ButtonState state);
void scrollPosChanged (View *view, int x, int y);
void viewportSizeChanged (View *view, int width, int height);
inline Platform *getPlatform ()
{
return platform;
}
/* delegated */
inline int textWidth (style::Font *font, const char *text, int len)
{
return platform->textWidth (font, text, len);
}
inline char *textToUpper (const char *text, int len)
{
return platform->textToUpper (text, len);
}
inline char *textToLower (const char *text, int len)
{
return platform->textToLower (text, len);
}
inline int nextGlyph (const char *text, int idx)
{
return platform->nextGlyph (text, idx);
}
inline int prevGlyph (const char *text, int idx)
{
return platform->prevGlyph (text, idx);
}
inline float dpiX ()
{
return platform->dpiX ();
}
inline float dpiY ()
{
return platform->dpiY ();
}
inline style::Font *createFont (style::FontAttrs *attrs, bool tryEverything)
{
return platform->createFont (attrs, tryEverything);
}
inline bool fontExists (const char *name)
{
return platform->fontExists (name);
}
inline style::Color *createColor (int color)
{
return platform->createColor (color);
}
inline style::Tooltip *createTooltip (const char *text)
{
return platform->createTooltip (text);
}
inline void cancelTooltip ()
{
return platform->cancelTooltip ();
}
inline Imgbuf *createImgbuf (Imgbuf::Type type, int width, int height,
double gamma)
{
return platform->createImgbuf (type, width, height, gamma);
}
inline void copySelection(const char *text)
{
platform->copySelection(text);
}
inline ui::ResourceFactory *getResourceFactory ()
{
return platform->getResourceFactory ();
}
inline void connect (Receiver *receiver) {
emitter.connectLayout (receiver); }
/** \brief See dw::core::FindtextState::search. */
inline FindtextState::Result search (const char *str, bool caseSens,
int backwards)
{ return findtextState.search (str, caseSens, backwards); }
/** \brief See dw::core::FindtextState::resetSearch. */
inline void resetSearch () { findtextState.resetSearch (); }
void setBgColor (style::Color *color);
void setBgImage (style::StyleImage *bgImage,
style::BackgroundRepeat bgRepeat,
style::BackgroundAttachment bgAttachment,
style::Length bgPositionX, style::Length bgPositionY);
inline style::Color* getBgColor () { return bgColor; }
inline style::StyleImage* getBgImage () { return bgImage; }
};
} // namespace core
} // namespace dw
#endif // __DW_LAYOUT_HH__

83
dw/listitem.cc Normal file
View File

@ -0,0 +1,83 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "listitem.hh"
#include "../lout/debug.hh"
#include <stdio.h>
namespace dw {
int ListItem::CLASS_ID = -1;
ListItem::ListItem (ListItem *ref, bool limitTextWidth):
AlignedTextblock (limitTextWidth)
{
DBG_OBJ_CREATE ("dw::ListItem");
registerName ("dw::ListItem", &CLASS_ID);
setRefTextblock (ref);
}
ListItem::~ListItem()
{
DBG_OBJ_DELETE ();
}
bool ListItem::usesMaxGeneratorWidth ()
{
return true;
}
void ListItem::initWithWidget (core::Widget *widget,
core::style::Style *style)
{
hasListitemValue = true;
addWidget (widget, style);
addSpace (style);
if (style->listStylePosition == core::style::LIST_STYLE_POSITION_OUTSIDE)
updateValue ();
}
void ListItem::initWithText (const char *text, core::style::Style *style)
{
hasListitemValue = true;
addText (text, style);
addSpace (style);
if (style->listStylePosition == core::style::LIST_STYLE_POSITION_OUTSIDE)
updateValue ();
}
int ListItem::getValue ()
{
if (words->size () == 0)
return 0;
else
return words->getRef(0)->size.width + words->getRef(0)->origSpace;
}
void ListItem::setMaxValue (int maxValue, int value)
{
leftInnerPadding = maxValue;
line1Offset = - value;
redrawY = 0;
queueResize (0, true);
}
} // namespace dw

29
dw/listitem.hh Normal file
View File

@ -0,0 +1,29 @@
#ifndef __DW_LISTITEM_HH__
#define __DW_LISTITEM_HH__
#include "core.hh"
#include "alignedtextblock.hh"
namespace dw {
class ListItem: public AlignedTextblock
{
protected:
int getValue ();
void setMaxValue (int maxValue, int value);
public:
static int CLASS_ID;
ListItem(ListItem *ref, bool limitTextWidth);
~ListItem();
bool usesMaxGeneratorWidth ();
void initWithWidget (core::Widget *widget, core::style::Style *style);
void initWithText (const char *text, core::style::Style *style);
};
} // namespace dw
#endif // __DW_LISTITEM_HH__

618
dw/oofawarewidget.cc Normal file
View File

@ -0,0 +1,618 @@
/*
* Dillo Widget
*
* Copyright 2014 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.
*
* 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 "oofawarewidget.hh"
#include "ooffloatsmgr.hh"
#include "oofposabsmgr.hh"
#include "oofposrelmgr.hh"
#include "oofposfixedmgr.hh"
using namespace dw;
using namespace dw::core;
using namespace dw::core::style;
using namespace lout::object;
using namespace lout::misc;
using namespace lout::container::typed;
namespace dw {
namespace oof {
const char *OOFAwareWidget::OOFM_NAME[NUM_OOFM] = {
"FLOATS", "ABSOLUTE", "RELATIVE", "FIXED"
};
int OOFAwareWidget::CLASS_ID = -1;
OOFAwareWidget::OOFAwareWidget ()
{
DBG_OBJ_CREATE ("dw::oof::OOFAwareWidget");
registerName ("dw::oof::OOFAwareWidget", &CLASS_ID);
for (int i = 0; i < NUM_OOFM; i++) {
oofContainer[i] = NULL;
DBG_OBJ_ARRSET_PTR ("oofContainer", i, oofContainer[i]);
outOfFlowMgr[i] = NULL;
}
}
OOFAwareWidget::~OOFAwareWidget ()
{
for (int i = 0; i < NUM_OOFM; i++) {
if(outOfFlowMgr[i]) {
// I feel more comfortable by letting the OOF aware widget delete
// these widgets, instead of doing this in ~OutOfFlowMgr.
for (int j = 0; j < outOfFlowMgr[i]->getNumWidgets (); j++)
delete outOfFlowMgr[i]->getWidget (j);
delete outOfFlowMgr[i];
}
}
DBG_OBJ_DELETE ();
}
const char *OOFAwareWidget::stackingLevelText (int level)
{
switch (level) {
case SL_START: return "START";
case SL_BACKGROUND: return "BACKGROUND";
case SL_SC_BOTTOM: return "SC_BOTTOM";
case SL_IN_FLOW: return "IN_FLOW";
case SL_OOF_REF: return "OOF_REF";
case SL_OOF_CONT: return "OOF_CONT";
case SL_SC_TOP: return "SC_TOP";
case SL_END: return "END";
default: return "???";
}
}
void OOFAwareWidget::notifySetAsTopLevel ()
{
for (int i = 0; i < NUM_OOFM; i++) {
oofContainer[i] = this;
DBG_OBJ_ARRSET_PTR ("oofContainer", i, oofContainer[i]);
}
}
int OOFAwareWidget::getOOFMIndex (Widget *widget)
{
DBG_OBJ_ENTER_O ("construct", 0, NULL, "getOOFMIndex", "%p", widget);
DBG_OBJ_MSGF_O ("construct", 1, NULL, "position = %s, float = %s",
widget->getStyle()->position
== style::POSITION_STATIC ? "static" :
(widget->getStyle()->position
== style::POSITION_RELATIVE ? "relative" :
(widget->getStyle()->position
== style::POSITION_ABSOLUTE ? "absolute" :
(widget->getStyle()->position
== style::POSITION_FIXED ? "fixed" : "???"))),
widget->getStyle()->vloat == style::FLOAT_NONE ? "none" :
(widget->getStyle()->vloat == style::FLOAT_LEFT ? "left" :
(widget->getStyle()->vloat == style::FLOAT_RIGHT ?
"right" : "???")));
int index = -1;
if (testWidgetFloat (widget))
index = OOFM_FLOATS;
else if (testWidgetAbsolutelyPositioned (widget))
index = OOFM_ABSOLUTE;
else if (testWidgetRelativelyPositioned (widget))
index = OOFM_RELATIVE;
else if (testWidgetFixedlyPositioned (widget))
index = OOFM_FIXED;
else
lout::misc::assertNotReached ();
DBG_OBJ_LEAVE_VAL_O (NULL, "%d (%s)", index, OOFM_NAME[index]);
return index;
}
bool OOFAwareWidget::isOOFContainer (Widget *widget, int oofmIndex)
{
// TODO The methods isPossibleContainer() and isPossibleContainerParent()
// are only used in few cases. Does not matter currently, however.
switch (oofmIndex) {
case OOFM_FLOATS:
return widget->instanceOf (OOFAwareWidget::CLASS_ID) &&
(// For floats, only some OOF aware widgets are considered as
// containers.
((OOFAwareWidget*)widget)->isPossibleOOFContainer (OOFM_FLOATS) &&
// The second condition: that this block is "out of flow", in a
// wider sense.
(// The toplevel widget is "out of flow", since there is no
// parent, and so no context.
widget->getParent() == NULL ||
// A similar reasoning applies to a widget with an
// unsuitable parent (typical example: a table cell (this
// is also a text block, so possible float container)
// within a table widget, which is not a suitable float
// container parent).
!(widget->getParent()->instanceOf (OOFAwareWidget::CLASS_ID) &&
((OOFAwareWidget*)widget->getParent())
->isPossibleOOFContainerParent (OOFM_FLOATS)) ||
// Inline blocks are containing blocks, too.
widget->getStyle()->display == DISPLAY_INLINE_BLOCK ||
// Same for blocks with 'overview' set to another value than
// (the default value) 'visible'.
widget->getStyle()->overflow != OVERFLOW_VISIBLE ||
// Finally, "out of flow" in a narrower sense: floats;
// absolutely and fixedly positioned elements. (No
// relatively positioned elements; since the latters
// constitute a stacking context, drawing of floats gets
// somewhat more complicated; see "interrupting the drawing
// process" in "dw-stacking-context.doc".
testWidgetOutOfFlow (widget)));
case OOFM_RELATIVE:
case OOFM_ABSOLUTE:
return widget->instanceOf (OOFAwareWidget::CLASS_ID) &&
(widget->getParent() == NULL ||
OOFAwareWidget::testWidgetPositioned (widget));
case OOFM_FIXED:
// The single container for fixedly positioned elements is the
// toplevel (canvas; actually the viewport). (The toplevel
// widget should always be a textblock; at least this is the
// case in dillo.)
return widget->getParent() == NULL;
default:
// compiler happiness
lout::misc::assertNotReached ();
return false;
}
}
void OOFAwareWidget::notifySetParent ()
{
// Search for containing blocks.
for (int oofmIndex = 0; oofmIndex < NUM_OOFM; oofmIndex++) {
oofContainer[oofmIndex] = NULL;
for (Widget *widget = this;
widget != NULL && oofContainer[oofmIndex] == NULL;
widget = widget->getParent ())
if (isOOFContainer (widget, oofmIndex)) {
assert (widget->instanceOf (OOFAwareWidget::CLASS_ID));
oofContainer[oofmIndex] = (OOFAwareWidget*)widget;
}
DBG_OBJ_ARRSET_PTR ("oofContainer", oofmIndex, oofContainer[oofmIndex]);
assert (oofContainer[oofmIndex] != NULL);
}
}
void OOFAwareWidget::initOutOfFlowMgrs ()
{
if (oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS] == NULL) {
oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS] =
new OOFFloatsMgr (oofContainer[OOFM_FLOATS], OOFM_FLOATS);
DBG_OBJ_ASSOC (oofContainer[OOFM_FLOATS],
oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS]);
}
if (oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE] == NULL) {
oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE] =
new OOFPosAbsMgr (oofContainer[OOFM_ABSOLUTE]);
DBG_OBJ_ASSOC (oofContainer[OOFM_ABSOLUTE],
oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE]);
}
if (oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE] == NULL) {
oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE] =
new OOFPosRelMgr (oofContainer[OOFM_RELATIVE]);
DBG_OBJ_ASSOC (oofContainer[OOFM_RELATIVE],
oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE]);
}
if (oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED] == NULL) {
oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED] =
new OOFPosFixedMgr (oofContainer[OOFM_FIXED]);
DBG_OBJ_ASSOC (oofContainer[OOFM_FIXED],
oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED]);
}
}
void OOFAwareWidget::correctRequisitionByOOF (Requisition *requisition,
void (*splitHeightFun) (int, int*,
int*))
{
DBG_OBJ_ENTER ("resize", 0, "correctRequisitionByOOF", "%d * (%d + %d), ...",
requisition->width, requisition->ascent,
requisition->descent);
requisitionWithoutOOF = *requisition;
for (int i = 0; i < NUM_OOFM; i++) {
if (outOfFlowMgr[i]) {
DBG_OBJ_MSGF ("resize", 1, "OOFM for %s", OOFM_NAME[i]);
DBG_OBJ_MSG_START ();
int oofWidth, oofHeight;
outOfFlowMgr[i]->getSize (requisition, &oofWidth, &oofHeight);
DBG_OBJ_MSGF ("resize", 1, "result: %d * %d", oofWidth, oofHeight);
if (oofWidth > requisition->width) {
if (outOfFlowMgr[i]->containerMustAdjustExtraSpace () &&
adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) {
extraSpace.right = max (extraSpace.right,
oofWidth - requisition->width);
DBG_OBJ_SET_NUM ("extraSpace.right", extraSpace.right);
}
requisition->width = oofWidth;
}
if (oofHeight > requisition->ascent + requisition->descent) {
if (outOfFlowMgr[i]->containerMustAdjustExtraSpace () &&
adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) {
extraSpace.bottom = max (extraSpace.bottom,
oofHeight - (requisition->ascent +
requisition->descent));
DBG_OBJ_SET_NUM ("extraSpace.bottom", extraSpace.bottom);
}
splitHeightFun (oofHeight,
&requisition->ascent, &requisition->descent);
}
if (!adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) {
requisitionWithoutOOF.width = max (requisitionWithoutOOF.width,
oofWidth);
if (oofHeight >
requisitionWithoutOOF.ascent + requisitionWithoutOOF.descent)
splitHeightFun (oofHeight, &requisitionWithoutOOF.ascent,
&requisitionWithoutOOF.descent);
}
DBG_OBJ_MSGF ("resize", 1, "after correction: %d * (%d + %d)",
requisition->width, requisition->ascent,
requisition->descent);
DBG_OBJ_MSG_END ();
} else
DBG_OBJ_MSGF ("resize", 1, "no OOFM for %s", OOFM_NAME[i]);
}
DBG_OBJ_SET_NUM ("requisitionWithoutOOF.width", requisitionWithoutOOF.width);
DBG_OBJ_SET_NUM ("requisitionWithoutOOF.ascent",
requisitionWithoutOOF.ascent);
DBG_OBJ_SET_NUM ("requisitionWithoutOOF.descent",
requisitionWithoutOOF.descent);
DBG_OBJ_LEAVE ();
}
void OOFAwareWidget::correctExtremesByOOF (Extremes *extremes)
{
DBG_OBJ_ENTER ("resize", 0, "correctExtremesByOOF", "%d (%d) / %d (%d)",
extremes->minWidth, extremes->minWidthIntrinsic,
extremes->maxWidth, extremes->maxWidthIntrinsic);
for (int i = 0; i < NUM_OOFM; i++) {
if (outOfFlowMgr[i]) {
DBG_OBJ_MSGF ("resize", 1, "OOFM for %s", OOFM_NAME[i]);
DBG_OBJ_MSG_START ();
int oofMinWidth, oofMaxWidth;
outOfFlowMgr[i]->getExtremes (extremes, &oofMinWidth, &oofMaxWidth);
DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
oofMinWidth, oofMaxWidth);
extremes->minWidth = max (extremes->minWidth, oofMinWidth);
extremes->minWidthIntrinsic = max (extremes->minWidthIntrinsic,
oofMinWidth);
extremes->maxWidth = max (extremes->maxWidth, oofMaxWidth);
extremes->maxWidthIntrinsic = max (extremes->maxWidthIntrinsic,
oofMinWidth);
extremes->adjustmentWidth = max (extremes->adjustmentWidth,
oofMinWidth);
DBG_OBJ_MSGF ("resize", 1, "after correction: %d (%d) / %d (%d)",
extremes->minWidth, extremes->minWidthIntrinsic,
extremes->maxWidth, extremes->maxWidthIntrinsic);
DBG_OBJ_MSG_END ();
} else
DBG_OBJ_MSGF ("resize", 1, "no OOFM for %s", OOFM_NAME[i]);
}
DBG_OBJ_LEAVE ();
}
void OOFAwareWidget::sizeAllocateStart (Allocation *allocation)
{
for (int i = 0; i < NUM_OOFM; i++)
if (oofContainer[i]->outOfFlowMgr[i])
oofContainer[i]->outOfFlowMgr[i]->sizeAllocateStart (this, allocation);
}
void OOFAwareWidget::sizeAllocateEnd ()
{
for (int i = 0; i < NUM_OOFM; i++)
if (oofContainer[i]->outOfFlowMgr[i])
oofContainer[i]->outOfFlowMgr[i]->sizeAllocateEnd (this);
}
void OOFAwareWidget::containerSizeChangedForChildrenOOF ()
{
for (int i = 0; i < NUM_OOFM; i++)
if (outOfFlowMgr[i])
outOfFlowMgr[i]->containerSizeChangedForChildren ();
}
bool OOFAwareWidget::doesWidgetOOFInterruptDrawing (Widget *widget)
{
DBG_OBJ_ENTER ("draw", 0, "doesWidgetOOFInterruptDrawing", "%p", widget);
bool result;
if (IMPL_POS) {
// This is the generator of the widget.
int oofmIndex = getOOFMIndex (widget);
DBG_OBJ_MSGF ("draw", 1, "oofmIndex = %d", oofmIndex);
int cl = oofContainer[oofmIndex]->stackingContextWidget->getLevel (),
gl = stackingContextWidget->getLevel ();
result = cl < gl;
DBG_OBJ_MSGF ("draw", 1,"%d < %d => %s", cl, gl, boolToStr (result));
} else
result = false;
DBG_OBJ_LEAVE_VAL ("%s", boolToStr (result));
return result;
}
void OOFAwareWidget::draw (View *view, Rectangle *area, DrawingContext *context)
{
DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
area->x, area->y, area->width, area->height);
for (int level = SL_START + 1; level < SL_END; level++)
drawLevel (view, area, level, context);
DBG_OBJ_LEAVE ();
}
void OOFAwareWidget::drawLevel (View *view, Rectangle *area, int level,
DrawingContext *context)
{
DBG_OBJ_ENTER ("draw", 0, "OOFAwareWidget::drawLevel",
"(%d, %d, %d * %d), %s",
area->x, area->y, area->width, area->height,
stackingLevelText (level));
switch (level) {
case SL_START:
break;
case SL_BACKGROUND:
drawWidgetBox (view, area, false);
break;
case SL_SC_BOTTOM:
if (stackingContextMgr)
stackingContextMgr->drawBottom (view, area, context);
break;
case SL_IN_FLOW:
// Should be implemented in the sub class.
break;
case SL_OOF_REF:
// Should be implemented in the sub class (when references are hold).
break;
case SL_OOF_CONT:
drawOOF (view, area, context);
break;
case SL_SC_TOP:
if (stackingContextMgr)
stackingContextMgr->drawTop (view, area, context);
break;
case SL_END:
break;
default:
fprintf (stderr, "OOFAwareWidget::drawLevel: invalid level %s (%d)",
stackingLevelText (level), level);
break;
}
DBG_OBJ_LEAVE ();
}
void OOFAwareWidget::drawOOF (View *view, Rectangle *area,
DrawingContext *context)
{
for (int i = 0; i < NUM_OOFM; i++) {
if(outOfFlowMgr[i])
outOfFlowMgr[i]->draw (view, area, context);
}
}
Widget *OOFAwareWidget::getWidgetAtPoint (int x, int y,
GettingWidgetAtPointContext *context)
{
DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y);
Widget *widgetAtPoint = NULL;
if (inAllocation (x, y)) {
for (int level = SL_END - 1; widgetAtPoint == NULL && level > SL_START;
level--)
widgetAtPoint = getWidgetAtPointLevel (x, y, level, context);
}
DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint);
DBG_OBJ_LEAVE ();
return widgetAtPoint;
}
Widget *OOFAwareWidget::getWidgetAtPointLevel (int x, int y, int level,
GettingWidgetAtPointContext
*context)
{
DBG_OBJ_ENTER ("events", 0, "OOFAwareWidget::getWidgetAtPointLevel",
"%d, %d, %s", x, y, stackingLevelText (level));
Widget *widgetAtPoint = NULL;
switch (level) {
case SL_BACKGROUND:
if (inAllocation (x, y))
widgetAtPoint = this;
break;
case SL_SC_BOTTOM:
if (stackingContextMgr)
widgetAtPoint =
stackingContextMgr->getBottomWidgetAtPoint (x, y, context);
break;
case SL_IN_FLOW:
// Should be implemented in the sub class.
assertNotReached ("getWidgetAtPoint (SL_IN_FLOW) for %s",
getClassName ());
break;
case SL_OOF_REF:
// Should be implemented in the sub class (when references are hold).
break;
case SL_OOF_CONT:
widgetAtPoint = getWidgetOOFAtPoint (x, y, context);
break;
case SL_SC_TOP:
if (stackingContextMgr)
widgetAtPoint =
stackingContextMgr->getTopWidgetAtPoint (x, y, context);
break;
default:
fprintf (stderr,
"OOFAwareWidget::getWidgetAtPointLevel: invalid level %s (%d)",
stackingLevelText (level), level);
break;
}
DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint);
DBG_OBJ_LEAVE ();
return widgetAtPoint;
}
Widget *OOFAwareWidget::getWidgetOOFAtPoint (int x, int y,
GettingWidgetAtPointContext
*context)
{
Widget *widgetAtPoint = NULL;
for (int i = NUM_OOFM -1; widgetAtPoint == NULL && i >= 0; i--) {
if(outOfFlowMgr[i])
widgetAtPoint = outOfFlowMgr[i]->getWidgetAtPoint (x, y, context);
}
return widgetAtPoint;
}
void OOFAwareWidget::removeChild (Widget *child)
{
// Sub classes should implement this method (and Textblock and
// Table do so), so this point is only reached from
// ~OOFAwareWidget, which removes widgets out of flow.
assert (isWidgetOOF (child));
}
void OOFAwareWidget::updateReference (int ref)
{
notImplemented ("OOFAwareWidget::updateReference");
}
void OOFAwareWidget::widgetRefSizeChanged (int externalIndex)
{
notImplemented ("OOFAwareWidget::widgetRefSizeChanged");
}
void OOFAwareWidget::oofSizeChanged (bool extremesChanged)
{
DBG_OBJ_ENTER ("resize", 0, "oofSizeChanged", "%s",
extremesChanged ? "true" : "false");
queueResize (-1, extremesChanged);
// Extremes changes may become also relevant for the children.
if (extremesChanged)
containerSizeChanged ();
DBG_OBJ_LEAVE ();
}
int OOFAwareWidget::getGeneratorX (int oofmIndex)
{
notImplemented ("OOFAwareWidget::getGeneratorX");
return 0;
}
int OOFAwareWidget::getGeneratorY (int oofmIndex)
{
notImplemented ("OOFAwareWidget::getGeneratorY");
return 0;
}
int OOFAwareWidget::getGeneratorWidth ()
{
notImplemented ("OOFAwareWidget::getGeneratorWidth");
return 0;
}
int OOFAwareWidget::getMaxGeneratorWidth ()
{
notImplemented ("OOFAwareWidget::getMaxGeneratorWidth");
return 0;
}
bool OOFAwareWidget::usesMaxGeneratorWidth ()
{
notImplemented ("OOFAwareWidget::usesMaxGeneratorWidth");
return false;
}
bool OOFAwareWidget::isPossibleOOFContainer (int oofmIndex)
{
return oofmIndex != OOFM_FLOATS;
}
bool OOFAwareWidget::isPossibleOOFContainerParent (int oofmIndex)
{
return oofmIndex != OOFM_FLOATS;
}
bool OOFAwareWidget::adjustExtraSpaceWhenCorrectingRequisitionByOOF ()
{
return true;
}
} // namespace oof
} // namespace dw

302
dw/oofawarewidget.hh Normal file
View File

@ -0,0 +1,302 @@
#ifndef __DW_OOFAWAREWIDGET_HH__
#define __DW_OOFAWAREWIDGET_HH__
#include "core.hh"
#include "outofflowmgr.hh"
namespace dw {
namespace oof {
/**
* \brief Base class for widgets which can act as container and
* generator for widgets out of flow.
*
* (Perhaps it should be diffenciated between the two roles, container
* and generator, but this would make multiple inheritance necessary.)
*
* See \ref dw-out-of-flow for an overview.
*
* Requirements for sub classes (in most cases refer to dw::Textblock
* as a good example):
*
* - A sub class should at least take care to call these methods at the
* respective points:
*
* - dw::oof::OOFAwareWidget::correctRequisitionByOOF (from
* dw::core::Widget::getExtremesImpl)
* - dw::oof::OOFAwareWidget::correctExtremesByOOF (from
* dw::core::Widget::sizeRequestImpl)
* - dw::oof::OOFAwareWidget::sizeAllocateStart
* - dw::oof::OOFAwareWidget::sizeAllocateEnd (latter two from
* dw::core::Widget::sizeAllocateImpl)
* - dw::oof::OOFAwareWidget::containerSizeChangedForChildrenOOF
* (from dw::core::Widget::containerSizeChangedForChildren)
* - dw::oof::OOFAwareWidget::drawOOF (from dw::core::Widget::draw)
* - dw::oof::OOFAwareWidget::getWidgetOOFAtPoint (from
* dw::core::Widget::getWidgetAtPoint)
*
* - Implementations of dw::core::Widget::getAvailWidthOfChild and
* dw::core::Widget::getAvailHeightOfChild have to distinguish
* between widgets in flow and out of flow; general pattern:
*
* \code
* if (isWidgetOOF (child) && getWidgetOutOfFlowMgr(child) &&
* getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child))
* width =
* getWidgetOutOfFlowMgr(child)->getAvailWidthOfChild (child,forceValue);
* else {
* // ... specific implementation ...
* \endcode
*
* See also implementations of dw::Textblock and dw::Table. (Open
* issue: What about dw::core::Widget::correctRequisitionOfChild and
* dw::core::Widget::correctExtremesOfChild? Currently, all widgets
* are used the default implementation.)
*
* - Iterators have to consider widgets out of flow;
* dw::oof::OOFAwareWidget::OOFAwareWidgetIterator is recommended as
* base class.
*
* - dw::core::Widget::parentRef has to be set for widgets in flow; if
* not used further, a simple *makeParentRefInFlow(0)* is sufficient
* (as dw::Table::addCell does). Widgets which are only containers,
* but not generators, do not have to care about widgets out of
* flow in this regard.
*
* For both generators and containers of floats (which is only
* implemented by dw::Textblock) it gets a bit more complicated.
*
* \todo Currently, on the level of dw::oof::OOFAwareWidget, nothing
* is done about dw::core::Widget::markSizeChange and
* dw::core::Widget::markExtremesChange. This does not matter, though:
* dw::Textblock takes care of these, and dw::Table is only connected
* to subclasses of dw::oof::OOFPositionedMgr, which do care about
* these. However, this should be considered for completeness.
*/
class OOFAwareWidget: public core::Widget
{
protected:
enum { OOFM_FLOATS, OOFM_ABSOLUTE, OOFM_RELATIVE, OOFM_FIXED, NUM_OOFM };
static const char *OOFM_NAME[NUM_OOFM];
enum { PARENT_REF_OOFM_BITS = 3,
PARENT_REF_OOFM_MASK = (1 << PARENT_REF_OOFM_BITS) - 1 };
class OOFAwareWidgetIterator: public core::Iterator
{
private:
enum { NUM_SECTIONS = NUM_OOFM + 1 };
int sectionIndex; // 0 means in flow, otherwise OOFM index + 1
int index;
int numParts (int sectionIndex, int numContentsInFlow = -1);
void getPart (int sectionIndex, int index, core::Content *content);
protected:
virtual int numContentsInFlow () = 0;
virtual void getContentInFlow (int index, core::Content *content) = 0;
void setValues (int sectionIndex, int index);
inline void cloneValues (OOFAwareWidgetIterator *other)
{ other->setValues (sectionIndex, index); }
inline bool inFlow () { return sectionIndex == 0; }
inline int getInFlowIndex () { assert (inFlow ()); return index; }
void highlightOOF (int start, int end, core::HighlightLayer layer);
void unhighlightOOF (int direction, core::HighlightLayer layer);
void getAllocationOOF (int start, int end, core::Allocation *allocation);
public:
OOFAwareWidgetIterator (OOFAwareWidget *widget, core::Content::Type mask,
bool atEnd, int numContentsInFlow);
void intoStringBuffer(lout::misc::StringBuffer *sb);
int compareTo(lout::object::Comparable *other);
bool next ();
bool prev ();
};
inline bool isParentRefOOF (int parentRef)
{ return parentRef != -1 && (parentRef & PARENT_REF_OOFM_MASK); }
inline int makeParentRefInFlow (int inFlowSubRef)
{ return (inFlowSubRef << PARENT_REF_OOFM_BITS); }
inline int getParentRefInFlowSubRef (int parentRef)
{ assert (!isParentRefOOF (parentRef));
return parentRef >> PARENT_REF_OOFM_BITS; }
inline int makeParentRefOOF (int oofmIndex, int oofmSubRef)
{ return (oofmSubRef << PARENT_REF_OOFM_BITS) | (oofmIndex + 1); }
inline int getParentRefOOFSubRef (int parentRef)
{ assert (isParentRefOOF (parentRef));
return parentRef >> PARENT_REF_OOFM_BITS; }
inline int getParentRefOOFIndex (int parentRef)
{ assert (isParentRefOOF (parentRef));
return (parentRef & PARENT_REF_OOFM_MASK) - 1; }
inline oof::OutOfFlowMgr *getParentRefOutOfFlowMgr (int parentRef)
{ return outOfFlowMgr[getParentRefOOFIndex (parentRef)]; }
inline bool isWidgetOOF (Widget *widget)
{ return isParentRefOOF (widget->parentRef); }
inline int getWidgetInFlowSubRef (Widget *widget)
{ return getParentRefInFlowSubRef (widget->parentRef); }
inline int getWidgetOOFSubRef (Widget *widget)
{ return getParentRefOOFSubRef (widget->parentRef); }
inline int getWidgetOOFIndex (Widget *widget)
{ return getParentRefOOFIndex (widget->parentRef); }
inline oof::OutOfFlowMgr *getWidgetOutOfFlowMgr (Widget *widget)
{ return getParentRefOutOfFlowMgr (widget->parentRef); }
OOFAwareWidget *oofContainer[NUM_OOFM];
OutOfFlowMgr *outOfFlowMgr[NUM_OOFM];
core::Requisition requisitionWithoutOOF;
inline OutOfFlowMgr *searchOutOfFlowMgr (int oofmIndex)
{ return oofContainer[oofmIndex] ?
oofContainer[oofmIndex]->outOfFlowMgr[oofmIndex] : NULL; }
static int getOOFMIndex (Widget *widget);
void initOutOfFlowMgrs ();
void correctRequisitionByOOF (core::Requisition *requisition,
void (*splitHeightFun) (int, int*, int*));
void correctExtremesByOOF (core::Extremes *extremes);
void sizeAllocateStart (core::Allocation *allocation);
void sizeAllocateEnd ();
void containerSizeChangedForChildrenOOF ();
virtual void drawLevel (core::View *view, core::Rectangle *area, int level,
core::DrawingContext *context);
void drawOOF (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
Widget *getWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext *context);
virtual Widget *getWidgetAtPointLevel (int x, int y, int level,
core::GettingWidgetAtPointContext
*context);
Widget *getWidgetOOFAtPoint (int x, int y,
core::GettingWidgetAtPointContext *context);
static bool isOOFContainer (Widget *widget, int oofmIndex);
void notifySetAsTopLevel();
void notifySetParent();
void removeChild (Widget *child);
virtual bool adjustExtraSpaceWhenCorrectingRequisitionByOOF ();
public:
enum {
SL_START, SL_BACKGROUND, SL_SC_BOTTOM, SL_IN_FLOW, SL_OOF_REF,
SL_OOF_CONT, SL_SC_TOP, SL_END };
static int CLASS_ID;
OOFAwareWidget ();
~OOFAwareWidget ();
static const char *stackingLevelText (int level);
static inline bool testStyleFloat (core::style::Style *style)
{ return style->vloat != core::style::FLOAT_NONE; }
static inline bool testStyleAbsolutelyPositioned (core::style::Style *style)
{ return IMPL_POS && style->position == core::style::POSITION_ABSOLUTE; }
static inline bool testStyleFixedlyPositioned (core::style::Style *style)
{ return IMPL_POS && style->position == core::style::POSITION_FIXED; }
static inline bool testStyleRelativelyPositioned (core::style::Style *style)
{ return IMPL_POS && style->position == core::style::POSITION_RELATIVE; }
static inline bool testStylePositioned (core::style::Style *style)
{ return testStyleAbsolutelyPositioned (style) ||
testStyleRelativelyPositioned (style) ||
testStyleFixedlyPositioned (style); }
static inline bool testStyleOutOfFlow (core::style::Style *style)
{ // Second part is equivalent to testStylePositioned(), but we still keep
// the two separately.
return testStyleFloat (style) || testStyleAbsolutelyPositioned (style)
|| testStyleRelativelyPositioned (style)
|| testStyleFixedlyPositioned (style); }
static inline bool testWidgetFloat (Widget *widget)
{ return testStyleFloat (widget->getStyle ()); }
static inline bool testWidgetAbsolutelyPositioned (Widget *widget)
{ return testStyleAbsolutelyPositioned (widget->getStyle ()); }
static inline bool testWidgetFixedlyPositioned (Widget *widget)
{ return testStyleFixedlyPositioned (widget->getStyle ()); }
static inline bool testWidgetRelativelyPositioned (Widget *widget)
{ return testStyleRelativelyPositioned (widget->getStyle ()); }
static inline bool testWidgetPositioned (Widget *widget)
{ return testStylePositioned (widget->getStyle ()); }
static inline bool testWidgetOutOfFlow (Widget *widget)
{ return testStyleOutOfFlow (widget->getStyle ()); }
inline core::Requisition *getRequisitionWithoutOOF ()
{ return &requisitionWithoutOOF; }
bool doesWidgetOOFInterruptDrawing (Widget *widget);
void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
/**
* Update content in flow, down from `ref`. Uses e. g. when floats sizes have
* changed.
*/
virtual void updateReference (int ref);
/**
* Called by an implementation of dw::oof::OutOfFlowMgr (actually only
* OOFPosRelMgr) for the generator of a widget out of flow, when the
* reference size has changed. (The size of the reference is 0 * 0 for all
* other implementations of dw::oof::OutOfFlowMgr.)
*/
virtual void widgetRefSizeChanged (int externalIndex);
/**
* Called by an implementation of dw::oof::OutOfFlowMgr when the size of the
* container has changed, typically in sizeAllocateEnd.
*/
virtual void oofSizeChanged (bool extremesChanged);
/**
* Return position relative to container, not regarding
* margin/border/padding, Called by OOFFloatsMgr to position floats.
*/
virtual int getGeneratorX (int oofmIndex);
/**
* Return position relative to container, not regarding
* margin/border/padding, Called by OOFFloatsMgr to position floats.
*/
virtual int getGeneratorY (int oofmIndex);
/**
* Return width including margin/border/padding Called by OOFFloatsMgr to
* position floats.
*/
virtual int getGeneratorWidth ();
virtual int getMaxGeneratorWidth ();
virtual bool usesMaxGeneratorWidth ();
virtual bool isPossibleOOFContainer (int oofmIndex);
virtual bool isPossibleOOFContainerParent (int oofmIndex);
};
} // namespace oof
} // namespace dw
#endif // __DW_OOFAWAREWIDGET_HH__

View File

@ -0,0 +1,251 @@
/*
* Dillo Widget
*
* Copyright 2014 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.
*
* 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 "oofawarewidget.hh"
#include "ooffloatsmgr.hh"
#include "oofposabsmgr.hh"
#include "oofposfixedmgr.hh"
using namespace dw;
using namespace dw::core;
using namespace lout::misc;
using namespace lout::object;
namespace dw {
namespace oof {
// "numContentsInFlow" is passed here to avoid indirectly calling the (virtual)
// method numContentsInFlow() from the constructor.
OOFAwareWidget::OOFAwareWidgetIterator::OOFAwareWidgetIterator
(OOFAwareWidget *widget, Content::Type mask, bool atEnd,
int numContentsInFlow) :
Iterator (widget, mask, atEnd)
{
if (atEnd) {
sectionIndex = NUM_SECTIONS - 1;
while (sectionIndex >= 0 &&
numParts (sectionIndex, numContentsInFlow) == 0)
sectionIndex--;
index = numParts (sectionIndex, numContentsInFlow);
} else {
sectionIndex = 0;
index = -1;
}
content.type = atEnd ? core::Content::END : core::Content::START;
}
void OOFAwareWidget::OOFAwareWidgetIterator::setValues (int sectionIndex,
int index)
{
this->sectionIndex = sectionIndex;
this->index = index;
if (sectionIndex < 0 || index < 0)
content.type = core::Content::START;
else if (sectionIndex >= NUM_SECTIONS || index >= numParts (sectionIndex))
content.type = core::Content::END;
else
getPart (sectionIndex, index, &content);
}
int OOFAwareWidget::OOFAwareWidgetIterator::numParts (int sectionIndex,
int numContentsInFlow)
{
DBG_OBJ_ENTER_O ("iterator", 0, getWidget(), "numParts", "%d, %d",
sectionIndex, numContentsInFlow);
OOFAwareWidget *widget = (OOFAwareWidget*)getWidget();
int result;
if (sectionIndex < 0 || sectionIndex > NUM_SECTIONS) {
DBG_OBJ_MARKF_O("iterator", 0, getWidget(), "invalid sectionIndex %d",
sectionIndex);
result = 0;
} else if (sectionIndex == 0)
result = numContentsInFlow == -1 ?
this->numContentsInFlow () : numContentsInFlow;
else
result = widget->outOfFlowMgr[sectionIndex - 1] ?
widget->outOfFlowMgr[sectionIndex - 1]->getNumWidgets () : 0;
DBG_OBJ_MSGF_O ("iterator", 1, getWidget(), "=> %d", result);
DBG_OBJ_LEAVE_O (getWidget());
return result;
}
void OOFAwareWidget::OOFAwareWidgetIterator::getPart (int sectionIndex,
int index,
Content *content)
{
OOFAwareWidget *widget = (OOFAwareWidget*)getWidget();
if (sectionIndex == 0)
getContentInFlow (index, content);
else {
content->type = Content::WIDGET_OOF_CONT;
content->widget =
widget->outOfFlowMgr[sectionIndex - 1]->getWidget (index);
}
}
void OOFAwareWidget::OOFAwareWidgetIterator::intoStringBuffer (StringBuffer *sb)
{
Iterator::intoStringBuffer (sb);
sb->append (", sectionIndex = ");
sb->appendInt (sectionIndex);
sb->append (", index = ");
sb->appendInt (index);
}
int OOFAwareWidget::OOFAwareWidgetIterator::compareTo (Comparable *other)
{
OOFAwareWidgetIterator *otherTI = (OOFAwareWidgetIterator*)other;
if (sectionIndex != otherTI->sectionIndex)
return sectionIndex - otherTI->sectionIndex;
else
return index - otherTI->index;
}
bool OOFAwareWidget::OOFAwareWidgetIterator::next ()
{
DBG_OBJ_ENTER0_O ("iterator", 0, getWidget (),
"OOFAwareWidgetIterator/next");
DBG_IF_RTFL {
StringBuffer sb;
intoStringBuffer (&sb);
DBG_OBJ_MSGF_O ("iterator", 1, getWidget (),
"initial value: %s; sectionIndex = %d, index = %d",
sb.getChars (), sectionIndex, index);
}
bool found = false;
if (content.type != Content::END) {
while (!found) {
++index;
if (sectionIndex >= 0 && sectionIndex < NUM_SECTIONS &&
index >= 0 && index < numParts (sectionIndex)) {
getPart (sectionIndex, index, &content);
} else {
while (++sectionIndex < NUM_SECTIONS &&
numParts (sectionIndex) == 0) ;
if (sectionIndex >= NUM_SECTIONS) {
content.type = Content::END;
break;
} else {
index = -1;
continue;
}
}
found = (content.type & getMask());
}
}
DBG_IF_RTFL {
StringBuffer sb;
intoStringBuffer (&sb);
DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "final value: %s",
sb.getChars ());
}
DBG_OBJ_LEAVE_VAL_O (getWidget (), "%s", boolToStr (found));
return found;
}
bool OOFAwareWidget::OOFAwareWidgetIterator::prev ()
{
DBG_OBJ_ENTER0_O ("iterator", 0, getWidget (),
"OOFAwareWidgetIterator/prev");
DBG_IF_RTFL {
StringBuffer sb;
intoStringBuffer (&sb);
DBG_OBJ_MSGF_O ("iterator", 1, getWidget (),
"initial value: %s; sectionIndex = %d, index = %d",
sb.getChars (), sectionIndex, index);
}
bool found = false;
if (content.type != Content::START) {
while (!found) {
index--;
if (sectionIndex >= 0 && sectionIndex < NUM_SECTIONS &&
index >= 0 && index < numParts (sectionIndex)) {
getPart (sectionIndex, index, &content);
} else {
while (--sectionIndex >= 0 && numParts (sectionIndex) == 0) ;
if (sectionIndex < 0) {
content.type = Content::START;
break;
} else {
index = numParts (sectionIndex);
continue;
}
}
found = (content.type & getMask());
}
}
DBG_IF_RTFL {
StringBuffer sb;
intoStringBuffer (&sb);
DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "final value: %s",
sb.getChars ());
}
DBG_OBJ_LEAVE_VAL_O (getWidget (), "%s", boolToStr (found));
return found;
}
void OOFAwareWidget::OOFAwareWidgetIterator::highlightOOF (int start, int end,
HighlightLayer layer)
{
// TODO What about OOF widgets?
}
void OOFAwareWidget::OOFAwareWidgetIterator::unhighlightOOF (int direction,
HighlightLayer
layer)
{
// TODO What about OOF widgets?
}
void OOFAwareWidget::OOFAwareWidgetIterator::getAllocationOOF (int start,
int end,
Allocation
*allocation)
{
// TODO Consider start and end?
OOFAwareWidget *widget = (OOFAwareWidget*)getWidget();
*allocation = *(widget->outOfFlowMgr[sectionIndex - 1]
->getWidget(index)->getAllocation());
}
} // namespace oof
} // namespace dw

1385
dw/ooffloatsmgr.cc Normal file

File diff suppressed because it is too large Load Diff

290
dw/ooffloatsmgr.hh Normal file
View File

@ -0,0 +1,290 @@
#ifndef __DW_OOFFLOATSMGR_HH__
#define __DW_OOFFLOATSMGR_HH__
#include "outofflowmgr.hh"
namespace dw {
namespace oof {
/**
* \brief OutOfFlowMgr implementation dealing with floats.
*
* Note: The identifiers and comments of this class still refer to
* "Textblock" instead of "OOFAwareWidget"; should be cleaned up some
* day. (OTOH, these widgets are always textblocks.)
*/
class OOFFloatsMgr: public OutOfFlowMgr
{
friend class WidgetInfo;
private:
enum Side { LEFT, RIGHT };
OOFAwareWidget *container;
int oofmIndex;
core::Allocation containerAllocation;
class WidgetInfo: public lout::object::Object
{
private:
OOFFloatsMgr *oofm;
core::Widget *widget;
protected:
OOFFloatsMgr *getOOFFloatsMgr () { return oofm; }
public:
WidgetInfo (OOFFloatsMgr *oofm, core::Widget *widget);
inline core::Widget *getWidget () { return widget; }
};
class Float: public WidgetInfo
{
public:
class ComparePosition: public lout::object::Comparator
{
public:
int compare (Object *o1, Object *o2);
};
class CompareSideSpanningIndex: public lout::object::Comparator
{
public:
int compare (Object *o1, Object *o2);
};
class CompareGBAndExtIndex: public lout::object::Comparator
{
private:
OOFFloatsMgr *oofm;
public:
CompareGBAndExtIndex (OOFFloatsMgr *oofm)
{ this->oofm = oofm; }
int compare(Object *o1, Object *o2);
};
OOFAwareWidget *generator;
int externalIndex;
int index; // TODO Needed after SRDOP?
int yReq, yReal; // relative to container
int sideSpanningIndex;
core::Requisition size;
bool dirty;
Float (OOFFloatsMgr *oofm, core::Widget *widget,
OOFAwareWidget *generatingBlock, int externalIndex);
void intoStringBuffer(lout::misc::StringBuffer *sb);
bool covers (int y, int h);
};
/**
* This list is kept sorted.
*
* To prevent accessing methods of the base class in an
* uncontrolled way, the inheritance is private, not public; this
* means that all methods must be delegated (see iterator(), size()
* etc. below.)
*
* TODO Update comment: still sorted, but ...
*
* More: add() and change() may check order again.
*/
class SortedFloatsVector: private lout::container::typed::Vector<Float>
{
private:
OOFFloatsMgr *oofm;
Side side;
public:
inline SortedFloatsVector (OOFFloatsMgr *oofm, Side side,
bool ownerOfObjects) :
lout::container::typed::Vector<Float> (1, ownerOfObjects)
{ this->oofm = oofm; this->side = side; }
int findFloatIndex (OOFAwareWidget *lastGB, int lastExtIndex);
int find (int y, int start, int end);
int findFirst (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex,
int *lastReturn);
int findLastBeforeSideSpanningIndex (int sideSpanningIndex);
void put (Float *vloat);
inline lout::container::typed::Iterator<Float> iterator()
{ return lout::container::typed::Vector<Float>::iterator (); }
inline int size ()
{ return lout::container::typed::Vector<Float>::size (); }
inline Float *get (int pos)
{ return lout::container::typed::Vector<Float>::get (pos); }
inline void clear ()
{ lout::container::typed::Vector<Float>::clear (); }
};
class TBInfo: public WidgetInfo
{
public:
class ComparePosition: public lout::object::Comparator
{
private:
int oofmIndex;
public:
inline ComparePosition (int oofmIndex) { this->oofmIndex = oofmIndex; }
int compare (Object *o1, Object *o2);
};
int index; // position within "tbInfos"
int y; // used for sorting
TBInfo *parent;
int parentExtIndex;
// These two lists store all floats of a generator, in the order
// in which they are defined. Used for optimization
lout::container::typed::Vector<Float> *leftFloats, *rightFloats;
TBInfo (OOFFloatsMgr *oofm, OOFAwareWidget *textblock,
TBInfo *parent, int parentExtIndex);
~TBInfo ();
inline OOFAwareWidget *getOOFAwareWidget ()
{ return (OOFAwareWidget*)getWidget (); }
};
SortedFloatsVector *leftFloats, *rightFloats;
lout::container::typed::HashTable<lout::object::TypedPointer
<dw::core::Widget>, Float> *floatsByWidget;
lout::container::typed::Vector<TBInfo> *tbInfos;
lout::container::typed::HashTable<lout::object::TypedPointer<OOFAwareWidget>,
TBInfo> *tbInfosByOOFAwareWidget;
int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark;
bool SizeChanged;
void moveExternalIndices (lout::container::typed::Vector<Float> *list,
int oldStartIndex, int diff);
Float *findFloatByWidget (core::Widget *widget);
void updateGenerators (Float *vloat);
int findTBInfo (int y);
void sizeAllocateFloats (Side side);
int getGBWidthForAllocation (Float *vloat);
int calcFloatX (Float *vloat);
void drawFloats (SortedFloatsVector *list, core::View *view,
core::Rectangle *area, core::DrawingContext *context);
core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y,
core::GettingWidgetAtPointContext
*context);
bool collidesV (Float *vloat, Float *other, int *yReal);
bool collidesH (Float *vloat, Float *other);
void getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame,
SortedFloatsVector **listOpp, Side *side);
void getFloatsSize (core::Requisition *cbReq, Side side, int *width,
int *height);
void getFloatsExtremes (core::Extremes *cbExtr, Side side, int *minWidth,
int *maxWidth);
TBInfo *getOOFAwareWidget (OOFAwareWidget *widget);
TBInfo *getOOFAwareWidgetWhenRegistered (OOFAwareWidget *widget);
inline bool isOOFAwareWidgetRegistered (OOFAwareWidget *widget)
{ return getOOFAwareWidgetWhenRegistered (widget) != NULL; }
int getBorder (Side side, int y, int h, OOFAwareWidget *lastGB,
int lastExtIndex);
bool hasFloat (Side side, int y, int h, OOFAwareWidget *lastGB,
int lastExtIndex);
int getFloatHeight (Side side, int y, int h, OOFAwareWidget *lastGB,
int lastExtIndex);
int getClearPosition (OOFAwareWidget *widget, Side side);
void ensureFloatSize (Float *vloat);
inline static int createSubRefLeftFloat (int index) { return index << 1; }
inline static int createSubRefRightFloat (int index)
{ return (index << 1) | 1; }
inline static bool isSubRefLeftFloat (int ref)
{ return ref != -1 && (ref & 1) == 0; }
inline static bool isSubRefRightFloat (int ref)
{ return ref != -1 && (ref & 1) == 1; }
inline static int getFloatIndexFromSubRef (int ref)
{ return ref == -1 ? ref : (ref >> 1); }
public:
OOFFloatsMgr (OOFAwareWidget *container, int oofmIndex);
~OOFFloatsMgr ();
void sizeAllocateStart (OOFAwareWidget *caller,
core::Allocation *allocation);
void sizeAllocateEnd (OOFAwareWidget *caller);
void containerSizeChangedForChildren ();
void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
void markSizeChange (int ref);
void markExtremesChange (int ref);
core::Widget *getWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext *context);
void addWidgetInFlow (OOFAwareWidget *textblock, OOFAwareWidget *parentBlock,
int externalIndex);
int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generatingBlock,
int externalIndex);
void calcWidgetRefSize (core::Widget *widget,core::Requisition *size);
void moveExternalIndices (OOFAwareWidget *generatingBlock, int oldStartIndex,
int diff);
void tellPosition1 (core::Widget *widget, int x, int y);
void tellPosition2 (core::Widget *widget, int x, int y);
void tellIncompletePosition1 (core::Widget *generator, core::Widget *widget,
int x, int y);
void tellIncompletePosition2 (core::Widget *generator, core::Widget *widget,
int x, int y);
void getSize (core::Requisition *cbReq, int *oofWidth, int *oofHeight);
bool containerMustAdjustExtraSpace ();
void getExtremes (core::Extremes *cbExtr,
int *oofMinWidth, int *oofMaxWidth);
int getLeftBorder (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex);
int getRightBorder (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex);
bool hasFloatLeft (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex);
bool hasFloatRight (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex);
int getLeftFloatHeight (int y, int h, OOFAwareWidget *lastGB,
int lastExtIndex);
int getRightFloatHeight (int y, int h, OOFAwareWidget *lastGB,
int lastExtIndex);
bool affectsLeftBorder (core::Widget *widget);
bool affectsRightBorder (core::Widget *widget);
bool mayAffectBordersAtAll ();
int getClearPosition (OOFAwareWidget *textblock);
bool dealingWithSizeOfChild (core::Widget *child);
int getAvailWidthOfChild (core::Widget *child, bool forceValue);
int getAvailHeightOfChild (core::Widget *child, bool forceValue);
int getNumWidgets ();
core::Widget *getWidget (int i);
};
} // namespace oof
} // namespace dw
#endif // __DW_OOFFLOATSMGR_HH__

427
dw/oofposabslikemgr.cc Normal file
View File

@ -0,0 +1,427 @@
/*
* Dillo Widget
*
* Copyright 2015 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.
*
* 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 "oofposabslikemgr.hh"
using namespace dw::core;
using namespace lout::misc;
namespace dw {
namespace oof {
OOFPosAbsLikeMgr::OOFPosAbsLikeMgr (OOFAwareWidget *container) :
OOFPositionedMgr (container)
{
DBG_OBJ_CREATE ("dw::oof::OOFPosAbsLikeMgr");
}
OOFPosAbsLikeMgr::~OOFPosAbsLikeMgr ()
{
DBG_OBJ_DELETE ();
}
void OOFPosAbsLikeMgr::calcWidgetRefSize (Widget *widget, Requisition *size)
{
size->width = size->ascent = size->descent = 0;
}
void OOFPosAbsLikeMgr::sizeAllocateChildren ()
{
DBG_OBJ_ENTER0 ("resize.oofm", 0, "sizeAllocateChildren");
int refWidth = container->getAvailWidth (true) - containerBoxDiffWidth ();
int refHeight = container->getAvailHeight (true) - containerBoxDiffHeight ();
for (int i = 0; i < children->size(); i++) {
Child *child = children->get (i);
int x, y, width, ascent, descent;
calcPosAndSizeChildOfChild (child, refWidth, refHeight, &x, &y, &width,
&ascent, &descent);
Allocation childAllocation;
childAllocation.x = containerAllocation.x + x + containerBoxOffsetX ();
childAllocation.y = containerAllocation.y + y + containerBoxOffsetY ();
childAllocation.width = width;
childAllocation.ascent = ascent;
childAllocation.descent = descent;
child->widget->sizeAllocate (&childAllocation);
}
DBG_OBJ_LEAVE ();
}
void OOFPosAbsLikeMgr::getSize (Requisition *containerReq, int *oofWidth,
int *oofHeight)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "getSize", "%d * (%d + %d)",
containerReq->width, containerReq->ascent,
containerReq->descent);
*oofWidth = *oofHeight = 0;
int refWidth = container->getAvailWidth (true);
int refHeight = container->getAvailHeight (true);
for (int i = 0; i < children->size(); i++) {
Child *child = children->get(i);
// Children whose position cannot be determined will be
// considered later in sizeAllocateEnd.
if (posXDefined (child) && posYDefined (child)) {
int x, y, width, ascent, descent;
calcPosAndSizeChildOfChild (child, refWidth, refHeight, &x, &y, &width,
&ascent, &descent);
*oofWidth = max (*oofWidth, x + width) + containerBoxDiffWidth ();
*oofHeight =
max (*oofHeight, y + ascent + descent) + containerBoxDiffHeight ();
child->consideredForSize = true;
} else
child->consideredForSize = false;
}
DBG_OBJ_LEAVE_VAL ("%d * %d", *oofWidth, *oofHeight);
}
void OOFPosAbsLikeMgr::getExtremes (Extremes *containerExtr, int *oofMinWidth,
int *oofMaxWidth)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "getExtremes", "(%d / %d), ...",
containerExtr->minWidth, containerExtr->maxWidth);
*oofMinWidth = *oofMaxWidth = 0;
for (int i = 0; i < children->size(); i++) {
Child *child = children->get(i);
// Children whose position cannot be determined will be
// considered later in sizeAllocateEnd.
if (posXDefined (child)) {
int x, width;
Extremes childExtr;
child->widget->getExtremes (&childExtr);
// Here, we put the extremes of the container in relation to
// the extremes of the child, as sizes are put in relation
// for calculating the size. In one case, the allocation is
// used: when neither "left" nor "right" is set, and so the
// position told by the generator is used.
//
// If you look at the Textblock widget, you'll find that this
// is always boxOffsetX(), and the horizontal position of a
// textblock within its parent is also constant; so this is
// not a problem.
//
// (TODO What about a table cell within a table?)
calcHPosAndSizeChildOfChild (child, containerExtr->minWidth,
childExtr.minWidth, &x, &width);
*oofMinWidth = max (*oofMinWidth, x + width);
calcHPosAndSizeChildOfChild (child, containerExtr->maxWidth,
childExtr.maxWidth, &x, &width);
*oofMaxWidth = max (*oofMaxWidth, x + width);
child->consideredForExtremes = true;
} else
child->consideredForExtremes = false;
}
*oofMinWidth += containerBoxDiffWidth ();
*oofMaxWidth += containerBoxDiffWidth ();
DBG_OBJ_MSGF ("resize.oofm", 0, "=> %d / %d", *oofMinWidth, *oofMaxWidth);
DBG_OBJ_LEAVE ();
}
int OOFPosAbsLikeMgr::getAvailWidthOfChild (Widget *child, bool forceValue)
{
DBG_OBJ_ENTER ("resize.oofm", 0,
"OOFPositionedMgr/getAvailWidthOfChild", "%p, %s",
child, forceValue ? "true" : "false");
int width;
if (child->getStyle()->width == style::LENGTH_AUTO &&
child->getStyle()->minWidth == style::LENGTH_AUTO &&
child->getStyle()->maxWidth == style::LENGTH_AUTO) {
// TODO This should (perhaps?) only used when 'width' is undefined.
// TODO Is "boxDiffWidth()" correct here?
DBG_OBJ_MSG ("resize.oofm", 1, "no specification");
if (forceValue) {
int availWidth = container->getAvailWidth (true), left, right;
// Regard undefined values as 0:
if (!getPosLeft (child, availWidth, &left)) left = 0;
if (!getPosRight (child, availWidth, &right)) right = 0;
width = max (availWidth - containerBoxDiffWidth () - left - right, 0);
} else
width = -1;
} else {
if (forceValue) {
int availWidth = container->getAvailWidth (true);
child->calcFinalWidth (child->getStyle(),
availWidth - containerBoxDiffWidth (), NULL,
0, true, &width);
} else
width = -1;
}
if (width != -1)
width = max (width, child->getMinWidth (NULL, forceValue));
DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", width);
DBG_OBJ_LEAVE ();
return width;
}
int OOFPosAbsLikeMgr::getAvailHeightOfChild (Widget *child, bool forceValue)
{
DBG_OBJ_ENTER ("resize.oofm", 0,
"OOFPositionedMgr/getAvailHeightOfChild", "%p, %s",
child, forceValue ? "true" : "false");
int height;
if (child->getStyle()->height == style::LENGTH_AUTO &&
child->getStyle()->minHeight == style::LENGTH_AUTO &&
child->getStyle()->maxHeight == style::LENGTH_AUTO) {
// TODO This should (perhaps?) only used when 'height' is undefined.
// TODO Is "boxDiffHeight()" correct here?
DBG_OBJ_MSG ("resize.oofm", 1, "no specification");
if (forceValue) {
int availHeight = container->getAvailHeight (true), top, bottom;
// Regard undefined values as 0:
if (!getPosTop (child, availHeight, &top)) top = 0;
if (!getPosBottom (child, availHeight, &bottom)) bottom = 0;
height =
max (availHeight - containerBoxDiffHeight () - top - bottom, 0);
} else
height = -1;
} else {
if (forceValue) {
int availHeight = container->getAvailHeight (true);
height = child->calcHeight (child->getStyle()->height, true,
availHeight - containerBoxDiffHeight (),
NULL, true);
} else
height = -1;
}
DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", height);
DBG_OBJ_LEAVE ();
return height;
}
bool OOFPosAbsLikeMgr::posXAbsolute (Child *child)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "posXAbsolute", "[%p]", child->widget);
bool b =
(style::isAbsLength (child->widget->getStyle()->left) ||
style::isPerLength (child->widget->getStyle()->left)) &&
(style::isAbsLength (child->widget->getStyle()->right) ||
style::isPerLength (child->widget->getStyle()->right));
DBG_OBJ_LEAVE_VAL ("%s", boolToStr (b));
return b;
}
bool OOFPosAbsLikeMgr::posYAbsolute (Child *child)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "posYAbsolute", "[%p]", child->widget);
bool b =
(style::isAbsLength (child->widget->getStyle()->top) ||
style::isPerLength (child->widget->getStyle()->top)) &&
(style::isAbsLength (child->widget->getStyle()->bottom) ||
style::isPerLength (child->widget->getStyle()->bottom));
DBG_OBJ_LEAVE_VAL ("%s", boolToStr (b));
return b;
}
void OOFPosAbsLikeMgr::calcPosAndSizeChildOfChild (Child *child, int refWidth,
int refHeight, int *xPtr,
int *yPtr, int *widthPtr,
int *ascentPtr,
int *descentPtr)
{
// *xPtr and *yPtr refer to reference area; caller must adjust them.
DBG_OBJ_ENTER ("resize.oofm", 0, "calcPosAndSizeChildOfChild",
"[%p], %d, %d, ...", child->widget, refWidth, refHeight);
// TODO (i) Consider {min|max}-{width|height}. (ii) Height is always
// apportioned to descent (ascent is preserved), which makes sense
// when the children are textblocks. (iii) Consider minimal width
// (getMinWidth)?
Requisition childRequisition;
child->widget->sizeRequest (&childRequisition);
calcHPosAndSizeChildOfChild (child, refWidth, childRequisition.width,
xPtr, widthPtr);
calcVPosAndSizeChildOfChild (child, refHeight, childRequisition.ascent,
childRequisition.descent, yPtr, ascentPtr,
descentPtr);
DBG_OBJ_LEAVE ();
}
void OOFPosAbsLikeMgr::calcHPosAndSizeChildOfChild (Child *child, int refWidth,
int origChildWidth,
int *xPtr, int *widthPtr)
{
assert (refWidth != -1 || (xPtr == NULL && widthPtr == NULL));
int width;
bool widthDefined;
if (style::isAbsLength (child->widget->getStyle()->width)) {
DBG_OBJ_MSGF ("resize.oofm", 1, "absolute width: %dpx",
style::absLengthVal (child->widget->getStyle()->width));
width = style::absLengthVal (child->widget->getStyle()->width)
+ child->widget->boxDiffWidth ();
widthDefined = true;
} else if (style::isPerLength (child->widget->getStyle()->width)) {
DBG_OBJ_MSGF ("resize.oofm", 1, "percentage width: %g%%",
100 * style::perLengthVal_useThisOnlyForDebugging
(child->widget->getStyle()->width));
width = style::multiplyWithPerLength (refWidth,
child->widget->getStyle()->width)
+ child->widget->boxDiffWidth ();
widthDefined = true;
} else {
DBG_OBJ_MSG ("resize.oofm", 1, "width not specified");
width = origChildWidth;
widthDefined = false;
}
int left, right;
bool leftDefined = getPosLeft (child->widget, refWidth, &left),
rightDefined = getPosRight (child->widget, refWidth, &right);
DBG_OBJ_MSGF ("resize.oofm", 1,
"=> left = %d, right = %d, width = %d (defined: %s)",
left, right, width, widthDefined ? "true" : "false");
if (xPtr) {
if (!leftDefined && !rightDefined)
*xPtr = generatorPosX (child) + child->x;
else {
if (!leftDefined && rightDefined)
*xPtr = refWidth - width - right - containerBoxRestWidth ();
else if (leftDefined && !rightDefined)
*xPtr = left + containerBoxOffsetX ();
else {
*xPtr = left;
if (!widthDefined) {
width = refWidth - (left + right + containerBoxDiffWidth ());
DBG_OBJ_MSGF ("resize.oofm", 0, "=> width (corrected) = %d",
width);
}
}
}
DBG_OBJ_MSGF ("resize.oofm", 0, "=> x = %d", *xPtr);
}
if (widthPtr)
*widthPtr = width;
}
void OOFPosAbsLikeMgr::calcVPosAndSizeChildOfChild (Child *child, int refHeight,
int origChildAscent,
int origChildDescent,
int *yPtr, int *ascentPtr,
int *descentPtr)
{
assert (refHeight != -1 ||
(yPtr == NULL && ascentPtr == NULL && descentPtr == NULL));
int ascent = origChildAscent, descent = origChildDescent;
bool heightDefined;
if (style::isAbsLength (child->widget->getStyle()->height)) {
DBG_OBJ_MSGF ("resize.oofm", 1, "absolute height: %dpx",
style::absLengthVal (child->widget->getStyle()->height));
int height = style::absLengthVal (child->widget->getStyle()->height)
+ child->widget->boxDiffHeight ();
splitHeightPreserveAscent (height, &ascent, &descent);
heightDefined = true;
} else if (style::isPerLength (child->widget->getStyle()->height)) {
DBG_OBJ_MSGF ("resize.oofm", 1, "percentage height: %g%%",
100 * style::perLengthVal_useThisOnlyForDebugging
(child->widget->getStyle()->height));
int height =
style::multiplyWithPerLength (refHeight,
child->widget->getStyle()->height)
+ child->widget->boxDiffHeight ();
splitHeightPreserveAscent (height, &ascent, &descent);
heightDefined = true;
} else {
DBG_OBJ_MSG ("resize.oofm", 1, "height not specified");
heightDefined = false;
}
int top, bottom;
bool topDefined = getPosTop (child->widget, refHeight, &top),
bottomDefined = getPosBottom (child->widget, refHeight, &bottom);
DBG_OBJ_MSGF ("resize.oofm", 1,
"=> top = %d, bottom = %d, height = %d + %d (defined: %s)",
top, bottom, ascent, descent,
heightDefined ? "true" : "false");
if (yPtr) {
if (!topDefined && !bottomDefined)
*yPtr = generatorPosY (child) + child->y;
else {
if (!topDefined && bottomDefined)
*yPtr = refHeight - (ascent + descent) - bottom
- containerBoxDiffHeight ();
else if (topDefined && !bottomDefined)
*yPtr = top + containerBoxOffsetY ();
else {
*yPtr = top;
if (!heightDefined) {
int height =
refHeight - (top + bottom + containerBoxDiffHeight ());
splitHeightPreserveAscent (height, &ascent, &descent);
DBG_OBJ_MSGF ("resize.oofm", 0,
"=> ascent + descent (corrected) = %d + %d",
ascent, descent);
}
}
}
DBG_OBJ_MSGF ("resize.oofm", 0, "=> y = %d", *yPtr);
}
if (ascentPtr)
*ascentPtr = ascent;
if (descentPtr)
*descentPtr = descent;
}
} // namespace oof
} // namespace dw

61
dw/oofposabslikemgr.hh Normal file
View File

@ -0,0 +1,61 @@
#ifndef __DW_OOFPOSABSLIKEMGR_HH__
#define __DW_OOFPOSABSLIKEMGR_HH__
#include "oofpositionedmgr.hh"
namespace dw {
namespace oof {
class OOFPosAbsLikeMgr: public OOFPositionedMgr
{
protected:
virtual int containerBoxOffsetX () = 0;
virtual int containerBoxOffsetY () = 0;
virtual int containerBoxRestWidth () = 0;
virtual int containerBoxRestHeight () = 0;
inline int containerBoxDiffWidth ()
{ return containerBoxOffsetX () + containerBoxRestWidth (); }
inline int containerBoxDiffHeight ()
{ return containerBoxOffsetY () + containerBoxRestHeight (); }
bool haveExtremesChanged ();
void sizeAllocateChildren ();
bool posXAbsolute (Child *child);
bool posYAbsolute (Child *child);
void calcPosAndSizeChildOfChild (Child *child, int refWidth, int refHeight,
int *xPtr, int *yPtr, int *widthPtr,
int *ascentPtr, int *descentPtr);
void calcHPosAndSizeChildOfChild (Child *child, int refWidth,
int origChildWidth, int *xPtr,
int *widthPtr);
void calcVPosAndSizeChildOfChild (Child *child, int refHeight,
int origChildAscent, int origChildDescent,
int *yPtr, int *ascentPtr,
int *descentPtr);
public:
OOFPosAbsLikeMgr (OOFAwareWidget *container);
~OOFPosAbsLikeMgr ();
void calcWidgetRefSize (core::Widget *widget, core::Requisition *size);
void getSize (core::Requisition *containerReq, int *oofWidth,
int *oofHeight);
void getExtremes (core::Extremes *containerExtr,
int *oofMinWidth, int *oofMaxWidth);
int getAvailWidthOfChild (core::Widget *child, bool forceValue);
int getAvailHeightOfChild (core::Widget *child, bool forceValue);
};
} // namespace oof
} // namespace dw
#endif // __DW_OOFPOSABSLIKEMGR_HH__

68
dw/oofposabsmgr.cc Normal file
View File

@ -0,0 +1,68 @@
/*
* Dillo Widget
*
* Copyright 2014 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.
*
* 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 "oofposabsmgr.hh"
namespace dw {
namespace oof {
OOFPosAbsMgr::OOFPosAbsMgr (OOFAwareWidget *container) :
OOFPosAbsLikeMgr (container)
{
DBG_OBJ_CREATE ("dw::oof::OOFPosAbsMgr");
}
OOFPosAbsMgr::~OOFPosAbsMgr ()
{
DBG_OBJ_DELETE ();
}
// Comment for all containerBox* implementations: for the toplevel
// widget, assume margin = border = 0 (should perhaps set so when
// widgets are constructed), so that the padding area is actually the
// allocation.
int OOFPosAbsMgr::containerBoxOffsetX ()
{
return container->getParent () ?
container->boxOffsetX () - container->getStyle()->padding.left : 0;
}
int OOFPosAbsMgr::containerBoxOffsetY ()
{
return container->getParent () ?
container->boxOffsetY () - container->getStyle()->padding.top : 0;
}
int OOFPosAbsMgr::containerBoxRestWidth ()
{
return container->getParent () ?
container->boxRestWidth () - container->getStyle()->padding.right : 0;
}
int OOFPosAbsMgr::containerBoxRestHeight ()
{
return container->getParent () ?
container->boxRestHeight () - container->getStyle()->padding.bottom : 0;
}
} // namespace oof
} // namespace dw

27
dw/oofposabsmgr.hh Normal file
View File

@ -0,0 +1,27 @@
#ifndef __DW_OOFPOSABSMGR_HH__
#define __DW_OOFPOSABSMGR_HH__
#include "oofposabslikemgr.hh"
namespace dw {
namespace oof {
class OOFPosAbsMgr: public OOFPosAbsLikeMgr
{
protected:
int containerBoxOffsetX ();
int containerBoxOffsetY ();
int containerBoxRestWidth ();
int containerBoxRestHeight ();
public:
OOFPosAbsMgr (OOFAwareWidget *container);
~OOFPosAbsMgr ();
};
} // namespace oof
} // namespace dw
#endif // __DW_OOFPOSABSMGR_HH__

59
dw/oofposfixedmgr.cc Normal file
View File

@ -0,0 +1,59 @@
/*
* Dillo Widget
*
* Copyright 2014 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.
*
* 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 "oofposfixedmgr.hh"
namespace dw {
namespace oof {
OOFPosFixedMgr::OOFPosFixedMgr (OOFAwareWidget *container) :
OOFPosAbsLikeMgr (container)
{
DBG_OBJ_CREATE ("dw::oof::OOFPosFixedMgr");
}
OOFPosFixedMgr::~OOFPosFixedMgr ()
{
DBG_OBJ_DELETE ();
}
int OOFPosFixedMgr::containerBoxOffsetX ()
{
return 0;
}
int OOFPosFixedMgr::containerBoxOffsetY ()
{
return 0;
}
int OOFPosFixedMgr::containerBoxRestWidth ()
{
return 0;
}
int OOFPosFixedMgr::containerBoxRestHeight ()
{
return 0;
}
} // namespace oof
} // namespace dw

27
dw/oofposfixedmgr.hh Normal file
View File

@ -0,0 +1,27 @@
#ifndef __DW_OOFPOSFIXEDMGR_HH__
#define __DW_OOFPOSFIXEDMGR_HH__
#include "oofposabslikemgr.hh"
namespace dw {
namespace oof {
class OOFPosFixedMgr: public OOFPosAbsLikeMgr
{
protected:
int containerBoxOffsetX ();
int containerBoxOffsetY ();
int containerBoxRestWidth ();
int containerBoxRestHeight ();
public:
OOFPosFixedMgr (OOFAwareWidget *container);
~OOFPosFixedMgr ();
};
} // namespace oof
} // namespace dw
#endif // __DW_OOFPOSFIXEDMGR_HH__

405
dw/oofpositionedmgr.cc Normal file
View File

@ -0,0 +1,405 @@
/*
* Dillo Widget
*
* Copyright 2013-2014 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.
*
* 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 "oofpositionedmgr.hh"
#include "../lout/debug.hh"
using namespace lout::object;
using namespace lout::container::typed;
using namespace lout::misc;
using namespace dw::core;
using namespace dw::core::style;
namespace dw {
namespace oof {
OOFPositionedMgr::Child::Child (core::Widget *widget, OOFAwareWidget *generator,
int externalIndex)
{
this->widget = widget;
this->generator = generator;
this->externalIndex = externalIndex;
x = y = 0;
// Initially, this child does not actually have been considered,
// but since adding a new element will force a size/extremes
// calculation, this is equivalent.
consideredForSize = consideredForExtremes = true;
}
OOFPositionedMgr::OOFPositionedMgr (OOFAwareWidget *container)
{
DBG_OBJ_CREATE ("dw::oof::OOFPositionedMgr");
this->container = (OOFAwareWidget*)container;
children = new Vector<Child> (1, false);
childrenByWidget = new HashTable<TypedPointer<Widget>, Child> (true, true);
if(container->wasAllocated()) {
containerAllocationState = WAS_ALLOCATED;
containerAllocation = *(container->getAllocation());
} else {
containerAllocationState = NOT_ALLOCATED;
containerAllocation.x = -1;
containerAllocation.y = -1;
containerAllocation.width = 1;
containerAllocation.ascent = 1;
containerAllocation.descent = 0;
}
DBG_OBJ_SET_NUM ("children.size", children->size());
}
OOFPositionedMgr::~OOFPositionedMgr ()
{
delete children;
delete childrenByWidget;
DBG_OBJ_DELETE ();
}
void OOFPositionedMgr::sizeAllocateStart (OOFAwareWidget *caller,
Allocation *allocation)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateStart",
"%p, (%d, %d, %d * (%d + %d))",
caller, allocation->x, allocation->y, allocation->width,
allocation->ascent, allocation->descent);
if (caller == container) {
if (containerAllocationState == NOT_ALLOCATED)
containerAllocationState = IN_ALLOCATION;
containerAllocation = *allocation;
}
DBG_OBJ_LEAVE ();
}
void OOFPositionedMgr::sizeAllocateEnd (OOFAwareWidget *caller)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller);
if (caller == container) {
sizeAllocateChildren ();
bool extremesChanged = !allChildrenConsideredForExtremes ();
if (extremesChanged || doChildrenExceedContainer () ||
!allChildrenConsideredForSize ())
container->oofSizeChanged (extremesChanged);
containerAllocationState = WAS_ALLOCATED;
}
DBG_OBJ_LEAVE ();
}
bool OOFPositionedMgr::doChildrenExceedContainer ()
{
DBG_OBJ_ENTER0 ("resize.oofm", 0, "doChildrenExceedContainer");
// This method is called to determine whether the *requisition* of
// the container must be recalculated. So, we check the allocations
// of the children against the *requisition* of the container,
// which may (e. g. within tables) differ from the new allocation.
// (Generally, a widget may allocated at a different size.)
Requisition containerReq;
container->sizeRequest (&containerReq);
bool exceeds = false;
DBG_OBJ_MSG_START ();
for (int i = 0; i < children->size () && !exceeds; i++) {
Child *child = children->get (i);
Allocation *childAlloc = child->widget->getAllocation ();
DBG_OBJ_MSGF ("resize.oofm", 2,
"Does childAlloc = (%d, %d, %d * %d) exceed container "
"alloc+req = (%d, %d, %d * %d)?",
childAlloc->x, childAlloc->y, childAlloc->width,
childAlloc->ascent + childAlloc->descent,
containerAllocation.x, containerAllocation.y,
containerReq.width,
containerReq.ascent + containerReq.descent);
if (childAlloc->x + childAlloc->width
> containerAllocation.x + containerReq.width ||
childAlloc->y + childAlloc->ascent + childAlloc->descent
> containerAllocation.y +
containerReq.ascent + containerReq.descent) {
exceeds = true;
DBG_OBJ_MSG ("resize.oofm", 2, "Yes.");
} else
DBG_OBJ_MSG ("resize.oofm", 2, "No.");
}
DBG_OBJ_MSG_END ();
DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false");
DBG_OBJ_LEAVE ();
return exceeds;
}
void OOFPositionedMgr::containerSizeChangedForChildren ()
{
DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
for (int i = 0; i < children->size(); i++)
children->get(i)->widget->containerSizeChanged ();
DBG_OBJ_LEAVE ();
}
void OOFPositionedMgr::draw (View *view, Rectangle *area,
DrawingContext *context)
{
DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
area->x, area->y, area->width, area->height);
for (int i = 0; i < children->size(); i++) {
Child *child = children->get(i);
Rectangle childArea;
if (!context->hasWidgetBeenProcessedAsInterruption (child->widget) &&
!StackingContextMgr::handledByStackingContextMgr (child->widget) &&
child->widget->intersects (container, area, &childArea))
child->widget->draw (view, &childArea, context);
}
DBG_OBJ_LEAVE ();
}
void OOFPositionedMgr::addWidgetInFlow (OOFAwareWidget *widget,
OOFAwareWidget *parent,
int externalIndex)
{
}
int OOFPositionedMgr::addWidgetOOF (Widget *widget, OOFAwareWidget *generator,
int externalIndex)
{
DBG_OBJ_ENTER ("construct.oofm", 0, "addWidgetOOF", "%p, %p, %d",
widget, generator, externalIndex);
Child *child = new Child (widget, generator, externalIndex);
children->put (child);
childrenByWidget->put (new TypedPointer<Widget> (widget), child);
int subRef = children->size() - 1;
DBG_OBJ_SET_NUM ("children.size", children->size());
DBG_OBJ_ARRSET_PTR ("children", children->size() - 1, widget);
DBG_OBJ_SET_PTR_O (widget, "<Positioned>.generator", generator);
DBG_OBJ_SET_NUM_O (widget, "<Positioned>.externalIndex", externalIndex);
DBG_OBJ_MSGF ("construct.oofm", 1, "=> %d", subRef);
DBG_OBJ_LEAVE ();
return subRef;
}
void OOFPositionedMgr::moveExternalIndices (OOFAwareWidget *generator,
int oldStartIndex, int diff)
{
for (int i = 0; i < children->size (); i++) {
Child *child = children->get (i);
if (child->externalIndex >= oldStartIndex) {
child->externalIndex += diff;
DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.externalIndex",
child->externalIndex);
}
}
}
void OOFPositionedMgr::markSizeChange (int ref)
{
}
void OOFPositionedMgr::markExtremesChange (int ref)
{
}
Widget *OOFPositionedMgr::getWidgetAtPoint (int x, int y,
GettingWidgetAtPointContext
*context)
{
DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y);
Widget *widgetAtPoint = NULL;
for (int i = children->size() - 1; widgetAtPoint == NULL && i >= 0; i--) {
Widget *childWidget = children->get(i)->widget;
if (!context->hasWidgetBeenProcessedAsInterruption (childWidget) &&
!StackingContextMgr::handledByStackingContextMgr (childWidget))
widgetAtPoint = childWidget->getWidgetAtPoint (x, y, context);
}
DBG_OBJ_MSGF ("events", 0, "=> %p", widgetAtPoint);
DBG_OBJ_LEAVE ();
return widgetAtPoint;
}
void OOFPositionedMgr::tellPosition1 (Widget *widget, int x, int y)
{
}
void OOFPositionedMgr::tellPosition2 (Widget *widget, int x, int y)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "tellPosition2", "%p, %d, %d",
widget, x, y);
TypedPointer<Widget> key (widget);
Child *child = childrenByWidget->get (&key);
assert (child);
child->x = x;
child->y = y;
DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.x", x);
DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.y", y);
DBG_OBJ_LEAVE ();
}
void OOFPositionedMgr::tellIncompletePosition1 (Widget *generator,
Widget *widget, int x, int y)
{
// Nothing to do.
}
void OOFPositionedMgr::tellIncompletePosition2 (Widget *generator,
Widget *widget, int x, int y)
{
// TODO
}
bool OOFPositionedMgr::containerMustAdjustExtraSpace ()
{
return true;
}
int OOFPositionedMgr::getLeftBorder (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex)
{
return 0;
}
int OOFPositionedMgr::getRightBorder (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex)
{
return 0;
}
bool OOFPositionedMgr::hasFloatLeft (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex)
{
return false;
}
bool OOFPositionedMgr::hasFloatRight (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex)
{
return false;
}
int OOFPositionedMgr::getLeftFloatHeight (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex)
{
return 0;
}
int OOFPositionedMgr::getRightFloatHeight (int y, int h,
OOFAwareWidget *lastGen,
int lastExtIndex)
{
return 0;
}
int OOFPositionedMgr::getClearPosition (OOFAwareWidget *widget)
{
return 0;
}
bool OOFPositionedMgr::affectsLeftBorder (Widget *widget)
{
return false;
}
bool OOFPositionedMgr::affectsRightBorder (Widget *widget)
{
return false;
}
bool OOFPositionedMgr::mayAffectBordersAtAll ()
{
return false;
}
bool OOFPositionedMgr::dealingWithSizeOfChild (Widget *child)
{
return true;
}
int OOFPositionedMgr::getNumWidgets ()
{
return children->size();
}
Widget *OOFPositionedMgr::getWidget (int i)
{
return children->get(i)->widget;
}
bool OOFPositionedMgr::getPosBorder (style::Length cssValue, int refLength,
int *result)
{
if (style::isAbsLength (cssValue)) {
*result = style::absLengthVal (cssValue);
return true;
} else if (style::isPerLength (cssValue)) {
*result = style::multiplyWithPerLength (refLength, cssValue);
return true;
} else
// "false" means "undefined":
return false;
}
bool OOFPositionedMgr::allChildrenConsideredForSize ()
{
for (int i = 0; i < children->size(); i++)
if (!children->get(i)->consideredForSize)
return false;
return true;
}
bool OOFPositionedMgr::allChildrenConsideredForExtremes ()
{
for (int i = 0; i < children->size(); i++)
if (!children->get(i)->consideredForExtremes)
return false;
return true;
}
} // namespace oof
} // namespace dw

139
dw/oofpositionedmgr.hh Normal file
View File

@ -0,0 +1,139 @@
#ifndef __DW_OOFPOSITIONEDMGR_HH__
#define __DW_OOFPOSITIONEDMGR_HH__
#include "outofflowmgr.hh"
#include "oofawarewidget.hh"
namespace dw {
namespace oof {
class OOFPositionedMgr: public OutOfFlowMgr
{
protected:
class Child: public lout::object::Object
{
public:
core::Widget *widget;
OOFAwareWidget *generator;
int externalIndex, x, y;
bool consideredForSize, consideredForExtremes;
Child (core::Widget *widget, OOFAwareWidget *generator,
int externalIndex);
};
OOFAwareWidget *container;
core::Allocation containerAllocation;
enum { NOT_ALLOCATED, IN_ALLOCATION, WAS_ALLOCATED }
containerAllocationState;
lout::container::typed::Vector<Child> *children;
lout::container::typed::HashTable<lout::object::TypedPointer
<dw::core::Widget>,
Child> *childrenByWidget;
inline bool getPosLeft (core::Widget *child, int availWidth, int *result)
{ return getPosBorder (child->getStyle()->left, availWidth, result); }
inline bool getPosRight (core::Widget *child, int availWidth, int *result)
{ return getPosBorder (child->getStyle()->right, availWidth, result); }
inline bool getPosTop (core::Widget *child, int availHeight, int *result)
{ return getPosBorder (child->getStyle()->top, availHeight, result); }
inline bool getPosBottom (core::Widget *child, int availHeight, int *result)
{ return getPosBorder (child->getStyle()->bottom, availHeight, result); }
bool getPosBorder (core::style::Length cssValue, int refLength, int *result);
bool allChildrenConsideredForSize ();
bool allChildrenConsideredForExtremes ();
bool doChildrenExceedContainer ();
virtual void sizeAllocateChildren () = 0;
virtual bool posXAbsolute (Child *child) = 0;
virtual bool posYAbsolute (Child *child) = 0;
inline bool generatorPosDefined (Child *child) {
return child->generator == container ||
(containerAllocationState != NOT_ALLOCATED
&& child->generator->wasAllocated ());
}
inline int generatorPosX (Child *child) {
assert (generatorPosDefined (child));
return child->generator == container ? 0 :
child->generator->getAllocation()->x - containerAllocation.x;
}
inline int generatorPosY (Child *child) {
assert (generatorPosDefined (child));
return child->generator == container ? 0 :
child->generator->getAllocation()->y - containerAllocation.y;
}
inline bool posXDefined (Child *child)
{ return posXAbsolute (child) || generatorPosDefined (child); }
inline bool posYDefined (Child *child)
{ return posYAbsolute (child) || generatorPosDefined (child); }
public:
OOFPositionedMgr (OOFAwareWidget *container);
~OOFPositionedMgr ();
void sizeAllocateStart (OOFAwareWidget *caller,
core::Allocation *allocation);
void sizeAllocateEnd (OOFAwareWidget *caller);
void containerSizeChangedForChildren ();
void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
void markSizeChange (int ref);
void markExtremesChange (int ref);
core::Widget *getWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext *context);
void addWidgetInFlow (OOFAwareWidget *widget, OOFAwareWidget *parent,
int externalIndex);
int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generator,
int externalIndex);
void moveExternalIndices (OOFAwareWidget *generator, int oldStartIndex,
int diff);
void tellPosition1 (core::Widget *widget, int x, int y);
void tellPosition2 (core::Widget *widget, int x, int y);
void tellIncompletePosition1 (core::Widget *generator, core::Widget *widget,
int x, int y);
void tellIncompletePosition2 (core::Widget *generator, core::Widget *widget,
int x, int y);
bool containerMustAdjustExtraSpace ();
int getLeftBorder (int y, int h, OOFAwareWidget *lastGen, int lastExtIndex);
int getRightBorder (int y, int h, OOFAwareWidget *lastGen, int lastExtIndex);
bool hasFloatLeft (int y, int h, OOFAwareWidget *lastGen, int lastExtIndex);
bool hasFloatRight (int y, int h, OOFAwareWidget *lastGen, int lastExtIndex);
int getLeftFloatHeight (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex);
int getRightFloatHeight (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex);
int getClearPosition (OOFAwareWidget *widget);
bool affectsLeftBorder (core::Widget *widget);
bool affectsRightBorder (core::Widget *widget);
bool mayAffectBordersAtAll ();
bool dealingWithSizeOfChild (core::Widget *child);
int getNumWidgets ();
core::Widget *getWidget (int i);
};
} // namespace oof
} // namespace dw
#endif // __DW_OOFPOSITIONEDMGR_HH__

249
dw/oofposrelmgr.cc Normal file
View File

@ -0,0 +1,249 @@
/*
* Dillo Widget
*
* Copyright 2015 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.
*
* 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 "oofposrelmgr.hh"
using namespace dw::core;
using namespace lout::object;
using namespace lout::misc;
namespace dw {
namespace oof {
OOFPosRelMgr::OOFPosRelMgr (OOFAwareWidget *container) :
OOFPositionedMgr (container)
{
DBG_OBJ_CREATE ("dw::oof::OOFPosRelMgr");
}
OOFPosRelMgr::~OOFPosRelMgr ()
{
DBG_OBJ_DELETE ();
}
void OOFPosRelMgr::markSizeChange (int ref)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref);
Child *child = children->get(ref);
DBG_OBJ_MSGF ("resize.oofm", 1, "generator = %p, externalIndex = %d",
child->generator, child->externalIndex);
child->generator->widgetRefSizeChanged (child->externalIndex);
DBG_OBJ_LEAVE ();
}
void OOFPosRelMgr::markExtremesChange (int ref)
{
}
void OOFPosRelMgr::calcWidgetRefSize (Widget *widget, Requisition *size)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "calcWidgetRefSize", "%p", widget);
widget->sizeRequest (size);
// In some cases, the widget has been enlarged for widgets out of
// flow. Partly, this is done by adding "extra space"; however, at
// this point, the extra space is not relevant here. See
// "oofawarewidget.cc" for a calculation of RequisitionWithoutOOF.
// (Notice also that Widget::sizeRequest has to be called in all
// cases.)
if (widget->instanceOf (OOFAwareWidget::CLASS_ID))
*size = *((OOFAwareWidget*)widget)->getRequisitionWithoutOOF ();
DBG_OBJ_LEAVE_VAL ("%d * (%d + %d)",
size->width, size->ascent, size->descent);
}
void OOFPosRelMgr::sizeAllocateChildren ()
{
DBG_OBJ_ENTER0 ("resize.oofm", 0, "sizeAllocateChildren");
for (int i = 0; i < children->size (); i++) {
Child *child = children->get(i);
Requisition childReq;
child->widget->sizeRequest (&childReq);
Allocation childAlloc;
childAlloc.x = containerAllocation.x + getChildPosX (child);
childAlloc.y = containerAllocation.y + getChildPosY (child);
childAlloc.width = childReq.width;
childAlloc.ascent = childReq.ascent;
childAlloc.descent = childReq.descent;
child->widget->sizeAllocate (&childAlloc);
}
DBG_OBJ_LEAVE ();
}
void OOFPosRelMgr::getSize (Requisition *containerReq, int *oofWidth,
int *oofHeight)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "getSize", "%d * (%d + %d)",
containerReq->width, containerReq->ascent,
containerReq->descent);
*oofWidth = *oofHeight = 0;
for (int i = 0; i < children->size (); i++) {
Child *child = children->get(i);
// Children whose position cannot be determined will be
// considered later in sizeAllocateEnd.
if (posXDefined (child) && posYDefined (child)) {
Requisition childReq;
child->widget->sizeRequest (&childReq);
*oofWidth = max (*oofWidth, getChildPosX (child) + childReq.width);
*oofHeight = max (*oofHeight,
getChildPosY (child) + childReq.ascent
+ childReq.descent);
child->consideredForSize = true;
} else
child->consideredForSize = false;
}
DBG_OBJ_LEAVE_VAL ("%d * %d", *oofWidth, *oofHeight);
}
void OOFPosRelMgr::getExtremes (Extremes *containerExtr, int *oofMinWidth,
int *oofMaxWidth)
{
*oofMinWidth = *oofMaxWidth = 0;
for (int i = 0; i < children->size (); i++) {
Child *child = children->get(i);
// Children whose position cannot be determined will be
// considered later in sizeAllocateEnd.
if (posXDefined (child)) {
Extremes childExtr;
child->widget->getExtremes (&childExtr);
// Put the extremes of the container in relation to the extremes
// of the child, as in OOFPosAbsLikeMgr::getExtremes (see
// comment there).
*oofMinWidth = max (*oofMinWidth,
getChildPosX (child, containerExtr->minWidth)
+ childExtr.minWidth);
*oofMaxWidth = max (*oofMaxWidth,
getChildPosX (child, containerExtr->maxWidth)
+ childExtr.maxWidth);
child->consideredForExtremes = true;
} else
child->consideredForExtremes = false;
}
}
bool OOFPosRelMgr::posXAbsolute (Child *child)
{
return false;
}
bool OOFPosRelMgr::posYAbsolute (Child *child)
{
return false;
}
int OOFPosRelMgr::getChildPosX (Child *child, int refWidth)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosX", "[%p], %d",
child->widget, refWidth);
int gx = generatorPosX (child);
int dim = getChildPosDim (child->widget->getStyle()->left,
child->widget->getStyle()->right,
child->x,
refWidth
- child->widget->getStyle()->boxDiffWidth ());
DBG_OBJ_LEAVE_VAL ("%d + %d = %d", gx, dim, gx + dim);
return gx + dim;
}
int OOFPosRelMgr::getChildPosY (Child *child, int refHeight)
{
DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosY", "[%p], %d",
child->widget, refHeight);
int gy = generatorPosY (child);
int dim = getChildPosDim (child->widget->getStyle()->top,
child->widget->getStyle()->bottom,
child->y,
refHeight
- child->widget->getStyle()->boxDiffHeight ());
DBG_OBJ_LEAVE_VAL ("%d + %d = %d", gy, dim, gy + dim);
return gy + dim;
}
int OOFPosRelMgr::getChildPosDim (style::Length posCssValue,
style::Length negCssValue, int refPos,
int refLength)
{
// posCssValue refers to "left" or "top", negCssValue refers to "right" or
// "bottom". The former values are preferred ("left" over "right" etc.),
// which should later depend on the CSS value "direction".
DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosDim",
"<i>%d</i>, <i>%d</i>, %d, %d",
posCssValue, negCssValue, refPos, refLength);
int diff;
if (getPosBorder (posCssValue, refLength, &diff))
DBG_OBJ_MSGF ("resize.oofm", 1, "posCssValue: diff = %d", diff);
else {
if (getPosBorder (negCssValue, refLength, &diff)) {
DBG_OBJ_MSGF ("resize.oofm", 1, "negCssValue: diff = %d", diff);
diff *= -1;
} else
diff = 0;
}
DBG_OBJ_LEAVE_VAL ("%d + %d = %d", refPos, diff, refPos + diff);
return refPos + diff;
}
bool OOFPosRelMgr::dealingWithSizeOfChild (Widget *child)
{
return false;
}
int OOFPosRelMgr::getAvailWidthOfChild (Widget *child, bool forceValue)
{
notImplemented("OOFPosRelMgr::getAvailWidthOfChild");
return 0;
}
int OOFPosRelMgr::getAvailHeightOfChild (Widget *child, bool forceValue)
{
notImplemented ("OOFPosRelMgr::getAvailHeightOfChild");
return 0;
}
} // namespace oof
} // namespace dw

50
dw/oofposrelmgr.hh Normal file
View File

@ -0,0 +1,50 @@
#ifndef __DW_OOFPOSRELMGR_HH__
#define __DW_OOFPOSRELMGR_HH__
#include "oofpositionedmgr.hh"
namespace dw {
namespace oof {
class OOFPosRelMgr: public OOFPositionedMgr
{
protected:
void sizeAllocateChildren ();
bool posXAbsolute (Child *child);
bool posYAbsolute (Child *child);
int getChildPosX (Child *child, int refWidth);
int getChildPosY (Child *child, int refHeight);
int getChildPosDim (core::style::Length posCssValue,
core::style::Length negCssValue, int refPos,
int refLength);
inline int getChildPosX (Child *child)
{ return getChildPosX (child, container->getAvailWidth (true)); }
inline int getChildPosY (Child *child)
{ return getChildPosY (child, container->getAvailHeight (true)); }
public:
OOFPosRelMgr (OOFAwareWidget *container);
~OOFPosRelMgr ();
void markSizeChange (int ref);
void markExtremesChange (int ref);
void calcWidgetRefSize (core::Widget *widget, core::Requisition *size);
void getSize (core::Requisition *containerReq, int *oofWidth,
int *oofHeight);
void getExtremes (core::Extremes *containerExtr, int *oofMinWidth,
int *oofMaxWidth);
bool dealingWithSizeOfChild (core::Widget *child);
int getAvailWidthOfChild (core::Widget *child, bool forceValue);
int getAvailHeightOfChild (core::Widget *child, bool forceValue);
};
} // namespace oof
} // namespace dw
#endif // __DW_OOFPOSRELMGR_HH__

40
dw/outofflowmgr.cc Normal file
View File

@ -0,0 +1,40 @@
/*
* Dillo Widget
*
* Copyright 2013-2014 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.
*
* 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 "outofflowmgr.hh"
#include "oofawarewidget.hh"
#include "../lout/debug.hh"
namespace dw {
namespace oof {
OutOfFlowMgr::OutOfFlowMgr ()
{
}
OutOfFlowMgr::~OutOfFlowMgr ()
{
}
} // namespace oof
} // namespace dw

150
dw/outofflowmgr.hh Normal file
View File

@ -0,0 +1,150 @@
#ifndef __DW_OUTOFFLOWMGR_HH__
#define __DW_OUTOFFLOWMGR_HH__
#include "core.hh"
namespace dw {
/**
* \brief Out Of Flow. See \ref dw-out-of-flow.
*/
namespace oof {
class OOFAwareWidget;
/**
* \brief Represents additional data for OOF containers.
*/
class OutOfFlowMgr
{
public:
OutOfFlowMgr ();
virtual ~OutOfFlowMgr ();
virtual void sizeAllocateStart (OOFAwareWidget *caller,
core::Allocation *allocation) = 0;
virtual void sizeAllocateEnd (OOFAwareWidget *caller) = 0;
virtual void containerSizeChangedForChildren () = 0;
virtual void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context) = 0;
virtual void markSizeChange (int ref) = 0;
virtual void markExtremesChange (int ref) = 0;
virtual core::Widget *getWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext
*context) = 0;
virtual void addWidgetInFlow (OOFAwareWidget *widget,
OOFAwareWidget *parent, int externalIndex) = 0;
virtual int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generator,
int externalIndex) = 0;
virtual void calcWidgetRefSize (core::Widget *widget,
core::Requisition *size) = 0;
virtual void moveExternalIndices (OOFAwareWidget *generator,
int oldStartIndex, int diff) = 0;
/**
* \brief Called before tellPosition2, see there for more.
*/
virtual void tellPosition1 (core::Widget *widget, int x, int y) = 0;
/**
* \brief Called after tellPosition1.
*
* An implementation should only implement either tellPosition1 or
* tellPosition2. Coordinates are relative to the *container*.
*/
virtual void tellPosition2 (core::Widget *widget, int x, int y) = 0;
virtual void tellIncompletePosition1 (core::Widget *generator,
core::Widget *widget, int x, int y)
= 0;
virtual void tellIncompletePosition2 (core::Widget *generator,
core::Widget *widget, int x, int y)
= 0;
virtual void getSize (core::Requisition *containerReq, int *oofWidth,
int *oofHeight) = 0;
virtual bool containerMustAdjustExtraSpace ()= 0;
virtual void getExtremes (core::Extremes *containerExtr, int *oofMinWidth,
int *oofMaxWidth) = 0;
/**
* Get the left border for the vertical position of *y*, for a height
* of *h", based on floats; relative to the *container*.
*
* The border includes margin/border/padding of the calling textblock
* but is 0 if there is no float, so a caller should also consider
* other borders.
*/
virtual int getLeftBorder (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex) = 0;
/**
* Get the right border for the vertical position of *y*, for a height
* of *h*, based on floats; relative to the *container*.
*
* See also getLeftBorder().
*/
virtual int getRightBorder (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex) = 0;
/**
* Return whether there is a float on the left side. *y* is
* relative to the *container*.
*
* See also getLeftBorder().
*/
virtual bool hasFloatLeft (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex) = 0;
/**
* Return whether there is a float on the right side. *y* is
* relative to the *container*.
*
* See also hasFloatLeft(), getLeftBorder();
*/
virtual bool hasFloatRight (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex) = 0;
/**
* Assuming there is a float on the left side, return the rest
* height of it. *y* is relative to the *container*.
*
* See also getLeftBorder().
*/
virtual int getLeftFloatHeight (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex) = 0;
/**
* Assuming there is a float on the right side, return the rest
* height of it. *y* is relative to the *container*.
*
* See also getLeftFloatHeight(), getLeftBorder().
*/
virtual int getRightFloatHeight (int y, int h, OOFAwareWidget *lastGen,
int lastExtIndex) = 0;
virtual bool affectsLeftBorder (core::Widget *widget) = 0;
virtual bool affectsRightBorder (core::Widget *widget) = 0;
virtual bool mayAffectBordersAtAll () = 0;
/**
* Return value is relative to the *calling generator* (not container).
*/
virtual int getClearPosition (OOFAwareWidget *widget) = 0;
virtual bool dealingWithSizeOfChild (core::Widget *child) = 0;
virtual int getAvailWidthOfChild (core::Widget *child, bool forceValue) = 0;
virtual int getAvailHeightOfChild (core::Widget *child, bool forceValue) = 0;
// for iterators
virtual int getNumWidgets () = 0;
virtual core::Widget *getWidget (int i) = 0;
};
} // namespace oof
} // namespace dw
#endif // __DW_OUTOFFLOWMGR_HH__

172
dw/platform.hh Normal file
View File

@ -0,0 +1,172 @@
#ifndef __DW_PLATFORM_HH__
#define __DW_PLATFORM_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
/**
* \brief An interface to encapsulate some platform dependencies.
*
* \sa\ref dw-overview
*/
class Platform: public lout::object::Object
{
public:
/*
* -----------------------------------
* General
* -----------------------------------
*/
/**
* \brief This methods notifies the platform, that it has been attached to
* a layout.
*/
virtual void setLayout (Layout *layout) = 0;
/*
* -------------------------
* Operations on views
* -------------------------
*/
/**
* \brief This methods notifies the platform, that a view has been attached
* to the related layout.
*/
virtual void attachView (View *view) = 0;
/**
* \brief This methods notifies the platform, that a view has been detached
* from the related layout.
*/
virtual void detachView (View *view) = 0;
/*
* -----------------------------------
* Platform dependent properties
* -----------------------------------
*/
/**
* \brief Return the width of a text, with a given length and font.
*/
virtual int textWidth (style::Font *font, const char *text, int len) = 0;
/**
* \brief Return the string resulting from transforming text to uppercase.
*/
virtual char *textToUpper (const char *text, int len) = 0;
/**
* \brief Return the string resulting from transforming text to lowercase.
*/
virtual char *textToLower (const char *text, int len) = 0;
/**
* \brief Return the index of the next glyph in string text.
*/
virtual int nextGlyph (const char *text, int idx) = 0;
/**
* \brief Return the index of the previous glyph in string text.
*/
virtual int prevGlyph (const char *text, int idx) = 0;
/**
* \brief Return screen resolution in x-direction.
*/
virtual float dpiX () = 0;
/**
* \brief Return screen resolution in y-direction.
*/
virtual float dpiY () = 0;
/*
* ---------------------------------------------------------
* These are to encapsulate some platform dependencies
* ---------------------------------------------------------
*/
/**
* \brief Add an idle function.
*
* An idle function is called once, when no other
* tasks are to be done (e.g. there are no events to process), and then
* removed from the queue. The return value is a number, which can be
* used in removeIdle below.
*/
virtual int addIdle (void (Layout::*func) ()) = 0;
/**
* \brief Remove an idle function, which has not been processed yet.
*/
virtual void removeIdle (int idleId) = 0;
/*
* ---------------------
* Style Resources
* ---------------------
*/
/**
* \brief Create a (platform dependent) font.
*
* Typically, within a platform, a sub class of dw::core::style::Font
* is defined, which holds more platform dependent data.
*
* Also, this method must fill the attributes "font" (when needed),
* "ascent", "descent", "spaceSidth", "zeroWidth" and "xHeight". If
* "tryEverything" is true, several methods should be used to use
* another font, when the requested font is not available. Passing
* false is typically done, if the caller wants to test different
* variations.
*/
virtual style::Font *createFont (style::FontAttrs *attrs,
bool tryEverything) = 0;
virtual bool fontExists (const char *name) = 0;
/**
* \brief Create a color resource for a given 0xrrggbb value.
*/
virtual style::Color *createColor (int color) = 0;
/**
* \brief Create a tooltip
*/
virtual style::Tooltip *createTooltip (const char *text) = 0;
/**
* \brief Cancel a tooltip (either shown or requested)
*/
virtual void cancelTooltip () = 0;
/**
* \brief Create a (platform specific) image buffer.
*
* "gamma" is the value by which the image data is gamma-encoded.
*/
virtual Imgbuf *createImgbuf (Imgbuf::Type type, int width, int height,
double gamma) = 0;
/**
* \brief Copy selected text (0-terminated).
*/
virtual void copySelection(const char *text) = 0;
/**
* ...
*/
virtual ui::ResourceFactory *getResourceFactory () = 0;
};
} // namespace core
} // namespace dw
#endif // __DW_PLATFORM_HH__

5
dw/preview.xbm Normal file
View File

@ -0,0 +1,5 @@
#define preview_width 11
#define preview_height 11
static unsigned char preview_bits[] = {
0x20, 0x00, 0x70, 0x00, 0x20, 0x00, 0x20, 0x00, 0x22, 0x02, 0xff, 0x07,
0x22, 0x02, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x20, 0x00};

39
dw/regardingborder.cc Normal file
View File

@ -0,0 +1,39 @@
/*
* Dillo Widget
*
* Copyright 2015 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.
*
* 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 "regardingborder.hh"
#include <stdio.h>
namespace dw {
int RegardingBorder::CLASS_ID = -1;
RegardingBorder::RegardingBorder ()
{
DBG_OBJ_CREATE ("dw::RegardingBorder");
registerName ("dw::RegardingBorder", &CLASS_ID);
}
RegardingBorder::~RegardingBorder ()
{
DBG_OBJ_DELETE ();
}
} // namespace dw

24
dw/regardingborder.hh Normal file
View File

@ -0,0 +1,24 @@
#ifndef __DW_REGARDINGBORDER_HH__
#define __DW_REGARDINGBORDER_HH__
#include "oofawarewidget.hh"
namespace dw {
/**
* \brief Base class (rather a tag interface) for those widgets
* regarding borders defined by floats, and so allocated on the
* full width.
*/
class RegardingBorder: public oof::OOFAwareWidget
{
public:
static int CLASS_ID;
RegardingBorder ();
~RegardingBorder ();
};
} // namespace dw
#endif // __DW_REGARDINGBORDER_HH__

100
dw/ruler.cc Normal file
View File

@ -0,0 +1,100 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "ruler.hh"
#include "../lout/misc.hh"
#include <stdio.h>
namespace dw {
int Ruler::CLASS_ID = -1;
Ruler::Ruler ()
{
DBG_OBJ_CREATE ("dw::Ruler");
registerName ("dw::Ruler", &CLASS_ID);
}
Ruler::~Ruler ()
{
DBG_OBJ_DELETE ();
}
void Ruler::sizeRequestSimpl (core::Requisition *requisition)
{
requisition->width = lout::misc::max (getAvailWidth (true), boxDiffWidth ());
requisition->ascent = boxOffsetY ();
requisition->descent = boxRestHeight ();
}
void Ruler::getExtremesSimpl (core::Extremes *extremes)
{
extremes->minWidth = extremes->maxWidth = boxDiffWidth ();
extremes->minWidthIntrinsic = extremes->minWidth;
extremes->maxWidthIntrinsic = extremes->maxWidth;
correctExtremes (extremes, false);
extremes->adjustmentWidth =
lout::misc::min (extremes->minWidthIntrinsic, extremes->minWidth);
}
bool Ruler::isBlockLevel ()
{
return true;
}
void Ruler::containerSizeChangedForChildren ()
{
DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
// Nothing to do.
DBG_OBJ_LEAVE ();
}
bool Ruler::usesAvailWidth ()
{
return true;
}
void Ruler::draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context)
{
drawWidgetBox (view, area, false);
}
core::Widget *Ruler::getWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext
*context)
{
// Override (complex) implementation OOFAwareWidget::getWidgetAtPoint().
if (inAllocation (x, y))
return this;
else
return NULL;
}
core::Iterator *Ruler::iterator (core::Content::Type mask, bool atEnd)
{
/** \todo TextIterator? */
return new core::EmptyIterator (this, mask, atEnd);
}
} // namespace dw

45
dw/ruler.hh Normal file
View File

@ -0,0 +1,45 @@
#ifndef __RULER_HH__
#define __RULER_HH__
#include "regardingborder.hh"
namespace dw {
/**
* \brief Widget for drawing (horizontal) rules.
*
* This is really an empty widget, the HTML parser puts a border
* around it, and drawing is done in dw::core::Widget::drawWidgetBox.
*
* Ruler implements RegardingBorder; this way, it is simpler to fit
* the ruler exactly within the space between floats. Currently, the
* drawn area of the ruler is too large (but most of the superfluous
* part is hidden by the floats); this problem will soon solved here
* in the "dillo_grows" repository.
*/
class Ruler: public RegardingBorder
{
protected:
void sizeRequestSimpl (core::Requisition *requisition);
void getExtremesSimpl (core::Extremes *extremes);
void containerSizeChangedForChildren ();
bool usesAvailWidth ();
void draw (core::View *view, core::Rectangle *area,
core::DrawingContext *context);
core::Widget *getWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext *context);
public:
static int CLASS_ID;
Ruler ();
~Ruler ();
bool isBlockLevel ();
core::Iterator *iterator (core::Content::Type mask, bool atEnd);
};
} // namespace dw
#endif // __RULER_HH__

502
dw/selection.cc Normal file
View File

@ -0,0 +1,502 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "core.hh"
#include "../lout/debug.hh"
#include <string.h>
using namespace lout;
/*
* strndup() is a GNU extension.
*/
extern "C" char *strndup(const char *s, size_t size)
{
char *r = (char *) malloc (size + 1);
if (r) {
strncpy (r, s, size);
r[size] = 0;
}
return r;
}
namespace dw {
namespace core {
SelectionState::SelectionState ()
{
DBG_OBJ_CREATE ("dw::core::SelectionState");
layout = NULL;
selectionState = NONE;
from = NULL;
to = NULL;
linkState = LINK_NONE;
link = NULL;
}
SelectionState::~SelectionState ()
{
reset ();
DBG_OBJ_DELETE ();
}
void SelectionState::reset ()
{
resetSelection ();
resetLink ();
}
void SelectionState::resetSelection ()
{
if (from)
delete from;
from = NULL;
if (to)
delete to;
to = NULL;
selectionState = NONE;
}
void SelectionState::resetLink ()
{
if (link)
delete link;
link = NULL;
linkState = LINK_NONE;
}
bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo,
EventButton *event)
{
DBG_IF_RTFL {
misc::StringBuffer sb;
it->intoStringBuffer (&sb);
DBG_OBJ_ENTER ("events", 0, "buttonPress", "[%s], %d, %d, ...",
sb.getChars (), charPos, linkNo);
}
Widget *itWidget = it->getWidget ();
bool ret = false;
if (event) {
if (event->button == 3) {
// menu popup
layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
ret = true;
} else if (linkNo != -1) {
// link handling
(void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
resetLink ();
linkState = LINK_PRESSED;
linkButton = event->button;
DeepIterator *newLink = new DeepIterator (it);
if (newLink->isEmpty ()) {
delete newLink;
resetLink ();
} else {
link = newLink;
// It may be that the user has pressed on something activatable
// (linkNo != -1), but there is no contents, e.g. with images
// without ALTernative text.
if (link) {
linkChar = correctCharPos (link, charPos);
linkNumber = linkNo;
}
}
// We do not return the value of the signal method,
// but we do actually process this event.
ret = true;
} else if (event->button == 1) {
// normal selection handling
highlight (false, 0);
resetSelection ();
selectionState = SELECTING;
DeepIterator *newFrom = new DeepIterator (it);
if (newFrom->isEmpty ()) {
delete newFrom;
resetSelection ();
} else {
from = newFrom;
fromChar = correctCharPos (from, charPos);
to = from->cloneDeepIterator ();
toChar = correctCharPos (to, charPos);
}
ret = true;
}
}
DBG_OBJ_MSGF ("events", 1, "=> %s", ret ? "true" : "false");
DBG_OBJ_LEAVE ();
return ret;
}
bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo,
EventButton *event)
{
DBG_IF_RTFL {
misc::StringBuffer sb;
it->intoStringBuffer (&sb);
DBG_OBJ_ENTER ("events", 0, "buttonRelease", "[%s], %d, %d, ...",
sb.getChars (), charPos, linkNo);
}
Widget *itWidget = it->getWidget ();
bool ret = false;
if (linkState == LINK_PRESSED && event && event->button == linkButton) {
// link handling
ret = true;
if (linkNo != -1)
(void) layout->emitLinkRelease (itWidget, linkNo, -1, -1, -1, event);
// The link where the user clicked the mouse button?
if (linkNo == linkNumber) {
resetLink ();
(void) layout->emitLinkClick (itWidget, linkNo, -1, -1, -1, event);
} else {
if (event->button == 1)
// Reset links and switch to selection mode. The selection
// state will be set to SELECTING, which is handled some lines
// below.
switchLinkToSelection (it, charPos);
}
}
if (selectionState == SELECTING && event && event->button == 1) {
// normal selection
ret = true;
adjustSelection (it, charPos);
if (from->compareTo (to) == 0 && fromChar == toChar)
// nothing selected
resetSelection ();
else {
copy ();
selectionState = SELECTED;
}
}
DBG_OBJ_MSGF ("events", 1, "=> %s", ret ? "true" : "false");
DBG_OBJ_LEAVE ();
return ret;
}
bool SelectionState::buttonMotion (Iterator *it, int charPos, int linkNo,
EventMotion *event)
{
if (linkState == LINK_PRESSED) {
//link handling
if (linkNo != linkNumber)
// No longer the link where the user clicked the mouse button.
// Reset links and switch to selection mode.
switchLinkToSelection (it, charPos);
// Still in link: do nothing.
} else if (selectionState == SELECTING) {
// selection
adjustSelection (it, charPos);
}
return true;
}
/**
* \brief General form of dw::core::SelectionState::buttonPress,
* dw::core::SelectionState::buttonRelease and
* dw::core::SelectionState::buttonMotion.
*/
bool SelectionState::handleEvent (EventType eventType, Iterator *it,
int charPos, int linkNo,
MousePositionEvent *event)
{
switch (eventType) {
case BUTTON_PRESS:
return buttonPress (it, charPos, linkNo, (EventButton*)event);
case BUTTON_RELEASE:
return buttonRelease (it, charPos, linkNo, (EventButton*)event);
case BUTTON_MOTION:
return buttonMotion (it, charPos, linkNo, (EventMotion*)event);
default:
misc::assertNotReached ();
}
return false;
}
/**
* \brief This method is called when the user decides not to activate a link,
* but instead select text.
*/
void SelectionState::switchLinkToSelection (Iterator *it, int charPos)
{
// It may be that selection->link is NULL, see a_Selection_button_press.
if (link) {
// Reset old selection.
highlight (false, 0);
resetSelection ();
// Transfer link state into selection state.
from = link->cloneDeepIterator ();
fromChar = linkChar;
to = from->createVariant (it);
toChar = correctCharPos (to, charPos);
selectionState = SELECTING;
// Reset link status.
resetLink ();
highlight (true, 0);
} else {
// A link was pressed on, but there is nothing to select. Reset
// everything.
resetSelection ();
resetLink ();
}
}
/**
* \brief This method is used by core::dw::SelectionState::buttonMotion and
* core::dw::SelectionState::buttonRelease, and changes the second limit of
* the already existing selection region.
*/
void SelectionState::adjustSelection (Iterator *it, int charPos)
{
DeepIterator *newTo;
int newToChar, cmpOld, cmpNew, cmpDiff, len;
bool bruteHighlighting = false;
newTo = to->createVariant (it);
newToChar = correctCharPos (newTo, charPos);
cmpOld = to->compareTo (from);
cmpNew = newTo->compareTo (from);
if (cmpOld == 0 || cmpNew == 0) {
// Either before, or now, the limits differ only by the character
// position.
bruteHighlighting = true;
} else if (cmpOld * cmpNew < 0) {
// The selection order has changed, i.e. the user moved the selection
// end again beyond the position he started.
bruteHighlighting = true;
} else {
// Here, cmpOld and cmpNew are equivalent and != 0.
cmpDiff = newTo->compareTo (to);
if (cmpOld * cmpDiff > 0) {
// The user has enlarged the selection. Highlight the difference.
if (cmpDiff < 0) {
len = correctCharPos (to, END_OF_WORD);
highlight0 (true, newTo, newToChar, to, len + 1, 1);
} else {
highlight0 (true, to, 0, newTo, newToChar, -1);
}
} else {
if (cmpOld * cmpDiff < 0) {
// The user has reduced the selection. Unhighlight the difference.
highlight0 (false, to, 0, newTo, 0, cmpDiff);
}
// Otherwise, the user has changed the position only slightly.
// In both cases, re-highlight the new position.
if (cmpOld < 0) {
len = correctCharPos (newTo, END_OF_WORD);
newTo->highlight (newToChar, len + 1, HIGHLIGHT_SELECTION);
} else
newTo->highlight (0, newToChar, HIGHLIGHT_SELECTION);
}
}
if (bruteHighlighting)
highlight (false, 0);
delete to;
to = newTo;
toChar = newToChar;
if (bruteHighlighting)
highlight (true, 0);
}
/**
* \brief This method deals especially with the case that a widget passes
* dw::core::SelectionState::END_OF_WORD.
*/
int SelectionState::correctCharPos (DeepIterator *it, int charPos)
{
Iterator *top = it->getTopIterator ();
int len;
if (top->getContent()->type == Content::TEXT)
len = strlen(top->getContent()->text);
else
len = 1;
return misc::min(charPos, len);
}
void SelectionState::highlight0 (bool fl, DeepIterator *from, int fromChar,
DeepIterator *to, int toChar, int dir)
{
DeepIterator *a, *b, *i;
int cmp, aChar, bChar;
bool start;
if (from && to) {
cmp = from->compareTo (to);
if (cmp == 0) {
if (fl) {
if (fromChar < toChar)
from->highlight (fromChar, toChar, HIGHLIGHT_SELECTION);
else
from->highlight (toChar, fromChar, HIGHLIGHT_SELECTION);
} else
from->unhighlight (0, HIGHLIGHT_SELECTION);
return;
}
if (cmp < 0) {
a = from;
aChar = fromChar;
b = to;
bChar = toChar;
} else {
a = to;
aChar = toChar;
b = from;
bChar = fromChar;
}
for (i = a->cloneDeepIterator (), start = true;
(cmp = i->compareTo (b)) <= 0;
i->next (), start = false) {
if (i->getContent()->type == Content::TEXT) {
if (fl) {
if (start) {
i->highlight (aChar, strlen (i->getContent()->text) + 1,
HIGHLIGHT_SELECTION);
} else if (cmp == 0) {
// the end
i->highlight (0, bChar, HIGHLIGHT_SELECTION);
} else {
i->highlight (0, strlen (i->getContent()->text) + 1,
HIGHLIGHT_SELECTION);
}
} else {
i->unhighlight (dir, HIGHLIGHT_SELECTION);
}
}
}
delete i;
}
}
void SelectionState::copy()
{
if (from && to) {
Iterator *si;
DeepIterator *a, *b, *i;
int cmp, aChar, bChar;
bool start;
char *tmp;
misc::StringBuffer strbuf;
cmp = from->compareTo (to);
if (cmp == 0) {
if (from->getContent()->type == Content::TEXT) {
si = from->getTopIterator ();
if (fromChar < toChar)
tmp = strndup (si->getContent()->text + fromChar,
toChar - fromChar);
else
tmp = strndup (si->getContent()->text + toChar,
fromChar - toChar);
strbuf.appendNoCopy (tmp);
}
} else {
if (cmp < 0) {
a = from;
aChar = fromChar;
b = to;
bChar = toChar;
} else {
a = to;
aChar = toChar;
b = from;
bChar = fromChar;
}
for (i = a->cloneDeepIterator (), start = true;
(cmp = i->compareTo (b)) <= 0;
i->next (), start = false) {
si = i->getTopIterator ();
switch (si->getContent()->type) {
case Content::TEXT:
if (start) {
tmp = strndup (si->getContent()->text + aChar,
strlen (i->getContent()->text) - aChar);
strbuf.appendNoCopy (tmp);
} else if (cmp == 0) {
// the end
tmp = strndup (si->getContent()->text, bChar);
strbuf.appendNoCopy (tmp);
} else
strbuf.append (si->getContent()->text);
if (si->getContent()->space && cmp != 0)
strbuf.append (" ");
break;
case Content::BREAK:
if (si->getContent()->breakSpace > 0)
strbuf.append ("\n\n");
else
strbuf.append ("\n");
break;
default:
// Make pedantic compilers happy. Especially
// DW_CONTENT_WIDGET is never returned by a DwDeepIterator.
break;
}
}
delete i;
}
layout->copySelection(strbuf.getChars());
}
}
} // namespace core
} // namespace dw

241
dw/selection.hh Normal file
View File

@ -0,0 +1,241 @@
#ifndef __DW_SELECTION_H__
#define __DW_SELECTION_H__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
/**
* \brief This class handles selections, as well as activation of links,
* which is closely related.
*
* <h3>General Overview</h3>
*
* dw::core::SelectionState is associated with dw::core::Layout. The selection
* state is controlled by "abstract events", which are sent by single
* widgets by calling one of the following methods:
*
* <ul>
* <li> dw::core::SelectionState::buttonPress for button press events,
* <li> dw::core::SelectionState::buttonRelease for button release events, and
* <li> dw::core::SelectionState::buttonMotion for motion events (with pressed
* mouse button).
* </ul>
*
* The widget must construct simple iterators (dw::core::Iterator), which will
* be transferred to deep iterators (dw::core::DeepIterator), see below for
* more details. All event handling methods have the same signature, the
* arguments in detail are:
*
* <table>
* <tr><td>dw::core::Iterator *it <td>the iterator pointing on the item
* under the mouse pointer; this
* iterator \em must be created with
* dw::core::Content::SELECTION_CONTENT
* as mask
* <tr><td>int charPos <td>the exact (character) position
* within the iterator,
* <tr><td>int linkNo <td>if this item is associated with a
* link, its number (see
* dw::core::Layout::LinkReceiver),
* otherwise -1
* <tr><td>dw::core::EventButton *event <td>the event itself; only the button
* is used
* </table>
*
* Look also at dw::core::SelectionState::handleEvent, which may be useful
* in some circumstances.
*
* In some cases, \em charPos would be difficult to determine. E.g., when
* the dw::Textblock widget decides that the user is pointing on a position
* <i>at the end</i> of an image (DwImage), it constructs a simple iterator
* pointing on this image widget. In a simple iterator, that fact that
* the pointer is at the end, would be represented by \em charPos == 1. But
* when transferring this simple iterator into an deep iterator, this
* simple iterator is discarded and instead the stack has an iterator
* pointing to text at the top. As a result, only the first letter of the
* ALT text would be copied.
*
* To avoid this problem, widgets should in this case pass
* dw::core::SelectionState::END_OF_WORD as \em charPos, which is then
* automatically reduced to the actual length of the deep(!) iterator.
*
* The return value is the same as in DwWidget event handling methods.
* I.e., in most cases, they should simply return it. The events
* dw::core::Layout::LinkReceiver::press,
* dw::core::Layout::LinkReceiver::release and
* dw::core::Layout::LinkReceiver::click (but not
* dw::core::Layout::LinkReceiver::enter) are emitted by these methods, so
* that widgets which let dw::core::SelectionState handle links, should only
* emit dw::core::Layout::LinkReceiver::enter for themselves.
*
* <h3>Selection State</h3>
*
* Selection interferes with handling the activation of links, so the
* latter is also handled by the dw::core::SelectionState. Details are based on
* following guidelines:
*
* <ol>
* <li> It should be simple to select links and to start selection in
* links. The rule to distinguish between link activation and
* selection is that the selection starts as soon as the user leaves
* the link. (This is, IMO, a useful feature. Even after drag and
* drop has been implemented in dillo, this should be somehow
* preserved.)
*
* <li> The selection should stay as long as possible, i.e., the old
* selection is only cleared when a new selection is started.
* </ol>
*
* The latter leads to a model with two states: the selection state and
* the link handling state.
*
* The general selection works, for events not pointing on links, like
* this (numbers in parentheses after the event denote the button, "n"
* means arbitrary button):
*
* \dot
* digraph G {
* node [shape=ellipse, fontname=Helvetica, fontsize=10];
* edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
* color="#404040", labelfontcolor="#000080",
* fontname=Helvetica, fontsize=10, fontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
*
* NONE;
* SELECTING;
* q [label="Anything selected?", shape=plaintext];
* SELECTED;
*
* NONE -> SELECTING [label="press(1)\non non-link"];
* SELECTING -> SELECTING [label="motion(1)"];
* SELECTING -> q [label="release(1)"];
* q -> SELECTED [label="yes"];
* q -> NONE [label="no"];
* SELECTED -> SELECTING [label="press(1)"];
*
* }
* \enddot
*
* The selected region is represented by two instances of
* dw::core::DeepIterator.
*
* Links are handled by a different state machine:
*
* \dot
* digraph G {
* node [shape=ellipse, fontname=Helvetica, fontsize=10];
* edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
* color="#404040", labelfontcolor="#000080",
* fontname=Helvetica, fontsize=10, fontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
*
* LINK_NONE;
* LINK_PRESSED;
* click [label="Emit \"click\" signal.", shape=record];
* q11 [label="Still the same link?", shape=plaintext];
* q21 [label="Still the same link?", shape=plaintext];
* q22 [label="n == 1?", shape=plaintext];
* SELECTED [label="Switch selection\nto SELECTED", shape=record];
* q12 [label="n == 1?", shape=plaintext];
* SELECTING [label="Switch selection\nto SELECTING", shape=record];
*
* LINK_NONE -> LINK_PRESSED [label="press(n)\non link"];
* LINK_PRESSED -> q11 [label="motion(n)"];
* q11 -> LINK_PRESSED [label="yes"];
* q11 -> q12 [label="no"];
* q12 -> SELECTING [label="yes"];
* SELECTING -> LINK_NONE;
* q12 -> LINK_NONE [label="no"];
* LINK_PRESSED -> q21 [label="release(n)"];
* q21 -> click [label="yes"];
* click -> LINK_NONE;
* q21 -> q22 [label="no"];
* q22 -> SELECTED [label="yes"];
* SELECTED -> LINK_NONE;
* q22 -> LINK_NONE [label="no"];
* }
* \enddot
*
* Switching selection simply means that the selection state will
* eventually be SELECTED/SELECTING, with the original and the current
* position making up the selection region. This happens for button 1,
* events with buttons other than 1 do not affect selection at all.
*
*
* \todo dw::core::SelectionState::buttonMotion currently always assumes
* that button 1 has been pressed (since otherwise it would not do
* anything). This should be made a bit cleaner.
*
* \todo The selection should be cleared, when the user selects something
* somewhere else (perhaps switched into "non-active" mode, as e.g. Gtk+
* does).
*
*/
class SelectionState
{
public:
enum { END_OF_WORD = 1 << 30 };
private:
Layout *layout;
// selection
enum {
NONE,
SELECTING,
SELECTED
} selectionState;
DeepIterator *from, *to;
int fromChar, toChar;
// link handling
enum {
LINK_NONE,
LINK_PRESSED
} linkState;
int linkButton;
DeepIterator *link;
int linkChar, linkNumber;
void resetSelection ();
void resetLink ();
void switchLinkToSelection (Iterator *it, int charPos);
void adjustSelection (Iterator *it, int charPos);
static int correctCharPos (DeepIterator *it, int charPos);
void highlight (bool fl, int dir)
{ highlight0 (fl, from, fromChar, to, toChar, dir); }
void highlight0 (bool fl, DeepIterator *from, int fromChar,
DeepIterator *to, int toChar, int dir);
void copy ();
public:
enum EventType { BUTTON_PRESS, BUTTON_RELEASE, BUTTON_MOTION };
SelectionState ();
~SelectionState ();
inline void setLayout (Layout *layout) { this->layout = layout; }
void reset ();
bool buttonPress (Iterator *it, int charPos, int linkNo,
EventButton *event);
bool buttonRelease (Iterator *it, int charPos, int linkNo,
EventButton *event);
bool buttonMotion (Iterator *it, int charPos, int linkNo,
EventMotion *event);
bool handleEvent (EventType eventType, Iterator *it, int charPos,
int linkNo, MousePositionEvent *event);
};
} // namespace core
} // namespace dw
#endif // __DW_SELECTION_H__

143
dw/simpletablecell.cc Normal file
View File

@ -0,0 +1,143 @@
/*
* Dillo Widget
*
* Copyright 2014 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.
*
* 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 "simpletablecell.hh"
#include "tablecell.hh"
#include "../lout/debug.hh"
using namespace lout;
namespace dw {
int SimpleTableCell::CLASS_ID = -1;
SimpleTableCell::SimpleTableCell (bool limitTextWidth):
Textblock (limitTextWidth)
{
DBG_OBJ_CREATE ("dw::SimpleTableCell");
registerName ("dw::SimpleTableCell", &CLASS_ID);
}
SimpleTableCell::~SimpleTableCell()
{
DBG_OBJ_DELETE ();
}
bool SimpleTableCell::getAdjustMinWidth ()
{
return tablecell::getAdjustMinWidth ();
}
bool SimpleTableCell::isBlockLevel ()
{
return tablecell::isBlockLevel ();
}
bool SimpleTableCell::usesMaxGeneratorWidth ()
{
return tablecell::usesMaxGeneratorWidth ();
}
int SimpleTableCell::getAvailWidthOfChild (Widget *child, bool forceValue)
{
DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::getAvailWidthOfChild",
"%p, %s", child, forceValue ? "true" : "false");
int width = tablecell::correctAvailWidthOfChild
(this, child, Textblock::getAvailWidthOfChild (child, forceValue),
forceValue);
DBG_OBJ_LEAVE ();
return width;
}
int SimpleTableCell::getAvailHeightOfChild (Widget *child, bool forceValue)
{
DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::getAvailHeightOfChild",
"%p, %s", child, forceValue ? "true" : "false");
int height = tablecell::correctAvailHeightOfChild
(this, child, Textblock::getAvailHeightOfChild (child, forceValue),
forceValue);
DBG_OBJ_LEAVE ();
return height;
}
void SimpleTableCell::correctRequisitionOfChild (Widget *child,
core::Requisition *requisition,
void (*splitHeightFun) (int,
int*,
int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight)
{
DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::correctRequisitionOfChild",
"%p, %d * (%d + %d), ..., %s, %s", child, requisition->width,
requisition->ascent, requisition->descent,
misc::boolToStr (allowDecreaseWidth),
misc::boolToStr (allowDecreaseHeight));
Textblock::correctRequisitionOfChild (child, requisition, splitHeightFun,
allowDecreaseWidth,
allowDecreaseHeight);
tablecell::correctCorrectedRequisitionOfChild (this, child, requisition,
splitHeightFun,
allowDecreaseWidth,
allowDecreaseHeight);
DBG_OBJ_LEAVE ();
}
void SimpleTableCell::correctExtremesOfChild (Widget *child,
core::Extremes *extremes,
bool useAdjustmentWidth)
{
DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::correctExtremesOfChild",
"%p, %d (%d) / %d (%d)",
child, extremes->minWidth, extremes->minWidthIntrinsic,
extremes->maxWidth, extremes->maxWidthIntrinsic);
Textblock::correctExtremesOfChild (child, extremes, useAdjustmentWidth);
tablecell::correctCorrectedExtremesOfChild (this, child, extremes,
useAdjustmentWidth);
DBG_OBJ_LEAVE ();
}
int SimpleTableCell::applyPerWidth (int containerWidth,
core::style::Length perWidth)
{
return tablecell::applyPerWidth (this, containerWidth, perWidth);
}
int SimpleTableCell::applyPerHeight (int containerHeight,
core::style::Length perHeight)
{
return tablecell::applyPerHeight (this, containerHeight, perHeight);
}
bool SimpleTableCell::adjustExtraSpaceWhenCorrectingRequisitionByOOF ()
{
return tablecell::adjustExtraSpaceWhenCorrectingRequisitionByOOF ();
}
} // namespace dw

42
dw/simpletablecell.hh Normal file
View File

@ -0,0 +1,42 @@
#ifndef __DW_SIMPLETABLECELL_HH__
#define __DW_SIMPLETABLECELL_HH__
#include "textblock.hh"
namespace dw {
class SimpleTableCell: public Textblock
{
protected:
int getAvailWidthOfChild (Widget *child, bool forceValue);
int getAvailHeightOfChild (Widget *child, bool forceValue);
void correctRequisitionOfChild (Widget *child,
core::Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight);
void correctExtremesOfChild (Widget *child, core::Extremes *extremes,
bool useAdjustmentWidth);
bool getAdjustMinWidth ();
bool adjustExtraSpaceWhenCorrectingRequisitionByOOF ();
public:
static int CLASS_ID;
SimpleTableCell (bool limitTextWidth);
~SimpleTableCell ();
int applyPerWidth (int containerWidth, core::style::Length perWidth);
int applyPerHeight (int containerHeight, core::style::Length perHeight);
bool isBlockLevel ();
bool usesMaxGeneratorWidth ();
};
} // namespace dw
#endif // __DW_SIMPLETABLECELL_HH__

195
dw/stackingcontextmgr.cc Normal file
View File

@ -0,0 +1,195 @@
/*
* Dillo Widget
*
* Copyright 2014 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.
*
* 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 "core.hh"
#include "../lout/debug.hh"
using namespace lout::misc;
using namespace lout::container::typed;
namespace dw {
namespace core {
StackingContextMgr::StackingContextMgr (Widget *widget)
{
DBG_OBJ_CREATE ("dw::core::StackingContextMgr");
this->widget = widget;
childSCWidgets = new Vector<Widget> (1, false);
DBG_OBJ_SET_NUM ("childSCWidgets.size", childSCWidgets->size());
numZIndices = 0;
zIndices = NULL;
DBG_OBJ_SET_NUM ("numZIndices", numZIndices);
}
StackingContextMgr::~StackingContextMgr ()
{
delete childSCWidgets;
if (zIndices)
free (zIndices);
DBG_OBJ_DELETE ();
}
void StackingContextMgr::addChildSCWidget (Widget *widget)
{
DBG_OBJ_ENTER ("common.scm", 0, "addChildSCWidget", "%p [z-index = %d]",
widget, widget->getStyle()->zIndex);
int pos = findZIndex (widget->getStyle()->zIndex, true);
DBG_OBJ_MSGF ("common.scm", 1, "pos = %d", pos);
if (pos == -1) {
pos = findZIndex (widget->getStyle()->zIndex, false);
DBG_OBJ_MSGF ("common.scm", 1, "pos = %d", pos);
numZIndices++;
DBG_OBJ_SET_NUM ("numZIndices", numZIndices);
zIndices = (int*)(zIndices ?
realloc (zIndices, numZIndices * sizeof (int)) :
malloc (numZIndices * sizeof (int)));
for (int i = numZIndices - 1; i >= pos + 1; i--) {
zIndices[i] = zIndices[i - 1];
DBG_OBJ_ARRSET_NUM ("zIndex", i, zIndices[i]);
}
zIndices[pos] = widget->getStyle()->zIndex;
DBG_OBJ_ARRSET_NUM ("zIndex", pos, zIndices[pos]);
}
childSCWidgets->put (widget);
DBG_OBJ_SET_NUM ("childSCWidgets.size", childSCWidgets->size());
DBG_OBJ_ARRSET_PTR ("childSCWidgets", childSCWidgets->size() - 1, widget);
DBG_OBJ_LEAVE ();
}
int StackingContextMgr::findZIndex (int zIndex, bool mustExist)
{
int result = -123; // Compiler happiness: GCC 4.7 does not handle this?
if (numZIndices == 0)
result = mustExist ? -1 : 0;
else {
int low = 0, high = numZIndices - 1;
bool found = false;
while (!found) {
int index = (low + high) / 2;
if (zIndex == zIndices[index]) {
found = true;
result = index;
} else {
if (low >= high) {
if (mustExist) {
found = true;
result = -1;
} else {
found = true;
result = zIndex > zIndices[index] ? index + 1 : index;
}
}
if (zIndex < zIndices[index])
high = index - 1;
else
low = index + 1;
}
}
}
return result;
}
void StackingContextMgr::draw (View *view, Rectangle *area, int startZIndex,
int endZIndex, DrawingContext *context)
{
DBG_OBJ_ENTER ("draw", 0, "draw", "[%d, %d, %d * %d], %d, %d",
area->x, area->y, area->width, area->height, startZIndex,
endZIndex);
for (int zIndexIndex = 0; zIndexIndex < numZIndices; zIndexIndex++) {
// Wrong region of z-indices (top or bottom) is simply ignored
// (as well as non-defined zIndices).
if (zIndices != NULL && zIndices[zIndexIndex] >= startZIndex &&
zIndices[zIndexIndex] <= endZIndex) {
DBG_OBJ_MSGF ("draw", 1, "drawing zIndex = %d", zIndices[zIndexIndex]);
DBG_OBJ_MSG_START ();
for (int i = 0; i < childSCWidgets->size (); i++) {
Widget *child = childSCWidgets->get (i);
DBG_OBJ_MSGF ("draw", 2, "widget %p has zIndex = %d",
child, child->getStyle()->zIndex);
Rectangle childArea;
if (child->getStyle()->zIndex == zIndices[zIndexIndex] &&
child->intersects (widget, area, &childArea))
child->draw (view, &childArea, context);
}
DBG_OBJ_MSG_END ();
}
}
DBG_OBJ_LEAVE ();
}
Widget *StackingContextMgr::getWidgetAtPoint (int x, int y,
GettingWidgetAtPointContext
*context,
int startZIndex, int endZIndex)
{
DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y);
Widget *widgetAtPoint = NULL;
for (int zIndexIndex = numZIndices - 1;
widgetAtPoint == NULL && zIndexIndex >= 0; zIndexIndex--) {
// Wrong region of z-indices (top or bottom) is simply ignored
// (as well as non-defined zIndices).
if (zIndices != NULL && zIndices[zIndexIndex] >= startZIndex &&
zIndices[zIndexIndex] <= endZIndex) {
DBG_OBJ_MSGF ("events", 1, "searching zIndex = %d",
zIndices[zIndexIndex]);
DBG_OBJ_MSG_START ();
for (int i = childSCWidgets->size () - 1;
widgetAtPoint == NULL && i >= 0; i--) {
Widget *child = childSCWidgets->get (i);
DBG_OBJ_MSGF ("events", 2, "widget %p has zIndex = %d",
child, child->getStyle()->zIndex);
if (child->getStyle()->zIndex == zIndices[zIndexIndex])
widgetAtPoint = child->getWidgetAtPoint (x, y, context);
}
DBG_OBJ_MSG_END ();
}
}
DBG_OBJ_MSGF ("events", 0, "=> %p", widgetAtPoint);
DBG_OBJ_LEAVE ();
return widgetAtPoint;
}
} // namespace core
} // namespace dw

76
dw/stackingcontextmgr.hh Normal file
View File

@ -0,0 +1,76 @@
#ifndef __DW_STACKINGCONTEXTMGR_HH__
#define __DW_STACKINGCONTEXTMGR_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
#include "../lout/container.hh"
#include <limits.h>
namespace dw {
namespace core {
/**
* \brief See \ref dw-stacking-context.
*/
class StackingContextMgr
{
private:
Widget *widget;
lout::container::typed::Vector<Widget> *childSCWidgets;
int *zIndices, numZIndices;
int findZIndex (int zIndex, bool mustExist);
void draw (View *view, Rectangle *area, int startZIndex, int endZIndex,
DrawingContext *context);
Widget *getWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext *context,
int startZIndex, int endZIndex);
public:
StackingContextMgr (Widget *widget);
~StackingContextMgr ();
inline static bool isEstablishingStackingContext (Widget *widget) {
return IMPL_POS &&
widget->getStyle()->position != style::POSITION_STATIC &&
widget->getStyle()->zIndex != style::Z_INDEX_AUTO;
}
inline static bool handledByStackingContextMgr (Widget *widget) {
// Each widget establishing a stacking context is child of another
// stacking context, so drawn by StackingContextMgr::drawTop or
// StackingContextMgr::drawBottom etc.
return widget->getParent () != NULL
&& isEstablishingStackingContext (widget);
}
void addChildSCWidget (Widget *widget);
inline int getNumZIndices () { return numZIndices; }
inline int getNumChildSCWidgets () { return childSCWidgets->size (); }
inline void drawBottom (View *view, Rectangle *area, DrawingContext *context)
{ draw (view, area, INT_MIN, -1, context); }
void drawTop (View *view, Rectangle *area, DrawingContext *context)
{ draw (view, area, 0, INT_MAX, context); }
inline Widget *getTopWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext
*context)
{ return getWidgetAtPoint (x, y, context, 0, INT_MAX); }
inline Widget *getBottomWidgetAtPoint (int x, int y,
core::GettingWidgetAtPointContext
*context)
{ return getWidgetAtPoint (x, y, context, INT_MIN, -1); }
};
} // namespace core
} // namespace dw
#endif // __DW_STACKINGCONTEXTMGR_HH__

1477
dw/style.cc Normal file

File diff suppressed because it is too large Load Diff

923
dw/style.hh Normal file
View File

@ -0,0 +1,923 @@
#ifndef __DW_STYLE_HH__
#define __DW_STYLE_HH__
#include <stdint.h>
#include <math.h>
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
#include <limits.h>
#include "../lout/signal.hh"
#include "../lout/debug.hh"
namespace dw {
namespace core {
/**
* \brief Anything related to Dillo %Widget styles is defined here.
*
* <h3>Overview</h3>
*
* dw::core::style::Style provides some resources and attributes for
* drawing widgets, as well as for parts of a widget (e.g., dw::Textblock
* uses styles for its words). Creating a style is done by filling a
* dw::core::style::StyleAttrs with the attributes and calling
* dw::core::style::Style::create:
*
* \code
* dw::core::style::Style styleAttrs;
* dw::core::style::Style *style;
* dw::core::Layout *layout;
*
* // ...
*
* styleAttrs.foo = bar;
* // etc.
* style = dw::core::style::Style::create (&styleAttrs, layout);
* // do something with style
* \endcode
*
* After this, the attributes of a dw::core::style::Style should not be
* changed anymore, since styles are often shared between different
* widgets etc. (see below). Most times, you simply copy the attributes
* of another style (possible, since dw::core::style::Style is a sub
* class of dw::core::style::StyleAttrs), modify them and create a new
* style:
*
* \code
* styleAttrs = *anotherStyle;
* styleAttrs.foo = baz;
* style = dw::core::style::Style::create (&styleAttrs, layout);
* \endcode
*
* The dw::core::style::Font structure can be created by
* dw::core::style::Font::create, in a similar, with
* dw::core::style::FontAttrs, and colors by
* dw::core::style::Color::create, passing 0xrrggbb as an
* argument. Furthermore, there is dw::core::style::Tooltip, created by
* dw::core::style::Tooltip::create.
*
* Notice that fonts, colors and tooltips are only intended to be used in
* conjunction with dw::core::style::Style.
*
*
* <h3>Naming</h3>
*
* dw::core::style::Style will become important for CSS, each CSS
* attribute, which is supported by dillo, will refer to an attribute in
* dw::core::style::Style. For this reason, the attributes in
* dw::core::style::Style get the names from the CSS attributes, with
* "camelCase" instead of hyphens (e.g. "background-color" becomes
* "backgroundColor").
*
* However, dw::core::style::Style will be extended by some more
* attributes, which are not defined by CSS. To distinguish them, they
* get the prefix "x_", e.g. dw::core::style::Style::x_link.
*
*
* <h3>Lengths and Percentages</h3>
*
* dw::core::style::Length is a simple data type for lengths and
* percentages:
*
* <ul>
* <li> A length refers to an absolute measurement. It is used to
* represent the HTML type %Pixels; and the CSS type \<length\>.
*
* For CSS lengths, there are two units: (i) pixels and absolute
* units, which have to be converted to pixels (a pixel is, unlike
* in the CSS specification, treated as absolute unit), and (ii) the
* relative units "em" and "ex" (see below).
*
* <li> A percentage refers to a value relative to another value. It is
* used for the HTML type %Length; (except %Pixels;), and the CSS
* type \<percentage\>.
*
* <li> A relative length can be used in lists of HTML MultiLengths.
* </ul>
*
* Since many values in CSS may be either lengths or percentages, a
* single type is very useful.
*
* <h4>Useful Functions</h4>
*
* Creating lengths:
*
* <ul>
* <li> dw::core::style::createAbsLength
* <li> dw::core::style::createPerLength
* <li> dw::core::style::createRelLength
* </ul>
*
* Examine lengths:
*
* <ul>
* <li> dw::core::style::isAbsLength
* <li> dw::core::style::isPerLength
* <li> dw::core::style::isRelLength
* <li> dw::core::style::absLengthVal
* <li> dw::core::style::perLengthVal
* <li> dw::core::style::relLengthVal
* </ul>
*
*
* <h3>Boxes</h3>
*
* <h4>The CSS %Box Model</h4>
*
* For borders, margins etc., the box model defined by CSS2 is
* used. dw::core::style::Style contains some members defining these
* attributes. A dw::core::Widget must use these values for any
* calculation of sizes. There are some helper functions (see
* dw/style.hh). A dw::core::style::Style box looks quite similar to a
* CSS box:
*
* \image html dw-style-box-model.png
*
* <h4>Background colors</h4>
*
* The background color is stored in
* dw::core::style::Style::backgroundColor, which may be NULL (the
* background color of the parent widget is shining through).
*
* For toplevel widgets, this color is set as the background color of the
* views (dw::core::View::setBgColor), for other widgets, a filled
* rectangle is drawn, covering the content and padding. (This is
* compliant with CSS2, the background color of the toplevel element
* covers the whole canvas.)
*
* <h4>Drawing</h4>
*
* The following methods may be useful:
*
* <ul>
* <li> dw::core::Widget::drawWidgetBox for drawing the box of a widget
* (typically at the beginning of the implementation of
* dw::core::Widget::draw), and
*
* <li> dw::core::Widget::drawBox, for drawing parts of a widget (e.g.
* dw::Textblock::Word, which has its own dw::Textblock::Word::style).
* </ul>
*
*
* <h3>Notes on Memory Management</h3>
*
* Memory management is done by reference counting,
* dw::core::style::Style::create returns a pointer to
* dw::core::style::Style with an increased reference counter, so you
* should care about calling dw::core::style::Style::unref if it is not
* used anymore. You do \em not need to care about the reference counters
* of fonts and styles.
*
* In detail:
*
* <ul>
* <li> dw::core::style::Style::ref is called in
*
* <ul>
* <li> dw::core::Widget::setStyle to assign a style to a widget,
* <li> dw::Textblock::addText, dw::Textblock::addWidget,
* dw::Textblock::addAnchor, dw::Textblock::addSpace,
* dw::Textblock::addParbreak and dw::Textblock::addLinebreak,
* to assign a style to a dw::Textblock::Word, and
* <li> by the HTML parser, when pushing an element on the stack.
* </ul>
*
* <li> dw::core::style::Style::unref is called in
*
* <ul>
* <li> dw::core::Widget::~Widget, dw::Textblock::~Textblock, by the
* HTML parser, when popping an element from the stack, and
* <li> dw::core::Widget::setStyle, dw::Textblock::addText etc.,
* these methods overwrite an existing style.
* </ul>
* </ul>
*/
namespace style {
enum Cursor {
CURSOR_CROSSHAIR,
CURSOR_DEFAULT,
CURSOR_POINTER,
CURSOR_MOVE,
CURSOR_E_RESIZE,
CURSOR_NE_RESIZE,
CURSOR_NW_RESIZE,
CURSOR_N_RESIZE,
CURSOR_SE_RESIZE,
CURSOR_SW_RESIZE,
CURSOR_S_RESIZE,
CURSOR_W_RESIZE,
CURSOR_TEXT,
CURSOR_WAIT,
CURSOR_HELP
};
enum BorderCollapse {
BORDER_MODEL_SEPARATE,
BORDER_MODEL_COLLAPSE
};
enum BorderStyle {
BORDER_NONE,
BORDER_HIDDEN,
BORDER_DOTTED,
BORDER_DASHED,
BORDER_SOLID,
BORDER_DOUBLE,
BORDER_GROOVE,
BORDER_RIDGE,
BORDER_INSET,
BORDER_OUTSET
};
enum BackgroundRepeat {
BACKGROUND_REPEAT,
BACKGROUND_REPEAT_X,
BACKGROUND_REPEAT_Y,
BACKGROUND_NO_REPEAT
};
enum BackgroundAttachment {
BACKGROUND_ATTACHMENT_SCROLL,
BACKGROUND_ATTACHMENT_FIXED
};
enum TextAlignType {
TEXT_ALIGN_LEFT,
TEXT_ALIGN_RIGHT,
TEXT_ALIGN_CENTER,
TEXT_ALIGN_JUSTIFY,
TEXT_ALIGN_STRING
};
enum VAlignType {
VALIGN_TOP,
VALIGN_BOTTOM,
VALIGN_MIDDLE,
VALIGN_BASELINE,
VALIGN_SUB,
VALIGN_SUPER,
VALIGN_TEXT_TOP,
VALIGN_TEXT_BOTTOM
};
enum TextTransform {
TEXT_TRANSFORM_NONE,
TEXT_TRANSFORM_CAPITALIZE,
TEXT_TRANSFORM_UPPERCASE,
TEXT_TRANSFORM_LOWERCASE
};
/**
* \todo Incomplete. Has to be completed for a CSS implementation.
*/
enum DisplayType {
DISPLAY_BLOCK,
DISPLAY_INLINE,
DISPLAY_INLINE_BLOCK,
DISPLAY_LIST_ITEM,
DISPLAY_NONE,
DISPLAY_TABLE,
DISPLAY_TABLE_ROW_GROUP,
DISPLAY_TABLE_HEADER_GROUP,
DISPLAY_TABLE_FOOTER_GROUP,
DISPLAY_TABLE_ROW,
DISPLAY_TABLE_CELL
};
enum LineType {
LINE_NORMAL,
LINE_DOTTED,
LINE_DASHED
};
enum ListStylePosition {
LIST_STYLE_POSITION_INSIDE,
LIST_STYLE_POSITION_OUTSIDE
};
enum ListStyleType {
LIST_STYLE_TYPE_DISC,
LIST_STYLE_TYPE_CIRCLE,
LIST_STYLE_TYPE_SQUARE,
LIST_STYLE_TYPE_DECIMAL,
LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO,
LIST_STYLE_TYPE_LOWER_ROMAN,
LIST_STYLE_TYPE_UPPER_ROMAN,
LIST_STYLE_TYPE_LOWER_GREEK,
LIST_STYLE_TYPE_LOWER_ALPHA,
LIST_STYLE_TYPE_LOWER_LATIN,
LIST_STYLE_TYPE_UPPER_ALPHA,
LIST_STYLE_TYPE_UPPER_LATIN,
LIST_STYLE_TYPE_HEBREW,
LIST_STYLE_TYPE_ARMENIAN,
LIST_STYLE_TYPE_GEORGIAN,
LIST_STYLE_TYPE_CJK_IDEOGRAPHIC,
LIST_STYLE_TYPE_HIRAGANA,
LIST_STYLE_TYPE_KATAKANA,
LIST_STYLE_TYPE_HIRAGANA_IROHA,
LIST_STYLE_TYPE_KATAKANA_IROHA,
LIST_STYLE_TYPE_NONE
};
enum FontStyle {
FONT_STYLE_NORMAL,
FONT_STYLE_ITALIC,
FONT_STYLE_OBLIQUE
};
enum FontVariant {
FONT_VARIANT_NORMAL,
FONT_VARIANT_SMALL_CAPS
};
enum Overflow {
OVERFLOW_VISIBLE,
OVERFLOW_HIDDEN,
OVERFLOW_SCROLL,
OVERFLOW_AUTO
};
enum Position {
POSITION_STATIC,
POSITION_RELATIVE,
POSITION_ABSOLUTE,
POSITION_FIXED
};
enum TextDecoration {
TEXT_DECORATION_NONE = 0,
TEXT_DECORATION_UNDERLINE = 1 << 0,
TEXT_DECORATION_OVERLINE = 1 << 1,
TEXT_DECORATION_LINE_THROUGH = 1 << 2,
TEXT_DECORATION_BLINK = 1 << 3
};
enum WhiteSpace {
WHITE_SPACE_NORMAL,
WHITE_SPACE_PRE,
WHITE_SPACE_NOWRAP,
WHITE_SPACE_PRE_WRAP,
WHITE_SPACE_PRE_LINE
};
enum FloatType {
FLOAT_NONE,
FLOAT_LEFT,
FLOAT_RIGHT
};
enum ClearType {
CLEAR_LEFT,
CLEAR_RIGHT,
CLEAR_BOTH,
CLEAR_NONE
};
enum {
/**
* \brief 'z-index' is stored as int; use this for the value 'auto'.
*
* Only some random value, which has to be checked explicitly; do
* not compare this (less or greater) to integer values of
* 'z-index'.
*/
Z_INDEX_AUTO = INT_MAX
};
/**
* \brief Type for representing all lengths within dw::core::style.
*
* Lengths are int's. Absolute lengths are represented in the following way:
*
* \image html dw-style-length-absolute.png
*
* Percentages:
*
* \image html dw-style-length-percentage.png
*
* Relative lengths (only used in HTML):
*
* \image html dw-style-length-relative.png
*
* This is an implementation detail, use one of the following functions:
*
* Creating lengths:
*
* <ul>
* <li> dw::core::style::createAbsLength
* <li> dw::core::style::createPerLength
* <li> dw::core::style::createRelLength
* </ul>
*
* Examine lengths:
*
* <ul>
* <li> dw::core::style::isAbsLength
* <li> dw::core::style::isPerLength
* <li> dw::core::style::isRelLength
* <li> dw::core::style::absLengthVal
* <li> dw::core::style::perLengthVal
* <li> dw::core::style::relLengthVal
* </ul>
*
* "auto" lengths are represented as dw::core::style::LENGTH_AUTO.
*/
typedef int Length;
/** \brief Returns a length of \em n pixels. */
inline Length createAbsLength(int n) { return (n << 2) | 1; }
/** \brief Returns a percentage, \em v is relative to 1, not to 100. */
inline Length createPerLength(double v) {
return ((int)(v * (1 << 18)) & ~3) | 2; }
/** \brief Returns a relative length. */
inline Length createRelLength(double v) {
return ((int)(v * (1 << 18)) & ~3) | 3; }
/** \brief Returns true if \em l is an absolute length. */
inline bool isAbsLength(Length l) { return (l & 3) == 1; }
/** \brief Returns true if \em l is a percentage. */
inline bool isPerLength(Length l) { return (l & 3) == 2; }
/** \brief Returns true if \em l is a relative length. */
inline bool isRelLength(Length l) { return (l & 3) == 3; }
/** \brief Returns the value of a length in pixels, as an integer. */
inline int absLengthVal(Length l) { return l >> 2; }
/** \brief Returns the value of a percentage, relative to 1, as a double.
*
* When possible, do not use this function directly; it may be removed
* soon. Instead, use multiplyWithPerLength or multiplyWithPerLengthRounded.
*/
inline double perLengthVal_useThisOnlyForDebugging(Length l)
{ return (double)(l & ~3) / (1 << 18); }
/** \brief Returns the value of a relative length, as a float.
*
* When possible, do not use this function directly; it may be removed
* soon.
*/
inline double relLengthVal(Length l) { return (double)(l & ~3) / (1 << 18); }
/**
* \brief Multiply an int with a percentage length, returning int.
*
* Use this instead of perLengthVal, when possible.
*/
inline int multiplyWithPerLength(int x, Length l) {
return (int) round((double) x * perLengthVal_useThisOnlyForDebugging (l));
}
/**
* \brief Like multiplyWithPerLength, but rounds to nearest integer
* instead of down.
*
* (This function exists for backward compatibility.)
*/
inline int multiplyWithPerLengthRounded(int x, Length l) {
return lout::misc::roundInt (x * perLengthVal_useThisOnlyForDebugging (l));
}
inline int multiplyWithRelLength(int x, Length l) {
return x * relLengthVal(l);
}
enum {
/** \brief Represents "auto" lengths. */
LENGTH_AUTO = 0
};
/**
* \brief Represents a dimension box according to the CSS box model.
*
* Used for dw::core::style::Style::margin,
* dw::core::style::Style::borderWidth, and dw::core::style::Style::padding.
*/
class Box
{
public:
/* in future also percentages */
int top, right, bottom, left;
inline void setVal(int val) { top = right = bottom = left = val; }
inline bool equals (Box *other) {
return top == other->top &&
right == other->right &&
bottom == other->bottom &&
left == other->left;
}
inline int hashValue () {
return top + right + bottom + left;
}
};
class Tooltip;
class Font;
class Color;
class StyleImage;
/**
* \sa dw::core::style
*/
class StyleAttrs : public lout::object::Object
{
public:
Font *font;
int textDecoration; /* No TextDecoration because of problems converting
* TextDecoration <-> int */
Color *color, *backgroundColor;
StyleImage *backgroundImage;
BackgroundRepeat backgroundRepeat;
BackgroundAttachment backgroundAttachment;
Length backgroundPositionX; // "left" defined by "0%" etc. (see CSS spec)
Length backgroundPositionY; // "top" defined by "0%" etc. (see CSS spec)
TextAlignType textAlign;
VAlignType valign;
char textAlignChar; /* In future, strings will be supported. */
TextTransform textTransform;
FloatType vloat; /* "float" is a keyword. */
ClearType clear;
Overflow overflow;
Position position;
Length top, bottom, left, right;
int hBorderSpacing, vBorderSpacing, wordSpacing;
Length width, height, lineHeight, textIndent;
Length minWidth, maxWidth, minHeight, maxHeight;
Box margin, borderWidth, padding;
BorderCollapse borderCollapse;
struct { Color *top, *right, *bottom, *left; } borderColor;
struct { BorderStyle top, right, bottom, left; } borderStyle;
DisplayType display;
WhiteSpace whiteSpace;
ListStylePosition listStylePosition;
ListStyleType listStyleType;
Cursor cursor;
int zIndex;
int x_link;
int x_img;
Tooltip *x_tooltip;
char x_lang[2]; /* Either x_lang[0] == x_lang[1] == 0 (no language
set), or x_lang contains the RFC 1766 country
code in lower case letters. (Only two letters
allowed, currently.) */
void initValues ();
void resetValues ();
bool sizeDiffs (StyleAttrs *otherStyleAttrs);
inline void setBorderColor(Color *val) {
borderColor.top = borderColor.right = borderColor.bottom
= borderColor.left = val; }
inline void setBorderStyle(BorderStyle val) {
borderStyle.top = borderStyle.right = borderStyle.bottom
= borderStyle.left = val; }
inline int boxOffsetX ()
{ return margin.left + borderWidth.left + padding.left; }
inline int boxRestWidth ()
{ return margin.right + borderWidth.right + padding.right; }
inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); }
inline int boxOffsetY ()
{ return margin.top + borderWidth.top + padding.top; }
inline int boxRestHeight ()
{ return margin.bottom + borderWidth.bottom + padding.bottom; }
inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); }
inline bool hasBackground ()
{ return backgroundColor != NULL || backgroundImage != NULL; }
bool equals (lout::object::Object *other);
int hashValue ();
};
/**
* \sa dw::core::style
*/
class Style: public StyleAttrs
{
private:
static int totalRef;
int refCount;
static lout::container::typed::HashTable <StyleAttrs, Style> *styleTable;
Style (StyleAttrs *attrs);
protected:
~Style();
void copyAttrs (StyleAttrs *attrs);
public:
inline static Style *create (StyleAttrs *attrs)
{
Style *style = styleTable->get (attrs);
if (style) {
style->ref ();
} else {
style = new Style (attrs);
styleTable->put(style, style);
}
return style;
}
inline void ref () { refCount++; }
inline void unref () { if (--refCount == 0) delete this; }
};
/**
* \sa dw::core::style
*/
class TooltipAttrs: public lout::object::String
{
public:
TooltipAttrs(const char *text): lout::object::String(text) { }
};
/**
* \sa dw::core::style
*/
class Tooltip: public TooltipAttrs
{
private:
int refCount;
protected:
Tooltip (const char *text): TooltipAttrs(text) { refCount = 0; }
public:
static Tooltip *create (dw::core::Layout *layout, const char *text);
inline void ref () { refCount++; }
inline void unref ()
{ if (--refCount == 0) delete this; }
inline virtual void onEnter () { }
inline virtual void onLeave () { }
inline virtual void onMotion () { }
};
/**
* \sa dw::core::style
*/
class FontAttrs: public lout::object::Object
{
public:
const char *name;
int size;
int weight;
int letterSpacing;
FontVariant fontVariant;
FontStyle style;
bool equals(lout::object::Object *other);
int hashValue();
};
/**
* \sa dw::core::style
*/
class Font: public FontAttrs
{
private:
int refCount;
static Font *create0 (Layout *layout, FontAttrs *attrs, bool tryEverything);
protected:
inline Font () {
DBG_OBJ_CREATE ("dw::core::style::Font");
refCount = 0;
}
virtual ~Font ();
void copyAttrs (FontAttrs *attrs);
public:
int ascent, descent;
int spaceWidth;
int xHeight;
int zeroWidth;
static Font *create (Layout *layout, FontAttrs *attrs);
static bool exists (Layout *layout, const char *name);
inline void ref () { refCount++; }
inline void unref () { if (--refCount == 0) delete this; }
};
/**
* \sa dw::core::style
*/
class ColorAttrs: public lout::object::Object
{
protected:
int color;
public:
inline ColorAttrs(int color)
{
this->color = color;
}
inline int getColor () { return color; }
bool equals(lout::object::Object *other);
int hashValue();
};
/**
* \sa dw::core::style
*/
class Color: public ColorAttrs
{
private:
int refCount;
void remove(dw::core::Layout *layout);
int shadeColor (int color, int d);
protected:
inline Color (int color): ColorAttrs (color) {
DBG_OBJ_CREATE ("dw::core::style::Color");
refCount = 0;
}
virtual ~Color ();
public:
enum Shading { SHADING_NORMAL, SHADING_INVERSE, SHADING_DARK, SHADING_LIGHT,
SHADING_NUM };
protected:
int shadeColor (int color, Shading shading);
public:
static Color *create (Layout *layout, int color);
inline void ref () { refCount++; }
inline void unref ()
{ if (--refCount == 0) delete this; }
};
class StyleImage: public lout::signal::ObservedObject
{
private:
class StyleImgRenderer: public ImgRenderer
{
private:
StyleImage *image;
public:
inline StyleImgRenderer (StyleImage *image) { this->image = image; }
void setBuffer (core::Imgbuf *buffer, bool resize);
void drawRow (int row);
void finish ();
void fatal ();
};
int refCount, tilesX, tilesY;
Imgbuf *imgbufSrc, *imgbufTiled;
ImgRendererDist *imgRendererDist;
StyleImgRenderer *styleImgRenderer;
StyleImage ();
~StyleImage ();
public:
/**
* \brief Useful (but not mandatory) base class for updates of
* areas with background images.
*/
class ExternalImgRenderer: public ImgRenderer
{
public:
void setBuffer (core::Imgbuf *buffer, bool resize);
void drawRow (int row);
void finish ();
void fatal ();
/**
* \brief If this method returns false, nothing is done at all.
*/
virtual bool readyToDraw () = 0;
/**
* \brief Return the area covered by the background image.
*/
virtual void getBgArea (int *x, int *y, int *width, int *height) = 0;
/**
* \brief Return the "reference area".
*
* See comment of "drawBackground".
*/
virtual void getRefArea (int *xRef, int *yRef, int *widthRef,
int *heightRef) = 0;
virtual StyleImage *getBackgroundImage () = 0;
virtual BackgroundRepeat getBackgroundRepeat () = 0;
virtual BackgroundAttachment getBackgroundAttachment () = 0;
virtual Length getBackgroundPositionX () = 0;
virtual Length getBackgroundPositionY () = 0;
/**
* \brief Draw (or queue for drawing) an area, which is given in
* canvas coordinates.
*/
virtual void draw (int x, int y, int width, int height) = 0;
};
/**
* \brief Suitable for widgets and parts of widgets.
*/
class ExternalWidgetImgRenderer: public ExternalImgRenderer
{
public:
void getPaddingArea (int *x, int *y, int *width, int *height);
StyleImage *getBackgroundImage ();
BackgroundRepeat getBackgroundRepeat ();
BackgroundAttachment getBackgroundAttachment ();
Length getBackgroundPositionX ();
Length getBackgroundPositionY ();
/**
* \brief Return the style this background image is part of.
*/
virtual Style *getStyle () = 0;
};
static StyleImage *create () { return new StyleImage (); }
inline void ref () { refCount++; }
inline void unref ()
{ if (--refCount == 0) delete this; }
inline Imgbuf *getImgbufSrc () { return imgbufSrc; }
inline Imgbuf *getImgbufTiled (bool repeatX, bool repeatY)
{ return (imgbufTiled && repeatX && repeatY) ? imgbufTiled : imgbufSrc; }
inline int getTilesX (bool repeatX, bool repeatY)
{ return (imgbufTiled && repeatX && repeatY) ? tilesX : 1; }
inline int getTilesY (bool repeatX, bool repeatY)
{ return (imgbufTiled && repeatX && repeatY) ? tilesY : 1; }
inline ImgRenderer *getMainImgRenderer () { return imgRendererDist; }
/**
* \brief Add an additional ImgRenderer, especially used for
* drawing.
*/
inline void putExternalImgRenderer (ImgRenderer *ir)
{ imgRendererDist->put (ir); }
/**
* \brief Remove a previously added additional ImgRenderer.
*/
inline void removeExternalImgRenderer (ImgRenderer *ir)
{ imgRendererDist->remove (ir); }
};
void drawBorder (View *view, Layout *layout, Rectangle *area,
int x, int y, int width, int height,
Style *style, bool inverse);
void drawBackground (View *view, Layout *layout, Rectangle *area,
int x, int y, int width, int height,
int xRef, int yRef, int widthRef, int heightRef,
Style *style, Color *bgColor, bool inverse, bool atTop);
void drawBackgroundImage (View *view, StyleImage *backgroundImage,
BackgroundRepeat backgroundRepeat,
BackgroundAttachment backgroundAttachment,
Length backgroundPositionX,
Length backgroundPositionY,
int x, int y, int width, int height,
int xRef, int yRef, int widthRef, int heightRef);
void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType);
} // namespace style
} // namespace core
} // namespace dw
#endif // __DW_STYLE_HH__

1651
dw/table.cc Normal file

File diff suppressed because it is too large Load Diff

518
dw/table.hh Normal file
View File

@ -0,0 +1,518 @@
#ifndef __DW_TABLE_HH__
#define __DW_TABLE_HH__
#include "oofawarewidget.hh"
#include "alignedtablecell.hh"
#include "../lout/misc.hh"
namespace dw {
/**
* \brief A Widget for rendering tables.
*
* <div style="border: 2px solid #ff0000; margin-top: 0.5em;
* margin-bottom: 0.5em; padding: 0.5em 1em;
* background-color: #ffefe0"><b>Warning:</b> Some parts of this
* description are outdated since \ref dw-grows.</div>
*
* <h3>Introduction</h3>
*
* The dw::Table widget is used to render HTML tables.
*
* Each cell is itself a separate widget. Any widget may be used, however, in
* dillo, only instances of dw::Textblock and dw::TableCell are used as
* children of dw::Table.
*
*
* <h3>Sizes</h3>
*
* <h4>General</h4>
*
* The following diagram shows the dependencies between the different
* functions, which are related to size calculation. Click on the boxes
* for more information.
*
* \dot
* digraph G {
* node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"];
* edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica,
* labelfontsize=10, color="#404040", labelfontcolor="#000080",
* fontname=Helvetica, fontsize=10];
* fontname=Helvetica; fontsize=10;
*
* sizeRequestImpl [color="#0000ff", URL="\ref dw::Table::sizeRequestImpl"];
* sizeAllocateImpl [color="#0000ff",
* URL="\ref dw::Table::sizeAllocateImpl"];
* getExtremes [color="#0000ff", URL="\ref dw::core::Widget::getExtremes"];
* getExtremesImpl [color="#0000ff", URL="\ref dw::Table::getExtremesImpl"];
*
* calcCellSizes [label="calcCellSizes (calcHeights = true)",
* URL="\ref dw::Table::calcCellSizes"];
* forceCalcCellSizes [label="forceCalcCellSizes (calcHeights = true)",
* URL="\ref dw::Table::forceCalcCellSizes"];
* actuallyCalcCellSizes[label="actuallyCalcCellSizes (calcHeights = true)",
* URL="\ref dw::Table::actuallyCalcCellSizes"];
* forceCalcColumnExtremes[URL="\ref dw::Table::forceCalcColumnExtremes"];
*
* sizeRequestImpl -> forceCalcCellSizes [label="[B]"];
* sizeAllocateImpl -> calcCellSizes [label="[A]"];
* getExtremesImpl -> forceCalcColumnExtremes [label="[B]"];
*
* forceCalcCellSizes -> actuallyCalcCellSizes;
* actuallyCalcCellSizes-> getExtremes;
* getExtremes -> getExtremesImpl [style="dashed", label="[C]"];
*
* calcCellSizes -> forceCalcCellSizes [style="dashed", label="[C]"];
* }
* \enddot
*
* [A] In this case, the new calculation is \em not forced, but only
* done, when necessary.
*
* [B] In this case, the new calculation is always necessary, since [C]
* is the case.
*
* [C] Whether this function is called, depends on NEEDS_RESIZE /
* RESIZE_QUEUED / EXTREMES_CHANGED / EXTREMES_QUEUED.
*
* **TODO:**
*
* - Are <tt>*[cC]alcCellSizes (calcHeights = *false*)</tt> not
* necessary anymore?
* - Calculating available sizes (Table::getAvailWidthOfChild) should
* be documented in this diagram, too.
*
* <h4>Apportionment</h4>
*
* \sa\ref rounding-errors
*
* Given two array \f$e_{i,\min}\f$ and \f$e_{i,\max}\f$, which
* represent the column minima and maxima, and a total width \f$W\f$, \em
* apportionment means to calculate column widths \f$w_{i}\f$, with
*
* \f[e_{i,\min} \le w_{i} \le e_{i,\max}\f]
*
* and
*
* \f[\sum w_{i} = W\f]
*
* There are different algorithms for apportionment, a simple one is
* recommended in the HTML 4.0.1 specification
* (http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.5.2.2):
*
* \f[w_{i} = e_{i,\min} +
* {e_{i,\max} - e_{i,\min}\over\sum e_{i,\max} - \sum e_{i,\min}}
* (W - \sum e_{i,\min})\f]
*
* This one is used currently, but another one will be used soon, which is
* described below. The rest of this chapter is independent of the exact
* apportionment algorithm.
*
* When referring to the apportionment function, we will call it
* \f$a_i (W, (e_{i,\min}), (e_{i,\min}))\f$ and write
* something like this:
*
* \f[w_{i} = a_i (W, (e_{i,\min}), (e_{i,\max})) \f]
*
* It is implemented by dw::Table::apportion.
*
* <h4>Column Extremes</h4>
*
* \sa\ref rounding-errors
*
* The sizes, which all other sizes depend on, are column extremes, which
* define, how wide a column may be at min and at max. They are
* calculated in the following way:
*
* <ol>
* <li> First, only cells with colspan = 1 are regarded:
* \f[ e_{\hbox{base},i,\min} = \max \{ e_{\hbox{cell},i,j,\min} \} \f]
* \f[ e_{\hbox{base},i,\max} = \max \{ e_{\hbox{cell},i,j,\max} \} \f]
* only for cells \f$(i, j)\f$ with colspan = 1.
*
* <li> Then,
* \f$e_{\hbox{span},i,\min}\f$ (but not \f$e_{\hbox{span},i,\max}\f$)
* are calculated from cells with colspan > 1. (In the following formulas,
* the cell at \f$(i_1, j)\f$ always span from \f$i_1\f$ to \f$i_2\f$.)
* If the minimal width of the column exceeds the sum of the column minima
* calculated in the last step:
* \f[e_{\hbox{cell},i_1,j,\min} >
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\min}\f]
* then the minimal width of this cell is apportioned to the columns:
*
* <ul>
* <li> If the minimal width of this cell also exceeds the sum of the
* column maxima:
* \f[e_{\hbox{cell},i_1,j,\min} >
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}\f]
* then \f$e_{\hbox{cell},i_1,j,\min}\f$ is apportioned in a simple
* way:
* \f[e_{\hbox{span},i,j,\min} =
* e_{\hbox{base},i,\max}
* {e_{\hbox{span},i,j,\min} \over
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}}\f]
* <li> Otherwise, the apportionment function is used:
* \f[e_{\hbox{span},i,j,\min} =
* a_i (e_{\hbox{cell},i_1,j,\min},
* (e_{\hbox{cell},i_1,j,\min} \ldots
* e_{\hbox{cell},i_2,j,\min}),
* (e_{\hbox{cell},i_1,j,\max} \ldots
* e_{\hbox{cell},i_2,j,\max}))\f]
* </ul>
*
* After this, \f$e_{\hbox{span},i,\min}\f$ is then the maximum of all
* \f$e_{\hbox{span},i,j,\min}\f$.
*
* <li> Finally, the maximum of both is used.
* \f[ e_{i,\min} =
* \max \{ e_{\hbox{base},i,\min}, e_{\hbox{span},i,\min} \} \f]
* \f[ e_{i,\max} =
* \max \{ e_{\hbox{base},i,\max}, e_{i,\min} \} \f]
* For the maxima, there is no \f$e_{\hbox{span},i,\max}\f$, but it has to
* be assured, that the maximum is always greater than or equal to the
* minimum.
*
* </ol>
*
* Generally, if absolute widths are specified, they are, instead of the
* results of dw::core::Widget::getExtremes, taken for the minimal and
* maximal width of a cell (minus the box difference, i.e. the difference
* between content size and widget size). If the content width
* specification is smaller than the minimal content width of the widget
* (determined by dw::core::Widget::getExtremes), the latter is used
* instead.
*
* If percentage widths are specified, they are also collected, as column
* maxima. A similar method as for the extremes is used, for cells with
* colspan > 1:
*
* \f[w_{\hbox{span},i,j,\%} =
* a_i (w_{\hbox{cell},i_1,j,\%},
* (e_{\hbox{cell},i_1,j,\min} \ldots e_{\hbox{cell},i_2,j,\min}),
* (e_{\hbox{cell},i_1,j,\max} \ldots e_{\hbox{cell},i_2,j,\max}))\f]
*
* <h4>Cell Sizes</h4>
*
* <h5>Determining the Width of the Table</h5>
*
* The total width is
*
* <ul>
* <li> the specified absolute width of the table, when given, or
* <li> the available width (set by dw::Table::setWidth [TODO outdated]) times
* the specified percentage width of t(at max 100%), if the latter is
* given, or
* <li> otherwise the available width.
* </ul>
*
* In any case, it is corrected, if it is less than the minimal width
* (but not if it is greater than the maximal width).
*
* \bug The parentheses is not fully clear, look at the old code.
*
* Details on differences because of styles are omitted. Below, this
* total width is called \f$W\f$.
*
* <h5>Evaluating percentages</h5>
*
* The following algorithms are used to solve collisions between
* different size specifications (absolute and percentage). Generally,
* inherent sizes and specified absolute sizes are preferred.
*
* <ol>
* <li> First, calculate the sum of the minimal widths, for columns, where
* no percentage width has been specified. The difference to the total
* width is at max available to the columns with percentage width
* specifications:
* \f[W_{\hbox{columns}_\%,\hbox{available}} = W - \sum e_{i,\min}\f]
* with only those columns \f$i\f$ with no percentage width specification.
*
* <li> Then, calculate the sum of the widths, which the columns with
* percentage width specification would allocate, when fully adhering to
* them:
* \f[W_{\hbox{columns}_\%,\hbox{best}} = W \sum w_{i,\%}\f]
* with only those columns \f$i\f$ with a percentage width specification.
*
* <li> Two cases are distinguished:
*
* <ul>
* <li> \f$W_{\hbox{columns}_\%,\hbox{available}} \ge
* W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the
* percentage widths can be used without any modification, by
* setting the extremes:
* \f[e_{i,\min} = e_{i,\max} = W w_{i,\%}\f]
* for only those columns \f$i\f$ with a percentage width
* specification.
*
* <li> \f$W_{\hbox{columns}_\%,\hbox{available}} <
* W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the widths
* for these columns must be cut down:
* \f[e_{i,\min} = e_{i,\max} =
* w_{i,\%}
* {W_{\hbox{columns}_\%,\hbox{available}} \over
* w_{\hbox{total},\%}}\f]
* with
* \f[w_{\hbox{total},\%} = \sum w_{i,\%}\f]
* in both cases for only those columns \f$i\f$ with a percentage
* width specification.
* </ul>
* </ol>
*
* (\f$e_{i,\min}\f$ and \f$e_{i,\max}\f$ are set \em temporarily here,
* the notation should be a bit clearer.)
*
*
* <h5>Column Widths</h5>
*
* The column widths are now simply calculated by applying the
* apportionment function.
*
*
* <h5>Row Heights</h5>
*
* ...
*
* <h3>Alternative Apportionment Algorithm</h3>
*
* The algorithm described here tends to result in more homogeneous column
* widths.
*
* The following rule leads to well-defined \f$w_{i}\f$: All columns
* \f$i\f$ have have the same width \f$w\f$, except:
* <ul>
* <li> \f$w < e_{i,\min}\f$, or
* <li> \f$w > e_{i,\max}\f$.
* </ul>
*
* Furthermore, \f$w\f$ is
* <ul>
* <li> less than all \f$e_{i,\min}\f$ of columns not having \f$w\f$ as
* width, and
* <li> greater than all \f$e_{i,\min}\f$ of columns not having \f$w\f$ as
* width.
* </ul>
*
* Of course, \f$\sum w_{i} = W\f$ must be the case.
*
* Based on an initial value \f$w = {W\over n}\f$, \f$w\f$ can iteratively
* adjusted, based on these rules.
*
*
* <h3>Borders, Paddings, Spacing</h3>
*
* Currently, DwTable supports only the separated borders model (see CSS
* specification). Borders, paddings, spacing is done by creating
* dw::core::style::Style structures with values equivalent to following CSS:
*
* <pre>
* TABLE {
* border: outset \em table-border;
* border-collapse: separate;
* border-spacing: \em table-cellspacing;
* background-color: \em table-bgcolor;
* }
*
* TD TH {
* border: inset \em table-border;
* padding: \em table-cellspacing;
* background-color: \em td/th-bgcolor;
* }
* </pre>
*
* Here, \em foo-bar refers to the attribute \em bar of the tag \em foo foo.
* Look at the HTML parser for more details.
*/
class Table: public oof::OOFAwareWidget
{
private:
struct Cell {
core::Widget *widget;
int colspanOrig, colspanEff, rowspan;
};
struct SpanSpace {
int startCol, startRow; // where the cell starts
};
struct Child
{
enum {
CELL, // cell starts here
SPAN_SPACE // part of a spanning cell
} type;
union {
struct Cell cell;
struct SpanSpace spanSpace;
};
};
class TableIterator: public OOFAwareWidgetIterator
{
protected:
int numContentsInFlow ();
void getContentInFlow (int index, core::Content *content);
public:
TableIterator (Table *table, core::Content::Type mask, bool atEnd);
lout::object::Object *clone();
void highlight (int start, int end, core::HighlightLayer layer);
void unhighlight (int direction, core::HighlightLayer layer);
void getAllocation (int start, int end, core::Allocation *allocation);
};
friend class TableIterator;
static bool adjustTableMinWidth;
bool limitTextWidth, rowClosed;
int numRows, numCols, curRow, curCol;
lout::misc::SimpleVector<Child*> *children;
int redrawX, redrawY;
/**
* \brief The extremes of all columns.
*/
lout::misc::SimpleVector<core::Extremes> *colExtremes;
/**
* \brief Whether the column itself (in the future?) or at least one
* cell in this column or spanning over this column has CSS
* 'width' specified.
*
* Filled by forceCalcColumnExtremes(), since it is needed to
* calculate the column widths.
*/
lout::misc::SimpleVector<bool> *colWidthSpecified;
int numColWidthSpecified;
/**
* \brief Whether the column itself (in the future?) or at least one
* cell in this column or spanning over this column has CSS
* 'width' specified *as percentage value*.
*
* Filled by forceCalcColumnExtremes(), since it is needed to
* calculate the column widths.
*/
lout::misc::SimpleVector<bool> *colWidthPercentage;
int numColWidthPercentage;
/**
* \brief The widths of all columns.
*/
lout::misc::SimpleVector<int> *colWidths;
/**
* Row cumulative height array: cumHeight->size() is numRows + 1,
* cumHeight->get(0) is 0, cumHeight->get(numRows) is the total table
* height.
*/
lout::misc::SimpleVector<int> *cumHeight;
/**
* If a Cell has rowspan > 1, it goes into this array
*/
lout::misc::SimpleVector<int> *rowSpanCells;
lout::misc::SimpleVector<int> *baseline;
lout::misc::SimpleVector<core::style::Style*> *rowStyle;
bool colWidthsUpToDateWidthColExtremes;
enum ExtrMod { MIN, MIN_INTR, MIN_MIN, MAX_MIN, MAX, MAX_INTR, DATA };
const char *getExtrModName (ExtrMod mod);
int getExtreme (core::Extremes *extremes, ExtrMod mod);
void setExtreme (core::Extremes *extremes, ExtrMod mod, int value);
int getColExtreme (int col, ExtrMod mod, void *data);
inline void setColExtreme (int col, ExtrMod mod, void *data, int value);
inline bool childDefined(int n)
{
return n < children->size() && children->get(n) != NULL &&
children->get(n)->type != Child::SPAN_SPACE;
}
int calcAvailWidthForDescendant (Widget *child);
void reallocChildren (int newNumCols, int newNumRows);
void calcCellSizes (bool calcHeights);
void forceCalcCellSizes (bool calcHeights);
void actuallyCalcCellSizes (bool calcHeights);
void apportionRowSpan ();
void forceCalcColumnExtremes ();
void calcExtremesSpanMultiCols (int col, int cs,
core::Extremes *cellExtremes,
ExtrMod minExtrMod, ExtrMod maxExtrMod,
void *extrData);
void calcAdjustmentWidthSpanMultiCols (int col, int cs,
core::Extremes *cellExtremes);
void apportion2 (int totalWidth, int firstCol, int lastCol,
ExtrMod minExtrMod, ExtrMod maxExtrMod, void *extrData,
lout::misc::SimpleVector<int> *dest, int destOffset);
void setCumHeight (int row, int value)
{
if (value != cumHeight->get (row)) {
redrawY = lout::misc::min ( redrawY, value );
cumHeight->set (row, value);
}
}
protected:
void sizeRequestSimpl (core::Requisition *requisition);
void getExtremesSimpl (core::Extremes *extremes);
void sizeAllocateImpl (core::Allocation *allocation);
void resizeDrawImpl ();
bool getAdjustMinWidth () { return Table::adjustTableMinWidth; }
int getAvailWidthOfChild (Widget *child, bool forceValue);
void containerSizeChangedForChildren ();
bool affectsSizeChangeContainerChild (Widget *child);
bool usesAvailWidth ();
bool isBlockLevel ();
void drawLevel (core::View *view, core::Rectangle *area, int level,
core::DrawingContext *context);
Widget *getWidgetAtPointLevel (int x, int y, int level,
core::GettingWidgetAtPointContext *context);
//bool buttonPressImpl (core::EventButton *event);
//bool buttonReleaseImpl (core::EventButton *event);
//bool motionNotifyImpl (core::EventMotion *event);
void removeChild (Widget *child);
public:
static int CLASS_ID;
inline static void setAdjustTableMinWidth (bool adjustTableMinWidth)
{ Table::adjustTableMinWidth = adjustTableMinWidth; }
inline static bool getAdjustTableMinWidth ()
{ return Table::adjustTableMinWidth; }
Table(bool limitTextWidth);
~Table();
int applyPerWidth (int containerWidth, core::style::Length perWidth);
int applyPerHeight (int containerHeight, core::style::Length perHeight);
core::Iterator *iterator (core::Content::Type mask, bool atEnd);
void addCell (Widget *widget, int colspan, int rowspan);
void addRow (core::style::Style *style);
AlignedTableCell *getCellRef ();
};
} // namespace dw
#endif // __DW_TABLE_HH__

89
dw/table_iterator.cc Normal file
View File

@ -0,0 +1,89 @@
/*
* Dillo Widget
*
* Copyright 2005-2007, 2014 Sebastian Geerken <sgeerken@dillo.org>
*
* (This file was originally part of textblock.cc.)
*
* 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.
*
* 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 "table.hh"
using namespace lout;
namespace dw {
Table::TableIterator::TableIterator (Table *table,
core::Content::Type mask, bool atEnd):
OOFAwareWidgetIterator (table, mask, atEnd, table->children->size ())
{
}
object::Object *Table::TableIterator::clone()
{
TableIterator *tIt =
new TableIterator ((Table*)getWidget(), getMask(), false);
cloneValues (tIt);
return tIt;
}
void Table::TableIterator::highlight (int start, int end,
core::HighlightLayer layer)
{
if (inFlow ()) {
/** todo Needs this an implementation? */
} else
highlightOOF (start, end, layer);
}
void Table::TableIterator::unhighlight (int direction,
core::HighlightLayer layer)
{
if (inFlow ()) {
// ???
} else
unhighlightOOF (direction, layer);
}
void Table::TableIterator::getAllocation (int start, int end,
core::Allocation *allocation)
{
if (inFlow ()) {
/** \bug Not implemented. */
} else
getAllocationOOF (start, end, allocation);
}
int Table::TableIterator::numContentsInFlow ()
{
return ((Table*)getWidget())->children->size ();
}
void Table::TableIterator::getContentInFlow (int index,
core::Content *content)
{
Table *table = (Table*)getWidget();
if (table->children->get(index) != NULL &&
table->children->get(index)->type == Child::CELL) {
content->type = core::Content::WIDGET_IN_FLOW;
content->widget = table->children->get(index)->cell.widget;
} else
content->type = core::Content::INVALID;
}
} // namespace dw

129
dw/tablecell.cc Normal file
View File

@ -0,0 +1,129 @@
/*
* Dillo Widget
*
* Copyright 2014 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.
*
* 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 "tablecell.hh"
#include "table.hh"
using namespace lout;
namespace dw {
/**
* \brief Provided some common implementations of virtual widget
* methods.
*
* Once I understand how diamond inheritance works, a class TableCell
* will be provided, from which SimpleTableCell and AlignedTableCell
* will inherit, additionally (in a multiple way).
*/
namespace tablecell {
bool getAdjustMinWidth ()
{
return Table::getAdjustTableMinWidth ();
}
bool isBlockLevel ()
{
return false;
}
int correctAvailWidthOfChild (core::Widget *widget, core::Widget *child,
int width, bool forceValue)
{
DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell::correctAvailWidthOfChild",
"%p, %d, %s", child, width, forceValue ? "true" : "false");
// Make sure that this width does not exceed the width of the table
// cell (minus margin/border/padding).
if (width != -1) {
int thisWidth = widget->getAvailWidth (forceValue);
DBG_OBJ_MSGF_O ("resize", 1, widget, "thisWidth = %d", thisWidth);
if (thisWidth != -1)
width =
lout::misc::max (lout::misc::min (width,
thisWidth
- widget->boxDiffWidth ()),
0);
}
DBG_OBJ_MSGF_O ("resize", 1, widget, "=> %d", width);
DBG_OBJ_LEAVE_O (widget);
return width;
}
int correctAvailHeightOfChild (core::Widget *widget, core::Widget *child,
int height, bool forceValue)
{
// Something to do?
return height;
}
void correctCorrectedRequisitionOfChild (core::Widget *widget,
core::Widget *child,
core::Requisition *requisition,
void (*splitHeightFun) (int, int*,
int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight)
{
DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell::correctRequisitionOfChild",
"%p, %d * (%d + %d), ..., %s, %s",
child, requisition->width, requisition->ascent,
requisition->descent, misc::boolToStr (allowDecreaseWidth),
misc::boolToStr (allowDecreaseHeight));
// Make sure that this width does not exceed the width of the table
// cell (minus margin/border/padding).
int thisWidth = widget->getAvailWidth (true);
DBG_OBJ_MSGF_O ("resize", 1, widget, "thisWidth = %d", thisWidth);
int newWidth =
lout::misc::max (lout::misc::min (requisition->width,
thisWidth - widget->boxDiffWidth ()),
0);
requisition->width = allowDecreaseWidth ?
newWidth : misc::max (requisition->width, newWidth);
DBG_OBJ_LEAVE_O (widget);
}
void correctCorrectedExtremesOfChild (core::Widget *widget, core::Widget *child,
core::Extremes *extremes,
bool useAdjustmentWidth)
{
// Something to do?
}
int applyPerWidth (core::Widget *widget, int containerWidth,
core::style::Length perWidth)
{
return core::style::multiplyWithPerLength (containerWidth, perWidth);
}
int applyPerHeight (core::Widget *widget, int containerHeight,
core::style::Length perHeight)
{
return core::style::multiplyWithPerLength (containerHeight, perHeight);
}
} // namespace dw
} // namespace dw

42
dw/tablecell.hh Normal file
View File

@ -0,0 +1,42 @@
#ifndef __DW_TABLECELL_HH__
#define __DW_TABLECELL_HH__
#include "core.hh"
namespace dw {
namespace tablecell {
inline bool usesMaxGeneratorWidth () { return true; }
bool getAdjustMinWidth ();
bool isBlockLevel ();
int correctAvailWidthOfChild (core::Widget *widget, core::Widget *child,
int width, bool forceValue);
int correctAvailHeightOfChild (core::Widget *widget, core::Widget *child,
int height, bool forceValue);
void correctCorrectedRequisitionOfChild (core::Widget *widget,
core::Widget *child,
core::Requisition *requisition,
void (*splitHeightFun) (int, int*,
int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight);
void correctCorrectedExtremesOfChild (core::Widget *widget, core::Widget *child,
core::Extremes *extremes,
bool useAdjustmentWidth);
int applyPerWidth (core::Widget *widget, int containerWidth,
core::style::Length perWidth);
int applyPerHeight (core::Widget *widget, int containerHeight,
core::style::Length perHeight);
inline bool adjustExtraSpaceWhenCorrectingRequisitionByOOF () { return false; }
} // namespace dw
} // namespace dw
#endif // __DW_TABLECELL_HH__

3377
dw/textblock.cc Normal file

File diff suppressed because it is too large Load Diff

1015
dw/textblock.hh Normal file

File diff suppressed because it is too large Load Diff

285
dw/textblock_iterator.cc Normal file
View File

@ -0,0 +1,285 @@
/*
* Dillo Widget
*
* Copyright 2005-2007, 2012-2013 Sebastian Geerken <sgeerken@dillo.org>
*
* (Parts of this file were originally part of textblock.cc.)
*
* 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.
*
* 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 "textblock.hh"
#include "../lout/msg.h"
#include "../lout/misc.hh"
#include <stdio.h>
#include <math.h>
using namespace lout;
namespace dw {
Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
core::Content::Type mask,
bool atEnd):
OOFAwareWidgetIterator (textblock, mask, atEnd, textblock->words->size ())
{
}
Textblock::TextblockIterator
*Textblock::TextblockIterator::createWordIndexIterator (Textblock *textblock,
core::Content::Type
mask,
int wordIndex)
{
TextblockIterator *tbIt = new TextblockIterator (textblock, mask, false);
tbIt->setValues (0, wordIndex);
return tbIt;
}
object::Object *Textblock::TextblockIterator::clone()
{
TextblockIterator *tbIt =
new TextblockIterator ((Textblock*)getWidget(), getMask(), false);
cloneValues (tbIt);
return tbIt;
}
void Textblock::TextblockIterator::highlight (int start, int end,
core::HighlightLayer layer)
{
DBG_OBJ_ENTER_O ("iterator", 0, getWidget (), "TextblockIterator::highlight",
"..., %d, %d, %d", start, end, layer);
DBG_IF_RTFL {
misc::StringBuffer sb;
intoStringBuffer (&sb);
DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "iterator: %s",
sb.getChars ());
}
if (inFlow ()) {
DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "in-flow index: %d",
getInFlowIndex ());
Textblock *textblock = (Textblock*)getWidget();
int index = getInFlowIndex (), index1 = index, index2 = index;
int oldStartIndex = textblock->hlStart[layer].index;
int oldStartChar = textblock->hlStart[layer].nChar;
int oldEndIndex = textblock->hlEnd[layer].index;
int oldEndChar = textblock->hlEnd[layer].nChar;
if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) {
/* nothing is highlighted */
textblock->hlStart[layer].index = index;
textblock->hlEnd[layer].index = index;
}
if (textblock->hlStart[layer].index >= index) {
index2 = textblock->hlStart[layer].index;
textblock->hlStart[layer].index = index;
textblock->hlStart[layer].nChar = start;
}
if (textblock->hlEnd[layer].index <= index) {
index2 = textblock->hlEnd[layer].index;
textblock->hlEnd[layer].index = index;
textblock->hlEnd[layer].nChar = end;
}
DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "index",
textblock->hlStart[layer].index);
DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "nChar",
textblock->hlStart[layer].nChar);
DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "index",
textblock->hlEnd[layer].index);
DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "nChar",
textblock->hlEnd[layer].nChar);
if (oldStartIndex != textblock->hlStart[layer].index ||
oldStartChar != textblock->hlStart[layer].nChar ||
oldEndIndex != textblock->hlEnd[layer].index ||
oldEndChar != textblock->hlEnd[layer].nChar)
textblock->queueDrawRange (index1, index2);
} else {
highlightOOF (start, end, layer);
}
DBG_OBJ_LEAVE_O (getWidget ());
}
void Textblock::TextblockIterator::unhighlight (int direction,
core::HighlightLayer layer)
{
DBG_OBJ_ENTER_O ("iterator", 0, getWidget (),
"TextblockIterator/unhighlight", "..., %d, %d",
direction, layer);
DBG_IF_RTFL {
misc::StringBuffer sb;
intoStringBuffer (&sb);
DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "iterator: %s",
sb.getChars ());
}
if (inFlow ()) {
DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "in-flow index: %d",
getInFlowIndex ());
Textblock *textblock = (Textblock*)getWidget();
int index = getInFlowIndex (), index1 = index, index2 = index;
if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index)
return;
int oldStartIndex = textblock->hlStart[layer].index;
int oldStartChar = textblock->hlStart[layer].nChar;
int oldEndIndex = textblock->hlEnd[layer].index;
int oldEndChar = textblock->hlEnd[layer].nChar;
if (direction == 0) {
index1 = textblock->hlStart[layer].index;
index2 = textblock->hlEnd[layer].index;
textblock->hlStart[layer].index = 1;
textblock->hlEnd[layer].index = 0;
} else if (direction > 0 && textblock->hlStart[layer].index <= index) {
index1 = textblock->hlStart[layer].index;
textblock->hlStart[layer].index = index + 1;
textblock->hlStart[layer].nChar = 0;
} else if (direction < 0 && textblock->hlEnd[layer].index >= index) {
index1 = textblock->hlEnd[layer].index;
textblock->hlEnd[layer].index = index - 1;
textblock->hlEnd[layer].nChar = INT_MAX;
}
DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "index",
textblock->hlStart[layer].index);
DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "nChar",
textblock->hlStart[layer].nChar);
DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "index",
textblock->hlEnd[layer].index);
DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "nChar",
textblock->hlEnd[layer].nChar);
if (oldStartIndex != textblock->hlStart[layer].index ||
oldStartChar != textblock->hlStart[layer].nChar ||
oldEndIndex != textblock->hlEnd[layer].index ||
oldEndChar != textblock->hlEnd[layer].nChar)
textblock->queueDrawRange (index1, index2);
} else
unhighlightOOF (direction, layer);
DBG_OBJ_LEAVE_O (getWidget ());
}
void Textblock::TextblockIterator::getAllocation (int start, int end,
core::Allocation *allocation)
{
if (inFlow ()) {
Textblock *textblock = (Textblock*)getWidget();
int index = getInFlowIndex ();
Word *word = textblock->words->getRef (index);
int firstWordOfLine, textOffset, lineYOffsetCanvas, lineBorderAscent;
int lineIndex = textblock->findLineOfWord (index);
// It may be that the line does not exist yet.
if (lineIndex != -1) {
// Line exists: simple.
Line *line = textblock->lines->getRef (lineIndex);
firstWordOfLine = line->firstWord;
textOffset = line->textOffset;
lineYOffsetCanvas = textblock->lineYOffsetCanvas (line);
lineBorderAscent = line->borderAscent;
} else {
// Line does not exist. Calculate the values in a similar way as in
// Textblock::addLine().
Line *prevLine = textblock->lines->size () > 0 ?
textblock->lines->getLastRef () : NULL;
firstWordOfLine = prevLine ? prevLine->lastWord + 1 : 0;
// The variable textOffset, defined below, is what Line::leftOffset
// will be for the next line; Line::textOffset itself cannot be
// calculated before the line is complete.
bool regardBorder =
textblock->mustBorderBeRegarded (textblock->lines->size ());
textOffset =
misc::max (regardBorder ? textblock->newLineLeftBorder : 0,
textblock->boxOffsetX () + textblock->leftInnerPadding
+ (textblock->lines->size () == 0 ?
textblock->line1OffsetEff : 0));
lineYOffsetCanvas = textblock->yOffsetOfLineToBeCreated ();
lineBorderAscent = 0;
for (int i = firstWordOfLine; i < textblock->words->size (); i++) {
Word *w = textblock->words->getRef (i);
int borderAscent =
w->content.type == core::Content::WIDGET_IN_FLOW ?
w->size.ascent - w->content.widget->getStyle()->margin.top :
w->size.ascent;
lineBorderAscent = misc::max (lineBorderAscent, borderAscent);
}
}
allocation->x = textblock->allocation.x + textOffset;
for (int i = firstWordOfLine; i < index; i++) {
Word *w = textblock->words->getRef(i);
allocation->x += w->size.width + w->effSpace;
}
if (start > 0 && word->content.type == core::Content::TEXT) {
allocation->x += textblock->textWidth (word->content.text, 0, start,
word->style,
word->flags & Word::WORD_START,
(word->flags & Word::WORD_END)
&& word->content.text[start]
== 0);
}
allocation->y = lineYOffsetCanvas + lineBorderAscent - word->size.ascent;
allocation->width = word->size.width;
if (word->content.type == core::Content::TEXT) {
int wordEnd = strlen(word->content.text);
if (start > 0 || end < wordEnd) {
end = misc::min(end, wordEnd); /* end could be INT_MAX */
allocation->width =
textblock->textWidth (word->content.text, start, end - start,
word->style,
(word->flags & Word::WORD_START)
&& start == 0,
(word->flags & Word::WORD_END)
&& word->content.text[end] == 0);
}
}
allocation->ascent = word->size.ascent;
allocation->descent = word->size.descent;
} else
getAllocationOOF (start, end, allocation);
}
int Textblock::TextblockIterator::numContentsInFlow ()
{
return ((Textblock*)getWidget())->words->size ();
}
void Textblock::TextblockIterator::getContentInFlow (int index,
core::Content *content)
{
*content = ((Textblock*)getWidget())->words->getRef(index)->content;
}
} // namespace dw

2295
dw/textblock_linebreaking.cc Normal file

File diff suppressed because it is too large Load Diff

199
dw/tools.cc Normal file
View File

@ -0,0 +1,199 @@
#include "core.hh"
namespace dw {
namespace core {
using namespace lout::misc;
SizeParams::SizeParams ()
{
DBG_OBJ_CREATE ("dw::core::SizeParams");
init ();
debugPrint ();
}
SizeParams::SizeParams (int numPos, Widget **references, int *x, int *y)
{
DBG_OBJ_CREATE ("dw::core::SizeParams");
init ();
fill (numPos, references, x, y);
debugPrint ();
}
SizeParams::SizeParams (const SizeParams &other)
{
DBG_OBJ_CREATE ("dw::core::SizeParams");
init ();
fill (other.numPos, other.references, other.x, other.y);
debugPrint ();
}
SizeParams::~SizeParams ()
{
cleanup ();
DBG_OBJ_DELETE ();
}
SizeParams &SizeParams::operator=(const SizeParams &other)
{
cleanup ();
init ();
fill (other.numPos, other.references, other.x, other.y);
debugPrint ();
return *this;
}
void SizeParams::init ()
{
numPos = 0;
references = NULL;
x = y = NULL;
}
void SizeParams::cleanup ()
{
if (references)
delete[] references;
if (x)
delete[] x;
if (y)
delete[] y;
init ();
}
void SizeParams::fill (int numPos, Widget **references, int *x, int *y)
{
DBG_OBJ_ENTER ("resize", 0, "fill", "%d, ...", numPos);
cleanup ();
this->numPos = numPos;
this->references = new Widget*[numPos];
this->x = new int[numPos];
this->y = new int[numPos];
for (int i = 0; i < numPos; i++) {
this->references[i] = references[i];
this->x[i] = x[i];
this->y[i] = y[i];
}
debugPrint ();
DBG_OBJ_LEAVE ();
}
void SizeParams::forChild (Widget *parent, Widget *child, int xRel, int yRel,
SizeParams *childParams)
{
DBG_OBJ_ENTER ("resize", 0, "forChild", "%p, %p, %d, %d, %p",
parent, child, xRel, yRel, childParams);
childParams->cleanup ();
int numChildReferences = child->numSizeRequestReferences ();
childParams->numPos = 0;
childParams->references = new Widget*[numChildReferences];
childParams->x = new int[numChildReferences];
childParams->y = new int[numChildReferences];
for (int i = 0; i < numChildReferences; i++) {
Widget *childReference = child->sizeRequestReference (i);
if (childReference == parent) {
childParams->references[childParams->numPos] = childReference;
childParams->x[childParams->numPos] = xRel;
childParams->y[childParams->numPos] = yRel;
childParams->numPos++;
} else {
bool found = false;
for (int j = 0; !found && j < numPos; j++) {
if (childReference == references[j]) {
found = true;
childParams->references[childParams->numPos] = childReference;
childParams->x[childParams->numPos] = x[j] + xRel;
childParams->y[childParams->numPos] = y[j] + yRel;
childParams->numPos++;
}
}
}
}
childParams->debugPrint ();
DBG_OBJ_LEAVE ();
}
bool SizeParams::findReference (Widget *reference, int *x, int *y)
{
DBG_OBJ_ENTER ("resize", 0, "findReference", "%p", reference);
bool found = false;
for (int i = 0; i < numPos && !found; i++) {
if (reference == references[i]) {
if (x)
*x = this->x[i];
if (y)
*y = this->y[i];
found = true;
}
}
if (found) {
if (x && y)
DBG_OBJ_LEAVE_VAL ("true, x = %d, y = %d", *x, *y);
else if (x)
DBG_OBJ_LEAVE_VAL ("true, x = %d", *x);
else if (y)
DBG_OBJ_LEAVE_VAL ("true, y = %d", *y);
else
DBG_OBJ_LEAVE_VAL ("%s", "true");
} else
DBG_OBJ_LEAVE_VAL ("%s", "false");
return found;
}
/**
* Compares two instances, but considers a change in the order of the reference
* widgets as equivalent.
*/
bool SizeParams::isEquivalent (SizeParams *other)
{
DBG_OBJ_ENTER ("resize", 0, "isEquivalent", "%p", other);
bool result;
if (numPos != other->numPos)
result = false;
else {
result = true;
for (int i = 0; result && i < numPos; i++) {
bool otherFound = false;
for (int j = 0; !otherFound && j < numPos; j++) {
DBG_OBJ_MSGF ("resize", 1,
"#%d = (%p, %d, %d) vs. #%d = (%p, %d, %d)",
i, references[i], x[i], y[i], j, other->references[j],
other->x[j], other->y[j]);
if (references[i] == other->references[j]) {
otherFound = true;
if (!(x[i] == other->x[j] && y[i] == other->y[j]))
result = false;
}
}
if (!otherFound)
result = false;
}
}
DBG_OBJ_LEAVE_VAL ("%s", boolToStr (result));
return result;
}
} // namespace core
} // namespace dw

66
dw/tools.hh Normal file
View File

@ -0,0 +1,66 @@
#ifndef __DW_TOOLS_HH__
#define __DW_TOOLS_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
#include "core.hh"
#include "../lout/debug.hh"
namespace dw {
namespace core {
/**
* \brief Hold arguments passed to dw::core::Widget::sizeRequest and
* dw::core::Widget::getExtremes, as described in \ref dw-size-request-pos.
*/
class SizeParams
{
private:
int numPos;
Widget **references;
int *x, *y;
void init ();
void cleanup ();
inline void debugPrint () {
DBG_IF_RTFL {
DBG_OBJ_SET_NUM ("numPos", numPos);
for (int i = 0; i < numPos; i++) {
DBG_OBJ_ARRSET_PTR ("references", i, references[i]);
DBG_OBJ_ARRSET_NUM ("x", i, x[i]);
DBG_OBJ_ARRSET_NUM ("y", i, y[i]);
}
}
}
public:
SizeParams ();
SizeParams (int numPos, Widget **references, int *x, int *y);
SizeParams (const SizeParams &other);
~SizeParams ();
SizeParams &operator=(const SizeParams &other);
void fill (int numPos, Widget **references, int *x, int *y);
void forChild (Widget *parent, Widget *child, int xRel, int yRel,
SizeParams *childParams);
bool findReference (Widget *reference, int *x, int *y);
bool isEquivalent (SizeParams *other);
inline int getNumPos () { return numPos; }
inline Widget **getReferences () { return references; }
inline int *getX () { return x; }
inline int *getY () { return y; }
inline Widget *getReference (int i) { return references[i]; }
inline int getX (int i) { return x[i]; }
inline int getY (int i) { return y[i]; }
};
} // namespace core
} // namespace dw
#endif // __DW_TOOLS_HH__

359
dw/types.cc Normal file
View File

@ -0,0 +1,359 @@
/*
* Dillo Widget
*
* 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.
*
* 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 "core.hh"
#include "../lout/msg.h"
using namespace lout;
namespace dw {
namespace core {
Rectangle::Rectangle (int x, int y, int width, int height)
{
this->x = x;
this->y = y;
this->width = width;
this->height = height;
}
/*
* Draw rectangle in view relative to point (x,y).
*/
void Rectangle::draw (core::View *view, core::style::Style *style, int x,int y)
{
const bool filled = false;
view->drawRectangle(style->color, core::style::Color::SHADING_NORMAL,filled,
x + this->x, y + this->y, this->width, this->height);
}
/**
* Return whether this rectangle and otherRect intersect. If yes,
* return the intersection rectangle in dest.
*/
bool Rectangle::intersectsWith (Rectangle *otherRect, Rectangle *dest)
{
bool doIntersect =
this->x < otherRect->x + otherRect->width &&
this->y < otherRect->y + otherRect->height &&
otherRect->x < this->x + this->width &&
otherRect->y < this->y + this->height;
if (doIntersect) {
dest->x = misc::max(this->x, otherRect->x);
dest->y = misc::max(this->y, otherRect->y);
dest->width = misc::min(this->x + this->width,
otherRect->x + otherRect->width) - dest->x;
dest->height = misc::min(this->y + this->height,
otherRect->y + otherRect->height) - dest->y;
} else {
dest->x = dest->y = dest->width = dest->height = 0;
}
return doIntersect;
}
/*
* Return whether this is a subset of otherRect.
*/
bool Rectangle::isSubsetOf (Rectangle *otherRect)
{
return
x >= otherRect->x &&
y >= otherRect->y &&
x + width <= otherRect->x + otherRect->width &&
y + height <= otherRect->y + otherRect->height;
}
bool Rectangle::isPointWithin (int x, int y)
{
return
x >= this->x && y >= this->y &&
x < this->x + width && y < this->y + height;
}
// ----------------------------------------------------------------------
Circle::Circle (int x, int y, int radius)
{
this->x = x;
this->y = y;
this->radius = radius;
}
/*
* Draw circle in view relative to point (x,y).
*/
void Circle::draw (core::View *view, core::style::Style *style, int x, int y)
{
const bool filled = false;
view->drawArc(style->color, core::style::Color::SHADING_NORMAL, filled,
x + this->x, y + this->y, 2 * this->radius, 2 * this->radius,
0, 360);
}
bool Circle::isPointWithin (int x, int y)
{
return
(x - this->x) * (x - this->x) + (y - this->y) * (y - this->y)
<= radius * radius;
}
// ----------------------------------------------------------------------
Polygon::Polygon ()
{
points = new misc::SimpleVector<Point> (8);
minx = miny = 0xffffff;
maxx = maxy = -0xffffff;
}
Polygon::~Polygon ()
{
delete points;
}
/*
* Draw polygon in view relative to point (x,y).
*/
void Polygon::draw (core::View *view, core::style::Style *style, int x, int y)
{
if (points->size()) {
int i;
const bool filled = false, convex = false;
Point *pointArray = (Point *)malloc(points->size()*sizeof(struct Point));
for (i = 0; i < points->size(); i++) {
pointArray[i].x = x + points->getRef(i)->x;
pointArray[i].y = y + points->getRef(i)->y;
}
view->drawPolygon(style->color, core::style::Color::SHADING_NORMAL,
filled, convex, pointArray, i);
free(pointArray);
}
}
void Polygon::addPoint (int x, int y)
{
points->increase ();
points->getRef(points->size () - 1)->x = x;
points->getRef(points->size () - 1)->y = y;
minx = misc::min(minx, x);
miny = misc::min(miny, y);
maxx = misc::max(maxx, x);
maxy = misc::max(maxy, y);
}
/**
* \brief Return, whether the line, limited by (ax1, ay1) and (ax2, ay2),
* crosses the unlimited line, determined by two points (bx1, by1) and
* (bx2, by2).
*/
bool Polygon::linesCross0(int ax1, int ay1, int ax2, int ay2,
int bx1, int by1, int bx2, int by2)
{
/** TODO Some more description */
// If the scalar product is 0, it means that one point is on the second
// line, so we check for <= 0, not < 0.
int z1 = zOfVectorProduct (ax1 - bx1, ay1 - by1, bx2 - bx1, by2 - by1);
int z2 = zOfVectorProduct (ax2 - bx1, ay2 - by1, bx2 - bx1, by2 - by1);
return (z1 <= 0 && z2 >= 0) || (z1 >= 0 && z2 <= 0);
}
/**
* \brief Return, whether the line, limited by (ax1, ay1) and (ax2, ay2),
* crosses the line, limited by (bx1, by1) and (bx2, by2).
*/
bool Polygon::linesCross(int ax1, int ay1, int ax2, int ay2,
int bx1, int by1, int bx2, int by2)
{
bool cross =
linesCross0 (ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) &&
linesCross0 (bx1, by1, bx2, by2, ax1, ay1, ax2, ay2);
_MSG("(%d, %d) - (%d, %d) and (%d, %d) - (%d, %d) cross? %s.\n",
ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, cross ? "Yes" : "No");
return cross;
}
bool Polygon::isPointWithin (int x, int y)
{
if (points->size () < 3 ||
(x < minx || x > maxx || y < miny || y >= maxy))
return false;
else {
int numCrosses = 0;
for (int i = 0; i < points->size () - 1; i++) {
if (linesCross (minx - 1, miny - 1, x, y,
points->getRef(i)->x, points->getRef(i)->y,
points->getRef(i + 1)->x, points->getRef(i + 1)->y))
numCrosses++;
}
if (linesCross (minx - 1, miny - 1, x, y,
points->getRef(points->size () - 1)->x,
points->getRef(points->size () - 1)->y,
points->getRef(0)->x, points->getRef(0)->y))
numCrosses++;
return numCrosses % 2 == 1;
}
}
Region::Region()
{
rectangleList = new container::typed::List <Rectangle> (true);
}
Region::~Region()
{
delete rectangleList;
}
/**
* \brief Add a rectangle to the region and combine it with
* existing rectangles if possible.
* The number of rectangles is forced to be less than 16
* by combining excessive rectangles.
*/
void Region::addRectangle (Rectangle *rPointer)
{
container::typed::Iterator <Rectangle> it;
Rectangle *r = new Rectangle (rPointer->x, rPointer->y,
rPointer->width, rPointer->height);
for (it = rectangleList->iterator (); it.hasNext (); ) {
Rectangle *ownRect = it.getNext ();
int combinedHeight =
misc::max(r->y + r->height, ownRect->y + ownRect->height) -
misc::min(r->y, ownRect->y);
int combinedWidth =
misc::max(r->x + r->width, ownRect->x + ownRect->width) -
misc::min(r->x, ownRect->x);
if (rectangleList->size() >= 16 ||
combinedWidth * combinedHeight <=
ownRect->width * ownRect->height + r->width * r->height) {
r->x = misc::min(r->x, ownRect->x);
r->y = misc::min(r->y, ownRect->y);
r->width = combinedWidth;
r->height = combinedHeight;
rectangleList->removeRef (ownRect);
}
}
rectangleList->append (r);
}
Content::Type Content::maskForSelection (bool followReferences)
{
Content::Type widgetMask = (Content::Type)
(Content::WIDGET_IN_FLOW |
(followReferences ? Content::WIDGET_OOF_REF : Content::WIDGET_OOF_CONT));
return (Content::Type)(Content::SELECTION_CONTENT | widgetMask);
}
void Content::intoStringBuffer(Content *content, misc::StringBuffer *sb)
{
switch(content->type) {
case START:
sb->append ("<start>");
break;
case END:
sb->append ("<end>");
break;
case TEXT:
sb->append ("\"");
sb->append (content->text);
sb->append ("\"");
break;
case WIDGET_IN_FLOW:
sb->append ("<widget in flow: ");
sb->appendPointer (content->widget);
sb->append (" (");
sb->append (content->widget->getClassName());
sb->append (")>");
break;
case WIDGET_OOF_REF:
sb->append ("<widget oof ref: ");
sb->appendPointer (content->widgetReference->widget);
sb->append (" (");
sb->append (content->widgetReference->widget->getClassName());
sb->append (", ");
sb->appendInt (content->widgetReference->parentRef);
sb->append (")>");
break;
case WIDGET_OOF_CONT:
sb->append ("<widget oof cont: ");
sb->appendPointer (content->widget);
sb->append (" (");
sb->append (content->widget->getClassName());
sb->append (")>");
break;
case BREAK:
sb->append ("<break>");
break;
default:
sb->append ("<");
sb->appendInt (content->type);
sb->append ("?>");
break;
}
}
void Content::maskIntoStringBuffer(Type mask, misc::StringBuffer *sb)
{
sb->append ((mask & START) ? "st" : "--");
sb->append (":");
sb->append ((mask & END) ? "en" : "--");
sb->append (":");
sb->append ((mask & TEXT) ? "tx" : "--");
sb->append (":");
sb->append ((mask & WIDGET_IN_FLOW) ? "wf" : "--");
sb->append (":");
sb->append ((mask & WIDGET_OOF_REF) ? "Wr" : "--");
sb->append (":");
sb->append ((mask & WIDGET_OOF_CONT) ? "Wc" : "--");
sb->append (":");
sb->append ((mask & BREAK) ? "br" : "--");
}
void Content::print (Content *content)
{
misc::StringBuffer sb;
intoStringBuffer (content, &sb);
printf ("%s", sb.getChars ());
}
void Content::printMask (Type mask)
{
misc::StringBuffer sb;
maskIntoStringBuffer (mask, &sb);
printf ("%s", sb.getChars ());
}
} // namespace core
} // namespace dw

319
dw/types.hh Normal file
View File

@ -0,0 +1,319 @@
#ifndef __DW_TYPES_HH__
#define __DW_TYPES_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
namespace style {
class Style;
}
enum HPosition
{
HPOS_LEFT,
HPOS_CENTER,
HPOS_RIGHT,
HPOS_INTO_VIEW, /* scroll only, until the content in question comes
* into view */
HPOS_NO_CHANGE
};
enum VPosition
{
VPOS_TOP,
VPOS_CENTER,
VPOS_BOTTOM,
VPOS_INTO_VIEW, /* scroll only, until the content in question comes
* into view */
VPOS_NO_CHANGE
};
enum ScrollCommand {SCREEN_UP_CMD, SCREEN_DOWN_CMD, SCREEN_LEFT_CMD,
SCREEN_RIGHT_CMD, LINE_UP_CMD, LINE_DOWN_CMD,
LEFT_CMD, RIGHT_CMD, TOP_CMD, BOTTOM_CMD, NONE_CMD};
/*
* Different "layers" may be highlighted in a widget.
*/
enum HighlightLayer
{
HIGHLIGHT_SELECTION,
HIGHLIGHT_FINDTEXT,
HIGHLIGHT_NUM_LAYERS
};
struct Point
{
int x;
int y;
};
/**
* \brief Abstract interface for different shapes.
*/
class Shape: public lout::object::Object
{
public:
virtual bool isPointWithin (int x, int y) = 0;
virtual void draw (core::View *view, core::style::Style *style, int x,
int y) = 0;
};
/**
* \brief dw::core::Shape implementation for simple rectangles.
*/
class Rectangle: public Shape
{
public:
int x;
int y;
int width;
int height;
inline Rectangle () { }
Rectangle (int x, int y, int width, int height);
void draw (core::View *view, core::style::Style *style, int x, int y);
bool intersectsWith (Rectangle *otherRect, Rectangle *dest);
bool isSubsetOf (Rectangle *otherRect);
bool isPointWithin (int x, int y);
bool isEmpty () { return width <= 0 || height <= 0; };
};
/**
* \brief dw::core::Shape implementation for simple circles.
*/
class Circle: public Shape
{
public:
int x, y, radius;
Circle (int x, int y, int radius);
void draw (core::View *view, core::style::Style *style, int x, int y);
bool isPointWithin (int x, int y);
};
/**
* \brief dw::core::Shape implementation for polygons.
*/
class Polygon: public Shape
{
private:
lout::misc::SimpleVector<Point> *points;
int minx, miny, maxx, maxy;
/**
* \brief Return the z-coordinate of the vector product of two
* vectors, whose z-coordinate is 0 (so that x and y of
* the vector product is 0, too).
*/
inline int zOfVectorProduct(int x1, int y1, int x2, int y2) {
return x1 * y2 - x2 * y1;
}
bool linesCross0(int ax1, int ay1, int ax2, int ay2,
int bx1, int by1, int bx2, int by2);
bool linesCross(int ax1, int ay1, int ax2, int ay2,
int bx1, int by1, int bx2, int by2);
public:
Polygon ();
~Polygon ();
void draw (core::View *view, core::style::Style *style, int x, int y);
void addPoint (int x, int y);
bool isPointWithin (int x, int y);
};
/**
* Implementation for a point set.
* Currently represented as a set of rectangles not containing
* each other.
* It is guaranteed that the rectangles returned by rectangles ()
* cover all rectangles that were added with addRectangle ().
*/
class Region
{
private:
lout::container::typed::List <Rectangle> *rectangleList;
public:
Region ();
~Region ();
void clear () { rectangleList->clear (); };
void addRectangle (Rectangle *r);
lout::container::typed::Iterator <Rectangle> rectangles ()
{
return rectangleList->iterator ();
};
};
/**
* \brief Represents the allocation, i.e. actual position and size of a
* dw::core::Widget.
*/
struct Allocation
{
int x;
int y;
int width;
int ascent;
int descent;
};
struct Requisition
{
int width;
int ascent;
int descent;
};
struct Extremes
{
int minWidth;
int maxWidth;
int minWidthIntrinsic;
int maxWidthIntrinsic;
int adjustmentWidth;
};
class WidgetReference: public lout::object::Object
{
public:
Widget *widget;
int parentRef;
WidgetReference (Widget *widget) { parentRef = -1; this->widget = widget; }
};
struct Content
{
enum Type {
START = 1 << 0,
END = 1 << 1,
TEXT = 1 << 2,
/** \brief widget in normal flow, so that _this_ widget
(containing this content) is both container (parent) and
generator */
WIDGET_IN_FLOW = 1 << 3,
/** \brief widget out of flow (OOF); _this_ widget (containing
this content) is only the container (parent), but _not_
generator */
WIDGET_OOF_CONT = 1 << 4,
/** \brief reference to a widget out of flow (OOF); _this_
widget (containing this content) is only the generator
(parent), but _not_ container */
WIDGET_OOF_REF = 1 << 5,
BREAK = 1 << 6,
/** \brief can be used internally, but should never be exposed,
e. g. by iterators */
INVALID = 1 << 7,
ALL = 0xff,
REAL_CONTENT = 0xff ^ (START | END),
SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally
ANY_WIDGET = WIDGET_IN_FLOW | WIDGET_OOF_CONT | WIDGET_OOF_REF
};
/* Content is embedded in struct Word therefore we
* try to be space efficient.
*/
short type;
bool space;
union {
const char *text;
Widget *widget;
WidgetReference *widgetReference;
int breakSpace;
};
static Content::Type maskForSelection (bool followReferences);
static void intoStringBuffer(Content *content, lout::misc::StringBuffer *sb);
static void maskIntoStringBuffer(Type mask, lout::misc::StringBuffer *sb);
static void print (Content *content);
static void printMask (Type mask);
inline Widget *getWidget () {
assert (type & ANY_WIDGET);
return type == WIDGET_OOF_REF ? widgetReference->widget : widget;
}
};
/**
* \brief Base class for dw::core::DrawingContext and
* dw::core::GettingWidgetAtPointContext.
*
* Not to be confused with the *stacking context* as defined by CSS.
*/
class StackingProcessingContext
{
private:
lout::container::typed::HashSet<lout::object::TypedPointer<Widget> >
*widgetsProcessedAsInterruption;
public:
inline StackingProcessingContext () {
widgetsProcessedAsInterruption =
new lout::container::typed::HashSet<lout::object::
TypedPointer<Widget> > (true);
}
inline ~StackingProcessingContext ()
{ delete widgetsProcessedAsInterruption; }
inline bool hasWidgetBeenProcessedAsInterruption (Widget *widget) {
lout::object::TypedPointer<Widget> key (widget);
return widgetsProcessedAsInterruption->contains (&key);
}
inline void addWidgetProcessedAsInterruption (Widget *widget) {
lout::object::TypedPointer<Widget> *key =
new lout::object::TypedPointer<Widget> (widget);
return widgetsProcessedAsInterruption->put (key);
}
};
/**
* \brief Set at the top when drawing.
*
* See \ref dw-interrupted-drawing for details.
*/
class DrawingContext: public StackingProcessingContext
{
private:
Rectangle toplevelArea;
public:
inline DrawingContext (Rectangle *toplevelArea) {
this->toplevelArea = *toplevelArea;
}
inline Rectangle *getToplevelArea () { return &toplevelArea; }
};
/**
* \brief Set at the top when getting the widget at the point.
*
* Similar to dw::core::DrawingContext.
*/
class GettingWidgetAtPointContext: public StackingProcessingContext
{
};
} // namespace core
} // namespace dw
#endif // __DW_TYPES_HH__

531
dw/ui.cc Normal file
View File

@ -0,0 +1,531 @@
/*
* Dillo Widget
*
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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.
*
* 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 "core.hh"
#include "../lout/debug.hh"
#include <stdio.h>
namespace dw {
namespace core {
namespace ui {
using namespace lout;
using namespace lout::object;
int Embed::CLASS_ID = -1;
Embed::Embed(Resource *resource)
{
DBG_OBJ_CREATE ("dw::core::ui::Embed");
registerName ("dw::core::ui::Embed", &CLASS_ID);
this->resource = resource;
resource->setEmbed (this);
DBG_OBJ_ASSOC_CHILD (resource);
}
Embed::~Embed()
{
delete resource;
DBG_OBJ_DELETE ();
}
void Embed::sizeRequestSimpl (Requisition *requisition)
{
resource->sizeRequest (requisition);
// TODO Correction should perhaps be left to the resources.
correctRequisition(requisition, core::splitHeightPreserveAscent, true, true);
}
void Embed::getExtremesSimpl (Extremes *extremes)
{
resource->getExtremes (extremes);
correctExtremes (extremes, false);
extremes->adjustmentWidth =
misc::min (extremes->minWidthIntrinsic, extremes->minWidth);
}
void Embed::sizeAllocateImpl (Allocation *allocation)
{
resource->sizeAllocate (allocation);
}
int Embed::getAvailWidthOfChild (Widget *child, bool forceValue)
{
return resource->getAvailWidthOfChild (child, forceValue);
}
int Embed::getAvailHeightOfChild (Widget *child, bool forceValue)
{
return resource->getAvailHeightOfChild (child, forceValue);
}
void Embed::correctRequisitionOfChild (Widget *child,
Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight)
{
resource->correctRequisitionOfChild (child, requisition, splitHeightFun,
allowDecreaseWidth,
allowDecreaseHeight);
}
void Embed::correctExtremesOfChild (Widget *child, Extremes *extremes,
bool useAdjustmentWidth)
{
resource->correctExtremesOfChild (child, extremes, useAdjustmentWidth);
}
void Embed::containerSizeChangedForChildren ()
{
DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
resource->containerSizeChangedForChildren ();
DBG_OBJ_LEAVE ();
}
void Embed::enterNotifyImpl (core::EventCrossing *event)
{
resource->emitEnter();
Widget::enterNotifyImpl(event);
}
void Embed::leaveNotifyImpl (core::EventCrossing *event)
{
resource->emitLeave();
Widget::leaveNotifyImpl(event);
}
bool Embed::buttonPressImpl (core::EventButton *event)
{
bool handled;
if (event->button == 3) {
resource->emitClicked(event);
handled = true;
} else {
handled = false;
}
return handled;
}
void Embed::setDisplayed (bool displayed)
{
resource->setDisplayed (displayed);
}
void Embed::setEnabled (bool enabled)
{
resource->setEnabled (enabled);
}
void Embed::draw (View *view, Rectangle *area, DrawingContext *context)
{
drawWidgetBox (view, area, false);
resource->draw (view, area, context);
}
Iterator *Embed::iterator (Content::Type mask, bool atEnd)
{
return resource->iterator (mask, atEnd);
}
void Embed::setStyle (style::Style *style)
{
resource->setStyle (style);
Widget::setStyle (style);
}
// ----------------------------------------------------------------------
bool Resource::ActivateEmitter::emitToReceiver (lout::signal::Receiver
*receiver,
int signalNo,
int argc, Object **argv)
{
ActivateReceiver *ar = (ActivateReceiver*)receiver;
Resource *res = (Resource*)((Pointer*)argv[0])->getValue ();
switch (signalNo) {
case 0:
ar->activate (res);
break;
case 1:
ar->enter (res);
break;
case 2:
ar->leave (res);
break;
default:
misc::assertNotReached ();
}
return false;
}
void Resource::ActivateEmitter::emitActivate (Resource *resource)
{
Pointer p (resource);
Object *argv[1] = { &p };
emitVoid (0, 1, argv);
}
void Resource::ActivateEmitter::emitEnter (Resource *resource)
{
Pointer p (resource);
Object *argv[1] = { &p };
emitVoid (1, 1, argv);
}
void Resource::ActivateEmitter::emitLeave (Resource *resource)
{
Pointer p (resource);
Object *argv[1] = { &p };
emitVoid (2, 1, argv);
}
// ----------------------------------------------------------------------
Resource::~Resource ()
{
DBG_OBJ_DELETE ();
}
void Resource::setEmbed (Embed *embed)
{
this->embed = embed;
}
void Resource::getExtremes (Extremes *extremes)
{
DBG_OBJ_ENTER0 ("resize", 0, "getExtremes");
/* Simply return the requisition width */
Requisition requisition;
sizeRequest (&requisition);
extremes->minWidth = extremes->maxWidth = requisition.width;
extremes->minWidthIntrinsic = extremes->minWidth;
extremes->maxWidthIntrinsic = extremes->maxWidth;
DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
extremes->minWidth, extremes->maxWidth);
DBG_OBJ_LEAVE ();
}
void Resource::sizeAllocate (Allocation *allocation)
{
}
int Resource::getAvailWidthOfChild (Widget *child, bool forceValue)
{
// Only used when the resource contains other dillo widgets.
misc::notImplemented ("Resource::getAvailWidthOfChild");
return 0;
}
int Resource::getAvailHeightOfChild (Widget *child, bool forceValue)
{
// Only used when the resource contains other dillo widgets.
misc::notImplemented ("Resource::getAvailHeightOfChild");
return 0;
}
void Resource::correctRequisitionOfChild (Widget *child,
Requisition *requisition,
void (*splitHeightFun) (int, int*,
int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight)
{
// Only used when the resource contains other dillo widgets.
misc::notImplemented ("Resource::correctRequisitionOfChild");
}
void Resource::correctExtremesOfChild (Widget *child, Extremes *extremes,
bool useAdjustmentWidth)
{
// Only used when the resource contains other dillo widgets.
misc::notImplemented ("Resource::correctExtremesOfChild");
}
void Resource::containerSizeChangedForChildren ()
{
// No children by default.
}
void Resource::setDisplayed (bool displayed)
{
}
void Resource::draw (View *view, Rectangle *area, DrawingContext *context)
{
}
void Resource::setStyle (style::Style *style)
{
}
void Resource::emitEnter ()
{
activateEmitter.emitEnter(this);
}
void Resource::emitLeave ()
{
activateEmitter.emitLeave(this);
}
bool Resource::ClickedEmitter::emitToReceiver(lout::signal::Receiver *receiver,
int signalNo, int argc,
Object **argv)
{
((ClickedReceiver*)receiver)
->clicked ((Resource*)((Pointer*)argv[0])->getValue (),
(EventButton*)((Pointer*)argv[1])->getValue());
return false;
}
void Resource::ClickedEmitter::emitClicked (Resource *resource,
EventButton *event)
{
Pointer p1 (resource);
Pointer p2 (event);
Object *argv[2] = { &p1, &p2 };
emitVoid (0, 2, argv);
}
// ----------------------------------------------------------------------
Iterator *LabelButtonResource::iterator (Content::Type mask, bool atEnd)
{
/** \todo Perhaps in brackets? */
// return new TextIterator (getEmbed (), mask, atEnd, getLabel ());
/** \bug Not implemented. */
return new EmptyIterator (getEmbed (), mask, atEnd);
}
// ----------------------------------------------------------------------
void ComplexButtonResource::LayoutReceiver::resizeQueued (bool extremesChanged)
{
DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver::resizeQueued", "%s",
extremesChanged ? "true" : "false");
resource->queueResize (extremesChanged);
DBG_OBJ_LEAVE ();
}
ComplexButtonResource::ComplexButtonResource ()
{
DBG_OBJ_CREATE ("dw::core::ui::ComplexButtonResource");
layout = NULL;
layoutReceiver.resource = this;
click_x = click_y = -1;
}
void ComplexButtonResource::init (Widget *widget)
{
childWidget = widget;
/* FIXME: Buttons should not need a full Layout */
layout = new Layout (createPlatform (), false);
setLayout (layout);
DBG_OBJ_ASSOC_CHILD (layout);
layout->setWidget (widget);
layout->connect (&layoutReceiver);
if (getEmbed ())
childWidget->setQuasiParent (getEmbed ());
}
void ComplexButtonResource::setEmbed (Embed *embed)
{
ButtonResource::setEmbed (embed);
if (childWidget)
childWidget->setQuasiParent (getEmbed ());
}
ComplexButtonResource::~ComplexButtonResource ()
{
delete layout;
DBG_OBJ_DELETE ();
}
void ComplexButtonResource::sizeRequest (Requisition *requisition)
{
DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
Requisition widgetRequisition;
childWidget->sizeRequest (&widgetRequisition);
requisition->width = widgetRequisition.width + 2 * reliefXThickness ();
requisition->ascent = widgetRequisition.ascent + reliefYThickness ();
requisition->descent = widgetRequisition.descent + reliefYThickness ();
DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
requisition->width, requisition->ascent, requisition->descent);
DBG_OBJ_LEAVE ();
}
void ComplexButtonResource::getExtremes (Extremes *extremes)
{
DBG_OBJ_ENTER0 ("resize", 0, "getExtremes");
Extremes widgetExtremes;
childWidget->getExtremes (&widgetExtremes);
extremes->minWidth = widgetExtremes.minWidth + 2 * reliefXThickness ();
extremes->maxWidth = widgetExtremes.maxWidth + 2 * reliefXThickness ();
extremes->minWidthIntrinsic = extremes->minWidth;
extremes->maxWidthIntrinsic = extremes->maxWidth;
DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
extremes->minWidth, extremes->maxWidth);
DBG_OBJ_LEAVE ();
}
void ComplexButtonResource::sizeAllocate (Allocation *allocation)
{
}
int ComplexButtonResource::getAvailWidthOfChild (Widget *child, bool forceValue)
{
int embedWidth = getEmbed()->getAvailWidth (forceValue);
if (embedWidth == -1)
return -1;
else
return misc::max (embedWidth - 2 * reliefXThickness (), 0);
}
int ComplexButtonResource::getAvailHeightOfChild (Widget *child,
bool forceValue)
{
int embedHeight = getEmbed()->getAvailHeight (forceValue);
if (embedHeight == -1)
return -1;
else
return misc::max (embedHeight - 2 * reliefYThickness (), 0);
}
void ComplexButtonResource::correctRequisitionOfChild (Widget *child,
Requisition *requisition,
void (*splitHeightFun)
(int, int*, int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight)
{
// Similar to Widget::correctRequisitionOfChild, but for percentage
// the relief has to be considered.
if (style::isPerLength (child->getStyle()->width)) {
/* FIXME: Typo for getAvailWidth()? */
int availWidth = getEmbed()->getAvailHeight (false);
if (availWidth != -1) {
int baseWidth = misc::max (availWidth
- getEmbed()->boxDiffWidth ()
- 2 * reliefXThickness (),
0);
int newWidth =
child->applyPerWidth (baseWidth, child->getStyle()->width);
requisition->width = allowDecreaseWidth ?
newWidth : misc::max (requisition->width, newWidth);
}
} else
getEmbed()->correctReqWidthOfChildNoRec (child, requisition,
allowDecreaseWidth);
// TODO Percentage heights are ignored again.
getEmbed()->correctReqHeightOfChildNoRec(child, requisition,
splitHeightFun, allowDecreaseWidth);
}
void ComplexButtonResource::correctExtremesOfChild (Widget *child,
Extremes *extremes,
bool useAdjustmentWidth)
{
// Similar to Widget::correctExtremesOfChild, but for percentage
// the relief has to be considered.
if (style::isPerLength (child->getStyle()->width)) {
int availWidth = getEmbed()->getAvailHeight (false);
if (availWidth != -1) {
int baseWidth = misc::max (availWidth
- getEmbed()->boxDiffWidth ()
- 2 * reliefXThickness (),
0);
extremes->minWidth = extremes->maxWidth =
child->applyPerWidth (baseWidth, child->getStyle()->width);
}
} else
getEmbed()->correctExtremesOfChildNoRec (child, extremes,
useAdjustmentWidth);
}
void ComplexButtonResource::containerSizeChangedForChildren ()
{
layout->containerSizeChanged ();
}
Iterator *ComplexButtonResource::iterator (Content::Type mask, bool atEnd)
{
/**
* \bug Implementation.
* This is a bit more complicated: We have two layouts here.
*/
return new EmptyIterator (getEmbed (), mask, atEnd);
}
// ----------------------------------------------------------------------
Iterator *TextResource::iterator (Content::Type mask, bool atEnd)
{
// return new TextIterator (getEmbed (), mask, atEnd, getText ());
/** \bug Not implemented. */
return new EmptyIterator (getEmbed (), mask, atEnd);
}
// ----------------------------------------------------------------------
Iterator *CheckButtonResource::iterator (Content::Type mask, bool atEnd)
{
//return new TextIterator (getEmbed (), mask, atEnd,
// isActivated () ? "[X]" : "[ ]");
/** \bug Not implemented. */
return new EmptyIterator (getEmbed (), mask, atEnd);
}
// ----------------------------------------------------------------------
RadioButtonResource::GroupIterator::~GroupIterator ()
{
}
Iterator *RadioButtonResource::iterator (Content::Type mask, bool atEnd)
{
//return new TextIterator (getEmbed (), mask, atEnd,
// isActivated () ? "(*)" : "( )");
/** \bug Not implemented. */
return new EmptyIterator (getEmbed (), mask, atEnd);
}
} // namespace ui
} // namespace core
} // namespace dw

607
dw/ui.hh Normal file
View File

@ -0,0 +1,607 @@
#ifndef __DW_UI_HH__
#define __DW_UI_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
/**
* \brief Anything related to embedded UI widgets is defined here.
*
* UI resources are another abstraction for Dw widgets, which are not
* fully implemented in a platform-independent way. Typically, they
* involve creating widgets, which the underlying UI toolkit provides.
*
* As you see in this diagram:
*
* \dot
* digraph G {
* node [shape=record, fontname=Helvetica, fontsize=10];
* edge [arrowhead="none", arrowtail="empty", dir="both",
* labelfontname=Helvetica, labelfontsize=10, color="#404040",
* labelfontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
*
* subgraph cluster_core {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core";
*
* subgraph cluster_ui {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core::ui";
*
* Embed [URL="\ref dw::core::ui::Embed"];
* Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
* LabelButtonResource [color="#a0a0a0",
* URL="\ref dw::core::ui::LabelButtonResource"];
* EntryResource [color="#a0a0a0",
* URL="\ref dw::core::ui::EntryResource"];
* etc [color="#a0a0a0", label="..."];
* }
*
* Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
* }
*
* subgraph cluster_fltk {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::fltk::ui";
*
* FltkLabelButtonResource
* [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
* FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
* }
*
* Widget -> Embed;
* Embed -> Resource [arrowhead="open", arrowtail="none",
* headlabel="1", taillabel="1"];
* Resource -> LabelButtonResource;
* Resource -> EntryResource;
* Resource -> etc;
* LabelButtonResource -> FltkLabelButtonResource;
* EntryResource -> FltkEntryResource;
* }
* \enddot
*
* <center>[\ref uml-legend "legend"]</center>
*
* there are several levels:
*
* <ol>
* <li> The Dw widget is dw::core::ui::Embed. It delegates most to
* dw::core::ui::Resource, which has similar methods like
* dw::core::Widget.
*
* <li> There are several sub interfaces of dw::core::ui::Resource, which
* may provide methods, as e.g. dw::core::ui::ListResource::addItem. In a
* platform independent context, you can cast the result of
* dw::core::ui::Embed::getResource to a specific sub class, if you
* know, which one is used. E.g., if you know, that a given instance
* dw::core::ui::Embed refers to a dw::core::ui::ListResource, you can
* write something like:
*
* \code
* dw::core::ui::Embed *embed;
* //...
* ((dw::core::ui::ListResource*)embed->getResource ())->addItem ("Hello!");
* \endcode
*
* <li> These sub classes are then fully implemented in a platform specific
* way. For an example, look at dw::fltk::ui.
* </ol>
*
* There is a factory interface, dw::core::ui::ResourceFactory, which
* provides methods for creating common resources. By calling
* dw::core::Layout::getResourceFactory, which calls
* dw::core::Platform::getResourceFactory, you get the factory for the used
* platform.
*
* It is possible to define additional sub classes of
* dw::core::ui::Resource, but since they are not provided by
* dw::core::ui::ResourceFactory, you have to define some other
* abstractions, if you want to remain platform independent.
*
*
* <h3>...</h3>
*
*
* <h3>Resources needed for HTML</h3>
*
* This chapter describes, how the form controls defined by HTML are
* implemented in Dw. Some of them do not refer to UI resources, but to
* other widgets, links to the respective documentations are provided
* here.
*
* <h4>Resources created with \<INPUT\></h4>
*
* The HTML \<INPUT\> is always implemented by using UI
* resources. \<INPUT\> element has the following attributes:
*
* <table>
* <tr><th>Attribute <th>Implementation
* <tr><td>type <td>This defines the resource you have to instantiate.
* <tr><td>name <td>Not needed within Dw.
* <tr><td>value <td>The initial value is treated differently by different
* resources.
* <tr><td>checked <td>Parameter to
* dw::core::ui::ResourceFactory::createCheckButtonResource
* and dw::core::ui::ResourceFactory::createRadioButtonResource.
* <tr><td>disabled <td>This is provided for all resources by
* dw::core::ui::Resource::setEnabled.
* <tr><td>readonly <td>This is provided by
* dw::core::ui::TextResource::setEditable.
* <tr><td>size <td>This is handled by styles.
* <tr><td>maxlength <td>Parameter of
* dw::core::ui::ResourceFactory::createEntryResource.
* <tr><td>src <td>Handled by the caller (HTML parser).
* <tr><td>alt <td>Handled by the caller (HTML parser).
* <tr><td>usemap <td>Handled by the caller (HTML parser).
* <tr><td>ismap <td>Handled by the caller (HTML parser).
* <tr><td>tabindex <td>Not supported currently.
* <tr><td>accesskey <td>Not supported currently.
* <tr><td>onfocus <td>Not supported currently.
* <tr><td>onblur <td>Not supported currently.
* <tr><td>onselect <td>Not supported currently.
* <tr><td>onchange <td>Not supported currently.
* <tr><td>accept <td>Not supported currently.
* </table>
*
* For the different values of \em type, the following resources can be
* used:
*
* <table>
* <tr><th>Type <th>Resource
* <th>Factory Method
* <tr><td>text <td>dw::core::ui::EntryResource
* <td>dw::core::ui::ResourceFactory::createEntryResource
* <tr><td>password <td>dw::core::ui::EntryResource
* <td>dw::core::ui::ResourceFactory::createEntryResource
* <tr><td>checkbox <td>dw::core::ui::CheckButtonResource
* <td>dw::core::ui::ResourceFactory::createCheckButtonResource
* <tr><td>radio <td>dw::core::ui::RadioButtonResource
* <td>dw::core::ui::ResourceFactory::createRadioButtonResource
* <tr><td>submit <td>dw::core::ui::LabelButtonResource
* <td>dw::core::ui::ResourceFactory::createLabelButtonResource
* <tr><td>image <td>dw::core::ui::ComplexButtonResource
* <td>dw::core::ui::ResourceFactory::createComplexButtonResource,
* width a dw::Image inside and relief = false.
* <tr><td>reset <td>dw::core::ui::LabelButtonResource
* <td>dw::core::ui::ResourceFactory::createLabelButtonResource
* <tr><td>button <td>dw::core::ui::LabelButtonResource
* <td>dw::core::ui::ResourceFactory::createLabelButtonResource
* <tr><td>hidden <td>No rendering necessary.
* <td>-
* <tr><td>file <td>Not supported currently.
* <td>-
* </table>
*
* <h4>\<SELECT\>, \<OPTGROUP\>, and \<OPTION\></h4>
*
* \<SELECT\> is implemented either by dw::core::ui::OptionMenuResource
* (better suitable for \em size = 1 and single selection) or
* dw::core::ui::ListResource, which have a common base,
* dw::core::ui::SelectionResource. In the latter case, \em size must be
* specified via dw::core::style::Style.
*
* Factory methods are dw::core::ui::ResourceFactory::createListResource and
* dw::core::ui::ResourceFactory::createOptionMenuResource.
*
* \<OPTION\>'s are added via dw::core::ui::SelectionResource::addItem.
*
* \<OPTGROUP\> are created by using dw::core::ui::SelectionResource::pushGroup
* and dw::core::ui::SelectionResource::popGroup.
*
* For lists, the selection mode must be set in
* dw::core::ui::ResourceFactory::createListResource.
*
* <h4>\<TEXTAREA\></h4>
*
* \<TEXTAREA\> is implemented by dw::core::ui::MultiLineTextResource,
* the factory method is
* dw::core::ui::ResourceFactory::createMultiLineTextResource.
* dw::core::ui::TextResource::setEditable can be used, as for entries.
*
* <h4>\<BUTTON\></h4>
*
* For handling \<BUTTON\>, dw::core::ui::ComplexButtonResource should be used,
* with a dw::Textblock inside, and relief = true. The contents of \<BUTTON\>
* is then added to the dw::Textblock.
*
* \todo describe activation signal
*/
namespace ui {
class Resource;
/**
* \brief A widget for embedding UI widgets.
*
* \sa dw::core::ui
*/
class Embed: public Widget
{
friend class Resource;
private:
Resource *resource;
protected:
void sizeRequestSimpl (Requisition *requisition);
void getExtremesSimpl (Extremes *extremes);
void sizeAllocateImpl (Allocation *allocation);
int getAvailWidthOfChild (Widget *child, bool forceValue);
int getAvailHeightOfChild (Widget *child, bool forceValue);
void correctRequisitionOfChild (Widget *child,
Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight);
void correctExtremesOfChild (Widget *child, Extremes *extremes,
bool useAdjustmentWidth);
void containerSizeChangedForChildren ();
void enterNotifyImpl (core::EventCrossing *event);
void leaveNotifyImpl (core::EventCrossing *event);
bool buttonPressImpl (core::EventButton *event);
public:
static int CLASS_ID;
Embed(Resource *resource);
~Embed();
void setDisplayed (bool displayed);
void setEnabled (bool enabled);
void draw (View *view, Rectangle *area, DrawingContext *context);
Iterator *iterator (Content::Type mask, bool atEnd);
void setStyle (style::Style *style);
inline Resource *getResource () { return resource; }
inline void correctReqWidthOfChildNoRec (Widget *child,
Requisition *requisition,
bool allowDecreaseWidth)
{ Widget::correctReqWidthOfChild (child, requisition, allowDecreaseWidth); }
inline void correctReqHeightOfChildNoRec (Widget *child,
Requisition *requisition,
void (*splitHeightFun) (int, int*,
int*),
bool allowDecreaseHeight)
{ Widget::correctReqHeightOfChild (child, requisition, splitHeightFun,
allowDecreaseHeight); }
virtual void correctExtremesOfChildNoRec (Widget *child, Extremes *extremes,
bool useAdjustmentWidth)
{ Widget::correctExtremesOfChild (child, extremes, useAdjustmentWidth); }
};
/**
* \brief Basic interface for all resources.
*
* \sa dw::core::ui
*/
class Resource
{
friend class Embed;
public:
/**
* \brief Receiver interface for the "activate" signal.
*/
class ActivateReceiver: public lout::signal::Receiver
{
public:
virtual void activate (Resource *resource) = 0;
virtual void enter (Resource *resource) = 0;
virtual void leave (Resource *resource) = 0;
};
/**
* \brief Receiver interface for the "clicked" signal.
*/
class ClickedReceiver: public lout::signal::Receiver
{
public:
virtual void clicked (Resource *resource, EventButton *event) = 0;
};
private:
class ActivateEmitter: public lout::signal::Emitter
{
protected:
bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
int argc, Object **argv);
public:
inline void connectActivate (ActivateReceiver *receiver) {
connect (receiver); }
void emitActivate (Resource *resource);
void emitEnter (Resource *resource);
void emitLeave (Resource *resource);
};
class ClickedEmitter: public lout::signal::Emitter
{
protected:
bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
int argc, Object **argv);
public:
inline void connectClicked (ClickedReceiver *receiver) {
connect (receiver); }
void emitClicked (Resource *resource, EventButton *event);
};
Embed *embed;
ActivateEmitter activateEmitter;
ClickedEmitter clickedEmitter;
void emitEnter ();
void emitLeave ();
protected:
inline void queueResize (bool extremesChanged) {
if (embed) embed->queueResize (0, extremesChanged);
}
virtual Embed *getEmbed () { return embed; }
virtual void setEmbed (Embed *embed);
inline void emitActivate () {
return activateEmitter.emitActivate (this); }
inline void emitClicked (EventButton *event) {
clickedEmitter.emitClicked (this, event); }
public:
inline Resource ()
{ embed = NULL; DBG_OBJ_CREATE ("dw::core::ui::Resource"); }
virtual ~Resource ();
virtual void sizeRequest (Requisition *requisition) = 0;
virtual void getExtremes (Extremes *extremes);
virtual void sizeAllocate (Allocation *allocation);
virtual int getAvailWidthOfChild (Widget *child, bool forceValue);
virtual int getAvailHeightOfChild (Widget *child, bool forceValue);
virtual void correctRequisitionOfChild (Widget *child,
Requisition *requisition,
void (*splitHeightFun) (int, int*,
int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight);
virtual void correctExtremesOfChild (Widget *child, Extremes *extremes,
bool useAdjustmentWidth);
virtual void containerSizeChangedForChildren ();
virtual void setDisplayed (bool displayed);
virtual void draw (View *view, Rectangle *area, DrawingContext *context);
virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0;
virtual void setStyle (style::Style *style);
virtual bool isEnabled () = 0;
virtual void setEnabled (bool enabled) = 0;
inline void connectActivate (ActivateReceiver *receiver) {
activateEmitter.connectActivate (receiver); }
inline void connectClicked (ClickedReceiver *receiver) {
clickedEmitter.connectClicked (receiver); }
};
class ButtonResource: public Resource
{};
/**
* \brief Interface for labelled buttons resources.
*/
class LabelButtonResource: public ButtonResource
{
public:
Iterator *iterator (Content::Type mask, bool atEnd);
virtual const char *getLabel () = 0;
virtual void setLabel (const char *label) = 0;
};
class ComplexButtonResource: public ButtonResource
{
private:
class LayoutReceiver: public Layout::Receiver
{
public:
ComplexButtonResource *resource;
void resizeQueued (bool extremesChanged);
};
friend class LayoutReceiver;
LayoutReceiver layoutReceiver;
Widget *childWidget;
protected:
Layout *layout;
int click_x, click_y;
void setEmbed (Embed *embed);
virtual Platform *createPlatform () = 0;
virtual void setLayout (Layout *layout) = 0;
virtual int reliefXThickness () = 0;
virtual int reliefYThickness () = 0;
void init (Widget *widget);
public:
ComplexButtonResource ();
~ComplexButtonResource ();
void sizeRequest (Requisition *requisition);
void getExtremes (Extremes *extremes);
void sizeAllocate (Allocation *allocation);
int getAvailWidthOfChild (Widget *child, bool forceValue);
int getAvailHeightOfChild (Widget *child, bool forceValue);
void correctRequisitionOfChild (Widget *child,
Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight);
void correctExtremesOfChild (Widget *child, Extremes *extremes,
bool useAdjustmentWidth);
void containerSizeChangedForChildren ();
Iterator *iterator (Content::Type mask, bool atEnd);
int getClickX () {return click_x;};
int getClickY () {return click_y;};
};
/**
* \brief Base interface for dw::core::ui::ListResource and
* dw::core::ui::OptionMenuResource.
*/
class SelectionResource: public Resource
{
public:
virtual void addItem (const char *str, bool enabled, bool selected) = 0;
virtual void setItem (int index, bool selected) = 0;
virtual void pushGroup (const char *name, bool enabled) = 0;
virtual void popGroup () = 0;
virtual int getNumberOfItems () = 0;
virtual bool isSelected (int index) = 0;
};
class ListResource: public SelectionResource
{
public:
enum SelectionMode {
/**
* \brief Exactly one item is selected.
*
* If no item is selected initially, the first one is selected.
*/
SELECTION_EXACTLY_ONE,
/**
* \brief Exactly one item is selected, except possibly at the beginning.
*
* If no item is selected initially, no one is selected automatically.
* The user may not unselect the only selected item.
*/
SELECTION_EXACTLY_ONE_BY_USER,
/**
* \brief At most one item is selected.
*
* If no item is selected initially, no one is selected automatically.
* The user may unselect the only selected item.
*/
SELECTION_AT_MOST_ONE,
/**
* \brief An arbitrary number of items may be selected.
*/
SELECTION_MULTIPLE
};
};
class OptionMenuResource: public SelectionResource
{
};
class TextResource: public Resource
{
public:
Iterator *iterator (Content::Type mask, bool atEnd);
virtual const char *getText () = 0;
virtual void setText (const char *text) = 0;
virtual bool isEditable () = 0;
virtual void setEditable (bool editable) = 0;
};
class EntryResource: public TextResource
{
public:
enum { UNLIMITED_SIZE = -1 };
virtual void setMaxLength (int maxlen) = 0;
};
class MultiLineTextResource: public TextResource
{
};
class ToggleButtonResource: public Resource
{
public:
virtual bool isActivated () = 0;
virtual void setActivated (bool activated) = 0;
};
class CheckButtonResource: public ToggleButtonResource
{
public:
Iterator *iterator (Content::Type mask, bool atEnd);
};
class RadioButtonResource: public ToggleButtonResource
{
public:
class GroupIterator
{
protected:
GroupIterator () { }
virtual ~GroupIterator ();
public:
virtual bool hasNext () = 0;
virtual RadioButtonResource *getNext () = 0;
virtual void unref () = 0;
};
/**
* \brief Return an iterator, to access all radio button resources
* within the group.
*/
virtual GroupIterator *groupIterator () = 0;
Iterator *iterator (Content::Type mask, bool atEnd);
};
/**
* \brief A factory for the common resource.
*/
class ResourceFactory: public lout::object::Object
{
public:
virtual LabelButtonResource *createLabelButtonResource (const char *label)
= 0;
virtual ComplexButtonResource *createComplexButtonResource (Widget *widget,
bool relief)
= 0;
virtual ListResource *createListResource (ListResource::SelectionMode
selectionMode, int rows) = 0;
virtual OptionMenuResource *createOptionMenuResource () = 0;
virtual EntryResource *createEntryResource (int size, bool password,
const char *label,
const char *placeholder) = 0;
virtual MultiLineTextResource *createMultiLineTextResource (int cols,
int rows,
const char *placeholder) = 0;
virtual CheckButtonResource *createCheckButtonResource (bool activated) = 0;
virtual RadioButtonResource *createRadioButtonResource (RadioButtonResource
*groupedWith,
bool activated) = 0;
};
} // namespace ui
} // namespace core
} // namespace dw
#endif // __DW_UI_HH__

213
dw/view.hh Normal file
View File

@ -0,0 +1,213 @@
#ifndef __DW_VIEW_HH__
#define __DW_VIEW_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
namespace dw {
namespace core {
/**
* \brief An interface to encapsulate platform dependent drawing.
*
* \sa\ref dw-overview, \ref dw-layout-views
*/
class View: public lout::object::Object
{
public:
/*
* ----------------------------
* Operations on the view
* ----------------------------
*/
/**
* \brief This methods notifies the view, that it has been attached to a
* layout.
*/
virtual void setLayout (Layout *layout) = 0;
/**
* \brief Set the canvas size.
*/
virtual void setCanvasSize (int width, int ascent, int descent) = 0;
/**
* \brief Set the cursor appearance.
*/
virtual void setCursor (style::Cursor cursor) = 0;
/**
* \brief Set the background of the view.
*/
virtual void setBgColor (style::Color *color) = 0;
/*
* ---------------------------------------------------------
* Scrolling and Related. Only usesViewport must be
* implemented, if it returns false, the other methods
* are never called.
* ---------------------------------------------------------
*/
/**
* \brief Return, whether this view uses a viewport.
*/
virtual bool usesViewport () = 0;
/**
* \brief Get the thickness of the horizontal scrollbar, when it is
* visible.
*
* Does not have to be implemented, when usesViewport returns false.
*/
virtual int getHScrollbarThickness () = 0;
/**
* \brief Get the thickness of the vertical scrollbar, when it is
* visible.
*
* Does not have to be implemented, when usesViewport returns false.
*/
virtual int getVScrollbarThickness () = 0;
virtual int getScrollbarOnLeft () = 0;
/**
* \brief Scroll the viewport to the given position.
*
* Does not have to be implemented, when usesViewport returns false.
*/
virtual void scrollTo (int x, int y) = 0;
/**
* \brief Scroll the viewport as commanded.
*/
virtual void scroll (ScrollCommand) { };
/**
* \brief Set the viewport size.
*
* Does not have to be implemented, when usesViewport returns false.
*
* This will normally imply a resize of the UI widget. Width and height are
* the dimensions of the new size, \em including the scrollbar thicknesses.
*
*/
virtual void setViewportSize (int width, int height,
int hScrollbarThickness,
int vScrollbarThickness) = 0;
/*
* -----------------------
* Drawing functions
* -----------------------
*/
/**
* \brief Called before drawing.
*
* All actual drawing operations will be enclosed into calls of
* dw::core:View::startDrawing and dw::core:View::finishDrawing. They
* may be implemented, e.g. when a backing
* pixmap is used, to prevent flickering. StartDrawing() will then
* initialize the backing pixmap, all other drawing operations will draw
* into it, and finishDrawing() will merge it into the window.
*/
virtual void startDrawing (Rectangle *area) = 0;
/**
* \brief Called after drawing.
*
* \sa dw::core:View::startDrawing
*/
virtual void finishDrawing (Rectangle *area) = 0;
/**
* \brief Queue a region, which is given in \em canvas coordinates, for
* drawing.
*
* The view implementation is responsible, that this region is drawn, either
* immediately, or (which is more typical, since more efficient) the areas
* are collected, combined (as far as possible), and the drawing is later
* done in an idle function.
*/
virtual void queueDraw (Rectangle *area) = 0;
/**
* \brief Queue the total viewport for drawing.
*
* \sa dw::core::View::queueDraw
*/
virtual void queueDrawTotal () = 0;
/**
* \brief Cancel a draw queue request.
*
* If dw::core::View::queueDraw or dw::core::View::queueDrawTotal have been
* called before, and the actual drawing was not processed yet, the actual
* drawing should be cancelled. Otherwise, the cancellation should be
* ignored.
*/
virtual void cancelQueueDraw () = 0;
/*
* The following methods should be self-explaining.
*/
virtual void drawPoint (style::Color *color,
style::Color::Shading shading,
int x, int y) = 0;
virtual void drawLine (style::Color *color,
style::Color::Shading shading,
int x1, int y1, int x2, int y2) = 0;
virtual void drawTypedLine (style::Color *color,
style::Color::Shading shading,
style::LineType type, int width,
int x1, int y1, int x2, int y2) = 0;
virtual void drawRectangle (style::Color *color,
style::Color::Shading shading, bool filled,
int x, int y, int width, int height) = 0;
virtual void drawArc (style::Color *color,
style::Color::Shading shading, bool filled,
int centerX, int centerY, int width, int height,
int angle1, int angle2) = 0;
virtual void drawPolygon (style::Color *color,
style::Color::Shading shading,
bool filled, bool convex, Point *points,
int npoints) = 0;
virtual void drawText (style::Font *font,
style::Color *color,
style::Color::Shading shading,
int x, int y, const char *text, int len) = 0;
virtual void drawSimpleWrappedText (style::Font *font, style::Color *color,
style::Color::Shading shading,
int x, int y, int w, int h,
const char *text) = 0;
virtual void drawImage (Imgbuf *imgbuf, int xRoot, int yRoot,
int x, int y, int width, int height) = 0;
/*
* --------------
* Clipping
* --------------
*/
/*
* To prevent drawing outside of a given area, a clipping view may be
* requested, which also implements this interface. The clipping view is
* related to the parent view (clipping views may be nested!), anything
* which is drawn into this clipping view, is later merged again into the
* parent view. An implementation will typically use additional pixmaps,
* which are later merged into the parent view pixmap/window.
*/
virtual View *getClippingView (int x, int y, int width, int height) = 0;
virtual void mergeClippingView (View *clippingView) = 0;
};
} // namespace core
} // namespace dw
#endif // __DW_VIEW_HH__

2273
dw/widget.cc Normal file

File diff suppressed because it is too large Load Diff

635
dw/widget.hh Normal file
View File

@ -0,0 +1,635 @@
/*
* Dillo Widget
*
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
* Copyright 2023-2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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.
*
* 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/>.
*/
#ifndef __DW_WIDGET_HH__
#define __DW_WIDGET_HH__
#ifndef __INCLUDED_FROM_DW_CORE_HH__
# error Do not include this file directly, use "core.hh" instead.
#endif
#include "../lout/identity.hh"
/**
* \brief The type for callback functions.
*/
typedef void (*DW_Callback_t)(void *data);
namespace dw {
namespace core {
/**
* \brief The base class of all dillo widgets.
*
* \sa\ref dw-overview, \ref dw-layout-widgets
*/
class Widget: public lout::identity::IdentifiableObject
{
friend class Layout;
protected:
enum Flags {
/**
* \todo Comment this.
*/
RESIZE_QUEUED = 1 << 0,
/**
* \todo Comment this.
*/
EXTREMES_QUEUED = 1 << 1,
/**
* \brief Set, when dw::core::Widget::requisition is not up to date
* anymore.
*
* \todo Update, see RESIZE_QUEUED.
*/
NEEDS_RESIZE = 1 << 2,
/**
* \brief Only used internally, set to enforce size allocation.
*
* In some cases, the size of a widget remains the same, but the
* children are allocated at different positions and in
* different sizes, so that a simple comparison of old and new
* allocation is insufficient. Therefore, this flag is set
* (indirectly, as ALLOCATE_QUEUED) in queueResize.
*/
NEEDS_ALLOCATE = 1 << 3,
/**
* \todo Comment this.
*/
ALLOCATE_QUEUED = 1 << 4,
/**
* \brief Set, when dw::core::Widget::extremes is not up to date
* anymore.
*
* \todo Update, see RESIZE_QUEUED.
*/
EXTREMES_CHANGED = 1 << 5,
/**
* \brief Set, when a widget was already once allocated,
*
* The dw::Image widget uses this flag, see dw::Image::setBuffer.
*/
WAS_ALLOCATED = 1 << 6
};
/**
* \brief Implementation which represents the whole widget.
*
* The only instance is set created needed.
*/
class WidgetImgRenderer: public style::StyleImage::ExternalWidgetImgRenderer
{
private:
Widget *widget;
public:
inline WidgetImgRenderer (Widget *widget) { this->widget = widget; }
bool readyToDraw ();
void getBgArea (int *x, int *y, int *width, int *height);
void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef);
style::Style *getStyle ();
void draw (int x, int y, int width, int height);
};
WidgetImgRenderer *widgetImgRenderer;
private:
static bool adjustMinWidth;
/**
* \brief The parent widget, NULL for top-level widgets.
*/
Widget *parent;
/**
* \brief ...
*/
Widget *quasiParent;
/**
* \brief The generating widget, NULL for top-level widgets, or if
* not set; in the latter case, the effective generator (see
* getGenerator) is the parent.
*/
Widget *generator;
/**
* \brief The containing widget, equivalent to the "containing
* block" defined by CSS. May be NULL, in this case the viewport
* is used.
*/
Widget *container;
WidgetReference *widgetReference;
style::Style *style;
Flags flags;
/**
* \brief Size_request() stores the result of the last call of
* size_request_impl().
*
* Do not read this directly, but call size_request().
*/
Requisition requisition;
SizeParams requisitionParams;
/**
* \brief Analogue to dw::core::Widget::requisition.
*/
Extremes extremes;
SizeParams extremesParams;
/**
* \brief See dw::core::Widget::setBgColor().
*/
style::Color *bgColor;
/**
* \brief See dw::core::Widget::setButtonSensitive().
*/
bool buttonSensitive;
/**
* \brief See dw::core::Widget::setButtonSensitive().
*/
bool buttonSensitiveSet;
void queueResize (int ref, bool extremesChanged, bool fast);
inline void queueResizeFast (int ref, bool extremesChanged)
{ queueResize (ref, extremesChanged, true); }
public:
/**
* \brief This value is defined by the parent widget, and used for
* incremential resizing.
*
* See documentation for an explanation.
*/
int parentRef;
protected:
/**
* \brief The current allocation: size and position, always relative to the
* canvas. The allocation is the outermost box for the widget, as in the CSS
* box model. It also includes the extraSpace.
*/
Allocation allocation;
inline int getHeight () { return allocation.ascent + allocation.descent; }
inline int getContentWidth() { return allocation.width - boxDiffWidth (); }
inline int getContentHeight() { return getHeight () - boxDiffHeight (); }
/**
* Preferred aspect ratio of the widget. Set to 0 when there is none. It is
* computed as width / height.
*/
float ratio;
Layout *layout;
/**
* \brief Space around the margin box. Allocation is extraSpace +
* margin + border + padding + contents.
*
* See also dw::core::Widget::calcExtraSpace and
* dw::core::Widget::calcExtraSpaceImpl. Also, it is feasible to
* correct this value within dw::core::Widget::sizeRequestImpl.
*/
style::Box extraSpace;
/**
* \brief Set iff this widget constitutes a stacking context, as defined by
* CSS.
*/
StackingContextMgr *stackingContextMgr;
/**
* \brief The bottom-most ancestor (or this) for which stackingContextMgr is
* set.
*/
Widget *stackingContextWidget;
inline StackingContextMgr *getNextStackingContextMgr ()
{ return stackingContextWidget->stackingContextMgr; }
/*inline void printFlags () {
DBG_IF_RTFL {
char buf[10 * 3 - 1 + 1];
snprintf (buf, sizeof (buf), "%s:%s:%s:%s:%s:%s:%s",
(flags & RESIZE_QUEUED) ? "Rq" : "--",
(flags & EXTREMES_QUEUED) ? "Eq" : "--",
(flags & NEEDS_RESIZE) ? "nR" : "--",
(flags & NEEDS_ALLOCATE) ? "nA" : "--",
(flags & ALLOCATE_QUEUED) ? "Aq" : "--",
(flags & EXTREMES_CHANGED) ? "Ec" : "--",
(flags & WAS_ALLOCATED) ? "wA" : "--");
DBG_OBJ_SET_SYM ("flags", buf);
}
}*/
inline void printFlag (Flags f) {
DBG_IF_RTFL {
switch (f) {
case RESIZE_QUEUED:
DBG_OBJ_SET_SYM ("flags.RESIZE_QUEUED",
(flags & RESIZE_QUEUED) ? "true" : "false");
break;
case EXTREMES_QUEUED:
DBG_OBJ_SET_SYM ("flags.EXTREMES_QUEUED",
(flags & EXTREMES_QUEUED) ? "true" : "false");
break;
case NEEDS_RESIZE:
DBG_OBJ_SET_SYM ("flags.NEEDS_RESIZE",
(flags & NEEDS_RESIZE) ? "true" : "false");
break;
case NEEDS_ALLOCATE:
DBG_OBJ_SET_SYM ("flags.NEEDS_ALLOCATE",
(flags & NEEDS_ALLOCATE) ? "true" : "false");
break;
case ALLOCATE_QUEUED:
DBG_OBJ_SET_SYM ("flags.ALLOCATE_QUEUED",
(flags & ALLOCATE_QUEUED) ? "true" : "false");
break;
case EXTREMES_CHANGED:
DBG_OBJ_SET_SYM ("flags.EXTREMES_CHANGED",
(flags & EXTREMES_CHANGED) ? "true" : "false");
break;
case WAS_ALLOCATED:
DBG_OBJ_SET_SYM ("flags.WAS_ALLOCATED",
(flags & WAS_ALLOCATED) ? "true" : "false");
break;
}
}
}
inline void setFlags (Flags f)
{ flags = (Flags)(flags | f); printFlag (f); }
inline void unsetFlags (Flags f)
{ flags = (Flags)(flags & ~f); printFlag (f); }
inline void queueDraw ()
{ queueDrawArea (0, 0, allocation.width, getHeight()); }
void queueDrawArea (int x, int y, int width, int height);
inline void queueResize (int ref, bool extremesChanged)
{ queueResize (ref, extremesChanged, false); }
/**
* \brief See \ref dw-widget-sizes.
*/
virtual void sizeRequestImpl (Requisition *requisition, int numPos,
Widget **references, int *x, int *y);
/**
* \brief Simple variant, to be implemented by widgets with sizes
* not depending on positions.
*/
virtual void sizeRequestSimpl (Requisition *requisition);
/**
* \brief See \ref dw-widget-sizes.
*/
virtual void getExtremesImpl (Extremes *extremes, int numPos,
Widget **references, int *x, int *y);
/**
* \brief Simple variant, to be implemented by widgets with
* extremes not depending on positions.
*/
virtual void getExtremesSimpl (Extremes *extremes);
virtual void calcExtraSpaceImpl (int numPos, Widget **references, int *x,
int *y);
/**
* \brief See \ref dw-widget-sizes.
*/
virtual void sizeAllocateImpl (Allocation *allocation);
/**
* \brief Called after sizeAllocateImpl() to redraw necessary areas.
* By default the whole widget is redrawn.
*/
virtual void resizeDrawImpl () { queueDraw (); };
/**
* \brief See \ref dw-widget-sizes.
*/
virtual void markSizeChange (int ref);
/**
* \brief See \ref dw-widget-sizes.
*/
virtual void markExtremesChange (int ref);
virtual int getAvailWidthOfChild (Widget *child, bool forceValue);
virtual int getAvailHeightOfChild (Widget *child, bool forceValue);
virtual void correctRequisitionOfChild (Widget *child,
Requisition *requisition,
void (*splitHeightFun) (int, int*,
int*),
bool allowDecreaseWidth,
bool allowDecreaseHeight);
void correctRequisitionViewport (Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseWidth, bool allowDecreaseHeight);
void correctReqWidthOfChild (Widget *child, Requisition *requisition,
bool allowDecreaseWidth);
void correctReqHeightOfChild (Widget *child, Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseHeight);
bool correctReqAspectRatio (int pass, Widget *child, Requisition *requisition,
bool allowDecreaseWidth, bool allowDecreaseHeight,
void (*splitHeightFun) (int, int*, int*));
virtual void correctExtremesOfChild (Widget *child, Extremes *extremes,
bool useAdjustmentWidth);
virtual void containerSizeChangedForChildren ();
virtual bool affectedByContainerSizeChange ();
virtual bool affectsSizeChangeContainerChild (Widget *child);
virtual bool usesAvailWidth ();
virtual bool usesAvailHeight ();
virtual void notifySetAsTopLevel();
virtual void notifySetParent();
virtual bool buttonPressImpl (EventButton *event);
virtual bool buttonReleaseImpl (EventButton *event);
virtual bool motionNotifyImpl (EventMotion *event);
virtual void enterNotifyImpl (EventCrossing *event);
virtual void leaveNotifyImpl (EventCrossing *event);
inline char *addAnchor (const char* name)
{ return layout->addAnchor (this, name); }
inline char *addAnchor (const char* name, int y)
{ return layout->addAnchor (this, name, y); }
inline void changeAnchor (char* name, int y)
{ layout->changeAnchor (this, name, y); }
inline void removeAnchor (char* name)
{ if (layout) layout->removeAnchor (this, name); }
//inline void updateBgColor () { layout->updateBgColor (); }
inline void setCursor (style::Cursor cursor)
{ layout->setCursor (cursor); }
#if 0
inline bool selectionButtonPress (Iterator *it, int charPos, int linkNo,
EventButton *event, bool withinContent)
{ return layout->selectionState.buttonPress (it, charPos, linkNo, event); }
inline bool selectionButtonRelease (Iterator *it, int charPos, int linkNo,
EventButton *event, bool withinContent)
{ return layout->selectionState.buttonRelease (it, charPos, linkNo, event);}
inline bool selectionButtonMotion (Iterator *it, int charPos, int linkNo,
EventMotion *event, bool withinContent)
{ return layout->selectionState.buttonMotion (it, charPos, linkNo, event); }
#endif
inline bool selectionHandleEvent (SelectionState::EventType eventType,
Iterator *it, int charPos, int linkNo,
MousePositionEvent *event)
{ return layout->selectionState.handleEvent (eventType, it, charPos, linkNo,
event); }
private:
void *deleteCallbackData;
DW_Callback_t deleteCallbackFunc;
public:
inline void setDeleteCallback(DW_Callback_t func, void *data)
{ deleteCallbackFunc = func; deleteCallbackData = data; }
private:
bool resizeIdleEntered () { return layout && layout->resizeIdleCounter; }
void enterQueueResize () { if (layout) layout->queueResizeCounter++; }
void leaveQueueResize () { if (layout) layout->queueResizeCounter--; }
bool queueResizeEntered () { return layout && layout->queueResizeCounter; }
void enterSizeAllocate () { if (layout) layout->sizeAllocateCounter++; }
void leaveSizeAllocate () { if (layout) layout->sizeAllocateCounter--; }
bool sizeAllocateEntered () { return layout && layout->sizeAllocateCounter; }
void enterSizeRequest () { if (layout) layout->sizeRequestCounter++; }
void leaveSizeRequest () { if (layout) layout->sizeRequestCounter--; }
bool sizeRequestEntered () { return layout && layout->sizeRequestCounter; }
void enterGetExtremes () { if (layout) layout->getExtremesCounter++; }
void leaveGetExtremes () { if (layout) layout->getExtremesCounter--; }
bool getExtremesEntered () { return layout && layout->getExtremesCounter; }
public:
static int CLASS_ID;
inline static void setAdjustMinWidth (bool adjustMinWidth)
{ Widget::adjustMinWidth = adjustMinWidth; }
Widget ();
~Widget ();
inline bool resizeQueued () { return flags & RESIZE_QUEUED; }
inline bool extremesQueued () { return flags & EXTREMES_QUEUED; }
inline bool needsResize () { return flags & NEEDS_RESIZE; }
inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; }
inline bool allocateQueued () { return flags & ALLOCATE_QUEUED; }
inline bool extremesChanged () { return flags & EXTREMES_CHANGED; }
inline bool wasAllocated () { return flags & WAS_ALLOCATED; }
void setParent (Widget *parent);
void setQuasiParent (Widget *quasiParent);
void setGenerator (Widget *generator) { this->generator = generator; }
inline style::Style *getStyle () { return style; }
/** \todo I do not like this. */
inline Allocation *getAllocation () { return &allocation; }
inline bool inAllocation (int x, int y) {
return wasAllocated () && x >= allocation.x && y >= allocation.y &&
x <= allocation.x + allocation.width &&
y <= allocation.y + getHeight ();
}
inline int boxOffsetX ()
{ return extraSpace.left + getStyle()->boxOffsetX (); }
inline int boxRestWidth ()
{ return extraSpace.right + getStyle()->boxRestWidth (); }
inline int boxDiffWidth () { return boxOffsetX () + boxRestWidth (); }
inline int boxOffsetY ()
{ return extraSpace.top + getStyle()->boxOffsetY (); }
inline int boxRestHeight ()
{ return extraSpace.bottom + getStyle()->boxRestHeight (); }
inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); }
/**
* \brief See \ref dw-widget-sizes (or \ref dw-size-request-pos).
*/
virtual int numSizeRequestReferences ();
/**
* \brief See \ref dw-widget-sizes (or \ref dw-size-request-pos).
*/
virtual Widget *sizeRequestReference (int index);
/**
* \brief See \ref dw-widget-sizes (or \ref dw-size-request-pos).
*/
virtual int numGetExtremesReferences ();
/**
* \brief See \ref dw-widget-sizes (or \ref dw-size-request-pos).
*/
virtual Widget *getExtremesReference (int index);
void sizeRequest (Requisition *requisition, int numPos = 0,
Widget **references = NULL, int *x = NULL, int *y = NULL);
void getExtremes (Extremes *extremes, int numPos = 0,
Widget **references = NULL, int *x = NULL, int *y = NULL);
void sizeAllocate (Allocation *allocation);
void calcExtraSpace (int numPos, Widget **references, int *x, int *y);
int getAvailWidth (bool forceValue);
int getAvailHeight (bool forceValue);
virtual bool getAdjustMinWidth () { return Widget::adjustMinWidth; }
void correctRequisition (Requisition *requisition,
void (*splitHeightFun) (int, int*, int*),
bool allowDecreaseWidth, bool allowDecreaseHeight);
void correctExtremes (Extremes *extremes, bool useAdjustmentWidth);
int calcWidth (style::Length cssValue, int refWidth, Widget *refWidget,
int limitMinWidth, bool forceValue);
void calcFinalWidth (style::Style *style, int refWidth, Widget *refWidget,
int limitMinWidth, bool forceValue, int *finalWidth);
int calcHeight (style::Length cssValue, bool usePercentage, int refHeight,
Widget *refWidget, bool forceValue);
static void adjustHeight (int *height, bool allowDecreaseHeight, int ascent,
int descent);
virtual int applyPerWidth (int containerWidth, style::Length perWidth);
virtual int applyPerHeight (int containerHeight, style::Length perHeight);
int getMinWidth (Extremes *extremes, bool forceValue);
virtual bool isBlockLevel ();
virtual bool isPossibleContainer ();
void containerSizeChanged ();
bool intersects (Widget *refWidget, Rectangle *area,
Rectangle *intersection);
/** Area is given in widget coordinates. */
virtual void draw (View *view, Rectangle *area, DrawingContext *context) = 0;
void drawInterruption (View *view, Rectangle *area, DrawingContext *context);
virtual Widget *getWidgetAtPoint (int x, int y,
GettingWidgetAtPointContext *context);
Widget *getWidgetAtPointInterrupted (int x, int y,
GettingWidgetAtPointContext *context);
bool buttonPress (EventButton *event);
bool buttonRelease (EventButton *event);
bool motionNotify (EventMotion *event);
void enterNotify (EventCrossing *event);
void leaveNotify (EventCrossing *event);
virtual void setStyle (style::Style *style);
void setBgColor (style::Color *bgColor);
style::Color *getBgColor ();
style::Color *getFgColor ();
void drawBox (View *view, style::Style *style, Rectangle *area,
int x, int y, int width, int height, bool inverse);
void drawWidgetBox (View *view, Rectangle *area, bool inverse);
void drawSelected (View *view, Rectangle *area);
void setButtonSensitive (bool buttonSensitive);
inline bool isButtonSensitive () { return buttonSensitive; }
inline Widget *getParent () { return parent; }
inline Widget *getContainer () { return container; }
Widget *getTopLevel ();
int getLevel ();
int getGeneratorLevel ();
Widget *getNearestCommonAncestor (Widget *otherWidget);
inline WidgetReference *getWidgetReference () { return widgetReference; }
inline void setWidgetReference (WidgetReference *widgetReference) {
this->widgetReference = widgetReference;
DBG_OBJ_SET_PTR ("widgetReference", widgetReference);
}
inline Widget *getGenerator () { return generator ? generator : parent; }
inline Layout *getLayout () { return layout; }
void scrollTo (HPosition hpos, VPosition vpos,
int x, int y, int width, int height);
void getMarginArea (int *xMar, int *yMar, int *widthMar, int *heightMar);
void getBorderArea (int *xBor, int *yBor, int *widthBor, int *heightBor);
void getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad);
/**
* \brief Return an iterator for this widget.
*
* \em mask can narrow the types returned by the iterator, this can
* enhance performance quite much, e.g. when only searching for child
* widgets.
*
* With \em atEnd == false, the iterator starts \em before the beginning,
* i.e. the first call of dw::core::Iterator::next will let the iterator
* point on the first piece of contents. Likewise, With \em atEnd == true,
* the iterator starts \em after the last piece of contents, call
* dw::core::Iterator::prev in this case.
*/
virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0;
virtual void removeChild (Widget *child);
};
void splitHeightPreserveAscent (int height, int *ascent, int *descent);
void splitHeightPreserveDescent (int height, int *ascent, int *descent);
} // namespace core
} // namespace dw
#endif // __DW_WIDGET_HH__