diff options
Diffstat (limited to 'dw')
-rw-r--r-- | dw/Makefile.am | 1 | ||||
-rw-r--r-- | dw/fltkplatform.cc | 4 | ||||
-rw-r--r-- | dw/fltkui.cc | 8 | ||||
-rw-r--r-- | dw/fltkviewbase.cc | 5 | ||||
-rw-r--r-- | dw/hyphenator.cc | 365 | ||||
-rw-r--r-- | dw/hyphenator.hh | 88 | ||||
-rw-r--r-- | dw/table.hh | 20 | ||||
-rw-r--r-- | dw/textblock.cc | 190 | ||||
-rw-r--r-- | dw/textblock_iterator.cc | 219 | ||||
-rw-r--r-- | dw/textblock_linebreaking.cc | 2 |
10 files changed, 609 insertions, 293 deletions
diff --git a/dw/Makefile.am b/dw/Makefile.am index 61c92b98..aa5c88c3 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -74,6 +74,7 @@ libDw_widgets_a_SOURCES = \ tablecell.cc \ tablecell.hh \ textblock.cc \ + textblock_iterator.cc \ textblock_linebreaking.cc \ textblock.hh diff --git a/dw/fltkplatform.cc b/dw/fltkplatform.cc index 099c449c..fecd76a7 100644 --- a/dw/fltkplatform.cc +++ b/dw/fltkplatform.cc @@ -123,8 +123,8 @@ FltkFont::FltkFont (core::style::FontAttrs *attrs) font = family->get (fa); fl_font(font, size); - /* WORKAROUND: fl_width(uint_t) is not working on non-xft X. - * Reported to FLTK as STR #2688 */ + // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in + // 1.3.0 (STR #2688). spaceWidth = misc::max(0, (int)fl_width(" ") + letterSpacing); int xx, xy, xw, xh; fl_text_extents("x", xx, xy, xw, xh); diff --git a/dw/fltkui.cc b/dw/fltkui.cc index f9e6f3f6..7d2f6cc5 100644 --- a/dw/fltkui.cc +++ b/dw/fltkui.cc @@ -568,8 +568,8 @@ void FltkEntryResource::sizeRequest (core::Requisition *requisition) if (displayed() && style) { FltkFont *font = (FltkFont*)style->font; fl_font(font->font,font->size); - /* WORKAROUND: fl_width(uint_t) is not working on non-xft X. - * Reported to FLTK as STR #2688 */ + // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in + // 1.3.0 (STR #2688). requisition->width = (int)fl_width ("n") * (maxLength == UNLIMITED_MAX_LENGTH ? 10 : maxLength) @@ -691,8 +691,8 @@ void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition) if (style) { FltkFont *font = (FltkFont*)style->font; fl_font(font->font,font->size); - /* WORKAROUND: fl_width(uint_t) is not working on non-xft X. - * Reported to FLTK as STR #2688 */ + // WORKAROUND: A bug with fl_width(uint_t) on non-xft X was present in + // 1.3.0 (STR #2688). requisition->width = (int)fl_width ("n") * numCols + 2 * RELIEF_X_THICKNESS; requisition->ascent = diff --git a/dw/fltkviewbase.cc b/dw/fltkviewbase.cc index 240937e2..d9782a4e 100644 --- a/dw/fltkviewbase.cc +++ b/dw/fltkviewbase.cc @@ -472,8 +472,11 @@ void FltkViewBase::drawArc (core::style::Color *color, int y = translateCanvasYToViewY (centerY) - height / 2; fl_arc(x, y, width, height, angle1, angle2); - if (filled) + if (filled) { + // WORKAROUND: We call both fl_arc and fl_pie due to a FLTK bug + // (STR #2703) that was present in 1.3.0. fl_pie(x, y, width, height, angle1, angle2); + } } void FltkViewBase::drawPolygon (core::style::Color *color, diff --git a/dw/hyphenator.cc b/dw/hyphenator.cc index 97844538..62a47a73 100644 --- a/dw/hyphenator.cc +++ b/dw/hyphenator.cc @@ -2,9 +2,9 @@ #include "../lout/misc.hh" #include "../lout/unicode.hh" +#include <limits.h> #include <stdio.h> #include <string.h> -#include <limits.h> #define LEN 1000 @@ -25,26 +25,44 @@ HashTable <TypedPair <TypedPointer <core::Platform>, ConstString>, Hyphenator> (true, true); Hyphenator::Hyphenator (core::Platform *platform, - const char *patFile, const char *excFile) + const char *patFile, const char *excFile, int pack) { this->platform = platform; - tree = NULL; // As long we are not sure whether a pattern file can be read. + trie = NULL; // As long we are not sure whether a pattern file can be read. - FILE *patF = fopen (patFile, "r"); - if (patF) { - tree = new HashTable <Integer, Collection <Integer> > (true, true); - while (!feof (patF)) { - char buf[LEN + 1]; - char *s = fgets (buf, LEN, patF); - if (s) { - // TODO Better exit with an error, when the line is too long. - int l = strlen (s); - if (s[l - 1] == '\n') - s[l - 1] = 0; - insertPattern (s); + char buf[PATH_MAX + 1]; + snprintf(buf, sizeof (buf), "%s.trie", patFile); + FILE *trieF = fopen (buf, "r"); + + if (trieF) { + trie = new Trie (); + if (trie->load (trieF) != 0) { + delete trie; + trie = NULL; + } + fclose (trieF); + } + + if (trie == NULL) { + TrieBuilder trieBuilder(pack); + FILE *patF = fopen (patFile, "r"); + if (patF) { + + while (!feof (patF)) { + char buf[LEN + 1]; + char *s = fgets (buf, LEN, patF); + if (s) { + // TODO Better exit with an error, when the line is too long. + int l = strlen (s); + if (s[l - 1] == '\n') + s[l - 1] = 0; + insertPattern (&trieBuilder, s); + } } + + trie = trieBuilder.createTrie (); + fclose (patF); } - fclose (patF); } exceptions = NULL; // Again, only instantiated when needed. @@ -69,10 +87,8 @@ Hyphenator::Hyphenator (core::Platform *platform, Hyphenator::~Hyphenator () { - if (tree) - delete tree; - if (exceptions) - delete exceptions; + delete trie; + delete exceptions; } Hyphenator *Hyphenator::getHyphenator (core::Platform *platform, @@ -111,45 +127,38 @@ Hyphenator *Hyphenator::getHyphenator (core::Platform *platform, return hyphenator; } -void Hyphenator::insertPattern (char *s) +void Hyphenator::insertPattern (TrieBuilder *trieBuilder, char *s) { // Convert the a pattern like 'a1bc3d4' into a string of chars 'abcd' // and a list of points [ 0, 1, 0, 3, 4 ]. int l = strlen (s); char chars [l + 1]; - Vector <Integer> *points = new Vector <Integer> (1, true); + SimpleVector<char> points (1); // TODO numbers consisting of multiple digits? // TODO Encoding: This implementation works exactly like the Python // implementation, based on UTF-8. Does this always work? int numChars = 0; - for (int i = 0; s[i]; i++) - if (s[i] >= '0' && s[i] <= '9') - points->put (new Integer (s[i] - '0'), numChars); - else + for (int i = 0; s[i]; i++) { + if (s[i] >= '0' && s[i] <= '9') { + points.setSize(numChars + 1, '0'); + points.set(numChars, s[i]); + } else { chars[numChars++] = s[i]; + } + } chars[numChars] = 0; - for (int i = 0; i < numChars + 1; i++) { - Integer *val = points->get (i); - if (val == NULL) - points->put (new Integer (0), i); - } + points.setSize(numChars + 2, '0'); + points.set(numChars + 1, '\0'); // Insert the pattern into the tree. Each character finds a dict // another level down in the tree, and leaf nodes have the list of // points. - HashTable <Integer, Collection <Integer> > *t = tree; - for (int i = 0; chars[i]; i++) { - Integer c (chars[i]); - if (!t->contains(&c)) - t->put (new Integer (chars[i]), - new HashTable <Integer, Collection <Integer> > (true, true)); - t = (HashTable <Integer, Collection <Integer> >*) t->get (&c); - } + //printf("insertPattern %s\n", chars); - t->put (new Integer (0), points); + trieBuilder->insert (chars, points.getArray ()); } void Hyphenator::insertException (char *s) @@ -214,7 +223,7 @@ bool Hyphenator::isCharPartOfActualWord (char *s) */ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks) { - if ((tree == NULL && exceptions ==NULL) || !isHyphenationCandidate (word)) { + if ((trie == NULL && exceptions ==NULL) || !isHyphenationCandidate (word)) { *numBreaks = 0; return NULL; } @@ -232,7 +241,7 @@ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks) if (wordLc[startActualWord] == 0) { // No letters etc in word: do not hyphenate at all. - delete wordLc; + free (wordLc); *numBreaks = 0; return NULL; } @@ -254,14 +263,14 @@ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks) int *result = new int[exceptionalBreaks->size()]; for (int i = 0; i < exceptionalBreaks->size(); i++) result[i] = exceptionalBreaks->get(i)->getValue() + startActualWord; - delete wordLc; + free (wordLc); *numBreaks = exceptionalBreaks->size(); return result; } - // tree == NULL means that there is no pattern file. - if (tree == NULL) { - delete wordLc; + // trie == NULL means that there is no pattern file. + if (trie == NULL) { + free (wordLc); *numBreaks = 0; return NULL; } @@ -275,25 +284,19 @@ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks) SimpleVector <int> points (l + 1); points.setSize (l + 1, 0); - Integer null (0); - for (int i = 0; i < l; i++) { - HashTable <Integer, Collection <Integer> > *t = tree; - for (int j = i; j < l; j++) { - Integer c (work[j]); - if (t->contains (&c)) { - t = (HashTable <Integer, Collection <Integer> >*) t->get (&c); - if (t->contains (&null)) { - Vector <Integer> *p = (Vector <Integer>*) t->get (&null); - - for (int k = 0; k < p->size (); k++) - points.set(i + k, lout::misc::max (points.get (i + k), - p->get(k)->getValue())); - } - } else - break; + int state = trie->root; + + for (int j = i; j < l && trie->validState (state); j++) { + const char *p = trie->getData((unsigned char) work[j], &state); + + if (p) { + for (int k = 0; p[k]; k++) + points.set(i + k, + lout::misc::max (points.get (i + k), p[k] - '0')); + } } - } + } // No hyphens in the first two chars or the last two. // Characters are not bytes, so UTF-8 characters must be counted. @@ -317,19 +320,241 @@ int *Hyphenator::hyphenateWord(const char *word, int *numBreaks) } } - delete wordLc; + free (wordLc); *numBreaks = breakPos.size (); if (*numBreaks == 0) return NULL; else { - // Could save some cycles by directly returning the array in the - // SimpleVector. - int *breakPosArray = new int[*numBreaks]; - for (int i = 0; i < *numBreaks; i++) - breakPosArray[i] = breakPos.get(i); - return breakPosArray; + return breakPos.detachArray (); } } +Trie::TrieNode TrieBuilder::trieNodeNull = {'\0', 0, NULL}; + +TrieBuilder::TrieBuilder (int pack) +{ + this->pack = pack; + dataList = new SimpleVector <DataEntry> (10000); + stateStack = new SimpleVector <StackEntry> (10); + tree = new SimpleVector <Trie::TrieNode> (20000); + dataZone = new ZoneAllocator (1024); + stateStackPush(0); +} + +TrieBuilder::~TrieBuilder () +{ + delete dataList; + delete stateStack; + delete tree; + delete dataZone; +} + +void TrieBuilder::insert (const char *key, const char *value) +{ + dataList->increase (); + dataList->getLastRef ()->key = (unsigned char *) strdup(key); + dataList->getLastRef ()->value = dataZone->strdup (value); +} + +int TrieBuilder::keyCompare (const void *p1, const void *p2) +{ + DataEntry *pd1 = (DataEntry *) p1; + DataEntry *pd2 = (DataEntry *) p2; + + return strcmp ((char *) pd1->key, (char *) pd2->key); +} + +int TrieBuilder::insertState (StackEntry *state, bool root) +{ + int i, j; + + if (state->count == 0) + return 0; + + if (root) { + i = 0; // we reseve slot 0 for the root state + } else { + /* The bigger pack is the more slots we check and the smaller + * the trie will be, but CPU consumption also increases. + * Reasonable values for pack seemt to be between 256 and 1024. + */ + i = tree->size () - pack + 2 * state->count; + + if (i < 256) // reserve first 256 entries for the root state + i = 256; + } + + for (;; i++) { + if (i + 256 > tree->size ()) + tree->setSize (i + 256, trieNodeNull); + + for (j = 1; j < 256; j++) { + Trie::TrieNode *tn = tree->getRef(i + j); + + if (tn->c == j || ((state->next[j] || state->data[j]) && tn->c != 0)) + break; + } + + if (j == 256) // found a suitable slot + break; + } + + for (int j = 1; j < 256; j++) { + Trie::TrieNode *tn = tree->getRef(i + j); + + if (state->next[j] || state->data[j]) { + tn->c = j; + tn->next = state->next[j]; + tn->data = state->data[j]; + } + } + + assert (root || i >= 256); + assert (!root || i == 0); + return i; +} + +void TrieBuilder::stateStackPush (unsigned char c) +{ + stateStack->increase (); + StackEntry *e = stateStack->getLastRef (); + memset (e, 0, sizeof (StackEntry)); + e->c = c; +} + +int TrieBuilder::stateStackPop () +{ + int next = insertState (stateStack->getLastRef (), stateStack->size () == 1); + unsigned char c = stateStack->getLastRef ()->c; + const char *data = stateStack->getLastRef ()->data1; + + stateStack->setSize (stateStack->size () - 1); + + if (stateStack->size () > 0) { + assert (stateStack->getLastRef ()->next[c] == 0); + assert (stateStack->getLastRef ()->data[c] == NULL); + stateStack->getLastRef ()->next[c] = next; + stateStack->getLastRef ()->data[c] = data; + stateStack->getLastRef ()->count++; + } + + return next; +} + +Trie *TrieBuilder::createTrie () +{ + // we need to sort the patterns as byte strings not as unicode + qsort (dataList->getArray (), dataList->size (), + sizeof (DataEntry), keyCompare); + + for (int i = 0; i < dataList->size (); i++) { + insertSorted (dataList->getRef (i)->key, dataList->getRef (i)->value); + free (dataList->getRef (i)->key); + } + + while (stateStack->size ()) + stateStackPop (); + + int size = tree->size (); + Trie *trie = new Trie(tree->detachArray(), size, true, dataZone); + dataZone = NULL; + return trie; +} + +void TrieBuilder::insertSorted (unsigned char *s, const char *data) +{ + int len = strlen((char*)s); + + for (int i = 0; i < len; i++) { + if (stateStack->size () > i + 1 && + stateStack->getRef (i + 1)->c != s[i]) { + for (int j = stateStack->size () - 1; j >= i + 1; j--) + stateStackPop(); + } + + if (i + 1 >= stateStack->size ()) + stateStackPush(s[i]); + } + + while (stateStack->size () > len + 1) + stateStackPop(); + + assert (stateStack->size () == len + 1); + stateStack->getLastRef ()->data1 = data; +} + +Trie::Trie (TrieNode *array, int size, bool freeArray, ZoneAllocator *dataZone) +{ + this->array = array; + this->size = size; + this->freeArray = freeArray; + this->dataZone = dataZone; +}; + +Trie::~Trie () +{ + delete dataZone; + if (freeArray) + free(array); +}; + +void Trie::save (FILE *file) +{ + for (int i = 0; i < size; i++) { + Trie::TrieNode *tn = &array[i]; + + if (tn->data) + fprintf(file, "%u, %u, %s\n", tn->c, tn->next, tn->data); + else + fprintf(file, "%u, %u\n", tn->c, tn->next); + } +} + +int Trie::load (FILE *file) +{ + int next, c, maxNext = 0; + SimpleVector <TrieNode> tree (100); + dataZone = new ZoneAllocator (1024); + + while (!feof (file)) { + char buf[LEN + 1]; + char *s = fgets (buf, LEN, file); + + if (!s) + continue; + + char data[LEN + 1]; + int n = sscanf (s, "%u, %u, %s", &c, &next, data); + + if (n >= 2 && c >= 0 && c < 256 && next >= 0) { + tree.increase (); + tree.getLastRef ()->c = c; + tree.getLastRef ()->next = next; + if (n >= 3) + tree.getLastRef ()->data = dataZone->strdup (data); + else + tree.getLastRef ()->data = NULL; + + if (next > maxNext) + maxNext = next; + } else { + goto error; + } + } + + if (maxNext >= tree.size ()) + goto error; + + size = tree.size (); + array = tree.detachArray (); + freeArray = true; + return 0; + +error: + delete dataZone; + dataZone = NULL; + return 1; +} + } // namespace dw diff --git a/dw/hyphenator.hh b/dw/hyphenator.hh index 47530467..eadcf081 100644 --- a/dw/hyphenator.hh +++ b/dw/hyphenator.hh @@ -7,9 +7,84 @@ namespace dw { +class Trie { + public: + struct TrieNode { + unsigned char c; + uint16_t next; + const char *data; + }; + + private: + TrieNode *array; + int size; + bool freeArray; + lout::misc::ZoneAllocator *dataZone; + + public: + Trie (TrieNode *array = NULL, int size = 0, bool freeArray = false, + lout::misc::ZoneAllocator *dataZone = NULL); + ~Trie (); + + static const int root = 0; + inline bool validState (int state) { return state >= 0 && state < size; }; + inline const char *getData (unsigned char c, int *state) + { + if (!validState (*state)) + return NULL; + + TrieNode *tn = array + *state + c; + + if (tn->c == c) { + *state = tn->next > 0 ? tn->next : -1; + return tn->data; + } else { + *state = -1; + return NULL; + } + }; + void save (FILE *file); + int load (FILE *file); +}; + +class TrieBuilder { + private: + struct StackEntry { + unsigned char c; + int count; + int next[256]; + const char *data[256]; + const char *data1; + }; + + struct DataEntry { + unsigned char *key; + const char *value; + }; + + int pack; + static Trie::TrieNode trieNodeNull; + lout::misc::SimpleVector <Trie::TrieNode> *tree; + lout::misc::SimpleVector <DataEntry> *dataList; + lout::misc::SimpleVector <StackEntry> *stateStack; + lout::misc::ZoneAllocator *dataZone; + + static int keyCompare (const void *p1, const void *p2); + void stateStackPush (unsigned char c); + int stateStackPop (); + int insertState (StackEntry *state, bool root); + void insertSorted (unsigned char *key, const char *value); + + public: + TrieBuilder (int pack); + ~TrieBuilder (); + + void insert (const char *key, const char *value); + Trie *createTrie(); +}; + class Hyphenator: public lout::object::Object { -private: static lout::container::typed::HashTable <lout::object::TypedPair <lout::object::TypedPointer <core::Platform>, lout::object::ConstString>, @@ -21,26 +96,27 @@ private: * independent, but based on UTF-8. Clarify? Change? */ core::Platform *platform; - lout::container::typed::HashTable <lout::object::Integer, - lout::container::typed::Collection - <lout::object::Integer> > *tree; + Trie *trie; + lout::container::typed::HashTable <lout::object::ConstString, lout::container::typed::Vector <lout::object::Integer> > *exceptions; - void insertPattern (char *s); + + void insertPattern (TrieBuilder *trieBuilder, char *s); void insertException (char *s); bool isCharPartOfActualWord (char *s); public: Hyphenator (core::Platform *platform, - const char *patFile, const char *excFile); + const char *patFile, const char *excFile, int pack = 256); ~Hyphenator(); static Hyphenator *getHyphenator (core::Platform *platform, const char *language); static bool isHyphenationCandidate (const char *word); int *hyphenateWord(const char *word, int *numBreaks); + void saveTrie (FILE *fp) { trie->save (fp); }; }; } // namespace dw diff --git a/dw/table.hh b/dw/table.hh index 87bbaa76..7bcc6c1b 100644 --- a/dw/table.hh +++ b/dw/table.hh @@ -118,10 +118,8 @@ namespace dw { * * <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, @@ -130,29 +128,22 @@ namespace dw { * the cell at \f$(i_1, j)\f$ always span from \f$i_1\f$ to \f$i_2\f$.) * If the minimal width of the column exceeds the sum of the column minima * calculated in the last step: - * * \f[e_{\hbox{cell},i_1,j,\min} > * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\min}\f] - * * then the minimal width of this cell is apportioned to the columns: * * <ul> * <li> If the minimal width of this cell also exceeds the sum of the * column maxima: - * * \f[e_{\hbox{cell},i_1,j,\min} > * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}\f] - * * then \f$e_{\hbox{cell},i_1,j,\min}\f$ is apportioned in a simple * way: - * * \f[e_{\hbox{span},i,j,\min} = * e_{\hbox{base},i,\max} * {e_{\hbox{span},i,j,\min} \over * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}}\f] - * * <li> Otherwise, the apportionment function is used: - * * \f[e_{\hbox{span},i,j,\min} = * a_i (e_{\hbox{cell},i_1,j,\min}, * (e_{\hbox{cell},i_1,j,\min} \ldots @@ -172,6 +163,7 @@ namespace dw { * For the maxima, there is no \f$e_{\hbox{span},i,\max}\f$, but it has to * be assured, that the maximum is always greater than or equal to the * minimum. + * * </ol> * * Generally, if absolute widths are specified, they are, instead of the @@ -223,17 +215,13 @@ namespace dw { * no percentage width has been specified. The difference to the total * width is at max available to the columns with percentage width * specifications: - * * \f[W_{\hbox{columns}_\%,\hbox{available}} = W - \sum e_{i,\min}\f] - * * with only those columns \f$i\f$ with no percentage width specification. * * <li> Then, calculate the sum of the widths, which the columns with * percentage width specification would allocate, when fully adhering to * them: - * * \f[W_{\hbox{columns}_\%,\hbox{best}} = W \sum w_{i,\%}\f] - * * with only those columns \f$i\f$ with a percentage width specification. * * <li> Two cases are distinguished: @@ -243,25 +231,19 @@ namespace dw { * W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the * percentage widths can be used without any modification, by * setting the extremes: - * * \f[e_{i,\min} = e_{i,\max} = W w_{i,\%}\f] - * * for only those columns \f$i\f$ with a percentage width * specification. * * <li> \f$W_{\hbox{columns}_\%,\hbox{available}} < * W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the widths * for these columns must be cut down: - * * \f[e_{i,\min} = e_{i,\max} = * w_{i,\%} * {W_{\hbox{columns}_\%,\hbox{available}} \over * w_{\hbox{total},\%}}\f] - * * with - * * \f[w_{\hbox{total},\%} = \sum w_{i,\%}\f] - * * in both cases for only those columns \f$i\f$ with a percentage * width specification. * </ul> diff --git a/dw/textblock.cc b/dw/textblock.cc index 8c3134a6..ff94486b 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -2063,194 +2063,4 @@ core::Allocation *Textblock::getCBAllocation () return &allocation; } -// ---------------------------------------------------------------------- - -Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, - core::Content::Type mask, - bool atEnd): - core::Iterator (textblock, mask, atEnd) -{ - index = atEnd ? textblock->words->size () : -1; - content.type = atEnd ? core::Content::END : core::Content::START; -} - -Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, - core::Content::Type mask, - int index): - core::Iterator (textblock, mask, false) -{ - this->index = index; - - if (index < 0) - content.type = core::Content::START; - else if (index >= textblock->words->size ()) - content.type = core::Content::END; - else - content = textblock->words->getRef(index)->content; -} - -object::Object *Textblock::TextblockIterator::clone() -{ - return new TextblockIterator ((Textblock*)getWidget(), getMask(), index); -} - -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->getRef(index)->content.type & getMask()) == 0); - - content = textblock->words->getRef(index)->content; - return true; -} - -bool Textblock::TextblockIterator::prev () -{ - Textblock *textblock = (Textblock*)getWidget(); - - if (content.type == core::Content::START) - return false; - - do { - index--; - if (index < 0) { - content.type = core::Content::START; - return false; - } - } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); - - content = textblock->words->getRef(index)->content; - return true; -} - -void Textblock::TextblockIterator::highlight (int start, int end, - core::HighlightLayer layer) -{ - Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; - - if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) { - /* nothing is highlighted */ - textblock->hlStart[layer].index = index; - textblock->hlEnd[layer].index = index; - } - - if (textblock->hlStart[layer].index >= index) { - index2 = textblock->hlStart[layer].index; - textblock->hlStart[layer].index = index; - textblock->hlStart[layer].nChar = start; - } - - if (textblock->hlEnd[layer].index <= index) { - index2 = textblock->hlEnd[layer].index; - textblock->hlEnd[layer].index = index; - textblock->hlEnd[layer].nChar = end; - } - - textblock->queueDrawRange (index1, index2); -} - -void Textblock::TextblockIterator::unhighlight (int direction, - core::HighlightLayer layer) -{ - Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; - - if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) - return; - - if (direction == 0) { - index1 = textblock->hlStart[layer].index; - index2 = textblock->hlEnd[layer].index; - textblock->hlStart[layer].index = 1; - textblock->hlEnd[layer].index = 0; - } else if (direction > 0 && textblock->hlStart[layer].index <= index) { - index1 = textblock->hlStart[layer].index; - textblock->hlStart[layer].index = index + 1; - textblock->hlStart[layer].nChar = 0; - } else if (direction < 0 && textblock->hlEnd[layer].index >= index) { - index1 = textblock->hlEnd[layer].index; - textblock->hlEnd[layer].index = index - 1; - textblock->hlEnd[layer].nChar = INT_MAX; - } - - textblock->queueDrawRange (index1, index2); -} - -void Textblock::queueDrawRange (int index1, int index2) -{ - int from = misc::min (index1, index2); - int to = misc::max (index1, index2); - - from = misc::min (from, words->size () - 1); - from = misc::max (from, 0); - to = misc::min (to, words->size () - 1); - to = misc::max (to, 0); - - 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, y, allocation.width, h); - } -} - -void Textblock::TextblockIterator::getAllocation (int start, int end, - core::Allocation *allocation) -{ - Textblock *textblock = (Textblock*)getWidget(); - int lineIndex = textblock->findLineOfWord (index); - Line *line = textblock->lines->getRef (lineIndex); - Word *word = textblock->words->getRef (index); - - allocation->x = - textblock->allocation.x + textblock->lineXOffsetWidget (line); - - 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->textWidth (word->content.text, 0, start, - word->style); - } - 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->textWidth (word->content.text, start, end - start, - word->style); - } - } - allocation->ascent = word->size.ascent; - allocation->descent = word->size.descent; -} - } // namespace dw diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc new file mode 100644 index 00000000..63720ffb --- /dev/null +++ b/dw/textblock_iterator.cc @@ -0,0 +1,219 @@ +/* + * Dillo Widget + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "textblock.hh" +#include "../lout/msg.h" +#include "../lout/misc.hh" + +#include <stdio.h> +#include <math.h> + +using namespace lout; + +namespace dw { + +Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, + core::Content::Type mask, + bool atEnd): + core::Iterator (textblock, mask, atEnd) +{ + index = atEnd ? textblock->words->size () : -1; + content.type = atEnd ? core::Content::END : core::Content::START; +} + +Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, + core::Content::Type mask, + int index): + core::Iterator (textblock, mask, false) +{ + this->index = index; + + if (index < 0) + content.type = core::Content::START; + else if (index >= textblock->words->size ()) + content.type = core::Content::END; + else + content = textblock->words->getRef(index)->content; +} + +object::Object *Textblock::TextblockIterator::clone() +{ + return new TextblockIterator ((Textblock*)getWidget(), getMask(), index); +} + +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->getRef(index)->content.type & getMask()) == 0); + + content = textblock->words->getRef(index)->content; + return true; +} + +bool Textblock::TextblockIterator::prev () +{ + Textblock *textblock = (Textblock*)getWidget(); + + if (content.type == core::Content::START) + return false; + + do { + index--; + if (index < 0) { + content.type = core::Content::START; + return false; + } + } while ((textblock->words->getRef(index)->content.type & getMask()) == 0); + + content = textblock->words->getRef(index)->content; + return true; +} + +void Textblock::TextblockIterator::highlight (int start, int end, + core::HighlightLayer layer) +{ + Textblock *textblock = (Textblock*)getWidget(); + int index1 = index, index2 = index; + + if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) { + /* nothing is highlighted */ + textblock->hlStart[layer].index = index; + textblock->hlEnd[layer].index = index; + } + + if (textblock->hlStart[layer].index >= index) { + index2 = textblock->hlStart[layer].index; + textblock->hlStart[layer].index = index; + textblock->hlStart[layer].nChar = start; + } + + if (textblock->hlEnd[layer].index <= index) { + index2 = textblock->hlEnd[layer].index; + textblock->hlEnd[layer].index = index; + textblock->hlEnd[layer].nChar = end; + } + + textblock->queueDrawRange (index1, index2); +} + +void Textblock::TextblockIterator::unhighlight (int direction, + core::HighlightLayer layer) +{ + Textblock *textblock = (Textblock*)getWidget(); + int index1 = index, index2 = index; + + if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) + return; + + if (direction == 0) { + index1 = textblock->hlStart[layer].index; + index2 = textblock->hlEnd[layer].index; + textblock->hlStart[layer].index = 1; + textblock->hlEnd[layer].index = 0; + } else if (direction > 0 && textblock->hlStart[layer].index <= index) { + index1 = textblock->hlStart[layer].index; + textblock->hlStart[layer].index = index + 1; + textblock->hlStart[layer].nChar = 0; + } else if (direction < 0 && textblock->hlEnd[layer].index >= index) { + index1 = textblock->hlEnd[layer].index; + textblock->hlEnd[layer].index = index - 1; + textblock->hlEnd[layer].nChar = INT_MAX; + } + + textblock->queueDrawRange (index1, index2); +} + +void Textblock::queueDrawRange (int index1, int index2) +{ + int from = misc::min (index1, index2); + int to = misc::max (index1, index2); + + from = misc::min (from, words->size () - 1); + from = misc::max (from, 0); + to = misc::min (to, words->size () - 1); + to = misc::max (to, 0); + + 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, y, allocation.width, h); + } +} + +void Textblock::TextblockIterator::getAllocation (int start, int end, + core::Allocation *allocation) +{ + Textblock *textblock = (Textblock*)getWidget(); + int lineIndex = textblock->findLineOfWord (index); + Line *line = textblock->lines->getRef (lineIndex); + Word *word = textblock->words->getRef (index); + + allocation->x = + textblock->allocation.x + textblock->lineXOffsetWidget (line); + + 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->textWidth (word->content.text, 0, start, + word->style); + } + 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->textWidth (word->content.text, start, end - start, + word->style); + } + } + allocation->ascent = word->size.ascent; + allocation->descent = word->size.descent; +} + +} // namespace dw diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index f183ae0a..3835273d 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -712,7 +712,7 @@ int Textblock::hyphenateWord (int wordIndex) origWord.style->unref (); origWord.spaceStyle->unref (); - delete breakPos; + free (breakPos); } else { words->getRef(wordIndex)->canBeHyphenated = false; } |