1 /*
2  * This file is part of YAD.
3  *
4  * YAD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * YAD is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with YAD. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Copyright (C) 2008-2019, Victor Ananjevsky <ananasik@gmail.com>
18  */
19 
20 #include <string.h>
21 #include <stdlib.h>
22 
23 #include "yad.h"
24 
25 static GtkWidget *list_view;
26 
27 static GHashTable *row_hash = NULL;
28 
29 static gint fore_col, back_col, font_col;
30 static guint n_cols = 0;
31 
32 static gulong select_hndl = 0;
33 
34 static inline void
yad_list_add_row(GtkTreeStore * m,GtkTreeIter * it,gchar * row_id,gchar * par_id)35 yad_list_add_row (GtkTreeStore *m, GtkTreeIter *it, gchar *row_id, gchar *par_id)
36 {
37   GtkTreePath *row_path;
38   GtkTreeIter pit, *parent = NULL;
39 
40   if (par_id && par_id[0])
41     {
42       GtkTreePath *par_path = g_hash_table_lookup (row_hash, par_id);
43       if (par_path)
44         {
45           if (gtk_tree_model_get_iter (GTK_TREE_MODEL (m), &pit, par_path))
46             parent = &pit;
47         }
48     }
49 
50   if (options.list_data.add_on_top)
51     gtk_tree_store_prepend (m, it, parent);
52   else
53     gtk_tree_store_append (m, it, parent);
54 
55   row_path = gtk_tree_model_get_path (GTK_TREE_MODEL (m), it);
56   if (row_id && row_id[0])
57     g_hash_table_insert (row_hash, row_id, row_path);
58   if (options.common_data.tail)
59     gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (list_view), row_path, NULL, FALSE, 1.0, 1.0);
60 }
61 
62 static gboolean
list_activate_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)63 list_activate_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
64 {
65   if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter)
66     {
67       if (options.list_data.dclick_action)
68         {
69           /* FIXME: check this under gtk-3.0 */
70           if (event->state & GDK_CONTROL_MASK)
71             {
72               if (options.plug == -1)
73                 yad_exit (options.data.def_resp);
74             }
75           else
76             return FALSE;
77         }
78       else
79         {
80           if (options.plug == -1)
81             yad_exit (options.data.def_resp);
82         }
83 
84       return TRUE;
85     }
86 
87   return FALSE;
88 }
89 
90 /* custom tooltip signal handler for no-markup mode */
91 static gboolean
tooltip_cb(GtkWidget * w,gint x,gint y,gboolean mode,GtkTooltip * tip,gpointer data)92 tooltip_cb (GtkWidget *w, gint x, gint y, gboolean mode, GtkTooltip *tip, gpointer data)
93 {
94   gchar *str;
95   gint bx, by;
96   GtkTreePath *path;
97   GtkTreeIter iter;
98   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (w));
99 
100   gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (w), x, y, &bx, &by);
101   if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (w), bx, by, &path, NULL, NULL, NULL))
102     return FALSE;
103 
104   gtk_tree_model_get_iter (model, &iter, path);
105   gtk_tree_path_free (path);
106 
107   gtk_tree_model_get (model, &iter, options.list_data.tooltip_column - 1, &str, -1);
108   if (str)
109     {
110       gtk_tooltip_set_text (tip, str);
111       return TRUE;
112     }
113 
114   return FALSE;
115 }
116 
117 static void
toggled_cb(GtkCellRendererToggle * cell,gchar * path_str,gpointer data)118 toggled_cb (GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
119 {
120   gint column;
121   gboolean fixed;
122   GtkTreeIter iter;
123   GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
124   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
125 
126   column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
127   gtk_tree_model_get_iter (model, &iter, path);
128   gtk_tree_model_get (model, &iter, column, &fixed, -1);
129 
130   fixed ^= 1;
131 
132   gtk_tree_store_set (GTK_TREE_STORE (model), &iter, column, fixed, -1);
133 
134   gtk_tree_path_free (path);
135 }
136 
137 static gboolean
runtoggle(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)138 runtoggle (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
139 {
140   gint col = GPOINTER_TO_INT (data);
141   gtk_tree_store_set (GTK_TREE_STORE (model), iter, col, FALSE, -1);
142   return FALSE;
143 }
144 
145 static void
rtoggled_cb(GtkCellRendererToggle * cell,gchar * path_str,gpointer data)146 rtoggled_cb (GtkCellRendererToggle *cell, gchar *path_str, gpointer data)
147 {
148   gint column;
149   GtkTreeIter iter;
150   GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
151   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
152 
153   column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
154 
155   gtk_tree_model_foreach (model, runtoggle, GINT_TO_POINTER (column));
156 
157   gtk_tree_model_get_iter (model, &iter, path);
158   gtk_tree_store_set (GTK_TREE_STORE (model), &iter, column, TRUE, -1);
159 
160   gtk_tree_path_free (path);
161 }
162 
163 static void
cell_edited_cb(GtkCellRendererText * cell,const gchar * path_string,const gchar * new_text,gpointer data)164 cell_edited_cb (GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text, gpointer data)
165 {
166   gint column;
167   GtkTreeIter iter;
168   GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
169   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
170   YadColumn *col;
171 
172   column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
173   gtk_tree_model_get_iter (model, &iter, path);
174   col = (YadColumn *) g_slist_nth_data (options.list_data.columns, column);
175 
176   if (col->type == YAD_COLUMN_NUM)
177     gtk_tree_store_set (GTK_TREE_STORE (model), &iter, column, g_ascii_strtoll (new_text, NULL, 10), -1);
178   else if (col->type == YAD_COLUMN_FLOAT)
179     gtk_tree_store_set (GTK_TREE_STORE (model), &iter, column, g_ascii_strtod (new_text, NULL), -1);
180   else
181     gtk_tree_store_set (GTK_TREE_STORE (model), &iter, column, new_text, -1);
182 
183   gtk_tree_path_free (path);
184 }
185 
186 static gboolean
regex_search(GtkTreeModel * model,gint col,const gchar * key,GtkTreeIter * iter,gpointer data)187 regex_search (GtkTreeModel *model, gint col, const gchar *key, GtkTreeIter *iter, gpointer data)
188 {
189   static GRegex *pattern = NULL;
190   static guint pos = 0;
191   gchar *str;
192 
193   if (key[pos])
194     {
195       if (pattern)
196         g_regex_unref (pattern);
197       pattern = g_regex_new (key, G_REGEX_CASELESS | G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY, NULL);
198       pos = strlen (key);
199     }
200 
201   if (pattern)
202     {
203       gboolean ret;
204 
205       gtk_tree_model_get (model, iter, col, &str, -1);
206 
207       ret = g_regex_match (pattern, str, G_REGEX_MATCH_NOTEMPTY, NULL);
208       /* if get it, clear key end position */
209       if (!ret)
210         pos = 0;
211 
212       return !ret;
213     }
214   else
215     return TRUE;
216 }
217 
218 static GtkTreeModel *
create_model()219 create_model ()
220 {
221   GtkTreeStore *store;
222   GType *ctypes;
223   gint i;
224 
225   ctypes = g_new0 (GType, n_cols);
226 
227   if (options.list_data.checkbox)
228     {
229       YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, 0);
230       col->type = YAD_COLUMN_CHECK;
231     }
232   else if (options.list_data.radiobox)
233     {
234       YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, 0);
235       col->type = YAD_COLUMN_RADIO;
236     }
237 
238   for (i = 0; i < n_cols; i++)
239     {
240       YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, i);
241 
242       switch (col->type)
243         {
244         case YAD_COLUMN_CHECK:
245         case YAD_COLUMN_RADIO:
246           ctypes[i] = G_TYPE_BOOLEAN;
247           break;
248         case YAD_COLUMN_NUM:
249         case YAD_COLUMN_SIZE:
250         case YAD_COLUMN_BAR:
251           ctypes[i] = G_TYPE_INT64;
252           break;
253         case YAD_COLUMN_FLOAT:
254           ctypes[i] = G_TYPE_DOUBLE;
255           break;
256         case YAD_COLUMN_IMAGE:
257           ctypes[i] = GDK_TYPE_PIXBUF;
258           break;
259         case YAD_COLUMN_ATTR_FORE:
260           ctypes[i] = G_TYPE_STRING;
261           fore_col = i;
262           break;
263         case YAD_COLUMN_ATTR_BACK:
264           ctypes[i] = G_TYPE_STRING;
265           back_col = i;
266           break;
267         case YAD_COLUMN_ATTR_FONT:
268           ctypes[i] = G_TYPE_STRING;
269           font_col = i;
270           break;
271         default:
272           ctypes[i] = G_TYPE_STRING;
273           break;
274         }
275     }
276 
277   store = gtk_tree_store_newv (n_cols, ctypes);
278 
279   return GTK_TREE_MODEL (store);
280 }
281 
282 static void
float_col_format(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)283 float_col_format (GtkTreeViewColumn *col, GtkCellRenderer *cell, GtkTreeModel *model,
284                   GtkTreeIter *iter, gpointer data)
285 {
286   gdouble val;
287   gchar buf[20];
288 
289   gtk_tree_model_get (model, iter, GPOINTER_TO_INT (data), &val, -1);
290   g_snprintf (buf, sizeof (buf), "%.*f", options.common_data.float_precision, val);
291   g_object_set (cell, "text", buf, NULL);
292 }
293 
294 static void
size_col_format(GtkTreeViewColumn * col,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)295 size_col_format (GtkTreeViewColumn *col, GtkCellRenderer *cell, GtkTreeModel *model,
296                   GtkTreeIter *iter, gpointer data)
297 {
298   guint64 val;
299   gchar buf[20], *sz;
300 
301   gtk_tree_model_get (model, iter, GPOINTER_TO_INT (data), &val, -1);
302 #if GLIB_CHECK_VERSION(2,30,0)
303   sz = g_format_size_full (val, options.common_data.size_fmt);
304 #elif  GLIB_CHECK_VERSION(2,16,0)
305   sz = g_format_size_for_display (val);
306 #else
307   sz = g_strdup_printf ("%d", val);
308 #endif
309   g_snprintf (buf, sizeof (buf), "%s", sz);
310   g_free (sz);
311   g_object_set (cell, "text", buf, NULL);
312 }
313 
314 static void
set_column_title(GtkTreeViewColumn * col,gchar * title)315 set_column_title (GtkTreeViewColumn *col, gchar *title)
316 {
317   GtkWidget *lbl;
318   gchar **str;
319 
320   str = g_strsplit (title, options.common_data.item_separator, 2);
321   lbl = gtk_label_new (NULL);
322 
323   if (options.data.no_markup)
324     {
325       gtk_label_set_text (GTK_LABEL (lbl), str[0]);
326       if (str[1] || options.list_data.header_tips)
327         gtk_widget_set_tooltip_text (lbl, str[1] ? str[1] : str[0]);
328     }
329   else
330     {
331       gtk_label_set_markup (GTK_LABEL (lbl), str[0]);
332       if (str[1] || options.list_data.header_tips)
333         gtk_widget_set_tooltip_markup (lbl, str[1] ? str[1] : str[0]);
334     }
335 
336   gtk_widget_show (lbl);
337   gtk_tree_view_column_set_widget (col, lbl);
338 }
339 
340 static void
add_columns()341 add_columns ()
342 {
343   gint i;
344   GtkCellRenderer *renderer;
345   GtkTreeViewColumn *column;
346 
347   for (i = 0; i < n_cols; i++)
348     {
349       YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, i);
350 
351       if (i == options.list_data.hide_column - 1 || col->type == YAD_COLUMN_HIDDEN ||
352           i == fore_col || i == back_col || i == font_col)
353         continue;
354 
355       switch (col->type)
356         {
357         case YAD_COLUMN_CHECK:
358         case YAD_COLUMN_RADIO:
359           renderer = gtk_cell_renderer_toggle_new ();
360           column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "active", i, NULL);
361           set_column_title (column, col->name);
362           if (back_col != -1)
363             gtk_tree_view_column_add_attribute (column, renderer, "cell-background", back_col);
364           if (col->type == YAD_COLUMN_RADIO)
365             {
366               gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer), TRUE);
367               g_signal_connect (renderer, "toggled", G_CALLBACK (rtoggled_cb), NULL);
368             }
369           else
370             g_signal_connect (renderer, "toggled", G_CALLBACK (toggled_cb), NULL);
371           break;
372         case YAD_COLUMN_IMAGE:
373           renderer = gtk_cell_renderer_pixbuf_new ();
374           column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "pixbuf", i, NULL);
375           set_column_title (column, col->name);
376           if (back_col != -1)
377             gtk_tree_view_column_add_attribute (column, renderer, "cell-background", back_col);
378           break;
379         case YAD_COLUMN_NUM:
380         case YAD_COLUMN_SIZE:
381         case YAD_COLUMN_FLOAT:
382           renderer = gtk_cell_renderer_text_new ();
383           if (col->editable)
384             {
385               g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
386               g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited_cb), NULL);
387             }
388           column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", i, NULL);
389           set_column_title (column, col->name);
390           if (fore_col != -1)
391             gtk_tree_view_column_add_attribute (column, renderer, "foreground", fore_col);
392           if (back_col != -1)
393             gtk_tree_view_column_add_attribute (column, renderer, "cell-background", back_col);
394           if (font_col != -1)
395             gtk_tree_view_column_add_attribute (column, renderer, "font", font_col);
396           gtk_tree_view_column_set_sort_column_id (column, i);
397           gtk_tree_view_column_set_resizable (column, TRUE);
398           if (col->type == YAD_COLUMN_FLOAT)
399             gtk_tree_view_column_set_cell_data_func (column, renderer, float_col_format, GINT_TO_POINTER (i), NULL);
400           else if (col->type == YAD_COLUMN_SIZE)
401             gtk_tree_view_column_set_cell_data_func (column, renderer, size_col_format, GINT_TO_POINTER (i), NULL);
402           break;
403         case YAD_COLUMN_BAR:
404           renderer = gtk_cell_renderer_progress_new ();
405           column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "value", i, NULL);
406           set_column_title (column, col->name);
407           if (back_col != -1)
408             gtk_tree_view_column_add_attribute (column, renderer, "cell-background", back_col);
409           gtk_tree_view_column_set_sort_column_id (column, i);
410           gtk_tree_view_column_set_resizable (column, TRUE);
411           break;
412         default:
413           renderer = gtk_cell_renderer_text_new ();
414           if (col->editable)
415             {
416               g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
417               g_signal_connect (renderer, "edited", G_CALLBACK (cell_edited_cb), NULL);
418             }
419           if (options.data.no_markup)
420             column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "text", i, NULL);
421           else
422             column = gtk_tree_view_column_new_with_attributes (NULL, renderer, "markup", i, NULL);
423           set_column_title (column, col->name);
424           if (col->ellipsize)
425             g_object_set (G_OBJECT (renderer), "ellipsize", options.list_data.ellipsize, NULL);
426           if (col->wrap)
427             {
428               g_object_set (G_OBJECT (renderer), "wrap-width", options.list_data.wrap_width, NULL);
429               g_object_set (G_OBJECT (renderer), "wrap-mode", PANGO_WRAP_WORD_CHAR, NULL);
430             }
431           if (fore_col != -1)
432             gtk_tree_view_column_add_attribute (column, renderer, "foreground", fore_col);
433           if (back_col != -1)
434             gtk_tree_view_column_add_attribute (column, renderer, "cell-background", back_col);
435           if (font_col != -1)
436             gtk_tree_view_column_add_attribute (column, renderer, "font", font_col);
437           gtk_tree_view_column_set_sort_column_id (column, i);
438           gtk_tree_view_column_set_resizable (column, TRUE);
439 
440           if (col->type == YAD_COLUMN_TIP)
441             options.list_data.tooltip_column = i + 1;
442           break;
443         }
444       g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (i));
445       gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
446 
447       gtk_tree_view_column_set_clickable (column, options.list_data.clickable);
448 
449       if (col->type != YAD_COLUMN_CHECK && col->type != YAD_COLUMN_IMAGE)
450         {
451           if (i == options.list_data.expand_column - 1 || options.list_data.expand_column == 0)
452             gtk_tree_view_column_set_expand (column, TRUE);
453         }
454     }
455 
456   if (options.list_data.checkbox && !options.list_data.search_column)
457     options.list_data.search_column += 1;
458   if (options.list_data.search_column <= n_cols)
459     {
460       options.list_data.search_column -= 1;
461       gtk_tree_view_set_search_column (GTK_TREE_VIEW (list_view), options.list_data.search_column);
462     }
463 }
464 
465 static void
cell_set_data(GtkTreeIter * it,guint num,gchar * data)466 cell_set_data (GtkTreeIter *it, guint num, gchar *data)
467 {
468   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
469   YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, num);
470 
471   switch (col->type)
472     {
473     case YAD_COLUMN_CHECK:
474     case YAD_COLUMN_RADIO:
475         gtk_tree_store_set (GTK_TREE_STORE (model), it, num, get_bool_val (data), -1);
476       break;
477     case YAD_COLUMN_NUM:
478     case YAD_COLUMN_SIZE:
479       gtk_tree_store_set (GTK_TREE_STORE (model), it, num, g_ascii_strtoll (data, NULL, 10), -1);
480       break;
481     case YAD_COLUMN_FLOAT:
482       gtk_tree_store_set (GTK_TREE_STORE (model), it, num, g_ascii_strtod (data, NULL), -1);
483       break;
484     case YAD_COLUMN_BAR:
485       {
486         gint64 val = g_ascii_strtoll (data, NULL, 10);
487         if (val < 0)
488           val = 0;
489         if (val > 100)
490           val = 100;
491         gtk_tree_store_set (GTK_TREE_STORE (model), it, num, val, -1);
492         break;
493       }
494     case YAD_COLUMN_IMAGE:
495       {
496         GdkPixbuf *pb = get_pixbuf (data, YAD_SMALL_ICON, FALSE);
497         if (pb)
498           {
499             gtk_tree_store_set (GTK_TREE_STORE (model), it, num, pb, -1);
500             g_object_unref (pb);
501           }
502         break;
503       }
504     default:
505       if (data && *data)
506         gtk_tree_store_set (GTK_TREE_STORE (model), it, num, data, -1);
507       break;
508     }
509 }
510 
511 static gchar *
cell_get_data(GtkTreeIter * it,guint num)512 cell_get_data (GtkTreeIter *it, guint num)
513 {
514   gchar *data = NULL;
515   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
516   YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, num);
517 
518   switch (col->type)
519     {
520     case YAD_COLUMN_CHECK:
521     case YAD_COLUMN_RADIO:
522       {
523         gboolean bval;
524         gtk_tree_model_get (model, it, num, &bval, -1);
525         data = g_strdup (print_bool_val (bval));
526         break;
527       }
528     case YAD_COLUMN_NUM:
529     case YAD_COLUMN_SIZE:
530     case YAD_COLUMN_BAR:
531       {
532         gint64 nval;
533         gtk_tree_model_get (model, it, num, &nval, -1);
534         data = g_strdup_printf ("%ld", (long) nval);
535         break;
536       }
537     case YAD_COLUMN_FLOAT:
538       {
539         gdouble nval;
540         gtk_tree_model_get (model, it, num, &nval, -1);
541         data = g_strdup_printf ("%lf", nval);
542         break;
543       }
544     case YAD_COLUMN_IMAGE:
545       {
546         data = g_strdup ("''");
547         break;
548       }
549     default:
550       {
551         gchar *cval;
552         gtk_tree_model_get (model, it, num, &cval, -1);
553         if (cval)
554           data = g_shell_quote (cval);
555         break;
556       }
557     }
558 
559   return data;
560 }
561 
562 static gboolean
handle_stdin(GIOChannel * channel,GIOCondition condition,gpointer data)563 handle_stdin (GIOChannel *channel, GIOCondition condition, gpointer data)
564 {
565   static GtkTreeIter iter;
566   static gint column_count = 0;
567   static gint row_count = 0;
568   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
569 
570   if ((condition == G_IO_IN) || (condition == G_IO_IN + G_IO_HUP))
571     {
572       GError *err = NULL;
573       GString *string = g_string_new (NULL);
574       gboolean node_added = FALSE;
575 
576       while (channel->is_readable != TRUE)
577         usleep (100);
578 
579       do
580         {
581           gint status;
582 
583           do
584             {
585               status = g_io_channel_read_line_string (channel, string, NULL, &err);
586 
587               while (gtk_events_pending ())
588                 gtk_main_iteration ();
589             }
590           while (status == G_IO_STATUS_AGAIN);
591 
592           if (status != G_IO_STATUS_NORMAL)
593             {
594               if (err)
595                 {
596                   g_printerr ("yad_list_handle_stdin(): %s\n", err->message);
597                   g_error_free (err);
598                   err = NULL;
599                 }
600               /* stop handling */
601               g_io_channel_shutdown (channel, TRUE, NULL);
602               return FALSE;
603             }
604 
605           strip_new_line (string->str);
606 
607           /* clear list if ^L received */
608           if (string->str[0] == '\014')
609             {
610               GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
611               if (select_hndl)
612                 g_signal_handler_block (G_OBJECT (sel), select_hndl);
613               gtk_tree_store_clear (GTK_TREE_STORE (model));
614               row_count = column_count = 0;
615               if (row_hash)
616                 g_hash_table_remove_all (row_hash);
617               if (select_hndl)
618                 g_signal_handler_unblock (G_OBJECT (sel), select_hndl);
619               continue;
620             }
621 
622           if (row_count == 0 && column_count == 0)
623             {
624               if (options.list_data.tree_mode)
625                 {
626                   if (!node_added)
627                     {
628                       gchar **ids = g_strsplit (string->str, ":", 2);
629                       yad_list_add_row (GTK_TREE_STORE (model), &iter, ids[0], ids[1]);
630                       node_added = TRUE;
631                       continue;
632                     }
633                   else
634                     node_added = FALSE;
635                 }
636               else
637                 yad_list_add_row (GTK_TREE_STORE (model), &iter, NULL, NULL);
638             }
639           else if (column_count == n_cols)
640             {
641               /* We're starting a new row */
642               if (options.list_data.tree_mode)
643                 {
644                   if (!node_added)
645                     {
646                       gchar **ids = g_strsplit (string->str, ":", 2);
647                       yad_list_add_row (GTK_TREE_STORE (model), &iter, ids[0], ids[1]);
648                       node_added = TRUE;
649                       continue;
650                     }
651                   else
652                     node_added = FALSE;
653                 }
654               else
655                 yad_list_add_row (GTK_TREE_STORE (model), &iter, NULL, NULL);
656               column_count = 0;
657               row_count++;
658               if (options.list_data.limit && row_count >= options.list_data.limit)
659                 {
660                   gtk_tree_model_get_iter_first (model, &iter);
661                   gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
662                 }
663             }
664 
665           cell_set_data (&iter, column_count, string->str);
666           column_count++;
667         }
668       while (g_io_channel_get_buffer_condition (channel) == G_IO_IN);
669       g_string_free (string, TRUE);
670     }
671 
672   if (options.list_data.tree_expanded)
673     gtk_tree_view_expand_all (GTK_TREE_VIEW (list_view));
674 
675   if ((condition != G_IO_IN) && (condition != G_IO_IN + G_IO_HUP))
676     {
677       g_io_channel_shutdown (channel, TRUE, NULL);
678       return FALSE;
679     }
680 
681   return TRUE;
682 }
683 
684 static void
fill_data()685 fill_data ()
686 {
687   GtkTreeIter iter;
688   GtkTreeStore *model = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list_view)));
689   GIOChannel *channel;
690 
691   if (options.extra_data && *options.extra_data)
692     {
693       gchar **args = options.extra_data;
694       gint i = 0;
695 
696       gtk_widget_freeze_child_notify (list_view);
697 
698       while (args[i] != NULL)
699         {
700           gint j;
701 
702           if (options.list_data.tree_mode)
703             {
704               gchar **ids = g_strsplit (args[i], ":", 2);
705               yad_list_add_row (model, &iter, ids[0], ids[1]);
706               i++;
707             }
708           else
709             yad_list_add_row (model, &iter, NULL, NULL);
710           for (j = 0; j < n_cols; j++, i++)
711             {
712               if (args[i] == NULL)
713                 break;
714 
715               cell_set_data (&iter, j, args[i]);
716             }
717         }
718 
719       gtk_widget_thaw_child_notify (list_view);
720     }
721 
722   if (options.common_data.listen || !(options.extra_data && *options.extra_data))
723     {
724       channel = g_io_channel_unix_new (0);
725       g_io_channel_set_encoding (channel, NULL, NULL);
726       g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
727       g_io_add_watch (channel, G_IO_IN | G_IO_HUP, handle_stdin, NULL);
728     }
729 }
730 
731 static gchar *
get_data_as_string(GtkTreeIter * iter)732 get_data_as_string (GtkTreeIter *iter)
733 {
734   GString *str;
735   gchar *res;
736   guint i;
737 
738   str = g_string_new (NULL);
739 
740   for (i = 0; i < n_cols; i++)
741     {
742       gchar *val = cell_get_data (iter, i);
743       if (val)
744         {
745           g_string_append_printf (str, "%s ", val);
746           g_free (val);
747         }
748     }
749 
750   str->str[str->len-1] = '\0';
751   res = str->str;
752   g_string_free (str, FALSE);
753 
754   return res;
755 }
756 
757 static void edit_row_cb (GtkMenuItem *item, gpointer data);
758 
759 static void
double_click_cb(GtkTreeView * view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer data)760 double_click_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
761 {
762   GtkTreeModel *model;
763   GtkTreeIter iter;
764 
765   model = gtk_tree_view_get_model (view);
766 
767   if (options.list_data.dclick_action)
768     {
769       gchar *cmd, *args = NULL;
770 
771       if (gtk_tree_model_get_iter (model, &iter, path))
772         args = get_data_as_string (&iter);
773       else
774         args = g_strdup ("");
775 
776       if (g_strstr_len (options.list_data.dclick_action, -1, "%s"))
777         {
778           static GRegex *regex = NULL;
779 
780           if (!regex)
781             regex = g_regex_new ("\%s", G_REGEX_OPTIMIZE, 0, NULL);
782           cmd = g_regex_replace_literal (regex, options.list_data.dclick_action, -1, 0, args, 0, NULL);
783         }
784       else
785         cmd = g_strdup_printf ("%s %s", options.list_data.dclick_action, args);
786       g_free (args);
787 
788       if (cmd[0] == '@')
789         {
790           gchar *data = NULL;
791           gint exit;
792 
793           exit = run_command_sync (cmd + 1, &data);
794           if (exit == 0)
795             {
796               gint i;
797               gchar **lines = g_strsplit (data, "\n", 0);
798 
799               for (i = 0; i < n_cols; i++)
800                 {
801                   if (lines[i] == NULL)
802                     break;
803 
804                   cell_set_data (&iter, i, lines[i]);
805                 }
806               g_strfreev (lines);
807             }
808           g_free (data);
809         }
810       else
811         run_command_async (cmd);
812 
813       g_free (cmd);
814     }
815   else if (options.common_data.editable && options.list_data.row_action)
816     edit_row_cb (NULL, NULL);
817   else
818     {
819       if (options.list_data.checkbox)
820         {
821           if (gtk_tree_model_get_iter (model, &iter, path))
822             {
823               gboolean chk;
824 
825               gtk_tree_model_get (model, &iter, 0, &chk, -1);
826               chk = !chk;
827               gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 0, chk, -1);
828             }
829         }
830       else if (options.list_data.radiobox)
831         {
832           if (gtk_tree_model_get_iter (model, &iter, path))
833             {
834               gtk_tree_model_foreach (model, runtoggle, GINT_TO_POINTER (0));
835               gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 0, TRUE, -1);
836             }
837         }
838       else if (options.plug == -1)
839         yad_exit (options.data.def_resp);
840     }
841 }
842 
843 static void
select_cb(GtkTreeSelection * sel,gpointer data)844 select_cb (GtkTreeSelection *sel, gpointer data)
845 {
846   GtkTreeModel *model;
847   GtkTreeIter iter;
848   gchar *cmd, *args;
849 
850   if (!gtk_tree_selection_get_selected (sel, &model, &iter))
851     return;
852 
853   args = get_data_as_string (&iter);
854   if (!args)
855     args = g_strdup ("");
856 
857   if (g_strstr_len (options.list_data.select_action, -1, "%s"))
858     {
859       static GRegex *regex = NULL;
860 
861       if (!regex)
862         regex = g_regex_new ("\%s", G_REGEX_OPTIMIZE, 0, NULL);
863       cmd = g_regex_replace_literal (regex, options.list_data.select_action, -1, 0, args, 0, NULL);
864     }
865   else
866     cmd = g_strdup_printf ("%s %s", options.list_data.select_action, args);
867   g_free (args);
868 
869   run_command_async (cmd);
870 
871   g_free (cmd);
872 }
873 
874 static void
add_row_cb(GtkMenuItem * item,gpointer data)875 add_row_cb (GtkMenuItem *item, gpointer data)
876 {
877   GtkTreeModel *model;
878   GtkTreeIter iter;
879   gchar *cmd;
880 
881   model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
882   if (g_object_get_data (G_OBJECT (item), "child") != NULL)
883     {
884       GtkTreeIter parent;
885       GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
886 
887       if (gtk_tree_selection_get_selected (sel, NULL, &parent))
888         gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent);
889       else
890         gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
891     }
892   else
893     gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
894 
895   if (options.list_data.row_action)
896     {
897       gchar *out = NULL;
898       gint exit;
899 
900       /* hide menu first */
901       gtk_menu_popdown (GTK_MENU (data));
902       while (gtk_events_pending ())
903         gtk_main_iteration ();
904 
905       /* run command */
906       cmd = g_strdup_printf ("%s add", options.list_data.row_action);
907       exit = run_command_sync (cmd, &out);
908       g_free (cmd);
909       if (exit == 0)
910         {
911           guint i;
912           gchar **lines = g_strsplit (out, "\n", 0);
913 
914           for (i = 0; i < n_cols; i++)
915             {
916               if (lines[i] == NULL)
917                 break;
918 
919               cell_set_data (&iter, i, lines[i]);
920             }
921           g_strfreev (lines);
922         }
923       g_free (out);
924     }
925 }
926 
927 static void
edit_row_cb(GtkMenuItem * item,gpointer data)928 edit_row_cb (GtkMenuItem *item, gpointer data)
929 {
930   GtkTreeIter iter;
931   GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
932 
933   if (!gtk_tree_selection_get_selected (sel, NULL, &iter))
934     return;
935 
936   if (options.list_data.row_action)
937     {
938       gchar *cmd, *args, *out = NULL;
939       gint exit;
940 
941       /* hide menu first */
942       gtk_menu_popdown (GTK_MENU (data));
943       while (gtk_events_pending ())
944         gtk_main_iteration ();
945 
946       /* run command */
947       args = get_data_as_string (&iter);
948       cmd = g_strdup_printf ("%s edit %s", options.list_data.row_action, args);
949       g_free (args);
950       exit = run_command_sync (cmd, &out);
951       g_free (cmd);
952       if (exit == 0)
953         {
954           guint i;
955           gchar **lines = g_strsplit (out, "\n", 0);
956 
957           for (i = 0; i < n_cols; i++)
958             {
959               if (lines[i] == NULL)
960                 break;
961 
962               cell_set_data (&iter, i, lines[i]);
963             }
964           g_strfreev (lines);
965         }
966       g_free (out);
967     }
968 }
969 
970 static void
del_row_cb(GtkMenuItem * item,gpointer data)971 del_row_cb (GtkMenuItem *item, gpointer data)
972 {
973   GtkTreeIter iter;
974   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
975   GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
976 
977   if (gtk_tree_selection_get_selected (sel, NULL, &iter))
978     {
979       if (options.list_data.row_action)
980         {
981           gchar *cmd, *args;
982           gint exit;
983 
984           /* hide menu first */
985           gtk_menu_popdown (GTK_MENU (data));
986           while (gtk_events_pending ())
987             gtk_main_iteration ();
988 
989           /* run command */
990           args = get_data_as_string (&iter);
991           cmd = g_strdup_printf ("%s del %s", options.list_data.row_action, args);
992           g_free (args);
993           exit = run_command_sync (cmd, NULL);
994           g_free (cmd);
995           if (exit == 0)
996             gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
997         }
998       else
999         gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
1000     }
1001 }
1002 
1003 static void
copy_row_cb(GtkMenuItem * item,gpointer data)1004 copy_row_cb (GtkMenuItem *item, gpointer data)
1005 {
1006   GtkTreeIter iter;
1007   GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
1008   GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
1009 
1010   if (gtk_tree_selection_get_selected (sel, NULL, &iter))
1011     {
1012       GtkTreeIter new_iter, parent;
1013       gint i;
1014 
1015       if (gtk_tree_model_iter_parent (model, &parent, &iter))
1016         gtk_tree_store_insert_after (GTK_TREE_STORE (model), &new_iter, &parent, &iter);
1017       else
1018         gtk_tree_store_insert_after (GTK_TREE_STORE (model), &new_iter, NULL, &iter);
1019 
1020       for (i = 0; i < n_cols; i++)
1021         {
1022           GdkPixbuf *pb;
1023           gchar *tv;
1024           gint64 iv;
1025           gfloat fv;
1026           gboolean bv;
1027           YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, i);
1028 
1029           switch (col->type)
1030             {
1031             case YAD_COLUMN_CHECK:
1032             case YAD_COLUMN_RADIO:
1033               gtk_tree_model_get (model, &iter, i, &bv, -1);
1034               gtk_tree_store_set (GTK_TREE_STORE (model), &new_iter, i, bv, -1);
1035               break;
1036             case YAD_COLUMN_NUM:
1037             case YAD_COLUMN_SIZE:
1038             case YAD_COLUMN_BAR:
1039               gtk_tree_model_get (model, &iter, i, &iv, -1);
1040               gtk_tree_store_set (GTK_TREE_STORE (model), &new_iter, i, iv, -1);
1041               break;
1042             case YAD_COLUMN_FLOAT:
1043               gtk_tree_model_get (model, &iter, i, &fv, -1);
1044               gtk_tree_store_set (GTK_TREE_STORE (model), &new_iter, i, fv, -1);
1045               break;
1046             case YAD_COLUMN_IMAGE:
1047               gtk_tree_model_get (model, &iter, i, &pb, -1);
1048               gtk_tree_store_set (GTK_TREE_STORE (model), &new_iter, i, g_object_ref (pb), -1);
1049               break;
1050             default:
1051               gtk_tree_model_get (model, &iter, i, &tv, -1);
1052               gtk_tree_store_set (GTK_TREE_STORE (model), &new_iter, i, g_strdup (tv), -1);
1053               break;
1054             }
1055         }
1056     }
1057 }
1058 
1059 static gboolean
popup_menu_cb(GtkWidget * w,GdkEventButton * ev,gpointer data)1060 popup_menu_cb (GtkWidget *w, GdkEventButton *ev, gpointer data)
1061 {
1062   static GtkWidget *menu = NULL;
1063   if (ev->button == 3)
1064     {
1065       GtkWidget *item;
1066 
1067       if (menu == NULL)
1068         {
1069           menu = gtk_menu_new ();
1070           gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
1071 
1072           item = gtk_menu_item_new_with_label (_("Add row"));
1073           gtk_widget_show (item);
1074           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1075           g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (add_row_cb), menu);
1076 
1077           if (options.list_data.tree_mode)
1078             {
1079               item = gtk_menu_item_new_with_label (_("Add child row"));
1080               gtk_widget_show (item);
1081               gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1082               g_object_set_data (G_OBJECT (item), "child", "1");
1083               g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (add_row_cb), menu);
1084             }
1085 
1086           item = gtk_menu_item_new_with_label (_("Delete row"));
1087           gtk_widget_show (item);
1088           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1089           g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (del_row_cb), menu);
1090 
1091           if (options.list_data.row_action)
1092             {
1093               item = gtk_menu_item_new_with_label (_("Edit row"));
1094               gtk_widget_show (item);
1095               gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1096               g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (del_row_cb), menu);
1097             }
1098 
1099           item = gtk_menu_item_new_with_label (_("Duplicate row"));
1100           gtk_widget_show (item);
1101           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1102           g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (copy_row_cb), menu);
1103 
1104           gtk_widget_show (menu);
1105         }
1106       gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
1107     }
1108   return FALSE;
1109 }
1110 
1111 static gboolean
row_sep_func(GtkTreeModel * m,GtkTreeIter * it,gpointer data)1112 row_sep_func (GtkTreeModel *m, GtkTreeIter *it, gpointer data)
1113 {
1114   gchar *name;
1115 
1116   if (!options.list_data.sep_value)
1117     return FALSE;
1118 
1119   gtk_tree_model_get (m, it, options.list_data.sep_column - 1, &name, -1);
1120   return (strcmp (name, options.list_data.sep_value) == 0);
1121 }
1122 
1123 static inline void
parse_cols_props()1124 parse_cols_props ()
1125 {
1126   /* set editable property for columns */
1127   if (options.common_data.editable)
1128     {
1129       if (options.list_data.editable_cols)
1130         {
1131           gchar **cnum;
1132           guint i = 0;
1133 
1134           cnum = g_strsplit (options.list_data.editable_cols, ",", -1);
1135 
1136           while (cnum[i])
1137             {
1138               gint num = atoi (cnum[i]);
1139               if (num)
1140                 {
1141                   YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, num - 1);
1142                   if (col)
1143                     col->editable = TRUE;
1144                 }
1145               i++;
1146             }
1147           g_strfreev (cnum);
1148         }
1149       else
1150         {
1151           GSList *c;
1152           for (c = options.list_data.columns; c; c = c->next)
1153             {
1154               YadColumn *col = (YadColumn *) c->data;
1155               col->editable = TRUE;
1156             }
1157         }
1158     }
1159 
1160   /* set wrap property for columns */
1161   if (options.list_data.wrap_width > 0)
1162     {
1163       if (options.list_data.wrap_cols)
1164         {
1165           gchar **cnum;
1166           guint i = 0;
1167 
1168           cnum = g_strsplit (options.list_data.wrap_cols, ",", -1);
1169 
1170           while (cnum[i])
1171             {
1172               gint num = atoi (cnum[i]);
1173               if (num)
1174                 {
1175                   YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, num - 1);
1176                   if (col)
1177                     col->wrap = TRUE;
1178                 }
1179               i++;
1180             }
1181           g_strfreev (cnum);
1182         }
1183       else
1184         {
1185           GSList *c;
1186           for (c = options.list_data.columns; c; c = c->next)
1187             {
1188               YadColumn *col = (YadColumn *) c->data;
1189               col->wrap = TRUE;
1190             }
1191         }
1192     }
1193 
1194   /* set ellipsize property for columns */
1195   if (options.list_data.ellipsize)
1196     {
1197       if (options.list_data.ellipsize_cols)
1198         {
1199           gchar **cnum;
1200           guint i = 0;
1201 
1202           cnum = g_strsplit (options.list_data.ellipsize_cols, ",", -1);
1203 
1204           while (cnum[i])
1205             {
1206               gint num = atoi (cnum[i]);
1207               if (num)
1208                 {
1209                   YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, num - 1);
1210                   if (col)
1211                     col->ellipsize = TRUE;
1212                 }
1213               i++;
1214             }
1215           g_strfreev (cnum);
1216         }
1217       else
1218         {
1219           GSList *c;
1220           for (c = options.list_data.columns; c; c = c->next)
1221             {
1222               YadColumn *col = (YadColumn *) c->data;
1223               col->ellipsize = TRUE;
1224             }
1225         }
1226     }
1227 }
1228 
1229 GtkWidget *
list_create_widget(GtkWidget * dlg)1230 list_create_widget (GtkWidget *dlg)
1231 {
1232   GtkWidget *w;
1233   GtkTreeModel *model;
1234 
1235   fore_col = back_col = font_col = -1;
1236 
1237   if (options.debug)
1238     {
1239       if (options.list_data.checkbox || options.list_data.radiobox)
1240         g_printerr (_("WARNING: You are use --checklist or --radiolist option. Those options obsoleted and will be removed in the future\n"));
1241     }
1242 
1243   n_cols = g_slist_length (options.list_data.columns);
1244   if (n_cols == 0)
1245     {
1246       g_printerr (_("No column titles specified for List dialog.\n"));
1247       return NULL;
1248     }
1249 
1250   if (options.list_data.tree_mode)
1251     row_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gtk_tree_path_free);
1252 
1253   parse_cols_props ();
1254 
1255   /* create widget */
1256   w = gtk_scrolled_window_new (NULL, NULL);
1257   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w), GTK_SHADOW_ETCHED_IN);
1258   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), options.hscroll_policy, options.vscroll_policy);
1259 
1260   model = create_model ();
1261 
1262   list_view = gtk_tree_view_new_with_model (model);
1263   gtk_widget_set_name (list_view, "yad-list-widget");
1264   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list_view), !options.list_data.no_headers);
1265   gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (list_view), options.list_data.grid_lines);
1266   gtk_tree_view_set_reorderable (GTK_TREE_VIEW (list_view), options.common_data.editable);
1267   g_object_unref (model);
1268 
1269   gtk_container_add (GTK_CONTAINER (w), list_view);
1270 
1271   add_columns ();
1272 
1273   /* add popup menu */
1274   if (options.common_data.editable)
1275     g_signal_connect_swapped (G_OBJECT (list_view), "button_press_event", G_CALLBACK (popup_menu_cb), NULL);
1276 
1277   /* add tooltip column */
1278   if (options.list_data.tooltip_column > 0)
1279     {
1280       if (options.list_data.simple_tips || options.data.no_markup)
1281         {
1282           gtk_widget_set_has_tooltip (list_view, TRUE);
1283           g_signal_connect (G_OBJECT (list_view), "query-tooltip", G_CALLBACK (tooltip_cb), NULL);
1284         }
1285       else
1286         gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (list_view), options.list_data.tooltip_column - 1);
1287     }
1288 
1289   /* set search function for regex search */
1290   if (options.list_data.search_column != -1 && options.list_data.regex_search)
1291     {
1292       YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns,
1293                                                        options.list_data.search_column);
1294 
1295       if (col->type == YAD_COLUMN_TEXT)
1296         gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (list_view), regex_search, NULL, NULL);
1297     }
1298 
1299   /* add row separator function */
1300   if (options.list_data.sep_column > 0)
1301     gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (list_view), row_sep_func, NULL, NULL);
1302 
1303   if (options.list_data.no_selection)
1304     {
1305       GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
1306       gtk_tree_selection_set_mode (sel, GTK_SELECTION_NONE);
1307       gtk_widget_set_can_focus (list_view, FALSE);
1308     }
1309   else
1310     {
1311       GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
1312 
1313       if (options.common_data.multi && !options.list_data.checkbox && !options.list_data.radiobox)
1314         gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
1315 
1316       if (!options.common_data.multi && options.list_data.select_action)
1317         select_hndl = g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (select_cb), NULL);
1318 
1319       g_signal_connect (G_OBJECT (list_view), "row-activated", G_CALLBACK (double_click_cb), dlg);
1320       g_signal_connect (G_OBJECT (list_view), "key-press-event", G_CALLBACK (list_activate_cb), dlg);
1321     }
1322 
1323   /* load data */
1324   fill_data ();
1325 
1326   if (options.list_data.tree_expanded)
1327     gtk_tree_view_expand_all (GTK_TREE_VIEW (list_view));
1328 
1329   return w;
1330 }
1331 
1332 static void
print_col(GtkTreeModel * model,GtkTreeIter * iter,gint num)1333 print_col (GtkTreeModel *model, GtkTreeIter *iter, gint num)
1334 {
1335   YadColumn *col = (YadColumn *) g_slist_nth_data (options.list_data.columns, num);
1336 
1337   /* don't print attributes */
1338   if (col->type == YAD_COLUMN_ATTR_FORE || col->type == YAD_COLUMN_ATTR_BACK || col->type == YAD_COLUMN_ATTR_FONT)
1339     return;
1340 
1341   switch (col->type)
1342     {
1343     case YAD_COLUMN_CHECK:
1344     case YAD_COLUMN_RADIO:
1345       {
1346         gboolean bval;
1347         gtk_tree_model_get (model, iter, num, &bval, -1);
1348         if (options.common_data.quoted_output)
1349           g_printf ("'%s'", print_bool_val (bval));
1350         else
1351           g_printf ("%s", print_bool_val (bval));
1352         break;
1353       }
1354     case YAD_COLUMN_NUM:
1355     case YAD_COLUMN_SIZE:
1356     case YAD_COLUMN_BAR:
1357       {
1358         gint64 nval;
1359         gtk_tree_model_get (model, iter, num, &nval, -1);
1360         if (options.common_data.quoted_output)
1361           g_printf ("'%ld'", (long) nval);
1362         else
1363           g_printf ("%ld", (long) nval);
1364         break;
1365       }
1366     case YAD_COLUMN_FLOAT:
1367       {
1368         gdouble nval;
1369         gtk_tree_model_get (model, iter, num, &nval, -1);
1370         if (options.common_data.quoted_output)
1371           g_printf ("'%.*f'", options.common_data.float_precision, nval);
1372         else
1373           g_printf ("%.*f", options.common_data.float_precision, nval);
1374         break;
1375       }
1376     case YAD_COLUMN_IMAGE:
1377       if (options.common_data.quoted_output)
1378         g_printf ("''");
1379       break;
1380     default:
1381       {
1382         gchar *val;
1383         gtk_tree_model_get (model, iter, num, &val, -1);
1384         if (options.common_data.quoted_output)
1385           {
1386             gchar *buf = g_shell_quote (val);
1387             g_printf ("%s", buf);
1388             g_free (buf);
1389           }
1390         else
1391           g_printf ("%s", val ? val : "");
1392         break;
1393       }
1394     }
1395   g_printf ("%s", options.common_data.separator);
1396 }
1397 
1398 static void
print_selected(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1399 print_selected (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1400 {
1401   gint i,col;
1402 
1403   col = options.list_data.print_column;
1404 
1405   if (col && col <= n_cols)
1406     print_col (model, iter, col - 1);
1407   else
1408     {
1409       for (i = 0; i < n_cols; i++)
1410         print_col (model, iter, i);
1411     }
1412   g_printf ("\n");
1413 }
1414 
1415 static void
print_all(GtkTreeModel * model,GtkTreeIter * parent)1416 print_all (GtkTreeModel *model, GtkTreeIter *parent)
1417 {
1418   GtkTreeIter iter;
1419   gint i;
1420 
1421   if (gtk_tree_model_iter_children (model, &iter, parent))
1422     {
1423       do
1424         {
1425           for (i = 0; i < n_cols; i++)
1426             print_col (model, &iter, i);
1427           g_printf ("\n");
1428           /* print children */
1429           print_all (model, &iter);
1430         }
1431       while (gtk_tree_model_iter_next (model, &iter));
1432     }
1433 }
1434 
1435 void
list_print_result(void)1436 list_print_result (void)
1437 {
1438   GtkTreeModel *model;
1439   gint col = options.list_data.print_column;
1440 
1441   model = gtk_tree_view_get_model (GTK_TREE_VIEW (list_view));
1442 
1443   if (options.list_data.print_all)
1444     {
1445       print_all (model, NULL);
1446       return;
1447     }
1448 
1449   if (options.list_data.checkbox || options.list_data.radiobox)
1450     {
1451       // don't check in cycle
1452       if (col > 0)
1453         {
1454           GtkTreeIter iter;
1455 
1456           if (gtk_tree_model_get_iter_first (model, &iter))
1457             {
1458               do
1459                 {
1460                   gboolean chk;
1461                   gtk_tree_model_get (model, &iter, 0, &chk, -1);
1462                   if (chk)
1463                     {
1464                       print_col (model, &iter, col - 1);
1465                       g_printf ("\n");
1466                     }
1467                 }
1468               while (gtk_tree_model_iter_next (model, &iter));
1469             }
1470         }
1471       else
1472         {
1473           GtkTreeIter iter;
1474 
1475           if (gtk_tree_model_get_iter_first (model, &iter))
1476             {
1477               do
1478                 {
1479                   gboolean chk;
1480                   gtk_tree_model_get (model, &iter, 0, &chk, -1);
1481                   if (chk)
1482                     {
1483                       gint i;
1484                       for (i = 0; i < n_cols; i++)
1485                         print_col (model, &iter, i);
1486                       g_printf ("\n");
1487                     }
1488                 }
1489               while (gtk_tree_model_iter_next (model, &iter));
1490             }
1491         }
1492     }
1493   else
1494     {
1495       GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
1496       gtk_tree_selection_selected_foreach (sel, print_selected, NULL);
1497     }
1498 }
1499