Dillo v3.1.1-46-g8a360e32
Loading...
Searching...
No Matches
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
27using namespace lout;
28
29/*
30 * strndup() is a GNU extension.
31 */
32extern "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
44namespace dw {
45namespace core {
46
48{
49 DBG_OBJ_CREATE ("dw::core::SelectionState");
50
51 layout = NULL;
52
54 from = NULL;
55 to = NULL;
56
58 link = NULL;
59}
60
66
68{
70 resetLink ();
71}
72
74{
75 if (from)
76 delete from;
77 from = NULL;
78 if (to)
79 delete to;
80 to = NULL;
82}
83
84
86{
87 if (link)
88 delete link;
89 link = NULL;
91}
92
93bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo,
94 EventButton *event)
95{
98 it->intoStringBuffer (&sb);
99 DBG_OBJ_ENTER ("events", 0, "buttonPress", "[%s], %d, %d, ...",
100 sb.getChars (), charPos, linkNo);
101 }
102
103 Widget *itWidget = it->getWidget ();
104 bool ret = false;
105
106 if (event) {
107 if (event->button == 3) {
108 // menu popup
109 layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
110 ret = true;
111 } else if (linkNo != -1) {
112 // link handling
113 (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
114 resetLink ();
116 linkButton = event->button;
117 DeepIterator *newLink = new DeepIterator (it);
118 if (newLink->isEmpty ()) {
119 delete newLink;
120 resetLink ();
121 } else {
122 link = newLink;
123 // It may be that the user has pressed on something activatable
124 // (linkNo != -1), but there is no contents, e.g. with images
125 // without ALTernative text.
126 if (link) {
127 linkChar = correctCharPos (link, charPos);
128 linkNumber = linkNo;
129 }
130 }
131 // We do not return the value of the signal method,
132 // but we do actually process this event.
133 ret = true;
134 } else if (event->button == 1) {
135 // normal selection handling
136 highlight (false, 0);
138
140 DeepIterator *newFrom = new DeepIterator (it);
141 if (newFrom->isEmpty ()) {
142 delete newFrom;
144 } else {
145 from = newFrom;
146 fromChar = correctCharPos (from, charPos);
148 toChar = correctCharPos (to, charPos);
149 }
150 ret = true;
151 }
152 }
153
154 DBG_OBJ_MSGF ("events", 1, "=> %s", ret ? "true" : "false");
155 DBG_OBJ_LEAVE ();
156 return ret;
157}
158
159bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo,
160 EventButton *event)
161{
164 it->intoStringBuffer (&sb);
165 DBG_OBJ_ENTER ("events", 0, "buttonRelease", "[%s], %d, %d, ...",
166 sb.getChars (), charPos, linkNo);
167 }
168
169 Widget *itWidget = it->getWidget ();
170 bool ret = false;
171
172 if (linkState == LINK_PRESSED && event && event->button == linkButton) {
173 // link handling
174 ret = true;
175 if (linkNo != -1)
176 (void) layout->emitLinkRelease (itWidget, linkNo, -1, -1, -1, event);
177
178 // The link where the user clicked the mouse button?
179 if (linkNo == linkNumber) {
180 resetLink ();
181 (void) layout->emitLinkClick (itWidget, linkNo, -1, -1, -1, event);
182 } else {
183 if (event->button == 1)
184 // Reset links and switch to selection mode. The selection
185 // state will be set to SELECTING, which is handled some lines
186 // below.
187 switchLinkToSelection (it, charPos);
188 }
189 }
190
191 if (selectionState == SELECTING && event && event->button == 1) {
192 // normal selection
193 ret = true;
194 adjustSelection (it, charPos);
195
196 if (from->compareTo (to) == 0 && fromChar == toChar)
197 // nothing selected
199 else {
200 copy ();
202 }
203 }
204
205 DBG_OBJ_MSGF ("events", 1, "=> %s", ret ? "true" : "false");
206 DBG_OBJ_LEAVE ();
207 return ret;
208}
209
210bool SelectionState::buttonMotion (Iterator *it, int charPos, int linkNo,
211 EventMotion *event)
212{
213 if (linkState == LINK_PRESSED) {
214 //link handling
215 if (linkNo != linkNumber)
216 // No longer the link where the user clicked the mouse button.
217 // Reset links and switch to selection mode.
218 switchLinkToSelection (it, charPos);
219 // Still in link: do nothing.
220 } else if (selectionState == SELECTING) {
221 // selection
222 adjustSelection (it, charPos);
223 }
224
225 return true;
226}
227
234 int charPos, int linkNo,
235 MousePositionEvent *event)
236{
237 switch (eventType) {
238 case BUTTON_PRESS:
239 return buttonPress (it, charPos, linkNo, (EventButton*)event);
240
241 case BUTTON_RELEASE:
242 return buttonRelease (it, charPos, linkNo, (EventButton*)event);
243
244 case BUTTON_MOTION:
245 return buttonMotion (it, charPos, linkNo, (EventMotion*)event);
246
247
248 default:
250 }
251
252 return false;
253}
254
255
261{
262 // It may be that selection->link is NULL, see a_Selection_button_press.
263 if (link) {
264 // Reset old selection.
265 highlight (false, 0);
267
268 // Transfer link state into selection state.
271 to = from->createVariant (it);
272 toChar = correctCharPos (to, charPos);
274
275 // Reset link status.
276 resetLink ();
277
278 highlight (true, 0);
279
280 } else {
281 // A link was pressed on, but there is nothing to select. Reset
282 // everything.
284 resetLink ();
285 }
286}
287
294{
295 DeepIterator *newTo;
296 int newToChar, cmpOld, cmpNew, cmpDiff, len;
297 bool bruteHighlighting = false;
298
299 newTo = to->createVariant (it);
300 newToChar = correctCharPos (newTo, charPos);
301
302 cmpOld = to->compareTo (from);
303 cmpNew = newTo->compareTo (from);
304
305 if (cmpOld == 0 || cmpNew == 0) {
306 // Either before, or now, the limits differ only by the character
307 // position.
308 bruteHighlighting = true;
309 } else if (cmpOld * cmpNew < 0) {
310 // The selection order has changed, i.e. the user moved the selection
311 // end again beyond the position he started.
312 bruteHighlighting = true;
313 } else {
314 // Here, cmpOld and cmpNew are equivalent and != 0.
315 cmpDiff = newTo->compareTo (to);
316
317 if (cmpOld * cmpDiff > 0) {
318 // The user has enlarged the selection. Highlight the difference.
319 if (cmpDiff < 0) {
321 highlight0 (true, newTo, newToChar, to, len + 1, 1);
322 } else {
323 highlight0 (true, to, 0, newTo, newToChar, -1);
324 }
325 } else {
326 if (cmpOld * cmpDiff < 0) {
327 // The user has reduced the selection. Unhighlight the difference.
328 highlight0 (false, to, 0, newTo, 0, cmpDiff);
329 }
330
331 // Otherwise, the user has changed the position only slightly.
332 // In both cases, re-highlight the new position.
333 if (cmpOld < 0) {
334 len = correctCharPos (newTo, END_OF_WORD);
335 newTo->highlight (newToChar, len + 1, HIGHLIGHT_SELECTION);
336 } else
337 newTo->highlight (0, newToChar, HIGHLIGHT_SELECTION);
338 }
339 }
340
341 if (bruteHighlighting)
342 highlight (false, 0);
343
344 delete to;
345 to = newTo;
346 toChar = newToChar;
347
348 if (bruteHighlighting)
349 highlight (true, 0);
350}
351
357{
358 Iterator *top = it->getTopIterator ();
359 int len;
360
361 if (top->getContent()->type == Content::TEXT)
362 len = strlen(top->getContent()->text);
363 else
364 len = 1;
365
366 return misc::min(charPos, len);
367}
368
369void SelectionState::highlight0 (bool fl, DeepIterator *from, int fromChar,
370 DeepIterator *to, int toChar, int dir)
371{
372 DeepIterator *a, *b, *i;
373 int cmp, aChar, bChar;
374 bool start;
375
376 if (from && to) {
377 cmp = from->compareTo (to);
378 if (cmp == 0) {
379 if (fl) {
380 if (fromChar < toChar)
382 else
384 } else
386 return;
387 }
388
389 if (cmp < 0) {
390 a = from;
391 aChar = fromChar;
392 b = to;
393 bChar = toChar;
394 } else {
395 a = to;
396 aChar = toChar;
397 b = from;
398 bChar = fromChar;
399 }
400
401 for (i = a->cloneDeepIterator (), start = true;
402 (cmp = i->compareTo (b)) <= 0;
403 i->next (), start = false) {
404 if (i->getContent()->type == Content::TEXT) {
405 if (fl) {
406 if (start) {
407 i->highlight (aChar, strlen (i->getContent()->text) + 1,
409 } else if (cmp == 0) {
410 // the end
411 i->highlight (0, bChar, HIGHLIGHT_SELECTION);
412 } else {
413 i->highlight (0, strlen (i->getContent()->text) + 1,
415 }
416 } else {
418 }
419 }
420 }
421 delete i;
422 }
423}
424
426{
427 if (from && to) {
428 Iterator *si;
429 DeepIterator *a, *b, *i;
430 int cmp, aChar, bChar;
431 bool start;
432 char *tmp;
433 misc::StringBuffer strbuf;
434
435 cmp = from->compareTo (to);
436 if (cmp == 0) {
437 if (from->getContent()->type == Content::TEXT) {
438 si = from->getTopIterator ();
439 if (fromChar < toChar)
440 tmp = strndup (si->getContent()->text + fromChar,
441 toChar - fromChar);
442 else
443 tmp = strndup (si->getContent()->text + toChar,
444 fromChar - toChar);
445 strbuf.appendNoCopy (tmp);
446 }
447 } else {
448 if (cmp < 0) {
449 a = from;
450 aChar = fromChar;
451 b = to;
452 bChar = toChar;
453 } else {
454 a = to;
455 aChar = toChar;
456 b = from;
457 bChar = fromChar;
458 }
459
460 for (i = a->cloneDeepIterator (), start = true;
461 (cmp = i->compareTo (b)) <= 0;
462 i->next (), start = false) {
463 si = i->getTopIterator ();
464 switch (si->getContent()->type) {
465 case Content::TEXT:
466 if (start) {
467 tmp = strndup (si->getContent()->text + aChar,
468 strlen (i->getContent()->text) - aChar);
469 strbuf.appendNoCopy (tmp);
470 } else if (cmp == 0) {
471 // the end
472 tmp = strndup (si->getContent()->text, bChar);
473 strbuf.appendNoCopy (tmp);
474 } else
475 strbuf.append (si->getContent()->text);
476
477 if (si->getContent()->space && cmp != 0)
478 strbuf.append (" ");
479
480 break;
481
482 case Content::BREAK:
483 if (si->getContent()->breakSpace > 0)
484 strbuf.append ("\n\n");
485 else
486 strbuf.append ("\n");
487 break;
488 default:
489 // Make pedantic compilers happy. Especially
490 // DW_CONTENT_WIDGET is never returned by a DwDeepIterator.
491 break;
492 }
493 }
494 delete i;
495 }
496
497 layout->copySelection(strbuf.getChars());
498 }
499}
500
501} // namespace core
502} // namespace dw
A stack of iterators, to iterate recursively through a widget tree.
Definition iterator.hh:147
DeepIterator * cloneDeepIterator()
Definition iterator.hh:196
DeepIterator * createVariant(Iterator *it)
Definition iterator.cc:656
void highlight(int start, int end, HighlightLayer layer)
Highlight a part of the current content.
Definition iterator.hh:207
int compareTo(lout::object::Comparable *other)
Compare two objects, this and other.
Definition iterator.cc:610
Content * getContent()
Definition iterator.hh:190
bool next()
Move iterator forward and store content it.
Definition iterator.cc:671
Iterator * getTopIterator()
Definition iterator.hh:189
void unhighlight(int direction, HighlightLayer layer)
Definition iterator.hh:220
Represents a button press or release event.
Definition events.hh:58
Represents a mouse motion event.
Definition events.hh:68
Iterators are used to iterate through the contents of a widget.
Definition iterator.hh:20
Widget * getWidget()
Definition iterator.hh:36
void intoStringBuffer(lout::misc::StringBuffer *sb)
Store a textual representation of the object in a misc::StringBuffer.
Definition iterator.cc:58
Content * getContent()
Definition iterator.hh:37
bool emitLinkRelease(Widget *w, int link, int img, int x, int y, EventButton *event)
Definition layout.hh:268
bool emitLinkClick(Widget *w, int link, int img, int x, int y, EventButton *event)
Definition layout.hh:272
bool emitLinkPress(Widget *w, int link, int img, int x, int y, EventButton *event)
Definition layout.hh:264
void copySelection(const char *text)
Definition layout.hh:415
Base class for all mouse events related to a specific position.
Definition events.hh:49
void highlight(bool fl, int dir)
Definition selection.hh:212
enum dw::core::SelectionState::@16 selectionState
bool handleEvent(EventType eventType, Iterator *it, int charPos, int linkNo, MousePositionEvent *event)
General form of dw::core::SelectionState::buttonPress, dw::core::SelectionState::buttonRelease and dw...
Definition selection.cc:233
bool buttonPress(Iterator *it, int charPos, int linkNo, EventButton *event)
Definition selection.cc:93
void adjustSelection(Iterator *it, int charPos)
This method is used by core::dw::SelectionState::buttonMotion and core::dw::SelectionState::buttonRel...
Definition selection.cc:293
bool buttonRelease(Iterator *it, int charPos, int linkNo, EventButton *event)
Definition selection.cc:159
static int correctCharPos(DeepIterator *it, int charPos)
This method deals especially with the case that a widget passes dw::core::SelectionState::END_OF_WORD...
Definition selection.cc:356
enum dw::core::SelectionState::@17 linkState
bool buttonMotion(Iterator *it, int charPos, int linkNo, EventMotion *event)
Definition selection.cc:210
void switchLinkToSelection(Iterator *it, int charPos)
This method is called when the user decides not to activate a link, but instead select text.
Definition selection.cc:260
void highlight0(bool fl, DeepIterator *from, int fromChar, DeepIterator *to, int toChar, int dir)
Definition selection.cc:369
The base class of all dillo widgets.
Definition widget.hh:44
A class for fast concatenation of a large number of strings.
Definition misc.hh:567
void append(const char *str)
Append a NUL-terminated string to the buffer, with copying.
Definition misc.hh:590
void appendNoCopy(char *str)
Append a NUL-terminated string to the buffer, without copying.
Definition misc.cc:62
const char * getChars()
Return a NUL-terminated strings containing all appended strings.
Definition misc.cc:86
#define DBG_IF_RTFL
Definition debug.hh:73
#define DBG_OBJ_DELETE()
#define DBG_OBJ_CREATE(klass)
#define DBG_OBJ_MSGF(aspect, prio, fmt,...)
#define DBG_OBJ_ENTER(aspect, prio, funname, fmt,...)
#define DBG_OBJ_LEAVE()
@ HIGHLIGHT_SELECTION
Definition types.hh:44
Dw is in this namespace, or sub namespaces of this one.
T min(T a, T b)
Definition misc.hh:20
void assertNotReached()
Definition misc.hh:36
char * strndup(const char *s, size_t size)
Definition selection.cc:32
const char * text
Definition types.hh:236