/*****************************************************************
Copyright ( c ) 2000 Matthias Elter
Permission is hereby granted , free of charge , to any person obtaining a copy
of this software and associated documentation files ( the " Software " ) , to deal
in the Software without restriction , including without limitation the rights
to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
copies of the Software , and to permit persons to whom the Software is
furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN
AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# undef Bool // For enable-final
# include <tdelocale.h>
# include <twinmodule.h>
# include <kdebug.h>
# include <tdeconfig.h>
# include <kprocess.h>
# include <kshell.h>
# include <twin.h>
# include <kstandarddirs.h>
# include <tdemessagebox.h>
# include <tdeapplication.h>
# include <dcopclient.h>
# include <tdeglobal.h>
# include "dockbarextension.h"
# include "dockbarextension.moc"
# include <X11/X.h>
# include <X11/Xlib.h>
# include <X11/Xutil.h>
extern " C "
{
KDE_EXPORT KPanelExtension * init ( TQWidget * parent , const TQString & configFile )
{
TDEGlobal : : locale ( ) - > insertCatalogue ( " dockbarextension " ) ;
return new DockBarExtension ( configFile , KPanelExtension : : Normal ,
0 , parent , " dockbarextension " ) ;
}
}
DockBarExtension : : DockBarExtension ( const TQString & configFile , Type type ,
int actions , TQWidget * parent , const char * name )
: KPanelExtension ( configFile , type , actions , parent , name )
{
dragging_container = 0 ;
twin_module = new KWinModule ( TQT_TQOBJECT ( this ) ) ;
connect ( twin_module , TQT_SIGNAL ( windowAdded ( WId ) ) , TQT_SLOT ( windowAdded ( WId ) ) ) ;
setMinimumSize ( DockContainer : : sz ( ) , DockContainer : : sz ( ) ) ;
setSizePolicy ( TQSizePolicy : : Expanding , TQSizePolicy : : Expanding ) ;
loadContainerConfig ( ) ;
}
DockBarExtension : : ~ DockBarExtension ( )
{
// kill nicely the applets
for ( DockContainer : : Vector : : const_iterator it = containers . constBegin ( ) ;
it ! = containers . constEnd ( ) ;
+ + it )
{
( * it ) - > kill ( ) ;
}
if ( dragging_container ) delete dragging_container ;
}
TQSize DockBarExtension : : sizeHint ( Position p , TQSize ) const
{
if ( p = = Left | | p = = Right )
return TQSize ( DockContainer : : sz ( ) , DockContainer : : sz ( ) * containers . count ( ) ) ;
else
return TQSize ( DockContainer : : sz ( ) * containers . count ( ) , DockContainer : : sz ( ) ) ;
}
void DockBarExtension : : resizeEvent ( TQResizeEvent * )
{
layoutContainers ( ) ;
}
void DockBarExtension : : windowAdded ( WId win )
{
// try to read WM_COMMAND
int argc ;
char * * argv ;
TQString command ;
if ( XGetCommand ( tqt_xdisplay ( ) , win , & argv , & argc ) ) {
command = KShell : : joinArgs ( argv , argc ) ;
XFreeStringList ( argv ) ;
}
// try to read wm hints
WId resIconwin = 0 ;
XWMHints * wmhints = XGetWMHints ( tqt_xdisplay ( ) , win ) ;
if ( 0 ! = wmhints ) { // we managed to read wm hints
// read IconWindowHint
bool is_valid = false ;
/* a good dockapp set the icon hint and the state hint,
if it uses its icon , the window initial state must be " withdrawn "
if not , then the initial state must be " normal "
this filters the problematic Eterm whose initial state is " normal "
and which has an iconwin .
*/
if ( ( wmhints - > flags & IconWindowHint ) & &
( wmhints - > flags & StateHint ) ) {
resIconwin = wmhints - > icon_window ;
is_valid = ( resIconwin & & wmhints - > initial_state = = 0 ) | |
( resIconwin = = 0 & & wmhints - > initial_state = = 1 ) ;
/* an alternative is a window who does not have an icon,
but whose initial state is set to " withdrawn " . This has been
added for wmxmms . . . I hope it won ' t swallow to much windows : - /
*/
} else if ( ( wmhints - > flags & IconWindowHint ) = = 0 & &
( wmhints - > flags & StateHint ) ) {
is_valid = ( wmhints - > initial_state = = 0 ) ;
}
XFree ( wmhints ) ;
if ( ! is_valid )
return ; // we won't swallow this one
}
else
return ;
// The following if statement was at one point commented out,
// without a comment as to why. This caused problems like
// Eterm windows getting swallowed whole. So, perhaps now I'll
// get bug reports about whatever commenting it out was supposed
// to fix.
if ( resIconwin = = 0 )
resIconwin = win ;
// try to read class hint
XClassHint hint ;
TQString resClass , resName ;
if ( XGetClassHint ( tqt_xdisplay ( ) , win , & hint ) ) {
resName = hint . res_name ;
resClass = hint . res_class ;
}
else {
kdDebug ( ) < < " Could not read XClassHint of window " < < win < < endl ;
return ;
}
/* withdrawing the window prevents twin from managing the window,
which causes the double - launch bug ( one instance from the twin
session , and one from the dockbar ) bug when kde is restarted */
if ( resIconwin ! = win ) {
XWithdrawWindow ( tqt_xdisplay ( ) , win , tqt_xscreen ( ) ) ;
while ( KWin : : windowInfo ( win , NET : : XAWMState ) . mappingState ( ) ! = NET : : Withdrawn ) ;
}
// add a container
embedWindow ( resIconwin , command , resName , resClass ) ;
saveContainerConfig ( ) ;
}
void DockBarExtension : : layoutContainers ( )
{
int i = 0 ;
for ( DockContainer : : Vector : : const_iterator it = containers . constBegin ( ) ;
it ! = containers . constEnd ( ) ;
+ + it )
{
if ( orientation ( ) = = Qt : : Horizontal )
( * it ) - > move ( DockContainer : : sz ( ) * i , 0 ) ;
else
( * it ) - > move ( 0 , DockContainer : : sz ( ) * i ) ;
i + + ;
}
}
void DockBarExtension : : embedWindow ( WId win , TQString command , TQString resName , TQString resClass )
{
if ( win = = 0 ) return ;
DockContainer * container = 0 ;
bool ncmd = false ;
for ( DockContainer : : Vector : : const_iterator it = containers . constBegin ( ) ;
it ! = containers . constEnd ( ) ;
+ + it )
{
DockContainer * c = * it ;
if ( c - > embeddedWinId ( ) = = 0 & &
c - > resName ( ) = = resName & &
c - > resClass ( ) = = resClass & &
( command . isNull ( ) | | c - > command ( ) = = command ) )
{
container = c ;
break ;
}
}
if ( container = = 0 ) {
TQString cmd = command . isNull ( ) ? resClass : command ;
if ( TDEStandardDirs : : findExe ( KShell : : splitArgs ( cmd ) . front ( ) ) . isEmpty ( ) )
ncmd = true ;
container = new DockContainer ( cmd , this , resName , resClass ) ;
addContainer ( container ) ;
}
container - > embed ( win ) ;
layoutContainers ( ) ;
emit updateLayout ( ) ;
if ( ncmd )
container - > askNewCommand ( ) ;
}
void DockBarExtension : : addContainer ( DockContainer * c , int pos )
{
if ( pos = = - 1 )
{
containers . append ( c ) ;
}
else
{
DockContainer : : Vector : : iterator it = containers . begin ( ) ;
for ( int i = 0 ; i < pos & & it ! = containers . end ( ) ; + + i )
{
+ + it ;
}
+ + it ;
containers . insert ( it , c ) ;
}
connect ( c , TQT_SIGNAL ( embeddedWindowDestroyed ( DockContainer * ) ) ,
TQT_SLOT ( embeddedWindowDestroyed ( DockContainer * ) ) ) ;
connect ( c , TQT_SIGNAL ( settingsChanged ( DockContainer * ) ) ,
TQT_SLOT ( settingsChanged ( DockContainer * ) ) ) ;
c - > resize ( DockContainer : : sz ( ) , DockContainer : : sz ( ) ) ;
c - > show ( ) ;
}
void DockBarExtension : : removeContainer ( DockContainer * c )
{
DockContainer : : Vector : : iterator it = tqFind ( containers . begin ( ) , containers . end ( ) , c ) ;
if ( it = = containers . end ( ) )
{
return ;
}
containers . erase ( it ) ;
delete c ;
layoutContainers ( ) ;
}
void DockBarExtension : : embeddedWindowDestroyed ( DockContainer * c )
{
removeContainer ( c ) ;
saveContainerConfig ( ) ;
emit updateLayout ( ) ;
}
void DockBarExtension : : settingsChanged ( DockContainer * )
{
saveContainerConfig ( ) ;
}
void DockBarExtension : : saveContainerConfig ( )
{
TQStringList applet_list ;
TDEConfig * conf = config ( ) ;
unsigned count = 0 ;
for ( DockContainer : : Vector : : const_iterator it = containers . constBegin ( ) ;
it ! = containers . constEnd ( ) ;
+ + it )
{
DockContainer * c = * it ;
if ( ! c - > command ( ) . isEmpty ( ) )
{
TQString applet_gid = TQString ( " Applet_%1 " ) . arg ( TQString : : number ( count ) ) ;
applet_list . append ( applet_gid ) ;
conf - > setGroup ( applet_gid ) ;
conf - > writePathEntry ( " Command " , c - > command ( ) ) ;
conf - > writePathEntry ( " resName " , c - > resName ( ) ) ;
conf - > writeEntry ( " resClass " , c - > resClass ( ) ) ;
+ + count ;
}
}
conf - > setGroup ( " General " ) ;
conf - > writeEntry ( " Applets " , applet_list ) ;
conf - > deleteEntry ( " Commands " ) ; // cleanup old config
conf - > sync ( ) ;
}
void DockBarExtension : : loadContainerConfig ( )
{
TDEConfig * conf = config ( ) ;
conf - > setGroup ( " General " ) ;
TQStringList applets = conf - > readListEntry ( " Applets " ) ;
TQStringList fail_list ;
for ( TQStringList : : Iterator it = applets . begin ( ) ; it ! = applets . end ( ) ; + + it ) {
if ( ! conf - > hasGroup ( * it ) ) continue ;
conf - > setGroup ( * it ) ;
TQString cmd = conf - > readPathEntry ( " Command " ) ;
TQString resName = conf - > readPathEntry ( " resName " ) ;
TQString resClass = conf - > readEntry ( " resClass " ) ;
if ( cmd . isEmpty ( ) | | resName . isEmpty ( ) | | resClass . isEmpty ( ) ) continue ;
DockContainer * c = new DockContainer ( cmd , this , resName , resClass ) ;
addContainer ( c ) ;
TDEProcess proc ;
proc < < KShell : : splitArgs ( cmd ) ;
if ( ! proc . start ( TDEProcess : : DontCare ) ) {
fail_list . append ( cmd ) ;
removeContainer ( c ) ;
}
}
if ( ! fail_list . isEmpty ( ) )
KMessageBox : : queuedMessageBox ( 0 , KMessageBox : : Information , i18n ( " The following dockbar applets could not be started: %1 " ) . arg ( fail_list . join ( " , " ) ) , i18n ( " kicker: information " ) , 0 ) ;
saveContainerConfig ( ) ;
}
int DockBarExtension : : findContainerAtPoint ( const TQPoint & p )
{
int i = 0 ;
for ( DockContainer : : Vector : : const_iterator it = containers . constBegin ( ) ;
it ! = containers . constEnd ( ) ;
+ + it , + + i )
{
if ( ( * it ) - > geometry ( ) . contains ( p ) )
{
return i ;
}
}
return - 1 ;
}
void DockBarExtension : : mousePressEvent ( TQMouseEvent * e ) {
if ( e - > button ( ) = = Qt : : LeftButton ) {
// Store the position of the mouse clic.
mclic_pos = e - > pos ( ) ;
} else if ( e - > button ( ) = = Qt : : RightButton ) {
int pos = findContainerAtPoint ( e - > pos ( ) ) ;
if ( pos ! = - 1 ) containers . at ( pos ) - > popupMenu ( e - > globalPos ( ) ) ;
}
}
void DockBarExtension : : mouseReleaseEvent ( TQMouseEvent * e ) {
if ( e - > button ( ) ! = Qt : : LeftButton ) return ;
if ( dragging_container ) {
releaseMouse ( ) ;
original_container - > embed ( dragging_container - > embeddedWinId ( ) ) ;
delete dragging_container ; dragging_container = 0 ;
layoutContainers ( ) ;
saveContainerConfig ( ) ;
}
}
void DockBarExtension : : mouseMoveEvent ( TQMouseEvent * e ) {
if ( ! ( e - > state ( ) & Qt : : LeftButton ) ) return ;
if ( dragging_container = = 0 ) {
// Check whether the user has moved far enough.
int delay = TQApplication : : startDragDistance ( ) ;
if ( ( mclic_pos - e - > pos ( ) ) . manhattanLength ( ) > delay ) {
int pos = findContainerAtPoint ( e - > pos ( ) ) ;
original_container = 0 ;
if ( pos > - 1 ) {
original_container = containers . at ( pos ) ;
mclic_dock_pos = e - > pos ( ) - original_container - > pos ( ) ;
dragged_container_original_pos = pos ;
dragging_container = new DockContainer ( original_container - > command ( ) , 0 , original_container - > resName ( ) , original_container - > resClass ( ) , true ) ;
dragging_container - > show ( ) ;
dragging_container - > embed ( original_container - > embeddedWinId ( ) ) ;
grabMouse ( ) ;
}
}
}
if ( dragging_container ) {
dragging_container - > move ( e - > globalPos ( ) - mclic_dock_pos ) ;
// change layout of other containers
TQPoint dragpos ( dragging_container - > pos ( ) ) ,
barpos ( mapToGlobal ( pos ( ) ) ) ;
int pdrag1 , pdrag2 , psz ;
pdrag1 = dragpos . x ( ) - barpos . x ( ) + DockContainer : : sz ( ) / 2 ;
pdrag2 = dragpos . y ( ) - barpos . y ( ) + DockContainer : : sz ( ) / 2 ;
if ( orientation ( ) = = Qt : : Vertical ) {
int tmp = pdrag1 ; pdrag1 = pdrag2 ; pdrag2 = tmp ;
psz = height ( ) ;
} else psz = width ( ) ;
if ( pdrag2 > = 0 & & pdrag2 < DockContainer : : sz ( ) & & pdrag1 > = 0 & & pdrag1 < psz )
pdrag1 / = DockContainer : : sz ( ) ;
else
pdrag1 = dragged_container_original_pos ;
DockContainer : : Vector : : iterator it = tqFind ( containers . begin ( ) , containers . end ( ) , original_container ) ;
if ( it = = containers . end ( ) )
{
return ;
}
DockContainer : : Vector : : iterator target = containers . begin ( ) ;
for ( int i = 0 ; i < pdrag1 & & target ! = containers . end ( ) ; + + i )
{
+ + target ;
}
containers . erase ( it ) ;
containers . insert ( target , original_container ) ;
layoutContainers ( ) ;
}
}