/*
* 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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
# include <QPoint>
# include <QPointF>
# include <QPainterPath>
# include "UBDockPalette.h"
# include "core/UBSettings.h"
# include "frameworks/UBPlatformUtils.h"
# include "core/UBApplication.h"
# include "core/UBPreferencesController.h"
# include "core/memcheck.h"
/**
* \ brief The constructor
*/
UBDockPalette : : UBDockPalette ( QWidget * parent , const char * name )
: QWidget ( parent , Qt : : FramelessWindowHint | Qt : : X11BypassWindowManagerHint )
, mOrientation ( eUBDockOrientation_Left )
, mPreferredWidth ( 100 )
, mPreferredHeight ( 100 )
, mCanResize ( false )
, mResized ( false )
, mCollapseWidth ( 150 )
, mLastWidth ( - 1 )
, mHTab ( 0 )
, mpStackWidget ( NULL )
, mpLayout ( NULL )
, mCurrentTab ( 0 )
{
setObjectName ( name ) ;
mpLayout = new QVBoxLayout ( ) ;
setLayout ( mpLayout ) ;
mpStackWidget = new QStackedWidget ( this ) ;
mpLayout - > addWidget ( mpStackWidget ) ;
// clear the tab widgets
mTabWidgets . clear ( ) ;
// We let 2 pixels in order to keep a small border for the resizing
setMinimumWidth ( 2 * border ( ) + 2 ) ;
if ( parent )
{
setAttribute ( Qt : : WA_NoMousePropagation ) ;
setAttribute ( Qt : : WA_TranslucentBackground ) ;
}
else
{
// standalone window
setAttribute ( Qt : : WA_TranslucentBackground ) ;
// !!!! Should be included into Windows after QT recompilation
# ifdef Q_WS_MAC
//setAttribute(Qt::WA_MacNoShadow);
# endif
}
mBackgroundBrush = QBrush ( UBSettings : : paletteColor ) ;
// This is the only way to set the background as transparent!
setStyleSheet ( " QWidget {background-color: transparent} " ) ;
// Set the position of the tab
onToolbarPosUpdated ( ) ;
connect ( UBSettings : : settings ( ) - > appToolBarPositionedAtTop , SIGNAL ( changed ( QVariant ) ) , this , SLOT ( onToolbarPosUpdated ( ) ) ) ;
}
/**
* \ brief The destructor
*/
UBDockPalette : : ~ UBDockPalette ( )
{
if ( NULL ! = mpStackWidget )
{
delete mpStackWidget ;
mpStackWidget = NULL ;
}
if ( NULL ! = mpLayout )
{
delete mpLayout ;
mpLayout = NULL ;
}
}
/**
* \ brief Get the current orientation
* @ return the current orientation
*/
eUBDockOrientation UBDockPalette : : orientation ( )
{
return mOrientation ;
}
/**
* \ brief Set the current orientation
* @ param orientation as the given orientation
*/
void UBDockPalette : : setOrientation ( eUBDockOrientation orientation )
{
// Set the size
mOrientation = orientation ;
if ( orientation = = eUBDockOrientation_Left | | orientation = = eUBDockOrientation_Right )
{
setMaximumHeight ( parentWidget ( ) - > height ( ) ) ;
setMinimumHeight ( maximumHeight ( ) ) ;
}
else if ( orientation = = eUBDockOrientation_Top | | orientation = = eUBDockOrientation_Bottom )
{
setMaximumWidth ( parentWidget ( ) - > width ( ) ) ;
setMinimumWidth ( maximumWidth ( ) ) ;
}
}
/**
* \ brief Handle the mouse move event
* @ param as the mouse event
*/
void UBDockPalette : : mouseMoveEvent ( QMouseEvent * event )
{
QPoint p = event - > pos ( ) ;
if ( mCanResize & & ( ( mMousePressPos - p ) . manhattanLength ( ) > QApplication : : startDragDistance ( ) ) )
{
switch ( mOrientation )
{
case eUBDockOrientation_Left :
if ( p . x ( ) < collapseWidth ( ) & & p . x ( ) > = minimumWidth ( ) )
{
resize ( border ( ) , height ( ) ) ;
mLastWidth = collapseWidth ( ) + 1 ;
mResized = true ;
}
else if ( p . x ( ) < = maximumWidth ( ) & & p . x ( ) > = minimumWidth ( ) )
{
resize ( p . x ( ) , height ( ) ) ;
mResized = true ;
}
break ;
case eUBDockOrientation_Right :
if ( ( this - > x ( ) + p . x ( ) > parentWidget ( ) - > width ( ) - collapseWidth ( ) ) & & ( this - > x ( ) + p . x ( ) < parentWidget ( ) - > width ( ) ) )
{
resize ( border ( ) , height ( ) ) ;
mLastWidth = collapseWidth ( ) + 1 ;
mResized = true ;
}
else if ( ( this - > x ( ) + p . x ( ) > = parentWidget ( ) - > width ( ) - maximumWidth ( ) ) & & ( this - > x ( ) + p . x ( ) < = parentWidget ( ) - > width ( ) - this - > minimumWidth ( ) ) )
{
resize ( parentWidget ( ) - > width ( ) - ( this - > x ( ) + p . x ( ) ) , height ( ) ) ;
mResized = true ;
}
break ;
case eUBDockOrientation_Top :
case eUBDockOrientation_Bottom :
if ( p . y ( ) < = maximumHeight ( ) )
{
resize ( width ( ) , p . y ( ) ) ;
mResized = true ;
}
break ;
default :
break ;
}
}
}
/**
* \ brief Handle the mouse press event
* @ param as the mouse event
*/
void UBDockPalette : : mousePressEvent ( QMouseEvent * event )
{
mClickTime = QTime : : currentTime ( ) ;
// The goal here is to verify if the user can resize the widget.
// It is only possible to resize it if the border is selected
QPoint p = event - > pos ( ) ;
mMousePressPos = p ;
mResized = false ;
switch ( mOrientation )
{
case eUBDockOrientation_Left :
if ( ( p . x ( ) > = width ( ) - 2 * border ( ) ) & & ( p . x ( ) < = width ( ) ) )
{
mCanResize = true ;
}
break ;
case eUBDockOrientation_Right :
if ( ( p . x ( ) > = 0 ) & & ( p . x ( ) < = 2 * border ( ) ) )
{
mCanResize = true ;
}
break ;
case eUBDockOrientation_Top :
// Not supported yet
break ;
case eUBDockOrientation_Bottom :
// Not supported yet
break ;
default :
break ;
}
}
/**
* \ brief Handle the mouse release event
* @ param event as the mouse event
*/
void UBDockPalette : : mouseReleaseEvent ( QMouseEvent * event )
{
Q_UNUSED ( event ) ;
if ( ! mResized & & mClickTime . elapsed ( ) < CLICKTIME )
{
int nbTabs = mTabWidgets . size ( ) ;
int clickedTab = 0 ;
// If the clicked position is in the tab, perform the related action
if ( eUBDockOrientation_Left = = mOrientation )
{
if ( mMousePressPos . x ( ) > = width ( ) - 2 * border ( ) & &
mMousePressPos . y ( ) > = mHTab & &
mMousePressPos . x ( ) < = width ( ) & &
mMousePressPos . y ( ) < = mHTab + nbTabs * TABSIZE + ( nbTabs - 1 ) * tabSpacing ( ) )
{
clickedTab = ( mMousePressPos . y ( ) - mHTab ) / ( TABSIZE + tabSpacing ( ) ) ;
tabClicked ( clickedTab ) ;
}
}
else if ( eUBDockOrientation_Right = = mOrientation )
{
if ( mMousePressPos . x ( ) > = 0 & &
mMousePressPos . x ( ) < = 2 * border ( ) & &
mMousePressPos . y ( ) > = mHTab & &
mMousePressPos . y ( ) < = mHTab + nbTabs * TABSIZE + ( nbTabs - 1 ) * tabSpacing ( ) )
{
clickedTab = ( mMousePressPos . y ( ) - mHTab ) / ( TABSIZE + tabSpacing ( ) ) ;
tabClicked ( clickedTab ) ;
}
}
}
mCanResize = false ;
}
void UBDockPalette : : resizeEvent ( QResizeEvent * event )
{
Q_UNUSED ( event ) ;
updateMaxWidth ( ) ;
if ( parentWidget ( ) )
{
setMinimumHeight ( parentWidget ( ) - > height ( ) ) ;
}
// Set the position
QPoint origin ;
switch ( mOrientation )
{
case eUBDockOrientation_Right :
origin . setX ( parentWidget ( ) - > width ( ) - this - > width ( ) ) ;
origin . setY ( 0 ) ;
break ;
case eUBDockOrientation_Bottom :
// Not supported yet
case eUBDockOrientation_Top :
// Not supported yet
case eUBDockOrientation_Left :
default :
origin . setX ( 0 ) ;
origin . setY ( 0 ) ;
break ;
}
move ( origin . x ( ) , origin . y ( ) ) ;
}
/**
* \ brief Handle the mouse enter event
* @ param event as the mouse event
*/
void UBDockPalette : : enterEvent ( QEvent * event )
{
Q_UNUSED ( event ) ;
// We want to set the cursor as an arrow everytime it enters the palette
setCursor ( Qt : : ArrowCursor ) ;
}
/**
* \ brief Handle the mouse leave event
* @ param event as the mouse event
*/
void UBDockPalette : : leaveEvent ( QEvent * event )
{
Q_UNUSED ( event ) ;
// Restore the cursor to its previous shape
unsetCursor ( ) ;
}
/**
* \ brief Draw the palette
* @ param event as the paint event
*/
void UBDockPalette : : paintEvent ( QPaintEvent * event )
{
Q_UNUSED ( event ) ;
QPainter painter ( this ) ;
painter . setRenderHint ( QPainter : : Antialiasing ) ;
painter . setPen ( Qt : : NoPen ) ;
painter . setBrush ( mBackgroundBrush ) ;
QPainterPath path ;
path . setFillRule ( Qt : : WindingFill ) ;
int nbTabs = mTabWidgets . size ( ) ;
if ( 0 < nbTabs )
{
// First draw the BIG RECTANGLE (I write it big because the rectangle is big...)
if ( mOrientation = = eUBDockOrientation_Left )
{
path . addRect ( 0.0 , 0.0 , width ( ) - 2 * border ( ) , height ( ) ) ;
}
else if ( mOrientation = = eUBDockOrientation_Right )
{
path . addRect ( 2 * border ( ) , 0.0 , width ( ) - 2 * border ( ) , height ( ) ) ;
}
// THEN DRAW THE small tabs (yes, the tabs are small...)
if ( eUBDockTabOrientation_Up = = mTabsOrientation )
{
mHTab = border ( ) ;
}
else
{
mHTab = height ( ) - border ( ) - nbTabs * TABSIZE - ( nbTabs - 1 ) * tabSpacing ( ) ;
}
for ( int i = 0 ; i < mTabWidgets . size ( ) ; i + + )
{
UBDockPaletteWidget * pCrntWidget = mTabWidgets . at ( i ) ;
if ( NULL ! = pCrntWidget )
{
if ( mOrientation = = eUBDockOrientation_Left )
{
path . addRoundedRect ( width ( ) - 4 * border ( ) , mHTab + i * TABSIZE + i * tabSpacing ( ) , 4 * border ( ) , TABSIZE , radius ( ) , radius ( ) ) ;
painter . drawPath ( path ) ;
QPixmap iconPixmap ;
if ( mCollapseWidth > = width ( ) )
{
// Get the collapsed icon
iconPixmap = pCrntWidget - > iconToRight ( ) ;
}
else
{
// Get the expanded icon
iconPixmap = pCrntWidget - > iconToLeft ( ) ;
}
painter . drawPixmap ( width ( ) - 2 * border ( ) + 1 , mHTab + i * TABSIZE + i * tabSpacing ( ) + 1 , 2 * border ( ) - 4 , TABSIZE - 2 , iconPixmap ) ;
}
else if ( mOrientation = = eUBDockOrientation_Right )
{
path . addRoundedRect ( 0.0 , mHTab + i * TABSIZE + i * tabSpacing ( ) , 4 * border ( ) , TABSIZE , radius ( ) , radius ( ) ) ;
painter . drawPath ( path ) ;
QPixmap iconPixmap ;
if ( mCollapseWidth > = width ( ) )
{
// Get the collapsed icon
iconPixmap = pCrntWidget - > iconToLeft ( ) ;
}
else
{
// Get the expanded icon
iconPixmap = pCrntWidget - > iconToRight ( ) ;
}
painter . drawPixmap ( 2 , mHTab + i * TABSIZE + i * tabSpacing ( ) + 1 , 2 * border ( ) - 4 , TABSIZE - 2 , iconPixmap ) ;
}
else
{
painter . drawRoundedRect ( border ( ) , border ( ) , width ( ) - 2 * border ( ) , height ( ) - 2 * border ( ) , radius ( ) , radius ( ) ) ;
}
}
}
}
}
/**
* \ brief Set the background brush
* @ param brush as the given brush
*/
void UBDockPalette : : setBackgroundBrush ( const QBrush & brush )
{
if ( mBackgroundBrush ! = brush )
{
mBackgroundBrush = brush ;
update ( ) ;
}
}
/**
* Get the border size
* @ return the border size
*/
int UBDockPalette : : border ( )
{
return 15 ;
}
/**
* \ brief Get the radius size
* @ return the radius size
*/
int UBDockPalette : : radius ( )
{
return 5 ;
}
/**
* \ brief Set the maximum width
*/
void UBDockPalette : : updateMaxWidth ( )
{
// Only the inherited class will overload this method
}
/**
* \ brief Get the collapse width value
* @ return the collapse widht value
*/
int UBDockPalette : : collapseWidth ( )
{
return mCollapseWidth ;
}
/**
* \ brief collapse / expand automatically the palette after a click on its tab
*/
void UBDockPalette : : tabClicked ( int tabIndex )
{
// If the current tab is not the clicked one, show its content
if ( mCurrentTab ! = tabIndex )
{
showTabWidget ( tabIndex ) ;
}
// else collapse the palette
else
{
toggleCollapseExpand ( ) ;
}
}
void UBDockPalette : : showTabWidget ( int tabIndex )
{
mpStackWidget - > setCurrentIndex ( tabIndex ) ;
mCurrentTab = tabIndex ;
// Expand it if collapsed
if ( mLastWidth ! = - 1 )
{
toggleCollapseExpand ( ) ;
}
}
void UBDockPalette : : toggleCollapseExpand ( )
{
if ( mLastWidth = = - 1 )
{
// The palette must be collapsed
mLastWidth = width ( ) ;
resize ( 2 * border ( ) , height ( ) ) ;
}
else
{
// The palette will be expanded
resize ( mLastWidth , height ( ) ) ;
mLastWidth = - 1 ;
}
}
void UBDockPalette : : setTabsOrientation ( eUBDockTabOrientation orientation )
{
mTabsOrientation = orientation ;
}
void UBDockPalette : : onToolbarPosUpdated ( )
{
// Get the position of the tab
if ( UBSettings : : settings ( ) - > appToolBarPositionedAtTop - > get ( ) . toBool ( ) )
{
setTabsOrientation ( eUBDockTabOrientation_Up ) ;
}
else
{
setTabsOrientation ( eUBDockTabOrientation_Down ) ;
}
update ( ) ;
}
int UBDockPalette : : customMargin ( )
{
return 5 ;
}
void UBDockPalette : : addTabWidget ( UBDockPaletteWidget * widget )
{
if ( ! mTabWidgets . contains ( widget ) )
{
widget - > setVisible ( true ) ;
mTabWidgets . append ( widget ) ;
mpStackWidget - > addWidget ( widget ) ;
mpStackWidget - > setCurrentWidget ( widget ) ;
update ( ) ;
}
}
void UBDockPalette : : removeTab ( const QString & widgetName )
{
for ( int i = 0 ; i < mTabWidgets . size ( ) ; i + + )
{
UBDockPaletteWidget * pCrntWidget = mTabWidgets . at ( i ) ;
if ( NULL ! = pCrntWidget & & ( pCrntWidget - > name ( ) = = widgetName ) )
{
mpStackWidget - > removeWidget ( pCrntWidget ) ;
mTabWidgets . remove ( i ) ;
pCrntWidget - > hide ( ) ;
update ( ) ;
break ;
}
}
}
void UBDockPalette : : onResizeRequest ( QResizeEvent * event )
{
resizeEvent ( event ) ;
}
int UBDockPalette : : tabSpacing ( )
{
return 2 ;
}
// This method is used to show the tab widget
void UBDockPalette : : onShowTabWidget ( const QString & widgetName )
{
for ( int i = 0 ; i < mRegisteredWidgets . size ( ) ; i + + )
{
UBDockPaletteWidget * pCrntWidget = mRegisteredWidgets . at ( i ) ;
if ( NULL ! = pCrntWidget & & ( pCrntWidget - > name ( ) = = widgetName ) )
{
addTabWidget ( pCrntWidget ) ;
break ;
}
}
}
// This method is used to hide the tab widget
void UBDockPalette : : onHideTabWidget ( const QString & widgetName )
{
removeTab ( widgetName ) ;
}
void UBDockPalette : : connectSignals ( )
{
for ( int i = 0 ; i < mRegisteredWidgets . size ( ) ; i + + )
{
connect ( mRegisteredWidgets . at ( i ) , SIGNAL ( showTab ( QString ) ) , this , SLOT ( onShowTabWidget ( QString ) ) ) ;
connect ( mRegisteredWidgets . at ( i ) , SIGNAL ( hideTab ( QString ) ) , this , SLOT ( onHideTabWidget ( QString ) ) ) ;
}
}
void UBDockPalette : : registerWidget ( UBDockPaletteWidget * widget )
{
if ( ! mRegisteredWidgets . contains ( widget ) )
{
mRegisteredWidgets . append ( widget ) ;
// By default, the widget is hidden
widget - > hide ( ) ;
}
}