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 xv_setstr(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
= CreateFileSelectionBox(app_shell
, "Hotlinked file");
78 AddFileSelectionBoxCB(fsb
, do_hotlinkfile_proc
, data
);
79 ManageChild(fsb
->FSB
);
82 RaiseWindow(fsb
->dialog
);
88 * We use a stack of static buffers to work around asynchronous
89 * refresh/redraw events
91 #define STACKLEN (VISIBLE_SS_ROWS*VISIBLE_SS_COLS)
93 static char *get_cell_content(SSDataUI
*ui
, int row
, int column
, int *format
)
95 static char buf
[STACKLEN
][32];
96 static int stackp
= 0;
98 int nrows
= ssd_get_nrows(ui
->q
);
99 ss_column
*col
= ssd_get_col(ui
->q
, column
);
102 if (col
&& row
>= 0 && row
< nrows
) {
104 *format
= col
->format
;
105 switch (col
->format
) {
107 s
= ((char **) col
->data
)[row
];
110 prec
= project_get_prec(get_parent_project(ui
->q
));
111 sprintf(buf
[stackp
], "%.*g", prec
, ((double *) col
->data
)[row
]);
116 /* get rid of spaces */
117 while (s
&& *s
== ' ') {
130 static int drawcellCB(TableEvent
*event
)
132 SSDataUI
*ui
= (SSDataUI
*) event
->anydata
;
135 event
->value_type
= TABLE_CELL_STRING
;
136 event
->value
= get_cell_content(ui
, event
->row
, event
->col
, &format
);
141 static int enterCB(TableEvent
*event
)
143 SSDataUI
*ui
= (SSDataUI
*) event
->anydata
;
145 int ncols
= ssd_get_ncols(ui
->q
);
147 if (event
->col
>= 0 && event
->col
<= ncols
) {
148 TableDeselectAllCells(ui
->mw
);
155 static int leaveCB(TableEvent
*event
)
157 SSDataUI
*ui
= (SSDataUI
*) event
->anydata
;
159 int nrows
= ssd_get_nrows(ui
->q
);
160 int ncols
= ssd_get_ncols(ui
->q
);
166 GraceApp
*gapp
= gapp_from_quark(ui
->q
);
168 if (event
->row
< 0 || event
->col
< 0 || event
->col
> ncols
) {
172 if (event
->row
>= nrows
&& !string_is_empty(event
->value
)) {
173 if (ssd_set_nrows(ui
->q
, event
->row
+ 1) == RETURN_SUCCESS
) {
178 if (event
->col
== ncols
&& !string_is_empty(event
->value
)) {
179 if (parse_date_or_number(get_parent_project(ui
->q
),
180 event
->value
, FALSE
, get_date_hint(gapp
), &value
) == RETURN_SUCCESS
) {
181 format
= FFORMAT_NUMBER
;
183 format
= FFORMAT_STRING
;
185 if (ssd_add_col(ui
->q
, format
)) {
191 if (event
->col
< ncols
) {
192 char *old_value
= get_cell_content(ui
, event
->row
, event
->col
, &format
);
193 if (!strings_are_equal(old_value
, event
->value
)) {
196 if (ssd_set_string(ui
->q
, event
->row
, event
->col
, event
->value
) ==
198 quark_dirtystate_set(ui
->q
, TRUE
);
203 if (graal_eval_expr(grace_get_graal(gapp
->grace
),
204 event
->value
, &value
, gproject_get_top(gapp
->gp
)) == RETURN_SUCCESS
) {
210 prec
= project_get_prec(get_parent_project(ui
->q
));
211 sprintf(buf
, "%.*g", prec
, value
);
213 if (parse_date_or_number(get_parent_project(ui
->q
),
214 buf
, FALSE
, get_date_hint(gapp
), &val
) == RETURN_SUCCESS
) {
216 if (ssd_set_value(ui
->q
, event
->row
, event
->col
, val
) == RETURN_SUCCESS
) {
217 quark_dirtystate_set(ui
->q
, TRUE
);
222 errmsg("Can't parse input value");
231 snapshot_and_update(gapp
->gp
, FALSE
);
237 static int labelCB(TableEvent
*event
)
239 SSDataUI
*ui
= (SSDataUI
*) event
->anydata
;
240 static int last_row
, last_column
;
243 if (!event
|| event
->type
!= MOUSE_PRESS
) {
247 if (event
->button
== LEFT_BUTTON
) {
248 TableCommitEdit(ui
->mw
, TRUE
);
250 if (event
->row_label
) {
251 if (event
->modifiers
& CONTROL_MODIFIER
) {
252 if (TableIsRowSelected(ui
->mw
, event
->row
)) {
253 TableDeselectRow(ui
->mw
, event
->row
);
255 TableSelectRow(ui
->mw
, event
->row
);
257 last_row
= event
->row
;
259 if ((event
->modifiers
& SHIFT_MODIFIER
) && last_row
>= 0) {
260 for (i
= MIN2(last_row
, event
->row
); i
<= MAX2(last_row
, event
->row
); i
++) {
261 TableSelectRow(ui
->mw
, i
);
264 TableDeselectAllCells(ui
->mw
);
265 TableSelectRow(ui
->mw
, event
->row
);
266 last_row
= event
->row
;
271 if (event
->modifiers
& CONTROL_MODIFIER
) {
272 if (TableIsColSelected(ui
->mw
, event
->col
)) {
273 TableDeselectCol(ui
->mw
, event
->col
);
275 TableSelectCol(ui
->mw
, event
->col
);
277 last_column
= event
->col
;
279 if ((event
->modifiers
& SHIFT_MODIFIER
) && last_column
>= 0) {
280 for (i
= MIN2(last_column
, event
->col
); i
<= MAX2(last_column
, event
->col
); i
++) {
281 TableSelectCol(ui
->mw
, i
);
284 TableDeselectAllCells(ui
->mw
);
285 TableSelectCol(ui
->mw
, event
->col
);
286 last_column
= event
->col
;
293 if (event
->button
== RIGHT_BUTTON
) {
295 if (!event
->row_label
) {
296 ui
->cb_column
= event
->col
;
299 col
= ssd_get_col(ui
->q
, ui
->cb_column
);
300 SetSensitive(ui
->delete_btn
, col
!= NULL
);
301 SetSensitive(ui
->index_btn
, col
!= NULL
&& ui
->cb_column
!= 0 &&
302 (col
->format
== FFORMAT_NUMBER
|| col
->format
== FFORMAT_DATE
));
303 SetSensitive(ui
->unindex_btn
, ui
->cb_column
== 0 && col
!= NULL
&&
304 ssd_is_indexed(ui
->q
));
306 ShowMenu(ui
->popup
, event
->udata
);
312 static void col_delete_cb(Widget but
, void *udata
)
314 SSDataUI
*ui
= (SSDataUI
*) udata
;
315 if (ssd_delete_col(ui
->q
, ui
->cb_column
) == RETURN_SUCCESS
) {
316 snapshot_and_update(gapp
->gp
, TRUE
);
320 static void index_cb(Widget but
, void *udata
)
322 SSDataUI
*ui
= (SSDataUI
*) udata
;
323 if (ssd_set_index(ui
->q
, ui
->cb_column
) == RETURN_SUCCESS
) {
324 snapshot_and_update(gapp
->gp
, TRUE
);
328 static void unindex_cb(Widget but
, void *udata
)
330 SSDataUI
*ui
= (SSDataUI
*) udata
;
331 if (ssd_set_indexed(ui
->q
, FALSE
) == RETURN_SUCCESS
) {
332 snapshot_and_update(gapp
->gp
, TRUE
);
336 static void col_cb(ListStructure
*sel
, int n
, int *values
, void *data
)
338 SSDataUI
*ui
= (SSDataUI
*) data
;
339 Quark
*ssd
= (Quark
*) sel
->anydata
;
343 SetSensitive(ui
->col_label
->text
, TRUE
);
344 SetTextString(ui
->col_label
, ssd_get_col_label(ssd
, col
));
346 SetSensitive(ui
->col_label
->text
, FALSE
);
350 SSDataUI
*create_ssd_ui(ExplorerUI
*eui
)
354 Widget tab
, fr
, rc
, rc1
, wbut
;
356 ui
= xmalloc(sizeof(SSDataUI
));
360 memset(ui
, 0, sizeof(SSDataUI
));
362 /* ------------ Tabs -------------- */
364 tab
= CreateTab(eui
->scrolled_window
);
365 AddHelpCB(tab
, "doc/UsersGuide.html#ssd-properties");
369 /* ------------ Main tab -------------- */
370 ui
->main_tp
= CreateTabPage(tab
, "Data");
372 ui
->mw
= CreateTable("SSD", ui
->main_tp
,
373 EXTRA_SS_ROWS
, EXTRA_SS_COLS
,
374 VISIBLE_SS_ROWS
, VISIBLE_SS_COLS
);
375 TableSSDInit(ui
->mw
);
376 TableSetDefaultColWidth(ui
->mw
, CELL_WIDTH
);
377 TableSetDefaultColLabelAlignment(ui
->mw
, ALIGN_CENTER
);
379 AddTableDrawCellCB(ui
->mw
, drawcellCB
, ui
);
380 AddTableLeaveCellCB(ui
->mw
, leaveCB
, ui
);
381 AddTableEnterCellCB(ui
->mw
, enterCB
, ui
);
382 AddTableLabelActivateCB(ui
->mw
, labelCB
, ui
);
384 ui
->popup
= CreatePopupMenu(ui
->mw
);
385 ui
->delete_btn
= CreateMenuButton(ui
->popup
, "Delete column", '\0', col_delete_cb
, ui
);
386 ui
->index_btn
= CreateMenuButton(ui
->popup
, "Set as index", '\0', index_cb
, ui
);
387 ui
->unindex_btn
= CreateMenuButton(ui
->popup
, "Unset index", '\0', unindex_cb
, ui
);
390 /* ------------ Column props -------------- */
391 ui
->column_tp
= CreateTabPage(tab
, "Columns");
392 ui
->col_sel
= CreateColChoice(ui
->column_tp
, "Column:", LIST_TYPE_SINGLE
);
393 AddListChoiceCB(ui
->col_sel
, col_cb
, ui
);
395 ui
->col_label
= CreateCSText(ui
->column_tp
, "Label:");
396 SetSensitive(ui
->col_label
->text
, FALSE
);
397 AddTextInputCB(ui
->col_label
, text_explorer_cb
, eui
);
400 /* ------------ Hotlink tab -------------- */
401 ui
->hotlink_tp
= CreateTabPage(tab
, "Hotlink");
403 fr
= CreateFrame(ui
->hotlink_tp
, "Hotlink");
404 rc
= CreateVContainer(fr
);
405 rc1
= CreateHContainer(rc
);
406 ui
->hotlink
= CreateToggleButton(rc1
, "Enabled");
407 ui
->hotsrc
= CreateOptionChoiceVA(rc1
, "Source type:",
411 rc1
= CreateHContainer(rc
);
412 ui
->hotfile
= CreateTextItem(rc1
, 20, "File name:");
413 wbut
= CreateButton(rc1
, "Browse...");
414 AddButtonCB(wbut
, create_hotfiles_popup
, ui
);
419 void update_ssd_ui(SSDataUI
*ui
, Quark
*q
)
422 int i
, nc
, nr
, new_nc
, new_nr
, ncols
, nrows
, nfixed_cols
;
423 int delta_nc
, delta_nr
;
425 char **rowlabels
, **collabels
;
428 TableDeselectAllCells(ui
->mw
);
433 ncols
= ssd_get_ncols(q
);
434 nrows
= ssd_get_nrows(q
);
436 new_nc
= ncols
+ EXTRA_SS_COLS
;
437 new_nr
= nrows
+ EXTRA_SS_ROWS
;
439 if (ssd_is_indexed(q
)) {
445 nr
= TableGetNrows(ui
->mw
);
446 nc
= TableGetNcols(ui
->mw
);
448 delta_nr
= new_nr
- nr
;
449 delta_nc
= new_nc
- nc
;
452 TableAddRows(ui
->mw
, delta_nr
);
453 } else if (delta_nr
< 0) {
454 TableDeleteRows(ui
->mw
, -delta_nr
);
457 rowlabels
= xmalloc(new_nr
*sizeof(char *));
458 for (i
= 0; i
< new_nr
; i
++) {
460 sprintf(buf
, "%d", i
+ 1);
461 rowlabels
[i
] = copy_string(NULL
, buf
);
463 TableSetRowLabels(ui
->mw
, rowlabels
);
464 for (i
= 0; i
< new_nr
; i
++) {
469 maxlengths
= xmalloc(new_nc
*SIZEOF_INT
);
470 collabels
= xmalloc(new_nc
*sizeof(char *));
472 for (i
= 0; i
< new_nc
; i
++) {
473 ss_column
*col
= ssd_get_col(q
, i
);
474 if (col
&& col
->format
== FFORMAT_STRING
) {
475 maxlengths
[i
] = STRING_CELL_WIDTH
;
477 maxlengths
[i
] = 2*CELL_WIDTH
;
479 if (col
&& !string_is_empty(col
->label
)) {
480 collabels
[i
] = copy_string(NULL
, col
->label
);
486 sprintf(buf
, "%c", coli
%26 + 'A');
487 while ((coli
/= 26)) {
488 memmove(&buf
[1], buf
, strlen(buf
) + 1);
489 buf
[0] = coli
%26 + 'A' - 1;
492 collabels
[i
] = copy_string(NULL
, buf
);
497 TableAddCols(ui
->mw
, delta_nc
);
498 } else if (delta_nc
< 0) {
499 TableDeleteCols(ui
->mw
, -delta_nc
);
502 TableSetColMaxlengths(ui
->mw
, maxlengths
);
503 TableSetColLabels(ui
->mw
, collabels
);
504 TableSetFixedCols(ui
->mw
, nfixed_cols
);
505 TableUpdateVisibleRowsCols(ui
->mw
);
508 for (i
= 0; i
< new_nc
; i
++) {
513 UpdateColChoice(ui
->col_sel
, q
);
517 int set_ssd_data(SSDataUI
*ui
, Quark
*q
, void *caller
)
519 int retval
= RETURN_SUCCESS
;
523 /* commit the last entered cell changes */
524 TableCommitEdit(ui
->mw
, FALSE
);
527 if (!caller
|| caller
== ui
->col_label
) {
529 if (GetSingleListChoice(ui
->col_sel
, &col
) == RETURN_SUCCESS
) {
530 char *s
= GetTextString(ui
->col_label
);
531 ssd_set_col_label(q
, col
, s
);
534 /* FIXME: this is an overkill */
535 update_ssd_ui(ui
, q
);