libyui-gtk  2.44.9
YGTreeView.cc
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 /*
5  Textdomain "gtk"
6  */
7 
8 #include "YGi18n.h"
9 #define YUILogComponent "gtk"
10 #include <yui/Libyui_config.h>
11 #include "YGUI.h"
12 #include "YGUtils.h"
13 #include "YGWidget.h"
14 #include "YSelectionWidget.h"
15 #include "YGSelectionStore.h"
16 #include "ygtktreeview.h"
17 #include "YGMacros.h"
18 #include <string.h>
19 
20 /* A generic widget for table related widgets. */
21 
23 {
24 protected:
25  guint m_blockTimeout;
26  int markColumn;
27  GtkWidget *m_count;
28 
29 public:
30  YGTreeView (YWidget *ywidget, YWidget *parent, const std::string &label, bool tree)
31  : YGScrolledWidget (ywidget, parent, label, YD_VERT, YGTK_TYPE_TREE_VIEW, NULL),
32  YGSelectionStore (tree)
33  {
34  gtk_tree_view_set_headers_visible (getView(), FALSE);
35 
36  /* Yast tools expect the user to be unable to un-select the row. They
37  generally don't check to see if the returned value is -1. So, just
38  disallow un-selection. */
39  gtk_tree_selection_set_mode (getSelection(), GTK_SELECTION_BROWSE);
40 
41  connect (getSelection(), "changed", G_CALLBACK (selection_changed_cb), this);
42  connect (getWidget(), "row-activated", G_CALLBACK (activated_cb), this);
43  connect (getWidget(), "right-click", G_CALLBACK (right_click_cb), this);
44 
45  m_blockTimeout = 0; // GtkTreeSelection idiotically fires when showing widget
46  markColumn = -1; m_count = NULL;
47  blockSelected();
48  g_signal_connect (getWidget(), "map", G_CALLBACK (block_init_cb), this);
49  }
50 
51  virtual ~YGTreeView()
52  { if (m_blockTimeout) g_source_remove (m_blockTimeout); }
53 
54  inline GtkTreeView *getView()
55  { return GTK_TREE_VIEW (getWidget()); }
56  inline GtkTreeSelection *getSelection()
57  { return gtk_tree_view_get_selection (getView()); }
58 
59  void addTextColumn (int iconCol, int textCol)
60  { addTextColumn ("", YAlignUnchanged, iconCol, textCol); }
61 
62  void addTextColumn (const std::string &header, YAlignmentType align, int icon_col, int text_col)
63  {
64  gfloat xalign = -1;
65  switch (align) {
66  case YAlignBegin: xalign = 0.0; break;
67  case YAlignCenter: xalign = 0.5; break;
68  case YAlignEnd: xalign = 1.0; break;
69  case YAlignUnchanged: break;
70  }
71 
72  GtkTreeViewColumn *column = gtk_tree_view_column_new();
73  gtk_tree_view_column_set_title (column, header.c_str());
74 
75  GtkCellRenderer *renderer;
76  renderer = gtk_cell_renderer_pixbuf_new();
77  gtk_tree_view_column_pack_start (column, renderer, FALSE);
78  gtk_tree_view_column_set_attributes (column, renderer, "pixbuf", icon_col, NULL);
79 
80  renderer = gtk_cell_renderer_text_new();
81  gtk_tree_view_column_pack_start (column, renderer, TRUE);
82  gtk_tree_view_column_set_attributes (column, renderer, "text", text_col, NULL);
83  if (xalign != -1)
84  g_object_set (renderer, "xalign", xalign, NULL);
85 
86  gtk_tree_view_column_set_resizable (column, TRUE);
87  gtk_tree_view_append_column (getView(), column);
88  if (gtk_tree_view_get_search_column (getView()) == -1)
89  gtk_tree_view_set_search_column (getView(), text_col);
90  }
91 
92  void addCheckColumn (int check_col)
93  {
94  GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
95  g_object_set_data (G_OBJECT (renderer), "column", GINT_TO_POINTER (check_col));
96  GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (
97  NULL, renderer, "active", check_col, NULL);
98  gtk_tree_view_column_set_cell_data_func (column, renderer, inconsistent_mark_cb, this, NULL);
99  g_signal_connect (G_OBJECT (renderer), "toggled",
100  G_CALLBACK (toggled_cb), this);
101 
102  gtk_tree_view_column_set_resizable (column, TRUE);
103  gtk_tree_view_append_column (getView(), column);
104  if (markColumn == -1)
105  markColumn = check_col;
106  }
107 
108  void readModel()
109  { gtk_tree_view_set_model (getView(), getModel()); }
110 
111  void addCountWidget (YWidget *yparent)
112  {
113  bool mainWidget = !yparent || !strcmp (yparent->widgetClass(), "YVBox") || !strcmp (yparent->widgetClass(), "YReplacePoint");
114  if (mainWidget) {
115  m_count = gtk_label_new ("0");
116  GtkWidget *hbox = YGTK_HBOX_NEW(4);
117  gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
118 
119  GtkWidget *label = gtk_label_new (_("Total selected:"));
120  //gtk_box_pack_start (GTK_BOX (hbox), gtk_event_box_new(), TRUE, TRUE, 0);
121  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
122  gtk_box_pack_start (GTK_BOX (hbox), m_count, FALSE, TRUE, 0);
123  gtk_box_pack_start (GTK_BOX (YGWidget::getWidget()), hbox, FALSE, TRUE, 0);
124  gtk_widget_show_all (hbox);
125  }
126  }
127 
128  void syncCount()
129  {
130  if (!m_count) return;
131 
132  struct inner {
133  static gboolean foreach (
134  GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis)
135  {
136  YGTreeView *pThis = (YGTreeView *) _pThis;
137  gboolean mark;
138  gtk_tree_model_get (model, iter, pThis->markColumn, &mark, -1);
139  if (mark) {
140  int count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model), "count"));
141  g_object_set_data (G_OBJECT (model), "count", GINT_TO_POINTER (count+1));
142  }
143  return FALSE;
144  }
145  };
146 
147  GtkTreeModel *model = getModel();
148  g_object_set_data (G_OBJECT (model), "count", 0);
149  gtk_tree_model_foreach (model, inner::foreach, this);
150 
151  int count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model), "count"));
152  gchar *str = g_strdup_printf ("%d", count);
153  gtk_label_set_text (GTK_LABEL (m_count), str);
154  g_free (str);
155  }
156 
157  void focusItem (YItem *item, bool select)
158  {
159  GtkTreeIter iter;
160  getTreeIter (item, &iter);
161  blockSelected();
162 
163  if (select) {
164  GtkTreePath *path = gtk_tree_model_get_path (getModel(), &iter);
165  gtk_tree_view_expand_to_path (getView(), path);
166 
167  if (gtk_tree_selection_get_mode (getSelection()) != GTK_SELECTION_MULTIPLE)
168  gtk_tree_view_scroll_to_cell (getView(), path, NULL, TRUE, 0.5, 0);
169  gtk_tree_path_free (path);
170 
171  gtk_tree_selection_select_iter (getSelection(), &iter);
172  }
173  else
174  gtk_tree_selection_unselect_iter (getSelection(), &iter);
175  }
176 
177  void unfocusAllItems()
178  {
179  blockSelected();
180  gtk_tree_selection_unselect_all (getSelection());
181  }
182 
183  void unmarkAll()
184  {
185  struct inner {
186  static gboolean foreach_unmark (
187  GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis)
188  {
189  YGTreeView *pThis = (YGTreeView *) _pThis;
190  pThis->setRowMark (iter, pThis->markColumn, FALSE);
191  return FALSE;
192  }
193  };
194 
195  gtk_tree_model_foreach (getModel(), inner::foreach_unmark, this);
196  }
197 
198  YItem *getFocusItem()
199  {
200  GtkTreeIter iter;
201  if (gtk_tree_selection_get_selected (getSelection(), NULL, &iter))
202  return getYItem (&iter);
203  return NULL;
204  }
205 
206  virtual bool _immediateMode() { return true; }
207  virtual bool _shrinkable() { return false; }
208  virtual bool _recursiveSelection() { return false; }
209 
210  void setMark (GtkTreeIter *iter, YItem *yitem, gint column, bool state, bool recursive)
211  {
212  setRowMark (iter, column, state);
213  yitem->setSelected (state);
214 
215  if (recursive)
216  for (YItemConstIterator it = yitem->childrenBegin();
217  it != yitem->childrenEnd(); it++) {
218  GtkTreeIter _iter;
219  getTreeIter (*it, &_iter);
220  setMark (&_iter, *it, column, state, true);
221  }
222  }
223 
224  void toggleMark (GtkTreePath *path, gint column)
225  {
226  GtkTreeIter iter;
227  if (!gtk_tree_model_get_iter (getModel(), &iter, path))
228  return;
229  gboolean state;
230  gtk_tree_model_get (getModel(), &iter, column, &state, -1);
231  state = !state;
232 
233  YItem *yitem = getYItem (&iter);
234  setMark (&iter, yitem, column, state, _recursiveSelection());
235  syncCount();
236  emitEvent (YEvent::ValueChanged);
237  }
238 
239  // YGWidget
240 
241  virtual unsigned int getMinSize (YUIDimension dim)
242  {
243  if (dim == YD_VERT)
244  return YGUtils::getCharsHeight (getWidget(), _shrinkable() ? 2 : 5);
245  return 80;
246  }
247 
248 protected:
249  static gboolean block_selected_timeout_cb (gpointer data)
250  {
251  YGTreeView *pThis = (YGTreeView *) data;
252  pThis->m_blockTimeout = 0;
253  return FALSE;
254  }
255 
256  void blockSelected()
257  { // GtkTreeSelection only fires when idle; so set a timeout
258  if (m_blockTimeout) g_source_remove (m_blockTimeout);
259  m_blockTimeout = g_timeout_add_full (G_PRIORITY_LOW, 50, block_selected_timeout_cb, this, NULL);
260  }
261 
262  static void block_init_cb (GtkWidget *widget, YGTreeView *pThis)
263  { pThis->blockSelected(); }
264 
265  // callbacks
266 
267  static bool all_marked (GtkTreeModel *model, GtkTreeIter *iter, int mark_col)
268  {
269  gboolean marked;
270  GtkTreeIter child_iter;
271  if (gtk_tree_model_iter_children (model, &child_iter, iter))
272  do {
273  gtk_tree_model_get (model, &child_iter, mark_col, &marked, -1);
274  if (!marked) return false;
275  all_marked (model, &child_iter, mark_col);
276  } while (gtk_tree_model_iter_next (model, &child_iter));
277  return true;
278  }
279 
280  static void inconsistent_mark_cb (GtkTreeViewColumn *column,
281  GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
282  { // used for trees -- show inconsistent if one node is check but another isn't
283  YGTreeView *pThis = (YGTreeView *) data;
284  gboolean marked;
285  gtk_tree_model_get (model, iter, pThis->markColumn, &marked, -1);
286  gboolean consistent = !marked || all_marked (model, iter, pThis->markColumn);
287  g_object_set (G_OBJECT (cell), "inconsistent", !consistent, NULL);
288  }
289 
290  static void selection_changed_cb (GtkTreeSelection *selection, YGTreeView *pThis)
291  {
292  struct inner {
293  static gboolean foreach_sync_select (
294  GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis)
295  {
296  YGTreeView *pThis = (YGTreeView *) _pThis;
297  GtkTreeSelection *selection = pThis->getSelection();
298  bool sel = gtk_tree_selection_iter_is_selected (selection, iter);
299  pThis->getYItem (iter)->setSelected (sel);
300  return FALSE;
301  }
302  };
303 
304  if (pThis->m_blockTimeout) return;
305  if (pThis->markColumn == -1)
306  gtk_tree_model_foreach (pThis->getModel(), inner::foreach_sync_select, pThis);
307  if (pThis->_immediateMode())
308  pThis->emitEvent (YEvent::SelectionChanged, IF_NOT_PENDING_EVENT);
309  }
310 
311  static void activated_cb (GtkTreeView *tree_view, GtkTreePath *path,
312  GtkTreeViewColumn *column, YGTreeView* pThis)
313  {
314  if (pThis->markColumn >= 0)
315  pThis->toggleMark (path, pThis->markColumn);
316  else {
317  // for tree - expand/collpase double-clicked rows
318  if (gtk_tree_view_row_expanded (tree_view, path))
319  gtk_tree_view_collapse_row (tree_view, path);
320  else
321  gtk_tree_view_expand_row (tree_view, path, FALSE);
322 
323  pThis->emitEvent (YEvent::Activated);
324  }
325  }
326 
327  static void toggled_cb (GtkCellRendererToggle *renderer, gchar *path_str,
328  YGTreeView *pThis)
329  {
330  GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
331  gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (renderer), "column"));
332  pThis->toggleMark (path, column);
333  gtk_tree_path_free (path);
334 
335  // un/marking a sub-node can cause changes of "inconsistency"
336  if (gtk_tree_path_get_depth (path) >= 2)
337  gtk_widget_queue_draw (pThis->getWidget());
338  }
339 
340  static void right_click_cb (YGtkTreeView *view, gboolean outreach, YGTreeView *pThis)
341  { pThis->emitEvent (YEvent::ContextMenuActivated); }
342 };
343 
344 #include "YTable.h"
345 #include "YGDialog.h"
346 #include <gdk/gdkkeysyms.h>
347 #include <string.h>
348 
349 class YGTable : public YTable, public YGTreeView
350 {
351 public:
352  YGTable (YWidget *parent, YTableHeader *headers, bool multiSelection)
353  : YTable (NULL, headers, multiSelection),
354  YGTreeView (this, parent, std::string(), false)
355  {
356  gtk_tree_view_set_headers_visible (getView(), TRUE);
357  // gtk_tree_view_set_rules_hint (getView(), columns() > 1);
358  ygtk_tree_view_set_empty_text (YGTK_TREE_VIEW (getView()), _("No entries."));
359  if (multiSelection)
360  gtk_tree_selection_set_mode (getSelection(), GTK_SELECTION_MULTIPLE);
361 
362  GType types [columns()*2];
363  for (int i = 0; i < columns(); i++) {
364  int t = i*2;
365  types[t+0] = GDK_TYPE_PIXBUF;
366  types[t+1] = G_TYPE_STRING;
367  addTextColumn (header(i), alignment (i), t, t+1);
368  }
369  createStore (columns()*2, types);
370  readModel();
371  if (!keepSorting())
372  setSortable (true);
373 
374  // if last col is aligned: add some dummy so that it doesn't expand.
375  YAlignmentType lastAlign = alignment (columns()-1);
376  if (lastAlign == YAlignCenter || lastAlign == YAlignEnd)
377  gtk_tree_view_append_column (getView(), gtk_tree_view_column_new());
378 
379  g_signal_connect (getWidget(), "key-press-event", G_CALLBACK (key_press_event_cb), this);
380  }
381 
382  void setSortable (bool sortable)
383  {
384  if (!sortable && !gtk_widget_get_realized (getWidget()))
385  return;
386  int n = 0;
387  GList *columns = gtk_tree_view_get_columns (getView());
388  for (GList *i = columns; i; i = i->next, n++) {
389  GtkTreeViewColumn *column = (GtkTreeViewColumn *) i->data;
390  if (n >= YGTable::columns())
391  break;
392  if (sortable) {
393  int index = (n*2)+1;
394  if (!sortable)
395  index = -1;
396  gtk_tree_sortable_set_sort_func (
397  GTK_TREE_SORTABLE (getModel()), index, tree_sort_cb,
398  GINT_TO_POINTER (index), NULL);
399  gtk_tree_view_column_set_sort_column_id (column, index);
400  }
401  else
402  gtk_tree_view_column_set_sort_column_id (column, -1);
403  }
404  g_list_free (columns);
405  }
406 
407  void setCell (GtkTreeIter *iter, int column, const YTableCell *cell)
408  {
409  if (!cell) return;
410  std::string label (cell->label());
411  if (label == "X")
412  label = YUI::app()->glyph (YUIGlyph_CheckMark);
413 
414  int index = column * 2;
415  setRowText (iter, index, cell->iconName(), index+1, label, this);
416  }
417 
418  // YGTreeView
419 
420  virtual bool _immediateMode() { return immediateMode(); }
421 
422  // YTable
423 
424  virtual void setKeepSorting (bool keepSorting)
425  {
426  YTable::setKeepSorting (keepSorting);
427  setSortable (!keepSorting);
428  if (!keepSorting) {
429  GtkTreeViewColumn *column = gtk_tree_view_get_column (getView(), 0);
430  if (column)
431  gtk_tree_view_column_clicked (column);
432  }
433  }
434 
435  virtual void cellChanged (const YTableCell *cell)
436  {
437  GtkTreeIter iter;
438  getTreeIter (cell->parent(), &iter);
439  setCell (&iter, cell->column(), cell);
440  }
441 
442  // YGSelectionStore
443 
444  void doAddItem (YItem *_item)
445  {
446  YTableItem *item = dynamic_cast <YTableItem *> (_item);
447  if (item) {
448  GtkTreeIter iter;
449  addRow (item, &iter);
450  int i = 0;
451  for (YTableCellIterator it = item->cellsBegin();
452  it != item->cellsEnd(); it++)
453  if (i >= columns())
454  {
455  yuiWarning() << "Item contains too many columns, current column is (starting from 0) " << i
456  << " but only " << columns() << " columns are configured. Skipping..." << std::endl;
457  }
458  else
459  setCell (&iter, i++, *it);
460  if (item->selected())
461  focusItem (item, true);
462  }
463  else
464  yuiError() << "Can only add YTableItems to a YTable.\n";
465  }
466 
467  void doSelectItem (YItem *item, bool select)
468  { focusItem (item, select); }
469 
470  void doDeselectAllItems()
471  { unfocusAllItems(); }
472 
473  // callbacks
474 
475  static void activateButton (YWidget *button)
476  {
477  YWidgetEvent *event = new YWidgetEvent (button, YEvent::Activated);
478  YGUI::ui()->sendEvent (event);
479  }
480 
481  static void hack_right_click_cb (YGtkTreeView *view, gboolean outreach, YGTable *pThis)
482  {
483  if (pThis->notifyContextMenu())
484  return YGTreeView::right_click_cb (view, outreach, pThis);
485 
486  // If no context menu is specified, hack one ;-)
487 
488  struct inner {
489  static void key_activate_cb (GtkMenuItem *item, YWidget *button)
490  { activateButton (button); }
491  static void appendItem (GtkWidget *menu, const gchar *stock, int key)
492  {
493  YWidget *button = YGDialog::currentDialog()->getFunctionWidget (key);
494  if (button) {
495  GtkWidget *item;
496  item = gtk_menu_item_new_with_mnemonic (stock);
497  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
498  g_signal_connect (G_OBJECT (item), "activate",
499  G_CALLBACK (key_activate_cb), button);
500  }
501  }
502  };
503 
504  GtkWidget *menu = gtk_menu_new();
505  YGDialog *dialog = YGDialog::currentDialog();
506  if (dialog->getClassWidgets ("YTable").size() == 1) {
507  // if more than one table exists, function keys would be ambiguous
508  if (outreach) {
509  if (dialog->getFunctionWidget(3))
510  inner::appendItem (menu, "list-add", 3);
511  }
512  else {
513  if (dialog->getFunctionWidget(4))
514  inner::appendItem (menu, "edit-cut", 4);
515  if (dialog->getFunctionWidget(5))
516  inner::appendItem (menu, "list-remove", 5);
517  }
518  }
519 
520  menu = ygtk_tree_view_append_show_columns_item (YGTK_TREE_VIEW (view), menu);
521  ygtk_tree_view_popup_menu (view, menu);
522  }
523 
524  static gboolean key_press_event_cb (GtkWidget *widget, GdkEventKey *event, YGTable *pThis)
525  {
526  if (event->keyval == GDK_KEY_Delete) {
527  YWidget *button = YGDialog::currentDialog()->getFunctionWidget (5);
528  if (button)
529  activateButton (button);
530  else
531  gtk_widget_error_bell (widget);
532  return TRUE;
533  }
534  return FALSE;
535  }
536 
537  static gint tree_sort_cb (
538  GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer _index)
539  {
540  int index = GPOINTER_TO_INT (_index);
541  gchar *str_a, *str_b;
542  gtk_tree_model_get (model, a, index, &str_a, -1);
543  gtk_tree_model_get (model, b, index, &str_b, -1);
544  if (!str_a) str_a = g_strdup ("");
545  if (!str_b) str_b = g_strdup ("");
546  int ret = strcmp (str_a, str_b);
547  g_free (str_a); g_free (str_b);
548  return ret;
549  }
550 
551  YGLABEL_WIDGET_IMPL (YTable)
552  YGSELECTION_WIDGET_IMPL (YTable)
553 };
554 
555 YTable *YGWidgetFactory::createTable (YWidget *parent, YTableHeader *headers,
556  bool multiSelection)
557 {
558  return new YGTable (parent, headers, multiSelection);
559 }
560 
561 #include "YSelectionBox.h"
562 
563 class YGSelectionBox : public YSelectionBox, public YGTreeView
564 {
565 public:
566  YGSelectionBox (YWidget *parent, const std::string &label)
567  : YSelectionBox (NULL, label),
568  YGTreeView (this, parent, label, false)
569  {
570  GType types [2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING };
571  addTextColumn (0, 1);
572  createStore (2, types);
573  readModel();
574  }
575 
576  // YGTreeView
577 
578  virtual bool _shrinkable() { return shrinkable(); }
579 
580  // YGSelectionStore
581 
582  void doAddItem (YItem *item)
583  {
584  GtkTreeIter iter;
585  addRow (item, &iter);
586  setRowText (&iter, 0, item->iconName(), 1, item->label(), this);
587  if (item->selected())
588  focusItem (item, true);
589  }
590 
591  void doSelectItem (YItem *item, bool select)
592  { focusItem (item, select); }
593 
594  void doDeselectAllItems()
595  { unfocusAllItems(); }
596 
597  YGLABEL_WIDGET_IMPL (YSelectionBox)
598  YGSELECTION_WIDGET_IMPL (YSelectionBox)
599 };
600 
601 YSelectionBox *YGWidgetFactory::createSelectionBox (YWidget *parent, const std::string &label)
602 { return new YGSelectionBox (parent, label); }
603 
604 #include "YMultiSelectionBox.h"
605 
606 class YGMultiSelectionBox : public YMultiSelectionBox, public YGTreeView
607 {
608 public:
609  YGMultiSelectionBox (YWidget *parent, const std::string &label)
610  : YMultiSelectionBox (NULL, label),
611  YGTreeView (this, parent, label, false)
612  {
613  GType types [3] = { G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING };
614  addCheckColumn (0);
615  addTextColumn (1, 2);
616  createStore (3, types);
617  readModel();
618  addCountWidget (parent);
619  }
620 
621  // YGTreeView
622 
623  virtual bool _shrinkable() { return shrinkable(); }
624 
625  // YGSelectionStore
626 
627  void doAddItem (YItem *item)
628  {
629  GtkTreeIter iter;
630  addRow (item, &iter);
631  setRowMark (&iter, 0, item->selected());
632  setRowText (&iter, 1, item->iconName(), 2, item->label(), this);
633  syncCount();
634  }
635 
636  void doSelectItem (YItem *item, bool select)
637  {
638  GtkTreeIter iter;
639  getTreeIter (item, &iter);
640  setRowMark (&iter, 0, select);
641  syncCount();
642  }
643 
644  void doDeselectAllItems()
645  { unmarkAll(); syncCount(); }
646 
647  // YMultiSelectionBox
648 
649  virtual YItem *currentItem()
650  { return getFocusItem(); }
651 
652  virtual void setCurrentItem (YItem *item)
653  { focusItem (item, true); }
654 
655  YGLABEL_WIDGET_IMPL (YMultiSelectionBox)
656  YGSELECTION_WIDGET_IMPL (YMultiSelectionBox)
657 };
658 
659 YMultiSelectionBox *YGWidgetFactory::createMultiSelectionBox (YWidget *parent, const std::string &label)
660 { return new YGMultiSelectionBox (parent, label); }
661 
662 #include "YTree.h"
663 #include "YTreeItem.h"
664 
665 class YGTree : public YTree, public YGTreeView
666 {
667 public:
668  YGTree (YWidget *parent, const std::string &label, bool multiselection, bool recursiveSelection)
669  : YTree (NULL, label, multiselection, recursiveSelection),
670  YGTreeView (this, parent, label, true)
671  {
672  if (multiselection) {
673  GType types [3] = { GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN };
674  addCheckColumn (2);
675  addTextColumn (0, 1);
676  createStore (3, types);
677  addCountWidget (parent);
678  }
679  else
680  {
681  GType types [2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING };
682  addTextColumn (0, 1);
683  createStore (2, types);
684  }
685  readModel();
686 
687  g_signal_connect (getWidget(), "row-collapsed", G_CALLBACK (row_collapsed_cb), this);
688  g_signal_connect (getWidget(), "row-expanded", G_CALLBACK (row_expanded_cb), this);
689  }
690 
691  virtual bool _recursiveSelection() { return recursiveSelection(); }
692 
693  void addNode (YItem *item, GtkTreeIter *parent)
694  {
695  GtkTreeIter iter;
696  addRow (item, &iter, parent);
697  setRowText (&iter, 0, item->iconName(), 1, item->label(), this);
698  if (item->selected()) {
699  if (hasMultiSelection())
700  setRowMark (&iter, 2, item->selected());
701  else
702  focusItem (item, true);
703  }
704  if (((YTreeItem *) item)->isOpen())
705  expand (&iter);
706  for (YItemConstIterator it = item->childrenBegin();
707  it != item->childrenEnd(); it++)
708  addNode (*it, &iter);
709  }
710 
711 
712  void expand (GtkTreeIter *iter)
713  {
714  GtkTreePath *path = gtk_tree_model_get_path (getModel(), iter);
715  gtk_tree_view_expand_row (getView(), path, FALSE);
716  gtk_tree_path_free (path);
717  }
718 #if 0
719  bool isReallyOpen (YTreeItem *item) // are parents open as well?
720  {
721  for (YTreeItem *i = item; i; i = i->parent())
722  if (!i->isOpen())
723  return false;
724  return true;
725  }
726 #endif
727 
728  // YTree
729 
730  virtual void rebuildTree()
731  {
732  blockSelected();
733 
734  doDeleteAllItems();
735  for (YItemConstIterator it = YTree::itemsBegin(); it != YTree::itemsEnd(); it++)
736  addNode (*it, NULL);
737 
738  int depth = getTreeDepth();
739  gtk_tree_view_set_show_expanders (getView(), depth > 1);
740  gtk_tree_view_set_enable_tree_lines (getView(), depth > 3);
741 
742  // for whatever reason, we need to expand nodes only after the model
743  // is fully initialized
744  struct inner {
745  static gboolean foreach_sync_open (
746  GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis)
747  {
748  YGTree *pThis = (YGTree *) _pThis;
749  YTreeItem *item = (YTreeItem *) pThis->getYItem (iter);
750  if (item->isOpen())
751  gtk_tree_view_expand_row (pThis->getView(), path, FALSE);
752  return FALSE;
753  }
754  };
755 
756  g_signal_handlers_block_by_func (getWidget(), (gpointer) row_expanded_cb, this);
757  gtk_tree_model_foreach (getModel(), inner::foreach_sync_open, this);
758  g_signal_handlers_unblock_by_func (getWidget(), (gpointer) row_expanded_cb, this);
759 
760  syncCount();
761  }
762 
763  virtual YTreeItem *currentItem()
764  { return (YTreeItem *) getFocusItem(); }
765 
766  void _markItem (YItem *item, bool select, bool recursive) {
767  GtkTreeIter iter;
768  getTreeIter (item, &iter);
769  setRowMark (&iter, 2, select);
770 
771  if (recursive) {
772  YTreeItem *_item = (YTreeItem *) item;
773  for (YItemConstIterator it = _item->childrenBegin();
774  it != _item->childrenEnd(); it++)
775  _markItem (*it, select, true);
776  }
777  }
778 
779  // YGSelectionStore
780 
781  void doAddItem (YItem *item) {} // rebuild will be called anyway
782 
783  void doSelectItem (YItem *item, bool select)
784  {
785  if (hasMultiSelection()) {
786  _markItem (item, select, recursiveSelection());
787  syncCount();
788  }
789  else
790  focusItem (item, select);
791  }
792 
793  void doDeselectAllItems()
794  {
795  if (hasMultiSelection()) {
796  unmarkAll();
797  syncCount();
798  }
799  else
800  unfocusAllItems();
801  }
802 
803  // callbacks
804 
805  void reportRowOpen (GtkTreeIter *iter, bool open)
806  {
807  YTreeItem *item = static_cast <YTreeItem *> (getYItem (iter));
808  item->setOpen (open);
809  }
810 
811  static void row_collapsed_cb (GtkTreeView *view, GtkTreeIter *iter,
812  GtkTreePath *path, YGTree *pThis)
813  { pThis->reportRowOpen (iter, false); }
814 
815  static void row_expanded_cb (GtkTreeView *view, GtkTreeIter *iter,
816  GtkTreePath *path, YGTree *pThis)
817  { pThis->reportRowOpen (iter, true); }
818 
819 #if 0
820  // we do a bit of a work-around here to mimic -qt behavior... A node can
821  // be initialized as open, yet its parent, or some grand-parent, be closed.
822  // We thus honor the open state when its parent gets open.
823  YTreeItem *item = static_cast <YTreeItem *> (pThis->getYItem (iter));
824  for (YItemConstIterator it = item->childrenBegin();
825  it != item->childrenEnd(); it++) {
826  const YTreeItem *child = static_cast <YTreeItem *> (*it);
827  if (child->isOpen()) {
828  GtkTreeIter iter;
829  if (pThis->getIter (child, &iter))
830  pThis->expand (&iter);
831  }
832  }
833 #endif
834 
835  YGLABEL_WIDGET_IMPL (YTree)
836  YGSELECTION_WIDGET_IMPL (YTree)
837 };
838 
839 YTree *YGWidgetFactory::createTree (YWidget *parent, const std::string &label, bool multiselection, bool recursiveSelection)
840 { return new YGTree (parent, label, multiselection, recursiveSelection); }
841 
YGTable
Definition: YGTreeView.cc:349
YGMultiSelectionBox
Definition: YGTreeView.cc:606
YGScrolledWidget
Definition: YGWidget.h:155
YGSelectionBox
Definition: YGTreeView.cc:563
_YGtkTreeView
Definition: ygtktreeview.h:26
YGTree
Definition: YGTreeView.cc:665
YGSelectionStore
Definition: YGSelectionStore.h:14
YGDialog
Definition: YGDialog.h:14
YGTreeView
Definition: YGTreeView.cc:22