Set set length on assignment.
[grace.git] / src / ssd_ui.c
blob8e64cc382474234f9813d1b35be6e645f2385260
1 /*
2 * Grace - GRaphing, Advanced Computation and Exploration of data
3 *
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
5 *
6 * Copyright (c) 1996-2005 Grace Development Team
7 *
8 * Maintained by Evgeny Stambulchik
9 *
11 * All Rights Reserved
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.
28 /* SSData UI */
30 #include <stdlib.h>
31 #include <string.h>
33 #include "events.h"
34 #include "utils.h"
35 #include "explorer.h"
36 #include "xprotos.h"
37 #include "globals.h"
39 /* default cell value precision */
40 #define CELL_PREC 8
42 /* default cell value format */
43 #define CELL_FORMAT FORMAT_GENERAL
45 /* default cell width */
46 #define CELL_WIDTH 12
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);
64 return TRUE;
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;
74 set_wait_cursor();
76 if (fsb == NULL) {
77 fsb = CreateFileSelectionBox(app_shell, "Hotlinked file");
78 AddFileSelectionBoxCB(fsb, do_hotlinkfile_proc, data);
79 ManageChild(fsb->FSB);
82 RaiseWindow(fsb->dialog);
84 unset_wait_cursor();
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);
100 char *s;
102 if (col && row >= 0 && row < nrows) {
103 unsigned int prec;
104 *format = col->format;
105 switch (col->format) {
106 case FFORMAT_STRING:
107 s = ((char **) col->data)[row];
108 break;
109 default:
110 prec = project_get_prec(get_parent_project(ui->q));
111 sprintf(buf[stackp], "%.*g", prec, ((double *) col->data)[row]);
112 s = buf[stackp];
113 stackp++;
114 stackp %= STACKLEN;
116 /* get rid of spaces */
117 while (s && *s == ' ') {
118 s++;
121 break;
123 } else {
124 s = "";
127 return s;
130 static int drawcellCB(TableEvent *event)
132 SSDataUI *ui = (SSDataUI *) event->anydata;
133 int format;
135 event->value_type = TABLE_CELL_STRING;
136 event->value = get_cell_content(ui, event->row, event->col, &format);
138 return TRUE;
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);
149 return TRUE;
150 } else {
151 return FALSE;
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);
161 int format;
162 double value;
164 int changed = FALSE;
166 GraceApp *gapp = gapp_from_quark(ui->q);
168 if (event->row < 0 || event->col < 0 || event->col > ncols) {
169 return TRUE;
172 if (event->row >= nrows && !string_is_empty(event->value)) {
173 if (ssd_set_nrows(ui->q, event->row + 1) == RETURN_SUCCESS) {
174 changed = TRUE;
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;
182 } else {
183 format = FFORMAT_STRING;
185 if (ssd_add_col(ui->q, format)) {
186 ncols++;
187 changed = TRUE;
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)) {
194 switch (format) {
195 case FFORMAT_STRING:
196 if (ssd_set_string(ui->q, event->row, event->col, event->value) ==
197 RETURN_SUCCESS) {
198 quark_dirtystate_set(ui->q, TRUE);
199 changed = TRUE;
201 break;
202 default:
203 if (graal_eval_expr(grace_get_graal(gapp->grace),
204 event->value, &value, gproject_get_top(gapp->gp)) == RETURN_SUCCESS) {
206 unsigned int prec;
207 char buf[32];
208 double val;
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);
218 changed = TRUE;
221 } else {
222 errmsg("Can't parse input value");
223 return FALSE;
225 break;
230 if (changed) {
231 snapshot_and_update(gapp->gp, FALSE);
234 return TRUE;
237 static int labelCB(TableEvent *event)
239 SSDataUI *ui = (SSDataUI *) event->anydata;
240 static int last_row, last_column;
241 int i;
243 if (!event || event->type != MOUSE_PRESS) {
244 return TRUE;
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);
254 } else {
255 TableSelectRow(ui->mw, event->row);
257 last_row = event->row;
258 } else
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);
263 } else {
264 TableDeselectAllCells(ui->mw);
265 TableSelectRow(ui->mw, event->row);
266 last_row = event->row;
269 last_column = -1;
270 } else {
271 if (event->modifiers & CONTROL_MODIFIER) {
272 if (TableIsColSelected(ui->mw, event->col)) {
273 TableDeselectCol(ui->mw, event->col);
274 } else {
275 TableSelectCol(ui->mw, event->col);
277 last_column = event->col;
278 } else
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);
283 } else {
284 TableDeselectAllCells(ui->mw);
285 TableSelectCol(ui->mw, event->col);
286 last_column = event->col;
289 last_row = -1;
293 if (event->button == RIGHT_BUTTON) {
294 ss_column *col;
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);
309 return TRUE;
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;
341 if (ssd && n == 1) {
342 int col = values[0];
343 SetSensitive(ui->col_label->text, TRUE);
344 SetTextString(ui->col_label, ssd_get_col_label(ssd, col));
345 } else {
346 SetSensitive(ui->col_label->text, FALSE);
350 SSDataUI *create_ssd_ui(ExplorerUI *eui)
352 SSDataUI *ui;
354 Widget tab, fr, rc, rc1, wbut;
356 ui = xmalloc(sizeof(SSDataUI));
357 if (!ui) {
358 return NULL;
360 memset(ui, 0, sizeof(SSDataUI));
362 /* ------------ Tabs -------------- */
364 tab = CreateTab(eui->scrolled_window);
365 AddHelpCB(tab, "doc/UsersGuide.html#ssd-properties");
367 ui->top = tab;
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:",
408 "Disk", SOURCE_DISK,
409 "Pipe", SOURCE_PIPE,
410 NULL);
411 rc1 = CreateHContainer(rc);
412 ui->hotfile = CreateTextItem(rc1, 20, "File name:");
413 wbut = CreateButton(rc1, "Browse...");
414 AddButtonCB(wbut, create_hotfiles_popup, ui);
416 return ui;
419 void update_ssd_ui(SSDataUI *ui, Quark *q)
421 if (ui && q) {
422 int i, nc, nr, new_nc, new_nr, ncols, nrows, nfixed_cols;
423 int delta_nc, delta_nr;
424 int *maxlengths;
425 char **rowlabels, **collabels;
427 if (ui->q != q) {
428 TableDeselectAllCells(ui->mw);
431 ui->q = q;
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)) {
440 nfixed_cols = 1;
441 } else {
442 nfixed_cols = 0;
445 nr = TableGetNrows(ui->mw);
446 nc = TableGetNcols(ui->mw);
448 delta_nr = new_nr - nr;
449 delta_nc = new_nc - nc;
451 if (delta_nr > 0) {
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++) {
459 char buf[32];
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++) {
465 xfree(rowlabels[i]);
467 xfree(rowlabels);
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;
476 } else {
477 maxlengths[i] = 2*CELL_WIDTH;
479 if (col && !string_is_empty(col->label)) {
480 collabels[i] = copy_string(NULL, col->label);
481 } else {
482 unsigned int coli;
483 char buf[32];
485 coli = i;
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);
496 if (delta_nc > 0) {
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);
507 xfree(maxlengths);
508 for (i = 0; i < new_nc; i++) {
509 xfree(collabels[i]);
511 xfree(collabels);
513 UpdateColChoice(ui->col_sel, q);
517 int set_ssd_data(SSDataUI *ui, Quark *q, void *caller)
519 int retval = RETURN_SUCCESS;
521 if (ui && q) {
522 if (!caller) {
523 /* commit the last entered cell changes */
524 TableCommitEdit(ui->mw, FALSE);
527 if (!caller || caller == ui->col_label) {
528 int col;
529 if (GetSingleListChoice(ui->col_sel, &col) == RETURN_SUCCESS) {
530 char *s = GetTextString(ui->col_label);
531 ssd_set_col_label(q, col, s);
532 xfree(s);
534 /* FIXME: this is an overkill */
535 update_ssd_ui(ui, q);
540 return retval;