Initial import of Dillo
This commit is contained in:
109
dw/Makefile.am
Normal file
109
dw/Makefile.am
Normal 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
218
dw/alignedtablecell.cc
Normal 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
51
dw/alignedtablecell.hh
Normal 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
107
dw/alignedtextblock.cc
Normal 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
61
dw/alignedtextblock.hh
Normal 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
92
dw/bullet.cc
Normal 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
31
dw/bullet.hh
Normal 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
66
dw/core.hh
Normal 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
83
dw/events.hh
Normal 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
298
dw/findtext.cc
Normal 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
84
dw/findtext.hh
Normal 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
136
dw/fltkcomplexbutton.cc
Normal 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
72
dw/fltkcomplexbutton.hh
Normal 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
36
dw/fltkcore.hh
Normal 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
110
dw/fltkflatview.cc
Normal 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
38
dw/fltkflatview.hh
Normal 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
614
dw/fltkimgbuf.cc
Normal 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
93
dw/fltkimgbuf.hh
Normal 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
48
dw/fltkmisc.cc
Normal 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
22
dw/fltkmisc.hh
Normal 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
732
dw/fltkplatform.cc
Normal 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
188
dw/fltkplatform.hh
Normal 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
311
dw/fltkpreview.cc
Normal 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
96
dw/fltkpreview.hh
Normal 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
1778
dw/fltkui.cc
Normal file
File diff suppressed because it is too large
Load Diff
539
dw/fltkui.hh
Normal file
539
dw/fltkui.hh
Normal 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
736
dw/fltkviewbase.cc
Normal 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
141
dw/fltkviewbase.hh
Normal 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
679
dw/fltkviewport.cc
Normal 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
117
dw/fltkviewport.hh
Normal 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
603
dw/hyphenator.cc
Normal 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
115
dw/hyphenator.hh
Normal 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
567
dw/image.cc
Normal 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
184
dw/image.hh
Normal 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
233
dw/imgbuf.hh
Normal 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
67
dw/imgrenderer.cc
Normal 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
87
dw/imgrenderer.hh
Normal 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
902
dw/iterator.cc
Normal 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
269
dw/iterator.hh
Normal 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
1357
dw/layout.cc
Normal file
File diff suppressed because it is too large
Load Diff
451
dw/layout.hh
Normal file
451
dw/layout.hh
Normal 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
83
dw/listitem.cc
Normal 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
29
dw/listitem.hh
Normal 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
618
dw/oofawarewidget.cc
Normal 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
302
dw/oofawarewidget.hh
Normal 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__
|
||||
251
dw/oofawarewidget_iterator.cc
Normal file
251
dw/oofawarewidget_iterator.cc
Normal 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
1385
dw/ooffloatsmgr.cc
Normal file
File diff suppressed because it is too large
Load Diff
290
dw/ooffloatsmgr.hh
Normal file
290
dw/ooffloatsmgr.hh
Normal 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
427
dw/oofposabslikemgr.cc
Normal 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
61
dw/oofposabslikemgr.hh
Normal 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
68
dw/oofposabsmgr.cc
Normal 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
27
dw/oofposabsmgr.hh
Normal 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
59
dw/oofposfixedmgr.cc
Normal 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
27
dw/oofposfixedmgr.hh
Normal 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
405
dw/oofpositionedmgr.cc
Normal 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
139
dw/oofpositionedmgr.hh
Normal 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
249
dw/oofposrelmgr.cc
Normal 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
50
dw/oofposrelmgr.hh
Normal 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
40
dw/outofflowmgr.cc
Normal 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
150
dw/outofflowmgr.hh
Normal 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
172
dw/platform.hh
Normal 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
5
dw/preview.xbm
Normal 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
39
dw/regardingborder.cc
Normal 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
24
dw/regardingborder.hh
Normal 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
100
dw/ruler.cc
Normal 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
45
dw/ruler.hh
Normal 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
502
dw/selection.cc
Normal 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
241
dw/selection.hh
Normal 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
143
dw/simpletablecell.cc
Normal 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
42
dw/simpletablecell.hh
Normal 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
195
dw/stackingcontextmgr.cc
Normal 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
76
dw/stackingcontextmgr.hh
Normal 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
1477
dw/style.cc
Normal file
File diff suppressed because it is too large
Load Diff
923
dw/style.hh
Normal file
923
dw/style.hh
Normal 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
1651
dw/table.cc
Normal file
File diff suppressed because it is too large
Load Diff
518
dw/table.hh
Normal file
518
dw/table.hh
Normal 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
89
dw/table_iterator.cc
Normal 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
129
dw/tablecell.cc
Normal 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
42
dw/tablecell.hh
Normal 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
3377
dw/textblock.cc
Normal file
File diff suppressed because it is too large
Load Diff
1015
dw/textblock.hh
Normal file
1015
dw/textblock.hh
Normal file
File diff suppressed because it is too large
Load Diff
285
dw/textblock_iterator.cc
Normal file
285
dw/textblock_iterator.cc
Normal 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
2295
dw/textblock_linebreaking.cc
Normal file
File diff suppressed because it is too large
Load Diff
199
dw/tools.cc
Normal file
199
dw/tools.cc
Normal 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
66
dw/tools.hh
Normal 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
359
dw/types.cc
Normal 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
319
dw/types.hh
Normal 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
531
dw/ui.cc
Normal 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
607
dw/ui.hh
Normal 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
213
dw/view.hh
Normal 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
2273
dw/widget.cc
Normal file
File diff suppressed because it is too large
Load Diff
635
dw/widget.hh
Normal file
635
dw/widget.hh
Normal 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__
|
||||
Reference in New Issue
Block a user