dillo  3.0.5
About: dillo is a small, fast, extensible Web browser particularly suitable for older or smaller computers and embedded systems (but only limited or no support for frames, CSS, JavaScript, Java).
  Fossies Dox: dillo-3.0.5.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

selection.cc
Go to the documentation of this file.
1 /*
2  * Dillo Widget
3  *
4  * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 
21 
22 #include "core.hh"
23 #include "../lout/debug.hh"
24 
25 #include <string.h>
26 
27 using namespace lout;
28 
29 /*
30  * strndup() is a GNU extension.
31  */
32 extern "C" char *strndup(const char *s, size_t size)
33 {
34  char *r = (char *) malloc (size + 1);
35 
36  if (r) {
37  strncpy (r, s, size);
38  r[size] = 0;
39  }
40 
41  return r;
42 }
43 
44 namespace dw {
45 namespace core {
46 
47 SelectionState::SelectionState ()
48 {
49  DBG_OBJ_CREATE ("dw::core::SelectionState");
50 
51  layout = NULL;
52 
53  selectionState = NONE;
54  from = NULL;
55  to = NULL;
56 
57  linkState = LINK_NONE;
58  link = NULL;
59 }
60 
61 SelectionState::~SelectionState ()
62 {
63  reset ();
64  DBG_OBJ_DELETE ();
65 }
66 
67 void SelectionState::reset ()
68 {
69  resetSelection ();
70  resetLink ();
71 }
72 
73 void SelectionState::resetSelection ()
74 {
75  if (from)
76  delete from;
77  from = NULL;
78  if (to)
79  delete to;
80  to = NULL;
81  selectionState = NONE;
82 }
83 
84 
85 void SelectionState::resetLink ()
86 {
87  if (link)
88  delete link;
89  link = NULL;
90  linkState = LINK_NONE;
91 }
92 
93 bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo,
94  EventButton *event)
95 {
96  Widget *itWidget = it->getWidget ();
97  bool ret = false;
98 
99  if (!event) return ret;
100 
101  if (event->button == 3) {
102  // menu popup
103  layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
104  ret = true;
105  } else if (linkNo != -1) {
106  // link handling
107  (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
108  resetLink ();
109  linkState = LINK_PRESSED;
110  linkButton = event->button;
111  DeepIterator *newLink = new DeepIterator (it);
112  if (newLink->isEmpty ()) {
113  delete newLink;
114  resetLink ();
115  } else {
116  link = newLink;
117  // It may be that the user has pressed on something activatable
118  // (linkNo != -1), but there is no contents, e.g. with images
119  // without ALTernative text.
120  if (link) {
121  linkChar = correctCharPos (link, charPos);
122  linkNumber = linkNo;
123  }
124  }
125  // We do not return the value of the signal method,
126  // but we do actually process this event.
127  ret = true;
128  } else if (event->button == 1) {
129  // normal selection handling
130  highlight (false, 0);
131  resetSelection ();
132 
133  selectionState = SELECTING;
134  DeepIterator *newFrom = new DeepIterator (it);
135  if (newFrom->isEmpty ()) {
136  delete newFrom;
137  resetSelection ();
138  } else {
139  from = newFrom;
140  fromChar = correctCharPos (from, charPos);
141  to = from->cloneDeepIterator ();
142  toChar = correctCharPos (to, charPos);
143  }
144  ret = true;
145  }
146 
147  return ret;
148 }
149 
150 bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo,
151  EventButton *event)
152 {
153  Widget *itWidget = it->getWidget ();
154  bool ret = false;
155 
156  if (linkState == LINK_PRESSED && event && event->button == linkButton) {
157  // link handling
158  ret = true;
159  if (linkNo != -1)
160  (void) layout->emitLinkRelease (itWidget, linkNo, -1, -1, -1, event);
161 
162  // The link where the user clicked the mouse button?
163  if (linkNo == linkNumber) {
164  resetLink ();
165  (void) layout->emitLinkClick (itWidget, linkNo, -1, -1, -1, event);
166  } else {
167  if (event->button == 1)
168  // Reset links and switch to selection mode. The selection
169  // state will be set to SELECTING, which is handled some lines
170  // below.
171  switchLinkToSelection (it, charPos);
172  }
173  }
174 
175  if (selectionState == SELECTING && event && event->button == 1) {
176  // normal selection
177  ret = true;
178  adjustSelection (it, charPos);
179 
180  if (from->compareTo (to) == 0 && fromChar == toChar)
181  // nothing selected
182  resetSelection ();
183  else {
184  copy ();
185  selectionState = SELECTED;
186  }
187  }
188 
189  return ret;
190 }
191 
192 bool SelectionState::buttonMotion (Iterator *it, int charPos, int linkNo,
193  EventMotion *event)
194 {
195  if (linkState == LINK_PRESSED) {
196  //link handling
197  if (linkNo != linkNumber)
198  // No longer the link where the user clicked the mouse button.
199  // Reset links and switch to selection mode.
200  switchLinkToSelection (it, charPos);
201  // Still in link: do nothing.
202  } else if (selectionState == SELECTING) {
203  // selection
204  adjustSelection (it, charPos);
205  }
206 
207  return true;
208 }
209 
215 bool SelectionState::handleEvent (EventType eventType, Iterator *it,
216  int charPos, int linkNo,
217  MousePositionEvent *event)
218 {
219  switch (eventType) {
220  case BUTTON_PRESS:
221  return buttonPress (it, charPos, linkNo, (EventButton*)event);
222 
223  case BUTTON_RELEASE:
224  return buttonRelease (it, charPos, linkNo, (EventButton*)event);
225 
226  case BUTTON_MOTION:
227  return buttonMotion (it, charPos, linkNo, (EventMotion*)event);
228 
229 
230  default:
232  }
233 
234  return false;
235 }
236 
237 
242 void SelectionState::switchLinkToSelection (Iterator *it, int charPos)
243 {
244  // It may be that selection->link is NULL, see a_Selection_button_press.
245  if (link) {
246  // Reset old selection.
247  highlight (false, 0);
248  resetSelection ();
249 
250  // Transfer link state into selection state.
251  from = link->cloneDeepIterator ();
252  fromChar = linkChar;
253  to = from->createVariant (it);
254  toChar = correctCharPos (to, charPos);
255  selectionState = SELECTING;
256 
257  // Reset link status.
258  resetLink ();
259 
260  highlight (true, 0);
261 
262  } else {
263  // A link was pressed on, but there is nothing to select. Reset
264  // everything.
265  resetSelection ();
266  resetLink ();
267  }
268 }
269 
275 void SelectionState::adjustSelection (Iterator *it, int charPos)
276 {
277  DeepIterator *newTo;
278  int newToChar, cmpOld, cmpNew, cmpDiff, len;
279  bool bruteHighlighting = false;
280 
281  newTo = to->createVariant (it);
282  newToChar = correctCharPos (newTo, charPos);
283 
284  cmpOld = to->compareTo (from);
285  cmpNew = newTo->compareTo (from);
286 
287  if (cmpOld == 0 || cmpNew == 0) {
288  // Either before, or now, the limits differ only by the character
289  // position.
290  bruteHighlighting = true;
291  } else if (cmpOld * cmpNew < 0) {
292  // The selection order has changed, i.e. the user moved the selection
293  // end again beyond the position he started.
294  bruteHighlighting = true;
295  } else {
296  // Here, cmpOld and cmpNew are equivalent and != 0.
297  cmpDiff = newTo->compareTo (to);
298 
299  if (cmpOld * cmpDiff > 0) {
300  // The user has enlarged the selection. Highlight the difference.
301  if (cmpDiff < 0) {
302  len = correctCharPos (to, END_OF_WORD);
303  highlight0 (true, newTo, newToChar, to, len + 1, 1);
304  } else {
305  highlight0 (true, to, 0, newTo, newToChar, -1);
306  }
307  } else {
308  if (cmpOld * cmpDiff < 0) {
309  // The user has reduced the selection. Unhighlight the difference.
310  highlight0 (false, to, 0, newTo, 0, cmpDiff);
311  }
312 
313  // Otherwise, the user has changed the position only slightly.
314  // In both cases, re-highlight the new position.
315  if (cmpOld < 0) {
316  len = correctCharPos (newTo, END_OF_WORD);
317  newTo->highlight (newToChar, len + 1, HIGHLIGHT_SELECTION);
318  } else
319  newTo->highlight (0, newToChar, HIGHLIGHT_SELECTION);
320  }
321  }
322 
323  if (bruteHighlighting)
324  highlight (false, 0);
325 
326  delete to;
327  to = newTo;
328  toChar = newToChar;
329 
330  if (bruteHighlighting)
331  highlight (true, 0);
332 }
333 
338 int SelectionState::correctCharPos (DeepIterator *it, int charPos)
339 {
340  Iterator *top = it->getTopIterator ();
341  int len;
342 
343  if (top->getContent()->type == Content::TEXT)
344  len = strlen(top->getContent()->text);
345  else
346  len = 1;
347 
348  return misc::min(charPos, len);
349 }
350 
351 void SelectionState::highlight0 (bool fl, DeepIterator *from, int fromChar,
352  DeepIterator *to, int toChar, int dir)
353 {
354  DeepIterator *a, *b, *i;
355  int cmp, aChar, bChar;
356  bool start;
357 
358  if (from && to) {
359  cmp = from->compareTo (to);
360  if (cmp == 0) {
361  if (fl) {
362  if (fromChar < toChar)
363  from->highlight (fromChar, toChar, HIGHLIGHT_SELECTION);
364  else
365  from->highlight (toChar, fromChar, HIGHLIGHT_SELECTION);
366  } else
367  from->unhighlight (0, HIGHLIGHT_SELECTION);
368  return;
369  }
370 
371  if (cmp < 0) {
372  a = from;
373  aChar = fromChar;
374  b = to;
375  bChar = toChar;
376  } else {
377  a = to;
378  aChar = toChar;
379  b = from;
380  bChar = fromChar;
381  }
382 
383  for (i = a->cloneDeepIterator (), start = true;
384  (cmp = i->compareTo (b)) <= 0;
385  i->next (), start = false) {
386  if (i->getContent()->type == Content::TEXT) {
387  if (fl) {
388  if (start) {
389  i->highlight (aChar, strlen (i->getContent()->text) + 1,
391  } else if (cmp == 0) {
392  // the end
393  i->highlight (0, bChar, HIGHLIGHT_SELECTION);
394  } else {
395  i->highlight (0, strlen (i->getContent()->text) + 1,
397  }
398  } else {
400  }
401  }
402  }
403  delete i;
404  }
405 }
406 
407 void SelectionState::copy()
408 {
409  if (from && to) {
410  Iterator *si;
411  DeepIterator *a, *b, *i;
412  int cmp, aChar, bChar;
413  bool start;
414  char *tmp;
415  misc::StringBuffer strbuf;
416 
417  cmp = from->compareTo (to);
418  if (cmp == 0) {
419  if (from->getContent()->type == Content::TEXT) {
420  si = from->getTopIterator ();
421  if (fromChar < toChar)
422  tmp = strndup (si->getContent()->text + fromChar,
423  toChar - fromChar);
424  else
425  tmp = strndup (si->getContent()->text + toChar,
426  fromChar - toChar);
427  strbuf.appendNoCopy (tmp);
428  }
429  } else {
430  if (cmp < 0) {
431  a = from;
432  aChar = fromChar;
433  b = to;
434  bChar = toChar;
435  } else {
436  a = to;
437  aChar = toChar;
438  b = from;
439  bChar = fromChar;
440  }
441 
442  for (i = a->cloneDeepIterator (), start = true;
443  (cmp = i->compareTo (b)) <= 0;
444  i->next (), start = false) {
445  si = i->getTopIterator ();
446  switch (si->getContent()->type) {
447  case Content::TEXT:
448  if (start) {
449  tmp = strndup (si->getContent()->text + aChar,
450  strlen (i->getContent()->text) - aChar);
451  strbuf.appendNoCopy (tmp);
452  } else if (cmp == 0) {
453  // the end
454  tmp = strndup (si->getContent()->text, bChar);
455  strbuf.appendNoCopy (tmp);
456  } else
457  strbuf.append (si->getContent()->text);
458 
459  if (si->getContent()->space && cmp != 0)
460  strbuf.append (" ");
461 
462  break;
463 
464  case Content::BREAK:
465  if (si->getContent()->breakSpace > 0)
466  strbuf.append ("\n\n");
467  else
468  strbuf.append ("\n");
469  break;
470  default:
471  // Make pedantic compilers happy. Especially
472  // DW_CONTENT_WIDGET is never returned by a DwDeepIterator.
473  break;
474  }
475  }
476  delete i;
477  }
478 
479  layout->copySelection(strbuf.getChars());
480  }
481 }
482 
483 } // namespace core
484 } // namespace dw
DBG_OBJ_DELETE
#define DBG_OBJ_DELETE()
Definition: debug.hh:176
dw::core::DeepIterator::isEmpty
bool isEmpty()
Definition: iterator.cc:566
dw::core::DeepIterator::getContent
Content * getContent()
Definition: iterator.hh:179
DBG_OBJ_CREATE
#define DBG_OBJ_CREATE(klass)
Definition: debug.hh:175
dw::core::EventButton::button
int button
Definition: events.hh:61
dw::core::SelectionState::EventType
EventType
Definition: selection.hh:220
dw::core::MousePositionEvent
Base class for all mouse events related to a specific position.
Definition: events.hh:48
dw::core::EventButton
Represents a button press or release event.
Definition: events.hh:57
strndup
char * strndup(const char *s, size_t size)
Definition: selection.cc:32
dw::core::DeepIterator
A stack of iterators, to iterate recursively through a widget tree.
Definition: iterator.hh:145
dw::core::Iterator
Iterators are used to iterate through the contents of a widget.
Definition: iterator.hh:19
lout::misc::assertNotReached
void assertNotReached()
Definition: misc.hh:35
dw::core::DeepIterator::cloneDeepIterator
DeepIterator * cloneDeepIterator()
Definition: iterator.hh:185
dw::core::Content::space
bool space
Definition: types.hh:202
dw::core::Content::text
const char * text
Definition: types.hh:204
dw::core::DeepIterator::getTopIterator
Iterator * getTopIterator()
Definition: iterator.hh:178
dw::core::Iterator::getWidget
Widget * getWidget()
Definition: iterator.hh:35
lout::misc::min
T min(T a, T b)
Definition: misc.hh:19
lout::misc::StringBuffer
A class for fast concatenation of a large number of strings.
Definition: misc.hh:493
dw::core::EventMotion
Represents a mouse motion event.
Definition: events.hh:67
dw::core::Iterator::getContent
Content * getContent()
Definition: iterator.hh:36
lout
Definition: container.cc:26
lout::misc::StringBuffer::append
void append(const char *str)
Append a NUL-terminated string to the buffer, with copying.
Definition: misc.hh:517
dw::core::Content::breakSpace
int breakSpace
Definition: types.hh:206
dw::core::DeepIterator::createVariant
DeepIterator * createVariant(Iterator *it)
Definition: iterator.cc:560
dw::core::HIGHLIGHT_SELECTION
Definition: types.hh:44
core.hh
dw::core::DeepIterator::compareTo
int compareTo(lout::object::Comparable *other)
Compare two objects c1 and c2.
Definition: iterator.cc:538
lout::misc::StringBuffer::appendNoCopy
void appendNoCopy(char *str)
Append a NUL-terminated string to the buffer, without copying.
Definition: misc.cc:68
dw::core::DeepIterator::next
bool next()
Move iterator forward and store content it.
Definition: iterator.cc:575
dw::core::DeepIterator::unhighlight
void unhighlight(int direction, HighlightLayer layer)
Definition: iterator.hh:209
dw::core::Widget
The base class of all dillo widgets.
Definition: widget.hh:23
lout::misc::StringBuffer::getChars
const char * getChars()
Return a NUL-terminated strings containing all appended strings.
Definition: misc.cc:92
dw
Dw is in this namespace, or sub namespaces of this one.
Definition: alignedtextblock.cc:26
dw::core::Content::type
short type
Definition: types.hh:201
dw::core::DeepIterator::highlight
void highlight(int start, int end, HighlightLayer layer)
Highlight a part of the current content.
Definition: iterator.hh:196