1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* This file is part of the GtkHTML library
3 *
4 * Copyright (C) 2000 Helix Code, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHcANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include <gtk/gtk.h>
23
24 #include "htmlcursor.h"
25 #include "htmlengine-edit-cursor.h"
26 #include "htmlengine-edit-cut-and-paste.h"
27 #include "htmlentity.h"
28 #include "htmlinterval.h"
29 #include "htmlselection.h"
30 #include "htmlengine-edit.h"
31 #include "htmlengine-edit-selection-updater.h"
32
33 static gboolean
optimize_selection(HTMLEngine * e,HTMLInterval * i)34 optimize_selection (HTMLEngine *e,
35 HTMLInterval *i)
36 {
37 HTMLInterval *s = e->selection;
38 gboolean optimized = FALSE;
39
40 g_return_val_if_fail (s, FALSE);
41
42 /* printf ("change selection (%3d,%3d) --> (%3d,%3d)\n",
43 * s->from.offset, s->to.offset,
44 * i->from.offset, i->to.offset); */
45 if (html_point_eq (&i->from, &s->from)) {
46 HTMLPoint *max;
47
48 max = html_point_max (&i->to, &s->to);
49 if (max) {
50 if (max == &i->to) {
51 HTMLInterval *sel;
52
53 /* printf ("optimize 1\n"); */
54 sel = html_interval_new (s->to.object, i->to.object,
55 i->from.object == s->to.object
56 ? i->from.offset : (html_object_is_container (s->to.object) ? s->to.offset : 0), i->to.offset);
57 html_interval_select (sel, e);
58 html_interval_destroy (sel);
59 html_interval_destroy (s);
60 e->selection = i;
61 optimized = TRUE;
62 } else {
63 HTMLInterval *usel;
64
65 /* printf ("optimize 2\n"); */
66 usel = html_interval_new (i->to.object, s->to.object,
67 html_object_is_container (i->to.object) ? i->to.offset : 0, s->to.offset);
68 html_interval_unselect (usel, e);
69 if (!html_object_is_container (i->to.object) && i->to.offset) {
70 gint from = i->from.object == i->to.object ? i->from.offset : 0;
71 html_object_select_range (i->to.object, e,
72 from, i->to.offset - from,
73 !html_engine_frozen (e));
74 }
75 html_interval_destroy (usel);
76 html_interval_destroy (s);
77 e->selection = i;
78 optimized = TRUE;
79 }
80 }
81 } else if (html_point_eq (&i->to, &s->to)) {
82 HTMLPoint *min;
83
84 min = html_point_min (&i->from, &s->from);
85 if (min) {
86 if (min == &i->from) {
87 HTMLInterval *sel;
88
89 /* printf ("optimize 3\n"); */
90 sel = html_interval_new (i->from.object, s->from.object,
91 i->from.offset,
92 i->to.object == s->from.object
93 ? i->to.offset
94 : (html_object_is_container (s->from.object) ? s->from.offset : html_object_get_length (s->from.object)));
95 html_interval_select (sel, e);
96 html_interval_destroy (sel);
97 html_interval_destroy (s);
98 e->selection = i;
99 optimized = TRUE;
100 } else {
101 HTMLInterval *usel;
102
103 /* printf ("optimize 4\n"); */
104 usel = html_interval_new (s->from.object, i->from.object,
105 s->from.offset,
106 html_object_is_container (i->from.object) ? i->from.offset : html_object_get_length (i->from.object));
107 html_interval_unselect (usel, e);
108 if (!html_object_is_container (i->from.object) && i->from.offset != html_object_get_length (i->from.object)) {
109 gint to = i->to.object == i->from.object
110 ? s->to.offset
111 : html_object_get_length (i->from.object);
112 html_object_select_range (i->from.object, e,
113 i->from.offset, to - i->from.offset,
114 !html_engine_frozen (e));
115 }
116 html_interval_destroy (usel);
117 html_interval_destroy (s);
118 e->selection = i;
119 optimized = TRUE;
120 }
121 }
122 }
123
124 /* if (optimized)
125 * printf ("Optimized\n"); */
126
127 return optimized;
128 }
129
130 static void
clear_primary(HTMLEngine * e)131 clear_primary (HTMLEngine *e) {
132 if (e->primary)
133 html_object_destroy (e->primary);
134
135 e->primary = NULL;
136 e->primary_len = 0;
137 }
138
139 void
html_engine_select_interval(HTMLEngine * e,HTMLInterval * i)140 html_engine_select_interval (HTMLEngine *e,
141 HTMLInterval *i)
142 {
143 e = html_engine_get_top_html_engine (e);
144 html_engine_hide_cursor (e);
145 if (e->selection && html_interval_eq (e->selection, i)) {
146 html_interval_destroy (i);
147 } else if (i && i->from.object == i->to.object && i->from.offset == i->to.offset) {
148 /* shouldn't select zero letters */
149 html_interval_destroy (i);
150 html_engine_unselect_all (e);
151 } else {
152 if (!e->selection || !optimize_selection (e, i)) {
153 html_engine_unselect_all (e);
154 e->selection = i;
155 html_interval_select (e->selection, e);
156 }
157 }
158
159 html_engine_show_cursor (e);
160 }
161
162 void
html_engine_select_region(HTMLEngine * e,gint x1,gint y1,gint x2,gint y2)163 html_engine_select_region (HTMLEngine *e,
164 gint x1,
165 gint y1,
166 gint x2,
167 gint y2)
168 {
169 HTMLPoint *a, *b;
170
171 g_return_if_fail (e != NULL);
172 g_return_if_fail (HTML_IS_ENGINE (e));
173
174 e = html_engine_get_top_html_engine (e);
175 if (e->clue == NULL)
176 return;
177
178 /* printf ("selection %d,%d x %d,%d\n", x1, y1, x2, y2); */
179
180 a = html_engine_get_point_at (e, x1, y1, TRUE);
181 b = html_engine_get_point_at (e, x2, y2, TRUE);
182
183 if (a && b) {
184 HTMLInterval *new_selection;
185
186 /* printf ("points offsets %d, %d\n", a->offset, b->offset); */
187
188 new_selection = html_interval_new_from_points (a, b);
189 html_interval_validate (new_selection);
190 html_engine_select_interval (e, new_selection);
191 }
192
193 if (a)
194 html_point_destroy (a);
195 if (b)
196 html_point_destroy (b);
197 }
198
199 void
html_engine_select_all(HTMLEngine * e)200 html_engine_select_all (HTMLEngine *e)
201 {
202 HTMLObject *a, *b;
203
204 g_return_if_fail (e != NULL);
205 g_return_if_fail (HTML_IS_ENGINE (e));
206
207 e = html_engine_get_top_html_engine (e);
208 if (e->clue == NULL || HTML_CLUE (e->clue)->head == NULL)
209 return;
210
211 a = html_object_get_head_leaf (e->clue);
212 b = html_object_get_tail_leaf (e->clue);
213
214 if (a && b) {
215 HTMLInterval *new_selection;
216
217 new_selection = html_interval_new (a, b, 0, html_object_get_length (b));
218 html_interval_validate (new_selection);
219 html_engine_select_interval (e, new_selection);
220 }
221 }
222
223 void
html_engine_clear_selection(HTMLEngine * e)224 html_engine_clear_selection (HTMLEngine *e)
225 {
226 /* printf ("clear selection\n"); */
227
228 if (e->selection) {
229 html_interval_destroy (e->selection);
230 html_engine_edit_selection_updater_reset (e->selection_updater);
231 e->selection = NULL;
232
233 /*
234 if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == GTK_WIDGET (e->widget)->window)
235 gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
236 html_selection_current_time ());
237 */
238 }
239
240 clear_primary (e);
241 }
242
243 void
html_engine_unselect_all(HTMLEngine * e)244 html_engine_unselect_all (HTMLEngine *e)
245 {
246 e = html_engine_get_top_html_engine (e);
247 if (e->selection) {
248 html_engine_hide_cursor (e);
249 html_interval_unselect (e->selection, e);
250 html_engine_clear_selection (e);
251 html_engine_show_cursor (e);
252 }
253 }
254
255 static void
remove_mark(HTMLEngine * e)256 remove_mark (HTMLEngine *e)
257 {
258 if (e->editable || e->caret_mode) {
259 if (e->mark == NULL)
260 return;
261
262 html_cursor_destroy (e->mark);
263 e->mark = NULL;
264 }
265 }
266
267 void
html_engine_deactivate_selection(HTMLEngine * e)268 html_engine_deactivate_selection (HTMLEngine *e)
269 {
270 remove_mark (e);
271 html_engine_clear_selection (e);
272 }
273
274 void
html_engine_disable_selection(HTMLEngine * e)275 html_engine_disable_selection (HTMLEngine *e)
276 {
277 g_return_if_fail (e != NULL);
278 g_return_if_fail (HTML_IS_ENGINE (e));
279
280 html_engine_hide_cursor (e);
281 remove_mark (e);
282 html_engine_unselect_all (e);
283 e->selection_mode = FALSE;
284 html_engine_show_cursor (e);
285 }
286
287 static gboolean
line_interval(HTMLEngine * e,HTMLCursor * begin,HTMLCursor * end)288 line_interval (HTMLEngine *e,
289 HTMLCursor *begin,
290 HTMLCursor *end)
291 {
292 return html_cursor_beginning_of_line (begin, e) && html_cursor_end_of_line (end, e);
293 }
294
295 gboolean
html_selection_word(gunichar uc)296 html_selection_word (gunichar uc)
297 {
298 return uc && uc != ' ' && uc != '\t' && uc != ENTITY_NBSP /* white space */
299 && uc != '(' && uc != ')' && uc != '[' && uc != ']';
300 }
301
302 gboolean
html_selection_spell_word(gunichar uc,gboolean * cited)303 html_selection_spell_word (gunichar uc,
304 gboolean *cited)
305 {
306 if (uc == '\'' || uc == '`') {
307 *cited = TRUE;
308 return FALSE;
309 } else {
310 return g_unichar_isalpha (uc);
311 }
312 }
313
314 static gboolean
word_interval(HTMLEngine * e,HTMLCursor * begin,HTMLCursor * end)315 word_interval (HTMLEngine *e,
316 HTMLCursor *begin,
317 HTMLCursor *end)
318 {
319 /* move to the begin of word */
320 while (html_selection_word (html_cursor_get_prev_char (begin)))
321 html_cursor_backward (begin, e);
322 /* move to the end of word */
323 while (html_selection_word (html_cursor_get_current_char (end)))
324 html_cursor_forward (end, e);
325
326 return (begin->object && end->object);
327 }
328
329 static void
selection_helper(HTMLEngine * e,gboolean (* get_interval)(HTMLEngine * e,HTMLCursor * begin,HTMLCursor * end))330 selection_helper (HTMLEngine *e,
331 gboolean (*get_interval)(HTMLEngine *e,
332 HTMLCursor *begin,
333 HTMLCursor *end))
334 {
335 HTMLCursor *cursor, *begin, *end;
336 HTMLInterval *i;
337
338 html_engine_unselect_all (e);
339 cursor = html_engine_get_cursor (e);
340
341 if (cursor->object) {
342 begin = html_cursor_dup (cursor);
343 end = html_cursor_dup (cursor);
344
345 if ((*get_interval) (e, begin, end)) {
346 i = html_interval_new_from_cursor (begin, end);
347 html_engine_select_interval (e, i);
348 }
349
350 html_cursor_destroy (begin);
351 html_cursor_destroy (end);
352 }
353 html_cursor_destroy (cursor);
354 }
355
356 void
html_engine_select_word(HTMLEngine * e)357 html_engine_select_word (HTMLEngine *e)
358 {
359 selection_helper (e, word_interval);
360 }
361
362 void
html_engine_select_line(HTMLEngine * e)363 html_engine_select_line (HTMLEngine *e)
364 {
365 selection_helper (e, line_interval);
366 }
367
368 gboolean
html_engine_is_selection_active(HTMLEngine * e)369 html_engine_is_selection_active (HTMLEngine *e)
370 {
371 html_engine_edit_selection_updater_do_idle (e->selection_updater);
372 if (e->selection) {
373 return (!html_engine_get_editable (e) || e->mark) ? TRUE : FALSE;
374 }
375
376 return FALSE;
377 }
378
379 static void
test_point(HTMLObject * o,HTMLEngine * e,gpointer data)380 test_point (HTMLObject *o,
381 HTMLEngine *e,
382 gpointer data)
383 {
384 HTMLPoint *point = (HTMLPoint *) data;
385
386 if (point->object == o) {
387 if (point->object == e->selection->from.object && point->offset < e->selection->from.offset)
388 return;
389 if (point->object == e->selection->to.object && point->offset > e->selection->to.offset)
390 return;
391
392 /* this indicates that object is IN the selection */
393 point->object = NULL;
394 }
395 }
396
397 gboolean
html_engine_point_in_selection(HTMLEngine * e,HTMLObject * obj,guint offset)398 html_engine_point_in_selection (HTMLEngine *e,
399 HTMLObject *obj,
400 guint offset)
401 {
402 HTMLPoint *point;
403 gboolean rv;
404
405 if (!html_engine_is_selection_active (e) || !obj)
406 return FALSE;
407
408 point = html_point_new (obj, offset);
409 html_interval_forall (e->selection, e, test_point, point);
410 rv = point->object == NULL;
411
412 html_point_destroy (point);
413
414 return rv;
415 }
416
417 void
html_engine_activate_selection(HTMLEngine * e,guint32 time)418 html_engine_activate_selection (HTMLEngine *e,
419 guint32 time)
420 {
421 /* printf ("activate selection\n"); */
422
423 if (e->selection && e->block_selection == 0 && gtk_widget_get_realized (GTK_WIDGET (e->widget))) {
424 /* gtk_selection_owner_set (GTK_WIDGET (e->widget), GDK_SELECTION_PRIMARY, time); */
425 /* printf ("activated (%u).\n", time); */
426 clear_primary (e);
427 html_engine_copy_object (e, &e->primary, &e->primary_len);
428 }
429 }
430
431 void
html_engine_block_selection(HTMLEngine * e)432 html_engine_block_selection (HTMLEngine *e)
433 {
434 e->block_selection++;
435 }
436
437 void
html_engine_unblock_selection(HTMLEngine * e)438 html_engine_unblock_selection (HTMLEngine *e)
439 {
440 e->block_selection--;
441 }
442
443 void
html_engine_update_selection_active_state(HTMLEngine * e,guint32 time)444 html_engine_update_selection_active_state (HTMLEngine *e,
445 guint32 time)
446 {
447 if (html_engine_is_selection_active (e))
448 html_engine_activate_selection (e, time ? time : gtk_get_current_event_time ());
449 else
450 html_engine_deactivate_selection (e);
451 }
452