1 /*
2 * sheet-control-gui.c: Implements a graphic control for a sheet.
3 *
4 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
5 * Copyright (C) 1997-1999 Miguel de Icaza (miguel@kernel.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
24 #include <gnumeric.h>
25 #include <sheet-control-gui-priv.h>
26
27 #include <sheet.h>
28 #include <sheet-private.h>
29 #include <sheet-view.h>
30 #include <sheet-merge.h>
31 #include <workbook.h>
32 #include <workbook-view.h>
33 #include <workbook-cmd-format.h>
34 #include <wbc-gtk-impl.h>
35 #include <cell.h>
36 #include <selection.h>
37 #include <style.h>
38 #include <sheet-style.h>
39 #include <sheet-object-impl.h>
40 #include <sheet-object-cell-comment.h>
41 #include <sheet-object-image.h>
42 #include <gui-util.h>
43 #include <gutils.h>
44 #include <parse-util.h>
45 #include <selection.h>
46 #include <application.h>
47 #include <cellspan.h>
48 #include <cmd-edit.h>
49 #include <commands.h>
50 #include <gnm-commands-slicer.h>
51 #include <clipboard.h>
52 #include <dialogs/dialogs.h>
53 #include <gui-file.h>
54 #include <sheet-merge.h>
55 #include <ranges.h>
56 #include <xml-sax.h>
57 #include <style-color.h>
58 #include <gnumeric-conf.h>
59
60 #include <gnm-pane-impl.h>
61 #include <item-bar.h>
62 #include <item-cursor.h>
63 #include <widgets/gnm-expr-entry.h>
64 #include <gnm-sheet-slicer.h>
65 #include <input-msg.h>
66
67 #include <go-data-slicer-field.h>
68 #include <goffice/goffice.h>
69
70 #include <gdk/gdkkeysyms.h>
71 #include <gsf/gsf-impl-utils.h>
72 #include <gsf/gsf-input.h>
73 #include <gsf/gsf-output-memory.h>
74
75 #include <string.h>
76
77 static GObjectClass *scg_parent_class;
78
79 static void scg_unant (SheetControl *sc);
80 static void set_resize_pane_pos (SheetControlGUI *scg, GtkPaned *p);
81 static void cb_resize_pane_motion (GtkPaned *p, GParamSpec *pspec, SheetControlGUI *scg);
82
83
84 /**
85 * scg_pane:
86 * @scg: #SheetControlGUI
87 * @pane: the pane index.
88 *
89 * Returns: (transfer none): the pane.
90 **/
91 GnmPane *
scg_pane(SheetControlGUI * scg,int p)92 scg_pane (SheetControlGUI *scg, int p)
93 {
94 /* it is ok to request a pane when we are not frozen */
95 g_return_val_if_fail (GNM_IS_SCG (scg), NULL);
96 g_return_val_if_fail (p >= 0, NULL);
97 g_return_val_if_fail (p < 4, NULL);
98
99 return scg->pane[p];
100 }
101
102 /**
103 * scg_view:
104 * @scg: #SheetControlGUI
105 *
106 * Returns: (transfer none): the sheet view.
107 **/
108 SheetView *
scg_view(SheetControlGUI const * scg)109 scg_view (SheetControlGUI const *scg)
110 {
111 g_return_val_if_fail (GNM_IS_SCG (scg), NULL);
112 return scg->sheet_control.view;
113 }
114
115
116 /**
117 * scg_sheet:
118 * @scg: #SheetControlGUI
119 *
120 * Returns: (transfer none): the sheet.
121 **/
122 Sheet *
scg_sheet(SheetControlGUI const * scg)123 scg_sheet (SheetControlGUI const *scg)
124 {
125 return sc_sheet ((SheetControl *)scg);
126 }
127
128
129 /**
130 * scg_wbc:
131 * @scg: #SheetControlGUI
132 *
133 * Returns: (transfer none): the workbook control.
134 **/
135 WorkbookControl *
scg_wbc(SheetControlGUI const * scg)136 scg_wbc (SheetControlGUI const *scg)
137 {
138 g_return_val_if_fail (GNM_IS_SCG (scg), NULL);
139 return scg->sheet_control.wbc;
140 }
141
142
143 /**
144 * scg_wbcg:
145 * @scg: #SheetControlGUI
146 *
147 * Returns: (transfer none): the #WBCGtk.
148 **/
149
150 WBCGtk *
scg_wbcg(SheetControlGUI const * scg)151 scg_wbcg (SheetControlGUI const *scg)
152 {
153 g_return_val_if_fail (GNM_IS_SCG (scg), NULL);
154 return scg->wbcg;
155 }
156
157 static void
scg_redraw_all(SheetControl * sc,gboolean headers)158 scg_redraw_all (SheetControl *sc, gboolean headers)
159 {
160 SheetControlGUI *scg = (SheetControlGUI *)sc;
161
162 g_return_if_fail (GNM_IS_SCG (scg));
163
164 SCG_FOREACH_PANE (scg, pane, {
165 goc_canvas_invalidate (GOC_CANVAS (pane),
166 G_MININT64, 0, G_MAXINT64, G_MAXINT64);
167 if (headers) {
168 if (NULL != pane->col.canvas)
169 goc_canvas_invalidate (pane->col.canvas,
170 0, 0, G_MAXINT64, G_MAXINT64);
171 if (NULL != pane->row.canvas)
172 goc_canvas_invalidate (pane->row.canvas,
173 0, 0, G_MAXINT64, G_MAXINT64);
174 }
175 });
176 }
177
178 static void
scg_redraw_range(SheetControl * sc,GnmRange const * r)179 scg_redraw_range (SheetControl *sc, GnmRange const *r)
180 {
181 SheetControlGUI *scg = (SheetControlGUI *)sc;
182 Sheet const *sheet = scg_sheet (scg);
183 GnmRange visible, area;
184
185 /*
186 * Getting the bounding box causes row respans to be done if
187 * needed. That can be expensive, so just redraw the whole
188 * sheet if the row count is too big.
189 */
190 if (r->end.row - r->start.row > 500) {
191 scg_redraw_all (sc, FALSE);
192 return;
193 }
194
195 /* We potentially do a lot of recalcs as part of this, so make sure
196 stuff that caches sub-computations see the whole thing instead
197 of clearing between cells. */
198 gnm_app_recalc_start ();
199
200 SCG_FOREACH_PANE (scg, pane, {
201 visible.start = pane->first;
202 visible.end = pane->last_visible;
203
204 if (range_intersection (&area, r, &visible)) {
205 sheet_range_bounding_box (sheet, &area);
206 gnm_pane_redraw_range (pane, &area);
207 }
208 };);
209
210 gnm_app_recalc_finish ();
211 }
212
213 static void
scg_redraw_headers(SheetControl * sc,gboolean const col,gboolean const row,GnmRange const * r)214 scg_redraw_headers (SheetControl *sc,
215 gboolean const col, gboolean const row,
216 GnmRange const * r /* optional == NULL */)
217 {
218 SheetControlGUI *scg = (SheetControlGUI *)sc;
219 GnmPane *pane;
220 int i;
221 double scale;
222
223 /*
224 * A rough guess of the trade off point between of redrawing all
225 * and calculating the redraw size
226 */
227 const int COL_HEURISTIC = 20;
228 const int ROW_HEURISTIC = 50;
229
230 for (i = scg->active_panes; i-- > 0 ; ) {
231 if (NULL == (pane = scg->pane[i]))
232 continue;
233
234 if (col && pane->col.canvas != NULL) {
235 int left = 0, right = G_MAXINT - 1;
236 GocCanvas * const col_canvas = GOC_CANVAS (pane->col.canvas);
237 scale = goc_canvas_get_pixels_per_unit (col_canvas);
238
239 if (r != NULL) {
240 int const size = r->end.col - r->start.col;
241 if (-COL_HEURISTIC < size && size < COL_HEURISTIC) {
242 left = pane->first_offset.x +
243 scg_colrow_distance_get (scg, TRUE,
244 pane->first.col, r->start.col);
245 right = left +
246 scg_colrow_distance_get (scg, TRUE,
247 r->start.col, r->end.col+1);
248 }
249 }
250 goc_canvas_invalidate (col_canvas,
251 left / scale, 0, right / scale, G_MAXINT64);
252 }
253
254 if (row && pane->row.canvas != NULL) {
255 gint64 top = 0, bottom = G_MAXINT64 - 1;
256 scale = goc_canvas_get_pixels_per_unit (pane->row.canvas);
257 if (r != NULL) {
258 int const size = r->end.row - r->start.row;
259 if (-ROW_HEURISTIC < size && size < ROW_HEURISTIC) {
260 top = pane->first_offset.y +
261 scg_colrow_distance_get (scg, FALSE,
262 pane->first.row, r->start.row);
263 bottom = top +
264 scg_colrow_distance_get (scg, FALSE,
265 r->start.row, r->end.row+1);
266 }
267 }
268 goc_canvas_invalidate (GOC_CANVAS (pane->row.canvas),
269 0, top / scale, G_MAXINT64, bottom / scale);
270 }
271 }
272 }
273
274 static void
cb_outline_button(GtkWidget * btn,SheetControlGUI * scg)275 cb_outline_button (GtkWidget *btn, SheetControlGUI *scg)
276 {
277 SheetControl *sc = (SheetControl *) scg;
278 WorkbookControl *wbc = sc->wbc;
279 GPtrArray const *btns;
280 unsigned i = 0;
281 gboolean is_cols = g_object_get_data (G_OBJECT (btn), "is_cols") != NULL;
282
283 /* which button */
284 btns = is_cols ? scg->col_group.buttons : scg->row_group.buttons;
285 for (i = 0; i < btns->len; i++)
286 if (g_ptr_array_index (btns, i) == btn)
287 break;
288
289 g_return_if_fail (i < btns->len);
290
291 cmd_global_outline_change (wbc, is_cols, i+1);
292 }
293
294 static void
scg_setup_group_buttons(SheetControlGUI * scg,unsigned max_outline,GnmItemBar const * ib,gboolean is_cols,int w,int h,GPtrArray * btns,GtkWidget * box)295 scg_setup_group_buttons (SheetControlGUI *scg, unsigned max_outline,
296 GnmItemBar const *ib, gboolean is_cols, int w, int h,
297 GPtrArray *btns, GtkWidget *box)
298 {
299 PangoFontDescription *font_desc;
300 unsigned i;
301 Sheet const *sheet = scg_sheet (scg);
302
303 if (!sheet->display_outlines)
304 max_outline = 0;
305 else if (max_outline > 0)
306 max_outline++;
307
308 while (btns->len > max_outline) {
309 GtkWidget *w = g_ptr_array_remove_index_fast (btns, btns->len - 1);
310 gtk_container_remove (GTK_CONTAINER (box),
311 gtk_widget_get_parent (w));
312 }
313
314 while (btns->len < max_outline) {
315 GtkWidget *out = gtk_alignment_new (.5, .5, 1., 1.);
316 GtkWidget *in = gtk_alignment_new (.5, .5, 0., 0.);
317 GtkWidget *btn = gtk_button_new ();
318 char *tmp = g_strdup_printf ("<small>%d</small>", btns->len+1);
319 GtkWidget *label = gtk_label_new (NULL);
320 gtk_label_set_markup (GTK_LABEL (label), tmp);
321 g_free (tmp);
322
323 gtk_widget_set_can_focus (btn, FALSE);
324 gtk_container_add (GTK_CONTAINER (in), label);
325 gtk_container_add (GTK_CONTAINER (btn), in);
326 gtk_container_add (GTK_CONTAINER (out), btn);
327 gtk_box_pack_start (GTK_BOX (box), out, TRUE, TRUE, 0);
328 g_ptr_array_add (btns, btn);
329
330 g_signal_connect (G_OBJECT (btn),
331 "clicked",
332 G_CALLBACK (cb_outline_button), scg);
333 if (is_cols)
334 g_object_set_data (G_OBJECT (btn),
335 "is_cols", GINT_TO_POINTER (1));
336 }
337
338 font_desc = item_bar_normal_font (ib);
339
340 /* size all of the button so things work after a zoom */
341 for (i = 0 ; i < btns->len ; i++) {
342 GtkWidget *btn = g_ptr_array_index (btns, i);
343 GtkWidget *label = gtk_bin_get_child (GTK_BIN (gtk_bin_get_child (GTK_BIN (btn))));
344 gtk_widget_set_size_request (GTK_WIDGET (btn), w, h);
345 gtk_widget_override_font (label, font_desc);
346 }
347
348 pango_font_description_free (font_desc);
349 gtk_widget_show_all (box);
350 }
351
352 static void
scg_resize(SheetControlGUI * scg,G_GNUC_UNUSED gboolean force_scroll)353 scg_resize (SheetControlGUI *scg, G_GNUC_UNUSED gboolean force_scroll)
354 {
355 Sheet const *sheet = scg_sheet (scg);
356 GnmPane *pane = scg_pane (scg, 0);
357 int h, w, btn_h, btn_w, tmp;
358
359 if (!pane)
360 return;
361
362 /* Recalibrate the starting offsets */
363 pane->first_offset.x = scg_colrow_distance_get (scg,
364 TRUE, 0, pane->first.col);
365 pane->first_offset.y = scg_colrow_distance_get (scg,
366 FALSE, 0, pane->first.row);
367
368 /* resize Pane[0] headers */
369 h = gnm_item_bar_calc_size (scg->pane[0]->col.item);
370 btn_h = h - gnm_item_bar_indent (scg->pane[0]->col.item);
371 w = gnm_item_bar_calc_size (scg->pane[0]->row.item);
372 btn_w = w - gnm_item_bar_indent (scg->pane[0]->row.item);
373 gtk_widget_set_size_request (scg->select_all_btn, btn_w, btn_h);
374 gtk_widget_set_size_request (GTK_WIDGET (scg->pane[0]->col.canvas), -1, h);
375 gtk_widget_set_size_request (GTK_WIDGET (scg->pane[0]->row.canvas), w, -1);
376
377 tmp = gnm_item_bar_group_size (scg->pane[0]->col.item,
378 sheet->cols.max_outline_level);
379 scg_setup_group_buttons (scg, sheet->cols.max_outline_level,
380 scg->pane[0]->col.item, TRUE,
381 tmp, tmp, scg->col_group.buttons, scg->col_group.button_box);
382 scg_setup_group_buttons (scg, sheet->rows.max_outline_level,
383 scg->pane[0]->row.item, FALSE,
384 -1, btn_h, scg->row_group.buttons, scg->row_group.button_box);
385
386 if (scg->active_panes != 1 && gnm_sheet_view_is_frozen (scg_view (scg))) {
387 GnmCellPos const *tl = &scg_view (scg)->frozen_top_left;
388 GnmCellPos const *br = &scg_view (scg)->unfrozen_top_left;
389 int const l = scg_colrow_distance_get (scg, TRUE,
390 0, tl->col);
391 int const r = scg_colrow_distance_get (scg, TRUE,
392 tl->col, br->col) + l;
393 int const t = scg_colrow_distance_get (scg, FALSE,
394 0, tl->row);
395 int const b = scg_colrow_distance_get (scg, FALSE,
396 tl->row, br->row) + t;
397 int i;
398 int fw = MIN (scg->screen_width, r - l);
399 int fh = MIN (scg->screen_height, b - t);
400
401 /* pane 0 has already been done */
402 for (i = scg->active_panes; i-- > 1 ; ) {
403 GnmPane *pane = scg->pane[i];
404 if (NULL != pane) {
405 pane->first_offset.x = scg_colrow_distance_get (
406 scg, TRUE, 0, pane->first.col);
407 pane->first_offset.y = scg_colrow_distance_get (
408 scg, FALSE, 0, pane->first.row);
409 }
410 }
411
412 if (scg->pane[1]) {
413 if (gnm_debug_flag ("frozen-panes"))
414 g_printerr ("Pane 1: %d\n", r - l);
415
416 gtk_widget_set_size_request (GTK_WIDGET (scg->pane[1]), fw, -1);
417 /* The item_bar_calcs should be equal */
418 /* FIXME : The canvas gets confused when the initial scroll
419 * region is set too early in its life cycle.
420 * It likes it to be at the origin, we can live with that for now.
421 * However, we really should track the bug eventually.
422 */
423 h = gnm_item_bar_calc_size (scg->pane[1]->col.item);
424 gtk_widget_set_size_request (GTK_WIDGET (scg->pane[1]->col.canvas), fw, h);
425 }
426
427 if (scg->pane[3]) {
428 if (gnm_debug_flag ("frozen-panes"))
429 g_printerr ("Pane 2: %d\n", b - t);
430
431 gtk_widget_set_size_request (GTK_WIDGET (scg->pane[3]), -1, fh);
432 /* The item_bar_calcs should be equal */
433 w = gnm_item_bar_calc_size (scg->pane[3]->row.item);
434 gtk_widget_set_size_request (GTK_WIDGET (scg->pane[3]->row.canvas), w, fh);
435 }
436
437 if (scg->pane[2]) {
438 if (gnm_debug_flag ("frozen-panes"))
439 g_printerr ("Pane 3: %d %d\n", r - l, b - t);
440
441 gtk_widget_set_size_request (GTK_WIDGET (scg->pane[2]), fw, fh);
442 }
443 }
444
445 SCG_FOREACH_PANE (scg, pane, {
446 gnm_pane_reposition_cursors (pane);
447 });
448 }
449
450 static void
scg_resize_virt(SheetControl * sc,gboolean force_scroll)451 scg_resize_virt (SheetControl *sc, gboolean force_scroll)
452 {
453 scg_resize ((SheetControlGUI *)sc, force_scroll);
454 }
455
456 static void
gnm_adjustment_configure(GtkAdjustment * adjustment,gdouble value,gdouble lower,gdouble upper,gdouble step_increment,gdouble page_increment,gdouble page_size)457 gnm_adjustment_configure (GtkAdjustment *adjustment,
458 gdouble value,
459 gdouble lower,
460 gdouble upper,
461 gdouble step_increment,
462 gdouble page_increment,
463 gdouble page_size)
464 {
465 g_object_freeze_notify (G_OBJECT (adjustment));
466
467 // These do nothing if value isn't changed
468 gtk_adjustment_set_lower (adjustment, lower);
469 gtk_adjustment_set_upper (adjustment, upper);
470 gtk_adjustment_set_step_increment (adjustment, step_increment);
471 gtk_adjustment_set_page_increment (adjustment, page_increment);
472 gtk_adjustment_set_page_size (adjustment, page_size);
473
474 g_object_thaw_notify (G_OBJECT (adjustment));
475
476 // These fire signals if nothing changes, so check by hand
477 if (!(gtk_adjustment_get_value (adjustment) == value))
478 gtk_adjustment_set_value (adjustment, value);
479
480 }
481
482 /**
483 * scg_scrollbar_config:
484 * @sc:
485 *
486 * Manages the scrollbar dimensions and paging parameters.
487 * Currently sizes things based on the cols/rows visible in pane-0. This has
488 * several subtleties.
489 *
490 * 1) Using cols/rows instead of pixels means that the scrollbar changes size
491 * as it passes through regions with different sized cols/rows.
492 *
493 * 2) It does NOT take into account hidden rows/cols So a region that contains
494 * a large hidden segment will appear larger.
495 *
496 * 3) It only uses pane-0 because that is the only one that can scroll in both
497 * dimensions. The others are of fixed size.
498 */
499 static gboolean
scg_scrollbar_config_real(SheetControl const * sc)500 scg_scrollbar_config_real (SheetControl const *sc)
501 {
502 SheetControlGUI *scg = GNM_SCG (sc);
503 GtkAdjustment *va = scg->va;
504 GtkAdjustment *ha = scg->ha;
505 GnmPane *pane = scg_pane (scg, 0);
506 SheetView const *sv = sc->view;
507 Sheet const *sheet = sv->sheet;
508
509 if (pane) {
510 int const last_col = pane->last_full.col;
511 int const last_row = pane->last_full.row;
512 int max_col = last_col;
513 int max_row = last_row;
514
515 if (max_row < sheet->rows.max_used)
516 max_row = sheet->rows.max_used;
517 if (max_row < sheet->max_object_extent.row)
518 max_row = sheet->max_object_extent.row;
519 gnm_adjustment_configure
520 (va,
521 pane->first.row,
522 gnm_sheet_view_is_frozen (sv) ? sv->unfrozen_top_left.row : 0,
523 max_row + 1,
524 1,
525 MAX (gtk_adjustment_get_page_size (va) - 3.0, 1.0),
526 last_row - pane->first.row + 1);
527
528 if (max_col < sheet->cols.max_used)
529 max_col = sheet->cols.max_used;
530 if (max_col < sheet->max_object_extent.col)
531 max_col = sheet->max_object_extent.col;
532 gnm_adjustment_configure
533 (ha,
534 pane->first.col,
535 gnm_sheet_view_is_frozen (sv) ? sv->unfrozen_top_left.col : 0,
536 max_col + 1,
537 1,
538 MAX (gtk_adjustment_get_page_size (ha) - 3.0, 1.0),
539 last_col - pane->first.col + 1);
540 }
541
542 scg->scroll_bar_timer = 0;
543 return FALSE;
544 }
545
546
547 static void
scg_scrollbar_config(SheetControl * sc)548 scg_scrollbar_config (SheetControl *sc)
549 {
550 SheetControlGUI *scg = GNM_SCG (sc);
551 /* See bug 789412 */
552 if (!scg->scroll_bar_timer)
553 scg->scroll_bar_timer =
554 g_timeout_add (1,
555 (GSourceFunc) scg_scrollbar_config_real,
556 scg);
557 }
558
559 void
scg_colrow_size_set(SheetControlGUI * scg,gboolean is_cols,int index,int new_size_pixels)560 scg_colrow_size_set (SheetControlGUI *scg,
561 gboolean is_cols, int index, int new_size_pixels)
562 {
563 WorkbookControl *wbc = scg_wbc (scg);
564 SheetView *sv = scg_view (scg);
565
566 /* If all cols/rows in the selection are completely selected
567 * then resize all of them, otherwise just resize the selected col/row.
568 */
569 if (!sv_is_full_colrow_selected (sv, is_cols, index))
570 cmd_resize_colrow (wbc, sv->sheet, is_cols,
571 colrow_get_index_list (index, index, NULL), new_size_pixels);
572 else
573 workbook_cmd_resize_selected_colrow (wbc, sv->sheet, is_cols,
574 new_size_pixels);
575 }
576
577 void
scg_select_all(SheetControlGUI * scg)578 scg_select_all (SheetControlGUI *scg)
579 {
580 Sheet *sheet = scg_sheet (scg);
581 gboolean const rangesel = wbcg_rangesel_possible (scg->wbcg);
582
583 if (rangesel) {
584 scg_rangesel_bound (scg,
585 0, 0, gnm_sheet_get_last_col (sheet), gnm_sheet_get_last_row (sheet));
586 gnm_expr_entry_signal_update (
587 wbcg_get_entry_logical (scg->wbcg), TRUE);
588 } else if (wbc_gtk_get_guru (scg->wbcg) == NULL) {
589 SheetView *sv = scg_view (scg);
590
591 scg_mode_edit (scg);
592 wbcg_edit_finish (scg->wbcg, WBC_EDIT_REJECT, NULL);
593 sv_selection_reset (sv);
594 sv_selection_add_full (sv, sv->edit_pos.col, sv->edit_pos.row,
595 0, 0, gnm_sheet_get_last_col (sheet),
596 gnm_sheet_get_last_row (sheet),
597 GNM_SELECTION_MODE_ADD);
598 }
599 sheet_update (sheet);
600 }
601
602 gboolean
scg_colrow_select(SheetControlGUI * scg,gboolean is_cols,int index,int modifiers)603 scg_colrow_select (SheetControlGUI *scg, gboolean is_cols,
604 int index, int modifiers)
605 {
606 SheetView *sv = scg_view (scg);
607 gboolean const rangesel = wbcg_rangesel_possible (scg->wbcg);
608
609 if (!rangesel &&
610 !wbcg_edit_finish (scg->wbcg, WBC_EDIT_ACCEPT, NULL))
611 return FALSE;
612
613 if (modifiers & GDK_SHIFT_MASK) {
614 if (rangesel) {
615 if (is_cols)
616 scg_rangesel_extend_to (scg, index, -1);
617 else
618 scg_rangesel_extend_to (scg, -1, index);
619 } else {
620 if (is_cols)
621 sv_selection_extend_to (sv, index, -1);
622 else
623 sv_selection_extend_to (sv, -1, index);
624 }
625 } else {
626 if (!rangesel && !(modifiers & GDK_CONTROL_MASK))
627 sv_selection_reset (sv);
628
629 if (rangesel) {
630 if (is_cols)
631 scg_rangesel_bound (scg,
632 index, 0, index, gnm_sheet_get_last_row (sv->sheet));
633 else
634 scg_rangesel_bound (scg,
635 0, index, gnm_sheet_get_last_col (sv->sheet), index);
636 } else if (is_cols) {
637 GnmPane *pane =
638 scg_pane (scg, scg->pane[3] ? 3 : 0);
639 sv_selection_add_full (sv,
640 index, pane->first.row,
641 index, 0,
642 index, gnm_sheet_get_last_row (sv->sheet),
643 GNM_SELECTION_MODE_ADD);
644 } else {
645 GnmPane *pane =
646 scg_pane (scg, scg->pane[1] ? 1 : 0);
647 sv_selection_add_full (sv,
648 pane->first.col, index,
649 0, index,
650 gnm_sheet_get_last_col (sv->sheet), index,
651 GNM_SELECTION_MODE_ADD);
652 }
653 }
654
655 /* The edit pos, and the selection may have changed */
656 if (!rangesel)
657 sheet_update (sv->sheet);
658 return TRUE;
659 }
660
661 /***************************************************************************/
662
663 static void
cb_select_all_btn_draw(GtkWidget * widget,cairo_t * cr,SheetControlGUI * scg)664 cb_select_all_btn_draw (GtkWidget *widget, cairo_t *cr, SheetControlGUI *scg)
665 {
666 int offset = scg_sheet (scg)->text_is_rtl ? -1 : 0;
667 GtkAllocation a;
668 GtkStyleContext *ctxt = gtk_widget_get_style_context (widget);
669
670 gtk_widget_get_allocation (widget, &a);
671
672 gtk_style_context_save (ctxt);
673 gtk_style_context_set_state (ctxt, GTK_STATE_FLAG_NORMAL);
674 gtk_render_background (ctxt, cr, offset + 1, 1,
675 a.width - 1, a.height - 1);
676 gtk_render_frame (ctxt, cr, offset, 0, a.width + 1, a.height + 1);
677 gtk_style_context_restore (ctxt);
678 }
679
680 static gboolean
cb_select_all_btn_event(G_GNUC_UNUSED GtkWidget * widget,GdkEvent * event,SheetControlGUI * scg)681 cb_select_all_btn_event (G_GNUC_UNUSED GtkWidget *widget, GdkEvent *event, SheetControlGUI *scg)
682 {
683 if (event->type == GDK_BUTTON_PRESS) {
684 scg_select_all (scg);
685 return TRUE;
686 }
687
688 return FALSE;
689 }
690
691 static void
cb_vscrollbar_value_changed(GtkRange * range,SheetControlGUI * scg)692 cb_vscrollbar_value_changed (GtkRange *range, SheetControlGUI *scg)
693 {
694 GtkAdjustment *adj = gtk_range_get_adjustment (range);
695 scg_set_top_row (scg, gtk_adjustment_get_value (adj));
696 }
697
698 static void
cb_hscrollbar_value_changed(GtkRange * range,SheetControlGUI * scg)699 cb_hscrollbar_value_changed (GtkRange *range, SheetControlGUI *scg)
700 {
701 GtkAdjustment *adj = gtk_range_get_adjustment (range);
702 scg_set_left_col (scg, gtk_adjustment_get_value (adj));
703 }
704
705 static void
cb_hscrollbar_adjust_bounds(GtkRange * range,gdouble new_value,Sheet * sheet)706 cb_hscrollbar_adjust_bounds (GtkRange *range, gdouble new_value, Sheet *sheet)
707 {
708 GtkAdjustment *adj = gtk_range_get_adjustment (range);
709 double upper = gtk_adjustment_get_upper (adj);
710 double page_size = gtk_adjustment_get_page_size (adj);
711 gdouble limit = upper - page_size;
712 if (upper < gnm_sheet_get_max_cols (sheet) && new_value >= limit) {
713 upper = new_value + page_size + 1;
714 if (upper > gnm_sheet_get_max_cols (sheet))
715 upper = gnm_sheet_get_max_cols (sheet);
716 gtk_adjustment_set_upper (adj, upper);
717 }
718 }
719 static void
cb_vscrollbar_adjust_bounds(GtkRange * range,gdouble new_value,Sheet * sheet)720 cb_vscrollbar_adjust_bounds (GtkRange *range, gdouble new_value, Sheet *sheet)
721 {
722 GtkAdjustment *adj = gtk_range_get_adjustment (range);
723 double upper = gtk_adjustment_get_upper (adj);
724 double page_size = gtk_adjustment_get_page_size (adj);
725 gdouble limit = upper - page_size;
726 if (upper < gnm_sheet_get_max_rows (sheet) && new_value >= limit) {
727 upper = new_value + page_size + 1;
728 if (upper > gnm_sheet_get_max_rows (sheet))
729 upper = gnm_sheet_get_max_rows (sheet);
730 gtk_adjustment_set_upper (adj, upper);
731 }
732 }
733
734 static void
cb_table_destroy(SheetControlGUI * scg)735 cb_table_destroy (SheetControlGUI *scg)
736 {
737 SheetControl *sc = (SheetControl *) scg;
738 int i;
739
740 g_clear_object (&scg->grid);
741
742 scg_mode_edit (scg); /* finish any object edits */
743 scg_unant (sc); /* Make sure that everything is unanted */
744
745 if (scg->wbcg) {
746 GtkWindow *toplevel = wbcg_toplevel (scg->wbcg);
747
748 /* Only pane-0 ever gets focus */
749 if (NULL != toplevel &&
750 gtk_window_get_focus (toplevel) == GTK_WIDGET (scg_pane (scg, 0)))
751 gtk_window_set_focus (toplevel, NULL);
752 }
753
754 for (i = scg->active_panes; i-- > 0 ; )
755 if (NULL != scg->pane[i]) {
756 gtk_widget_destroy (GTK_WIDGET (scg->pane[i]));
757 scg->pane[i] = NULL;
758 }
759
760 g_object_unref (scg);
761 }
762
763 static void
scg_init(SheetControlGUI * scg)764 scg_init (SheetControlGUI *scg)
765 {
766 scg->comment.selected = NULL;
767 scg->comment.item = NULL;
768 scg->comment.timer = 0;
769
770 scg->delayedMovement.timer = 0;
771 scg->delayedMovement.handler = NULL;
772
773 scg->grab_stack = 0;
774 scg->selected_objects = NULL;
775
776 scg->im.item = NULL;
777 scg->im.timer = 0;
778
779 // These shouldn't matter and will be overwritten
780 scg->screen_width = 1920;
781 scg->screen_height = 1200;
782 }
783
784 /*************************************************************************/
785
786 /**
787 * gnm_pane_update_inital_top_left:
788 * A convenience routine to store the new topleft back in the view.
789 */
790 static void
gnm_pane_update_inital_top_left(GnmPane const * pane)791 gnm_pane_update_inital_top_left (GnmPane const *pane)
792 {
793 if (pane->index == 0) {
794 SheetView *sv = scg_view (pane->simple.scg);
795 sv->initial_top_left = pane->first;
796 }
797 }
798
799 static gint64
bar_set_left_col(GnmPane * pane,int new_first_col)800 bar_set_left_col (GnmPane *pane, int new_first_col)
801 {
802 GocCanvas *colc;
803 gint64 col_offset;
804
805
806 col_offset = pane->first_offset.x +=
807 scg_colrow_distance_get (pane->simple.scg, TRUE, pane->first.col, new_first_col);
808 pane->first.col = new_first_col;
809
810 /* Scroll the column headers */
811 if (NULL != (colc = pane->col.canvas))
812 goc_canvas_scroll_to (colc, col_offset / colc->pixels_per_unit, 0);
813
814 return col_offset;
815 }
816
817 static void
gnm_pane_set_left_col(GnmPane * pane,int new_first_col)818 gnm_pane_set_left_col (GnmPane *pane, int new_first_col)
819 {
820 Sheet *sheet;
821 g_return_if_fail (pane != NULL);
822 sheet = scg_sheet (pane->simple.scg);
823 g_return_if_fail (0 <= new_first_col && new_first_col < gnm_sheet_get_max_cols (sheet));
824
825 if (pane->first.col != new_first_col) {
826 GocCanvas * const canvas = GOC_CANVAS (pane);
827 gint64 const col_offset = bar_set_left_col (pane, new_first_col);
828
829 gnm_pane_compute_visible_region (pane, FALSE);
830 goc_canvas_scroll_to (canvas, col_offset / canvas->pixels_per_unit, pane->first_offset.y / canvas->pixels_per_unit);
831 gnm_pane_update_inital_top_left (pane);
832 }
833 }
834
835 void
scg_set_left_col(SheetControlGUI * scg,int col)836 scg_set_left_col (SheetControlGUI *scg, int col)
837 {
838 Sheet const *sheet;
839 GnmRange const *bound;
840
841 g_return_if_fail (GNM_IS_SCG (scg));
842
843 sheet = scg_sheet (scg);
844 bound = &sheet->priv->unhidden_region;
845 if (col < bound->start.col)
846 col = bound->start.col;
847 else if (col >= gnm_sheet_get_max_cols (sheet))
848 col = gnm_sheet_get_last_col (sheet);
849 else if (col > bound->end.col)
850 col = bound->end.col;
851
852 if (scg->pane[1]) {
853 int right = scg_view (scg)->unfrozen_top_left.col;
854 if (col < right)
855 col = right;
856 }
857 if (scg->pane[3])
858 gnm_pane_set_left_col (scg_pane (scg, 3), col);
859 gnm_pane_set_left_col (scg_pane (scg, 0), col);
860 }
861
862 static gint64
bar_set_top_row(GnmPane * pane,int new_first_row)863 bar_set_top_row (GnmPane *pane, int new_first_row)
864 {
865 GocCanvas *rowc;
866 gint64 row_offset;
867
868 row_offset = pane->first_offset.y +=
869 scg_colrow_distance_get (pane->simple.scg, FALSE, pane->first.row, new_first_row);
870 pane->first.row = new_first_row;
871
872 /* Scroll the row headers */
873 if (NULL != (rowc = pane->row.canvas))
874 goc_canvas_scroll_to (rowc, 0, row_offset / rowc->pixels_per_unit);
875
876 return row_offset;
877 }
878
879 static void
gnm_pane_set_top_row(GnmPane * pane,int new_first_row)880 gnm_pane_set_top_row (GnmPane *pane, int new_first_row)
881 {
882 Sheet *sheet;
883 g_return_if_fail (pane != NULL);
884 sheet = scg_sheet (pane->simple.scg);
885 g_return_if_fail (0 <= new_first_row && new_first_row < gnm_sheet_get_max_rows (sheet));
886
887 if (pane->first.row != new_first_row) {
888 GocCanvas * const canvas = GOC_CANVAS(pane);
889 gint64 const row_offset = bar_set_top_row (pane, new_first_row);
890 gint64 col_offset = pane->first_offset.x;
891
892 gnm_pane_compute_visible_region (pane, FALSE);
893 goc_canvas_scroll_to (canvas, col_offset / canvas->pixels_per_unit, row_offset / canvas->pixels_per_unit);
894 gnm_pane_update_inital_top_left (pane);
895 }
896 }
897
898 void
scg_set_top_row(SheetControlGUI * scg,int row)899 scg_set_top_row (SheetControlGUI *scg, int row)
900 {
901 Sheet const *sheet;
902 GnmRange const *bound;
903
904 g_return_if_fail (GNM_IS_SCG (scg));
905
906 sheet = scg_sheet (scg);
907 bound = &sheet->priv->unhidden_region;
908 if (row < bound->start.row)
909 row = bound->start.row;
910 else if (row >= gnm_sheet_get_max_rows (sheet))
911 row = gnm_sheet_get_last_row (sheet);
912 else if (row > bound->end.row)
913 row = bound->end.row;
914
915 if (scg->pane[3]) {
916 int bottom = scg_view (scg)->unfrozen_top_left.row;
917 if (row < bottom)
918 row = bottom;
919 }
920 if (scg->pane[1])
921 gnm_pane_set_top_row (scg_pane (scg, 1), row);
922 gnm_pane_set_top_row (scg_pane (scg, 0), row);
923 }
924
925 static void
gnm_pane_set_top_left(GnmPane * pane,int col,int row,gboolean force_scroll)926 gnm_pane_set_top_left (GnmPane *pane,
927 int col, int row, gboolean force_scroll)
928 {
929 gboolean changed = FALSE;
930 gint64 col_offset, row_offset;
931 GocCanvas *canvas;
932
933 g_return_if_fail (0 <= col &&
934 col < gnm_sheet_get_max_cols (scg_sheet (pane->simple.scg)));
935 g_return_if_fail (0 <= row &&
936 row < gnm_sheet_get_max_rows (scg_sheet (pane->simple.scg)));
937
938 if (pane->first.col != col || force_scroll) {
939 if (force_scroll) {
940 /* Clear the offsets in case col/row size changed */
941 pane->first_offset.x = 0;
942 pane->first.col = 0;
943 }
944
945 col_offset = bar_set_left_col (pane, col);
946 changed = TRUE;
947 } else {
948 col_offset = pane->first_offset.x;
949 }
950
951 if (pane->first.row != row || force_scroll) {
952 if (force_scroll) {
953 /* Clear the offsets in case col/row size changed */
954 pane->first_offset.y = 0;
955 pane->first.row = 0;
956 }
957 row_offset = bar_set_top_row (pane, row);
958 changed = TRUE;
959 } else
960 row_offset = pane->first_offset.y;
961
962 if (!changed)
963 return;
964
965 gnm_pane_compute_visible_region (pane, force_scroll);
966 canvas = GOC_CANVAS (pane);
967 goc_canvas_scroll_to (canvas, col_offset / canvas->pixels_per_unit, row_offset / canvas->pixels_per_unit);
968 gnm_pane_update_inital_top_left (pane);
969 }
970
971 static void
scg_set_top_left(SheetControl * sc,int col,int row)972 scg_set_top_left (SheetControl *sc, int col, int row)
973 {
974 SheetControlGUI *scg = (SheetControlGUI *)sc;
975
976 g_return_if_fail (GNM_IS_SCG (scg));
977
978 if (!scg->pane[0])
979 return;
980 /* We could be faster if necessary */
981 scg_set_left_col (scg, col);
982 scg_set_top_row (scg, row);
983 }
984
985 static void
gnm_pane_make_cell_visible(GnmPane * pane,int col,int row,gboolean const force_scroll)986 gnm_pane_make_cell_visible (GnmPane *pane, int col, int row,
987 gboolean const force_scroll)
988 {
989 GocCanvas *canvas;
990 Sheet *sheet;
991 int new_first_col, new_first_row;
992 GnmRange range;
993 GtkAllocation ca;
994
995 g_return_if_fail (GNM_IS_PANE (pane));
996
997 /* Avoid calling this before the canvas is realized: We do not know the
998 * visible area, and would unconditionally scroll the cell to the top
999 * left of the viewport.
1000 */
1001 if (!gtk_widget_get_realized (GTK_WIDGET (pane)))
1002 return;
1003
1004 sheet = scg_sheet (pane->simple.scg);
1005 g_return_if_fail (col >= 0);
1006 g_return_if_fail (row >= 0);
1007 g_return_if_fail (col < gnm_sheet_get_max_cols (sheet));
1008 g_return_if_fail (row < gnm_sheet_get_max_rows (sheet));
1009
1010 canvas = GOC_CANVAS (pane);
1011 range.start.col = range.end.col = col;
1012 range.start.row = range.end.row = row;
1013 gnm_sheet_merge_find_bounding_box (sheet, &range);
1014
1015 gtk_widget_get_allocation (GTK_WIDGET (canvas), &ca);
1016
1017 /* Find the new pane->first.col */
1018 if (range.start.col < pane->first.col) {
1019 new_first_col = range.start.col;
1020 } else if (range.end.col > pane->last_full.col) {
1021 int width = ca.width;
1022 ColRowInfo const * const ci = sheet_col_get_info (sheet, range.end.col);
1023 if (ci->size_pixels < width) {
1024 int first_col = (pane->last_visible.col == pane->first.col)
1025 ? pane->first.col : range.end.col;
1026
1027 for (; first_col > 0; --first_col) {
1028 ColRowInfo const * const ci = sheet_col_get_info (sheet, first_col);
1029 if (ci->visible) {
1030 width -= ci->size_pixels;
1031 if (width < 0)
1032 break;
1033 }
1034 }
1035 new_first_col = first_col+1;
1036 if (new_first_col > range.start.col)
1037 new_first_col = range.start.col;
1038 } else
1039 new_first_col = col;
1040 } else
1041 new_first_col = pane->first.col;
1042
1043 /* Find the new pane->first.row */
1044 if (range.start.row < pane->first.row) {
1045 new_first_row = range.start.row;
1046 } else if (range.end.row > pane->last_full.row) {
1047 int height = ca.height;
1048 ColRowInfo const * const ri = sheet_row_get_info (sheet, range.end.row);
1049 if (ri->size_pixels < height) {
1050 int first_row = (pane->last_visible.row == pane->first.row)
1051 ? pane->first.row : range.end.row;
1052
1053 for (; first_row > 0; --first_row) {
1054 ColRowInfo const * const ri = sheet_row_get_info (sheet, first_row);
1055 if (ri->visible) {
1056 height -= ri->size_pixels;
1057 if (height < 0)
1058 break;
1059 }
1060 }
1061 new_first_row = first_row+1;
1062 if (new_first_row > range.start.row)
1063 new_first_row = range.start.row;
1064 } else
1065 new_first_row = row;
1066 } else
1067 new_first_row = pane->first.row;
1068
1069 gnm_pane_set_top_left (pane, new_first_col, new_first_row,
1070 force_scroll);
1071 }
1072
1073 /**
1074 * scg_make_cell_visible:
1075 * @scg: The gui control
1076 * @col:
1077 * @row:
1078 * @force_scroll: Completely recalibrate the offsets to the new position
1079 * @couple_panes: Scroll scroll dynamic panes back to bounds if target
1080 * is in frozen segment.
1081 *
1082 * Ensure that cell (col, row) is visible.
1083 * Sheet is scrolled if cell is outside viewport.
1084 */
1085 void
scg_make_cell_visible(SheetControlGUI * scg,int col,int row,gboolean force_scroll,gboolean couple_panes)1086 scg_make_cell_visible (SheetControlGUI *scg, int col, int row,
1087 gboolean force_scroll, gboolean couple_panes)
1088 {
1089 SheetView const *sv = scg_view (scg);
1090 GnmCellPos const *tl, *br;
1091
1092 g_return_if_fail (GNM_IS_SCG (scg));
1093
1094 if (!scg->active_panes)
1095 return;
1096
1097 tl = &sv->frozen_top_left;
1098 br = &sv->unfrozen_top_left;
1099 if (col < br->col) {
1100 if (row >= br->row) { /* pane 1 */
1101 if (col < tl->col)
1102 col = tl->col;
1103 gnm_pane_make_cell_visible (scg->pane[1],
1104 col, row, force_scroll);
1105 gnm_pane_set_top_left (scg->pane[0],
1106 couple_panes ? br->col : scg->pane[0]->first.col,
1107 scg->pane[1]->first.row,
1108 force_scroll);
1109 if (couple_panes && scg->pane[3])
1110 gnm_pane_set_left_col (scg->pane[3], br->col);
1111 } else if (couple_panes) { /* pane 2 */
1112 /* FIXME : We may need to change the way this routine
1113 * is used to fix this. Because we only know what the
1114 * target cell is we cannot absolutely differentiate
1115 * between col & row scrolling. For now use the
1116 * heuristic that if the col was visible this is a
1117 * vertical jump.
1118 */
1119 if (scg->pane[2]->first.col <= col &&
1120 scg->pane[2]->last_visible.col >= col) {
1121 scg_set_top_row (scg, row);
1122 } else
1123 scg_set_left_col (scg, col);
1124 }
1125 } else if (row < br->row) { /* pane 3 */
1126 if (row < tl->row)
1127 row = tl->row;
1128 gnm_pane_make_cell_visible (scg->pane[3],
1129 col, row, force_scroll);
1130 gnm_pane_set_top_left (scg->pane[0],
1131 scg->pane[3]->first.col,
1132 couple_panes
1133 ? br->row
1134 : scg->pane[0]->first.row,
1135 force_scroll);
1136 if (couple_panes && scg->pane[1])
1137 gnm_pane_set_top_row (scg->pane[1],
1138 br->row);
1139 } else { /* pane 0 */
1140 gnm_pane_make_cell_visible (scg->pane[0],
1141 col, row, force_scroll);
1142 if (scg->pane[1])
1143 gnm_pane_set_top_left (scg->pane[1],
1144 tl->col, scg->pane[0]->first.row, force_scroll);
1145 if (scg->pane[3])
1146 gnm_pane_set_top_left (scg->pane[3],
1147 scg->pane[0]->first.col, tl->row, force_scroll);
1148 }
1149 if (scg->pane[2])
1150 gnm_pane_set_top_left (scg->pane[2],
1151 tl->col, tl->row, force_scroll);
1152 }
1153
1154 static void
scg_make_cell_visible_virt(SheetControl * sc,int col,int row,gboolean couple_panes)1155 scg_make_cell_visible_virt (SheetControl *sc, int col, int row,
1156 gboolean couple_panes)
1157 {
1158 scg_make_cell_visible ((SheetControlGUI *)sc, col, row,
1159 FALSE, couple_panes);
1160 }
1161
1162 /*************************************************************************/
1163
1164 static void
scg_set_panes(SheetControl * sc)1165 scg_set_panes (SheetControl *sc)
1166 {
1167 SheetControlGUI *scg = (SheetControlGUI *) sc;
1168 SheetView *sv = sc->view;
1169 gboolean const being_frozen = gnm_sheet_view_is_frozen (sv);
1170 GocDirection direction = (sv_sheet (sv)->text_is_rtl)? GOC_DIRECTION_RTL: GOC_DIRECTION_LTR;
1171
1172 g_return_if_fail (GNM_IS_SHEET_VIEW (sv));
1173
1174 if (!scg->pane[0])
1175 return;
1176 if (being_frozen) {
1177 GnmCellPos const *tl = &sv->frozen_top_left;
1178 GnmCellPos const *br = &sv->unfrozen_top_left;
1179 gboolean const freeze_h = br->col > tl->col;
1180 gboolean const freeze_v = br->row > tl->row;
1181
1182 gnm_pane_bound_set (scg->pane[0],
1183 br->col, br->row,
1184 gnm_sheet_get_last_col (sv->sheet), gnm_sheet_get_last_row (sv->sheet));
1185
1186 if (freeze_h) {
1187 scg->active_panes = 2;
1188 if (!scg->pane[1]) {
1189 scg->pane[1] = gnm_pane_new (scg, TRUE, FALSE, 1);
1190 gnm_pane_set_direction (scg->pane[1], direction);
1191 gtk_grid_attach (scg->grid,
1192 GTK_WIDGET (scg->pane[1]),
1193 2, 3, 1, 1);
1194 gtk_grid_attach (scg->grid,
1195 GTK_WIDGET (scg->pane[1]->col.canvas),
1196 2, 0, 1, 2);
1197 }
1198 gnm_pane_bound_set (scg->pane[1],
1199 tl->col, br->row, br->col - 1, gnm_sheet_get_last_row (sv->sheet));
1200 }
1201 if (freeze_h && freeze_v) {
1202 scg->active_panes = 4;
1203 if (!scg->pane[2]) {
1204 scg->pane[2] = gnm_pane_new (scg, FALSE, FALSE, 2);
1205 gnm_pane_set_direction (scg->pane[2], direction);
1206 gtk_grid_attach (scg->grid,
1207 GTK_WIDGET (scg->pane[2]),
1208 2, 2, 1, 1);
1209 }
1210 gnm_pane_bound_set (scg->pane[2],
1211 tl->col, tl->row, br->col - 1, br->row - 1);
1212 }
1213 if (freeze_v) {
1214 scg->active_panes = 4;
1215 if (!scg->pane[3]) {
1216 scg->pane[3] = gnm_pane_new (scg, FALSE, TRUE, 3);
1217 gnm_pane_set_direction (scg->pane[3], direction);
1218 gtk_grid_attach (scg->grid,
1219 GTK_WIDGET (scg->pane[3]),
1220 3, 2, 1, 1);
1221 gtk_grid_attach (scg->grid,
1222 GTK_WIDGET (scg->pane[3]->row.canvas),
1223 0, 2, 2, 1);
1224 }
1225 gnm_pane_bound_set (scg->pane[3],
1226 br->col, tl->row, gnm_sheet_get_last_col (sv->sheet), br->row - 1);
1227 }
1228 } else {
1229 int i;
1230 for (i = 1 ; i <= 3 ; i++)
1231 if (scg->pane[i]) {
1232 gtk_widget_destroy (GTK_WIDGET (scg->pane[i]));
1233 scg->pane[i] = NULL;
1234 }
1235
1236 scg->active_panes = 1;
1237 gnm_pane_bound_set (scg->pane[0],
1238 0, 0, gnm_sheet_get_last_col (sv->sheet), gnm_sheet_get_last_row (sv->sheet));
1239 }
1240
1241 gtk_widget_show_all (GTK_WIDGET (scg->grid));
1242
1243 /* in case headers are hidden */
1244 scg_adjust_preferences (scg);
1245 scg_resize (scg, TRUE);
1246
1247 if (being_frozen) {
1248 GnmCellPos const *tl = &sc->view->frozen_top_left;
1249
1250 if (scg->pane[1])
1251 gnm_pane_set_left_col (scg->pane[1], tl->col);
1252 if (scg->pane[2])
1253 gnm_pane_set_top_left (scg->pane[2], tl->col, tl->row, TRUE);
1254 if (scg->pane[3])
1255 gnm_pane_set_top_row (scg->pane[3], tl->row);
1256 }
1257 set_resize_pane_pos (scg, scg->vpane);
1258 set_resize_pane_pos (scg, scg->hpane);
1259 }
1260
1261 static void
cb_wbc_destroyed(SheetControlGUI * scg)1262 cb_wbc_destroyed (SheetControlGUI *scg)
1263 {
1264 scg->wbcg = NULL;
1265 scg->sheet_control.wbc = NULL;
1266 }
1267
1268 static void
cb_scg_redraw(SheetControlGUI * scg)1269 cb_scg_redraw (SheetControlGUI *scg)
1270 {
1271 scg_adjust_preferences (scg);
1272 scg_redraw_all (&scg->sheet_control, TRUE);
1273 }
1274
1275 static void
cb_scg_redraw_resize(SheetControlGUI * scg)1276 cb_scg_redraw_resize (SheetControlGUI *scg)
1277 {
1278 cb_scg_redraw (scg);
1279 scg_resize (scg, FALSE);
1280 }
1281
1282 static void
cb_scg_sheet_resized(SheetControlGUI * scg)1283 cb_scg_sheet_resized (SheetControlGUI *scg)
1284 {
1285 cb_scg_redraw_resize (scg);
1286 sc_set_panes (&scg->sheet_control);
1287 }
1288
1289 static void
cb_scg_direction_changed(SheetControlGUI * scg)1290 cb_scg_direction_changed (SheetControlGUI *scg)
1291 {
1292 /* set direction in the canvas */
1293 int i = scg->active_panes;
1294 while (i-- > 0) {
1295 GnmPane *pane = scg->pane[i];
1296 if (NULL != pane)
1297 gnm_pane_set_direction (scg->pane[i],
1298 scg_sheet (scg)->text_is_rtl? GOC_DIRECTION_RTL: GOC_DIRECTION_LTR);
1299 }
1300 scg_resize (scg, TRUE);
1301 }
1302
1303 static GnmPane const *
resize_pane_pos(SheetControlGUI * scg,GtkPaned * p,int * colrow_result,gint64 * guide_pos)1304 resize_pane_pos (SheetControlGUI *scg, GtkPaned *p,
1305 int *colrow_result, gint64 *guide_pos)
1306 {
1307 ColRowInfo const *cri;
1308 GnmPane const *pane = scg_pane (scg, 0);
1309 gboolean const vert = (p == scg->hpane);
1310 int colrow, handle;
1311 gint64 pos = gtk_paned_get_position (p);
1312
1313 gtk_widget_style_get (GTK_WIDGET (p), "handle-size", &handle, NULL);
1314 pos += handle / 2;
1315 if (vert) {
1316 if (gtk_widget_get_visible (GTK_WIDGET (pane->row.canvas))) {
1317 GtkAllocation ca;
1318 gtk_widget_get_allocation (GTK_WIDGET (pane->row.canvas), &ca);
1319 pos -= ca.width;
1320 }
1321 if (scg->pane[1]) {
1322 GtkAllocation pa;
1323 gtk_widget_get_allocation (GTK_WIDGET (scg->pane[1]),
1324 &pa);
1325
1326 if (pos < pa.width)
1327 pane = scg_pane (scg, 1);
1328 else
1329 pos -= pa.width;
1330 }
1331 pos = MAX (pos, 0);
1332 pos += pane->first_offset.x;
1333 colrow = gnm_pane_find_col (pane, pos, guide_pos);
1334 } else {
1335 if (gtk_widget_get_visible (GTK_WIDGET (pane->col.canvas))) {
1336 GtkAllocation ca;
1337 gtk_widget_get_allocation (GTK_WIDGET (pane->col.canvas), &ca);
1338 pos -= ca.height;
1339 }
1340 if (scg->pane[3]) {
1341 GtkAllocation pa;
1342 gtk_widget_get_allocation (GTK_WIDGET (scg->pane[3]),
1343 &pa);
1344 if (pos < pa.height)
1345 pane = scg_pane (scg, 3);
1346 else
1347 pos -= pa.height;
1348 }
1349 pos = MAX (pos, 0);
1350 pos += pane->first_offset.y;
1351 colrow = gnm_pane_find_row (pane, pos, guide_pos);
1352 }
1353 cri = sheet_colrow_get_info (scg_sheet (scg), colrow, vert);
1354 if (pos >= (*guide_pos + cri->size_pixels / 2)) {
1355 *guide_pos += cri->size_pixels;
1356 colrow++;
1357 }
1358 if (NULL != colrow_result)
1359 *colrow_result = colrow;
1360
1361 return pane;
1362 }
1363
1364 static void
scg_gtk_paned_set_position(SheetControlGUI * scg,GtkPaned * p,int pane_pos)1365 scg_gtk_paned_set_position (SheetControlGUI *scg, GtkPaned *p, int pane_pos)
1366 {
1367 /* A negative position is special to GtkPaned. */
1368 pane_pos = MAX (pane_pos, 0);
1369
1370 if (p == scg->vpane)
1371 scg->vpos = pane_pos;
1372 else
1373 scg->hpos = pane_pos;
1374
1375 gtk_paned_set_position (p, pane_pos);
1376 }
1377
1378 static void
set_resize_pane_pos(SheetControlGUI * scg,GtkPaned * p)1379 set_resize_pane_pos (SheetControlGUI *scg, GtkPaned *p)
1380 {
1381 int handle_size, pane_pos, size;
1382 GnmPane *pane0 = scg->pane[0];
1383
1384 if (!pane0)
1385 return;
1386
1387 if (p == scg->vpane) {
1388 if (gtk_widget_get_visible (GTK_WIDGET (pane0->col.canvas))) {
1389 GtkAllocation alloc;
1390 gtk_widget_get_allocation (GTK_WIDGET (pane0->col.canvas), &alloc);
1391 pane_pos = alloc.height;
1392 } else
1393 pane_pos = 0;
1394 if (scg->pane[3]) {
1395 gtk_widget_get_size_request (
1396 GTK_WIDGET (scg->pane[3]), NULL, &size);
1397 pane_pos += size;
1398 }
1399 } else {
1400 if (gtk_widget_get_visible (GTK_WIDGET (pane0->row.canvas))) {
1401 GtkAllocation alloc;
1402 gtk_widget_get_allocation (GTK_WIDGET (pane0->row.canvas), &alloc);
1403 pane_pos = alloc.width;
1404 } else
1405 pane_pos = 0;
1406 if (scg->pane[1]) {
1407 gtk_widget_get_size_request (
1408 GTK_WIDGET (scg->pane[1]), &size, NULL);
1409 pane_pos += size;
1410 }
1411 }
1412 gtk_widget_style_get (GTK_WIDGET (p), "handle-size", &handle_size, NULL);
1413 pane_pos -= handle_size / 2;
1414
1415 g_signal_handlers_block_by_func (G_OBJECT (p),
1416 G_CALLBACK (cb_resize_pane_motion), scg);
1417 scg_gtk_paned_set_position (scg, p, pane_pos);
1418 g_signal_handlers_unblock_by_func (G_OBJECT (p),
1419 G_CALLBACK (cb_resize_pane_motion), scg);
1420 }
1421
1422 static void
1423 cb_check_resize (GtkPaned *p, GtkAllocation *allocation, SheetControlGUI *scg);
1424
1425 static gboolean
resize_pane_finish(SheetControlGUI * scg,GtkPaned * p)1426 resize_pane_finish (SheetControlGUI *scg, GtkPaned *p)
1427 {
1428 SheetView *sv = scg_view (scg);
1429 GnmCellPos frozen_tl, unfrozen_tl;
1430 GnmPane const *pane;
1431 int colrow;
1432 gint64 guide_pos;
1433
1434 #warning GTK3: replace this?
1435 #if 0
1436 if (p->in_drag)
1437 return TRUE;
1438 #endif
1439 pane = resize_pane_pos (scg, p, &colrow, &guide_pos);
1440
1441 if (gnm_sheet_view_is_frozen (sv)) {
1442 frozen_tl = sv->frozen_top_left;
1443 unfrozen_tl = sv->unfrozen_top_left;
1444 } else
1445 frozen_tl = pane->first;
1446 if (p == scg->hpane) {
1447 unfrozen_tl.col = colrow;
1448 if (!gnm_sheet_view_is_frozen (sv))
1449 unfrozen_tl.row = frozen_tl.row = 0;
1450 } else {
1451 unfrozen_tl.row = colrow;
1452 if (!gnm_sheet_view_is_frozen (sv))
1453 unfrozen_tl.col = frozen_tl.col = 0;
1454 }
1455 gnm_sheet_view_freeze_panes (sv, &frozen_tl, &unfrozen_tl);
1456
1457 scg->pane_drag_handler = 0;
1458 scg_size_guide_stop (scg);
1459
1460 set_resize_pane_pos (scg, p);
1461
1462 g_signal_handlers_unblock_by_func
1463 (G_OBJECT (p),
1464 G_CALLBACK (cb_check_resize), scg);
1465
1466 return FALSE;
1467 }
1468 static gboolean
cb_resize_vpane_finish(SheetControlGUI * scg)1469 cb_resize_vpane_finish (SheetControlGUI *scg)
1470 {
1471 return resize_pane_finish (scg, scg->vpane);
1472 }
1473 static gboolean
cb_resize_hpane_finish(SheetControlGUI * scg)1474 cb_resize_hpane_finish (SheetControlGUI *scg)
1475 {
1476 return resize_pane_finish (scg, scg->hpane);
1477 }
1478
1479 static void
cb_resize_pane_motion(GtkPaned * p,G_GNUC_UNUSED GParamSpec * pspec,SheetControlGUI * scg)1480 cb_resize_pane_motion (GtkPaned *p,
1481 G_GNUC_UNUSED GParamSpec *pspec,
1482 SheetControlGUI *scg)
1483 {
1484 gboolean const vert = (p == scg->hpane);
1485 int colrow;
1486 gint64 guide_pos;
1487
1488 resize_pane_pos (scg, p, &colrow, &guide_pos);
1489 #warning GTK3: what replaces p->in_drag?
1490 if (scg->pane_drag_handler == 0/* && p->in_drag*/) {
1491 g_signal_handlers_block_by_func
1492 (G_OBJECT (p),
1493 G_CALLBACK (cb_check_resize), scg);
1494 scg_size_guide_start (scg, vert, colrow, FALSE);
1495 scg->pane_drag_handler = g_timeout_add (250,
1496 vert ? (GSourceFunc) cb_resize_hpane_finish
1497 : (GSourceFunc) cb_resize_vpane_finish,
1498 (gpointer) scg);
1499 }
1500 if (scg->pane_drag_handler)
1501 scg_size_guide_motion (scg, vert, guide_pos);
1502
1503 }
1504
1505 static void
cb_check_resize(GtkPaned * p,G_GNUC_UNUSED GtkAllocation * allocation,SheetControlGUI * scg)1506 cb_check_resize (GtkPaned *p, G_GNUC_UNUSED GtkAllocation *allocation,
1507 SheetControlGUI *scg)
1508 {
1509 gboolean const vert = (p == scg->vpane);
1510 gint max, pos = vert ? scg->vpos : scg->hpos;
1511
1512 g_object_get (G_OBJECT (p), "max-position", &max, NULL);
1513 if (pos > max)
1514 pos = max;
1515
1516 if (gtk_paned_get_position (p) != pos) {
1517 g_signal_handlers_block_by_func
1518 (G_OBJECT (p),
1519 G_CALLBACK (cb_resize_pane_motion), scg);
1520 gtk_paned_set_position (p, pos);
1521 g_signal_handlers_unblock_by_func
1522 (G_OBJECT (p),
1523 G_CALLBACK (cb_resize_pane_motion), scg);
1524 }
1525 }
1526
1527 struct resize_closure {
1528 GtkPaned *p;
1529 SheetControlGUI *scg;
1530 };
1531
1532 static gboolean
idle_resize(struct resize_closure * r)1533 idle_resize (struct resize_closure *r)
1534 {
1535
1536 set_resize_pane_pos (r->scg, r->p);
1537 g_free (r);
1538 return FALSE;
1539 }
1540
1541 static void
cb_canvas_resize(GtkWidget * w,G_GNUC_UNUSED GtkAllocation * allocation,SheetControlGUI * scg)1542 cb_canvas_resize (GtkWidget *w, G_GNUC_UNUSED GtkAllocation *allocation,
1543 SheetControlGUI *scg)
1544 {
1545 struct resize_closure *r = g_new (struct resize_closure, 1);
1546 r->scg = scg;
1547 r->p = (w == GTK_WIDGET (scg->pane[0]->col.canvas))? scg->hpane: scg->vpane;
1548 /* The allocation is not correct at this point, weird */
1549 g_idle_add ((GSourceFunc) idle_resize, r);
1550 }
1551
1552 static gboolean
post_create_cb(SheetControlGUI * scg)1553 post_create_cb (SheetControlGUI *scg)
1554 {
1555 Sheet *sheet = sc_sheet (GNM_SHEET_CONTROL (scg));
1556 if (sheet->sheet_objects)
1557 scg_object_select (scg, (SheetObject *) sheet->sheet_objects->data);
1558 return FALSE;
1559 }
1560
1561 static gboolean
sheet_object_key_pressed(G_GNUC_UNUSED GtkWidget * w,GdkEventKey * event,SheetControlGUI * scg)1562 sheet_object_key_pressed (G_GNUC_UNUSED GtkWidget *w, GdkEventKey *event, SheetControlGUI *scg)
1563 {
1564 Sheet *sheet = scg_sheet (scg);
1565 WorkbookControl * wbc = scg_wbc (scg);
1566 Workbook * wb = wb_control_get_workbook (wbc);
1567 switch (event->keyval) {
1568 case GDK_KEY_KP_Page_Up:
1569 case GDK_KEY_Page_Up:
1570 if ((event->state & GDK_CONTROL_MASK) != 0){
1571 if ((event->state & GDK_SHIFT_MASK) != 0){
1572 WorkbookSheetState * old_state = workbook_sheet_state_new(wb);
1573 int old_pos = sheet->index_in_wb;
1574
1575 if (old_pos > 0){
1576 workbook_sheet_move(sheet, -1);
1577 cmd_reorganize_sheets (wbc, old_state, sheet);
1578 }
1579 } else {
1580 gnm_notebook_prev_page (scg->wbcg->bnotebook);
1581 }
1582 return FALSE;
1583 }
1584 break;
1585 case GDK_KEY_KP_Page_Down:
1586 case GDK_KEY_Page_Down:
1587
1588 if ((event->state & GDK_CONTROL_MASK) != 0){
1589 if ((event->state & GDK_SHIFT_MASK) != 0){
1590 WorkbookSheetState * old_state = workbook_sheet_state_new(wb);
1591 int num_sheets = workbook_sheet_count(wb);
1592 gint old_pos = sheet->index_in_wb;
1593
1594 if (old_pos < num_sheets - 1){
1595 workbook_sheet_move(sheet, 1);
1596 cmd_reorganize_sheets (wbc, old_state, sheet);
1597 }
1598 } else {
1599 gnm_notebook_next_page (scg->wbcg->bnotebook);
1600 }
1601 return FALSE;
1602 }
1603 break;
1604 }
1605 return TRUE;
1606 }
1607
1608 static void
cb_screen_changed(GtkWidget * widget,G_GNUC_UNUSED GdkScreen * prev,SheetControlGUI * scg)1609 cb_screen_changed (GtkWidget *widget, G_GNUC_UNUSED GdkScreen *prev,
1610 SheetControlGUI *scg)
1611 {
1612 GdkScreen *screen = gtk_widget_get_screen (widget);
1613
1614 if (screen) {
1615 scg->screen_width = gdk_screen_get_width (screen);
1616 scg->screen_height = gdk_screen_get_height (screen);
1617 }
1618 }
1619
1620 SheetControlGUI *
sheet_control_gui_new(SheetView * sv,WBCGtk * wbcg)1621 sheet_control_gui_new (SheetView *sv, WBCGtk *wbcg)
1622 {
1623 SheetControlGUI *scg;
1624 Sheet *sheet;
1625 GocDirection direction;
1626 GdkRGBA cfore, cback;
1627
1628 g_return_val_if_fail (GNM_IS_SHEET_VIEW (sv), NULL);
1629
1630 sheet = sv_sheet (sv);
1631 direction = (sheet->text_is_rtl)? GOC_DIRECTION_RTL: GOC_DIRECTION_LTR;
1632
1633 scg = g_object_new (GNM_SCG_TYPE, NULL);
1634 scg->wbcg = wbcg;
1635 scg->sheet_control.wbc = GNM_WBC (wbcg);
1636
1637 g_object_weak_ref (G_OBJECT (wbcg),
1638 (GWeakNotify) cb_wbc_destroyed,
1639 scg);
1640
1641 if (sheet->sheet_type == GNM_SHEET_DATA) {
1642 scg->active_panes = 1;
1643 scg->pane[0] = NULL;
1644 scg->pane[1] = NULL;
1645 scg->pane[2] = NULL;
1646 scg->pane[3] = NULL;
1647 scg->pane_drag_handler = 0;
1648
1649 scg->col_group.buttons = g_ptr_array_new ();
1650 scg->row_group.buttons = g_ptr_array_new ();
1651 scg->col_group.button_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1652 g_object_set (scg->col_group.button_box,
1653 "halign", GTK_ALIGN_CENTER,
1654 "homogeneous", TRUE,
1655 NULL);
1656 scg->row_group.button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1657 g_object_set (scg->row_group.button_box,
1658 "valign", GTK_ALIGN_CENTER,
1659 "homogeneous", TRUE,
1660 NULL);
1661 scg->select_all_btn = gtk_drawing_area_new ();
1662 gtk_style_context_add_class (gtk_widget_get_style_context (scg->select_all_btn),
1663 GTK_STYLE_CLASS_BUTTON);
1664 gtk_style_context_add_class (gtk_widget_get_style_context (scg->select_all_btn),
1665 "all");
1666 gtk_widget_add_events (scg->select_all_btn, GDK_BUTTON_PRESS_MASK);
1667 g_signal_connect (G_OBJECT (scg->select_all_btn), "draw",
1668 G_CALLBACK (cb_select_all_btn_draw), scg);
1669 g_signal_connect (G_OBJECT (scg->select_all_btn), "event",
1670 G_CALLBACK (cb_select_all_btn_event), scg);
1671
1672 scg->grid = GTK_GRID (gtk_grid_new ());
1673 gtk_grid_attach (scg->grid, scg->col_group.button_box,
1674 1, 0, 1, 1);
1675 gtk_grid_attach (scg->grid, scg->row_group.button_box,
1676 0, 1, 1, 1);
1677 gtk_grid_attach (scg->grid, scg->select_all_btn, 1, 1, 1, 1);
1678
1679 scg->pane[1] = scg->pane[2] = scg->pane[3] = NULL;
1680 scg->pane[0] = gnm_pane_new (scg, TRUE, TRUE, 0);
1681 gnm_pane_set_direction (scg->pane[0], direction);
1682 gtk_grid_attach (scg->grid,
1683 GTK_WIDGET (scg->pane[0]->col.canvas),
1684 3, 0, 1, 2);
1685 gtk_grid_attach (scg->grid,
1686 GTK_WIDGET (scg->pane[0]->row.canvas),
1687 0, 3, 2, 1);
1688 g_object_set (scg->pane[0],
1689 "hexpand", TRUE,
1690 "vexpand", TRUE,
1691 NULL);
1692 gtk_grid_attach (scg->grid, GTK_WIDGET (scg->pane[0]),
1693 3, 3, 1, 1);
1694 g_signal_connect_after (G_OBJECT (scg->pane[0]->col.canvas), "size-allocate",
1695 G_CALLBACK (cb_canvas_resize), scg);
1696 g_signal_connect_after (G_OBJECT (scg->pane[0]->row.canvas), "size-allocate",
1697 G_CALLBACK (cb_canvas_resize), scg);
1698
1699 scg->va = (GtkAdjustment *)gtk_adjustment_new (0., 0., 1, 1., 1., 1.);
1700 scg->vs = g_object_new (GTK_TYPE_SCROLLBAR,
1701 "orientation", GTK_ORIENTATION_VERTICAL,
1702 "adjustment", scg->va,
1703 NULL);
1704 g_signal_connect (G_OBJECT (scg->vs),
1705 "value_changed",
1706 G_CALLBACK (cb_vscrollbar_value_changed), scg);
1707 g_signal_connect (G_OBJECT (scg->vs),
1708 "adjust_bounds",
1709 G_CALLBACK (cb_vscrollbar_adjust_bounds), sheet);
1710
1711 scg->ha = (GtkAdjustment *)gtk_adjustment_new (0., 0., 1, 1., 1., 1.);
1712 scg->hs = g_object_new (GTK_TYPE_SCROLLBAR,
1713 "adjustment", scg->ha,
1714 NULL);
1715 g_signal_connect (G_OBJECT (scg->hs),
1716 "value_changed",
1717 G_CALLBACK (cb_hscrollbar_value_changed), scg);
1718 g_signal_connect (G_OBJECT (scg->hs),
1719 "adjust_bounds",
1720 G_CALLBACK (cb_hscrollbar_adjust_bounds), sheet);
1721
1722 g_object_ref (scg->grid);
1723 scg->vpane = g_object_new (GTK_TYPE_PANED, "orientation", GTK_ORIENTATION_VERTICAL, NULL);
1724 gtk_paned_add1 (scg->vpane, gtk_label_new (NULL)); /* use a spacer */
1725 gtk_paned_add2 (scg->vpane, scg->vs);
1726 scg_gtk_paned_set_position (scg, scg->vpane, 0);
1727 gtk_widget_set_vexpand (GTK_WIDGET (scg->vpane), TRUE);
1728 gtk_grid_attach (scg->grid,
1729 GTK_WIDGET (scg->vpane), 4, 0, 1, 4);
1730 scg->hpane = g_object_new (GTK_TYPE_PANED, NULL);
1731 gtk_paned_add1 (scg->hpane, gtk_label_new (NULL)); /* use a spacer */
1732 gtk_paned_add2 (scg->hpane, scg->hs);
1733 scg_gtk_paned_set_position (scg, scg->hpane, 0);
1734 gtk_widget_set_hexpand (GTK_WIDGET (scg->hpane), TRUE);
1735 gtk_grid_attach (scg->grid,
1736 GTK_WIDGET (scg->hpane), 0, 4, 4, 1);
1737 /* do not connect until after setting position */
1738 g_signal_connect (G_OBJECT (scg->vpane), "notify::position",
1739 G_CALLBACK (cb_resize_pane_motion), scg);
1740 g_signal_connect (G_OBJECT (scg->hpane), "notify::position",
1741 G_CALLBACK (cb_resize_pane_motion), scg);
1742 g_signal_connect_after (G_OBJECT (scg->vpane), "size-allocate",
1743 G_CALLBACK (cb_check_resize), scg);
1744 g_signal_connect_after (G_OBJECT (scg->hpane), "size-allocate",
1745 G_CALLBACK (cb_check_resize), scg);
1746
1747 g_signal_connect_data (G_OBJECT (scg->grid),
1748 "size-allocate",
1749 G_CALLBACK (scg_scrollbar_config), scg, NULL,
1750 G_CONNECT_AFTER | G_CONNECT_SWAPPED);
1751 g_signal_connect_object (G_OBJECT (scg->grid),
1752 "destroy",
1753 G_CALLBACK (cb_table_destroy), G_OBJECT (scg),
1754 G_CONNECT_SWAPPED);
1755
1756 gnm_sheet_view_attach_control (sv, GNM_SHEET_CONTROL (scg));
1757
1758 g_object_connect (G_OBJECT (sheet),
1759 "swapped_signal::notify::text-is-rtl", cb_scg_direction_changed, scg,
1760 "swapped_signal::notify::display-formulas", cb_scg_redraw, scg,
1761 "swapped_signal::notify::display-zeros", cb_scg_redraw, scg,
1762 "swapped_signal::notify::display-grid", cb_scg_redraw, scg,
1763 "swapped_signal::notify::display-column-header", scg_adjust_preferences, scg,
1764 "swapped_signal::notify::display-row-header", scg_adjust_preferences, scg,
1765 "swapped_signal::notify::use-r1c1", cb_scg_redraw, scg,
1766 "swapped_signal::notify::display-outlines", cb_scg_redraw_resize, scg,
1767 "swapped_signal::notify::display-outlines-below", cb_scg_redraw_resize, scg,
1768 "swapped_signal::notify::display-outlines-right", cb_scg_redraw_resize, scg,
1769 "swapped_signal::notify::columns", cb_scg_sheet_resized, scg,
1770 "swapped_signal::notify::rows", cb_scg_sheet_resized, scg,
1771 NULL);
1772 } else {
1773 scg->active_panes = 0;
1774 scg->grid = GTK_GRID (gtk_grid_new ());
1775 g_object_ref (scg->grid);
1776 sheet->hide_col_header = sheet->hide_row_header = FALSE;
1777 if (sheet->sheet_type == GNM_SHEET_OBJECT) {
1778 /* WHY store this in ->vs? */
1779 scg->vs = g_object_new (GOC_TYPE_CANVAS,
1780 "hexpand", TRUE,
1781 "vexpand", TRUE,
1782 NULL);
1783 gtk_style_context_add_class (gtk_widget_get_style_context (scg->vs),
1784 "full-sheet");
1785 gtk_grid_attach (scg->grid, scg->vs, 0, 0, 1, 1);
1786 gtk_widget_set_can_focus (scg->vs, TRUE);
1787 gtk_widget_set_can_default (scg->vs, TRUE);
1788 g_signal_connect (G_OBJECT (scg->vs), "key-press-event",
1789 G_CALLBACK (sheet_object_key_pressed), scg);
1790 }
1791 gnm_sheet_view_attach_control (sv, GNM_SHEET_CONTROL (scg));
1792 if (scg->vs) {
1793 g_object_set_data (G_OBJECT (scg->vs), "sheet-control", scg);
1794 if (sheet->sheet_objects) {
1795 /* we need an idle function because not everything is initialized at this point */
1796 sheet_object_new_view ((SheetObject *) sheet->sheet_objects->data,
1797 (SheetObjectViewContainer*) scg->vs);
1798 g_idle_add ((GSourceFunc) post_create_cb, scg);
1799 }
1800 }
1801 }
1802
1803 scg->label = g_object_new
1804 (GNM_NOTEBOOK_BUTTON_TYPE,
1805 "label", sheet->name_unquoted,
1806 //"valign", GTK_ALIGN_START,
1807 "background-color",
1808 (sheet->tab_color
1809 ? go_color_to_gdk_rgba (sheet->tab_color->go_color,
1810 &cback)
1811 : NULL),
1812 "text-color",
1813 (sheet->tab_text_color
1814 ? go_color_to_gdk_rgba (sheet->tab_text_color->go_color,
1815 &cfore)
1816 : NULL),
1817 NULL);
1818 g_object_ref (scg->label);
1819
1820 g_signal_connect (G_OBJECT (scg->grid),
1821 "screen-changed",
1822 G_CALLBACK (cb_screen_changed),
1823 scg);
1824
1825 return scg;
1826 }
1827
1828 static void
scg_comment_timer_clear(SheetControlGUI * scg)1829 scg_comment_timer_clear (SheetControlGUI *scg)
1830 {
1831 if (scg->comment.timer != 0) {
1832 g_source_remove (scg->comment.timer);
1833 scg->comment.timer = 0;
1834 }
1835 }
1836
1837 static void
scg_im_destroy(SheetControlGUI * scg)1838 scg_im_destroy (SheetControlGUI *scg) {
1839 if (scg->im.timer != 0) {
1840 g_source_remove (scg->im.timer);
1841 scg->im.timer = 0;
1842 }
1843 if (scg->im.item) {
1844 gtk_widget_destroy (scg->im.item);
1845 scg->im.item = NULL;
1846 }
1847 }
1848
1849 static void
scg_finalize(GObject * object)1850 scg_finalize (GObject *object)
1851 {
1852 SheetControlGUI *scg = GNM_SCG (object);
1853 SheetControl *sc = (SheetControl *) scg;
1854 Sheet *sheet = scg_sheet (scg);
1855 GSList *ptr;
1856
1857 /* remove the object view before we disappear */
1858 scg_object_unselect (scg, NULL);
1859 if (*scg->pane)
1860 for (ptr = sheet->sheet_objects; ptr != NULL ; ptr = ptr->next )
1861 SCG_FOREACH_PANE (scg, pane,
1862 g_object_unref (
1863 sheet_object_get_view (ptr->data, (SheetObjectViewContainer *)pane));
1864 );
1865
1866 if (scg->col_group.buttons) {
1867 g_ptr_array_free (scg->col_group.buttons, TRUE);
1868 g_ptr_array_free (scg->row_group.buttons, TRUE);
1869 }
1870
1871 if (scg->pane_drag_handler) {
1872 g_source_remove (scg->pane_drag_handler);
1873 scg->pane_drag_handler = 0;
1874 }
1875
1876 if (scg->scroll_bar_timer) {
1877 g_source_remove (scg->scroll_bar_timer);
1878 scg->scroll_bar_timer = 0;
1879 }
1880
1881 scg_comment_timer_clear (scg);
1882
1883 if (scg->delayedMovement.timer != 0) {
1884 g_source_remove (scg->delayedMovement.timer);
1885 scg->delayedMovement.timer = 0;
1886 }
1887 scg_comment_unselect (scg, scg->comment.selected);
1888
1889 scg_im_destroy (scg);
1890
1891 if (sc->view) {
1892 Sheet *sheet = sv_sheet (sc->view);
1893 g_signal_handlers_disconnect_by_func (sheet, scg_adjust_preferences, scg);
1894 g_signal_handlers_disconnect_by_func (sheet, cb_scg_redraw, scg);
1895 g_signal_handlers_disconnect_by_func (sheet, cb_scg_redraw_resize, scg);
1896 g_signal_handlers_disconnect_by_func (sheet, cb_scg_sheet_resized, scg);
1897 g_signal_handlers_disconnect_by_func (sheet, cb_scg_direction_changed, scg);
1898 gnm_sheet_view_detach_control (sc->view, sc);
1899 }
1900
1901 if (scg->grid) {
1902 gtk_widget_destroy (GTK_WIDGET (scg->grid));
1903 g_object_unref (scg->grid);
1904 scg->grid = NULL;
1905 }
1906
1907 g_clear_object (&scg->label);
1908
1909 if (scg->wbcg != NULL)
1910 g_object_weak_unref (G_OBJECT (scg->wbcg),
1911 (GWeakNotify) cb_wbc_destroyed,
1912 scg);
1913
1914 (*scg_parent_class->finalize) (object);
1915 }
1916
1917 static void
scg_unant(SheetControl * sc)1918 scg_unant (SheetControl *sc)
1919 {
1920 SheetControlGUI *scg = (SheetControlGUI *)sc;
1921
1922 g_return_if_fail (GNM_IS_SCG (scg));
1923
1924 /* Always have a pane 0 */
1925 if (scg->active_panes == 0 || scg->pane[0]->cursor.animated == NULL)
1926 return;
1927
1928 SCG_FOREACH_PANE (scg, pane, {
1929 GSList *l;
1930
1931 for (l = pane->cursor.animated; l; l = l->next) {
1932 GocItem *item = l->data;
1933 goc_item_destroy (item);
1934 }
1935
1936 g_slist_free (pane->cursor.animated);
1937 pane->cursor.animated = NULL;
1938 });
1939 }
1940
1941 static void
scg_ant(SheetControl * sc)1942 scg_ant (SheetControl *sc)
1943 {
1944 SheetControlGUI *scg = (SheetControlGUI *)sc;
1945 GList *l;
1946
1947 g_return_if_fail (GNM_IS_SCG (scg));
1948
1949 if (scg->active_panes == 0)
1950 return;
1951
1952 /* Always have a grid 0 */
1953 if (NULL != scg->pane[0]->cursor.animated)
1954 scg_unant (sc);
1955
1956 for (l = sc->view->ants; l; l = l->next) {
1957 GnmRange const *r = l->data;
1958
1959 SCG_FOREACH_PANE (scg, pane, {
1960 GnmItemCursor *ic = GNM_ITEM_CURSOR (goc_item_new (
1961 pane->grid_items,
1962 gnm_item_cursor_get_type (),
1963 "SheetControlGUI", scg,
1964 "style", GNM_ITEM_CURSOR_ANTED,
1965 NULL));
1966 gnm_item_cursor_bound_set (ic, r);
1967 pane->cursor.animated =
1968 g_slist_prepend (pane->cursor.animated, ic);
1969 });
1970 }
1971 }
1972
1973 void
scg_adjust_preferences(SheetControlGUI * scg)1974 scg_adjust_preferences (SheetControlGUI *scg)
1975 {
1976 Sheet const *sheet = scg_sheet (scg);
1977
1978 SCG_FOREACH_PANE (scg, pane, {
1979 if (pane->col.canvas != NULL) {
1980 gtk_widget_set_visible (GTK_WIDGET (pane->col.canvas),
1981 !sheet->hide_col_header);
1982 }
1983
1984 if (pane->row.canvas != NULL) {
1985 gtk_widget_set_visible (GTK_WIDGET (pane->row.canvas),
1986 !sheet->hide_row_header);
1987 }
1988 });
1989
1990 if (scg->select_all_btn) {
1991 /* we used to test for the corner table existence, why??? */
1992 gboolean visible = !(sheet->hide_col_header || sheet->hide_row_header);
1993 gtk_widget_set_visible (scg->select_all_btn, visible);
1994 gtk_widget_set_visible (scg->row_group.button_box, visible);
1995 gtk_widget_set_visible (scg->col_group.button_box, visible);
1996
1997 if (scg_wbc (scg) != NULL) {
1998 WorkbookView *wbv = wb_control_view (scg_wbc (scg));
1999 gtk_widget_set_visible (scg->hs,
2000 wbv->show_horizontal_scrollbar);
2001
2002 gtk_widget_set_visible (scg->vs,
2003 wbv->show_vertical_scrollbar);
2004 }
2005 }
2006 }
2007
2008 /***************************************************************************/
2009
2010 enum {
2011 CONTEXT_CUT = 1,
2012 CONTEXT_COPY,
2013 CONTEXT_PASTE,
2014 CONTEXT_PASTE_SPECIAL,
2015 CONTEXT_INSERT,
2016 CONTEXT_DELETE,
2017 CONTEXT_CLEAR_CONTENT,
2018 CONTEXT_FORMAT_CELL,
2019 CONTEXT_FORMAT_CELL_COND,
2020 CONTEXT_CELL_AUTOFIT_WIDTH,
2021 CONTEXT_CELL_AUTOFIT_HEIGHT,
2022 CONTEXT_CELL_MERGE,
2023 CONTEXT_CELL_UNMERGE,
2024 CONTEXT_COL_WIDTH,
2025 CONTEXT_COL_HIDE,
2026 CONTEXT_COL_UNHIDE,
2027 CONTEXT_COL_AUTOFIT,
2028 CONTEXT_ROW_HEIGHT,
2029 CONTEXT_ROW_HIDE,
2030 CONTEXT_ROW_UNHIDE,
2031 CONTEXT_ROW_AUTOFIT,
2032 CONTEXT_COMMENT_EDIT,
2033 CONTEXT_COMMENT_ADD,
2034 CONTEXT_COMMENT_REMOVE,
2035 CONTEXT_HYPERLINK_EDIT,
2036 CONTEXT_HYPERLINK_ADD,
2037 CONTEXT_HYPERLINK_REMOVE,
2038 CONTEXT_DATA_SLICER_REFRESH, /* refresh and redraw */
2039 CONTEXT_DATA_SLICER_EDIT /* prop dialog */
2040 };
2041 static void
context_menu_handler(GnmPopupMenuElement const * element,gpointer user_data)2042 context_menu_handler (GnmPopupMenuElement const *element,
2043 gpointer user_data)
2044 {
2045 SheetControlGUI *scg = user_data;
2046 SheetControl *sc = (SheetControl *) scg;
2047 SheetView *sv = sc->view;
2048 Sheet *sheet = sv->sheet;
2049 WBCGtk *wbcg = scg->wbcg;
2050 WorkbookControl *wbc = sc->wbc;
2051
2052 g_return_if_fail (element != NULL);
2053 g_return_if_fail (IS_SHEET (sheet));
2054
2055 switch (element->index) {
2056 case CONTEXT_CUT:
2057 gnm_sheet_view_selection_cut (sv, wbc);
2058 break;
2059 case CONTEXT_COPY:
2060 gnm_sheet_view_selection_copy (sv, wbc);
2061 break;
2062 case CONTEXT_PASTE:
2063 cmd_paste_to_selection (wbc, sv, PASTE_DEFAULT);
2064 break;
2065 case CONTEXT_PASTE_SPECIAL:
2066 dialog_paste_special (wbcg);
2067 break;
2068 case CONTEXT_INSERT:
2069 dialog_insert_cells (wbcg);
2070 break;
2071 case CONTEXT_DELETE:
2072 dialog_delete_cells (wbcg);
2073 break;
2074 case CONTEXT_CLEAR_CONTENT:
2075 cmd_selection_clear (wbc, CLEAR_VALUES);
2076 break;
2077 case CONTEXT_FORMAT_CELL:
2078 dialog_cell_format (wbcg, FD_CURRENT, 0);
2079 break;
2080 case CONTEXT_FORMAT_CELL_COND:
2081 dialog_cell_format_cond (wbcg);
2082 break;
2083 case CONTEXT_CELL_AUTOFIT_HEIGHT:
2084 workbook_cmd_autofit_selection
2085 (wbc, wb_control_cur_sheet (wbc), FALSE);
2086 break;
2087 case CONTEXT_CELL_AUTOFIT_WIDTH:
2088 workbook_cmd_autofit_selection
2089 (wbc, wb_control_cur_sheet (wbc), TRUE);
2090 break;
2091 case CONTEXT_CELL_MERGE : {
2092 GSList *range_list = selection_get_ranges
2093 (wb_control_cur_sheet_view (wbc), FALSE);
2094 cmd_merge_cells (wbc, wb_control_cur_sheet (wbc), range_list, FALSE);
2095 range_fragment_free (range_list);
2096 }
2097 break;
2098 case CONTEXT_CELL_UNMERGE : {
2099 GSList *range_list = selection_get_ranges
2100 (wb_control_cur_sheet_view (wbc), FALSE);
2101 cmd_unmerge_cells (wbc, wb_control_cur_sheet (wbc), range_list);
2102 range_fragment_free (range_list);
2103
2104 }
2105 break;
2106 case CONTEXT_COL_WIDTH:
2107 dialog_col_width (wbcg, FALSE);
2108 break;
2109 case CONTEXT_COL_AUTOFIT:
2110 workbook_cmd_resize_selected_colrow
2111 (wbc, wb_control_cur_sheet (wbc), TRUE, -1);
2112 break;
2113 case CONTEXT_COL_HIDE:
2114 cmd_selection_colrow_hide (wbc, TRUE, FALSE);
2115 break;
2116 case CONTEXT_COL_UNHIDE:
2117 cmd_selection_colrow_hide (wbc, TRUE, TRUE);
2118 break;
2119 case CONTEXT_ROW_HEIGHT:
2120 dialog_row_height (wbcg, FALSE);
2121 break;
2122 case CONTEXT_ROW_AUTOFIT:
2123 workbook_cmd_resize_selected_colrow
2124 (wbc, wb_control_cur_sheet (wbc), FALSE, -1);
2125 break;
2126 case CONTEXT_ROW_HIDE:
2127 cmd_selection_colrow_hide (wbc, FALSE, FALSE);
2128 break;
2129 case CONTEXT_ROW_UNHIDE:
2130 cmd_selection_colrow_hide (wbc, FALSE, TRUE);
2131 break;
2132 case CONTEXT_COMMENT_EDIT:
2133 case CONTEXT_COMMENT_ADD:
2134 dialog_cell_comment (wbcg, sheet, &sv->edit_pos);
2135 break;
2136 case CONTEXT_COMMENT_REMOVE:
2137 cmd_selection_clear (GNM_WBC (wbcg), CLEAR_COMMENTS);
2138 break;
2139 case CONTEXT_HYPERLINK_EDIT:
2140 case CONTEXT_HYPERLINK_ADD:
2141 dialog_hyperlink (wbcg, sc);
2142 break;
2143
2144 case CONTEXT_HYPERLINK_REMOVE: {
2145 GnmStyle *style = gnm_style_new ();
2146 GSList *l;
2147 int n_links = 0;
2148 gchar const *format;
2149 gchar *name;
2150
2151 for (l = scg_view (scg)->selections; l != NULL; l = l->next) {
2152 GnmRange const *r = l->data;
2153 GnmStyleList *styles;
2154
2155 styles = sheet_style_collect_hlinks (sheet, r);
2156 n_links += g_slist_length (styles);
2157 style_list_free (styles);
2158 }
2159 format = ngettext ("Remove %d Link", "Remove %d Links", n_links);
2160 name = g_strdup_printf (format, n_links);
2161 gnm_style_set_hlink (style, NULL);
2162 cmd_selection_format (wbc, style, NULL, name);
2163 g_free (name);
2164 break;
2165 }
2166 case CONTEXT_DATA_SLICER_REFRESH:
2167 cmd_slicer_refresh (wbc);
2168 break;
2169 case CONTEXT_DATA_SLICER_EDIT:
2170 dialog_data_slicer (wbcg, FALSE);
2171 break;
2172
2173 default:
2174 break;
2175 }
2176 }
2177
2178 void
scg_context_menu(SheetControlGUI * scg,GdkEvent * event,gboolean is_col,gboolean is_row)2179 scg_context_menu (SheetControlGUI *scg, GdkEvent *event,
2180 gboolean is_col, gboolean is_row)
2181 {
2182 SheetView *sv = scg_view (scg);
2183 Sheet *sheet = sv_sheet (sv);
2184
2185 enum {
2186 CONTEXT_DISPLAY_FOR_CELLS = 1 << 0,
2187 CONTEXT_DISPLAY_FOR_ROWS = 1 << 1,
2188 CONTEXT_DISPLAY_FOR_COLS = 1 << 2,
2189 CONTEXT_DISPLAY_WITH_HYPERLINK = 1 << 3,
2190 CONTEXT_DISPLAY_WITHOUT_HYPERLINK = 1 << 4,
2191 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE = 1 << 5,
2192 CONTEXT_DISPLAY_WITH_DATA_SLICER = 1 << 6,
2193 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW = 1 << 7,
2194 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL = 1 << 8,
2195 CONTEXT_DISPLAY_WITH_COMMENT = 1 << 9,
2196 CONTEXT_DISPLAY_WITHOUT_COMMENT = 1 << 10,
2197 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE = 1 << 11
2198 };
2199 enum {
2200 CONTEXT_DISABLE_PASTE_SPECIAL = 1 << 0,
2201 CONTEXT_DISABLE_FOR_ROWS = 1 << 1,
2202 CONTEXT_DISABLE_FOR_COLS = 1 << 2,
2203 CONTEXT_DISABLE_FOR_CELLS = 1 << 3,
2204 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION = 1 << 4,
2205 CONTEXT_DISABLE_FOR_ALL_COLS = 1 << 5,
2206 CONTEXT_DISABLE_FOR_ALL_ROWS = 1 << 6,
2207 CONTEXT_DISABLE_FOR_NOMERGES = 1 << 7,
2208 CONTEXT_DISABLE_FOR_ONLYMERGES = 1 << 8
2209 };
2210
2211 /* Note: keep the following two in sync!*/
2212 enum {
2213 POPUPITEM_CUT = 0,
2214 POPUPITEM_COPY,
2215 POPUPITEM_PASTE,
2216 POPUPITEM_PASTESPECIAL,
2217 POPUPITEM_SEP1,
2218 POPUPITEM_INSERT_CELL,
2219 POPUPITEM_DELETE_CELL,
2220 POPUPITEM_INSERT_COLUMN,
2221 POPUPITEM_DELETE_COLUMN,
2222 POPUPITEM_INSERT_ROW,
2223 POPUPITEM_DELETE_ROW,
2224 POPUPITEM_CLEAR_CONTENTS,
2225 POPUPITEM_SEP2,
2226 POPUPITEM_COMMENT_ADD,
2227 POPUPITEM_COMMENT_EDIT,
2228 POPUPITEM_COMMENT_REMOVE,
2229 POPUPITEM_LINK_ADD,
2230 POPUPITEM_LINK_EDIT,
2231 POPUPITEM_LINK_REMOVE,
2232 POPUPITEM_SEP3,
2233 POPUPITEM_DATASLICER_EDIT,
2234 POPUPITEM_DATASLICER_REFRESH,
2235 POPUPITEM_DATASLICER_FIELD_ORDER,
2236 POPUPITEM_DATASLICER_LEFT,
2237 POPUPITEM_DATASLICER_RIGHT,
2238 POPUPITEM_DATASLICER_UP,
2239 POPUPITEM_DATASLICER_DOWN,
2240 POPUPITEM_DATASLICER_SUBMENU,
2241 POPUPITEM_FORMAT
2242 };
2243
2244 static GnmPopupMenuElement popup_elements[] = {
2245 { N_("Cu_t"), "edit-cut",
2246 0, 0, CONTEXT_CUT, NULL },
2247 { N_("_Copy"), "edit-copy",
2248 0, 0, CONTEXT_COPY, NULL },
2249 { N_("_Paste"), "edit-paste",
2250 0, 0, CONTEXT_PASTE, NULL },
2251 { N_("Paste _Special"), "edit-paste",
2252 0, CONTEXT_DISABLE_PASTE_SPECIAL, CONTEXT_PASTE_SPECIAL, NULL },
2253
2254 { "", NULL, 0, 0, 0, NULL },
2255
2256 { N_("_Insert Cells..."), NULL,
2257 CONTEXT_DISPLAY_FOR_CELLS,
2258 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION, CONTEXT_INSERT, NULL },
2259 { N_("_Delete Cells..."), "edit-delete",
2260 CONTEXT_DISPLAY_FOR_CELLS,
2261 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION, CONTEXT_DELETE, NULL },
2262 { N_("_Insert Column(s)"), "gnumeric-column-add",
2263 CONTEXT_DISPLAY_FOR_COLS,
2264 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION,
2265 CONTEXT_INSERT, NULL },
2266 { N_("_Delete Column(s)"), "gnumeric-column-delete",
2267 CONTEXT_DISPLAY_FOR_COLS,
2268 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION,
2269 CONTEXT_DELETE, NULL },
2270 { N_("_Insert Row(s)"), "gnumeric-row-add",
2271 CONTEXT_DISPLAY_FOR_ROWS,
2272 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION,
2273 CONTEXT_INSERT, NULL },
2274 { N_("_Delete Row(s)"), "gnumeric-row-delete",
2275 CONTEXT_DISPLAY_FOR_ROWS,
2276 CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION,
2277 CONTEXT_DELETE, NULL },
2278
2279 { N_("Clear Co_ntents"), "edit-clear",
2280 0, 0, CONTEXT_CLEAR_CONTENT, NULL },
2281
2282 { "", NULL, CONTEXT_DISPLAY_FOR_CELLS, 0, 0, NULL },
2283
2284 { N_("Add _Comment..."), "gnumeric-comment-add",
2285 CONTEXT_DISPLAY_WITHOUT_COMMENT, 0, CONTEXT_COMMENT_ADD, NULL },
2286 { N_("Edit Co_mment..."),"gnumeric-comment-edit",
2287 CONTEXT_DISPLAY_WITH_COMMENT, 0, CONTEXT_COMMENT_EDIT, NULL },
2288 { N_("_Remove Comments"), "gnumeric-comment-delete",
2289 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE, 0, CONTEXT_COMMENT_REMOVE, NULL },
2290
2291 { N_("Add _Hyperlink..."), "gnumeric-link-add",
2292 CONTEXT_DISPLAY_WITHOUT_HYPERLINK, 0,
2293 CONTEXT_HYPERLINK_ADD, NULL },
2294 { N_("Edit _Hyperlink..."), "gnumeric-link-edit",
2295 CONTEXT_DISPLAY_WITH_HYPERLINK, 0,
2296 CONTEXT_HYPERLINK_EDIT, NULL },
2297 { N_("_Remove Hyperlink"), "gnumeric-link-delete",
2298 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE, 0,
2299 CONTEXT_HYPERLINK_REMOVE, NULL },
2300
2301 { "", NULL, 0, 0, 0, NULL },
2302
2303 { N_("_Edit DataSlicer"), NULL,
2304 CONTEXT_DISPLAY_WITH_DATA_SLICER, 0,
2305 CONTEXT_DATA_SLICER_EDIT, NULL },
2306 { N_("_Refresh DataSlicer"), NULL,
2307 CONTEXT_DISPLAY_WITH_DATA_SLICER, 0,
2308 CONTEXT_DATA_SLICER_REFRESH, NULL },
2309
2310 { N_("DataSlicer Field _Order "), NULL,
2311 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW | CONTEXT_DISPLAY_WITH_DATA_SLICER_COL, 0,
2312 -1, NULL }, /* start sub menu */
2313 { N_("Left"), "go-previous",
2314 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW, 0,
2315 CONTEXT_DATA_SLICER_REFRESH, NULL },
2316 { N_("Right"), "go-next",
2317 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW, 0,
2318 CONTEXT_DATA_SLICER_REFRESH, NULL },
2319 { N_("Up"), "go-up",
2320 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL, 0,
2321 CONTEXT_DATA_SLICER_REFRESH, NULL },
2322 { N_("Down"), "go-down",
2323 CONTEXT_DISPLAY_WITH_DATA_SLICER_COL, 0,
2324 CONTEXT_DATA_SLICER_REFRESH, NULL },
2325 { "", NULL,
2326 CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW | CONTEXT_DISPLAY_WITH_DATA_SLICER_COL, 0,
2327 -1, NULL }, /* end sub menu */
2328
2329 { N_("_Format All Cells..."), GTK_STOCK_PROPERTIES,
2330 0, 0, CONTEXT_FORMAT_CELL, NULL },
2331 { N_("C_onditional Formatting..."), GTK_STOCK_PROPERTIES,
2332 0, 0, CONTEXT_FORMAT_CELL_COND, NULL },
2333 { N_("Cell"), NULL, 0, 0, -1, NULL},/* start sub menu */
2334 { N_("_Merge"), "gnumeric-cells-merge", 0,
2335 CONTEXT_DISABLE_FOR_ONLYMERGES, CONTEXT_CELL_MERGE, NULL },
2336 { N_("_Unmerge"), "gnumeric-cells-split", 0,
2337 CONTEXT_DISABLE_FOR_NOMERGES, CONTEXT_CELL_UNMERGE, NULL },
2338 { N_("Auto Fit _Width"), "gnumeric-column-size", 0, 0, CONTEXT_CELL_AUTOFIT_WIDTH, NULL },
2339 { N_("Auto Fit _Height"), "gnumeric-row-size", 0, 0, CONTEXT_CELL_AUTOFIT_HEIGHT, NULL },
2340 { "", NULL, 0, 0, -1, NULL},/* end sub menu */
2341
2342
2343 /* Column specific (Note some labels duplicate row labels) */
2344 { N_("Column"), NULL, 0, 0, -1, NULL},/* start sub menu */
2345 { N_("_Width..."), "gnumeric-column-size", 0, 0, CONTEXT_COL_WIDTH, NULL },
2346 { N_("_Auto Fit Width"), "gnumeric-column-size", 0, 0, CONTEXT_COL_AUTOFIT, NULL },
2347 { N_("_Hide"), "gnumeric-column-hide", 0, CONTEXT_DISABLE_FOR_ALL_COLS, CONTEXT_COL_HIDE, NULL },
2348 { N_("_Unhide"), "gnumeric-column-unhide", 0, 0, CONTEXT_COL_UNHIDE, NULL },
2349 { "", NULL, 0, 0, -1, NULL},/* end sub menu */
2350
2351 /* Row specific (Note some labels duplicate col labels) */
2352 { N_("Row"), NULL, 0, 0, -1, NULL},/* start sub menu */
2353 { N_("Hei_ght..."), "gnumeric-row-size", 0, 0, CONTEXT_ROW_HEIGHT, NULL },
2354 { N_("_Auto Fit Height"), "gnumeric-row-size", 0, 0, CONTEXT_ROW_AUTOFIT, NULL },
2355 { N_("_Hide"), "gnumeric-row-hide", 0, CONTEXT_DISABLE_FOR_ALL_ROWS, CONTEXT_ROW_HIDE, NULL },
2356 { N_("_Unhide"), "gnumeric-row-unhide", 0, 0, CONTEXT_ROW_UNHIDE, NULL },
2357 { "", NULL, 0, 0, -1, NULL},/* end sub menu */
2358
2359 { NULL, NULL, 0, 0, 0, NULL },
2360 };
2361
2362 /* row and column specific operations */
2363 int display_filter =
2364 ((!is_col && !is_row) ? CONTEXT_DISPLAY_FOR_CELLS : 0) |
2365 (is_col ? CONTEXT_DISPLAY_FOR_COLS : 0) |
2366 (is_row ? CONTEXT_DISPLAY_FOR_ROWS : 0);
2367
2368 /* Paste special only applies to local copies, not cuts, or remote
2369 * items
2370 */
2371 int sensitivity_filter =
2372 (!gnm_app_clipboard_is_empty () &&
2373 !gnm_app_clipboard_is_cut ())
2374 ? 0 : CONTEXT_DISABLE_PASTE_SPECIAL;
2375
2376 GSList *l;
2377 gboolean has_link = FALSE, has_comment = FALSE;
2378 int n_comments = 0, n_links = 0, n_cols = 0, n_rows = 0, n_cells = 0;
2379 GnmSheetSlicer *slicer;
2380 GnmRange rge;
2381 int n_sel = 0;
2382 gboolean full_sheet = FALSE, only_merges = TRUE, no_merges = TRUE;
2383
2384 wbcg_edit_finish (scg->wbcg, WBC_EDIT_REJECT, NULL);
2385
2386 /* Now see if there is some selection which selects a whole row or a
2387 * whole column and disable the insert/delete col/row menu items
2388 * accordingly
2389 */
2390 for (l = scg_view (scg)->selections; l != NULL; l = l->next) {
2391 GnmRange const *r = l->data;
2392 GnmRange const *merge;
2393 GSList *objs, *merges;
2394 GnmStyleList *styles;
2395 int h, w;
2396 gboolean rfull_h = range_is_full (r, sheet, TRUE);
2397 gboolean rfull_v = range_is_full (r, sheet, FALSE);
2398
2399 n_sel++;
2400
2401 if (!range_is_singleton (r)) {
2402 merge = gnm_sheet_merge_is_corner (sheet, &(r->start));
2403 if (NULL == merge || !range_equal (merge, r))
2404 only_merges = FALSE;
2405 merges = gnm_sheet_merge_get_overlap (sheet, r);
2406 if (merges != NULL) {
2407 no_merges = FALSE;
2408 g_slist_free (merges);
2409 }
2410 }
2411
2412 if (rfull_v) {
2413 display_filter |= CONTEXT_DISPLAY_FOR_COLS;
2414 display_filter &= ~CONTEXT_DISPLAY_FOR_CELLS;
2415 sensitivity_filter |= CONTEXT_DISABLE_FOR_ALL_ROWS;
2416 } else
2417 sensitivity_filter |= CONTEXT_DISABLE_FOR_ROWS;
2418
2419
2420 if (rfull_h) {
2421 display_filter |= CONTEXT_DISPLAY_FOR_ROWS;
2422 display_filter &= ~CONTEXT_DISPLAY_FOR_CELLS;
2423 sensitivity_filter |= CONTEXT_DISABLE_FOR_ALL_COLS;
2424 } else
2425 sensitivity_filter |= CONTEXT_DISABLE_FOR_COLS;
2426
2427 if (!(rfull_h || rfull_v))
2428 sensitivity_filter |= CONTEXT_DISABLE_FOR_CELLS;
2429
2430 full_sheet = full_sheet || (rfull_h && rfull_v);
2431
2432 h = range_height (r);
2433 w = range_width (r);
2434 n_cols += w;
2435 n_rows += h;
2436 n_cells += w * h;
2437
2438 styles = sheet_style_collect_hlinks (sheet, r);
2439 n_links += g_slist_length (styles);
2440 style_list_free (styles);
2441
2442 objs = sheet_objects_get (sheet, r, GNM_CELL_COMMENT_TYPE);
2443 n_comments += g_slist_length (objs);
2444 g_slist_free (objs);
2445 }
2446
2447 if (only_merges)
2448 sensitivity_filter |= CONTEXT_DISABLE_FOR_ONLYMERGES;
2449 if (no_merges)
2450 sensitivity_filter |= CONTEXT_DISABLE_FOR_NOMERGES;
2451
2452
2453 if ((display_filter & CONTEXT_DISPLAY_FOR_COLS) &&
2454 (display_filter & CONTEXT_DISPLAY_FOR_ROWS))
2455 display_filter = 0;
2456 if (n_sel > 1)
2457 sensitivity_filter |= CONTEXT_DISABLE_FOR_DISCONTIGUOUS_SELECTION;
2458
2459 has_comment = (sheet_get_comment (sheet, &sv->edit_pos) != NULL);
2460 range_init_cellpos (&rge, &sv->edit_pos);
2461 has_link = (NULL != sheet_style_region_contains_link (sheet, &rge));
2462
2463 slicer = gnm_sheet_view_editpos_in_slicer (scg_view (scg));
2464 /* FIXME: disabled for now */
2465 if (0 && slicer) {
2466 GODataSlicerField *dsf = gnm_sheet_slicer_field_header_at_pos (slicer, &sv->edit_pos);
2467 if (NULL != dsf) {
2468 if (go_data_slicer_field_get_field_type_pos (dsf, GDS_FIELD_TYPE_COL) >= 0)
2469 display_filter |= CONTEXT_DISPLAY_WITH_DATA_SLICER_COL;
2470 if (go_data_slicer_field_get_field_type_pos (dsf, GDS_FIELD_TYPE_ROW) >= 0)
2471 display_filter |= CONTEXT_DISPLAY_WITH_DATA_SLICER_ROW;
2472 }
2473 display_filter |= CONTEXT_DISPLAY_WITH_DATA_SLICER;
2474 display_filter &= ~CONTEXT_DISPLAY_FOR_CELLS;
2475 }
2476
2477 if (display_filter & CONTEXT_DISPLAY_FOR_CELLS) {
2478 char const *format;
2479 display_filter |= ((has_link) ?
2480 CONTEXT_DISPLAY_WITH_HYPERLINK : CONTEXT_DISPLAY_WITHOUT_HYPERLINK);
2481 display_filter |= ((n_links > 0) ?
2482 CONTEXT_DISPLAY_WITH_HYPERLINK_IN_RANGE : CONTEXT_DISPLAY_WITHOUT_HYPERLINK);
2483 display_filter |= ((has_comment) ?
2484 CONTEXT_DISPLAY_WITH_COMMENT : CONTEXT_DISPLAY_WITHOUT_COMMENT);
2485 display_filter |= ((n_comments > 0) ?
2486 CONTEXT_DISPLAY_WITH_COMMENT_IN_RANGE : CONTEXT_DISPLAY_WITHOUT_COMMENT);
2487 if (n_links > 0) {
2488 /* xgettext : %d gives the number of links. This is input to ngettext. */
2489 format = ngettext ("_Remove %d Link", "_Remove %d Links", n_links);
2490 popup_elements[POPUPITEM_LINK_REMOVE].allocated_name = g_strdup_printf (format, n_links);
2491 }
2492 if (n_comments > 0) {
2493 /* xgettext : %d gives the number of comments. This is input to ngettext. */
2494 format = ngettext ("_Remove %d Comment", "_Remove %d Comments", n_comments);
2495 popup_elements[POPUPITEM_COMMENT_REMOVE].allocated_name = g_strdup_printf (format, n_comments);
2496 }
2497 format = ngettext ("_Insert %d Cell...", "_Insert %d Cells...", n_cells);
2498 popup_elements[POPUPITEM_INSERT_CELL].allocated_name = g_strdup_printf (format, n_cells);
2499 format = ngettext ("_Delete %d Cell...", "_Delete %d Cells...", n_cells);
2500 popup_elements[POPUPITEM_DELETE_CELL].allocated_name = g_strdup_printf (format, n_cells);
2501 }
2502
2503 if (display_filter & CONTEXT_DISPLAY_FOR_COLS) {
2504 char const *format;
2505 format = ngettext ("_Insert %d Column", "_Insert %d Columns", n_cols);
2506 popup_elements[POPUPITEM_INSERT_COLUMN].allocated_name = g_strdup_printf (format, n_cols);
2507 format = ngettext ("_Delete %d Column", "_Delete %d Columns", n_cols);
2508 popup_elements[POPUPITEM_DELETE_COLUMN].allocated_name = g_strdup_printf (format, n_cols);
2509 if (!(sensitivity_filter & (CONTEXT_DISABLE_FOR_CELLS | CONTEXT_DISABLE_FOR_ROWS))) {
2510 format = ngettext ("_Format %d Column", "_Format %d Columns", n_cols);
2511 popup_elements[POPUPITEM_FORMAT].allocated_name
2512 = g_strdup_printf (format, n_cols);
2513 }
2514 }
2515 if (display_filter & CONTEXT_DISPLAY_FOR_ROWS) {
2516 char const *format;
2517 format = ngettext ("_Insert %d Row", "_Insert %d Rows", n_rows);
2518 popup_elements[POPUPITEM_INSERT_ROW].allocated_name = g_strdup_printf (format, n_rows);
2519 format = ngettext ("_Delete %d Row", "_Delete %d Rows", n_rows);
2520 popup_elements[POPUPITEM_DELETE_ROW].allocated_name = g_strdup_printf (format, n_rows);
2521
2522 if (!(sensitivity_filter & (CONTEXT_DISABLE_FOR_CELLS | CONTEXT_DISABLE_FOR_COLS))) {
2523 format = ngettext ("_Format %d Row", "_Format %d Rows", n_rows);
2524 popup_elements[POPUPITEM_FORMAT].allocated_name
2525 = g_strdup_printf (format, n_rows);
2526 }
2527 }
2528 if (!popup_elements[POPUPITEM_FORMAT].allocated_name && !full_sheet) {
2529 char const *format;
2530 format = ngettext ("_Format %d Cell...", "_Format %d Cells...", n_cells);
2531 popup_elements[POPUPITEM_FORMAT].allocated_name = g_strdup_printf (format, n_cells);
2532 }
2533
2534
2535 gnm_create_popup_menu (popup_elements,
2536 &context_menu_handler, scg, NULL,
2537 display_filter, sensitivity_filter, event);
2538 }
2539
2540 static gboolean
cb_redraw_sel(G_GNUC_UNUSED SheetView * sv,GnmRange const * r,gpointer user_data)2541 cb_redraw_sel (G_GNUC_UNUSED SheetView *sv, GnmRange const *r, gpointer user_data)
2542 {
2543 SheetControl *sc = user_data;
2544 scg_redraw_range (sc, r);
2545 scg_redraw_headers (sc, TRUE, TRUE, r);
2546 return TRUE;
2547 }
2548
2549 void
scg_cursor_visible(SheetControlGUI * scg,gboolean is_visible)2550 scg_cursor_visible (SheetControlGUI *scg, gboolean is_visible)
2551 {
2552 SheetControl *sc = (SheetControl *) scg;
2553
2554 /* there is always a grid 0 */
2555 if (NULL == scg->pane[0])
2556 return;
2557
2558 SCG_FOREACH_PANE (scg, pane,
2559 gnm_item_cursor_set_visibility (pane->cursor.std, is_visible););
2560
2561 sv_selection_foreach (sc->view, cb_redraw_sel, sc);
2562 }
2563
2564 /***************************************************************************/
2565
2566 /**
2567 * scg_mode_edit:
2568 * @scg: The sheet control
2569 *
2570 * Put @sheet into the standard state 'edit mode'. This shuts down
2571 * any object editing and frees any objects that are created but not
2572 * realized.
2573 **/
2574 void
scg_mode_edit(SheetControlGUI * scg)2575 scg_mode_edit (SheetControlGUI *scg)
2576 {
2577 WBCGtk *wbcg;
2578 g_return_if_fail (GNM_IS_SCG (scg));
2579
2580 wbcg = scg->wbcg;
2581
2582 if (wbcg != NULL) /* Can be NULL during destruction */
2583 wbcg_insert_object_clear (wbcg);
2584
2585 scg_object_unselect (scg, NULL);
2586
2587 /* During destruction we have already been disconnected
2588 * so don't bother changing the cursor */
2589 if (scg->grid != NULL &&
2590 scg_sheet (scg) != NULL &&
2591 scg_view (scg) != NULL) {
2592 scg_set_display_cursor (scg);
2593 scg_cursor_visible (scg, TRUE);
2594 }
2595
2596 if (wbcg != NULL && wbc_gtk_get_guru (wbcg) != NULL &&
2597 scg == wbcg_cur_scg (wbcg))
2598 wbcg_edit_finish (wbcg, WBC_EDIT_REJECT, NULL);
2599
2600 if (wbcg)
2601 wb_control_update_action_sensitivity (GNM_WBC (wbcg));
2602 }
2603
2604 static void
scg_mode_edit_virt(SheetControl * sc)2605 scg_mode_edit_virt (SheetControl *sc)
2606 {
2607 scg_mode_edit ((SheetControlGUI *)sc);
2608 }
2609
2610 static int
calc_obj_place(GnmPane * pane,gint64 canvas_coord,gboolean is_col,double * offset)2611 calc_obj_place (GnmPane *pane, gint64 canvas_coord, gboolean is_col,
2612 double *offset)
2613 {
2614 gint64 origin;
2615 int colrow;
2616 ColRowInfo const *cri;
2617 Sheet const *sheet = scg_sheet (pane->simple.scg);
2618
2619 if (is_col) {
2620 colrow = gnm_pane_find_col (pane, canvas_coord, &origin);
2621 cri = sheet_col_get_info (sheet, colrow);
2622 } else {
2623 colrow = gnm_pane_find_row (pane, canvas_coord, &origin);
2624 cri = sheet_row_get_info (sheet, colrow);
2625 }
2626
2627 /* TODO : handle other anchor types */
2628 *offset = (canvas_coord - origin) / (double)cri->size_pixels;
2629 return colrow;
2630 }
2631
2632 #define SO_CLASS(so) GNM_SO_CLASS (G_OBJECT_GET_CLASS(so))
2633
2634 /**
2635 * scg_object_select:
2636 * @scg: The #SheetControl to edit in.
2637 * @so: The #SheetObject to select.
2638 *
2639 * Adds @so to the set of selected objects and prepares it for user editing.
2640 * Adds a reference to @ref if it is selected.
2641 **/
2642 void
scg_object_select(SheetControlGUI * scg,SheetObject * so)2643 scg_object_select (SheetControlGUI *scg, SheetObject *so)
2644 {
2645 double *coords;
2646
2647 if (scg->selected_objects == NULL) {
2648 if (wb_view_is_protected (sv_wbv (scg_view (scg)), TRUE) ||
2649 !wbcg_edit_finish (scg->wbcg, WBC_EDIT_ACCEPT, NULL))
2650 return;
2651 g_object_ref (so);
2652
2653 wbcg_insert_object_clear (scg->wbcg);
2654 scg_cursor_visible (scg, FALSE);
2655 scg_set_display_cursor (scg);
2656 scg_unant (GNM_SHEET_CONTROL (scg));
2657
2658 scg->selected_objects = g_hash_table_new_full (
2659 g_direct_hash, g_direct_equal,
2660 (GDestroyNotify) g_object_unref, (GDestroyNotify) g_free);
2661 wb_control_update_action_sensitivity (scg_wbc (scg));
2662 } else {
2663 g_return_if_fail (g_hash_table_lookup (scg->selected_objects, so) == NULL);
2664 g_object_ref (so);
2665 }
2666
2667 coords = g_new (double, 4);
2668 scg_object_anchor_to_coords (scg, sheet_object_get_anchor (so), coords);
2669 g_hash_table_insert (scg->selected_objects, so, coords);
2670 g_signal_connect_object (so, "unrealized",
2671 G_CALLBACK (scg_mode_edit), scg, G_CONNECT_SWAPPED);
2672
2673 SCG_FOREACH_PANE (scg, pane,
2674 gnm_pane_object_update_bbox (pane, so););
2675 }
2676
2677 static void
cb_scg_object_unselect(SheetObject * so,G_GNUC_UNUSED double * coords,SheetControlGUI * scg)2678 cb_scg_object_unselect (SheetObject *so, G_GNUC_UNUSED double *coords, SheetControlGUI *scg)
2679 {
2680 SCG_FOREACH_PANE (scg, pane, gnm_pane_object_unselect (pane, so););
2681 g_signal_handlers_disconnect_by_func (so,
2682 scg_mode_edit, scg);
2683 }
2684
2685 /**
2686 * scg_object_unselect:
2687 * @scg: #SheetControlGUI
2688 * @so: #SheetObject (optionally NULL)
2689 *
2690 * unselect the supplied object, and drop out of edit mode if this is the last
2691 * one. If @so == NULL unselect _all_ objects.
2692 **/
2693 void
scg_object_unselect(SheetControlGUI * scg,SheetObject * so)2694 scg_object_unselect (SheetControlGUI *scg, SheetObject *so)
2695 {
2696 WorkbookControl *wbc = scg_wbc (scg);
2697
2698 /* cheesy cycle avoidance */
2699 if (scg->selected_objects == NULL)
2700 return;
2701
2702 if (so != NULL) {
2703 double *pts = g_hash_table_lookup (scg->selected_objects, so);
2704 g_return_if_fail (pts != NULL);
2705 cb_scg_object_unselect (so, pts, scg);
2706 g_hash_table_remove (scg->selected_objects, so);
2707 if (g_hash_table_size (scg->selected_objects) > 0)
2708 return;
2709 } else
2710 g_hash_table_foreach (scg->selected_objects,
2711 (GHFunc) cb_scg_object_unselect, scg);
2712
2713 g_hash_table_destroy (scg->selected_objects);
2714 scg->selected_objects = NULL;
2715 scg_mode_edit (scg);
2716 if (wbc)
2717 wb_control_update_action_sensitivity (wbc);
2718 }
2719
2720 void
scg_object_select_next(SheetControlGUI * scg,gboolean reverse)2721 scg_object_select_next (SheetControlGUI *scg, gboolean reverse)
2722 {
2723 Sheet *sheet = scg_sheet (scg);
2724 GSList *ptr = sheet->sheet_objects;
2725
2726 g_return_if_fail (ptr != NULL);
2727
2728 if ((scg->selected_objects == NULL) ||
2729 (g_hash_table_size (scg->selected_objects) == 0)) {
2730 scg_object_select (scg, ptr->data);
2731 return;
2732 } else {
2733 GSList *prev = NULL;
2734 for (; ptr != NULL ; prev = ptr, ptr = ptr->next)
2735 if (NULL != g_hash_table_lookup
2736 (scg->selected_objects, ptr->data)) {
2737 SheetObject *target;
2738 if (reverse) {
2739 if (ptr->next == NULL)
2740 target = sheet->sheet_objects->data;
2741 else
2742 target = ptr->next->data;
2743 } else {
2744 if (NULL == prev) {
2745 GSList *last = g_slist_last (ptr);
2746 target = last->data;
2747 } else
2748 target = prev->data;
2749 }
2750 if (ptr->data != target) {
2751 scg_object_unselect (scg, NULL);
2752 scg_object_select (scg, target);
2753 return;
2754 }
2755 }
2756 }
2757 }
2758
2759 typedef struct {
2760 SheetControlGUI *scg;
2761 GnmPane *pane;
2762 SheetObject *primary_object;
2763 int drag_type;
2764 double dx, dy;
2765 gboolean symmetric;
2766 gboolean snap_to_grid;
2767 gboolean is_mouse_move;
2768 } ObjDragInfo;
2769
2770 static double
snap_pos_to_grid(ObjDragInfo const * info,gboolean is_col,double pos,gboolean to_min)2771 snap_pos_to_grid (ObjDragInfo const *info, gboolean is_col, double pos,
2772 gboolean to_min)
2773 {
2774 GnmPane const *pane = info->pane;
2775 Sheet const *sheet = scg_sheet (info->scg);
2776 int cell = is_col ? pane->first.col : pane->first.row;
2777 gint64 pixel = is_col ? pane->first_offset.x : pane->first_offset.y;
2778 gboolean snap = FALSE;
2779 int length = 0;
2780 ColRowInfo const *cr_info;
2781 int sheet_max = colrow_max (is_col, sheet);
2782
2783 if (pos < pixel) {
2784 while (cell > 0 && pos < pixel) {
2785 cr_info = sheet_colrow_get_info (sheet, --cell, is_col);
2786 if (cr_info->visible) {
2787 length = cr_info->size_pixels;
2788 pixel -= length;
2789 }
2790 }
2791 if (pos < pixel)
2792 pos = pixel;
2793 } else {
2794 do {
2795 cr_info = sheet_colrow_get_info (sheet, cell, is_col);
2796 if (cr_info->visible) {
2797 length = cr_info->size_pixels;
2798 if (pixel <= pos && pos <= pixel + length)
2799 snap = TRUE;
2800 pixel += length;
2801 }
2802 } while (++cell < sheet_max && !snap);
2803 pixel -= length;
2804 if (snap) {
2805 if (info->is_mouse_move)
2806 pos = (fabs (pos - pixel) < fabs (pos - pixel - length)) ? pixel : pixel + length;
2807 else
2808 pos = (pixel == pos) ? pixel : (to_min ? pixel : pixel + length);
2809 }
2810 }
2811 return/* sign */ pos;
2812 }
2813
2814 static void
apply_move(SheetObject * so,int x_idx,int y_idx,double * coords,ObjDragInfo * info,gboolean snap_to_grid)2815 apply_move (SheetObject *so, int x_idx, int y_idx, double *coords,
2816 ObjDragInfo *info, gboolean snap_to_grid)
2817 {
2818 gboolean move_x = (x_idx >= 0);
2819 gboolean move_y = (y_idx >= 0);
2820 double x, y;
2821
2822 x = move_x ? coords[x_idx] + info->dx : 0;
2823 y = move_y ? coords[y_idx] + info->dy : 0;
2824
2825 if (snap_to_grid) {
2826 g_return_if_fail (info->pane != NULL);
2827
2828 if (move_x)
2829 x = snap_pos_to_grid (info, TRUE, x, info->dx < 0.);
2830 if (move_y)
2831 y = snap_pos_to_grid (info, FALSE, y, info->dy < 0.);
2832 if (info->primary_object == so || NULL == info->primary_object) {
2833 if (move_x) info->dx = x - coords[x_idx];
2834 if (move_y) info->dy = y - coords[y_idx];
2835 }
2836 }
2837
2838 if (move_x) coords[x_idx] = x;
2839 if (move_y) coords[y_idx] = y;
2840
2841 if (info->symmetric && !snap_to_grid) {
2842 if (move_x) coords[x_idx == 0 ? 2 : 0] -= info->dx;
2843 if (move_y) coords[y_idx == 1 ? 3 : 1] -= info->dy;
2844 }
2845 }
2846
2847 static void
drag_object(SheetObject * so,double * coords,ObjDragInfo * info)2848 drag_object (SheetObject *so, double *coords, ObjDragInfo *info)
2849 {
2850 static struct {
2851 int x_idx, y_idx;
2852 } const idx_info[8] = {
2853 { 0, 1}, {-1, 1}, { 2, 1}, { 0,-1},
2854 { 2,-1}, { 0, 3}, {-1, 3}, { 2, 3}
2855 };
2856
2857 g_return_if_fail (info->drag_type <= 8);
2858
2859 if (info->drag_type == 8) {
2860 apply_move (so, 0, 1, coords, info, info->snap_to_grid);
2861 apply_move (so, 2, 3, coords, info, FALSE);
2862 } else
2863 apply_move (so,
2864 idx_info[info->drag_type].x_idx,
2865 idx_info[info->drag_type].y_idx,
2866 coords, info, info->snap_to_grid);
2867 SCG_FOREACH_PANE (info->scg, pane,
2868 gnm_pane_object_update_bbox (pane, so););
2869 }
2870
2871 static void
cb_drag_selected_objects(SheetObject * so,double * coords,ObjDragInfo * info)2872 cb_drag_selected_objects (SheetObject *so, double *coords, ObjDragInfo *info)
2873 {
2874 if (so != info->primary_object)
2875 drag_object (so, coords, info);
2876 }
2877
2878 /**
2879 * scg_objects_drag:
2880 * @scg: #SheetControlGUI
2881 * @primary: #SheetObject (optionally NULL)
2882 * @dx:
2883 * @dy:
2884 * @drag_type:
2885 * @symmetric:
2886 *
2887 * Move the control points and drag views of the currently selected objects to
2888 * a new position. This movement is only made in @scg not in the actual
2889 * objects.
2890 **/
2891 void
scg_objects_drag(SheetControlGUI * scg,GnmPane * pane,SheetObject * primary,gdouble * dx,gdouble * dy,int drag_type,gboolean symmetric,gboolean snap_to_grid,gboolean is_mouse_move)2892 scg_objects_drag (SheetControlGUI *scg, GnmPane *pane,
2893 SheetObject *primary,
2894 gdouble *dx, gdouble *dy,
2895 int drag_type, gboolean symmetric,
2896 gboolean snap_to_grid,
2897 gboolean is_mouse_move)
2898 {
2899 double *coords;
2900
2901 ObjDragInfo info;
2902 info.scg = scg;
2903 info.pane = pane;
2904 info.primary_object = primary;
2905 info.dx = *dx;
2906 info.dy = *dy;
2907 info.symmetric = symmetric;
2908 info.drag_type = drag_type;
2909 info.snap_to_grid = snap_to_grid;
2910 info.is_mouse_move = is_mouse_move;
2911
2912 if (primary != NULL) {
2913 coords = g_hash_table_lookup (scg->selected_objects, primary);
2914 drag_object (primary, coords, &info);
2915 }
2916
2917 g_hash_table_foreach (scg->selected_objects,
2918 (GHFunc) cb_drag_selected_objects, &info);
2919
2920 *dx = info.dx;
2921 *dy = info.dy;
2922 }
2923
2924 typedef struct {
2925 SheetControlGUI *scg;
2926 GSList *objects, *anchors;
2927 } CollectObjectsData;
2928 static void
cb_collect_objects_to_commit(SheetObject * so,double * coords,CollectObjectsData * data)2929 cb_collect_objects_to_commit (SheetObject *so, double *coords, CollectObjectsData *data)
2930 {
2931 SheetObjectAnchor *anchor = sheet_object_anchor_dup (
2932 sheet_object_get_anchor (so));
2933 if (!sheet_object_can_resize (so)) {
2934 /* FIXME: that code should be invalid */
2935 double scale = goc_canvas_get_pixels_per_unit (GOC_CANVAS (data->scg->pane[0])) / 72.;
2936 sheet_object_default_size (so, coords + 2, coords + 3);
2937 coords[2] *= gnm_app_display_dpi_get (TRUE) * scale;
2938 coords[3] *= gnm_app_display_dpi_get (FALSE) * scale;
2939 coords[2] += coords[0];
2940 coords[3] += coords[1];
2941 }
2942 scg_object_coords_to_anchor (data->scg, coords, anchor);
2943 data->objects = g_slist_prepend (data->objects, so);
2944 data->anchors = g_slist_prepend (data->anchors, anchor);
2945
2946 if (!sheet_object_rubber_band_directly (so)) {
2947 SCG_FOREACH_PANE (data->scg, pane, {
2948 GocItem **ctrl_pts = g_hash_table_lookup (pane->drag.ctrl_pts, so);
2949 if (NULL != ctrl_pts[9]) {
2950 double const *pts = g_hash_table_lookup (
2951 pane->simple.scg->selected_objects, so);
2952 SheetObjectView *sov = sheet_object_get_view (so,
2953 (SheetObjectViewContainer *)pane);
2954
2955 g_object_unref (ctrl_pts[9]);
2956 ctrl_pts[9] = NULL;
2957
2958 if (NULL == sov)
2959 sov = sheet_object_new_view (so, (SheetObjectViewContainer *) pane);
2960 if (NULL != sov)
2961 sheet_object_view_set_bounds (sov, pts, TRUE);
2962 }
2963 });
2964 }
2965 }
2966
2967 static char *
scg_objects_drag_commit_get_undo_text(int drag_type,int n,gboolean created_objects)2968 scg_objects_drag_commit_get_undo_text (int drag_type, int n,
2969 gboolean created_objects)
2970 {
2971 char const *format;
2972
2973 if (created_objects) {
2974 if (drag_type == 8)
2975 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2976 format = ngettext ("Duplicate %d Object", "Duplicate %d Objects", n);
2977 else
2978 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2979 format = ngettext ("Insert %d Object", "Insert %d Objects", n);
2980 } else {
2981 if (drag_type == 8)
2982 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2983 format = ngettext ("Move %d Object", "Move %d Objects", n);
2984 else
2985 /* xgettext : %d gives the number of objects. This is input to ngettext. */
2986 format = ngettext ("Resize %d Object", "Resize %d Objects", n);
2987 }
2988
2989 return g_strdup_printf (format, n);
2990
2991 }
2992
2993 void
scg_objects_drag_commit(SheetControlGUI * scg,int drag_type,gboolean created_objects,GOUndo ** pundo,GOUndo ** predo,gchar ** undo_title)2994 scg_objects_drag_commit (SheetControlGUI *scg, int drag_type,
2995 gboolean created_objects,
2996 GOUndo **pundo, GOUndo **predo, gchar **undo_title)
2997 {
2998 CollectObjectsData data;
2999 char *text = NULL;
3000 GOUndo *undo = NULL;
3001 GOUndo *redo = NULL;
3002
3003 data.objects = data.anchors = NULL;
3004 data.scg = scg;
3005 g_hash_table_foreach (scg->selected_objects,
3006 (GHFunc) cb_collect_objects_to_commit, &data);
3007
3008 undo = sheet_object_move_undo (data.objects, created_objects);
3009 redo = sheet_object_move_do (data.objects, data.anchors, created_objects);
3010 text = scg_objects_drag_commit_get_undo_text
3011 (drag_type, g_slist_length (data.objects), created_objects);
3012
3013 if (pundo && predo) {
3014 *pundo = undo;
3015 *predo = redo;
3016 if (undo_title)
3017 *undo_title = text;
3018 } else {
3019 cmd_generic (GNM_WBC (scg_wbcg (scg)),
3020 text, undo, redo);
3021 g_free (text);
3022 }
3023 g_slist_free (data.objects);
3024 g_slist_free_full (data.anchors, g_free);
3025 }
3026
3027 void
scg_objects_nudge(SheetControlGUI * scg,GnmPane * pane,int drag_type,double dx,double dy,gboolean symmetric,gboolean snap_to_grid)3028 scg_objects_nudge (SheetControlGUI *scg, GnmPane *pane,
3029 int drag_type, double dx, double dy, gboolean symmetric, gboolean snap_to_grid)
3030 {
3031 /* no nudging if we are creating an object */
3032 if (!scg->wbcg->new_object) {
3033 scg_objects_drag (scg, pane, NULL, &dx, &dy, drag_type, symmetric, snap_to_grid, FALSE);
3034 scg_objects_drag_commit (scg, drag_type, FALSE, NULL, NULL, NULL);
3035 }
3036 }
3037
3038 void
scg_object_coords_to_anchor(SheetControlGUI const * scg,double const * coords,SheetObjectAnchor * in_out)3039 scg_object_coords_to_anchor (SheetControlGUI const *scg,
3040 double const *coords, SheetObjectAnchor *in_out)
3041 {
3042 Sheet *sheet = scg_sheet (scg);
3043 /* pane 0 always exists and the others are always use the same basis */
3044 GnmPane *pane = scg_pane ((SheetControlGUI *)scg, 0);
3045 double tmp[4];
3046 g_return_if_fail (GNM_IS_SCG (scg));
3047 g_return_if_fail (coords != NULL);
3048
3049 in_out->base.direction = GOD_ANCHOR_DIR_NONE_MASK;
3050 if (coords[0] > coords[2]) {
3051 tmp[0] = coords[2];
3052 tmp[2] = coords[0];
3053 } else {
3054 tmp[0] = coords[0];
3055 tmp[2] = coords[2];
3056 in_out->base.direction = GOD_ANCHOR_DIR_RIGHT;
3057 }
3058 if (coords[1] > coords[3]) {
3059 tmp[1] = coords[3];
3060 tmp[3] = coords[1];
3061 } else {
3062 tmp[1] = coords[1];
3063 tmp[3] = coords[3];
3064 in_out->base.direction |= GOD_ANCHOR_DIR_DOWN;
3065 }
3066
3067 switch (in_out->mode) {
3068 case GNM_SO_ANCHOR_TWO_CELLS:
3069 in_out->cell_bound.start.col = calc_obj_place (pane, tmp[0], TRUE,
3070 in_out->offset + 0);
3071 in_out->cell_bound.start.row = calc_obj_place (pane, tmp[1], FALSE,
3072 in_out->offset + 1);
3073 in_out->cell_bound.end.col = calc_obj_place (pane, tmp[2], TRUE,
3074 in_out->offset + 2);
3075 in_out->cell_bound.end.row = calc_obj_place (pane, tmp[3], FALSE,
3076 in_out->offset + 3);
3077 break;
3078 case GNM_SO_ANCHOR_ONE_CELL:
3079 in_out->cell_bound.start.col = calc_obj_place (pane, tmp[0], TRUE,
3080 in_out->offset + 0);
3081 in_out->cell_bound.start.row = calc_obj_place (pane, tmp[1], FALSE,
3082 in_out->offset + 1);
3083 in_out->cell_bound.end = in_out->cell_bound.start;
3084 in_out->offset[2] = (tmp[2] - tmp[0]) / colrow_compute_pixel_scale (sheet, TRUE);
3085 in_out->offset[3] = (tmp[3] - tmp[1]) / colrow_compute_pixel_scale (sheet, FALSE);
3086 break;
3087 case GNM_SO_ANCHOR_ABSOLUTE: {
3088 double h, v;
3089 range_init (&in_out->cell_bound, 0, 0, 0, 0);
3090 h = colrow_compute_pixel_scale (sheet, TRUE);
3091 v = colrow_compute_pixel_scale (sheet, FALSE);
3092 in_out->offset[0] = tmp[0] / h;
3093 in_out->offset[1] = tmp[1] / v;
3094 in_out->offset[2] = (tmp[2] - tmp[0]) / h;
3095 in_out->offset[3] = (tmp[3] - tmp[1]) / v;
3096 break;
3097 }
3098 }
3099 }
3100
3101 static double
cell_offset_calc_pixel(Sheet const * sheet,int i,gboolean is_col,double offset)3102 cell_offset_calc_pixel (Sheet const *sheet, int i, gboolean is_col,
3103 double offset)
3104 {
3105 ColRowInfo const *cri = sheet_colrow_get_info (sheet, i, is_col);
3106 return offset * cri->size_pixels;
3107 }
3108
3109 void
scg_object_anchor_to_coords(SheetControlGUI const * scg,SheetObjectAnchor const * anchor,double * coords)3110 scg_object_anchor_to_coords (SheetControlGUI const *scg,
3111 SheetObjectAnchor const *anchor, double *coords)
3112 {
3113 Sheet *sheet = scg_sheet (scg);
3114 GODrawingAnchorDir direction;
3115 gint64 pixels[4];
3116 GnmRange const *r;
3117
3118 g_return_if_fail (GNM_IS_SCG (scg));
3119 g_return_if_fail (anchor != NULL);
3120 g_return_if_fail (coords != NULL);
3121
3122 r = &anchor->cell_bound;
3123 if (anchor->mode != GNM_SO_ANCHOR_ABSOLUTE) {
3124 pixels[0] = scg_colrow_distance_get (scg, TRUE, 0, r->start.col);
3125 pixels[1] = scg_colrow_distance_get (scg, FALSE, 0, r->start.row);
3126 if (anchor->mode == GNM_SO_ANCHOR_TWO_CELLS) {
3127 pixels[2] = pixels[0] + scg_colrow_distance_get (scg, TRUE,
3128 r->start.col, r->end.col);
3129 pixels[3] = pixels[1] + scg_colrow_distance_get (scg, FALSE,
3130 r->start.row, r->end.row);
3131 /* add .5 to offsets so that the rounding is optimal */
3132 pixels[0] += cell_offset_calc_pixel (sheet, r->start.col,
3133 TRUE, anchor->offset[0]) + .5;
3134 pixels[1] += cell_offset_calc_pixel (sheet, r->start.row,
3135 FALSE, anchor->offset[1]) + .5;
3136 pixels[2] += cell_offset_calc_pixel (sheet, r->end.col,
3137 TRUE, anchor->offset[2]) + .5;
3138 pixels[3] += cell_offset_calc_pixel (sheet, r->end.row,
3139 FALSE, anchor->offset[3]) + .5;
3140 } else {
3141 /* add .5 to offsets so that the rounding is optimal */
3142 pixels[0] += cell_offset_calc_pixel (sheet, r->start.col,
3143 TRUE, anchor->offset[0]) + .5;
3144 pixels[1] += cell_offset_calc_pixel (sheet, r->start.row,
3145 FALSE, anchor->offset[1]) + .5;
3146 pixels[2] = pixels[0] + go_fake_floor (anchor->offset[2] * colrow_compute_pixel_scale (sheet, TRUE) + .5);
3147 pixels[3] = pixels[1] + go_fake_floor (anchor->offset[3] * colrow_compute_pixel_scale (sheet, TRUE) + .5);
3148 }
3149 } else {
3150 double h, v;
3151 h = colrow_compute_pixel_scale (sheet, TRUE);
3152 v = colrow_compute_pixel_scale (sheet, FALSE);
3153 pixels[0] = go_fake_floor (anchor->offset[0] * h);
3154 pixels[1] = go_fake_floor (anchor->offset[1] * v);
3155 pixels[2] = go_fake_floor ((anchor->offset[0] + anchor->offset[2]) * h);
3156 pixels[3] = go_fake_floor ((anchor->offset[1] + anchor->offset[3]) * v);
3157 }
3158
3159 direction = anchor->base.direction;
3160 if (direction == GOD_ANCHOR_DIR_UNKNOWN)
3161 direction = GOD_ANCHOR_DIR_DOWN_RIGHT;
3162
3163 coords[0] = pixels[direction & GOD_ANCHOR_DIR_H_MASK ? 0 : 2];
3164 coords[1] = pixels[direction & GOD_ANCHOR_DIR_V_MASK ? 1 : 3];
3165 coords[2] = pixels[direction & GOD_ANCHOR_DIR_H_MASK ? 2 : 0];
3166 coords[3] = pixels[direction & GOD_ANCHOR_DIR_V_MASK ? 3 : 1];
3167 }
3168
3169 /***************************************************************************/
3170
3171 static gboolean
scg_comment_display_filter_cb(PangoAttribute * attribute,gboolean * state)3172 scg_comment_display_filter_cb (PangoAttribute *attribute, gboolean *state)
3173 {
3174 if (attribute->klass->type == PANGO_ATTR_FOREGROUND &&
3175 attribute->start_index != attribute->end_index)
3176 *state = TRUE;
3177 return FALSE;
3178 }
3179
3180 /**
3181 * scg_comment_display:
3182 * @scg: The SheetControl
3183 * @cc: A cell comment
3184 *
3185 */
3186 void
scg_comment_display(SheetControlGUI * scg,GnmComment * cc,int x,int y)3187 scg_comment_display (SheetControlGUI *scg, GnmComment *cc,
3188 int x, int y)
3189 {
3190 g_return_if_fail (GNM_IS_SCG (scg));
3191
3192 scg_comment_timer_clear (scg);
3193
3194 /* If someone clicked and dragged the comment marker this may be NULL */
3195 if (scg->comment.selected == NULL)
3196 return;
3197
3198 if (cc == NULL)
3199 cc = scg->comment.selected;
3200 else if (scg->comment.selected != cc)
3201 scg_comment_unselect (scg, scg->comment.selected);
3202
3203 g_return_if_fail (GNM_IS_CELL_COMMENT (cc));
3204
3205 if (scg->comment.item == NULL) {
3206 GtkWidget *label, *box;
3207 char *comment_text;
3208 PangoAttrList *comment_markup;
3209 char const *comment_author;
3210
3211 g_object_get (G_OBJECT (cc),
3212 "text", &comment_text,
3213 "markup", &comment_markup,
3214 NULL);
3215 comment_author = cell_comment_author_get (cc);
3216
3217 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
3218
3219 if (comment_author != NULL) {
3220 char *text;
3221 PangoAttrList *attrs;
3222 PangoAttribute *attr;
3223
3224 /* xgettext: this is a by-line for cell comments */
3225 text = g_strdup_printf (_("By %s:"), comment_author);
3226 label = gtk_label_new (text);
3227 g_free (text);
3228
3229 attrs = pango_attr_list_new ();
3230 attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
3231 attr->start_index = 0;
3232 attr->end_index = G_MAXINT;
3233 pango_attr_list_insert (attrs, attr);
3234 gtk_label_set_attributes (GTK_LABEL (label), attrs);
3235 pango_attr_list_unref (attrs);
3236
3237 gtk_widget_set_halign (label, GTK_ALIGN_START);
3238 gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
3239 gtk_box_set_spacing (GTK_BOX (box), 10);
3240 }
3241
3242 label = gtk_label_new (comment_text);
3243 if (comment_markup) {
3244 gboolean font_colour_set = FALSE;
3245 pango_attr_list_filter
3246 (comment_markup,
3247 (PangoAttrFilterFunc) scg_comment_display_filter_cb,
3248 &font_colour_set);
3249 if (font_colour_set) {
3250 /* Imported comments may have a font colour set. */
3251 /* If that is the case, we set a background colour. */
3252 guint length = strlen (comment_text);
3253 PangoAttribute *attr = pango_attr_foreground_new (0,0,0);
3254 attr->start_index = 0;
3255 attr->end_index = length;
3256 pango_attr_list_insert_before (comment_markup, attr);
3257 attr = pango_attr_background_new (255*255, 255*255, 224*255 );
3258 attr->start_index = 0;
3259 attr->end_index = length;
3260 pango_attr_list_insert_before (comment_markup, attr);
3261 }
3262 gtk_label_set_attributes (GTK_LABEL (label), comment_markup);
3263 }
3264 g_free (comment_text);
3265 gtk_widget_set_halign (label, GTK_ALIGN_START);
3266 gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
3267
3268 gnm_convert_to_tooltip (GTK_WIDGET (scg->grid), box);
3269
3270 scg->comment.item = gtk_widget_get_toplevel (box);
3271 gtk_window_move (GTK_WINDOW (scg->comment.item),
3272 x + 10, y + 10);
3273
3274 gtk_widget_show_all (scg->comment.item);
3275 }
3276 }
3277
3278 static gint
cb_cell_comment_timer(SheetControlGUI * scg)3279 cb_cell_comment_timer (SheetControlGUI *scg)
3280 {
3281 g_return_val_if_fail (GNM_IS_SCG (scg), FALSE);
3282 g_return_val_if_fail (scg->comment.timer != 0, FALSE);
3283
3284 scg->comment.timer = 0;
3285 scg_comment_display (scg, scg->comment.selected,
3286 scg->comment.x, scg->comment.y);
3287 return FALSE;
3288 }
3289
3290 /**
3291 * scg_comment_select:
3292 * @scg: The SheetControl
3293 * @cc: A cell comment
3294 *
3295 * Prepare @cc for display.
3296 */
3297 void
scg_comment_select(SheetControlGUI * scg,GnmComment * cc,int x,int y)3298 scg_comment_select (SheetControlGUI *scg, GnmComment *cc, int x, int y)
3299 {
3300 g_return_if_fail (GNM_IS_SCG (scg));
3301
3302 if (scg->comment.selected != NULL)
3303 scg_comment_unselect (scg, scg->comment.selected);
3304
3305 g_return_if_fail (scg->comment.timer == 0);
3306
3307 scg->comment.selected = cc;
3308 scg->comment.timer = g_timeout_add (1000,
3309 (GSourceFunc)cb_cell_comment_timer, scg);
3310 scg->comment.x = x;
3311 scg->comment.y = y;
3312 }
3313
3314 /**
3315 * scg_comment_unselect:
3316 * @scg: The SheetControl
3317 * @cc: A cell comment
3318 *
3319 * If @cc is the current cell comment being edited/displayed shutdown the
3320 * display mechanism.
3321 */
3322 void
scg_comment_unselect(SheetControlGUI * scg,GnmComment * cc)3323 scg_comment_unselect (SheetControlGUI *scg, GnmComment *cc)
3324 {
3325 g_return_if_fail (GNM_IS_SCG (scg));
3326
3327 if (cc == scg->comment.selected) {
3328 scg->comment.selected = NULL;
3329 scg_comment_timer_clear (scg);
3330
3331 if (scg->comment.item != NULL) {
3332 gtk_widget_destroy (scg->comment.item);
3333 scg->comment.item = NULL;
3334 }
3335 }
3336 }
3337
3338 /************************************************************************/
3339 /* Col/Row size support routines. */
3340
3341 gint64
scg_colrow_distance_get(SheetControlGUI const * scg,gboolean is_cols,int from,int to)3342 scg_colrow_distance_get (SheetControlGUI const *scg, gboolean is_cols,
3343 int from, int to)
3344 {
3345 Sheet *sheet = scg_sheet (scg);
3346 ColRowCollection const *collection;
3347 int default_size;
3348 int i;
3349 gint64 pixels = 0;
3350 int sign = 1;
3351
3352 g_return_val_if_fail (GNM_IS_SCG (scg), 1);
3353
3354 if (from > to) {
3355 int const tmp = to;
3356 to = from;
3357 from = tmp;
3358 sign = -1;
3359 }
3360
3361 g_return_val_if_fail (from >= 0, 1);
3362
3363 if (is_cols) {
3364 g_return_val_if_fail (to <= gnm_sheet_get_max_cols (sheet), 1);
3365 collection = &sheet->cols;
3366 } else {
3367 g_return_val_if_fail (to <= gnm_sheet_get_max_rows (sheet), 1);
3368 collection = &sheet->rows;
3369 }
3370
3371 /* Do not use col_row_foreach, it ignores empties.
3372 * Optimize this so that long jumps are not quite so horrific
3373 * for performance.
3374 */
3375 default_size = collection->default_style.size_pixels;
3376 for (i = from ; i < to ; ++i) {
3377 ColRowSegment const *segment =
3378 COLROW_GET_SEGMENT(collection, i);
3379
3380 if (segment != NULL) {
3381 ColRowInfo const *cri = segment->info[COLROW_SUB_INDEX (i)];
3382 if (cri == NULL)
3383 pixels += default_size;
3384 else if (cri->visible)
3385 pixels += cri->size_pixels;
3386 } else {
3387 int segment_end = COLROW_SEGMENT_END (i)+1;
3388 if (segment_end > to)
3389 segment_end = to;
3390 pixels += default_size * (segment_end - i);
3391 i = segment_end - 1;
3392 }
3393 }
3394
3395 return pixels*sign;
3396 }
3397
3398 /*************************************************************************/
3399
3400 static void
scg_cursor_bound(SheetControl * sc,GnmRange const * r)3401 scg_cursor_bound (SheetControl *sc, GnmRange const *r)
3402 {
3403 SheetControlGUI *scg = (SheetControlGUI *) sc;
3404 SCG_FOREACH_PANE (scg, pane, gnm_pane_cursor_bound_set (pane, r););
3405 }
3406
3407 static void
scg_recompute_visible_region(SheetControl * sc,gboolean full_recompute)3408 scg_recompute_visible_region (SheetControl *sc, gboolean full_recompute)
3409 {
3410 SheetControlGUI *scg = (SheetControlGUI *) sc;
3411
3412 SCG_FOREACH_PANE (scg, pane,
3413 gnm_pane_compute_visible_region (pane, full_recompute););
3414 }
3415
3416 void
scg_edit_start(SheetControlGUI * scg)3417 scg_edit_start (SheetControlGUI *scg)
3418 {
3419 g_return_if_fail (GNM_IS_SCG (scg));
3420
3421 SCG_FOREACH_PANE (scg, pane, gnm_pane_edit_start (pane););
3422 }
3423
3424 void
scg_edit_stop(SheetControlGUI * scg)3425 scg_edit_stop (SheetControlGUI *scg)
3426 {
3427 g_return_if_fail (GNM_IS_SCG (scg));
3428
3429 scg_rangesel_stop (scg, FALSE);
3430 SCG_FOREACH_PANE (scg, pane, gnm_pane_edit_stop (pane););
3431 }
3432
3433 /**
3434 * scg_rangesel_changed:
3435 * @scg: The scg
3436 *
3437 * Notify expr_entry that the expression range has changed.
3438 **/
3439 static void
scg_rangesel_changed(SheetControlGUI * scg,int base_col,int base_row,int move_col,int move_row)3440 scg_rangesel_changed (SheetControlGUI *scg,
3441 int base_col, int base_row,
3442 int move_col, int move_row)
3443 {
3444 GnmExprEntry *expr_entry;
3445 gboolean ic_changed;
3446 GnmRange *r, last_r;
3447 Sheet *sheet;
3448
3449 g_return_if_fail (GNM_IS_SCG (scg));
3450
3451 scg->rangesel.base_corner.col = base_col;
3452 scg->rangesel.base_corner.row = base_row;
3453 scg->rangesel.move_corner.col = move_col;
3454 scg->rangesel.move_corner.row = move_row;
3455
3456 r = &scg->rangesel.displayed;
3457 if (base_col < move_col) {
3458 r->start.col = base_col;
3459 r->end.col = move_col;
3460 } else {
3461 r->end.col = base_col;
3462 r->start.col = move_col;
3463 }
3464 if (base_row < move_row) {
3465 r->start.row = base_row;
3466 r->end.row = move_row;
3467 } else {
3468 r->end.row = base_row;
3469 r->start.row = move_row;
3470 }
3471
3472 sheet = scg_sheet (scg);
3473 expr_entry = wbcg_get_entry_logical (scg->wbcg);
3474
3475 gnm_expr_entry_freeze (expr_entry);
3476 /* The order here is tricky.
3477 * 1) Assign the range to the expr entry.
3478 */
3479 ic_changed = gnm_expr_entry_load_from_range (
3480 expr_entry, sheet, r);
3481
3482 /* 2) if the expr entry changed the region get the new region */
3483 if (ic_changed)
3484 gnm_expr_entry_get_rangesel (expr_entry, r, NULL);
3485
3486 /* 3) now double check that all merged regions are fully contained */
3487 last_r = *r;
3488 gnm_sheet_merge_find_bounding_box (sheet, r);
3489 if (!range_equal (&last_r, r))
3490 gnm_expr_entry_load_from_range (expr_entry, sheet, r);
3491
3492 gnm_expr_entry_thaw (expr_entry);
3493
3494 SCG_FOREACH_PANE (scg, pane, gnm_pane_rangesel_bound_set (pane, r););
3495 }
3496
3497 void
scg_rangesel_start(SheetControlGUI * scg,int base_col,int base_row,int move_col,int move_row)3498 scg_rangesel_start (SheetControlGUI *scg,
3499 int base_col, int base_row,
3500 int move_col, int move_row)
3501 {
3502 GnmRange r;
3503
3504 g_return_if_fail (GNM_IS_SCG (scg));
3505
3506 if (scg->rangesel.active)
3507 return;
3508
3509 if (scg->wbcg->rangesel != NULL)
3510 g_warning ("misconfiged rangesel");
3511
3512 scg->wbcg->rangesel = scg;
3513 scg->rangesel.active = TRUE;
3514
3515 gnm_expr_entry_find_range (wbcg_get_entry_logical (scg->wbcg));
3516
3517 range_init (&r, base_col, base_row, move_col, move_row);
3518 SCG_FOREACH_PANE (scg, pane, gnm_pane_rangesel_start (pane, &r););
3519 scg_rangesel_changed (scg, base_col, base_row, move_col, move_row);
3520 }
3521
3522 void
scg_rangesel_stop(SheetControlGUI * scg,gboolean clear_string)3523 scg_rangesel_stop (SheetControlGUI *scg, gboolean clear_string)
3524 {
3525 g_return_if_fail (GNM_IS_SCG (scg));
3526
3527 if (!scg->rangesel.active)
3528 return;
3529 if (scg->wbcg->rangesel != scg)
3530 g_warning ("misconfiged rangesel");
3531
3532 scg->wbcg->rangesel = NULL;
3533 scg->rangesel.active = FALSE;
3534 SCG_FOREACH_PANE (scg, pane, gnm_pane_rangesel_stop (pane););
3535
3536 gnm_expr_entry_rangesel_stop (wbcg_get_entry_logical (scg->wbcg),
3537 clear_string);
3538 }
3539
3540 /**
3541 * scg_set_display_cursor:
3542 * @scg:
3543 *
3544 * Set the displayed cursor type.
3545 */
3546 void
scg_set_display_cursor(SheetControlGUI * scg)3547 scg_set_display_cursor (SheetControlGUI *scg)
3548 {
3549 GdkCursorType cursor = GDK_CURSOR_IS_PIXMAP;
3550
3551 g_return_if_fail (GNM_IS_SCG (scg));
3552
3553 if (scg->wbcg->new_object != NULL)
3554 cursor = GDK_CROSSHAIR;
3555
3556 SCG_FOREACH_PANE (scg, pane, {
3557 GtkWidget *w = GTK_WIDGET (pane);
3558 if (gtk_widget_get_window (w)) {
3559 if (cursor == GDK_CURSOR_IS_PIXMAP)
3560 gnm_widget_set_cursor (w, pane->mouse_cursor);
3561 else
3562 gnm_widget_set_cursor_type (w, cursor);
3563 }
3564 });
3565 }
3566
3567 void
scg_rangesel_extend_to(SheetControlGUI * scg,int col,int row)3568 scg_rangesel_extend_to (SheetControlGUI *scg, int col, int row)
3569 {
3570 int base_col, base_row;
3571
3572 if (col < 0) {
3573 base_col = 0;
3574 col = gnm_sheet_get_last_col (scg_sheet (scg));
3575 } else
3576 base_col = scg->rangesel.base_corner.col;
3577 if (row < 0) {
3578 base_row = 0;
3579 row = gnm_sheet_get_last_row (scg_sheet (scg));
3580 } else
3581 base_row = scg->rangesel.base_corner.row;
3582
3583 if (scg->rangesel.active)
3584 scg_rangesel_changed (scg, base_col, base_row, col, row);
3585 else
3586 scg_rangesel_start (scg, base_col, base_row, col, row);
3587 }
3588
3589 void
scg_rangesel_bound(SheetControlGUI * scg,int base_col,int base_row,int move_col,int move_row)3590 scg_rangesel_bound (SheetControlGUI *scg,
3591 int base_col, int base_row,
3592 int move_col, int move_row)
3593 {
3594 if (scg->rangesel.active)
3595 scg_rangesel_changed (scg, base_col, base_row, move_col, move_row);
3596 else
3597 scg_rangesel_start (scg, base_col, base_row, move_col, move_row);
3598 }
3599
3600 void
scg_rangesel_move(SheetControlGUI * scg,int n,gboolean jump_to_bound,gboolean horiz)3601 scg_rangesel_move (SheetControlGUI *scg, int n, gboolean jump_to_bound,
3602 gboolean horiz)
3603 {
3604 SheetView *sv = scg_view (scg);
3605 GnmCellPos tmp;
3606
3607 if (!scg->rangesel.active) {
3608 tmp.col = sv->edit_pos_real.col;
3609 tmp.row = sv->edit_pos_real.row;
3610 } else
3611 tmp = scg->rangesel.base_corner;
3612
3613 if (horiz)
3614 tmp.col = sheet_find_boundary_horizontal (
3615 sv_sheet (sv), tmp.col, tmp.row, tmp.row, n, jump_to_bound);
3616 else
3617 tmp.row = sheet_find_boundary_vertical (
3618 sv_sheet (sv), tmp.col, tmp.row, tmp.col, n, jump_to_bound);
3619
3620 if (scg->rangesel.active)
3621 scg_rangesel_changed (scg, tmp.col, tmp.row, tmp.col, tmp.row);
3622 else
3623 scg_rangesel_start (scg, tmp.col, tmp.row, tmp.col, tmp.row);
3624 scg_make_cell_visible (scg, tmp.col, tmp.row, FALSE, FALSE);
3625 gnm_expr_entry_signal_update (
3626 wbcg_get_entry_logical (scg->wbcg), FALSE);
3627 }
3628
3629 void
scg_rangesel_extend(SheetControlGUI * scg,int n,gboolean jump_to_bound,gboolean horiz)3630 scg_rangesel_extend (SheetControlGUI *scg, int n,
3631 gboolean jump_to_bound, gboolean horiz)
3632 {
3633 Sheet *sheet = scg_sheet (scg);
3634
3635 if (scg->rangesel.active) {
3636 GnmCellPos tmp = scg->rangesel.move_corner;
3637
3638 if (horiz)
3639 tmp.col = sheet_find_boundary_horizontal (sheet,
3640 tmp.col, tmp.row, scg->rangesel.base_corner.row,
3641 n, jump_to_bound);
3642 else
3643 tmp.row = sheet_find_boundary_vertical (sheet,
3644 tmp.col, tmp.row, scg->rangesel.base_corner.col,
3645 n, jump_to_bound);
3646
3647 scg_rangesel_changed (scg,
3648 scg->rangesel.base_corner.col,
3649 scg->rangesel.base_corner.row, tmp.col, tmp.row);
3650
3651 scg_make_cell_visible (scg,
3652 scg->rangesel.move_corner.col,
3653 scg->rangesel.move_corner.row, FALSE, TRUE);
3654 gnm_expr_entry_signal_update (
3655 wbcg_get_entry_logical (scg->wbcg), FALSE);
3656 } else
3657 scg_rangesel_move (scg, n, jump_to_bound, horiz);
3658 }
3659
3660 /**
3661 * scg_cursor_move:
3662 * @scg: The scg
3663 * @dir: Number of units to move the cursor
3664 * @jump_to_bound: skip from the start to the end of ranges
3665 * of filled or unfilled cells.
3666 * @horiz: is the movement horizontal or vertical
3667 *
3668 * Moves the cursor count rows
3669 */
3670 void
scg_cursor_move(SheetControlGUI * scg,int n,gboolean jump_to_bound,gboolean horiz)3671 scg_cursor_move (SheetControlGUI *scg, int n,
3672 gboolean jump_to_bound, gboolean horiz)
3673 {
3674 SheetView *sv = scg_view (scg);
3675 GnmCellPos tmp = sv->edit_pos_real;
3676 int step = (n>0) ? 1 : -1;
3677
3678 if (!wbcg_edit_finish (scg->wbcg, WBC_EDIT_ACCEPT, NULL))
3679 return;
3680
3681 if (horiz)
3682 tmp.col = sheet_find_boundary_horizontal (sv->sheet,
3683 tmp.col + n - step, tmp.row, tmp.row,
3684 step, jump_to_bound);
3685 else
3686 tmp.row = sheet_find_boundary_vertical
3687 (sv->sheet,
3688 tmp.col, tmp.row + n - step,
3689 tmp.col,
3690 step, jump_to_bound);
3691
3692 sv_selection_reset (sv);
3693 gnm_sheet_view_cursor_set (sv, &tmp,
3694 tmp.col, tmp.row, tmp.col, tmp.row, NULL);
3695 gnm_sheet_view_make_cell_visible (sv, tmp.col, tmp.row, FALSE);
3696 sv_selection_add_pos (sv, tmp.col, tmp.row, GNM_SELECTION_MODE_ADD);
3697 }
3698
3699 /**
3700 * scg_cursor_extend:
3701 * @scg: The scg
3702 * @n: Units to extend the selection
3703 * @jump_to_bound: Move to transitions between cells and blanks,
3704 * or move in single steps.
3705 * @horiz: extend vertically or horizontally.
3706 */
3707 void
scg_cursor_extend(SheetControlGUI * scg,int n,gboolean jump_to_bound,gboolean horiz)3708 scg_cursor_extend (SheetControlGUI *scg, int n,
3709 gboolean jump_to_bound, gboolean horiz)
3710 {
3711 SheetView *sv = scg_view (scg);
3712 GnmCellPos move = sv->cursor.move_corner;
3713 GnmCellPos visible = scg->pane[0]->first;
3714
3715 if (!wbcg_edit_finish (scg->wbcg, WBC_EDIT_ACCEPT, NULL))
3716 return;
3717
3718 if (horiz)
3719 visible.col = move.col = sheet_find_boundary_horizontal (sv->sheet,
3720 move.col, move.row, sv->cursor.base_corner.row,
3721 n, jump_to_bound);
3722 else
3723 visible.row = move.row = sheet_find_boundary_vertical (sv->sheet,
3724 move.col, move.row, sv->cursor.base_corner.col,
3725 n, jump_to_bound);
3726
3727 sv_selection_extend_to (sv, move.col, move.row);
3728 gnm_sheet_view_make_cell_visible (sv, visible.col, visible.row, FALSE);
3729 }
3730
3731 void
scg_take_focus(SheetControlGUI * scg)3732 scg_take_focus (SheetControlGUI *scg)
3733 {
3734 g_return_if_fail (GNM_IS_SCG (scg));
3735
3736 /* FIXME: Slightly hackish. */
3737 if (wbcg_toplevel (scg->wbcg))
3738 gtk_window_set_focus (wbcg_toplevel (scg->wbcg),
3739 (scg_sheet (scg)->sheet_type == GNM_SHEET_OBJECT)?
3740 GTK_WIDGET (scg->vs): GTK_WIDGET (scg_pane (scg, 0)));
3741 }
3742
3743 /*********************************************************************************/
3744 void
scg_size_guide_start(SheetControlGUI * scg,gboolean vert,int colrow,gboolean is_colrow_resize)3745 scg_size_guide_start (SheetControlGUI *scg,
3746 gboolean vert, int colrow, gboolean is_colrow_resize)
3747 {
3748 g_return_if_fail (GNM_IS_SCG (scg));
3749 SCG_FOREACH_PANE (scg, pane,
3750 gnm_pane_size_guide_start (pane, vert, colrow, is_colrow_resize););
3751 }
3752 void
scg_size_guide_motion(SheetControlGUI * scg,gboolean vert,gint64 guide_pos)3753 scg_size_guide_motion (SheetControlGUI *scg, gboolean vert, gint64 guide_pos)
3754 {
3755 g_return_if_fail (GNM_IS_SCG (scg));
3756 SCG_FOREACH_PANE (scg, pane,
3757 gnm_pane_size_guide_motion (pane, vert, guide_pos););
3758 }
3759 void
scg_size_guide_stop(SheetControlGUI * scg)3760 scg_size_guide_stop (SheetControlGUI *scg)
3761 {
3762 g_return_if_fail (GNM_IS_SCG (scg));
3763 SCG_FOREACH_PANE (scg, pane,
3764 gnm_pane_size_guide_stop (pane););
3765 }
3766 /*********************************************************************************/
3767
3768 void
scg_special_cursor_start(SheetControlGUI * scg,int style,int button)3769 scg_special_cursor_start (SheetControlGUI *scg, int style, int button)
3770 {
3771 g_return_if_fail (GNM_IS_SCG (scg));
3772
3773 SCG_FOREACH_PANE (scg, pane,
3774 gnm_pane_special_cursor_start (pane, style, button););
3775 }
3776
3777 void
scg_special_cursor_stop(SheetControlGUI * scg)3778 scg_special_cursor_stop (SheetControlGUI *scg)
3779 {
3780 g_return_if_fail (GNM_IS_SCG (scg));
3781
3782 SCG_FOREACH_PANE (scg, pane,
3783 gnm_pane_special_cursor_stop (pane););
3784 }
3785
3786 gboolean
scg_special_cursor_bound_set(SheetControlGUI * scg,GnmRange const * r)3787 scg_special_cursor_bound_set (SheetControlGUI *scg, GnmRange const *r)
3788 {
3789 gboolean changed = FALSE;
3790
3791 g_return_val_if_fail (GNM_IS_SCG (scg), FALSE);
3792
3793 SCG_FOREACH_PANE (scg, pane,
3794 changed |= gnm_pane_special_cursor_bound_set (pane, r););
3795 return changed;
3796 }
3797
3798 static void
scg_object_create_view(SheetControl * sc,SheetObject * so)3799 scg_object_create_view (SheetControl *sc, SheetObject *so)
3800 {
3801 SheetControlGUI *scg = GNM_SCG (sc);
3802 if (scg->active_panes)
3803 SCG_FOREACH_PANE (scg, pane,
3804 sheet_object_new_view (so, (SheetObjectViewContainer *)pane););
3805 else
3806 sheet_object_new_view (so, (SheetObjectViewContainer *)scg->vs);
3807 }
3808
3809 static void
scg_scale_changed(SheetControl * sc)3810 scg_scale_changed (SheetControl *sc)
3811 {
3812 SheetControlGUI *scg = (SheetControlGUI *)sc;
3813 Sheet *sheet = scg_sheet (scg);
3814 double z;
3815 GSList *ptr;
3816
3817 g_return_if_fail (GNM_IS_SCG (scg));
3818
3819 z = sheet->last_zoom_factor_used;
3820
3821 SCG_FOREACH_PANE (scg, pane, {
3822 if (pane->col.canvas != NULL)
3823 goc_canvas_set_pixels_per_unit (pane->col.canvas, z);
3824 if (pane->row.canvas != NULL)
3825 goc_canvas_set_pixels_per_unit (pane->row.canvas, z);
3826 goc_canvas_set_pixels_per_unit (GOC_CANVAS (pane), z);
3827 });
3828
3829 scg_resize (scg, TRUE);
3830 set_resize_pane_pos (scg, scg->vpane);
3831 set_resize_pane_pos (scg, scg->hpane);
3832 /* now, update sheet objects positions and sizes */
3833 for (ptr = sheet->sheet_objects; ptr; ptr = ptr->next)
3834 sheet_object_update_bounds (GNM_SO (ptr->data), NULL);
3835 }
3836
3837 static gboolean
cb_cell_im_timer(SheetControlGUI * scg)3838 cb_cell_im_timer (SheetControlGUI *scg)
3839 {
3840 g_return_val_if_fail (GNM_IS_SCG (scg), FALSE);
3841 g_return_val_if_fail (scg->im.timer != 0, FALSE);
3842
3843 scg->im.timer = 0;
3844 scg_im_destroy (scg);
3845 return FALSE;
3846 }
3847
3848 static GnmPane *
scg_find_pane(SheetControlGUI * scg,GnmCellPos * pos)3849 scg_find_pane (SheetControlGUI *scg, GnmCellPos *pos)
3850 {
3851 int i;
3852
3853 for (i = 0; i < scg->active_panes; i++) {
3854 GnmPane *pane = scg->pane[i];
3855
3856 if (pane &&
3857 pane->first.col <= pos->col &&
3858 pane->first.row <= pos->row &&
3859 pane->last_visible.col >= pos->col &&
3860 pane->last_visible.row >= pos->row)
3861 return pane;
3862 }
3863 return NULL;
3864 }
3865
3866 static void
scg_show_im_tooltip(SheetControl * sc,GnmInputMsg * im,GnmCellPos * pos)3867 scg_show_im_tooltip (SheetControl *sc, GnmInputMsg *im, GnmCellPos *pos)
3868 {
3869 SheetControlGUI *scg = (SheetControlGUI *)sc;
3870 GnmPane *pane;
3871
3872 g_return_if_fail (GNM_IS_SCG (scg));
3873
3874 scg_im_destroy (scg);
3875
3876 pane = scg_find_pane (scg, pos);
3877
3878 if (im && pane) {
3879 GtkWidget *label, *box;
3880 char const *text, *title;
3881 int len_text, len_title;
3882 int x, y, x_origin, y_origin;
3883 GtkAllocation allocation;
3884 Sheet *sheet = scg_sheet (scg);
3885 gboolean rtl = sheet->text_is_rtl;
3886
3887 text = gnm_input_msg_get_msg (im);
3888 title = gnm_input_msg_get_title (im);
3889 len_text = (text == NULL) ? 0 : strlen (text);
3890 len_title = (title == NULL) ? 0 : strlen (title);
3891
3892 if ((len_text == 0) && (len_title == 0))
3893 return;
3894
3895 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
3896
3897 if (len_title > 0) {
3898 PangoAttrList *attrs;
3899 PangoAttribute *attr;
3900
3901 label = gtk_label_new (title);
3902
3903 attrs = pango_attr_list_new ();
3904 attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
3905 attr->start_index = 0;
3906 attr->end_index = G_MAXINT;
3907 pango_attr_list_insert (attrs, attr);
3908 gtk_label_set_attributes (GTK_LABEL (label), attrs);
3909 pango_attr_list_unref (attrs);
3910
3911 gtk_widget_set_halign (label, GTK_ALIGN_START);
3912 gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
3913 }
3914 if (len_text > 0) {
3915 label = gtk_label_new (text);
3916
3917 gtk_widget_set_halign (label, GTK_ALIGN_START);
3918 gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
3919 if (len_title > 0)
3920 gtk_box_set_spacing (GTK_BOX (box), 10);
3921 }
3922 gnm_convert_to_tooltip (GTK_WIDGET (scg->grid), box);
3923 scg->im.item = gtk_widget_get_toplevel (box);
3924
3925 x = sheet_col_get_distance_pixels
3926 (sheet, pane->first.col, pos->col + (rtl ? 1 : 0));
3927
3928 y = sheet_row_get_distance_pixels
3929 (sheet, pane->first.row, pos->row + 1);
3930
3931 gtk_widget_get_allocation (GTK_WIDGET (pane), &allocation);
3932 if (rtl)
3933 x = allocation.width - x;
3934 x += allocation.x;
3935 y += allocation.y;
3936
3937 gdk_window_get_position
3938 (gtk_widget_get_parent_window (GTK_WIDGET (pane)),
3939 &x_origin, &y_origin);
3940 x += x_origin;
3941 y += y_origin;
3942
3943 gtk_window_move (GTK_WINDOW (scg->im.item), x + 10, y + 10);
3944 gtk_widget_show_all (scg->im.item);
3945 scg->im.timer = g_timeout_add (1500, (GSourceFunc)cb_cell_im_timer, scg);
3946 }
3947 }
3948
3949 static void
scg_freeze_object_view(SheetControl * sc,gboolean freeze)3950 scg_freeze_object_view (SheetControl *sc, gboolean freeze)
3951 {
3952 SCG_FOREACH_PANE
3953 (GNM_SCG(sc), pane,
3954 GocGroup *g = pane->object_views;
3955 goc_group_freeze (g, freeze););
3956 }
3957
3958 static void
scg_class_init(GObjectClass * object_class)3959 scg_class_init (GObjectClass *object_class)
3960 {
3961 SheetControlClass *sc_class = SHEET_CONTROL_CLASS (object_class);
3962
3963 g_return_if_fail (sc_class != NULL);
3964
3965 scg_parent_class = g_type_class_peek_parent (object_class);
3966
3967 object_class->finalize = scg_finalize;
3968
3969 sc_class->resize = scg_resize_virt;
3970 sc_class->redraw_all = scg_redraw_all;
3971 sc_class->redraw_range = scg_redraw_range;
3972 sc_class->redraw_headers = scg_redraw_headers;
3973 sc_class->ant = scg_ant;
3974 sc_class->unant = scg_unant;
3975 sc_class->scrollbar_config = scg_scrollbar_config;
3976 sc_class->mode_edit = scg_mode_edit_virt;
3977 sc_class->set_top_left = scg_set_top_left;
3978 sc_class->recompute_visible_region = scg_recompute_visible_region;
3979 sc_class->make_cell_visible = scg_make_cell_visible_virt;
3980 sc_class->cursor_bound = scg_cursor_bound;
3981 sc_class->set_panes = scg_set_panes;
3982 sc_class->object_create_view = scg_object_create_view;
3983 sc_class->scale_changed = scg_scale_changed;
3984 sc_class->show_im_tooltip = scg_show_im_tooltip;
3985 sc_class->freeze_object_view = scg_freeze_object_view;
3986 }
3987
GSF_CLASS(SheetControlGUI,sheet_control_gui,scg_class_init,scg_init,GNM_SHEET_CONTROL_TYPE)3988 GSF_CLASS (SheetControlGUI, sheet_control_gui,
3989 scg_class_init, scg_init, GNM_SHEET_CONTROL_TYPE)
3990
3991 static gint
3992 cb_scg_queued_movement (SheetControlGUI *scg)
3993 {
3994 Sheet const *sheet = scg_sheet (scg);
3995 scg->delayedMovement.timer = 0;
3996 (*scg->delayedMovement.handler) (scg,
3997 scg->delayedMovement.n, FALSE,
3998 scg->delayedMovement.horiz);
3999 if (wbcg_is_editing (scg->wbcg))
4000 sheet_update_only_grid (sheet);
4001 else
4002 sheet_update (sheet);
4003 return FALSE;
4004 }
4005
4006 /**
4007 * scg_queue_movement:
4008 * @scg:
4009 * @handler: (scope async): The movement handler
4010 * @n: how far
4011 * @jump: TRUE jump to bound
4012 * @horiz: TRUE move by cols
4013 *
4014 * Do motion compression when possible to avoid redrawing an area that will
4015 * disappear when we scroll again.
4016 **/
4017 void
scg_queue_movement(SheetControlGUI * scg,SCGUIMoveFunc handler,int n,gboolean jump,gboolean horiz)4018 scg_queue_movement (SheetControlGUI *scg,
4019 SCGUIMoveFunc handler,
4020 int n, gboolean jump, gboolean horiz)
4021 {
4022 g_return_if_fail (GNM_IS_SCG (scg));
4023
4024 /* do we need to flush a pending movement */
4025 if (scg->delayedMovement.timer != 0) {
4026 if (jump ||
4027 /* do not skip more than 3 requests at a time */
4028 scg->delayedMovement.counter > 3 ||
4029 scg->delayedMovement.handler != handler ||
4030 scg->delayedMovement.horiz != horiz) {
4031 g_source_remove (scg->delayedMovement.timer);
4032 (*scg->delayedMovement.handler) (scg,
4033 scg->delayedMovement.n, FALSE,
4034 scg->delayedMovement.horiz);
4035 scg->delayedMovement.handler = NULL;
4036 scg->delayedMovement.timer = 0;
4037 } else {
4038 scg->delayedMovement.counter++;
4039 scg->delayedMovement.n += n;
4040 return;
4041 }
4042 }
4043
4044 /* jumps are always immediate */
4045 if (jump) {
4046 Sheet const *sheet = scg_sheet (scg);
4047 (*handler) (scg, n, TRUE, horiz);
4048 if (wbcg_is_editing (scg->wbcg))
4049 sheet_update_only_grid (sheet);
4050 else
4051 sheet_update (sheet);
4052 return;
4053 }
4054
4055 scg->delayedMovement.counter = 1;
4056 scg->delayedMovement.handler = handler;
4057 scg->delayedMovement.horiz = horiz;
4058 scg->delayedMovement.n = n;
4059 scg->delayedMovement.timer = g_timeout_add (10,
4060 (GSourceFunc)cb_scg_queued_movement, scg);
4061 }
4062
4063 static void
scg_image_create(SheetControlGUI * scg,SheetObjectAnchor * anchor,guint8 const * data,unsigned len)4064 scg_image_create (SheetControlGUI *scg, SheetObjectAnchor *anchor,
4065 guint8 const *data, unsigned len)
4066 {
4067 SheetObjectImage *soi;
4068 SheetObject *so;
4069 double w, h;
4070
4071 /* ensure that we are not editing anything else */
4072 scg_mode_edit (scg);
4073
4074 soi = g_object_new (GNM_SO_IMAGE_TYPE, NULL);
4075 sheet_object_image_set_image (soi, "", data, len);
4076
4077 so = GNM_SO (soi);
4078 sheet_object_set_anchor (so, anchor);
4079 sheet_object_set_sheet (so, scg_sheet (scg));
4080 scg_object_select (scg, so);
4081 sheet_object_default_size (so, &w, &h);
4082 scg_objects_drag (scg, NULL, NULL, &w, &h, 7, FALSE, FALSE, FALSE);
4083 scg_objects_drag_commit (scg, 7, TRUE, NULL, NULL, NULL);
4084 }
4085
4086 void
scg_paste_image(SheetControlGUI * scg,GnmRange * where,guint8 const * data,unsigned len)4087 scg_paste_image (SheetControlGUI *scg, GnmRange *where,
4088 guint8 const *data, unsigned len)
4089 {
4090 SheetObjectAnchor anchor;
4091
4092 sheet_object_anchor_init (&anchor, where, NULL,
4093 GOD_ANCHOR_DIR_DOWN_RIGHT, GNM_SO_ANCHOR_TWO_CELLS);
4094 scg_image_create (scg, &anchor, data, len);
4095 }
4096
4097 static void
scg_drag_receive_img_data(SheetControlGUI * scg,double x,double y,guint8 const * data,unsigned len)4098 scg_drag_receive_img_data (SheetControlGUI *scg, double x, double y,
4099 guint8 const *data, unsigned len)
4100 {
4101 double coords[4];
4102 SheetObjectAnchor anchor;
4103
4104 sheet_object_anchor_init (&anchor, NULL, NULL,
4105 GOD_ANCHOR_DIR_DOWN_RIGHT, GNM_SO_ANCHOR_TWO_CELLS);
4106 coords[0] = coords[2] = x;
4107 coords[1] = coords[3] = y;
4108 scg_object_coords_to_anchor (scg, coords, &anchor);
4109 scg_image_create (scg, &anchor, data, len);
4110 }
4111
4112 static void
scg_drag_receive_img_uri(SheetControlGUI * scg,double x,double y,const gchar * uri)4113 scg_drag_receive_img_uri (SheetControlGUI *scg, double x, double y, const gchar *uri)
4114 {
4115 GError *err = NULL;
4116 GsfInput *input = go_file_open (uri, &err);
4117 GOIOContext *ioc = go_io_context_new (GO_CMD_CONTEXT (scg->wbcg));
4118
4119 if (input != NULL) {
4120 unsigned len = gsf_input_size (input);
4121 guint8 const *data = gsf_input_read (input, len, NULL);
4122
4123 scg_drag_receive_img_data (scg, x, y, data, len);
4124 g_object_unref (input);
4125 } else
4126 go_cmd_context_error (GO_CMD_CONTEXT (ioc), err);
4127
4128 if (go_io_error_occurred (ioc) ||
4129 go_io_warning_occurred (ioc)) {
4130 go_io_error_display (ioc);
4131 go_io_error_clear (ioc);
4132 }
4133 g_object_unref (ioc);
4134 }
4135
4136 static void
scg_drag_receive_spreadsheet(SheetControlGUI * scg,const gchar * uri)4137 scg_drag_receive_spreadsheet (SheetControlGUI *scg, const gchar *uri)
4138 {
4139 GError *err = NULL;
4140 GsfInput *input = go_file_open (uri, &err);
4141 GOIOContext *ioc = go_io_context_new (GO_CMD_CONTEXT (scg->wbcg));
4142
4143 if (input != NULL) {
4144 WorkbookView *wbv;
4145
4146 wbv = workbook_view_new_from_input (input, uri, NULL, ioc, NULL);
4147 if (wbv != NULL)
4148 gui_wb_view_show (scg->wbcg,
4149 wbv);
4150
4151 } else
4152 go_cmd_context_error (GO_CMD_CONTEXT (ioc), err);
4153
4154 if (go_io_error_occurred (ioc) ||
4155 go_io_warning_occurred (ioc)) {
4156 go_io_error_display (ioc);
4157 go_io_error_clear (ioc);
4158 }
4159 g_object_unref (ioc);
4160 }
4161
4162 static void
scg_paste_cellregion(SheetControlGUI * scg,double x,double y,GnmCellRegion * content)4163 scg_paste_cellregion (SheetControlGUI *scg, double x, double y,
4164 GnmCellRegion *content)
4165 {
4166 WorkbookControl *wbc = scg_wbc (scg);
4167 Sheet *sheet = scg_sheet (scg) ;
4168 GnmPasteTarget pt;
4169 SheetObjectAnchor anchor;
4170 double coords[4];
4171
4172 sheet_object_anchor_init (&anchor, NULL, NULL,
4173 GOD_ANCHOR_DIR_DOWN_RIGHT, GNM_SO_ANCHOR_TWO_CELLS);
4174 coords[0] = coords[2] = x;
4175 coords[1] = coords[3] = y;
4176 scg_object_coords_to_anchor (scg, coords, &anchor);
4177 paste_target_init (&pt, sheet, &anchor.cell_bound, PASTE_ALL_SHEET);
4178 if (content && ((content->cols > 0 && content->rows > 0) ||
4179 content->objects != NULL))
4180 cmd_paste_copy (wbc, &pt, content);
4181 }
4182
4183 static void
scg_drag_receive_cellregion(SheetControlGUI * scg,double x,double y,const char * data,unsigned len)4184 scg_drag_receive_cellregion (SheetControlGUI *scg, double x, double y,
4185 const char *data, unsigned len)
4186 {
4187 GnmCellRegion *content;
4188 GOIOContext *io_context =
4189 go_io_context_new (GO_CMD_CONTEXT (scg->wbcg));
4190
4191 content = gnm_xml_cellregion_read (scg_wbc (scg), io_context,
4192 scg_sheet (scg), data, len);
4193 g_object_unref (io_context);
4194 if (content != NULL) {
4195 scg_paste_cellregion (scg, x, y, content);
4196 cellregion_unref (content);
4197 }
4198 }
4199
4200 static void
scg_drag_receive_uri_list(SheetControlGUI * scg,double x,double y,const char * data,unsigned len)4201 scg_drag_receive_uri_list (SheetControlGUI *scg, double x, double y,
4202 const char *data, unsigned len)
4203 {
4204 char *cdata = g_strndup (data, len);
4205 GSList *urls = go_file_split_urls (cdata);
4206 GSList *l;
4207
4208 g_free (cdata);
4209 for (l = urls; l; l = l-> next) {
4210 char const *uri_str = l->data;
4211 gchar *mime = go_get_mime_type (uri_str);
4212 /* Note that we have imperfect detection of mime-type with some
4213 * platforms, e.g. Win32. In the worst case if
4214 * go_get_mime_type() doesn't return "application/x-gnumeric"
4215 * (registry corruption?) it will give "text/plain" and a
4216 * spreadsheet file is assumed. */
4217 if (!mime)
4218 continue;
4219
4220 if (!strncmp (mime, "image/", 6))
4221 scg_drag_receive_img_uri (scg, x, y, uri_str);
4222 else if (!strcmp (mime, "application/x-gnumeric") ||
4223 !strcmp (mime, "application/vnd.ms-excel") ||
4224 !strcmp (mime, "application/vnd.sun.xml.calc") ||
4225 !strcmp (mime, "application/vnd.oasis.opendocument.spreadsheet") ||
4226 !strcmp (mime, "application/vnd.lotus-1-2-3") ||
4227 !strcmp (mime, "application/x-applix-spreadsheet") ||
4228 !strcmp (mime, "application/x-dbase") ||
4229 !strcmp (mime, "application/x-oleo") ||
4230 !strcmp (mime, "application/x-quattropro") ||
4231 !strcmp (mime, "application/x-sc") ||
4232 /* !strcmp (mime, "application/xhtml+xml") || */
4233 !strcmp (mime, "text/spreadsheet") ||
4234 !strcmp (mime, "text/tab-separated-values") ||
4235 !strcmp (mime, "text/x-comma-separated-values") ||
4236 !strcmp (mime, "text/html") ||
4237 !strcmp (mime, "text/plain")) {
4238 scg_drag_receive_spreadsheet (scg, uri_str);
4239 } else {
4240 g_printerr ("Received URI %s with mime type %s.\n", uri_str, mime);
4241 g_printerr ("I have no idea what to do with that.\n");
4242 }
4243 g_free (mime);
4244 }
4245 g_slist_free_full (urls, (GDestroyNotify) g_free);
4246 }
4247
4248 static void
scg_drag_receive_same_process(SheetControlGUI * scg,GtkWidget * source_widget,double x,double y)4249 scg_drag_receive_same_process (SheetControlGUI *scg, GtkWidget *source_widget,
4250 double x, double y)
4251 {
4252 SheetControlGUI *source_scg = NULL;
4253 GnmPane *pane;
4254
4255 g_return_if_fail (source_widget != NULL);
4256 g_return_if_fail (GNM_IS_PANE (source_widget));
4257
4258 pane = GNM_PANE (source_widget);
4259 x *= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane));
4260 y *= goc_canvas_get_pixels_per_unit (GOC_CANVAS (pane));
4261 source_scg = pane->simple.scg;
4262 if (source_scg == scg) {
4263 GdkWindow *window;
4264 GdkModifierType mask;
4265 gint64 xx = x, yy = y;
4266 gint64 origin_x = 0, origin_y = 0;
4267 gboolean make_dup;
4268 GOUndo *undo = NULL;
4269 GOUndo *redo = NULL;
4270 gchar *title = NULL;
4271
4272 window = gtk_widget_get_parent_window (GTK_WIDGET (pane));
4273 gdk_window_get_device_position (window,
4274 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_window_get_display (window))),
4275 NULL, NULL, &mask);
4276
4277 make_dup = ((mask & GDK_CONTROL_MASK) != 0);
4278
4279 /* When copying objects, we have to create a copy of current selection.
4280 * Since new objects are on top of canvas, we have to move current selection
4281 * back to original position, create a copy of selected objects, make them
4282 * the current selection, then move these objects to drop location. */
4283
4284 if (make_dup) {
4285 xx = origin_x = pane->drag.origin_x;
4286 yy = origin_y = pane->drag.origin_y;
4287 }
4288
4289 gnm_pane_objects_drag (pane, NULL, xx, yy, 8, FALSE,
4290 (mask & GDK_SHIFT_MASK) != 0);
4291 pane->drag.origin_x = pane->drag.last_x;
4292 pane->drag.origin_y = pane->drag.last_y;
4293
4294 if (make_dup) {
4295 GSList *ptr, *objs = go_hash_keys (scg->selected_objects);
4296 GOUndo *nudge_undo = NULL;
4297 GOUndo *nudge_redo = NULL;
4298 double dx, dy;
4299
4300 for (ptr = objs ; ptr != NULL ; ptr = ptr->next) {
4301 SheetObject *dup_obj = sheet_object_dup (ptr->data);
4302 if (dup_obj != NULL) {
4303 sheet_object_set_sheet (dup_obj, scg_sheet (scg));
4304 scg_object_select (scg, dup_obj);
4305 g_object_unref (dup_obj);
4306 scg_object_unselect (scg, ptr->data);
4307 }
4308 }
4309 g_slist_free (objs);
4310 scg_objects_drag_commit (scg, 8, TRUE, &undo, &redo, &title);
4311 dx = x - origin_x;
4312 dy = y - origin_y;
4313 scg_objects_drag (scg, pane, NULL, &dx, &dy, 8, FALSE, FALSE, FALSE);
4314 scg_objects_drag_commit (scg, 8, FALSE, &nudge_undo, &nudge_redo, NULL);
4315 undo = go_undo_combine (undo, nudge_undo);
4316 redo = go_undo_combine (nudge_redo, redo);
4317 } else
4318 scg_objects_drag_commit (scg, 8, FALSE, &undo, &redo, &title);
4319 cmd_generic (GNM_WBC (scg_wbcg (scg)), title, undo, redo);
4320 g_free (title);
4321 } else {
4322 GnmCellRegion *content;
4323 GSList *objects;
4324
4325 g_return_if_fail (GNM_IS_SCG (source_scg));
4326
4327 objects = go_hash_keys (source_scg->selected_objects);
4328 content = clipboard_copy_obj (scg_sheet (source_scg),
4329 objects);
4330 if (content != NULL) {
4331 scg_paste_cellregion (scg, x, y, content);
4332 cellregion_unref (content);
4333 }
4334 g_slist_free (objects);
4335 }
4336 }
4337
4338 /* Keep in sync with gtk_selection_data_targets_include_text() */
4339 static gboolean
is_text_target(gchar * target_type)4340 is_text_target (gchar *target_type)
4341 {
4342 const gchar *charset;
4343 gchar *text_plain_locale;
4344 gboolean ret;
4345
4346 g_get_charset (&charset);
4347 text_plain_locale = g_strdup_printf ("text/plain;charset=%s", charset);
4348 ret = !strcmp (target_type, "UTF8_STRING") ||
4349 !strcmp (target_type, "COMPOUND_TEXT") ||
4350 !strcmp (target_type, "TEXT") ||
4351 !strcmp (target_type, "STRING") ||
4352 !strcmp (target_type, "text/plain;charset=utf-8") ||
4353 !strcmp (target_type, text_plain_locale) ||
4354 !strcmp (target_type, "text/plain");
4355 g_free (text_plain_locale);
4356 return ret;
4357 }
4358
4359 void
scg_drag_data_received(SheetControlGUI * scg,GtkWidget * source_widget,double x,double y,GtkSelectionData * selection_data)4360 scg_drag_data_received (SheetControlGUI *scg, GtkWidget *source_widget,
4361 double x, double y, GtkSelectionData *selection_data)
4362 {
4363 gchar *target_type = gdk_atom_name (gtk_selection_data_get_target (selection_data));
4364 const char *sel_data = (const char *)gtk_selection_data_get_data (selection_data);
4365 gsize sel_len = gtk_selection_data_get_length (selection_data);
4366
4367 if (!strcmp (target_type, "text/uri-list")) {
4368 scg_drag_receive_uri_list (scg, x, y, sel_data, sel_len);
4369
4370 } else if (!strncmp (target_type, "image/", 6)) {
4371 scg_drag_receive_img_data (scg, x, y, sel_data, sel_len);
4372 } else if (!strcmp (target_type, "GNUMERIC_SAME_PROC")) {
4373 scg_drag_receive_same_process (scg, source_widget, x, y);
4374 } else if (!strcmp (target_type, "application/x-gnumeric")) {
4375 scg_drag_receive_cellregion (scg, x, y, sel_data, sel_len);
4376 } else
4377 g_warning ("Unknown target type '%s'!", target_type);
4378
4379 if (gnm_debug_flag ("dnd")) {
4380 if (!strcmp (target_type, "x-special/gnome-copied-files")) {
4381 char *cdata = g_strndup (sel_data, sel_len);
4382 g_print ("data length: %d, data: %s\n",
4383 (int)sel_len, cdata);
4384 g_free (cdata);
4385 } else if (!strcmp (target_type, "_NETSCAPE_URL")) {
4386 char *cdata = g_strndup (sel_data, sel_len);
4387 g_print ("data length: %d, data: %s\n",
4388 (int)sel_len, cdata);
4389 g_free (cdata);
4390 } else if (is_text_target (target_type)) {
4391 char *cdata = g_strndup (sel_data, sel_len);
4392 g_print ("data length: %d, data: %s\n",
4393 (int)sel_len, cdata);
4394 g_free (cdata);
4395 } else if (!strcmp (target_type, "text/html")) {
4396 char *cdata = g_strndup (sel_data, sel_len);
4397 /* For mozilla, need to convert the encoding */
4398 g_print ("data length: %d, data: %s\n", (int)sel_len, cdata);
4399 g_free (cdata);
4400 }
4401 }
4402
4403 g_free (target_type);
4404 }
4405
4406 static void
scg_drag_send_image(G_GNUC_UNUSED SheetControlGUI * scg,GtkSelectionData * selection_data,GSList * objects,gchar const * mime_type)4407 scg_drag_send_image (G_GNUC_UNUSED SheetControlGUI *scg,
4408 GtkSelectionData *selection_data,
4409 GSList *objects,
4410 gchar const *mime_type)
4411 {
4412 SheetObject *so = NULL;
4413 GsfOutput *output;
4414 GsfOutputMemory *omem;
4415 gsf_off_t osize;
4416 char *format;
4417 GSList *ptr;
4418
4419 for (ptr = objects; ptr != NULL; ptr = ptr->next) {
4420 if (GNM_IS_SO_IMAGEABLE (GNM_SO (ptr->data))) {
4421 so = GNM_SO (ptr->data);
4422 break;
4423 }
4424 }
4425 if (so == NULL) {
4426 g_warning ("non imageable object requested as image\n");
4427 return;
4428 }
4429
4430 format = go_mime_to_image_format (mime_type);
4431 if (!format) {
4432 g_warning ("No image format for %s\n", mime_type);
4433 g_free (format);
4434 return;
4435 }
4436
4437 output = gsf_output_memory_new ();
4438 omem = GSF_OUTPUT_MEMORY (output);
4439 sheet_object_write_image (so, format, -1.0, output, NULL);
4440 osize = gsf_output_size (output);
4441
4442 gtk_selection_data_set
4443 (selection_data,
4444 gtk_selection_data_get_target (selection_data),
4445 8, gsf_output_memory_get_bytes (omem), osize);
4446 gsf_output_close (output);
4447 g_object_unref (output);
4448 g_free (format);
4449 }
4450
4451 static void
scg_drag_send_graph(G_GNUC_UNUSED SheetControlGUI * scg,GtkSelectionData * selection_data,GSList * objects,gchar const * mime_type)4452 scg_drag_send_graph (G_GNUC_UNUSED SheetControlGUI *scg,
4453 GtkSelectionData *selection_data,
4454 GSList *objects,
4455 gchar const *mime_type)
4456 {
4457 SheetObject *so = NULL;
4458 GsfOutput *output;
4459 GsfOutputMemory *omem;
4460 gsf_off_t osize;
4461 GSList *ptr;
4462
4463 for (ptr = objects; ptr != NULL; ptr = ptr->next)
4464 if (GNM_IS_SO_EXPORTABLE (GNM_SO (ptr->data))) {
4465 so = GNM_SO (ptr->data);
4466 break;
4467 }
4468
4469 if (so == NULL) {
4470 g_warning ("non exportable object requested\n");
4471 return;
4472 }
4473
4474 output = gsf_output_memory_new ();
4475 omem = GSF_OUTPUT_MEMORY (output);
4476 sheet_object_write_object (so, mime_type, output, NULL,
4477 gnm_conventions_default);
4478 osize = gsf_output_size (output);
4479
4480 gtk_selection_data_set
4481 (selection_data,
4482 gtk_selection_data_get_target (selection_data),
4483 8, gsf_output_memory_get_bytes (omem), osize);
4484 gsf_output_close (output);
4485 g_object_unref (output);
4486 }
4487
4488 static void
scg_drag_send_clipboard_objects(SheetControl * sc,GtkSelectionData * selection_data,GSList * objects)4489 scg_drag_send_clipboard_objects (SheetControl *sc,
4490 GtkSelectionData *selection_data,
4491 GSList *objects)
4492 {
4493 GnmCellRegion *content = clipboard_copy_obj (sc_sheet (sc), objects);
4494 GsfOutputMemory *output;
4495
4496 if (content == NULL)
4497 return;
4498
4499 output = gnm_cellregion_to_xml (content);
4500 gtk_selection_data_set
4501 (selection_data,
4502 gtk_selection_data_get_target (selection_data),
4503 8,
4504 gsf_output_memory_get_bytes (output),
4505 gsf_output_size (GSF_OUTPUT (output)));
4506 g_object_unref (output);
4507 cellregion_unref (content);
4508 }
4509
4510 static void
scg_drag_send_text(SheetControlGUI * scg,GtkSelectionData * sd)4511 scg_drag_send_text (SheetControlGUI *scg, GtkSelectionData *sd)
4512 {
4513 Sheet *sheet = scg_sheet (scg);
4514 GnmRange range = sheet_get_extent (sheet, TRUE, TRUE);
4515 GnmCellRegion *reg = clipboard_copy_range (sheet, &range);
4516 GString *s = cellregion_to_string (reg, TRUE, sheet_date_conv (sheet));
4517
4518 cellregion_unref (reg);
4519 if (!s)
4520 return;
4521 gtk_selection_data_set (sd, gtk_selection_data_get_target (sd),
4522 8, s->str, s->len);
4523 g_string_free (s, TRUE);
4524 }
4525
4526 void
scg_drag_data_get(SheetControlGUI * scg,GtkSelectionData * selection_data)4527 scg_drag_data_get (SheetControlGUI *scg, GtkSelectionData *selection_data)
4528 {
4529 GdkAtom target = gtk_selection_data_get_target (selection_data);
4530 gchar *target_name = gdk_atom_name (target);
4531 GSList *objects = scg->selected_objects
4532 ? go_hash_keys (scg->selected_objects)
4533 : NULL;
4534
4535 if (strcmp (target_name, "GNUMERIC_SAME_PROC") == 0)
4536 /* Set dummy selection for process internal dnd */
4537 gtk_selection_data_set (selection_data, target,
4538 8, (const guint8 *)"", 1);
4539 else if (strcmp (target_name, "GNUMERIC_SHEET") == 0)
4540 gtk_selection_data_set (selection_data, target,
4541 8, (void *)scg, sizeof (scg));
4542 else if (strcmp (target_name, "application/x-gnumeric") == 0)
4543 scg_drag_send_clipboard_objects (GNM_SHEET_CONTROL (scg),
4544 selection_data, objects);
4545 else if (strcmp (target_name, "application/x-goffice-graph") == 0)
4546 scg_drag_send_graph (scg, selection_data, objects, target_name);
4547 else if (strncmp (target_name, "image/", 6) == 0)
4548 scg_drag_send_image (scg, selection_data, objects, target_name);
4549 else if (strcmp (target_name, "UTF8_STRING") == 0)
4550 scg_drag_send_text (scg, selection_data);
4551
4552 g_free (target_name);
4553 g_slist_free (objects);
4554 }
4555
4556 void
scg_delete_sheet_if_possible(SheetControlGUI * scg)4557 scg_delete_sheet_if_possible (SheetControlGUI *scg)
4558 {
4559 SheetControl *sc = (SheetControl *) scg;
4560 Sheet *sheet = scg_sheet (scg);
4561 Workbook *wb = sheet->workbook;
4562
4563 /* If this is the last sheet left, ignore the request */
4564 if (workbook_sheet_count (wb) != 1) {
4565 WorkbookSheetState *old_state = workbook_sheet_state_new (wb);
4566 WorkbookControl *wbc = sc->wbc;
4567 workbook_sheet_delete (sheet);
4568 /* Careful: sc just ceased to be valid. */
4569 cmd_reorganize_sheets (wbc, old_state, sheet);
4570 }
4571 }
4572
4573 void
scg_reload_item_edits(SheetControlGUI * scg)4574 scg_reload_item_edits (SheetControlGUI *scg)
4575 {
4576 SCG_FOREACH_PANE (scg, pane, {
4577 if (pane->editor != NULL)
4578 goc_item_bounds_changed
4579 (GOC_ITEM (pane->editor));
4580 });
4581 }
4582