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