2 * Grace - GRaphing, Advanced Computation and Exploration of data
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
6 * Copyright (c) 1996-2005 Grace Development Team
8 * Maintained by Evgeny Stambulchik
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
39 /* default cell value precision */
42 /* default cell value format */
43 #define CELL_FORMAT FORMAT_GENERAL
45 /* default cell width */
48 /* string cell width */
49 #define STRING_CELL_WIDTH 128
51 /* minimum size of the spreadsheet matrix */
52 #define EXTRA_SS_ROWS 20
53 #define EXTRA_SS_COLS 3
55 #define VISIBLE_SS_ROWS 18
56 #define VISIBLE_SS_COLS 3
58 static int do_hotlinkfile_proc(FSBStructure
*fsb
, char *filename
, void *data
)
60 SSDataUI
*ui
= (SSDataUI
*) data
;
62 TextSetString(ui
->hotfile
, filename
);
68 * create file selection pop up to choose the file for hotlink
70 static void create_hotfiles_popup(Widget but
, void *data
)
72 static FSBStructure
*fsb
= NULL
;
77 fsb
= CreateFSBDialog(app_shell
, "Hotlinked file");
78 AddFSBDialogCB(fsb
, do_hotlinkfile_proc
, data
);
79 FSBDialogSetPattern(fsb
, "*.dat");
80 WidgetManage(fsb
->FSB
);
83 DialogRaise(fsb
->FSB
);
89 * We use a stack of static buffers to work around asynchronous
90 * refresh/redraw events
92 #define STACKLEN (VISIBLE_SS_ROWS*VISIBLE_SS_COLS)
94 static char *get_cell_content(SSDataUI
*ui
, int row
, int column
, int *format
)
96 static char buf
[STACKLEN
][32];
97 static int stackp
= 0;
99 int nrows
= ssd_get_nrows(ui
->q
);
100 ss_column
*col
= ssd_get_col(ui
->q
, column
);
103 if (col
&& row
>= 0 && row
< nrows
) {
105 *format
= col
->format
;
106 switch (col
->format
) {
108 s
= ((char **) col
->data
)[row
];
111 prec
= project_get_prec(get_parent_project(ui
->q
));
112 sprintf(buf
[stackp
], "%.*g", prec
, ((double *) col
->data
)[row
]);
117 /* get rid of spaces */
118 while (s
&& *s
== ' ') {
131 static int drawcellCB(TableEvent
*event
)
133 SSDataUI
*ui
= (SSDataUI
*) event
->anydata
;
136 event
->value_type
= TABLE_CELL_STRING
;
137 event
->value
= get_cell_content(ui
, event
->row
, event
->col
, &format
);
142 static int enterCB(TableEvent
*event
)
144 SSDataUI
*ui
= (SSDataUI
*) event
->anydata
;
146 int ncols
= ssd_get_ncols(ui
->q
);
148 if (event
->col
>= 0 && event
->col
<= ncols
) {
149 TableDeselectAllCells(ui
->mw
);
156 static int leaveCB(TableEvent
*event
)
158 SSDataUI
*ui
= (SSDataUI
*) event
->anydata
;
160 int nrows
= ssd_get_nrows(ui
->q
);
161 int ncols
= ssd_get_ncols(ui
->q
);
167 GraceApp
*gapp
= gapp_from_quark(ui
->q
);
169 if (event
->row
< 0 || event
->col
< 0 || event
->col
> ncols
) {
173 if (event
->row
>= nrows
&& !string_is_empty(event
->value
)) {
174 if (ssd_set_nrows(ui
->q
, event
->row
+ 1) == RETURN_SUCCESS
) {
179 if (event
->col
== ncols
&& !string_is_empty(event
->value
)) {
180 if (parse_date_or_number(get_parent_project(ui
->q
),
181 event
->value
, FALSE
, get_date_hint(gapp
), &value
) == RETURN_SUCCESS
) {
182 format
= FFORMAT_NUMBER
;
184 format
= FFORMAT_STRING
;
186 if (ssd_add_col(ui
->q
, format
)) {
192 if (event
->col
< ncols
) {
193 char *old_value
= get_cell_content(ui
, event
->row
, event
->col
, &format
);
194 if (!strings_are_equal(old_value
, event
->value
)) {
197 if (ssd_set_string(ui
->q
, event
->row
, event
->col
, event
->value
) ==
199 quark_dirtystate_set(ui
->q
, TRUE
);
204 if (graal_eval_expr(grace_get_graal(gapp
->grace
),
205 event
->value
, &value
, gproject_get_top(gapp
->gp
)) == RETURN_SUCCESS
) {
211 prec
= project_get_prec(get_parent_project(ui
->q
));
212 sprintf(buf
, "%.*g", prec
, value
);
214 if (parse_date_or_number(get_parent_project(ui
->q
),
215 buf
, FALSE
, get_date_hint(gapp
), &val
) == RETURN_SUCCESS
) {
217 if (ssd_set_value(ui
->q
, event
->row
, event
->col
, val
) == RETURN_SUCCESS
) {
218 quark_dirtystate_set(ui
->q
, TRUE
);
223 errmsg("Can't parse input value");
232 snapshot_and_update(gapp
->gp
, FALSE
);
238 static int labelCB(TableEvent
*event
)
240 SSDataUI
*ui
= (SSDataUI
*) event
->anydata
;
241 static int last_row
, last_column
;
244 if (!event
|| event
->type
!= MOUSE_PRESS
) {
248 if (event
->button
== LEFT_BUTTON
) {
249 TableCommitEdit(ui
->mw
, TRUE
);
251 if (event
->row_label
) {
252 if (event
->modifiers
& CONTROL_MODIFIER
) {
253 if (TableIsRowSelected(ui
->mw
, event
->row
)) {
254 TableDeselectRow(ui
->mw
, event
->row
);
256 TableSelectRow(ui
->mw
, event
->row
);
258 last_row
= event
->row
;
260 if ((event
->modifiers
& SHIFT_MODIFIER
) && last_row
>= 0) {
261 for (i
= MIN2(last_row
, event
->row
); i
<= MAX2(last_row
, event
->row
); i
++) {
262 TableSelectRow(ui
->mw
, i
);
265 TableDeselectAllCells(ui
->mw
);
266 TableSelectRow(ui
->mw
, event
->row
);
267 last_row
= event
->row
;
272 if (event
->modifiers
& CONTROL_MODIFIER
) {
273 if (TableIsColSelected(ui
->mw
, event
->col
)) {
274 TableDeselectCol(ui
->mw
, event
->col
);
276 TableSelectCol(ui
->mw
, event
->col
);
278 last_column
= event
->col
;
280 if ((event
->modifiers
& SHIFT_MODIFIER
) && last_column
>= 0) {
281 for (i
= MIN2(last_column
, event
->col
); i
<= MAX2(last_column
, event
->col
); i
++) {
282 TableSelectCol(ui
->mw
, i
);
285 TableDeselectAllCells(ui
->mw
);
286 TableSelectCol(ui
->mw
, event
->col
);
287 last_column
= event
->col
;
294 if (event
->button
== RIGHT_BUTTON
) {
296 if (!event
->row_label
) {
297 ui
->cb_column
= event
->col
;
300 col
= ssd_get_col(ui
->q
, ui
->cb_column
);
301 WidgetSetSensitive(ui
->delete_btn
, col
!= NULL
);
302 WidgetSetSensitive(ui
->index_btn
, col
!= NULL
&& ui
->cb_column
!= 0 &&
303 (col
->format
== FFORMAT_NUMBER
|| col
->format
== FFORMAT_DATE
));
304 WidgetSetSensitive(ui
->unindex_btn
, ui
->cb_column
== 0 && col
!= NULL
&&
305 ssd_is_indexed(ui
->q
));
307 PopupMenuShow(ui
->popup
, event
->udata
);
313 static void col_delete_cb(Widget but
, void *udata
)
315 SSDataUI
*ui
= (SSDataUI
*) udata
;
316 if (ssd_delete_col(ui
->q
, ui
->cb_column
) == RETURN_SUCCESS
) {
317 snapshot_and_update(gapp
->gp
, TRUE
);
321 static void index_cb(Widget but
, void *udata
)
323 SSDataUI
*ui
= (SSDataUI
*) udata
;
324 if (ssd_set_index(ui
->q
, ui
->cb_column
) == RETURN_SUCCESS
) {
325 snapshot_and_update(gapp
->gp
, TRUE
);
329 static void unindex_cb(Widget but
, void *udata
)
331 SSDataUI
*ui
= (SSDataUI
*) udata
;
332 if (ssd_set_indexed(ui
->q
, FALSE
) == RETURN_SUCCESS
) {
333 snapshot_and_update(gapp
->gp
, TRUE
);
337 static void col_cb(ListStructure
*sel
, int n
, int *values
, void *data
)
339 SSDataUI
*ui
= (SSDataUI
*) data
;
340 Quark
*ssd
= (Quark
*) sel
->anydata
;
344 WidgetSetSensitive(ui
->col_label
->text
, TRUE
);
345 TextSetString(ui
->col_label
, ssd_get_col_label(ssd
, col
));
347 WidgetSetSensitive(ui
->col_label
->text
, FALSE
);
351 SSDataUI
*create_ssd_ui(ExplorerUI
*eui
)
355 Widget tab
, fr
, rc
, rc1
, wbut
;
357 ui
= xmalloc(sizeof(SSDataUI
));
361 memset(ui
, 0, sizeof(SSDataUI
));
363 /* ------------ Tabs -------------- */
365 tab
= CreateTab(eui
->scrolled_window
);
366 AddHelpCB(tab
, "doc/UsersGuide.html#ssd-properties");
368 /* ------------ Main tab -------------- */
369 ui
->main_tp
= CreateTabPage(tab
, "Data");
371 ui
->mw
= CreateTable("SSD", ui
->main_tp
,
372 EXTRA_SS_ROWS
, EXTRA_SS_COLS
,
373 VISIBLE_SS_ROWS
, VISIBLE_SS_COLS
);
374 TableSSDInit(ui
->mw
);
375 TableSetDefaultColWidth(ui
->mw
, CELL_WIDTH
);
376 TableSetDefaultColLabelAlignment(ui
->mw
, ALIGN_CENTER
);
378 AddTableDrawCellCB(ui
->mw
, drawcellCB
, ui
);
379 AddTableLeaveCellCB(ui
->mw
, leaveCB
, ui
);
380 AddTableEnterCellCB(ui
->mw
, enterCB
, ui
);
381 AddTableLabelActivateCB(ui
->mw
, labelCB
, ui
);
383 ui
->popup
= CreatePopupMenu(ui
->mw
);
384 ui
->delete_btn
= CreateMenuButton(ui
->popup
, "Delete column", '\0', col_delete_cb
, ui
);
385 ui
->index_btn
= CreateMenuButton(ui
->popup
, "Set as index", '\0', index_cb
, ui
);
386 ui
->unindex_btn
= CreateMenuButton(ui
->popup
, "Unset index", '\0', unindex_cb
, ui
);
389 /* ------------ Column props -------------- */
390 ui
->column_tp
= CreateTabPage(tab
, "Columns");
391 ui
->col_sel
= CreateColChoice(ui
->column_tp
, "Column:", LIST_TYPE_SINGLE
);
392 AddListChoiceCB(ui
->col_sel
, col_cb
, ui
);
394 ui
->col_label
= CreateCSText(ui
->column_tp
, "Label:");
395 WidgetSetSensitive(ui
->col_label
->text
, FALSE
);
396 AddTextActivateCB(ui
->col_label
, text_explorer_cb
, eui
);
399 /* ------------ Hotlink tab -------------- */
400 ui
->hotlink_tp
= CreateTabPage(tab
, "Hotlink");
402 fr
= CreateFrame(ui
->hotlink_tp
, "Hotlink");
403 rc
= CreateVContainer(fr
);
404 rc1
= CreateHContainer(rc
);
405 ui
->hotlink
= CreateToggleButton(rc1
, "Enabled");
406 ui
->hotsrc
= CreateOptionChoiceVA(rc1
, "Source type:",
410 rc1
= CreateHContainer(rc
);
411 ui
->hotfile
= CreateText2(rc1
, "File name:", 20);
412 wbut
= CreateButton(rc1
, "Browse...");
413 AddButtonCB(wbut
, create_hotfiles_popup
, ui
);
415 SelectTabPage(tab
, ui
->main_tp
);
422 void update_ssd_ui(SSDataUI
*ui
, Quark
*q
)
425 int i
, nc
, nr
, new_nc
, new_nr
, ncols
, nrows
, nfixed_cols
;
426 int delta_nc
, delta_nr
;
428 char **rowlabels
, **collabels
;
431 TableDeselectAllCells(ui
->mw
);
436 ncols
= ssd_get_ncols(q
);
437 nrows
= ssd_get_nrows(q
);
439 new_nc
= ncols
+ EXTRA_SS_COLS
;
440 new_nr
= nrows
+ EXTRA_SS_ROWS
;
442 if (ssd_is_indexed(q
)) {
448 nr
= TableGetNrows(ui
->mw
);
449 nc
= TableGetNcols(ui
->mw
);
451 delta_nr
= new_nr
- nr
;
452 delta_nc
= new_nc
- nc
;
455 TableAddRows(ui
->mw
, delta_nr
);
456 } else if (delta_nr
< 0) {
457 TableDeleteRows(ui
->mw
, -delta_nr
);
460 rowlabels
= xmalloc(new_nr
*sizeof(char *));
461 for (i
= 0; i
< new_nr
; i
++) {
463 sprintf(buf
, "%d", i
+ 1);
464 rowlabels
[i
] = copy_string(NULL
, buf
);
466 TableSetRowLabels(ui
->mw
, rowlabels
);
467 for (i
= 0; i
< new_nr
; i
++) {
472 maxlengths
= xmalloc(new_nc
*SIZEOF_INT
);
473 collabels
= xmalloc(new_nc
*sizeof(char *));
475 for (i
= 0; i
< new_nc
; i
++) {
476 ss_column
*col
= ssd_get_col(q
, i
);
477 if (col
&& col
->format
== FFORMAT_STRING
) {
478 maxlengths
[i
] = STRING_CELL_WIDTH
;
480 maxlengths
[i
] = 2*CELL_WIDTH
;
482 if (col
&& !string_is_empty(col
->label
)) {
483 collabels
[i
] = copy_string(NULL
, col
->label
);
489 sprintf(buf
, "%c", coli
%26 + 'A');
490 while ((coli
/= 26)) {
491 memmove(&buf
[1], buf
, strlen(buf
) + 1);
492 buf
[0] = coli
%26 + 'A' - 1;
495 collabels
[i
] = copy_string(NULL
, buf
);
500 TableAddCols(ui
->mw
, delta_nc
);
501 } else if (delta_nc
< 0) {
502 TableDeleteCols(ui
->mw
, -delta_nc
);
505 TableSetColMaxlengths(ui
->mw
, maxlengths
);
506 TableSetColLabels(ui
->mw
, collabels
);
507 TableSetFixedCols(ui
->mw
, nfixed_cols
);
508 TableUpdateVisibleRowsCols(ui
->mw
);
511 for (i
= 0; i
< new_nc
; i
++) {
516 UpdateColChoice(ui
->col_sel
, q
);
520 int set_ssd_data(SSDataUI
*ui
, Quark
*q
, void *caller
)
522 int retval
= RETURN_SUCCESS
;
526 /* commit the last entered cell changes */
527 TableCommitEdit(ui
->mw
, FALSE
);
530 if (!caller
|| caller
== ui
->col_label
) {
532 if (GetSingleListChoice(ui
->col_sel
, &col
) == RETURN_SUCCESS
) {
533 char *s
= TextGetString(ui
->col_label
);
534 ssd_set_col_label(q
, col
, s
);
537 /* FIXME: this is an overkill */
538 update_ssd_ui(ui
, q
);