libyui-gtk-pkg  2.43.2
 All Classes
ygtkpkgpatternview.cc
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 /* YGtkPkgPatternList, pattern and language list implementations */
5 // check the header file for information about this widget
6 
7 /*
8  Textdomain "gtk"
9  */
10 
11 #include "YGi18n.h"
12 #include "config.h"
13 #include "YGUtils.h"
14 #include "ygtkpkgpatternview.h"
15 #include "ygtktreeview.h"
16 #include "ygtkcellrenderertext.h"
17 #include <gtk/gtk.h>
18 
19 #define GRAY_COLOR "#727272"
20 
21 enum Column {
22  HAS_CHECK_COLUMN, CHECK_COLUMN, HAS_ICON_COLUMN, ICON_COLUMN,
23  TEXT_COLUMN, ORDER_COLUMN, POINTER_COLUMN, TOTAL_COLUMNS
24 };
25 
27  GtkWidget *scroll, *view;
28 
29  Impl()
30  { Ypp::addSelListener (this); }
31  ~Impl()
32  {
33  Ypp::removeSelListener (this);
34  if (!g_object_get_data (G_OBJECT (getModel()), "patterns"))
35  gtk_tree_model_foreach (getModel(), free_foreach_cb, this);
36  }
37 
38  GtkTreeModel *getModel()
39  { return gtk_tree_view_get_model (GTK_TREE_VIEW (view)); }
40 
41  static gboolean free_foreach_cb (GtkTreeModel *model,
42  GtkTreePath *path, GtkTreeIter *iter, gpointer data)
43  {
44  gpointer _data;
45  gtk_tree_model_get (model, iter, POINTER_COLUMN, &_data, -1);
46  g_free (_data);
47  return FALSE;
48  }
49 
50  static gboolean update_foreach_cb (GtkTreeModel *model,
51  GtkTreePath *path, GtkTreeIter *iter, gpointer data)
52  {
53  gpointer _data;
54  gtk_tree_model_get (model, iter, POINTER_COLUMN, &_data, -1);
55  if (_data) {
56  ZyppSelectablePtr zsel = (ZyppSelectablePtr) _data;
57  Ypp::Selectable sel (zsel);
58 
59  GtkTreeStore *store = GTK_TREE_STORE (model);
60  gtk_tree_store_set (store, iter, CHECK_COLUMN,
61  sel.isInstalled() || sel.toInstall(), -1);
62  }
63  return FALSE;
64  }
65 
66  virtual void selectableModified()
67  { gtk_tree_model_foreach (getModel(), update_foreach_cb, this); }
68 };
69 
70 static Ypp::Selectable get_iter_selectable (GtkTreeModel *model, GtkTreeIter *iter)
71 {
72  if (g_object_get_data (G_OBJECT (model), "patterns")) {
73  ZyppSelectablePtr zsel;
74  gtk_tree_model_get (model, iter, POINTER_COLUMN, &zsel, -1);
75  return Ypp::Selectable (zsel);
76  }
77  else {
78  gchar *code;
79  gtk_tree_model_get (model, iter, POINTER_COLUMN, &code, -1);
80  zypp::Locale locale (code);
81  g_free (code);
82  return Ypp::Selectable (locale);
83  }
84 }
85 
86 static void set_row (
87  GtkTreeStore *store, GtkTreeIter *iter, Ypp::Selectable &sel, ZyppPattern pattern)
88 {
89  std::string order;
90  GdkPixbuf *pixbuf = 0;
91  gpointer ptr;
92  if (pattern) {
93  std::string filename (pattern->icon().asString());
94  if (filename == zypp::Pathname("yast-system").asString() || filename.empty())
95  filename = "pattern-generic";
96  if (filename.compare (0, 2, "./", 2) == 0)
97  filename.erase (0, 2);
98  filename = zypp::str::form ("%s/icons/%dx%d/apps/%s.png",
99  THEMEDIR, 32, 32, filename.c_str());
100  pixbuf = YGUtils::loadPixbuf (filename);
101 /*
102  if (pixbuf && !sel.isInstalled()) {
103  GdkPixbuf *_pixbuf = pixbuf;
104  pixbuf = YGUtils::setOpacity (_pixbuf, 50, true);
105  g_object_unref (_pixbuf);
106  }
107 */
108  order = pattern->order();
109  ptr = get_pointer (sel.zyppSel());
110  }
111  else
112  ptr = g_strdup (sel.zyppLocale().code().c_str());
113 
114  gtk_tree_store_set (store, iter, HAS_CHECK_COLUMN, TRUE,
115  CHECK_COLUMN, sel.isInstalled() || sel.toInstall(), HAS_ICON_COLUMN, TRUE,
116  ICON_COLUMN, pixbuf, TEXT_COLUMN, (sel.name() + '\n').c_str(),
117  ORDER_COLUMN, order.c_str(),
118  POINTER_COLUMN, ptr, -1);
119 
120  if (pixbuf)
121  g_object_unref (G_OBJECT (pixbuf));
122 }
123 
124 static gboolean set_rows_cb (GtkTreeModel *model,
125  GtkTreePath *path, GtkTreeIter *iter, gpointer data)
126 {
127  gpointer _data;
128  gtk_tree_model_get (model, iter, POINTER_COLUMN, &_data, -1);
129  if (!_data) return FALSE;
130 
131  Ypp::Selectable sel (get_iter_selectable (model, iter));
132 
133  int installed, total;
134  Ypp::Collection (sel).stats (&installed, &total);
135  gchar *text = g_strdup_printf ("%s\n<span color=\"" GRAY_COLOR
136  "\"><small>Installed %d of %d</small></span>",
137  sel.name().c_str(), installed, total);
138 
139  GtkTreeStore *store = GTK_TREE_STORE (model);
140  gtk_tree_store_set (store, iter, TEXT_COLUMN, text, -1);
141  g_free (text);
142  return FALSE;
143 }
144 
145 static gboolean set_rows_idle (gpointer data)
146 {
147  gtk_tree_model_foreach (GTK_TREE_MODEL (data), set_rows_cb, NULL);
148  return FALSE;
149 }
150 
151 static bool find_name (GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, const std::string &name)
152 {
153  GtkTreeModel *model = GTK_TREE_MODEL (store);
154  bool done = false;
155  if (gtk_tree_model_iter_children (model, iter, parent))
156  do {
157  gchar *value;
158  gtk_tree_model_get (model, iter, TEXT_COLUMN, &value, -1);
159  if (name == value)
160  done = true;
161  g_free (value);
162  if (done)
163  return true;
164  } while (gtk_tree_model_iter_next (model, iter));
165  return false;
166 }
167 
168 static bool find_place (GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, const std::string &order)
169 {
170  GtkTreeModel *model = GTK_TREE_MODEL (store);
171  bool done = false;
172  if (gtk_tree_model_iter_children (model, iter, parent))
173  do {
174  gchar *value;
175  gtk_tree_model_get (model, iter, ORDER_COLUMN, &value, -1);
176  if (order < value)
177  done = true;
178  g_free (value);
179  if (done) return true;
180  } while (gtk_tree_model_iter_next (model, iter));
181  return false;
182 }
183 
184 static void insert_node (GtkTreeStore *store, GtkTreeIter *parent, GtkTreeIter *iter, const std::string &order)
185 {
186  if (find_place (store, parent, iter, order)) {
187  GtkTreeIter sibling = *iter;
188  gtk_tree_store_insert_before (store, iter, parent, &sibling);
189  }
190  else
191  gtk_tree_store_append (store, iter, parent);
192 }
193 
194 static void insert_pattern (
195  GtkTreeStore *store, GtkTreeIter *parent, Ypp::Selectable &sel, ZyppPattern pattern)
196 {
197  GtkTreeIter iter;
198  insert_node (store, parent, &iter, pattern->order());
199  set_row (store, &iter, sel, pattern);
200 }
201 
202 static void insert_category (GtkTreeStore *store, Ypp::Selectable &sel, ZyppPattern pattern)
203 {
204  GtkTreeIter iter;
205  std::string text ("<big><b>" + pattern->category() + "</b></big>");
206 
207  if (!find_name (store, NULL, &iter, text)) {
208  insert_node (store, NULL, &iter, pattern->order());
209  gtk_tree_store_set (store, &iter, HAS_CHECK_COLUMN, FALSE,
210  CHECK_COLUMN, FALSE, HAS_ICON_COLUMN, FALSE, ICON_COLUMN, NULL,
211  TEXT_COLUMN, text.c_str(),
212  ORDER_COLUMN, pattern->order().c_str(), POINTER_COLUMN, NULL, -1);
213  }
214  insert_pattern (store, &iter, sel, pattern);
215 }
216 
217 static void insert_language (GtkTreeStore *store, Ypp::Selectable &sel)
218 {
219  std::string name (sel.name());
220  bool done = false;
221  GtkTreeModel *model = GTK_TREE_MODEL (store);
222  GtkTreeIter iter;
223  if (gtk_tree_model_iter_children (model, &iter, NULL))
224  do {
225  gchar *value;
226  gtk_tree_model_get (model, &iter, TEXT_COLUMN, &value, -1);
227  if (g_utf8_collate (name.c_str(), value) < 0)
228  done = true;
229  g_free (value);
230  if (done) break;
231  } while (gtk_tree_model_iter_next (model, &iter));
232 
233  if (done) {
234  GtkTreeIter sibling = iter;
235  gtk_tree_store_insert_before (store, &iter, NULL, &sibling);
236  }
237  else
238  gtk_tree_store_append (store, &iter, NULL);
239 
240  set_row (store, &iter, sel, NULL);
241 }
242 
243 static void install_cb (GtkWidget *widget, ZyppSelectablePtr zsel)
244 { Ypp::Selectable (zsel).install(); }
245 static void undo_cb (GtkWidget *widget, ZyppSelectablePtr zsel)
246 { Ypp::Selectable (zsel).undo(); }
247 static void remove_cb (GtkWidget *widget, ZyppSelectablePtr zsel)
248 { Ypp::Selectable (zsel).remove(); }
249 
250 static GtkWidget *menu_item_append (GtkWidget *menu, const char *_label, const char *stock, bool sensitive)
251 {
252  std::string label;
253  if (_label)
254  label = YGUtils::mapKBAccel (_label);
255  GtkWidget *item;
256  if (_label) {
257  item = gtk_image_menu_item_new_with_mnemonic (label.c_str());
258  if (stock) {
259  GtkWidget *icon = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_MENU);
260  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), icon);
261  }
262  }
263  else
264  item = gtk_image_menu_item_new_from_stock (stock, NULL);
265  gtk_widget_set_sensitive (item, sensitive);
266  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
267  return item;
268 }
269 
270 static void right_click_cb (YGtkTreeView *yview, gboolean outreach, YGtkPkgPatternView *pThis)
271 {
272  if (!outreach) {
273  GtkTreeView *view = GTK_TREE_VIEW (yview);
274  GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
275  GtkTreeModel *model;
276  GtkTreeIter iter;
277  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
278  ZyppSelectablePtr zsel;
279  gtk_tree_model_get (model, &iter, POINTER_COLUMN, &zsel, -1);
280  Ypp::Selectable sel (zsel);
281 
282  GtkWidget *menu = gtk_menu_new(), *item;
283  if (sel.toModify()) {
284  item = menu_item_append (menu, NULL, GTK_STOCK_UNDO, true);
285  g_signal_connect (G_OBJECT (item), "activate",
286  G_CALLBACK (undo_cb), zsel);
287  }
288  else {
289  if (sel.isInstalled()) {
290  if (sel.canRemove()) {
291  item = menu_item_append (menu, _("&Remove"), GTK_STOCK_REMOVE, true);
292  g_signal_connect (G_OBJECT (item), "activate",
293  G_CALLBACK (remove_cb), zsel);
294  }
295  else
296  menu_item_append (menu, _("Remove: cannot remove patterns"), NULL, false);
297  }
298  else {
299  item = menu_item_append (menu, _("&Install"), GTK_STOCK_ADD, true);
300  g_signal_connect (G_OBJECT (item), "activate",
301  G_CALLBACK (install_cb), zsel);
302  }
303  }
304 
305  ygtk_tree_view_popup_menu (yview, menu);
306  }
307  }
308 }
309 
310 static void toggle_iter (GtkTreeModel *model, GtkTreeIter *iter)
311 {
312  Ypp::Selectable sel (get_iter_selectable (model, iter));
313  sel.toModify() ? sel.undo() : sel.install();
314 }
315 
316 static void row_activated_cb (GtkTreeView *view,
317  GtkTreePath *path, GtkTreeViewColumn *column, YGtkPkgPatternView *pThis)
318 {
319  GtkTreeModel *model = pThis->impl->getModel();
320  GtkTreeIter iter;
321  if (gtk_tree_model_get_iter (model, &iter, path))
322  toggle_iter (model, &iter);
323 }
324 
325 static void toggled_cb (GtkCellRendererToggle *renderer, gchar *path, YGtkPkgPatternView *pThis)
326 {
327  GtkTreeModel *model = pThis->impl->getModel();
328  GtkTreeIter iter;
329  if (gtk_tree_model_get_iter_from_string (model, &iter, path))
330  toggle_iter (model, &iter);
331 }
332 
333 static gboolean can_tree_select_cb (GtkTreeSelection *selection,
334  GtkTreeModel *model, GtkTreePath *path, gboolean path_selected, gpointer data)
335 {
336  GtkTreeIter iter;
337  gtk_tree_model_get_iter (model, &iter, path);
338  gpointer _data;
339  gtk_tree_model_get (model, &iter, POINTER_COLUMN, &_data, -1);
340  return _data != NULL;
341 }
342 
343 static void selection_changed_cb (GtkTreeSelection *selection, YGtkPkgPatternView *pThis)
344 { pThis->notify(); }
345 
346 static gboolean query_tooltip_cb (GtkWidget *widget, gint x, gint y,
347  gboolean keyboard_mode, GtkTooltip *tooltip, YGtkPkgPatternView *pThis)
348 {
349  GtkTreeView *view = GTK_TREE_VIEW (widget);
350  GtkTreeModel *model;
351  GtkTreePath *path;
352  GtkTreeIter iter;
353  if (gtk_tree_view_get_tooltip_context (view,
354  &x, &y, keyboard_mode, &model, &path, &iter)) {
355  gtk_tree_view_set_tooltip_row (view, tooltip, path);
356  gtk_tree_path_free (path);
357 
358  GtkTreeViewColumn *column;
359  int bx, by;
360  gtk_tree_view_convert_widget_to_bin_window_coords (
361  view, x, y, &bx, &by);
362  gtk_tree_view_get_path_at_pos (
363  view, x, y, NULL, &column, NULL, NULL);
364 
365  ZyppSelectablePtr zsel;
366  gtk_tree_model_get (model, &iter, POINTER_COLUMN, &zsel, -1);
367  if (zsel) {
368  Ypp::Selectable sel (zsel);
369 
370  if (column == ygtk_tree_view_get_column (YGTK_TREE_VIEW (view), 0)) { // check-marks
371  if (sel.isInstalled())
372  gtk_tooltip_set_text (tooltip,
373  _("Installed: cannot remove a pattern.\n\n"
374  "You must manually remove the individual packages you no "
375  "longer want to keep."));
376  else
377  gtk_tooltip_set_text (tooltip, _("Not installed"));
378  }
379  else {
380  std::string text (YGUtils::escapeMarkup (sel.description (false)));
381  gtk_tooltip_set_text (tooltip, text.c_str());
382  }
383  return TRUE;
384  }
385  }
386  return FALSE;
387 }
388 
389 YGtkPkgPatternView::YGtkPkgPatternView (Ypp::Selectable::Type type)
390 : impl (new Impl())
391 {
392  assert (type == Ypp::Selectable::LANGUAGE || type == Ypp::Selectable::PATTERN);
393 
394  GtkTreeStore *store = gtk_tree_store_new (TOTAL_COLUMNS, G_TYPE_BOOLEAN,
395  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING,
396  G_TYPE_STRING, G_TYPE_POINTER);
397 
398  if (type == Ypp::Selectable::LANGUAGE) {
399  Ypp::LangQuery query;
400  Ypp::List list (query);
401  list.sort (Ypp::List::NAME_SORT, true);
402  for (int i = 0; i < list.size(); i++)
403  insert_language (store, list.get(i));
404  }
405  else {
406  // HACK: in opensuse 11.2, zypp crashes when re-iterating patterns
407 #if 1 // zypp::PoolQuery is crashing on me when using Patterns
408  Ypp::PoolQuery query (type);
409  while (query.hasNext()) {
410  Ypp::Selectable sel = query.next();
411  ZyppPattern pattern = castZyppPattern (sel.zyppSel()->theObj());
412  if (pattern->userVisible())
413  insert_category (store, sel, pattern);
414  }
415 /* static Ypp::List list (0);
416  if (list.size() == 0) {
417  Ypp::PoolQuery query (type);
418  list = Ypp::List (query);
419  }
420  for (int i = 0; i < list.size(); i++) {
421  Ypp::Selectable &sel = list.get (i);
422  ZyppPattern pattern = castZyppPattern (sel.zyppSel()->theObj());
423  if (pattern->userVisible())
424  insert_category (store, sel, pattern);
425  }*/
426 #else
427  for (zypp::ResPoolProxy::const_iterator it =
428  zypp::getZYpp()->poolProxy().byKindBegin <zypp::Pattern>();
429  it != zypp::getZYpp()->poolProxy().byKindEnd <zypp::Pattern>();
430  it++) {
431  Ypp::Selectable sel (*it);
432  ZyppPattern pattern = castZyppPattern (sel.zyppSel()->theObj());
433  if (pattern->userVisible())
434  insert_category (store, sel, pattern);
435  }
436 #endif
437  g_object_set_data (G_OBJECT (store), "patterns", GINT_TO_POINTER (1));
438  }
439 
440  impl->view = ygtk_tree_view_new (NULL);
441  GtkTreeView *view = GTK_TREE_VIEW (impl->view);
442  gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
443  g_object_unref (G_OBJECT (store));
444  gtk_tree_view_set_headers_visible (view, FALSE);
445  gtk_tree_view_set_search_column (view, TEXT_COLUMN);
446  gtk_tree_view_expand_all (view);
447  gtk_tree_view_set_show_expanders (view, FALSE);
448  gtk_widget_set_has_tooltip (impl->view, TRUE);
449  g_signal_connect (G_OBJECT (view), "query-tooltip",
450  G_CALLBACK (query_tooltip_cb), this);
451  g_signal_connect (G_OBJECT (view), "right-click",
452  G_CALLBACK (right_click_cb), this);
453  g_signal_connect (G_OBJECT (view), "row-activated",
454  G_CALLBACK (row_activated_cb), this);
455 
456  GtkCellRenderer *renderer;
457  GtkTreeViewColumn *column;
458  renderer = gtk_cell_renderer_toggle_new();
459  column = gtk_tree_view_column_new_with_attributes (
460  NULL, renderer, "active", CHECK_COLUMN, "visible", HAS_CHECK_COLUMN, NULL);
461  g_signal_connect (G_OBJECT (renderer), "toggled",
462  G_CALLBACK (toggled_cb), this);
463  ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column);
464 
465  bool reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL;
466 
467  column = gtk_tree_view_column_new();
468  gtk_tree_view_column_set_title (column, NULL);
469  gtk_tree_view_column_set_spacing (column, 4);
470 
471  GtkCellRenderer *pix_renderer = gtk_cell_renderer_pixbuf_new();
472  if (!reverse)
473  gtk_tree_view_column_pack_start (column, pix_renderer, FALSE);
474 
475  renderer = ygtk_cell_renderer_text_new();
476  gtk_tree_view_column_pack_start (column, renderer, TRUE);
477  gtk_tree_view_column_set_attributes (column, renderer,
478  "markup", TEXT_COLUMN, NULL);
479  g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
480 
481  if (reverse)
482  gtk_tree_view_column_pack_start (column, pix_renderer, FALSE);
483  gtk_tree_view_column_set_attributes (column, pix_renderer,
484  "pixbuf", ICON_COLUMN, "visible", HAS_ICON_COLUMN, NULL);
485 
486  gtk_tree_view_column_set_resizable (column, TRUE);
487  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
488  gtk_tree_view_column_set_expand (column, TRUE);
489  ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column);
490 
491  GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
492  gtk_tree_selection_set_select_function (selection, can_tree_select_cb, NULL, NULL);
493  g_signal_connect (G_OBJECT (selection), "changed",
494  G_CALLBACK (selection_changed_cb), this);
495  clearSelection();
496 
497  impl->scroll = gtk_scrolled_window_new (NULL, NULL);
498  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->scroll),
499  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
500  gtk_scrolled_window_set_shadow_type (
501  GTK_SCROLLED_WINDOW (impl->scroll), GTK_SHADOW_IN);
502  gtk_container_add (GTK_CONTAINER (impl->scroll), impl->view);
503  gtk_widget_show_all (impl->scroll);
504 
505  g_idle_add_full (G_PRIORITY_LOW, set_rows_idle, store, NULL);
506 }
507 
508 YGtkPkgPatternView::~YGtkPkgPatternView()
509 { delete impl; }
510 
511 GtkWidget *YGtkPkgPatternView::getWidget()
512 { return impl->scroll; }
513 
514 void YGtkPkgPatternView::clearSelection()
515 {
516  GtkTreeView *view = GTK_TREE_VIEW (impl->view);
517  GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
518  g_signal_handlers_block_by_func (selection, (gpointer) selection_changed_cb, this);
519  gtk_tree_selection_unselect_all (selection);
520  g_signal_handlers_unblock_by_func (selection, (gpointer) selection_changed_cb, this);
521 }
522 
523 bool YGtkPkgPatternView::writeQuery (Ypp::PoolQuery &query)
524 {
525  GtkTreeView *view = GTK_TREE_VIEW (impl->view);
526  GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
527  GtkTreeModel *model;
528  GtkTreeIter iter;
529  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
530  Ypp::Selectable sel (get_iter_selectable (model, &iter));
531  Ypp::Collection collection (sel);
532  query.addCriteria (new Ypp::FromCollectionMatch (collection));
533  return true;
534  }
535  return false;
536 }
537 
538 bool isPatternsPoolEmpty()
539 { return zyppPool().empty <zypp::Pattern>(); }
540