/***************************************************************************
* Copyright ( C ) 2004 by Hans Oischinger *
* hans . oischinger @ kde - mail . net *
* *
* 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 . , *
* 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "komposelayout.h"
# include "komposewidget.h"
# include "komposeviewmanager.h"
# include "komposetaskmanager.h"
# include "komposefullscreenwidget.h"
# include <math.h>
# include <twin.h>
# include <kdebug.h>
KomposeLayout : : KomposeLayout ( KomposeWidget * parent , int type , int dist , const char * name )
: TQObject ( parent , name ) ,
spacing ( dist ) ,
widgetsChanged ( false ) ,
currentRows ( 0 ) ,
currentColumns ( 0 ) ,
parentWidget ( parent )
{
setType ( type ) ;
currentSize = TQSize ( 1 , 1 ) ;
}
KomposeLayout : : ~ KomposeLayout ( )
{ }
void KomposeLayout : : setType ( int t )
{
/*
if ( t = = TLAYOUT_TASKCONTAINERS & &
! KomposeSettings : : instance ( ) - > getDynamicVirtDeskLayout ( ) )
t = TLAYOUT_GENERIC ;
layoutType = t ;
*/
layoutType = TLAYOUT_GENERIC ;
}
void KomposeLayout : : add ( KomposeWidget * w )
{
//kdDebug() << "KomposeLayout::add()@%s - Added widget to layout", parent()->className());
//kdDebug() << "KomposeLayout::add() - Added widget to layout");
list . append ( w ) ;
widgetsChanged = true ;
}
void KomposeLayout : : remove ( KomposeWidget * w )
{
list . remove ( w ) ;
widgetsChanged = true ;
}
void KomposeLayout : : arrangeLayout ( )
{
//kdDebug() << "KomposeLayout::arrangeLayout()");
rearrangeContents ( ) ;
}
void KomposeLayout : : rearrangeContents ( )
{
// Check or empty list
if ( list . count ( ) = = 0 )
{
kdDebug ( ) < < " KomposeLayout::rearrangeContents() - empty list... skipping! " < < endl ;
return ;
}
// Check for layout Type and do the work
if ( layoutType = = TLAYOUT_TASKCONTAINERS )
{
filledContainers . clear ( ) ;
emptyContainers . clear ( ) ;
// Check for empty containers
TQPtrListIterator < KomposeWidget > it ( list ) ;
KomposeWidget * task ;
while ( ( task = it . current ( ) ) ! = 0 )
{
+ + it ;
KomposeTaskContainerWidget * containerTask = dynamic_cast < KomposeTaskContainerWidget * > ( task ) ;
if ( containerTask - > getNumofChilds ( ) > 0 )
{
filledContainers . append ( containerTask ) ;
}
else
{
emptyContainers . append ( containerTask ) ;
}
}
// Arrange filled containers
TQRect filledRect ( 0 ,
0 ,
parentWidget - > width ( ) ,
parentWidget - > height ( ) - ( 40 + 2 * spacing ) ) ;
// arrange the filled desktops taking 90% of the screen
rearrangeContents ( filledRect , filledContainers ) ;
// Arrange empty containers
TQRect emptyRect ( 0 ,
parentWidget - > height ( ) - ( 40 + 2 * spacing ) ,
parentWidget - > width ( ) ,
( 40 + 2 * spacing ) ) ;
// arrange the empty widget in one row
rearrangeContents ( emptyRect , emptyContainers , 1 , - 1 , false ) ;
}
else // default type (generic)
{
TQRect availRect ( 0 ,
0 ,
parentWidget - > width ( ) ,
parentWidget - > height ( ) ) ;
rearrangeContents ( availRect , list ) ;
}
currentSize = parentWidget - > size ( ) ;
widgetsChanged = false ;
}
/**
* availRect specifies the size & pos of the contents
* Specify either rows or cols to set a fixed number of those ( setting both won ' t work correctly )
*/
void KomposeLayout : : rearrangeContents ( const TQRect & availRect , const TQPtrList < KomposeWidget > widgets , int rows , int columns , bool setMemberRowsCols )
{
// Check or empty list
if ( widgets . count ( ) = = 0 )
{
kdDebug ( ) < < " KomposeLayout::rearrangeContents() - empty list... skipping! " < < endl ;
return ;
}
TQPtrListIterator < KomposeWidget > it ( widgets ) ;
// Calculate grid's rows & cols
if ( rows ! = - 1 ) // rows have been specified
{
columns = ( int ) ceil ( widgets . count ( ) / rows ) ;
}
else if ( columns ! = - 1 ) // columns have been specified
{
rows = ( int ) ceil ( widgets . count ( ) / columns ) ;
}
else // neither rows nor cols have been specified
{
double parentRatio = ( double ) availRect . width ( ) / ( double ) availRect . height ( ) ;
// Use more columns than rows when parent's width > parent's height
if ( parentRatio > 1 )
{
columns = ( int ) ceil ( sqrt ( widgets . count ( ) ) ) ;
rows = ( int ) ceil ( ( double ) widgets . count ( ) / ( double ) columns ) ;
}
else
{
rows = ( int ) ceil ( sqrt ( widgets . count ( ) ) ) ;
columns = ( int ) ceil ( ( double ) widgets . count ( ) / ( double ) rows ) ;
}
}
kdDebug ( ) < < " KomposeLayout::rearrangeContents() - Relayouting " < < widgets . count ( ) < < " child widgets with " < < rows < < " rows & " < < columns < < " columns " < < endl ;
// Calculate width & height
int w = ( availRect . width ( ) - ( columns + 1 ) * spacing ) / columns ;
int h = ( availRect . height ( ) - ( rows + 1 ) * spacing ) / rows ;
TQValueList < TQRect > geometryRects ;
TQValueList < int > maxRowHeights ;
// Process rows
for ( int i = 0 ; i < rows ; + + i )
{
int xOffsetFromLastCol = 0 ;
int maxHeightInRow = 0 ;
// Process columns
for ( int j = 0 ; j < columns ; + + j )
{
KomposeWidget * task ;
// Check for end of List
if ( ( task = it . current ( ) ) = = 0 )
break ;
// Calculate width and height of widget
double ratio = task - > getAspectRatio ( ) ;
int widgetw = 100 ;
int widgeth = 100 ;
int usableW = w ;
int usableH = h ;
// use width of two boxes if there is no right neighbour
if ( it . atLast ( ) & & j ! = columns - 1 )
{
usableW = 2 * w ;
}
+ + it ; // We need access to the neighbour in the following
// expand if right neighbour has ratio < 1
if ( j ! = columns - 1 & & it . current ( ) & & it . current ( ) - > getAspectRatio ( ) < 1 )
{
int addW = w - it . current ( ) - > getWidthForHeight ( h ) ;
if ( addW > 0 )
{
usableW = w + addW ;
}
}
if ( ratio = = - 1 )
{
widgetw = w ;
widgeth = h ;
}
else
{
double widthForHeight = task - > getWidthForHeight ( usableH ) ;
double heightForWidth = task - > getHeightForWidth ( usableW ) ;
if ( ( ratio > = 1.0 & & heightForWidth < = usableH ) | |
( ratio < 1.0 & & widthForHeight > usableW ) )
{
widgetw = usableW ;
widgeth = ( int ) heightForWidth ;
}
else if ( ( ratio < 1.0 & & widthForHeight < = usableW ) | |
( ratio > = 1.0 & & heightForWidth > usableH ) )
{
widgeth = usableH ;
widgetw = ( int ) widthForHeight ;
}
}
// Set the Widget's size
int alignmentXoffset = 0 ;
int alignmentYoffset = 0 ;
if ( i = = 0 & & h > widgeth )
alignmentYoffset = h - widgeth ;
if ( j = = 0 & & w > widgetw )
alignmentXoffset = w - widgetw ;
TQRect geom ( availRect . x ( ) + j * ( w + spacing ) + spacing + alignmentXoffset + xOffsetFromLastCol ,
availRect . y ( ) + i * ( h + spacing ) + spacing + alignmentYoffset ,
widgetw , widgeth ) ;
geometryRects . append ( geom ) ;
// Set the x offset for the next column
if ( alignmentXoffset = = 0 )
xOffsetFromLastCol + = widgetw - w ;
if ( maxHeightInRow < widgeth )
maxHeightInRow = widgeth ;
}
maxRowHeights . append ( maxHeightInRow ) ;
}
it . toFirst ( ) ;
TQValueList < TQRect > : : iterator geomIt = geometryRects . begin ( ) ;
TQValueList < int > : : iterator maxRowHeightIt = maxRowHeights . begin ( ) ;
int topOffset = 0 ;
for ( int i = 0 ; i < rows ; + + i )
{
// Process columns again
for ( int j = 0 ; j < columns ; + + j )
{
KomposeWidget * task ;
if ( ( task = it . current ( ) ) = = 0 )
break ;
TQRect geom = * geomIt ;
geom . setY ( geom . y ( ) + topOffset ) ;
// geom.setHeight( geom.height() - topOffset );
task - > setGeometry ( geom ) ;
kdDebug ( ) < < " KomposeLayout::rearrangeContents() - Put item " < < task - > className ( ) < < " at x: " < < geom . x ( ) < < " y: " < < geom . y ( ) < < " with size: " < < geom . width ( ) < < " x " < < geom . height ( ) < < endl ;
+ + geomIt ;
+ + it ;
}
if ( * maxRowHeightIt - h > 0 )
topOffset + = * maxRowHeightIt - h ;
+ + maxRowHeightIt ;
}
// Sync cols/rows member vars to current cols/rows
if ( setMemberRowsCols )
{
currentRows = rows ;
currentColumns = columns ;
}
}
/*
* Search for neighbour ( called from outside )
*/
KomposeWidget * KomposeLayout : : getNeighbour ( const KomposeWidget * widget , int direction , int wrap )
{
kdDebug ( ) < < " KomposeLayout::getNeighbour() - Called with list.count: " < < list . count ( ) < < endl ;
if ( layoutType = = TLAYOUT_TASKCONTAINERS )
{
KomposeWidget * neighbour = 0 ;
if ( filledContainers . containsRef ( widget ) )
{
if ( ( neighbour = getNeighbour ( filledContainers , widget , direction , WLAYOUT_HORIZONTAL ) ) = = 0 )
return emptyContainers . first ( ) ;
}
else if ( emptyContainers . containsRef ( widget ) )
{
if ( ( neighbour = getNeighbour ( emptyContainers , widget , direction , WLAYOUT_HORIZONTAL ) ) = = 0 )
if ( direction = = DLAYOUT_TOP )
return filledContainers . last ( ) ;
else
return filledContainers . first ( ) ;
}
return neighbour ;
}
else if ( layoutType = = TLAYOUT_GENERIC )
return getNeighbour ( list , widget , direction , wrap ) ;
kdDebug ( ) < < " KomposeLayout::getNeighbour() - this should never happen! " < < endl ;
return NULL ;
}
/*
* Search for neighbour in the given list ( called from inside )
*/
KomposeWidget * KomposeLayout : : getNeighbour (
TQPtrList < KomposeWidget > listToSearch ,
const KomposeWidget * widget ,
int direction ,
int wrap )
{
TQPtrListIterator < KomposeWidget > it ( listToSearch ) ;
KomposeWidget * task ;
KomposeWidget * neighbour ;
int index = 0 ;
if ( widget = = 0 )
{
kdDebug ( ) < < " KomposeLayout::getNeighbour() - NULL startWidget given. using first() " < < endl ;
return listToSearch . first ( ) ;
}
while ( ( task = it . current ( ) ) ! = 0 )
{
if ( task = = widget )
{
switch ( direction )
{
case DLAYOUT_RIGHT :
+ + it ;
if ( ( index ) % currentColumns = = currentColumns - 1 | | ( neighbour = it . current ( ) ) = = 0 )
{
if ( wrap = = WLAYOUT_HORIZONTAL | | wrap = = WLAYOUT_BOTH )
return listToSearch . at ( index + 1 - currentColumns ) ;
kdDebug ( ) < < " KomposeLayout::getNeighbour() - No valid neighbour available " < < endl ;
return NULL ;
}
return neighbour ;
case DLAYOUT_LEFT :
- - it ;
if ( index % currentColumns = = 0 | | ( neighbour = it . current ( ) ) = = 0 )
{
if ( wrap = = WLAYOUT_HORIZONTAL | | wrap = = WLAYOUT_BOTH )
if ( ( uint ) ( index + currentColumns - 1 ) < listToSearch . count ( ) )
return listToSearch . at ( index + currentColumns - 1 ) ;
else
return listToSearch . last ( ) ;
kdDebug ( ) < < " KomposeLayout::getNeighbour() - No valid neighbour available " < < endl ;
return NULL ;
}
return neighbour ;
case DLAYOUT_TOP :
if ( index < currentColumns | | ( neighbour = listToSearch . at ( index - currentColumns ) ) = = 0 )
{
if ( wrap = = WLAYOUT_VERTICAL | | wrap = = WLAYOUT_BOTH )
if ( listToSearch . count ( ) % currentColumns = = 0 | | listToSearch . count ( ) % currentColumns > ( uint ) ( index ) )
return listToSearch . at ( ( currentRows - 1 ) * currentColumns + index ) ;
else
return listToSearch . at ( ( currentRows - 2 ) * currentColumns + index ) ;
kdDebug ( ) < < " KomposeLayout::getNeighbour() - No valid neighbour available " < < endl ;
return NULL ;
}
return neighbour ;
case DLAYOUT_BOTTOM :
if ( listToSearch . count ( ) < = ( uint ) ( index + currentColumns ) | | ( neighbour = listToSearch . at ( index + currentColumns ) ) = = 0 )
{
if ( wrap = = WLAYOUT_VERTICAL | | wrap = = WLAYOUT_BOTH )
return listToSearch . at ( index % currentColumns ) ;
kdDebug ( ) < < " KomposeLayout::getNeighbour() - No valid neighbour available " < < endl ;
return NULL ;
}
return neighbour ;
}
}
+ + index ;
+ + it ;
}
kdDebug ( ) < < " KomposeLayout::getNeighbour() - this should never happen! " < < endl ;
return NULL ;
}
# include "komposelayout.moc"