diff options
Diffstat (limited to 'dw')
51 files changed, 2916 insertions, 2711 deletions
diff --git a/dw/alignedtextblock.cc b/dw/alignedtextblock.cc index bb24a3bc..dde408b2 100644 --- a/dw/alignedtextblock.cc +++ b/dw/alignedtextblock.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -27,8 +26,8 @@ namespace dw { AlignedTextblock::List::List () { - textblocks = new misc::SimpleVector <AlignedTextblock*> (4); - values = new misc::SimpleVector <int> (4); + textblocks = new lout::misc::SimpleVector <AlignedTextblock*> (4); + values = new lout::misc::SimpleVector <int> (4); maxValue = 0; refCount = 0; } @@ -54,7 +53,7 @@ void AlignedTextblock::List::unref(int pos) textblocks->set (pos, NULL); refCount--; - if(refCount == 0) + if (refCount == 0) delete this; } @@ -63,12 +62,12 @@ int AlignedTextblock::CLASS_ID = -1; AlignedTextblock::AlignedTextblock (bool limitTextWidth): Textblock (limitTextWidth) { - registerName ("dw::AlignedTextblock", &CLASS_ID); + registerName ("dw::AlignedTextblock", &CLASS_ID); } void AlignedTextblock::setRefTextblock (AlignedTextblock *ref) { - if(ref == NULL) + if (ref == NULL) list = new List(); else list = ref->list; @@ -76,7 +75,7 @@ void AlignedTextblock::setRefTextblock (AlignedTextblock *ref) listPos = list->add (this); updateValue (); } - + AlignedTextblock::~AlignedTextblock() { list->unref (listPos); @@ -86,7 +85,7 @@ 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)); diff --git a/dw/alignedtextblock.hh b/dw/alignedtextblock.hh index e855f1cc..7bac15bb 100644 --- a/dw/alignedtextblock.hh +++ b/dw/alignedtextblock.hh @@ -16,8 +16,8 @@ private: class List { private: - misc::SimpleVector <AlignedTextblock*> *textblocks; - misc::SimpleVector <int> *values; + lout::misc::SimpleVector <AlignedTextblock*> *textblocks; + lout::misc::SimpleVector <int> *values; int maxValue, refCount; ~List (); diff --git a/dw/bullet.cc b/dw/bullet.cc index cc13867c..af7f5451 100644 --- a/dw/bullet.cc +++ b/dw/bullet.cc @@ -14,14 +14,13 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "bullet.hh" - + #include <stdio.h> namespace dw { @@ -58,7 +57,7 @@ void Bullet::draw (core::View *view, core::Rectangle *area) case core::style::LIST_STYLE_TYPE_DISC: default: view->drawArc (getStyle()->color, core::style::Color::SHADING_NORMAL, - filled, x, y, l, l, 0, 360); + filled, x + l/2, y + l/2, l, l, 0, 360); } } diff --git a/dw/bullet.hh b/dw/bullet.hh index 00912bd8..98854abb 100644 --- a/dw/bullet.hh +++ b/dw/bullet.hh @@ -15,8 +15,8 @@ class Bullet: public core::Widget { protected: void sizeRequestImpl (core::Requisition *requisition); - void draw (core::View *view, core::Rectangle *area); - core::Iterator *iterator (core::Content::Type mask, bool atEnd); + void draw (core::View *view, core::Rectangle *area); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); public: Bullet (); diff --git a/dw/events.hh b/dw/events.hh index 860472ab..c29cb7f4 100644 --- a/dw/events.hh +++ b/dw/events.hh @@ -9,7 +9,7 @@ namespace dw { namespace core { /** - * \brief Platform independant representation. + * \brief Platform independent representation. */ enum ButtonState { @@ -25,10 +25,10 @@ enum ButtonState /** * \brief Base class for all events. * - * The dw::core::Event hierarchy describes events in a platform independant + * The dw::core::Event hierarchy describes events in a platform independent * way. */ -class Event: public object::Object +class Event: public lout::object::Object { public: }; diff --git a/dw/findtext.cc b/dw/findtext.cc index 15de9f16..f3e0ba20 100644 --- a/dw/findtext.cc +++ b/dw/findtext.cc @@ -14,13 +14,13 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "core.hh" +#include "../lout/msg.h" namespace dw { namespace core { @@ -66,14 +66,15 @@ void FindtextState::setWidget (Widget *widget) hlIterator = NULL; } -FindtextState::Result FindtextState::search (const char *key, bool caseSens) +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 || @@ -86,18 +87,25 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens) if (nexttab) delete[] nexttab; - nexttab = createNexttab (key, caseSens); - + nexttab = createNexttab (key, caseSens, backwards); + if (iterator) delete iterator; iterator = new CharIterator (widget); - iterator->next (); + + 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 ()) { + if (search0 (backwards, firstTrial)) { // Highlighlighting is done with a clone. hlIterator = iterator->cloneCharIterator (); for (int i = 0; key[i]; i++) @@ -110,16 +118,21 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens) iterator->next (); return SUCCESS; } else { - if (firstTrial) + if (firstTrial) { return NOT_FOUND; - else { + } else { // Nothing found anymore, reset the state for the next trial. delete iterator; iterator = new CharIterator (widget); - iterator->next (); - + 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); + Result result2 = search (key, caseSens, backwards); assert (result2 == SUCCESS); return RESTART; } @@ -135,11 +148,32 @@ void FindtextState::resetSearch () if (key) delete key; - key = NULL; + 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 *key, bool caseSens) +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); @@ -156,6 +190,9 @@ int *FindtextState::createNexttab (const char *key, bool caseSens) j = nexttab[j]; } while (i < l - 1); + if (backwards) + delete [] key; + return nexttab; } @@ -179,30 +216,76 @@ bool FindtextState::unhighlight () return false; } -bool FindtextState::search0 () +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 (), key[j], caseSens)) { + if (j == -1 || charsEqual (iterator->getChar(),searchKey[j],caseSens)) { j++; - nextit = iterator->next (); + nextit = backwards ? iterator->prev () : iterator->next (); } else j = nexttab[j]; } while (nextit && j < l); - + if (j >= l) { - // Go back to where the word was found. - for (int i = 0; i < l; i++) - iterator->prev (); - return true; - } else - return false; + 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 dw diff --git a/dw/findtext.hh b/dw/findtext.hh index d0c20206..14789878 100644 --- a/dw/findtext.hh +++ b/dw/findtext.hh @@ -14,12 +14,12 @@ class FindtextState { public: typedef enum { - /** \brief The next occurance of the pattern has been found. */ + /** \brief The next occurrence of the pattern has been found. */ SUCCESS, /** - * \brief There is no further occurance of the pattern, instead, the - * first occurance has been selected. + * \brief There is no further occurrence of the pattern, instead, the + * first occurrence has been selected. */ RESTART, @@ -48,31 +48,33 @@ private: * 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; + CharIterator *hlIterator; + + static const char* rev(const char* _str); /* reverse a C string */ - static int *createNexttab (const char *key, bool caseSens); + static int *createNexttab (const char *needle,bool caseSens,bool backwards); bool unhighlight (); - bool search0 (); + 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); } + (isspace (c1) && isspace (c2)); } public: FindtextState (); ~FindtextState (); void setWidget (Widget *widget); - Result search (const char *key, bool caseSens); + Result search (const char *key, bool caseSens, bool backwards); void resetSearch (); }; diff --git a/dw/fltkcomplexbutton.cc b/dw/fltkcomplexbutton.cc index 6c15cee3..f80d0eb0 100644 --- a/dw/fltkcomplexbutton.cc +++ b/dw/fltkcomplexbutton.cc @@ -1,21 +1,22 @@ -// -// -// Copyright 1998-2006 by Bill Spitzak and others. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Library General Public -// License as published by the Free Software Foundation; either -// version 3 of the License, or (at your option) any later version. -// -// This library 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 -// Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -// USA. + +// fltkcomplexbutton.cc contains code from FLTK2's src/Button.cxx +// that is Copyright 1998-2006 by Bill Spitzak and others. +// (see http://svn.easysw.com/public/fltk/fltk/trunk/src/Button.cxx) + +/* + * 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 <fltk/events.h> #include <fltk/damage.h> @@ -87,7 +88,7 @@ int ComplexButton::handle(int event, const Rectangle& rectangle) { if (pushed()) return 1; // ignore extra pushes on currently-pushed button initial_state = state(); clear_flag(PUSHED); - do_callback(); + /* do_callback(); */ case DRAG: { bool inside = event_inside(rectangle); if (inside) { @@ -112,7 +113,7 @@ int ComplexButton::handle(int event, const Rectangle& rectangle) { redraw(DAMAGE_VALUE); if (type() == RADIO) setonly(); - else if (type()) // TOGGLE + else if (type() == TOGGLE) state(!initial_state); else { state(initial_state); @@ -139,7 +140,7 @@ int ComplexButton::handle(int event, const Rectangle& rectangle) { setonly(); if (when() & WHEN_CHANGED) do_callback(); else set_changed(); } - } else if (type()) { // TOGGLE + } else if (type() == TOGGLE) { state(!state()); if (when() & WHEN_CHANGED) do_callback(); else set_changed(); } diff --git a/dw/fltkcomplexbutton.hh b/dw/fltkcomplexbutton.hh index 72799d42..83160c06 100644 --- a/dw/fltkcomplexbutton.hh +++ b/dw/fltkcomplexbutton.hh @@ -1,22 +1,22 @@ -// -// -// Copyright 2002 by Bill Spitzak and others. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Library General Public -// License as published by the Free Software Foundation; either -// version 2 of the License, or (at your option) any later version. -// -// This library 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 -// Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -// USA. -// + +// fltkcomplexbutton.hh contains code from FLTK2's fltk/Button.h +// that is Copyright 2002 by Bill Spitzak and others. +// (see http://svn.easysw.com/public/fltk/fltk/trunk/fltk/Button.h) + +/* + * 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__ diff --git a/dw/fltkflatview.cc b/dw/fltkflatview.cc index 7f5f88a6..85385394 100644 --- a/dw/fltkflatview.cc +++ b/dw/fltkflatview.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -52,7 +51,7 @@ void FltkFlatView::setCanvasSize (int width, int ascent, int descent) #if 0 FltkWidgetView::setCanvasSize (width, ascent, descent); - + w (width); h (ascent + descent); #endif diff --git a/dw/fltkflatview.hh b/dw/fltkflatview.hh index dee5498f..5106cbdf 100644 --- a/dw/fltkflatview.hh +++ b/dw/fltkflatview.hh @@ -22,7 +22,7 @@ protected: 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 (); diff --git a/dw/fltkimgbuf.cc b/dw/fltkimgbuf.cc index b8beb1cb..6ba7ff1d 100644 --- a/dw/fltkimgbuf.cc +++ b/dw/fltkimgbuf.cc @@ -14,34 +14,36 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "fltkcore.hh" +#include "../lout/msg.h" #include "../lout/misc.hh" #include <fltk/draw.h> #include <fltk/Color.h> +#define IMAGE_MAX_AREA (6000 * 6000) + using namespace fltk; namespace dw { namespace fltk { -using namespace container::typed; +using namespace lout::container::typed; FltkImgbuf::FltkImgbuf (Type type, int width, int height) { - //printf("FltkImgbuf: new root %p\n", this); + _MSG("FltkImgbuf: new root %p\n", this); init (type, width, height, NULL); } FltkImgbuf::FltkImgbuf (Type type, int width, int height, FltkImgbuf *root) { - //printf("FltkImgbuf: new scaled %p, root is %p\n", this, root); + _MSG("FltkImgbuf: new scaled %p, root is %p\n", this, root); init (type, width, height, root); } @@ -58,19 +60,18 @@ void FltkImgbuf::init (Type type, int width, int height, FltkImgbuf *root) case RGB: bpp = 3; break; default: bpp = 1; break; } - //fprintf(stderr,"FltkImgbuf::init width=%d height=%d bpp=%d\n", - // width, height, bpp); + _MSG("FltkImgbuf::init width=%d height=%d bpp=%d\n", width, height, bpp); 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 misc::BitSet (height); + copiedRows = new lout::misc::BitSet (height); // The list is only used for root buffers. if (isRoot()) - scaledBuffers = new container::typed::List <FltkImgbuf> (true); + scaledBuffers = new lout::container::typed::List <FltkImgbuf> (true); else scaledBuffers = NULL; @@ -85,12 +86,7 @@ void FltkImgbuf::init (Type type, int width, int height, FltkImgbuf *root) FltkImgbuf::~FltkImgbuf () { - //printf ("FltkImgbuf::~FltkImgbuf (%s)\n", isRoot() ? "root" : "scaled"); - - //if (root) - // printf("FltkImgbuf[scaled %p, root is %p]: deleted\n", this, root); - //else - // printf("FltkImgbuf[root %p]: deleted\n", this); + _MSG("~FltkImgbuf[%s %p] deleted\n", isRoot() ? "root":"scaled", this); if (!isRoot()) root->detachScaledBuf (this); @@ -110,8 +106,8 @@ void FltkImgbuf::detachScaledBuf (FltkImgbuf *scaledBuf) { scaledBuffers->detachRef (scaledBuf); - //printf("FltkImgbuf[root %p]: scaled buffer %p is detached, %d left\n", - // this, scaledBuf, scaledBuffers->size ()); + _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 @@ -128,17 +124,17 @@ inline void FltkImgbuf::scaleRow (int row, const core::byte *data) { int sr1 = scaledY (row); int sr2 = scaledY (row + 1); - - for(int sr = sr1; sr < sr2; sr++) { + + for (int sr = sr1; sr < sr2; sr++) { // Avoid multiple passes. if (copiedRows->get(sr)) continue; copiedRows->set (sr, true); if (sr == sr1) { - for(int px = 0; px < root->width; px++) { + 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++) { + for (int sp = px1; sp < px2; sp++) { memcpy(rawdata + (sr*width + sp)*bpp, data + px*bpp, bpp); } } @@ -168,16 +164,16 @@ void FltkImgbuf::newScan () if (isRoot()) { for (Iterator<FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext();){ FltkImgbuf *sb = it.getNext (); - sb->copiedRows->clear(); + sb->copiedRows->clear(); } } } core::Imgbuf* FltkImgbuf::getScaledBuf (int width, int height) { - if (root) + if (!isRoot()) return root->getScaledBuf (width, height); - + if (width == this->width && height == this->height) { ref (); return this; @@ -191,6 +187,18 @@ core::Imgbuf* FltkImgbuf::getScaledBuf (int width, int height) } } + /* 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 (width <= 0 || height <= 0 || + width > IMAGE_MAX_AREA / height) { + MSG("FltkImgbuf::getScaledBuf: suspicious image size request %dx%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, this); scaledBuffers->append (sb); @@ -207,8 +215,8 @@ void FltkImgbuf::getRowArea (int row, dw::core::Rectangle *area) area->y = row; area->width = width; area->height = 1; - //fprintf(stderr,"::getRowArea: area x=%d y=%d width=%d height=%d\n", - // area->x, area->y, area->width, area->height); + _MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n", + area->x, area->y, area->width, area->height); } else { // scaled buffer int sr1 = scaledY (row); @@ -218,8 +226,8 @@ void FltkImgbuf::getRowArea (int row, dw::core::Rectangle *area) area->y = sr1; area->width = width; area->height = sr2 - sr1; - //fprintf(stderr,"::getRowArea: area x=%d y=%d width=%d height=%d\n", - // area->x, area->y, area->width, area->height); + _MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n", + area->x, area->y, area->width, area->height); } } @@ -238,28 +246,30 @@ void FltkImgbuf::ref () refCount++; //if (root) - // printf("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n", - // this, root, refCount); + // MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n", + // this, root, refCount); //else - // printf("FltkImgbuf[root %p]: ref() => %d\n", this, refCount); + // MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount); } void FltkImgbuf::unref () { //if (root) - // printf("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n", - // this, root, refCount - 1); + // MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n", + // this, root, refCount - 1); //else - // printf("FltkImgbuf[root %p]: ref() => %d\n", this, refCount - 1); + // 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) + if (scaledBuffers->isEmpty () && deleteOnUnref) { delete this; - else - printf("FltkImgbuf[root %p]: not deleted\n", this); + } else { + _MSG("FltkImgbuf[root %p]: not deleted. numScaled=%d\n", + this, scaledBuffers->size ()); + } } else // Scaled buffer buffer, simply delete it. delete this; @@ -268,7 +278,7 @@ void FltkImgbuf::unref () bool FltkImgbuf::lastReference () { - return refCount == 1 && + return refCount == 1 && (scaledBuffers == NULL || scaledBuffers->isEmpty ()); } @@ -293,29 +303,15 @@ int FltkImgbuf::scaledY(int ySrc) } void FltkImgbuf::draw (::fltk::Widget *target, int xRoot, int yRoot, - int x, int y, int width, int height) + int x, int y, int width, int height) { - // TODO (i): Implementation. - // TODO (ii): Clarify the question, whether "target" is the current widget - // (and so has not to be passed at all). + // TODO: Clarify the question, whether "target" is the current widget + // (and so has not to be passed at all). -/* - setcolor (0); + _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); - for (int row = y; row < y + height; row++) { - if (copiedRows->get (row)) { - ::fltk::Rectangle rect (x + xRoot, row + yRoot, width, 1); - fillrect (rect); - } - } -*/ - - //fprintf(stderr,"::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 1 if (x > this->width || y > this->height) { return; } @@ -328,19 +324,10 @@ void FltkImgbuf::draw (::fltk::Widget *target, int xRoot, int yRoot, height = this->height - y; } - // almost OK for rows. For some unknown reason it trims the bottom and - // rightmost parts when scrolling. + // Draw ::fltk::Rectangle rect (xRoot + x, yRoot + y, width, height); PixelType ptype = (type == RGBA) ? ::fltk::RGBA : ::fltk::RGB; drawimage(rawdata+bpp*(y*this->width + x),ptype,rect,bpp*this->width); - -#else - // OK for full image. - ::fltk::Rectangle rect (xRoot, yRoot, this->width, this->height); - PixelType ptype = (type == RGBA) ? ::fltk::RGBA : ::fltk::RGB; - drawimage(rawdata,ptype,rect); -#endif -//} } } // namespace dw diff --git a/dw/fltkmisc.cc b/dw/fltkmisc.cc index 08d75854..5d20a87a 100644 --- a/dw/fltkmisc.cc +++ b/dw/fltkmisc.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ diff --git a/dw/fltkplatform.cc b/dw/fltkplatform.cc index 337f4dba..97e10710 100644 --- a/dw/fltkplatform.cc +++ b/dw/fltkplatform.cc @@ -14,17 +14,19 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - +#include "../lout/msg.h" #include "fltkcore.hh" #include <fltk/draw.h> #include <fltk/run.h> #include <fltk/events.h> +#include <fltk/Monitor.h> +#include <fltk/InvisibleBox.h> +#include <fltk/Tooltip.h> #include <fltk/utf.h> #include <stdio.h> @@ -32,6 +34,7 @@ namespace dw { namespace fltk { using namespace ::fltk; +using namespace lout; /** * \todo Distinction between italics and oblique would be nice. @@ -47,23 +50,22 @@ FltkFont::FltkFont (core::style::FontAttrs *attrs) copyAttrs (attrs); int fa = 0; - if(weight >= 500) + if (weight >= 500) fa |= BOLD; - if(style != core::style::FONT_STYLE_NORMAL) + if (style != core::style::FONT_STYLE_NORMAL) fa |= ITALIC; font = ::fltk::font(name, fa); - if(font == NULL) { - fprintf(stderr, "No font '%s', using default sans-serif font.\n", name); + if (font == NULL) { /* * If using xft, fltk::HELVETICA just means sans, fltk::COURIER * means mono, and fltk::TIMES means serif. */ - font = HELVETICA; - } + font = HELVETICA->plus (fa); + } setfont(font, size); - spaceWidth = (int)getwidth(" "); + spaceWidth = misc::max(0, (int)getwidth(" ") + letterSpacing); int xw, xh; measure("x", xw, xh); xHeight = xh; @@ -81,6 +83,12 @@ FltkFont::~FltkFont () fontsTable->remove (this); } +bool +FltkPlatform::fontExists (const char *name) +{ + return ::fltk::font(name) != NULL; +} + FltkFont* FltkFont::create (core::style::FontAttrs *attrs) { @@ -100,11 +108,9 @@ container::typed::HashTable <dw::core::style::ColorAttrs, new container::typed::HashTable <dw::core::style::ColorAttrs, FltkColor> (false, false); -FltkColor::FltkColor (int color, core::style::Color::Type type): - Color (color, type) +FltkColor::FltkColor (int color): Color (color) { this->color = color; - this->type = type; /* * fltk/setcolor.cxx: @@ -122,13 +128,10 @@ FltkColor::FltkColor (int color, core::style::Color::Type type): colors[SHADING_NORMAL] = ::fltk::BLACK; if (!(colors[SHADING_INVERSE] = shadeColor (color, SHADING_INVERSE) << 8)) colors[SHADING_INVERSE] = ::fltk::BLACK; - - if(type == core::style::Color::TYPE_SHADED) { - if (!(colors[SHADING_DARK] = shadeColor (color, SHADING_DARK) << 8)) - colors[SHADING_DARK] = ::fltk::BLACK; - if (!(colors[SHADING_LIGHT] = shadeColor (color, SHADING_LIGHT) << 8)) - colors[SHADING_LIGHT] = ::fltk::BLACK; - } + if (!(colors[SHADING_DARK] = shadeColor (color, SHADING_DARK) << 8)) + colors[SHADING_DARK] = ::fltk::BLACK; + if (!(colors[SHADING_LIGHT] = shadeColor (color, SHADING_LIGHT) << 8)) + colors[SHADING_LIGHT] = ::fltk::BLACK; } FltkColor::~FltkColor () @@ -136,17 +139,74 @@ FltkColor::~FltkColor () colorsTable->remove (this); } -FltkColor * FltkColor::create (int col, core::style::Color::Type type) +FltkColor * FltkColor::create (int col) { - ColorAttrs attrs(col, type); + ColorAttrs attrs(col); FltkColor *color = colorsTable->get (&attrs); if (color == NULL) { - color = new FltkColor (col, type); + color = new FltkColor (col); colorsTable->put (color, color); } - return color; + return color; +} + +FltkTooltip::FltkTooltip (const char *text) : Tooltip(text) +{ + shown = false; + + if (!text || !strpbrk(text, "&@")) { + escaped_str = NULL; + } else { + /* + * WORKAROUND: ::fltk::Tooltip::tooltip_timeout() makes instance_ + * if necessary, and immediately uses it. This means that we can't + * get our hands on it to set RAW_LABEL until after it has been shown + * once. So let's escape the special characters ourselves. + */ + const char *src = text; + char *dest = escaped_str = (char *) malloc(strlen(text) * 2 + 1); + + while (*src) { + if (*src == '&' || *src == '@') + *dest++ = *src; + *dest++ = *src++; + } + *dest = '\0'; + } +} + +FltkTooltip::~FltkTooltip () +{ + if (shown) + ::fltk::Tooltip::exit(); + if (escaped_str) + free(escaped_str); +} + +FltkTooltip *FltkTooltip::create (const char *text) +{ + return new FltkTooltip(text); +} + +void FltkTooltip::onEnter() +{ + fltk::Widget *widget = fltk::belowmouse(); + + ::fltk::Tooltip::enter(widget, *((fltk::Rectangle *)widget), + escaped_str ? escaped_str : str); + shown = true; +} + +void FltkTooltip::onLeave() +{ + ::fltk::Tooltip::exit(); + shown = false; +} + +void FltkTooltip::onMotion() +{ } void FltkView::addFltkWidget (::fltk::Widget *widget, @@ -187,9 +247,9 @@ core::ui::ListResource * FltkPlatform::FltkResourceFactory::createListResource (core::ui ::ListResource ::SelectionMode - selectionMode) + selectionMode, int rows) { - return new ui::FltkListResource (platform, selectionMode); + return new ui::FltkListResource (platform, selectionMode, rows); } core::ui::OptionMenuResource * @@ -200,9 +260,10 @@ FltkPlatform::FltkResourceFactory::createOptionMenuResource () core::ui::EntryResource * FltkPlatform::FltkResourceFactory::createEntryResource (int maxLength, - bool password) + bool password, + const char *label) { - return new ui::FltkEntryResource (platform, maxLength, password); + return new ui::FltkEntryResource (platform, maxLength, password, label); } core::ui::MultiLineTextResource * @@ -238,7 +299,7 @@ FltkPlatform::FltkPlatform () idleFuncRunning = false; idleFuncId = 0; - views = new container::typed::List <FltkView> (false); + view = NULL; resources = new container::typed::List <ui::FltkResource> (false); resourceFactory.setPlatform (this); @@ -246,10 +307,9 @@ FltkPlatform::FltkPlatform () FltkPlatform::~FltkPlatform () { - if(idleFuncRunning) + if (idleFuncRunning) remove_idle (generalStaticIdle, (void*)this); delete idleQueue; - delete views; delete resources; } @@ -261,34 +321,51 @@ void FltkPlatform::setLayout (core::Layout *layout) void FltkPlatform::attachView (core::View *view) { - views->append ((FltkView*)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 ((FltkView*)view); + resource->attachView (this->view); } } void FltkPlatform::detachView (core::View *view) { - views->removeRef ((FltkView*)view); + if (this->view != view) + MSG_ERR("FltkPlatform::detachView: this->view: %p view: %p\n", + this->view, view); for (container::typed::Iterator <ui::FltkResource> it = resources->iterator (); it.hasNext (); ) { ui::FltkResource *resource = it.getNext (); resource->detachView ((FltkView*)view); } + this->view = NULL; } int FltkPlatform::textWidth (core::style::Font *font, const char *text, int len) { + int width; FltkFont *ff = (FltkFont*) font; setfont (ff->font, ff->size); - return (int) getwidth (text, len); + width = (int) getwidth (text, len); + + if (font->letterSpacing) { + int curr = 0, next = 0; + + while (next < len) { + next = nextGlyph(text, curr); + width += font->letterSpacing; + curr = next; + } + } + return width; } int FltkPlatform::nextGlyph (const char *text, int idx) @@ -301,6 +378,16 @@ int FltkPlatform::prevGlyph (const char *text, int idx) return utf8back (&text[idx - 1], text, &text[strlen (text)]) - text; } +float FltkPlatform::dpiX () +{ + return ::fltk::Monitor::all ().dpi_x (); +} + +float FltkPlatform::dpiY () +{ + return ::fltk::Monitor::all ().dpi_y (); +} + void FltkPlatform::generalStaticIdle (void *data) { ((FltkPlatform*)data)->generalIdle(); @@ -314,12 +401,12 @@ void FltkPlatform::generalIdle () /* Execute the first function in the list. */ idleFunc = idleQueue->getFirst (); (layout->*(idleFunc->func)) (); - + /* Remove this function. */ idleQueue->removeRef(idleFunc); } - if(idleQueue->isEmpty()) { + if (idleQueue->isEmpty()) { idleFuncRunning = false; remove_idle (generalStaticIdle, (void*)this); } @@ -334,7 +421,7 @@ 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) { + if (!idleFuncRunning) { add_idle (generalStaticIdle, (void*)this); idleFuncRunning = true; } @@ -355,15 +442,15 @@ void FltkPlatform::removeIdle (int idleId) container::typed::Iterator <IdleFunc> it; IdleFunc *idleFunc; - for(found = false, it = idleQueue->iterator(); !found && it.hasNext(); ) { + for (found = false, it = idleQueue->iterator(); !found && it.hasNext(); ) { idleFunc = it.getNext(); - if(idleFunc->id == idleId) { + if (idleFunc->id == idleId) { idleQueue->removeRef (idleFunc); found = true; } } - if(idleFuncRunning && idleQueue->isEmpty()) + if (idleFuncRunning && idleQueue->isEmpty()) remove_idle (generalStaticIdle, (void*)this); } @@ -374,14 +461,14 @@ core::style::Font *FltkPlatform::createFont (core::style::FontAttrs return FltkFont::create (attrs); } -core::style::Color *FltkPlatform::createSimpleColor (int color) +core::style::Color *FltkPlatform::createColor (int color) { - return FltkColor::create (color, core::style::Color::TYPE_SIMPLE); + return FltkColor::create (color); } -core::style::Color *FltkPlatform::createShadedColor (int color) +core::style::Tooltip *FltkPlatform::createTooltip (const char *text) { - return FltkColor::create (color, core::style::Color::TYPE_SHADED); + return FltkTooltip::create (text); } void FltkPlatform::copySelection(const char *text) @@ -404,12 +491,7 @@ core::ui::ResourceFactory *FltkPlatform::getResourceFactory () void FltkPlatform::attachResource (ui::FltkResource *resource) { resources->append (resource); - - for (container::typed::Iterator <FltkView> it = views->iterator (); - it.hasNext (); ) { - FltkView *view = it.getNext (); - resource->attachView (view); - } + resource->attachView (view); } void FltkPlatform::detachResource (ui::FltkResource *resource) diff --git a/dw/fltkplatform.hh b/dw/fltkplatform.hh index 7b3d3e73..7a708938 100644 --- a/dw/fltkplatform.hh +++ b/dw/fltkplatform.hh @@ -24,7 +24,7 @@ class FltkFont: public core::style::Font public: ::fltk::Font *font; - + static FltkFont *create (core::style::FontAttrs *attrs); }; @@ -34,13 +34,27 @@ class FltkColor: public core::style::Color static lout::container::typed::HashTable <dw::core::style::ColorAttrs, FltkColor> *colorsTable; - FltkColor (int color, core::style::Color::Type type); + FltkColor (int color); ~FltkColor (); public: int colors[SHADING_NUM]; - static FltkColor *create(int color, core::style::Color::Type type); + static FltkColor *create(int color); +}; + +class FltkTooltip: public core::style::Tooltip +{ +private: + FltkTooltip (const char *text); + ~FltkTooltip (); + bool shown; + char *escaped_str; /* fltk WORKAROUND */ +public: + static FltkTooltip *create(const char *text); + void onEnter(); + void onLeave(); + void onMotion(); }; @@ -78,10 +92,12 @@ private: core::ui::ComplexButtonResource * createComplexButtonResource (core::Widget *widget, bool relief); core::ui::ListResource * - createListResource (core::ui::ListResource::SelectionMode selectionMode); + createListResource (core::ui::ListResource::SelectionMode selectionMode, + int rows); core::ui::OptionMenuResource *createOptionMenuResource (); core::ui::EntryResource *createEntryResource (int maxLength, - bool password); + bool password, + const char *label); core::ui::MultiLineTextResource *createMultiLineTextResource (int cols, int rows); core::ui::CheckButtonResource *createCheckButtonResource (bool @@ -109,7 +125,7 @@ private: static void generalStaticIdle(void *data); void generalIdle(); - lout::container::typed::List <FltkView> *views; + FltkView *view; lout::container::typed::List <ui::FltkResource> *resources; public: @@ -117,23 +133,26 @@ public: ~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); 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); - core::style::Color *createSimpleColor (int color); - core::style::Color *createShadedColor (int color); - + bool fontExists (const char *name); + core::style::Color *createColor (int color); + core::style::Tooltip *createTooltip (const char *text); + core::Imgbuf *createImgbuf (core::Imgbuf::Type type, int width, int height); void copySelection(const char *text); diff --git a/dw/fltkpreview.cc b/dw/fltkpreview.cc index 6bed7adf..7096420f 100644 --- a/dw/fltkpreview.cc +++ b/dw/fltkpreview.cc @@ -14,11 +14,10 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - +#include "../lout/msg.h" #include "fltkpreview.hh" #include "fltkmisc.hh" @@ -104,6 +103,11 @@ void FltkPreview::scrollTo (int x, int y) 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) @@ -155,7 +159,7 @@ bool FltkPreview::usesFltkWidgets () return false; } -void FltkPreview::drawFltkWidget (Widget *widget, +void FltkPreview::drawFltkWidget (::fltk::Widget *widget, core::Rectangle *area) { } @@ -191,7 +195,7 @@ void FltkPreviewWindow::reallocate () int mx, my, width, height; bool warp = false; - if(preview->canvasHeight * maxWidth > maxHeight * preview->canvasWidth) { + if (preview->canvasHeight * maxWidth > maxHeight * preview->canvasWidth) { // Expand to maximal height (most likely case). width = preview->canvasWidth * maxHeight / preview->canvasHeight; height = maxHeight; @@ -288,7 +292,7 @@ int FltkPreviewButton::handle (int event) case RELEASE: window->hideWindow (); return Button::handle (event); - + default: return Button::handle (event); } diff --git a/dw/fltkpreview.hh b/dw/fltkpreview.hh index 2464db89..13db2811 100644 --- a/dw/fltkpreview.hh +++ b/dw/fltkpreview.hh @@ -34,6 +34,7 @@ public: int getHScrollbarThickness (); int getVScrollbarThickness (); void scrollTo (int x, int y); + void scroll (dw::core::ScrollCommand cmd); void setViewportSize (int width, int height, int hScrollbarThickness, int vScrollbarThickness); diff --git a/dw/fltkui.cc b/dw/fltkui.cc index 9255d7d2..c1bfd873 100644 --- a/dw/fltkui.cc +++ b/dw/fltkui.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -23,19 +22,18 @@ #include "fltkcore.hh" #include "fltkflatview.hh" #include "fltkcomplexbutton.hh" +#include "../lout/msg.h" #include "../lout/misc.hh" #include <stdio.h> #include <fltk/Widget.h> #include <fltk/Group.h> #include <fltk/Input.h> -#include <fltk/SecretInput.h> #include <fltk/TextEditor.h> #include <fltk/RadioButton.h> #include <fltk/CheckButton.h> #include <fltk/Choice.h> #include <fltk/Browser.h> -#include <fltk/MultiBrowser.h> #include <fltk/Font.h> #include <fltk/draw.h> #include <fltk/Symbol.h> @@ -49,8 +47,8 @@ namespace ui { enum { RELIEF_X_THICKNESS = 3, RELIEF_Y_THICKNESS = 3 }; -using namespace object; -using namespace container::typed; +using namespace lout::object; +using namespace lout::container::typed; FltkResource::FltkResource (FltkPlatform *platform) { @@ -63,6 +61,8 @@ FltkResource::FltkResource (FltkPlatform *platform) allocation.descent = 0; style = NULL; + + enabled = true; } /** @@ -71,98 +71,72 @@ FltkResource::FltkResource (FltkPlatform *platform) */ void FltkResource::init (FltkPlatform *platform) { - viewsAndWidgets = new container::typed::List <ViewAndWidget> (true); + view = NULL; + widget = NULL; platform->attachResource (this); } FltkResource::~FltkResource () { platform->detachResource (this); - for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator (); - it.hasNext(); ) { - ViewAndWidget *viewAndWidget = it.getNext (); - - if (viewAndWidget->widget) { - if (viewAndWidget->view) { - viewAndWidget->view->removeFltkWidget(viewAndWidget->widget); - } - delete viewAndWidget->widget; + if (widget) { + if (view) { + view->removeFltkWidget(widget); } - + delete widget; } - delete viewsAndWidgets; - if(style) + if (style) style->unref (); } void FltkResource::attachView (FltkView *view) { + if (this->view) + MSG_ERR("FltkResource::attachView: multiple views!\n"); + if (view->usesFltkWidgets ()) { - ViewAndWidget *viewAndWidget = new ViewAndWidget(); - viewAndWidget->view = view; - - viewAndWidget->widget = createNewWidget (&allocation); - viewAndWidget->view->addFltkWidget (viewAndWidget->widget, &allocation); - viewsAndWidgets->append (viewAndWidget); + this->view = view; + + widget = createNewWidget (&allocation); + view->addFltkWidget (widget, &allocation); if (style) - setWidgetStyle (viewAndWidget->widget, style); + setWidgetStyle (widget, style); + if (! enabled) + widget->deactivate (); } } void FltkResource::detachView (FltkView *view) { - for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator (); - it.hasNext(); ) { - ViewAndWidget *viewAndWidget = it.getNext (); - if (viewAndWidget->view == view) { - viewsAndWidgets->removeRef (viewAndWidget); - return; - } - } - - fprintf (stderr, "FltkResource::detachView: View not found."); + if (this->view != view) + MSG_ERR("FltkResource::detachView: this->view: %p view: %p\n", + this->view, view); + this->view = NULL; } void FltkResource::sizeAllocate (core::Allocation *allocation) { this->allocation = *allocation; - - for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator (); - it.hasNext(); ) { - ViewAndWidget *viewAndWidget = it.getNext (); - viewAndWidget->view->allocateFltkWidget (viewAndWidget->widget, - allocation); - } + view->allocateFltkWidget (widget, allocation); } void FltkResource::draw (core::View *view, core::Rectangle *area) { FltkView *fltkView = (FltkView*)view; - if (fltkView->usesFltkWidgets ()) { - for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator (); - it.hasNext(); ) { - ViewAndWidget *viewAndWidget = it.getNext (); - if (viewAndWidget->view == fltkView) { - fltkView->drawFltkWidget (viewAndWidget->widget, area); - break; - } - } + if (fltkView->usesFltkWidgets () && this->view == fltkView) { + fltkView->drawFltkWidget (widget, area); } } void FltkResource::setStyle (core::style::Style *style) { - if(this->style) + if (this->style) this->style->unref (); this->style = style; style->ref (); - for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator (); - it.hasNext(); ) { - ViewAndWidget *viewAndWidget = it.getNext (); - setWidgetStyle (viewAndWidget->widget, style); - } + setWidgetStyle (widget, style); } void FltkResource::setWidgetStyle (::fltk::Widget *widget, @@ -176,39 +150,62 @@ void FltkResource::setWidgetStyle (::fltk::Widget *widget, FltkColor *bg = (FltkColor*)style->backgroundColor; if (bg) { + int normal_bg = bg->colors[FltkColor::SHADING_NORMAL]; + if (style->color) { - /* - * TODO: if/when CSS is implemented, test whether style->color - * will consistently provide readable widgets. - */ - int32_t c = bg->colors[FltkColor::SHADING_NORMAL]; - int r = (c >> 24) & 0xff, g = (c >> 16) & 0xff, b = (c >> 8) & 0xff; - bool light = (r + g >= 0x150) || (r + g + b >= 0x180); - - widget->labelcolor(light? ::fltk::BLACK : ::fltk::WHITE); - widget->textcolor(light? ::fltk::BLACK : ::fltk::WHITE); - widget->selection_color(light? ::fltk::BLACK : ::fltk::WHITE); + int style_fg = ((FltkColor*)style->color)->colors + [FltkColor::SHADING_NORMAL]; + ::fltk::Color fg = ::fltk::contrast(style_fg, normal_bg); + + widget->labelcolor(fg); + widget->textcolor(fg); + widget->selection_color(fg); } - widget->color(bg->colors[FltkColor::SHADING_NORMAL]); - widget->buttoncolor(bg->colors[FltkColor::SHADING_NORMAL]); - widget->selection_textcolor(bg->colors[FltkColor::SHADING_NORMAL]); - if (!(widget->type() & (::fltk::Widget::RADIO|::fltk::Widget::TOGGLE))) { + widget->color(normal_bg); + widget->buttoncolor(normal_bg); + widget->selection_textcolor(normal_bg); + if (widget->type() != ::fltk::Widget::RADIO && + widget->type() != ::fltk::Widget::TOGGLE) { /* it looks awful to highlight the buttons */ widget->highlight_color(bg->colors[FltkColor::SHADING_LIGHT]); } } } - + +void FltkResource::setDisplayed(bool displayed) +{ + if (displayed) + widget->show(); + else + widget->hide(); +} + +bool FltkResource::displayed() +{ + bool ret = false; + + if (widget) { + // visible() is not the same thing as being show()n exactly, but + // show()/hide() set it appropriately for our purposes. + ret = widget->visible(); + } + return ret; +} + bool FltkResource::isEnabled () { - /** \bug Not implemented. */ - return true; + return enabled; } void FltkResource::setEnabled (bool enabled) { - /** \bug Not implemented. */ + this->enabled = enabled; + + if (enabled) + widget->activate (); + else + widget->deactivate (); } // ---------------------------------------------------------------------- @@ -286,11 +283,47 @@ void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition) } } +/* + * Get FLTK state and translate to dw + * + * TODO: find a good home for this and the fltkviewbase.cc original. + */ +static core::ButtonState getDwButtonState () +{ + int s1 = ::fltk::event_state (); + int s2 = (core::ButtonState)0; + + if (s1 & ::fltk::SHIFT) s2 |= core::SHIFT_MASK; + if (s1 & ::fltk::CTRL) s2 |= core::CONTROL_MASK; + if (s1 & ::fltk::ALT) s2 |= core::META_MASK; + if (s1 & ::fltk::BUTTON1) s2 |= core::BUTTON1_MASK; + if (s1 & ::fltk::BUTTON2) s2 |= core::BUTTON2_MASK; + if (s1 & ::fltk::BUTTON3) s2 |= core::BUTTON3_MASK; + + return (core::ButtonState)s2; +} + +static void setButtonEvent(dw::core::EventButton *event) +{ + event->xCanvas = ::fltk::event_x(); + event->yCanvas = ::fltk::event_y(); + event->state = getDwButtonState(); + event->button = ::fltk::event_button(); + event->numPressed = ::fltk::event_clicks() + 1; +} + void FltkLabelButtonResource::widgetCallback (::fltk::Widget *widget, void *data) { - if (widget->when () & ::fltk::WHEN_RELEASE) - ((FltkLabelButtonResource*)data)->emitActivate (); + if ((widget->when () & ::fltk::WHEN_RELEASE) && + ((::fltk::event_key() == ::fltk::ReturnKey) || + (::fltk::event_button() == ::fltk::LeftButton || + ::fltk::event_button() == ::fltk::MiddleButton))) { + FltkLabelButtonResource *lbr = (FltkLabelButtonResource*) data; + dw::core::EventButton event; + setButtonEvent(&event); + lbr->emitClicked(&event); + } } const char *FltkLabelButtonResource::getLabel () @@ -304,12 +337,7 @@ void FltkLabelButtonResource::setLabel (const char *label) delete this->label; this->label = strdup (label); - for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator (); - it.hasNext(); ) { - ViewAndWidget *viewAndWidget = it.getNext (); - viewAndWidget->widget->label (this->label); - } - + widget->label (this->label); queueResize (true); } @@ -320,7 +348,7 @@ FltkComplexButtonResource::FltkComplexButtonResource (FltkPlatform *platform, *widget, bool relief): FltkSpecificResource <dw::core::ui::ComplexButtonResource> (platform) { - viewsAndViews = new container::typed::List <ViewAndView> (true); + flatView = topView = NULL; this->relief = relief; FltkResource::init (platform); ComplexButtonResource::init (widget); @@ -328,7 +356,6 @@ FltkComplexButtonResource::FltkComplexButtonResource (FltkPlatform *platform, FltkComplexButtonResource::~FltkComplexButtonResource () { - delete viewsAndViews; } void FltkComplexButtonResource::widgetCallback (::fltk::Widget *widget, @@ -336,14 +363,17 @@ void FltkComplexButtonResource::widgetCallback (::fltk::Widget *widget, { FltkComplexButtonResource *res = (FltkComplexButtonResource*)data; - /* would be best not to send click pos. if the image could not be loaded */ - if (::fltk::event() == ::fltk::RELEASE && - ::fltk::event_button() == ::fltk::LeftButton) { + if (widget->when() == ::fltk::WHEN_RELEASE && + ((::fltk::event_key() == ::fltk::ReturnKey) || + (::fltk::event_button() == ::fltk::LeftButton || + ::fltk::event_button() == ::fltk::MiddleButton))) { res->click_x = ::fltk::event_x(); res->click_y = ::fltk::event_y(); - res->emitActivate (); + dw::core::EventButton event; + setButtonEvent(&event); + res->emitClicked(&event); } else { - ((FltkViewBase*)res->lastFlatView)->handle(::fltk::event()); + ((FltkViewBase*)res->flatView)->handle(::fltk::event()); } } @@ -356,56 +386,30 @@ void FltkComplexButtonResource::attachView (FltkView *view) { FltkResource::attachView (view); - if (view->usesFltkWidgets ()) { - ViewAndView *viewAndView = new ViewAndView(); - viewAndView->topView = view; - viewAndView->flatView = lastFlatView; - viewsAndViews->append (viewAndView); - } + if (view->usesFltkWidgets ()) + topView = view; } void FltkComplexButtonResource::detachView (FltkView *view) { FltkResource::detachView (view); - - for (Iterator <ViewAndView> it = viewsAndViews->iterator (); - it.hasNext(); ) { - ViewAndView *viewAndView = it.getNext (); - if (viewAndView->topView == view) { - viewsAndViews->removeRef (viewAndView); - return; - } - } - - fprintf (stderr, - "FltkComplexButtonResourceResource::detachView: View not " - "found.\n"); } void FltkComplexButtonResource::sizeAllocate (core::Allocation *allocation) { FltkResource::sizeAllocate (allocation); - for (Iterator <ViewAndView> it = viewsAndViews->iterator (); - it.hasNext(); ) { - ViewAndView *viewAndView = it.getNext (); - ((FltkFlatView*)viewAndView->flatView)->resize ( - reliefXThickness (), - reliefYThickness (), - allocation->width - 2 * reliefXThickness (), - allocation->ascent + allocation->descent - 2 * reliefYThickness ()); - - ((FltkFlatView*)viewAndView->flatView)->parent ()->init_sizes (); - } + ((FltkFlatView*)flatView)->resize ( + reliefXThickness (), reliefYThickness (), + allocation->width - 2 * reliefXThickness (), + allocation->ascent + allocation->descent - 2 * reliefYThickness ()); + + ((FltkFlatView*)flatView)->parent ()->init_sizes (); } void FltkComplexButtonResource::setLayout (dw::core::Layout *layout) { - for (Iterator <ViewAndView> it = viewsAndViews->iterator (); - it.hasNext(); ) { - ViewAndView *viewAndView = it.getNext (); - layout->attachView (viewAndView->flatView); - } + layout->attachView (flatView); } int FltkComplexButtonResource::reliefXThickness () @@ -430,30 +434,28 @@ int FltkComplexButtonResource::reliefYThickness () if (!relief) button->box(::fltk::FLAT_BOX); - FltkFlatView *flatView = - new FltkFlatView (allocation->x + reliefXThickness (), - allocation->y + reliefYThickness (), - allocation->width - 2 * reliefXThickness (), - allocation->ascent + allocation->descent - - 2 * reliefYThickness ()); - button->add (flatView); - - lastFlatView = flatView; + flatView = new FltkFlatView (allocation->x + reliefXThickness (), + allocation->y + reliefYThickness (), + allocation->width - 2 * reliefXThickness (), + allocation->ascent + allocation->descent + - 2 * reliefYThickness ()); + button->add ((FltkFlatView *)flatView); if (layout) - layout->attachView (lastFlatView); + layout->attachView (flatView); return button; } // ---------------------------------------------------------------------- FltkEntryResource::FltkEntryResource (FltkPlatform *platform, int maxLength, - bool password): + bool password, const char *label): FltkSpecificResource <dw::core::ui::EntryResource> (platform) { this->maxLength = maxLength; this->password = password; - + this->label = label ? strdup(label) : NULL; + initText = NULL; editable = false; @@ -464,46 +466,51 @@ FltkEntryResource::~FltkEntryResource () { if (initText) delete initText; + if (label) + delete label; } ::fltk::Widget *FltkEntryResource::createNewWidget (core::Allocation *allocation) { ::fltk::Input *input = - password ? - new ::fltk::SecretInput (allocation->x, allocation->y, - allocation->width, - allocation->ascent + allocation->descent) : new ::fltk::Input (allocation->x, allocation->y, allocation->width, allocation->ascent + allocation->descent); + if (password) + input->type(::fltk::Input::SECRET); input->callback (widgetCallback, this); input->when (::fltk::WHEN_ENTER_KEY_ALWAYS); - if (viewsAndWidgets->isEmpty ()) { - // First widget created, attach the set text. - if (initText) - input->value (initText); - } else - input->value - (((::fltk::Input*)viewsAndWidgets->getFirst()->widget)->value ()); + if (label) { + input->label(label); + input->set_flag(::fltk::ALIGN_INSIDE_LEFT); + } + if (initText) + input->value (initText); return input; } +void FltkEntryResource::setDisplayed(bool displayed) +{ + FltkResource::setDisplayed(displayed); + queueResize(true); +} + void FltkEntryResource::sizeRequest (core::Requisition *requisition) { - if (style) { + if (displayed() && style) { FltkFont *font = (FltkFont*)style->font; ::fltk::setfont(font->font,font->size); requisition->width = - (int)::fltk::getwidth ("M", 1) + (int)::fltk::getwidth ("n", 1) * (maxLength == UNLIMITED_MAX_LENGTH ? 10 : maxLength) + 2 * RELIEF_X_THICKNESS; requisition->ascent = font->ascent + RELIEF_Y_THICKNESS; requisition->descent = font->descent + RELIEF_Y_THICKNESS; } else { - requisition->width = 1; - requisition->ascent = 1; + requisition->width = 0; + requisition->ascent = 0; requisition->descent = 0; } } @@ -517,7 +524,7 @@ void FltkEntryResource::widgetCallback (::fltk::Widget *widget, * The Back or Forward, buttons, or the first click on a rendered * page. BUG: this must be investigated and reported to FLTK2 team */ - printf ("when = %d\n", widget->when ()); + MSG("when = %d\n", widget->when ()); if ((widget->when () & ::fltk::WHEN_ENTER_KEY_ALWAYS) && (::fltk::event_key() == ::fltk::ReturnKey)) ((FltkEntryResource*)data)->emitActivate (); @@ -525,10 +532,7 @@ void FltkEntryResource::widgetCallback (::fltk::Widget *widget, const char *FltkEntryResource::getText () { - if (viewsAndWidgets->isEmpty ()) - return initText; - else - return ((::fltk::Input*)viewsAndWidgets->getFirst()->widget)->value (); + return ((::fltk::Input*)widget)->value (); } void FltkEntryResource::setText (const char *text) @@ -537,11 +541,7 @@ void FltkEntryResource::setText (const char *text) delete initText; initText = strdup (text); - for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator (); - it.hasNext(); ) { - ViewAndWidget *viewAndWidget = it.getNext (); - ((::fltk::Input*)viewAndWidget->widget)->value (initText); - } + ((::fltk::Input*)widget)->value (initText); } bool FltkEntryResource::isEditable () @@ -557,7 +557,7 @@ void FltkEntryResource::setEditable (bool editable) // ---------------------------------------------------------------------- FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform, - int cols, int rows): + int cols, int rows): FltkSpecificResource <dw::core::ui::MultiLineTextResource> (platform) { buffer = new ::fltk::TextBuffer; @@ -568,11 +568,11 @@ FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform, // Check values. Upper bound check is left to the caller. if (numCols < 1) { - fprintf (stderr, "WARNING: numCols = %d is set to 1.\n", numCols); + MSG_WARN("numCols = %d is set to 1.\n", numCols); numCols = 1; } if (numRows < 1) { - fprintf (stderr, "WARNING: numRows = %d is set to 1.\n", numRows); + MSG_WARN("numRows = %d is set to 1.\n", numRows); numRows = 1; } @@ -582,11 +582,7 @@ FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform, FltkMultiLineTextResource::~FltkMultiLineTextResource () { /* Free memory avoiding a double-free of text buffers */ - for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator (); - it.hasNext(); ) { - ViewAndWidget *viewAndWidget = it.getNext (); - ((::fltk::TextEditor *) viewAndWidget->widget)->buffer (0); - } + ((::fltk::TextEditor *) widget)->buffer (0); delete buffer; } @@ -607,13 +603,13 @@ void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition) FltkFont *font = (FltkFont*)style->font; ::fltk::setfont(font->font,font->size); requisition->width = - (int)::fltk::getwidth ("X", 1) * numCols + + (int)::fltk::getwidth ("n", 1) * numCols + 2 * RELIEF_X_THICKNESS; requisition->ascent = - font->ascent + RELIEF_Y_THICKNESS; + RELIEF_Y_THICKNESS + font->ascent + + (font->ascent + font->descent) * (numRows - 1); requisition->descent = font->descent + - (font->ascent + font->descent) * (numRows - 1) + RELIEF_Y_THICKNESS; } else { requisition->width = 1; @@ -647,7 +643,7 @@ void FltkMultiLineTextResource::setEditable (bool editable) template <class I> FltkToggleButtonResource<I>::FltkToggleButtonResource (FltkPlatform *platform, bool activated): - FltkSpecificResource <I> (platform) + FltkSpecificResource <I> (platform) { initActivated = activated; } @@ -664,13 +660,7 @@ template <class I> *allocation) { ::fltk::Button *button = createNewButton (allocation); - - if (this->viewsAndWidgets->isEmpty ()) - button->value (initActivated); - else - button->value (((::fltk::Button*)this->viewsAndWidgets - ->getFirst()->widget)->value ()); - + button->value (initActivated); return button; } @@ -697,12 +687,7 @@ void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition) template <class I> bool FltkToggleButtonResource<I>::FltkToggleButtonResource::isActivated () { - if (this->viewsAndWidgets->isEmpty ()) - return initActivated; - else - return - ((::fltk::Button*)this->viewsAndWidgets->getFirst()->widget) - ->value (); + return ((::fltk::Button*)this->widget)->value (); } @@ -710,13 +695,7 @@ template <class I> void FltkToggleButtonResource<I>::setActivated (bool activated) { initActivated = activated; - - for (Iterator <FltkResource::ViewAndWidget> it = - this->viewsAndWidgets->iterator (); - it.hasNext(); ) { - FltkResource::ViewAndWidget *viewAndWidget = it.getNext (); - ((::fltk::Button*)viewAndWidget->widget)->value (initActivated); - } + ((::fltk::Button*)this->widget)->value (initActivated); } // ---------------------------------------------------------------------- @@ -736,7 +715,7 @@ FltkCheckButtonResource::~FltkCheckButtonResource () ::fltk::Button *FltkCheckButtonResource::createNewButton (core::Allocation - *allocation) + *allocation) { ::fltk::CheckButton *cb = new ::fltk::CheckButton (allocation->x, allocation->y, allocation->width, @@ -767,7 +746,7 @@ void FltkRadioButtonResource::Group::FltkGroupIterator::unref () FltkRadioButtonResource::Group::Group (FltkRadioButtonResource *radioButtonResource) { - list = new container::typed::List <FltkRadioButtonResource> (false); + list = new lout::container::typed::List <FltkRadioButtonResource> (false); connect (radioButtonResource); } @@ -775,7 +754,7 @@ FltkRadioButtonResource::Group::~Group () { delete list; } - + void FltkRadioButtonResource::Group::connect (FltkRadioButtonResource *radioButtonResource) { @@ -836,7 +815,7 @@ void FltkRadioButtonResource::buttonClicked () } ::fltk::Button *FltkRadioButtonResource::createNewButton (core::Allocation - *allocation) + *allocation) { /* * Groups of fltk::RadioButton must be added to one fltk::Group, which is @@ -1025,7 +1004,7 @@ template <class I> void FltkSelectionResource<I>::addItem (const char *str, } } } - + template <class I> void FltkSelectionResource<I>::pushGroup (const char *name, bool enabled) { @@ -1038,7 +1017,7 @@ template <class I> void FltkSelectionResource<I>::pushGroup (const char *name, ::fltk::ItemGroup *group = item->createNewGroupWidget (); widgetStack->stack->getTop()->getTypedValue()->add (group); widgetStack->stack->push (new TypedPointer < ::fltk::Menu> (group)); - if(!enabled) + if (!enabled) group->deactivate (); } } @@ -1123,7 +1102,7 @@ void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition) requisition->width = 1; requisition->ascent = 1; requisition->descent = 0; - } + } } void FltkOptionMenuResource::addItem (const char *str, @@ -1146,10 +1125,12 @@ bool FltkOptionMenuResource::isSelected (int index) FltkListResource::FltkListResource (FltkPlatform *platform, core::ui::ListResource::SelectionMode - selectionMode): + selectionMode, int rowCount): FltkSelectionResource <dw::core::ui::ListResource> (platform), itemsSelected(8) { + mode = selectionMode; + showRows = rowCount; init (platform); } @@ -1161,9 +1142,10 @@ FltkListResource::~FltkListResource () ::fltk::Menu *FltkListResource::createNewMenu (core::Allocation *allocation) { ::fltk::Menu *menu = - new ::fltk::MultiBrowser (allocation->x, allocation->y, - allocation->width, - allocation->ascent + allocation->descent); + new ::fltk::Browser (allocation->x, allocation->y, allocation->width, + allocation->ascent + allocation->descent); + if (mode == SELECTION_MULTIPLE) + menu->type(::fltk::Browser::MULTI); menu->set_flag (::fltk::RAW_LABEL); menu->callback(widgetCallback,this); menu->when(::fltk::WHEN_CHANGED); @@ -1173,10 +1155,25 @@ FltkListResource::~FltkListResource () void FltkListResource::widgetCallback (::fltk::Widget *widget, void *data) { ::fltk::Widget *fltkItem = ((::fltk::Menu *) widget)->item (); - int index = (long) (fltkItem->user_data ()); + int index = -1; + if (fltkItem) + index = (long) (fltkItem->user_data ()); if (index > -1) { - bool selected = fltkItem->selected (); - ((FltkListResource *) data)->itemsSelected.set (index, selected); + /* A MultiBrowser will trigger a callback for each item that is + * selected and each item that is deselected, but a "plain" + * Browser will only trigger the callback for the newly selected item + * (for which selected() is false, incidentally). + */ + FltkListResource *res = (FltkListResource *) data; + if (res->mode == SELECTION_MULTIPLE) { + bool selected = fltkItem->selected (); + res->itemsSelected.set (index, selected); + } else { + int size = res->itemsSelected.size(); + for (int i = 0; i < size; i++) + res->itemsSelected.set (i, false); + res->itemsSelected.set (index, true); + } } } @@ -1195,18 +1192,24 @@ void FltkListResource::sizeRequest (core::Requisition *requisition) if (style) { FltkFont *font = (FltkFont*)style->font; ::fltk::setfont(font->font,font->size); - int maxStringWidth = getMaxStringWidth (); - requisition->ascent = font->ascent + RELIEF_Y_THICKNESS; - requisition->descent = - (getNumberOfItems () - 1) * (font->ascent) + font->descent; - requisition->width = maxStringWidth - + (font->ascent + font->descent) * 4 / 5 - + 2 * RELIEF_X_THICKNESS; + int rows = getNumberOfItems(); + if (showRows < rows) { + rows = showRows; + } + /* + * The widget sometimes shows scrollbars when they are not required. + * The following values try to keep any scrollbars from obscuring + * options, at the cost of showing too much whitespace at times. + */ + requisition->width = getMaxStringWidth() + 24; + requisition->ascent = font->ascent + 2 + + (rows - 1) * (font->ascent + font->descent + 1); + requisition->descent = font->descent + 3; } else { requisition->width = 1; requisition->ascent = 1; requisition->descent = 0; - } + } } bool FltkListResource::isSelected (int index) diff --git a/dw/fltkui.hh b/dw/fltkui.hh index 4de99d27..245d5aad 100644 --- a/dw/fltkui.hh +++ b/dw/fltkui.hh @@ -14,8 +14,6 @@ namespace dw { namespace fltk { -using namespace lout; - /** * \brief FLTK implementation of dw::core::ui. * @@ -27,7 +25,7 @@ using namespace lout; * edge [arrowhead="none", arrowtail="empty", 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"; @@ -67,10 +65,10 @@ using namespace lout; * dw::core::ui::Resource is ambiguous for * dw::fltk::ui::FltkLabelButtonResource. * - * To solve this, we have to remove the depencency between + * 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 explicitely delegated from + * dw::fltk::ui::FltkResource, must be explicitly delegated from * dw::fltk::ui::FltkLabelButtonResourceto dw::fltk::ui::FltkResource: * * \dot @@ -79,7 +77,7 @@ using namespace lout; * edge [arrowhead="none", arrowtail="empty", 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"; @@ -120,7 +118,7 @@ using namespace lout; * edge [arrowhead="none", arrowtail="empty", 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"; @@ -174,17 +172,14 @@ namespace ui { /** * ... */ -class FltkResource: public object::Object +class FltkResource: public lout::object::Object { -protected: - class ViewAndWidget: public object::Object - { - public: - FltkView *view; - ::fltk::Widget *widget; - }; +private: + bool enabled; - container::typed::List <ViewAndWidget> *viewsAndWidgets; +protected: + FltkView *view; + ::fltk::Widget *widget; core::Allocation allocation; FltkPlatform *platform; @@ -195,12 +190,14 @@ protected: virtual ::fltk::Widget *createNewWidget (core::Allocation *allocation) = 0; void setWidgetStyle (::fltk::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); @@ -257,15 +254,7 @@ private: static void widgetCallback (::fltk::Widget *widget, void *data); protected: - class ViewAndView: public object::Object - { - public: - FltkView *topView, *flatView; - }; - - FltkView *lastFlatView; - - container::typed::List <ViewAndView> *viewsAndViews; + FltkView *topView, *flatView; void attachView (FltkView *view); void detachView (FltkView *view); @@ -274,7 +263,7 @@ protected: dw::core::Platform *createPlatform (); void setLayout (dw::core::Layout *layout); - + int reliefXThickness (); int reliefYThickness (); @@ -298,15 +287,18 @@ private: int maxLength; bool password; const char *initText; + char *label; bool editable; static void widgetCallback (::fltk::Widget *widget, void *data); + void setDisplayed (bool displayed); protected: ::fltk::Widget *createNewWidget (core::Allocation *allocation); public: - FltkEntryResource (FltkPlatform *platform, int maxLength, bool password); + FltkEntryResource (FltkPlatform *platform, int maxLength, bool password, + const char *label); ~FltkEntryResource (); void sizeRequest (core::Requisition *requisition); @@ -351,7 +343,7 @@ private: protected: virtual ::fltk::Button *createNewButton (core::Allocation *allocation) = 0; ::fltk::Widget *createNewWidget (core::Allocation *allocation); - + public: FltkToggleButtonResource (FltkPlatform *platform, bool activated); @@ -388,10 +380,10 @@ private: public dw::core::ui::RadioButtonResource::GroupIterator { private: - container::typed::Iterator <FltkRadioButtonResource> it; + lout::container::typed::Iterator <FltkRadioButtonResource> it; public: - inline FltkGroupIterator (container::typed::List + inline FltkGroupIterator (lout::container::typed::List <FltkRadioButtonResource> *list) { it = list->iterator (); } @@ -401,15 +393,15 @@ private: void unref (); }; - container::typed::List <FltkRadioButtonResource> *list; + lout::container::typed::List <FltkRadioButtonResource> *list; protected: ~Group (); public: Group (FltkRadioButtonResource *radioButtonResource); - - inline container::typed::Iterator <FltkRadioButtonResource> iterator () + + inline lout::container::typed::Iterator <FltkRadioButtonResource> iterator () { return list->iterator (); } @@ -446,7 +438,7 @@ template <class I> class FltkSelectionResource: public FltkSpecificResource <I> { protected: - class Item: public object::Object + class Item: public lout::object::Object { public: enum Type { ITEM, START, END } type; @@ -462,19 +454,19 @@ protected: ::fltk::ItemGroup *createNewGroupWidget (); }; - class WidgetStack: public object::Object + class WidgetStack: public lout::object::Object { public: ::fltk::Menu *widget; - container::typed::Stack <object::TypedPointer < ::fltk::Menu> > *stack; + lout::container::typed::Stack <lout::object::TypedPointer < ::fltk::Menu> > *stack; WidgetStack (::fltk::Menu *widget); ~WidgetStack (); }; - container::typed::List <WidgetStack> *widgetStacks; - container::typed::List <Item> *allItems; - container::typed::Vector <Item> *items; + lout::container::typed::List <WidgetStack> *widgetStacks; + lout::container::typed::List <Item> *allItems; + lout::container::typed::Vector <Item> *items; Item *createNewItem (typename Item::Type type, const char *name = NULL, @@ -491,10 +483,10 @@ public: FltkSelectionResource (FltkPlatform *platform); ~FltkSelectionResource (); - dw::core::Iterator *iterator (dw::core::Content::Type mask, bool atEnd); + dw::core::Iterator *iterator (dw::core::Content::Type mask, bool atEnd); void addItem (const char *str, bool enabled, bool selected); - + void pushGroup (const char *name, bool enabled); void popGroup (); @@ -532,11 +524,13 @@ protected: private: static void widgetCallback (::fltk::Widget *widget, void *data); - misc::SimpleVector <bool> itemsSelected; - + lout::misc::SimpleVector <bool> itemsSelected; + int showRows; + ListResource::SelectionMode mode; public: FltkListResource (FltkPlatform *platform, - core::ui::ListResource::SelectionMode selectionMode); + core::ui::ListResource::SelectionMode selectionMode, + int rows); ~FltkListResource (); void addItem (const char *str, bool enabled, bool selected); diff --git a/dw/fltkviewbase.cc b/dw/fltkviewbase.cc index b35b07df..7d519f05 100644 --- a/dw/fltkviewbase.cc +++ b/dw/fltkviewbase.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -30,6 +29,7 @@ #include <fltk/run.h> #include <stdio.h> +#include "../lout/msg.h" using namespace fltk; using namespace lout::object; @@ -47,9 +47,8 @@ FltkViewBase::FltkViewBase (int x, int y, int w, int h, const char *label): canvasWidth = 1; canvasHeight = 1; bgColor = WHITE; - lastDraw = time(0); - drawDelay = 2; /* in seconds */ mouse_x = mouse_y = 0; + exposeArea = NULL; if (backBuffer == NULL) { backBuffer = new Image (); } @@ -74,10 +73,10 @@ void FltkViewBase::draw () int d = damage (); if ((d & DAMAGE_VALUE) && !(d & DAMAGE_EXPOSE)) { - container::typed::Iterator <core::Rectangle> it; + lout::container::typed::Iterator <core::Rectangle> it; for (it = drawRegion.rectangles (); it.hasNext (); ) { - drawRectangle (it.getNext (), true); + draw (it.getNext (), DRAW_BUFFERED); } drawRegion.clear (); @@ -96,16 +95,18 @@ void FltkViewBase::draw () w (), h ()); - drawRectangle (&rect, false); - - if (! (d & DAMAGE_SCROLL)) { + if (d == 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::drawRectangle (const core::Rectangle *rect, - bool doubleBuffer) +void FltkViewBase::draw (const core::Rectangle *rect, + DrawType type) { int offsetX = 0, offsetY = 0; @@ -116,7 +117,7 @@ void FltkViewBase::drawRectangle (const core::Rectangle *rect, translateCanvasXToViewX (rect->x) + offsetX, translateCanvasYToViewY (rect->y) + offsetY, rect->width, rect->height); - + ::fltk::intersect_with_clip (viewRect); viewRect.x (viewRect.x () - offsetX); @@ -129,14 +130,16 @@ void FltkViewBase::drawRectangle (const core::Rectangle *rect, viewRect.w (), viewRect.h ()); - if (doubleBuffer && backBuffer && !backBufferInUse) { + exposeArea = &viewRect; + + if (type == DRAW_BUFFERED && backBuffer && !backBufferInUse) { backBufferInUse = true; { GSave gsave; backBuffer->setsize (viewRect.w (), viewRect.h ()); backBuffer->make_current (); - translate (-viewRect.x (), -viewRect.y ()); + translate (-viewRect.x (), -viewRect.y ()); setcolor (bgColor); fillrect (viewRect); @@ -147,19 +150,21 @@ void FltkViewBase::drawRectangle (const core::Rectangle *rect, viewRect); backBufferInUse = false; - } else if (doubleBuffer) { + } 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 push_clip (viewRect); setcolor (bgColor); fillrect (viewRect); theLayout->expose (this, &r); pop_clip (); } else { - // if doubleBuffer is false we assume that a clipping - // rectangle has been set already setcolor (bgColor); fillrect (viewRect); theLayout->expose (this, &r); } + + exposeArea = NULL; } } @@ -178,13 +183,13 @@ core::ButtonState getDwButtonState () { int s1 = event_state (); int s2 = (core::ButtonState)0; - - if(s1 & SHIFT) s2 |= core::SHIFT_MASK; - if(s1 & CTRL) s2 |= core::CONTROL_MASK; - if(s1 & ALT) s2 |= core::META_MASK; - if(s1 & BUTTON1) s2 |= core::BUTTON1_MASK; - if(s1 & BUTTON2) s2 |= core::BUTTON2_MASK; - if(s1 & BUTTON3) s2 |= core::BUTTON3_MASK; + + if (s1 & SHIFT) s2 |= core::SHIFT_MASK; + if (s1 & CTRL) s2 |= core::CONTROL_MASK; + if (s1 & ALT) s2 |= core::META_MASK; + if (s1 & BUTTON1) s2 |= core::BUTTON1_MASK; + if (s1 & BUTTON2) s2 |= core::BUTTON2_MASK; + if (s1 & BUTTON3) s2 |= core::BUTTON3_MASK; return (core::ButtonState)s2; } @@ -194,7 +199,7 @@ int FltkViewBase::handle (int event) bool processed; /** - * \todo Consider, whether this from the FLTK documentation has any + * \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. " */ @@ -205,7 +210,11 @@ int FltkViewBase::handle (int event) translateViewXToCanvasX (event_x ()), translateViewYToCanvasY (event_y ()), getDwButtonState (), event_button ()); - //printf ("PUSH => %s\n", processed ? "true" : "false"); + _MSG("PUSH => %s\n", processed ? "true" : "false"); + if (processed) { + /* pressed dw content; give focus to the view */ + ::fltk::focus(this); + } return processed ? true : Group::handle (event); case RELEASE: @@ -214,7 +223,7 @@ int FltkViewBase::handle (int event) translateViewXToCanvasX (event_x ()), translateViewYToCanvasY (event_y ()), getDwButtonState (), event_button ()); - //printf ("RELEASE => %s\n", processed ? "true" : "false"); + _MSG("RELEASE => %s\n", processed ? "true" : "false"); return processed ? true : Group::handle (event); case MOVE: @@ -225,7 +234,7 @@ int FltkViewBase::handle (int event) translateViewXToCanvasX (mouse_x), translateViewYToCanvasY (mouse_y), getDwButtonState ()); - //printf ("MOVE => %s\n", processed ? "true" : "false"); + _MSG("MOVE => %s\n", processed ? "true" : "false"); return processed ? true : Group::handle (event); case DRAG: @@ -234,7 +243,7 @@ int FltkViewBase::handle (int event) translateViewXToCanvasX (event_x ()), translateViewYToCanvasY (event_y ()), getDwButtonState ()); - //printf ("DRAG => %s\n", processed ? "true" : "false"); + _MSG("DRAG => %s\n", processed ? "true" : "false"); return processed ? true : Group::handle (event); case ENTER: @@ -246,7 +255,7 @@ int FltkViewBase::handle (int event) case LEAVE: theLayout->leaveNotify (this, getDwButtonState ()); return Group::handle (event); - + default: return Group::handle (event); } @@ -269,7 +278,7 @@ void FltkViewBase::setCursor (core::style::Cursor cursor) { static Cursor *mapDwToFltk[] = { CURSOR_CROSS, - CURSOR_ARROW, + CURSOR_DEFAULT, CURSOR_HAND, CURSOR_MOVE, CURSOR_WE, @@ -288,7 +297,7 @@ void FltkViewBase::setCursor (core::style::Cursor cursor) /* static char *cursorName[] = { "CURSOR_CROSS", - "CURSOR_ARROW", + "CURSOR_DEFAULT", "CURSOR_HAND", "CURSOR_MOVE", "CURSOR_WE", @@ -304,7 +313,7 @@ void FltkViewBase::setCursor (core::style::Cursor cursor) "CURSOR_HELP" }; - printf ("Cursor changes to '%s'.\n", cursorName[cursor]); + MSG("Cursor changes to '%s'.\n", cursorName[cursor]); */ /** \bug Does not work */ @@ -333,33 +342,13 @@ void FltkViewBase::queueDraw (core::Rectangle *area) redraw (DAMAGE_VALUE); } -static void drawTotalTimeout (void *data) -{ - FltkViewBase *view = (FltkViewBase*) data; - if (time(0) >= view->lastDraw + view->drawDelay) { - view->drawTotal (); - } else { - ::fltk::add_timeout (0.2f, drawTotalTimeout, data); - } -} - -void FltkViewBase::drawTotal () -{ - //static int calls = 0; - //printf(" FltkViewBase::drawTotal calls = %d\n", ++calls); - redraw (DAMAGE_EXPOSE); - lastDraw = time (0); - cancelQueueDraw (); -} - void FltkViewBase::queueDrawTotal () { - drawTotal (); + redraw (DAMAGE_EXPOSE); } void FltkViewBase::cancelQueueDraw () { - ::fltk::remove_timeout (drawTotalTimeout, this); } void FltkViewBase::drawPoint (core::style::Color *color, @@ -383,10 +372,26 @@ void FltkViewBase::drawRectangle (core::style::Color *color, int x, int y, int width, int height) { setcolor(((FltkColor*)color)->colors[shading]); + if (width < 0) { + x += width; + width = -width; + } + if (height < 0) { + y += height; + height = -height; + } + int x1 = translateCanvasXToViewX (x); int y1 = translateCanvasYToViewY (y); int x2 = translateCanvasXToViewX (x + width); int y2 = translateCanvasYToViewY (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); + ::fltk::Rectangle rect (x1, y1, x2 - x1, y2 - y1); if (filled) fillrect (rect); @@ -396,13 +401,13 @@ void FltkViewBase::drawRectangle (core::style::Color *color, void FltkViewBase::drawArc (core::style::Color *color, core::style::Color::Shading shading, bool filled, - int x, int y, int width, int height, + int centerX, int centerY, int width, int height, int angle1, int angle2) { setcolor(((FltkColor*)color)->colors[shading]); - int x1 = translateCanvasXToViewX (x); - int y1 = translateCanvasYToViewY (y); - ::fltk::Rectangle rect (x1, y1, width, height); + int x = translateCanvasXToViewX (centerX) - width / 2; + int y = translateCanvasYToViewY (centerY) - height / 2; + ::fltk::Rectangle rect (x, y, width, height); addchord(rect, angle1, angle2); closepath(); if (filled) @@ -427,7 +432,7 @@ void FltkViewBase::drawPolygon (core::style::Color *color, fillpath(); else strokepath(); - } + } } core::View *FltkViewBase::getClippingView (int x, int y, int width, int height) @@ -456,7 +461,7 @@ FltkWidgetView::~FltkWidgetView () void FltkWidgetView::layout () { /** - * pass layout to child widgets. This is needed for complex fltk + * pass layout to child widgets. This is needed for complex fltk * widgets as TextEditor. * We can't use Group::layout() as that would rearrange the widgets. */ @@ -477,8 +482,23 @@ void FltkWidgetView::drawText (core::style::Font *font, FltkFont *ff = (FltkFont*)font; setfont(ff->font, ff->size); setcolor(((FltkColor*)color)->colors[shading]); - drawtext(text, len, - translateCanvasXToViewX (x), translateCanvasYToViewY (y)); + + if (!font->letterSpacing) { + drawtext(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; + + while (next < len) { + next = theLayout->nextGlyph(text, curr); + drawtext(text + curr, next - curr, viewX, viewY); + viewX += font->letterSpacing + (int)getwidth(text + curr,next - curr); + curr = next; + } + } } void FltkWidgetView::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot, diff --git a/dw/fltkviewbase.hh b/dw/fltkviewbase.hh index fcc2b43d..5e1981f8 100644 --- a/dw/fltkviewbase.hh +++ b/dw/fltkviewbase.hh @@ -16,17 +16,28 @@ namespace fltk { class FltkViewBase: public FltkView, public ::fltk::Group { private: + typedef enum { DRAW_PLAIN, DRAW_CLIPPED, DRAW_BUFFERED } DrawType; + int bgColor; core::Region drawRegion; + ::fltk::Rectangle *exposeArea; static ::fltk::Image *backBuffer; static bool backBufferInUse; - void drawRectangle (const core::Rectangle *rect, bool doubleBuffer); + void draw (const core::Rectangle *rect, DrawType type); void drawChildWidgets (); - -public: - time_t lastDraw; - time_t drawDelay; + inline void clipPoint (int *x, int *y, int border) { + if (exposeArea) { + if (*x < exposeArea->x () - border) + *x = exposeArea->x () - border; + if (*x > exposeArea->r () + border) + *x = exposeArea->r () + border; + if (*y < exposeArea->y () - border) + *y = exposeArea->y () - border; + if (*y > exposeArea->b () + border) + *y = exposeArea->b () + border; + } + } protected: core::Layout *theLayout; @@ -54,7 +65,6 @@ public: void finishDrawing (core::Rectangle *area); void queueDraw (core::Rectangle *area); void queueDrawTotal (); - void drawTotal (); void cancelQueueDraw (); void drawPoint (core::style::Color *color, core::style::Color::Shading shading, @@ -67,7 +77,7 @@ public: int x, int y, int width, int height); void drawArc (core::style::Color *color, core::style::Color::Shading shading, bool filled, - int x, int y, int width, int height, + int centerX, int centerY, int width, int height, int angle1, int angle2); void drawPolygon (core::style::Color *color, core::style::Color::Shading shading, @@ -84,7 +94,7 @@ class FltkWidgetView: public FltkViewBase public: FltkWidgetView (int x, int y, int w, int h, const char *label = 0); ~FltkWidgetView (); - + void layout(); void drawText (core::style::Font *font, diff --git a/dw/fltkviewport.cc b/dw/fltkviewport.cc index c999745a..a8555c60 100644 --- a/dw/fltkviewport.cc +++ b/dw/fltkviewport.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -27,8 +26,10 @@ #include <fltk/events.h> #include <stdio.h> +#include "../lout/msg.h" using namespace fltk; +using namespace lout; using namespace lout::object; using namespace lout::container::typed; @@ -104,8 +105,8 @@ void FltkViewport::adjustScrollbarsAndGadgetsAllocation () vscrollbar->w (SCROLLBAR_THICKNESS); int x = w () - SCROLLBAR_THICKNESS, y = h () - SCROLLBAR_THICKNESS; - for(Iterator <TypedPointer < ::fltk::Widget> > it = gadgets->iterator (); - it.hasNext (); ) { + for (Iterator <TypedPointer < ::fltk::Widget> > it = gadgets->iterator (); + it.hasNext (); ) { ::fltk::Widget *widget = it.getNext()->getTypedValue (); widget->x (0); widget->y (0); @@ -156,7 +157,7 @@ void FltkViewport::layout () { theLayout->viewportSizeChanged (this, w(), h()); adjustScrollbarsAndGadgetsAllocation (); - + FltkWidgetView::layout (); } @@ -167,10 +168,10 @@ void FltkViewport::draw_area (void *data, const Rectangle& cr ) vp->FltkWidgetView::draw (); - for(Iterator <TypedPointer < ::fltk::Widget> > it = vp->gadgets->iterator (); - it.hasNext (); ) { - ::fltk::Widget *widget = it.getNext()->getTypedValue (); - vp->draw_child (*widget); + for (Iterator <TypedPointer < ::fltk::Widget> > it = vp->gadgets->iterator(); + it.hasNext (); ) { + ::fltk::Widget *widget = it.getNext()->getTypedValue (); + vp->draw_child (*widget); } pop_clip(); @@ -205,13 +206,13 @@ void FltkViewport::draw () } } - scrollDX = 0; - scrollDY = 0; + scrollDX = 0; + scrollDY = 0; } int FltkViewport::handle (int event) { - //printf("FltkViewport::handle %d\n", event); + _MSG("FltkViewport::handle %d\n", event); if (hscrollbar->Rectangle::contains (event_x (), event_y ()) && !(event_state() & (SHIFT | CTRL | ALT)) && @@ -225,13 +226,30 @@ int FltkViewport::handle (int event) } switch(event) { + case ::fltk::KEY: + /* Tell fltk we want to receive KEY events as SHORTCUT. + * As we don't know the exact keybindings set by the user, we ask + * for all of them (except TabKey to keep form navigation). */ + if (::fltk::event_key() != TabKey) + return 0; + break; + case ::fltk::FOCUS: /** \bug Draw focus box. */ - return 1; + + /* If the user clicks with the left button we take focus + * and thereby unfocus any form widgets. + * Otherwise we let fltk do the focus handling. + */ + if (::fltk::event_button() == ::fltk::LeftButton || focus_index() < 0) { + focus_index(-1); + return 1; + } + break; case ::fltk::UNFOCUS: /** \bug Undraw focus box. */ - return 1; + break; case ::fltk::PUSH: take_focus(); @@ -261,12 +279,12 @@ int FltkViewport::handle (int event) case ::fltk:: MOUSEWHEEL: return (event_dx() ? hscrollbar : vscrollbar)->handle(event); break; - + case ::fltk::RELEASE: if (::fltk::event_button() == ::fltk::MiddleButton) { dragScrolling = 0; setCursor (core::style::CURSOR_DEFAULT); - } + } break; case ::fltk::ENTER: @@ -279,60 +297,6 @@ int FltkViewport::handle (int event) case ::fltk::LEAVE: mouse_x = mouse_y = -1; break; - - case ::fltk::KEY: - /* tell fltk we want to receive these KEY events as SHORTCUT */ - switch (::fltk::event_key()) { - case PageUpKey: - case PageDownKey: - case SpaceKey: - case DownKey: - case UpKey: - case RightKey: - case LeftKey: - case HomeKey: - case EndKey: - return 0; - } - break; - - case ::fltk::SHORTCUT: - switch (::fltk::event_key()) { - case PageUpKey: - case 'b': - case 'B': - scroll (0, -vscrollbar->pagesize ()); - return 1; - - case PageDownKey: - case SpaceKey: - scroll (0, vscrollbar->pagesize ()); - return 1; - - case DownKey: - scroll (0, (int) vscrollbar->linesize ()); - return 1; - - case UpKey: - scroll (0, (int) -vscrollbar->linesize ()); - return 1; - - case RightKey: - scroll ((int) hscrollbar->linesize (), 0); - return 1; - - case LeftKey: - scroll ((int) -hscrollbar->linesize (), 0); - return 1; - - case HomeKey: - scrollTo (scrollX, 0); - return 1; - - case EndKey: - scrollTo (scrollX, canvasHeight); /* gets adjusted in scrollTo () */ - return 1; - } } return FltkWidgetView::handle (event); @@ -361,7 +325,7 @@ void FltkViewport::positionChanged () /* * For scrollbars, this currently sets the same step to both vertical and * horizontal. It may me differentiated if necessary. - */ + */ void FltkViewport::setScrollStep(int step) { vscrollbar->linesize(step); @@ -419,6 +383,27 @@ void FltkViewport::scroll (int dx, int dy) scrollTo (scrollX + dx, scrollY + dy); } +void FltkViewport::scroll (core::ScrollCommand cmd) +{ + if (cmd == core::SCREEN_UP_CMD) { + scroll (0, -vscrollbar->pagesize ()); + } else if (cmd == core::SCREEN_DOWN_CMD) { + scroll (0, vscrollbar->pagesize ()); + } else if (cmd == core::LINE_UP_CMD) { + scroll (0, (int) -vscrollbar->linesize ()); + } else if (cmd == core::LINE_DOWN_CMD) { + scroll (0, (int) vscrollbar->linesize ()); + } else if (cmd == core::LEFT_CMD) { + scroll ((int) -hscrollbar->linesize (), 0); + } else if (cmd == core::RIGHT_CMD) { + scroll ((int) 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 () */ + } +} + void FltkViewport::setViewportSize (int width, int height, int hScrollbarThickness, int vScrollbarThickness) @@ -442,8 +427,8 @@ void FltkViewport::updateCanvasWidgets (int dx, int dy) // scroll all child widgets except scroll bars for (int i = children () - 1; i > 0; i--) { ::fltk::Widget *widget = child (i); - - if (widget == hscrollbar || widget == vscrollbar) + + if (widget == hscrollbar || widget == vscrollbar) continue; widget->x (widget->x () - dx); diff --git a/dw/fltkviewport.hh b/dw/fltkviewport.hh index 6af377d4..1e7f54f6 100644 --- a/dw/fltkviewport.hh +++ b/dw/fltkviewport.hh @@ -26,7 +26,8 @@ private: ::fltk::Scrollbar *vscrollbar, *hscrollbar; GadgetOrientation gadgetOrientation[4]; - container::typed::List <object::TypedPointer < ::fltk::Widget> > *gadgets; + lout::container::typed::List <lout::object::TypedPointer < ::fltk::Widget> > + *gadgets; void adjustScrollbarsAndGadgetsAllocation (); void adjustScrollbarValues (); @@ -49,7 +50,7 @@ protected: public: FltkViewport (int x, int y, int w, int h, const char *label = 0); ~FltkViewport (); - + void layout(); void draw (); int handle (int event); @@ -60,6 +61,7 @@ public: int getHScrollbarThickness (); int getVScrollbarThickness (); 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); diff --git a/dw/image.cc b/dw/image.cc index ce54f561..cab40ed5 100644 --- a/dw/image.cc +++ b/dw/image.cc @@ -14,13 +14,13 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "image.hh" +#include "../lout/msg.h" #include "../lout/misc.hh" namespace dw { @@ -38,6 +38,18 @@ 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; @@ -105,6 +117,15 @@ 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; @@ -139,16 +160,34 @@ Image::~Image() delete altText; if (buffer) buffer->unref (); + if (mapKey) + delete mapKey; } void Image::sizeRequestImpl (core::Requisition *requisition) { if (buffer) { - requisition->width = buffer->getRootWidth (); - requisition->ascent = buffer->getRootHeight (); + if (getStyle ()->height == core::style::LENGTH_AUTO && + core::style::isAbsLength (getStyle ()->width) && + buffer->getRootWidth () > 0) { + // preserve aspect ratio when only width is given + requisition->width = core::style::absLengthVal (getStyle ()->width); + requisition->ascent = buffer->getRootHeight () * + requisition->width / buffer->getRootWidth (); + } else if (getStyle ()->width == core::style::LENGTH_AUTO && + core::style::isAbsLength (getStyle ()->height) && + buffer->getRootHeight () > 0) { + // preserve aspect ratio when only height is given + requisition->ascent = core::style::absLengthVal (getStyle ()->height); + requisition->width = buffer->getRootWidth () * + requisition->ascent / buffer->getRootHeight (); + } else { + requisition->width = buffer->getRootWidth (); + requisition->ascent = buffer->getRootHeight (); + } requisition->descent = 0; } else { - if(altText && altText[0]) { + if (altText && altText[0]) { if (altTextWidth == -1) altTextWidth = layout->textWidth (getStyle()->font, altText, strlen (altText)); @@ -181,20 +220,21 @@ void Image::sizeAllocateImpl (core::Allocation *allocation) dx = getStyle()->boxDiffWidth (); dy = getStyle()->boxDiffHeight (); #if 0 - printf("boxDiffHeight = %d + %d, buffer=%p\n", - getStyle()->boxOffsetY(), getStyle()->boxRestHeight(), buffer); - printf("getContentWidth() = allocation.width - style->boxDiffWidth ()" - " = %d - %d = %d\n", - this->allocation.width, getStyle()->boxDiffWidth(), - this->allocation.width - getStyle()->boxDiffWidth()); - printf("getContentHeight() = getHeight() - style->boxDiffHeight ()" - " = %d - %d = %d\n", this->getHeight(), getStyle()->boxDiffHeight(), - this->getHeight() - getStyle()->boxDiffHeight()); + MSG("boxDiffHeight = %d + %d, buffer=%p\n", + getStyle()->boxOffsetY(), getStyle()->boxRestHeight(), buffer); + MSG("getContentWidth() = allocation.width - style->boxDiffWidth ()" + " = %d - %d = %d\n", + this->allocation.width, getStyle()->boxDiffWidth(), + this->allocation.width - getStyle()->boxDiffWidth()); + MSG("getContentHeight() = getHeight() - style->boxDiffHeight ()" + " = %d - %d = %d\n", this->getHeight(), getStyle()->boxDiffHeight(), + this->getHeight() - getStyle()->boxDiffHeight()); #endif - if (buffer != NULL && - /* It may be, that the image is allocated at zero content size. In this - * case, we simply wait. */ - getContentWidth () > 0 && getContentHeight () > 0) { + if (buffer && + (allocation->width - dx > 0 || + allocation->ascent + allocation->descent - dy > 0)) { + // Zero content size : simply wait... + // Only one dimension: naturally scale oldBuffer = buffer; buffer = oldBuffer->getScaledBuf (allocation->width - dx, allocation->ascent @@ -209,35 +249,64 @@ void Image::enterNotifyImpl (core::EventCrossing *event) currLink = getStyle()->x_link; if (currLink != -1) { - (void) emitLinkEnter (currLink, -1, -1, -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) emitLinkEnter (-1, -1, -1, -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 - getStyle()->boxOffsetX(); + + ret = misc::min(getContentWidth(), misc::max(ret, 0)); + return ret; +} + +int Image::contentY (core::MousePositionEvent *event) +{ + int ret = event->yWidget - getStyle()->boxOffsetY(); + + ret = misc::min(getContentHeight(), misc::max(ret, 0)); + return ret; } bool Image::motionNotifyImpl (core::EventMotion *event) { - if (mapList) { - /* client-side image map */ - int newLink = mapList->link (mapKey, event->xWidget, event->yWidget); - if (newLink != currLink) { - currLink = newLink; - clicking = false; - setCursor(newLink == -1 ? core::style::CURSOR_DEFAULT : - core::style::CURSOR_POINTER); - (void) emitLinkEnter (newLink, -1, -1, -1); + 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); } - } else if (isMap && currLink != -1) { - /* server-side image map */ - (void) emitLinkEnter (currLink, -1, event->xWidget, event->yWidget); } return true; } @@ -245,27 +314,29 @@ bool Image::motionNotifyImpl (core::EventMotion *event) bool Image::buttonPressImpl (core::EventButton *event) { bool ret = false; - currLink = mapList ? mapList->link (mapKey, event->xWidget, event->yWidget): - getStyle()->x_link; + + currLink = mapList? mapList->link (mapKey, contentX(event),contentY(event)): + getStyle()->x_link; if (event->button == 3){ - (void)emitLinkPress(currLink, getStyle()->x_img, -1,-1,event); + (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, event->xWidget, event->yWidget): + currLink = mapList ? mapList->link (mapKey, contentX(event),contentY(event)): getStyle()->x_link; if (clicking) { - int x = isMap ? event->xWidget : -1; - int y = isMap ? event->yWidget : -1; + int x = isMap ? contentX(event) : -1; + int y = isMap ? contentY(event) : -1; clicking = false; - emitLinkClick (currLink, getStyle()->x_img, x, y, event); + layout->emitLinkClick (this, currLink, getStyle()->x_img, x, y, event); return true; } return false; @@ -292,22 +363,25 @@ void Image::draw (core::View *view, core::Rectangle *area) intersection.x - dx, intersection.y - dy, intersection.width, intersection.height); } else { - if(altText && altText[0]) { + core::View *clippingView; + + if (altText && altText[0]) { + core::View *usedView = view; + + clippingView = NULL; + if (altTextWidth == -1) altTextWidth = layout->textWidth (getStyle()->font, altText, strlen (altText)); - - core::View *clippingView = NULL, *usedView = view; - if (allocation.width < altTextWidth || - allocation.ascent < getStyle()->font->ascent || - allocation.descent < getStyle()->font->descent) { + + if ((getContentWidth() < altTextWidth) || + (getContentHeight() < + getStyle()->font->ascent + getStyle()->font->descent)) { clippingView = usedView = view->getClippingView (allocation.x + getStyle()->boxOffsetX (), allocation.y + getStyle()->boxOffsetY (), - allocation.width - - getStyle()->boxDiffWidth (), - allocation.ascent + allocation.descent - - getStyle()->boxDiffHeight ()); + getContentWidth(), + getContentHeight()); } usedView->drawText (getStyle()->font, getStyle()->color, @@ -317,9 +391,21 @@ void Image::draw (core::View *view, core::Rectangle *area) + getStyle()->font->ascent, altText, strlen(altText)); - if(clippingView) + if (clippingView) view->mergeClippingView (clippingView); } + if (mapKey) { + clippingView = view->getClippingView (allocation.x + + getStyle()->boxOffsetX (), + allocation.y + + getStyle()->boxOffsetY (), + getContentWidth(), + getContentHeight()); + mapList->drawMap(mapKey, clippingView, getStyle(), + allocation.x + getStyle()->boxOffsetX (), + allocation.y + getStyle()->boxOffsetY ()); + view->mergeClippingView (clippingView); + } } /** TODO: draw selection */ @@ -339,11 +425,10 @@ void Image::setBuffer (core::Imgbuf *buffer, bool resize) if (resize) queueResize (0, true); - // If the image has not yet been allocated, or is allocated at zero - // content size, the first part is useless. if (wasAllocated () && getContentWidth () > 0 && getContentHeight () > 0) { - this->buffer = - buffer->getScaledBuf (getContentWidth (), getContentHeight ()); + // Only scale when both dimensions are known. + this->buffer = + buffer->getScaledBuf (getContentWidth (), getContentHeight ()); } else { this->buffer = buffer; buffer->ref (); @@ -358,7 +443,7 @@ void Image::drawRow (int row) core::Rectangle area; assert (buffer != NULL); - + buffer->getRowArea (row, &area); if (area.width && area.height) queueDrawArea (area.x + getStyle()->boxOffsetX (), @@ -386,6 +471,8 @@ void Image::setIsMap () void Image::setUseMap (ImageMapsList *list, object::Object *key) { mapList = list; + if (mapKey && mapKey != key) + delete mapKey; mapKey = key; } diff --git a/dw/image.hh b/dw/image.hh index 37c27e0d..a8314d4f 100644 --- a/dw/image.hh +++ b/dw/image.hh @@ -38,7 +38,8 @@ private: 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); @@ -47,7 +48,7 @@ private: lout::container::typed::HashTable <lout::object::Object, ImageMap> *imageMaps; ImageMap *currentMap; - + public: ImageMapsList (); ~ImageMapsList (); @@ -55,6 +56,8 @@ public: 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); }; @@ -68,7 +71,7 @@ public: * <h3>Signals</h3> * * For image maps, dw::Image uses the signals defined in - * dw::core::Widget::LinkReceiver. For client side image maps, -1 is + * 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. * @@ -89,8 +92,8 @@ public: * * 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::Widget::LinkReceiver. - * + * 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. @@ -103,13 +106,13 @@ public: * 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::Widget::LinkReceiver. + * delegated to dw::core::Layout::LinkReceiver. * * \sa\ref dw-images-and-backgrounds */ @@ -129,15 +132,17 @@ protected: void sizeRequestImpl (core::Requisition *requisition); void sizeAllocateImpl (core::Allocation *allocation); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area); 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); + //core::Iterator *iterator (Content::Type mask, bool atEnd); public: static int CLASS_ID; @@ -145,7 +150,7 @@ public: Image(const char *altText); ~Image(); - core::Iterator *iterator (core::Content::Type mask, bool atEnd); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); inline core::Imgbuf *getBuffer () { return buffer; } void setBuffer (core::Imgbuf *buffer, bool resize = false); @@ -154,6 +159,13 @@ public: 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 diff --git a/dw/imgbuf.hh b/dw/imgbuf.hh index 8948bbef..150bf165 100644 --- a/dw/imgbuf.hh +++ b/dw/imgbuf.hh @@ -8,13 +8,11 @@ namespace dw { namespace core { -using namespace lout; - /** - * \brief The platform independant interface for image buffers. + * \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 independant interface. The purpose of + * but have this general, platform independent interface. The purpose of * an image buffer is * * <ol> @@ -53,16 +51,16 @@ using namespace lout; * * * <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, heigt, @@ -71,15 +69,15 @@ using namespace lout; * 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> @@ -92,7 +90,7 @@ using namespace lout; * <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> * @@ -120,7 +118,7 @@ using namespace lout; * \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); @@ -133,12 +131,12 @@ using namespace lout; * 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: @@ -149,7 +147,7 @@ using namespace lout; * <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 @@ -158,7 +156,7 @@ using namespace lout; * * \sa \ref dw-images-and-backgrounds */ -class Imgbuf: public object::Object, public lout::signal::ObservedObject +class Imgbuf: public lout::object::Object, public lout::signal::ObservedObject { public: enum Type { RGB, RGBA, GRAY, INDEXED, INDEXED_ALPHA }; diff --git a/dw/iterator.cc b/dw/iterator.cc index 39e09d41..e9431e9b 100644 --- a/dw/iterator.cc +++ b/dw/iterator.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -23,6 +22,8 @@ #include "core.hh" #include <limits.h> +using namespace lout; + namespace dw { namespace core { @@ -57,7 +58,7 @@ bool Iterator::equals (Object *other) /** * \brief Delete the iterator. * - * The desctructor is hidden, implementations may use optimizations for + * The destructor is hidden, implementations may use optimizations for * the allocation. (Will soon be the case for dw::core::EmptyIteratorFactory.) */ void Iterator::unref () @@ -146,7 +147,7 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end, // |----------------------------| // width // - // Therefor, we the region smaller, so that the region will be + // Therefore, we make the region smaller, so that the region will be // displayed like this: // // ,-- alloc1 @@ -159,7 +160,7 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end, // |----------| // width // - + /** \todo Changes in the viewport size, until the idle function is * called, are not regarded. */ @@ -210,7 +211,7 @@ int EmptyIterator::compareTo (misc::Comparable *other) if (content.type == otherIt->content.type) return 0; - else if(content.type == Content::START) + else if (content.type == Content::START) return -1; else return +1; @@ -468,7 +469,7 @@ DeepIterator::DeepIterator (Iterator *it) // since an DeepIterator should never return widgets. if (it->getContent()->type == Content::WIDGET) { Iterator *it2; - + // The second argument of searchDownward is actually a matter of // taste :-) if ((it2 = searchDownward (it, mask, false)) || @@ -484,10 +485,10 @@ DeepIterator::DeepIterator (Iterator *it) hasContents = false; } } - + //DEBUG_MSG (1, " => %s\n", a_Dw_iterator_text (it)); - if(hasContents) { + if (hasContents) { // If this widget has parents, we must construct appropriate iterators. // // \todo There may be a faster way instead of iterating through the @@ -509,7 +510,7 @@ DeepIterator::DeepIterator (Iterator *it) break; } } - + stack.put (it, thisLevel); content = *(it->getContent()); } @@ -552,10 +553,10 @@ int DeepIterator::compareTo (misc::Comparable *other) while (stack.get(level)->getWidget () != otherDeepIterator->stack.get(level)->getWidget ()) 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. */ @@ -574,7 +575,7 @@ bool DeepIterator::isEmpty () { bool DeepIterator::next () { Iterator *it = stack.getTop (); - + if (it->next ()) { if (it->getContent()->type == Content::WIDGET) { // Widget: new iterator on stack, to search in this widget. @@ -596,7 +597,7 @@ bool DeepIterator::next () content.type = Content::END; return false; } - } + } } /** @@ -607,7 +608,7 @@ bool DeepIterator::next () bool DeepIterator::prev () { Iterator *it = stack.getTop (); - + if (it->prev ()) { if (it->getContent()->type == Content::WIDGET) { // Widget: new iterator on stack, to search in this widget. @@ -629,7 +630,7 @@ bool DeepIterator::prev () content.type = Content::START; return false; } - } + } } // ----------------- @@ -679,7 +680,7 @@ 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->next()) { if (it->getContent()->type == Content::BREAK) ch = '\n'; else { // if (it->getContent()->type == Content::TEXT) @@ -695,7 +696,7 @@ bool CharIterator::next () ch = END; return false; } - } else if(ch == END) + } else if (ch == END) return false; else { // at this point, it->getContent()->type == Content::TEXT @@ -708,16 +709,16 @@ bool CharIterator::next () 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->prev()) { if (it->getContent()->type == Content::BREAK) ch = '\n'; else { // if (it->getContent()->type == Content::TEXT) @@ -739,14 +740,14 @@ bool CharIterator::prev () ch = START; return false; } - } else if(ch == START) + } 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, diff --git a/dw/iterator.hh b/dw/iterator.hh index 605217ec..838d66a1 100644 --- a/dw/iterator.hh +++ b/dw/iterator.hh @@ -16,7 +16,7 @@ namespace core { * * \sa dw::core::Widget::iterator */ -class Iterator: public object::Object, public misc::Comparable +class Iterator: public lout::object::Object, public lout::misc::Comparable { protected: Iterator(Widget *widget, Content::Type mask, bool atEnd); @@ -65,10 +65,10 @@ public: /** * \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. + * + * 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; @@ -80,7 +80,7 @@ public: * 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, @@ -96,12 +96,12 @@ class EmptyIterator: public Iterator { private: EmptyIterator (EmptyIterator &it); - + public: EmptyIterator (Widget *widget, Content::Type mask, bool atEnd); - - object::Object *clone(); - int compareTo(misc::Comparable *other); + + lout::object::Object *clone(); + int compareTo(lout::misc::Comparable *other); bool next (); bool prev (); void highlight (int start, int end, HighlightLayer layer); @@ -119,14 +119,14 @@ 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(misc::Comparable *other); + + int compareTo(lout::misc::Comparable *other); bool next (); bool prev (); @@ -142,13 +142,13 @@ public: * iterators do not have the limitation, that iteration is only done within * a widget, instead, child widgets are iterated through recursively. */ -class DeepIterator: public object::Object, public misc::Comparable +class DeepIterator: public lout::object::Object, public lout::misc::Comparable { private: - class Stack: public container::typed::Vector<Iterator> + class Stack: public lout::container::typed::Vector<Iterator> { public: - inline Stack (): container::typed::Vector<Iterator> (4, false) { } + 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); } @@ -168,14 +168,14 @@ private: inline DeepIterator () { } -public: +public: DeepIterator(Iterator *it); ~DeepIterator(); - object::Object *clone (); + lout::object::Object *clone (); DeepIterator *createVariant(Iterator *it); - inline Iterator *getTopIterator () { return stack.getTop(); } + inline Iterator *getTopIterator () { return stack.getTop(); } inline Content *getContent () { return &content; } bool isEmpty (); @@ -183,12 +183,12 @@ public: bool next (); bool prev (); inline DeepIterator *cloneDeepIterator() { return (DeepIterator*)clone(); } - int compareTo(misc::Comparable *other); + int compareTo(lout::misc::Comparable *other); /** * \brief Highlight a part of the current content. * - * Unhighlight the current content by passing -1 as start (see also + * 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. @@ -216,10 +216,12 @@ public: start, end, hpos, vpos); } }; -class CharIterator: public object::Object, public misc::Comparable +class CharIterator: public lout::object::Object, public lout::misc::Comparable { public: - enum { START = -1, END = -2 }; + // START and END must not clash with any char value + // neither for signed nor unsigned char. + enum { START = 257, END = 258 }; private: DeepIterator *it; @@ -231,8 +233,8 @@ public: CharIterator (Widget *widget); ~CharIterator (); - object::Object *clone(); - int compareTo(misc::Comparable *other); + lout::object::Object *clone(); + int compareTo(lout::misc::Comparable *other); bool next (); bool prev (); diff --git a/dw/layout.cc b/dw/layout.cc index 060c182f..d8b96d55 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -14,17 +14,18 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "core.hh" +#include "../lout/msg.h" #include "../lout/debug.hh" #include "../lout/misc.hh" +using namespace lout; using namespace lout::container; using namespace lout::object; @@ -38,8 +39,8 @@ void Layout::Receiver::canvasSizeChanged (int width, int ascent, int descent) // ---------------------------------------------------------------------- bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver, - int signalNo, - int argc, Object **argv) + int signalNo, int argc, + lout::object::Object **argv) { Receiver *layoutReceiver = (Receiver*)receiver; @@ -54,7 +55,7 @@ bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver, misc::assertNotReached (); } - return false; + return false; } void Layout::Emitter::emitCanvasSizeChanged (int width, @@ -65,6 +66,110 @@ void Layout::Emitter::emitCanvasSizeChanged (int width, emitVoid (CANVAS_SIZE_CHANGED, 3, argv); } +// ---------------------------------------------------------------------- + +bool Layout::LinkReceiver::enter (Widget *widget, int link, int img, + int x, int y) +{ + return false; +} + +bool Layout::LinkReceiver::press (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + return false; +} + +bool Layout::LinkReceiver::release (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + return false; +} + +bool Layout::LinkReceiver::click (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + return false; +} + +// ---------------------------------------------------------------------- + +bool Layout::LinkEmitter::emitToReceiver (lout::signal::Receiver *receiver, + int signalNo, int argc, + lout::object::Object **argv) +{ + LinkReceiver *linkReceiver = (LinkReceiver*)receiver; + + switch (signalNo) { + case ENTER: + return linkReceiver->enter ((Widget*)argv[0], + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue (), + ((Integer*)argv[3])->getValue (), + ((Integer*)argv[4])->getValue ()); + + case PRESS: + return linkReceiver->press ((Widget*)argv[0], + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue (), + ((Integer*)argv[3])->getValue (), + ((Integer*)argv[4])->getValue (), + (EventButton*)argv[5]); + + case RELEASE: + return linkReceiver->release ((Widget*)argv[0], + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue (), + ((Integer*)argv[3])->getValue (), + ((Integer*)argv[4])->getValue (), + (EventButton*)argv[5]); + + case CLICK: + return linkReceiver->click ((Widget*)argv[0], + ((Integer*)argv[1])->getValue (), + ((Integer*)argv[2])->getValue (), + ((Integer*)argv[3])->getValue (), + ((Integer*)argv[4])->getValue (), + (EventButton*)argv[5]); + + default: + misc::assertNotReached (); + } + return false; +} + +bool Layout::LinkEmitter::emitEnter (Widget *widget, int link, int img, + int x, int y) +{ + Integer ilink (link), iimg (img), ix (x), iy (y); + Object *argv[5] = { widget, &ilink, &iimg, &ix, &iy }; + return emitBool (ENTER, 5, argv); +} + +bool Layout::LinkEmitter::emitPress (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + Integer ilink (link), iimg (img), ix (x), iy (y); + Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; + return emitBool (PRESS, 6, argv); +} + +bool Layout::LinkEmitter::emitRelease (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + Integer ilink (link), iimg (img), ix (x), iy (y); + Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; + return emitBool (RELEASE, 6, argv); +} + +bool Layout::LinkEmitter::emitClick (Widget *widget, int link, int img, + int x, int y, EventButton *event) +{ + Integer ilink (link), iimg (img), ix (x), iy (y); + Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; + return emitBool (CLICK, 6, argv); +} + // --------------------------------------------------------------------- Layout::Anchor::~Anchor () @@ -77,7 +182,7 @@ Layout::Anchor::~Anchor () Layout::Layout (Platform *platform) { this->platform = platform; - views = new container::typed::List <View> (true); + view = NULL; topLevel = NULL; widgetAtPoint = NULL; @@ -87,9 +192,11 @@ Layout::Layout (Platform *platform) cursor = style::CURSOR_DEFAULT; canvasWidth = canvasAscent = canvasDescent = 0; - + usesViewport = false; scrollX = scrollY = 0; + viewportWidth = viewportHeight = 0; + hScrollbarThickness = vScrollbarThickness = 0; requestedAnchor = NULL; scrollIdleId = -1; @@ -120,7 +227,7 @@ Layout::~Layout () if (topLevel) delete topLevel; delete platform; - delete views; + delete view; delete anchorsTable; delete textZone; } @@ -128,7 +235,7 @@ Layout::~Layout () void Layout::addWidget (Widget *widget) { if (topLevel) { - fprintf (stderr, "widget already set\n"); + MSG_WARN("widget already set\n"); return; } @@ -154,13 +261,10 @@ void Layout::removeWidget () canvasWidth = canvasAscent = canvasDescent = 0; scrollX = scrollY = 0; - for (typed::Iterator <View> it = views->iterator (); it.hasNext (); ) { - View *view = it.getNext (); - view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent); - if (view->usesViewport ()) - view->setViewportSize (viewportWidth, viewportHeight, 0, 0); - view->queueDrawTotal (); - } + view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent); + if (view->usesViewport ()) + view->setViewportSize (viewportWidth, viewportHeight, 0, 0); + view->queueDrawTotal (); setAnchor (NULL); updateAnchor (); @@ -192,7 +296,10 @@ void Layout::setWidget (Widget *widget) */ void Layout::attachView (View *view) { - views->append (view); + if (this->view) + MSG_ERR("attachView: Multiple views for layout!\n"); + + this->view = view; platform->attachView (view); /* @@ -234,11 +341,12 @@ void Layout::attachView (View *view) void Layout::detachView (View *view) { + if (this->view != view) + MSG_ERR("detachView: this->view: %p view %p\n", this->view, view); + view->setLayout (NULL); platform->detachView (view); - - views->detachRef (view); - + this->view = NULL; /** * \todo Actually, viewportMarkerWidthDiff and * viewportMarkerHeightDiff have to be recalculated here, since the @@ -247,8 +355,14 @@ void Layout::detachView (View *view) */ } +void Layout::scroll(ScrollCommand cmd) +{ + if (view->usesViewport ()) + view->scroll(cmd); +} + /** - * \brief Scrolls all viewports, so that the region [x, y, width, height] + * \brief Scrolls all viewports, so that the region [x, y, width, height] * is seen, according to hpos and vpos. */ void Layout::scrollTo (HPosition hpos, VPosition vpos, @@ -262,8 +376,8 @@ void Layout::scrollTo0 (HPosition hpos, VPosition vpos, bool scrollingInterrupted) { if (usesViewport) { - //printf ("scrollTo (%d, %d, %s)\n", - // x, y, scrollingInterrupted ? "true" : "false"); + _MSG("scrollTo (%d, %d, %s)\n", + x, y, scrollingInterrupted ? "true" : "false"); scrollTargetHpos = hpos; scrollTargetVpos = vpos; @@ -271,12 +385,12 @@ void Layout::scrollTo0 (HPosition hpos, VPosition vpos, scrollTargetY = y; scrollTargetWidth = width; scrollTargetHeight = height; - + if (scrollIdleId == -1) { scrollIdleId = platform->addIdle (&Layout::scrollIdle); scrollIdleNotInterrupted = true; } - + scrollIdleNotInterrupted = scrollIdleNotInterrupted || !scrollingInterrupted; } @@ -334,11 +448,7 @@ void Layout::scrollIdle () if (xChanged || yChanged) { adjustScrollPos (); - for (container::typed::Iterator <View> it = views->iterator (); - it.hasNext (); ) { - View *thisView = it.getNext(); - thisView->scrollTo (scrollX, scrollY); - } + view->scrollTo (scrollX, scrollY); } scrollIdleId = -1; @@ -354,7 +464,7 @@ void Layout::adjustScrollPos () canvasAscent + canvasDescent - (viewportHeight - hScrollbarThickness)); scrollY = misc::max (scrollY, 0); - //printf("adjustScrollPos: scrollX=%d scrollY=%d\n", scrollX, scrollY); + _MSG("adjustScrollPos: scrollX=%d scrollY=%d\n", scrollX, scrollY); } bool Layout::calcScrollInto (int requestedValue, int requestedSize, @@ -414,7 +524,7 @@ void Layout::draw (View *view, Rectangle *area) */ void Layout::setAnchor (const char *anchor) { - //printf ("setAnchor (%s)\n", anchor); + _MSG("setAnchor (%s)\n", anchor); if (requestedAnchor) delete requestedAnchor; @@ -450,7 +560,7 @@ char *Layout::addAnchor (Widget *widget, const char* name, int y) void Layout::changeAnchor (Widget *widget, char* name, int y) { - String key (name); + String key (name); Anchor *anchor = anchorsTable->get (&key); assert (anchor); assert (anchor->widget == widget); @@ -488,11 +598,7 @@ void Layout::setCursor (style::Cursor cursor) { if (cursor != this->cursor) { this->cursor = cursor; - - for (typed::Iterator <View> it = views->iterator (); it.hasNext (); ) { - View *view = it.getNext (); - view->setCursor (cursor); - } + view->setCursor (cursor); } } @@ -514,17 +620,13 @@ void Layout::updateBgColor () bgColor = topLevel->getStyle()->backgroundColor; else bgColor = NULL; - - for (typed::Iterator <View> it = views->iterator (); it.hasNext (); ) { - View *view = it.getNext (); - view->setBgColor (bgColor); - } + view->setBgColor (bgColor); } void Layout::resizeIdle () { //static int calls = 0; - //printf(" Layout::resizeIdle calls = %d\n", ++calls); + //MSG(" Layout::resizeIdle calls = %d\n", ++calls); while (resizeIdleId != -1) { // Reset already here, since in this function, queueResize() may be @@ -550,12 +652,9 @@ void Layout::resizeIdle () emitter.emitCanvasSizeChanged ( canvasWidth, canvasAscent, canvasDescent); - // Tell the views about the new world size. - for (typed::Iterator <View> it = views->iterator (); it.hasNext ();) { - View *view = it.getNext (); - view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent); - // view->queueDrawTotal (false); - } + // Tell the view about the new world size. + view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent); + // view->queueDrawTotal (false); if (usesViewport) { int actualHScrollbarThickness = @@ -564,7 +663,7 @@ void Layout::resizeIdle () (canvasAscent + canvasDescent > viewportHeight) ? vScrollbarThickness : 0; - if (!canvasHeightGreater && + if (!canvasHeightGreater && canvasAscent + canvasDescent > viewportHeight - actualHScrollbarThickness) { canvasHeightGreater = true; @@ -573,14 +672,10 @@ void Layout::resizeIdle () } // Set viewport sizes. - for (typed::Iterator <View> it = views->iterator (); - it.hasNext (); ) { - View *view = it.getNext (); - if (view->usesViewport ()) - view->setViewportSize (viewportWidth, viewportHeight, - actualHScrollbarThickness, - actualVScrollbarThickness); - } + if (view->usesViewport ()) + view->setViewportSize (viewportWidth, viewportHeight, + actualHScrollbarThickness, + actualVScrollbarThickness); } } @@ -592,7 +687,7 @@ void Layout::resizeIdle () } void Layout::setSizeHints () -{ +{ if (topLevel) { topLevel->setWidth (viewportWidth - (canvasHeightGreater ? vScrollbarThickness : 0)); @@ -611,11 +706,7 @@ void Layout::queueDraw (int x, int y, int width, int height) if (area.isEmpty ()) return; - for (container::typed::Iterator <View> it = views->iterator (); - it.hasNext (); ) { - View *view = it.getNext (); - view->queueDraw (&area); - } + view->queueDraw (&area); } void Layout::queueDrawExcept (int x, int y, int width, int height, @@ -623,15 +714,15 @@ void Layout::queueDrawExcept (int x, int y, int width, int height, if (x == ex && y == ey && width == ewidth && height == eheight) return; - + // queueDraw() the four rectangles within rectangle (x, y, width, height) // around rectangle (ex, ey, ewidth, eheight). // Some or all of these may be empty. - // upper left corner of the intersection rectangle + // upper left corner of the intersection rectangle int ix1 = misc::max (x, ex); int iy1 = misc::max (y, ey); - // lower right corner of the intersection rectangle + // lower right corner of the intersection rectangle int ix2 = misc::min (x + width, ex + ewidth); int iy2 = misc::min (y + height, ey + eheight); @@ -644,11 +735,7 @@ void Layout::queueDrawExcept (int x, int y, int width, int height, void Layout::queueResize () { if (resizeIdleId == -1) { - for (container::typed::Iterator <View> it = views->iterator (); - it.hasNext (); ) { - View *view = it.getNext (); - view->cancelQueueDraw (); - } + view->cancelQueueDraw (); resizeIdleId = platform->addIdle (&Layout::resizeIdle); } @@ -659,10 +746,10 @@ void Layout::queueResize () bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed, int x, int y, ButtonState state, int button) - + { EventButton event; - + moveToWidgetAtPoint (x, y, state); event.xCanvas = x; @@ -670,7 +757,7 @@ bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed, event.state = state; event.button = button; event.numPressed = numPressed; - + return processMouseEvent (&event, type, true); } @@ -683,13 +770,13 @@ bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed, bool Layout::motionNotify (View *view, int x, int y, ButtonState state) { EventButton event; - + moveToWidgetAtPoint (x, y, state); event.xCanvas = x; event.yCanvas = y; event.state = state; - + return processMouseEvent (&event, MOTION_NOTIFY, true); } @@ -706,7 +793,7 @@ void Layout::enterNotify (View *view, int x, int y, ButtonState state) lastWidget = widgetAtPoint; moveToWidgetAtPoint (x, y, state); - if(widgetAtPoint) { + if (widgetAtPoint) { event.state = state; event.lastWidget = lastWidget; event.currentWidget = widgetAtPoint; @@ -727,7 +814,7 @@ void Layout::leaveNotify (View *view, ButtonState state) lastWidget = widgetAtPoint; moveOutOfView (state); - if(lastWidget) { + if (lastWidget) { event.state = state; event.lastWidget = lastWidget; event.currentWidget = widgetAtPoint; @@ -740,8 +827,8 @@ void Layout::leaveNotify (View *view, ButtonState state) */ Widget *Layout::getWidgetAtPoint (int x, int y) { - //_MSG ("------------------------------------------------------------\n"); - //_MSG ("widget at (%d, %d)\n", x, y); + _MSG ("------------------------------------------------------------\n"); + _MSG ("widget at (%d, %d)\n", x, y); if (topLevel) return topLevel->getWidgetAtPoint (x, y, 0); else @@ -761,11 +848,11 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) EventCrossing crossingEvent; if (newWidgetAtPoint != widgetAtPoint) { - // The mouse pointer has been moved into another widget. + // The mouse pointer has been moved into another widget. if (newWidgetAtPoint && widgetAtPoint) ancestor = newWidgetAtPoint->getNearestCommonAncestor (widgetAtPoint); - else if(newWidgetAtPoint) + else if (newWidgetAtPoint) ancestor = newWidgetAtPoint->getTopLevel (); else ancestor = widgetAtPoint->getTopLevel (); @@ -777,7 +864,7 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) for (w = widgetAtPoint; w != ancestor; w = w->getParent ()) trackLen++; trackLen++; // for the ancestor - if(newWidgetAtPoint) + if (newWidgetAtPoint) // second part for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ()) trackLen++; @@ -789,7 +876,7 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) for (w = widgetAtPoint; w != ancestor; w = w->getParent ()) track[i++] = w; track[i++] = ancestor; - if(newWidgetAtPoint) { + if (newWidgetAtPoint) { /* second part */ i = trackLen - 1; for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ()) @@ -801,13 +888,13 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) crossingEvent.state = state; crossingEvent.currentWidget = widgetAtPoint; // ??? crossingEvent.lastWidget = widgetAtPoint; // ??? - + if (i != 0) track[i]->enterNotify (&crossingEvent); if (i != trackLen - 1) track[i]->leaveNotify (&crossingEvent); } - + delete[] track; widgetAtPoint = newWidgetAtPoint; @@ -827,7 +914,7 @@ bool Layout::processMouseEvent (MousePositionEvent *event, Widget *widget; for (widget = widgetAtPoint; widget; widget = widget->getParent ()) { - if(!mayBeSuppressed || widget->isButtonSensitive ()) { + if (!mayBeSuppressed || widget->isButtonSensitive ()) { event->xWidget = event->xCanvas - widget->getAllocation()->x; event->yWidget = event->yCanvas - widget->getAllocation()->y; @@ -846,6 +933,10 @@ bool Layout::processMouseEvent (MousePositionEvent *event, } } } + if (type == BUTTON_PRESS) + return emitLinkPress (NULL, -1, -1, -1, -1, (EventButton*)event); + else if (type == BUTTON_RELEASE) + return emitLinkRelease(NULL, -1, -1, -1, -1, (EventButton*)event); return false; } @@ -860,14 +951,6 @@ void Layout::scrollPosChanged (View *view, int x, int y) if (x != scrollX || y != scrollY) { scrollX = x; scrollY = y; - - // Tell all views about the scrolling position, except the caller. - for (container::typed::Iterator <View> it = views->iterator (); - it.hasNext (); ) { - View *thisView = it.getNext(); - if(view != thisView && thisView->usesViewport ()) - thisView->scrollTo (scrollX, scrollY); - } setAnchor (NULL); updateAnchor (); @@ -880,12 +963,12 @@ void Layout::scrollPosChanged (View *view, int x, int y) */ void Layout::viewportSizeChanged (View *view, int width, int height) { - //printf("Layout::viewportSizeChanged w=%d h=%d new_w=%d new_h=%d\n", - // viewportWidth, viewportHeight, width, height); + _MSG("Layout::viewportSizeChanged w=%d h=%d new_w=%d new_h=%d\n", + viewportWidth, viewportHeight, width, height); /* If the width has become higher, we test again, whether the vertical * scrollbar (so to speak) can be hidden again. */ - if(usesViewport && width > viewportWidth) + if (usesViewport && width > viewportWidth) canvasHeightGreater = false; /* if size changes, redraw this view. @@ -897,21 +980,6 @@ void Layout::viewportSizeChanged (View *view, int width, int height) viewportHeight = height; setSizeHints (); - - int actualHScrollbarThickness = - (canvasWidth > viewportWidth) ? hScrollbarThickness : 0; - int actualVScrollbarThickness = - (canvasAscent + canvasDescent > viewportWidth) ? vScrollbarThickness : 0; - - /* Tell all views about the size, except the caller. */ - for (container::typed::Iterator <View> it = views->iterator (); - it.hasNext (); ) { - View *thisView = it.getNext(); - if(view != thisView && thisView->usesViewport ()) - thisView->setViewportSize (viewportWidth, viewportHeight, - actualHScrollbarThickness, - actualVScrollbarThickness); - } } } // namespace dw diff --git a/dw/layout.hh b/dw/layout.hh index 13b8f312..304cf166 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -13,7 +13,7 @@ namespace core { * * \sa\ref dw-overview, \ref dw-layout-widgets, \ref dw-layout-views */ -class Layout: public object::Object +class Layout: public lout::object::Object { friend class Widget; @@ -29,6 +29,78 @@ public: 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 { @@ -37,7 +109,7 @@ private: protected: bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo, - int argc, Object **argv); + int argc, lout::object::Object **argv); public: inline void connectLayout (Receiver *receiver) { connect (receiver); } @@ -47,7 +119,7 @@ private: Emitter emitter; - class Anchor: public object::Object + class Anchor: public lout::object::Object { public: char *name; @@ -58,10 +130,10 @@ private: }; Platform *platform; - container::typed::List <View> *views; + View *view; Widget *topLevel, *widgetAtPoint; - /* The state, which must be projected into the views. */ + /* The state, which must be projected into the view. */ style::Color *bgColor; style::Cursor cursor; int canvasWidth, canvasAscent, canvasDescent; @@ -70,7 +142,7 @@ private: int scrollX, scrollY, viewportWidth, viewportHeight; bool canvasHeightGreater; int hScrollbarThickness, vScrollbarThickness; - + HPosition scrollTargetHpos; VPosition scrollTargetVpos; int scrollTargetX, scrollTargetY, scrollTargetWidth, scrollTargetHeight; @@ -80,7 +152,7 @@ private: bool scrollIdleNotInterrupted; /* Anchors of the widget tree */ - container::typed::HashTable <object::String, Anchor> *anchorsTable; + lout::container::typed::HashTable <lout::object::String, Anchor> *anchorsTable; SelectionState selectionState; FindtextState findtextState; @@ -136,13 +208,31 @@ private: int ex, int ey, int ewidth, int eheight); void queueResize (); void removeWidget (); - + public: Layout (Platform *platform); ~Layout (); - misc::ZoneAllocator *textZone; - + 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); @@ -159,6 +249,7 @@ public: void scrollTo (HPosition hpos, VPosition vpos, int x, int y, int width, int height); + void scroll (ScrollCommand); void setAnchor (const char *anchor); /* View */ @@ -168,7 +259,7 @@ public: /** * \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. @@ -216,19 +307,34 @@ public: 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 style::Color *createSimpleColor (int color) + inline bool fontExists (const char *name) + { + return platform->fontExists (name); + } + + inline style::Color *createColor (int color) { - return platform->createSimpleColor (color); + return platform->createColor (color); } - inline style::Color *createShadedColor (int color) + inline style::Tooltip *createTooltip (const char *text) { - return platform->createShadedColor (color); + return platform->createTooltip (text); } inline Imgbuf *createImgbuf (Imgbuf::Type type, int width, int height) @@ -250,8 +356,9 @@ public: emitter.connectLayout (receiver); } /** \brief See dw::core::FindtextState::search. */ - inline FindtextState::Result search (const char *str, bool caseSens) - { return findtextState.search (str, caseSens); } + 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 (); } diff --git a/dw/listitem.cc b/dw/listitem.cc index ba960b46..ed7a2c75 100644 --- a/dw/listitem.cc +++ b/dw/listitem.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -41,16 +40,20 @@ ListItem::~ListItem() void ListItem::initWithWidget (core::Widget *widget, core::style::Style *style) { + hasListitemValue = true; addWidget (widget, style); addSpace (style); - updateValue (); + if (style->listStylePosition == core::style::LIST_STYLE_POSITION_OUTSIDE) + updateValue (); } -void ListItem::initWithText (char *text, core::style::Style *style) +void ListItem::initWithText (const char *text, core::style::Style *style) { + hasListitemValue = true; addText (text, style); - addSpace (style); - updateValue (); + addSpace (style); + if (style->listStylePosition == core::style::LIST_STYLE_POSITION_OUTSIDE) + updateValue (); } int ListItem::getValue () @@ -58,7 +61,7 @@ int ListItem::getValue () if (words->size () == 0) return 0; else - return words->get(0).size.width + words->get(0).origSpace; + return words->getRef(0)->size.width + words->getRef(0)->origSpace; } void ListItem::setMaxValue (int maxValue, int value) diff --git a/dw/listitem.hh b/dw/listitem.hh index ea24af3e..2e303d5d 100644 --- a/dw/listitem.hh +++ b/dw/listitem.hh @@ -19,7 +19,7 @@ public: ~ListItem(); void initWithWidget (core::Widget *widget, core::style::Style *style); - void initWithText (char *texty, core::style::Style *style); + void initWithText (const char *text, core::style::Style *style); }; } // namespace dw diff --git a/dw/platform.hh b/dw/platform.hh index 0ae5d508..69d1feab 100644 --- a/dw/platform.hh +++ b/dw/platform.hh @@ -13,7 +13,7 @@ namespace core { * * \sa\ref dw-overview */ -class Platform: public object::Object +class Platform: public lout::object::Object { public: /* @@ -27,7 +27,7 @@ public: * a layout. */ virtual void setLayout (Layout *layout) = 0; - + /* * ------------------------- * Operations on views @@ -45,10 +45,10 @@ public: * from the related layout. */ virtual void detachView (View *view) = 0; - + /* * ----------------------------------- - * Platform dependant properties + * Platform dependent properties * ----------------------------------- */ @@ -66,7 +66,17 @@ public: * \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 @@ -75,7 +85,7 @@ public: /** * \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 @@ -95,10 +105,10 @@ public: */ /** - * \brief Create a (platform dependant) font. + * \brief Create a (platform dependent) font. * * Typically, within a platform, a sub class of dw::core::style::Font - * is defined, which holds more platform dependant data. + * is defined, which holds more platform dependent data. * * Also, this method must fill the attributes "font" (when needed), * "ascent", "descent", "spaceSidth" and "xHeight". If "tryEverything" @@ -108,17 +118,18 @@ public: */ virtual style::Font *createFont (style::FontAttrs *attrs, bool tryEverything) = 0; - + + virtual bool fontExists (const char *name) = 0; + /** - * \brief Create a simple color resource for a given 0xrrggbb value. + * \brief Create a color resource for a given 0xrrggbb value. */ - virtual style::Color *createSimpleColor (int color) = 0; + virtual style::Color *createColor (int color) = 0; /** - * \brief Create a shaded color resource for a given 0xrrggbb value. + * \brief Create a tooltip */ - virtual style::Color *createShadedColor (int color) = 0; - + virtual style::Tooltip *createTooltip (const char *text) = 0; /* * -------------------- diff --git a/dw/ruler.cc b/dw/ruler.cc index abefa1bf..6dce07d0 100644 --- a/dw/ruler.cc +++ b/dw/ruler.cc @@ -14,15 +14,14 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "ruler.hh" #include "../lout/misc.hh" - + #include <stdio.h> namespace dw { diff --git a/dw/ruler.hh b/dw/ruler.hh index a1ae67ea..32e859a1 100644 --- a/dw/ruler.hh +++ b/dw/ruler.hh @@ -17,12 +17,12 @@ class Ruler: public core::Widget { protected: void sizeRequestImpl (core::Requisition *requisition); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area); public: Ruler (); - core::Iterator *iterator (core::Content::Type mask, bool atEnd); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); }; } // namespace dw diff --git a/dw/selection.cc b/dw/selection.cc index 3153576f..275eddaa 100644 --- a/dw/selection.cc +++ b/dw/selection.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -24,6 +23,8 @@ #include <string.h> +using namespace lout; + /* * strndup() is a GNU extension. */ @@ -95,13 +96,13 @@ void SelectionState::resetLink () link = NULL; linkState = LINK_NONE; } - + bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo, EventButton *event, bool withinContent) { Widget *itWidget = it->getWidget (); bool ret = false; - + if (event && event->button == 1 && !withinContent && event->numPressed == 2) { // When the user double-clicks on empty parts, emit the double click @@ -117,8 +118,7 @@ bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo, if (linkNo != -1) { // link handling if (event) { - // return value is ignored - itWidget->emitLinkPress (linkNo, -1, -1, -1, event); + (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); resetLink (); linkState = LINK_PRESSED; linkButton = event->button; @@ -161,7 +161,7 @@ bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo, } else { if (event && event->button == 3) { // menu popup - itWidget->emitLinkPress (-1, -1, -1, -1, event); + layout->emitLinkPress (itWidget, -1, -1, -1, -1, event); ret = true; } } @@ -181,14 +181,12 @@ bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo, // link handling ret = true; if (linkNo != -1) - // return value is ignored - itWidget->emitLinkRelease (linkNo, -1, -1, -1, event); + (void) layout->emitLinkRelease (itWidget, linkNo, -1, -1, -1, event); // The link where the user clicked the mouse button? if (linkNo == linkNumber) { resetLink (); - // return value is ignored - itWidget->emitLinkClick (linkNo, -1, -1, -1, event); + (void) layout->emitLinkClick (itWidget, linkNo, -1, -1, -1, event); } else { if (event->button == 1) // Reset links and switch to selection mode. The selection diff --git a/dw/selection.hh b/dw/selection.hh index 9cc8d25f..ba03fd18 100644 --- a/dw/selection.hh +++ b/dw/selection.hh @@ -8,30 +8,28 @@ namespace dw { namespace core { -using namespace lout; - /** * \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 @@ -42,7 +40,7 @@ using namespace lout; * within the iterator, * <tr><td>int linkNo <td>if this item is associated with a * link, its number (see - * dw::core::Widget::LinkReceiver), + * dw::core::Layout::LinkReceiver), * otherwise -1 * <tr><td>dw::core::EventButton *event <td>the event itself; only the button * is used @@ -64,26 +62,26 @@ using namespace lout; * 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::Widget::LinkReceiver::press, - * dw::core::Widget::LinkReceiver::release and - * dw::core::Widget::LinkReceiver::click (but not - * dw::core::Widget::LinkReceiver::enter) are emitted by these methods, so + * 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::Widget::LinkReceiver::enter for themselves. - * + * 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 @@ -91,18 +89,18 @@ using namespace lout; * 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 parantheses after the event denote the button, "n" * means arbitrary button): - * + * * \dot * digraph G { * node [shape=ellipse, fontname=Helvetica, fontsize=10]; @@ -122,15 +120,15 @@ using namespace lout; * 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]; @@ -170,16 +168,16 @@ using namespace lout; * 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 { @@ -248,15 +246,15 @@ private: public: enum EventType { BUTTON_PRESS, BUTTON_RELEASE, BUTTON_MOTION }; - + SelectionState (); ~SelectionState (); - + inline void setLayout (Layout *layout) { this->layout = layout; } void reset (); inline void connectDoubleClick (DoubleClickReceiver *receiver) { doubleClickEmitter.connectDoubleClick (receiver); } - + bool buttonPress (Iterator *it, int charPos, int linkNo, EventButton *event, bool withinContent); bool buttonRelease (Iterator *it, int charPos, int linkNo, diff --git a/dw/style.cc b/dw/style.cc index 207c47a3..b361e068 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <stdio.h> @@ -24,6 +23,9 @@ #include <ctype.h> #include "core.hh" +#include "../lout/msg.h" + +using namespace lout; namespace dw { namespace core { @@ -37,14 +39,13 @@ void StyleAttrs::initValues () textDecoration = TEXT_DECORATION_NONE; textAlign = TEXT_ALIGN_LEFT; textAlignChar = '.'; + listStylePosition = LIST_STYLE_POSITION_OUTSIDE; listStyleType = LIST_STYLE_TYPE_DISC; - valign = VALIGN_MIDDLE; + valign = VALIGN_BASELINE; backgroundColor = NULL; - width = LENGTH_AUTO; - height = LENGTH_AUTO; + width = height = lineHeight = LENGTH_AUTO; vloat = FLOAT_NONE; clear = CLEAR_NONE; - margin.setVal (0); borderWidth.setVal (0); padding.setVal (0); @@ -52,6 +53,7 @@ void StyleAttrs::initValues () setBorderStyle (BORDER_NONE); hBorderSpacing = 0; vBorderSpacing = 0; + wordSpacing = 0; display = DISPLAY_INLINE; whiteSpace = WHITE_SPACE_NORMAL; @@ -64,12 +66,9 @@ void StyleAttrs::initValues () */ void StyleAttrs::resetValues () { - x_link = -1; x_img = -1; - x_tooltip = NULL; - textAlign = TEXT_ALIGN_LEFT; /* ??? */ - valign = VALIGN_MIDDLE; + valign = VALIGN_BASELINE; textAlignChar = '.'; vloat = FLOAT_NONE; /** \todo Correct? Check specification. */ clear = CLEAR_NONE; /** \todo Correct? Check specification. */ @@ -86,8 +85,6 @@ void StyleAttrs::resetValues () vBorderSpacing = 0; display = DISPLAY_INLINE; - whiteSpace = WHITE_SPACE_NORMAL; - cursor = CURSOR_DEFAULT; /** \todo Check CSS specification again. */ } /** @@ -121,8 +118,10 @@ bool StyleAttrs::equals (object::Object *other) { textAlignChar == otherAttrs->textAlignChar && hBorderSpacing == otherAttrs->hBorderSpacing && vBorderSpacing == otherAttrs->vBorderSpacing && + wordSpacing == otherAttrs->wordSpacing && width == otherAttrs->width && height == otherAttrs->height && + lineHeight == otherAttrs->lineHeight && margin.equals (&otherAttrs->margin) && borderWidth.equals (&otherAttrs->borderWidth) && padding.equals (&otherAttrs->padding) && @@ -136,7 +135,9 @@ bool StyleAttrs::equals (object::Object *other) { borderStyle.left == otherAttrs->borderStyle.left && display == otherAttrs->display && whiteSpace == otherAttrs->whiteSpace && + listStylePosition == otherAttrs->listStylePosition && listStyleType == otherAttrs->listStyleType && + cursor == otherAttrs->cursor && x_link == otherAttrs->x_link && x_img == otherAttrs->x_img && x_tooltip == otherAttrs->x_tooltip); @@ -152,10 +153,12 @@ int StyleAttrs::hashValue () { textAlignChar + hBorderSpacing + vBorderSpacing + + wordSpacing + width + height + + lineHeight + margin.hashValue () + - borderWidth.hashValue () + + borderWidth.hashValue () + padding.hashValue () + (intptr_t) borderColor.top + (intptr_t) borderColor.right + @@ -167,7 +170,9 @@ int StyleAttrs::hashValue () { borderStyle.left + display + whiteSpace + + listStylePosition + listStyleType + + cursor + x_link + x_img + (intptr_t) x_tooltip; @@ -222,7 +227,7 @@ Style::~Style () x_tooltip->unref(); styleTable->remove (this); - totalRef--; + totalRef--; } void Style::copyAttrs (StyleAttrs *attrs) @@ -238,8 +243,10 @@ void Style::copyAttrs (StyleAttrs *attrs) clear = attrs->clear; hBorderSpacing = attrs->hBorderSpacing; vBorderSpacing = attrs->vBorderSpacing; + wordSpacing = attrs->wordSpacing; width = attrs->width; height = attrs->height; + lineHeight = attrs->lineHeight; margin = attrs->margin; borderWidth = attrs->borderWidth; padding = attrs->padding; @@ -247,6 +254,7 @@ void Style::copyAttrs (StyleAttrs *attrs) borderStyle = attrs->borderStyle; display = attrs->display; whiteSpace = attrs->whiteSpace; + listStylePosition = attrs->listStylePosition; listStyleType = attrs->listStyleType; cursor = attrs->cursor; x_link = attrs->x_link; @@ -261,8 +269,11 @@ bool FontAttrs::equals(object::Object *other) FontAttrs *otherAttrs = (FontAttrs*)other; return this == otherAttrs || - (size == otherAttrs->size && weight == otherAttrs->weight && - style == otherAttrs->style && strcmp (name, otherAttrs->name) == 0); + (size == otherAttrs->size && + weight == otherAttrs->weight && + style == otherAttrs->style && + letterSpacing == otherAttrs->letterSpacing && + strcmp (name, otherAttrs->name) == 0); } int FontAttrs::hashValue() @@ -271,12 +282,13 @@ int FontAttrs::hashValue() h = (h << 5) - h + size; h = (h << 5) - h + weight; h = (h << 5) - h + style; + h = (h << 5) - h + letterSpacing; return h; } Font::~Font () { - delete name; + free ((char*)name); } void Font::copyAttrs (FontAttrs *attrs) @@ -285,6 +297,7 @@ void Font::copyAttrs (FontAttrs *attrs) size = attrs->size; weight = attrs->weight; style = attrs->style; + letterSpacing = attrs->letterSpacing; } Font *Font::create0 (Layout *layout, FontAttrs *attrs, @@ -298,43 +311,9 @@ Font *Font::create (Layout *layout, FontAttrs *attrs) return create0 (layout, attrs, false); } -Font *Font::createFromList (Layout *layout, FontAttrs *attrs, - char *defaultFamily) +bool Font::exists (Layout *layout, const char *name) { - Font *font = NULL; - FontAttrs attrs2; - char *comma, *list, *current; - - attrs2 = *attrs; - current = list = strdup (attrs->name); - - while (current && (font == NULL)) { - comma = strchr (current, ','); - if (comma) *comma = 0; - - attrs2.name = current; - font = create0 (layout, &attrs2, false); - if (font) - break; - - if (comma) { - current = comma + 1; - while (isspace (*current)) current++; - } else - current = NULL; - } - - delete list; - - if (font == NULL) { - attrs2.name = defaultFamily; - font = create0 (layout, &attrs2, true); - } - - if (font == NULL) - fprintf (stderr, "Could not find any font.\n"); - - return font; + return layout->fontExists (name); } // ---------------------------------------------------------------------- @@ -342,12 +321,12 @@ Font *Font::createFromList (Layout *layout, FontAttrs *attrs, bool ColorAttrs::equals(object::Object *other) { ColorAttrs *oc = (ColorAttrs*)other; - return this == oc || (color == oc->color && type == oc->type); + return this == oc || (color == oc->color); } int ColorAttrs::hashValue() { - return color ^ type; + return color; } Color::~Color () @@ -410,72 +389,33 @@ int Color::shadeColor (int color, Shading shading) } } - -Color *Color::create (Layout *layout, int col, Type type) + +Color *Color::create (Layout *layout, int col) { - ColorAttrs attrs(col, type); - Color *color = NULL; - - switch (type) { - case TYPE_SIMPLE: - color = layout->createSimpleColor (col); - break; - case TYPE_SHADED: - color = layout->createShadedColor (col); - break; - } + ColorAttrs attrs(col); - return color; + return layout->createColor (col); +} + +Tooltip *Tooltip::create (Layout *layout, const char *text) +{ + return layout->createTooltip (text); } // ---------------------------------------------------------------------- -/** - * \brief Draw a part of a border. - */ -static void drawPolygon (View *view, Color *color, Color::Shading shading, - int x1, int y1, int x2, int y2, - int width, int w1, int w2) -{ - int points[4][2]; - - if (width != 0) { - if (width == 1) { - if (x1 == x2) - view->drawLine (color, shading, x1, y1, x2, y2 - 1); - else - view->drawLine (color, shading, x1, y1, x2 - 1, y2); - } else if (width == -1) { - if (x1 == x2) - view->drawLine (color, shading, x1 - 1, y1, x2 - 1, y2 - 1); - else - view->drawLine (color, shading, x1, y1 - 1, x2 - 1, y2 - 1); - } else { - points[0][0] = x1; - points[0][1] = y1; - points[1][0] = x2; - points[1][1] = y2; - - if (x1 == x2) { - points[2][0] = x1 + width; - points[2][1] = y2 + w2; - points[3][0] = x1 + width; - points[3][1] = y1 + w1; - } else { - points[2][0] = x2 + w2; - points[2][1] = y1 + width; - points[3][0] = x1 + w1; - points[3][1] = y1 + width; - } - - /* - printf ("drawPolygon: (%d, %d) .. (%d, %d) .. (%d, %d) .. (%d, %d)\n", - points[0][0], points[0][1], points[1][0], points[1][1], - points[2][0], points[2][1], points[3][0], points[3][1]); - */ - view->drawPolygon (color, shading, true, points, 4); - } - } +static void drawTriangle (View *view, Color *color, Color::Shading shading, + int x1, int y1, int x2, int y2, int x3, int y3) { + int points[3][2]; + + points[0][0] = x1; + points[0][1] = y1; + points[1][0] = x2; + points[1][1] = y2; + points[2][0] = x3; + points[2][1] = y3; + + view->drawPolygon (color, shading, true, points, 3); } /** @@ -492,18 +432,17 @@ void drawBorder (View *view, Rectangle *area, Color::Shading top, right, bottom, left; int xb1, yb1, xb2, yb2, xp1, yp1, xp2, yp2; - if (style->borderStyle.top == BORDER_NONE) - return; - + // top left and bottom right point of outer border boundary xb1 = x + style->margin.left; yb1 = y + style->margin.top; - xb2 = xb1 + width - style->margin.left - style->margin.right; - yb2 = yb1 + height - style->margin.top - style->margin.bottom; + xb2 = x + width - style->margin.right; + yb2 = y + height - style->margin.bottom; - xp1 = xb1 + style->borderWidth.top; - yp1 = yb1 + style->borderWidth.left; - xp2 = xb2 + style->borderWidth.bottom; - yp2 = yb2 + style->borderWidth.right; + // top left and bottom right point of inner border boundary + xp1 = xb1 + style->borderWidth.left; + yp1 = yb1 + style->borderWidth.top; + xp2 = xb2 - style->borderWidth.right; + yp2 = yb2 - style->borderWidth.bottom; light = inverse ? Color::SHADING_DARK : Color::SHADING_LIGHT; dark = inverse ? Color::SHADING_LIGHT : Color::SHADING_DARK; @@ -525,18 +464,47 @@ void drawBorder (View *view, Rectangle *area, break; } - drawPolygon (view, style->borderColor.top, top, xb1, yb1, xb2, yb1, - style->borderWidth.top, style->borderWidth.left, - - style->borderWidth.right); - drawPolygon (view, style->borderColor.right, right, xb2, yb1, xb2, yb2, - - style->borderWidth.right, style->borderWidth.top, - - style->borderWidth.bottom); - drawPolygon (view, style->borderColor.bottom, bottom, xb1, yb2, xb2, yb2, - - style->borderWidth.bottom, style->borderWidth.left, - - style->borderWidth.right); - drawPolygon (view, style->borderColor.left, left, xb1, yb1, xb1, yb2, - style->borderWidth.left, style->borderWidth.top, - - style->borderWidth.bottom); + if (style->borderStyle.top != BORDER_NONE && style->borderColor.top) + view->drawRectangle(style->borderColor.top, top, true, + xb1, yb1, xb2 - xb1, style->borderWidth.top); + + if (style->borderStyle.bottom != BORDER_NONE && style->borderColor.bottom) + view->drawRectangle(style->borderColor.bottom, bottom, true, + xb1, yb2, xb2 - xb1, - style->borderWidth.bottom); + + if (style->borderStyle.left != BORDER_NONE && style->borderColor.left) + view->drawRectangle(style->borderColor.left, left, true, + xb1, yp1, style->borderWidth.left, yp2 - yp1); + + if (style->borderWidth.left > 1) { + if (style->borderWidth.top > 1 && + (style->borderColor.left != style->borderColor.top || + left != top)) + drawTriangle (view, style->borderColor.left, left, + xb1, yp1, xp1, yp1, xb1, yb1); + if (style->borderWidth.bottom > 1 && + (style->borderColor.left != style->borderColor.bottom || + left != bottom)) + drawTriangle (view, style->borderColor.left, left, + xb1, yp2, xp1, yp2, xb1, yb2); + } + + if (style->borderStyle.right != BORDER_NONE && style->borderColor.right) + view->drawRectangle(style->borderColor.right, right, true, + xb2, yp1, - style->borderWidth.right, yp2 - yp1); + + if (style->borderWidth.right > 1) { + if (style->borderWidth.top > 1 && + (style->borderColor.right != style->borderColor.top || + right != top)) + drawTriangle (view, style->borderColor.right, right, + xb2, yp1, xp2, yp1, xb2, yb1); + if (style->borderWidth.bottom > 1 && + (style->borderColor.right != style->borderColor.bottom || + right != bottom)) + drawTriangle (view, style->borderColor.right, right, + xb2, yp2, xp2, yp2, xb2, yb2); + } } @@ -574,12 +542,12 @@ void drawBackground (View *view, Rectangle *area, // ---------------------------------------------------------------------- static const char - *roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" }, - *roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }, - *roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }, - *roman_I3[] = { "","M","MM","MMM","MMMM" }; + *const roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" }, + *const roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" }, + *const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" }, + *const roman_I3[] = { "","M","MM","MMM","MMMM" }; -void strtolower (char *s) +static void strtolower (char *s) { for ( ; *s; s++) *s = tolower (*s); @@ -596,16 +564,21 @@ void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType) bool low = false; int start_ch = 'A'; + if (buflen <= 0) + return; + switch(listStyleType){ case LIST_STYLE_TYPE_LOWER_ALPHA: + case LIST_STYLE_TYPE_LOWER_LATIN: start_ch = 'a'; case LIST_STYLE_TYPE_UPPER_ALPHA: + case LIST_STYLE_TYPE_UPPER_LATIN: i0 = num - 1; i1 = i0/26 - 1; i2 = i1/26 - 1; if (i2 > 25) /* more than 26+26^2+26^3=18278 elements ? */ - sprintf(buf, "****."); + snprintf(buf, buflen, "****."); else - sprintf(buf, "%c%c%c.", + snprintf(buf, buflen, "%c%c%c.", i2<0 ? ' ' : start_ch + i2%26, i1<0 ? ' ' : start_ch + i1%26, i0<0 ? ' ' : start_ch + i0%26); @@ -617,18 +590,23 @@ void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType) i1 = i0/10; i2 = i1/10; i3 = i2/10; i0 %= 10; i1 %= 10; i2 %= 10; if (num < 0 || i3 > 4) /* more than 4999 elements ? */ - sprintf(buf, "****."); + snprintf(buf, buflen, "****."); else snprintf(buf, buflen, "%s%s%s%s.", roman_I3[i3], roman_I2[i2], roman_I1[i1], roman_I0[i0]); - if (low) - strtolower(buf); break; case LIST_STYLE_TYPE_DECIMAL: default: - sprintf(buf, "%d.", num); + snprintf(buf, buflen, "%d.", num); break; } + + // ensure termination + buf[buflen - 1] = '\0'; + + if (low) + strtolower(buf); + } } // namespace style diff --git a/dw/style.hh b/dw/style.hh index a96a3471..81d47930 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -14,100 +14,100 @@ 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 hythens (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 lenghts, there are two units: (i) pixels and absolute + * + * 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 @@ -116,61 +116,61 @@ namespace core { * <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, @@ -179,9 +179,9 @@ namespace core { * 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 fom the stack, and @@ -237,7 +237,9 @@ enum VAlignType { VALIGN_MIDDLE, VALIGN_BASELINE, VALIGN_SUB, - VALIGN_SUPER + VALIGN_SUPER, + VALIGN_TEXT_TOP, + VALIGN_TEXT_BOTTOM, }; /** @@ -247,15 +249,19 @@ enum DisplayType { DISPLAY_BLOCK, DISPLAY_INLINE, 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, - DISPLAY_LAST + DISPLAY_TABLE_CELL }; +enum ListStylePosition { + LIST_STYLE_POSITION_INSIDE, + LIST_STYLE_POSITION_OUTSIDE +}; enum ListStyleType { LIST_STYLE_TYPE_DISC, LIST_STYLE_TYPE_CIRCLE, @@ -297,7 +303,9 @@ enum TextDecoration { enum WhiteSpace { WHITE_SPACE_NORMAL, WHITE_SPACE_PRE, - WHITE_SPACE_NOWRAP + WHITE_SPACE_NOWRAP, + WHITE_SPACE_PRE_WRAP, + WHITE_SPACE_PRE_LINE, }; enum FloatType { @@ -331,15 +339,15 @@ enum ClearType { * 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 @@ -349,7 +357,7 @@ enum ClearType { * <li> dw::core::style::relLengthVal * </ul> * - * "auto" lenghths are represented as dw::core::style::LENGTH_AUTO. + * "auto" lengths are represented as dw::core::style::LENGTH_AUTO. */ typedef int Length; @@ -358,11 +366,11 @@ 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; } + return ((int)(v * (1 << 18)) & ~3) | 2; } /** \brief Returns a relative length. */ inline Length createRelLength(double v) { - return (int)(v * (1 << 18)) & ~3 | 3; } + 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; } @@ -418,7 +426,7 @@ class Tooltip; /** * \sa dw::core::style */ -class StyleAttrs : public object::Object +class StyleAttrs : public lout::object::Object { public: Font *font; @@ -433,8 +441,8 @@ public: FloatType vloat; /* "float" is a keyword. */ ClearType clear; - int hBorderSpacing, vBorderSpacing; - Length width, height; + int hBorderSpacing, vBorderSpacing, wordSpacing; + Length width, height, lineHeight; Box margin, borderWidth, padding; struct { Color *top, *right, *bottom, *left; } borderColor; @@ -442,13 +450,14 @@ public: DisplayType display; WhiteSpace whiteSpace; + ListStylePosition listStylePosition; ListStyleType listStyleType; Cursor cursor; int x_link; int x_img; Tooltip *x_tooltip; - + void initValues (); void resetValues (); @@ -482,7 +491,7 @@ public: inline bool hasBackground () { return backgroundColor != NULL; } - bool equals (object::Object *other); + bool equals (lout::object::Object *other); int hashValue (); }; @@ -495,7 +504,7 @@ class Style: public StyleAttrs private: static int totalRef; int refCount; - static container::typed::HashTable <StyleAttrs, Style> *styleTable; + static lout::container::typed::HashTable <StyleAttrs, Style> *styleTable; Style (StyleAttrs *attrs); @@ -518,17 +527,17 @@ public: } inline void ref () { refCount++; } - inline void unref () { if(--refCount == 0) delete this; } + inline void unref () { if (--refCount == 0) delete this; } }; /** * \sa dw::core::style */ -class TooltipAttrs: public object::String +class TooltipAttrs: public lout::object::String { public: - TooltipAttrs(const char *text): object::String(text) { } + TooltipAttrs(const char *text): lout::object::String(text) { } }; /** @@ -539,34 +548,34 @@ class Tooltip: public TooltipAttrs private: int refCount; +protected: Tooltip (const char *text): TooltipAttrs(text) { refCount = 0; } public: - inline Tooltip *create (Layout *layout, const char *text) - { return new Tooltip (text); } - + static Tooltip *create (dw::core::Layout *layout, const char *text); inline void ref () { refCount++; } inline void unref () - { if(--refCount == 0) delete this; } + { if (--refCount == 0) delete this; } - inline void onEnter () { } - inline void onLeave () { } - inline void onMotion () { } + inline virtual void onEnter () { } + inline virtual void onLeave () { } + inline virtual void onMotion () { } }; /** * \sa dw::core::style */ -class FontAttrs: public object::Object +class FontAttrs: public lout::object::Object { public: const char *name; int size; int weight; + int letterSpacing; FontStyle style; - bool equals(object::Object *other); + bool equals(lout::object::Object *other); int hashValue(); }; @@ -593,37 +602,30 @@ public: int xHeight; static Font *create (Layout *layout, FontAttrs *attrs); - static Font *createFromList (Layout *layout, FontAttrs *attrs, - char *defaultFamily); + static bool exists (Layout *layout, const char *name); inline void ref () { refCount++; } - inline void unref () { if(--refCount == 0) delete this; } + inline void unref () { if (--refCount == 0) delete this; } }; /** * \sa dw::core::style */ -class ColorAttrs: public object::Object +class ColorAttrs: public lout::object::Object { -public: - enum Type { TYPE_SIMPLE, TYPE_SHADED }; - protected: int color; - Type type; public: - inline ColorAttrs(int color, Type type) + inline ColorAttrs(int color) { this->color = color; - this->type = type; } inline int getColor () { return color; } - inline Type getType () { return type; } - bool equals(object::Object *other); + bool equals(lout::object::Object *other); int hashValue(); }; @@ -636,12 +638,11 @@ class Color: public ColorAttrs private: int refCount; - static Color *create (Layout *layout, int color, Type type); void remove(dw::core::Layout *layout); - int shadeColor (int color, int d); + int shadeColor (int color, int d); protected: - inline Color (int color, Type type): ColorAttrs (color, type) { + inline Color (int color): ColorAttrs (color) { refCount = 0; } virtual ~Color (); @@ -653,19 +654,11 @@ protected: int shadeColor (int color, Shading shading); public: - inline static Color *createSimple (Layout *layout, int color) - { - return create (layout, color, TYPE_SIMPLE); - } - - inline static Color *createShaded (Layout *layout, int color) - { - return create (layout, color, TYPE_SHADED); - } + static Color *create (Layout *layout, int color); inline void ref () { refCount++; } inline void unref () - { if(--refCount == 0) delete this; } + { if (--refCount == 0) delete this; } }; void drawBorder (View *view, Rectangle *area, diff --git a/dw/table.cc b/dw/table.cc index 4135799b..51718587 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -14,17 +14,18 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ //#define DBG #include "table.hh" +#include "../lout/msg.h" #include "../lout/misc.hh" #define MAX misc::max +using namespace lout; namespace dw { @@ -34,6 +35,7 @@ Table::Table(bool limitTextWidth) { registerName ("dw::Table", &CLASS_ID); setFlags (USES_HINTS); + setButtonSensitive(false); this->limitTextWidth = limitTextWidth; @@ -113,7 +115,7 @@ void Table::sizeRequestImpl (core::Requisition *requisition) getStyle()->boxDiffHeight () + cumHeight->get (numRows) + getStyle()->vBorderSpacing; requisition->descent = 0; - + } void Table::getExtremesImpl (core::Extremes *extremes) @@ -141,10 +143,8 @@ void Table::getExtremesImpl (core::Extremes *extremes) core::style::absLengthVal(getStyle()->width)); } -#ifdef DBG - printf(" Table::getExtremesImpl, {%d, %d} numCols=%d\n", - extremes->minWidth, extremes->maxWidth, numCols); -#endif + _MSG(" Table::getExtremesImpl, {%d, %d} numCols=%d\n", + extremes->minWidth, extremes->maxWidth, numCols); } void Table::sizeAllocateImpl (core::Allocation *allocation) @@ -172,14 +172,14 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) core::Allocation childAllocation; core::Requisition childRequisition; - + children->get(n)->cell.widget->sizeRequest (&childRequisition); childAllocation.x = x; childAllocation.y = cumHeight->get (row) + offy; childAllocation.width = width; childAllocation.ascent = childRequisition.ascent; - childAllocation.descent = + childAllocation.descent = cumHeight->get (row + children->get(n)->cell.rowspan) - cumHeight->get (row) - getStyle()->vBorderSpacing - childRequisition.ascent; @@ -188,7 +188,7 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) } x += colWidths->get (col) + getStyle()->hBorderSpacing; - } + } } void Table::resizeDrawImpl () @@ -200,12 +200,10 @@ void Table::resizeDrawImpl () } void Table::setWidth (int width) -{ +{ // If limitTextWidth is set, a queueResize may also be necessary. if (availWidth != width || limitTextWidth) { -#ifdef DBG - printf(" Table::setWidth %d\n", width); -#endif + _MSG(" Table::setWidth %d\n", width); availWidth = width; queueResize (0, false); } @@ -258,7 +256,7 @@ void Table::draw (core::View *view, core::Rectangle *area) } } } - + void Table::removeChild (Widget *child) { /** \bug Not implemented. */ @@ -277,22 +275,22 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) // We limit the values for colspan and rowspan to 50, to avoid // attacks by malicious web pages. if (colspan > 50 || colspan < 0) { - fprintf (stderr, "WARNING: colspan = %d is set to 50.\n", colspan); + MSG_WARN("colspan = %d is set to 50.\n", colspan); colspan = 50; } if (rowspan > 50 || rowspan <= 0) { - fprintf (stderr, "WARNING: rowspan = %d is set to 50.\n", rowspan); + MSG_WARN("rowspan = %d is set to 50.\n", rowspan); rowspan = 50; } if (numRows == 0) { // to prevent a crash - fprintf (stderr, "WARNING: Cell without row.\n"); + MSG("addCell: cell without row.\n"); addRow (NULL); } if (rowClosed) { - fprintf (stderr, "WARNING: Last cell had colspan=0.\n"); + MSG_WARN("Last cell had colspan=0.\n"); addRow (NULL); } @@ -308,10 +306,9 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) child->type == Child::SPAN_SPACE) curCol++; -#ifdef DBG - printf("Table::addCell numCols=%d,curCol=%d,colspan=%d,colspanEff=%d\n", - numCols, curCol, colspan, colspanEff); -#endif + _MSG("Table::addCell numCols=%d,curCol=%d,colspan=%d,colspanEff=%d\n", + numCols, curCol, colspan, colspanEff); + // Increase children array, when necessary. if (curRow + rowspan > numRows) reallocChildren (numCols, curRow + rowspan); @@ -337,7 +334,7 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) child->cell.colspanEff = colspanEff; child->cell.rowspan = rowspan; children->set (curRow * numCols + curCol, child); - + curCol += colspanEff; widget->setParent (this); @@ -351,18 +348,18 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) for (int col = 0; col < numCols; col++) { int n = row * numCols + col; if (!(child = children->get (n))) { - printf("[null ] "); + MSG("[null ] "); } else if (children->get(n)->type == Child::CELL) { - printf("[CELL rs=%d] ", child->cell.rowspan); + MSG("[CELL rs=%d] ", child->cell.rowspan); } else if (children->get(n)->type == Child::SPAN_SPACE) { - printf("[SPAN rs=%d] ", child->cell.rowspan); + MSG("[SPAN rs=%d] ", child->cell.rowspan); } else { - printf("[Unk. ] "); + MSG("[Unk. ] "); } } - printf("\n"); + MSG("\n"); } - printf("\n"); + MSG("\n"); #endif } @@ -458,7 +455,7 @@ void Table::reallocChildren (int newNumCols, int newNumRows) for (int row = numRows; row < newNumRows; row++) for (int col = 0; col < newNumCols; col++) children->set (row * newNumCols + col, NULL); - + // Simple arrays. rowStyle->setSize (newNumRows); for (int row = numRows; row < newNumRows; row++) @@ -485,7 +482,7 @@ void Table::forceCalcCellSizes () // Will also call calcColumnExtremes(), when needed. getExtremes (&extremes); - + if (core::style::isAbsLength (getStyle()->width)) { totalWidth = core::style::absLengthVal (getStyle()->width); } else if (core::style::isPerLength (getStyle()->width)) { @@ -503,28 +500,28 @@ void Table::forceCalcCellSizes () totalWidth = availWidth; forceTotalWidth = 0; } -#ifdef DBG - printf(" availWidth = %d\n", availWidth); - printf(" totalWidth1 = %d\n", totalWidth); -#endif + + _MSG(" availWidth = %d\n", availWidth); + _MSG(" totalWidth1 = %d\n", totalWidth); + if (totalWidth < extremes.minWidth) totalWidth = extremes.minWidth; totalWidth = totalWidth - (numCols + 1) * getStyle()->hBorderSpacing - getStyle()->boxDiffWidth (); -#ifdef DBG - printf(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol); -#endif + + _MSG(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol); + colWidths->setSize (numCols, 0); cumHeight->setSize (numRows + 1, 0); rowSpanCells->setSize (0); baseline->setSize (numRows); -#ifdef DBG - printf(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth); - printf(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth()); - printf(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); -#endif + + _MSG(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth); + _MSG(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth()); + _MSG(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); + apportion_percentages2 (totalWidth, forceTotalWidth); if (!hasColPercent) @@ -583,10 +580,9 @@ void Table::apportionRowSpan () continue; // Cell size is too small. -#ifdef DBG - printf("Short cell %d, sumRows=%d spanHeight=%d\n", - n,sumRows,spanHeight); -#endif + _MSG("Short cell %d, sumRows=%d spanHeight=%d\n", + n,sumRows,spanHeight); + // Fill height array if (!rowHeight) { rowHeight = new int[numRows]; @@ -594,10 +590,10 @@ void Table::apportionRowSpan () rowHeight[i] = cumHeight->get(i+1) - cumHeight->get(i); } #ifdef DBG - printf (" rowHeight { "); + MSG(" rowHeight { "); for (int i = 0; i < numRows; i++) - printf ("%d ", rowHeight[i]); - printf ("}\n"); + MSG("%d ", rowHeight[i]); + MSG("}\n"); #endif // Calc new row sizes for this span. @@ -607,12 +603,12 @@ void Table::apportionRowSpan () sumRows == 0 ? (int)((float)(spanHeight-cumHnew_i)/(row+rs-i)) : (sumRows-cumh_i) <= 0 ? 0 : (int)((float)(spanHeight-cumHnew_i)*rowHeight[i]/(sumRows-cumh_i)); -#ifdef DBG - printf (" i=%-3d h=%d hnew_i=%d =%d*%d/%d cumh_i=%d cumHnew_i=%d\n", - i,rowHeight[i],hnew_i, - spanHeight-cumHnew_i,rowHeight[i],sumRows-cumh_i, - cumh_i, cumHnew_i); -#endif + + _MSG(" i=%-3d h=%d hnew_i=%d =%d*%d/%d cumh_i=%d cumHnew_i=%d\n", + i,rowHeight[i],hnew_i, + spanHeight-cumHnew_i,rowHeight[i],sumRows-cumh_i, + cumh_i, cumHnew_i); + cumHnew_i += hnew_i; cumh_i += rowHeight[i]; rowHeight[i] = hnew_i; @@ -642,9 +638,8 @@ void Table::calcColumnExtremes () */ void Table::forceCalcColumnExtremes () { -#ifdef DBG - printf(" Table::forceCalcColumnExtremes numCols=%d\n", numCols); -#endif + _MSG(" Table::forceCalcColumnExtremes numCols=%d\n", numCols); + if (numCols == 0) return; @@ -678,13 +673,13 @@ void Table::forceCalcColumnExtremes () cellMinW = cellExtremes.minWidth; cellMaxW = cellExtremes.maxWidth; } -#ifdef DBG - printf("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n", - col, - colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth, - cellMinW, cellMaxW); -#endif + + _MSG("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n", + col, + colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth, + cellMinW, cellMaxW); + colExtremes->getRef(col)->minWidth = MAX (colExtremes->getRef(col)->minWidth, cellMinW); colExtremes->getRef(col)->maxWidth = @@ -737,10 +732,10 @@ void Table::forceCalcColumnExtremes () minSumCols += colExtremes->getRef(col+i)->minWidth; maxSumCols += colExtremes->getRef(col+i)->maxWidth; } -#ifdef DBG - printf("cs=%d spanWidth=%d,%d sumCols=%d,%d\n", - cs,cellMinW,cellMaxW,minSumCols,maxSumCols); -#endif + + _MSG("cs=%d spanWidth=%d,%d sumCols=%d,%d\n", + cs,cellMinW,cellMaxW,minSumCols,maxSumCols); + if (minSumCols >= cellMinW && maxSumCols >= cellMaxW) continue; @@ -785,7 +780,7 @@ void Table::forceCalcColumnExtremes () int d_a = colExtremes->getRef(i)->maxWidth; int d_w = curAppW > 0 ? (int)((float)curExtraW * d_a/curAppW) : 0; if (d_a < 0||d_w < 0) { - printf("d_a=%d d_w=%d\n",d_a,d_w); + MSG("d_a=%d d_w=%d\n",d_a,d_w); exit(1); } wMin = colExtremes->getRef(i)->minWidth + d_w; @@ -809,19 +804,19 @@ void Table::forceCalcColumnExtremes () cumMaxWnew += wMax; cumMaxWold += colExtremes->getRef(i)->maxWidth; colExtremes->getRef(i)->maxWidth = wMax; -#ifdef DBG - printf ("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n", - i,wMin,wMax,cumMaxWold); -#endif + + _MSG("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n", + i,wMin,wMax,cumMaxWold); + } #ifdef DBG - printf ("col min,max: ["); + MSG("col min,max: ["); for (int i = 0; i < numCols; i++) - printf ("%d,%d ", - colExtremes->getRef(i)->minWidth, - colExtremes->getRef(i)->maxWidth); - printf ("]\n"); - printf ("getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); + MSG("%d,%d ", + colExtremes->getRef(i)->minWidth, + colExtremes->getRef(i)->maxWidth); + MSG("]\n"); + MSG("getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing); #endif } } @@ -835,13 +830,13 @@ void Table::apportion2 (int totalWidth, int forceTotalWidth) if (colExtremes->size() == 0) return; #ifdef DBG - printf("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", - availWidth, totalWidth, forceTotalWidth); - printf("app2, extremes: ( "); + MSG("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", + availWidth, totalWidth, forceTotalWidth); + MSG("app2, extremes: ( "); for (int i = 0; i < colExtremes->size (); i++) - printf("%d,%d ", - colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); - printf(")\n"); + MSG("%d,%d ", + colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); + MSG(")\n"); #endif int minAutoWidth = 0, maxAutoWidth = 0, availAutoWidth = totalWidth; for (int col = 0; col < numCols; col++) { @@ -871,10 +866,9 @@ void Table::apportion2 (int totalWidth, int forceTotalWidth) int curMaxWidth = maxAutoWidth; int curNewWidth = minAutoWidth; for (int col = 0; col < numCols; col++) { -#ifdef DBG - printf("app2, col %d, minWidth=%d maxWidth=%d\n", - col,extremes->get(col).minWidth, colExtremes->get(col).maxWidth); -#endif + _MSG("app2, col %d, minWidth=%d maxWidth=%d\n", + col,extremes->get(col).minWidth, colExtremes->get(col).maxWidth); + if (colPercents->get(col) != LEN_AUTO) continue; @@ -882,19 +876,19 @@ void Table::apportion2 (int totalWidth, int forceTotalWidth) int colMaxWidth = colExtremes->getRef(col)->maxWidth; int w = (curMaxWidth <= 0) ? 0 : (int)((float)curTargetWidth * colMaxWidth/curMaxWidth); -#ifdef DBG - printf("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d " - "curNewWidth=%d ", - curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth); - printf("w = %d, ", w); -#endif + + _MSG("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d " + "curNewWidth=%d ", + curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth); + _MSG("w = %d, ", w); + if (w <= colMinWidth) w = colMinWidth; else if (curNewWidth - colMinWidth + w > curTargetWidth) w = colMinWidth + curExtraWidth; -#ifdef DBG - printf("w = %d\n", w); -#endif + + _MSG("w = %d\n", w); + curNewWidth -= colMinWidth; curMaxWidth -= colMaxWidth; curExtraWidth -= (w - colMinWidth); @@ -902,10 +896,10 @@ void Table::apportion2 (int totalWidth, int forceTotalWidth) setColWidth (col, w); } #ifdef DBG - printf("app2, result: ( "); + MSG("app2, result: ( "); for (int i = 0; i < colWidths->size (); i++) - printf("%d ", colWidths->get (i)); - printf(")\n"); + MSG("%d ", colWidths->get (i)); + MSG(")\n"); #endif } @@ -917,19 +911,17 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) return; // If there's a table-wide percentage, totalWidth comes already scaled. -#ifdef DBG - printf("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", - availWidth, totalWidth, forceTotalWidth); -#endif + _MSG("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n", + availWidth, totalWidth, forceTotalWidth); if (!hasColPercent) { #ifdef DBG - printf("APP_P, only a table-wide percentage\n"); - printf("APP_P, extremes = { "); + MSG("APP_P, only a table-wide percentage\n"); + MSG("APP_P, extremes = { "); for (int col = 0; col < numCols; col++) - printf("%d,%d ", colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth); - printf("}\n"); + MSG("%d,%d ", colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth); + MSG("}\n"); #endif // It has only a table-wide percentage. Apportion non-absolute widths. int sumMaxWidth = 0, perAvailWidth = totalWidth; @@ -939,10 +931,10 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) else sumMaxWidth += colExtremes->getRef(col)->maxWidth; } -#ifdef DBG - printf("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n", - perAvailWidth, sumMaxWidth); -#endif + + _MSG("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n", + perAvailWidth, sumMaxWidth); + for (int col = 0; col < numCols; col++) { int max_wi = colExtremes->getRef(col)->maxWidth, new_wi; if (colPercents->get(col) != LEN_ABS) { @@ -954,17 +946,16 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) } } #ifdef DBG - printf("APP_P, result = { "); + MSG("APP_P, result = { "); for (int col = 0; col < numCols; col++) - printf("%d ", result->get(col)); - printf("}\n"); + MSG("%d ", colWidths->get(col)); + MSG("}\n"); #endif } else { // we'll have to apportion... -#ifdef DBG - printf("APP_P, we'll have to apportion...\n"); -#endif + _MSG("APP_P, we'll have to apportion...\n"); + // Calculate cumPercent and available space float cumPercent = 0.0f; int hasAutoCol = 0; @@ -979,11 +970,10 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) } sumMinWidth += colExtremes->getRef(col)->minWidth; sumMaxWidth += colExtremes->getRef(col)->maxWidth; -#ifdef DBG - printf("APP_P, col %d minWidth=%d maxWidth=%d\n", col, - colExtremes->getRef(col)->minWidth, - colExtremes->getRef(col)->maxWidth); -#endif + + _MSG("APP_P, col %d minWidth=%d maxWidth=%d\n", col, + colExtremes->getRef(col)->minWidth, + colExtremes->getRef(col)->maxWidth); } int oldTotalWidth = totalWidth; if (!forceTotalWidth) { @@ -1011,11 +1001,11 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) workingWidth = totalWidth; curPerWidth = sumMinWidth; } -#ifdef DBG - printf("APP_P, oldTotalWidth=%d totalWidth=%d" - " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n", - oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer); -#endif + + _MSG("APP_P, oldTotalWidth=%d totalWidth=%d" + " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n", + oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer); + for (int col = 0; col < numCols; col++) { int colMinWidth = colExtremes->getRef(col)->minWidth; if (colPercents->get(col) >= 0.0f) { @@ -1035,20 +1025,20 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) if (cumPercent < 0.99f) { // Will have to apportion the other columns #ifdef DBG - printf("APP_P, extremes: ( "); + MSG("APP_P, extremes: ( "); for (int i = 0; i < colExtremes->size (); i++) - printf("%d,%d ", - colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); - printf(")\n"); + MSG("%d,%d ", + colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth); + MSG(")\n"); #endif curPerWidth -= sumMinNonPer; int perWidth = (int)(curPerWidth/cumPercent); totalWidth = MAX (totalWidth, perWidth); totalWidth = misc::min (totalWidth, oldTotalWidth); -#ifdef DBG - printf("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n", - curPerWidth, perWidth, totalWidth); -#endif + + _MSG("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n", + curPerWidth, perWidth, totalWidth); + if (hasAutoCol == 0) { // Special case, cumPercent < 100% and no other columns to expand. // We'll honor totalWidth by expanding the percentage cols. @@ -1062,22 +1052,22 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth) } } #ifdef DBG - printf("APP_P, result ={ "); + MSG("APP_P, result ={ "); for (int col = 0; col < numCols; col++) - printf("%d ", colWidths->get(col)); - printf("}\n"); + MSG("%d ", colWidths->get(col)); + MSG("}\n"); #endif apportion2 (totalWidth, 2); #ifdef DBG - printf("APP_P, percent={"); + MSG("APP_P, percent={"); for (int col = 0; col < numCols; col++) - printf("%f ", colPercents->get(col)); - printf("}\n"); - printf("APP_P, result ={ "); + MSG("%f ", colPercents->get(col)); + MSG("}\n"); + MSG("APP_P, result ={ "); for (int col = 0; col < numCols; col++) - printf("%d ", colWidths->get(col)); - printf("}\n"); + MSG("%d ", colWidths->get(col)); + MSG("}\n"); #endif } } @@ -1117,7 +1107,7 @@ int Table::TableIterator::compareTo(misc::Comparable *other) { return index - ((TableIterator*)other)->index; } - + bool Table::TableIterator::next () { Table *table = (Table*)getWidget(); @@ -1130,7 +1120,7 @@ bool Table::TableIterator::next () content.type = core::Content::END; return false; } - + do { index++; if (index >= table->children->size ()) { @@ -1157,7 +1147,7 @@ bool Table::TableIterator::prev () content.type = core::Content::START; return false; } - + do { index--; if (index < 0) { diff --git a/dw/table.hh b/dw/table.hh index ec2bacc8..87bbaa76 100644 --- a/dw/table.hh +++ b/dw/table.hh @@ -11,22 +11,22 @@ namespace dw { * \brief A Widget for rendering tables. * * <h3>Introduction</h3> - * + * * The dw::Table widget is used to render HTML tables. - * - * Each cell is itself an own widget. Any widget may be used, however, in + * + * 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 informations. - * + * for more information. + * * \dot * digraph G { * node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"]; @@ -34,125 +34,125 @@ namespace dw { * 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"]; * getExtremesImpl [color="#0000ff", URL="\ref dw::Table::getExtremesImpl"]; - * + * * subgraph cluster_sizes { * style="dashed"; color="#8080c0"; * calcCellSizes [URL="\ref dw::Table::calcCellSizes"]; * forceCalcCellSizes [URL="\ref dw::Table::forceCalcCellSizes"]; * } - * + * * subgraph cluster_extremes { * style="dashed"; color="#8080c0"; * calcColumnExtremes [URL="\ref dw::Table::calcColumnExtremes"]; * forceCalcColumnExtremes[URL="\ref dw::Table::forceCalcColumnExtremes"]; * } - * + * * sizeRequestImpl -> forceCalcCellSizes [label="[B]"]; * sizeAllocateImpl -> calcCellSizes [label="[A]"]; * getExtremesImpl -> forceCalcColumnExtremes [label="[B]"]; - * + * * forceCalcCellSizes -> calcColumnExtremes; - * + * * calcCellSizes -> forceCalcCellSizes [style="dashed", label="[C]"]; * calcColumnExtremes -> forceCalcColumnExtremes [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 allways necessary, since [C] * is the case. - * + * * [C] Whether this function is called, depends on NEEDS_RESIZE / * EXTREMES_CHANGED. - * - * + * + * * <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 independant of the exact + * 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 exeeds the sum of the column minima + * 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 exeeds the sum of the + * <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 @@ -160,133 +160,133 @@ namespace dw { * (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 or equal than/to the + * be assured, that the maximum is always greater than or equal to the * minimum. * </ol> - * - * Generally, if absolute widths are speficied, they are, instead of the + * + * 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) times the specifies - * percentage width pf t(at max 100%), if the latter is given, or + * <li> the available width (set by dw::Table::setWidth) 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 parantheses is not fully clear, look at the old code. - * + * + * \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 - * then: - * + * 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. + * 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 - * apportenment function. - * - * + * 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 lead to well-defined \f$w_{i}\f$: All columns + * 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 @@ -306,13 +306,13 @@ namespace dw { * 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; @@ -320,14 +320,14 @@ namespace dw { * 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. */ @@ -361,8 +361,8 @@ private: TableIterator (Table *table, core::Content::Type mask, bool atEnd); TableIterator (Table *table, core::Content::Type mask, int index); - object::Object *clone(); - int compareTo(misc::Comparable *other); + lout::object::Object *clone(); + int compareTo(lout::misc::Comparable *other); bool next (); bool prev (); @@ -377,37 +377,37 @@ private: int availWidth, availAscent, availDescent; // set by set... int numRows, numCols, curRow, curCol; - misc::SimpleVector<Child*> *children; + lout::misc::SimpleVector<Child*> *children; int redrawX, redrawY; - + /** * \brief The extremes of all columns. */ - misc::SimpleVector<core::Extremes> *colExtremes; + lout::misc::SimpleVector<core::Extremes> *colExtremes; /** * \brief The widths of all columns. */ - misc::SimpleVector<int> *colWidths; + 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. */ - misc::SimpleVector<int> *cumHeight; + lout::misc::SimpleVector<int> *cumHeight; /** * If a Cell has rowspan > 1, it goes into this array */ - misc::SimpleVector<int> *rowSpanCells; + lout::misc::SimpleVector<int> *rowSpanCells; /** * If a Cell has colspan > 1, it goes into this array */ - misc::SimpleVector<int> *colSpanCells; - misc::SimpleVector<int> *baseline; + lout::misc::SimpleVector<int> *colSpanCells; + lout::misc::SimpleVector<int> *baseline; - misc::SimpleVector<core::style::Style*> *rowStyle; + lout::misc::SimpleVector<core::style::Style*> *rowStyle; /** * hasColPercent becomes true when any cell specifies a percentage width. @@ -415,7 +415,7 @@ private: */ enum { LEN_AUTO = -1, LEN_ABS = -2}; int hasColPercent; - misc::SimpleVector<float> *colPercents; + lout::misc::SimpleVector<float> *colPercents; inline bool childDefined(int n) { @@ -438,7 +438,7 @@ private: void setCumHeight (int row, int value) { if (value != cumHeight->get (row)) { - redrawY = misc::min ( redrawY, value ); + redrawY = lout::misc::min ( redrawY, value ); cumHeight->set (row, value); } } @@ -446,7 +446,7 @@ private: inline void setColWidth (int col, int value) { if (value != colWidths->get (col)) { - redrawX = misc::min (redrawX, value); + redrawX = lout::misc::min (redrawX, value); colWidths->set (col, value); } } @@ -460,7 +460,7 @@ protected: void setWidth (int width); void setAscent (int ascent); void setDescent (int descent); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area); //bool buttonPressImpl (core::EventButton *event); //bool buttonReleaseImpl (core::EventButton *event); @@ -474,7 +474,7 @@ public: Table(bool limitTextWidth); ~Table(); - core::Iterator *iterator (core::Content::Type mask, bool atEnd); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); void addCell (Widget *widget, int colspan, int rowspan); void addRow (core::style::Style *style); diff --git a/dw/tablecell.cc b/dw/tablecell.cc index b4d404f7..5b93fe86 100644 --- a/dw/tablecell.cc +++ b/dw/tablecell.cc @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -29,13 +28,14 @@ int TableCell::CLASS_ID = -1; TableCell::TableCell (TableCell *ref, bool limitTextWidth): AlignedTextblock (limitTextWidth) -{ +{ registerName ("dw::TableCell", &CLASS_ID); /** \bug ignoreLine1OffsetSometimes does not work? */ //ignoreLine1OffsetSometimes = true; charWordIndex = -1; setRefTextblock (ref); + setButtonSensitive(true); } TableCell::~TableCell() @@ -45,7 +45,7 @@ TableCell::~TableCell() void TableCell::wordWrap(int wordIndex) { Textblock::Word *word; - char *p; + const char *p; Textblock::wordWrap (wordIndex); diff --git a/dw/textblock.cc b/dw/textblock.cc index 6bfd4d90..33d3a1b0 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -14,18 +14,20 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "textblock.hh" #include "table.hh" // Yes, this is ugly. -- SG +#include "../lout/msg.h" #include "../lout/misc.hh" #include <stdio.h> #include <math.h> // remove again #include <limits.h> +using namespace lout; + namespace dw { int Textblock::CLASS_ID = -1; @@ -34,15 +36,16 @@ Textblock::Textblock (bool limitTextWidth) { registerName ("dw::Textblock", &CLASS_ID); setFlags (USES_HINTS); + setButtonSensitive(true); - listItem = false; + hasListitemValue = false; innerPadding = 0; line1Offset = 0; line1OffsetEff = 0; ignoreLine1OffsetSometimes = false; mustQueueResize = false; redrawY = 0; - lastWordDrawn = 0; + lastWordDrawn = -1; /* * The initial sizes of lines and words should not be @@ -56,6 +59,7 @@ Textblock::Textblock (bool limitTextWidth) lines = new misc::SimpleVector <Line> (1); words = new misc::SimpleVector <Word> (1); leftFloatSide = rightFloatSide = NULL; + anchors = new misc::SimpleVector <Anchor> (1); //DBG_OBJ_SET_NUM(page, "num_lines", num_lines); @@ -97,17 +101,20 @@ Textblock::~Textblock () Word *word = words->getRef (i); if (word->content.type == core::Content::WIDGET) delete word->content.widget; - else if (word->content.type == core::Content::ANCHOR) - /* This also frees the names (see removeAnchor() and related). */ - removeAnchor(word->content.anchor); - word->style->unref (); word->spaceStyle->unref (); } + for (int i = 0; i < anchors->size(); i++) { + Anchor *anchor = anchors->getRef (i); + /* This also frees the names (see removeAnchor() and related). */ + removeAnchor(anchor->name); + } + delete lines; delete words; - + delete anchors; + if(leftFloatSide) delete leftFloatSide; if(rightFloatSide) @@ -133,11 +140,12 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) Line *lastLine = lines->getRef (lines->size () - 1); requisition->width = misc::max (lastLine->maxLineWidth, lastLineWidth); - /* Note: the break_space of the last line is ignored, so breaks + /* Note: the breakSpace of the last line is ignored, so breaks at the end of a textblock are not visible. */ - requisition->ascent = lines->getRef(0)->ascent; + requisition->ascent = lines->getRef(0)->boxAscent; requisition->descent = lastLine->top - + lastLine->ascent + lastLine->descent - lines->getRef(0)->ascent; + + lastLine->boxAscent + lastLine->boxDescent - + lines->getRef(0)->boxAscent; } else { requisition->width = lastLineWidth; requisition->ascent = 0; @@ -237,21 +245,23 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) for (lineIndex = wrapRef; lineIndex < lines->size (); lineIndex++) { //DBG_MSGF (widget, "extremes", 0, "line %d", lineIndex); //DBG_MSG_START (widget); + core::style::WhiteSpace ws; line = lines->getRef (lineIndex); - nowrap = - words->getRef(line->firstWord)->style->whiteSpace - != core::style::WHITE_SPACE_NORMAL; + ws = words->getRef(line->firstWord)->style->whiteSpace; + nowrap = ws == core::style::WHITE_SPACE_PRE || + ws == core::style::WHITE_SPACE_NOWRAP; //DEBUG_MSG (DEBUG_SIZE_LEVEL, " line %d (of %d), nowrap = %d\n", // lineIndex, page->num_lines, nowrap); - for (wordIndex = line->firstWord; wordIndex < line->lastWord; + for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { word = words->getRef (wordIndex); getWordExtremes (word, &wordExtremes); /* For the first word, we simply add the line1_offset. */ + /* This test looks questionable */ if (ignoreLine1OffsetSometimes && wordIndex == 0) { wordExtremes.minWidth += line1Offset; //DEBUG_MSG (DEBUG_SIZE_LEVEL + 1, @@ -266,8 +276,8 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth = wordExtremes.minWidth; } - //printf("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n", - // parMax, wordExtremes.maxWidth, prevWordSpace); + _MSG("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n", + parMax, wordExtremes.maxWidth, prevWordSpace); if (word->content.type != core::Content::BREAK) parMax += prevWordSpace; parMax += wordExtremes.maxWidth; @@ -279,11 +289,9 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) // word_extremes.maxWidth); } - if ((line->lastWord > line->firstWord && - words->getRef(line->lastWord - 1)->content.type + if ((words->getRef(line->lastWord)->content.type == core::Content::BREAK ) || lineIndex == lines->size () - 1 ) { - word = words->getRef (line->lastWord - 1); //DEBUG_MSG (DEBUG_SIZE_LEVEL + 2, // " parMax = %d, after word %d (%s)\n", @@ -335,7 +343,6 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) int xCursor; core::Allocation childAllocation; core::Allocation *oldChildAllocation; - int wordInLine; if (allocation->width != this->allocation.width) { redrawY = 0; @@ -345,17 +352,15 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) line = lines->getRef (lineIndex); xCursor = lineXOffsetWidget (line); - wordInLine = 0; - for (wordIndex = line->firstWord; wordIndex < line->lastWord; + for (wordIndex = line->firstWord; wordIndex <= line->lastWord; wordIndex++) { word = words->getRef (wordIndex); - if (wordIndex == lastWordDrawn) { + if (wordIndex == lastWordDrawn + 1) { redrawY = misc::min (redrawY, lineYOffsetWidget (line)); } - switch (word->content.type) { - case core::Content::WIDGET: + if (word->content.type == core::Content::WIDGET) { /** \todo Justification within the line is done here. */ childAllocation.x = xCursor + allocation->x; /* align=top: @@ -367,7 +372,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) * http://www.dillo.org/test/img/ */ childAllocation.y = lineYOffsetCanvasAllocation (line, allocation) - + (line->ascent - word->size.ascent); + + (line->boxAscent - word->size.ascent); // - word->content.widget->getStyle()->margin.top; childAllocation.width = word->size.width; childAllocation.ascent = word->size.ascent; @@ -376,9 +381,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) // + word->content.widget->getStyle()->margin.bottom; oldChildAllocation = word->content.widget->getAllocation(); - - if (childAllocation.x != oldChildAllocation->x || - childAllocation.y != oldChildAllocation->y || + + if (childAllocation.x != oldChildAllocation->x || + childAllocation.y != oldChildAllocation->y || childAllocation.width != oldChildAllocation->width) { /* The child widget has changed its position or its width * so we need to redraw from this line onwards. @@ -397,38 +402,53 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) * child might be a table covering the whole page so we would * end up redrawing the whole screen over and over. * The drawing of the child content is left to the child itself. + * However this optimization is only possible if the widget is + * the only word in the line apart from an optional BREAK. + * Otherwise the height change of the widget could change the + * position of other words in the line, requiring a + * redraw of the complete line. */ - int childChangedY = - misc::min(childAllocation.y - allocation->y + - childAllocation.ascent + childAllocation.descent, - oldChildAllocation->y - this->allocation.y + - oldChildAllocation->ascent + oldChildAllocation->descent); - - redrawY = misc::min (redrawY, childChangedY); + if (line->lastWord == line->firstWord || + (line->lastWord == line->firstWord + 1 && + words->getRef (line->lastWord)->content.type == + core::Content::BREAK)) { + + int childChangedY = + misc::min(childAllocation.y - allocation->y + + childAllocation.ascent + childAllocation.descent, + oldChildAllocation->y - this->allocation.y + + oldChildAllocation->ascent + + oldChildAllocation->descent); + + redrawY = misc::min (redrawY, childChangedY); + } else { + redrawY = misc::min (redrawY, lineYOffsetWidget (line)); + } } - word->content.widget->sizeAllocate (&childAllocation); - break; - - case core::Content::ANCHOR: - changeAnchor (word->content.anchor, - lineYOffsetCanvasAllocation (line, allocation)); - break; - - default: - wordInLine++; - // make compiler happy - break; } xCursor += (word->size.width + word->effSpace); } } - + if(leftFloatSide) leftFloatSide->sizeAllocate(allocation); if(rightFloatSide) rightFloatSide->sizeAllocate(allocation); + + for (int i = 0; i < anchors->size(); i++) { + Anchor *anchor = anchors->getRef(i); + int y; + + if (anchor->wordIndex >= words->size()) { + y = allocation->y + allocation->ascent + allocation->descent; + } else { + Line *line = lines->getRef(findLineOfWord (anchor->wordIndex)); + y = lineYOffsetCanvasAllocation (line, allocation); + } + changeAnchor (anchor->name, y); + } } void Textblock::resizeDrawImpl () @@ -594,28 +614,22 @@ bool Textblock::motionNotifyImpl (core::EventMotion *event) if (event->state & core::BUTTON1_MASK) return sendSelectionEvent (core::SelectionState::BUTTON_MOTION, event); else { - int linkOld, wordIndex; - core::style::Tooltip *tooltipOld; - - wordIndex = findWord (event->xWidget, event->yWidget); + bool inSpace; + int linkOld = hoverLink; + core::style::Tooltip *tooltipOld = hoverTooltip; + const Word *word = findWord (event->xWidget, event->yWidget, &inSpace); // cursor from word or widget style - if (wordIndex == -1) + if (word == NULL) { setCursor (getStyle()->cursor); - else - setCursor (words->getRef(wordIndex)->style->cursor); - - linkOld = hoverLink; - tooltipOld = hoverTooltip; - - if (wordIndex == -1) { hoverLink = -1; hoverTooltip = NULL; } else { - hoverLink = words->getRef(wordIndex)->style->x_link; - hoverTooltip = words->getRef(wordIndex)->style->x_tooltip; + core::style::Style *style = inSpace ? word->spaceStyle : word->style; + setCursor (style->cursor); + hoverLink = style->x_link; + hoverTooltip = style->x_tooltip; } - // Show/hide tooltip if (tooltipOld != hoverTooltip) { if (tooltipOld) @@ -626,7 +640,7 @@ bool Textblock::motionNotifyImpl (core::EventMotion *event) hoverTooltip->onMotion (); if (hoverLink != linkOld) - return emitLinkEnter (hoverLink, -1, -1, -1); + return layout->emitLinkEnter (this, hoverLink, -1, -1, -1); else return hoverLink != -1; } @@ -639,7 +653,11 @@ void Textblock::enterNotifyImpl (core::EventCrossing *event) void Textblock::leaveNotifyImpl (core::EventCrossing *event) { hoverLink = -1; - (void) emitLinkEnter (hoverLink, -1, -1, -1); + (void) layout->emitLinkEnter (this, hoverLink, -1, -1, -1); + if (hoverTooltip) { + hoverTooltip->onLeave(); + hoverTooltip = NULL; + } } /** @@ -651,114 +669,110 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, core::Iterator *it; Line *line, *lastLine; int nextWordStartX, wordStartX, wordX, nextWordX, yFirst, yLast; - int charPos = 0, prevPos, wordIndex, lineIndex, link; + int charPos = 0, link = -1, prevPos, wordIndex, lineIndex; Word *word; - bool found, withinContent, r; - - if (words->size () == 0) - // no contens at all - return false; + bool found, r, withinContent = true; - // In most cases true, so set here: - link = -1; - withinContent = true; - - lastLine = lines->getRef (lines->size () - 1); - yFirst = lineYOffsetCanvasI (0); - yLast = - lineYOffsetCanvas (lastLine) + lastLine->ascent + lastLine->descent; - if (event->yCanvas < yFirst) { - // Above the first line: take the first word. - withinContent = false; - wordIndex = 0; - charPos = 0; - } else if (event->yCanvas >= yLast) { - // Below the last line: take the last word. + if (words->size () == 0) { withinContent = false; - wordIndex = words->size () - 1; - word = words->getRef (wordIndex); - charPos = word->content.type == core::Content::TEXT ? - strlen (word->content.text) : 0; + wordIndex = -1; } else { - lineIndex = findLineIndex (event->yWidget); - line = lines->getRef (lineIndex); - - // Pointer within the break space? - if (event->yWidget > - (lineYOffsetWidget (line) + line->ascent + line->descent)) { - // Choose this break. + lastLine = lines->getRef (lines->size () - 1); + yFirst = lineYOffsetCanvasI (0); + yLast = lineYOffsetCanvas (lastLine) + lastLine->boxAscent + + lastLine->boxDescent; + if (event->yCanvas < yFirst) { + // Above the first line: take the first word. withinContent = false; - wordIndex = line->lastWord - 1; + wordIndex = 0; charPos = 0; - } else if (event->xWidget < lineXOffsetWidget (line)) { - // Left of the first word in the line. - wordIndex = line->firstWord; + } else if (event->yCanvas >= yLast) { + // Below the last line: take the last word. withinContent = false; - charPos = 0; + wordIndex = words->size () - 1; + word = words->getRef (wordIndex); + charPos = word->content.type == core::Content::TEXT ? + strlen (word->content.text) : 0; } else { - nextWordStartX = lineXOffsetWidget (line); - found = false; - for (wordIndex = line->firstWord; - !found && wordIndex < line->lastWord; - wordIndex++) { - word = words->getRef (wordIndex); - wordStartX = nextWordStartX; - nextWordStartX += word->size.width + word->effSpace; - - if (event->xWidget >= wordStartX && - event->xWidget < nextWordStartX) { - // We have found the word. - if (word->content.type == core::Content::TEXT) { - // Search the character the mouse pointer is in. - // nextWordX is the right side of this character. - charPos = 0; - while ((nextWordX = wordStartX + - layout->textWidth (word->style->font, - word->content.text, charPos)) - <= event->xWidget) - charPos = layout->nextGlyph (word->content.text, charPos); - - // The left side of this character. - prevPos = layout->prevGlyph (word->content.text, charPos); - wordX = wordStartX + layout->textWidth (word->style->font, - word->content.text, - prevPos); - - // If the mouse pointer is left from the middle, use the left - // position, otherwise, use the right one. - if (event->xWidget <= (wordX + nextWordX) / 2) - charPos = prevPos; - } else { - // Depends on whether the pointer is within the left or - // right half of the (non-text) word. - if (event->xWidget >= - (wordStartX + nextWordStartX) / 2) - charPos = core::SelectionState::END_OF_WORD; - else + lineIndex = findLineIndex (event->yWidget); + line = lines->getRef (lineIndex); + + // Pointer within the break space? + if (event->yWidget > + (lineYOffsetWidget (line) + line->boxAscent + line->boxDescent)) { + // Choose this break. + withinContent = false; + wordIndex = line->lastWord; + charPos = 0; + } else if (event->xWidget < lineXOffsetWidget (line)) { + // Left of the first word in the line. + wordIndex = line->firstWord; + withinContent = false; + charPos = 0; + } else { + nextWordStartX = lineXOffsetWidget (line); + found = false; + for (wordIndex = line->firstWord; + !found && wordIndex <= line->lastWord; + wordIndex++) { + word = words->getRef (wordIndex); + wordStartX = nextWordStartX; + nextWordStartX += word->size.width + word->effSpace; + + if (event->xWidget >= wordStartX && + event->xWidget < nextWordStartX) { + // We have found the word. + if (word->content.type == core::Content::TEXT) { + // Search the character the mouse pointer is in. + // nextWordX is the right side of this character. charPos = 0; + while ((nextWordX = wordStartX + + layout->textWidth (word->style->font, + word->content.text, charPos)) + <= event->xWidget) + charPos = layout->nextGlyph (word->content.text, + charPos); + // The left side of this character. + prevPos = layout->prevGlyph (word->content.text, charPos); + wordX = wordStartX + layout->textWidth (word->style->font, + word->content.text, + prevPos); + + // If the mouse pointer is left from the middle, use the + // left position, otherwise, use the right one. + if (event->xWidget <= (wordX + nextWordX) / 2) + charPos = prevPos; + } else { + // Depends on whether the pointer is within the left or + // right half of the (non-text) word. + if (event->xWidget >= + (wordStartX + nextWordStartX) / 2) + charPos = core::SelectionState::END_OF_WORD; + else + charPos = 0; + } + + found = true; + link = word->style ? word->style->x_link : -1; + break; } - - found = true; - link = word->style ? word->style->x_link : -1; - break; } - } - if (!found) { - // No word found in this line (i.e. we are on the right side), - // take the last of this line. - withinContent = false; - wordIndex = line->lastWord - 1; - if (wordIndex >= words->size ()) - wordIndex--; - word = words->getRef (wordIndex); - charPos = word->content.type == core::Content::TEXT ? - strlen (word->content.text) : - (int)core::SelectionState::END_OF_WORD; + if (!found) { + // No word found in this line (i.e. we are on the right side), + // take the last of this line. + withinContent = false; + wordIndex = line->lastWord; + if (wordIndex >= words->size ()) + wordIndex--; + word = words->getRef (wordIndex); + charPos = word->content.type == core::Content::TEXT ? + strlen (word->content.text) : + (int)core::SelectionState::END_OF_WORD; + } } } } - it = new TextblockIterator (this, core::Content::SELECTION_CONTENT, wordIndex); r = selectionHandleEvent (eventType, it, charPos, link, event, @@ -794,12 +808,12 @@ void Textblock::justifyLine (Line *line, int availWidth) diff = availWidth - lastLineWidth; if (diff > 0) { origSpaceSum = 0; - for (i = line->firstWord; i < line->lastWord - 1; i++) + for (i = line->firstWord; i < line->lastWord; i++) origSpaceSum += words->getRef(i)->origSpace; origSpaceCum = 0; lastEffSpaceDiffCum = 0; - for (i = line->firstWord; i < line->lastWord - 1; i++) { + for (i = line->firstWord; i < line->lastWord; i++) { origSpaceCum += words->getRef(i)->origSpace; if (origSpaceCum == 0) @@ -818,9 +832,9 @@ void Textblock::justifyLine (Line *line, int availWidth) } -void Textblock::addLine (int wordInd, bool newPar) +Textblock::Line *Textblock::addLine (int wordIndex, bool newPar) { - Line *lastLine, *plastLine; + Line *lastLine; //DBG_MSG (page, "wrap", 0, "Dw_page_add_line"); //DBG_MSG_START (page); @@ -833,29 +847,19 @@ void Textblock::addLine (int wordInd, bool newPar) lastLine = lines->getRef (lines->size () - 1); - if (lines->size () == 1) - plastLine = NULL; - else - plastLine = lines->getRef (lines->size () - 2); - - if (plastLine) { - /* second or more lines: copy values of last line */ - lastLine->top = - plastLine->top + plastLine->ascent + - plastLine->descent + plastLine->breakSpace; - lastLine->maxLineWidth = plastLine->maxLineWidth; - lastLine->maxWordMin = plastLine->maxWordMin; - lastLine->maxParMax = plastLine->maxParMax; - lastLine->parMin = plastLine->parMin; - lastLine->parMax = plastLine->parMax; - } else { - /* first line: initialize values */ + if (lines->size () == 1) { lastLine->top = 0; lastLine->maxLineWidth = line1OffsetEff; lastLine->maxWordMin = 0; lastLine->maxParMax = 0; - lastLine->parMin = line1OffsetEff; - lastLine->parMax = line1OffsetEff; + } else { + Line *prevLine = lines->getRef (lines->size () - 2); + + lastLine->top = prevLine->top + prevLine->boxAscent + + prevLine->boxDescent + prevLine->breakSpace; + lastLine->maxLineWidth = prevLine->maxLineWidth; + lastLine->maxWordMin = prevLine->maxWordMin; + lastLine->maxParMax = prevLine->maxParMax; } //DBG_OBJ_ARRSET_NUM (page, "lines.%d.top", page->num_lines - 1, @@ -871,9 +875,9 @@ void Textblock::addLine (int wordInd, bool newPar) //DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMax", page->num_lines - 1, // lastLine->parMax); - lastLine->firstWord = wordInd; - lastLine->ascent = 0; - lastLine->descent = 0; + lastLine->firstWord = wordIndex; + lastLine->boxAscent = lastLine->contentAscent = 0; + lastLine->boxDescent = lastLine->contentDescent = 0; lastLine->marginDescent = 0; lastLine->breakSpace = 0; lastLine->leftOffset = 0; @@ -881,9 +885,9 @@ void Textblock::addLine (int wordInd, bool newPar) lastLine->boxRight = 0; //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, - // lastLine->ascent); + // lastLine->boxAscent); //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1, - // lastLine->descent); + // lastLine->boxDescent); /* update values in line */ lastLine->maxLineWidth = misc::max (lastLine->maxLineWidth, lastLineWidth); @@ -898,6 +902,10 @@ void Textblock::addLine (int wordInd, bool newPar) //DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxParMax", page->num_lines - 1, // lastLine->maxParMax); + /* The following code looks questionable (especially since the values + * will be overwritten). In any case, line1OffsetEff is probably + * supposed to go into lastLinePar*, not lastLine->par*. + */ if (lines->size () > 1) { lastLine->parMin = 0; lastLine->parMax = 0; @@ -921,18 +929,19 @@ void Textblock::addLine (int wordInd, bool newPar) // lastLine->parMax); //DBG_MSG_END (page); + return lastLine; } /* - * This method is called in two cases: (i) when a word is added (by - * Dw_page_add_word), and (ii) when a page has to be (partially) - * rewrapped. It does word wrap, and adds new lines, if necesary. + * This method is called in two cases: (i) when a word is added + * (ii) when a page has to be (partially) rewrapped. It does word wrap, + * and adds new lines if necessary. */ void Textblock::wordWrap(int wordIndex) { Line *lastLine; - Word *word, *prevWord; - int availWidth, lastSpace, leftOffset; + Word *word; + int availWidth, lastSpace, leftOffset, len; bool newLine = false, newPar = false; core::Extremes wordExtremes; @@ -948,6 +957,17 @@ void Textblock::wordWrap(int wordIndex) availWidth = layout->getWidthViewport () - 10; word = words->getRef (wordIndex); + word->effSpace = word->origSpace; + + /* Test whether line1Offset can be used. */ + if (wordIndex == 0) { + if (ignoreLine1OffsetSometimes && + line1Offset + word->size.width > availWidth) { + line1OffsetEff = 0; + } else { + line1OffsetEff = line1Offset; + } + } if(word->content.type == dw::core::Content::FLOAT_REF) { @@ -955,10 +975,12 @@ void Textblock::wordWrap(int wordIndex) int y = allocation.y - containingBox->allocation.y + getStyle()->boxOffsetY() + (line ? line->top : 0); - int lineHeight = line ? line->ascent + line->descent : 0; + int lineHeight = line ? line->boxAscent + line->boxDescent : 0; containingBox->handleFloatInContainer(word->content.widget, misc::max(lines->size() - 1, 0), y, lastLineWidth, lineHeight); } + + if (lines->size () == 0) { //DBG_MSG (page, "wrap", 0, "first line"); @@ -966,72 +988,61 @@ void Textblock::wordWrap(int wordIndex) newPar = true; lastLine = NULL; } else { + Word *prevWord = words->getRef (wordIndex - 1); + lastLine = lines->getRef (lines->size () - 1); - if (lines->size () > 0) { - prevWord = words->getRef (wordIndex - 1); - if (prevWord->content.type == core::Content::BREAK) { - //DBG_MSG (page, "wrap", 0, "after a break"); - /* previous word is a break */ - newLine = true; - newPar = true; - } else if (word->style->whiteSpace - != core::style::WHITE_SPACE_NORMAL) { - //DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)", - // word->style->white_space); - newLine = false; - newPar = false; - } else { - if (lastLine->firstWord != wordIndex) { - /* Does new word fit into the last line? */ - //DBG_MSGF (page, "wrap", 0, - // "word %d (%s) fits? (%d + %d + %d <= %d)...", - // word_ind, a_Dw_content_html (&word->content), - // page->lastLine_width, prevWord->orig_space, - // word->size.width, availWidth); - newLine = (lastLineWidth + prevWord->origSpace + - word->size.width - > availWidth - - (lastLine->boxLeft + lastLine->boxRight)); - //DBG_MSGF (page, "wrap", 0, "... %s.", - // newLine ? "No" : "Yes"); - } - } + if (prevWord->content.type == core::Content::BREAK) { + //DBG_MSG (page, "wrap", 0, "after a break"); + /* previous word is a break */ + newLine = true; + newPar = true; + } else if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP || + word->style->whiteSpace == core::style::WHITE_SPACE_PRE) { + //DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)", + // word->style->white_space); + newLine = false; + newPar = false; + } else if (lastLine->firstWord != wordIndex) { + /* Does new word fit into the last line? */ + //DBG_MSGF (page, "wrap", 0, + // "word %d (%s) fits? (%d + %d + %d <= %d)...", + // word_ind, a_Dw_content_html (&word->content), + // page->lastLine_width, prevWord->orig_space, + // word->size.width, availWidth); + newLine = lastLineWidth + prevWord->origSpace + word->size.width > + availWidth - (lastLine->boxLeft + lastLine->boxRight); + //DBG_MSGF (page, "wrap", 0, "... %s.", + // newLine ? "No" : "Yes"); } } - /* Has sometimes the wrong value. */ - word->effSpace = word->origSpace; - //DBG_OBJ_ARRSET_NUM (page,"words.%d.eff_space", word_ind, word->eff_space); - - /* Test, whether line1_offset can be used. */ - if (wordIndex == 0) { - if (ignoreLine1OffsetSometimes) { - if (line1Offset + word->size.width > availWidth) - line1OffsetEff = 0; - else - line1OffsetEff = line1Offset; - } else - line1OffsetEff = line1Offset; + if (newLine) { + if (word->style->textAlign == core::style::TEXT_ALIGN_JUSTIFY && + lastLine != NULL && !newPar) { + justifyLine (lastLine, availWidth); + } + lastLine = addLine (wordIndex, newPar); } - if (lastLine != NULL && newLine && !newPar && - word->style->textAlign == core::style::TEXT_ALIGN_JUSTIFY) - justifyLine (lastLine, availWidth); + lastLine->lastWord = wordIndex; + lastLine->boxAscent = misc::max (lastLine->boxAscent, word->size.ascent); + lastLine->boxDescent = misc::max (lastLine->boxDescent, word->size.descent); - if (newLine) { - addLine (wordIndex, newPar); - lastLine = lines->getRef (lines->size () - 1); - } + len = word->style->font->ascent; + if (word->style->valign == core::style::VALIGN_SUPER) + len += len / 2; + lastLine->contentAscent = misc::max (lastLine->contentAscent, len); - lastLine->lastWord = wordIndex + 1; - lastLine->ascent = misc::max (lastLine->ascent, (int) word->size.ascent); - lastLine->descent = misc::max (lastLine->descent, (int) word->size.descent); + len = word->style->font->descent; + if (word->style->valign == core::style::VALIGN_SUB) + len += word->style->font->ascent / 3; + lastLine->contentDescent = misc::max (lastLine->contentDescent, len); //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, - // lastLine->ascent); + // lastLine->boxAscent); //DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1, - // lastLine->descent); + // lastLine->boxDescent); if (word->content.type == core::Content::WIDGET) { lastLine->marginDescent = @@ -1049,49 +1060,52 @@ void Textblock::wordWrap(int wordIndex) /* Here, we know already what the break and the bottom margin * contributed to the space before this line. */ - lastLine->ascent = - misc::max (lastLine->ascent, + lastLine->boxAscent = + misc::max (lastLine->boxAscent, word->size.ascent + word->content.widget->getStyle()->margin.top); //DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1, - // lastLine->ascent); + // lastLine->boxAscent); } - } else + } else { lastLine->marginDescent = - misc::max (lastLine->marginDescent, lastLine->descent); + misc::max (lastLine->marginDescent, lastLine->boxDescent); - getWordExtremes (word, &wordExtremes); - lastSpace = (wordIndex > 0) ? words->getRef(wordIndex - 1)->origSpace : 0; + if (word->content.type == core::Content::BREAK) + lastLine->breakSpace = + misc::max (word->content.breakSpace, + lastLine->marginDescent - lastLine->boxDescent, + lastLine->breakSpace); + } - if (word->content.type == core::Content::BREAK) - lastLine->breakSpace = - misc::max (word->content.breakSpace, - lastLine->marginDescent - lastLine->descent, - lastLine->breakSpace); + lastSpace = (wordIndex > 0) ? words->getRef(wordIndex - 1)->origSpace : 0; - lastLineWidth += word->size.width; if (!newLine) lastLineWidth += lastSpace; - - lastLineParMin += wordExtremes.maxWidth; - lastLineParMax += wordExtremes.maxWidth; if (!newPar) { lastLineParMin += lastSpace; lastLineParMax += lastSpace; } - if (word->style->whiteSpace != core::style::WHITE_SPACE_NORMAL) { + lastLineWidth += word->size.width; + + getWordExtremes (word, &wordExtremes); + lastLineParMin += wordExtremes.maxWidth; /* Why maxWidth? */ + lastLineParMax += wordExtremes.maxWidth; + + if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP || + word->style->whiteSpace == core::style::WHITE_SPACE_PRE) { lastLine->parMin += wordExtremes.minWidth + lastSpace; /* This may also increase the accumulated minimum word width. */ lastLine->maxWordMin = misc::max (lastLine->maxWordMin, lastLine->parMin); /* NOTE: Most code relies on that all values of nowrap are equal for all * words within one line. */ - } else - /* Simple case. */ + } else { lastLine->maxWordMin = misc::max (lastLine->maxWordMin, wordExtremes.minWidth); + } //DBG_OBJ_SET_NUM(page, "lastLine_par_min", page->lastLine_par_min); //DBG_OBJ_SET_NUM(page, "lastLine_par_max", page->lastLine_par_max); @@ -1102,8 +1116,9 @@ void Textblock::wordWrap(int wordIndex) //DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1, // lastLine->max_word_min); - /* Finally, justify the line. Breaks are ignored, since the HTML - * parser sometimes assignes the wrong style to them. (TODO: ) */ + /* Align the line. + * \todo Use block's style instead once paragraphs become proper blocks. + */ if (word->content.type != core::Content::BREAK) { switch (word->style->textAlign) { case core::style::TEXT_ALIGN_LEFT: @@ -1112,15 +1127,12 @@ void Textblock::wordWrap(int wordIndex) * future) */ leftOffset = 0; break; - case core::style::TEXT_ALIGN_RIGHT: leftOffset = availWidth - lastLineWidth; break; - case core::style::TEXT_ALIGN_CENTER: leftOffset = (availWidth - lastLineWidth) / 2; break; - default: /* compiler happiness */ leftOffset = 0; @@ -1130,22 +1142,22 @@ void Textblock::wordWrap(int wordIndex) if (leftOffset < 0) leftOffset = 0; - if (listItem && lastLine == lines->getRef (0)) { + if (hasListitemValue && lastLine == lines->getRef (0)) { /* List item markers are always on the left. */ lastLine->leftOffset = 0; words->getRef(0)->effSpace = words->getRef(0)->origSpace + leftOffset; //DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", 0, // page->words[0].eff_space); - } else + } else { lastLine->leftOffset = leftOffset; - + } + int y = allocation.y - containingBox->allocation.y + getStyle()->boxOffsetX () + lastLine->top; lastLine->boxLeft = calcLeftFloatBorder(y, this); lastLine->boxRight = calcRightFloatBorder(y, this); } - mustQueueResize = true; //DBG_MSG_END (page); @@ -1160,6 +1172,7 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) { core::Requisition requisition; int availWidth, availAscent, availDescent; + core::style::Style *wstyle = widget->getStyle(); /* We ignore line1_offset[_eff]. */ availWidth = this->availWidth - getStyle()->boxDiffWidth () - innerPadding; @@ -1171,39 +1184,37 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size) widget->setAscent (availAscent); widget->setDescent (availDescent); widget->sizeRequest (size); - size->ascent -= widget->getStyle()->margin.top; - size->descent -= widget->getStyle()->margin.bottom; +// size->ascent -= wstyle->margin.top; +// size->descent -= wstyle->margin.bottom; } else { /* TODO: Use margin.{top|bottom} here, like above. * (No harm for the next future.) */ - if (widget->getStyle()->width == core::style::LENGTH_AUTO || - widget->getStyle()->height == core::style::LENGTH_AUTO) + if (wstyle->width == core::style::LENGTH_AUTO || + wstyle->height == core::style::LENGTH_AUTO) widget->sizeRequest (&requisition); - if (widget->getStyle()->width == core::style::LENGTH_AUTO) + if (wstyle->width == core::style::LENGTH_AUTO) size->width = requisition.width; - else if (core::style::isAbsLength (widget->getStyle()->width)) + else if (core::style::isAbsLength (wstyle->width)) /* Fixed lengths are only applied to the content, so we have to * add padding, border and margin. */ - size->width = core::style::absLengthVal (widget->getStyle()->width) - + widget->getStyle()->boxDiffWidth (); + size->width = core::style::absLengthVal (wstyle->width) + + wstyle->boxDiffWidth (); else - size->width = - (int) (core::style::perLengthVal (widget->getStyle()->width) - * availWidth); + size->width = (int) (core::style::perLengthVal (wstyle->width) + * availWidth); - if (widget->getStyle()->height == core::style::LENGTH_AUTO) { + if (wstyle->height == core::style::LENGTH_AUTO) { size->ascent = requisition.ascent; size->descent = requisition.descent; - } else if (core::style::isAbsLength (widget->getStyle()->height)) { + } else if (core::style::isAbsLength (wstyle->height)) { /* Fixed lengths are only applied to the content, so we have to * add padding, border and margin. */ - size->ascent = - core::style::absLengthVal (widget->getStyle()->height) - + widget->getStyle()->boxDiffHeight (); + size->ascent = core::style::absLengthVal (wstyle->height) + + wstyle->boxDiffHeight (); size->descent = 0; } else { - double len = core::style::perLengthVal (widget->getStyle()->height); + double len = core::style::perLengthVal (wstyle->height); size->ascent = (int) (len * availAscent); size->descent = (int) (len * availDescent); } @@ -1238,8 +1249,7 @@ void Textblock::rewrap () //DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines); //DBG_OBJ_SET_NUM(page, "lastLine_width", page->lastLine_width); - /* In the word list, we start at the last word, plus one (see definition - * of last_word), in the line before. */ + /* In the word list, start at the last word plus one in the line before. */ if (wrapRef > 0) { /* Note: In this case, Dw_page_real_word_wrap will immediately find * the need to rewrap the line, since we start with the last one (plus @@ -1250,11 +1260,11 @@ void Textblock::rewrap () lastLineParMin = lastLine->parMin; lastLineParMax = lastLine->parMax; - wordIndex = lastLine->lastWord; - for (i = lastLine->firstWord; i < lastLine->lastWord - 1; i++) + wordIndex = lastLine->lastWord + 1; + for (i = lastLine->firstWord; i < lastLine->lastWord; i++) lastLineWidth += (words->getRef(i)->size.width + words->getRef(i)->origSpace); - lastLineWidth += words->getRef(lastLine->lastWord - 1)->size.width; + lastLineWidth += words->getRef(lastLine->lastWord)->size.width; } else { lastLineParMin = 0; lastLineParMax = 0; @@ -1298,6 +1308,156 @@ void Textblock::rewrap () } /* + * Draw the decorations on a word. + */ +void Textblock::decorateText(core::View *view, core::style::Style *style, + core::style::Color::Shading shading, + int x, int yBase, int width) +{ + int y; + + if (style->textDecoration & core::style::TEXT_DECORATION_UNDERLINE) { + y = yBase + 1; + view->drawLine (style->color, shading, x, y, x + width - 1, y); + } + if (style->textDecoration & core::style::TEXT_DECORATION_OVERLINE) { + y = yBase - style->font->ascent + 1; + view->drawLine (style->color, shading, x, y, x + width - 1, y); + } + if (style->textDecoration & core::style::TEXT_DECORATION_LINE_THROUGH) { + int height = 1 + style->font->xHeight / 10; + + y = yBase + (style->font->descent - style->font->ascent) / 2; + view->drawRectangle (style->color, shading, true, x, y, width, height); + } +} + +/* + * Draw a word of text. + */ +void Textblock::drawText(int wordIndex, core::View *view,core::Rectangle *area, + int xWidget, int yWidgetBase) +{ + Word *word = words->getRef(wordIndex); + int xWorld = allocation.x + xWidget; + core::style::Style *style = word->style; + int yWorldBase; + + /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */ + if (style->valign == core::style::VALIGN_SUB) + yWidgetBase += style->font->ascent / 3; + else if (style->valign == core::style::VALIGN_SUPER) { + yWidgetBase -= style->font->ascent / 2; + } + yWorldBase = yWidgetBase + allocation.y; + + view->drawText (style->font, style->color, + core::style::Color::SHADING_NORMAL, xWorld, yWorldBase, + word->content.text, strlen (word->content.text)); + + if (style->textDecoration) + decorateText(view, style, core::style::Color::SHADING_NORMAL, xWorld, + yWorldBase, word->size.width); + + for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { + if (hlStart[layer].index <= wordIndex && + hlEnd[layer].index >= wordIndex) { + const int wordLen = strlen (word->content.text); + int xStart, width; + int firstCharIdx = 0; + int lastCharIdx = wordLen; + + if (wordIndex == hlStart[layer].index) + firstCharIdx = misc::min (hlStart[layer].nChar, wordLen); + + if (wordIndex == hlEnd[layer].index) + lastCharIdx = misc::min (hlEnd[layer].nChar, wordLen); + + xStart = xWorld; + if (firstCharIdx) + xStart += layout->textWidth (style->font, word->content.text, + firstCharIdx); + if (firstCharIdx == 0 && lastCharIdx == wordLen) + width = word->size.width; + else + width = layout->textWidth (style->font, + word->content.text + firstCharIdx, + lastCharIdx - firstCharIdx); + if (width > 0) { + /* Highlight text */ + core::style::Color *wordBgColor; + + if (!(wordBgColor = style->backgroundColor)) + wordBgColor = getBgColor(); + + /* Draw background for highlighted text. */ + view->drawRectangle ( + wordBgColor, core::style::Color::SHADING_INVERSE, true, xStart, + yWorldBase - style->font->ascent, width, + style->font->ascent + style->font->descent); + + /* Highlight the text. */ + view->drawText (style->font, style->color, + core::style::Color::SHADING_INVERSE, xStart, + yWorldBase, word->content.text + firstCharIdx, + lastCharIdx - firstCharIdx); + + if (style->textDecoration) + decorateText(view, style, core::style::Color::SHADING_INVERSE, + xStart, yWorldBase, width); + } + } + } +} + +/* + * Draw a space. + */ +void Textblock::drawSpace(int wordIndex, core::View *view, + core::Rectangle *area, int xWidget, int yWidgetBase) +{ + Word *word = words->getRef(wordIndex); + int xWorld = allocation.x + xWidget; + int yWorldBase; + core::style::Style *style = word->spaceStyle; + bool highlight = false; + + /* Adjust the space baseline if it is <SUP>-ed or <SUB>-ed */ + if (style->valign == core::style::VALIGN_SUB) + yWidgetBase += style->font->ascent / 3; + else if (style->valign == core::style::VALIGN_SUPER) { + yWidgetBase -= style->font->ascent / 2; + } + yWorldBase = allocation.y + yWidgetBase; + + for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { + if (hlStart[layer].index <= wordIndex && + hlEnd[layer].index > wordIndex) { + highlight = true; + break; + } + } + if (highlight) { + core::style::Color *spaceBgColor; + + if (!(spaceBgColor = style->backgroundColor)) + spaceBgColor = getBgColor(); + + view->drawRectangle ( + spaceBgColor, core::style::Color::SHADING_INVERSE, true, xWorld, + yWorldBase - style->font->ascent, word->effSpace, + style->font->ascent + style->font->descent); + } + if (style->textDecoration) { + core::style::Color::Shading shading = highlight ? + core::style::Color::SHADING_INVERSE : + core::style::Color::SHADING_NORMAL; + + decorateText(view, style, shading, xWorld, yWorldBase, word->effSpace); + } +} + +/* * Paint a line * - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!) * - area is used always (ev. set it to event->area) @@ -1305,219 +1465,56 @@ void Textblock::rewrap () */ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) { - Word *word; - int wordIndex; - int xWidget, yWidget, xWorld, yWorld, yWorldBase; - int startHL, widthHL; - int wordLen; - int diff, effHLStart, effHLEnd, layer; - core::Widget *child; - core::Rectangle childArea; - core::style::Color *color, *thisBgColor, *wordBgColor; + int xWidget = lineXOffsetWidget(line); + int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; /* Here's an idea on how to optimize this routine to minimize the number - * of calls to gdk_draw_string: + * of drawing calls: * * Copy the text from the words into a buffer, adding a new word * only if: the attributes match, and the spacing is either zero or * equal to the width of ' '. In the latter case, copy a " " into * the buffer. Then draw the buffer. */ - thisBgColor = getBgColor (); + for (int wordIndex = line->firstWord; + wordIndex <= line->lastWord && xWidget < area->x + area->width; + wordIndex++) { + Word *word = words->getRef(wordIndex); - xWidget = lineXOffsetWidget(line); - xWorld = allocation.x + xWidget; - yWidget = lineYOffsetWidget (line); - yWorld = allocation.y + yWidget; - yWorldBase = yWorld + line->ascent; + if (xWidget + word->size.width + word->effSpace >= area->x) { + if (word->content.type == core::Content::TEXT || + word->content.type == core::Content::WIDGET) { - for (wordIndex = line->firstWord; wordIndex < line->lastWord; - wordIndex++) { - word = words->getRef(wordIndex); - diff = 0; - color = word->style->color; - - //DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.x", wordIndex, - // xWidget); - //DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.y", wordIndex, - // yWidget); - - switch (word->content.type) { - case core::Content::TEXT: - if (word->style->backgroundColor) - wordBgColor = word->style->backgroundColor; - else - wordBgColor = thisBgColor; - - /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */ - if (word->style->valign == core::style::VALIGN_SUB) - diff = word->size.ascent / 2; - else if (word->style->valign == core::style::VALIGN_SUPER) - diff -= word->size.ascent / 3; - - /* Draw background (color, image), when given. */ - if (word->style->hasBackground () && word->size.width > 0) - drawBox (view, word->style, area, - xWidget, yWidget + line->ascent - word->size.ascent, - word->size.width, word->size.ascent + word->size.descent, - false); - - /* Draw space background (color, image), when given. */ - if (word->spaceStyle->hasBackground () && word->effSpace > 0) - drawBox (view, word->spaceStyle, area, - xWidget + word->size.width, - yWidget + line->ascent - word->size.ascent, - word->effSpace, word->size.ascent + word->size.descent, - false); - view->drawText (word->style->font, color, - core::style::Color::SHADING_NORMAL, - xWorld, yWorldBase + diff, - word->content.text, strlen (word->content.text)); - - /* underline */ - if (word->style->textDecoration & - core::style::TEXT_DECORATION_UNDERLINE) - view->drawLine (color, core::style::Color::SHADING_NORMAL, - xWorld, yWorldBase + 1 + diff, - xWorld + word->size.width - 1, - yWorldBase + 1 + diff); - if (wordIndex + 1 < line->lastWord && - (word->spaceStyle->textDecoration - & core::style::TEXT_DECORATION_UNDERLINE)) - view->drawLine (word->spaceStyle->color, - core::style::Color::SHADING_NORMAL, - xWorld + word->size.width, - yWorldBase + 1 + diff, - xWorld + word->size.width + word->effSpace - 1, - yWorldBase + 1 + diff); - - /* strike-through */ - if (word->style->textDecoration - & core::style::TEXT_DECORATION_LINE_THROUGH) - view->drawLine (color, core::style::Color::SHADING_NORMAL, - xWorld, - yWorldBase - word->size.ascent / 2 + diff, - xWorld + word->size.width - 1, - yWorldBase - word->size.ascent / 2 + diff); - if (wordIndex + 1 < line->lastWord && - (word->spaceStyle->textDecoration - & core::style::TEXT_DECORATION_LINE_THROUGH)) - view->drawLine (word->spaceStyle->color, - core::style::Color::SHADING_NORMAL, - xWorld + word->size.width, - yWorldBase - word->size.ascent / 2 + diff, - xWorld + word->size.width + word->effSpace - 1, - yWorldBase - word->size.ascent / 2 + diff); - - for (layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { - if (hlStart[layer].index <= wordIndex && - hlEnd[layer].index >= wordIndex) { - - wordLen = strlen (word->content.text); - effHLEnd = misc::min (wordLen, hlEnd[layer].nChar); - effHLStart = 0; - if (wordIndex == hlStart[layer].index) - effHLStart = misc::min (hlStart[layer].nChar, wordLen); - - effHLEnd = wordLen; - if (wordIndex == hlEnd[layer].index) - effHLEnd = misc::min (hlEnd[layer].nChar, wordLen); - - startHL = xWorld + layout->textWidth (word->style->font, - word->content.text, - effHLStart); - widthHL = - layout->textWidth (word->style->font, - word->content.text + effHLStart, - effHLEnd - effHLStart); - - // If the space after this word highlighted, and this word - // is not the last one in this line, highlight also the - // space. - /** \todo This should also be done with spaces after non-text - * words, but this is not yet defined very well. */ - if (wordIndex < hlEnd[layer].index && - wordIndex < words->size () && - wordIndex != line->lastWord - 1) - widthHL += word->effSpace; - - - if (widthHL != 0) { - /* Draw background for highlighted text. */ - view->drawRectangle (wordBgColor, - core::style::Color::SHADING_INVERSE, - true, startHL, - yWorldBase - word->size.ascent, - widthHL, - word->size.ascent + word->size.descent); - - /* Highlight the text. */ - view->drawText (word->style->font, - color, core::style::Color::SHADING_INVERSE, - startHL, yWorldBase + diff, - word->content.text + effHLStart, - effHLEnd - effHLStart); - - /* underline and strike-through */ - if (word->style->textDecoration - & core::style::TEXT_DECORATION_UNDERLINE) - view->drawLine (color, - core::style::Color::SHADING_INVERSE, - startHL, yWorldBase + 1 + diff, - startHL + widthHL - 1, - yWorldBase + 1 + diff); - if (word->style->textDecoration - & core::style::TEXT_DECORATION_LINE_THROUGH) - view->drawLine (color, - core::style::Color::SHADING_INVERSE, - startHL, - yWorldBase - word->size.ascent / 2 + diff, - startHL + widthHL - 1, - yWorldBase - word->size.ascent / 2 - + diff); + if (word->size.width > 0) { + if (word->style->hasBackground ()) { + drawBox (view, word->style, area, xWidget, + yWidgetBase - line->boxAscent, word->size.width, + line->boxAscent + line->boxDescent, false); } - } - } - break; - - case core::Content::WIDGET: - child = word->content.widget; - if (child->intersects (area, &childArea)) - child->draw (view, &childArea); - break; + if (word->content.type == core::Content::WIDGET) { + core::Widget *child = word->content.widget; + core::Rectangle childArea; - case core::Content::ANCHOR: case core::Content::BREAK: - /* nothing - an anchor/break isn't seen */ - /* - * Historical note: - * > BUG: sometimes anchors have x_space; - * > we subtract that just in case --EG - * This is inconsistent with other parts of the code, so it should - * be tried to prevent this earlier.--SG - */ - /* - * x_viewport -= word->size.width + word->eff_space; - * xWidget -= word->size.width + word->eff_space; - */ -#if 0 - /* Useful for testing: draw breaks. */ - if (word->content.type == DW_CONTENT_BREAK) - gdk_draw_rectangle (window, color, TRUE, - p_Dw_widget_xWorld_to_viewport (widget, - widget->allocation.x + - Dw_page_line_total_x_offset(page, line)), - y_viewport_base + line->descent, - DW_WIDGET_CONTENT_WIDTH(widget), - word->content.break_space); -#endif - break; + if (child->intersects (area, &childArea)) + child->draw (view, &childArea); + } else { + drawText(wordIndex, view, area, xWidget, yWidgetBase); + } + } + if (word->effSpace > 0 && wordIndex < line->lastWord && + words->getRef(wordIndex + 1)->content.type != + core::Content::BREAK) { + if (word->spaceStyle->hasBackground ()) + drawBox (view, word->spaceStyle, area, + xWidget + word->size.width, + yWidgetBase - line->boxAscent, word->effSpace, + line->boxAscent + line->boxDescent, false); + drawSpace(wordIndex, view, area, xWidget + word->size.width, + yWidgetBase); + } - default: - fprintf (stderr, "BUG!!! at (%d, %d).\n", xWorld, yWorldBase + diff); - break; + } } - - xWorld += word->size.width + word->effSpace; xWidget += word->size.width + word->effSpace; } } @@ -1534,19 +1531,19 @@ int Textblock::findLineIndex (int y) while ( step > 1 ) { index = low + step; if (index <= maxIndex && - lineYOffsetWidgetI (index) < y) + lineYOffsetWidgetI (index) <= y) low = index; step = (step + 1) >> 1; } - if (low < maxIndex && lineYOffsetWidgetI (low + 1) < y) + if (low < maxIndex && lineYOffsetWidgetI (low + 1) <= y) low++; /* * This new routine returns the line number between (top) and - * (top + size.ascent + size.descent + break_space): the space + * (top + size.ascent + size.descent + breakSpace): the space * _below_ the line is considered part of the line. Old routine - * returned line number between (top - previous_line->break_space) + * returned line number between (top - previous_line->breakSpace) * and (top + size.ascent + size.descent): the space _above_ the * line was considered part of the line. This is important for * Dw_page_find_link() --EG @@ -1562,13 +1559,13 @@ int Textblock::findLineOfWord (int wordIndex) { int high = lines->size () - 1, index, low = 0; - //g_return_val_if_fail (word_index >= 0, -1); - //g_return_val_if_fail (word_index < page->num_words, -1); + if (wordIndex < 0 || wordIndex >= words->size ()) + return -1; while (true) { index = (low + high) / 2; if (wordIndex >= lines->getRef(index)->firstWord) { - if (wordIndex < lines->getRef(index)->lastWord) + if (wordIndex <= lines->getRef(index)->lastWord) return index; else low = index + 1; @@ -1580,29 +1577,36 @@ int Textblock::findLineOfWord (int wordIndex) /** * \brief Find the index of the word, or -1. */ -int Textblock::findWord (int x, int y) +Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) { int lineIndex, wordIndex; - int xCursor, lastXCursor; + int xCursor, lastXCursor, yWidgetBase; Line *line; Word *word; + *inSpace = false; + if ((lineIndex = findLineIndex (y)) >= lines->size ()) - return -1; + return NULL; line = lines->getRef (lineIndex); - if (lineYOffsetWidget (line) + line->ascent + line->descent <= y) - return -1; + yWidgetBase = lineYOffsetWidget (line) + line->boxAscent; + if (yWidgetBase + line->boxDescent <= y) + return NULL; xCursor = lineXOffsetWidget (line); - for (wordIndex = line->firstWord; wordIndex < line->lastWord; wordIndex++) { + for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { word = words->getRef (wordIndex); lastXCursor = xCursor; xCursor += word->size.width + word->effSpace; - if (lastXCursor <= x && xCursor > x) - return wordIndex; + if (lastXCursor <= x && xCursor > x && + y > yWidgetBase - word->size.ascent && + y <= yWidgetBase + word->size.descent) { + *inSpace = x >= xCursor - word->effSpace; + return word; + } } - return -1; + return NULL; } void Textblock::draw (core::View *view, core::Rectangle *area) @@ -1670,37 +1674,67 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent, /** * Calculate the size of a text word. */ -void Textblock::calcTextSize (const char *text, core::style::Style *style, +void Textblock::calcTextSize (const char *text, size_t len, + core::style::Style *style, core::Requisition *size) { - size->width = - layout->textWidth (style->font, text, strlen (text)); + size->width = layout->textWidth (style->font, text, len); size->ascent = style->font->ascent; size->descent = style->font->descent; + /* + * For 'normal' line height, just use ascent and descent from font. + * For absolute/percentage, line height is relative to font size, which + * is (irritatingly) smaller than ascent+descent. + */ + if (style->lineHeight != core::style::LENGTH_AUTO) { + int height, leading; + float factor = style->font->size; + + factor /= (style->font->ascent + style->font->descent); + + size->ascent = size->ascent * factor + 0.5; + size->descent = size->descent * factor + 0.5; + + /* TODO: The containing block's line-height property gives a minimum + * height for the line boxes. (Even when it's set to 'normal', i.e., + * AUTO? Apparently.) Once all block elements make Textblocks or + * something, this can be handled. + */ + if (core::style::isAbsLength (style->lineHeight)) + height = core::style::absLengthVal(style->lineHeight); + else + height = core::style::perLengthVal(style->lineHeight) * + style->font->size; + leading = height - style->font->size; + + size->ascent += leading / 2; + size->descent += leading - (leading / 2); + } + /* In case of a sub or super script we increase the word's height and * potentially the line's height. */ if (style->valign == core::style::VALIGN_SUB) - size->descent += (size->ascent / 2); + size->descent += (style->font->ascent / 3); else if (style->valign == core::style::VALIGN_SUPER) - size->ascent += (size->ascent / 3); + size->ascent += (style->font->ascent / 2); } /** - * Add a word to the page structure. Stashes the argument pointer in - * the page data structure so that it will be deallocated on destroy. + * Add a word to the page structure. */ -void Textblock::addText (const char *text, core::style::Style *style) +void Textblock::addText (const char *text, size_t len, + core::style::Style *style) { Word *word; core::Requisition size; - calcTextSize (text, style, &size); + calcTextSize (text, len, style, &size); word = addWord (size.width, size.ascent, size.descent, style); word->content.type = core::Content::TEXT; - word->content.text = layout->textZone->strdup(text); + word->content.text = layout->textZone->strndup(text, len); //DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", page->num_words - 1, // word->content.text); @@ -1717,7 +1751,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) core::Requisition size; /* We first assign -1 as parent_ref, since the call of widget->size_request - * will otherwise let this DwPage be rewrapped from the beginning. + * will otherwise let this Textblock be rewrapped from the beginning. * (parent_ref is actually undefined, but likely has the value 0.) At the, * end of this function, the correct value is assigned. */ widget->parentRef = -1; @@ -1749,7 +1783,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) /** - * Add an anchor to the page. "name" is copied, so no strdup is neccessary for + * Add an anchor to the page. "name" is copied, so no strdup is necessary for * the caller. * * Return true on success, and false, when this anchor had already been @@ -1757,11 +1791,10 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) */ bool Textblock::addAnchor (const char *name, core::style::Style *style) { - Word *word; char *copy; int y; - // Since an anchor does not take any space, it is safe to call + // Since an anchor does not take any space, it is safe to call // addAnchor already here. if (wasAllocated ()) { if (lines->size () == 0) @@ -1774,15 +1807,17 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style) if (copy == NULL) /** - * \todo It may be neccessary for future uses to save the anchor in + * \todo It may be necessary for future uses to save the anchor in * some way, e.g. when parts of the widget tree change. */ return false; else { - word = addWord (0, 0, 0, style); - word->content.type = core::Content::ANCHOR; - word->content.anchor = copy; - wordWrap (words->size () - 1); + Anchor *anchor; + + anchors->increase(); + anchor = anchors->getRef(anchors->size() - 1); + anchor->name = copy; + anchor->wordIndex = words->size(); return true; } } @@ -1793,22 +1828,15 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style) */ void Textblock::addSpace (core::style::Style *style) { - int nl, nw; - int space; + int wordIndex = words->size () - 1; - nl = lines->size () - 1; - if (nl >= 0) { - nw = words->size () - 1; - if (nw >= 0) { - /* TODO: remove this test case */ - //if (page->words[nw].orig_space != 0) { - // _MSG(" a_Dw_page_add_space:: already existing space!!!\n"); - //} + if (wordIndex >= 0) { + Word *word = words->getRef(wordIndex); - space = style->font->spaceWidth; - words->getRef(nw)->origSpace = space; - words->getRef(nw)->effSpace = space; - words->getRef(nw)->content.space = true; + if (!word->content.space) { + word->content.space = true; + word->effSpace = word->origSpace = style->font->spaceWidth + + style->wordSpacing; //DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", nw, // page->words[nw].orig_space); @@ -1816,9 +1844,8 @@ void Textblock::addSpace (core::style::Style *style) // page->words[nw].eff_space); //DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", nw, // page->words[nw].content.space); - - words->getRef(nw)->spaceStyle->unref (); - words->getRef(nw)->spaceStyle = style; + word->spaceStyle->unref (); + word->spaceStyle = style; style->ref (); } } @@ -1830,45 +1857,41 @@ void Textblock::addSpace (core::style::Style *style) */ void Textblock::addParbreak (int space, core::style::Style *style) { - Word *word, *word2 = NULL; // Latter for compiler happiness, search! - bool isfirst; - Widget *widget; - int lineno; + Word *word; /* A break may not be the first word of a page, or directly after the bullet/number (which is the first word) in a list item. (See also comment in Dw_page_size_request.) */ if (words->size () == 0 || - (listItem && words->size () == 1)) { + (hasListitemValue && words->size () == 1)) { /* This is a bit hackish: If a break is added as the first/second word of a page, and the parent widget is also a - DwPage, and there is a break before -- this is the case when + Textblock, and there is a break before -- this is the case when a widget is used as a text box (lists, blockquotes, list items etc) -- then we simply adjust the break before, in a way that the space is in any case visible. */ + Widget *widget; - /* Find the widget where to adjust the break_space. */ + /* Find the widget where to adjust the breakSpace. */ for (widget = this; widget->getParent() && widget->getParent()->instanceOf (Textblock::CLASS_ID); widget = widget->getParent ()) { Textblock *textblock2 = (Textblock*)widget->getParent (); - if (textblock2->listItem) - isfirst = (textblock2->words->get(1).content.type - == core::Content::WIDGET - && textblock2->words->get(1).content.widget == widget); - else - isfirst = (textblock2->words->get(0).content.type - == core::Content::WIDGET - && textblock2->words->get(0).content.widget == widget); + int index = textblock2->hasListitemValue ? 1 : 0; + bool isfirst = (textblock2->words->getRef(index)->content.type + == core::Content::WIDGET + && textblock2->words->getRef(index)->content.widget + == widget); if (!isfirst) { /* The page we searched for has been found. */ + Word *word2; // ABC - lineno = widget->parentRef >> 3; + int lineno = widget->parentRef >> 3; if (lineno > 0 && (word2 = textblock2->words->getRef(textblock2->lines - ->get(lineno - 1).firstWord)) && + ->getRef(lineno - 1)->firstWord)) && word2->content.type == core::Content::BREAK) { if (word2->content.breakSpace < space) { word2->content.breakSpace = space; @@ -1897,7 +1920,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) misc::max (word->content.breakSpace, space); lastLine->breakSpace = misc::max (word->content.breakSpace, - lastLine->marginDescent - lastLine->descent, + lastLine->marginDescent - lastLine->boxDescent, lastLine->breakSpace); return; } @@ -1916,7 +1939,7 @@ void Textblock::addLinebreak (core::style::Style *style) Word *word; if (words->size () == 0 || - words->get(words->size () - 1).content.type == core::Content::BREAK) + words->getRef(words->size () - 1)->content.type == core::Content::BREAK) // An <BR> in an empty line gets the height of the current font // (why would someone else place it here?), ... word = addWord (0, style->font->ascent, style->font->descent, style); @@ -1926,7 +1949,6 @@ void Textblock::addLinebreak (core::style::Style *style) word->content.type = core::Content::BREAK; word->content.breakSpace = 0; - word->style = style; wordWrap (words->size () - 1); } @@ -1949,9 +1971,9 @@ void Textblock::addFloatIntoGenerator (core::Widget *widget, core::style::Style /** * \brief Search recursively through widget. - * + * * This is an optimized version of the general - * dw::core::Widget::getWidgetAtPoint method. + * dw::core::Widget::getWidgetAtPoint method. */ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) { @@ -1973,7 +1995,7 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) line = lines->getRef (lineIndex); - for (wordIndex = line->firstWord; wordIndex < line->lastWord; wordIndex++) { + for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { Word *word = words->getRef (wordIndex); if (word->content.type == core::Content::WIDGET) { @@ -1996,19 +2018,16 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) */ void Textblock::handOverBreak (core::style::Style *style) { - #if 0 - MISSING - DwPageLine *last_line; - DwWidget *parent; - - if (page->num_lines == 0) - return; + if (lines->size() > 0) { + Widget *parent; + Line *lastLine = lines->getRef (lines->size () - 1); - last_line = &page->lines[page->num_lines - 1]; - if (last_line->break_space != 0 && - (parent = DW_WIDGET(page)->parent) && DW_IS_PAGE (parent)) - a_Dw_page_add_parbreak (DW_PAGE (parent), last_line->break_space, style); -#endif + if (lastLine->breakSpace != 0 && (parent = getParent()) && + parent->instanceOf (Textblock::CLASS_ID)) { + Textblock *textblock2 = (Textblock*) parent; + textblock2->addParbreak(lastLine->breakSpace, style); + } + } } /* @@ -2039,10 +2058,10 @@ void Textblock::changeLinkColor (int link, int newColor) for (int lineIndex = 0; lineIndex < lines->size(); lineIndex++) { bool changed = false; Line *line = lines->getRef (lineIndex); - int wordIndex; + int wordIdx; - for (wordIndex = line->firstWord;wordIndex < line->lastWord;wordIndex++){ - Word *word = words->getRef(wordIndex); + for (wordIdx = line->firstWord; wordIdx <= line->lastWord; wordIdx++){ + Word *word = words->getRef(wordIdx); if (word->style->x_link == link) { core::style::StyleAttrs styleAttrs; @@ -2051,14 +2070,14 @@ void Textblock::changeLinkColor (int link, int newColor) case core::Content::TEXT: { core::style::Style *old_style = word->style; styleAttrs = *old_style; - styleAttrs.color = core::style::Color::createSimple (layout, - newColor); + styleAttrs.color = core::style::Color::create (layout, + newColor); word->style = core::style::Style::create (layout, &styleAttrs); old_style->unref(); old_style = word->spaceStyle; styleAttrs = *old_style; - styleAttrs.color = core::style::Color::createSimple (layout, - newColor); + styleAttrs.color = core::style::Color::create (layout, + newColor); word->spaceStyle = core::style::Style::create(layout, &styleAttrs); old_style->unref(); @@ -2067,10 +2086,10 @@ void Textblock::changeLinkColor (int link, int newColor) case core::Content::WIDGET: { core::Widget *widget = word->content.widget; styleAttrs = *widget->getStyle(); - styleAttrs.color = core::style::Color::createSimple (layout, - newColor); + styleAttrs.color = core::style::Color::create (layout, + newColor); styleAttrs.setBorderColor( - core::style::Color::createShaded(layout, newColor)); + core::style::Color::create (layout, newColor)); widget->setStyle( core::style::Style::create (layout, &styleAttrs)); break; @@ -2083,7 +2102,7 @@ void Textblock::changeLinkColor (int link, int newColor) } if (changed) queueDrawArea (0, lineYOffsetWidget(line), allocation.width, - line->ascent + line->descent); + line->boxAscent + line->boxDescent); } } @@ -2325,7 +2344,7 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, else if (index >= textblock->words->size ()) content.type = core::Content::END; else - content = textblock->words->get(index).content; + content = textblock->words->getRef(index)->content; } object::Object *Textblock::TextblockIterator::clone() @@ -2337,23 +2356,23 @@ int Textblock::TextblockIterator::compareTo(misc::Comparable *other) { return index - ((TextblockIterator*)other)->index; } - + bool Textblock::TextblockIterator::next () { Textblock *textblock = (Textblock*)getWidget(); if (content.type == core::Content::END) return false; - + do { index++; if (index >= textblock->words->size ()) { content.type = core::Content::END; return false; } - } while ((textblock->words->get(index).content.type & getMask()) == 0); + } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); - content = textblock->words->get(index).content; + content = textblock->words->getRef(index)->content; return true; } @@ -2363,16 +2382,16 @@ bool Textblock::TextblockIterator::prev () if (content.type == core::Content::START) return false; - + do { index--; if (index < 0) { content.type = core::Content::START; return false; } - } while ((textblock->words->get(index).content.type & getMask()) == 0); + } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); - content = textblock->words->get(index).content; + content = textblock->words->getRef(index)->content; return true; } @@ -2440,16 +2459,19 @@ void Textblock::queueDrawRange (int index1, int index2) to = misc::min (to, words->size () - 1); to = misc::max (to, 0); - int line1 = findLineOfWord (from); - int line2 = findLineOfWord (to); + int line1idx = findLineOfWord (from); + int line2idx = findLineOfWord (to); + + if (line1idx >= 0 && line2idx >= 0) { + Line *line1 = lines->getRef (line1idx), + *line2 = lines->getRef (line2idx); + int y = lineYOffsetWidget (line1) + line1->boxAscent - + line1->contentAscent; + int h = lineYOffsetWidget (line2) + line2->boxAscent + + line2->contentDescent - y; - queueDrawArea (0, - lineYOffsetWidgetI (line1), - allocation.width, - lineYOffsetWidgetI (line2) - - lineYOffsetWidgetI (line1) - + lines->getRef (line2)->ascent - + lines->getRef (line2)->descent); + queueDrawArea (0, y, allocation.width, h); + } } void Textblock::TextblockIterator::getAllocation (int start, int end, @@ -2462,13 +2484,31 @@ void Textblock::TextblockIterator::getAllocation (int start, int end, allocation->x = textblock->allocation.x + textblock->lineXOffsetWidget (line); - for (int i = line->firstWord; i < index; i++) - allocation->x += textblock->words->getRef(i)->size.width; - allocation->y = - textblock->allocation.y - + textblock->lineYOffsetWidget (line) + line->ascent - word->size.ascent; + for (int i = line->firstWord; 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->layout->textWidth (word->style->font, + word->content.text, + start); + } + allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent - + 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->layout->textWidth (word->style->font, + word->content.text + start, + end - start); + } + } allocation->ascent = word->size.ascent; allocation->descent = word->size.descent; } diff --git a/dw/textblock.hh b/dw/textblock.hh index 80e16393..15f81eb1 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -6,8 +6,6 @@ namespace dw { -using namespace lout; - /** * \brief A Widget for rendering text blocks, i.e. paragraphs or sequences * of paragraphs. @@ -16,118 +14,118 @@ using namespace lout; * floats have been implementet. Will be updated and extended soon. * * <h3>Signals</h3> - * + * * dw::Textblock uses the signals defined in - * dw::core::Widget::LinkReceiver, related to links. The coordinates are + * dw::core::Layout::LinkReceiver, related to links. The coordinates are * always -1. - * - * + * + * * <h3>Collapsing Spaces</h3> - * + * * The idea behind this is that every paragraph has a specific vertical * space around and that they are combined to one space, according to * rules stated below. A paragraph consists either of the lines between * two paragraph breaks within a dw::Textblock, or of a dw::Textblock * within a dw::Textblock, in a single line; the latter is used for * indented boxes and list items. - * + * * The rules: - * + * * <ol> * <li> If a paragraph is following by another, the space between them is the * maximum of both box spaces: - * + * * \image html dw-textblock-collapsing-spaces-1-1.png - * + * * are combined like this: - * + * * \image html dw-textblock-collapsing-spaces-1-2.png - * + * * <li> a) If one paragraph is the first paragraph within another, the upper * space of these paragraphs collapse. b) The analogue is the case for the * last box: - * + * * \image html dw-textblock-collapsing-spaces-2-1.png - * + * * If B and C are put into A, the result is: - * + * * \image html dw-textblock-collapsing-spaces-2-2.png * </ol> - * + * * For achieving this, there are some features of dw::Textblock: - * + * * <ul> * <li> Consequent breaks are automatically combined, according to * rule 1. See the code of dw::Textblock::addParBreak for details. - * + * * <li> If a break is added as the first word of the dw::Textblock within * another dw::Textblock, collapsing according to rule 2a is done * automatically. See the code of dw::Textblock::addParBreak. - * + * * <li> To collapse spaces according to rule 2b, * dw::Textblock::addParBreak::handOverBreak must be called for * the \em inner widget. The HTML parser does this in * Html_eventually_pop_dw. * </ul> - * - * + * + * * <h3>Collapsing Margins</h3> - * + * * Collapsing margins, as defined in the CSS2 specification, are, * supported in addition to collapsing spaces. Also, spaces and margins * collapse themselves. I.e., the space between two paragraphs is the * maximum of the space calculated as described in "Collapsing Spaces" * and the space calculated according to the rules for collapsing margins. - * + * * (This is an intermediate hybrid state, collapsing spaces are used in * the current version of dillo, while I implemented collapsing margins * for the CSS prototype and integrated it already into the main trunk. For * a pure CSS-based dillo, collapsing spaces will not be needed anymore, and * may be removed for simplicity.) - * - * + * + * * <h3>Some Internals</h3> - * - * There are two lists, dw::Textblock::words and - * dw::Textblock::lines. The word list is quite static; only new words - * may be added. A word is either text, a widget, a break or an - * anchor. Anchors are stored in the text, because it may be necessary to - * correct the scroller positions at rewrapping. - * - * Lines refer to the word list (first and last), they are completely + * + * There are 3 lists, dw::Textblock::words, dw::Textblock::lines, and + * dw::Textblock::anchors. The word list is quite static; only new words + * may be added. A word is either text, a widget, or a break. + * + * Lines refer to the word list (first and last). They are completely * redundant, i.e., they can be rebuilt from the words. Lines can be * rewrapped either completely or partially (see "Incremental Resizing" * below). For the latter purpose, several values are accumulated in the * lines. See dw::Textblock::Line for details. - * - * + * + * Anchors associate the anchor name with the index of the next word at + * the point of the anchor. + * * <h4>Incremental Resizing</h4> - * + * * dw::Textblock makes use of incremental resizing as described in \ref * dw-widget-sizes. The parentRef is, for children of a dw::Textblock, simply * the number of the line. - * + * * Generally, there are three cases which may change the size of the * widget: - * + * * <ul> * <li> The available size of the widget has changed, e.g., because the * user has changed the size of the browser window. In this case, * it is necessary to rewrap all the lines. - * + * * <li> A child widget has changed its size. In this case, only a rewrap * down from the line where this widget is located is necessary. - * + * * (This case is very important for tables. Tables are quite at the * bottom, so that a partial rewrap is relevant. Otherwise, tables * change their size quite often, so that this is necessary for a * fast, non-blocking rendering) - * + * * <li> A word (or widget, break etc.) is added to the text block. This * makes it possible to reuse the old size by simply adjusting the * current width and height, so no rewrapping is necessary. * </ul> - * + * * The state of the size calculation is stored in wrapRef within * dw::Textblock, which has the value -1 if no rewrapping of lines * necessary, or otherwise the line from which a rewrap is necessary. @@ -141,7 +139,7 @@ private: class FloatSide { protected: - class Float: public object::Object + class Float: public lout::object::Object { public: Textblock *floatGenerator; @@ -150,8 +148,8 @@ private: }; Textblock *floatContainer; - container::typed::Vector<Float> *floats; - container::typed::HashTable<object::TypedPointer<dw::core::Widget>, Float> *floatsByWidget; + lout::container::typed::Vector<Float> *floats; + lout::container::typed::HashTable<lout::object::TypedPointer<dw::core::Widget>, Float> *floatsByWidget; Float *findFloat(int y); @@ -198,13 +196,13 @@ private: protected: struct Line { - int firstWord; /* first-word's position in DwPageWord [0 based] */ - int lastWord; /* last-word's position in DwPageWord [1 based] */ + int firstWord; /* first word's index in word vector */ + int lastWord; /* last word's index in word vector */ /* "top" is always relative to the top of the first line, i.e. * page->lines[0].top is always 0. */ - int top, ascent, descent, breakSpace; - int leftOffset; /* nonzero for centered and rightly-aligned text */ + int top, boxAscent, boxDescent, contentAscent, contentDescent, + breakSpace, leftOffset; int boxLeft, boxRight; /* This is similar to descent, but includes the bottom margins of the @@ -229,9 +227,9 @@ protected: /* TODO: perhaps add a xLeft? */ core::Requisition size; /* Space after the word, only if it's not a break: */ - unsigned short origSpace; /* from font, set by addSpace */ - unsigned short effSpace; /* effective space, set by wordWrap, - * used for drawing etc. */ + short origSpace; /* from font, set by addSpace */ + short effSpace; /* effective space, set by wordWrap, + * used for drawing etc. */ core::Content content; core::style::Style *style; @@ -239,6 +237,12 @@ protected: later set by a_Dw_page_add_space */ }; + struct Anchor + { + char *name; + int wordIndex; + }; + class TextblockIterator: public core::Iterator { private: @@ -250,8 +254,8 @@ protected: TextblockIterator (Textblock *textblock, core::Content::Type mask, int index); - object::Object *clone(); - int compareTo(misc::Comparable *other); + lout::object::Object *clone(); + int compareTo(lout::misc::Comparable *other); bool next (); bool prev (); @@ -263,7 +267,7 @@ protected: friend class TextblockIterator; /* These fields provide some ad-hoc-functionality, used by sub-classes. */ - bool listItem; /* If true, the first word of the page is treated + bool hasListitemValue; /* If true, the first word of the page is treated specially (search in source). */ int innerPadding; /* This is an additional padding on the left side (used by ListItem). */ @@ -304,17 +308,13 @@ protected: int lastLineParMax; int wrapRef; /* [0 based] */ - misc::SimpleVector <Line> *lines; - misc::SimpleVector <Word> *words; + lout::misc::SimpleVector <Line> *lines; + lout::misc::SimpleVector <Word> *words; + lout::misc::SimpleVector <Anchor> *anchors; struct {int index, nChar;} hlStart[core::HIGHLIGHT_NUM_LAYERS], hlEnd[core::HIGHLIGHT_NUM_LAYERS]; - /* The word index of the link under a button press, and the char - * position */ - int linkPressedIndex; - int link_pressedCharPos; - int hoverLink; /* The link under the button. */ core::style::Tooltip *hoverTooltip; /* The tooltip under the button. No ref * hold. */ @@ -324,17 +324,24 @@ protected: void getWordExtremes (Word *word, core::Extremes *extremes); void markChange (int ref); void justifyLine (Line *line, int availWidth); - void addLine (int wordInd, bool newPar); + Line *addLine (int wordInd, bool newPar); void calcWidgetSize (core::Widget *widget, core::Requisition *size); void rewrap (); + void decorateText(core::View *view, core::style::Style *style, + core::style::Color::Shading shading, + int x, int yBase, int width); + void drawText(int wordIndex, core::View *view, core::Rectangle *area, + int xWidget, int yWidgetBase); + void drawSpace(int wordIndex, core::View *view, core::Rectangle *area, + int xWidget, int yWidgetBase); void drawLine (Line *line, core::View *view, core::Rectangle *area); int findLineIndex (int y); int findLineOfWord (int wordIndex); - int findWord (int x, int y); + Word *findWord (int x, int y, bool *inSpace); Word *addWord (int width, int ascent, int descent, core::style::Style *style); - void calcTextSize (const char *text, core::style::Style *style, + void calcTextSize (const char *text, size_t len, core::style::Style *style, core::Requisition *size); void addFloatIntoContainer(core::Widget *widget, Textblock *floatGenerator); @@ -355,7 +362,7 @@ protected: */ inline int lineXOffsetContents (Line *line) { - return innerPadding + line->leftOffset + line->boxLeft + + return innerPadding + line->leftOffset + line->boxLeft + (line == lines->getRef (0) ? line1OffsetEff : 0); } @@ -371,7 +378,7 @@ protected: inline int lineYOffsetWidgetAllocation (Line *line, core::Allocation *allocation) { - return line->top + (allocation->ascent - lines->getRef(0)->ascent); + return line->top + (allocation->ascent - lines->getRef(0)->boxAscent); } inline int lineYOffsetWidget (Line *line) @@ -400,7 +407,7 @@ protected: { return lineYOffsetWidget (lines->getRef (lineIndex)); } - + inline int lineYOffsetCanvasI (int lineIndex) { return lineYOffsetCanvas (lines->getRef (lineIndex)); @@ -423,7 +430,7 @@ protected: void setWidth (int width); void setAscent (int ascent); void setDescent (int descent); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area); bool buttonPressImpl (core::EventButton *event); bool buttonReleaseImpl (core::EventButton *event); @@ -439,11 +446,15 @@ public: Textblock(bool limitTextWidth); ~Textblock(); - core::Iterator *iterator (core::Content::Type mask, bool atEnd); + core::Iterator *iterator (core::Content::Type mask, bool atEnd); void flush (); - void addText (const char *text, core::style::Style *style); + void addText (const char *text, size_t len, core::style::Style *style); + inline void addText (const char *text, core::style::Style *style) + { + addText (text, strlen(text), style); + } void addWidget (core::Widget *widget, core::style::Style *style); bool addAnchor (const char *name, core::style::Style *style); void addSpace(core::style::Style *style); diff --git a/dw/types.cc b/dw/types.cc index 94f95b42..672ec7f6 100644 --- a/dw/types.cc +++ b/dw/types.cc @@ -1,3 +1,7 @@ +// Rectangle::intersectsWith() has code that was derived from gdkrectangle.c. +// gdkrectangle.c bears the notice that GDK, the GIMP Drawing Kit, is +// "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald". + /* * Dillo Widget * @@ -14,13 +18,15 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * 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 { @@ -33,12 +39,20 @@ Rectangle::Rectangle (int x, int y, int width, int height) 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. - * - * \todo The function has been copied from gdktrectangle.c. Is this relevant - * for copyright? */ bool Rectangle::intersectsWith (Rectangle *otherRect, Rectangle *dest) { @@ -121,6 +135,18 @@ Circle::Circle (int x, int y, int radius) 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 @@ -142,6 +168,27 @@ 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; + int (*pointArray)[2] = + (int (*)[2]) malloc(points->size() * sizeof(*pointArray)); + + for (i = 0; i < points->size(); i++) { + pointArray[i][0] = x + points->getRef(i)->x; + pointArray[i][1] = y + points->getRef(i)->y; + } + view->drawPolygon(style->color, core::style::Color::SHADING_NORMAL, + filled, pointArray, i); + free(pointArray); + } +} + void Polygon::addPoint (int x, int y) { points->increase (); @@ -180,8 +227,8 @@ bool Polygon::linesCross(int ax1, int ay1, int ax2, int ay2, bool cross = linesCross0 (ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) && linesCross0 (bx1, by1, bx2, by2, ax1, ay1, ax2, ay2); - //printf ("(%d, %d) - (%d, %d) and (%d, %d) - (%d, %d) cross? %s.\n", - // ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, cross ? "Yes" : "No"); + _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; } @@ -248,7 +295,7 @@ void Region::addRectangle (Rectangle *rPointer) r->y = misc::min(r->y, ownRect->y); r->width = combinedWidth; r->height = combinedHeight; - + rectangleList->removeRef (ownRect); } } diff --git a/dw/types.hh b/dw/types.hh index cd35e1f6..420a500f 100644 --- a/dw/types.hh +++ b/dw/types.hh @@ -8,7 +8,9 @@ namespace dw { namespace core { -using namespace lout; +namespace style { + class Style; +} enum HPosition { @@ -30,6 +32,8 @@ enum VPosition VPOS_NO_CHANGE }; +enum ScrollCommand {SCREEN_UP_CMD, SCREEN_DOWN_CMD, LINE_UP_CMD, LINE_DOWN_CMD, + LEFT_CMD, RIGHT_CMD, TOP_CMD, BOTTOM_CMD}; /* * Different "layers" may be highlighted in a widget. @@ -50,10 +54,12 @@ struct Point /** * \brief Abstract interface for different shapes. */ -class Shape: public object::Object +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; }; /** @@ -70,6 +76,7 @@ public: 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); @@ -86,6 +93,7 @@ public: 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); }; @@ -95,12 +103,12 @@ public: class Polygon: public Shape { private: - misc::SimpleVector<Point> *points; + 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 + * 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) { @@ -116,6 +124,7 @@ 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); }; @@ -130,8 +139,8 @@ public: class Region { private: - container::typed::List <Rectangle> *rectangleList; - + lout::container::typed::List <Rectangle> *rectangleList; + public: Region (); ~Region (); @@ -140,7 +149,7 @@ public: void addRectangle (Rectangle *r); - container::typed::Iterator <Rectangle> rectangles () + lout::container::typed::Iterator <Rectangle> rectangles () { return rectangleList->iterator (); }; @@ -179,22 +188,20 @@ struct Content END = 1 << 1, TEXT = 1 << 2, WIDGET = 1 << 3, - ANCHOR = 1 << 4, - BREAK = 1 << 5, + BREAK = 1 << 4, FLOAT_REF = 1 << 6, /** \todo A bit ugly. */ ALL = 0xff, REAL_CONTENT = 0xff ^ (START | END | FLOAT_REF), SELECTION_CONTENT = TEXT | WIDGET | BREAK }; /* Content is embedded in struct Word therefore we - * try to be space efficient. + * try to be space efficient. */ short type; bool space; union { const char *text; Widget *widget; - char *anchor; int breakSpace; }; }; @@ -14,8 +14,7 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -28,7 +27,8 @@ namespace dw { namespace core { namespace ui { -using namespace object; +using namespace lout; +using namespace lout::object; int Embed::CLASS_ID = -1; @@ -62,11 +62,26 @@ void Embed::sizeAllocateImpl (Allocation *allocation) 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::setWidth (int width) @@ -84,6 +99,16 @@ void Embed::setDescent (int descent) resource->setDescent (descent); } +void Embed::setDisplayed (bool displayed) +{ + resource->setDisplayed (displayed); +} + +void Embed::setEnabled (bool enabled) +{ + resource->setEnabled (enabled); +} + void Embed::draw (View *view, Rectangle *area) { drawWidgetBox (view, area, false); @@ -183,6 +208,10 @@ void Resource::setDescent (int descent) { } +void Resource::setDisplayed (bool displayed) +{ +} + void Resource::draw (View *view, Rectangle *area) { } @@ -201,31 +230,23 @@ void Resource::emitLeave () activateEmitter.emitLeave(this); } -// ---------------------------------------------------------------------- - -bool ButtonResource::ClickedEmitter::emitToReceiver (lout::signal::Receiver - *receiver, - int signalNo, - int argc, - Object **argv) +bool Resource::ClickedEmitter::emitToReceiver(lout::signal::Receiver *receiver, + int signalNo, int argc, + Object **argv) { ((ClickedReceiver*)receiver) - ->clicked ((ButtonResource*)((Pointer*)argv[0])->getValue (), - ((Integer*)argv[1])->getValue (), - ((Integer*)argv[2])->getValue (), - ((Integer*)argv[3])->getValue ()); + ->clicked ((Resource*)((Pointer*)argv[0])->getValue (), + (EventButton*)((Pointer*)argv[1])->getValue()); return false; } -void ButtonResource::ClickedEmitter::emitClicked (ButtonResource *resource, - int buttonNo, int x, int y) +void Resource::ClickedEmitter::emitClicked (Resource *resource, + EventButton *event) { - Integer i1 (buttonNo); - Integer i2 (x); - Integer i3 (y); - Pointer p (resource); - Object *argv[4] = { &p, &i1, &i2, &i3 }; - emitVoid (0, 4, argv); + Pointer p1 (resource); + Pointer p2 (event); + Object *argv[2] = { &p1, &p2 }; + emitVoid (0, 2, argv); } // ---------------------------------------------------------------------- @@ -14,24 +14,24 @@ namespace core { * 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", 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", @@ -40,19 +40,19 @@ namespace core { * 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"]; @@ -63,16 +63,16 @@ namespace core { * 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 @@ -80,54 +80,54 @@ namespace core { * 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>Resouces 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>Resouces 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 instanciate. + * <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 + * <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. @@ -146,10 +146,10 @@ namespace core { * <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 @@ -175,35 +175,35 @@ namespace core { * <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. @@ -232,6 +232,7 @@ protected: void sizeAllocateImpl (Allocation *allocation); void enterNotifyImpl (core::EventCrossing *event); void leaveNotifyImpl (core::EventCrossing *event); + bool buttonPressImpl (core::EventButton *event); public: static int CLASS_ID; @@ -242,6 +243,8 @@ public: void setWidth (int width); void setAscent (int ascent); void setDescent (int descent); + void setDisplayed (bool displayed); + void setEnabled (bool enabled); void draw (View *view, Rectangle *area); Iterator *iterator (Content::Type mask, bool atEnd); void setStyle (style::Style *style); @@ -271,6 +274,14 @@ public: 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 @@ -286,8 +297,20 @@ private: 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 (); @@ -301,10 +324,12 @@ protected: inline void emitActivate () { return activateEmitter.emitActivate (this); } + inline void emitClicked (EventButton *event) { + clickedEmitter.emitClicked (this, event); } public: inline Resource () { embed = NULL; } - + virtual ~Resource (); virtual void sizeRequest (Requisition *requisition) = 0; @@ -313,8 +338,9 @@ public: virtual void setWidth (int width); virtual void setAscent (int ascent); virtual void setDescent (int descent); + virtual void setDisplayed (bool displayed); virtual void draw (View *view, Rectangle *area); - virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; + virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; virtual void setStyle (style::Style *style); virtual bool isEnabled () = 0; @@ -322,44 +348,13 @@ public: inline void connectActivate (ActivateReceiver *receiver) { activateEmitter.connectActivate (receiver); } + inline void connectClicked (ClickedReceiver *receiver) { + clickedEmitter.connectClicked (receiver); } }; class ButtonResource: public Resource -{ -public: - /** - * \brief Receiver interface for the "clicked" signal. - */ - class ClickedReceiver: public lout::signal::Receiver - { - public: - virtual void clicked (ButtonResource *resource, int buttonNo, int x, - int y) = 0; - }; - -private: - 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 (ButtonResource *resource, int buttonNo, int x, int y); - }; - - ClickedEmitter clickedEmitter; - -protected: - inline void emitClicked (int buttonNo, int x, int y) { - return clickedEmitter.emitClicked (this, buttonNo, x, y); } - -public: - inline void connectClicked (ClickedReceiver *receiver) { - clickedEmitter.connectClicked (receiver); } -}; +{}; /** * \brief Interface for labelled buttons resources. @@ -367,7 +362,7 @@ public: class LabelButtonResource: public ButtonResource { public: - Iterator *iterator (Content::Type mask, bool atEnd); + Iterator *iterator (Content::Type mask, bool atEnd); virtual const char *getLabel () = 0; virtual void setLabel (const char *label) = 0; @@ -397,7 +392,7 @@ protected: virtual Platform *createPlatform () = 0; virtual void setLayout (Layout *layout) = 0; - + virtual int reliefXThickness () = 0; virtual int reliefYThickness () = 0; @@ -419,14 +414,14 @@ public: }; /** - * \brief Base interface for dw::core::ui::ListResource and + * \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 pushGroup (const char *name, bool enabled) = 0; virtual void popGroup () = 0; @@ -448,7 +443,7 @@ public: /** * \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. */ @@ -476,7 +471,7 @@ class OptionMenuResource: public SelectionResource class TextResource: public Resource { public: - Iterator *iterator (Content::Type mask, bool atEnd); + Iterator *iterator (Content::Type mask, bool atEnd); virtual const char *getText () = 0; virtual void setText (const char *text) = 0; @@ -505,7 +500,7 @@ public: class CheckButtonResource: public ToggleButtonResource { public: - Iterator *iterator (Content::Type mask, bool atEnd); + Iterator *iterator (Content::Type mask, bool atEnd); }; class RadioButtonResource: public ToggleButtonResource @@ -529,14 +524,14 @@ public: */ virtual GroupIterator *groupIterator () = 0; - Iterator *iterator (Content::Type mask, bool atEnd); + Iterator *iterator (Content::Type mask, bool atEnd); }; /** * \brief A factory for the common resource. */ -class ResourceFactory: public object::Object +class ResourceFactory: public lout::object::Object { public: virtual LabelButtonResource *createLabelButtonResource (const char *label) @@ -545,10 +540,10 @@ public: bool relief) = 0; virtual ListResource *createListResource (ListResource::SelectionMode - selectionMode) = 0; + selectionMode, int rows) = 0; virtual OptionMenuResource *createOptionMenuResource () = 0; - virtual EntryResource *createEntryResource (int maxLength, - bool password) = 0; + virtual EntryResource *createEntryResource (int maxLength, bool password, + const char *label) = 0; virtual MultiLineTextResource *createMultiLineTextResource (int cols, int rows) = 0; virtual CheckButtonResource *createCheckButtonResource (bool activated) = 0; @@ -13,7 +13,7 @@ namespace core { * * \sa\ref dw-overview, \ref dw-layout-views */ -class View: public object::Object +class View: public lout::object::Object { public: /* @@ -34,7 +34,7 @@ public: virtual void setCanvasSize (int width, int ascent, int descent) = 0; /** - * \brief Set the cursor appearance. + * \brief Set the cursor appearance. */ virtual void setCursor (style::Cursor cursor) = 0; @@ -42,7 +42,7 @@ public: * \brief Set the background of the view. */ virtual void setBgColor (style::Color *color) = 0; - + /* * --------------------------------------------------------- * Scrolling and Related. Only usesViewport must be @@ -80,6 +80,11 @@ public: 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. @@ -107,7 +112,7 @@ public: /** * \brief Called before drawing. * - * All actual drawing operations will be enclosed into calls of + * 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 @@ -166,7 +171,7 @@ public: int x, int y, int width, int height) = 0; virtual void drawArc (style::Color *color, style::Color::Shading shading, bool filled, - int x, int y, int width, int height, + int centerX, int centerY, int width, int height, int angle1, int angle2) = 0; virtual void drawPolygon (style::Color *color, style::Color::Shading shading, diff --git a/dw/widget.cc b/dw/widget.cc index b664b433..751cdcdf 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -14,222 +14,22 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "core.hh" +#include "../lout/msg.h" #include "../lout/debug.hh" +using namespace lout; using namespace lout::object; namespace dw { namespace core { -bool Widget::EventReceiver::buttonPress (Widget *widget, EventButton *event) -{ - return false; -} - -bool Widget::EventReceiver::buttonRelease (Widget *widget, EventButton *event) -{ - return false; -} - -bool Widget::EventReceiver::motionNotify (Widget *widget, EventMotion *event) -{ - return false; -} - -void Widget::EventReceiver::enterNotify (Widget *widget, EventCrossing *event) -{ -} - -void Widget::EventReceiver::leaveNotify (Widget *widget, EventCrossing *event) -{ -} - - -bool Widget::EventEmitter::emitToReceiver (lout::signal::Receiver *receiver, - int signalNo, - int argc, Object **argv) -{ - EventReceiver *eventReceiver = (EventReceiver*)receiver; - - switch (signalNo) { - case BUTTON_PRESS: - return eventReceiver->buttonPress ((Widget*)argv[0], - (EventButton*)argv[1]); - - case BUTTON_RELEASE: - return eventReceiver->buttonRelease ((Widget*)argv[0], - (EventButton*)argv[1]); - - case MOTION_NOTIFY: - return eventReceiver->motionNotify ((Widget*)argv[0], - (EventMotion*)argv[1]); - - case ENTER_NOTIFY: - eventReceiver->enterNotify ((Widget*)argv[0], - (EventCrossing*)argv[1]); - break; - - case LEAVE_NOTIFY: - eventReceiver->leaveNotify ((Widget*)argv[1], - (EventCrossing*)argv[0]); - break; - - default: - misc::assertNotReached (); - } - - /* Compiler happiness. */ - return false; -} - -bool Widget::EventEmitter::emitButtonPress (Widget *widget, EventButton *event) -{ - Object *argv[2] = { widget, event }; - return emitBool (BUTTON_PRESS, 2, argv); -} - -bool Widget::EventEmitter::emitButtonRelease (Widget *widget, - EventButton *event) -{ - Object *argv[2] = { widget, event }; - return emitBool (BUTTON_RELEASE, 2, argv); -} - -bool Widget::EventEmitter::emitMotionNotify (Widget *widget, - EventMotion *event) -{ - Object *argv[2] = { widget, event }; - return emitBool (MOTION_NOTIFY, 2, argv); -} - -void Widget::EventEmitter::emitEnterNotify (Widget *widget, - EventCrossing *event) -{ - Object *argv[2] = { widget, event }; - emitVoid (ENTER_NOTIFY, 2, argv); -} - -void Widget::EventEmitter::emitLeaveNotify (Widget *widget, - EventCrossing *event) -{ - Object *argv[2] = { widget, event }; - emitVoid (LEAVE_NOTIFY, 2, argv); -} - -// ---------------------------------------------------------------------- - -bool Widget::LinkReceiver::enter (Widget *widget, int link, int img, - int x, int y) -{ - return false; -} - -bool Widget::LinkReceiver::press (Widget *widget, int link, int img, - int x, int y, EventButton *event) -{ - return false; -} - -bool Widget::LinkReceiver::release (Widget *widget, int link, int img, - int x, int y, EventButton *event) -{ - return false; -} - -bool Widget::LinkReceiver::click (Widget *widget, int link, int img, - int x, int y, EventButton *event) -{ - return false; -} - - -bool Widget::LinkEmitter::emitToReceiver (lout::signal::Receiver *receiver, - int signalNo, - int argc, Object **argv) -{ - LinkReceiver *linkReceiver = (LinkReceiver*)receiver; - - switch (signalNo) { - case ENTER: - return linkReceiver->enter ((Widget*)argv[0], - ((Integer*)argv[1])->getValue (), - ((Integer*)argv[2])->getValue (), - ((Integer*)argv[3])->getValue (), - ((Integer*)argv[4])->getValue ()); - - case PRESS: - return linkReceiver->press ((Widget*)argv[0], - ((Integer*)argv[1])->getValue (), - ((Integer*)argv[2])->getValue (), - ((Integer*)argv[3])->getValue (), - ((Integer*)argv[4])->getValue (), - (EventButton*)argv[5]); - - case RELEASE: - return linkReceiver->release ((Widget*)argv[0], - ((Integer*)argv[1])->getValue (), - ((Integer*)argv[2])->getValue (), - ((Integer*)argv[3])->getValue (), - ((Integer*)argv[4])->getValue (), - (EventButton*)argv[5]); - - case CLICK: - return linkReceiver->click ((Widget*)argv[0], - ((Integer*)argv[1])->getValue (), - ((Integer*)argv[2])->getValue (), - ((Integer*)argv[3])->getValue (), - ((Integer*)argv[4])->getValue (), - (EventButton*)argv[5]); - - default: - misc::assertNotReached (); - } - - /* Compiler happiness. */ - return false; -} - -bool Widget::LinkEmitter::emitEnter (Widget *widget, int link, int img, - int x, int y) -{ - Integer ilink (link), iimg (img), ix (x), iy (y); - Object *argv[5] = { widget, &ilink, &iimg, &ix, &iy }; - return emitBool (ENTER, 5, argv); -} - -bool Widget::LinkEmitter::emitPress (Widget *widget, int link, int img, - int x, int y, EventButton *event) -{ - Integer ilink (link), iimg (img), ix (x), iy (y); - Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; - return emitBool (PRESS, 6, argv); -} - -bool Widget::LinkEmitter::emitRelease (Widget *widget, int link, int img, - int x, int y, EventButton *event) -{ - Integer ilink (link), iimg (img), ix (x), iy (y); - Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; - return emitBool (RELEASE, 6, argv); -} - -bool Widget::LinkEmitter::emitClick (Widget *widget, int link, int img, - int x, int y, EventButton *event) -{ - Integer ilink (link), iimg (img), ix (x), iy (y); - Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event }; - return emitBool (CLICK, 6, argv); -} - - // ---------------------------------------------------------------------- int Widget::CLASS_ID = -1; @@ -237,7 +37,7 @@ int Widget::CLASS_ID = -1; Widget::Widget () { registerName ("dw::core::Widget", &CLASS_ID); - + flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS); parent = NULL; layout = NULL; @@ -317,7 +117,7 @@ void Widget::queueDrawArea (int x, int y, int width, int height) { /** \todo Maybe only the intersection? */ layout->queueDraw (x + allocation.x, y + allocation.y, width, height); - //printf("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height); + _MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height); } /** @@ -460,35 +260,27 @@ void Widget::sizeAllocate (Allocation *allocation) bool Widget::buttonPress (EventButton *event) { - bool b1 = buttonPressImpl (event); - bool b2 = eventEmitter.emitButtonPress (this, event); - return b1 || b2; + return buttonPressImpl (event); } bool Widget::buttonRelease (EventButton *event) { - bool b1 = buttonReleaseImpl (event); - bool b2 = eventEmitter.emitButtonRelease (this, event); - return b1 || b2; + return buttonReleaseImpl (event); } bool Widget::motionNotify (EventMotion *event) { - bool b1 = motionNotifyImpl (event); - bool b2 = eventEmitter.emitMotionNotify (this, event); - return b1 || b2; + return motionNotifyImpl (event); } void Widget::enterNotify (EventCrossing *event) { enterNotifyImpl (event); - eventEmitter.emitEnterNotify (this, event); } void Widget::leaveNotify (EventCrossing *event) { leaveNotifyImpl (event); - eventEmitter.emitLeaveNotify (this, event); } /** @@ -497,7 +289,7 @@ void Widget::leaveNotify (EventCrossing *event) * The old style is automatically unreferred, the new is referred. If this * call causes the widget to change its size, dw::core::Widget::queueResize * is called. - */ + */ void Widget::setStyle (style::Style *style) { bool sizeChanged; @@ -549,7 +341,7 @@ style::Color *Widget::getBgColor () widget = widget->parent; } - fprintf (stderr, "No background color found!\n"); + MSG_WARN("No background color found!\n"); return NULL; } @@ -694,10 +486,10 @@ Widget *Widget::getNearestCommonAncestor (Widget *otherWidget) /* Search upwards. */ while (widget1 != widget2) { if (widget1->parent == NULL) { - fprintf (stderr, "widgets in different trees\n"); + MSG_WARN("widgets in different trees\n"); return NULL; } - + widget1 = widget1->parent; widget2 = widget2->parent; } @@ -708,7 +500,7 @@ Widget *Widget::getNearestCommonAncestor (Widget *otherWidget) /** * \brief Search recursively through widget. - * + * * Used by dw::core::Layout:getWidgetAtPoint. */ Widget *Widget::getWidgetAtPoint (int x, int y, int level) @@ -740,7 +532,7 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level) level + 1); it->unref (); - + if (childAtPoint) return childAtPoint; else @@ -823,12 +615,20 @@ bool Widget::motionNotifyImpl (EventMotion *event) return false; } -void Widget::enterNotifyImpl (EventCrossing *event) +void Widget::enterNotifyImpl (EventCrossing *) { + core::style::Tooltip *tooltip = getStyle()->x_tooltip; + + if (tooltip) + tooltip->onEnter(); } -void Widget::leaveNotifyImpl (EventCrossing *event) +void Widget::leaveNotifyImpl (EventCrossing *) { + core::style::Tooltip *tooltip = getStyle()->x_tooltip; + + if (tooltip) + tooltip->onLeave(); } void Widget::removeChild (Widget *child) diff --git a/dw/widget.hh b/dw/widget.hh index fde48ecc..013be27b 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -20,149 +20,16 @@ namespace core { * * \sa\ref dw-overview, \ref dw-layout-widgets */ -class Widget: public identity::IdentifiableObject +class Widget: public lout::identity::IdentifiableObject { friend class Layout; -public: - class EventReceiver: public lout::signal::Receiver - { - public: - virtual bool buttonPress (Widget *widget, EventButton *event); - virtual bool buttonRelease (Widget *widget, EventButton *event); - virtual bool motionNotify (Widget *widget, EventMotion *event); - virtual void enterNotify (Widget *widget, EventCrossing *event); - virtual void leaveNotify (Widget *widget, EventCrossing *event); - }; - - /** - * \brief This receiver is for signals related to HTML pages. - * - * The \em link argument to all signals defines a number, which has - * been passed before, e.g. by setting dw::core::style::Style::x_link. - * When defining this number (e.g in dw::core::style::Style::x_link), - * and when receiving the signal, the caller must interpret these numbers - * in a consistent way. In the HTML link block, this number is an index - * to an array of URLs. - * - * \em link = -1 represents an undefined link. - * - * The \em img argument to all signals defines a number which has - * been passed before, e.g. by setting dw::core::style::Style::x_img. - * When defining this number (e.g in dw::core::style::Style::x_img), - * and when receiving the signal, the caller must interpret these numbers - * in a consistent way. In the HTML link block, this number is an index - * to an array of structures containing image information. - * - * \em img = -1 represents an undefined image. - * - * \em x and \em y define the coordinates within the link area. They are - * only used for server-side image maps, see dw::Image. - * - * \sa dw::Image, dw::Textblock - */ - 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); - }; - -private: - class EventEmitter: public lout::signal::Emitter - { - private: - enum { BUTTON_PRESS, BUTTON_RELEASE, MOTION_NOTIFY, ENTER_NOTIFY, - LEAVE_NOTIFY }; - - protected: - bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo, - int argc, Object **argv); - - public: - inline void connectEvent (EventReceiver *receiver) - { connect (receiver); } - - bool emitButtonPress (Widget *widget, EventButton *event); - bool emitButtonRelease (Widget *widget, EventButton *event); - bool emitMotionNotify (Widget *widget, EventMotion *event); - void emitEnterNotify (Widget *widget, EventCrossing *event); - void emitLeaveNotify (Widget *widget, EventCrossing *event); - }; - - class LinkEmitter: public lout::signal::Emitter - { - private: - enum { ENTER, PRESS, RELEASE, CLICK }; - - protected: - bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo, - int argc, 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); - }; - - EventEmitter eventEmitter; - - style::Style *style; - - protected: enum Flags { /** * \brief Set, when dw::core::Widget::requisition is not up to date * anymore. - */ + */ NEEDS_RESIZE = 1 << 0, /** @@ -175,7 +42,7 @@ protected: /** * \brief Set, when dw::core::Widget::extremes is not up to date * anymore. - */ + */ EXTREMES_CHANGED = 1 << 2, /** @@ -183,7 +50,7 @@ protected: * methods are implemented. * * Will hopefully be removed, after redesigning the size model. - */ + */ USES_HINTS = 1 << 3, /** @@ -191,7 +58,7 @@ protected: * some contents, e.g. an image, as opposed to a horizontal ruler. * * Will hopefully be removed, after redesigning the size model. - */ + */ HAS_CONTENTS = 1 << 4, /** @@ -207,6 +74,7 @@ private: * \brief The parent widget, NULL for top-level widgets. */ Widget *parent; + style::Style *style; Flags flags; @@ -248,7 +116,6 @@ public: int parentRef; protected: - LinkEmitter linkEmitter; /** * \brief The current allocation: size and position, always relative to the @@ -266,7 +133,7 @@ protected: inline void setFlags (Flags f) { flags = (Flags)(flags | f); } inline void unsetFlags (Flags f) { flags = (Flags)(flags & ~f); } - + inline void queueDraw () { @@ -346,7 +213,7 @@ protected: EventMotion *event, bool withinContent) { return layout->selectionState.buttonMotion (it, charPos, linkNo, event, withinContent); } - + inline bool selectionHandleEvent (SelectionState::EventType eventType, Iterator *it, int charPos, int linkNo, MousePositionEvent *event, @@ -372,28 +239,6 @@ public: inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; } inline bool extremesChanged () { return flags & EXTREMES_CHANGED; } inline bool wasAllocated () { return flags & WAS_ALLOCATED; } - - inline void connectEvent (EventReceiver *receiver) - { eventEmitter.connectEvent (receiver); } - - inline void connectLink (LinkReceiver *receiver) - { linkEmitter.connectLink (receiver); } - - inline bool emitLinkEnter (int link, int img, int x, int y) - { return linkEmitter.emitEnter (this, link, img, x, y); } - - inline bool emitLinkPress (int link, int img, - int x, int y, EventButton *event) - { return linkEmitter.emitPress (this, link, img, x, y, event); } - - inline bool emitLinkRelease (int link, int img, - int x, int y, EventButton *event) - { return linkEmitter.emitRelease (this, link, img, x, y, event); } - - inline bool emitLinkClick (int link, int img, - int x, int y, EventButton *event) - { return linkEmitter.emitClick (this, link, img, x, y, event); } - inline bool usesHints () { return flags & USES_HINTS; } inline bool hasContents () { return flags & HAS_CONTENTS; } @@ -420,7 +265,7 @@ public: 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 (); |