libyui-gtk  2.43.7
 All Classes
YGTreeView-mine.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 #include "YTable.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 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 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  YTableItem *pYTableItem = dynamic_cast<YTableItem*>(yitem);
235  if (pYTableItem)
236  {
237  YTable * pTable = dynamic_cast<YTable*>(this);
238  if (pTable)
239  {
240  if ( (pTable->selectionMode() == YTableMode::YTableCheckBoxOnFirstColumn && column == 0)
241  ||
242  (pTable->selectionMode() == YTableMode::YTableCheckBoxOnLastColumn && column == pTable->columns()*3 ))
243  {
244  setRowMark (&iter, column, state);
245  emitEvent (YEvent::ValueChanged);
246  }
247 
248  }
249  }
250  else
251  {
252  setMark (&iter, yitem, column, state, _recursiveSelection());
253  syncCount();
254  emitEvent (YEvent::ValueChanged);
255  }
256  }
257 
258  // YGWidget
259 
260  virtual unsigned int getMinSize (YUIDimension dim)
261  {
262  if (dim == YD_VERT)
263  return YGUtils::getCharsHeight (getWidget(), _shrinkable() ? 2 : 5);
264  return 80;
265  }
266 
267 protected:
268  static gboolean block_selected_timeout_cb (gpointer data)
269  {
270  YGTreeView *pThis = (YGTreeView *) data;
271  pThis->m_blockTimeout = 0;
272  return FALSE;
273  }
274 
275  void blockSelected()
276  { // GtkTreeSelection only fires when idle; so set a timeout
277  if (m_blockTimeout) g_source_remove (m_blockTimeout);
278  m_blockTimeout = g_timeout_add_full (G_PRIORITY_LOW, 50, block_selected_timeout_cb, this, NULL);
279  }
280 
281  static void block_init_cb (GtkWidget *widget, YGTreeView *pThis)
282  { pThis->blockSelected(); }
283 
284  // callbacks
285 
286  static bool all_marked (GtkTreeModel *model, GtkTreeIter *iter, int mark_col)
287  {
288  gboolean marked;
289  GtkTreeIter child_iter;
290  if (gtk_tree_model_iter_children (model, &child_iter, iter))
291  do {
292  gtk_tree_model_get (model, &child_iter, mark_col, &marked, -1);
293  if (!marked) return false;
294  all_marked (model, &child_iter, mark_col);
295  } while (gtk_tree_model_iter_next (model, &child_iter));
296  return true;
297  }
298 
299  static void inconsistent_mark_cb (GtkTreeViewColumn *column,
300  GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
301  { // used for trees -- show inconsistent if one node is check but another isn't
302  YGTreeView *pThis = (YGTreeView *) data;
303  gboolean marked;
304  gtk_tree_model_get (model, iter, pThis->markColumn, &marked, -1);
305  gboolean consistent = !marked || all_marked (model, iter, pThis->markColumn);
306  g_object_set (G_OBJECT (cell), "inconsistent", !consistent, NULL);
307  }
308 
309  static void selection_changed_cb (GtkTreeSelection *selection, YGTreeView *pThis)
310  {
311  struct inner {
312  static gboolean foreach_sync_select (
313  GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis)
314  {
315  YGTreeView *pThis = (YGTreeView *) _pThis;
316  GtkTreeSelection *selection = pThis->getSelection();
317  bool sel = gtk_tree_selection_iter_is_selected (selection, iter);
318  pThis->getYItem (iter)->setSelected (sel);
319  return FALSE;
320  }
321  };
322 
323  if (pThis->m_blockTimeout) return;
324  if (pThis->markColumn == -1)
325  gtk_tree_model_foreach (pThis->getModel(), inner::foreach_sync_select, pThis);
326  if (pThis->_immediateMode())
327  pThis->emitEvent (YEvent::SelectionChanged, IF_NOT_PENDING_EVENT);
328  }
329 
330  static void activated_cb (GtkTreeView *tree_view, GtkTreePath *path,
331  GtkTreeViewColumn *column, YGTreeView* pThis)
332  {
333  if (pThis->markColumn >= 0)
334  {
335  YTable *pTable = dynamic_cast<YTable*>(pThis);
336  if (pTable)
337  {
338  GtkTreeViewColumn* col = gtk_tree_view_get_column (pThis->getView(), pThis->markColumn);
339  if (col == column)
340  pThis->toggleMark (path, pThis->markColumn);
341  else
342  pThis->emitEvent (YEvent::Activated);
343  }
344  else
345  pThis->toggleMark (path, pThis->markColumn);
346  }
347  else {
348  // for tree - expand/collpase double-clicked rows
349  if (gtk_tree_view_row_expanded (tree_view, path))
350  gtk_tree_view_collapse_row (tree_view, path);
351  else
352  gtk_tree_view_expand_row (tree_view, path, FALSE);
353 
354  pThis->emitEvent (YEvent::Activated);
355  }
356  }
357 
358  static void toggled_cb (GtkCellRendererToggle *renderer, gchar *path_str,
359  YGTreeView *pThis)
360  {
361  GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
362  gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (renderer), "column"));
363  pThis->toggleMark (path, column);
364  gtk_tree_path_free (path);
365 
366  // un/marking a sub-node can cause changes of "inconsistency"
367  if (gtk_tree_path_get_depth (path) >= 2)
368  gtk_widget_queue_draw (pThis->getWidget());
369  }
370 
371  static void right_click_cb (YGtkTreeView *view, gboolean outreach, YGTreeView *pThis)
372  { pThis->emitEvent (YEvent::ContextMenuActivated); }
373 };
374 
375 #include "YGDialog.h"
376 #include <gdk/gdkkeysyms.h>
377 #include <string.h>
378 
379 class YGTable : public YTable, public YGTreeView
380 {
381 public:
382  YGTable (YWidget *parent, YTableHeader *headers, YTableMode mode)
383  : YTable (NULL, headers, mode),
384  YGTreeView (this, parent, std::string(), false)
385  {
386  gtk_tree_view_set_headers_visible (getView(), TRUE);
387  gtk_tree_view_set_rules_hint (getView(), columns() > 1);
388  ygtk_tree_view_set_empty_text (YGTK_TREE_VIEW (getView()), _("No entries."));
389  if ( mode == YTableMultiSelection )
390  gtk_tree_selection_set_mode (getSelection(), GTK_SELECTION_MULTIPLE);
391  int columnNumber = columns();
392  int columnOffset = 0;
393 
394  yuiMilestone() << " Slection mode " << mode << std::endl;
395 
396  if (mode == YTableCheckBoxOnFirstColumn)
397  {
398  columnNumber += 1;
399  columnOffset = 1;
400  }
401  else if ( mode == YTableCheckBoxOnLastColumn)
402  columnNumber += 1;
403  GType types [columnNumber*3];
404  for (int i = 0; i < columnNumber; i++) {
405  int t = i*3;
406  types[t+0] = G_TYPE_BOOLEAN;
407  types[t+1] = GDK_TYPE_PIXBUF;
408  types[t+2] = G_TYPE_STRING;
409  if ( (i==0 && mode == YTableCheckBoxOnFirstColumn) ||
410  (i == columnNumber-1 && mode == YTableCheckBoxOnLastColumn))
411  addCheckColumn(t);
412  else
413  addTextColumn (header(i-columnOffset), alignment (i-columnOffset), t+1, t+2);
414  }
415  createStore (columnNumber*3, types);
416  readModel();
417  if (!keepSorting())
418  setSortable (true);
419 
420  // if last col is aligned: add some dummy so that it doesn't expand.
421  YAlignmentType lastAlign = alignment (columns()-1);
422  if (lastAlign == YAlignCenter || lastAlign == YAlignEnd)
423  gtk_tree_view_append_column (getView(), gtk_tree_view_column_new());
424 
425  g_signal_connect (getWidget(), "key-press-event", G_CALLBACK (key_press_event_cb), this);
426  }
427 
428  void setSortable (bool sortable)
429  {
430  if (!sortable && !gtk_widget_get_realized (getWidget()))
431  return;
432  int n = 0;
433  GList *columns = gtk_tree_view_get_columns (getView());
434  for (GList *i = columns; i; i = i->next, n++) {
435  GtkTreeViewColumn *column = (GtkTreeViewColumn *) i->data;
436  if (n >= YGTable::columns())
437  break;
438  if (sortable) {
439  // anaselli it was (n*2)+1
440  int index = (n*3)+1;
441  if (!sortable)
442  index = -1;
443  gtk_tree_sortable_set_sort_func (
444  GTK_TREE_SORTABLE (getModel()), index, tree_sort_cb,
445  GINT_TO_POINTER (index), NULL);
446  gtk_tree_view_column_set_sort_column_id (column, index);
447  }
448  else
449  gtk_tree_view_column_set_sort_column_id (column, -1);
450  }
451  g_list_free (columns);
452  }
453 
454  void setCell (GtkTreeIter *iter, int column, const YTableCell *cell)
455  {
456  if (!cell) return;
457  std::string label (cell->label());
458  if (label == "X")
459  label = YUI::app()->glyph (YUIGlyph_CheckMark);
460 
461  int index = column * 3;
462  setRowText (iter, index+1, cell->iconName(), index+2, label, this);
463  }
464 
465  // YGTreeView
466 
467  virtual bool _immediateMode() { return immediateMode(); }
468 
469  // YTable
470 
471  virtual void setKeepSorting (bool keepSorting)
472  {
473  YTable::setKeepSorting (keepSorting);
474  setSortable (!keepSorting);
475  if (!keepSorting) {
476  GtkTreeViewColumn *column = gtk_tree_view_get_column (getView(), 0);
477  if (column)
478  gtk_tree_view_column_clicked (column);
479  }
480  }
481 
482  virtual void cellChanged (const YTableCell *cell)
483  {
484  GtkTreeIter iter;
485  getTreeIter (cell->parent(), &iter);
486  setCell (&iter, cell->column(), cell);
487  }
488 
489  // YGSelectionStore
490 
491  void doAddItem (YItem *_item)
492  {
493  YTableItem *item = dynamic_cast <YTableItem *> (_item);
494  if (item) {
495  GtkTreeIter iter;
496  addRow (item, &iter);
497  int i = 0;
498  int column_offset = 0;
499  if (selectionMode() == YTableMode::YTableCheckBoxOnFirstColumn )
500  {
501  column_offset=1;
502  setRowMark(&iter, i++, item->selected());
503  }
504  for (YTableCellIterator it = item->cellsBegin();
505  it != item->cellsEnd(); it++)
506  {
507  if ((i-column_offset) > columns())
508  {
509  yuiWarning() << "Item contains too many columns, current is " << i-column_offset
510  << " but only " << columns() << " columns are configured" << std::endl;
511  }
512  else
513  setCell (&iter, i++, *it);
514  }
515  if (selectionMode() == YTableMode::YTableCheckBoxOnLastColumn )
516  {
517  yuiMilestone() << " columns " << columns() << std::endl;
518  setRowMark(&iter, columns()*3, item->selected());
519  }
520  if (item->selected())
521  focusItem (item, true);
522  }
523  else
524  yuiError() << "Can only add YTableItems to a YTable.\n";
525  }
526 
527  void doSelectItem (YItem *item, bool select)
528  { focusItem (item, select); }
529 
530  void doDeselectAllItems()
531  { unfocusAllItems(); }
532 
533  // callbacks
534 
535  static void activateButton (YWidget *button)
536  {
537  YWidgetEvent *event = new YWidgetEvent (button, YEvent::Activated);
538  YGUI::ui()->sendEvent (event);
539  }
540 
541  static void hack_right_click_cb (YGtkTreeView *view, gboolean outreach, YGTable *pThis)
542  {
543  if (pThis->notifyContextMenu())
544  return YGTreeView::right_click_cb (view, outreach, pThis);
545 
546  // If no context menu is specified, hack one ;-)
547 
548  struct inner {
549  static void key_activate_cb (GtkMenuItem *item, YWidget *button)
550  { activateButton (button); }
551  static void appendItem (GtkWidget *menu, const gchar *stock, int key)
552  {
553  YWidget *button = YGDialog::currentDialog()->getFunctionWidget (key);
554  if (button) {
555  GtkWidget *item;
556  item = gtk_image_menu_item_new_from_stock (stock, NULL);
557  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
558  g_signal_connect (G_OBJECT (item), "activate",
559  G_CALLBACK (key_activate_cb), button);
560  }
561  }
562  };
563 
564  GtkWidget *menu = gtk_menu_new();
565  YGDialog *dialog = YGDialog::currentDialog();
566  if (dialog->getClassWidgets ("YTable").size() == 1) {
567  // if more than one table exists, function keys would be ambiguous
568  if (outreach) {
569  if (dialog->getFunctionWidget(3))
570  inner::appendItem (menu, GTK_STOCK_ADD, 3);
571  }
572  else {
573  if (dialog->getFunctionWidget(4))
574  inner::appendItem (menu, GTK_STOCK_EDIT, 4);
575  if (dialog->getFunctionWidget(5))
576  inner::appendItem (menu, GTK_STOCK_DELETE, 5);
577  }
578  }
579 
580  menu = ygtk_tree_view_append_show_columns_item (YGTK_TREE_VIEW (view), menu);
581  ygtk_tree_view_popup_menu (view, menu);
582  }
583 
584  static gboolean key_press_event_cb (GtkWidget *widget, GdkEventKey *event, YGTable *pThis)
585  {
586  if (event->keyval == GDK_KEY_Delete) {
587  YWidget *button = YGDialog::currentDialog()->getFunctionWidget (5);
588  if (button)
589  activateButton (button);
590  else
591  gtk_widget_error_bell (widget);
592  return TRUE;
593  }
594  return FALSE;
595  }
596 
597  static gint tree_sort_cb (
598  GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer _index)
599  {
600  int index = GPOINTER_TO_INT (_index);
601  gchar *str_a, *str_b;
602  gtk_tree_model_get (model, a, index, &str_a, -1);
603  gtk_tree_model_get (model, b, index, &str_b, -1);
604  if (!str_a) str_a = g_strdup ("");
605  if (!str_b) str_b = g_strdup ("");
606  int ret = strcmp (str_a, str_b);
607  g_free (str_a); g_free (str_b);
608  return ret;
609  }
610 
611  YGLABEL_WIDGET_IMPL (YTable)
612  YGSELECTION_WIDGET_IMPL (YTable)
613 };
614 
615 YTable *YGWidgetFactory::createTable (YWidget *parent, YTableHeader *headers,
616  bool multiSelection)
617 {
618  return new YGTable (parent, headers, (multiSelection ? YTableMode::YTableMultiSelection : YTableMode::YTableSingleLineSelection) );
619 }
620 
621 YTable *YGWidgetFactory::createTable (YWidget *parent, YTableHeader *headers,
622  YTableMode mode)
623 {
624  return new YGTable (parent, headers, mode);
625 }
626 
627 #include "YSelectionBox.h"
628 
629 class YGSelectionBox : public YSelectionBox, public YGTreeView
630 {
631 public:
632  YGSelectionBox (YWidget *parent, const std::string &label)
633  : YSelectionBox (NULL, label),
634  YGTreeView (this, parent, label, false)
635  {
636  GType types [2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING };
637  addTextColumn (0, 1);
638  createStore (2, types);
639  readModel();
640  }
641 
642  // YGTreeView
643 
644  virtual bool _shrinkable() { return shrinkable(); }
645 
646  // YGSelectionStore
647 
648  void doAddItem (YItem *item)
649  {
650  GtkTreeIter iter;
651  addRow (item, &iter);
652  setRowText (&iter, 0, item->iconName(), 1, item->label(), this);
653  if (item->selected())
654  focusItem (item, true);
655  }
656 
657  void doSelectItem (YItem *item, bool select)
658  { focusItem (item, select); }
659 
660  void doDeselectAllItems()
661  { unfocusAllItems(); }
662 
663  YGLABEL_WIDGET_IMPL (YSelectionBox)
664  YGSELECTION_WIDGET_IMPL (YSelectionBox)
665 };
666 
667 YSelectionBox *YGWidgetFactory::createSelectionBox (YWidget *parent, const std::string &label)
668 { return new YGSelectionBox (parent, label); }
669 
670 #include "YMultiSelectionBox.h"
671 
672 class YGMultiSelectionBox : public YMultiSelectionBox, public YGTreeView
673 {
674 public:
675  YGMultiSelectionBox (YWidget *parent, const std::string &label)
676  : YMultiSelectionBox (NULL, label),
677  YGTreeView (this, parent, label, false)
678  {
679  GType types [3] = { G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING };
680  addCheckColumn (0);
681  addTextColumn (1, 2);
682  createStore (3, types);
683  readModel();
684  addCountWidget (parent);
685  }
686 
687  // YGTreeView
688 
689  virtual bool _shrinkable() { return shrinkable(); }
690 
691  // YGSelectionStore
692 
693  void doAddItem (YItem *item)
694  {
695  GtkTreeIter iter;
696  addRow (item, &iter);
697  setRowMark (&iter, 0, item->selected());
698  setRowText (&iter, 1, item->iconName(), 2, item->label(), this);
699  syncCount();
700  }
701 
702  void doSelectItem (YItem *item, bool select)
703  {
704  GtkTreeIter iter;
705  getTreeIter (item, &iter);
706  setRowMark (&iter, 0, select);
707  syncCount();
708  }
709 
710  void doDeselectAllItems()
711  { unmarkAll(); syncCount(); }
712 
713  // YMultiSelectionBox
714 
715  virtual YItem *currentItem()
716  { return getFocusItem(); }
717 
718  virtual void setCurrentItem (YItem *item)
719  { focusItem (item, true); }
720 
721  YGLABEL_WIDGET_IMPL (YMultiSelectionBox)
722  YGSELECTION_WIDGET_IMPL (YMultiSelectionBox)
723 };
724 
725 YMultiSelectionBox *YGWidgetFactory::createMultiSelectionBox (YWidget *parent, const std::string &label)
726 { return new YGMultiSelectionBox (parent, label); }
727 
728 #include "YTree.h"
729 #include "YTreeItem.h"
730 
731 class YGTree : public YTree, public YGTreeView
732 {
733 public:
734  YGTree (YWidget *parent, const std::string &label, bool multiselection, bool recursiveSelection)
735  : YTree (NULL, label, multiselection, recursiveSelection),
736  YGTreeView (this, parent, label, true)
737  {
738  if (multiselection) {
739  GType types [3] = { GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN };
740  addCheckColumn (2);
741  addTextColumn (0, 1);
742  createStore (3, types);
743  addCountWidget (parent);
744  }
745  else
746  {
747  GType types [2] = { GDK_TYPE_PIXBUF, G_TYPE_STRING };
748  addTextColumn (0, 1);
749  createStore (2, types);
750  }
751  readModel();
752 
753  g_signal_connect (getWidget(), "row-collapsed", G_CALLBACK (row_collapsed_cb), this);
754  g_signal_connect (getWidget(), "row-expanded", G_CALLBACK (row_expanded_cb), this);
755  }
756 
757  virtual bool _recursiveSelection() { return recursiveSelection(); }
758 
759  void addNode (YItem *item, GtkTreeIter *parent)
760  {
761  GtkTreeIter iter;
762  addRow (item, &iter, parent);
763  setRowText (&iter, 0, item->iconName(), 1, item->label(), this);
764 #if 0 // yast2-qt ignores `selected flag
765  if (item->selected()) {
766  if (hasMultiSelection())
767  setRowMark (&iter, 2, item->selected());
768  else
769  focusItem (item, true);
770  }
771  if (((YTreeItem *) item)->isOpen())
772  expand (&iter);
773 #endif
774  for (YItemConstIterator it = item->childrenBegin();
775  it != item->childrenEnd(); it++)
776  addNode (*it, &iter);
777  }
778 
779 #if 0
780  void expand (GtkTreeIter *iter)
781  {
782  GtkTreePath *path = gtk_tree_model_get_path (getModel(), iter);
783  gtk_tree_view_expand_row (getView(), path, FALSE);
784  gtk_tree_path_free (path);
785  }
786 
787  bool isReallyOpen (YTreeItem *item) // are parents open as well?
788  {
789  for (YTreeItem *i = item; i; i = i->parent())
790  if (!i->isOpen())
791  return false;
792  return true;
793  }
794 #endif
795 
796  // YTree
797 
798  virtual void rebuildTree()
799  {
800  blockSelected();
801 
802  doDeleteAllItems();
803  for (YItemConstIterator it = YTree::itemsBegin(); it != YTree::itemsEnd(); it++)
804  addNode (*it, NULL);
805 
806  int depth = getTreeDepth();
807  gtk_tree_view_set_show_expanders (getView(), depth > 1);
808  gtk_tree_view_set_enable_tree_lines (getView(), depth > 3);
809 
810  // for whatever reason, we need to expand nodes only after the model
811  // is fully initialized
812  struct inner {
813  static gboolean foreach_sync_open (
814  GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer _pThis)
815  {
816  YGTree *pThis = (YGTree *) _pThis;
817  YTreeItem *item = (YTreeItem *) pThis->getYItem (iter);
818  if (item->isOpen())
819  gtk_tree_view_expand_row (pThis->getView(), path, FALSE);
820  return FALSE;
821  }
822  };
823 
824  g_signal_handlers_block_by_func (getWidget(), (gpointer) row_expanded_cb, this);
825  gtk_tree_model_foreach (getModel(), inner::foreach_sync_open, this);
826  g_signal_handlers_unblock_by_func (getWidget(), (gpointer) row_expanded_cb, this);
827 
828  syncCount();
829  }
830 
831  virtual YTreeItem *currentItem()
832  { return (YTreeItem *) getFocusItem(); }
833 
834  void _markItem (YItem *item, bool select, bool recursive) {
835  GtkTreeIter iter;
836  getTreeIter (item, &iter);
837  setRowMark (&iter, 2, select);
838 
839  if (recursive) {
840  YTreeItem *_item = (YTreeItem *) item;
841  for (YItemConstIterator it = _item->childrenBegin();
842  it != _item->childrenEnd(); it++)
843  _markItem (*it, select, true);
844  }
845  }
846 
847  // YGSelectionStore
848 
849  void doAddItem (YItem *item) {} // rebuild will be called anyway
850 
851  void doSelectItem (YItem *item, bool select)
852  {
853  if (hasMultiSelection()) {
854  _markItem (item, select, recursiveSelection());
855  syncCount();
856  }
857  else
858  focusItem (item, select);
859  }
860 
861  void doDeselectAllItems()
862  {
863  if (hasMultiSelection()) {
864  unmarkAll();
865  syncCount();
866  }
867  else
868  unfocusAllItems();
869  }
870 
871  // callbacks
872 
873  void reportRowOpen (GtkTreeIter *iter, bool open)
874  {
875  YTreeItem *item = static_cast <YTreeItem *> (getYItem (iter));
876  item->setOpen (open);
877  }
878 
879  static void row_collapsed_cb (GtkTreeView *view, GtkTreeIter *iter,
880  GtkTreePath *path, YGTree *pThis)
881  { pThis->reportRowOpen (iter, false); }
882 
883  static void row_expanded_cb (GtkTreeView *view, GtkTreeIter *iter,
884  GtkTreePath *path, YGTree *pThis)
885  { pThis->reportRowOpen (iter, true); }
886 
887 #if 0
888  // we do a bit of a work-around here to mimic -qt behavior... A node can
889  // be initialized as open, yet its parent, or some grand-parent, be closed.
890  // We thus honor the open state when its parent gets open.
891  YTreeItem *item = static_cast <YTreeItem *> (pThis->getYItem (iter));
892  for (YItemConstIterator it = item->childrenBegin();
893  it != item->childrenEnd(); it++) {
894  const YTreeItem *child = static_cast <YTreeItem *> (*it);
895  if (child->isOpen()) {
896  GtkTreeIter iter;
897  if (pThis->getIter (child, &iter))
898  pThis->expand (&iter);
899  }
900  }
901 #endif
902 
903  YGLABEL_WIDGET_IMPL (YTree)
904  YGSELECTION_WIDGET_IMPL (YTree)
905 };
906 
907 YTree *YGWidgetFactory::createTree (YWidget *parent, const std::string &label, bool multiselection, bool recursiveSelection)
908 { return new YGTree (parent, label, multiselection, recursiveSelection); }
909