libyui-qt-pkg  2.45.15
YQPkgVersionsView.cc
1 /**************************************************************************
2 Copyright (C) 2000 - 2010 Novell, Inc.
3 All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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 along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 
19 **************************************************************************/
20 
21 
22 /*---------------------------------------------------------------------\
23 | |
24 | __ __ ____ _____ ____ |
25 | \ \ / /_ _/ ___|_ _|___ \ |
26 | \ V / _` \___ \ | | __) | |
27 | | | (_| |___) || | / __/ |
28 | |_|\__,_|____/ |_| |_____| |
29 | |
30 | core system |
31 | (C) SuSE GmbH |
32 \----------------------------------------------------------------------/
33 
34  File: YQPkgVersionsView.cc
35 
36  Author: Stefan Hundhammer <sh@suse.de>
37 
38  Textdomain "qt-pkg"
39 
40 /-*/
41 
42 #define YUILogComponent "qt-pkg"
43 
44 #include <YQZypp.h>
45 #include <zypp/Repository.h>
46 #include "YUILog.h"
47 #include <QTabWidget>
48 #include <QRegExp>
49 #include <QHeaderView>
50 #include <QStylePainter>
51 #include <QStyleOptionButton>
52 #include <QMessageBox>
53 #include <QApplication>
54 
55 
56 #include "YQPkgVersionsView.h"
57 #include "YQPkgRepoList.h"
58 #include "YQIconPool.h"
59 #include "YQSignalBlocker.h"
60 #include "YQi18n.h"
61 #include "utf8.h"
62 
63 #define ICONOFFSET 3 // the status icons have an asymmetrical transparent border
64 
65 using std::endl;
66 
68  : QScrollArea( parent )
69  , _content(0)
70  , _layout(0)
71 {
72  _selectable = 0;
73  _isMixedMultiVersion = false;
74  _parentTab = dynamic_cast<QTabWidget *> (parent);
75 
76  _buttons = new QButtonGroup(this);
77 
78  if ( _parentTab )
79  {
80  connect( _parentTab, SIGNAL( currentChanged( int ) ),
81  this, SLOT ( reload ( int ) ) );
82  }
83 }
84 
85 
87 {
88  // NOP
89 }
90 
91 
92 void
93 YQPkgVersionsView::reload( int newCurrent )
94 {
95  if ( _parentTab && _parentTab->widget( newCurrent ) == this )
96  showDetailsIfVisible( _selectable );
97 }
98 
99 
100 void
102 {
103  _selectable = selectable;
104  _isMixedMultiVersion = isMixedMultiVersion( selectable );
105 
106  if ( _parentTab ) // Is this view embedded into a tab widget?
107  {
108  if ( _parentTab->currentWidget() == this ) // Is this page the topmost?
109  showDetails( selectable );
110  }
111  else // No tab parent - simply show data unconditionally.
112  {
113  showDetails( selectable );
114  }
115 }
116 
117 
118 void
119 YQPkgVersionsView::showDetails( ZyppSel selectable )
120 {
121  _selectable = selectable;
122  _isMixedMultiVersion = isMixedMultiVersion( selectable );
123 
124  if ( ! selectable )
125  {
126  // Delete all installed items
127  qDeleteAll( _installed );
128  _installed.clear();
129 
130  _content = new QWidget( this );
131  setWidget( _content );
132  _content->show();
133  return;
134  }
135 
136  // old widget is autodestroyed by setWidget later
137  _content = new QWidget( this );
138  _layout = new QVBoxLayout( _content );
139  _content->setLayout( _layout );
140 
141  QLabel * pkgNameLabel = new QLabel( this );
142 
143  if ( ! selectable->theObj() )
144  return;
145 
146  _layout->addWidget( pkgNameLabel );
147 
148  QFont font = pkgNameLabel->font();
149  font.setBold( true );
150 
151  QFontMetrics fm( font) ;
152  font.setPixelSize( (int) ( fm.height() * 1.1 ) );
153 
154  pkgNameLabel->setFont( font );
155  pkgNameLabel->setText( fromUTF8(selectable->theObj()->name().c_str()) );
156 
157  // New scope
158  {
159  QListIterator<QAbstractButton*> it( _buttons->buttons() );
160 
161  while ( it.hasNext() )
162  {
163  delete it.next();
164  }
165  }
166 
167  // Delete all installed items
168  qDeleteAll( _installed );
169  _installed.clear();
170 
171  if ( selectable->multiversionInstall() ) // at least one (!) PoolItem is multiversion
172  {
173  //
174  // Find installed and available objects (for multiversion view)
175  //
176  {
177  zypp::ui::Selectable::picklist_iterator it = selectable->picklistBegin();
178 
179  while ( it != selectable->picklistEnd() )
180  {
181  YQPkgMultiVersion * version = new YQPkgMultiVersion( this, selectable, *it );
182 
183  _installed.push_back( version );
184  _layout->addWidget( version );
185 
186  connect( version, SIGNAL( statusChanged() ),
187  this, SIGNAL( statusChanged() ) );
188 
189  connect( this, SIGNAL( statusChanged() ),
190  version, SLOT ( update() ) );
191 
192  ++it;
193  }
194 
195  }
196  }
197  else
198  {
199  //
200  // Fill installed objects
201  //
202  {
203  zypp::ui::Selectable::installed_iterator it = selectable->installedBegin();
204 
205  while ( it != selectable->installedEnd() )
206  {
207  QString text = _( "%1-%2 from vendor %3 (installed)" )
208  .arg( fromUTF8( (*it)->edition().asString().c_str() ) )
209  .arg( fromUTF8( (*it)->arch().asString().c_str() ) )
210  .arg( fromUTF8( (*it)->vendor().c_str() ) ) ;
211 
212  QWidget * installedVersion = new QWidget( this );
213  QHBoxLayout * instLayout = new QHBoxLayout( installedVersion );
214  instLayout->setContentsMargins( 0, 0, 0, 0 );
215 
216  QLabel * icon = new QLabel( installedVersion );
217  icon->setPixmap( YQIconPool::pkgSatisfied() );
218  instLayout->addWidget( icon );
219 
220  QLabel * textLabel = new QLabel( text, installedVersion );
221  instLayout->addWidget( textLabel );
222  instLayout->addStretch();
223 
224  _installed.push_back( installedVersion );
225  _layout->addWidget( installedVersion );
226 
227  ++it;
228  }
229  }
230 
231 
232  //
233  // Fill available objects
234  //
235 
236  {
237  zypp::ui::Selectable::available_iterator it = selectable->availableBegin();
238 
239  while ( it != selectable->availableEnd() )
240  {
241  QRadioButton *radioButton = new YQPkgVersion( this, selectable, *it );
242  connect( radioButton, SIGNAL( clicked( bool ) ),
243  this, SLOT ( checkForChangedCandidate() ) );
244 
245  _buttons->addButton( radioButton );
246  _layout->addWidget( radioButton );
247 
248 
249  if ( selectable->hasCandidateObj() &&
250  selectable->candidateObj()->edition() == (*it)->edition() &&
251  selectable->candidateObj()->arch() == (*it)->arch() )
252  {
253  radioButton->setChecked(true);
254  }
255 
256  ++it;
257  }
258  }
259  }
260 
261  _layout->addStretch();
262  setWidget( _content );
263  _content->show();
264 }
265 
266 
267 void
269 {
270  QListIterator<QAbstractButton*> it( _buttons->buttons() );
271 
272  while ( it.hasNext() )
273  {
274  YQPkgVersion * versionItem = dynamic_cast<YQPkgVersion *> (it.next());
275 
276  if ( versionItem && versionItem->isChecked() )
277  {
278  ZyppObj newCandidate = versionItem->zyppObj();
279 
280  if ( _selectable && *newCandidate != _selectable->candidateObj() )
281  {
282  yuiMilestone() << "Candidate changed" << endl;
283 
284  // Change status of selectable
285 
286  ZyppStatus status = _selectable->status();
287 
288  if ( !_selectable->installedEmpty() &&
289  _selectable->installedObj()->arch() == newCandidate->arch() &&
290  _selectable->installedObj()->edition() == newCandidate->edition() )
291  {
292  // Switch back to the original instance -
293  // the version that was previously installed
294  status = S_KeepInstalled;
295  }
296  else
297  {
298  switch ( status )
299  {
300  case S_KeepInstalled:
301  case S_Protected:
302  case S_AutoDel:
303  case S_AutoUpdate:
304  case S_Del:
305  case S_Update:
306 
307  status = S_Update;
308  break;
309 
310  case S_NoInst:
311  case S_Taboo:
312  case S_Install:
313  case S_AutoInstall:
314  status = S_Install;
315  break;
316  }
317  }
318 
319  _selectable->setStatus( status );
320 
321 
322  // Set candidate
323 
324  _selectable->setCandidate( newCandidate );
325  emit candidateChanged( newCandidate );
326  return;
327  }
328  }
329  }
330 }
331 
332 
333 QSize
335 {
336  return QSize( 0, 0 );
337 }
338 
339 
340 bool
342 {
343  ZyppPoolItem poolItem = newSelected->zyppPoolItem();
344  Q_CHECK_PTR( poolItem );
345 
346  bool multiVersion = poolItem->multiversionInstall();
347 
348  yuiMilestone() << "Selected: "
349  << ( multiVersion ? "Multiversion " : "Non-Multiversion " )
350  << newSelected->text()
351  << endl;
352 
353  if ( anyMultiVersionToInstall( !multiVersion ) )
354  {
355  yuiMilestone() << "Multiversion and non-multiversion conflict!" << endl;
356  bool forceContinue = mixedMultiVersionPopup( multiVersion );
357 
358  if ( forceContinue )
359  {
360  _selectable->setPickStatus( poolItem, S_Install );
361  emit statusChanged(); // update status icons for all versions
362  }
363  else
364  {
365  // Nothing to do here: The status of this item was not changed yet;
366  // simply leave it like it was.
367  }
368 
369  return true; // handled here
370  }
371  else
372  {
373  return false; // Not handled here
374  }
375 }
376 
377 
378 bool
380 {
381  // Translators: Popup dialog text. Try to keep the lines about the same length.
382  QString msg = _( "You are trying to install multiversion-capable\n"
383  "and non-multiversion-capable versions of this\n"
384  "package at the same time." );
385  msg += "\n\n";
386 
387  if ( multiversion )
388  {
389  msg +=
390  _( "This version is multiversion-capable.\n"
391  "\n"
392  "Press \"Continue\" to install this version\n"
393  "and unselect the non-multiversion-capable version,\n"
394  "\"Cancel\" to unselect this version and keep the other one." );
395  }
396  else
397  {
398  msg +=
399  _( "This version is not multiversion-capable.\n"
400  "\n"
401  "Press \"Continue\" to install only this version\n"
402  "and unselect all other versions,\n"
403  "\"Cancel\" to unselect this version and keep the other ones." );
404  }
405 
406  // Dialog heading
407  QString heading = _( "Incompatible Package Versions" );
408 
409  int buttonNo = QMessageBox::question( 0, // parent
410  heading,
411  msg,
412  _( "C&ontinue" ), // button #0
413  _( "&Cancel" ) ); // button #1
414  yuiMilestone() << "User hit " << (buttonNo == 0 ? "[Continue]" : "[Cancel]" ) << endl;
415 
416  return buttonNo == 0;
417 }
418 
419 
420 
421 bool
423 {
424  if ( ! _selectable )
425  return false;
426 
427  zypp::ui::Selectable::available_iterator it = _selectable->availableBegin();
428 
429  while ( it != _selectable->availableEnd() )
430  {
431  if ( it->multiversionInstall() == multiversion )
432  {
433  switch ( _selectable->pickStatus( *it ) )
434  {
435  case S_Install:
436  case S_AutoInstall:
437  yuiMilestone() << "Found " << ( multiversion ? "multiversion" : "non-multiversion" )
438  << " to install" << endl;
439  return true;
440 
441  default:
442  break;
443  }
444  }
445 
446  ++it;
447  }
448 
449  yuiMilestone() << "No " << ( multiversion ? "multiversion" : "non-multiversion" )
450  << " to install" << endl;
451  return false;
452 }
453 
454 
455 void
457 {
458  if ( ! _selectable )
459  return;
460 
461  zypp::ui::Selectable::available_iterator it = _selectable->availableBegin();
462 
463  while ( it != _selectable->availableEnd() )
464  {
465  if ( it->multiversionInstall() )
466  {
467  switch ( _selectable->pickStatus( *it ) )
468  {
469  case S_Install:
470  case S_AutoInstall:
471  _selectable->setPickStatus( *it, S_NoInst );
472  yuiMilestone() << "Unselecting " << *it << endl;
473  break;
474 
475  default:
476  break;
477  }
478  }
479 
480  ++it;
481  }
482 }
483 
484 
485 bool
487 {
488  if ( ! selectable )
489  return false;
490 
491  zypp::ui::Selectable::available_iterator it = selectable->availableBegin();
492 
493  if ( it == selectable->availableEnd() )
494  return false;
495 
496  bool multiversion = it->multiversionInstall();
497 
498  while ( it != selectable->availableEnd() )
499  {
500  if ( it->multiversionInstall() != multiversion )
501  {
502  yuiMilestone() << "Mixed multiversion" << endl;
503  return true;
504  }
505 
506  ++it;
507  }
508 
509  return false;
510 }
511 
512 
513 
514 
515 
516 
517 YQPkgVersion::YQPkgVersion( QWidget * parent,
518  ZyppSel selectable,
519  ZyppObj zyppObj )
520  : QRadioButton( parent )
521  , _selectable( selectable )
522  , _zyppObj( zyppObj )
523 {
524  // Translators: %1 is a package version, %2 the package architecture,
525  // %3 describes the repository where it comes from,
526  // %4 is the repository's priority
527  // %5 is the vendor of the package
528  // Examples:
529  // 2.5.23-i568 from Packman with priority 100 and vendor openSUSE
530  // 3.17.4-i386 from openSUSE-11.1 update repository with priority 20 and vendor openSUSE
531  // ^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^
532  // %1 %2 %3 %4 %5
533  setText( _( "%1-%2 from %3 with priority %4 and vendor %5" )
534  .arg( fromUTF8( zyppObj->edition().asString().c_str() ) )
535  .arg( fromUTF8( zyppObj->arch().asString().c_str() ) )
536  .arg( fromUTF8( zyppObj->repository().info().name().c_str() ) )
537  .arg( zyppObj->repository().info().priority() )
538  .arg( fromUTF8( zyppObj->vendor().c_str() ) ) );
539 }
540 
541 
543 {
544  // NOP
545 }
546 
547 
548 QString
550 {
551  QString tip;
552 
553  if ( *zyppObj() == selectable()->installedObj() )
554  tip = _( "This version is installed in your system." );
555 
556  return tip;
557 }
558 
559 
560 
561 
563  ZyppSel selectable,
564  ZyppPoolItem zyppPoolItem )
565  : QCheckBox( parent )
566  , _parent( parent )
567  , _selectable( selectable )
568  , _zyppPoolItem( zyppPoolItem )
569 {
570  setText (_( "%1-%2 from %3 with priority %4 and vendor %5" )
571  .arg( fromUTF8( zyppPoolItem->edition().asString().c_str() ) )
572  .arg( fromUTF8( zyppPoolItem->arch().asString().c_str() ) )
573  .arg( fromUTF8( zyppPoolItem->repository().info().name().c_str() ) )
574  .arg( zyppPoolItem->repository().info().priority() )
575  .arg( fromUTF8( zyppPoolItem->vendor().c_str() ) ));
576 
577  connect( this, SIGNAL( toggled( bool) ),
578  this, SLOT ( slotIconClicked() ) );
579 }
580 
581 
583 {
584  // NOP
585 }
586 
587 
588 void YQPkgMultiVersion::slotIconClicked()
589 {
590  {
591  YQSignalBlocker sigBlocker( this ); // prevent checkmark, we draw the status icons ourselves
592  setChecked( false );
593  }
594  cycleStatus();
595 }
596 
597 
599 {
600 
601  ZyppStatus oldStatus = _selectable->pickStatus( _zyppPoolItem );
602  ZyppStatus newStatus = oldStatus;
603 
604  switch ( oldStatus )
605  {
606  case S_Install:
607  case S_AutoInstall:
608  case S_Protected:
609  newStatus = S_NoInst;
610  break;
611 
612  case S_KeepInstalled:
613  case S_Update:
614  case S_AutoUpdate:
615  newStatus = S_Del;
616  break;
617 
618 
619  case S_Del:
620  case S_AutoDel:
621  newStatus = S_KeepInstalled;
622  break;
623 
624  case S_NoInst:
625  case S_Taboo:
626  newStatus = S_Install;
627  break;
628  }
629 
630  bool handled = false;
631 
632  if ( _parent->isMixedMultiVersion() &&
633  newStatus == S_Install &&
634  oldStatus != newStatus )
635  {
636  handled = _parent->handleMixedMultiVersion( this );
637  }
638 
639  if ( ! handled )
640  setStatus( newStatus );
641 
642  yuiMilestone() << "oldStatus: " << oldStatus << endl;
643  ZyppStatus actualStatus = _selectable->pickStatus( _zyppPoolItem );
644 
645  if ( actualStatus != newStatus )
646  yuiWarning() << "FAILED to set new status: " << newStatus
647  << " actual Status: " << actualStatus << endl;
648  else
649  yuiMilestone() << "newStatus:" << newStatus << endl;
650 
651  if ( oldStatus != actualStatus )
652  {
653  update();
654  emit statusChanged();
655  }
656 }
657 
658 
659 void YQPkgMultiVersion::setStatus( ZyppStatus newStatus )
660 {
661  yuiMilestone() << "Setting pick status to " << newStatus << endl;
662  _selectable->setPickStatus( _zyppPoolItem, newStatus );
663 }
664 
665 
666 void YQPkgMultiVersion::paintEvent(QPaintEvent *)
667 {
668  // draw the usual checkbox
669  QStylePainter p(this);
670  QStyleOptionButton opt;
671  initStyleOption(&opt);
672  p.drawControl(QStyle::CE_CheckBox, opt);
673 
674 
675  // calculate position and draw the status icon
676  QRect elementRect = style()->subElementRect ( QStyle::SE_CheckBoxIndicator, &opt);
677  QPixmap icon = statusIcon( _selectable->pickStatus(_zyppPoolItem) );
678 
679  QPoint start = elementRect.center() - icon.rect().center();
680  QRect rect = QRect(start.x() - ICONOFFSET, start.y(), icon.width(), icon.height());
681 
682  p.drawItemPixmap( rect, 0, icon );
683 }
684 
685 
686 QPixmap YQPkgMultiVersion::statusIcon( ZyppStatus status )
687 {
688  QPixmap icon = YQIconPool::pkgNoInst();
689 
690  switch ( status )
691  {
692  case S_Del: icon = YQIconPool::pkgDel(); break;
693  case S_Install: icon = YQIconPool::pkgInstall(); break;
694  case S_KeepInstalled: icon = YQIconPool::pkgKeepInstalled(); break;
695  case S_NoInst: icon = QPixmap(); break;
696  case S_Protected: icon = YQIconPool::pkgProtected(); break;
697  case S_Taboo: icon = YQIconPool::pkgTaboo(); break;
698  case S_Update: icon = YQIconPool::pkgUpdate(); break;
699 
700  case S_AutoDel: icon = YQIconPool::pkgAutoDel(); break;
701  case S_AutoInstall: icon = YQIconPool::pkgAutoInstall(); break;
702  case S_AutoUpdate: icon = YQIconPool::pkgAutoUpdate(); break;
703 
704  // Intentionally omitting 'default' branch so the compiler can
705  // catch unhandled enum states
706  }
707  return icon;
708 }
709 
710 
711 
712 #include "YQPkgVersionsView.moc"
ZyppPoolItem zyppPoolItem() const
Returns the original ZYPP object.
ZyppObj zyppObj() const
Returns the original ZYPP object.
virtual QString toolTip(int column)
Returns a tool tip text for a specific column of this item.
bool isMixedMultiVersion() const
Return the cached value for the current selectable.
YQPkgMultiVersion(YQPkgVersionsView *parent, ZyppSel selectable, ZyppPoolItem zyppPoolItem)
Constructor.
YQPkgVersion(QWidget *parent, ZyppSel selectable, ZyppObj zyppObj)
Constructor.
void showDetailsIfVisible(ZyppSel selectable)
Show details for the specified package.
virtual ~YQPkgVersion()
Destructor.
virtual ~YQPkgMultiVersion()
Destructor.
YQPkgVersionsView(QWidget *parent)
Constructor.
Package version selector: Display a list of available versions from all the different installation so...
virtual QSize minimumSizeHint() const
Returns the minimum size required for this widget.
void paintEvent(QPaintEvent *)
Paints checkboxes with status icons instead of a checkmark.
void checkForChangedCandidate()
Check for changed candidates.
bool anyMultiVersionToInstall(bool multiversion) const
Check if any package version is marked for installation where its &#39;multiversion&#39; flag is set to &#39;mult...
bool mixedMultiVersionPopup(bool multiversion) const
Ask user if he really wants to install incompatible package versions.
void unselectAllMultiVersion()
Unselect all multiversion package versions.
void candidateChanged(ZyppObj newCandidate)
Emitted when the user changes the candidate.
virtual ~YQPkgVersionsView()
Destructor.
bool handleMixedMultiVersion(YQPkgMultiVersion *newSelected)
Negotiate between multiversion and non-multiversion packages if there are both kinds in that selectab...
ZyppSel selectable() const
Returns the original ZYPP selectable.
void statusChanged()
Emitted when the status of this package version is changed.
void cycleStatus()
Cycle the package status to the next valid value.
static bool isMixedMultiVersion(ZyppSel selectable)
Return &#39;true&#39; if &#39;selectable&#39; has mixed multiversion flags, &#39;false&#39; if all its pool items are of the ...
void statusChanged()
Emitted when the status of any package changed.
void reload(int newCurrent)
Show data for the current package.
void showDetails(ZyppSel selectable)
Show details for the specified package.