1 /*
2 * gretl -- Gnu Regression, Econometrics and Time-series Library
3 * Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 /* treeutils.c for gretl */
21
22 #include "gretl.h"
23 #include "treeutils.h"
24 #include "datafiles.h"
25 #include "dlgutils.h"
26 #include "menustate.h"
27
28 static void tree_view_get_string2 (GtkTreeView *view,
29 GtkTreePath *path,
30 int col, gchar **val);
31
32 /* special comparator which always preserves "const" (variable 0) in
33 the first position when sorting variables by name */
34
list_alpha_compare(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer p)35 static gint list_alpha_compare (GtkTreeModel *model,
36 GtkTreeIter *a, GtkTreeIter *b,
37 gpointer p)
38 {
39 gchar *str_a, *str_b;
40 gchar *id_a, *id_b;
41 GtkSortType order;
42 gint scol, ret;
43
44 gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model),
45 &scol, &order);
46
47 gtk_tree_model_get(model, a, 0, &id_a, scol, &str_a, -1);
48 gtk_tree_model_get(model, b, 0, &id_b, scol, &str_b, -1);
49
50 if (atoi(id_a) == 0) {
51 ret = (order == GTK_SORT_DESCENDING)? 1 : 0;
52 } else if (atoi(id_b) == 0) {
53 ret = (order == GTK_SORT_DESCENDING)? 0 : 1;
54 } else {
55 ret = strcmp(str_a, str_b);
56 }
57
58 g_free(id_a);
59 g_free(id_b);
60
61 g_free(str_a);
62 g_free(str_b);
63
64 return ret;
65 }
66
67 /* special comparator which preserves 0 in first position when sorting
68 variables by ID number */
69
list_id_compare(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer p)70 static gint list_id_compare (GtkTreeModel *model,
71 GtkTreeIter *a, GtkTreeIter *b,
72 gpointer p)
73 {
74 gchar *id_a, *id_b;
75 GtkSortType order;
76 gint ia, ib, scol, ret;
77
78 gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model),
79 &scol, &order);
80
81 gtk_tree_model_get(model, a, 0, &id_a, -1);
82 gtk_tree_model_get(model, b, 0, &id_b, -1);
83
84 ia = atoi(id_a);
85 ib = atoi(id_b);
86
87 if (ia == 0) {
88 ret = (order == GTK_SORT_DESCENDING)? 1 : 0;
89 } else if (ib == 0) {
90 ret = (order == GTK_SORT_DESCENDING)? 0 : 1;
91 } else {
92 ret = ia - ib;
93 }
94
95 g_free(id_a);
96 g_free(id_b);
97
98 return ret;
99 }
100
101 /* special comparator to put the series in a database window back into
102 "natural" order (by position in the database) in place of a
103 descending sort action in the first (series name) column
104 */
105
db_series_compare(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,GtkTreeViewColumn * col)106 static gint db_series_compare (GtkTreeModel *model,
107 GtkTreeIter *a, GtkTreeIter *b,
108 GtkTreeViewColumn *col)
109 {
110 GtkSortType order;
111 gint scol, ret;
112
113 gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model),
114 &scol, &order);
115
116 if (order == GTK_SORT_ASCENDING) {
117 /* alphabetize as usual */
118 gchar *sa, *sb;
119
120 gtk_tree_model_get(model, a, 0, &sa, -1);
121 gtk_tree_model_get(model, b, 0, &sb, -1);
122 ret = strcmp(sa, sb);
123 g_free(sa);
124 g_free(sb);
125 } else {
126 /* 'natural' order in place of descending sort:
127 use the last (integer) column */
128 gint ia, ib;
129
130 gtk_tree_model_get(model, a, 3, &ia, -1);
131 gtk_tree_model_get(model, b, 3, &ib, -1);
132 ret = ib - ia;
133 gtk_tree_view_column_set_sort_indicator(col, FALSE);
134 }
135
136 return ret;
137 }
138
139 static gint
compare_treenames(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer p)140 compare_treenames (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
141 gpointer p)
142 {
143 gchar *s1, *s2;
144 gint ret;
145
146 gtk_tree_model_get(model, a, 0, &s1, -1);
147 gtk_tree_model_get(model, b, 0, &s2, -1);
148
149 ret = strcmp(gretl_lower(s1), gretl_lower(s2));
150
151 g_free(s1);
152 g_free(s2);
153
154 return ret;
155 }
156
157 /* sort in ascending order by the strings in the first column
158 of the treeview in @vwin
159 */
160
presort_treelist(windata_t * vwin)161 void presort_treelist (windata_t *vwin)
162 {
163 GtkTreeModel *model;
164
165 model = gtk_tree_view_get_model(GTK_TREE_VIEW(vwin->listbox));
166
167 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
168 0, GTK_SORT_ASCENDING);
169 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), 0,
170 compare_treenames, NULL, NULL);
171 }
172
173
no_select_row_zero(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)174 static gboolean no_select_row_zero (GtkTreeSelection *selection,
175 GtkTreeModel *model,
176 GtkTreePath *path,
177 gboolean path_currently_selected,
178 gpointer data)
179 {
180 return tree_path_get_row_number(path) != 0;
181 }
182
get_selected_varnum(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,int * v)183 static void get_selected_varnum (GtkTreeModel *model, GtkTreePath *path,
184 GtkTreeIter *iter, int *v)
185 {
186 gchar *id;
187
188 gtk_tree_model_get(model, iter, 0, &id, -1);
189 *v = atoi(id);
190 g_free(id);
191 }
192
tree_selection_count(GtkTreeSelection * select,int * vnum)193 int tree_selection_count (GtkTreeSelection *select, int *vnum)
194 {
195 int selcount = 0;
196
197 if (select != NULL) {
198 selcount = gtk_tree_selection_count_selected_rows(select);
199 }
200
201 if (vnum != NULL && selcount == 1) {
202 gtk_tree_selection_selected_foreach(select,
203 (GtkTreeSelectionForeachFunc)
204 get_selected_varnum,
205 vnum);
206 }
207
208 return selcount;
209 }
210
vwin_selection_count(windata_t * vwin,int * row)211 int vwin_selection_count (windata_t *vwin, int *row)
212 {
213 GtkTreeView *view = GTK_TREE_VIEW(vwin->listbox);
214 GtkTreeSelection *sel = gtk_tree_view_get_selection(view);
215
216 return tree_selection_count(sel, row);
217 }
218
my_gtk_entry_append_text(GtkEntry * entry,gchar * add)219 static void my_gtk_entry_append_text (GtkEntry *entry, gchar *add)
220 {
221 const gchar *old = gtk_entry_get_text(entry);
222 gchar *new = g_strdup_printf("%s%s", old, add);
223
224 gtk_entry_set_text(entry, new);
225 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
226 g_free(new);
227 }
228
update_dialogs_from_varclick(int active_var)229 static void update_dialogs_from_varclick (int active_var)
230 {
231 GtkWidget *active_edit_id = get_active_edit_id();
232 GtkWidget *active_edit_name = get_active_edit_name();
233 GtkWidget *active_edit_text = get_active_edit_text();
234 const gchar *edttext;
235
236 if (active_edit_id != NULL) {
237 gchar addvar[9];
238
239 edttext = gtk_entry_get_text(GTK_ENTRY(active_edit_id));
240 if (*edttext != '\0') {
241 sprintf(addvar, " %d", active_var);
242 } else {
243 sprintf(addvar, "%d", active_var);
244 }
245 my_gtk_entry_append_text(GTK_ENTRY(active_edit_id), addvar);
246 } else if (active_edit_name != NULL) {
247 edttext = gtk_entry_get_text(GTK_ENTRY(active_edit_name));
248 my_gtk_entry_append_text(GTK_ENTRY(active_edit_name),
249 dataset->varname[active_var]);
250 } else if (active_edit_text != NULL) {
251 GtkTextBuffer *tbuf;
252
253 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(active_edit_text));
254 if (tbuf != NULL) {
255 gtk_text_buffer_insert_at_cursor(tbuf,
256 dataset->varname[active_var],
257 -1);
258 }
259 }
260 }
261
262 #ifdef OS_OSX
263
264 /* Support for command-click as replacement for Ctrl-click
265 on OS X */
266
maybe_do_meta_click(GdkEventButton * event,GtkTreeView * view,GtkTreePath * path)267 static gboolean maybe_do_meta_click (GdkEventButton *event,
268 GtkTreeView *view,
269 GtkTreePath *path)
270 {
271 if (event->state & GDK_MOD2_MASK) {
272 /* The idea here is that we add to the current selection
273 and return TRUE to block further processing of the
274 click, so as to implement selection of non-contiguous
275 rows in the GtkTreeView. It works on Linux using
276 GDK_CONTROL_MASK as the criterion (i.e. faking the
277 standard behavior of Ctrl-click as test of concept).
278 */
279 GtkTreeSelection *sel = gtk_tree_view_get_selection(view);
280
281 gtk_tree_selection_select_path(sel, path);
282 return TRUE;
283 } else {
284 return FALSE;
285 }
286 }
287
288 #endif
289
290 /* The idea here is that when the user clicks on a series
291 that is a component of a MIDAS list, the whole list
292 becomes selected, so we can offer high-frequency options
293 in the right-click popup, such as displaying or plotting
294 the underlying high-frequency data at their native
295 frequency.
296 */
297
extend_midas_selection(GtkTreeView * view,GtkTreePath * path,int vnum,int p0)298 static int extend_midas_selection (GtkTreeView *view,
299 GtkTreePath *path,
300 int vnum, int p0)
301 {
302 GtkTreePath *path0 = gtk_tree_path_copy(path);
303 GtkTreePath *path1 = gtk_tree_path_copy(path);
304 int p, i, j, extended = 0;
305
306 if (!series_is_midas_anchor(dataset, vnum)) {
307 for (i=vnum-1, j=1; i>0; i--, j++) {
308 p = series_get_midas_period(dataset, i);
309 if (p != p0 + j) {
310 break;
311 }
312 if (gtk_tree_path_prev(path0)) {
313 extended++;
314 }
315 }
316 }
317
318 for (i=vnum+1, j=1; i<dataset->v; i++, j++) {
319 p = series_get_midas_period(dataset, i);
320 if (p == 0 || p != p0 - j) {
321 break;
322 }
323 gtk_tree_path_next(path1);
324 extended++;
325 }
326
327 if (extended) {
328 GtkTreeSelection *selection;
329
330 selection = gtk_tree_view_get_selection(view);
331 gtk_tree_selection_unselect_all(selection);
332 gtk_tree_selection_select_range(selection, path0, path1);
333 }
334
335 gtk_tree_path_free(path0);
336 gtk_tree_path_free(path1);
337
338 return extended;
339 }
340
341 /* Respond to a mouse click in gretl's main window. This callback
342 is connected after the one that deals with right-clicking to
343 handle popups, so here we can assume it's not a right-click.
344 */
345
main_varclick(GtkWidget * widget,GdkEventButton * event,windata_t * vwin)346 gboolean main_varclick (GtkWidget *widget, GdkEventButton *event,
347 windata_t *vwin)
348 {
349 GtkTreeView *view = GTK_TREE_VIEW(vwin->listbox);
350 GtkTreePath *path;
351 gboolean ret = FALSE;
352
353 if (dataset == NULL || dataset->n == 0) {
354 return TRUE;
355 }
356
357 if (gtk_tree_view_get_path_at_pos(view, event->x, event->y, &path,
358 NULL, NULL, NULL)) {
359 gint row = gtk_tree_path_get_indices(path)[0];
360
361 if (row == 0) {
362 ret = TRUE;
363 } else {
364 gchar *varnum;
365 int p;
366
367 g_object_set_data(G_OBJECT(vwin->listbox), "active_row",
368 GINT_TO_POINTER(row));
369 tree_view_get_string2(view, path, 0, &varnum);
370 vwin->active_var = atoi(varnum);
371 g_free(varnum);
372 p = series_get_midas_period(dataset, vwin->active_var);
373 if (p > 0 &&
374 extend_midas_selection(view, path,
375 vwin->active_var, p)) {
376 ret = TRUE;
377 } else {
378 update_dialogs_from_varclick(vwin->active_var);
379 #ifdef OS_OSX
380 ret = maybe_do_meta_click(event, view, path);
381 #endif
382 }
383 }
384
385 gtk_tree_path_free(path);
386 } else {
387 /* clicked below the lines representing variables */
388 return FALSE;
389 }
390
391 return ret;
392 }
393
394 static void
bool_col_toggled(GtkCellRendererToggle * cell,gchar * path_str,windata_t * vwin)395 bool_col_toggled (GtkCellRendererToggle *cell, gchar *path_str, windata_t *vwin)
396 {
397 GtkTreeView *treeview = GTK_TREE_VIEW(vwin->listbox);
398 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
399 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
400 GtkTreeIter iter;
401 gboolean val;
402 gint col;
403
404 col = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(vwin->main), "boolcol"));
405 gtk_tree_model_get_iter(model, &iter, path);
406 gtk_tree_model_get(model, &iter, col, &val, -1);
407
408 if (val) {
409 gtk_tree_path_free(path);
410 return;
411 }
412
413 gtk_list_store_set(GTK_LIST_STORE(model), &iter, col, TRUE, -1);
414 gtk_tree_path_free(path);
415 }
416
catch_listbox_key(GtkWidget * w,GdkEventKey * event,windata_t * vwin)417 static gint catch_listbox_key (GtkWidget *w, GdkEventKey *event,
418 windata_t *vwin)
419 {
420 int key = event->keyval;
421
422 #ifdef OS_OSX
423 if (key == GDK_w && cmd_key(event)) {
424 if (vwin != mdata) {
425 gtk_widget_destroy(vwin->main);
426 }
427 return TRUE;
428 }
429 #endif
430
431 if (key == GDK_q) {
432 /* Q = quit */
433 if (vwin != mdata) {
434 gtk_widget_destroy(vwin->main);
435 }
436 return TRUE;
437 } else if (key == GDK_f || key == GDK_g) {
438 /* F = find, G = find again */
439 if (vwin == mdata && !data_status) {
440 return TRUE;
441 }
442 if (event->state & GDK_CONTROL_MASK) {
443 if (key == GDK_f) {
444 listbox_find(NULL, vwin);
445 } else {
446 listbox_find_again(NULL, vwin);
447 }
448 return TRUE;
449 }
450 }
451
452 return FALSE;
453 }
454
check_series_pd(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,windata_t * vwin)455 static void check_series_pd (GtkTreeModel *model, GtkTreePath *path,
456 GtkTreeIter *iter, windata_t *vwin)
457 {
458 static int pd0;
459 gchar *tmp = NULL;
460
461 if (model == NULL) {
462 /* reset */
463 pd0 = 0;
464 return;
465 }
466
467 gtk_tree_model_get(model, iter, 2, &tmp, -1);
468 if (tmp != NULL) {
469 if (pd0 == 0) {
470 pd0 = *tmp;
471 } else if (*tmp != pd0) {
472 GtkTreeView *view = GTK_TREE_VIEW(vwin->listbox);
473 GtkTreeSelection *sel;
474
475 sel = gtk_tree_view_get_selection(view);
476 gtk_tree_selection_unselect_iter(sel, iter);
477 }
478 g_free(tmp);
479 }
480 }
481
set_active_row(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,windata_t * vwin)482 static void set_active_row (GtkTreeModel *model, GtkTreePath *path,
483 GtkTreeIter *iter, windata_t *vwin)
484 {
485 vwin->active_var = tree_path_get_row_number(path);
486 }
487
check_db_series_selection(GtkTreeSelection * sel,windata_t * vwin)488 static void check_db_series_selection (GtkTreeSelection *sel,
489 windata_t *vwin)
490 {
491 int nsel = gtk_tree_selection_count_selected_rows(sel);
492
493 if (nsel == 1) {
494 gtk_tree_selection_selected_foreach(sel,
495 (GtkTreeSelectionForeachFunc)
496 set_active_row, vwin);
497 } else {
498 check_series_pd(NULL, NULL, NULL, NULL);
499 gtk_tree_selection_selected_foreach(sel,
500 (GtkTreeSelectionForeachFunc)
501 check_series_pd, vwin);
502 }
503 }
504
id_col_clicked(GtkTreeViewColumn * column,GtkWidget * view)505 static void id_col_clicked (GtkTreeViewColumn *column, GtkWidget *view)
506 {
507 GtkTreeModel *model;
508 GtkSortType order;
509 gint scol;
510
511 model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
512 gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model),
513 &scol, &order);
514 if (order == GTK_SORT_ASCENDING) {
515 gtk_tree_view_column_set_sort_indicator(column, FALSE);
516 }
517 }
518
519 #define db_series_window(v) (v->role == NATIVE_SERIES || \
520 v->role == RATS_SERIES || \
521 v->role == PCGIVE_SERIES || \
522 v->role == REMOTE_SERIES)
523
vwin_add_list_box(windata_t * vwin,GtkBox * box,int ncols,int hidden_cols,GType * types,const char ** titles,int tree)524 void vwin_add_list_box (windata_t *vwin, GtkBox *box,
525 int ncols, int hidden_cols,
526 GType *types, const char **titles,
527 int tree)
528 {
529 GtkListStore *lstore = NULL;
530 GtkTreeStore *tstore = NULL;
531 GtkWidget *view, *scroller;
532 GtkCellRenderer *renderer;
533 GtkCellRenderer *bool_renderer = NULL;
534 GtkTreeViewColumn *column;
535 GtkTreeSelection *select;
536 int i, viscols;
537
538 viscols = ncols - hidden_cols;
539
540 if (tree) {
541 tstore = gtk_tree_store_newv(ncols, types);
542 vwin->listbox = view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tstore));
543 } else {
544 lstore = gtk_list_store_newv(ncols, types);
545 vwin->listbox = view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(lstore));
546 }
547
548 #ifndef G_OS_WIN32
549 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
550 #endif
551
552 renderer = gtk_cell_renderer_text_new();
553 g_object_set(renderer, "ypad", 0, NULL);
554
555 for (i=0; i<viscols; i++) {
556 if (types[i] == G_TYPE_BOOLEAN) {
557 bool_renderer = gtk_cell_renderer_toggle_new();
558 g_object_set_data(G_OBJECT(vwin->main), "boolcol", GINT_TO_POINTER(i));
559 g_signal_connect(G_OBJECT(bool_renderer), "toggled",
560 G_CALLBACK(bool_col_toggled), vwin);
561 column = gtk_tree_view_column_new_with_attributes(_(titles[i]),
562 bool_renderer,
563 "active", i,
564 NULL);
565 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
566 GTK_TREE_VIEW_COLUMN_FIXED);
567 gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column), 50);
568 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
569 } else {
570 column = gtk_tree_view_column_new_with_attributes(_(titles[i]),
571 renderer,
572 "text", i,
573 NULL);
574 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
575
576 if ((vwin->role == FUNC_FILES || vwin->role == REMOTE_FUNC_FILES) && i == 1) {
577 ; /* sorting functions by version number is not meaningful */
578 } else {
579 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), i);
580 }
581
582 if (vwin != mdata) {
583 g_object_set(G_OBJECT(column), "resizable", TRUE, NULL);
584 }
585
586 /* special actions on first column */
587 if (i == 0) {
588 if (vwin == mdata) {
589 g_signal_connect(G_OBJECT(column), "clicked",
590 G_CALLBACK(id_col_clicked), view);
591 } else if (db_series_window(vwin)) {
592 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(lstore), 0,
593 (GtkTreeIterCompareFunc)
594 db_series_compare,
595 column, NULL);
596 }
597 }
598 }
599 }
600
601 for (i=viscols; i<ncols; i++) {
602 column = gtk_tree_view_column_new_with_attributes(NULL,
603 renderer,
604 "text", i,
605 NULL);
606 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
607 gtk_tree_view_column_set_visible(column, FALSE);
608 }
609
610 /* set the selection properties on the tree view */
611 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
612
613 if (vwin == mdata) {
614 /* gretl main window */
615 gtk_tree_selection_set_mode(select, GTK_SELECTION_MULTIPLE);
616 gtk_tree_selection_set_select_function(select,
617 (GtkTreeSelectionFunc)
618 no_select_row_zero,
619 NULL, NULL);
620 gtk_widget_set_events(view, GDK_POINTER_MOTION_MASK
621 | GDK_POINTER_MOTION_HINT_MASK);
622 g_signal_connect(G_OBJECT(view), "motion-notify-event",
623 G_CALLBACK(listbox_drag), NULL);
624 } else if (db_series_window(vwin)) {
625 gtk_tree_selection_set_mode(select, GTK_SELECTION_MULTIPLE);
626 g_signal_connect(G_OBJECT(select), "changed",
627 G_CALLBACK(check_db_series_selection),
628 vwin);
629 } else {
630 gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
631 g_signal_connect(G_OBJECT(select), "changed",
632 G_CALLBACK(listbox_select_row),
633 vwin);
634 }
635
636 g_signal_connect(G_OBJECT(view), "key-press-event",
637 G_CALLBACK(catch_listbox_key), vwin);
638 g_signal_connect(G_OBJECT(view), "button-press-event",
639 G_CALLBACK(listbox_double_click), vwin);
640
641 /* set sort properties on the tree model */
642 if (vwin == mdata && tstore != NULL) {
643 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(tstore), 0,
644 (GtkTreeIterCompareFunc)
645 list_id_compare,
646 NULL, NULL);
647 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(tstore), 1,
648 (GtkTreeIterCompareFunc)
649 list_alpha_compare,
650 NULL, NULL);
651 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(tstore), 2,
652 (GtkTreeIterCompareFunc)
653 list_alpha_compare,
654 NULL, NULL);
655 }
656
657 if (lstore != NULL) {
658 g_object_unref(G_OBJECT(lstore));
659 } else if (tstore != NULL) {
660 g_object_unref(G_OBJECT(tstore));
661 }
662
663 scroller = gtk_scrolled_window_new(NULL, NULL);
664
665 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
666 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
667 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroller),
668 GTK_SHADOW_IN);
669
670 gtk_container_add(GTK_CONTAINER(scroller), view);
671 gtk_box_pack_start(box, scroller, TRUE, TRUE, TRUE);
672
673 gtk_widget_show(view);
674 gtk_widget_show(scroller);
675 }
676
set_main_colheads_clickable(gboolean s)677 void set_main_colheads_clickable (gboolean s)
678 {
679 GtkTreeViewColumn *col;
680 int i;
681
682 for (i=0; i<3; i++) {
683 col = gtk_tree_view_get_column(GTK_TREE_VIEW(mdata->listbox), i);
684 gtk_tree_view_column_set_clickable(col, s);
685 }
686
687 if (!s) {
688 gtk_widget_grab_focus(mdata->listbox);
689 }
690 }
691
tree_view_get_bool(GtkTreeView * view,int row,int col,gboolean * val)692 void tree_view_get_bool (GtkTreeView *view, int row, int col, gboolean *val)
693 {
694 GtkTreeModel *model;
695 GtkTreeIter iter;
696 gchar *path;
697
698 model = gtk_tree_view_get_model(view);
699 path = g_strdup_printf("%d", row);
700 if (gtk_tree_model_get_iter_from_string(model, &iter, path)) {
701 gtk_tree_model_get(model, &iter, col, val, -1);
702 }
703 g_free(path);
704 }
705
tree_view_get_int(GtkTreeView * view,int row,int col,gint * val)706 void tree_view_get_int (GtkTreeView *view, int row, int col, gint *val)
707 {
708 GtkTreeModel *model;
709 GtkTreeIter iter;
710 gchar *path;
711
712 model = gtk_tree_view_get_model(view);
713 path = g_strdup_printf("%d", row);
714 if (gtk_tree_model_get_iter_from_string(model, &iter, path)) {
715 gtk_tree_model_get(model, &iter, col, val, -1);
716 }
717 g_free(path);
718 }
719
tree_view_get_string(GtkTreeView * view,int row,int col,gchar ** val)720 void tree_view_get_string (GtkTreeView *view, int row, int col, gchar **val)
721 {
722 GtkTreeModel *model;
723 GtkTreeIter iter;
724 gchar *path;
725
726 model = gtk_tree_view_get_model(view);
727 path = g_strdup_printf("%d", row);
728 if (gtk_tree_model_get_iter_from_string(model, &iter, path)) {
729 gtk_tree_model_get(model, &iter, col, val, -1);
730 }
731 g_free(path);
732 }
733
tree_view_get_string2(GtkTreeView * view,GtkTreePath * path,int col,gchar ** val)734 static void tree_view_get_string2 (GtkTreeView *view,
735 GtkTreePath *path,
736 int col, gchar **val)
737 {
738 GtkTreeModel *model;
739 GtkTreeIter iter;
740
741 model = gtk_tree_view_get_model(view);
742 if (gtk_tree_model_get_iter(model, &iter, path)) {
743 gtk_tree_model_get(model, &iter, col, val, -1);
744 }
745 }
746
tree_view_set_string(GtkTreeView * view,int row,int col,const gchar * val,int tstore)747 static void tree_view_set_string (GtkTreeView *view, int row, int col,
748 const gchar *val, int tstore)
749 {
750 GtkTreeModel *model;
751 GtkTreeIter iter;
752 gchar *path;
753
754 model = gtk_tree_view_get_model(view);
755 path = g_strdup_printf("%d", row);
756 if (gtk_tree_model_get_iter_from_string(model, &iter, path)) {
757 if (tstore == 1) {
758 gtk_tree_store_set(GTK_TREE_STORE(model), &iter, col, val, -1);
759 } else {
760 gtk_list_store_set(GTK_LIST_STORE(model), &iter, col, val, -1);
761 }
762 }
763 g_free(path);
764 }
765
list_store_set_string(GtkTreeView * view,int row,int col,const gchar * val)766 void list_store_set_string (GtkTreeView *view, int row, int col, const gchar *val)
767 {
768 tree_view_set_string(view, row, col, val, 0);
769 }
770
tree_store_set_string(GtkTreeView * view,int row,int col,const gchar * val)771 void tree_store_set_string (GtkTreeView *view, int row, int col, const gchar *val)
772 {
773 tree_view_set_string(view, row, col, val, 1);
774 }
775
tree_path_get_row_number(GtkTreePath * path)776 int tree_path_get_row_number (GtkTreePath *path)
777 {
778 return gtk_tree_path_get_indices(path)[0];
779 }
780
tree_model_get_iter_last(GtkTreeModel * mod,GtkTreeIter * iter)781 void tree_model_get_iter_last (GtkTreeModel *mod, GtkTreeIter *iter)
782 {
783 GtkTreeIter first;
784 gboolean s;
785
786 s = gtk_tree_model_get_iter_first(mod, &first);
787 *iter = first;
788 while (s && gtk_tree_model_iter_next(mod, &first)) {
789 *iter = first;
790 }
791 }
792
tree_model_count_rows(GtkTreeModel * mod)793 int tree_model_count_rows (GtkTreeModel *mod)
794 {
795 GtkTreeIter iter;
796 int nrows = 0;
797
798 if (gtk_tree_model_get_iter_first(mod, &iter)) {
799 nrows = 1;
800 while (gtk_tree_model_iter_next(mod, &iter)) {
801 nrows++;
802 }
803 }
804
805 return nrows;
806 }
807
tree_model_iter_prev(GtkTreeModel * mod,GtkTreeIter * iter)808 gboolean tree_model_iter_prev (GtkTreeModel *mod, GtkTreeIter *iter)
809 {
810 GtkTreePath *path;
811 gboolean s;
812
813 path = gtk_tree_model_get_path(mod, iter);
814 s = gtk_tree_path_prev(path);
815 if (s) {
816 gtk_tree_model_get_iter(mod, iter, path);
817 }
818 gtk_tree_path_free(path);
819 return s;
820 }
821
add_to_selection_count(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,int * count)822 static void add_to_selection_count (GtkTreeModel *model, GtkTreePath *path,
823 GtkTreeIter *iter, int *count)
824 {
825 *count += 1;
826 }
827
add_to_selection_list(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,int * list)828 static void add_to_selection_list (GtkTreeModel *model, GtkTreePath *path,
829 GtkTreeIter *iter, int *list)
830 {
831 gchar *varnum = NULL;
832
833 gtk_tree_model_get(model, iter, 0, &varnum, -1);
834 list[0] += 1;
835 list[list[0]] = atoi(varnum);
836 g_free(varnum);
837 }
838
main_window_selection_as_list(void)839 int *main_window_selection_as_list (void)
840 {
841 GtkTreeSelection *select;
842 int *list = NULL;
843 int scount = 0;
844
845 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(mdata->listbox));
846 gtk_tree_selection_selected_foreach(select,
847 (GtkTreeSelectionForeachFunc)
848 add_to_selection_count,
849 &scount);
850
851 if (scount > 0) {
852 list = gretl_list_new(scount);
853 }
854
855 if (list != NULL) {
856 list[0] = 0;
857 gtk_tree_selection_selected_foreach(select,
858 (GtkTreeSelectionForeachFunc)
859 add_to_selection_list,
860 list);
861 }
862
863 return list;
864 }
865
main_window_selection_as_string(void)866 char *main_window_selection_as_string (void)
867 {
868 int *list = main_window_selection_as_list();
869 char *liststr = NULL;
870
871 if (list != NULL) {
872 int err = 0;
873
874 liststr = gretl_list_to_string(list, dataset, &err);
875 }
876
877 return liststr;
878 }
879