static SheetObjectView * gnm_soi_new_view (SheetObject *so, SheetObjectViewContainer *container) { SheetObjectImage *soi = SHEET_OBJECT_IMAGE (so); GocItem *item = NULL; GdkPixbuf *pixbuf, *placeholder = NULL; pixbuf = soi_get_pixbuf (soi, 1.); if (pixbuf == NULL) { placeholder = gtk_icon_theme_load_icon ( gtk_icon_theme_get_default (), "unknown_image", 100, 0, NULL); pixbuf = gdk_pixbuf_copy (placeholder); } item = goc_item_new ( gnm_pane_object_group (GNM_PANE (container)), so_image_goc_view_get_type (), NULL); goc_item_hide (goc_item_new (GOC_GROUP (item), GOC_TYPE_PIXBUF, "pixbuf", pixbuf, NULL)); g_object_unref (G_OBJECT (pixbuf)); if (placeholder) g_object_set_data (G_OBJECT (item), "tile", placeholder); return gnm_pane_object_register (so, item, TRUE); }
static void ig_reload_style (GnmItemGrid *ig) { GocItem *item = GOC_ITEM (ig); GtkStyleContext *context = goc_item_get_style_context (item); GtkBorder border; GtkStateFlags state = GTK_STATE_FLAG_NORMAL; GnmPane *pane = GNM_PANE (item->canvas); gtk_style_context_save (context); gtk_style_context_add_region (context, "function-marker", 0); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &ig->function_marker_color); gtk_style_context_get_border_color (context, state, &ig->function_marker_border_color); gtk_style_context_restore (context); gtk_style_context_save (context); gtk_style_context_add_region (context, "pane-divider", 0); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &ig->pane_divider_color); gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border); ig->pane_divider_width = border.top; /* Hack? */ gtk_style_context_restore (context); /* ---------------------------------------- */ context = gtk_widget_get_style_context (GTK_WIDGET (pane)); gtk_widget_style_get (GTK_WIDGET (pane), "function-indicator-size", &ig->function_marker_size, NULL); }
static gboolean comment_view_leave_notify (GocItem *item, double x, double y) { scg_comment_unselect (GNM_PANE (item->canvas)->simple.scg, GNM_CELL_COMMENT (sheet_object_view_get_so (GNM_SO_VIEW (item)))); return TRUE; }
static SheetObjectView * gnm_soi_new_view (SheetObject *so, SheetObjectViewContainer *container) { SheetObjectImage *soi = SHEET_OBJECT_IMAGE (so); GocItem *item = NULL; item = goc_item_new ( gnm_pane_object_group (GNM_PANE (container)), so_image_goc_view_get_type (), NULL); if (soi->image) { goc_item_hide (goc_item_new (GOC_GROUP (item), GOC_TYPE_IMAGE, "image", soi->image, "crop-bottom", soi->crop_bottom, "crop-left", soi->crop_left, "crop-right", soi->crop_right, "crop-top", soi->crop_top, NULL)); } else { GdkPixbuf *placeholder = go_gdk_pixbuf_load_from_file ("res:gnm:pixmaps/unknown_image.png"); GdkPixbuf *pixbuf = gdk_pixbuf_copy (placeholder); goc_item_hide (goc_item_new (GOC_GROUP (item), GOC_TYPE_PIXBUF, "pixbuf", pixbuf, NULL)); g_object_unref (pixbuf); g_object_set_data (G_OBJECT (item), "tile", placeholder); } return gnm_pane_object_register (so, item, TRUE); }
static gint cb_cursor_motion (GnmItemGrid *ig) { Sheet const *sheet = scg_sheet (ig->scg); GocCanvas *canvas = ig->canvas_item.canvas; GnmPane *pane = GNM_PANE (canvas); GdkCursor *cursor; GnmCellPos pos; GnmHLink *old_link; pos.col = gnm_pane_find_col (pane, ig->last_x, NULL); pos.row = gnm_pane_find_row (pane, ig->last_y, NULL); old_link = ig->cur_link; ig->cur_link = sheet_hlink_find (sheet, &pos); cursor = (ig->cur_link != NULL) ? ig->cursor_link : ig->cursor_cross; if (pane->mouse_cursor != cursor) { gnm_pane_mouse_cursor_set (pane, cursor); scg_set_display_cursor (ig->scg); } if (ig->cursor_timer != 0) { g_source_remove (ig->cursor_timer); ig->cursor_timer = 0; } if (old_link != ig->cur_link && ig->tip != NULL) { gtk_widget_destroy (gtk_widget_get_toplevel (ig->tip)); ig->tip = NULL; } return FALSE; }
static gboolean item_grid_button_released (GocItem *item, int button, G_GNUC_UNUSED double x_, G_GNUC_UNUSED double y_) { GnmItemGrid *ig = GNM_ITEM_GRID (item); GnmPane *pane = GNM_PANE (item->canvas); SheetControlGUI *scg = ig->scg; Sheet *sheet = scg_sheet (scg); ItemGridSelectionType selecting = ig->selecting; GdkEvent *event = goc_canvas_get_cur_event (item->canvas); if (button != 1 && button != 2) return FALSE; gnm_pane_slide_stop (pane); switch (selecting) { case GNM_ITEM_GRID_NO_SELECTION: return TRUE; case GNM_ITEM_GRID_SELECTING_FORMULA_RANGE : /* Removal of this code (2 lines) */ /* should fix http://bugzilla.gnome.org/show_bug.cgi?id=63485 */ /* sheet_make_cell_visible (sheet, */ /* sheet->edit_pos.col, sheet->edit_pos.row, FALSE); */ /* Fall through */ case GNM_ITEM_GRID_SELECTING_CELL_RANGE : sv_selection_simplify (scg_view (scg)); wb_view_selection_desc ( wb_control_view (scg_wbc (scg)), TRUE, NULL); break; default: g_assert_not_reached (); } ig->selecting = GNM_ITEM_GRID_NO_SELECTION; gnm_simple_canvas_ungrab (item, gdk_event_get_time (event)); if (selecting == GNM_ITEM_GRID_SELECTING_FORMULA_RANGE) gnm_expr_entry_signal_update ( wbcg_get_entry_logical (scg_wbcg (scg)), TRUE); if (selecting == GNM_ITEM_GRID_SELECTING_CELL_RANGE && button == 1) { GnmCellPos const *pos = sv_is_singleton_selected (scg_view (scg)); if (pos != NULL) { GnmHLink *link; /* check for hyper links */ link = sheet_hlink_find (sheet, pos); if (link != NULL) gnm_hlink_activate (link, scg_wbcg (scg)); } } return TRUE; }
static gboolean comment_view_button2_pressed (GocItem *item, int button, double x, double y) { SheetObject *so; GnmRange const *r; SheetControlGUI *scg; if (button !=1) return FALSE; scg = GNM_PANE (item->canvas)->simple.scg; so = sheet_object_view_get_so (GNM_SO_VIEW (item)); r = sheet_object_get_range (so); dialog_cell_comment (scg->wbcg, so->sheet, &r->start); return TRUE; }
static gboolean comment_view_enter_notify (GocItem *item, double x, double y) { int ix, iy; SheetObject *so; gnm_widget_set_cursor_type (GTK_WIDGET (item->canvas), GDK_ARROW); gnm_canvas_get_screen_position (item->canvas, x, y, &ix, &iy); so = sheet_object_view_get_so (GNM_SO_VIEW (item)); scg_comment_select (GNM_PANE (item->canvas)->simple.scg, GNM_CELL_COMMENT (so), ix, iy); return TRUE; }
static gboolean ccombo_activate (GtkTreeView *list, gboolean button) { SheetObjectView *sov = g_object_get_data (G_OBJECT (list), SOV_ID); GocItem *view = GOC_ITEM (sov); GnmPane *pane = GNM_PANE (view->canvas); GnmCComboViewClass *klass = GNM_CCOMBO_VIEW_GET_CLASS (sov); if ((klass->activate) (sheet_object_view_get_so (sov), list, scg_wbcg (pane->simple.scg), button)) { ccombo_popup_destroy (GTK_WIDGET (list)); return TRUE; } return FALSE; }
static void comment_view_reload_style (CommentView *cv) { GocItem *item = GOC_ITEM (cv); GnmPane *pane = GNM_PANE (item->canvas); GtkStyleContext *context; context = goc_item_get_style_context (item); gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &cv->comment_indicator_color); context = gtk_widget_get_style_context (GTK_WIDGET (pane)); gtk_widget_style_get (GTK_WIDGET (pane), "comment-indicator-size", &cv->comment_indicator_size, NULL); }
static gboolean comment_view_button_released (GocItem *item, int button, double x, double y) { SheetObject *so; int ix, iy; if (button != 1) return FALSE; gnm_canvas_get_screen_position (item->canvas, x, y, &ix, &iy); so = sheet_object_view_get_so (GNM_SO_VIEW (item)); scg_comment_display (GNM_PANE (item->canvas)->simple.scg, GNM_CELL_COMMENT (so), ix, iy); return TRUE; }
static gint cb_cursor_come_to_rest (GnmItemGrid *ig) { Sheet const *sheet = scg_sheet (ig->scg); GocCanvas *canvas = ig->canvas_item.canvas; GnmPane *pane = GNM_PANE (canvas); GnmHLink *link; gint64 x, y; GnmCellPos pos; char const *tiptext; /* Be anal and look it up in case something has destroyed the link * since the last motion */ x = ig->last_x; y = ig->last_y; pos.col = gnm_pane_find_col (pane, x, NULL); pos.row = gnm_pane_find_row (pane, y, NULL); link = sheet_hlink_find (sheet, &pos); if (link != NULL && (tiptext = gnm_hlink_get_tip (link)) != NULL) { g_return_val_if_fail (link == ig->cur_link, FALSE); if (ig->tip == NULL && strlen (tiptext) > 0) { GtkWidget *cw = GTK_WIDGET (canvas); int wx, wy; gnm_canvas_get_position (canvas, &wx, &wy, ig->last_x, ig->last_y); ig->tip = gnumeric_create_tooltip (cw); gtk_label_set_text (GTK_LABEL (ig->tip), tiptext); /* moving the tip window some pixels from wx,wy in order to * avoid a leave_notify event that would destroy the tip. * see #706659 */ gtk_window_move (GTK_WINDOW (gtk_widget_get_toplevel (ig->tip)), wx + 10, wy + 10); gtk_widget_show_all (gtk_widget_get_toplevel (ig->tip)); } } ig->tip_timer = 0; return FALSE; }
/** * gnm_cell_combo_view_new: * @so: #SheetObject * @type: #GType * @container: SheetObjectViewContainer (a GnmPane) * * Create and register an in cell combo to pick from an autofilter list. **/ SheetObjectView * gnm_cell_combo_view_new (SheetObject *so, GType type, SheetObjectViewContainer *container) { GnmPane *pane = GNM_PANE (container); GtkWidget *view_widget = gtk_button_new (); GocItem *ccombo = goc_item_new (pane->object_views, type, NULL); goc_item_new (GOC_GROUP (ccombo), GOC_TYPE_WIDGET, "widget", view_widget, NULL); gtk_widget_set_can_focus (view_widget, FALSE); gtk_container_add (GTK_CONTAINER (view_widget), ccombo_create_arrow (GNM_CCOMBO_VIEW (ccombo), so)); g_signal_connect_swapped (view_widget, "pressed", G_CALLBACK (cb_ccombo_button_pressed), ccombo); gtk_widget_show_all (view_widget); return gnm_pane_object_register (so, ccombo, FALSE); }
static gboolean ig_obj_create_begin (GnmItemGrid *ig, int button, gint64 x, gint64 y) { GnmPane *pane = GNM_PANE (GOC_ITEM (ig)->canvas); SheetObject *so = ig->scg->wbcg->new_object; SheetObjectAnchor anchor; double coords[4]; g_return_val_if_fail (ig->scg->selected_objects == NULL, TRUE); g_return_val_if_fail (so != NULL, TRUE); coords[0] = coords[2] = x; coords[1] = coords[3] = y; sheet_object_anchor_init (&anchor, NULL, NULL, GOD_ANCHOR_DIR_DOWN_RIGHT); scg_object_coords_to_anchor (ig->scg, coords, &anchor); sheet_object_set_anchor (so, &anchor); sheet_object_set_sheet (so, scg_sheet (ig->scg)); scg_object_select (ig->scg, so); gnm_pane_object_start_resize (pane, button, x, y, so, 7, TRUE); return TRUE; }
static SheetObjectView * gnm_so_path_new_view (SheetObject *so, SheetObjectViewContainer *container) { GnmSOPath *sop = GNM_SO_PATH (so); GnmSOPathView *item; /* FIXME: this is unsafe if the paths change after the view is created, * but this can't occur for now */ unsigned i; if (sop->path == NULL && sop->paths == NULL) return NULL; item = (GnmSOPathView *) goc_item_new ( gnm_pane_object_group (GNM_PANE (container)), so_path_goc_view_get_type (), NULL); if (sop->path) item->path = goc_item_new (GOC_GROUP (item), GOC_TYPE_PATH, "closed", TRUE, "fill-rule", TRUE, NULL); else { item->paths = g_ptr_array_sized_new (sop->paths->len); g_ptr_array_set_free_func (item->paths, g_object_unref); for (i = 0; i < sop->paths->len; i++) g_ptr_array_add (item->paths, goc_item_new (GOC_GROUP (item), GOC_TYPE_PATH, "closed", TRUE, "fill-rule", TRUE, NULL)); } cb_gnm_so_path_changed (sop, NULL, item); g_signal_connect_object (sop, "notify::style", G_CALLBACK (cb_gnm_so_path_changed), item, 0); return gnm_pane_object_register (so, GOC_ITEM (item), TRUE); }
static gboolean item_grid_motion (GocItem *item, double x_, double y_) { GnmItemGrid *ig = GNM_ITEM_GRID (item); GocCanvas *canvas = item->canvas; GnmPane *pane = GNM_PANE (canvas); GnmPaneSlideHandler slide_handler = NULL; gint64 x = x_ * canvas->pixels_per_unit, y = y_ * canvas->pixels_per_unit; switch (ig->selecting) { case GNM_ITEM_GRID_NO_SELECTION: if (ig->cursor_timer == 0) ig->cursor_timer = g_timeout_add (100, (GSourceFunc)cb_cursor_motion, ig); if (ig->tip_timer != 0) g_source_remove (ig->tip_timer); ig->tip_timer = g_timeout_add (500, (GSourceFunc)cb_cursor_come_to_rest, ig); ig->last_x = x; ig->last_y = y; return TRUE; case GNM_ITEM_GRID_SELECTING_CELL_RANGE : slide_handler = &cb_extend_cell_range; break; case GNM_ITEM_GRID_SELECTING_FORMULA_RANGE : slide_handler = &cb_extend_expr_range; break; default: g_assert_not_reached (); } gnm_pane_handle_motion (pane, canvas, x, y, GNM_PANE_SLIDE_X | GNM_PANE_SLIDE_Y | GNM_PANE_SLIDE_AT_COLROW_BOUND, slide_handler, NULL); return TRUE; }
/** * 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); } }
static gboolean item_grid_draw_region (GocItem const *item, cairo_t *cr, double x_0, double y_0, double x_1, double y_1) { GocCanvas *canvas = item->canvas; double scale = canvas->pixels_per_unit; gint64 x0 = x_0 * scale, y0 = y_0 * scale, x1 = x_1 * scale, y1 = y_1 * scale; gint width = x1 - x0; gint height = y1 - y0; GnmPane *pane = GNM_PANE (canvas); Sheet const *sheet = scg_sheet (pane->simple.scg); WBCGtk *wbcg = scg_wbcg (pane->simple.scg); GnmCell const * const edit_cell = wbcg->editing_cell; GnmItemGrid *ig = GNM_ITEM_GRID (item); ColRowInfo const *ri = NULL, *next_ri = NULL; int const dir = sheet->text_is_rtl ? -1 : 1; SheetView const *sv = scg_view (ig->scg); WorkbookView *wbv = sv_wbv (sv); gboolean show_function_cell_markers = wbv->show_function_cell_markers; gboolean show_extension_markers = wbv->show_extension_markers; /* we use the selected background color from an entry for selected cells */ GtkWidget *entry = gtk_entry_new (); GtkStyleContext *ctxt = gtk_widget_get_style_context (entry); /* To ensure that far and near borders get drawn we pretend to draw +-2 * pixels around the target area which would include the surrounding * borders if necessary */ /* TODO : there is an opportunity to speed up the redraw loop by only * painting the borders of the edges and not the content. * However, that feels like more hassle that it is worth. Look into this someday. */ int x; gint64 y, start_x, offset; int col, row, n, start_col, end_col; int start_row = gnm_pane_find_row (pane, y0-2, &y); int end_row = gnm_pane_find_row (pane, y1+2, NULL); gint64 const start_y = y - canvas->scroll_y1 * scale; GnmStyleRow sr, next_sr; GnmStyle const **styles; GnmBorder const **borders, **prev_vert; GnmBorder const *none = sheet->hide_grid ? NULL : gnm_style_border_none (); GnmRange view; GSList *merged_active, *merged_active_seen, *merged_used, *merged_unused, *ptr, **lag; int *colwidths = NULL; gboolean const draw_selection = ig->scg->selected_objects == NULL && wbcg->new_object == NULL; start_col = gnm_pane_find_col (pane, x0-2, &start_x); end_col = gnm_pane_find_col (pane, x1+2, NULL); g_return_val_if_fail (start_col <= end_col, TRUE); #if 0 g_printerr ("%s:", cell_coord_name (start_col, start_row)); g_printerr ("%s <= %ld vs ???", cell_coord_name(end_col, end_row), (long)y); g_printerr (" [%s]\n", cell_coord_name (ig->bound.end.col, ig->bound.end.row)); #endif /* clip to bounds */ if (end_col > ig->bound.end.col) end_col = ig->bound.end.col; if (end_row > ig->bound.end.row) end_row = ig->bound.end.row; /* Skip any hidden cols/rows at the start */ for (; start_col <= end_col ; ++start_col) { ri = sheet_col_get_info (sheet, start_col); if (ri->visible) break; } for (; start_row <= end_row ; ++start_row) { ri = sheet_row_get_info (sheet, start_row); if (ri->visible) break; } /* if everything is hidden no need to draw */ if (end_col < ig->bound.start.col || start_col > ig->bound.end.col || end_row < ig->bound.start.row || start_row > ig->bound.end.row) return TRUE; /* Respan all rows that need it. */ for (row = start_row; row <= end_row; row++) { ColRowInfo const *ri = sheet_row_get_info (sheet, row); if (ri->visible && ri->needs_respan) row_calc_spans ((ColRowInfo *)ri, row, sheet); } sheet_style_update_grid_color (sheet); /* Fill entire region with default background (even past far edge) */ cairo_save (cr); if (canvas->direction == GOC_DIRECTION_LTR) gtk_render_background (goc_item_get_style_context (item), cr, x0 - canvas->scroll_x1 * scale, y0 - canvas->scroll_y1 * scale, width, height); else gtk_render_background (goc_item_get_style_context (item), cr, canvas->width - x0 + canvas->scroll_x1 * scale - width, y0 - canvas->scroll_y1 * scale, width, height); cairo_restore (cr); /* Get ordered list of merged regions */ merged_active = merged_active_seen = merged_used = NULL; merged_unused = gnm_sheet_merge_get_overlap (sheet, range_init (&view, start_col, start_row, end_col, end_row)); /* * allocate a single blob of memory for all 8 arrays of pointers. * - 6 arrays of n GnmBorder const * * - 2 arrays of n GnmStyle const * * * then alias the arrays for easy access so that array [col] is valid * for all elements start_col-1 .. end_col+1 inclusive. * Note that this means that in some cases array [-1] is legal. */ n = end_col - start_col + 3; /* 1 before, 1 after, 1 fencepost */ style_row_init (&prev_vert, &sr, &next_sr, start_col, end_col, g_alloca (n * 8 * sizeof (gpointer)), sheet->hide_grid); /* load up the styles for the first row */ next_sr.row = sr.row = row = start_row; sheet_style_get_row (sheet, &sr); /* Collect the column widths */ colwidths = g_alloca (n * sizeof (int)); colwidths -= start_col; for (col = start_col; col <= end_col; col++) { ColRowInfo const *ci = sheet_col_get_info (sheet, col); colwidths[col] = ci->visible ? ci->size_pixels : -1; } goc_canvas_c2w (canvas, start_x / scale, 0, &x, NULL); start_x = x; for (y = start_y; row <= end_row; row = sr.row = next_sr.row, ri = next_ri) { /* Restore the set of ranges seen, but still active. * Reinverting list to maintain the original order */ g_return_val_if_fail (merged_active == NULL, TRUE); #if DEBUG_SELECTION_PAINT g_printerr ("row = %d (startcol = %d)\n", row, start_col); #endif while (merged_active_seen != NULL) { GSList *tmp = merged_active_seen->next; merged_active_seen->next = merged_active; merged_active = merged_active_seen; merged_active_seen = tmp; MERGE_DEBUG (merged_active->data, " : seen -> active\n"); } /* find the next visible row */ while (1) { ++next_sr.row; if (next_sr.row <= end_row) { next_ri = sheet_row_get_info (sheet, next_sr.row); if (next_ri->visible) { sheet_style_get_row (sheet, &next_sr); break; } } else { for (col = start_col ; col <= end_col; ++col) next_sr.vertical [col] = next_sr.bottom [col] = none; break; } } /* look for merges that start on this row, on the first painted row * also check for merges that start above. */ view.start.row = row; lag = &merged_unused; for (ptr = merged_unused; ptr != NULL; ) { GnmRange * const r = ptr->data; if (r->start.row <= row) { GSList *tmp = ptr; ptr = *lag = tmp->next; if (r->end.row < row) { tmp->next = merged_used; merged_used = tmp; MERGE_DEBUG (r, " : unused -> used\n"); } else { ColRowInfo const *ci = sheet_col_get_info (sheet, r->start.col); g_slist_free_1 (tmp); merged_active = g_slist_insert_sorted (merged_active, r, (GCompareFunc)merged_col_cmp); MERGE_DEBUG (r, " : unused -> active\n"); if (ci->visible) item_grid_draw_merged_range (cr, ig, start_x, y, &view, r, draw_selection, ctxt); } } else { lag = &(ptr->next); ptr = ptr->next; } } for (col = start_col, x = start_x; col <= end_col ; col++) { GnmStyle const *style; CellSpanInfo const *span; ColRowInfo const *ci = sheet_col_get_info (sheet, col); #if DEBUG_SELECTION_PAINT g_printerr ("col [%d] = %d\n", col, x); #endif if (!ci->visible) { if (merged_active != NULL) { GnmRange const *r = merged_active->data; if (r->end.col == col) { ptr = merged_active; merged_active = merged_active->next; if (r->end.row <= row) { ptr->next = merged_used; merged_used = ptr; MERGE_DEBUG (r, " : active2 -> used\n"); } else { ptr->next = merged_active_seen; merged_active_seen = ptr; MERGE_DEBUG (r, " : active2 -> seen\n"); } } } continue; } /* Skip any merged regions */ if (merged_active != NULL) { GnmRange const *r = merged_active->data; if (r->start.col <= col) { gboolean clear_top, clear_bottom = FALSE; int i, first = r->start.col; int last = r->end.col; ptr = merged_active; merged_active = merged_active->next; if (r->end.row <= row) { ptr->next = merged_used; merged_used = ptr; MERGE_DEBUG (r, " : active -> used\n"); /* in case something managed the bottom of a merge */ if (r->end.row < row) goto plain_draw; } else { ptr->next = merged_active_seen; merged_active_seen = ptr; MERGE_DEBUG (r, " : active -> seen\n"); if (next_sr.row <= r->end.row) clear_bottom = TRUE; } x += dir * scg_colrow_distance_get ( pane->simple.scg, TRUE, col, last+1); col = last; if (first < start_col) { first = start_col; sr.vertical [first] = NULL; } if (last > end_col) { last = end_col; sr.vertical [last+1] = NULL; } clear_top = (r->start.row != row); /* Clear the borders */ for (i = first ; i <= last ; i++) { if (clear_top) sr.top [i] = NULL; if (clear_bottom) sr.bottom [i] = NULL; if (i > first) sr.vertical [i] = NULL; } continue; } } plain_draw : /* a quick hack to deal with 142267 */ if (dir < 0) x -= ci->size_pixels; style = sr.styles [col]; item_grid_draw_background (cr, ig, style, col, row, x, y, ci->size_pixels, ri->size_pixels, draw_selection, ctxt); /* Is this part of a span? * 1) There are cells allocated in the row * (indicated by ri->spans != NULL) * 2) Look in the rows hash table to see if * there is a span descriptor. */ if (NULL == ri->spans || NULL == (span = row_span_get (ri, col))) { /* If it is being edited pretend it is empty to * avoid problems with long cells' * contents extending past the edge of the edit * box. Ignore blanks too. */ GnmCell const *cell = sheet_cell_get (sheet, col, row); if (!gnm_cell_is_empty (cell) && cell != edit_cell) { if (show_function_cell_markers) draw_function_marker (ig, cell, cr, x, y, ci->size_pixels, ri->size_pixels, dir); cell_draw (cell, cr, x, y, ci->size_pixels, ri->size_pixels, -1, show_extension_markers); } /* Only draw spaning cells after all the backgrounds * that we are going to draw have been drawn. No need * to draw the edit cell, or blanks. */ } else if (edit_cell != span->cell && (col == span->right || col == end_col)) { GnmCell const *cell = span->cell; int const start_span_col = span->left; int const end_span_col = span->right; int real_x = x; ColRowInfo const *cell_col = sheet_col_get_info (sheet, cell->pos.col); int center_offset = cell_col->size_pixels/2; int tmp_width = ci->size_pixels; if (col != cell->pos.col) style = sheet_style_get (sheet, cell->pos.col, row); /* x, y are relative to this cell origin, but the cell * might be using columns to the left (if it is set to right * justify or center justify) compute the pixel difference */ if (dir > 0 && start_span_col != cell->pos.col) center_offset += scg_colrow_distance_get ( pane->simple.scg, TRUE, start_span_col, cell->pos.col); else if (dir < 0 && end_span_col != cell->pos.col) center_offset += scg_colrow_distance_get ( pane->simple.scg, TRUE, cell->pos.col, end_span_col); if (start_span_col != col) { offset = scg_colrow_distance_get ( pane->simple.scg, TRUE, start_span_col, col); tmp_width += offset; if (dir > 0) real_x -= offset; sr.vertical [col] = NULL; } if (end_span_col != col) { offset = scg_colrow_distance_get ( pane->simple.scg, TRUE, col+1, end_span_col + 1); tmp_width += offset; if (dir < 0) real_x -= offset; } if (show_function_cell_markers) draw_function_marker (ig, cell, cr, real_x, y, tmp_width, ri->size_pixels, dir); cell_draw (cell, cr, real_x, y, tmp_width, ri->size_pixels, center_offset, show_extension_markers); } else if (col != span->left) sr.vertical [col] = NULL; if (dir > 0) x += ci->size_pixels; } gnm_style_borders_row_draw (prev_vert, &sr, cr, start_x, y, y+ri->size_pixels, colwidths, TRUE, dir); /* In case there were hidden merges that trailed off the end */ while (merged_active != NULL) { GnmRange const *r = merged_active->data; ptr = merged_active; merged_active = merged_active->next; if (r->end.row <= row) { ptr->next = merged_used; merged_used = ptr; MERGE_DEBUG (r, " : active3 -> used\n"); } else { ptr->next = merged_active_seen; merged_active_seen = ptr; MERGE_DEBUG (r, " : active3 -> seen\n"); } } /* roll the pointers */ borders = prev_vert; prev_vert = sr.vertical; sr.vertical = next_sr.vertical; next_sr.vertical = borders; borders = sr.top; sr.top = sr.bottom; sr.bottom = next_sr.top = next_sr.bottom; next_sr.bottom = borders; styles = sr.styles; sr.styles = next_sr.styles; next_sr.styles = styles; y += ri->size_pixels; } if (ig->bound.start.row > 0 && start_y < 1) ig_cairo_draw_bound (ig, cr, start_x, 1, x, 1); if (ig->bound.start.col > 0) { if (canvas->direction == GOC_DIRECTION_RTL && start_x >= goc_canvas_get_width (canvas)) { x = goc_canvas_get_width (canvas); ig_cairo_draw_bound (ig, cr, x, start_y, x, y); } else if (canvas->direction == GOC_DIRECTION_LTR && start_x < 1) ig_cairo_draw_bound (ig, cr, 1, start_y, 1, y); } g_object_ref_sink (entry); g_object_unref (entry); g_slist_free (merged_used); /* merges with bottom in view */ g_slist_free (merged_active_seen); /* merges with bottom the view */ g_slist_free (merged_unused); /* merges in hidden rows */ g_return_val_if_fail (merged_active == NULL, TRUE); return TRUE; }
static int item_grid_button_pressed (GocItem *item, int button, double x_, double y_) { GnmItemGrid *ig = GNM_ITEM_GRID (item); GocCanvas *canvas = item->canvas; GnmPane *pane = GNM_PANE (canvas); SheetControlGUI *scg = ig->scg; WBCGtk *wbcg = scg_wbcg (scg); SheetControl *sc = (SheetControl *)scg; SheetView *sv = sc_view (sc); Sheet *sheet = sv_sheet (sv); GnmCellPos pos; gboolean edit_showed_dialog; gboolean already_selected; GdkEvent *event = goc_canvas_get_cur_event (item->canvas); gint64 x = x_ * canvas->pixels_per_unit, y = y_ * canvas->pixels_per_unit; gnm_pane_slide_stop (pane); pos.col = gnm_pane_find_col (pane, x, NULL); pos.row = gnm_pane_find_row (pane, y, NULL); /* GnmRange check first */ if (pos.col >= gnm_sheet_get_max_cols (sheet)) return TRUE; if (pos.row >= gnm_sheet_get_max_rows (sheet)) return TRUE; /* A new object is ready to be realized and inserted */ if (wbcg->new_object != NULL) return ig_obj_create_begin (ig, button, x, y); /* If we are not configuring an object then clicking on the sheet * ends the edit. */ if (scg->selected_objects == NULL) wbcg_focus_cur_scg (wbcg); else if (wbc_gtk_get_guru (wbcg) == NULL) scg_mode_edit (scg); /* If we were already selecting a range of cells for a formula, * reset the location to a new place, or extend the selection. */ if (button == 1 && scg->rangesel.active) { ig->selecting = GNM_ITEM_GRID_SELECTING_FORMULA_RANGE; if (event->button.state & GDK_SHIFT_MASK) scg_rangesel_extend_to (scg, pos.col, pos.row); else scg_rangesel_bound (scg, pos.col, pos.row, pos.col, pos.row); gnm_pane_slide_init (pane); gnm_simple_canvas_grab (item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, gdk_event_get_time (event)); return TRUE; } /* If the user is editing a formula (wbcg_rangesel_possible) then we * enable the dynamic cell selection mode. */ if (button == 1 && wbcg_rangesel_possible (wbcg)) { scg_rangesel_start (scg, pos.col, pos.row, pos.col, pos.row); ig->selecting = GNM_ITEM_GRID_SELECTING_FORMULA_RANGE; gnm_pane_slide_init (pane); gnm_simple_canvas_grab (item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, gdk_event_get_time (event)); return TRUE; } /* While a guru is up ignore clicks */ if (wbc_gtk_get_guru (wbcg) != NULL) return TRUE; /* This was a regular click on a cell on the spreadsheet. Select it. * but only if the entered expression is valid */ if (!wbcg_edit_finish (wbcg, WBC_EDIT_ACCEPT, &edit_showed_dialog)) return TRUE; if (button == 1 && !sheet_selection_is_allowed (sheet, &pos)) return TRUE; /* Button == 1 is used to trigger hyperlinks (and possibly similar */ /* special cases. Otherwise button == 2 should behave exactly like */ /* button == 1. See bug #700792 */ /* buttons 1 and 2 will always change the selection, the other buttons will * only effect things if the target is not already selected. */ already_selected = sv_is_pos_selected (sv, pos.col, pos.row); if (button == 1 || button == 2 || !already_selected) { if (!(event->button.state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK))) sv_selection_reset (sv); if ((event->button.button != 1 && event->button.button != 2) || !(event->button.state & GDK_SHIFT_MASK) || sv->selections == NULL) { sv_selection_add_pos (sv, pos.col, pos.row, (already_selected && (event->button.state & GDK_CONTROL_MASK)) ? GNM_SELECTION_MODE_REMOVE : GNM_SELECTION_MODE_ADD); sv_make_cell_visible (sv, pos.col, pos.row, FALSE); } else sv_selection_extend_to (sv, pos.col, pos.row); sheet_update (sheet); } if (edit_showed_dialog) return TRUE; /* we already ignored the button release */ switch (button) { case 1: case 2: { guint32 double_click_time; /* * If the second click is on a different cell than the * first one this cannot be a double-click */ if (already_selected) { g_object_get (gtk_widget_get_settings (GTK_WIDGET (canvas)), "gtk-double-click-time", &double_click_time, NULL); if ((ig->last_click_time + double_click_time) > gdk_event_get_time (event) && wbcg_edit_start (wbcg, FALSE, FALSE)) { break; } } ig->last_click_time = gdk_event_get_time (event); ig->selecting = GNM_ITEM_GRID_SELECTING_CELL_RANGE; gnm_pane_slide_init (pane); gnm_simple_canvas_grab (item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, gdk_event_get_time (event)); break; } case 3: scg_context_menu (scg, event, FALSE, FALSE); break; default : break; } return TRUE; }