void row_calc_spans (ColRowInfo *ri, int row, Sheet const *sheet) { int left, right, col; GnmRange const *merged; GnmCell *cell; int const last = sheet->cols.max_used; row_destroy_span (ri); for (col = 0 ; col <= last ; ) { cell = sheet_cell_get (sheet, col, row); if (cell == NULL) { /* skip segments with no cells */ if (col == COLROW_SEGMENT_START (col) && NULL == COLROW_GET_SEGMENT (&(sheet->cols), col)) col = COLROW_SEGMENT_END (col) + 1; else col++; continue; } /* render as necessary */ (void)gnm_cell_fetch_rendered_value (cell, TRUE); if (gnm_cell_is_merged (cell)) { merged = gnm_sheet_merge_is_corner (sheet, &cell->pos); if (NULL != merged) { col = merged->end.col + 1; continue; } } cell_calc_span (cell, &left, &right); if (left != right) { cell_register_span (cell, left, right); col = right + 1; } else col++; } ri->needs_respan = FALSE; }
static void comment_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible) { CommentView *cv = (CommentView *)sov; GocPoints *points = goc_points_new (3); GocItem *item = GOC_ITEM (GOC_GROUP (sov)->children->data); if (visible) { SheetObject *so = sheet_object_view_get_so (sov); SheetControlGUI const *scg = GNM_SIMPLE_CANVAS (item->canvas)->scg; double scale; gint64 x, y, dx; int far_col; GnmRange const *r = gnm_sheet_merge_is_corner (so->sheet, &so->anchor.cell_bound.start); scale = 1. / item->canvas->pixels_per_unit; if (r != NULL) far_col = 1 + r->end.col; else far_col = 1 + so->anchor.cell_bound.start.col; /* TODO : This could be optimized using the offsets associated with the visible region */ /* Add 1 to y because we measure from start, x is measured from end, so * it does not need it */ y = scg_colrow_distance_get (scg, FALSE, 0, so->anchor.cell_bound.start.row)+ 1; points->points[0].y = scale * y; points->points[1].y = scale * y; points->points[2].y = scale * y + cv->comment_indicator_size; dx = cv->comment_indicator_size; x = scg_colrow_distance_get (scg, TRUE, 0, far_col); points->points[0].x = scale * x - dx; points->points[1].x = scale * x; points->points[2].x = scale * x; goc_item_set (item, "points", points, NULL); goc_points_unref (points); goc_item_show (GOC_ITEM (sov)); } else goc_item_hide (GOC_ITEM (sov)); }
/** * gnm_cell_combo_view_popdown: * @sov: #SheetObjectView * @activate_time: event time * * Open the popup window associated with @sov **/ void gnm_cell_combo_view_popdown (SheetObjectView *sov, guint32 activate_time) { GocItem *view = GOC_ITEM (sov); GnmPane *pane = GNM_PANE (view->canvas); SheetControlGUI *scg = pane->simple.scg; SheetObject *so = sheet_object_view_get_so (sov); Sheet const *sheet = sheet_object_get_sheet (so); GtkWidget *frame, *popup, *list, *container; int root_x, root_y; gboolean make_buttons = FALSE; GtkTreePath *clip = NULL, *select = NULL; GtkWindow *toplevel = wbcg_toplevel (scg_wbcg (scg)); GdkWindow *popup_window; GdkDevice *device; GnmRange const *merge; popup = gtk_window_new (GTK_WINDOW_POPUP); gtk_window_set_type_hint (GTK_WINDOW (popup), GDK_WINDOW_TYPE_HINT_COMBO); gtk_window_group_add_window (gtk_window_get_group (toplevel), GTK_WINDOW (popup)); go_gtk_window_set_transient (toplevel, GTK_WINDOW (popup)); gtk_window_set_resizable (GTK_WINDOW (popup), FALSE); gtk_window_set_decorated (GTK_WINDOW (popup), FALSE); gtk_window_set_screen (GTK_WINDOW (popup), gtk_widget_get_screen (GTK_WIDGET (toplevel))); list = ccombo_create_list (GNM_CCOMBO_VIEW (sov), so, &clip, &select, &make_buttons); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE); g_object_set_data (G_OBJECT (list), SOV_ID, sov); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); #if 0 range_dump (&so->anchor.cell_bound, ""); g_printerr (" : so = %p, view = %p\n", so, view); #endif if (clip != NULL) { GtkWidget *sw = gtk_scrolled_window_new ( gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (list)), gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (list))); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); g_object_set_data_full (G_OBJECT (list), "clip", clip, (GDestroyNotify)gtk_tree_path_free); gtk_container_add (GTK_CONTAINER (sw), list); /* * Do the sizing in a realize handler as newer versions of * gtk+ give us zero sizes until then. */ g_signal_connect_after (list, "realize", G_CALLBACK (cb_realize_treeview), sw); container = sw; } else container = list; if (make_buttons) { GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); GtkWidget *button; button = gtk_button_new_from_stock (GTK_STOCK_CANCEL); g_signal_connect_swapped (button, "clicked", G_CALLBACK (cb_ccombo_cancel_button), list); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 6); button = gtk_button_new_from_stock (GTK_STOCK_OK); g_signal_connect_swapped (button, "clicked", G_CALLBACK (cb_ccombo_ok_button), list); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 6); gtk_box_pack_start (GTK_BOX (vbox), container, FALSE, TRUE, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 6); container = vbox; } gtk_container_add (GTK_CONTAINER (frame), container); /* do the popup */ gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (pane)), &root_x, &root_y); if (sheet->text_is_rtl) { GtkAllocation pa; gtk_widget_get_allocation (GTK_WIDGET (pane), &pa); root_x += pa.width; root_x -= scg_colrow_distance_get (scg, TRUE, pane->first.col, so->anchor.cell_bound.start.col + 1); } else root_x += scg_colrow_distance_get (scg, TRUE, pane->first.col, so->anchor.cell_bound.start.col); merge = gnm_sheet_merge_is_corner (sheet, &(so->anchor.cell_bound.start)); gtk_window_move (GTK_WINDOW (popup), root_x, root_y + scg_colrow_distance_get (scg, FALSE, pane->first.row, so->anchor.cell_bound.start.row + ((merge == NULL) ? 1 : range_height (merge)))); gtk_container_add (GTK_CONTAINER (popup), frame); g_signal_connect (popup, "key_press_event", G_CALLBACK (cb_ccombo_key_press), list); g_signal_connect (popup, "button_press_event", G_CALLBACK (cb_ccombo_button_press), list); g_signal_connect_after (popup, "button_release_event", G_CALLBACK (cb_ccombo_button_release), list); g_signal_connect (list, "motion_notify_event", G_CALLBACK (cb_ccombo_list_motion), list); g_signal_connect (list, "button_press_event", G_CALLBACK (cb_ccombo_list_button_press), popup); gtk_widget_show_all (popup); /* after we show the window setup the selection (showing the list * clears the selection) */ if (select != NULL) { gtk_tree_selection_select_path ( gtk_tree_view_get_selection (GTK_TREE_VIEW (list)), select); gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), select, NULL, FALSE); gtk_tree_path_free (select); } gtk_widget_grab_focus (popup); gtk_widget_grab_focus (GTK_WIDGET (list)); ccombo_focus_change (GTK_WIDGET (list), TRUE); popup_window = gtk_widget_get_window (popup); device = gtk_get_current_event_device (); if (0 == gdk_device_grab (device, popup_window, GDK_OWNERSHIP_APPLICATION, TRUE, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK, NULL, activate_time)) { if (0 == gdk_device_grab (gdk_device_get_associated_device (device), popup_window, GDK_OWNERSHIP_APPLICATION, TRUE, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, NULL, activate_time)) gtk_grab_add (popup); else gdk_device_ungrab (device, activate_time); } }
/** * clipboard_paste_region: * @cr: The GnmCellRegion to paste. * @pt: Where to paste the values. * @cc: The context for error handling. * * Pastes the supplied GnmCellRegion (@cr) into the supplied * GnmPasteTarget (@pt). This operation is not undoable. It does not auto grow * the destination if the target is a singleton. This is a simple interface to * paste a region. * * returns : TRUE if there was a problem. **/ gboolean clipboard_paste_region (GnmCellRegion const *cr, GnmPasteTarget const *pt, GOCmdContext *cc) { int repeat_horizontal, repeat_vertical, clearFlags; int dst_cols, dst_rows, src_cols, src_rows; int i, j; GSList *ptr; GnmRange const *r; gboolean has_contents, adjust_merges = TRUE; struct paste_cell_data dat; GnmRange const *merge_src; g_return_val_if_fail (pt != NULL, TRUE); g_return_val_if_fail (cr != NULL, TRUE); /* we do not need any of this fancy stuff when pasting a simple object */ if (cr->cell_content == NULL && cr->styles == NULL && cr->merged == NULL && cr->objects != NULL) { if (pt->paste_flags & (PASTE_COMMENTS | PASTE_OBJECTS)) for (ptr = cr->objects; ptr; ptr = ptr->next) paste_object (pt, ptr->data, pt->range.start.col, pt->range.start.row); return FALSE; } r = &pt->range; dst_cols = range_width (r); dst_rows = range_height (r); src_cols = cr->cols; src_rows = cr->rows; /* If the source is a single cell or a single merge */ /* Treat a target of a single merge specially, don't split the merge */ if ((src_cols == 1 && src_rows == 1) || (g_slist_length (cr->merged) == 1 && (NULL != (merge_src = cr->merged->data)) && range_height (merge_src) == cr->rows && range_width (merge_src) == cr->cols)) { GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start); if (merge != NULL && range_equal (r, merge)) { dst_cols = dst_rows = 1; adjust_merges = FALSE; src_cols = 1; src_rows = 1; } /* Apparently links do not supercede merges */ } else if (pt->paste_flags & PASTE_LINK) adjust_merges = FALSE; has_contents = pt->paste_flags & (PASTE_CONTENTS|PASTE_AS_VALUES|PASTE_LINK); if (pt->paste_flags & PASTE_TRANSPOSE) { int tmp = src_cols; src_cols = src_rows; src_rows = tmp; } if (cr->not_as_contents && (pt->paste_flags & PASTE_CONTENTS)) { go_cmd_context_error_invalid (cc, _("Unable to paste"), _("Contents can only be pasted by value or by link.")); return TRUE; } /* calculate the tiling */ repeat_horizontal = dst_cols/src_cols; if (repeat_horizontal * src_cols != dst_cols) { char *msg = g_strdup_printf ( _("destination does not have an even multiple of source columns (%d vs %d)\n\n" "Try selecting a single cell or an area of the same shape and size."), dst_cols, src_cols); go_cmd_context_error_invalid (cc, _("Unable to paste"), msg); g_free (msg); return TRUE; } repeat_vertical = dst_rows/src_rows; if (repeat_vertical * src_rows != dst_rows) { char *msg = g_strdup_printf ( _("destination does not have an even multiple of source rows (%d vs %d)\n\n" "Try selecting a single cell or an area of the same shape and size."), dst_rows, src_rows); go_cmd_context_error_invalid (cc, _("Unable to paste"), msg); g_free (msg); return TRUE; } if ((pt->range.start.col + dst_cols) > gnm_sheet_get_max_cols (pt->sheet) || (pt->range.start.row + dst_rows) > gnm_sheet_get_max_rows (pt->sheet)) { go_cmd_context_error_invalid (cc, _("Unable to paste"), _("result passes the sheet boundary")); return TRUE; } clearFlags = 0; /* clear the region where we will paste */ if (has_contents) clearFlags = CLEAR_VALUES | CLEAR_NORESPAN; if (pt->paste_flags & PASTE_COMMENTS) clearFlags |= CLEAR_COMMENTS; /* No need to clear the formats. We will paste over top of these. */ /* if (pt->paste_flags & PASTE_FORMATS) clearFlags |= CLEAR_FORMATS; */ if (pt->paste_flags & (PASTE_OPER_MASK | PASTE_SKIP_BLANKS)) clearFlags = 0; /* remove merged regions even for operations, or blanks */ if (has_contents && adjust_merges) clearFlags |= CLEAR_MERGES; if (clearFlags != 0) { int const dst_col = pt->range.start.col; int const dst_row = pt->range.start.row; sheet_clear_region (pt->sheet, dst_col, dst_row, dst_col + dst_cols - 1, dst_row + dst_rows - 1, clearFlags, cc); } dat.translate_dates = cr->date_conv && !go_date_conv_equal (cr->date_conv, workbook_date_conv (pt->sheet->workbook)); for (i = 0; i < repeat_horizontal ; i++) for (j = 0; j < repeat_vertical ; j++) { int const left = i * src_cols + pt->range.start.col; int const top = j * src_rows + pt->range.start.row; dat.top_left.col = left; dat.top_left.row = top; dat.rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE; dat.rinfo.origin_sheet = dat.rinfo.target_sheet = pt->sheet; if (pt->paste_flags & PASTE_EXPR_LOCAL_RELOCATE) { dat.rinfo.origin.start = cr->base; dat.rinfo.origin.end.col = cr->base.col + cr->cols - 1; dat.rinfo.origin.end.row = cr->base.row + cr->rows - 1; dat.rinfo.col_offset = left - cr->base.col; dat.rinfo.row_offset = top - cr->base.row; } else { dat.rinfo.origin = pt->range; dat.rinfo.col_offset = 0; dat.rinfo.row_offset = 0; } /* Move the styles on here so we get correct formats before recalc */ if (pt->paste_flags & PASTE_FORMATS) { if (pt->paste_flags & PASTE_TRANSPOSE) sheet_style_set_list (pt->sheet, &dat.top_left, cr->styles, (sheet_style_set_list_cb_t) range_transpose, &dat.top_left); else if (pt->paste_flags & PASTE_FLIP_H) { int data = 2 * left + src_cols - 1; sheet_style_set_list (pt->sheet, &dat.top_left, cr->styles, (sheet_style_set_list_cb_t) range_flip_h, &data); } else if (pt->paste_flags & PASTE_FLIP_V) { int data = 2 * top + src_rows - 1; sheet_style_set_list (pt->sheet, &dat.top_left, cr->styles, (sheet_style_set_list_cb_t) range_flip_v, &data); } else sheet_style_set_list (pt->sheet, &dat.top_left, cr->styles, NULL, NULL); } if (has_contents && !(pt->paste_flags & PASTE_DONT_MERGE)) { for (ptr = cr->merged; ptr != NULL ; ptr = ptr->next) { GnmRange tmp = *((GnmRange const *)ptr->data); if (pt->paste_flags & PASTE_TRANSPOSE) { int x; x = tmp.start.col; tmp.start.col = tmp.start.row; tmp.start.row = x; x = tmp.end.col; tmp.end.col = tmp.end.row; tmp.end.row = x; } if (!range_translate (&tmp, pt->sheet, left, top)) gnm_sheet_merge_add (pt->sheet, &tmp, TRUE, cc); } } if (has_contents && (pt->paste_flags & PASTE_LINK)) { paste_link (pt, top, left, cr); continue; } if (has_contents && NULL != cr->cell_content) { dat.pt = pt; dat.cr = cr; g_hash_table_foreach (cr->cell_content, (GHFunc)cb_paste_cell, &dat); } if (pt->paste_flags & (PASTE_COMMENTS | PASTE_OBJECTS)) for (ptr = cr->objects; ptr; ptr = ptr->next) paste_object (pt, ptr->data, left, top); } if (!(pt->paste_flags & PASTE_NO_RECALC)) { if (has_contents) { sheet_region_queue_recalc (pt->sheet, r); sheet_flag_status_update_range (pt->sheet, r); } else sheet_flag_style_update_range (pt->sheet, r); sheet_range_calc_spans (pt->sheet, r, (pt->paste_flags & PASTE_FORMATS) ? GNM_SPANCALC_RE_RENDER : GNM_SPANCALC_RENDER); if (pt->paste_flags & PASTE_UPDATE_ROW_HEIGHT) rows_height_update (pt->sheet, &pt->range, FALSE); sheet_redraw_all (pt->sheet, FALSE); } return FALSE; }