libyui-qt-pkg  2.42.5
 All Classes Functions Variables Enumerations
YQPkgFilterTab.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: YQPkgFilterTab.cc
35 
36  Author: Stefan Hundhammer <sh@suse.de>
37 
38  Textdomain "qt-pkg"
39 
40 /-*/
41 
42 
43 #include <vector>
44 #include <algorithm> // std::swap()
45 
46 #include <QAction>
47 #include <QHBoxLayout>
48 #include <QMenu>
49 #include <QPushButton>
50 #include <QSplitter>
51 #include <QStackedWidget>
52 #include <QTabBar>
53 #include <QToolButton>
54 #include <QSettings>
55 
56 #define YUILogComponent "qt-pkg"
57 #include "YUILog.h"
58 
59 #include "YUI.h"
60 #include "YUIException.h"
61 #include "YApplication.h"
62 #include "YQPkgFilterTab.h"
63 #include "YQPkgDiskUsageList.h"
64 #include "YQSignalBlocker.h"
65 #include "YQIconPool.h"
66 #include "YQi18n.h"
67 #include "utf8.h"
68 
69 
70 using std::vector;
71 typedef vector<YQPkgFilterPage *> YQPkgFilterPageVector;
72 
73 #define SHOW_ONLY_IMPORTANT_PAGES 1
74 #define VIEW_BUTTON_LEFT 1
75 
76 #define SETTINGS_DIR "YaST2"
77 
78 
79 #define MARGIN 5 // inner margin between 3D borders and content
80 #define TOP_EXTRA_MARGIN 3
81 #define SPLITTER_HALF_SPACING 2
82 
83 
85 {
86  YQPkgFilterTabPrivate( const QString & name )
87  : settingsName( name )
88  , baseClassWidgetStack(0)
89  , outerSplitter(0)
90  , leftPaneSplitter(0)
91  , filtersWidgetStack(0)
92  , diskUsageList(0)
93  , rightPane(0)
94  , viewButton(0)
95  , closeButton(0)
96  , tabContextMenu(0)
97  , tabContextMenuPage(0)
98  {}
99 
100  QString settingsName;
101  QStackedWidget * baseClassWidgetStack;
102  QSplitter * outerSplitter;
103  QSplitter * leftPaneSplitter;
104  QStackedWidget * filtersWidgetStack;
105  YQPkgDiskUsageList * diskUsageList;
106  QWidget * rightPane;
107  QPushButton * viewButton;
108  QToolButton * closeButton;
109  QMenu * tabContextMenu;
110  QAction * actionMovePageLeft;
111  QAction * actionMovePageRight;
112  QAction * actionClosePage;
113  YQPkgFilterPage * tabContextMenuPage;
114  YQPkgFilterPageVector pages;
115 };
116 
117 
118 
119 
120 YQPkgFilterTab::YQPkgFilterTab( QWidget * parent, const QString & settingsName )
121  : QTabWidget( parent )
122  , priv( new YQPkgFilterTabPrivate( settingsName ) )
123 {
124  YUI_CHECK_NEW( priv );
125 
126  // Nasty hack: Find the base class's QStackedWidget in its widget tree so
127  // we have a place to put our own widgets. Unfortunately, this is private
128  // in the base class, but Qt lets us search the widget hierarchy by widget
129  // type.
130 
131  priv->baseClassWidgetStack = findChild<QStackedWidget*>();
132  YUI_CHECK_PTR( priv->baseClassWidgetStack );
133 
134  // Nasty hack: Disconnect the base class from signals from its tab bar.
135  // We will handle that signal on our own.
136 
137  disconnect( tabBar(), SIGNAL( currentChanged( int ) ), 0, 0 );
138 
139 
140  //
141  // Splitter that divides this widget into a left and a right pane
142  //
143 
144  priv->outerSplitter = new QSplitter( Qt::Horizontal, this );
145  YUI_CHECK_NEW( priv->outerSplitter );
146 
147  priv->outerSplitter->setSizePolicy( QSizePolicy( QSizePolicy::Expanding,
148  QSizePolicy::Expanding ) );
149  priv->baseClassWidgetStack->addWidget( priv->outerSplitter );
150 
151 
152 #if SHOW_ONLY_IMPORTANT_PAGES
153 
154  //
155  // "View" and "Close" buttons
156  //
157 
158  QWidget * buttonBox = new QWidget( this );
159  YUI_CHECK_NEW( buttonBox );
160  setCornerWidget( buttonBox, Qt::TopRightCorner );
161  buttonBox->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) );
162 
163  QHBoxLayout * buttonBoxLayout = new QHBoxLayout( buttonBox );
164  YUI_CHECK_NEW( buttonBoxLayout );
165  buttonBox->setLayout( buttonBoxLayout );
166  buttonBoxLayout->setContentsMargins( 0, 0, 0, 0 );
167 
168 #if VIEW_BUTTON_LEFT
169 
170  // Translators: Button with pop-up menu to open a new page (very much like
171  // in a web browser) with another package filter view or to switch to an
172  // existing one if it's open already
173 
174  priv->viewButton = new QPushButton( _( "&View" ), this );
175  YUI_CHECK_NEW( priv->viewButton );
176  setCornerWidget( priv->viewButton, Qt::TopLeftCorner );
177 #else
178  priv->viewButton = new QPushButton( _( "&View" ), buttonBox );
179  YUI_CHECK_NEW( priv->viewButton );
180  buttonBoxLayout->addWidget( priv->viewButton );
181 
182 #endif // VIEW_BUTTON_LEFT
183 
184  QMenu * menu = new QMenu();
185  YUI_CHECK_NEW( menu );
186  priv->viewButton->setMenu( menu );
187  menu->setTearOffEnabled( true );
188 
189  connect( menu, SIGNAL( triggered( QAction * ) ),
190  this, SLOT ( showPage ( QAction * ) ) );
191 
192 
193  priv->closeButton = new QToolButton( buttonBox );
194  YUI_CHECK_NEW( priv->closeButton );
195  buttonBoxLayout->addWidget( priv->closeButton );
196  priv->closeButton->setIcon( YQIconPool::tabRemove() );
197  priv->closeButton->setToolTip( _( "Close the current page" ) );
198 
199  connect( priv->closeButton, SIGNAL( clicked() ),
200  this, SLOT ( closeCurrentPage() ) );
201 
202 #endif // SHOW_ONLY_IMPORTANT_PAGES
203 
204 
205  //
206  // Splitter that divides the left pane into upper filters area and disk usage area
207  //
208 
209  priv->leftPaneSplitter = new QSplitter( Qt::Vertical, priv->outerSplitter );
210  YUI_CHECK_NEW( priv->leftPaneSplitter );
211 
212 
213  //
214  // Left pane content
215  //
216 
217  priv->filtersWidgetStack = new QStackedWidget( priv->leftPaneSplitter );
218  YUI_CHECK_NEW( priv->filtersWidgetStack );
219 
220  priv->diskUsageList = new YQPkgDiskUsageList( priv->leftPaneSplitter );
221  YUI_CHECK_NEW( priv->diskUsageList );
222 
223  {
224  QSplitter * sp = priv->leftPaneSplitter;
225  sp->setStretchFactor( sp->indexOf( priv->filtersWidgetStack ), 1 );
226  sp->setStretchFactor( sp->indexOf( priv->diskUsageList ), 2 );
227 
228 
229  // FIXME: Don't always hide the disk usage list
230  QList<int> sizes;
231  sizes << priv->leftPaneSplitter->height();
232  sizes << 0;
233  sp->setSizes( sizes );
234  }
235 
236 
237  //
238  // Right pane
239  //
240 
241  priv->rightPane = new QWidget( priv->outerSplitter );
242  YUI_CHECK_NEW( priv->rightPane );
243 
244 
245  //
246  // Stretch factors for left and right pane
247  //
248  {
249  QSplitter * sp = priv->outerSplitter;
250  sp->setStretchFactor( sp->indexOf( priv->leftPaneSplitter ), 0 );
251  sp->setStretchFactor( sp->indexOf( priv->rightPane ), 1 );
252  }
253 
254 
255  // Set up connections
256 
257  connect( tabBar(), SIGNAL( currentChanged( int ) ),
258  this, SLOT ( showPage ( int ) ) );
259 
260  tabBar()->installEventFilter( this ); // for tab context menu
261 
262 
263  //
264  // Cosmetics
265  //
266 
267  priv->baseClassWidgetStack->setContentsMargins( MARGIN, // left
268  MARGIN + TOP_EXTRA_MARGIN, // top
269  MARGIN, // right
270  MARGIN ); // bottom
271 
272  priv->leftPaneSplitter->setContentsMargins ( 0, // left
273  0, // top
274  SPLITTER_HALF_SPACING, // right
275  0 ); // bottom
276 
277  // priv->rightPane->setContentsMargins() is set when widgets are added to the right pane
278 }
279 
280 
282 {
283  saveSettings();
284 
285  for ( YQPkgFilterPageVector::iterator it = priv->pages.begin();
286  it != priv->pages.end();
287  ++it )
288  {
289  delete (*it);
290  }
291 
292  priv->pages.clear();
293 }
294 
295 
296 QWidget *
298 {
299  return priv->rightPane;
300 }
301 
302 
305 {
306  return priv->diskUsageList;
307 }
308 
309 
310 void
311 YQPkgFilterTab::addPage( const QString & pageLabel,
312  QWidget * pageContent,
313  const QString & internalName )
314 {
315  YQPkgFilterPage * page = new YQPkgFilterPage( pageLabel,
316  pageContent,
317  internalName );
318  YUI_CHECK_NEW( page );
319 
320  priv->pages.push_back( page );
321  priv->filtersWidgetStack->addWidget( pageContent );
322 
323 
324  if ( priv->viewButton && priv->viewButton->menu() )
325  {
326  QAction * action = new QAction( pageLabel, this );
327  YUI_CHECK_NEW( action );
328  action->setData( qVariantFromValue( pageContent ) );
329 
330  priv->viewButton->menu()->addAction( action );
331  }
332 
333 #if ! SHOW_ONLY_IMPORTANT_PAGES
334  page->tabIndex = tabBar()->addTab( pageLabel );
335  priv->closeButton->setEnabled( tabBar()->count() > 1 && page->closeEnabled );
336 #endif
337 }
338 
339 
340 void
341 YQPkgFilterTab::showPage( QWidget * pageContent )
342 {
343  YQPkgFilterPage * page = findPage( pageContent );
344  YUI_CHECK_PTR( page );
345 
346  showPage( page );
347 }
348 
349 
350 void
351 YQPkgFilterTab::showPage( const QString & internalName )
352 {
353  YQPkgFilterPage * page = findPage( internalName );
354  YUI_CHECK_PTR( page );
355 
356  showPage( page );
357 }
358 
359 
360 void
362 {
363  YQPkgFilterPage * page = findPage( tabIndex );
364 
365  if ( page )
366  showPage( page );
367 }
368 
369 
370 void
371 YQPkgFilterTab::showPage( QAction * action )
372 {
373  if ( ! action )
374  return;
375 
376  QWidget * pageContent = action->data().value<QWidget *>();
377  showPage( pageContent );
378 }
379 
380 
381 void
383 {
384  YUI_CHECK_PTR( page );
385  YQSignalBlocker sigBlocker( tabBar() );
386 
387  if ( page->tabIndex < 0 ) // No corresponding tab yet?
388  {
389  // Add a tab for that page
390  page->tabIndex = tabBar()->addTab( page->label );
391  }
392 
393  priv->filtersWidgetStack->setCurrentWidget( page->content );
394  tabBar()->setCurrentIndex( page->tabIndex );
395  priv->closeButton->setEnabled( tabBar()->count() > 1 && page->closeEnabled );
396  priv->tabContextMenuPage = page;
397 
398  emit currentChanged( page->content );
399 }
400 
401 
402 void
404 {
405  while ( tabBar()->count() > 0 )
406  {
407  tabBar()->removeTab( 0 );
408  }
409 
410  for ( YQPkgFilterPageVector::iterator it = priv->pages.begin();
411  it != priv->pages.end();
412  ++it )
413  {
414  (*it)->tabIndex = -1;
415  }
416 
417  priv->closeButton->setEnabled( false );
418 }
419 
420 
421 void
423 {
424  if ( tabBar()->count() > 1 )
425  {
426  int currentIndex = tabBar()->currentIndex();
427  YQPkgFilterPage * currentPage = findPage( currentIndex );
428 
429  if ( currentPage )
430  currentPage->tabIndex = -1;
431 
432  tabBar()->removeTab( currentIndex );
433 
434  //
435  // Adjust tab index of the active pages to the right of that page
436  //
437 
438  for ( YQPkgFilterPageVector::iterator it = priv->pages.begin();
439  it != priv->pages.end();
440  ++it )
441  {
442  YQPkgFilterPage * page = *it;
443 
444  if ( page->tabIndex >= currentIndex )
445  page->tabIndex--;
446  }
447 
448  showPage( tabBar()->currentIndex() );
449  }
450 }
451 
452 
454 YQPkgFilterTab::findPage( QWidget * pageContent )
455 {
456  for ( YQPkgFilterPageVector::iterator it = priv->pages.begin();
457  it != priv->pages.end();
458  ++it )
459  {
460  if ( (*it)->content == pageContent )
461  return *it;
462  }
463 
464  return 0;
465 }
466 
467 
469 YQPkgFilterTab::findPage( const QString & internalName )
470 {
471  for ( YQPkgFilterPageVector::iterator it = priv->pages.begin();
472  it != priv->pages.end();
473  ++it )
474  {
475  if ( (*it)->id == internalName )
476  return *it;
477  }
478 
479  return 0;
480 }
481 
482 
485 {
486  if ( tabIndex < 0 )
487  return 0;
488 
489  for ( YQPkgFilterPageVector::iterator it = priv->pages.begin();
490  it != priv->pages.end();
491  ++it )
492  {
493  if ( (*it)->tabIndex == tabIndex )
494  return *it;
495  }
496 
497  return 0;
498 }
499 
500 
501 int
503 {
504  return tabBar()->count();
505 }
506 
507 
508 bool
509 YQPkgFilterTab::eventFilter ( QObject * watchedObj, QEvent * event )
510 {
511  if ( watchedObj == tabBar() &&
512  event && event->type() == QEvent::MouseButtonPress )
513  {
514  QMouseEvent * mouseEvent = dynamic_cast<QMouseEvent *> (event);
515 
516  if ( mouseEvent && mouseEvent->button() == Qt::RightButton )
517  {
518  return postTabContextMenu( mouseEvent->pos() );
519  }
520  }
521 
522  return QTabWidget::eventFilter( watchedObj, event );
523 }
524 
525 
526 bool
528 {
529  int tabIndex = tabBar()->tabAt( pos );
530 
531  if ( tabIndex >= 0 ) // -1 means "no tab at that position"
532  {
533  priv->tabContextMenuPage = findPage( tabIndex );
534 
535  if ( priv->tabContextMenuPage )
536  {
537  if ( ! priv->tabContextMenu )
538  {
539  // On-demand menu creation
540 
541  priv->tabContextMenu = new QMenu( this );
542  YUI_CHECK_NEW( priv->tabContextMenu );
543 
544  // Translators: Change this to "right" for Arabic and Hebrew
545  priv->actionMovePageLeft = new QAction( YUI::yApp()->reverseLayout() ?
546  YQIconPool::arrowRight() : YQIconPool::arrowLeft(),
547  _( "Move page &left" ), this );
548  YUI_CHECK_NEW( priv->actionMovePageLeft );
549 
550  connect( priv->actionMovePageLeft, SIGNAL( triggered() ),
551  this, SLOT ( contextMovePageLeft() ) );
552 
553 
554  // Translators: Change this to "left" for Arabic and Hebrew
555  priv->actionMovePageRight = new QAction( YUI::yApp()->reverseLayout() ?
556  YQIconPool::arrowLeft() : YQIconPool::arrowRight(),
557  _( "Move page &right" ), this );
558  YUI_CHECK_NEW( priv->actionMovePageRight );
559 
560  connect( priv->actionMovePageRight, SIGNAL( triggered() ),
561  this, SLOT ( contextMovePageRight() ) );
562 
563 
564  priv->actionClosePage = new QAction( YQIconPool::tabRemove(), _( "&Close page" ), this );
565  YUI_CHECK_NEW( priv->actionClosePage );
566 
567  connect( priv->actionClosePage, SIGNAL( triggered() ),
568  this, SLOT ( contextClosePage() ) );
569 
570 
571  priv->tabContextMenu->addAction( priv->actionMovePageLeft );
572  priv->tabContextMenu->addAction( priv->actionMovePageRight );
573  priv->tabContextMenu->addAction( priv->actionClosePage );
574  }
575 
576  // Enable / disable actions
577 
578  priv->actionMovePageLeft->setEnabled( tabIndex > 0 );
579  priv->actionMovePageRight->setEnabled( tabIndex < ( tabBar()->count() - 1 ) );
580  priv->actionClosePage->setEnabled( tabBar()->count() > 1 && priv->tabContextMenuPage->closeEnabled );
581 
582  priv->tabContextMenu->popup( tabBar()->mapToGlobal( pos ) );
583 
584  return true; // event consumed - no further processing
585  }
586  }
587 
588  return false; // no tab at that position
589 }
590 
591 
592 void
594 {
595  if ( priv->tabContextMenuPage )
596  {
597  int contextPageIndex = priv->tabContextMenuPage->tabIndex;
598  int otherPageIndex = contextPageIndex-1;
599 
600  if ( otherPageIndex >= 0 )
601  {
602  swapTabs( priv->tabContextMenuPage, findPage( otherPageIndex ) );
603  }
604  }
605 }
606 
607 
608 void
610 {
611  if ( priv->tabContextMenuPage )
612  {
613  int contextPageIndex = priv->tabContextMenuPage->tabIndex;
614  int otherPageIndex = contextPageIndex+1;
615 
616  if ( otherPageIndex < tabBar()->count() )
617  {
618  swapTabs( priv->tabContextMenuPage, findPage( otherPageIndex ) );
619  }
620  }
621 }
622 
623 
624 void
626 {
627  if ( ! page1 or ! page2 )
628  return;
629 
630  int oldCurrentIndex = tabBar()->currentIndex();
631  std::swap( page1->tabIndex, page2->tabIndex );
632  tabBar()->setTabText( page1->tabIndex, page1->label );
633  tabBar()->setTabText( page2->tabIndex, page2->label );
634 
635 
636  // If one of the two pages was the currently displayed one,
637  // make sure the same page is still displayed.
638 
639  if ( oldCurrentIndex == page1->tabIndex )
640  {
641  YQSignalBlocker sigBlocker( tabBar() );
642  tabBar()->setCurrentIndex( page2->tabIndex );
643  }
644  else if ( oldCurrentIndex == page2->tabIndex )
645  {
646  YQSignalBlocker sigBlocker( tabBar() );
647  tabBar()->setCurrentIndex( page1->tabIndex );
648  }
649 }
650 
651 
652 void
654 {
655  if ( priv->tabContextMenuPage )
656  {
657  int pageIndex = priv->tabContextMenuPage->tabIndex;
658  priv->tabContextMenuPage->tabIndex = -1;
659  tabBar()->removeTab( pageIndex );
660 
661 
662  //
663  // Adjust tab index of the active pages to the right of that page
664  //
665 
666  for ( YQPkgFilterPageVector::iterator it = priv->pages.begin();
667  it != priv->pages.end();
668  ++it )
669  {
670  YQPkgFilterPage * page = *it;
671 
672  if ( page->tabIndex >= pageIndex )
673  page->tabIndex--;
674  }
675 
676  showPage( tabBar()->currentIndex() ); // display the new current page
677  }
678 }
679 
680 
681 void
683 {
684  closeAllPages();
685  QSettings settings( QSettings::UserScope, SETTINGS_DIR, priv->settingsName );
686 
687  int size = settings.beginReadArray( "Tab_Pages" );
688 
689  for ( int i=0; i < size; i++ )
690  {
691  settings.setArrayIndex(i);
692  QString id = settings.value( "Page_ID" ).toString();
693  YQPkgFilterPage * page = findPage( id );
694 
695  if ( page )
696  {
697  yuiDebug() << "Restoring page \"" << toUTF8( id ) << "\"" << std::endl;
698  showPage( page );
699  }
700  else
701  yuiWarning() << "No page with ID \"" << toUTF8( id ) << "\"" << std::endl;
702  }
703 
704  settings.endArray();
705 
706  QString id = settings.value( "Current_Page" ).toString();
707 
708  if ( ! id.isEmpty() )
709  showPage( id );
710 }
711 
712 
713 void
715 {
716  QSettings settings( QSettings::UserScope, SETTINGS_DIR, priv->settingsName );
717 
718  settings.beginWriteArray( "Tab_Pages" );
719 
720  for ( int i=0; i < tabBar()->count(); i++ )
721  {
722  YQPkgFilterPage * page = findPage( i );
723 
724  if ( page )
725  {
726  settings.setArrayIndex(i);
727 
728  if ( page->id.isEmpty() )
729  yuiWarning() << "No ID for tab page \"" << page->label << "\"" << std::endl;
730  else
731  {
732  yuiDebug() << "Saving page #" << i << ": \"" << toUTF8( page->id ) << "\"" << std::endl;
733  settings.setValue( "Page_ID", page->id );
734  }
735  }
736  }
737 
738  settings.endArray();
739 
740  YQPkgFilterPage * currentPage = findPage( tabBar()->currentIndex() );
741 
742  if ( currentPage )
743  settings.setValue( "Current_Page", currentPage->id );
744 }
745 
746 
747 
748 #include "YQPkgFilterTab.moc"