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