diff --git a/geometry.cpp b/geometry.cpp index 3afe51bded..1accbe9746 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -82,6 +82,15 @@ void Workspace::updateClientArea( bool force ) else new_areas[ (*it)->desktop() ] = new_areas[ (*it)->desktop() ].intersect( r ); } + if( topmenu_space != NULL ) + { + QRect topmenu_area = all; + topmenu_area.setTop( topMenuHeight()); + for( int i = 1; + i <= numberOfDesktops(); + ++i ) + new_areas[ i ] = new_areas[ i ].intersect( topmenu_area ); + } bool changed = force; for( int i = 1; @@ -104,6 +113,7 @@ void Workspace::updateClientArea( bool force ) rootInfo->setWorkArea( i, r ); } + updateTopMenuSpaceGeometry(); for( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) @@ -337,6 +347,16 @@ void Workspace::unclutterDesktop() } +void Workspace::updateTopMenuSpaceGeometry() + { + if( !managingTopMenus()) + return; + QRect area; + area = clientArea( MaximizeFullArea, QPoint( 0, 0 ), 1 ); // HACK desktop ? + area.setHeight( topMenuHeight()); + topmenu_space->setGeometry( area ); + } + //******************************************** // Client //******************************************** @@ -351,11 +371,9 @@ void Workspace::unclutterDesktop() QRect Client::adjustedClientArea( const QRect& area ) const { QRect r = area; - if( isTopMenu() && workspace()->managingTopMenus()) - { - r.setTop( r.top() + workspace()->topMenuHeight()); + // topmenu area is reserved in updateClientArea() + if( isTopMenu()) return r; - } NETStrut strut = info->strut(); if ( strut.left > 0 ) r.setLeft( r.left() + (int) strut.left ); diff --git a/layers.cpp b/layers.cpp index 2e4884b1f0..2b108864c3 100644 --- a/layers.cpp +++ b/layers.cpp @@ -131,39 +131,55 @@ void Workspace::propagateClients( bool propagate_new_clients ) // when passig pointers around. // restack the windows according to the stacking order - Window* new_stack = new Window[ stacking_order.count() + 1 ]; - int i = 0; + Window* new_stack = new Window[ stacking_order.count() + 2 ]; + int pos = 0; // Stack all windows under the support window. The support window is // not used for anything (besides the NETWM property), and it's not shown, // but it was lowered after kwin startup. Stacking all clients below // it ensures that no client will be ever shown above override-redirect // windows (e.g. popups). - new_stack[ i++ ] = supportWindow->winId(); - for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) - new_stack[i++] = (*it)->frameId(); + new_stack[ pos++ ] = supportWindow->winId(); + int topmenu_space_pos = 0; + for( ClientList::ConstIterator it = stacking_order.fromLast(); + it != stacking_order.end(); + --it ) + { + new_stack[ pos++ ] = (*it)->frameId(); + if( (*it)->isTopMenu()) + topmenu_space_pos = pos; + } + if( topmenu_space != NULL ) + { // make sure the topmenu space is below all topmenus, if there are any + for( int i = pos; + i > topmenu_space_pos; + --i ) + new_stack[ i ] = new_stack[ i - 1 ]; + new_stack[ topmenu_space_pos ] = topmenu_space->winId(); + ++pos; + } // TODO isn't it too inefficient to restart always all clients? // TODO don't restack not visible windows? - XRestackWindows(qt_xdisplay(), new_stack, i); + XRestackWindows(qt_xdisplay(), new_stack, pos); delete [] new_stack; if ( propagate_new_clients ) { cl = new Window[ desktops.count() + clients.count()]; - i = 0; + pos = 0; // TODO this is still not completely in the map order for ( ClientList::ConstIterator it = desktops.begin(); it != desktops.end(); ++it ) - cl[i++] = (*it)->window(); + cl[pos++] = (*it)->window(); for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it ) - cl[i++] = (*it)->window(); - rootInfo->setClientList( cl, i ); + cl[pos++] = (*it)->window(); + rootInfo->setClientList( cl, pos ); delete [] cl; } cl = new Window[ stacking_order.count()]; - i = 0; + pos = 0; for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) - cl[i++] = (*it)->window(); - rootInfo->setClientListStacking( cl, i ); + cl[pos++] = (*it)->window(); + rootInfo->setClientListStacking( cl, pos ); delete [] cl; #if 0 // not necessary anymore? diff --git a/options.cpp b/options.cpp index 90232f3dae..a74560fcc3 100644 --- a/options.cpp +++ b/options.cpp @@ -146,6 +146,13 @@ unsigned long Options::updateSettings() globalConfig.setGroup("KDE"); fade_tooltips = globalConfig.readBoolEntry("EffectFadeTooltip", false); animate_tooltips = globalConfig.readBoolEntry("EffectAnimateTooltip", false); + topmenus = globalConfig.readBoolEntry( "macStyle", false ); + + KConfig kdesktopcfg( "kdesktoprc", true ); + kdesktopcfg.setGroup( "Menubar" ); + desktop_topmenu = kdesktopcfg.readBoolEntry( "ShowMenubar", false ); + if( desktop_topmenu ) + topmenus = true; return changed; } diff --git a/options.h b/options.h index 94d1d162f4..6ee5b7ea31 100644 --- a/options.h +++ b/options.h @@ -252,6 +252,9 @@ class Options : public KDecorationOptions * @returns the activation delay for electric borders in milliseconds. */ int electricBorderDelay(); + + bool topMenuEnabled() const { return topmenus; } + bool desktopTopMenu() const { return desktop_topmenu; } private: WindowOperation OpTitlebarDblClick; @@ -276,6 +279,8 @@ class Options : public KDecorationOptions int electric_borders; int electric_border_delay; bool show_geometry_tip; + bool topmenus; + bool desktop_topmenu; }; extern Options* options; diff --git a/workspace.cpp b/workspace.cpp index 177eb1e666..367a15d371 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -296,26 +296,25 @@ void Workspace::init() topmenu_selection = new KSelectionOwner( topmenu_atom ); topmenu_watcher = new KSelectionWatcher( topmenu_atom ); topmenu_height = 0; - if( topmenu_selection->claim( false )) - { - managing_topmenus = true; - connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection())); - } - else - { - managing_topmenus = false; - connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); - } -// kdDebug() << "TOPMENU INIT:" << managing_topmenus << endl; + managing_topmenus = false; + topmenu_space = NULL; { // begin updates blocker block StackingUpdatesBlocker blocker( this ); + + if( options->topMenuEnabled() && topmenu_selection->claim( false )) + setupTopMenuHandling(); // this can call updateStackingOrder() + else + lostTopMenuSelection(); + XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins); for (i = 0; i < nwins; i++) { XGetWindowAttributes(qt_xdisplay(), wins[i], &attr); if (attr.override_redirect ) continue; + if( topmenu_space && topmenu_space->winId() == wins[ i ] ) + continue; if (attr.map_state != IsUnmapped) { if ( addSystemTrayWin( wins[i] ) ) @@ -400,6 +399,7 @@ Workspace::~Workspace() delete initPositioning; delete topmenu_watcher; delete topmenu_selection; + delete topmenu_space; _self = 0; } @@ -505,14 +505,15 @@ void Workspace::updateCurrentTopMenu() { // toplevel menubar handling Client* menubar = 0; + bool block_desktop_menubar = false; if( active_client ) { // show the new menu bar first... Client* menu_client = active_client; - // SELI TODO make this also depend on kdesktop's setting, like in menuapplet - bool block_desktop_menubar = active_client->isFullScreen(); for(;;) { + if( menu_client->isFullScreen()) + block_desktop_menubar = true; for( ClientList::ConstIterator it = menu_client->transients().begin(); it != menu_client->transients().end(); ++it ) @@ -524,30 +525,26 @@ void Workspace::updateCurrentTopMenu() if( menubar != NULL || !menu_client->isTransient()) break; if( menu_client->isModal() || menu_client->transientFor() == NULL ) - { // don't use mainwindow's menu if this is modal or group transient - if( menu_client->isModal() || menu_client->groupTransient()) - block_desktop_menubar = true; - break; - } + break; // don't use mainwindow's menu if this is modal or group transient menu_client = menu_client->transientFor(); } - if( !menubar && active_client->keepAbove()) - block_desktop_menubar = true; - if( !menubar && !block_desktop_menubar ) + } + if( !options->desktopTopMenu()) + block_desktop_menubar = true; + if( !menubar && !block_desktop_menubar ) + { + // Find the menubar of the desktop + Client* desktop = findDesktop( true, currentDesktop()); + if( desktop != NULL ) { - // Find the menubar of the desktop - Client* desktop = findDesktop( true, currentDesktop()); - if( desktop != NULL ) - { - for( ClientList::ConstIterator it = desktop->transients().begin(); - it != desktop->transients().end(); - ++it ) - if( (*it)->isTopMenu()) + for( ClientList::ConstIterator it = desktop->transients().begin(); + it != desktop->transients().end(); + ++it ) + if( (*it)->isTopMenu()) { menubar = *it; break; } - } } } @@ -569,7 +566,7 @@ void Workspace::updateCurrentTopMenu() (*it)->hideClient( true ); } #else - // TODO there should be a blank area where topmenus are placed, no need to keep + // There's a blank area where topmenus are placed, no need to keep // the desktop one always visible for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { @@ -729,13 +726,28 @@ void Workspace::slotReconfigure() else destroyBorderWindows(); + if( options->topMenuEnabled()) + { + if( !managingTopMenus() && topmenu_selection->claim( false )) + setupTopMenuHandling(); + } + else + { + if( managingTopMenus()) + { + topmenu_selection->release(); + lostTopMenuSelection(); + } + } topmenu_height = 0; // invalidate used menu height if( managingTopMenus()) { + updateTopMenuSpaceGeometry(); for( ClientList::ConstIterator it = topmenus.begin(); it != topmenus.end(); ++it ) (*it)->checkWorkspacePosition(); + updateCurrentTopMenu(); } } @@ -1815,6 +1827,7 @@ void Workspace::addTopMenu( Client* c ) if( minsize > topMenuHeight()) { topmenu_height = minsize; + updateTopMenuSpaceGeometry(); for( ClientList::ConstIterator it = topmenus.begin(); it != topmenus.end(); ++it ) @@ -1837,9 +1850,13 @@ void Workspace::removeTopMenu( Client* c ) void Workspace::lostTopMenuSelection() { // kdDebug() << "lost TopMenu selection" << endl; + if( !managing_topmenus ) + return; connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); disconnect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection())); managing_topmenus = false; + delete topmenu_space; + updateClientArea(); for( ClientList::ConstIterator it = topmenus.begin(); it != topmenus.end(); ++it ) @@ -1855,9 +1872,20 @@ void Workspace::lostTopMenuOwner() return; } // kdDebug() << "claimed TopMenu selection" << endl; + setupTopMenuHandling(); + } + +void Workspace::setupTopMenuHandling() + { + if( managing_topmenus ) + return; connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection())); disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); managing_topmenus = true; + topmenu_space = new QWidget; + updateTopMenuSpaceGeometry(); + topmenu_space->show(); + updateClientArea(); for( ClientList::ConstIterator it = topmenus.begin(); it != topmenus.end(); ++it ) diff --git a/workspace.h b/workspace.h index 5e2dc16586..9d018bfab5 100644 --- a/workspace.h +++ b/workspace.h @@ -341,6 +341,8 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine void updateCurrentTopMenu(); void addTopMenu( Client* c ); void removeTopMenu( Client* c ); + void setupTopMenuHandling(); + void updateTopMenuSpaceGeometry(); void updateToolWindows( bool also_hide ); // this is the right way to create a new client @@ -502,6 +504,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine KSelectionWatcher* topmenu_watcher; ClientList topmenus; // doesn't own them mutable int topmenu_height; + QWidget* topmenu_space; int set_active_client_recursion; int block_stacking_updates; // when >0, stacking updates are temporarily disabled