diff --git a/activation.cpp b/activation.cpp index 429fcb7694..1ece2b8fff 100644 --- a/activation.cpp +++ b/activation.cpp @@ -215,11 +215,8 @@ void Workspace::setActiveClient( Client* c, allowed_t ) { if ( active_client == c ) return; - if( popup && popup_client != c && set_active_client_recursion == 0 ) - { - popup->close(); - popup_client = 0; - } + if( active_popup && active_popup_client != c && set_active_client_recursion == 0 ) + closeActivePopup(); StackingUpdatesBlocker blocker( this ); ++set_active_client_recursion; if( active_client != NULL ) @@ -391,8 +388,7 @@ bool Workspace::activateNextClient( Client* c ) if( !( c == active_client || ( should_get_focus.count() > 0 && c == should_get_focus.last()))) return false; - if( popup ) - popup->close(); + closeActivePopup(); if( c != NULL ) { if( c == active_client ) @@ -836,6 +832,11 @@ void Client::updateUrgency() demandAttention(); } +void Client::shortcutActivated() + { + workspace()->activateClient( this, true ); // force + } + //**************************************** // Group //**************************************** diff --git a/client.h b/client.h index 0506ec4978..8cc077da07 100644 --- a/client.h +++ b/client.h @@ -18,6 +18,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include #include +#include #include #include #include @@ -210,6 +211,8 @@ class Client : public QObject, public KDecorationDefines void shrinkVertical(); bool providesContextHelp() const; + KShortcut shortcut() const; + void setShortcut( const KShortcut& cut ); bool performMouseCommand( Options::MouseCommand, QPoint globalPos, bool handled = false ); @@ -278,6 +281,7 @@ class Client : public QObject, public KDecorationDefines private slots: void autoRaise(); void shadeHover(); + void shortcutActivated(); private: friend class Bridge; // FRAME @@ -495,6 +499,7 @@ class Client : public QObject, public KDecorationDefines int border_left, border_right, border_top, border_bottom; QRegion _mask; static bool check_active_modal; // see Client::checkActiveModal() + KShortcut _shortcut; friend struct FetchNameInternalPredicate; friend struct CheckIgnoreFocusStealingProcedure; friend struct ResetupRulesProcedure; @@ -839,6 +844,19 @@ inline Window Client::moveResizeGrabWindow() const return move_resize_grab_window; } +inline KShortcut Client::shortcut() const + { + return _shortcut; + } + +inline void Client::setShortcut( const KShortcut& cut ) + { + if( _shortcut == cut ) + return; + _shortcut = cut; + workspace()->clientShortcutUpdated( this ); + } + #ifdef NDEBUG inline kndbgstream& operator<<( kndbgstream& stream, const Client* ) { return stream; } diff --git a/kcmkwin/kwinrules/detectwidget.cpp b/kcmkwin/kwinrules/detectwidget.cpp index a4587f8317..bf38605e60 100644 --- a/kcmkwin/kwinrules/detectwidget.cpp +++ b/kcmkwin/kwinrules/detectwidget.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -137,10 +138,7 @@ QString DetectDialog::selectedTitle() const Rules::StringMatch DetectDialog::titleMatch() const { -#if KDE_IS_VERSION( 3, 3, 90 ) -#warning Offer possibilities here -#endif - return Rules::UnimportantMatch; + return widget->match_title->isChecked() ? Rules::ExactMatch : Rules::UnimportantMatch; } bool DetectDialog::selectedWholeApp() const diff --git a/kcmkwin/kwinrules/detectwidgetbase.ui b/kcmkwin/kwinrules/detectwidgetbase.ui index 949dd3a416..f822d145f6 100644 --- a/kcmkwin/kwinrules/detectwidgetbase.ui +++ b/kcmkwin/kwinrules/detectwidgetbase.ui @@ -12,6 +12,9 @@ 325 + + Form3 + unnamed @@ -201,6 +204,17 @@ With some (non-KDE) applications whole window class can be sufficient for selecting a specific window in an application, as they set whole window class to contain both application and window role. + + + match_title + + + Match also window &title + + + Alt+T + + diff --git a/kwinbindings.cpp b/kwinbindings.cpp index f8cd17e50a..45266d66d2 100644 --- a/kwinbindings.cpp +++ b/kwinbindings.cpp @@ -57,6 +57,7 @@ DEF2( "Window Below Other Windows", I18N_NOOP("Keep Window Below Others"), 0, 0, slotWindowBelow() ); DEF( I18N_NOOP("Activate Window Demanding Attention"), CTRL+ALT+Qt::Key_A, 0, slotActivateAttentionWindow()); + DEF( I18N_NOOP("Setup Window Shortcut"), 0, 0, slotSetupWindowShortcut()); DEF2( "Window Pack Left", I18N_NOOP("Pack Window to the Left"), 0, 0, slotWindowPackLeft() ); DEF2( "Window Pack Right", I18N_NOOP("Pack Window to the Right"), diff --git a/lib/kdecoration.h b/lib/kdecoration.h index 88d73356f1..a3a082eb51 100644 --- a/lib/kdecoration.h +++ b/lib/kdecoration.h @@ -97,7 +97,8 @@ public: LowerOp, FullScreenOp, NoBorderOp, - NoOp + NoOp, + SetupWindowShortcutOp }; /** * Basic color types that should be recognized by all decoration styles. diff --git a/manage.cpp b/manage.cpp index 3e71644ced..68327829a9 100644 --- a/manage.cpp +++ b/manage.cpp @@ -131,6 +131,10 @@ bool Client::manage( Window w, bool isMapped ) if( session->userNoBorder ) setUserNoBorder( true ); } + + if( session && !session->shortcut.isNull()) + setShortcut( session->shortcut ); + // TODO use also rules for shortcut init_minimize = rules()->checkMinimize( init_minimize, !isMapped ); if( rules()->checkNoBorder( false, !isMapped )) diff --git a/sm.cpp b/sm.cpp index c281191468..308be65d90 100644 --- a/sm.cpp +++ b/sm.cpp @@ -113,6 +113,7 @@ void Workspace::storeSession( KConfig* config, SMSavePhase phase ) config->writeEntry( QString("skipPager")+n, c->skipPager() ); config->writeEntry( QString("userNoBorder")+n, c->isUserNoBorder() ); config->writeEntry( QString("windowType")+n, windowTypeToTxt( c->windowType())); + config->writeEntry( QString("shortcut")+n, c->shortcut().toStringInternal()); } } // TODO store also stacking order @@ -177,6 +178,7 @@ void Workspace::loadSessionInfo() info->skipPager = config->readBoolEntry( QString("skipPager")+n, FALSE ); info->userNoBorder = config->readBoolEntry( QString("userNoBorder")+n, FALSE ); info->windowType = txtToWindowType( config->readEntry( QString("windowType")+n ).latin1()); + info->shortcut = KShortcut( config->readEntry( QString("shortcut")+n )); info->active = ( active_client == i ); } } diff --git a/sm.h b/sm.h index 42e7cba106..58105b00c1 100644 --- a/sm.h +++ b/sm.h @@ -15,6 +15,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include #include +#include class QSocketNotifier; @@ -45,6 +46,7 @@ struct SessionInfo bool skipPager; bool userNoBorder; NET::WindowType windowType; + KShortcut shortcut; bool active; // means 'was active in the saved session' }; diff --git a/tabbox.cpp b/tabbox.cpp index 06a6a5cf82..3cba3b23be 100644 --- a/tabbox.cpp +++ b/tabbox.cpp @@ -844,6 +844,7 @@ bool Workspace::startKDEWalkThroughWindows() return false; tab_grab = TRUE; keys->setEnabled( false ); + client_keys->setEnabled( false ); tab_box->setMode( TabBox::WindowsMode ); tab_box->reset(); return TRUE; @@ -855,6 +856,7 @@ bool Workspace::startWalkThroughDesktops( int mode ) return false; control_grab = TRUE; keys->setEnabled( false ); + client_keys->setEnabled( false ); tab_box->setMode( (TabBox::Mode) mode ); tab_box->reset(); return TRUE; @@ -1006,6 +1008,7 @@ void Workspace::closeTabBox() removeTabBoxGrab(); tab_box->hide(); keys->setEnabled( true ); + client_keys->setEnabled( true ); tab_grab = FALSE; control_grab = FALSE; } @@ -1053,6 +1056,7 @@ void Workspace::tabBoxKeyRelease( const XKeyEvent& ev ) removeTabBoxGrab(); tab_box->hide(); keys->setEnabled( true ); + client_keys->setEnabled( true ); tab_grab = false; if( Client* c = tab_box->currentClient()) { @@ -1066,6 +1070,7 @@ void Workspace::tabBoxKeyRelease( const XKeyEvent& ev ) removeTabBoxGrab(); tab_box->hide(); keys->setEnabled( true ); + client_keys->setEnabled( true ); control_grab = False; if ( tab_box->currentDesktop() != -1 ) { diff --git a/useractions.cpp b/useractions.cpp index 5d84fdc3b0..94d4641bc9 100644 --- a/useractions.cpp +++ b/useractions.cpp @@ -61,6 +61,8 @@ QPopupMenu* Workspace::clientPopup() advanced_popup->insertItem( SmallIconSet( "window_fullscreen" ), i18n("&Fullscreen")+'\t'+keys->shortcut("Window Fullscreen").seq(0).toString(), Options::FullScreenOp ); advanced_popup->insertItem( i18n("&No Border")+'\t'+keys->shortcut("Window No Border").seq(0).toString(), Options::NoBorderOp ); + advanced_popup->insertItem( SmallIconSet("key_bindings"), + i18n("Window &Shortcut...")+'\t'+keys->shortcut("Setup Window Shortcut").seq(0).toString(), Options::SetupWindowShortcutOp ); advanced_popup->insertItem( SmallIconSet( "filesave" ), i18n("&Special Window Settings..."), Options::WindowRulesOp ); popup->insertItem(i18n("Ad&vanced"), advanced_popup ); @@ -92,7 +94,7 @@ QPopupMenu* Workspace::clientPopup() */ void Workspace::clientPopupAboutToShow() { - if ( !popup_client || !popup ) + if ( !active_popup_client || !popup ) return; if ( numberOfDesktops() == 1 ) @@ -105,21 +107,21 @@ void Workspace::clientPopupAboutToShow() initDesktopPopup(); } - popup->setItemEnabled( Options::ResizeOp, popup_client->isResizable() ); - popup->setItemEnabled( Options::MoveOp, popup_client->isMovable() ); - popup->setItemEnabled( Options::MaximizeOp, popup_client->isMaximizable() ); - popup->setItemChecked( Options::MaximizeOp, popup_client->maximizeMode() == Client::MaximizeFull ); + popup->setItemEnabled( Options::ResizeOp, active_popup_client->isResizable() ); + popup->setItemEnabled( Options::MoveOp, active_popup_client->isMovable() ); + popup->setItemEnabled( Options::MaximizeOp, active_popup_client->isMaximizable() ); + popup->setItemChecked( Options::MaximizeOp, active_popup_client->maximizeMode() == Client::MaximizeFull ); // This should be checked also when hover unshaded - popup->setItemChecked( Options::ShadeOp, popup_client->shadeMode() != ShadeNone ); - popup->setItemEnabled( Options::ShadeOp, popup_client->isShadeable()); - advanced_popup->setItemChecked( Options::KeepAboveOp, popup_client->keepAbove() ); - advanced_popup->setItemChecked( Options::KeepBelowOp, popup_client->keepBelow() ); - advanced_popup->setItemChecked( Options::FullScreenOp, popup_client->isFullScreen() ); - advanced_popup->setItemEnabled( Options::FullScreenOp, popup_client->userCanSetFullScreen() ); - advanced_popup->setItemChecked( Options::NoBorderOp, popup_client->noBorder() ); - advanced_popup->setItemEnabled( Options::NoBorderOp, popup_client->userCanSetNoBorder() ); - popup->setItemEnabled( Options::MinimizeOp, popup_client->isMinimizable() ); - popup->setItemEnabled( Options::CloseOp, popup_client->isCloseable() ); + popup->setItemChecked( Options::ShadeOp, active_popup_client->shadeMode() != ShadeNone ); + popup->setItemEnabled( Options::ShadeOp, active_popup_client->isShadeable()); + advanced_popup->setItemChecked( Options::KeepAboveOp, active_popup_client->keepAbove() ); + advanced_popup->setItemChecked( Options::KeepBelowOp, active_popup_client->keepBelow() ); + advanced_popup->setItemChecked( Options::FullScreenOp, active_popup_client->isFullScreen() ); + advanced_popup->setItemEnabled( Options::FullScreenOp, active_popup_client->userCanSetFullScreen() ); + advanced_popup->setItemChecked( Options::NoBorderOp, active_popup_client->noBorder() ); + advanced_popup->setItemEnabled( Options::NoBorderOp, active_popup_client->userCanSetNoBorder() ); + popup->setItemEnabled( Options::MinimizeOp, active_popup_client->isMinimizable() ); + popup->setItemEnabled( Options::CloseOp, active_popup_client->isCloseable() ); } @@ -173,7 +175,15 @@ void Workspace::desktopPopupAboutToShow() } } - +void Workspace::closeActivePopup() + { + if( active_popup ) + { + active_popup->close(); + active_popup = NULL; + active_popup_client = NULL; + } + } /*! Create the global accel object \c keys. @@ -204,10 +214,59 @@ void Workspace::readShortcuts() } +void Workspace::setupWindowShortcut( Client* c ) + { + assert( client_keys_dialog == NULL ); + keys->setEnabled( false ); + client_keys->setEnabled( false ); + client_keys_dialog = new ShortcutDialog( c->shortcut()); + client_keys_client = c; + connect( client_keys_dialog, SIGNAL( dialogDone( bool )), SLOT( setupWindowShortcutDone( bool ))); + QRect r = clientArea( ScreenArea, c ); + QSize size = client_keys_dialog->sizeHint(); + QPoint pos = c->pos() + c->clientPos(); + if( pos.x() + size.width() >= r.right()) + pos.setX( r.right() - size.width()); + if( pos.y() + size.height() >= r.bottom()) + pos.setY( r.bottom() - size.height()); + client_keys_dialog->move( pos ); + client_keys_dialog->show(); + active_popup = client_keys_dialog; + active_popup_client = c; + } + +void Workspace::setupWindowShortcutDone( bool ok ) + { + keys->setEnabled( true ); + client_keys->setEnabled( true ); + if( ok ) + { + client_keys_client->setShortcut( client_keys_dialog->shortcut()); + } + closeActivePopup(); + delete client_keys_dialog; + client_keys_dialog = NULL; + client_keys_client = NULL; + } + +void Workspace::clientShortcutUpdated( Client* c ) + { + QString key = QString::number( c->window()); + client_keys->remove( key ); + if( !c->shortcut().isNull()) + { + client_keys->insert( key, key ); + client_keys->setShortcut( key, c->shortcut()); + client_keys->setSlot( key, c, SLOT( shortcutActivated())); + client_keys->setActionEnabled( key, true ); + } + client_keys->updateConnections(); + } + void Workspace::clientPopupActivated( int id ) { WindowOperation op = static_cast< WindowOperation >( id ); - Client* c = popup_client ? popup_client : active_client; + Client* c = active_popup_client ? active_popup_client : active_client; QString type; switch( op ) { @@ -288,6 +347,9 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) case Options::WindowRulesOp: editWindowRules( c ); break; + case Options::SetupWindowShortcutOp: + setupWindowShortcut( c ); + break; case Options::LowerOp: lowerClient(c); break; @@ -607,6 +669,11 @@ void Workspace::slotWindowBelow() if( active_client ) performWindowOperation( active_client, Options::KeepBelowOp ); } +void Workspace::slotSetupWindowShortcut() + { + if( active_client ) + performWindowOperation( active_client, Options::SetupWindowShortcutOp ); + } /*! Move window to next desktop @@ -721,15 +788,15 @@ void Workspace::slotKillWindow() */ void Workspace::slotSendToDesktop( int desk ) { - if ( !popup_client ) + if ( !active_popup_client ) return; if ( desk == 0 ) { // the 'on_all_desktops' menu entry - popup_client->setOnAllDesktops( !popup_client->isOnAllDesktops()); + active_popup_client->setOnAllDesktops( !active_popup_client->isOnAllDesktops()); return; } - sendClientToDesktop( popup_client, desk, false ); + sendClientToDesktop( active_popup_client, desk, false ); } @@ -750,29 +817,30 @@ void Workspace::showWindowMenu( const QRect &pos, Client* cl ) return; if( !cl ) return; - if( popup_client != NULL ) // recursion + if( active_popup_client != NULL ) // recursion return; if ( cl->isDesktop() || cl->isDock() || cl->isTopMenu()) return; - popup_client = cl; + active_popup_client = cl; QPopupMenu* p = clientPopup(); + active_popup = p; int x = pos.left(); int y = pos.bottom(); - if (y == pos.top()) { + if (y == pos.top()) p->exec( QPoint( x, y ) ); - } else { + else + { QRect area = clientArea(ScreenArea, QPoint(x, y), currentDesktop()); int popupHeight = p->sizeHint().height(); - if (y + popupHeight < area.height()) { + if (y + popupHeight < area.height()) p->exec( QPoint( x, y ) ); - } else { + else p->exec( QPoint( x, pos.top() - popupHeight ) ); - } - } - popup_client = 0; + } + closeActivePopup(); } /*! diff --git a/utils.cpp b/utils.cpp index 3aeefdcf4b..cd3cdae33e 100644 --- a/utils.cpp +++ b/utils.cpp @@ -304,6 +304,50 @@ bool isLocalMachine( const QCString& host ) return false; } +#ifndef KCMRULES +ShortcutDialog::ShortcutDialog( const KShortcut& cut ) + : KShortcutDialog( cut, false /*TODO???*/ ) + { + // make it a popup, so that it has the grab + XSetWindowAttributes attrs; + attrs.override_redirect = True; + XChangeWindowAttributes( qt_xdisplay(), winId(), CWOverrideRedirect, &attrs ); + setWFlags( WType_Popup ); + } + +void ShortcutDialog::accept() + { + for( int i = 0; + ; + ++i ) + { + KKeySequence seq = shortcut().seq( i ); + if( seq.isNull()) + break; + if( seq.key( 0 ) == Key_Escape ) + { + reject(); + return; + } + if( seq.key( 0 ) == Key_Space ) + { // clear + setShortcut( KShortcut()); + KShortcutDialog::accept(); + return; + } + if( seq.key( 0 ).modFlags() == 0 ) + { // no shortcuts without modifiers + KShortcut cut = shortcut(); + cut.setSeq( i, KKeySequence()); + setShortcut( cut ); + return; + } + } + KShortcutDialog::accept(); + } +#endif + + } // namespace #ifndef KCMRULES diff --git a/utils.h b/utils.h index 6aeddc4f0a..9e1dcddb74 100644 --- a/utils.h +++ b/utils.h @@ -16,6 +16,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include #include +#include namespace KWinInternal { @@ -242,7 +243,23 @@ Time timestampDiff( Time time1, Time time2 ) // returns time2 - time1 } bool isLocalMachine( const QCString& host ); - + +#ifndef KCMRULES +// Qt dialogs emit no signal when closed :( +class ShortcutDialog + : public KShortcutDialog + { + Q_OBJECT + public: + ShortcutDialog( const KShortcut& cut ); + virtual void accept(); + signals: + void dialogDone( bool ok ); + protected: + virtual void done( int r ) { KShortcutDialog::done( r ); emit dialogDone( r == Accepted ); } + }; +#endif + } // namespace #endif diff --git a/workspace.cpp b/workspace.cpp index 510c013c48..765a3ced0d 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -64,7 +64,8 @@ Workspace::Workspace( bool restore ) QObject (0, "workspace"), current_desktop (0), number_of_desktops(0), - popup_client (0), + active_popup( NULL ), + active_popup_client( NULL ), desktop_widget (0), temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ), active_client (0), @@ -86,6 +87,9 @@ Workspace::Workspace( bool restore ) desk_popup (0), desk_popup_index (0), keys (0), + client_keys ( NULL ), + client_keys_dialog ( NULL ), + client_keys_client ( NULL ), root (0), workspaceInit (true), startup(0), electric_have_borders(false), @@ -171,6 +175,7 @@ Workspace::Workspace( bool restore ) 1 ); + client_keys = new KGlobalAccel( this ); initShortcuts(); tab_box = new TabBox( this ); popupinfo = new PopupInfo( ); @@ -425,6 +430,7 @@ Workspace::~Workspace() delete topmenu_watcher; delete topmenu_selection; delete topmenu_space; + delete client_keys_dialog; while( !rules.isEmpty()) { delete rules.front(); @@ -493,10 +499,13 @@ void Workspace::addClient( Client* c, allowed_t ) */ void Workspace::removeClient( Client* c, allowed_t ) { - if (c == active_client && popup) - popup->close(); - if( c == popup_client ) - popup_client = 0; + if (c == active_popup_client) + closeActivePopup(); + + if( client_keys_client == c ) + setupWindowShortcutDone( false ); + if( !c->shortcut().isNull()) + c->setShortcut( KShortcut()); // remove from client_keys if( c->isDialog()) Notify::raise( Notify::TransDelete ); @@ -1013,8 +1022,7 @@ bool Workspace::setCurrentDesktop( int new_desktop ) if (new_desktop < 1 || new_desktop > number_of_desktops ) return false; - if( popup ) - popup->close(); + closeActivePopup(); ++block_focus; // TODO Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date StackingUpdatesBlocker blocker( this ); diff --git a/workspace.h b/workspace.h index 8f36a46c62..ccc387d6df 100644 --- a/workspace.h +++ b/workspace.h @@ -28,6 +28,7 @@ License. See the file "COPYING" for the exact licensing terms. class QPopupMenu; class KConfig; class KGlobalAccel; +class KShortcutDialog; class KStartupInfo; class KStartupInfoId; class KStartupInfoData; @@ -236,6 +237,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine void focusToNull(); // SELI public? bool forcedGlobalMouseGrab() const; + void clientShortcutUpdated( Client* c ); void sessionSaveStarted(); void sessionSaveDone(); @@ -325,6 +327,9 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine void slotGrabWindow(); void slotGrabDesktop(); + void slotSetupWindowShortcut(); + void setupWindowShortcutDone( bool ); + void updateClientArea(); private slots: @@ -351,6 +356,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine void initShortcuts(); void readShortcuts(); void initDesktopPopup(); + void setupWindowShortcut( Client* c ); bool startKDEWalkThroughWindows(); bool startWalkThroughDesktops( int mode ); // TabBox::Mode::DesktopMode | DesktopListMode @@ -425,6 +431,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine void calcDesktopLayout(int &x, int &y) const; QPopupMenu* clientPopup(); + void closeActivePopup(); void updateClientArea( bool force ); @@ -434,7 +441,8 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine int number_of_desktops; QMemArray desktop_focus_chain; - Client* popup_client; + QWidget* active_popup; + Client* active_popup_client; QWidget* desktop_widget; @@ -498,6 +506,10 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine int desk_popup_index; KGlobalAccel *keys; + KGlobalAccel *client_keys; + KShortcutDialog* client_keys_dialog; + Client* client_keys_client; + WId root; PluginMgr *mgr;