libyui-gtk-pkg  2.43.2
 All Classes
YGPackageSelector.cc
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 /*
5  Textdomain "gtk"
6  */
7 
8 #include "YGi18n.h"
9 #include "config.h"
10 #include <string.h>
11 #include "YGUtils.h"
12 #include "YGDialog.h"
13 #include "YGUI.h"
14 #include "YGPackageSelector.h"
15 #include "yzyppwrapper.h"
16 #include "ygtkwizard.h"
17 #include "ygtkhtmlwrap.h"
18 #include "ygtkpkglistview.h"
19 #include "ygtkpkgsearchentry.h"
20 #include "ygtkpkgfilterview.h"
21 #include "ygtkpkgrpmgroupsview.h"
22 #include "ygtkpkgquerycombo.h"
23 #include "ygtkpkgpatternview.h"
24 #include "ygtkpkglanguageview.h"
25 #include "ygtkpkgundolist.h"
26 #include "ygtkpkgmenubar.h"
27 #include "ygtkpkgstatusbar.h"
28 #include "ygtkpkgdetailview.h"
29 
30 #include "ygtkpkghistorydialog.h"
31 #ifdef HAS_VESTIGIAL_DIALOG
32 #include "ygtkpkgvestigialdialog.h"
33 #endif
34 
35 //#define USE_LIST_BUTTONS
36 
37 YGPackageSelector *YGPackageSelector::singleton = 0;
38 
42 {
43 GtkWidget *m_widget, *m_toolbox;
44 YGtkPkgListView *m_list;
45 YGtkPkgQueryCombo *m_combo;
46 YGtkPkgSearchEntry *m_entry;
47 std::list <YGtkPkgQueryWidget *> m_queryWidgets;
48 YGtkPkgUndoList *m_undo;
49 YGtkPkgStatusBar *m_status;
50 YGtkPkgMenuBar *m_menu;
51 std::list <std::string> m_filterSuffices;
52 GtkWidget *m_overview;
53 YGtkPkgDetailView *m_details;
54 guint m_refresh_id;
55 Ypp::List m_refresh_list;
56 bool has_patterns_mode;
57 
58 struct SuffixFilter : public Ypp::Match {
59  SuffixFilter (Impl *pThis) : pThis (pThis) {}
60 
61  virtual bool match (Ypp::Selectable &sel)
62  {
63  if (!pThis->m_filterSuffices.empty()) {
64  std::string name (sel.name());
65  for (std::list <std::string>::iterator it = pThis->m_filterSuffices.begin();
66  it != pThis->m_filterSuffices.end(); it++)
67  if (YGUtils::endsWith (name, *it))
68  return false;
69  }
70  return true;
71  }
72 
73  Impl *pThis;
74 };
75 
76  GtkWidget *getWidget() { return m_widget; }
77 
78  GtkWidget *createMainArea()
79  {
80  bool onlineUpdate = YGPackageSelector::get()->onlineUpdateMode();
81 
82  m_entry = new YGtkPkgSearchEntry();
83  m_queryWidgets.push_back (m_entry);
84 
85  m_list = new YGtkPkgListView (false, Ypp::List::NAME_SORT, false, true);
86 #ifdef USE_LIST_BUTTONS
87  m_list->addImageColumn (NULL, STATUS_ICON_PROP);
88 #else
89  m_list->addCheckColumn (INSTALLED_CHECK_PROP);
90 #endif
91  m_list->addTextColumn (_("Name"), NAME_SUMMARY_PROP, true, -1);
92  m_list->addTextColumn (_("Version"), VERSION_PROP, true, 125);
93  if (!onlineUpdate)
94  m_list->addTextColumn (_("Size"), SIZE_PROP, false, 85);
95 #ifdef USE_LIST_BUTTONS
96  m_list->addButtonColumn (_("Action"), ACTION_BUTTON_PROP);
97 #endif
98  m_list->addTextColumn (_("Repository"), REPOSITORY_PROP, false, 180);
99  if (!onlineUpdate)
100  m_list->addTextColumn (_("Supportability"), SUPPORT_PROP, false, 120);
101  m_list->setListener (this);
102  m_entry->setActivateWidget (m_list->getView());
103 
104  GtkWidget *hbox = gtk_hbox_new (FALSE, 2);
105  GtkWidget *label = gtk_label_new_with_mnemonic (_("Package _listing:"));
106  gtk_label_set_mnemonic_widget (GTK_LABEL (label), m_list->getView());
107  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
108  gtk_box_pack_start (GTK_BOX (hbox), gtk_event_box_new(), TRUE, TRUE, 0);
109  gtk_box_pack_start (GTK_BOX (hbox), m_entry->getWidget(), FALSE, TRUE, 0);
110  m_toolbox = gtk_hbox_new (FALSE, 6);
111 
112  GtkWidget *overview = ygtk_html_wrap_new();
113  if (onlineUpdate)
114  ygtk_html_wrap_set_text (overview, _(
115  "<h1><img src=\"gtk-dialog-info\" />Overview</h1>"
116  "<p>Press a patch in the list to see more information about it.</p>"
117  "<p>To install a patch, just click on its \"checkbox\".</p>"), FALSE);
118  else
119  ygtk_html_wrap_set_text (overview, _(
120  "<h1><img src=\"gtk-dialog-info\" />Overview</h1>"
121  "<p>Browse packages using the groups list on the left.</p>"
122  "<p>Press a package in the list to see more information about it.</p>"
123  "<p>To install or remove a package, just click on its \"checkbox\".</p>"), FALSE);
124 
125  m_overview = gtk_scrolled_window_new (NULL, NULL);
126  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (m_overview),
127  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
128  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (m_overview),
129  GTK_SHADOW_IN);
130  gtk_container_add (GTK_CONTAINER (m_overview), overview);
131  GtkWidget *text = gtk_event_box_new();
132  gtk_container_add (GTK_CONTAINER (text), m_overview);
133 
134  GtkWidget *list_vbox = gtk_vbox_new (FALSE, 4);
135  gtk_box_pack_start (GTK_BOX (list_vbox), m_list->getWidget(), TRUE, TRUE, 0);
136  gtk_box_pack_start (GTK_BOX (list_vbox), m_toolbox, FALSE, TRUE, 0);
137 
138  GtkWidget *vpaned = gtk_vpaned_new();
139  gtk_paned_pack1 (GTK_PANED (vpaned), list_vbox, TRUE, FALSE);
140  gtk_paned_pack2 (GTK_PANED (vpaned), text, FALSE, TRUE);
141  YGUtils::setPaneRelPosition (vpaned, .65);
142 
143  GtkWidget *_vbox = gtk_vbox_new (FALSE, 0);
144  gtk_box_pack_start (GTK_BOX (_vbox), hbox, FALSE, TRUE, 0);
145  gtk_box_pack_start (GTK_BOX (_vbox), vpaned, TRUE, TRUE, 0);
146  return _vbox;
147  }
148 
149  GtkWidget *createSidebar()
150  {
151  m_combo = new YGtkPkgQueryCombo (this);
152  if (YGPackageSelector::get()->onlineUpdateMode()) {
153  m_combo->add (_("Priorities"));
154  m_combo->add (_("Repositories"));
155 
156  int active = YGPackageSelector::get()->repoMode() ? 1 : 0;
157  m_combo->setActive (active);
158  }
159  else {
160  m_combo->add (_("Groups"));
161  m_combo->add (_("RPM Groups"));
162  m_combo->add (_("Repositories"));
163  m_combo->add (_("Support"));
164  m_combo->add ("");
165  has_patterns_mode = !isPatternsPoolEmpty();
166  if (has_patterns_mode)
167  m_combo->add (_("Patterns"));
168  m_combo->add (_("Languages"));
169 
170  int active = 5;
171  if (YGPackageSelector::get()->repoMode())
172  active = 2;
173  else if (YGPackageSelector::get()->searchMode())
174  active = 0;
175  m_combo->setActive (active);
176  }
177  m_queryWidgets.push_back (m_combo);
178 
180  if (YGPackageSelector::get()->updateMode())
181  status->select (3);
182  m_queryWidgets.push_back (status);
183 
184  GtkWidget *vpaned = gtk_vpaned_new();
185  gtk_paned_pack1 (GTK_PANED (vpaned), m_combo->getWidget(), TRUE, FALSE);
186  gtk_paned_pack2 (GTK_PANED (vpaned), status->getWidget(), FALSE, FALSE);
187  YGUtils::setPaneRelPosition (vpaned, .80);
188  return vpaned;
189  }
190 
191  Impl() : m_menu (NULL), m_details (NULL), m_refresh_id (0), m_refresh_list (0)
192  {
193  Ypp::init();
194  m_undo = new YGtkPkgUndoList();
195  m_status = 0;
196 
197  GtkWidget *hpaned = gtk_hpaned_new();
198  gtk_paned_pack1 (GTK_PANED (hpaned), createSidebar(), FALSE, TRUE);
199  gtk_paned_pack2 (GTK_PANED (hpaned), createMainArea(), TRUE, FALSE);
200  YGUtils::setPaneRelPosition (hpaned, .28);
201 
202  m_widget = gtk_vbox_new (FALSE, 6);
203  gtk_box_pack_start (GTK_BOX (m_widget), hpaned, TRUE, TRUE, 0);
204  if (!YGPackageSelector::get()->onlineUpdateMode()) {
205  m_status = new YGtkPkgStatusBar (m_undo);
206  gtk_box_pack_start (GTK_BOX (m_widget), m_status->getWidget(), FALSE, TRUE, 0);
207  }
208  gtk_widget_show_all (m_widget);
209  gtk_widget_hide (m_toolbox);
210 
211  for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin();
212  it != m_queryWidgets.end(); it++)
213  (*it)->setListener (this);
214  }
215 
216  ~Impl()
217  {
218  if (m_refresh_id)
219  g_source_remove (m_refresh_id);
220  for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin();
221  it != m_queryWidgets.end(); it++)
222  delete *it;
223  delete m_list;
224  delete m_menu;
225  delete m_status;
226  delete m_details;
227  delete m_undo;
228  Ypp::finish();
229  }
230 
231  void clearQueryWidgets()
232  {
233  for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin();
234  it != m_queryWidgets.end(); it++)
235  (*it)->clearSelection();
236  }
237 
238  // Ypp::Interface
239 
240  static bool acceptText (Ypp::Selectable &selectable, const std::string &title,
241  const std::string &open, const std::string &text)
242  {
243  GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(),
244  (GtkDialogFlags) 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
245  "%s %s", selectable.name().c_str(), title.c_str());
246  if (!open.empty())
247  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
248  "%s", open.c_str());
249  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
250 
251  GtkWidget *view = ygtk_html_wrap_new(), *scroll;
252  ygtk_html_wrap_set_text (view, text.c_str(), FALSE);
253 
254  scroll = gtk_scrolled_window_new (NULL, NULL);
255  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
256  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
257  gtk_scrolled_window_set_shadow_type
258  (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
259  gtk_container_add (GTK_CONTAINER (scroll), view);
260 
261  GtkBox *content = GTK_BOX (gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
262  gtk_box_pack_start (content, scroll, TRUE, TRUE, 6);
263 
264  gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
265  gtk_window_set_default_size (GTK_WINDOW (dialog), 550, 500);
266  gtk_widget_show_all (dialog);
267 
268  gint ret = gtk_dialog_run (GTK_DIALOG (dialog));
269  bool confirmed = (ret == GTK_RESPONSE_YES);
270  gtk_widget_destroy (dialog);
271  return confirmed;
272  }
273 
274  virtual bool showLicense (Ypp::Selectable &sel, const std::string &license)
275  {
276  return acceptText (sel, _("License Agreement"),
277  _("Do you accept the terms of this license?"), license);
278  }
279 
280  virtual bool showMessage (Ypp::Selectable &sel, const std::string &message)
281  { return acceptText (sel, _("Warning Message"), _("Install anyway?"), message); }
282 
283  virtual bool resolveProblems (const std::list <Ypp::Problem *> &problems)
284  {
285  // we can't use ordinary radio buttons, as gtk+ enforces that in a group
286  // one must be selected...
287  #define DETAILS_PAD 25
288  enum ColumnAlias {
289  SHOW_TOGGLE_COL, ACTIVE_TOGGLE_COL, TEXT_COL, WEIGHT_TEXT_COL,
290  TEXT_PAD_COL, APPLY_PTR_COL, TOTAL_COLS
291  };
292 
293  struct inner {
294  static void solution_toggled (GtkTreeModel *model, GtkTreePath *path)
295  {
296  GtkTreeStore *store = GTK_TREE_STORE (model);
297  GtkTreeIter iter, parent;
298 
299  gboolean enabled;
300  bool *apply;
301  gtk_tree_model_get_iter (model, &iter, path);
302  gtk_tree_model_get (model, &iter, ACTIVE_TOGGLE_COL, &enabled,
303  APPLY_PTR_COL, &apply, -1);
304  if (!apply)
305  return;
306 
307  // disable all the other radios on the group, setting current
308  gtk_tree_model_get_iter (model, &iter, path);
309  if (gtk_tree_model_iter_parent (model, &parent, &iter)) {
310  gtk_tree_model_iter_children (model, &iter, &parent);
311  do {
312  gtk_tree_store_set (store, &iter, ACTIVE_TOGGLE_COL, FALSE, -1);
313  bool *apply;
314  gtk_tree_model_get (model, &iter, APPLY_PTR_COL, &apply, -1);
315  if (apply) *apply = false;
316  } while (gtk_tree_model_iter_next (model, &iter));
317  }
318 
319  enabled = !enabled;
320  *apply = enabled;
321  gtk_tree_model_get_iter (model, &iter, path);
322  gtk_tree_store_set (store, &iter, ACTIVE_TOGGLE_COL, enabled, -1);
323  }
324  static void cursor_changed_cb (GtkTreeView *view, GtkTreeModel *model)
325  {
326  GtkTreePath *path;
327  gtk_tree_view_get_cursor (view, &path, NULL);
328  solution_toggled (model, path);
329  gtk_tree_path_free (path);
330  }
331  };
332 
333  // model
334  GtkTreeStore *store = gtk_tree_store_new (TOTAL_COLS,
335  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_INT,
336  G_TYPE_INT, G_TYPE_POINTER);
337  for (std::list <Ypp::Problem *>::const_iterator it = problems.begin();
338  it != problems.end(); it++) {
339  GtkTreeIter problem_iter;
340  gtk_tree_store_append (store, &problem_iter, NULL);
341  gtk_tree_store_set (store, &problem_iter, SHOW_TOGGLE_COL, FALSE,
342  TEXT_COL, (*it)->description.c_str(), WEIGHT_TEXT_COL, PANGO_WEIGHT_BOLD, -1);
343  if (!(*it)->details.empty()) {
344  GtkTreeIter details_iter;
345  gtk_tree_store_append (store, &details_iter, &problem_iter);
346  gtk_tree_store_set (store, &details_iter, SHOW_TOGGLE_COL, FALSE,
347  TEXT_COL, (*it)->details.c_str(), TEXT_PAD_COL, DETAILS_PAD, -1);
348  }
349 
350  for (int i = 0; (*it)->getSolution (i); i++) {
351  Ypp::Problem::Solution *solution = (*it)->getSolution (i);
352  GtkTreeIter solution_iter;
353  gtk_tree_store_append (store, &solution_iter, &problem_iter);
354  gtk_tree_store_set (store, &solution_iter, SHOW_TOGGLE_COL, TRUE,
355  WEIGHT_TEXT_COL, PANGO_WEIGHT_NORMAL,
356  ACTIVE_TOGGLE_COL, FALSE, TEXT_COL, solution->description.c_str(),
357  APPLY_PTR_COL, &solution->apply, -1);
358  if (!solution->details.empty()) {
359  gtk_tree_store_append (store, &solution_iter, &problem_iter);
360  gtk_tree_store_set (store, &solution_iter, SHOW_TOGGLE_COL, FALSE,
361  WEIGHT_TEXT_COL, PANGO_WEIGHT_NORMAL,
362  TEXT_COL, solution->details.c_str(), TEXT_PAD_COL, DETAILS_PAD, -1);
363  }
364  }
365  }
366 
367  // interface
368  GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(),
369  GtkDialogFlags (0), GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, "%s",
370  _("There are some conflicts on the transaction that must be solved manually."));
371  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
372  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
373  GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, NULL);
374  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_APPLY);
375 
376  GtkWidget *view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
377  g_object_unref (G_OBJECT (store));
378  gtk_tree_selection_set_mode (gtk_tree_view_get_selection (
379  GTK_TREE_VIEW (view)), GTK_SELECTION_NONE);
380  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
381  gtk_tree_view_set_search_column (GTK_TREE_VIEW (view), TEXT_COL);
382 
383  GtkTreeViewColumn *column;
384  GtkCellRenderer *renderer;
385  column = gtk_tree_view_column_new();
386 
387  renderer = gtk_cell_renderer_toggle_new();
388  gtk_tree_view_column_pack_start (column, renderer, FALSE);
389  gtk_tree_view_column_set_attributes (column, renderer,
390  "visible", SHOW_TOGGLE_COL, "active", ACTIVE_TOGGLE_COL, NULL);
391  gtk_cell_renderer_toggle_set_radio (
392  GTK_CELL_RENDERER_TOGGLE (renderer), TRUE);
393  // we should not connect the actual toggle button, as we toggle on row press
394  g_signal_connect (G_OBJECT (view), "cursor-changed",
395  G_CALLBACK (inner::cursor_changed_cb), store);
396 
397  renderer = gtk_cell_renderer_text_new();
398  gtk_tree_view_column_pack_start (column, renderer, TRUE);
399  gtk_tree_view_column_set_attributes (column, renderer,
400  "text", TEXT_COL, "weight", WEIGHT_TEXT_COL, "xpad", TEXT_PAD_COL, NULL);
401 /* g_object_set (G_OBJECT (renderer), "wrap-width", 400,
402  "wrap-mode", PANGO_WRAP_WORD, NULL);*/
403 
404  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
405  gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
406  gtk_widget_set_has_tooltip (view, TRUE);
407 
408  GtkWidget *scroll = gtk_scrolled_window_new (NULL, NULL);
409  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
410  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
411  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll),
412  GTK_SHADOW_IN);
413  gtk_container_add (GTK_CONTAINER (scroll), view);
414 
415  GtkBox *content = GTK_BOX (gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
416  gtk_box_pack_start (content, scroll, TRUE, TRUE, 6);
417 
418  gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
419  gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 500);
420  gtk_widget_show_all (dialog);
421 
422  bool apply = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY);
423  // disconnect before destroying
424  g_signal_handlers_disconnect_by_data (G_OBJECT (view), store);
425 
426  gtk_widget_destroy (dialog);
427  return apply;
428  }
429 
430  // YGtkPkgListView::Listener
431 
432  virtual void selectionChanged()
433  {
434  Ypp::List selected = m_list->getSelected();
435  if (selected.size() == 0) return;
436 
437  if (m_overview) {
438  GtkWidget *parent = gtk_widget_get_parent (m_overview);
439  gtk_container_remove (GTK_CONTAINER (parent), m_overview);
440  m_overview = NULL;
441 
442  m_details = new YGtkPkgDetailView();
443  gtk_container_add (GTK_CONTAINER (parent), m_details->getWidget());
444  }
445 
446  m_details->setList (selected);
447  }
448 
449  // YGtkPkgQueryWidget::Listener
450 
451  void refreshQueryWidget (YGtkPkgQueryWidget *widget)
452  {
453  Ypp::Selectable::Type type = Ypp::Selectable::PACKAGE;
454  if (YGPackageSelector::get()->onlineUpdateMode())
455  type = Ypp::Selectable::PATCH;
456  Ypp::PoolQuery query (type);
457  for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin();
458  it != m_queryWidgets.end(); it++) {
459  if (*it == widget)
460  continue;
461  (*it)->writeQuery (query);
462  }
463  query.addCriteria (new SuffixFilter (this));
464 
465  Ypp::List list (query);
466  widget->updateList (list);
467  }
468 
469  void refreshFilters (Ypp::List list)
470  {
471  for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin();
472  it != m_queryWidgets.end(); it++) {
473  if ((*it)->begsUpdate()) {
474  if (YGPackageSelector::get()->yield()) return;
475 
476  if ((*it)->modified)
477  refreshQueryWidget (*it);
478  else
479  (*it)->updateList (list);
480  }
481  }
482  }
483 
484  void refreshToolbox()
485  {
486  // only present one toolbox widget as they may be quite large
487  GtkWidget *toolbox = 0;
488  for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin();
489  it != m_queryWidgets.end(); it++)
490  if ((toolbox = (*it)->createToolbox()))
491  break;
492 
493  GList *children = gtk_container_get_children (GTK_CONTAINER (m_toolbox));
494  for (GList *i = children; i; i = i->next)
495  gtk_container_remove (GTK_CONTAINER (m_toolbox), (GtkWidget *) i->data);
496  g_list_free (children);
497 
498  if (toolbox) {
499  gtk_box_pack_end (GTK_BOX (m_toolbox), toolbox, FALSE, TRUE, 0);
500  gtk_widget_show (m_toolbox);
501  }
502  else
503  gtk_widget_hide (m_toolbox);
504  }
505 
506  static gboolean refresh_filters_timeout_cb (gpointer data)
507  {
508  YGUI::ui()->busyCursor();
509  if (YGPackageSelector::get()->yield()) return FALSE;
510 
511  Impl *pThis = (Impl *) data;
512  pThis->refreshToolbox();
513  pThis->refreshFilters (pThis->m_refresh_list);
514  pThis->m_refresh_id = 0;
515 
516  YGUI::ui()->normalCursor();
517  return FALSE;
518  }
519 
520  virtual void refreshQuery()
521  {
522  if (m_refresh_id) {
523  g_source_remove (m_refresh_id);
524  m_refresh_id = 0;
525  }
526 
527  YGUI::ui()->busyCursor();
528  if (YGPackageSelector::get()->yield()) return;
529 
530  std::list <std::string> keywords;
531  if (m_entry->getAttribute() == Ypp::PoolQuery::NAME)
532  keywords = m_entry->getText();
533 
534  Ypp::Selectable::Type type = Ypp::Selectable::PACKAGE;
535  if (YGPackageSelector::get()->onlineUpdateMode())
536  type = Ypp::Selectable::PATCH;
537  Ypp::PoolQuery query (type);
538  for (std::list <YGtkPkgQueryWidget *>::iterator it = m_queryWidgets.begin();
539  it != m_queryWidgets.end(); it++)
540  (*it)->modified = (*it)->writeQuery (query);
541  query.addCriteria (new SuffixFilter (this));
542 
543  Ypp::List list (query);
544  m_list->setList (list);
545  m_list->setHighlight (keywords);
546 
547  YGUI::ui()->normalCursor();
548 
549  m_refresh_list = list;
550  int wait = 800;
551  if (keywords.empty())
552  wait = 500;
553  if (list.size() == 0)
554  wait = 200;
555  m_refresh_id = g_timeout_add_full (G_PRIORITY_LOW, wait, refresh_filters_timeout_cb, this, NULL);
556  }
557 
558  // YGtkPkgQueryCombo callback
559 
560  virtual YGtkPkgQueryWidget *createQueryWidget (YGtkPkgQueryCombo *combo, int index)
561  {
562  Ypp::Busy busy (0);
563  YGtkPkgFilterModel *model = 0;
564  if (YGPackageSelector::get()->onlineUpdateMode()) {
565  switch (index) {
566  case 0: model = new YGtkPkgPriorityModel(); break;
567  case 1: model = new YGtkPkgRepositoryModel(); break;
568  }
569  }
570  else {
571  switch (index) {
572  case 0: model = new YGtkPkgPKGroupModel(); break;
573  case 1: return new YGtkPkgRpmGroupsView();
574  case 2: model = new YGtkPkgRepositoryModel(); break;
575  case 3: model = new YGtkPkgSupportModel(); break;
576  case 5:
577  if (has_patterns_mode)
578  return new YGtkPkgPatternView (Ypp::Selectable::PATTERN);
579  case 6: return new YGtkPkgLanguageView();
580  }
581  }
582  return new YGtkPkgFilterView (model);
583  }
584 
585  // YGPackageSelector complementary methods
586 
587  static bool confirmCancel()
588  {
589  GtkWidget *dialog;
590  dialog = gtk_message_dialog_new (YGDialog::currentWindow(),
591  GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
592  "%s", _("Changes not saved!"));
593  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s",
594  _("Quit anyway?"));
595  gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
596  GTK_STOCK_QUIT, GTK_RESPONSE_YES, NULL);
597  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
598 
599  bool yes = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
600  gtk_widget_destroy (dialog);
601  return yes;
602  }
603 
604  bool confirmUnsupported()
605  {
606  struct UnsupportedMatch : public Ypp::Match {
607  virtual bool match (Ypp::Selectable &sel)
608  { return Ypp::Package (sel).support() <= 1; }
609  };
610 
611  Ypp::PoolQuery query (Ypp::Selectable::PACKAGE);
612  query.addCriteria (new Ypp::StatusMatch (Ypp::StatusMatch::TO_MODIFY));
613  query.addCriteria (new UnsupportedMatch());
614  Ypp::List list (query);
615  if(list.size() == 0)
616  return true;
617 
618  GtkWidget *dialog = gtk_message_dialog_new (YGDialog::currentWindow(),
619  GtkDialogFlags (0), GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
620  _("Unsupported packages"));
621  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
622  _("Please realize that the following software is either unsupported "
623  "or requires an additional customer contract for support."));
624  gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
625  GTK_STOCK_APPLY, GTK_RESPONSE_YES, NULL);
626  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
627  gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
628  gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500);
629 
630  YGtkPkgListView view (true, Ypp::List::NAME_SORT, false, true);
631  view.addCheckColumn (INSTALLED_CHECK_PROP);
632  view.addTextColumn (_("Name"), NAME_SUMMARY_PROP, true, -1);
633  view.addTextColumn (_("Supportability"), SUPPORT_PROP, true, 140);
634  view.addTextColumn (_("Version"), VERSION_PROP, true, 110);
635  view.addTextColumn (_("Repository"), REPOSITORY_PROP, false, 160);
636  view.setListener (this);
637  view.setList (list);
638 
639  GtkBox *content = GTK_BOX (gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
640  gtk_box_pack_start (content, view.getWidget(), TRUE, TRUE, 6);
641  gtk_widget_show_all (dialog);
642  int ret = gtk_dialog_run (GTK_DIALOG (dialog));
643  gtk_widget_destroy (dialog);
644  return ret == GTK_RESPONSE_YES;
645  }
646 };
647 
648 extern const char *pkg_help[], *patch_help[];
649 
650 static bool confirm_cb (void *pThis)
651 {
652  if (Ypp::isModified())
653  return YGPackageSelector::Impl::confirmCancel();
654  return true;
655 }
656 
657 static void wizard_action_cb (YGtkWizard *wizard, gpointer id,
658  gint id_type, YGPackageSelector *pThis)
659 {
660  const gchar *action = (gchar *) id;
661  yuiMilestone() << "Closing PackageSelector with '" << action << "'\n";
662  if (!strcmp (action, "accept"))
663  pThis->apply();
664  else if (!strcmp (action, "cancel"))
665  pThis->cancel();
666 }
667 
668 YGPackageSelector::YGPackageSelector (YWidget *parent, long mode)
669 : YPackageSelector (NULL, mode), YGWidget (this, parent, YGTK_TYPE_WIZARD, NULL),
670 m_historyDialog (NULL)
671 #ifdef HAS_VESTIGIAL_DIALOG
672 , m_vestigialDialog (NULL)
673 #endif
674 {
675  singleton = this;
676  setBorder (0);
677  YGDialog *dialog = YGDialog::currentDialog();
678  dialog->setCloseCallback (confirm_cb, this);
679 
680  const char *icon, *title, **help;
681  if (onlineUpdateMode()) {
682  icon = THEMEDIR "/icons/22x22/apps/yast-online_update.png";
683  title = _("Online Update");
684  help = patch_help;
685  }
686  else {
687  icon = THEMEDIR "/icons/22x22/apps/yast-software.png";
688  title = _("Software Manager");
689  help = pkg_help;
690  }
691 
692  YGtkWizard *wizard = YGTK_WIZARD (getWidget());
693  ygtk_wizard_set_header_text (wizard, title);
694  ygtk_wizard_set_header_icon (wizard, icon);
695  dialog->setIcon (icon);
696  ygtk_wizard_set_help_text (wizard, _("Please wait..."));
697 
698  std::string cancel_str (YGUtils::mapKBAccel ("&Cancel"));
699  std::string apply_str (YGUtils::mapKBAccel ("&Apply"));
700  ygtk_wizard_set_button_label (wizard, wizard->abort_button, _(cancel_str.c_str()), GTK_STOCK_CANCEL);
701  ygtk_wizard_set_button_str_id (wizard, wizard->abort_button, "cancel");
702  ygtk_wizard_set_button_label (wizard, wizard->back_button, NULL, NULL);
703  ygtk_wizard_set_button_label (wizard, wizard->next_button, _(apply_str.c_str()), GTK_STOCK_APPLY);
704  ygtk_wizard_set_button_str_id (wizard, wizard->next_button, "accept");
705  g_signal_connect (G_OBJECT (wizard), "action-triggered",
706  G_CALLBACK (wizard_action_cb), this);
707 
708  impl = new Impl(); // can take a little
709  ygtk_wizard_set_child (wizard, impl->getWidget());
710  impl->m_menu = new YGtkPkgMenuBar();
711  ygtk_wizard_set_custom_menubar (wizard, impl->m_menu->getWidget(), FALSE);
712  //** TEMP: work-around global-menubar-applet: see bug 595560
713  gtk_widget_show_all (impl->m_menu->getWidget());
714 
715  std::string str;
716  str.reserve (6820);
717  for (int i = 0; help[i]; i++)
718  str.append (help [i]);
719  ygtk_wizard_set_help_text (wizard, str.c_str());
720  dialog->setTitle (title);
721 
722  Ypp::setInterface (impl);
723  Ypp::runSolver(); // check dependencies at start
724  impl->refreshQuery();
725 
726  if (summaryMode()) popupChanges();
727 }
728 
729 YGPackageSelector::~YGPackageSelector()
730 {
731  delete m_historyDialog;
732 #ifdef HAS_VESTIGIAL_DIALOG
733  delete m_vestigialDialog;
734 #endif
735  delete impl;
736  singleton = 0;
737 }
738 
739 void YGPackageSelector::cancel()
740 {
741  if (Ypp::isModified())
742  if (!Impl::confirmCancel())
743  return;
744  YGUI::ui()->sendEvent (new YCancelEvent());
745 }
746 
747 void YGPackageSelector::apply()
748 {
749  if (!Ypp::runSolver(true)) return; // final dependencies check
750  if (onlineUpdateMode())
751  if (!Ypp::showPendingLicenses (Ypp::Selectable::PATCH)) return;
752  if (!Ypp::showPendingLicenses (Ypp::Selectable::PACKAGE)) return;
753 
754  if (Ypp::isModified()) { // confirm
755  if (!onlineUpdateMode() && confirmUnsupported()) {
756  if (!impl->confirmUnsupported())
757  return;
758  }
759  if (!impl->m_undo->popupDialog (true))
760  return;
761  }
762 
763  YGUI::ui()->sendEvent (new YMenuEvent ("accept"));
764 }
765 
766 void YGPackageSelector::showFilterWidget (const char *filter)
767 {
768  int index = -1;
769  if (!strcmp (filter, "patterns"))
770  index = 5;
771  assert (index != -1);
772  impl->m_combo->setActive (index);
773 }
774 
775 void YGPackageSelector::searchFor (Ypp::PoolQuery::StringAttribute attrb, const std::string &text)
776 {
777  impl->clearQueryWidgets();
778  impl->m_entry->setText (attrb, text);
779  impl->refreshQuery();
780 }
781 
782 void YGPackageSelector::popupChanges()
783 { impl->m_undo->popupDialog (false); }
784 
785 void YGPackageSelector::filterPkgSuffix (const std::string &suffix, bool enable)
786 {
787  impl->m_filterSuffices.remove (suffix);
788  if (enable)
789  impl->m_filterSuffices.push_back (suffix);
790  if (Ypp::getInterface() == impl) // inited ?
791  impl->refreshQuery();
792 }
793 
794 void YGPackageSelector::showRepoManager()
795 { YGUI::ui()->sendEvent (new YMenuEvent ("repo_mgr")); }
796 
797 void YGPackageSelector::showHistoryDialog()
798 {
799  if (!m_historyDialog)
800  m_historyDialog = new YGtkPkgHistoryDialog();
801  m_historyDialog->popup();
802 }
803 
804 #ifdef HAS_VESTIGIAL_DIALOG
805 void YGPackageSelector::showVestigialDialog()
806 {
807  if (!m_vestigialDialog)
808  m_vestigialDialog = new YGtkPkgVestigialDialog();
809  m_vestigialDialog->popup();
810 }
811 #endif
812 
813 YGtkPkgUndoList *YGPackageSelector::undoList()
814 { return impl->m_undo; }
815 
816 YGtkPkgSearchEntry *YGPackageSelector::getSearchEntry()
817 { return impl->m_entry; }
818 
819 bool YGPackageSelector::yield()
820 {
821  static int _id = 0;
822  int id = ++_id;
823  while (g_main_context_iteration (NULL, FALSE)) ;
824  return id != _id;
825 }
826 
827 #include "YGPackageSelectorPluginImpl.h"
828 
829 YPackageSelector *
830 YGPackageSelectorPluginImpl::createPackageSelector (YWidget *parent, long modeFlags)
831 {
832  modeFlags |= YPkg_SearchMode;
833  return new YGPackageSelector (parent, modeFlags);
834 }
835 
836 YWidget *
837 YGPackageSelectorPluginImpl::createPatternSelector (YWidget *parent, long modeFlags)
838 {
839  modeFlags &= YPkg_SearchMode;
840  return new YGPackageSelector (parent, modeFlags);
841 }
842 
843 YWidget *
844 YGPackageSelectorPluginImpl::createSimplePatchSelector (YWidget *parent, long modeFlags)
845 {
846  modeFlags |= YPkg_OnlineUpdateMode;
847  return new YGPackageSelector (parent, modeFlags);
848 }
849