libyui-ncurses-pkg  2.50.8
NCPkgPopupDeps.cc
1 /****************************************************************************
2 |
3 | Copyright (c) [2002-2011] Novell, Inc.
4 | All Rights Reserved.
5 |
6 | This program is free software; you can redistribute it and/or
7 | modify it under the terms of version 2 of the GNU General Public License as
8 | published by the Free Software Foundation.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program; if not, contact Novell, Inc.
17 |
18 | To contact Novell about this file by physical or electronic mail,
19 | you may find current contact information at www.novell.com
20 |
21 |***************************************************************************/
22 
23 
24 /*---------------------------------------------------------------------\
25 | |
26 | __ __ ____ _____ ____ |
27 | \ \ / /_ _/ ___|_ _|___ \ |
28 | \ V / _` \___ \ | | __) | |
29 | | | (_| |___) || | / __/ |
30 | |_|\__,_|____/ |_| |_____| |
31 | |
32 | core system |
33 | (C) SuSE GmbH |
34 \----------------------------------------------------------------------/
35 
36  File: NCPkgPopupDeps.cc
37 
38  Author: Gabriele Strattner <gs@suse.de>
39  Maintainer: Bubli <kmachalkova@suse.cz>
40 
41 /-*/
42 #define YUILogComponent "ncurses-pkg"
43 #include <YUILog.h>
44 
45 #include "NCPkgPopupDeps.h"
46 
47 #include "NCAlignment.h"
48 #include "NCTree.h"
49 #include "YDialog.h"
50 #include "NCLayoutBox.h"
51 #include "NCSpacing.h"
52 #include "NCPkgStrings.h"
53 #include "NCSelectionBox.h"
54 #include "NCMultiSelectionBox.h"
55 #include "NCPushButton.h"
56 #include "NCPopupInfo.h"
57 #include "NCInputField.h"
58 
59 #include "NCi18n.h"
60 
61 using std::endl;
62 
63 /*
64  Textdomain "ncurses-pkg"
65 */
66 
67 class NCProblemSelectionBox : public NCSelectionBox
68 {
70  NCProblemSelectionBox (const Self &); // prohibit copying
71  Self & operator= (const Self &); // prohibit assignment
72 
73  NCPkgPopupDeps * depsPopup; // to notify about changes
74 
75 protected:
76  virtual NCursesEvent wHandleInput( wint_t ch );
77 
78 public:
79  NCProblemSelectionBox (YWidget * parent, const std::string & label,
80  NCPkgPopupDeps * aDepsPopup)
81  : NCSelectionBox( parent, label),
82  depsPopup( aDepsPopup )
83  {}
84 
85  virtual ~NCProblemSelectionBox() {}
86 };
87 
88 class NCSolutionSelectionBox : public NCMultiSelectionBox
89 {
91  NCSolutionSelectionBox (const Self &); // prohibit copying
92  Self & operator= (const Self &); // prohibit assignment
93 
94  NCPkgPopupDeps * depsPopup;
95  std::map<YItem *, std::string> detailsMap;
96 
97 protected:
98  virtual NCursesEvent wHandleInput( wint_t ch );
99 
100 public:
101  NCSolutionSelectionBox (YWidget * parent, const std::string & label,
102  NCPkgPopupDeps * aDepsPopup)
103  : NCMultiSelectionBox( parent, label)
104  , depsPopup( aDepsPopup )
105  {}
106 
107  virtual ~NCSolutionSelectionBox() {}
108 
109  void saveDetails( YItem * item, std::string details )
110  {
111  detailsMap[item] = details;
112  }
113 };
114 
115 
116 ///////////////////////////////////////////////////////////////////
117 //
118 //
119 // METHOD NAME : NCPkgPopupDeps::NCPkgPopupDeps
120 // METHOD TYPE : Constructor
121 //
122 // DESCRIPTION :
123 //
124 NCPkgPopupDeps::NCPkgPopupDeps( const wpos at, NCPackageSelector * pkger )
125  : NCPopup( at, false )
126  , cancelButton( 0 )
127  , solveButton( 0 )
128  , solutionw( 0 )
129  , head( 0 )
130  , details( 0 )
131  , solDetails( 0 )
132  , packager( pkger )
133  , problemw( 0 )
134 
135 {
136  createLayout();
137 }
138 
139 ///////////////////////////////////////////////////////////////////
140 //
141 //
142 // METHOD NAME : NCPkgPopupDeps::~NCPkgPopupDeps
143 // METHOD TYPE : Destructor
144 //
145 // DESCRIPTION :
146 //
147 NCPkgPopupDeps::~NCPkgPopupDeps()
148 {
149 }
150 
151 ///////////////////////////////////////////////////////////////////
152 //
153 //
154 // METHOD NAME : NCPopupSelDeps::createLayout
155 // METHOD TYPE : void
156 //
157 // DESCRIPTION :
158 //
159 void NCPkgPopupDeps::createLayout()
160 {
161 
162  // vertical split is the (only) child of the dialog
163  NCLayoutBox * vSplit = new NCLayoutBox( this, YD_VERT );
164 
165  vSplit->setNotify( true );
166 
167  new NCSpacing( vSplit, YD_VERT, false, 1 );
168 
169  head = new NCLabel( vSplit, "", true ); // isHeading = true
170 
171  // only add spacings if there's enough space
172  if ( this->preferredHeight() > 25 )
173  new NCSpacing( vSplit, YD_VERT, false, 1 );
174 
175  NCAlignment * left = new NCAlignment( vSplit, YAlignBegin, YAlignUnchanged );
176  left->setWeight(YD_VERT, 30 );
177 
178  // the list containing the problems (the unresolved package dependencies)
179  problemw = new NCProblemSelectionBox( left, _( "&Problems" ), this);
180  problemw->setStretchable( YD_HORIZ, true );
181 
182  NCAlignment * left1 = new NCAlignment( vSplit, YAlignBegin, YAlignUnchanged );
183  left1->setWeight(YD_VERT, 10 );
184 
185  // show the details of the problem
186  details = new NCLabel ( left1, "", false, true ); // heading = false,
187  details->setStretchable( YD_HORIZ, true ); // outputField = true
188 
189  if ( this->preferredHeight() > 25 )
190  new NCSpacing( vSplit, YD_VERT, false, 0.5 ); // stretchable = false
191 
192  NCAlignment * left2 = new NCAlignment( vSplit, YAlignBegin, YAlignUnchanged );
193  left2->setWeight( YD_VERT, 30 );
194 
195  // the list containing the solutions of a dependency problem
196  solutionw = new NCSolutionSelectionBox ( left2, _( "Possible &Solutions" ), this);
197 
198  if ( this->preferredHeight() > 25 )
199  new NCSpacing( vSplit, YD_VERT, false, 1 );
200 
201  NCAlignment * left3 = new NCAlignment( vSplit, YAlignBegin, YAlignUnchanged );
202  left3->setWeight( YD_VERT, 30 );
203 
204  // show the details of the solution
205  solDetails = new NCRichText ( left3, "", true ); // plain text mode = true
206 
207  if ( this->preferredHeight() > 25 )
208  new NCSpacing( vSplit, YD_VERT, false, 1 ); // stretchable = false
209 
210  NCLayoutBox * hSplit = new NCLayoutBox( vSplit, YD_HORIZ );
211 
212  // add the solve button
213  solveButton = new NCPushButton( hSplit, NCPkgStrings::SolveLabel() );
214  solveButton->setFunctionKey( 10 );
215 
216  new NCSpacing( hSplit, YD_HORIZ, true, 0.2 ); // stretchable = true
217 
218  // add the cancel button
219  cancelButton = new NCPushButton( hSplit, NCPkgStrings::CancelLabel() );
220  cancelButton->setFunctionKey( 9 );
221 
222  if ( this->preferredHeight() > 25 )
223  new NCSpacing( vSplit, YD_VERT, false, 0.5 ); // stretchable = false
224 }
225 
226 ///////////////////////////////////////////////////////////////////
227 //
228 // showDependencies
229 //
230 //
231 bool NCPkgPopupDeps::showDependencies( NCPkgSolverAction action, bool * ok )
232 {
233  if ( !problemw )
234  return true;
235 
236  bool cancel = false;
237 
238  // set headline and table type
239  if ( head )
240  head->setLabel( NCPkgStrings::PackageDeps() );
241 
242  // evaluate the result and fill the list with packages
243  // which have unresolved deps
244  bool success = solve (problemw, action );
245  *ok = success;
246 
247  if (!success)
248  {
249  // show first dependency
250  showSolutions( problemw->getCurrentItem() );
251  NCursesEvent input = showDependencyPopup( action ); // show the dependencies
252 
253  if ( input == NCursesEvent::cancel
254  && input.detail != NCursesEvent::USERDEF )
255  {
256  cancel = true;
257  }
258  problemw->setKeyboardFocus();
259  }
260 
261  return cancel;
262 }
263 
264 
265 bool NCPkgPopupDeps::solve( NCSelectionBox * problemw, NCPkgSolverAction action )
266 {
267  if ( !problemw )
268  return false;
269 
270  yuiDebug() << "Solving..." << endl;
271 
272  NCPopupInfo * info = new NCPopupInfo( wpos( (NCurses::lines()-4)/2, (NCurses::cols()-18)/2 ),
273  "",
274  NCPkgStrings::Solving(),
276  );
277  info->setPreferredSize( 18, 4 );
278  info->popup();
279 
280  zypp::Resolver_Ptr resolver = zypp::getZYpp()->resolver();
281 
282  bool success = false;
283  switch ( action )
284  {
285  case S_Solve:
286  success = resolver->resolvePool();
287  break;
288  case S_Verify:
289  success = resolver->verifySystem(); // check hardware
290  break;
291  default:
292  yuiError() << "Unknown action for resolve" << endl;
293  }
294 
295  info->popdown();
296 
297  YDialog::deleteTopmostDialog();
298 
299  if (success)
300  return true;
301 
302  // clear list
303  problems.clear();
304  problemw->deleteAllItems();
305 
306  zypp::ResolverProblemList rproblems = resolver->problems();
307  zypp::ResolverProblemList::iterator
308  b = rproblems.begin(),
309  e = rproblems.end(),
310  i;
311  int idx;
312 
313  for (i = b, idx = 0; i != e; ++i, ++idx)
314  {
315  yuiMilestone() << "Problem: " << (*i)->description() << endl;
316  yuiMilestone() << "Details: " << (*i)->details() << endl;
317 
318  // no solution yet
319  problems.push_back (std::make_pair (*i, zypp::ProblemSolution_Ptr()));
320 
321  problemw->addItem( (*i)->description(), false ); // selected: false
322  }
323 
324  return false;
325 }
326 
327 bool NCPkgPopupDeps::showSolutions( int index )
328 {
329  if (!solutionw)
330  return false;
331 
332  unsigned int size = problems.size();
333 
334  if ( index < 0 || (unsigned int)index >= size )
335  return false;
336 
337  solutionw->startMultipleChanges();
338  solutionw->deleteAllItems();
339 
340  zypp::ResolverProblem_Ptr problem = problems[index].first;
341  zypp::ProblemSolution_Ptr user_solution = problems[index].second;
342 
343  details->setText( problem->details() );
344 
345  zypp::ProblemSolutionList solutions = problem->solutions();
346  zypp::ProblemSolutionList::iterator
347  bb = solutions.begin(),
348  ee = solutions.end(),
349  ii;
350 
351  bool showDetails = true;;
352  std::string description;
353 
354  for ( ii = bb; ii != ee; ++ii)
355  {
356  yuiMilestone() << "Solution: " << (*ii)->description() << endl;
357  yuiMilestone() << "Details: " << (*ii)->details() << endl;
358  yuiMilestone() << "User decision: " << user_solution << endl;
359 
360  description = (*ii)->description();
361 
362  if ( !((*ii)->details().empty()) )
363  // hint for the user: more information below
364  description += _( " see below" );
365 
366  if ( showDetails )
367  {
368  showSolutionDetails( (*ii)->details() ); // show details of 1. solution
369  showDetails = false;
370  }
371 
372  YItem *newItem = new YItem ( description, // text
373  (user_solution == *ii) ); // selected ?
374 
375  solutionw->addItem( newItem );
376  solutionw->saveDetails( newItem, (*ii)->details() );
377 
378  yuiDebug() << "Solution: " << (*ii) << endl; // Complete info
379  }
380 
381  solutionw->doneMultipleChanges();
382 
383  return true;
384 }
385 
386 
387 ///////////////////////////////////////////////////////////////////
388 //
389 //
390 // METHOD NAME : NCPkgPopupDeps::showDependencyPopup
391 // METHOD TYPE : void
392 //
393 // DESCRIPTION :
394 //
395 NCursesEvent NCPkgPopupDeps::showDependencyPopup( NCPkgSolverAction action )
396 {
397  postevent = NCursesEvent();
398 
399  do
400  {
401  popupDialog();
402  } while ( postAgain( action ) );
403 
404  popdownDialog();
405 
406  return postevent;
407 }
408 
409 ///////////////////////////////////////////////////////////////////
410 //
411 //
412 // METHOD NAME : NCPkgPopupDeps::preferredWidth
413 // METHOD TYPE : int
414 //
415 // DESCRIPTION :
416 //
417 int NCPkgPopupDeps::preferredWidth()
418 {
419  return NCurses::cols()-8;
420 }
421 
422 ///////////////////////////////////////////////////////////////////
423 //
424 //
425 // METHOD NAME : NCPkgPopupDeps::preferredHeight
426 // METHOD TYPE : int
427 //
428 // DESCRIPTION :
429 //
430 int NCPkgPopupDeps::preferredHeight()
431 {
432  return NCurses::lines()-5;
433 }
434 
435 ///////////////////////////////////////////////////////////////////
436 //
437 //
438 // METHOD NAME : NCPopup::wHandleInput
439 // METHOD TYPE : NCursesEvent
440 //
441 // DESCRIPTION :
442 //
443 NCursesEvent NCPkgPopupDeps::wHandleInput( wint_t ch )
444 {
445  if ( ch == 27 ) // ESC
446  return NCursesEvent::cancel;
447 
448  return NCDialog::wHandleInput( ch );
449 }
450 
451 ///////////////////////////////////////////////////////////////////
452 //
453 //
454 // METHOD NAME : NCPkgPopupDeps::postAgain
455 // METHOD TYPE : bool
456 //
457 // DESCRIPTION :
458 //
459 bool NCPkgPopupDeps::postAgain( NCPkgSolverAction action )
460 {
461  if ( ! postevent.widget )
462  return false;
463 
464  if ( postevent.widget == cancelButton )
465  {
466  // close the dialog
467  postevent = NCursesEvent::cancel;
468  }
469  else if ( postevent.widget == solveButton )
470  {
471  // apply the solution here
472  zypp::Resolver_Ptr resolver = zypp::getZYpp()->resolver();
473  ProblemSolutionCorrespondence::iterator
474  b = problems.begin(),
475  e = problems.end(),
476  i;
477  zypp::ProblemSolutionList solutions;
478  for (i = b; i != e; ++i)
479  {
480  // *i is std::pair< zypp::ResolverProblem_Ptr,
481  // zypp::ProblemSolution_Ptr >
482  if (i->second)
483  {
484  solutions.push_back (i->second);
485  }
486  }
487  resolver->applySolutions (solutions);
488 
489  // and solve again
490  bool success = solve (problemw, action );
491 
492  if ( !success )
493  {
494  problemw->setKeyboardFocus();
495  showSolutions( problemw->getCurrentItem() );
496  }
497  else // everything ok
498  {
499  // close the dialog
500  postevent = NCursesEvent::cancel;
501  }
502  }
503 
504  if ( postevent == NCursesEvent::cancel )
505  {
506  // return false means: close the popup dialog
507  return false;
508  }
509  return true;
510 }
511 
512 
513 ///////////////////////////////////////////////////////////////////
514 //
515 //
516 // METHOD NAME : NCPkgPopupDeps::setSolution
517 // METHOD TYPE : bool
518 //
519 // DESCRIPTION :
520 //
521 void NCPkgPopupDeps::setSolution (int index)
522 {
523  // we must search the list :( bad design here
524  // but the solution list is short
525  int prob_num = problemw->getCurrentItem();
526  zypp::ResolverProblem_Ptr problem = problems[prob_num].first;
527  zypp::ProblemSolution_Ptr sol = zypp::ProblemSolution_Ptr();
528 
529  zypp::ProblemSolutionList solutions = problem->solutions();
530  zypp::ProblemSolutionList::iterator
531  bb = solutions.begin(),
532  ee = solutions.end(),
533  ii;
534  int idx;
535 
536  for (ii = bb, idx = 0; ii != ee && idx < index; ++ii, ++idx)
537  {
538  // empty
539  }
540 
541  if (ii != ee)
542  sol = *ii;
543 
544  problems[prob_num] = std::make_pair (problem, sol);
545 }
546 
547 void NCPkgPopupDeps::showSolutionDetails( std::string details )
548 {
549  std::string text;
550  if ( details.empty() )
551  // hint for the user: there isn't any additional information
552  // (for the currently selected solution of a dependency problem)
553  text = _( "No further solution details available" );
554  else
555  text = details;
556 
557  if ( solDetails )
558  solDetails->setText( text );
559 
560 }
561 
562 ///////////////////////////////////////////////////////////////////
563 //
564 //
565 // METHOD NAME : NCProblemSelectionBox::wHandleInput
566 // METHOD TYPE : NCursesEvent
567 //
568 // DESCRIPTION :
569 //
570 NCursesEvent NCProblemSelectionBox::wHandleInput( wint_t key )
571 {
572  NCursesEvent ret = NCursesEvent::none;
573 
574  // call handleInput of NCPad
575  handleInput( key );
576 
577  switch ( key )
578  {
579  case KEY_UP:
580  case KEY_DOWN:
581  case KEY_NPAGE:
582  case KEY_PPAGE:
583  case KEY_END:
584  case KEY_HOME:
585  // show the corresponding information
586  depsPopup->showSolutions (getCurrentItem());
587  ret = NCursesEvent::handled;
588  break;
589 
590  default:
591  break;
592  }
593 
594  return ret;
595 }
596 
597 ///////////////////////////////////////////////////////////////////
598 //
599 //
600 // METHOD NAME : NCSolutionSelectionBox::wHandleInput
601 // METHOD TYPE : NCursesEvent
602 //
603 // DESCRIPTION :
604 //
605 NCursesEvent NCSolutionSelectionBox::wHandleInput( wint_t key )
606 {
607  NCursesEvent ret = NCMultiSelectionBox::wHandleInput( key );
608 
609  switch ( key )
610  {
611  case KEY_SPACE:
612  case KEY_RETURN:
613  {
614  // act like a radio button
615  // make sure that only one item is selected
616  YItem *cur = currentItem();
617  bool on = isItemSelected( cur );
618  if (on)
619  {
620  deselectAllItems();
621  selectItem (cur, true);
622  depsPopup->setSolution ( cur->index() );
623  }
624  break;
625  }
626 
627  case KEY_UP:
628  case KEY_DOWN:
629  // show details
630  depsPopup->showSolutionDetails( detailsMap[currentItem()] );
631  break;
632 
633  default:
634  break;
635  }
636 
637  return ret;
638 }
639 
static const std::string SolveLabel()
The label of the Solve button.
static const std::string PackageDeps()
The headline of the dependency popup.
static const std::string CancelLabel()
The label of the Cancel button.
static const std::string OKLabel()
The label of the OK button.