aboutsummaryrefslogtreecommitdiff
path: root/dw
diff options
context:
space:
mode:
Diffstat (limited to 'dw')
-rw-r--r--dw/Makefile.am1
-rw-r--r--dw/fltkplatform.cc4
-rw-r--r--dw/fltkui.cc8
-rw-r--r--dw/fltkviewbase.cc5
-rw-r--r--dw/hyphenator.cc365
-rw-r--r--dw/hyphenator.hh88
-rw-r--r--dw/table.hh20
-rw-r--r--dw/textblock.cc190
-rw-r--r--dw/textblock_iterator.cc219
-rw-r--r--dw/textblock_linebreaking.cc2
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;
}