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