libyui-gtk  2.43.7
 All Classes
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  setCell (&iter, i++, *it);
454  if (item->selected())
455  focusItem (item, true);
456  }
457  else
458  yuiError() << "Can only add YTableItems to a YTable.\n";
459  }
460 
461  void doSelectItem (YItem *item, bool select)
462  { focusItem (item, select); }
463 
464  void doDeselectAllItems()
465  { unfocusAllItems(); }
466 
467  // callbacks
468 
469  static void activateButton (YWidget *button)
470  {
471  YWidgetEvent *event = new YWidgetEvent (button, YEvent::Activated);
472  YGUI::ui()->sendEvent (event);
473  }
474 
475  static void hack_right_click_cb (YGtkTreeView *view, gboolean outreach, YGTable *pThis)
476  {
477  if (pThis->notifyContextMenu())
478  return YGTreeView::right_click_cb (view, outreach, pThis);
479 
480  // If no context menu is specified, hack one ;-)
481 
482  struct inner {
483  static void key_activate_cb (GtkMenuItem *item, YWidget *button)
484  { activateButton (button); }
485  static void appendItem (GtkWidget *menu, const gchar *stock, int key)
486  {
487  YWidget *button = YGDialog::currentDialog()->getFunctionWidget (key);
488  if (button) {
489  GtkWidget *item;
490  item = gtk_menu_item_new_with_mnemonic (stock);
491  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
492  g_signal_connect (G_OBJECT (item), "activate",
493  G_CALLBACK (key_activate_cb), button);
494  }
495  }
496  };
497 
498  GtkWidget *menu = gtk_menu_new();
499  YGDialog *dialog = YGDialog::currentDialog();
500  if (dialog->getClassWidgets ("YTable").size() == 1) {
501  // if more than one table exists, function keys would be ambiguous
502  if (outreach) {
503  if (dialog->getFunctionWidget(3))
504  inner::appendItem (menu, "list-add", 3);
505  }
506  else {
507  if (dialog->getFunctionWidget(4))
508  inner::appendItem (menu, "edit-cut", 4);
509  if (dialog->getFunctionWidget(5))
510  inner::appendItem (menu, "list-remove", 5);
511  }
512  }
513 
514  menu = ygtk_tree_view_append_show_columns_item (YGTK_TREE_VIEW (view), menu);
515  ygtk_tree_view_popup_menu (view, menu);
516  }
517 
518  static gboolean key_press_event_cb (GtkWidget *widget, GdkEventKey *event, YGTable *pThis)
519  {
520  if (event->keyval == GDK_KEY_Delete) {
521  YWidget *button = YGDialog::currentDialog()->getFunctionWidget (5);
522  if (button)
523  activateButton (button);
524  else
525  gtk_widget_error_bell (widget);
526  return TRUE;
527  }
528  return FALSE;
529  }
530 
531  static gint tree_sort_cb (
532  GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer _index)
533  {
534  int index = GPOINTER_TO_INT (_index);
535  gchar *str_a, *str_b;
536  gtk_tree_model_get (model, a, index, &str_a, -1);
537  gtk_tree_model_get (model, b, index, &str_b, -1);
538  if (!str_a) str_a = g_strdup ("");
539  if (!str_b) str_b = g_strdup ("");
540  int ret = strcmp (str_a, str_b);
541  g_free (str_a); g_free (str_b);
542  return ret;
543  }
544 
545  YGLABEL_WIDGET_IMPL (YTable)
546  YGSELECTION_WIDGET_IMPL (YTable)
547 };
548 
549 YTable *YGWidgetFactory::createTable (YWidget *parent, YTableHeader *headers,
550  bool multiSelection)
551 {
552  return new YGTable (parent, headers, multiSelection);
553 }
554 
555 #include "YSelectionBox.h"
556 
557 class YGSelectionBox : public YSelectionBox, public YGTreeView
558 {
559 public:
560  YGSelectionBox (YWidget *parent, const std::string &label)
561  : YSelectionBox (NULL, label),
562  YGTreeView (this, parent, label, false)
563  {
564  GType types [2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING };
565  addTextColumn (0, 1);
566  createStore (2, types);
567  readModel();
568  }
569 
570  // YGTreeView
571 
572  virtual bool _shrinkable() { return shrinkable(); }
573 
574  // YGSelectionStore
575 
576  void doAddItem (YItem *item)
577  {
578  GtkTreeIter iter;
579  addRow (item, &iter);
580  setRowText (&iter, 0, item->iconName(), 1, item->label(), this);
581  if (item->selected())
582  focusItem (item, true);
583  }
584 
585  void doSelectItem (YItem *item, bool select)
586  { focusItem (item, select); }
587 
588  void doDeselectAllItems()
589  { unfocusAllItems(); }
590 
591  YGLABEL_WIDGET_IMPL (YSelectionBox)
592  YGSELECTION_WIDGET_IMPL (YSelectionBox)
593 };
594 
595 YSelectionBox *YGWidgetFactory::createSelectionBox (YWidget *parent, const std::string &label)
596 { return new YGSelectionBox (parent, label); }
597 
598 #include "YMultiSelectionBox.h"
599 
600 class YGMultiSelectionBox : public YMultiSelectionBox, public YGTreeView
601 {
602 public:
603  YGMultiSelectionBox (YWidget *parent, const std::string &label)
604  : YMultiSelectionBox (NULL, label),
605  YGTreeView (this, parent, label, false)
606  {
607  GType types [3] = { G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING };
608  addCheckColumn (0);
609  addTextColumn (1, 2);
610  createStore (3, types);
611  readModel();
612  addCountWidget (parent);
613  }
614 
615  // YGTreeView
616 
617  virtual bool _shrinkable() { return shrinkable(); }
618 
619  // YGSelectionStore
620 
621  void doAddItem (YItem *item)
622  {
623  GtkTreeIter iter;
624  addRow (item, &iter);
625  setRowMark (&iter, 0, item->selected());
626  setRowText (&iter, 1, item->iconName(), 2, item->label(), this);
627  syncCount();
628  }
629 
630  void doSelectItem (YItem *item, bool select)
631  {
632  GtkTreeIter iter;
633  getTreeIter (item, &iter);
634  setRowMark (&iter, 0, select);
635  syncCount();
636  }
637 
638  void doDeselectAllItems()
639  { unmarkAll(); syncCount(); }
640 
641  // YMultiSelectionBox
642 
643  virtual YItem *currentItem()
644  { return getFocusItem(); }
645 
646  virtual void setCurrentItem (YItem *item)
647  { focusItem (item, true); }
648 
649  YGLABEL_WIDGET_IMPL (YMultiSelectionBox)
650  YGSELECTION_WIDGET_IMPL (YMultiSelectionBox)
651 };
652 
653 YMultiSelectionBox *YGWidgetFactory::createMultiSelectionBox (YWidget *parent, const std::string &label)
654 { return new YGMultiSelectionBox (parent, label); }
655 
656 #include "YTree.h"
657 #include "YTreeItem.h"
658 
659 class YGTree : public YTree, public YGTreeView
660 {
661 public:
662  YGTree (YWidget *parent, const std::string &label, bool multiselection, bool recursiveSelection)
663  : YTree (NULL, label, multiselection, recursiveSelection),
664  YGTreeView (this, parent, label, true)
665  {
666  if (multiselection) {
667  GType types [3] = { GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN };
668  addCheckColumn (2);
669  addTextColumn (0, 1);
670  createStore (3, types);
671  addCountWidget (parent);
672  }
673  else
674  {
675  GType types [2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING };
676  addTextColumn (0, 1);
677  createStore (2, types);
678  }
679  readModel();
680 
681  g_signal_connect (getWidget(), "row-collapsed", G_CALLBACK (row_collapsed_cb), this);
682  g_signal_connect (getWidget(), "row-expanded", G_CALLBACK (row_expanded_cb), this);
683  }
684 
685  virtual bool _recursiveSelection() { return recursiveSelection(); }
686 
687  void addNode (YItem *item, GtkTreeIter *parent)
688  {
689  GtkTreeIter iter;
690  addRow (item, &iter, parent);
691  setRowText (&iter, 0, item->iconName(), 1, item->label(), this);
692 #if 0 // yast2-qt ignores `selected flag
693  if (item->selected()) {
694  if (hasMultiSelection())
695  setRowMark (&iter, 2, item->selected());
696  else
697  focusItem (item, true);
698  }
699  if (((YTreeItem *) item)->isOpen())
700  expand (&iter);
701 #endif
702  for (YItemConstIterator it = item->childrenBegin();
703  it != item->childrenEnd(); it++)
704  addNode (*it, &iter);
705  }
706 
707 #if 0
708  void expand (GtkTreeIter *iter)
709  {
710  GtkTreePath *path = gtk_tree_model_get_path (getModel(), iter);
711  gtk_tree_view_expand_row (getView(), path, FALSE);
712  gtk_tree_path_free (path);
713  }
714 
715  bool isReallyOpen (YTreeItem *item) // are parents open as well?
716  {
717  for (YTreeItem *i = item; i; i = i->parent())
718  if (!i->isOpen())
719  return false;
720  return true;
721  }
722 #endif
723 
724  // YTree
725 
726  virtual void rebuildTree()
727  {
728  blockSelected();
729 
730  doDeleteAllItems();
731  for (YItemConstIterator it = YTree::itemsBegin(); it != YTree::itemsEnd(); it++)
732  addNode (*it, NULL);
733 
734  int depth = getTreeDepth();
735  gtk_tree_view_set_show_expanders (getView(), depth > 1);
736  gtk_tree_view_set_enable_tree_lines (getView(), depth > 3);
737 
738  // for whatever reason, we need to expand nodes only after the model
739  // is fully initialized
740  struct inner {
741  static gboolean foreach_sync_open (
742  GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis)
743  {
744  YGTree *pThis = (YGTree *) _pThis;
745  YTreeItem *item = (YTreeItem *) pThis->getYItem (iter);
746  if (item->isOpen())
747  gtk_tree_view_expand_row (pThis->getView(), path, FALSE);
748  return FALSE;
749  }
750  };
751 
752  g_signal_handlers_block_by_func (getWidget(), (gpointer) row_expanded_cb, this);
753  gtk_tree_model_foreach (getModel(), inner::foreach_sync_open, this);
754  g_signal_handlers_unblock_by_func (getWidget(), (gpointer) row_expanded_cb, this);
755 
756  syncCount();
757  }
758 
759  virtual YTreeItem *currentItem()
760  { return (YTreeItem *) getFocusItem(); }
761 
762  void _markItem (YItem *item, bool select, bool recursive) {
763  GtkTreeIter iter;
764  getTreeIter (item, &iter);
765  setRowMark (&iter, 2, select);
766 
767  if (recursive) {
768  YTreeItem *_item = (YTreeItem *) item;
769  for (YItemConstIterator it = _item->childrenBegin();
770  it != _item->childrenEnd(); it++)
771  _markItem (*it, select, true);
772  }
773  }
774 
775  // YGSelectionStore
776 
777  void doAddItem (YItem *item) {} // rebuild will be called anyway
778 
779  void doSelectItem (YItem *item, bool select)
780  {
781  if (hasMultiSelection()) {
782  _markItem (item, select, recursiveSelection());
783  syncCount();
784  }
785  else
786  focusItem (item, select);
787  }
788 
789  void doDeselectAllItems()
790  {
791  if (hasMultiSelection()) {
792  unmarkAll();
793  syncCount();
794  }
795  else
796  unfocusAllItems();
797  }
798 
799  // callbacks
800 
801  void reportRowOpen (GtkTreeIter *iter, bool open)
802  {
803  YTreeItem *item = static_cast <YTreeItem *> (getYItem (iter));
804  item->setOpen (open);
805  }
806 
807  static void row_collapsed_cb (GtkTreeView *view, GtkTreeIter *iter,
808  GtkTreePath *path, YGTree *pThis)
809  { pThis->reportRowOpen (iter, false); }
810 
811  static void row_expanded_cb (GtkTreeView *view, GtkTreeIter *iter,
812  GtkTreePath *path, YGTree *pThis)
813  { pThis->reportRowOpen (iter, true); }
814 
815 #if 0
816  // we do a bit of a work-around here to mimic -qt behavior... A node can
817  // be initialized as open, yet its parent, or some grand-parent, be closed.
818  // We thus honor the open state when its parent gets open.
819  YTreeItem *item = static_cast <YTreeItem *> (pThis->getYItem (iter));
820  for (YItemConstIterator it = item->childrenBegin();
821  it != item->childrenEnd(); it++) {
822  const YTreeItem *child = static_cast <YTreeItem *> (*it);
823  if (child->isOpen()) {
824  GtkTreeIter iter;
825  if (pThis->getIter (child, &iter))
826  pThis->expand (&iter);
827  }
828  }
829 #endif
830 
831  YGLABEL_WIDGET_IMPL (YTree)
832  YGSELECTION_WIDGET_IMPL (YTree)
833 };
834 
835 YTree *YGWidgetFactory::createTree (YWidget *parent, const std::string &label, bool multiselection, bool recursiveSelection)
836 { return new YGTree (parent, label, multiselection, recursiveSelection); }
837