libyui-gtk-pkg  2.42.9
 All Classes
ygtkpkgfilterview.cc
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 /* YGtkPkgFilterView, several zypp attribute query criteria implementations */
5 // check the header file for information about this widget
6 
7 /*
8  Textdomain "gtk"
9  */
10 
11 #include "YGi18n.h"
12 #include "YGUI.h"
13 #include "config.h"
14 #include "YGUtils.h"
15 #include "ygtkpkgfilterview.h"
16 #include "ygtkpkglistview.h"
17 #include "YGPackageSelector.h"
18 #include <gtk/gtk.h>
19 #include "ygtktreeview.h"
20 #include "ygtkcellrenderertext.h"
21 
22 // List store abstract
23 
25  GtkTreeModel *filter, *model;
26 
27  int convertlIterToStoreRow (GtkTreeIter *iter)
28  {
29  GtkTreeIter _iter;
30  gtk_tree_model_filter_convert_iter_to_child_iter (
31  GTK_TREE_MODEL_FILTER (filter), &_iter, iter);
32 
33  GtkTreePath *path = gtk_tree_model_get_path (model, &_iter);
34  int row = gtk_tree_path_get_indices (path)[0];
35  gtk_tree_path_free (path);
36  return row;
37  }
38 };
39 
40 YGtkPkgFilterModel::YGtkPkgFilterModel()
41 : impl (new Impl())
42 {
43  GtkListStore *store = gtk_list_store_new (TOTAL_COLUMNS, GDK_TYPE_PIXBUF,
44  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT,
45  G_TYPE_POINTER);
46  impl->model = GTK_TREE_MODEL (store);
47 
48  impl->filter = gtk_tree_model_filter_new (impl->model, NULL);
49  gtk_tree_model_filter_set_visible_column (
50  GTK_TREE_MODEL_FILTER (impl->filter), VISIBLE_COLUMN);
51  g_object_unref (G_OBJECT (impl->model));
52 }
53 
54 YGtkPkgFilterModel::~YGtkPkgFilterModel()
55 { g_object_unref (G_OBJECT (impl->filter)); }
56 
57 GtkTreeModel *YGtkPkgFilterModel::getModel()
58 { return impl->filter; }
59 
60 struct UpdateData {
61  YGtkPkgFilterModel *pThis;
62  Ypp::List *list;
64  : pThis (pThis), list (list) {}
65 };
66 
67 static gboolean update_list_cb (GtkTreeModel *model,
68  GtkTreePath *path, GtkTreeIter *iter, gpointer _data)
69 {
70  gchar *text;
71  UpdateData *data = (UpdateData *) _data;
72  gpointer mdata;
73  gtk_tree_model_get (model, iter, YGtkPkgFilterModel::TEXT_COLUMN, &text,
74  YGtkPkgFilterModel::DATA_COLUMN, &mdata, -1);
75 
76  bool separator = !(*text);
77  g_free (text);
78  if (separator) return FALSE;
79 
80  int row = gtk_tree_path_get_indices (path)[0];
81  if (row == 0 && data->pThis->firstRowIsAll())
82  data->pThis->setRowCount (0, data->list->size());
83  else
84  data->pThis->updateRow (*data->list, row, mdata);
85  return FALSE;
86 }
87 
88 void YGtkPkgFilterModel::updateList (Ypp::List list)
89 {
90  if (!begsUpdate()) return;
91  UpdateData data (this, &list);
92  gtk_tree_model_foreach (impl->model, update_list_cb, &data);
93 }
94 
95 bool YGtkPkgFilterModel::writeQuery (Ypp::PoolQuery &query, GtkTreeIter *iter)
96 {
97  gpointer data;
98  gtk_tree_model_get (impl->filter, iter, DATA_COLUMN, &data, -1);
99 
100  int row = impl->convertlIterToStoreRow (iter);
101  if (row == 0 && firstRowIsAll())
102  return false;
103  return writeRowQuery (query, row, data);
104 }
105 
106 GtkWidget *YGtkPkgFilterModel::createToolbox (GtkTreeIter *iter)
107 {
108  int row = impl->convertlIterToStoreRow (iter);
109  return createToolboxRow (row);
110 }
111 
112 void YGtkPkgFilterModel::addRow (const char *icon, const char *text,
113  bool enabled, gpointer data, bool defaultVisible)
114 {
115  // we use cell-render-pixbuf's "pixbuf" rather than "icon-name" so we
116  // can use a fixed size
117  GdkPixbuf *pixbuf = 0;
118  if (icon) {
119  if (!strcmp (icon, "empty")) {
120  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
121  gdk_pixbuf_fill (pixbuf, 0xffffff00);
122  }
123  else
124  pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
125  icon, 32, GtkIconLookupFlags (0), NULL);
126  }
127 
128  int weight = PANGO_WEIGHT_NORMAL;
129  if (firstRowIsAll() && gtk_tree_model_iter_n_children (impl->model, NULL) == 0)
130  weight = PANGO_WEIGHT_BOLD;
131 
132  GtkTreeIter iter;
133  GtkListStore *store = GTK_LIST_STORE (impl->model);
134  gtk_list_store_append (store, &iter);
135  gtk_list_store_set (store, &iter, ICON_COLUMN, pixbuf, TEXT_COLUMN, text,
136  COUNT_NUMBER_COLUMN, "", ENABLED_COLUMN, enabled, VISIBLE_COLUMN, defaultVisible,
137  DATA_COLUMN, data, WEIGHT_COLUMN, weight, -1);
138 
139  if (pixbuf) g_object_unref (pixbuf);
140 }
141 
142 void YGtkPkgFilterModel::addSeparator()
143 { addRow (NULL, "", true, NULL); }
144 
145 void YGtkPkgFilterModel::setRowCount (int row, int count)
146 {
147  GtkListStore *store = GTK_LIST_STORE (impl->model);
148  GtkTreeIter iter;
149  gtk_tree_model_iter_nth_child (impl->model, &iter, NULL, row);
150 
151  gchar *str = g_strdup_printf ("%d", count);
152  gtk_list_store_set (store, &iter, COUNT_NUMBER_COLUMN, str,
153  VISIBLE_COLUMN, row == 0 || count > 0, -1);
154  g_free (str);
155 }
156 
157 // Status
158 
160  Impl (YGtkPkgStatusModel *parent)
161  : parent (parent), list (0)
162  { Ypp::addSelListener (this); }
163 
164  ~Impl()
165  { Ypp::removeSelListener (this); }
166 
167  virtual void selectableModified()
168  { // make sure to update the "Modified" row on packages change
169  parent->updateRow (list, modifiedRow(), NULL);
170  }
171 
172  YGtkPkgStatusModel *parent;
173 
174  static int modifiedRow()
175  { return YGPackageSelector::get()->onlineUpdateMode() ? 2 : 5; }
176 
177  Ypp::List list;
178 };
179 
180 static Ypp::StatusMatch::Status rowToStatus (int row)
181 {
182  if (YGPackageSelector::get()->onlineUpdateMode())
183  switch (row) {
184  case 0: return Ypp::StatusMatch::NOT_INSTALLED;
185  case 1: return Ypp::StatusMatch::IS_INSTALLED;
186  case 2: return Ypp::StatusMatch::TO_MODIFY;
187  }
188  else
189  switch (row) {
190  case 1: return Ypp::StatusMatch::NOT_INSTALLED;
191  case 2: return Ypp::StatusMatch::IS_INSTALLED;
192  case 3: return Ypp::StatusMatch::HAS_UPGRADE;
193  case 4: return Ypp::StatusMatch::IS_LOCKED;
194  case 5: return Ypp::StatusMatch::TO_MODIFY;
195  }
196  return (Ypp::StatusMatch::Status) 0;
197 }
198 
199 YGtkPkgStatusModel::YGtkPkgStatusModel()
200 : impl (new Impl (this))
201 {
202  if (YGPackageSelector::get()->onlineUpdateMode()) {
203  // Translators: this refers to the package status
204  addRow (NULL, _("Available"), true, 0);
205  // Translators: this refers to the package status
206  addRow (NULL, _("Installed"), true, 0);
207  // Translators: this refers to the package status
208  addRow (NULL, _("Modified"), true, 0, false);
209  }
210  else {
211  // Translators: "Any status" may be translated as "All statuses" (whichever's smaller)
212  addRow (NULL, _("All packages"), true, 0);
213  // Translators: this refers to the package status
214  addRow (NULL, _("Not installed"), true, 0);
215  addRow (NULL, _("Installed"), true, 0);
216  // Translators: refers to package status: may be translated as "Upgrade"
217  addRow (NULL, _("Upgrades"), true, 0, false);
218  // Translators: this refers to the package status
219  addRow (NULL, _("Locked"), true, 0, false);
220  addRow (NULL, _("Modified"), true, 0, false);
221  }
222 }
223 
224 YGtkPkgStatusModel::~YGtkPkgStatusModel()
225 { delete impl; }
226 
227 bool YGtkPkgStatusModel::firstRowIsAll()
228 { return !YGPackageSelector::get()->onlineUpdateMode(); }
229 
230 void YGtkPkgStatusModel::updateRow (Ypp::List list, int row, gpointer data)
231 {
232  impl->list = list;
233  Ypp::StatusMatch match (rowToStatus (row));
234  setRowCount (row, list.count (&match));
235 }
236 
237 bool YGtkPkgStatusModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data)
238 {
239  query.addCriteria (new Ypp::StatusMatch (rowToStatus (row)));
240  return true;
241 }
242 
243 static void upgrade_patches_clicked_cb (GtkButton *button, YGtkPkgStatusModel *pThis)
244 {
245  Ypp::startTransactions();
246  for (int i = 0; i < pThis->impl->list.size(); i++) {
247  Ypp::Selectable sel = pThis->impl->list.get (i);
248  Ypp::Package pkg (sel);
249  if (sel.hasUpgrade() && pkg.isCandidatePatch())
250  sel.install();
251  }
252  Ypp::finishTransactions();
253 }
254 
255 static void upgrade_all_clicked_cb (GtkButton *button, YGtkPkgStatusModel *pThis)
256 {
257  Ypp::startTransactions();
258  for (int i = 0; i < pThis->impl->list.size(); i++) {
259  Ypp::Selectable sel = pThis->impl->list.get (i);
260  if (sel.hasUpgrade())
261  sel.install();
262  }
263  Ypp::finishTransactions();
264 }
265 
266 struct PkgHasPatchMatch : public Ypp::Match {
267  virtual bool match (Ypp::Selectable &sel)
268  {
269  Ypp::Package pkg (sel);
270  return pkg.isCandidatePatch();
271  }
272 };
273 
274 GtkWidget *YGtkPkgStatusModel::createToolboxRow (int row)
275 { // "Upgrade All" button
276  if (row == 3 && !YGPackageSelector::get()->onlineUpdateMode()) {
277  PkgHasPatchMatch match;
278  bool hasPatches = impl->list.count (&match);
279 
280  GtkWidget *hbox = gtk_hbox_new (FALSE, 6), *button, *icon;
281 
282  // Translators: this is shown next to the "Upgrade All" button
283  button = gtk_button_new_with_label (_("Only Upgrade Patches"));
284  gtk_widget_set_sensitive (button, hasPatches);
285  g_signal_connect (G_OBJECT (button), "clicked",
286  G_CALLBACK (upgrade_patches_clicked_cb), this);
287  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
288 
289  button = gtk_button_new_with_label (_("Upgrade All"));
290  icon = gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON);
291  gtk_button_set_image (GTK_BUTTON (button), icon);
292  g_signal_connect (G_OBJECT (button), "clicked",
293  G_CALLBACK (upgrade_all_clicked_cb), this);
294  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
295 
296  gtk_widget_show_all (hbox);
297  return hbox;
298  }
299  return NULL;
300 }
301 
302 // PackageKit-based Group
303 
304 struct PKGroup {
305  const char *name, *icon;
306  YPkgGroupEnum id;
307 
308  PKGroup (YPkgGroupEnum id) : id (id) {
309  name = zypp_tag_group_enum_to_localised_text (id);
310  icon = zypp_tag_enum_to_icon (id);
311  }
312  bool operator < (const PKGroup &other) const
313  { return strcmp (this->name, other.name) < 0; }
314 };
315 
316 YGtkPkgPKGroupModel::YGtkPkgPKGroupModel()
317 {
318  std::set <PKGroup> groups;
319  for (int i = 0; i < YPKG_GROUP_UNKNOWN; i++)
320  groups.insert (PKGroup ((YPkgGroupEnum) i));
321 
322  addRow ("empty", _("All packages"), true, 0);
323  for (std::set <PKGroup>::const_iterator it = groups.begin();
324  it != groups.end(); it++)
325  addRow (it->icon, it->name, true, GINT_TO_POINTER (((int)it->id)+1));
326 
327  for (int i = YPKG_GROUP_UNKNOWN; i < YPKG_GROUP_TOTAL; i++) {
328 #if ZYPP_VERSION >= 6030004
329  if (i == YPKG_GROUP_MULTIVERSION && zypp::sat::Pool::instance().multiversionEmpty())
330 #else
331  if (i == YPKG_GROUP_MULTIVERSION)
332 #endif
333  continue;
334  if (i == YPKG_GROUP_ORPHANED) continue;
335  std::string name = zypp_tag_group_enum_to_localised_text ((YPkgGroupEnum) i);
336  const char *icon = zypp_tag_enum_to_icon ((YPkgGroupEnum) i);
337  if (i == YPKG_GROUP_RECENT)
338  // Translators: when "upload" isn't easy to translate, translate this as "7 days old"
339  name += std::string ("\n<small>") + _("(uploaded last 7 days)") + "</small>";
340  addRow (icon, name.c_str(), true, GINT_TO_POINTER (i+1));
341  if (i == YPKG_GROUP_UNKNOWN)
342  addSeparator();
343  }
344 }
345 
346 void YGtkPkgPKGroupModel::updateRow (Ypp::List list, int row, gpointer data)
347 {
348  YPkgGroupEnum id = (YPkgGroupEnum) (GPOINTER_TO_INT (data)-1);
349  Ypp::PKGroupMatch match (id);
350  setRowCount (row, list.count (&match));
351 }
352 
353 bool YGtkPkgPKGroupModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data)
354 {
355  YPkgGroupEnum id = (YPkgGroupEnum) (GPOINTER_TO_INT (data)-1);
356  query.addCriteria (new Ypp::PKGroupMatch (id));
357  return true;
358 }
359 
360 // Repository
361 
363  // cannot store as point at the list-store
364  std::vector <Ypp::Repository> repos;
365  Ypp::Repository *selected;
366 
367  Impl() : selected (NULL)
368  { repos.reserve (zyppPool().knownRepositoriesSize()); }
369 };
370 
371 YGtkPkgRepositoryModel::YGtkPkgRepositoryModel()
372 : impl (new Impl())
373 {
374  addRow (NULL, _("All repositories"), true, 0);
375 
376  for (zypp::ResPoolProxy::repository_iterator it = zyppPool().knownRepositoriesBegin();
377  it != zyppPool().knownRepositoriesEnd(); it++) {
378  Ypp::Repository repo (*it);
379  const char *icon = getRepositoryStockIcon (repo);
380  std::string label (getRepositoryLabel (repo));
381  addRow (icon, label.c_str(), true, GINT_TO_POINTER (1));
382  impl->repos.push_back (repo);
383  }
384 
385  zypp::RepoManager manager;
386  std::list <zypp::RepoInfo> known_repos = manager.knownRepositories();
387  for (std::list <zypp::RepoInfo>::const_iterator it = known_repos.begin();
388  it != known_repos.end(); it++) {
389  Ypp::Repository repo (*it);
390  if (!repo.enabled()) {
391  const char *icon = getRepositoryStockIcon (repo);
392  std::string label (getRepositoryLabel (repo));
393  addRow (icon, label.c_str(), false, GINT_TO_POINTER (0));
394  }
395  }
396 
397  addRow (GTK_STOCK_MISSING_IMAGE, _("None"), true, GINT_TO_POINTER (2));
398 }
399 
400 YGtkPkgRepositoryModel::~YGtkPkgRepositoryModel()
401 { delete impl; }
402 
403 void YGtkPkgRepositoryModel::updateRow (Ypp::List list, int row, gpointer data)
404 {
405  if (GPOINTER_TO_INT (data) == 1) { // normal repo
406  Ypp::Repository &repo = impl->repos[row-1];
407  bool isSystem = repo.isSystem();
408  int count = 0;
409  for (int i = 0; i < list.size(); i++) {
410  Ypp::Selectable sel = list.get (i);
411  if (isSystem) {
412  if (sel.isInstalled())
413  count++;
414  }
415  else for (int i = 0; i < sel.totalVersions(); i++) {
416  Ypp::Version version = sel.version (i);
417  if (!version.isInstalled()) {
418  Ypp::Repository pkg_repo = version.repository();
419  if (repo == pkg_repo) {
420  count++;
421  break;
422  }
423  }
424  }
425  }
426  setRowCount (row, count);
427  }
428  else if (GPOINTER_TO_INT (data) == 2) { // 'none'
429  Ypp::PKGroupMatch match (YPKG_GROUP_ORPHANED);
430  setRowCount (row, list.count (&match));
431  }
432 }
433 
434 bool YGtkPkgRepositoryModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data)
435 {
436  impl->selected = 0;
437  if (GPOINTER_TO_INT (data) == 1) {
438  Ypp::Repository &repo = impl->repos[row-1];
439  query.addRepository (repo);
440  impl->selected = &repo;
441  }
442  else if (GPOINTER_TO_INT (data) == 2)
443  query.addCriteria (new Ypp::PKGroupMatch (YPKG_GROUP_ORPHANED));
444  return true;
445 }
446 
447 static void sync_toolbox_buttons (Ypp::Repository *repo, GtkWidget *box)
448 {
449  GtkWidget *button, *undo;
450  GList *children = gtk_container_get_children (GTK_CONTAINER (box));
451  button = (GtkWidget *) g_list_nth_data (children, 0);
452  undo = (GtkWidget *) g_list_nth_data (children, 1);
453  g_list_free (children);
454 
455  if (zypp::getZYpp()->resolver()->upgradingRepo (repo->zyppRepo())) {
456  gtk_widget_set_sensitive (button, FALSE);
457  gtk_widget_show (undo);
458  }
459  else {
460  gtk_widget_set_sensitive (button, TRUE);
461  gtk_widget_hide (undo);
462  }
463 }
464 
465 static void switch_repo_status (Ypp::Repository *repo)
466 {
467  ZyppRepository zrepo = repo->zyppRepo();
468  if (zypp::getZYpp()->resolver()->upgradingRepo (zrepo))
469  zypp::getZYpp()->resolver()->removeUpgradeRepo (zrepo);
470  else
471  zypp::getZYpp()->resolver()->addUpgradeRepo (zrepo);
472 }
473 
474 static void switch_clicked_cb (GtkButton *button, YGtkPkgRepositoryModel *pThis)
475 {
476  Ypp::Repository *repo = pThis->impl->selected;
477  switch_repo_status (repo);
478  if (!Ypp::runSolver()) // on solver cancel -- switch back
479  switch_repo_status (repo);
480  sync_toolbox_buttons (repo, gtk_widget_get_parent (GTK_WIDGET (button)));
481 }
482 
483 GtkWidget *YGtkPkgRepositoryModel::createToolboxRow (int row)
484 {
485  if (row > 0 && impl->selected) {
486  if (impl->selected->isOutdated()) {
487  GtkWidget *label = gtk_label_new (
488  _("Repository not refreshed in a long time."));
489  gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
490  YGUtils::setWidgetFont (label,
491  PANGO_STYLE_ITALIC, PANGO_WEIGHT_NORMAL, PANGO_SCALE_MEDIUM);
492  GtkWidget *icon = gtk_image_new_from_stock (
493  GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON);
494  GtkWidget *hbox = gtk_hbox_new (FALSE, 4);
495  gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, TRUE, 0);
496  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
497  gtk_widget_show_all (hbox);
498  return hbox;
499  }
500  else if (!impl->selected->isSystem()) {
501  GtkWidget *hbox = gtk_hbox_new (FALSE, 6), *button;
502  button = gtk_button_new_from_stock (GTK_STOCK_UNDO);
503  g_signal_connect (G_OBJECT (button), "clicked",
504  G_CALLBACK (switch_clicked_cb), this);
505  gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, TRUE, 0);
506  button = gtk_button_new_with_label (
507  _("Switch installed packages to the versions in this repository"));
508  gtk_button_set_image (GTK_BUTTON (button),
509  gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON));
510  g_signal_connect (G_OBJECT (button), "clicked",
511  G_CALLBACK (switch_clicked_cb), this);
512  gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, TRUE, 0);
513  gtk_widget_show_all (hbox);
514  sync_toolbox_buttons (impl->selected, hbox);
515  return hbox;
516  }
517  }
518  return NULL;
519 }
520 
521 static void edit_clicked_cb (GtkWidget *button, YGtkPkgRepositoryModel *pThis)
522 { YGPackageSelector::get()->showRepoManager(); }
523 
524 GtkWidget *YGtkPkgRepositoryModel::createInternalToolbox()
525 {
526  if (!YGPackageSelector::get()->repoMgrEnabled()) return NULL;
527  GtkWidget *button = gtk_button_new_with_label (_("Edit Repositories"));
528  GtkWidget *icon = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
529  gtk_button_set_image (GTK_BUTTON (button), icon);
530  g_signal_connect (G_OBJECT (button), "clicked",
531  G_CALLBACK (edit_clicked_cb), this);
532 
533  GtkWidget *align = gtk_alignment_new (0, 0, 0, 1);
534  gtk_container_add (GTK_CONTAINER (align), button);
535  return align;
536 }
537 
538 GtkWidget *YGtkPkgRepositoryModel::createInternalPopup()
539 {
540  if (!YGPackageSelector::get()->repoMgrEnabled()) return NULL;
541  GtkWidget *menu = gtk_menu_new();
542  GtkWidget *item = gtk_image_menu_item_new_with_mnemonic (_("Edit Repositories"));
543  GtkWidget *icon = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
544  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), icon);
545  gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
546  g_signal_connect (G_OBJECT (item), "activate",
547  G_CALLBACK (edit_clicked_cb), this);
548  return menu;
549 }
550 
551 // Support
552 
553 YGtkPkgSupportModel::YGtkPkgSupportModel()
554 {
555  addRow (NULL, _("All packages"), true, 0);
556  for (int i = 0; i < Ypp::Package::supportTotal(); i++)
557  addRow (NULL, Ypp::Package::supportSummary (i).c_str(), true, 0, false);
558 }
559 
560 void YGtkPkgSupportModel::updateRow (Ypp::List list, int row, gpointer data)
561 {
562  Ypp::SupportMatch match (row-1);
563  setRowCount (row, list.count (&match));
564 }
565 
566 bool YGtkPkgSupportModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data)
567 {
568  query.addCriteria (new Ypp::SupportMatch (row-1));
569  return true;
570 }
571 
572 // Priority
573 
574 YGtkPkgPriorityModel::YGtkPkgPriorityModel()
575 {
576  // Translators: "Any priority" may be translated as "All priorities" (whichever's smaller)
577  addRow (NULL, _("All patches"), true, 0);
578  for (int i = 0; i < Ypp::Patch::priorityTotal(); i++)
579  addRow (NULL, Ypp::Patch::prioritySummary (i), true, 0, false);
580 }
581 
582 void YGtkPkgPriorityModel::updateRow (Ypp::List list, int row, gpointer data)
583 {
584  Ypp::PriorityMatch match (row-1);
585  setRowCount (row, list.count (&match));
586 }
587 
588 bool YGtkPkgPriorityModel::writeRowQuery (Ypp::PoolQuery &query, int row, gpointer data)
589 {
590  query.addCriteria (new Ypp::PriorityMatch (row-1));
591  return true;
592 }
593 
594 // View widget
595 
597  GtkWidget *box, *scroll, *view;
598  YGtkPkgFilterModel *model;
599 
600  Impl (YGtkPkgFilterModel *model) : model (model) {}
601  ~Impl() { delete model; }
602 };
603 
604 static gboolean tree_selection_possible_cb (GtkTreeSelection *selection,
605  GtkTreeModel *model, GtkTreePath *path, gboolean is_selected, gpointer data)
606 {
607  GtkTreeIter iter;
608  gtk_tree_model_get_iter (model, &iter, path);
609  int col = GPOINTER_TO_INT (data);
610  gboolean val;
611  gtk_tree_model_get (model, &iter, col, &val, -1);
612  return val;
613 }
614 
615 static void selection_changed_cb (GtkTreeSelection *selection, YGtkPkgFilterView *pThis)
616 {
617  if (gtk_tree_selection_get_selected (selection, NULL, NULL))
618  pThis->notify();
619 }
620 
621 static void right_click_cb (YGtkTreeView *view, gboolean outreach, YGtkPkgFilterView *pThis)
622 {
623  GtkWidget *menu = pThis->impl->model->createInternalPopup();
624  if (menu) ygtk_tree_view_popup_menu (view, menu);
625 }
626 
627 YGtkPkgFilterView::YGtkPkgFilterView (YGtkPkgFilterModel *model)
628 : YGtkPkgQueryWidget(), impl (new Impl (model))
629 {
630  bool updates = model->begsUpdate();
631  impl->view = ygtk_tree_view_new (NULL);
632  gtk_tree_view_set_model (GTK_TREE_VIEW (impl->view), model->getModel());
633  GtkTreeView *view = GTK_TREE_VIEW (impl->view);
634  gtk_tree_view_set_headers_visible (view, FALSE);
635  gtk_tree_view_set_search_column (view, YGtkPkgFilterModel::TEXT_COLUMN);
636  if (updates)
637  gtk_tree_view_set_tooltip_column (view, YGtkPkgFilterModel::TEXT_COLUMN);
638  gtk_tree_view_set_enable_tree_lines (view, TRUE);
639  gtk_tree_view_set_row_separator_func (view, YGUtils::empty_row_is_separator_cb,
640  GINT_TO_POINTER (YGtkPkgFilterModel::TEXT_COLUMN), NULL);
641  gtk_tree_view_expand_all (view);
642  g_signal_connect (G_OBJECT (view), "right-click",
643  G_CALLBACK (right_click_cb), this);
644 
645  GtkCellRenderer *renderer;
646  GtkTreeViewColumn *column;
647  if (model->hasIconCol()) {
648  renderer = gtk_cell_renderer_pixbuf_new();
649  column = gtk_tree_view_column_new_with_attributes (NULL,
650  renderer, "pixbuf", YGtkPkgFilterModel::ICON_COLUMN,
651  "sensitive", YGtkPkgFilterModel::ENABLED_COLUMN, NULL);
652  ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column);
653  }
654  renderer = ygtk_cell_renderer_text_new();
655  column = gtk_tree_view_column_new_with_attributes (
656  NULL, renderer, "markup", YGtkPkgFilterModel::TEXT_COLUMN,
657  "sensitive", YGtkPkgFilterModel::ENABLED_COLUMN,
658  "weight", YGtkPkgFilterModel::WEIGHT_COLUMN, NULL);
659  g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
660  gtk_tree_view_column_set_expand (column, TRUE);
661  ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column);
662 
663  renderer = gtk_cell_renderer_text_new();
664  column = gtk_tree_view_column_new_with_attributes (
665  NULL, renderer, "text", YGtkPkgFilterModel::COUNT_NUMBER_COLUMN,
666  "sensitive", YGtkPkgFilterModel::ENABLED_COLUMN,
667  "weight", YGtkPkgFilterModel::WEIGHT_COLUMN, NULL);
668  g_object_set (G_OBJECT (renderer), "xalign", 1.0,
669  "scale", PANGO_SCALE_SMALL, "foreground", "#8c8c8c", NULL);
670  ygtk_tree_view_append_column (YGTK_TREE_VIEW (view), column);
671 
672  GtkTreeSelection *selection = gtk_tree_view_get_selection (view);
673  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
674  gtk_tree_selection_set_select_function (selection, tree_selection_possible_cb,
675  GINT_TO_POINTER (YGtkPkgFilterModel::ENABLED_COLUMN), NULL);
676  g_signal_connect (G_OBJECT (selection), "changed",
677  G_CALLBACK (selection_changed_cb), this);
678  clearSelection();
679 
680  impl->scroll = gtk_scrolled_window_new (NULL, NULL);
681  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->scroll),
682  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
683  gtk_scrolled_window_set_shadow_type (
684  GTK_SCROLLED_WINDOW (impl->scroll), GTK_SHADOW_IN);
685  gtk_container_add (GTK_CONTAINER (impl->scroll), impl->view);
686 
687  GtkWidget *toolbox = model->createInternalToolbox();
688  if (toolbox) {
689  impl->box = gtk_vbox_new (FALSE, 6);
690  gtk_box_pack_start (GTK_BOX (impl->box), impl->scroll, TRUE, TRUE, 0);
691  gtk_box_pack_start (GTK_BOX (impl->box), toolbox, FALSE, TRUE, 0);
692  }
693  else
694  impl->box = impl->scroll;
695  gtk_widget_show_all (impl->box);
696 }
697 
698 YGtkPkgFilterView::~YGtkPkgFilterView()
699 { delete impl; }
700 
701 GtkWidget *YGtkPkgFilterView::getWidget()
702 { return impl->box; }
703 
704 bool YGtkPkgFilterView::begsUpdate()
705 { return impl->model->begsUpdate(); }
706 
707 void YGtkPkgFilterView::updateList (Ypp::List list)
708 {
709  impl->model->updateList (list);
710 
711  // always ensure some row is selected
712  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view));
713  GtkTreeModel *model;
714  if (!gtk_tree_selection_get_selected (selection, &model, NULL)) {
715  GtkTreeIter iter;
716  if (gtk_tree_model_get_iter_first (model, &iter)) {
717  g_signal_handlers_block_by_func (selection, (gpointer) selection_changed_cb, this);
718  gtk_tree_selection_select_iter (selection, &iter);
719  g_signal_handlers_unblock_by_func (selection, (gpointer) selection_changed_cb, this);
720  }
721  }
722 }
723 
724 void YGtkPkgFilterView::clearSelection()
725 { select (0); /* select 1st row, so some row is selected */ }
726 
727 bool YGtkPkgFilterView::writeQuery (Ypp::PoolQuery &query)
728 {
729  GtkTreeModel *model;
730  GtkTreeIter iter;
731  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view));
732  if (gtk_tree_selection_get_selected (selection, &model, &iter))
733  return impl->model->writeQuery (query, &iter);
734  return false;
735 }
736 
737 GtkWidget *YGtkPkgFilterView::createToolbox()
738 {
739  GtkTreeIter iter;
740  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view));
741  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
742  return impl->model->createToolbox (&iter);
743  return NULL;
744 }
745 
746 void YGtkPkgFilterView::select (int row)
747 {
748  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->view));
749  g_signal_handlers_block_by_func (selection, (gpointer) selection_changed_cb, this);
750  GtkTreeIter iter;
751  if (row >= 0) {
752  gtk_tree_model_iter_nth_child (impl->model->getModel(), &iter, NULL, row);
753  gtk_tree_selection_select_iter (selection, &iter);
754  }
755  else
756  gtk_tree_selection_unselect_all (selection);
757  g_signal_handlers_unblock_by_func (selection, (gpointer) selection_changed_cb, this);
758 }
759