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 * MERCHANTABILITY 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 <config.h>
23 #include <ctype.h>
24
25 #include "gtkhtml.h"
26 #include "gtkhtml-private.h"
27 #include "htmlengine-edit-cursor.h"
28 #include "htmlengine-edit-selection-updater.h"
29 #include "htmlengine-edit-movement.h"
30 #include "htmlobject.h"
31
32
33 void
html_engine_update_selection_if_necessary(HTMLEngine * e)34 html_engine_update_selection_if_necessary (HTMLEngine *e)
35 {
36 if (e->mark == NULL)
37 return;
38
39 html_engine_edit_selection_updater_schedule (e->selection_updater);
40 }
41
42
43 guint
html_engine_move_cursor(HTMLEngine * e,HTMLEngineCursorMovement movement,guint count)44 html_engine_move_cursor (HTMLEngine *e,
45 HTMLEngineCursorMovement movement,
46 guint count)
47 {
48 gboolean (* movement_func) (HTMLCursor *, HTMLEngine *);
49 guint c;
50
51 g_return_val_if_fail (e != NULL, 0);
52 g_return_val_if_fail (HTML_IS_ENGINE (e), 0);
53
54 if (count == 0)
55 return 0;
56
57 switch (movement) {
58 case HTML_ENGINE_CURSOR_RIGHT:
59 movement_func = html_cursor_right;
60 break;
61
62 case HTML_ENGINE_CURSOR_LEFT:
63 movement_func = html_cursor_left;
64 break;
65
66 case HTML_ENGINE_CURSOR_UP:
67 movement_func = html_cursor_up;
68 break;
69
70 case HTML_ENGINE_CURSOR_DOWN:
71 movement_func = html_cursor_down;
72 break;
73
74 default:
75 g_warning ("Unsupported movement %d\n", (gint) movement);
76 return 0;
77 }
78
79 html_engine_hide_cursor (e);
80
81 for (c = 0; c < count; c++) {
82 if (!(* movement_func) (e->cursor, e))
83 break;
84 }
85
86 html_engine_update_focus_if_necessary (e, e->cursor->object, e->cursor->offset);
87 html_engine_show_cursor (e);
88 html_engine_update_selection_if_necessary (e);
89
90 return c;
91 }
92
93
94 /**
95 * html_engine_jump_to_object:
96 * @e: An HTMLEngine object
97 * @object: Object to move the cursor to
98 * @offset: Cursor offset within @object
99 *
100 * Move the cursor to object @object, at the specified @offset.
101 * Notice that this does *not* modify the selection.
102 *
103 * Return value:
104 **/
105 void
html_engine_jump_to_object(HTMLEngine * e,HTMLObject * object,guint offset)106 html_engine_jump_to_object (HTMLEngine *e,
107 HTMLObject *object,
108 guint offset)
109 {
110 g_return_if_fail (e != NULL);
111 g_return_if_fail (HTML_IS_ENGINE (e));
112 g_return_if_fail (object != NULL);
113
114 /* Delete the cursor in the original position. */
115 html_engine_hide_cursor (e);
116
117 /* Jump to the specified position. FIXME: Beep if it fails? */
118 html_cursor_jump_to (e->cursor, e, object, offset);
119 html_cursor_normalize (e->cursor);
120
121 /* Draw the cursor in the new position. */
122 html_engine_show_cursor (e);
123 }
124
125 /**
126 * html_engine_jump_at:
127 * @e: An HTMLEngine object
128 * @x: X coordinate
129 * @y: Y coordinate
130 *
131 * Make the cursor jump at the specified @x, @y pointer position.
132 **/
133 void
html_engine_jump_at(HTMLEngine * e,gint x,gint y)134 html_engine_jump_at (HTMLEngine *e,
135 gint x,
136 gint y)
137 {
138 HTMLObject *obj;
139 guint offset;
140
141 g_return_if_fail (e != NULL);
142 g_return_if_fail (HTML_IS_ENGINE (e));
143
144 gtk_html_im_reset (e->widget);
145
146 obj = html_engine_get_object_at (e, x, y, &offset, TRUE);
147 if (obj == NULL)
148 return;
149
150 /* printf ("jump to object type %d - %p offset %d\n", HTML_OBJECT_TYPE (obj), obj, offset); */
151 html_engine_jump_to_object (e, obj, offset);
152 /* printf ("jumped to object type %d - %p offset: %d\n", HTML_OBJECT_TYPE (e->cursor->object), e->cursor->object, e->cursor->offset); */
153 }
154
155
156 void
html_engine_beginning_of_document(HTMLEngine * engine)157 html_engine_beginning_of_document (HTMLEngine *engine)
158 {
159 g_return_if_fail (engine != NULL);
160 g_return_if_fail (HTML_IS_ENGINE (engine));
161
162 html_engine_hide_cursor (engine);
163 html_cursor_beginning_of_document (engine->cursor, engine);
164 html_engine_update_focus_if_necessary (engine, engine->cursor->object, engine->cursor->offset);
165 html_engine_show_cursor (engine);
166
167 html_engine_update_selection_if_necessary (engine);
168 }
169
170 void
html_engine_end_of_document(HTMLEngine * engine)171 html_engine_end_of_document (HTMLEngine *engine)
172 {
173 g_return_if_fail (engine != NULL);
174 g_return_if_fail (HTML_IS_ENGINE (engine));
175
176 html_engine_hide_cursor (engine);
177 html_cursor_end_of_document (engine->cursor, engine);
178 html_engine_update_focus_if_necessary (engine, engine->cursor->object, engine->cursor->offset);
179 html_engine_show_cursor (engine);
180
181 html_engine_update_selection_if_necessary (engine);
182 }
183
184
185 gboolean
html_engine_beginning_of_line(HTMLEngine * engine)186 html_engine_beginning_of_line (HTMLEngine *engine)
187 {
188 gboolean retval;
189
190 g_return_val_if_fail (engine != NULL, FALSE);
191 g_return_val_if_fail (HTML_IS_ENGINE (engine), FALSE);
192
193 html_engine_hide_cursor (engine);
194 retval = html_cursor_beginning_of_line (engine->cursor, engine);
195 html_engine_update_focus_if_necessary (engine, engine->cursor->object, engine->cursor->offset);
196 html_engine_show_cursor (engine);
197
198 html_engine_update_selection_if_necessary (engine);
199
200 return retval;
201 }
202
203 gboolean
html_engine_end_of_line(HTMLEngine * engine)204 html_engine_end_of_line (HTMLEngine *engine)
205 {
206 gboolean retval;
207
208 g_return_val_if_fail (engine != NULL, FALSE);
209 g_return_val_if_fail (HTML_IS_ENGINE (engine), FALSE);
210
211 html_engine_hide_cursor (engine);
212 retval = html_cursor_end_of_line (engine->cursor, engine);
213 html_engine_update_focus_if_necessary (engine, engine->cursor->object, engine->cursor->offset);
214 html_engine_show_cursor (engine);
215
216 html_engine_update_selection_if_necessary (engine);
217
218 return retval;
219 }
220
221 gboolean
html_engine_beginning_of_paragraph(HTMLEngine * engine)222 html_engine_beginning_of_paragraph (HTMLEngine *engine)
223 {
224 gboolean retval;
225
226 g_return_val_if_fail (engine != NULL, FALSE);
227 g_return_val_if_fail (HTML_IS_ENGINE (engine), FALSE);
228
229 html_engine_hide_cursor (engine);
230 retval = html_cursor_beginning_of_paragraph (engine->cursor, engine);
231 html_engine_update_focus_if_necessary (engine, engine->cursor->object, engine->cursor->offset);
232 html_engine_show_cursor (engine);
233
234 html_engine_update_selection_if_necessary (engine);
235
236 return retval;
237 }
238
239 gboolean
html_engine_end_of_paragraph(HTMLEngine * engine)240 html_engine_end_of_paragraph (HTMLEngine *engine)
241 {
242 gboolean retval;
243
244 g_return_val_if_fail (engine != NULL, FALSE);
245 g_return_val_if_fail (HTML_IS_ENGINE (engine), FALSE);
246
247 html_engine_hide_cursor (engine);
248 retval = html_cursor_end_of_paragraph (engine->cursor, engine);
249 html_engine_update_focus_if_necessary (engine, engine->cursor->object, engine->cursor->offset);
250 html_engine_show_cursor (engine);
251
252 html_engine_update_selection_if_necessary (engine);
253
254 return retval;
255 }
256
257
258 gint
html_engine_scroll_down(HTMLEngine * engine,gint amount)259 html_engine_scroll_down (HTMLEngine *engine,
260 gint amount)
261 {
262 HTMLCursor *cursor;
263 HTMLCursor prev_cursor;
264 gint start_x, start_y;
265 gint x, y, new_y;
266
267 g_return_val_if_fail (engine != NULL, 0);
268 g_return_val_if_fail (HTML_IS_ENGINE (engine), 0);
269
270 cursor = engine->cursor;
271
272 html_object_get_cursor_base (cursor->object, engine->painter, cursor->offset, &start_x, &start_y);
273
274 html_engine_hide_cursor (engine);
275
276 y = new_y = start_y;
277
278 while (1) {
279 html_cursor_copy (&prev_cursor, cursor);
280
281 new_y = html_cursor_down (cursor, engine);
282 html_object_get_cursor_base (cursor->object, engine->painter, cursor->offset, &x, &new_y);
283
284 /* FIXME html_cursor_down() is broken. It should
285 * return FALSE and TRUE appropriately. But I am lazy
286 * now. */
287 if (new_y == y)
288 break;
289
290 if (new_y < start_y) {
291 html_engine_show_cursor (engine);
292 return 0;
293 }
294
295 if (new_y - start_y >= amount) {
296 html_cursor_copy (cursor, &prev_cursor);
297 break;
298 }
299
300 y = new_y;
301 }
302
303 html_engine_update_focus_if_necessary (engine, engine->cursor->object, engine->cursor->offset);
304 html_engine_show_cursor (engine);
305
306 html_engine_update_selection_if_necessary (engine);
307
308 return new_y - start_y;
309 }
310
311 gint
html_engine_scroll_up(HTMLEngine * engine,gint amount)312 html_engine_scroll_up (HTMLEngine *engine,
313 gint amount)
314 {
315 HTMLCursor *cursor;
316 HTMLCursor prev_cursor;
317 gint start_x, start_y;
318 gint x, y, new_y;
319
320 g_return_val_if_fail (engine != NULL, 0);
321 g_return_val_if_fail (HTML_IS_ENGINE (engine), 0);
322
323 cursor = engine->cursor;
324
325 html_object_get_cursor_base (cursor->object, engine->painter, cursor->offset, &start_x, &start_y);
326
327 html_engine_hide_cursor (engine);
328
329 y = start_y;
330
331 while (1) {
332 html_cursor_copy (&prev_cursor, cursor);
333
334 html_cursor_up (cursor, engine);
335 html_object_get_cursor_base (cursor->object, engine->painter, cursor->offset, &x, &new_y);
336
337 /* FIXME html_cursor_down() is broken. It should
338 * return FALSE and TRUE appropriately. But I am lazy
339 * now. */
340 if (new_y == y)
341 break;
342
343 if (new_y > start_y) {
344 html_engine_show_cursor (engine);
345 return 0;
346 }
347
348 if (start_y - new_y >= amount) {
349 html_cursor_copy (cursor, &prev_cursor);
350 break;
351 }
352
353 y = new_y;
354 }
355
356 html_engine_update_focus_if_necessary (engine, engine->cursor->object, engine->cursor->offset);
357 html_engine_show_cursor (engine);
358
359 html_engine_update_selection_if_necessary (engine);
360
361 return start_y - new_y;
362 }
363
364
365 gboolean
html_engine_forward_word(HTMLEngine * e)366 html_engine_forward_word (HTMLEngine *e)
367 {
368 gboolean rv = FALSE;
369
370 g_return_val_if_fail (e != NULL, FALSE);
371 g_return_val_if_fail (HTML_IS_ENGINE (e), FALSE);
372
373 html_engine_hide_cursor (e);
374 while (!g_unichar_isalnum (html_cursor_get_current_char (e->cursor)) && html_cursor_forward (e->cursor, e))
375 rv = TRUE;
376 while (g_unichar_isalnum (html_cursor_get_current_char (e->cursor)) && html_cursor_forward (e->cursor, e))
377 rv = TRUE;
378 html_engine_update_focus_if_necessary (e, e->cursor->object, e->cursor->offset);
379 html_engine_show_cursor (e);
380 html_engine_update_selection_if_necessary (e);
381
382 return rv;
383 }
384
385 gboolean
html_engine_backward_word(HTMLEngine * e)386 html_engine_backward_word (HTMLEngine *e)
387 {
388 gboolean rv = FALSE;
389
390 g_return_val_if_fail (e != NULL, FALSE);
391 g_return_val_if_fail (HTML_IS_ENGINE (e), FALSE);
392
393 html_engine_hide_cursor (e);
394 while (!g_unichar_isalnum (html_cursor_get_prev_char (e->cursor)) && html_cursor_backward (e->cursor, e))
395 rv = TRUE;
396 while (g_unichar_isalnum (html_cursor_get_prev_char (e->cursor)) && html_cursor_backward (e->cursor, e))
397 rv = TRUE;
398 html_engine_update_focus_if_necessary (e, e->cursor->object, e->cursor->offset);
399 html_engine_show_cursor (e);
400 html_engine_update_selection_if_necessary (e);
401
402 return rv;
403 }
404
405 void
html_engine_edit_cursor_position_save(HTMLEngine * e)406 html_engine_edit_cursor_position_save (HTMLEngine *e)
407 {
408 g_return_if_fail (e != NULL);
409 g_return_if_fail (HTML_IS_ENGINE (e));
410
411 e->cursor_position_stack = g_slist_prepend (e->cursor_position_stack, GINT_TO_POINTER (e->cursor->position));
412 }
413
414 void
html_engine_edit_cursor_position_restore(HTMLEngine * e)415 html_engine_edit_cursor_position_restore (HTMLEngine *e)
416 {
417 GSList *link;
418
419 g_return_if_fail (e != NULL);
420 g_return_if_fail (HTML_IS_ENGINE (e));
421
422 if (!e->cursor_position_stack)
423 return;
424
425 html_engine_hide_cursor (e);
426 html_cursor_jump_to_position (e->cursor, e, GPOINTER_TO_INT (e->cursor_position_stack->data));
427 link = e->cursor_position_stack;
428 e->cursor_position_stack = g_slist_remove_link (e->cursor_position_stack, link);
429 g_slist_free (link);
430 html_engine_show_cursor (e);
431 }
432