Commit a3506bf7 authored by Jody Goldberg's avatar Jody Goldberg

- Disable insert/delete row/column that would split an array-formula

- Enable cut/copy/paste for array formulas.
- Fix multi-line cell drawing & whitespace issues.
- Change the default column width & grid colour to more closely match XL
- Add the edit popup menu in the row/col headers
- Short circuit the insert/delete dialogs to directly insert/delete when
  an entire row/column is selected.
parent 05ee5519
1999-07-11 Jody Goldberg <jgoldberg@home.com>
* src/item-grid.c (context_cut_cmd, context_copy_cmd context_paste_cmd,
context_paste_special_cmd, context_insert_cmd, context_delete_cmd,
context_clear_cmd, context_cell_format_cmd, create_popup_menu,
item_grid_popup_menu) : Operate on Sheet* rather than ItemGrid*.
* src/item-bar.c (item_bar_event) : Popup an item_grid_popup_menu
when Button3 is pressed. Excel also adds an entry to change
col widths/row heights, that will be next TODO.
* src/dialog-insert-cells.c, src/dialog-delete-cells.c :
Short circuit the menus when an entire row/column is selected. This
fixes the problem of selecting a column and accidentaly inserting a
row would clear the sheet inserting MAX_ROW rows and erasing
everything ...
* src/cell-draw.c (cell_draw) : Remove use of str_trim_spaces and
correct the bounding rectangle size for multiline entries.
(cell_split_text) : Rework logic to strip spaces when a forced split
causes a linebreak @ those spaces. Plus more comments.
* src/print-cell.c (cell_split_text, print_cell_text) : Ditto.
Switch accumulators to doubles.
* src/parser.y : Moved dump_value from here.
* src/expr.c : to here. And changed the format of CellRanges
to be more appealing.
* src/cell.c (cell_relocate) : Add some cruft to handle relinking
the corner cell of an array formula.
* src/clipboard.c (clipboard_prepend_cell) : Renamed from
clipboard_append_cell. So that there is truth in naming.
(clipboard_copy_cell_range) : Reverse the result of
clipboard_prepend_cell so that the upper left corner is 1st.
(Sheet *sheet, int col, int row, Cell *cell, void *user_data)
* src/expr.c (eval_range) : New function to ensure that range
arguments are recalculated.
(eval_expr) : Use it.
* src/sheet.c (range_check_for_partial_array) : New function. To avoid
subdividing arrays does some obvious optimizations.
(sheet_fill_selection_with) : Split out from here.
(sheet_delete_col, sheet_delete_row) : Used here too.
1999-07-11 Michael Meeks <michael@edenproject.org>
* Sort ChangeLog out, apologies.
......@@ -31,4 +78,4 @@
* src/expr.h: Add eval_sheet to help all over the place.
* src/eval.c (add_cell_range_deps): Use eval_sheet, and make
warning more correct.
\ No newline at end of file
warning more correct.
1999-07-11 Jody Goldberg <jgoldberg@home.com>
* src/item-grid.c (context_cut_cmd, context_copy_cmd context_paste_cmd,
context_paste_special_cmd, context_insert_cmd, context_delete_cmd,
context_clear_cmd, context_cell_format_cmd, create_popup_menu,
item_grid_popup_menu) : Operate on Sheet* rather than ItemGrid*.
* src/item-bar.c (item_bar_event) : Popup an item_grid_popup_menu
when Button3 is pressed. Excel also adds an entry to change
col widths/row heights, that will be next TODO.
* src/dialog-insert-cells.c, src/dialog-delete-cells.c :
Short circuit the menus when an entire row/column is selected. This
fixes the problem of selecting a column and accidentaly inserting a
row would clear the sheet inserting MAX_ROW rows and erasing
everything ...
* src/cell-draw.c (cell_draw) : Remove use of str_trim_spaces and
correct the bounding rectangle size for multiline entries.
(cell_split_text) : Rework logic to strip spaces when a forced split
causes a linebreak @ those spaces. Plus more comments.
* src/print-cell.c (cell_split_text, print_cell_text) : Ditto.
Switch accumulators to doubles.
* src/parser.y : Moved dump_value from here.
* src/expr.c : to here. And changed the format of CellRanges
to be more appealing.
* src/cell.c (cell_relocate) : Add some cruft to handle relinking
the corner cell of an array formula.
* src/clipboard.c (clipboard_prepend_cell) : Renamed from
clipboard_append_cell. So that there is truth in naming.
(clipboard_copy_cell_range) : Reverse the result of
clipboard_prepend_cell so that the upper left corner is 1st.
(Sheet *sheet, int col, int row, Cell *cell, void *user_data)
* src/expr.c (eval_range) : New function to ensure that range
arguments are recalculated.
(eval_expr) : Use it.
* src/sheet.c (range_check_for_partial_array) : New function. To avoid
subdividing arrays does some obvious optimizations.
(sheet_fill_selection_with) : Split out from here.
(sheet_delete_col, sheet_delete_row) : Used here too.
1999-07-11 Michael Meeks <michael@edenproject.org>
* Sort ChangeLog out, apologies.
......@@ -31,4 +78,4 @@
* src/expr.h: Add eval_sheet to help all over the place.
* src/eval.c (add_cell_range_deps): Use eval_sheet, and make
warning more correct.
\ No newline at end of file
warning more correct.
......@@ -34,81 +34,68 @@ draw_overflow (GdkDrawable *drawable, GdkGC *gc, GdkFont *font, int x1, int y1,
}
}
/*
* WARNING : This code is an almost exact duplicate of
* print-cell.c:cell_split_text
* Try to keep it that way.
*/
static GList *
cell_split_text (GdkFont *font, char const *text, int const width)
{
GList *list;
char const *p, *line_begin, *ideal_cut_spot = NULL;
int line_len, used;
gboolean last_was_cut_point = FALSE;
char const *p, *line_begin;
char const *first_whitespace = NULL;
char const *last_whitespace = NULL;
gboolean prev_was_space = FALSE;
GList *list = NULL;
int used = 0, used_last_space = 0;
list = NULL;
used = 0;
for (line_begin = p = text; *p; p++){
int len;
/* If there is an embeded return honour it */
if (*p == '\n'){
int const line_len = p - line_begin;
char *line = g_malloc (line_len + 1);
memcpy (line, line_begin, line_len);
line [line_len] = '\0';
list = g_list_append (list, line);
used = 0;
line_begin = p+1; /* skip the newline */
ideal_cut_spot = NULL;
last_was_cut_point = FALSE;
continue;
}
if (last_was_cut_point && *p != ' ')
ideal_cut_spot = p;
int const len_current = gdk_text_width (font, p, 1);
len = gdk_text_width (font, p, 1);
/* If we have overflowed, do the wrap */
if (used + len > width){
/* Wrap if there is an embeded newline, or we have overflowed */
if (*p == '\n' || used + len_current > width){
char const *begin = line_begin;
char *line;
if (ideal_cut_spot){
int const n = p - ideal_cut_spot + 1;
line_len = ideal_cut_spot - line_begin;
used = gdk_text_width (font, ideal_cut_spot, n);
line_begin = ideal_cut_spot;
int len;
if (*p == '\n'){
/* start after newline, preserve whitespace */
line_begin = p+1;
len = p - begin;
used = 0;
} else if (last_whitespace != NULL){
/* Split at the run of whitespace */
line_begin = last_whitespace + 1;
len = first_whitespace - begin;
used = len_current + used - used_last_space;
} else {
/* Split BEFORE this character */
used = len;
line_len = p - line_begin;
line_begin = p;
/* Split before the current character */
line_begin = p; /* next line starts here */
len = p - begin;
used = len_current;
}
line = g_malloc (line_len + 1);
memcpy (line, begin, line_len);
line [line_len] = 0;
list = g_list_append (list, line);
ideal_cut_spot = NULL;
} else
used += len;
list = g_list_append (list, g_strndup (begin, len));
first_whitespace = last_whitespace = NULL;
prev_was_space = FALSE;
continue;
}
if (*p == ' ')
last_was_cut_point = TRUE;
else
last_was_cut_point = FALSE;
}
if (*line_begin){
char *line;
line_len = p - line_begin;
line = g_malloc (line_len+1);
memcpy (line, line_begin, line_len);
line [line_len] = 0;
list = g_list_append (list, line);
used += len_current;
if (*p == ' '){
used_last_space = used;
last_whitespace = p;
if (!prev_was_space)
first_whitespace = p;
prev_was_space = TRUE;
} else
prev_was_space = FALSE;
}
/* Catch the final bit that did not wrap */
if (*line_begin)
list = g_list_append (list,
g_strndup (line_begin, p - line_begin));
return list;
}
......@@ -177,10 +164,10 @@ cell_draw (Cell *cell, SheetView *sheet_view, GdkGC *gc, GdkDrawable *drawable,
lines = cell_split_text (font, text, width);
line_count = g_list_length (lines);
rect.x = x1;
rect.y = y1;
rect.height = cell->row->pixels + 1;
rect.width = cell->col->pixels + 1;
rect.x = x1 + 1;
rect.y = y1 + 1;
rect.height = cell->row->pixels - 1;
rect.width = cell->col->pixels - 1;
gdk_gc_set_clip_rectangle (gc, &rect);
gdk_draw_rectangle (drawable, gc, TRUE,
......@@ -225,11 +212,8 @@ cell_draw (Cell *cell, SheetView *sheet_view, GdkGC *gc, GdkDrawable *drawable,
y_offset += font_height - 1;
for (l = lines; l; l = l->next){
char *str = l->data;
char const * const str = l->data;
/* Why do we need this. it breaks multi-line indents */
str = str_trim_spaces (str);
switch (halign){
case HALIGN_LEFT:
case HALIGN_JUSTIFY:
......
......@@ -9,6 +9,7 @@
#include <locale.h>
#include "gnumeric.h"
#include "gnumeric-sheet.h"
#include "gnumeric-util.h"
#include "eval.h"
#include "format.h"
#include "color.h"
......@@ -79,8 +80,7 @@ cell_set_formula (Cell *cell, const char *text)
if (new_expr->oper == OPER_ARRAY){
/* The corner sets up the entire array block */
if (new_expr->u.array.x != 0 || new_expr->u.array.y != 0)
{
if (new_expr->u.array.x != 0 || new_expr->u.array.y != 0) {
expr_tree_unref (new_expr);
return;
}
......@@ -455,27 +455,23 @@ cell_set_color_from_style (Cell *cell, StyleColor *foreground,
cell_modified (cell);
if (cell->style->valid_flags & STYLE_FORE_COLOR)
{
if (cell->style->valid_flags & STYLE_FORE_COLOR) {
cell->style->valid_flags ^= STYLE_FORE_COLOR;
style_color_unref (cell->style->fore_color);
}
if (cell->style->valid_flags & STYLE_BACK_COLOR)
{
if (cell->style->valid_flags & STYLE_BACK_COLOR) {
cell->style->valid_flags ^= STYLE_BACK_COLOR;
style_color_unref (cell->style->back_color);
}
if (background)
{
if (background) {
cell->style->valid_flags |= STYLE_BACK_COLOR;
style_color_ref (background);
}
cell->style->back_color = background;
if (foreground)
{
if (foreground) {
cell->style->valid_flags |= STYLE_FORE_COLOR;
style_color_ref (foreground);
}
......@@ -729,8 +725,7 @@ cell_set_text (Cell *cell, const char *text)
g_return_if_fail (cell != NULL);
g_return_if_fail (text != NULL);
if (cell->parsed_node != NULL && cell->parsed_node->oper == OPER_ARRAY)
{
if (cell->parsed_node != NULL && cell->parsed_node->oper == OPER_ARRAY) {
gnumeric_no_modify_array_notice (cell->sheet->workbook);
return;
}
......@@ -819,8 +814,7 @@ cell_set_array_formula (Sheet *sheet,
cell_unqueue_from_recalc (corner);
for (x = 0; x < num_cols; ++x)
for (y = 0; y < num_rows; ++y)
{
for (y = 0; y < num_rows; ++y) {
if (x == 0 && y == 0)
continue;
cell = sheet_cell_fetch (sheet, col_a+x,row_a+y);
......@@ -1116,6 +1110,26 @@ cell_relocate (Cell *cell, int col_diff, int row_diff)
/* 2. If the cell contains a formula, relocate the formula */
if (cell->parsed_node){
sheet_cell_formula_unlink (cell);
/*
* WARNING WARNING WARNING
*
* This will will only work if the new array cell has already
* been inserted.
*
* WARNING WARNING WARNING
*/
/* If cell was part of an array, reset the corner pointer */
if (cell->parsed_node->oper == OPER_ARRAY) {
int const x = cell->parsed_node->u.array.x;
int const y = cell->parsed_node->u.array.y;
if (x != 0 || y != 0)
cell->parsed_node->u.array.corner.cell =
sheet_cell_get (cell->sheet,
cell->col->pos - x,
cell->row->pos - y);
}
/* The following call also relinks the cell. */
cell_formula_changed (cell);
}
......@@ -1522,17 +1536,17 @@ cell_get_content (Cell *cell)
return str;
}
char *
cell_get_comment (Cell *cell)
{
char *str;
g_return_val_if_fail (cell != NULL, NULL);
if (cell->comment)
str = g_strdup (cell->comment->comment->str);
else
str = NULL;
return str;
}
char *
cell_get_comment (Cell *cell)
{
char *str;
g_return_val_if_fail (cell != NULL, NULL);
if (cell->comment)
str = g_strdup (cell->comment->comment->str);
else
str = NULL;
return str;
}
......@@ -436,7 +436,7 @@ typedef struct {
} append_cell_closure_t;
static int
clipboard_append_cell (Sheet *sheet, int col, int row, Cell *cell, void *user_data)
clipboard_prepend_cell (Sheet *sheet, int col, int row, Cell *cell, void *user_data)
{
append_cell_closure_t *c = user_data;
CellCopy *copy;
......@@ -477,7 +477,10 @@ clipboard_copy_cell_range (Sheet *sheet, int start_col, int start_row, int end_c
sheet_cell_foreach_range (
sheet, 1, start_col, start_row, end_col, end_row,
clipboard_append_cell, &c);
clipboard_prepend_cell, &c);
/* reverse the list so that upper left corner is first */
c.r->list = g_list_reverse (c.r->list);
clipboard_export_cell_region (sheet->workbook);
......
......@@ -65,7 +65,7 @@ color_init (void)
gdk_color_white (colormap, &gs_white);
gdk_color_black (colormap, &gs_black);
color_alloc_name ("gray60", &gs_light_gray);
color_alloc_name ("gray78", &gs_light_gray);
color_alloc_name ("gray20", &gs_dark_gray);
color_alloc_name ("red", &gs_red);
......
......@@ -25,6 +25,22 @@ dialog_delete_cells (Workbook *wb, Sheet *sheet)
if (!sheet_verify_selection_simple (sheet, _("delete cells")))
return;
ss = sheet->selections->data;
cols = ss->end_col - ss->start_col + 1;
rows = ss->end_row - ss->start_row + 1;
/* short circuit the dialog if an entire row/column is selected */
if (ss->start_row == 0 && ss->end_row >= SHEET_MAX_ROWS-1)
{
sheet_delete_col (sheet, ss->start_col, cols);
return;
}
if (ss->start_col == 0 && ss->end_col >= SHEET_MAX_COLS-1)
{
sheet_delete_row (sheet, ss->start_row, rows);
return;
}
ret = gtk_dialog_cauldron (
_("Delete cells"),
GTK_CAULDRON_DIALOG,
......@@ -43,10 +59,6 @@ dialog_delete_cells (Workbook *wb, Sheet *sheet)
if (strcmp (ret, GNOME_STOCK_BUTTON_CANCEL) == 0)
return;
ss = sheet->selections->data;
cols = ss->end_col - ss->start_col + 1;
rows = ss->end_row - ss->start_row + 1;
if (state [0]){
sheet_shift_rows (sheet, ss->start_col, ss->start_row, ss->end_row, -cols);
return;
......
/*
* dialog-insert-cells.c: Insert a number of cells.
* dialog-insert-cells.c: Insert a number of cells.
*
* Author:
* Miguel de Icaza (miguel@gnu.org)
......@@ -22,10 +22,26 @@ dialog_insert_cells (Workbook *wb, Sheet *sheet)
g_return_if_fail (wb != NULL);
g_return_if_fail (sheet != NULL);
g_return_if_fail (IS_SHEET (sheet));
if (!sheet_verify_selection_simple (sheet, _("insert cells")))
return;
ss = sheet->selections->data;
cols = ss->end_col - ss->start_col + 1;
rows = ss->end_row - ss->start_row + 1;
/* short circuit the dialog if an entire row/column is selected */
if (ss->start_row == 0 && ss->end_row >= SHEET_MAX_ROWS-1)
{
sheet_insert_col (sheet, ss->start_col, cols);
return;
}
if (ss->start_col == 0 && ss->end_col >= SHEET_MAX_COLS-1)
{
sheet_insert_row (sheet, ss->start_row, rows);
return;
}
ret = gtk_dialog_cauldron (
_("Insert cells"),
GTK_CAULDRON_DIALOG,
......@@ -40,14 +56,10 @@ dialog_insert_cells (Workbook *wb, Sheet *sheet)
if (ret == NULL)
return;
if (strcmp (ret, GNOME_STOCK_BUTTON_CANCEL) == 0)
return;
ss = sheet->selections->data;
cols = ss->end_col - ss->start_col + 1;
rows = ss->end_row - ss->start_row + 1;
if (state [0]){
sheet_shift_rows (sheet, ss->start_col, ss->start_row, ss->end_row, cols);
return;
......
......@@ -25,6 +25,22 @@ dialog_delete_cells (Workbook *wb, Sheet *sheet)
if (!sheet_verify_selection_simple (sheet, _("delete cells")))
return;
ss = sheet->selections->data;
cols = ss->end_col - ss->start_col + 1;
rows = ss->end_row - ss->start_row + 1;
/* short circuit the dialog if an entire row/column is selected */
if (ss->start_row == 0 && ss->end_row >= SHEET_MAX_ROWS-1)
{
sheet_delete_col (sheet, ss->start_col, cols);
return;
}
if (ss->start_col == 0 && ss->end_col >= SHEET_MAX_COLS-1)
{
sheet_delete_row (sheet, ss->start_row, rows);
return;
}
ret = gtk_dialog_cauldron (
_("Delete cells"),
GTK_CAULDRON_DIALOG,
......@@ -43,10 +59,6 @@ dialog_delete_cells (Workbook *wb, Sheet *sheet)
if (strcmp (ret, GNOME_STOCK_BUTTON_CANCEL) == 0)
return;
ss = sheet->selections->data;
cols = ss->end_col - ss->start_col + 1;
rows = ss->end_row - ss->start_row + 1;
if (state [0]){
sheet_shift_rows (sheet, ss->start_col, ss->start_row, ss->end_row, -cols);
return;
......
/*
* dialog-insert-cells.c: Insert a number of cells.
* dialog-insert-cells.c: Insert a number of cells.
*
* Author:
* Miguel de Icaza (miguel@gnu.org)
......@@ -22,10 +22,26 @@ dialog_insert_cells (Workbook *wb, Sheet *sheet)
g_return_if_fail (wb != NULL);
g_return_if_fail (sheet != NULL);
g_return_if_fail (IS_SHEET (sheet));
if (!sheet_verify_selection_simple (sheet, _("insert cells")))
return;
ss = sheet->selections->data;
cols = ss->end_col - ss->start_col + 1;
rows = ss->end_row - ss->start_row + 1;
/* short circuit the dialog if an entire row/column is selected */
if (ss->start_row == 0 && ss->end_row >= SHEET_MAX_ROWS-1)
{
sheet_insert_col (sheet, ss->start_col, cols);
return;
}
if (ss->start_col == 0 && ss->end_col >= SHEET_MAX_COLS-1)
{
sheet_insert_row (sheet, ss->start_row, rows);
return;
}
ret = gtk_dialog_cauldron (
_("Insert cells"),
GTK_CAULDRON_DIALOG,
......@@ -40,14 +56,10 @@ dialog_insert_cells (Workbook *wb, Sheet *sheet)
if (ret == NULL)
return;
if (strcmp (ret, GNOME_STOCK_BUTTON_CANCEL) == 0)
return;
ss = sheet->selections->data;
cols = ss->end_col - ss->start_col + 1;
rows = ss->end_row - ss->start_row + 1;
if (state [0]){
sheet_shift_rows (sheet, ss->start_col, ss->start_row, ss->end_row, cols);
return;
......
......@@ -1002,14 +1002,12 @@ value_area_get_x_y (EvalPosition const *ep, Value const *v, guint x, guint y)
b_row += ep->eval_row;
/* Handle inverted refereneces */
if (a_row > b_row)
{
if (a_row > b_row) {
int tmp = a_row;
a_row = b_row;
b_row = tmp;
}
if (a_col > b_col)
{
if (a_col > b_col) {
int tmp = a_col;
a_col = b_col;
b_col = tmp;
......@@ -1392,6 +1390,48 @@ compare (const Value *a, const Value *b)
return TYPE_ERROR;
}
/*
* Utility routine to ensure that all elements of a range are recalced as
* necessary.
*/
static void
eval_range (FunctionEvalInfo *s, Value *v)
{
int start_col, start_row, end_col, end_row;
CellRef * a = &v->v.cell_range.cell_a;
CellRef * b = &v->v.cell_range.cell_b;
Sheet * sheet = a->sheet ? a->sheet : s->pos.sheet;
Cell * cell;
int r, c;
int const gen = s->pos.sheet->workbook->generation;
cell_get_abs_col_row (a,
s->pos.eval_col, s->pos.eval_row,
&start_col, &start_row);
cell_get_abs_col_row (b,
s->pos.eval_col, s->pos.eval_row,
&end_col, &end_row);
if (a->sheet != b->sheet) {
g_warning ("3D references not-fully supported.\n"
"Recalc may be incorrect");
return;
}
for (r = start_row; r <= end_row; ++r)
for (c = start_col; c <= end_col; ++c) {
if ((cell = sheet_cell_get (sheet, c, r)) == NULL)
continue;
if (cell->generation != gen) {
cell->generation = gen;
if (cell->parsed_node &&
(cell->flags & CELL_QUEUED_FOR_RECALC))
cell_eval (cell);
}
}
}
Value *
eval_expr (FunctionEvalInfo *s, ExprTree *tree)
{
......@@ -1658,7 +1698,10 @@ eval_expr (FunctionEvalInfo *s, ExprTree *tree)
}
case OPER_CONSTANT:
return value_duplicate (tree->u.constant);
res = tree->u.constant;
if (res->type == VALUE_CELLRANGE)
eval_range (s, res);
return value_duplicate (res);
case OPER_NEG:
a = eval_expr (s, tree->u.value);
......@@ -1687,8 +1730,7 @@ eval_expr (FunctionEvalInfo *s, ExprTree *tree)
if (tree->u.array.corner.func.value != NULL)
value_release (tree->u.array.corner.func.value);
tree->u.array.corner.func.value = a;
} else
{
} else {
ExprTree const * const array =
expr_tree_array_formula_corner (tree);
if (array)
......@@ -1927,11 +1969,9 @@ do_expr_decode_tree (ExprTree *tree, const EvalPosition *fp, int paren_level)
int const x = tree->u.array.x;
int const y = tree->u.array.y;
char *res = "<ERROR>";
if (x != 0 || y != 0)
{
if (x != 0 || y != 0) {
ExprTree *array = expr_tree_array_formula_corner (tree);
if (array)