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 "htmlengine-edit-clueflowstyle.h"
24 #include "htmlundo.h"
25 #include "htmltype.h"
26 #include "htmlcursor.h"
27 #include "htmlselection.h"
28
29
30 /* Properties of a paragraph. */
31 struct _ClueFlowProps {
32 HTMLClueFlowStyle style;
33 HTMLListType item_type;
34 HTMLHAlignType alignment;
35 GByteArray *levels;
36 };
37 typedef struct _ClueFlowProps ClueFlowProps;
38
39 /* Data for the redo/undo operation. */
40 struct _ClueFlowStyleOperation {
41 HTMLUndoData data;
42
43 /* Whether we should go backward or forward when re-setting
44 * the style. */
45 gboolean forward;
46
47 /* List of properties for the paragraphs (ClueFlowProps). */
48 GList *prop_list;
49 };
50 typedef struct _ClueFlowStyleOperation ClueFlowStyleOperation;
51
52 static void
free_prop(ClueFlowProps * props)53 free_prop (ClueFlowProps *props)
54 {
55 g_byte_array_free (props->levels, TRUE);
56 g_free (props);
57 }
58
59 static void
free_prop_list(GList * list)60 free_prop_list (GList *list)
61 {
62 GList *p;
63
64 for (p = list; p != NULL; p = p->next) {
65 ClueFlowProps *props;
66
67 props = (ClueFlowProps *) p->data;
68 free_prop (props);
69 }
70
71 g_list_free (list);
72 }
73
74 static void
style_operation_destroy(HTMLUndoData * data)75 style_operation_destroy (HTMLUndoData *data)
76 {
77 free_prop_list (((ClueFlowStyleOperation *) data)->prop_list);
78 }
79
80 static ClueFlowStyleOperation *
style_operation_new(GList * prop_list,gboolean forward)81 style_operation_new (GList *prop_list,
82 gboolean forward)
83 {
84 ClueFlowStyleOperation *op;
85
86 op = g_new (ClueFlowStyleOperation, 1);
87
88 html_undo_data_init (HTML_UNDO_DATA (op));
89
90 op->data.destroy = style_operation_destroy;
91 op->prop_list = prop_list;
92 op->forward = forward;
93
94 return op;
95 }
96
97 static ClueFlowProps *
get_props(HTMLClueFlow * clueflow)98 get_props (HTMLClueFlow *clueflow)
99 {
100 ClueFlowProps *props;
101
102 props = g_new (ClueFlowProps, 1);
103
104 props->levels = html_clueflow_dup_levels (clueflow);
105 props->alignment = html_clueflow_get_halignment (clueflow);
106 props->style = html_clueflow_get_style (clueflow);
107 props->item_type = html_clueflow_get_item_type (clueflow);
108
109 return props;
110 }
111
112 static void
set_props(HTMLEngine * engine,HTMLClueFlow * clueflow,HTMLClueFlowStyle style,HTMLListType item_type,HTMLHAlignType alignment,gint indentation_delta,guint8 * indentation_levels,HTMLEngineSetClueFlowStyleMask mask)113 set_props (HTMLEngine *engine,
114 HTMLClueFlow *clueflow,
115 HTMLClueFlowStyle style,
116 HTMLListType item_type,
117 HTMLHAlignType alignment,
118 gint indentation_delta,
119 guint8 *indentation_levels,
120 HTMLEngineSetClueFlowStyleMask mask)
121 {
122 if (mask & HTML_ENGINE_SET_CLUEFLOW_INDENTATION)
123 html_clueflow_set_indentation (clueflow, engine, indentation_delta, indentation_levels);
124
125 if (mask & HTML_ENGINE_SET_CLUEFLOW_INDENTATION_DELTA)
126 html_clueflow_modify_indentation_by_delta (clueflow, engine, indentation_delta, indentation_levels);
127
128 /* FIXME levels mostly work now */
129 if (mask & HTML_ENGINE_SET_CLUEFLOW_STYLE) {
130 if (style == HTML_CLUEFLOW_STYLE_LIST_ITEM && clueflow->style != HTML_CLUEFLOW_STYLE_LIST_ITEM
131 && clueflow->levels->len == 0
132 && !(mask & (HTML_ENGINE_SET_CLUEFLOW_INDENTATION | HTML_ENGINE_SET_CLUEFLOW_INDENTATION_DELTA))) {
133 guint8 tmp = item_type;
134 html_clueflow_set_indentation (clueflow, engine, 1, &tmp);
135 } else if (clueflow->style == HTML_CLUEFLOW_STYLE_LIST_ITEM && style != HTML_CLUEFLOW_STYLE_LIST_ITEM
136 && clueflow->levels->len == 1
137 && !(mask & (HTML_ENGINE_SET_CLUEFLOW_INDENTATION | HTML_ENGINE_SET_CLUEFLOW_INDENTATION_DELTA))) {
138 html_clueflow_set_indentation (clueflow, engine, 0, NULL);
139 }
140 html_clueflow_set_style (clueflow, engine, style);
141 html_clueflow_set_item_type (clueflow, engine, item_type);
142 html_object_change_set_down (HTML_OBJECT (clueflow), HTML_CHANGE_ALL);
143 }
144 if (mask & HTML_ENGINE_SET_CLUEFLOW_ALIGNMENT)
145 html_clueflow_set_halignment (clueflow, engine, alignment);
146
147 }
148
149
150 /* Undo/redo operations. */
151
152 static void add_undo (HTMLEngine *engine, ClueFlowStyleOperation *op, HTMLUndoDirection dir);
153
154 static void
undo_or_redo(HTMLEngine * engine,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)155 undo_or_redo (HTMLEngine *engine,
156 HTMLUndoData *data,
157 HTMLUndoDirection dir,
158 guint position_after)
159 {
160 ClueFlowStyleOperation *op, *new_op;
161 ClueFlowProps *props, *orig_props;
162 HTMLObject *obj;
163 HTMLClueFlow *clueflow;
164 GList *prop_list;
165 GList *p;
166
167 op = (ClueFlowStyleOperation *) data;
168 g_assert (op != NULL);
169 g_assert (op->prop_list != NULL);
170
171 obj = engine->cursor->object;
172 g_assert (obj != NULL);
173
174 prop_list = NULL;
175
176 p = op->prop_list;
177
178 while (p != NULL && obj != NULL) {
179 if (HTML_OBJECT_TYPE (obj->parent) != HTML_TYPE_CLUEFLOW) {
180 g_warning ("(%s:%s) Eeeek! Unknown parent type `%s'.",
181 __FILE__, G_STRFUNC,
182 html_type_name (HTML_OBJECT_TYPE (obj->parent)));
183 break;
184 }
185
186 clueflow = HTML_CLUEFLOW (obj->parent);
187
188 orig_props = get_props (clueflow);
189 prop_list = g_list_prepend (prop_list, orig_props);
190
191 props = (ClueFlowProps *) p->data;
192
193 html_clueflow_set_levels (clueflow, engine, props->levels);
194 html_clueflow_set_style (clueflow, engine, props->style);
195 html_clueflow_set_item_type (clueflow, engine, props->item_type);
196 html_clueflow_set_levels (clueflow, engine, props->levels);
197 html_clueflow_set_halignment (clueflow, engine, props->alignment);
198
199 p = p->next;
200 if (p == NULL)
201 break;
202
203 /* Go forward object by object, until we find one
204 * whose parent (i.e. paragraph) is different. */
205 do {
206 if (op->forward)
207 obj = html_object_next_leaf (obj);
208 else
209 obj = html_object_prev_leaf (obj);
210
211 if (obj == NULL) {
212 /* This should not happen. */
213 g_warning ("(%s:%s) There were not enough paragraphs for "
214 "setting the paragraph style.",
215 __FILE__, G_STRFUNC);
216 break;
217 }
218 } while (obj != NULL && HTML_CLUEFLOW (obj->parent) == clueflow);
219 }
220
221 if (prop_list == NULL) {
222 /* This should not happen. */
223 g_warning ("%s:%s Eeek! Nothing done?", __FILE__, G_STRFUNC);
224 return;
225 }
226
227 prop_list = g_list_reverse (prop_list);
228
229 new_op = style_operation_new (prop_list, op->forward);
230
231 add_undo (engine, new_op, html_undo_direction_reverse (dir));
232 }
233
234 static HTMLUndoAction *
undo_action_from_op(HTMLEngine * engine,ClueFlowStyleOperation * op)235 undo_action_from_op (HTMLEngine *engine,
236 ClueFlowStyleOperation *op)
237 {
238 return html_undo_action_new ("Paragraph style change",
239 undo_or_redo, HTML_UNDO_DATA (op),
240 html_cursor_get_position (engine->cursor),
241 html_cursor_get_position (engine->cursor));
242 }
243
244 static void
add_undo(HTMLEngine * engine,ClueFlowStyleOperation * op,HTMLUndoDirection dir)245 add_undo (HTMLEngine *engine,
246 ClueFlowStyleOperation *op,
247 HTMLUndoDirection dir)
248 {
249 html_undo_add_action (engine->undo, engine, undo_action_from_op (engine, op), dir);
250 }
251
252
253 /* "Do" operations. */
254
255 static void
set_clueflow_style_in_region(HTMLEngine * engine,HTMLClueFlowStyle style,HTMLListType item_type,HTMLHAlignType alignment,gint indentation_delta,guint8 * indentation_levels,HTMLEngineSetClueFlowStyleMask mask,HTMLUndoDirection dir,gboolean do_undo)256 set_clueflow_style_in_region (HTMLEngine *engine,
257 HTMLClueFlowStyle style,
258 HTMLListType item_type,
259 HTMLHAlignType alignment,
260 gint indentation_delta,
261 guint8 *indentation_levels,
262 HTMLEngineSetClueFlowStyleMask mask,
263 HTMLUndoDirection dir,
264 gboolean do_undo)
265 {
266 HTMLClueFlow *clueflow;
267 HTMLObject *start, *end, *p;
268 GList *prop_list;
269 gboolean undo_forward;
270
271 if (html_cursor_precedes (engine->cursor, engine->mark)) {
272 start = engine->cursor->object;
273 end = engine->mark->object;
274 undo_forward = TRUE;
275 } else {
276 start = engine->mark->object;
277 end = engine->cursor->object;
278 undo_forward = FALSE;
279 }
280
281 prop_list = NULL;
282
283 p = start;
284 while (p != NULL) {
285 if (HTML_OBJECT_TYPE (p->parent) != HTML_TYPE_CLUEFLOW) {
286 g_warning ("(%s:%s) Eeeek! Unknown parent type `%s'.",
287 __FILE__, G_STRFUNC,
288 html_type_name (HTML_OBJECT_TYPE (p->parent)));
289 break;
290 }
291
292 clueflow = HTML_CLUEFLOW (p->parent);
293
294 if (do_undo)
295 prop_list = g_list_prepend (prop_list, get_props (clueflow));
296
297 set_props (engine, clueflow,
298 style, item_type, alignment, indentation_delta, indentation_levels,
299 mask);
300
301 if (p == end)
302 break;
303
304 do {
305 p = html_object_next_leaf (p);
306 } while (p != NULL && p != end && HTML_CLUEFLOW (p->parent) == clueflow);
307
308 if (p == end && HTML_CLUEFLOW (p->parent) == clueflow)
309 break;
310 }
311
312 if (!do_undo)
313 return;
314
315 add_undo (engine, style_operation_new (undo_forward ? g_list_reverse (prop_list) : prop_list, undo_forward), dir);
316 }
317
318 static void
set_clueflow_style_at_cursor(HTMLEngine * engine,HTMLClueFlowStyle style,HTMLListType item_type,HTMLHAlignType alignment,gint indentation_delta,guint8 * indentation_levels,HTMLEngineSetClueFlowStyleMask mask,HTMLUndoDirection dir,gboolean do_undo)319 set_clueflow_style_at_cursor (HTMLEngine *engine,
320 HTMLClueFlowStyle style,
321 HTMLListType item_type,
322 HTMLHAlignType alignment,
323 gint indentation_delta,
324 guint8 *indentation_levels,
325 HTMLEngineSetClueFlowStyleMask mask,
326 HTMLUndoDirection dir,
327 gboolean do_undo)
328 {
329 HTMLClueFlow *clueflow;
330 HTMLObject *curr;
331
332 curr = engine->cursor->object;
333
334 g_return_if_fail (curr != NULL);
335 g_return_if_fail (curr->parent != NULL);
336 g_return_if_fail (HTML_OBJECT_TYPE (curr->parent) == HTML_TYPE_CLUEFLOW);
337
338 clueflow = HTML_CLUEFLOW (curr->parent);
339
340 if (do_undo)
341 add_undo (engine, style_operation_new (g_list_append (NULL, get_props (clueflow)), TRUE), dir);
342
343 set_props (engine, clueflow,
344 style, item_type, alignment,
345 indentation_delta,
346 indentation_levels,
347 mask);
348
349 }
350
351
352 gboolean
html_engine_set_clueflow_style(HTMLEngine * engine,HTMLClueFlowStyle style,HTMLListType item_type,HTMLHAlignType alignment,gint indentation_delta,guint8 * indentation_levels,HTMLEngineSetClueFlowStyleMask mask,HTMLUndoDirection dir,gboolean do_undo)353 html_engine_set_clueflow_style (HTMLEngine *engine,
354 HTMLClueFlowStyle style,
355 HTMLListType item_type,
356 HTMLHAlignType alignment,
357 gint indentation_delta,
358 guint8 *indentation_levels,
359 HTMLEngineSetClueFlowStyleMask mask,
360 HTMLUndoDirection dir,
361 gboolean do_undo)
362 {
363 g_return_val_if_fail (engine != NULL, FALSE);
364 g_return_val_if_fail (HTML_IS_ENGINE (engine), FALSE);
365
366 html_engine_freeze (engine);
367 if (html_engine_is_selection_active (engine))
368 set_clueflow_style_in_region (engine,
369 style, item_type, alignment,
370 indentation_delta, indentation_levels,
371 mask,
372 dir, do_undo);
373 else
374 set_clueflow_style_at_cursor (engine,
375 style, item_type, alignment,
376 indentation_delta, indentation_levels,
377 mask,
378 dir, do_undo);
379 html_engine_thaw (engine);
380
381 /* This operation can never fail. */
382 return TRUE;
383 }
384
385
386 /* The following functions are used to report the current indentation
387 * as it should be shown e.g in a toolbar. */
388
389 static HTMLClueFlow *
get_current_para(HTMLEngine * engine)390 get_current_para (HTMLEngine *engine)
391 {
392 HTMLObject *current;
393 HTMLObject *parent;
394
395 current = engine->cursor->object;
396 if (current == NULL)
397 return NULL;
398
399 parent = current->parent;
400 if (parent == NULL)
401 return NULL;
402
403 if (HTML_OBJECT_TYPE (parent) != HTML_TYPE_CLUEFLOW)
404 return NULL;
405
406 return HTML_CLUEFLOW (parent);
407 }
408
409 void
html_engine_get_current_clueflow_style(HTMLEngine * engine,HTMLClueFlowStyle * style,HTMLListType * item_type)410 html_engine_get_current_clueflow_style (HTMLEngine *engine,
411 HTMLClueFlowStyle *style,
412 HTMLListType *item_type)
413 {
414 HTMLClueFlow *para;
415
416 /* FIXME TODO region */
417
418 *style = HTML_CLUEFLOW_STYLE_NORMAL;
419 *item_type = HTML_LIST_TYPE_UNORDERED;
420
421 g_return_if_fail (engine != NULL);
422 g_return_if_fail (HTML_IS_ENGINE (engine));
423
424 para = get_current_para (engine);
425 if (para) {
426 *style = para->style;
427 *item_type = para->item_type;
428 }
429 }
430
431 guint
html_engine_get_current_clueflow_indentation(HTMLEngine * engine)432 html_engine_get_current_clueflow_indentation (HTMLEngine *engine)
433 {
434 HTMLClueFlow *para;
435
436 /* FIXME TODO region */
437
438 g_return_val_if_fail (engine != NULL, 0);
439 g_return_val_if_fail (HTML_IS_ENGINE (engine), 0);
440
441 para = get_current_para (engine);
442 if (para == NULL)
443 return 0;
444
445 /* FIXME levels TODO levels */
446 return para->levels->len;
447 }
448
449 HTMLHAlignType
html_engine_get_current_clueflow_alignment(HTMLEngine * engine)450 html_engine_get_current_clueflow_alignment (HTMLEngine *engine)
451 {
452 HTMLClueFlow *para;
453
454 /* FIXME TODO region */
455
456 g_return_val_if_fail (engine != NULL, HTML_HALIGN_LEFT);
457 g_return_val_if_fail (HTML_IS_ENGINE (engine), HTML_HALIGN_LEFT);
458
459 para = get_current_para (engine);
460 if (para == NULL)
461 return 0;
462
463 return html_clueflow_get_halignment (para);
464 }
465