NetSurf
selection.c
Go to the documentation of this file.
1/*
2 * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
3 * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf 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; version 2 of the License.
10 *
11 * NetSurf 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 * \file
22 * implementation of text selection within browser windows.
23 */
24
25#include <stdlib.h>
26#include <string.h>
27
28#include "netsurf/clipboard.h"
30#include "netsurf/window.h"
31#include "utils/utils.h"
33
36#include "desktop/selection.h"
37
38
40 char *buffer;
41 size_t buffer_len;
42 size_t length;
43
46};
47
48
49typedef enum {
54
55struct selection {
56 struct content *c;
57
58 unsigned max_idx; /* total bytes in text representation */
59
60 unsigned start_idx; /* offset in bytes within text representation */
61 unsigned end_idx;
62
63 bool defined;
64
66};
67
68/**
69 * Redraws the given range of text.
70 *
71 * \param s selection object
72 * \param start_idx start offset (bytes) within the textual representation
73 * \param end_idx end offset (bytes) within the textual representation
74 */
75static nserror
76selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx)
77{
78 nserror res;
79
80 if (s->c->handler->textselection_redraw != NULL) {
81 res = s->c->handler->textselection_redraw(s->c,
82 start_idx,
83 end_idx);
84 } else {
86 }
87
88 return res;
89}
90
91
92/**
93 * Set the start position of the current selection, updating the screen.
94 *
95 * \param s selection object
96 * \param offset byte offset within textual representation
97 */
98static void selection_set_start(struct selection *s, unsigned offset)
99{
100 bool was_defined;
101 unsigned old_start;
102
103 old_start = s->start_idx;
104 s->start_idx = offset;
105
106 was_defined = s->defined;
107 s->defined = (s->start_idx < s->end_idx);
108
109 if (was_defined) {
110 if (offset < old_start) {
111 selection_redraw(s, s->start_idx, old_start);
112 } else {
113 selection_redraw(s, old_start, s->start_idx);
114 }
115 } else if (s->defined) {
117 }
118}
119
120
121/**
122 * Set the end position of the current selection, updating the screen.
123 *
124 * \param s selection object
125 * \param offset byte offset within textual representation
126 */
127static void selection_set_end(struct selection *s, unsigned offset)
128{
129 bool was_defined;
130 unsigned old_end;
131
132 old_end = s->end_idx;
133 s->end_idx = offset;
134
135 was_defined = s->defined;
136 s->defined = (s->start_idx < s->end_idx);
137
138 if (was_defined) {
139 if (offset < old_end) {
140 selection_redraw(s, s->end_idx, old_end);
141 } else {
142 selection_redraw(s, old_end, s->end_idx);
143 }
144 } else if (s->defined) {
146 }
147}
148
149
150/**
151 * Traverse the current selection, calling the handler function (with its
152 * handle) for all boxes that lie (partially) within the given range
153 *
154 * \param s The selection context.
155 * \param handler handler function to call
156 * \param handle handle to pass
157 * \return false iff traversal abandoned part-way through
158 */
159static bool
160selection_copy(struct selection *s, struct selection_string *selstr)
161{
162 nserror res;
163
164 if (s->c->handler->textselection_copy != NULL) {
165 res = s->c->handler->textselection_copy(s->c,
166 s->start_idx,
167 s->end_idx,
168 selstr);
169 } else {
171 }
172
173 if (res != NSERROR_OK) {
174 return false;
175 }
176 return true;
177}
178
179
180/**
181 * Append text to selection string.
182 *
183 * \param text text to be added
184 * \param length length of text in bytes
185 * \param space indicates whether a trailing space should be appended
186 * \param style The font style to use.
187 * \param sel_string string to append to, may be resized
188 * \return true iff successful
189 */
190bool
192 size_t length,
193 bool space,
194 plot_font_style_t *style,
195 struct selection_string *sel_string)
196{
197 size_t new_length = sel_string->length + length + (space ? 1 : 0) + 1;
198
199 if (style != NULL) {
200 /* Add text run style */
201 nsclipboard_styles *new_styles;
202
203 if (sel_string->n_styles == 0) {
204 assert(sel_string->length == 0);
205 }
206
207 new_styles = realloc(sel_string->styles,
208 (sel_string->n_styles + 1) *
209 sizeof(nsclipboard_styles));
210 if (new_styles == NULL) {
211 return false;
212 }
213
214 sel_string->styles = new_styles;
215
216 sel_string->styles[sel_string->n_styles].style = *style;
217 sel_string->styles[sel_string->n_styles].start =
218 sel_string->length;
219
220 sel_string->n_styles++;
221 }
222
223 if (new_length > sel_string->buffer_len) {
224 /* Need to extend buffer */
225 size_t new_alloc = new_length + (new_length / 4);
226 char *new_buff;
227
228 new_buff = realloc(sel_string->buffer, new_alloc);
229 if (new_buff == NULL) {
230 return false;
231 }
232
233 sel_string->buffer = new_buff;
234 sel_string->buffer_len = new_alloc;
235 }
236
237 /* Copy text onto end of existing text in buffer */
238 memcpy(sel_string->buffer + sel_string->length, text, length);
239 sel_string->length += length;
240
241 if (space) {
242 sel_string->buffer[sel_string->length++] = ' ';
243 }
244
245 /* Ensure NULL termination */
246 sel_string->buffer[sel_string->length] = '\0';
247
248 return true;
249}
250
251
252/* exported interface documented in desktop/selection.h */
254{
255 struct selection *sel;
256 sel = calloc(1, sizeof(struct selection));
257 if (sel) {
258 sel->c = c;
259 sel->drag_state = DRAG_NONE;
260 sel->max_idx = 0;
261 selection_clear(sel, false);
262 }
263
264 return sel;
265}
266
267
268/* exported interface documented in desktop/selection.h */
270{
271 if (s == NULL) {
272 return;
273 }
274
275 selection_clear(s, true);
276 free(s);
277}
278
279
280/* exported interface documented in desktop/selection.h */
282{
283 s->max_idx = 0;
284
285 if (s->c->handler->textselection_get_end != NULL) {
287 }
288
289 if (s->defined) {
290 if (s->end_idx > s->max_idx) {
291 s->end_idx = s->max_idx;
292 }
293 if (s->start_idx > s->max_idx) {
294 s->start_idx = s->max_idx;
295 }
296 s->defined = (s->end_idx > s->start_idx);
297 }
298}
299
300
301/* exported interface documented in desktop/selection.h */
303{
304 if (s->defined) {
305 selection_clear(s, true);
306 }
307
308 s->defined = false;
309 s->start_idx = 0;
310 s->end_idx = 0;
312
314}
315
316
317/* exported interface documented in desktop/selection.h */
318bool
320 struct browser_window *top,
322 unsigned idx)
323{
324 browser_mouse_state modkeys;
325 int pos = -1; /* 0 = inside selection, 1 = after it */
326
327 modkeys = (mouse & (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2));
328
329 top = browser_window_get_root(top);
330
331 if (s->defined) {
332 if (idx > s->start_idx) {
333 if (idx <= s->end_idx) {
334 pos = 0;
335 } else {
336 pos = 1;
337 }
338 }
339 }
340
341 if (!pos &&
342 ((mouse & BROWSER_MOUSE_DRAG_1) ||
343 (modkeys && (mouse & BROWSER_MOUSE_DRAG_2)))) {
344 /* drag-saving selection */
345 char *sel = selection_get_copy(s);
347 free(sel);
348 } else if (!modkeys) {
349 if (pos && (mouse & BROWSER_MOUSE_PRESS_1)) {
350 /* Clear the selection if mouse is pressed
351 * outside the selection, Otherwise clear on
352 * release (to allow for drags)
353 */
354
355 selection_clear(s, true);
356
357 } else if (mouse & BROWSER_MOUSE_DRAG_1) {
358 /* start new selection drag */
359
360 selection_clear(s, true);
361
362 selection_set_start(s, idx);
363 selection_set_end(s, idx);
364
365 s->drag_state = DRAG_END;
366
367 guit->window->event(top->window,
369
370 } else if (mouse & BROWSER_MOUSE_DRAG_2) {
371
372 /* adjust selection, but only if there is one */
373 if (!s->defined) {
374 return false; /* ignore Adjust drags */
375 }
376
377 if (pos >= 0) {
378 selection_set_end(s, idx);
379
380 s->drag_state = DRAG_END;
381 } else {
382 selection_set_start(s, idx);
383
385 }
386
387 guit->window->event(top->window,
389
390 } else if (mouse & BROWSER_MOUSE_CLICK_2) {
391
392 /* ignore Adjust clicks when there's no selection */
393 if (!s->defined) {
394 return false;
395 }
396
397 if (pos >= 0) {
398 selection_set_end(s, idx);
399 } else {
400 selection_set_start(s, idx);
401 }
403
404 } else {
405 return false;
406 }
407
408 } else {
409 /* not our problem */
410 return false;
411 }
412
413 /* this mouse click is selection-related */
414 return true;
415}
416
417
418/* exported interface documented in desktop/selection.h */
419void
420selection_track(struct selection *s, browser_mouse_state mouse, unsigned idx)
421{
422 if (!mouse) {
424 }
425
426 switch (s->drag_state) {
427
428 case DRAG_START:
429 if (idx > s->end_idx) {
430 unsigned old_end = s->end_idx;
431 selection_set_end(s, idx);
432 selection_set_start(s, old_end);
433 s->drag_state = DRAG_END;
434 } else {
435 selection_set_start(s, idx);
436 }
437 break;
438
439 case DRAG_END:
440 if (idx < s->start_idx) {
441 unsigned old_start = s->start_idx;
442 selection_set_start(s, idx);
443 selection_set_end(s, old_start);
445 } else {
446 selection_set_end(s, idx);
447 }
448 break;
449
450 default:
451 break;
452 }
453}
454
455
456/* exported interface documented in desktop/selection.h */
458{
459 struct selection_string sel_string = {
460 .buffer = NULL,
461 .buffer_len = 0,
462 .length = 0,
463
464 .n_styles = 0,
465 .styles = NULL
466 };
467
468 if (s == NULL || !s->defined)
469 return NULL;
470
471 if (!selection_copy(s, &sel_string)) {
472 free(sel_string.buffer);
473 free(sel_string.styles);
474 return NULL;
475 }
476
477 free(sel_string.styles);
478
479 return sel_string.buffer;
480}
481
482
483/* exported interface documented in desktop/selection.h */
485{
486 struct selection_string sel_string = {
487 .buffer = NULL,
488 .buffer_len = 0,
489 .length = 0,
490
491 .n_styles = 0,
492 .styles = NULL
493 };
494
495 if (s == NULL || !s->defined) {
496 return false;
497 }
498
499 if (!selection_copy(s, &sel_string)) {
500 free(sel_string.buffer);
501 free(sel_string.styles);
502 return false;
503 }
504
505 guit->clipboard->set(sel_string.buffer,
506 sel_string.length,
507 sel_string.styles,
508 sel_string.n_styles);
509
510 free(sel_string.buffer);
511 free(sel_string.styles);
512
513 return true;
514}
515
516
517/* exported interface documented in desktop/selection.h */
518bool selection_clear(struct selection *s, bool redraw)
519{
520 int old_start, old_end;
521 bool was_defined;
522
523 assert(s);
524
525 was_defined = s->defined;
526 old_start = s->start_idx;
527 old_end = s->end_idx;
528
529 s->defined = false;
530 s->start_idx = 0;
531 s->end_idx = 0;
532
533 if (redraw && was_defined) {
534 selection_redraw(s, old_start, old_end);
535 }
536
537 return was_defined;
538}
539
540
541/* exported interface documented in desktop/selection.h */
543{
544 assert(s);
545 s->defined = true;
546
549}
550
551
552/* exported interface documented in desktop/selection.h */
553void selection_set_position(struct selection *s, unsigned start, unsigned end)
554{
555 selection_set_start(s, start);
556 selection_set_end(s, end);
557}
558
559
560/* exported interface documented in desktop/selection.h */
561bool
563 unsigned start,
564 unsigned end,
565 unsigned *start_idx,
566 unsigned *end_idx)
567{
568 assert(s);
569
570 if (!s->defined) {
571 return false;
572 }
573
574 if ((end <= s->start_idx) ||
575 (start >= s->end_idx)) {
576 return false;
577 }
578
579 *start_idx = (s->start_idx >= start) ? (s->start_idx - start) : 0;
580 *end_idx = min(end, s->end_idx) - start;
581
582 return true;
583}
584
585/* exported interface documented in desktop/selection.h */
587{
588 return s->defined;
589}
590
592{
593 return s->drag_state != DRAG_NONE;
594}
595
597{
598 return s->drag_state == DRAG_START;
599}
600
602{
604}
Browser window private structure.
struct browser_window * browser_window_get_root(struct browser_window *bw)
Get the root level browser window.
Browser window creation and manipulation interface.
Protected interface to Content handling.
void selection_drag_end(struct selection *s)
Handles completion of a drag operation.
Definition: selection.c:601
bool selection_click(struct selection *s, struct browser_window *top, browser_mouse_state mouse, unsigned idx)
Handles mouse clicks (including drag starts) in or near a selection.
Definition: selection.c:319
void selection_reinit(struct selection *s)
Initialise the selection object to use the given box subtree as its root, ie.
Definition: selection.c:281
bool selection_highlighted(const struct selection *s, unsigned start, unsigned end, unsigned *start_idx, unsigned *end_idx)
Tests whether a text range lies partially within the selection, if there is a selection defined,...
Definition: selection.c:562
bool selection_clear(struct selection *s, bool redraw)
Clears the current selection, optionally causing the screen to be updated.
Definition: selection.c:518
seln_drag_state
Definition: selection.c:49
@ DRAG_START
Definition: selection.c:51
@ DRAG_NONE
Definition: selection.c:50
@ DRAG_END
Definition: selection.c:52
struct selection * selection_create(struct content *c)
Creates a new selection object associated with a browser window.
Definition: selection.c:253
bool selection_string_append(const char *text, size_t length, bool space, plot_font_style_t *style, struct selection_string *sel_string)
Append text to selection string.
Definition: selection.c:191
void selection_init(struct selection *s)
Initialise the selection object to use the given box subtree as its root, ie.
Definition: selection.c:302
static nserror selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx)
Redraws the given range of text.
Definition: selection.c:76
bool selection_copy_to_clipboard(struct selection *s)
Copy the selected contents to the clipboard.
Definition: selection.c:484
bool selection_dragging_start(struct selection *s)
Definition: selection.c:596
void selection_track(struct selection *s, browser_mouse_state mouse, unsigned idx)
Handles movements related to the selection, eg.
Definition: selection.c:420
static void selection_set_end(struct selection *s, unsigned offset)
Set the end position of the current selection, updating the screen.
Definition: selection.c:127
bool selection_active(struct selection *s)
determine if a selecion is active
Definition: selection.c:586
bool selection_dragging(struct selection *s)
Definition: selection.c:591
void selection_set_position(struct selection *s, unsigned start, unsigned end)
Set the position of the current selection, updating the screen.
Definition: selection.c:553
void selection_destroy(struct selection *s)
Destroys a selection object clearing it if nesessary.
Definition: selection.c:269
static bool selection_copy(struct selection *s, struct selection_string *selstr)
Traverse the current selection, calling the handler function (with its handle) for all boxes that lie...
Definition: selection.c:160
void selection_select_all(struct selection *s)
Selects all the text within the box subtree controlled by this selection object, updating the screen ...
Definition: selection.c:542
static void selection_set_start(struct selection *s, unsigned offset)
Set the start position of the current selection, updating the screen.
Definition: selection.c:98
char * selection_get_copy(struct selection *s)
Get copy of selection as string.
Definition: selection.c:457
Text selection within browser windows (interface).
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_NOT_IMPLEMENTED
Functionality is not implemented.
Definition: errors.h:61
@ NSERROR_OK
No error.
Definition: errors.h:30
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
Interface to core interface table.
Interface to platform-specific clipboard operations.
browser_mouse_state
Mouse state: 1 is primary mouse button.
Definition: mouse.h:52
@ BROWSER_MOUSE_PRESS_1
primary button pressed
Definition: mouse.h:59
@ BROWSER_MOUSE_CLICK_2
button 2 clicked.
Definition: mouse.h:72
@ BROWSER_MOUSE_MOD_2
2nd modifier key pressed (eg.
Definition: mouse.h:101
@ BROWSER_MOUSE_MOD_1
1st modifier key pressed (eg.
Definition: mouse.h:99
@ BROWSER_MOUSE_DRAG_1
start of button 1 drag
Definition: mouse.h:86
@ BROWSER_MOUSE_DRAG_2
start of button 2 drag
Definition: mouse.h:88
Interface to platform-specific graphical user interface window operations.
@ GW_EVENT_START_SELECTION
selection started
Definition: window.h:123
Interface to utility string handling.
Browser window data.
struct gui_window * window
Platform specific window data only valid at top level.
nserror(* textselection_copy)(struct content *c, unsigned start_idx, unsigned end_idx, struct selection_string *selstr)
copy selected text into selection string possibly with formatting
nserror(* textselection_get_end)(struct content *c, unsigned *end_idx)
get maximum index of text section.
nserror(* textselection_redraw)(struct content *c, unsigned start_idx, unsigned end_idx)
redraw an area of selected text
Content which corresponds to a single URL.
const struct content_handler * handler
Handler for content.
void(* set)(const char *buffer, size_t length, nsclipboard_styles styles[], int n_styles)
Core tells front end to put given text in clipboard.
Definition: clipboard.h:59
nserror(* event)(struct gui_window *gw, enum gui_window_event event)
Miscellaneous event occurred for a window.
Definition: window.h:254
void(* drag_save_selection)(struct gui_window *gw, const char *selection)
drag selection save
Definition: window.h:361
struct gui_clipboard_table * clipboard
Clipboard table.
Definition: gui_table.h:87
struct gui_window_table * window
Window table.
Definition: gui_table.h:66
size_t start
Start of run.
Definition: clipboard.h:34
plot_font_style_t style
Style to give text run.
Definition: clipboard.h:36
Font style for plotting.
Definition: plot_style.h:111
nsclipboard_styles * styles
Definition: selection.c:45
size_t buffer_len
Definition: selection.c:41
bool defined
Definition: selection.c:63
seln_drag_state drag_state
Definition: selection.c:65
struct content * c
Definition: selection.c:56
unsigned max_idx
Definition: selection.c:58
unsigned start_idx
Definition: selection.c:60
unsigned end_idx
Definition: selection.c:61
Interface to a number of general purpose functionality.
#define min(x, y)
Definition: utils.h:46
static nserror text(const struct redraw_context *ctx, const struct plot_font_style *fstyle, int x, int y, const char *text, size_t length)
Text plotting.
Definition: plot.c:978