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