static void qpro_parse_formula (QProReadState *state, int col, int row, guint8 const *data, guint8 const *end) { guint16 magic, ref_offset; #if 0 int flags = GSF_LE_GET_GUINT16 (data + 8); int length = GSF_LE_GET_GUINT16 (data + 10); #endif GnmValue *val; GSList *stack = NULL; GnmExprTop const *texpr = NULL; guint8 const *refs, *fmla; #ifdef DEBUG_MISSING dump_missing_functions (); #endif Q_CHECK_CONDITION (end - data >= 14); magic = GSF_LE_GET_GUINT16 (data + 6) & 0x7ff8; ref_offset = GSF_LE_GET_GUINT16 (data + 12); fmla = data + 14; refs = fmla + ref_offset; Q_CHECK_CONDITION (refs <= end); #if 0 puts (cell_coord_name (col, row)); gsf_mem_dump (data, 14); gsf_mem_dump (fmla, refs-fmla); gsf_mem_dump (refs, end-refs); #endif while (fmla < refs && *fmla != QPRO_OP_EOF) { QProOperators op = *fmla++; GnmExpr const *expr = NULL; #if 0 g_print ("Operator %d.\n", op); #endif switch (op) { case QPRO_OP_CONST_FLOAT: Q_CHECK_CONDITION (refs - fmla >= 8); expr = gnm_expr_new_constant (value_new_float ( gsf_le_get_double (fmla))); fmla += 8; break; case QPRO_OP_CELLREF: { GnmCellRef ref; guint16 tmp; Q_CHECK_CONDITION (end - refs >= 6); tmp = GSF_LE_GET_GUINT16 (refs + 4); ref.sheet = NULL; ref.col = *((gint8 *)(refs + 2)); ref.col_relative = (tmp & 0x4000) ? TRUE : FALSE; ref.row_relative = (tmp & 0x2000) ? TRUE : FALSE; if (ref.row_relative) ref.row = (int)(((gint16)((tmp & 0x1fff) << 3)) >> 3); else ref.row = tmp & 0x1fff; expr = gnm_expr_new_cellref (&ref); refs += 6; break; } case QPRO_OP_RANGEREF: { GnmCellRef a, b; guint16 tmp; Q_CHECK_CONDITION (end - refs >= 10); tmp = GSF_LE_GET_GUINT16 (refs + 4); a.sheet = NULL; a.col = *((gint8 *)(refs + 2)); a.col_relative = (tmp & 0x4000) ? TRUE : FALSE; a.row_relative = (tmp & 0x2000) ? TRUE : FALSE; if (a.row_relative) a.row = (int)(((gint16)((tmp & 0x1fff) << 3)) >> 3); else a.row = tmp & 0x1fff; tmp = GSF_LE_GET_GUINT16 (refs + 8); b.sheet = NULL; b.col = *((gint8 *)(refs + 6)); b.col_relative = (tmp & 0x4000) ? TRUE : FALSE; b.row_relative = (tmp & 0x2000) ? TRUE : FALSE; if (b.row_relative) b.row = (int)(((gint16)((tmp & 0x1fff) << 3)) >> 3); else b.row = tmp & 0x1fff; expr = gnm_expr_new_constant ( value_new_cellrange_unsafe (&a, &b)); refs += 10; break; }
/** * wbcg_edit_start: * @wbcg: The workbook to be edited. * @blankp: If true, erase current cell contents first. If false, leave the * contents alone. * @cursorp: If true, create an editing cursor in the current sheet. (If * false, the text will be editing in the edit box above the sheet, * but this is not handled by this function.) * * Initiate editing of a cell in the sheet. Note that we have two modes of * editing: * 1) in-cell editing when you just start typing, and * 2) above sheet editing when you hit F2. * * Returns TRUE if we did indeed start editing. Returns FALSE if the * cell-to-be-edited was locked. */ gboolean wbcg_edit_start (WBCGtk *wbcg, gboolean blankp, gboolean cursorp) { /* We could save this, but the situation is rare, if confusing. */ static gboolean warn_on_text_format = TRUE; SheetView *sv; SheetControlGUI *scg; GnmCell *cell; char *text = NULL; int col, row; WorkbookView *wbv; int cursor_pos = -1; g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg), FALSE); if (wbcg_is_editing (wbcg)) return TRUE; /* Avoid recursion, and do not begin editing if a guru is up */ if (wbcg->inside_editing || wbc_gtk_get_guru (wbcg) != NULL) return TRUE; wbcg->inside_editing = TRUE; wbv = wb_control_view (GNM_WBC (wbcg)); sv = wb_control_cur_sheet_view (GNM_WBC (wbcg)); scg = wbcg_cur_scg (wbcg); col = sv->edit_pos.col; row = sv->edit_pos.row; /* don't edit a locked cell */ /* TODO : extend this to disable edits that cannot succeed * like editing a single cell of an array. I think we have enough * information if we look at the selection. */ if (wb_view_is_protected (wbv, TRUE) && gnm_style_get_contents_locked (sheet_style_get (sv->sheet, col, row))) { char *pos = g_strdup_printf ( _("%s!%s is locked"), sv->sheet->name_quoted, cell_coord_name (col, row)); go_cmd_context_error_invalid (GO_CMD_CONTEXT (wbcg), pos, wb_view_is_protected (wbv, FALSE) ? _("Unprotect the workbook to enable editing.") : _("Unprotect the sheet to enable editing.")); wbcg->inside_editing = FALSE; g_free (pos); return FALSE; } cell = sheet_cell_get (sv->sheet, col, row); if (cell && warn_on_text_format && go_format_is_text (gnm_cell_get_format (cell)) && (gnm_cell_has_expr (cell) || !VALUE_IS_STRING (cell->value))) { gint res; /* Using GtkResponseType would yield a warning on the switch */ GtkWidget *check; GtkWidget *align; GtkWidget *d = gnm_message_dialog_create (wbcg_toplevel (wbcg), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, _("You are about to edit a cell with \"text\" format."), _("The cell does not currently contain text, though, so if " "you go on editing then the contents will be turned into " "text.")); gtk_dialog_add_button (GTK_DIALOG (d), GTK_STOCK_EDIT, GTK_RESPONSE_OK); go_gtk_dialog_add_button (GTK_DIALOG (d), _("Remove format"), GTK_STOCK_REMOVE, GNM_RESPONSE_REMOVE); gtk_dialog_add_button (GTK_DIALOG (d), GNM_STOCK_CANCEL, GTK_RESPONSE_CANCEL); gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_CANCEL); check = gtk_check_button_new_with_label (_("Show this dialog next time.")); g_signal_connect (check, "toggled", G_CALLBACK (cb_warn_toggled), &warn_on_text_format); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE); align = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_container_add (GTK_CONTAINER (align), check); gtk_widget_show_all (align); gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), align, TRUE, TRUE, 0); res = go_gtk_dialog_run (GTK_DIALOG (d), wbcg_toplevel (wbcg)); switch (res) { case GNM_RESPONSE_REMOVE: { GnmStyle *style = gnm_style_new (); gnm_style_set_format (style, go_format_general ()); if (!cmd_selection_format (GNM_WBC (wbcg), style, NULL, NULL)) break; /* Fall through. */ } default: case GTK_RESPONSE_CANCEL: wbcg->inside_editing = FALSE; return FALSE; case GTK_RESPONSE_OK: break; } } gnm_app_clipboard_unant (); if (blankp) gtk_entry_set_text (wbcg_get_entry (wbcg), ""); else if (cell != NULL) { gboolean quoted = FALSE; text = gnm_cell_get_text_for_editing (cell, sv->sheet, "ed, &cursor_pos); if (text) gtk_entry_set_text (wbcg_get_entry (wbcg), text); if (cell->value != NULL) { GOFormat const *fmt = VALUE_FMT (cell->value); if (fmt != NULL && go_format_is_markup (fmt)) { PangoAttrList *markup = pango_attr_list_copy ((PangoAttrList *)go_format_get_markup (fmt)); if (quoted) go_pango_attr_list_open_hole (markup, 0, 1); wbcg_edit_init_markup (wbcg, markup); } } } gnm_expr_entry_set_scg (wbcg->edit_line.entry, scg); gnm_expr_entry_set_flags (wbcg->edit_line.entry, GNM_EE_SHEET_OPTIONAL | GNM_EE_FORMULA_ONLY, GNM_EE_SINGLE_RANGE | GNM_EE_SHEET_OPTIONAL | GNM_EE_FORMULA_ONLY | GNM_EE_FORCE_REL_REF | GNM_EE_FORCE_ABS_REF); scg_edit_start (scg); /* Redraw the cell contents in case there was a span */ sheet_redraw_region (sv->sheet, col, row, col, row); if (cursorp && /* autocompletion code will not work in the edit line */ wbv->do_auto_completion && (text == NULL || g_unichar_isalpha (g_utf8_get_char (text)))) { wbcg->auto_complete = gnm_complete_sheet_new ( sv->sheet, col, row, workbook_edit_complete_notify, wbcg); wbcg->auto_completing = TRUE; wbcg->auto_max_size = 0; } else wbcg->auto_complete = NULL; /* Give the focus to the edit line */ if (!cursorp) gtk_window_set_focus (wbcg_toplevel (wbcg), (GtkWidget *) wbcg_get_entry (wbcg)); wbcg->editing = TRUE; wbcg->editing_sheet = sv->sheet; wbcg->editing_cell = cell; /* If this assert fails, it means editing was not shut down * properly before */ g_return_val_if_fail (wbcg->edit_line.signal_changed == 0, TRUE); wbcg->edit_line.signal_changed = g_signal_connect ( G_OBJECT (wbcg_get_entry (wbcg)), "changed", G_CALLBACK (cb_entry_changed), wbcg); wbcg->edit_line.signal_insert = g_signal_connect ( G_OBJECT (wbcg_get_entry (wbcg)), "insert-text", G_CALLBACK (cb_entry_insert_text), wbcg); wbcg->edit_line.signal_delete = g_signal_connect ( G_OBJECT (wbcg_get_entry (wbcg)), "delete-text", G_CALLBACK (cb_entry_delete_text), wbcg); wbcg->edit_line.signal_cursor_pos = g_signal_connect_swapped ( G_OBJECT (wbcg_get_entry (wbcg)), "notify::cursor-position", G_CALLBACK (cb_entry_cursor_pos), wbcg); wbcg->edit_line.signal_selection_bound = g_signal_connect_swapped ( G_OBJECT (wbcg_get_entry (wbcg)), "notify::selection-bound", G_CALLBACK (cb_entry_cursor_pos), wbcg); g_free (text); wb_control_update_action_sensitivity (GNM_WBC (wbcg)); wbcg->inside_editing = FALSE; gtk_editable_set_position (GTK_EDITABLE (wbcg_get_entry (wbcg)), cursor_pos); return TRUE; }
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; }