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