mirror of
https://github.com/KDE/konsole.git
synced 2026-05-03 04:04:18 -04:00
applied correctly on startup. Test for the tab bar being explicitly hidden with isHidden() rather than just isVisible() which will return true if the setting is applied before the container widget is shown. BUG: 162420 CCBUG: 157015 svn path=/trunk/KDE/kdebase/apps/konsole/; revision=810949
1114 lines
32 KiB
C++
1114 lines
32 KiB
C++
/*
|
|
This file is part of the Konsole Terminal.
|
|
|
|
Copyright 2006-2008 Robert Knight <robertknight@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA.
|
|
*/
|
|
|
|
// Own
|
|
#include "ViewContainer.h"
|
|
|
|
// Qt
|
|
#include <QtCore/QHash>
|
|
#include <QtGui/QLabel>
|
|
#include <QtGui/QLineEdit>
|
|
#include <QtGui/QBrush>
|
|
#include <QtGui/QListWidget>
|
|
#include <QtGui/QSplitter>
|
|
#include <QtGui/QStackedWidget>
|
|
#include <QtGui/QTabBar>
|
|
#include <QtGui/QToolButton>
|
|
#include <QtGui/QWidgetAction>
|
|
|
|
#include <QtGui/QDrag>
|
|
#include <QtGui/QDragMoveEvent>
|
|
#include <QMimeData>
|
|
|
|
// KDE
|
|
#include <KColorDialog>
|
|
#include <kcolorscheme.h>
|
|
#include <kcolorutils.h>
|
|
#include <kdebug.h>
|
|
#include <kconfiggroup.h>
|
|
#include <KLocale>
|
|
#include <KMenu>
|
|
#include <KColorCollection>
|
|
#include <KTabWidget>
|
|
|
|
// Konsole
|
|
#include "ViewProperties.h"
|
|
|
|
// TODO Perhaps move everything which is Konsole-specific into different files
|
|
#include "ProfileListWidget.h"
|
|
|
|
using namespace Konsole;
|
|
|
|
ViewContainer::ViewContainer(NavigationPosition position , QObject* parent)
|
|
: QObject(parent)
|
|
, _navigationDisplayMode(AlwaysShowNavigation)
|
|
, _navigationPosition(position)
|
|
{
|
|
}
|
|
ViewContainer::~ViewContainer()
|
|
{
|
|
foreach( QWidget* view , _views )
|
|
{
|
|
disconnect(view,SIGNAL(destroyed(QObject*)),this,SLOT(viewDestroyed(QObject*)));
|
|
}
|
|
|
|
emit destroyed(this);
|
|
}
|
|
void ViewContainer::moveViewWidget( int , int ) {}
|
|
void ViewContainer::setFeatures(Features features)
|
|
{ _features = features; }
|
|
ViewContainer::Features ViewContainer::features() const
|
|
{ return _features; }
|
|
void ViewContainer::moveActiveView( MoveDirection direction )
|
|
{
|
|
const int currentIndex = _views.indexOf( activeView() ) ;
|
|
int newIndex = -1;
|
|
|
|
switch ( direction )
|
|
{
|
|
case MoveViewLeft:
|
|
newIndex = qMax( currentIndex-1 , 0 );
|
|
break;
|
|
case MoveViewRight:
|
|
newIndex = qMin( currentIndex+1 , _views.count() -1 );
|
|
break;
|
|
}
|
|
|
|
Q_ASSERT( newIndex != -1 );
|
|
|
|
moveViewWidget( currentIndex , newIndex );
|
|
|
|
_views.swap(currentIndex,newIndex);
|
|
|
|
setActiveView( _views[newIndex] );
|
|
}
|
|
|
|
void ViewContainer::setNavigationDisplayMode(NavigationDisplayMode mode)
|
|
{
|
|
_navigationDisplayMode = mode;
|
|
navigationDisplayModeChanged(mode);
|
|
}
|
|
ViewContainer::NavigationPosition ViewContainer::navigationPosition() const
|
|
{
|
|
return _navigationPosition;
|
|
}
|
|
void ViewContainer::setNavigationPosition(NavigationPosition position)
|
|
{
|
|
// assert that this position is supported
|
|
Q_ASSERT( supportedNavigationPositions().contains(position) );
|
|
|
|
_navigationPosition = position;
|
|
|
|
navigationPositionChanged(position);
|
|
}
|
|
QList<ViewContainer::NavigationPosition> ViewContainer::supportedNavigationPositions() const
|
|
{
|
|
return QList<NavigationPosition>() << NavigationPositionTop;
|
|
}
|
|
ViewContainer::NavigationDisplayMode ViewContainer::navigationDisplayMode() const
|
|
{
|
|
return _navigationDisplayMode;
|
|
}
|
|
void ViewContainer::addView(QWidget* view , ViewProperties* item, int index)
|
|
{
|
|
if (index == -1)
|
|
_views.append(view);
|
|
else
|
|
_views.insert(index,view);
|
|
|
|
_navigation[view] = item;
|
|
|
|
connect( view , SIGNAL(destroyed(QObject*)) , this , SLOT( viewDestroyed(QObject*) ) );
|
|
|
|
addViewWidget(view,index);
|
|
|
|
emit viewAdded(view,item);
|
|
}
|
|
void ViewContainer::viewDestroyed(QObject* object)
|
|
{
|
|
QWidget* widget = static_cast<QWidget*>(object);
|
|
|
|
_views.removeAll(widget);
|
|
_navigation.remove(widget);
|
|
|
|
// FIXME This can result in ViewContainerSubClass::removeViewWidget() being
|
|
// called after the the widget's parent has been deleted or partially deleted
|
|
// in the ViewContainerSubClass instance's destructor.
|
|
//
|
|
// Currently deleteLater() is used to remove child widgets in the subclass
|
|
// constructors to get around the problem, but this is a hack and needs
|
|
// to be fixed.
|
|
removeViewWidget(widget);
|
|
|
|
emit viewRemoved(widget);
|
|
|
|
if (_views.count() == 0)
|
|
emit empty(this);
|
|
}
|
|
void ViewContainer::removeView(QWidget* view)
|
|
{
|
|
_views.removeAll(view);
|
|
_navigation.remove(view);
|
|
|
|
disconnect( view , SIGNAL(destroyed(QObject*)) , this , SLOT( viewDestroyed(QObject*) ) );
|
|
|
|
removeViewWidget(view);
|
|
|
|
emit viewRemoved(view);
|
|
|
|
if (_views.count() == 0)
|
|
emit empty(this);
|
|
|
|
}
|
|
|
|
const QList<QWidget*> ViewContainer::views()
|
|
{
|
|
return _views;
|
|
}
|
|
|
|
void ViewContainer::activateNextView()
|
|
{
|
|
QWidget* active = activeView();
|
|
|
|
int index = _views.indexOf(active);
|
|
|
|
if ( index == -1 )
|
|
return;
|
|
|
|
if ( index == _views.count() - 1 )
|
|
index = 0;
|
|
else
|
|
index++;
|
|
|
|
setActiveView( _views.at(index) );
|
|
}
|
|
|
|
void ViewContainer::activatePreviousView()
|
|
{
|
|
QWidget* active = activeView();
|
|
|
|
int index = _views.indexOf(active);
|
|
|
|
if ( index == -1 )
|
|
return;
|
|
|
|
if ( index == 0 )
|
|
index = _views.count() - 1;
|
|
else
|
|
index--;
|
|
|
|
setActiveView( _views.at(index) );
|
|
}
|
|
|
|
ViewProperties* ViewContainer::viewProperties( QWidget* widget )
|
|
{
|
|
Q_ASSERT( _navigation.contains(widget) );
|
|
|
|
return _navigation[widget];
|
|
}
|
|
|
|
QList<QWidget*> ViewContainer::widgetsForItem(ViewProperties* item) const
|
|
{
|
|
return _navigation.keys(item);
|
|
}
|
|
|
|
TabbedViewContainer::TabbedViewContainer(NavigationPosition position , QObject* parent) :
|
|
ViewContainer(position,parent)
|
|
,_newSessionButton(0)
|
|
,_tabContextMenu(0)
|
|
,_tabSelectColorMenu(0)
|
|
,_tabColorSelector(0)
|
|
,_tabColorCells(0)
|
|
,_contextMenuTab(0)
|
|
{
|
|
_tabWidget = new KTabWidget();
|
|
_tabContextMenu = new KMenu(_tabWidget);
|
|
|
|
_newSessionButton = new QToolButton(_tabWidget);
|
|
_newSessionButton->setAutoRaise(true);
|
|
_newSessionButton->setIcon( KIcon("tab-new") );
|
|
_newSessionButton->setText( i18n("New") );
|
|
_newSessionButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
_newSessionButton->setPopupMode(QToolButton::MenuButtonPopup);
|
|
|
|
QToolButton* closeButton = new QToolButton(_tabWidget);
|
|
closeButton->setIcon( KIcon("tab-close") );
|
|
closeButton->setAutoRaise(true);
|
|
connect( closeButton , SIGNAL(clicked()) , this , SLOT(closeTabClicked()) );
|
|
|
|
_tabWidget->setCornerWidget(_newSessionButton,Qt::TopLeftCorner);
|
|
_tabWidget->setCornerWidget(closeButton,Qt::TopRightCorner);
|
|
|
|
//Create a colour selection palette and fill it with a range of suitable colours
|
|
QString paletteName;
|
|
QStringList availablePalettes = KColorCollection::installedCollections();
|
|
|
|
if (availablePalettes.contains("40.colors"))
|
|
paletteName = "40.colors";
|
|
|
|
KColorCollection palette(paletteName);
|
|
|
|
//If the palette of colours was found, create a palette menu displaying those colors
|
|
//which the user chooses from when they activate the "Select Tab Color" sub-menu.
|
|
//
|
|
//If the palette is empty, default back to the old behaviour where the user is shown
|
|
//a color dialog when they click the "Select Tab Color" menu item.
|
|
if ( palette.count() > 0 )
|
|
{
|
|
_tabColorCells = new KColorCells(_tabWidget,palette.count()/8,8);
|
|
|
|
for (int i=0;i<palette.count();i++)
|
|
_tabColorCells->setColor(i,palette.color(i));
|
|
|
|
|
|
_tabSelectColorMenu = new KMenu(_tabWidget);
|
|
connect( _tabSelectColorMenu, SIGNAL(aboutToShow()) , this, SLOT(prepareColorCells()) );
|
|
_tabColorSelector = new QWidgetAction(_tabSelectColorMenu);
|
|
_tabColorSelector->setDefaultWidget(_tabColorCells);
|
|
|
|
_tabSelectColorMenu->addAction( _tabColorSelector );
|
|
|
|
connect(_tabColorCells,SIGNAL(colorSelected(int)),this,SLOT(selectTabColor()));
|
|
connect(_tabColorCells,SIGNAL(colorSelected(int)),_tabContextMenu,SLOT(hide()));
|
|
|
|
QAction* action = _tabSelectColorMenu->menuAction();
|
|
//_tabPopupMenu->addMenu(_tabSelectColorMenu);
|
|
action->setIcon( KIcon("colors") );
|
|
action->setText( i18n("Select &Tab Color") );
|
|
|
|
_viewActions << action;
|
|
}
|
|
else
|
|
{
|
|
// _viewActions << new KAction( KIcon("colors"),i18n("Select &Tab Color..."),this,
|
|
// SLOT(slotTabSelectColor()));
|
|
}
|
|
|
|
|
|
connect( _tabWidget , SIGNAL(currentChanged(int)) , this , SLOT(currentTabChanged(int)) );
|
|
connect( _tabWidget , SIGNAL(contextMenu(QWidget*,const QPoint&)),
|
|
SLOT(showContextMenu(QWidget*,const QPoint&)));
|
|
}
|
|
|
|
|
|
|
|
void TabbedViewContainer::currentTabChanged(int tab)
|
|
{
|
|
if ( tab >= 0 )
|
|
{
|
|
emit activeViewChanged( _tabWidget->widget(tab) );
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::closeTabClicked()
|
|
{
|
|
emit closeRequest(_tabWidget->currentWidget());
|
|
}
|
|
|
|
TabbedViewContainer::~TabbedViewContainer()
|
|
{
|
|
_tabContextMenu->deleteLater();
|
|
_tabWidget->deleteLater();
|
|
}
|
|
|
|
void TabbedViewContainer::setNewSessionMenu(QMenu* menu)
|
|
{
|
|
_newSessionButton->setMenu(menu);
|
|
}
|
|
void TabbedViewContainer::showContextMenu(QWidget* widget , const QPoint& position)
|
|
{
|
|
//TODO - Use the tab under the mouse, not just the active tab
|
|
_contextMenuTab = _tabWidget->indexOf(widget);
|
|
//NavigationItem* item = navigationItem( widget );
|
|
|
|
_tabContextMenu->clear();
|
|
// _tabContextMenu->addActions( item->contextMenuActions(_viewActions) );
|
|
_tabContextMenu->popup( position );
|
|
}
|
|
|
|
void TabbedViewContainer::prepareColorCells()
|
|
{
|
|
//set selected color in palette widget to color of active tab
|
|
|
|
QColor activeTabColor = _tabWidget->tabTextColor( _contextMenuTab );
|
|
|
|
for (int i=0;i<_tabColorCells->count();i++)
|
|
if ( activeTabColor == _tabColorCells->color(i) )
|
|
{
|
|
_tabColorCells->setSelected(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainer::addViewWidget( QWidget* view , int )
|
|
{
|
|
ViewProperties* item = viewProperties(view);
|
|
connect( item , SIGNAL(titleChanged(ViewProperties*)) , this , SLOT(updateTitle(ViewProperties*)));
|
|
connect( item , SIGNAL(iconChanged(ViewProperties*) ) , this ,SLOT(updateIcon(ViewProperties*)));
|
|
_tabWidget->addTab( view , item->icon() , item->title() );
|
|
}
|
|
void TabbedViewContainer::removeViewWidget( QWidget* view )
|
|
{
|
|
const int index = _tabWidget->indexOf(view);
|
|
|
|
if ( index != -1 )
|
|
_tabWidget->removeTab( index );
|
|
}
|
|
|
|
void TabbedViewContainer::updateIcon(ViewProperties* item)
|
|
{
|
|
QList<QWidget*> items = widgetsForItem(item);
|
|
QListIterator<QWidget*> itemIter(items);
|
|
|
|
while ( itemIter.hasNext() )
|
|
{
|
|
int index = _tabWidget->indexOf( itemIter.next() );
|
|
_tabWidget->setTabIcon( index , item->icon() );
|
|
}
|
|
}
|
|
void TabbedViewContainer::updateTitle(ViewProperties* item)
|
|
{
|
|
QList<QWidget*> items = widgetsForItem(item);
|
|
QListIterator<QWidget*> itemIter(items);
|
|
|
|
while ( itemIter.hasNext() )
|
|
{
|
|
int index = _tabWidget->indexOf( itemIter.next() );
|
|
_tabWidget->setTabText( index , item->title() );
|
|
}
|
|
|
|
}
|
|
|
|
QWidget* TabbedViewContainer::containerWidget() const
|
|
{
|
|
return _tabWidget;
|
|
}
|
|
|
|
QWidget* TabbedViewContainer::activeView() const
|
|
{
|
|
return _tabWidget->widget(_tabWidget->currentIndex());
|
|
}
|
|
|
|
void TabbedViewContainer::setActiveView(QWidget* view)
|
|
{
|
|
_tabWidget->setCurrentWidget(view);
|
|
}
|
|
|
|
void TabbedViewContainer::selectTabColor()
|
|
{
|
|
QColor color;
|
|
|
|
//If the color palette is available apply the current selected color to the tab, otherwise
|
|
//default back to showing KDE's color dialog instead.
|
|
if ( _tabColorCells )
|
|
{
|
|
color = _tabColorCells->color(_tabColorCells->selectedIndex());
|
|
|
|
if (!color.isValid())
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
QColor defaultColor = _tabWidget->palette().color( QPalette::Foreground );
|
|
QColor tempColor = _tabWidget->tabTextColor( _contextMenuTab );
|
|
|
|
if ( KColorDialog::getColor(tempColor,defaultColor,_tabWidget) == KColorDialog::Accepted )
|
|
color = tempColor;
|
|
else
|
|
return;
|
|
}
|
|
|
|
_tabWidget->setTabTextColor( _contextMenuTab , color );
|
|
}
|
|
|
|
ViewContainerTabBar::ViewContainerTabBar(QWidget* parent,TabbedViewContainerV2* container)
|
|
: KTabBar(parent)
|
|
, _container(container)
|
|
, _dropIndicator(0)
|
|
, _dropIndicatorIndex(-1)
|
|
, _drawIndicatorDisabled(false)
|
|
{
|
|
}
|
|
void ViewContainerTabBar::setDropIndicator(int index, bool drawDisabled)
|
|
{
|
|
if (!parentWidget() || _dropIndicatorIndex == index)
|
|
return;
|
|
|
|
_dropIndicatorIndex = index;
|
|
const int ARROW_SIZE = 22;
|
|
bool north = shape() == QTabBar::RoundedNorth || shape() == QTabBar::TriangularNorth;
|
|
|
|
if (!_dropIndicator || _drawIndicatorDisabled != drawDisabled)
|
|
{
|
|
if (!_dropIndicator)
|
|
{
|
|
_dropIndicator = new QLabel(parentWidget());
|
|
_dropIndicator->resize(ARROW_SIZE,ARROW_SIZE);
|
|
}
|
|
|
|
QIcon::Mode drawMode = drawDisabled ? QIcon::Disabled : QIcon::Normal;
|
|
const QString iconName = north ? "arrow-up" : "arrow-down";
|
|
_dropIndicator->setPixmap(KIcon(iconName).pixmap(ARROW_SIZE,ARROW_SIZE,drawMode));
|
|
_drawIndicatorDisabled = drawDisabled;
|
|
}
|
|
|
|
if (index < 0)
|
|
{
|
|
_dropIndicator->hide();
|
|
return;
|
|
}
|
|
|
|
const QRect rect = tabRect(index < count() ? index : index-1);
|
|
|
|
QPoint pos;
|
|
if (index < count())
|
|
pos = rect.topLeft();
|
|
else
|
|
pos = rect.topRight();
|
|
|
|
if (north)
|
|
pos.ry() += ARROW_SIZE;
|
|
else
|
|
pos.ry() -= ARROW_SIZE;
|
|
|
|
pos.rx() -= ARROW_SIZE/2;
|
|
|
|
_dropIndicator->move(mapTo(parentWidget(),pos));
|
|
_dropIndicator->show();
|
|
|
|
}
|
|
void ViewContainerTabBar::dragLeaveEvent(QDragLeaveEvent*)
|
|
{
|
|
setDropIndicator(-1);
|
|
}
|
|
void ViewContainerTabBar::dragEnterEvent(QDragEnterEvent* event)
|
|
{
|
|
if (event->mimeData()->hasFormat(ViewProperties::mimeType()) &&
|
|
event->source() != 0)
|
|
event->acceptProposedAction();
|
|
}
|
|
void ViewContainerTabBar::dragMoveEvent(QDragMoveEvent* event)
|
|
{
|
|
if (event->mimeData()->hasFormat(ViewProperties::mimeType())
|
|
&& event->source() != 0)
|
|
{
|
|
int index = dropIndex(event->pos());
|
|
if (index == -1)
|
|
index = count();
|
|
|
|
setDropIndicator(index,proposedDropIsSameTab(event));
|
|
|
|
event->acceptProposedAction();
|
|
}
|
|
}
|
|
int ViewContainerTabBar::dropIndex(const QPoint& pos) const
|
|
{
|
|
int tab = tabAt(pos);
|
|
if (tab < 0)
|
|
return tab;
|
|
|
|
// pick the closest tab boundary
|
|
QRect rect = tabRect(tab);
|
|
if ( (pos.x()-rect.left()) > (rect.width()/2) )
|
|
tab++;
|
|
|
|
if (tab == count())
|
|
return -1;
|
|
|
|
return tab;
|
|
}
|
|
bool ViewContainerTabBar::proposedDropIsSameTab(const QDropEvent* event) const
|
|
{
|
|
int index = dropIndex(event->pos());
|
|
int droppedId = ViewProperties::decodeMimeData(event->mimeData());
|
|
bool sameTabBar = event->source() == this;
|
|
|
|
if (!sameTabBar)
|
|
return false;
|
|
|
|
const QList<QWidget*> viewList = _container->views();
|
|
int sourceIndex = -1;
|
|
for (int i=0;i<count();i++)
|
|
{
|
|
int idAtIndex = _container->viewProperties(viewList[i])->identifier();
|
|
if (idAtIndex == droppedId)
|
|
sourceIndex = i;
|
|
}
|
|
|
|
bool sourceAndDropAreLast = sourceIndex == count()-1 && index == -1;
|
|
if (sourceIndex == index || sourceIndex == index-1 || sourceAndDropAreLast)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
void ViewContainerTabBar::dropEvent(QDropEvent* event)
|
|
{
|
|
setDropIndicator(-1);
|
|
|
|
if ( !event->mimeData()->hasFormat(ViewProperties::mimeType())
|
|
|| proposedDropIsSameTab(event) )
|
|
{
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
int index = dropIndex(event->pos());
|
|
int droppedId = ViewProperties::decodeMimeData(event->mimeData());
|
|
bool result = false;
|
|
emit _container->moveViewRequest(index,droppedId,result);
|
|
|
|
if (result)
|
|
event->accept();
|
|
else
|
|
event->ignore();
|
|
}
|
|
|
|
QSize ViewContainerTabBar::tabSizeHint(int index) const
|
|
{
|
|
return QTabBar::tabSizeHint(index);
|
|
}
|
|
QPixmap ViewContainerTabBar::dragDropPixmap(int tab)
|
|
{
|
|
Q_ASSERT(tab >= 0 && tab < count());
|
|
|
|
// TODO - grabWidget() works except that it includes part
|
|
// of the tab bar outside the tab itself if the tab has
|
|
// curved corners
|
|
const QRect rect = tabRect(tab);
|
|
const int borderWidth = 1;
|
|
|
|
QPixmap tabPixmap(rect.width()+borderWidth,
|
|
rect.height()+borderWidth);
|
|
QPainter painter(&tabPixmap);
|
|
painter.drawPixmap(0,0,QPixmap::grabWidget(this,rect));
|
|
QPen borderPen;
|
|
borderPen.setBrush(palette().dark());
|
|
borderPen.setWidth(borderWidth);
|
|
painter.setPen(borderPen);
|
|
painter.drawRect(0,0,rect.width(),rect.height());
|
|
painter.end();
|
|
|
|
return tabPixmap;
|
|
}
|
|
TabbedViewContainerV2::TabbedViewContainerV2(NavigationPosition position , QObject* parent)
|
|
: ViewContainer(position,parent)
|
|
{
|
|
_containerWidget = new QWidget;
|
|
_stackWidget = new QStackedWidget();
|
|
_tabBar = new ViewContainerTabBar(_containerWidget,this);
|
|
_tabBar->setDrawBase(true);
|
|
|
|
const int cornerButtonWidth = 50;
|
|
_newTabButton = new KPushButton(KIcon("tab-new"),QString(),_containerWidget);
|
|
// The button width here is hard coded, it would be better to use the value from
|
|
// the current style (see QTabWidget::setUpLayout())
|
|
_newTabButton->setFixedWidth(cornerButtonWidth);
|
|
_newTabButton->setFlat(true);
|
|
// new tab button is initially hidden, it will be shown when setFeatures() is called
|
|
// with the QuickNewView flag enabled
|
|
_newTabButton->setHidden(true);
|
|
|
|
_closeTabButton = new KPushButton(KIcon("tab-close"),QString(),_containerWidget);
|
|
_closeTabButton->setFixedWidth(cornerButtonWidth);
|
|
_closeTabButton->setFlat(true);
|
|
_closeTabButton->setHidden(true);
|
|
|
|
connect( _tabBar , SIGNAL(currentChanged(int)) , this , SLOT(currentTabChanged(int)) );
|
|
connect( _tabBar , SIGNAL(tabDoubleClicked(int)) , this , SLOT(tabDoubleClicked(int)) );
|
|
connect( _tabBar , SIGNAL(newTabRequest()) , this , SIGNAL(newViewRequest()) );
|
|
connect( _tabBar , SIGNAL(wheelDelta(int)) , this , SLOT(wheelScrolled(int)) );
|
|
connect( _tabBar , SIGNAL(mouseMiddleClick(int)) , this , SLOT(closeTab(int)) );
|
|
connect( _tabBar , SIGNAL(closeRequest(int)) , this , SLOT(closeTab(int)) );
|
|
connect( _tabBar , SIGNAL(initiateDrag(int)) , this , SLOT(startTabDrag(int)) );
|
|
|
|
connect( _newTabButton , SIGNAL(clicked()) , this , SIGNAL(newViewRequest()) );
|
|
connect( _closeTabButton , SIGNAL(clicked()) , this , SLOT(closeCurrentTab()) );
|
|
|
|
_layout = new TabbedViewContainerV2Layout;
|
|
_layout->setSpacing(0);
|
|
_layout->setMargin(0);
|
|
_tabBarLayout = new QHBoxLayout;
|
|
_tabBarLayout->setSpacing(0);
|
|
_tabBarLayout->setMargin(0);
|
|
_tabBarLayout->addWidget(_newTabButton);
|
|
_tabBarLayout->addWidget(_tabBar);
|
|
_tabBarLayout->addWidget(_closeTabButton);
|
|
_tabBarSpacer = new QSpacerItem(0,TabBarSpace);
|
|
|
|
_layout->addWidget(_stackWidget);
|
|
|
|
if ( position == NavigationPositionTop )
|
|
{
|
|
_layout->insertLayout(0,_tabBarLayout);
|
|
_layout->insertItemAt(0,_tabBarSpacer);
|
|
_tabBar->setShape(QTabBar::RoundedNorth);
|
|
}
|
|
else if ( position == NavigationPositionBottom )
|
|
{
|
|
_layout->insertLayout(-1,_tabBarLayout);
|
|
_layout->insertItemAt(-1,_tabBarSpacer);
|
|
_tabBar->setShape(QTabBar::RoundedSouth);
|
|
}
|
|
else
|
|
Q_ASSERT(false); // position not supported
|
|
|
|
_containerWidget->setLayout(_layout);
|
|
}
|
|
void TabbedViewContainerV2::setNewViewMenu(QMenu* menu)
|
|
{ _newTabButton->setDelayedMenu(menu); }
|
|
ViewContainer::Features TabbedViewContainerV2::supportedFeatures() const
|
|
{ return QuickNewView|QuickCloseView; }
|
|
void TabbedViewContainerV2::setFeatures(Features features)
|
|
{
|
|
ViewContainer::setFeatures(features);
|
|
|
|
const bool tabBarHidden = _tabBar->isHidden();
|
|
_newTabButton->setVisible(!tabBarHidden && (features & QuickNewView));
|
|
_closeTabButton->setVisible(!tabBarHidden && (features & QuickCloseView));
|
|
}
|
|
void TabbedViewContainerV2::closeCurrentTab()
|
|
{
|
|
if (_stackWidget->currentIndex() != -1)
|
|
{
|
|
closeTab(_stackWidget->currentIndex());
|
|
}
|
|
}
|
|
void TabbedViewContainerV2::closeTab(int tab)
|
|
{
|
|
Q_ASSERT(tab >= 0 && tab < _stackWidget->count());
|
|
|
|
if (viewProperties(_stackWidget->widget(tab))->confirmClose())
|
|
removeView(_stackWidget->widget(tab));
|
|
}
|
|
void TabbedViewContainerV2::setTabBarVisible(bool visible)
|
|
{
|
|
_tabBar->setVisible(visible);
|
|
_newTabButton->setVisible(visible && (features() & QuickNewView));
|
|
_closeTabButton->setVisible(visible && (features() & QuickCloseView));
|
|
if ( visible )
|
|
{
|
|
_tabBarSpacer->changeSize(0,TabBarSpace);
|
|
}
|
|
else
|
|
{
|
|
_tabBarSpacer->changeSize(0,0);
|
|
}
|
|
}
|
|
QList<ViewContainer::NavigationPosition> TabbedViewContainerV2::supportedNavigationPositions() const
|
|
{
|
|
return QList<NavigationPosition>() << NavigationPositionTop << NavigationPositionBottom;
|
|
}
|
|
void TabbedViewContainerV2::navigationPositionChanged(NavigationPosition position)
|
|
{
|
|
// this method assumes that there are only three items
|
|
// in the layout
|
|
Q_ASSERT( _layout->count() == 3 );
|
|
|
|
// index of stack widget in the layout when tab bar is at the bottom
|
|
const int StackIndexWithTabBottom = 0;
|
|
|
|
if ( position == NavigationPositionTop
|
|
&& _layout->indexOf(_stackWidget) == StackIndexWithTabBottom )
|
|
{
|
|
_layout->removeItem(_tabBarLayout);
|
|
_layout->removeItem(_tabBarSpacer);
|
|
|
|
_layout->insertLayout(0,_tabBarLayout);
|
|
_layout->insertItemAt(0,_tabBarSpacer);
|
|
_tabBar->setShape(QTabBar::RoundedNorth);
|
|
}
|
|
else if ( position == NavigationPositionBottom
|
|
&& _layout->indexOf(_stackWidget) != StackIndexWithTabBottom )
|
|
{
|
|
_layout->removeItem(_tabBarLayout);
|
|
_layout->removeItem(_tabBarSpacer);
|
|
|
|
_layout->insertLayout(-1,_tabBarLayout);
|
|
_layout->insertItemAt(-1,_tabBarSpacer);
|
|
_tabBar->setShape(QTabBar::RoundedSouth);
|
|
}
|
|
}
|
|
void TabbedViewContainerV2::navigationDisplayModeChanged(NavigationDisplayMode mode)
|
|
{
|
|
if ( mode == AlwaysShowNavigation && _tabBar->isHidden() )
|
|
setTabBarVisible(true);
|
|
|
|
if ( mode == AlwaysHideNavigation && !_tabBar->isHidden() )
|
|
setTabBarVisible(false);
|
|
|
|
if ( mode == ShowNavigationAsNeeded )
|
|
dynamicTabBarVisibility();
|
|
}
|
|
void TabbedViewContainerV2::dynamicTabBarVisibility()
|
|
{
|
|
if ( _tabBar->count() > 1 && _tabBar->isHidden() )
|
|
setTabBarVisible(true);
|
|
|
|
if ( _tabBar->count() < 2 && !_tabBar->isHidden() )
|
|
setTabBarVisible(false);
|
|
}
|
|
TabbedViewContainerV2::~TabbedViewContainerV2()
|
|
{
|
|
_containerWidget->deleteLater();
|
|
}
|
|
|
|
void TabbedViewContainerV2::startTabDrag(int tab)
|
|
{
|
|
QDrag* drag = new QDrag(_tabBar);
|
|
const QRect tabRect = _tabBar->tabRect(tab);
|
|
QPixmap tabPixmap = _tabBar->dragDropPixmap(tab);
|
|
|
|
drag->setPixmap(tabPixmap);
|
|
|
|
int id = viewProperties(views()[tab])->identifier();
|
|
QWidget* view = views()[tab];
|
|
drag->setMimeData(ViewProperties::createMimeData(id));
|
|
|
|
// start drag, if drag-and-drop is successful the view at 'tab' will be
|
|
// deleted
|
|
//
|
|
// if the tab was dragged onto another application
|
|
// which blindly accepted the drop then ignore it
|
|
if (drag->exec() == Qt::MoveAction && drag->target() != 0)
|
|
{
|
|
// Deleting the view may cause the view container to be deleted, which
|
|
// will also delete the QDrag object.
|
|
// This can cause a crash if Qt's internal drag-and-drop handling
|
|
// tries to delete it later.
|
|
//
|
|
// For now set the QDrag's parent to 0 so that it won't be deleted if
|
|
// this view container is destroyed.
|
|
//
|
|
// FIXME: Resolve this properly
|
|
drag->setParent(0);
|
|
removeView(view);
|
|
}
|
|
}
|
|
void TabbedViewContainerV2::tabDoubleClicked(int tab)
|
|
{
|
|
viewProperties( views()[tab] )->rename();
|
|
}
|
|
void TabbedViewContainerV2::moveViewWidget( int fromIndex , int toIndex )
|
|
{
|
|
QString text = _tabBar->tabText(fromIndex);
|
|
QIcon icon = _tabBar->tabIcon(fromIndex);
|
|
|
|
// FIXME - This will lose properties of the tab other than
|
|
// their text and icon when moving them
|
|
|
|
_tabBar->removeTab(fromIndex);
|
|
_tabBar->insertTab(toIndex,icon,text);
|
|
|
|
QWidget* widget = _stackWidget->widget(fromIndex);
|
|
_stackWidget->removeWidget(widget);
|
|
_stackWidget->insertWidget(toIndex,widget);
|
|
}
|
|
void TabbedViewContainerV2::currentTabChanged(int index)
|
|
{
|
|
_stackWidget->setCurrentIndex(index);
|
|
if (_stackWidget->widget(index))
|
|
emit activeViewChanged(_stackWidget->widget(index));
|
|
|
|
// clear activity indicators
|
|
setTabActivity(index,false);
|
|
}
|
|
|
|
void TabbedViewContainerV2::wheelScrolled(int delta)
|
|
{
|
|
if ( delta < 0 )
|
|
activateNextView();
|
|
else
|
|
activatePreviousView();
|
|
}
|
|
|
|
QWidget* TabbedViewContainerV2::containerWidget() const
|
|
{
|
|
return _containerWidget;
|
|
}
|
|
QWidget* TabbedViewContainerV2::activeView() const
|
|
{
|
|
return _stackWidget->currentWidget();
|
|
}
|
|
void TabbedViewContainerV2::setActiveView(QWidget* view)
|
|
{
|
|
const int index = _stackWidget->indexOf(view);
|
|
|
|
Q_ASSERT( index != -1 );
|
|
|
|
_stackWidget->setCurrentWidget(view);
|
|
_tabBar->setCurrentIndex(index);
|
|
}
|
|
void TabbedViewContainerV2::addViewWidget( QWidget* view , int index)
|
|
{
|
|
_stackWidget->insertWidget(index,view);
|
|
_stackWidget->updateGeometry();
|
|
|
|
ViewProperties* item = viewProperties(view);
|
|
connect( item , SIGNAL(titleChanged(ViewProperties*)) , this ,
|
|
SLOT(updateTitle(ViewProperties*)));
|
|
connect( item , SIGNAL(iconChanged(ViewProperties*) ) , this ,
|
|
SLOT(updateIcon(ViewProperties*)));
|
|
connect( item , SIGNAL(activity(ViewProperties*)) , this ,
|
|
SLOT(updateActivity(ViewProperties*)));
|
|
|
|
_tabBar->insertTab( index , item->icon() , item->title() );
|
|
|
|
if ( navigationDisplayMode() == ShowNavigationAsNeeded )
|
|
dynamicTabBarVisibility();
|
|
}
|
|
void TabbedViewContainerV2::removeViewWidget( QWidget* view )
|
|
{
|
|
const int index = _stackWidget->indexOf(view);
|
|
|
|
Q_ASSERT( index != -1 );
|
|
|
|
_stackWidget->removeWidget(view);
|
|
_tabBar->removeTab(index);
|
|
|
|
if ( navigationDisplayMode() == ShowNavigationAsNeeded )
|
|
dynamicTabBarVisibility();
|
|
}
|
|
|
|
void TabbedViewContainerV2::setTabActivity(int index , bool activity)
|
|
{
|
|
const QPalette& palette = _tabBar->palette();
|
|
KColorScheme colorScheme(palette.currentColorGroup());
|
|
const QColor colorSchemeActive = colorScheme.foreground(KColorScheme::ActiveText).color();
|
|
|
|
const QColor normalColor = palette.text().color();
|
|
const QColor activityColor = KColorUtils::mix(normalColor,colorSchemeActive);
|
|
|
|
QColor color = activity ? activityColor : QColor();
|
|
|
|
if ( color != _tabBar->tabTextColor(index) )
|
|
_tabBar->setTabTextColor(index,color);
|
|
}
|
|
|
|
void TabbedViewContainerV2::updateActivity(ViewProperties* item)
|
|
{
|
|
QListIterator<QWidget*> iter(widgetsForItem(item));
|
|
while ( iter.hasNext() )
|
|
{
|
|
const int index = _stackWidget->indexOf(iter.next());
|
|
|
|
if ( index != _stackWidget->currentIndex() )
|
|
{
|
|
setTabActivity(index,true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TabbedViewContainerV2::updateTitle(ViewProperties* item)
|
|
{
|
|
// prevent tab titles from becoming overly-long as this limits the number
|
|
// of tabs which can fit in the tab bar.
|
|
//
|
|
// if the view's title is overly long then trim it and select the
|
|
// right-most 20 characters (assuming they contain the most useful
|
|
// information) and insert an elide at the front
|
|
const int MAX_TAB_TEXT_LENGTH = 20;
|
|
|
|
QListIterator<QWidget*> iter(widgetsForItem(item));
|
|
while ( iter.hasNext() )
|
|
{
|
|
const int index = _stackWidget->indexOf( iter.next() );
|
|
|
|
QString tabText = item->title();
|
|
if (tabText.count() > MAX_TAB_TEXT_LENGTH)
|
|
tabText = tabText.right(MAX_TAB_TEXT_LENGTH).prepend("...");
|
|
|
|
_tabBar->setTabText( index , tabText );
|
|
}
|
|
}
|
|
void TabbedViewContainerV2::updateIcon(ViewProperties* item)
|
|
{
|
|
QListIterator<QWidget*> iter(widgetsForItem(item));
|
|
while ( iter.hasNext() )
|
|
{
|
|
const int index = _stackWidget->indexOf( iter.next() );
|
|
_tabBar->setTabIcon( index , item->icon() );
|
|
}
|
|
}
|
|
|
|
StackedViewContainer::StackedViewContainer(QObject* parent)
|
|
: ViewContainer(NavigationPositionTop,parent)
|
|
{
|
|
_stackWidget = new QStackedWidget;
|
|
}
|
|
StackedViewContainer::~StackedViewContainer()
|
|
{
|
|
_stackWidget->deleteLater();
|
|
}
|
|
QWidget* StackedViewContainer::containerWidget() const
|
|
{
|
|
return _stackWidget;
|
|
}
|
|
QWidget* StackedViewContainer::activeView() const
|
|
{
|
|
return _stackWidget->currentWidget();
|
|
}
|
|
void StackedViewContainer::setActiveView(QWidget* view)
|
|
{
|
|
_stackWidget->setCurrentWidget(view);
|
|
}
|
|
void StackedViewContainer::addViewWidget( QWidget* view , int )
|
|
{
|
|
_stackWidget->addWidget(view);
|
|
}
|
|
void StackedViewContainer::removeViewWidget( QWidget* view )
|
|
{
|
|
const int index = _stackWidget->indexOf(view);
|
|
|
|
Q_ASSERT( index != -1);
|
|
|
|
_stackWidget->removeWidget(view);
|
|
}
|
|
|
|
ListViewContainer::ListViewContainer(NavigationPosition position,QObject* parent)
|
|
: ViewContainer(position,parent)
|
|
{
|
|
_splitter = new QSplitter;
|
|
_stackWidget = new QStackedWidget(_splitter);
|
|
_listWidget = new ProfileListWidget(_splitter);
|
|
|
|
// elide left is used because the most informative part of the session name is often
|
|
// the rightmost part
|
|
//
|
|
// this means you get entries looking like:
|
|
//
|
|
// ...dirA ...dirB ...dirC ( helpful )
|
|
//
|
|
// instead of
|
|
//
|
|
// johnSmith@comput... johnSmith@comput... ( not so helpful )
|
|
//
|
|
|
|
_listWidget->setTextElideMode( Qt::ElideLeft );
|
|
_listWidget->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
|
|
_listWidget->setDragDropMode(QAbstractItemView::DragDrop);
|
|
_splitter->addWidget(_listWidget);
|
|
_splitter->addWidget(_stackWidget);
|
|
|
|
connect( _listWidget , SIGNAL(currentRowChanged(int)) , this , SLOT(rowChanged(int)) );
|
|
}
|
|
|
|
ListViewContainer::~ListViewContainer()
|
|
{
|
|
_splitter->deleteLater();
|
|
}
|
|
|
|
QWidget* ListViewContainer::containerWidget() const
|
|
{
|
|
return _splitter;
|
|
}
|
|
|
|
QWidget* ListViewContainer::activeView() const
|
|
{
|
|
return _stackWidget->currentWidget();
|
|
}
|
|
|
|
QBrush ListViewContainer::randomItemBackground(int r)
|
|
{
|
|
int i = r%6;
|
|
|
|
//and now for something truly unpleasant:
|
|
static const int r1[] = {255,190,190,255,190,255};
|
|
static const int r2[] = {255,180,180,255,180,255};
|
|
static const int b1[] = {190,255,190,255,255,190};
|
|
static const int b2[] = {180,255,180,255,255,180};
|
|
static const int g1[] = {190,190,255,190,255,255};
|
|
static const int g2[] = {180,180,255,180,255,255};
|
|
|
|
// hardcoded assumes item height is 32px
|
|
QLinearGradient gradient( QPoint(0,0) , QPoint(0,32) );
|
|
gradient.setColorAt(0,QColor(r1[i],g1[i],b1[i],100));
|
|
gradient.setColorAt(0.5,QColor(r2[i],g2[i],b2[i],100));
|
|
gradient.setColorAt(1,QColor(r1[i],g1[i],b1[i],100));
|
|
return QBrush(gradient);
|
|
}
|
|
|
|
void ListViewContainer::addViewWidget( QWidget* view , int )
|
|
{
|
|
_stackWidget->addWidget(view);
|
|
|
|
ViewProperties* properties = viewProperties(view);
|
|
|
|
QListWidgetItem* item = new QListWidgetItem(_listWidget);
|
|
item->setText( properties->title() );
|
|
item->setIcon( properties->icon() );
|
|
|
|
const int randomIndex = _listWidget->count();
|
|
item->setData( Qt::BackgroundRole , randomItemBackground(randomIndex) );
|
|
|
|
connect( properties , SIGNAL(titleChanged(ViewProperties*)) , this , SLOT(updateTitle(ViewProperties*)));
|
|
connect( properties , SIGNAL(iconChanged(ViewProperties*)) , this , SLOT(updateIcon(ViewProperties*)));
|
|
}
|
|
|
|
void ListViewContainer::removeViewWidget( QWidget* view )
|
|
{
|
|
int index = _stackWidget->indexOf(view);
|
|
_stackWidget->removeWidget(view);
|
|
delete _listWidget->takeItem( index );
|
|
}
|
|
|
|
void ListViewContainer::setActiveView( QWidget* view )
|
|
{
|
|
_stackWidget->setCurrentWidget(view);
|
|
_listWidget->setCurrentRow(_stackWidget->indexOf(view));
|
|
}
|
|
|
|
void ListViewContainer::rowChanged( int row )
|
|
{
|
|
// row may be -1 if the last row has been removed from the model
|
|
if ( row >= 0 )
|
|
{
|
|
_stackWidget->setCurrentIndex( row );
|
|
|
|
emit activeViewChanged( _stackWidget->currentWidget() );
|
|
}
|
|
}
|
|
|
|
void ListViewContainer::updateTitle( ViewProperties* properties )
|
|
{
|
|
QList<QWidget*> items = widgetsForItem(properties);
|
|
QListIterator<QWidget*> itemIter(items);
|
|
|
|
while ( itemIter.hasNext() )
|
|
{
|
|
int index = _stackWidget->indexOf( itemIter.next() );
|
|
_listWidget->item( index )->setText( properties->title() );
|
|
}
|
|
}
|
|
|
|
void ListViewContainer::updateIcon( ViewProperties* properties )
|
|
{
|
|
QList<QWidget*> items = widgetsForItem(properties);
|
|
QListIterator<QWidget*> itemIter(items);
|
|
|
|
while ( itemIter.hasNext() )
|
|
{
|
|
int index = _stackWidget->indexOf( itemIter.next() );
|
|
_listWidget->item( index )->setIcon( properties->icon() );
|
|
}
|
|
}
|
|
|
|
#include "ViewContainer.moc"
|