ksystemtray.cpp

00001 /* This file is part of the KDE libraries
00002 
00003     Copyright (C) 1999 Matthias Ettrich (ettrich@kde.org)
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "config.h"
00022 #include "kaction.h"
00023 #include "kmessagebox.h"
00024 #include "kshortcut.h"
00025 #include "ksystemtray.h"
00026 #include "kpopupmenu.h"
00027 #include "kapplication.h"
00028 #include "klocale.h"
00029 #include "kaboutdata.h"
00030 
00031 #ifdef Q_WS_X11
00032 #include <kwin.h> 
00033 #include <kwinmodule.h> 
00034 #include <qxembed.h> 
00035 #include <X11/Xlib.h>
00036 #endif
00037 
00038 #include <kiconloader.h>
00039 #include <kconfig.h>
00040 
00041 #include <qapplication.h>
00042 
00043 extern Time qt_x_time;
00044 
00045 class KSystemTrayPrivate
00046 {
00047 public:
00048     KSystemTrayPrivate()
00049     {
00050         actionCollection = 0;
00051     }
00052 
00053     ~KSystemTrayPrivate()
00054     {
00055         delete actionCollection;
00056     }
00057 
00058     KActionCollection* actionCollection;
00059     bool on_all_desktops; // valid only when the parent widget was hidden
00060 };
00061 
00062 KSystemTray::KSystemTray( QWidget* parent, const char* name )
00063     : QLabel( parent, name, WType_TopLevel )
00064 {
00065 #ifdef Q_WS_X11
00066     QXEmbed::initialize();
00067 #endif
00068     
00069     {
00070         XGrabServer (qt_xdisplay());
00071         static Atom selection =  XInternAtom( qt_xdisplay(), "_NET_SYSTEM_TRAY_S" + QCString().setNum( qt_xscreen()), False );
00072     
00073         /* look for the system tray window */
00074         Window tray = XGetSelectionOwner (qt_xdisplay(), selection);
00075 
00076         if (tray != None)
00077         {
00078                 /* found it */
00079                 XSelectInput (qt_xdisplay(), tray, StructureNotifyMask);
00080         }
00081 
00082         XUngrabServer (qt_xdisplay());
00083         XFlush (qt_xdisplay());
00084 
00085         if ( tray != None )
00086         {
00087             XEvent ev;
00088             memset(&ev, 0, sizeof( ev ));
00089             static Atom atom = XInternAtom( qt_xdisplay(), "_NET_SYSTEM_TRAY_OPCODE", False );
00090             ev.xclient.type = ClientMessage;
00091             ev.xclient.window = tray;
00092             ev.xclient.message_type = atom;
00093             ev.xclient.format = 32;
00094             ev.xclient.data.l[ 0 ] = qt_x_time;
00095             ev.xclient.data.l[ 1 ] = 0; // SYSTEM_TRAY_REQUEST_DOCK
00096             ev.xclient.data.l[ 2 ] = winId();
00097             ev.xclient.data.l[ 3 ] = 0; // unused
00098             ev.xclient.data.l[ 4 ] = 0; // unused
00099 
00100             XSendEvent( qt_xdisplay(), tray, False, NoEventMask, (XEvent *)&ev );
00101             XSync(qt_xdisplay(), FALSE );
00102         }
00103     }
00104 
00105 
00106     d = new KSystemTrayPrivate;
00107     d->actionCollection = new KActionCollection(this);
00108 
00109 #ifdef Q_WS_X11
00110     KWin::setSystemTrayWindowFor( winId(), parent?parent->topLevelWidget()->winId(): qt_xrootwin() );
00111 #endif
00112     setBackgroundMode(X11ParentRelative);
00113     setBackgroundOrigin(WindowOrigin);
00114     hasQuit = 0;
00115     menu = new KPopupMenu( this );
00116     menu->insertTitle( kapp->miniIcon(), kapp->caption() );
00117     move( -1000, -1000 );
00118     KStdAction::quit(this, SLOT(maybeQuit()), d->actionCollection);
00119 
00120     if (parentWidget())
00121     {
00122         new KAction(i18n("Minimize"), KShortcut(),
00123                     this, SLOT( minimizeRestoreAction() ),
00124                     d->actionCollection, "minimizeRestore");
00125 #ifdef Q_WS_X11
00126     KWin::WindowInfo info = KWin::windowInfo( parentWidget()->winId());
00127     d->on_all_desktops = info.onAllDesktops();
00128 #else
00129     d->on_all_desktops = false;
00130 #endif
00131     }
00132     else
00133     {
00134         d->on_all_desktops = false;
00135     }
00136     setCaption( KGlobal::instance()->aboutData()->programName());
00137 }
00138 
00139 KSystemTray::~KSystemTray()
00140 {
00141     delete d;
00142 }
00143 
00144 
00145 void KSystemTray::showEvent( QShowEvent * )
00146 {
00147     if ( !hasQuit ) {
00148     menu->insertSeparator();
00149         KAction* action = d->actionCollection->action("minimizeRestore");
00150 
00151         if (action)
00152         {
00153             action->plug(menu);
00154         }
00155 
00156         action = d->actionCollection->action(KStdAction::name(KStdAction::Quit));
00157 
00158         if (action)
00159         {
00160             action->plug(menu);
00161         }
00162 
00163     hasQuit = 1;
00164     }
00165 }
00166 
00167 // KDE4 remove
00168 void KSystemTray::enterEvent( QEvent* e )
00169 {
00170     QLabel::enterEvent( e );
00171 }
00172 
00173 KPopupMenu* KSystemTray::contextMenu() const
00174 {
00175     return menu;
00176 }
00177 
00178 
00179 void KSystemTray::mousePressEvent( QMouseEvent *e )
00180 {
00181     if ( !rect().contains( e->pos() ) )
00182     return;
00183 
00184     switch ( e->button() ) {
00185     case LeftButton:
00186         toggleActive();
00187     break;
00188     case MidButton:
00189     // fall through
00190     case RightButton:
00191     if ( parentWidget() ) {
00192             KAction* action = d->actionCollection->action("minimizeRestore");
00193         if ( parentWidget()->isVisible() )
00194         action->setText( i18n("&Minimize") );
00195         else
00196         action->setText( i18n("&Restore") );
00197     }
00198     contextMenuAboutToShow( menu );
00199     menu->popup( e->globalPos() );
00200     break;
00201     default:
00202     // nothing
00203     break;
00204     }
00205 }
00206 
00207 void KSystemTray::mouseReleaseEvent( QMouseEvent * )
00208 {
00209 }
00210 
00211 
00212 void KSystemTray::contextMenuAboutToShow( KPopupMenu* )
00213 {
00214 }
00215 
00216 // called from the popup menu - always do what the menu entry says,
00217 // i.e. if the window is shown, no matter if active or not, the menu
00218 // entry is "minimize", otherwise it's "restore"
00219 void KSystemTray::minimizeRestoreAction()
00220 {
00221     if ( parentWidget() ) {
00222         bool restore = !( parentWidget()->isVisible() );
00223     minimizeRestore( restore );
00224     }
00225 }
00226 
00227 void KSystemTray::maybeQuit()
00228 {
00229     QString query = i18n("<qt>Are you sure you want to quit <b>%1</b>?</qt>")
00230                         .arg(kapp->caption());
00231     if (KMessageBox::warningContinueCancel(this, query,
00232                                      i18n("Confirm Quit From System Tray"),
00233                                      KStdGuiItem::quit(),
00234                                      QString("systemtrayquit%1")
00235                                             .arg(kapp->caption())) !=
00236         KMessageBox::Continue)
00237     {
00238         return;
00239     }
00240 
00241     emit quitSelected();
00242 
00243     // KDE4: stop closing the parent widget? it results in complex application code
00244     //       instead make applications connect to the quitSelected() signal
00245 
00246     if (parentWidget())
00247     {
00248         parentWidget()->close();
00249     }
00250     else
00251     {
00252         qApp->closeAllWindows();
00253     }
00254 }
00255 
00256 void KSystemTray::toggleActive()
00257 {
00258     activateOrHide();
00259 }
00260 
00261 void KSystemTray::setActive()
00262 {
00263     minimizeRestore( true );
00264 }
00265 
00266 void KSystemTray::setInactive()
00267 {
00268     minimizeRestore( false );
00269 }
00270 
00271 // called when left-clicking the tray icon
00272 // if the window is not the active one, show it if needed, and activate it
00273 // (just like taskbar); otherwise hide it
00274 void KSystemTray::activateOrHide()
00275 {
00276     QWidget *pw = parentWidget();
00277 
00278     if ( !pw )
00279     return;
00280 
00281 #ifdef Q_WS_X11
00282     KWin::WindowInfo info1 = KWin::windowInfo( pw->winId(), NET::XAWMState | NET::WMState );
00283     // mapped = visible (but possibly obscured)
00284     bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
00285 //    - not mapped -> show, raise, focus
00286 //    - mapped
00287 //        - obscured -> raise, focus
00288 //        - not obscured -> hide
00289     if( !mapped )
00290         minimizeRestore( true );
00291     else
00292     {
00293         KWinModule module;
00294         for( QValueList< WId >::ConstIterator it = module.stackingOrder().fromLast();
00295              it != module.stackingOrder().end() && (*it) != pw->winId();
00296              --it )
00297         {
00298             KWin::WindowInfo info2 = KWin::windowInfo( *it,
00299                 NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType );
00300             if( info2.mappingState() != NET::Visible )
00301                 continue; // not visible on current desktop -> ignore
00302             if( !info2.geometry().intersects( pw->geometry()))
00303                 continue; // not obscuring the window -> ignore
00304             if( !info1.hasState( NET::KeepAbove ) && info2.hasState( NET::KeepAbove ))
00305                 continue; // obscured by window kept above -> ignore
00306             NET::WindowType type = info2.windowType( NET::NormalMask | NET::DesktopMask
00307                 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
00308                 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
00309             if( type == NET::Dock || type == NET::TopMenu )
00310                 continue; // obscured by dock or topmenu -> ignore
00311             pw->raise();
00312             KWin::activateWindow( pw->winId());
00313             return;
00314         }
00315         minimizeRestore( false ); // hide
00316     }
00317 #endif
00318 }
00319 
00320 void KSystemTray::minimizeRestore( bool restore )
00321 {
00322     QWidget* pw = parentWidget();
00323     if( !pw )
00324     return;
00325 #ifdef Q_WS_X11
00326     KWin::WindowInfo info = KWin::windowInfo( pw->winId(), NET::WMGeometry | NET::WMDesktop );
00327     if ( restore )
00328     {
00329     if( d->on_all_desktops )
00330         KWin::setOnAllDesktops( pw->winId(), true );
00331     else
00332         KWin::setCurrentDesktop( info.desktop() );
00333         pw->move( info.geometry().topLeft() ); // avoid placement policies
00334         pw->show();
00335         pw->raise();
00336     KWin::activateWindow( pw->winId() );
00337     } else {
00338     d->on_all_desktops = info.onAllDesktops();
00339     pw->hide();
00340     }
00341 #endif
00342 }
00343 
00344 KActionCollection* KSystemTray::actionCollection()
00345 {
00346     return d->actionCollection;
00347 }
00348     
00349 QPixmap KSystemTray::loadIcon( const QString &icon, KInstance *instance )
00350 {
00351     KConfig *appCfg = kapp->config();
00352     KConfigGroupSaver configSaver(appCfg, "System Tray");
00353     int iconWidth = appCfg->readNumEntry("systrayIconWidth", 22);
00354     return instance->iconLoader()->loadIcon( icon, KIcon::Panel, iconWidth );
00355 }
00356 
00357 void KSystemTray::setPixmap( const QPixmap& p )
00358 {
00359     QLabel::setPixmap( p );
00360 #ifdef Q_WS_X11
00361     KWin::setIcons( winId(), p, QPixmap());
00362 #endif
00363 }
00364 
00365 void KSystemTray::setCaption( const QString& s )
00366 {
00367     QLabel::setCaption( s );
00368 }
00369 
00370 void KSystemTray::virtual_hook( int, void* )
00371 { /*BASE::virtual_hook( id, data );*/ }
00372 
00373 #include "ksystemtray.moc"
00374 #include "kdockwindow.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys