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) 2001 Ximian, Inc.
5 * Authors: Radek Doulik
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include <config.h>
24 #include "htmlcursor.h"
25 #include "htmlengine.h"
26 #include "htmlengine-edit.h"
27 #include "htmlengine-edit-cut-and-paste.h"
28 #include "htmlengine-edit-table.h"
29 #include "htmlengine-edit-tablecell.h"
30 #include "htmlobject.h"
31 #include "htmltable.h"
32 #include "htmltablecell.h"
33 #include "htmlundo.h"
34
35 HTMLTableCell *
html_engine_get_table_cell(HTMLEngine * e)36 html_engine_get_table_cell (HTMLEngine *e)
37 {
38 g_assert (HTML_IS_ENGINE (e));
39
40 if (!e->cursor->object->parent || !e->cursor->object->parent->parent
41 || !HTML_IS_TABLE_CELL (e->cursor->object->parent->parent))
42 return NULL;
43
44 return HTML_TABLE_CELL (e->cursor->object->parent->parent);
45 }
46
47 typedef enum {
48 HTML_TABLE_CELL_BGCOLOR,
49 HTML_TABLE_CELL_BGPIXMAP,
50 HTML_TABLE_CELL_NOWRAP,
51 HTML_TABLE_CELL_HEADING,
52 HTML_TABLE_CELL_HALIGN,
53 HTML_TABLE_CELL_VALIGN,
54 HTML_TABLE_CELL_WIDTH
55 } HTMLTableCellAttrType;
56
57 union _HTMLTableCellUndoAttr {
58 gboolean no_wrap;
59 gboolean heading;
60
61 gchar *pixmap;
62
63 struct {
64 gint width;
65 gboolean percent;
66 } width;
67
68 struct {
69 GdkColor color;
70 gboolean has_bg_color;
71 } color;
72
73 HTMLHAlignType halign;
74 HTMLVAlignType valign;
75 };
76 typedef union _HTMLTableCellUndoAttr HTMLTableCellUndoAttr;
77
78 struct _HTMLTableCellSetAttrUndo {
79 HTMLUndoData data;
80
81 HTMLTableCellUndoAttr attr;
82 HTMLTableCellAttrType type;
83 };
84 typedef struct _HTMLTableCellSetAttrUndo HTMLTableCellSetAttrUndo;
85
86 static void
attr_destroy(HTMLUndoData * undo_data)87 attr_destroy (HTMLUndoData *undo_data)
88 {
89 HTMLTableCellSetAttrUndo *data = (HTMLTableCellSetAttrUndo *) undo_data;
90
91 switch (data->type) {
92 case HTML_TABLE_CELL_BGPIXMAP:
93 g_free (data->attr.pixmap);
94 break;
95 default:
96 ;
97 }
98 }
99
100 static HTMLTableCellSetAttrUndo *
attr_undo_new(HTMLTableCellAttrType type)101 attr_undo_new (HTMLTableCellAttrType type)
102 {
103 HTMLTableCellSetAttrUndo *undo = g_new (HTMLTableCellSetAttrUndo, 1);
104
105 html_undo_data_init (HTML_UNDO_DATA (undo));
106 undo->data.destroy = attr_destroy;
107 undo->type = type;
108
109 return undo;
110 }
111
112 /*
113 * bg color
114 *
115 */
116
117 static void table_cell_set_bg_color (HTMLEngine *e, HTMLTableCell *cell, GdkColor *c, HTMLUndoDirection dir);
118
119 static void
table_cell_set_bg_color_undo_action(HTMLEngine * e,HTMLUndoData * undo_data,HTMLUndoDirection dir,guint position_after)120 table_cell_set_bg_color_undo_action (HTMLEngine *e,
121 HTMLUndoData *undo_data,
122 HTMLUndoDirection dir,
123 guint position_after)
124 {
125 HTMLTableCellSetAttrUndo *data = (HTMLTableCellSetAttrUndo *) undo_data;
126
127 table_cell_set_bg_color (e, html_engine_get_table_cell (e), data->attr.color.has_bg_color
128 ? &data->attr.color.color : NULL, html_undo_direction_reverse (dir));
129 }
130
131 static void
table_cell_set_bg_color(HTMLEngine * e,HTMLTableCell * cell,GdkColor * c,HTMLUndoDirection dir)132 table_cell_set_bg_color (HTMLEngine *e,
133 HTMLTableCell *cell,
134 GdkColor *c,
135 HTMLUndoDirection dir)
136 {
137 HTMLTableCellSetAttrUndo *undo;
138
139 undo = attr_undo_new (HTML_TABLE_CELL_BGCOLOR);
140 undo->attr.color.color = cell->bg;
141 undo->attr.color.has_bg_color = cell->have_bg;
142 html_undo_add_action (e->undo, e,
143 html_undo_action_new ("Set cell background color", table_cell_set_bg_color_undo_action,
144 HTML_UNDO_DATA (undo),
145 html_cursor_get_position (e->cursor),
146 html_cursor_get_position (e->cursor)), dir);
147
148 html_object_set_bg_color (HTML_OBJECT (cell), c);
149 html_engine_queue_draw (e, HTML_OBJECT (cell));
150 }
151
152 void
html_engine_table_cell_set_bg_color(HTMLEngine * e,HTMLTableCell * cell,GdkColor * c)153 html_engine_table_cell_set_bg_color (HTMLEngine *e,
154 HTMLTableCell *cell,
155 GdkColor *c)
156 {
157 table_cell_set_bg_color (e, cell, c, HTML_UNDO_UNDO);
158 }
159
160 /*
161 * bg pixmap
162 *
163 */
164
165 static void table_cell_set_bg_pixmap (HTMLEngine *e, HTMLTableCell *cell, gchar *url, HTMLUndoDirection dir);
166
167 static void
table_cell_set_bg_pixmap_undo_action(HTMLEngine * e,HTMLUndoData * undo_data,HTMLUndoDirection dir,guint position_after)168 table_cell_set_bg_pixmap_undo_action (HTMLEngine *e,
169 HTMLUndoData *undo_data,
170 HTMLUndoDirection dir,
171 guint position_after)
172 {
173 HTMLTableCellSetAttrUndo *data = (HTMLTableCellSetAttrUndo *) undo_data;
174
175 table_cell_set_bg_pixmap (e, html_engine_get_table_cell (e), data->attr.pixmap, html_undo_direction_reverse (dir));
176 }
177
178 static void
table_cell_set_bg_pixmap(HTMLEngine * e,HTMLTableCell * cell,gchar * url,HTMLUndoDirection dir)179 table_cell_set_bg_pixmap (HTMLEngine *e,
180 HTMLTableCell *cell,
181 gchar *url,
182 HTMLUndoDirection dir)
183 {
184 HTMLImagePointer *iptr;
185 HTMLTableCellSetAttrUndo *undo;
186
187 undo = attr_undo_new (HTML_TABLE_CELL_BGPIXMAP);
188 undo->attr.pixmap = cell->have_bgPixmap ? g_strdup (cell->bgPixmap->url) : NULL;
189 html_undo_add_action (e->undo, e,
190 html_undo_action_new ("Set cell background pixmap", table_cell_set_bg_pixmap_undo_action,
191 HTML_UNDO_DATA (undo),
192 html_cursor_get_position (e->cursor),
193 html_cursor_get_position (e->cursor)), dir);
194
195 iptr = cell->bgPixmap;
196 cell->bgPixmap = url ? html_image_factory_register (e->image_factory, NULL, url, TRUE) : NULL;
197 if (cell->have_bgPixmap && iptr)
198 html_image_factory_unregister (e->image_factory, iptr, NULL);
199 cell->have_bgPixmap = url ? TRUE : FALSE;
200 html_engine_queue_draw (e, HTML_OBJECT (cell));
201 }
202
203 void
html_engine_table_cell_set_bg_pixmap(HTMLEngine * e,HTMLTableCell * cell,gchar * url)204 html_engine_table_cell_set_bg_pixmap (HTMLEngine *e,
205 HTMLTableCell *cell,
206 gchar *url)
207 {
208 table_cell_set_bg_pixmap (e, cell, url, HTML_UNDO_UNDO);
209 }
210
211 /*
212 * halign
213 *
214 */
215
216 static void table_cell_set_halign (HTMLEngine *e, HTMLTableCell *cell, HTMLHAlignType halign, HTMLUndoDirection dir);
217
218 static void
table_cell_set_halign_undo_action(HTMLEngine * e,HTMLUndoData * undo_data,HTMLUndoDirection dir,guint position_after)219 table_cell_set_halign_undo_action (HTMLEngine *e,
220 HTMLUndoData *undo_data,
221 HTMLUndoDirection dir,
222 guint position_after)
223 {
224 HTMLTableCellSetAttrUndo *data = (HTMLTableCellSetAttrUndo *) undo_data;
225
226 table_cell_set_halign (e, html_engine_get_table_cell (e), data->attr.halign, html_undo_direction_reverse (dir));
227 }
228
229 static void
table_cell_set_halign(HTMLEngine * e,HTMLTableCell * cell,HTMLHAlignType halign,HTMLUndoDirection dir)230 table_cell_set_halign (HTMLEngine *e,
231 HTMLTableCell *cell,
232 HTMLHAlignType halign,
233 HTMLUndoDirection dir)
234 {
235 HTMLTableCellSetAttrUndo *undo;
236
237 undo = attr_undo_new (HTML_TABLE_CELL_HALIGN);
238 undo->attr.halign = HTML_CLUE (cell)->halign;
239 html_undo_add_action (e->undo, e,
240 html_undo_action_new ("Set cell horizontal align", table_cell_set_halign_undo_action,
241 HTML_UNDO_DATA (undo),
242 html_cursor_get_position (e->cursor),
243 html_cursor_get_position (e->cursor)), dir);
244
245 HTML_CLUE (cell)->halign = halign;
246 html_engine_schedule_update (e);
247 }
248
249 void
html_engine_table_cell_set_halign(HTMLEngine * e,HTMLTableCell * cell,HTMLHAlignType halign)250 html_engine_table_cell_set_halign (HTMLEngine *e,
251 HTMLTableCell *cell,
252 HTMLHAlignType halign)
253 {
254 table_cell_set_halign (e, cell, halign, HTML_UNDO_UNDO);
255 }
256
257 /*
258 * valign
259 *
260 */
261
262 static void table_cell_set_valign (HTMLEngine *e, HTMLTableCell *cell, HTMLVAlignType valign, HTMLUndoDirection dir);
263
264 static void
table_cell_set_valign_undo_action(HTMLEngine * e,HTMLUndoData * undo_data,HTMLUndoDirection dir,guint position_after)265 table_cell_set_valign_undo_action (HTMLEngine *e,
266 HTMLUndoData *undo_data,
267 HTMLUndoDirection dir,
268 guint position_after)
269 {
270 HTMLTableCellSetAttrUndo *data = (HTMLTableCellSetAttrUndo *) undo_data;
271
272 table_cell_set_valign (e, html_engine_get_table_cell (e), data->attr.valign, html_undo_direction_reverse (dir));
273 }
274
275 static void
table_cell_set_valign(HTMLEngine * e,HTMLTableCell * cell,HTMLVAlignType valign,HTMLUndoDirection dir)276 table_cell_set_valign (HTMLEngine *e,
277 HTMLTableCell *cell,
278 HTMLVAlignType valign,
279 HTMLUndoDirection dir)
280 {
281 HTMLTableCellSetAttrUndo *undo;
282
283 undo = attr_undo_new (HTML_TABLE_CELL_VALIGN);
284 undo->attr.valign = HTML_CLUE (cell)->valign;
285 html_undo_add_action (e->undo, e,
286 html_undo_action_new ("Set cell vertical align", table_cell_set_valign_undo_action,
287 HTML_UNDO_DATA (undo),
288 html_cursor_get_position (e->cursor),
289 html_cursor_get_position (e->cursor)), dir);
290
291 HTML_CLUE (cell)->valign = valign;
292 html_engine_schedule_update (e);
293 }
294
295 void
html_engine_table_cell_set_valign(HTMLEngine * e,HTMLTableCell * cell,HTMLVAlignType valign)296 html_engine_table_cell_set_valign (HTMLEngine *e,
297 HTMLTableCell *cell,
298 HTMLVAlignType valign)
299 {
300 table_cell_set_valign (e, cell, valign, HTML_UNDO_UNDO);
301 }
302
303 /*
304 * nowrap
305 *
306 */
307
308 static void table_cell_set_no_wrap (HTMLEngine *e, HTMLTableCell *cell, gboolean no_wrap, HTMLUndoDirection dir);
309
310 static void
table_cell_set_no_wrap_undo_action(HTMLEngine * e,HTMLUndoData * undo_data,HTMLUndoDirection dir,guint position_after)311 table_cell_set_no_wrap_undo_action (HTMLEngine *e,
312 HTMLUndoData *undo_data,
313 HTMLUndoDirection dir,
314 guint position_after)
315 {
316 HTMLTableCellSetAttrUndo *data = (HTMLTableCellSetAttrUndo *) undo_data;
317
318 table_cell_set_no_wrap (e, html_engine_get_table_cell (e), data->attr.no_wrap, html_undo_direction_reverse (dir));
319 }
320
321 static void
table_cell_set_no_wrap(HTMLEngine * e,HTMLTableCell * cell,gboolean no_wrap,HTMLUndoDirection dir)322 table_cell_set_no_wrap (HTMLEngine *e,
323 HTMLTableCell *cell,
324 gboolean no_wrap,
325 HTMLUndoDirection dir)
326 {
327 if (cell->no_wrap != no_wrap) {
328 HTMLTableCellSetAttrUndo *undo;
329
330 undo = attr_undo_new (HTML_TABLE_CELL_NOWRAP);
331 undo->attr.no_wrap = cell->no_wrap;
332 html_undo_add_action (e->undo, e,
333 html_undo_action_new ("Set cell wrapping", table_cell_set_no_wrap_undo_action,
334 HTML_UNDO_DATA (undo),
335 html_cursor_get_position (e->cursor),
336 html_cursor_get_position (e->cursor)), dir);
337 cell->no_wrap = no_wrap;
338 html_object_change_set (HTML_OBJECT (cell), HTML_CHANGE_ALL_CALC);
339 html_engine_schedule_update (e);
340 }
341 }
342
343 void
html_engine_table_cell_set_no_wrap(HTMLEngine * e,HTMLTableCell * cell,gboolean no_wrap)344 html_engine_table_cell_set_no_wrap (HTMLEngine *e,
345 HTMLTableCell *cell,
346 gboolean no_wrap)
347 {
348 table_cell_set_no_wrap (e, cell, no_wrap, HTML_UNDO_UNDO);
349 }
350
351 /*
352 * heading
353 *
354 */
355
356 static void table_cell_set_heading (HTMLEngine *e, HTMLTableCell *cell, gboolean heading, HTMLUndoDirection dir);
357
358 static void
table_cell_set_heading_undo_action(HTMLEngine * e,HTMLUndoData * undo_data,HTMLUndoDirection dir,guint position_after)359 table_cell_set_heading_undo_action (HTMLEngine *e,
360 HTMLUndoData *undo_data,
361 HTMLUndoDirection dir,
362 guint position_after)
363 {
364 HTMLTableCellSetAttrUndo *data = (HTMLTableCellSetAttrUndo *) undo_data;
365
366 table_cell_set_heading (e, html_engine_get_table_cell (e), data->attr.heading, html_undo_direction_reverse (dir));
367 }
368
369 static void
table_cell_set_heading(HTMLEngine * e,HTMLTableCell * cell,gboolean heading,HTMLUndoDirection dir)370 table_cell_set_heading (HTMLEngine *e,
371 HTMLTableCell *cell,
372 gboolean heading,
373 HTMLUndoDirection dir)
374 {
375 if (cell->heading != heading) {
376 HTMLTableCellSetAttrUndo *undo;
377
378 undo = attr_undo_new (HTML_TABLE_CELL_HEADING);
379 undo->attr.heading = cell->heading;
380 html_undo_add_action (e->undo, e,
381 html_undo_action_new ("Set cell style", table_cell_set_heading_undo_action,
382 HTML_UNDO_DATA (undo),
383 html_cursor_get_position (e->cursor),
384 html_cursor_get_position (e->cursor)), dir);
385
386 cell->heading = heading;
387 html_object_change_set (HTML_OBJECT (cell), HTML_CHANGE_ALL_CALC);
388 html_object_change_set_down (HTML_OBJECT (cell), HTML_CHANGE_ALL);
389 html_engine_schedule_update (e);
390 }
391 }
392
393 void
html_engine_table_cell_set_heading(HTMLEngine * e,HTMLTableCell * cell,gboolean heading)394 html_engine_table_cell_set_heading (HTMLEngine *e,
395 HTMLTableCell *cell,
396 gboolean heading)
397 {
398 table_cell_set_heading (e, cell, heading, HTML_UNDO_UNDO);
399 }
400
401 /*
402 * width
403 *
404 */
405
406 static void table_cell_set_width (HTMLEngine *e, HTMLTableCell *cell, gint width, gboolean percent, HTMLUndoDirection dir);
407 static void
table_cell_set_width_undo_action(HTMLEngine * e,HTMLUndoData * undo_data,HTMLUndoDirection dir,guint position_after)408 table_cell_set_width_undo_action (HTMLEngine *e,
409 HTMLUndoData *undo_data,
410 HTMLUndoDirection dir,
411 guint position_after)
412 {
413 HTMLTableCellSetAttrUndo *data = (HTMLTableCellSetAttrUndo *) undo_data;
414
415 table_cell_set_width (e, html_engine_get_table_cell (e),
416 data->attr.width.width, data->attr.width.percent, html_undo_direction_reverse (dir));
417 }
418
419 static void
table_cell_set_width(HTMLEngine * e,HTMLTableCell * cell,gint width,gboolean percent,HTMLUndoDirection dir)420 table_cell_set_width (HTMLEngine *e,
421 HTMLTableCell *cell,
422 gint width,
423 gboolean percent,
424 HTMLUndoDirection dir)
425 {
426 if (cell->percent_width != percent || cell->fixed_width != width) {
427 HTMLTableCellSetAttrUndo *undo;
428
429 undo = attr_undo_new (HTML_TABLE_CELL_WIDTH);
430 undo->attr.width.width = cell->fixed_width;
431 undo->attr.width.percent = cell->percent_width;
432 html_undo_add_action (e->undo, e,
433 html_undo_action_new ("Set cell style", table_cell_set_width_undo_action,
434 HTML_UNDO_DATA (undo),
435 html_cursor_get_position (e->cursor),
436 html_cursor_get_position (e->cursor)), dir);
437
438 cell->percent_width = percent;
439 cell->fixed_width = width;
440 if (width && !percent)
441 HTML_OBJECT (cell)->flags |= HTML_OBJECT_FLAG_FIXEDWIDTH;
442 else
443 HTML_OBJECT (cell)->flags &= ~ HTML_OBJECT_FLAG_FIXEDWIDTH;
444 html_object_change_set (HTML_OBJECT (cell), HTML_CHANGE_ALL_CALC);
445 html_engine_schedule_update (e);
446 }
447 }
448
449 void
html_engine_table_cell_set_width(HTMLEngine * e,HTMLTableCell * cell,gint width,gboolean percent)450 html_engine_table_cell_set_width (HTMLEngine *e,
451 HTMLTableCell *cell,
452 gint width,
453 gboolean percent)
454 {
455 table_cell_set_width (e, cell, width, percent, HTML_UNDO_UNDO);
456 }
457
458 void
html_engine_delete_table_cell_contents(HTMLEngine * e)459 html_engine_delete_table_cell_contents (HTMLEngine *e)
460 {
461 HTMLTableCell *cell;
462
463 cell = html_engine_get_table_cell (e);
464 if (!cell)
465 return;
466
467 html_engine_prev_cell (e);
468 html_cursor_forward (e->cursor, e);
469 html_engine_set_mark (e);
470 html_engine_next_cell (e, FALSE);
471 html_cursor_backward (e->cursor, e);
472 html_engine_delete (e);
473 }
474
475 static void collapse_cspan (HTMLEngine *e, HTMLTableCell *cell, gint cspan, HTMLUndoDirection dir);
476 static void collapse_rspan (HTMLEngine *e, HTMLTableCell *cell, gint rspan, HTMLUndoDirection dir);
477
478 struct Move {
479 gboolean move;
480 gint rs, cs, rt, ct;
481 };
482 struct MoveCellRDUndo {
483 gint rspan, cspan;
484
485 struct Move *moved;
486 HTMLTableCell **removed;
487
488 struct Move move;
489 };
490
491 static struct MoveCellRDUndo *
move_cell_rd_undo_new(gint rspan,gint cspan)492 move_cell_rd_undo_new (gint rspan,
493 gint cspan)
494 {
495 struct MoveCellRDUndo *undo;
496
497 undo = g_new (struct MoveCellRDUndo, 1);
498 undo->rspan = rspan;
499 undo->cspan = cspan;
500 undo->moved = g_new0 (struct Move, rspan * cspan);
501 undo->removed = g_new0 (HTMLTableCell *, rspan * cspan);
502
503 return undo;
504 }
505
506 static void
move_cell_rd_undo_free(struct MoveCellRDUndo * undo)507 move_cell_rd_undo_free (struct MoveCellRDUndo *undo)
508 {
509 gint i;
510
511 for (i = 0; i < undo->rspan * undo->cspan; i++)
512 if (undo->removed[i])
513 html_object_destroy (HTML_OBJECT (undo->removed[i]));
514 g_free (undo->removed);
515 g_free (undo->moved);
516 }
517
518 static struct MoveCellRDUndo *
move_cell_rd(HTMLTable * t,HTMLTableCell * cell,gint rs,gint cs)519 move_cell_rd (HTMLTable *t,
520 HTMLTableCell *cell,
521 gint rs,
522 gint cs)
523 {
524 struct MoveCellRDUndo *undo;
525 gint r, c;
526
527 g_assert ((rs == 0 && cs > 0) || (cs == 0 && rs > 0));
528
529 undo = move_cell_rd_undo_new (cell->rspan, cell->cspan);
530 /* printf ("move %dx%d --> %dx%d\n", cell->row, cell->col, cell->row + rs, cell->col + cs); */
531 for (r = cell->row + cell->rspan - 1; r >= cell->row; r--)
532 for (c = cell->col + cell->cspan - 1; c >= cell->col; c--) {
533 if (r > cell->row + cell->rspan - 1 - rs || c > cell->col + cell->cspan - 1 - cs) {
534 gint nr = rs + r - (rs ? cell->rspan : 0), nc = cs + c - (cs ? cell->cspan : 0);
535
536 /* printf ("exchange: %dx%d <--> %dx%d (%p)\n", rs + r, cs + c, nr, nc, t->cells [rs][nc]); */
537 t->cells[nr][nc] = t->cells[rs + r][cs + c];
538 if (t->cells[nr][nc]) {
539 struct Move *move = &undo->moved[(r - cell->row) * cell->cspan + c - cell->col];
540
541 html_table_cell_set_position (t->cells[nr][nc], nr, nc);
542 move->rs = rs + r;
543 move->cs = cs + c;
544 move->rt = nr;
545 move->ct = nc;
546 move->move = TRUE;
547 /* printf ("moved: %dx%d --> %dx%d (%d, %d) %p\n", rs + r, cs + c, nr, nc, r - cell->row, c - cell->col, t->cells [nr][nc]); */
548 }
549 } else {
550 if (r >= cell->row + rs && c >= cell->col + cs) {
551 if (t->cells[rs + r][cs + c] && t->cells[rs + r][cs + c]->col == cs + c && t->cells[rs + r][cs + c]->row == rs + r) {
552 /* printf ("move destroy: %dx%d\n", rs + r, cs + c); */
553 /* html_object_destroy (HTML_OBJECT (t->cells [rs + r][cs + c])); */
554 /* printf ("removed: %dx%d (%d, %d)\n", rs + r, cs + c, r - cell->row, c - cell->col); */
555 undo->removed[(r - cell->row) * cell->cspan + c - cell->col] = t->cells[r][c];
556 }
557 t->cells[r][c] = NULL;
558 }
559 }
560 t->cells[rs + r][cs + c] = cell;
561 /* printf ("cell %dx%d <--\n", rs + r, cs + c); */
562 }
563 /* printf ("set %dx%d --> %dx%d\n", cell->row, cell->col, cell->row + rs, cell->col + cs); */
564 undo->move.rs = cell->row;
565 undo->move.cs = cell->col;
566 undo->move.rt = cell->row + rs;
567 undo->move.ct = cell->col + cs;
568
569 html_table_cell_set_position (cell, cell->row + rs, cell->col + cs);
570
571 return undo;
572 }
573
574 struct _ExpandSpanUndo {
575 HTMLUndoData data;
576
577 gint span;
578 GSList *move_undo;
579 };
580 typedef struct _ExpandSpanUndo ExpandSpanUndo;
581 #define EXPAND_UNDO(x) ((ExpandSpanUndo *) x)
582
583 static void
expand_undo_destroy(HTMLUndoData * undo_data)584 expand_undo_destroy (HTMLUndoData *undo_data)
585 {
586 ExpandSpanUndo *data = EXPAND_UNDO (undo_data);
587 GSList *slist;
588
589 for (slist = data->move_undo; slist; slist = slist->next)
590 move_cell_rd_undo_free ((struct MoveCellRDUndo *) slist->data);
591 g_slist_free (data->move_undo);
592 }
593
594 static HTMLUndoData *
expand_undo_data_new(gint span,GSList * slist)595 expand_undo_data_new (gint span,
596 GSList *slist)
597 {
598 ExpandSpanUndo *ud = g_new0 (ExpandSpanUndo, 1);
599
600 html_undo_data_init (HTML_UNDO_DATA (ud));
601 ud->data.destroy = expand_undo_destroy;
602 ud->span = span;
603 ud->move_undo = slist;
604
605 return HTML_UNDO_DATA (ud);
606 }
607
608 static void
move_cell_rd_undo(HTMLTable * table,struct MoveCellRDUndo * undo)609 move_cell_rd_undo (HTMLTable *table,
610 struct MoveCellRDUndo *undo)
611 {
612 HTMLTableCell *cell = table->cells[undo->move.rt][undo->move.ct];
613 gint r, c;
614
615 for (r = 0; r < undo->rspan; r++)
616 for (c = 0; c < undo->cspan; c++)
617 if (undo->moved[r * undo->cspan + c].move) {
618 struct Move *move = &undo->moved[r * undo->cspan + c];
619
620 /* printf ("move back: %dx%d --> %dx%d (%d, %d) %p\n", move->rt, move->ct, move->rs, move->cs, r, c, table->cells [move->rt][move->ct]); */
621 table->cells[move->rs][move->cs] = table->cells[move->rt][move->ct];
622 html_table_cell_set_position (table->cells[move->rs][move->cs], move->rs, move->cs);
623 table->cells[move->rt][move->ct] = NULL;
624 }
625
626 for (r = 0; r < cell->rspan; r++)
627 for (c = 0; c < cell->cspan; c++)
628 table->cells[undo->move.rt + r][undo->move.ct + c] = NULL;
629 for (r = 0; r < cell->rspan; r++)
630 for (c = 0; c < cell->cspan; c++)
631 table->cells[undo->move.rs + r][undo->move.cs + c] = cell;
632
633 html_table_cell_set_position (cell, undo->move.rs, undo->move.cs);
634 }
635
636 static void
expand_cspan_undo_action(HTMLEngine * e,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)637 expand_cspan_undo_action (HTMLEngine *e,
638 HTMLUndoData *data,
639 HTMLUndoDirection dir,
640 guint position_after)
641 {
642 GSList *slist;
643
644 html_engine_freeze (e);
645 collapse_cspan (e, html_engine_get_table_cell (e), EXPAND_UNDO (data)->span, html_undo_direction_reverse (dir));
646 for (slist = EXPAND_UNDO (data)->move_undo; slist; slist = slist->next)
647 move_cell_rd_undo (html_engine_get_table (e), (struct MoveCellRDUndo *) slist->data);
648 html_engine_thaw (e);
649 }
650
651 static void
expand_cspan_setup_undo(HTMLEngine * e,GSList * slist,gint cspan,guint position_before,HTMLUndoDirection dir)652 expand_cspan_setup_undo (HTMLEngine *e,
653 GSList *slist,
654 gint cspan,
655 guint position_before,
656 HTMLUndoDirection dir)
657 {
658 html_undo_add_action (e->undo, e,
659 html_undo_action_new ("Expand Column Span", expand_cspan_undo_action,
660 expand_undo_data_new (cspan, slist), html_cursor_get_position (e->cursor),
661 position_before),
662 dir);
663 }
664
665 static void
expand_cspan(HTMLEngine * e,HTMLTableCell * cell,gint cspan,HTMLUndoDirection dir)666 expand_cspan (HTMLEngine *e,
667 HTMLTableCell *cell,
668 gint cspan,
669 HTMLUndoDirection dir)
670 {
671 HTMLTable *table = HTML_TABLE (HTML_OBJECT (cell)->parent);
672 GSList *slist = NULL;
673 guint position_before = e->cursor->position;
674 gint r, c, *move_rows, max_move, add_cols;
675
676 move_rows = g_new0 (gint, cell->rspan);
677 for (r = cell->row; r < cell->row + cell->rspan; r++)
678 for (c = cell->col + cell->cspan; c < MIN (cell->col + cspan, table->totalCols); c++)
679 if (table->cells[r][c] && !html_clue_is_empty (HTML_CLUE (table->cells[r][c])) && move_rows[r - cell->row] == 0)
680 move_rows[r - cell->row] = cspan - (c - cell->col);
681
682 max_move = 0;
683 for (r = 0; r < cell->rspan; r++)
684 if (move_rows[r] > max_move)
685 max_move = move_rows[r];
686
687 add_cols = MAX (max_move, cspan - (table->totalCols - cell->col));
688 /* printf ("max move: %d add: %d\n", max_move, add_cols); */
689 for (c = 0; c < add_cols; c++)
690 html_table_insert_column (table, e, table->totalCols, NULL, dir);
691
692 if (max_move > 0) {
693 for (c = table->totalCols - max_move - 1; c >= cell->col + cspan - max_move; c--)
694 for (r = cell->row; r < cell->row + cell->rspan; r++) {
695 HTMLTableCell *ccell = table->cells[r][c];
696
697 if (ccell && ccell->col == c) {
698 slist = g_slist_prepend (slist, move_cell_rd (table, ccell, 0, max_move));
699 r += ccell->rspan - 1;
700 }
701 }
702 }
703
704 expand_cspan_setup_undo (e, slist, cell->cspan, position_before, dir);
705 cell->cspan = cspan;
706 for (r = cell->row; r < cell->row + cell->rspan; r++)
707 for (c = cell->col; c < cell->col + cell->cspan; c++)
708 table->cells[r][c] = cell;
709
710 html_object_change_set (HTML_OBJECT (cell), HTML_CHANGE_ALL);
711 }
712
713 struct _CollapseSpanUndo {
714 HTMLUndoData data;
715
716 gint span;
717 };
718 typedef struct _CollapseSpanUndo CollapseSpanUndo;
719 #define COLLAPSE_UNDO(x) ((CollapseSpanUndo *) x)
720
721 static HTMLUndoData *
collapse_undo_data_new(gint span)722 collapse_undo_data_new (gint span)
723 {
724 CollapseSpanUndo *ud = g_new0 (CollapseSpanUndo, 1);
725
726 html_undo_data_init (HTML_UNDO_DATA (ud));
727 ud->span = span;
728
729 return HTML_UNDO_DATA (ud);
730 }
731
732 static void
collapse_cspan_undo_action(HTMLEngine * e,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)733 collapse_cspan_undo_action (HTMLEngine *e,
734 HTMLUndoData *data,
735 HTMLUndoDirection dir,
736 guint position_after)
737 {
738 html_engine_freeze (e);
739 expand_cspan (e, html_engine_get_table_cell (e), COLLAPSE_UNDO (data)->span, html_undo_direction_reverse (dir));
740 html_engine_thaw (e);
741 }
742
743 static void
collapse_cspan_setup_undo(HTMLEngine * e,gint cspan,guint position_before,HTMLUndoDirection dir)744 collapse_cspan_setup_undo (HTMLEngine *e,
745 gint cspan,
746 guint position_before,
747 HTMLUndoDirection dir)
748 {
749 html_undo_add_action (e->undo, e,
750 html_undo_action_new ("Collapse Column Span", collapse_cspan_undo_action,
751 collapse_undo_data_new (cspan), html_cursor_get_position (e->cursor),
752 position_before),
753 dir);
754 }
755
756 static void
collapse_cspan(HTMLEngine * e,HTMLTableCell * cell,gint cspan,HTMLUndoDirection dir)757 collapse_cspan (HTMLEngine *e,
758 HTMLTableCell *cell,
759 gint cspan,
760 HTMLUndoDirection dir)
761 {
762 HTMLTable *table;
763 guint position_before = e->cursor->position;
764 gint r, c;
765
766 table = HTML_TABLE (HTML_OBJECT (cell)->parent);
767 for (c = cell->col + cspan; c < cell->col + cell->cspan; c++)
768 for (r = cell->row; r < cell->row + cell->rspan; r++) {
769 table->cells[r][c] = NULL;
770 html_table_set_cell (table, r, c, html_engine_new_cell (e, table));
771 html_table_cell_set_position (table->cells[r][c], r, c);
772 }
773
774 collapse_cspan_setup_undo (e, cell->cspan, position_before, dir);
775 cell->cspan = cspan;
776 html_object_change_set (HTML_OBJECT (cell), HTML_CHANGE_ALL);
777 }
778
779 void
html_engine_set_cspan(HTMLEngine * e,gint cspan)780 html_engine_set_cspan (HTMLEngine *e,
781 gint cspan)
782 {
783 HTMLTableCell *cell = html_engine_get_table_cell (e);
784
785 g_return_if_fail (cspan > 0);
786 g_return_if_fail (cell != NULL);
787
788 if (cell->cspan == cspan)
789 return;
790
791 html_engine_freeze (e);
792 if (cspan > cell->cspan)
793 expand_cspan (e, cell, cspan, HTML_UNDO_UNDO);
794 else
795 collapse_cspan (e, cell, cspan, HTML_UNDO_UNDO);
796 html_engine_thaw (e);
797 }
798
799 static gint
calc_rspan_max_move(HTMLTableCell * cell,gint rspan)800 calc_rspan_max_move (HTMLTableCell *cell,
801 gint rspan)
802 {
803 HTMLTable *table = HTML_TABLE (HTML_OBJECT (cell)->parent);
804 gint r, c, *move_cols, max_move;
805
806 move_cols = g_new0 (gint, cell->cspan);
807 for (c = cell->col; c < cell->col + cell->cspan; c++)
808 for (r = cell->row + cell->rspan; r < MIN (cell->row + rspan, table->totalRows); r++)
809 if (table->cells[r][c] && !html_clue_is_empty (HTML_CLUE (table->cells[r][c])) && move_cols[c - cell->col] == 0)
810 move_cols[c - cell->col] = rspan - (r - cell->row);
811
812 max_move = 0;
813 for (c = 0; c < cell->cspan; c++)
814 if (move_cols[c] > max_move)
815 max_move = move_cols[c];
816 g_free (move_cols);
817
818 return max_move;
819 }
820
821 static void
expand_rspan(HTMLEngine * e,HTMLTableCell * cell,gint rspan,HTMLUndoDirection dir)822 expand_rspan (HTMLEngine *e,
823 HTMLTableCell *cell,
824 gint rspan,
825 HTMLUndoDirection dir)
826 {
827 HTMLTable *table = HTML_TABLE (HTML_OBJECT (cell)->parent);
828 GSList *slist = NULL;
829 gint r, c, max_move, add_rows;
830
831 max_move = calc_rspan_max_move (cell, rspan);
832 add_rows = MAX (max_move, rspan - (table->totalRows - cell->row));
833 /* printf ("max move: %d add: %d\n", max_move, add_rows); */
834 for (r = 0; r < add_rows; r++)
835 html_table_insert_row (table, e, table->totalRows, NULL, dir);
836
837 if (max_move > 0) {
838 for (r = table->totalRows - max_move - 1; r >= cell->row + rspan - max_move; r--)
839 for (c = cell->col; c < cell->col + cell->cspan; c++) {
840 HTMLTableCell *ccell = table->cells[r][c];
841
842 if (ccell && ccell->row == r) {
843 slist = g_slist_prepend (slist, move_cell_rd (table, ccell, max_move, 0));
844 c += ccell->cspan - 1;
845 }
846 }
847 }
848
849 cell->rspan = rspan;
850 for (r = cell->row; r < cell->row + cell->rspan; r++)
851 for (c = cell->col; c < cell->col + cell->cspan; c++)
852 table->cells[r][c] = cell;
853
854 html_object_change_set (HTML_OBJECT (cell), HTML_CHANGE_ALL);
855 }
856
857 static void
collapse_rspan_undo_action(HTMLEngine * e,HTMLUndoData * data,HTMLUndoDirection dir,guint position_after)858 collapse_rspan_undo_action (HTMLEngine *e,
859 HTMLUndoData *data,
860 HTMLUndoDirection dir,
861 guint position_after)
862 {
863 html_engine_freeze (e);
864 expand_rspan (e, html_engine_get_table_cell (e), COLLAPSE_UNDO (data)->span, html_undo_direction_reverse (dir));
865 html_engine_thaw (e);
866 }
867
868 static void
collapse_rspan_setup_undo(HTMLEngine * e,gint rspan,guint position_before,HTMLUndoDirection dir)869 collapse_rspan_setup_undo (HTMLEngine *e,
870 gint rspan,
871 guint position_before,
872 HTMLUndoDirection dir)
873 {
874 html_undo_add_action (e->undo, e,
875 html_undo_action_new ("Collapse Row Span", collapse_rspan_undo_action,
876 collapse_undo_data_new (rspan), html_cursor_get_position (e->cursor),
877 position_before),
878 dir);
879 }
880
881 static void
collapse_rspan(HTMLEngine * e,HTMLTableCell * cell,gint rspan,HTMLUndoDirection dir)882 collapse_rspan (HTMLEngine *e,
883 HTMLTableCell *cell,
884 gint rspan,
885 HTMLUndoDirection dir)
886 {
887 HTMLTable *table;
888 guint position_before = e->cursor->position;
889 gint r, c;
890
891 table = HTML_TABLE (HTML_OBJECT (cell)->parent);
892 for (r = cell->row + rspan; r < cell->row + cell->rspan; r++)
893 for (c = cell->col; c < cell->col + cell->cspan; c++) {
894 table->cells[r][c] = NULL;
895 html_table_set_cell (table, r, c, html_engine_new_cell (e, table));
896 html_table_cell_set_position (table->cells[r][c], r, c);
897 }
898
899 collapse_rspan_setup_undo (e, cell->rspan, position_before, dir);
900 cell->rspan = rspan;
901 html_object_change_set (HTML_OBJECT (cell), HTML_CHANGE_ALL);
902 }
903
904 void
html_engine_set_rspan(HTMLEngine * e,gint rspan)905 html_engine_set_rspan (HTMLEngine *e,
906 gint rspan)
907 {
908 HTMLTableCell *cell = html_engine_get_table_cell (e);
909
910 g_return_if_fail (rspan > 0);
911 g_return_if_fail (cell != NULL);
912
913 if (cell->rspan == rspan)
914 return;
915
916 html_engine_freeze (e);
917 if (rspan > cell->rspan)
918 expand_rspan (e, cell, rspan, HTML_UNDO_UNDO);
919 else
920 collapse_rspan (e, cell, rspan, HTML_UNDO_UNDO);
921 html_engine_thaw (e);
922 }
923
924 gboolean
html_engine_cspan_delta(HTMLEngine * e,gint delta)925 html_engine_cspan_delta (HTMLEngine *e,
926 gint delta)
927 {
928 HTMLTableCell *cell;
929
930 cell = html_engine_get_table_cell (e);
931 if (cell) {
932 if (cell->cspan + delta > 0) {
933 html_engine_set_cspan (e, cell->cspan + delta);
934 return TRUE;
935 }
936 }
937
938 return FALSE;
939 }
940
941 gboolean
html_engine_rspan_delta(HTMLEngine * e,gint delta)942 html_engine_rspan_delta (HTMLEngine *e,
943 gint delta)
944 {
945 HTMLTableCell *cell;
946
947 cell = html_engine_get_table_cell (e);
948 if (cell) {
949 if (cell->rspan + delta > 0) {
950 html_engine_set_rspan (e, cell->rspan + delta);
951 return TRUE;
952 }
953 }
954
955 return FALSE;
956 }
957