You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdedocker/src/qtraylabel.cpp

810 lines
25 KiB

/*
* Copyright (C) 2004 Girish Ramakrishnan All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
// $Id: qtraylabel.cpp,v 1.31 2005/06/21 10:04:36 cs19713 Exp $
// Include all Qt includes before X
#include <qstring.h>
#include <qevent.h>
#include <qpoint.h>
#include <qtooltip.h>
#include <qtimer.h>
#include <qimage.h>
#include <qpixmap.h>
#include <qfileinfo.h>
#include <qapplication.h>
#include "trace.h"
#include "qtraylabel.h"
#include <X11/cursorfont.h>
#include <X11/xpm.h>
#include <Xmu/WinUtil.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "util.h"
void QTrayLabel::initialize(void)
{
mDocked = false;
mWithdrawn = true;
mBalloonTimeout = 4000;
mSkippingTaskbar = false;
mDockWhenMinimized = true;
mDesktop = 666; // setDockedWindow would set it a saner value
// Balloon's properties are set to match a Qt tool tip (see Qt source)
mBalloon = new QLabel(0, "balloon", WType_TopLevel | WStyle_StaysOnTop |
WStyle_Customize | WStyle_NoBorder |
WStyle_Tool | WX11BypassWM);
mBalloon->setFont(QToolTip::font());
mBalloon->setPalette(QToolTip::palette());
mBalloon->setAlignment(Qt::AlignLeft | Qt::AlignTop);
mBalloon->setAutoMask(FALSE);
mBalloon->setAutoResize(true);
setAlignment(Qt::AlignCenter);
setBackgroundMode(X11ParentRelative);
connect(&mRealityMonitor, SIGNAL(timeout()), this, SLOT(realityCheck()));
setDockedWindow(mDockedWindow);
sysTrayStatus(QPaintDevice::x11AppDisplay(), &mSysTray);
// Subscribe to system tray window notifications
if (mSysTray != None)
subscribe(QPaintDevice::x11AppDisplay(), mSysTray,
StructureNotifyMask, true);
}
// Describe ourselves in a few words
const char *QTrayLabel::me(void) const
{
static char temp[100];
snprintf(temp, sizeof(temp), "(%s,PID=%i,WID=0x%x)",
mProgName[0].latin1(), mPid, (unsigned) mDockedWindow);
return temp;
}
QTrayLabel::QTrayLabel(Window w, QWidget* parent, const QString& text)
:QLabel(parent, text, WStyle_Customize | WStyle_NoBorder | WStyle_Tool),
mDockedWindow(w), mPid(0)
{
initialize();
}
QTrayLabel::QTrayLabel(const QStringList& pname, pid_t pid, QWidget* parent)
:QLabel(parent, "TrayLabel", WStyle_Customize | WStyle_NoBorder | WStyle_Tool),
mDockedWindow(None), mProgName(pname), mPid(pid)
{
if (pname[0].at(0) != '/' && pname[0].find('/', 1) > 0)
mProgName[0] = QFileInfo(pname[0]).absFilePath(); // convert to absolute
initialize();
}
QTrayLabel::~QTrayLabel()
{
TRACE("%s Goodbye", me());
if (mDockedWindow == None) return;
// Leave the docked window is some sane state
mSkippingTaskbar = false;
skipTaskbar();
map();
}
/*
* Scans the windows in the desktop and checks if a window exists that we might
* be interested in
*/
void QTrayLabel::scanClients()
{
Window r, parent, *children;
unsigned nchildren = 0;
Display *display = QPaintDevice::x11AppDisplay();
QString ename = QFileInfo(mProgName[0]).fileName(); // strip out the path
XQueryTree(display, qt_xrootwin(), &r, &parent, &children, &nchildren);
TRACE("%s nchildren=%i", me(), nchildren);
for(unsigned i=0; i<nchildren; i++)
{
Window w = XmuClientWindow(display, children[i]);
TRACE("\t%s checking 0x%x", me(), (unsigned) w);
if (!isNormalWindow(display, w)) continue;
if (analyzeWindow(display, w, mPid, ename.latin1()))
{
TRACE("\t%s SOULMATE FOUND", me());
setDockedWindow(w);
break;
}
}
}
/*
* Do a reality check :). Note that this timer runs only when required. Does 3
* things,
* 1) If the system tray had disappeared, checks for arrival of new system tray
* 2) Check root window subscription since it is overwritten by Qt (see below)
* 3) Checks health of the process whose windows we are docking
*/
void QTrayLabel::realityCheck(void)
{
if (mSysTray == None)
{
// Check the system tray status if we were docked
if (sysTrayStatus(QPaintDevice::x11AppDisplay(), &mSysTray)
!= SysTrayPresent) return; // no luck
TRACE("%s System tray present", me());
dock();
subscribe(QPaintDevice::x11AppDisplay(), mSysTray,
StructureNotifyMask, true);
mRealityMonitor.stop();
return;
}
/*
* I am not sure when, but Qt at some point in time overwrites our
* subscription (SubstructureNotifyMask) on the root window. So, we check
* the status of root window subscription periodically. Now, from the time
* Qt overwrote our subscription to the time we discovered it, the
* window we are looking for could have been mapped and we would have never
* been informed (since Qt overrwrote the subscription). So we have to
* scan existing client list and dock. I have never seen this happen
* but I see it likely to happen during session restoration
*/
Display *display = QPaintDevice::x11AppDisplay();
XWindowAttributes attr;
XGetWindowAttributes(display, qt_xrootwin(), &attr);
if (!(attr.your_event_mask & SubstructureNotifyMask))
{
subscribe(display, None, SubstructureNotifyMask, true);
TRACE("%s rescanning clients since qt overrode mask", me());
scanClients();
}
if (mPid)
{
// Check process health
int status;
if (waitpid(mPid, &status, WNOHANG) == 0) return; // still running
TRACE("%s processDead", me());
mPid = 0;
processDead();
}
}
/*
* Sends a message to the WM to show this window on all the desktops
*/
void QTrayLabel::showOnAllDesktops(void)
{
TRACE("Showing on all desktops");
Display *d = QPaintDevice::x11AppDisplay();
long l[5] = { -1, 0, 0, 0, 0 }; // -1 = all, 0 = Desktop1, 1 = Desktop2 ...
sendMessage(d, qt_xrootwin(), mDockedWindow, "_NET_WM_DESKTOP", 32,
SubstructureNotifyMask | SubstructureRedirectMask, l, sizeof(l));
}
// System tray messages
const long SYSTEM_TRAY_REQUEST_DOCK = 0;
const long SYSTEM_TRAY_BEGIN_MESSAGE = 1;
const long SYSTEM_TRAY_CANCEL_MESSAGE = 2;
/*
* Add the window to the system tray. Different WM require different hints to be
* set. We support the following (Google for more information),
* 1. GNOME - SYSTEM_TRAY_REQUEST_DOCK (freedesktop.org)
* 2. KDE 3.x and above - _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR
* 3. Older KDE - KWM_DOCKWINDOW (Untested)
*/
void QTrayLabel::dock(void)
{
TRACE("%s", me());
mDocked = true;
if (mDockedWindow == None) return; // nothing to add
if (mSysTray == None) // no system tray yet
{
TRACE("%s starting reality monitor", me());
mRealityMonitor.start(500);
return;
}
Display *display = QPaintDevice::x11AppDisplay();
Window wid = winId();
// 1. GNOME and NET WM Specification
long l[5] = { CurrentTime, SYSTEM_TRAY_REQUEST_DOCK, wid, 0, 0 };
sendMessage(display, mSysTray, mSysTray, "_NET_SYSTEM_TRAY_OPCODE",
32, 0L, l, sizeof(l));
// 2. KDE 3.x and above
Atom tray_atom =
XInternAtom(display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False);
XChangeProperty(display, wid, tray_atom, XA_WINDOW, 32,
PropModeReplace, (unsigned char *) &wid, 1);
// 3. All other KDEs
tray_atom = XInternAtom(display, "KWM_DOCKWINDOW", False);
XChangeProperty(display, wid, tray_atom, XA_WINDOW, 32,
PropModeReplace, (unsigned char *) &wid, 1);
TRACE("%s ", me());
handleTitleChange();
handleIconChange();
if (mProgName.count() == 0) setAppName(mClass);
/*
* For Gnome, a delay is required before we do a show (dont ask me why)
* If I do a show() without any delay, sometimes the icon has width=1 pixel
* even though the minimumSizeHint = 24, 24. I have successfully got it
* working with with a delay of as little as 50ms. But since I
* dont understand why this delay is required, I am justifiably paranoid
*/
QTimer::singleShot(500, this, SLOT(show()));
// let the world know
emit docked(this);
emit docked();
}
/*
* Undocks. Removes us from the system tray. The spec doesnt say how an icon
* can be removed from the tray. KDE Spec says XUnmapWindow or XWithdraw should
* be used. It works but the system tray does not fill the void that we left
* in the tray. Looks like the system tray will resize only for DestroyEvents
*/
void QTrayLabel::undock(void)
{
TRACE("%s stopping reality monitor", me());
mRealityMonitor.stop();
XUnmapWindow(QPaintDevice::x11AppDisplay(), winId());
emit undocked(this);
emit undocked();
}
/*
* Maps the window from the same place it was withdrawn from
*/
void QTrayLabel::map(void)
{
TRACE("%s", me());
mWithdrawn = false;
if (mDockedWindow == None) return;
Display *display = QPaintDevice::x11AppDisplay();
if (mDesktop == -1)
{
/*
* We track _NET_WM_DESKTOP changes in the x11EventFilter. Its used here.
* _NET_WM_DESKTOP is set by the WM to the active desktop for newly
* mapped windows (like this one) at some point in time. We give
* the WM 200ms to do that. We will override that value to -1 (all
* desktops) on showOnAllDesktops().
*/
QTimer::singleShot(200, this, SLOT(showOnAllDesktops()));
}
/*
* A simple XMapWindow would not do. Some applications like xmms wont
* redisplay its other windows (like the playlist, equalizer) since the
* Withdrawn->Normal state change code does not map them. So we make the
* window go through Withdrawn->Iconify->Normal state.
*/
XWMHints *wm_hint = XGetWMHints(display, mDockedWindow);
if (wm_hint)
{
wm_hint->initial_state = IconicState;
XSetWMHints(display, mDockedWindow, wm_hint);
XFree(wm_hint);
}
XMapWindow(display, mDockedWindow);
mSizeHint.flags = USPosition; // Obsolete ?
XSetWMNormalHints(display, mDockedWindow, &mSizeHint);
// make it the active window
long l[5] = { None, CurrentTime, None, 0, 0 };
sendMessage(display, qt_xrootwin(), mDockedWindow, "_NET_ACTIVE_WINDOW", 32,
SubstructureNotifyMask | SubstructureRedirectMask, l, sizeof(l));
// skipTaskbar modifies _NET_WM_STATE. Make sure we dont override WMs value
QTimer::singleShot(230, this, SLOT(skipTaskbar()));
// disable docking when minized for some time (since we went to Iconic state)
mDockWhenMinimized = !mDockWhenMinimized;
QTimer::singleShot(230, this, SLOT(toggleDockWhenMinimized()));
}
void QTrayLabel::withdraw(void)
{
TRACE("%s", me());
mWithdrawn = true;
if (mDockedWindow == None) return;
Display *display = QPaintDevice::x11AppDisplay();
int screen = DefaultScreen(display);
long dummy;
XGetWMNormalHints(display, mDockedWindow, &mSizeHint, &dummy);
/*
* A simple call to XWithdrawWindow wont do. Here is what we do:
* 1. Iconify. This will make the application hide all its other windows. For
* example, xmms would take off the playlist and equalizer window.
* 2. Next tell the WM, that we would like to go to withdrawn state. Withdrawn
* state will remove us from the taskbar.
* Reference: ICCCM 4.1.4 Changing Window State
*/
XIconifyWindow(display, mDockedWindow, screen); // good for effects too
XUnmapWindow(display, mDockedWindow);
XUnmapEvent ev;
memset(&ev, 0, sizeof(ev));
ev.type = UnmapNotify;
ev.display = display;
ev.event = qt_xrootwin();
ev.window = mDockedWindow;
ev.from_configure = false;
XSendEvent(display, qt_xrootwin(), False,
SubstructureRedirectMask|SubstructureNotifyMask, (XEvent *)&ev);
XSync(display, False);
}
/*
* Skipping the taskbar is a bit painful. Basically, NET_WM_STATE needs to
* have _NET_WM_STATE_SKIP_TASKBAR. NET_WM_STATE needs to be updated
* carefully since it is a set of states.
*/
void QTrayLabel::skipTaskbar(void)
{
Atom __attribute__ ((unused)) type;
int __attribute__ ((unused)) format;
unsigned long __attribute__ ((unused)) left;
Atom *data = NULL;
unsigned long nitems = 0, num_states = 0;
Display *display = QPaintDevice::x11AppDisplay();
TRACE("%s", me());
Atom _NET_WM_STATE = XInternAtom(display, "_NET_WM_STATE", True);
Atom skip_atom = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
int ret = XGetWindowProperty(display, mDockedWindow, _NET_WM_STATE, 0,
20, False, AnyPropertyType, &type, &format,
&nitems, &left, (unsigned char **) &data);
Atom *old_states = (Atom *) data;
bool append = true, replace = false;
if ((ret == Success) && data)
{
// Search for the skip_atom. Stop when found
for (num_states = 0; num_states < nitems; num_states++)
if (old_states[num_states] == skip_atom) break;
if (mSkippingTaskbar)
append = (num_states >= nitems);
else
{
if (num_states < nitems)
{
replace = true; // need to remove skip_atom
for (; num_states < nitems - 1; num_states++)
old_states[num_states] = old_states[num_states + 1];
}
}
XFree(data);
}
TRACE("%s SkippingTaskar=%i append=%i replace=%i", me(), mSkippingTaskbar,
append, replace);
if (mSkippingTaskbar)
{
if (append)
XChangeProperty(display, mDockedWindow, _NET_WM_STATE, XA_ATOM, 32,
PropModeAppend, (unsigned char *) &skip_atom, 1);
}
else if (replace)
XChangeProperty(display, mDockedWindow, _NET_WM_STATE, XA_ATOM, 32,
PropModeReplace, (unsigned char *) &old_states, nitems - 1);
}
void QTrayLabel::setSkipTaskbar(bool skip)
{
TRACE("%s Skip=%i", me(), skip);
mSkippingTaskbar = skip;
if (mDockedWindow != None && !mWithdrawn) skipTaskbar();
}
/*
* Closes a window by sending _NET_CLOSE_WINDOW. For reasons best unknown we
* need to first map and then send the request.
*/
void QTrayLabel::close(void)
{
TRACE("%s", me());
Display *display = QPaintDevice::x11AppDisplay();
long l[5] = { 0, 0, 0, 0, 0 };
map();
sendMessage(display, qt_xrootwin(), mDockedWindow, "_NET_CLOSE_WINDOW", 32,
SubstructureNotifyMask | SubstructureRedirectMask,
l, sizeof(l));
}
/*
* Sets the tray icon. If the icon failed to load, we revert to application icon
*/
void QTrayLabel::setTrayIcon(const QString& icon)
{
mCustomIcon = icon;
if (QPixmap(mCustomIcon).isNull()) mCustomIcon = QString::null;
TRACE("%s mCustomIcon=%s", me(), mCustomIcon.latin1());
updateIcon();
}
/*
* Sets the docked window to w.
* A) Start/stop reality timer.
* B) Subscribe/Unsubscribe for root/w notifications as appropriate
* C) And of course, dock the window and apply some settings
*/
void QTrayLabel::setDockedWindow(Window w)
{
TRACE("%s %s reality monitor", me(),
mDockedWindow==None ? "Starting" : "Stopping");
// Check if we are allowed to dock this window (allows custom rules)
if (w != None) mDockedWindow = canDockWindow(w) ? w : None;
else mDockedWindow = None;
if (mDockedWindow == None) mRealityMonitor.start(500);
else mRealityMonitor.stop();
Display *d = QPaintDevice::x11AppDisplay();
// Subscribe for window or root window events
if (w == None) subscribe(d, None, SubstructureNotifyMask, true);
else
{
if (canUnsubscribeFromRoot())
subscribe(d, None, ~SubstructureNotifyMask, false);
else subscribe(d, None, SubstructureNotifyMask, true);
subscribe(d, w,
StructureNotifyMask | PropertyChangeMask |
VisibilityChangeMask | FocusChangeMask,
true);
}
if (mDocked && w!=None)
{
// store the desktop on which the window is being shown
getCardinalProperty(d, mDockedWindow,
XInternAtom(d, "_NET_WM_DESKTOP", True), &mDesktop);
if (mWithdrawn)
// show the window for sometime before docking
QTimer::singleShot(1000, this, SLOT(withdraw()));
else map();
dock();
}
}
/*
* Balloon text. Overload this if you dont like the way things are ballooned
*/
void QTrayLabel::balloonText()
{
TRACE("%s BalloonText=%s ToolTipText=%s", me(),
mBalloon->text().latin1(), QToolTip::textFor(this).latin1());
if (mBalloon->text() == QToolTip::textFor(this)) return;
#if 0 // I_GOT_NETWM_BALLOONING_TO_WORK
// if you can get NET WM ballooning to work let me know
static int id = 1;
long l[5] = { CurrentTime, SYSTEM_TRAY_BEGIN_MESSAGE, 2000,
mTitle.length(), id++
};
sendMessage(display, mSystemTray, winId(), "_NET_SYSTEM_TRAY_OPCODE", 32,
SubstructureNotifyMask | SubstructureRedirectMask,
l, sizeof(l));
int length = mTitle.length();
const char *data = mTitle.latin1();
while (length > 0)
{
sendMessage(display, mSystemTray, winId(), "_NET_SYSTEM_TRAY_MESSAGE_DATA", 8,
SubstructureNotifyMask | SubstructureRedirectMask,
(void *) data, length > 20 ? 20 : length);
length -= 20;
data += 20;
}
#else
// Manually do ballooning. See the Qt ToolTip code
QString oldText = mBalloon->text();
mBalloon->setText(QToolTip::textFor(this));
if (oldText.isEmpty()) return; // dont tool tip the first time
QPoint p = mapToGlobal(QPoint(0, -1 - mBalloon->height()));
if (p.x() + mBalloon->width() > QApplication::desktop()->width())
p.setX(p.x() + width() - mBalloon->width());
if (p.y() < 0) p.setY(height() + 1);
mBalloon->move(p);
mBalloon->show();
QTimer::singleShot(mBalloonTimeout, mBalloon, SLOT(hide()));
#endif
}
/*
* Update the title in the menu. Balloon the title change if necessary
*/
void QTrayLabel::handleTitleChange(void)
{
Display *display = QPaintDevice::x11AppDisplay();
char *window_name = NULL;
XFetchName(display, mDockedWindow, &window_name);
mTitle = window_name;
TRACE("%s has title [%s]", me(), mTitle.latin1());
if (window_name) XFree(window_name);
XClassHint ch;
if (XGetClassHint(display, mDockedWindow, &ch))
{
if (ch.res_class) mClass = QString(ch.res_class);
else if (ch.res_name) mClass = QString(ch.res_name);
if (ch.res_class) XFree(ch.res_class);
if (ch.res_name) XFree(ch.res_name);
}
updateTitle();
if (mBalloonTimeout) balloonText();
}
/*
* Overload this if you want a tool tip format that is different from the one
* below i.e "Title [Class]".
*/
void QTrayLabel::updateTitle()
{
TRACE("%s", me());
QString text = mTitle + " [" + mClass + "]";
QToolTip::remove(this);
QToolTip::add(this, text);
if (mBalloonTimeout) balloonText();
}
void QTrayLabel::handleIconChange(void)
{
char **window_icon = NULL;
TRACE("%s", me());
if (mDockedWindow == None) return;
Display *display = QPaintDevice::x11AppDisplay();
XWMHints *wm_hints = XGetWMHints(display, mDockedWindow);
if (wm_hints != NULL)
{
if (!(wm_hints->flags & IconMaskHint))
wm_hints->icon_mask = None;
/*
* We act paranoid here. Progams like KSnake has a bug where
* IconPixmapHint is set but no pixmap (Actually this happens with
* quite a few KDE programs) X-(
*/
if ((wm_hints->flags & IconPixmapHint) && (wm_hints->icon_pixmap))
XpmCreateDataFromPixmap(display, &window_icon, wm_hints->icon_pixmap,
wm_hints->icon_mask, NULL);
XFree(wm_hints);
}
QImage image;
if (!window_icon)
{
if (!image.load(QString(ICONS_PATH) + "/question.png"))
image.load(qApp->applicationDirPath() + "/icons/question.png");
}
else image = QPixmap((const char **) window_icon).convertToImage();
if (window_icon) XpmFree(window_icon);
mAppIcon = image.smoothScale(24, 24); // why?
setMinimumSize(mAppIcon.size());
setMaximumSize(mAppIcon.size());
updateIcon();
}
/*
* Overload this to possibly do operations on the pixmap before it is set
*/
void QTrayLabel::updateIcon()
{
TRACE("%s", me());
setPixmap(mCustomIcon.isEmpty() ? mAppIcon : mCustomIcon);
erase();
QPaintEvent pe(rect());
paintEvent(&pe);
}
/*
* Mouse activity on our label. RightClick = Menu. LeftClick = Toggle Map
*/
void QTrayLabel::mouseReleaseEvent(QMouseEvent * ev)
{
emit clicked(ev->button(), ev->globalPos());
}
/*
* Track drag event
*/
void QTrayLabel::dragEnterEvent(QDragEnterEvent *ev)
{
ev->accept();
map();
}
/*
* Event dispatcher
*/
bool QTrayLabel::x11EventFilter(XEvent *ev)
{
XAnyEvent *event = (XAnyEvent *)ev;
if (event->window == mSysTray)
{
if (event->type != DestroyNotify) return FALSE; // not interested in others
emit sysTrayDestroyed();
mSysTray = None;
TRACE("%s SystemTray disappeared. Starting timer", me());
mRealityMonitor.start(500);
return true;
}
else if (event->window == mDockedWindow)
{
if (event->type == DestroyNotify)
destroyEvent();
else if (event->type == PropertyNotify)
propertyChangeEvent(((XPropertyEvent *)event)->atom);
else if (event->type == VisibilityNotify)
{
if (((XVisibilityEvent *) event)->state == VisibilityFullyObscured)
obscureEvent();
}
else if (event->type == MapNotify)
{
mWithdrawn = false;
mapEvent();
}
else if (event->type == UnmapNotify)
{
mWithdrawn = true;
unmapEvent();
}
else if (event->type == FocusOut)
{
focusLostEvent();
}
return true; // Dont process this again
}
if (mDockedWindow != None || event->type != MapNotify) return FALSE;
TRACE("%s Will analyze window 0x%x", me(), (int)((XMapEvent *)event)->window);
// Check if this window is the soulmate we are looking for
Display *display = QPaintDevice::x11AppDisplay();
Window w = XmuClientWindow(display, ((XMapEvent *) event)->window);
if (!isNormalWindow(display, w)) return FALSE;
if (!analyzeWindow(display, w, mPid,
QFileInfo(mProgName[0]).fileName().latin1())) return FALSE;
// All right. Lets dock this baby
setDockedWindow(w);
return true;
}
void QTrayLabel::minimizeEvent(void)
{
TRACE("minimizeEvent");
if (mDockWhenMinimized) withdraw();
}
void QTrayLabel::destroyEvent(void)
{
TRACE("%s destroyEvent", me());
setDockedWindow(None);
if (!mPid) undock();
}
void QTrayLabel::propertyChangeEvent(Atom property)
{
Display *display = QPaintDevice::x11AppDisplay();
static Atom WM_NAME = XInternAtom(display, "WM_NAME", True);
static Atom WM_ICON = XInternAtom(display, "WM_ICON", True);
static Atom WM_STATE = XInternAtom(display, "WM_STATE", True);
static Atom _NET_WM_STATE = XInternAtom(display, "_NET_WM_STATE", True);
static Atom _NET_WM_DESKTOP = XInternAtom(display, "_NET_WM_DESKTOP", True);
if (property == WM_NAME)
handleTitleChange();
else if (property == WM_ICON)
handleIconChange();
else if (property == _NET_WM_STATE)
; // skipTaskbar();
else if (property == _NET_WM_DESKTOP)
{
TRACE("_NET_WM_DESKTOP changed");
getCardinalProperty(display, mDockedWindow, _NET_WM_DESKTOP, &mDesktop);
}
else if (property == WM_STATE)
{
Atom type = None;
int format;
unsigned long nitems, after;
unsigned char *data = NULL;
int r = XGetWindowProperty(display, mDockedWindow, WM_STATE,
0, 1, False, AnyPropertyType, &type,
&format, &nitems, &after, &data);
if ((r == Success) && data && (*(long *) data == IconicState))
{
minimizeEvent();
XFree(data);
}
}
}
// Session Management
bool QTrayLabel::saveState(QSettings &settings)
{
TRACE("%s saving state", me());
settings.writeEntry("/Application", mProgName.join(" "));
settings.writeEntry("/CustomIcon", mCustomIcon);
settings.writeEntry("/BalloonTimeout", mBalloonTimeout);
settings.writeEntry("/DockWhenMinimized", mDockWhenMinimized);
settings.writeEntry("/SkipTaskbar", mSkippingTaskbar);
settings.writeEntry("/Withdraw", mWithdrawn);
return true;
}
bool QTrayLabel::restoreState(QSettings &settings)
{
TRACE("%s restoring state", me());
mCustomIcon = settings.readEntry("/CustomIcon");
setBalloonTimeout(settings.readNumEntry("/BalloonTimeout"));
setDockWhenMinimized(settings.readBoolEntry("/DockWhenMinimized"));
setSkipTaskbar(settings.readBoolEntry("/SkipTaskbar"));
mWithdrawn = settings.readBoolEntry("/Withdraw");
dock();
/*
* Since we are getting restored, it is likely that the application that we
* are interested in has already been started (if we didnt launch it).
* So we scan the list of windows and grab the first one that satisfies us
* This implicitly assumes that if mPid!=0 then we launched it. Wait till
* the application really shows itself up before we do a scan (the reason
* why we have 2s
*/
if (!mPid) QTimer::singleShot(2000, this, SLOT(scanClients()));
return true;
}
// End kicking butt