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     setAlignment( alignment() | Qt::AlignVCenter | Qt::AlignHCenter );
00138 }
00139 
00140 KSystemTray::~KSystemTray()
00141 {
00142     delete d;
00143 }
00144 
00145 
00146 void KSystemTray::showEvent( QShowEvent * )
00147 {
00148     if ( !hasQuit ) {
00149     menu->insertSeparator();
00150         KAction* action = d->actionCollection->action("minimizeRestore");
00151 
00152         if (action)
00153         {
00154             action->plug(menu);
00155         }
00156 
00157         action = d->actionCollection->action(KStdAction::name(KStdAction::Quit));
00158 
00159         if (action)
00160         {
00161             action->plug(menu);
00162         }
00163 
00164     hasQuit = 1;
00165     }
00166 }
00167 
00168 // KDE4 remove
00169 void KSystemTray::enterEvent( QEvent* e )
00170 {
00171     QLabel::enterEvent( e );
00172 }
00173 
00174 KPopupMenu* KSystemTray::contextMenu() const
00175 {
00176     return menu;
00177 }
00178 
00179 
00180 void KSystemTray::mousePressEvent( QMouseEvent *e )
00181 {
00182     if ( !rect().contains( e->pos() ) )
00183     return;
00184 
00185     switch ( e->button() ) {
00186     case LeftButton:
00187         toggleActive();
00188     break;
00189     case MidButton:
00190     // fall through
00191     case RightButton:
00192     if ( parentWidget() ) {
00193             KAction* action = d->actionCollection->action("minimizeRestore");
00194         if ( parentWidget()->isVisible() )
00195         action->setText( i18n("&Minimize") );
00196         else
00197         action->setText( i18n("&Restore") );
00198     }
00199     contextMenuAboutToShow( menu );
00200     menu->popup( e->globalPos() );
00201     break;
00202     default:
00203     // nothing
00204     break;
00205     }
00206 }
00207 
00208 void KSystemTray::mouseReleaseEvent( QMouseEvent * )
00209 {
00210 }
00211 
00212 
00213 void KSystemTray::contextMenuAboutToShow( KPopupMenu* )
00214 {
00215 }
00216 
00217 // called from the popup menu - always do what the menu entry says,
00218 // i.e. if the window is shown, no matter if active or not, the menu
00219 // entry is "minimize", otherwise it's "restore"
00220 void KSystemTray::minimizeRestoreAction()
00221 {
00222     if ( parentWidget() ) {
00223         bool restore = !( parentWidget()->isVisible() );
00224     minimizeRestore( restore );
00225     }
00226 }
00227 
00228 void KSystemTray::maybeQuit()
00229 {
00230     QString query = i18n("<qt>Are you sure you want to quit <b>%1</b>?</qt>")
00231                         .arg(kapp->caption());
00232     if (KMessageBox::warningContinueCancel(this, query,
00233                                      i18n("Confirm Quit From System Tray"),
00234                                      KStdGuiItem::quit(),
00235                                      QString("systemtrayquit%1")
00236                                             .arg(kapp->caption())) !=
00237         KMessageBox::Continue)
00238     {
00239         return;
00240     }
00241 
00242     emit quitSelected();
00243 
00244     // KDE4: stop closing the parent widget? it results in complex application code
00245     //       instead make applications connect to the quitSelected() signal
00246 
00247     if (parentWidget())
00248     {
00249         parentWidget()->close();
00250     }
00251     else
00252     {
00253         qApp->closeAllWindows();
00254     }
00255 }
00256 
00257 void KSystemTray::toggleActive()
00258 {
00259     activateOrHide();
00260 }
00261 
00262 void KSystemTray::setActive()
00263 {
00264     minimizeRestore( true );
00265 }
00266 
00267 void KSystemTray::setInactive()
00268 {
00269     minimizeRestore( false );
00270 }
00271 
00272 // called when left-clicking the tray icon
00273 // if the window is not the active one, show it if needed, and activate it
00274 // (just like taskbar); otherwise hide it
00275 void KSystemTray::activateOrHide()
00276 {
00277     QWidget *pw = parentWidget();
00278 
00279     if ( !pw )
00280     return;
00281 
00282 #ifdef Q_WS_X11
00283     KWin::WindowInfo info1 = KWin::windowInfo( pw->winId(), NET::XAWMState | NET::WMState );
00284     // mapped = visible (but possibly obscured)
00285     bool mapped = (info1.mappingState() == NET::Visible) && !info1.isMinimized();
00286 //    - not mapped -> show, raise, focus
00287 //    - mapped
00288 //        - obscured -> raise, focus
00289 //        - not obscured -> hide
00290     if( !mapped )
00291         minimizeRestore( true );
00292     else
00293     {
00294         KWinModule module;
00295         for( QValueList< WId >::ConstIterator it = module.stackingOrder().fromLast();
00296              it != module.stackingOrder().end() && (*it) != pw->winId();
00297              --it )
00298         {
00299             KWin::WindowInfo info2 = KWin::windowInfo( *it,
00300                 NET::WMGeometry | NET::XAWMState | NET::WMState | NET::WMWindowType );
00301             if( info2.mappingState() != NET::Visible )
00302                 continue; // not visible on current desktop -> ignore
00303             if( !info2.geometry().intersects( pw->geometry()))
00304                 continue; // not obscuring the window -> ignore
00305             if( !info1.hasState( NET::KeepAbove ) && info2.hasState( NET::KeepAbove ))
00306                 continue; // obscured by window kept above -> ignore
00307             NET::WindowType type = info2.windowType( NET::NormalMask | NET::DesktopMask
00308                 | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
00309                 | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
00310             if( type == NET::Dock || type == NET::TopMenu )
00311                 continue; // obscured by dock or topmenu -> ignore
00312             pw->raise();
00313             KWin::activateWindow( pw->winId());
00314             return;
00315         }
00316         minimizeRestore( false ); // hide
00317     }
00318 #endif
00319 }
00320 
00321 void KSystemTray::minimizeRestore( bool restore )
00322 {
00323     QWidget* pw = parentWidget();
00324     if( !pw )
00325     return;
00326 #ifdef Q_WS_X11
00327     KWin::WindowInfo info = KWin::windowInfo( pw->winId(), NET::WMGeometry | NET::WMDesktop );
00328     if ( restore )
00329     {
00330     if( d->on_all_desktops )
00331         KWin::setOnAllDesktops( pw->winId(), true );
00332     else
00333         KWin::setCurrentDesktop( info.desktop() );
00334         pw->move( info.geometry().topLeft() ); // avoid placement policies
00335         pw->show();
00336         pw->raise();
00337     KWin::activateWindow( pw->winId() );
00338     } else {
00339     d->on_all_desktops = info.onAllDesktops();
00340     pw->hide();
00341     }
00342 #endif
00343 }
00344 
00345 KActionCollection* KSystemTray::actionCollection()
00346 {
00347     return d->actionCollection;
00348 }
00349     
00350 QPixmap KSystemTray::loadIcon( const QString &icon, KInstance *instance )
00351 {
00352     KConfig *appCfg = kapp->config();
00353     KConfigGroupSaver configSaver(appCfg, "System Tray");
00354     int iconWidth = appCfg->readNumEntry("systrayIconWidth", 22);
00355     return instance->iconLoader()->loadIcon( icon, KIcon::Panel, iconWidth );
00356 }
00357 
00358 void KSystemTray::setPixmap( const QPixmap& p )
00359 {
00360     QLabel::setPixmap( p );
00361 #ifdef Q_WS_X11
00362     KWin::setIcons( winId(), p, QPixmap());
00363 #endif
00364 }
00365 
00366 void KSystemTray::setCaption( const QString& s )
00367 {
00368     QLabel::setCaption( s );
00369 }
00370 
00371 void KSystemTray::virtual_hook( int, void* )
00372 { /*BASE::virtual_hook( id, data );*/ }
00373 
00374 #include "ksystemtray.moc"
00375 #include "kdockwindow.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys