|
|
|
/*
|
|
|
|
* 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 TQt includes before X
|
|
|
|
#include <tqstring.h>
|
|
|
|
#include <tqevent.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqinputdialog.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqpoint.h>
|
|
|
|
#include <tqtooltip.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
|
|
|
#include <khelpmenu.h>
|
|
|
|
#include <kiconloader.h>
|
|
|
|
#include <kstdaction.h>
|
|
|
|
#include <kstdguiitem.h>
|
|
|
|
|
|
|
|
#include <tdeconfig.h>
|
|
|
|
#include <tdefiledialog.h>
|
|
|
|
#include <tdeglobal.h>
|
|
|
|
#include <tdelocale.h>
|
|
|
|
#include <tdemessagebox.h>
|
|
|
|
#include <tdepopupmenu.h>
|
|
|
|
|
|
|
|
#include <X11/cursorfont.h>
|
|
|
|
#include <X11/xpm.h>
|
|
|
|
#include <X11/Xmu/WinUtil.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
#include "trace.h"
|
|
|
|
#include "traylabelmgr.h"
|
|
|
|
#include "tqtraylabel.h"
|
|
|
|
|
|
|
|
void TQTrayLabel::initialize(void)
|
|
|
|
{
|
|
|
|
mDocked = false;
|
|
|
|
mWithdrawn = true;
|
|
|
|
mBalloonTimeout = 4000;
|
|
|
|
mUndockWhenDead = false;
|
|
|
|
mDesktop = 666; // setDockedWindow would set it a saner value
|
|
|
|
|
|
|
|
// Balloon's properties are set to match a TQt tool tip (see TQt source)
|
|
|
|
mBalloon = new TQLabel(0, "balloon", WType_TopLevel | WStyle_StaysOnTop |
|
|
|
|
WStyle_Customize | WStyle_NoBorder |
|
|
|
|
WStyle_Tool | WX11BypassWM);
|
|
|
|
mBalloon->setFont(TQToolTip::font());
|
|
|
|
mBalloon->setPalette(TQToolTip::palette());
|
|
|
|
mBalloon->setAlignment(TQt::AlignLeft | TQt::AlignTop);
|
|
|
|
mBalloon->setAutoMask(FALSE);
|
|
|
|
mBalloon->setAutoResize(true);
|
|
|
|
setAlignment(TQt::AlignCenter);
|
|
|
|
setBackgroundMode(X11ParentRelative);
|
|
|
|
|
|
|
|
connect(&mRealityMonitor, TQ_SIGNAL(timeout()), this, TQ_SLOT(realityCheck()));
|
|
|
|
setDockedWindow(mDockedWindow);
|
|
|
|
|
|
|
|
sysTrayStatus(TQPaintDevice::x11AppDisplay(), &mSysTray);
|
|
|
|
// Subscribe to system tray window notifications
|
|
|
|
if (mSysTray != None)
|
|
|
|
subscribe(TQPaintDevice::x11AppDisplay(), mSysTray,
|
|
|
|
StructureNotifyMask, true);
|
|
|
|
|
|
|
|
installMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Describe ourselves in a few words
|
|
|
|
const char *TQTrayLabel::me(void) const
|
|
|
|
{
|
|
|
|
static char temp[100];
|
|
|
|
snprintf(temp, sizeof(temp), "(%s,PID=%i,WID=0x%x)",
|
|
|
|
appName().local8Bit().data(), mPid, (unsigned) mDockedWindow);
|
|
|
|
return temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQTrayLabel::TQTrayLabel(Window w, TQWidget *parent, const TQString &text)
|
|
|
|
: TQLabel(parent, text.utf8(), WStyle_Customize | WStyle_NoBorder | WStyle_Tool),
|
|
|
|
mDockedWindow(w), mPid(0)
|
|
|
|
{
|
|
|
|
initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQTrayLabel::TQTrayLabel(const TQStringList &pname, pid_t pid, TQWidget *parent)
|
|
|
|
: TQLabel(parent, "TrayLabel", WStyle_Customize | WStyle_NoBorder | WStyle_Tool),
|
|
|
|
mDockedWindow(None), mProgName(pname), mPid(pid)
|
|
|
|
{
|
|
|
|
initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQTrayLabel::~TQTrayLabel()
|
|
|
|
{
|
|
|
|
TRACE("%s Goodbye", me());
|
|
|
|
if (mDockedWindow == None) return;
|
|
|
|
// Leave the docked window is some sane state
|
|
|
|
mSkipTaskbar->setChecked(false);
|
|
|
|
skipTaskbar();
|
|
|
|
map();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::setAppName(const TQString &prog)
|
|
|
|
{
|
|
|
|
// FIXME HACK
|
|
|
|
// using "prog.lower()" relies on window and application name being the same.
|
|
|
|
if (mProgName.count() == 0)
|
|
|
|
{
|
|
|
|
mProgName.push_front(prog.lower());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scans the windows in the desktop and checks if a window exists that we might
|
|
|
|
* be interested in
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::scanClients()
|
|
|
|
{
|
|
|
|
Window r, parent, *children;
|
|
|
|
unsigned nchildren = 0;
|
|
|
|
Display *display = TQPaintDevice::x11AppDisplay();
|
|
|
|
TQString ename = TQFileInfo(appName()).fileName(); // strip out the path
|
|
|
|
|
|
|
|
XQueryTree(display, tqt_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(1) 0x%x", me(), (unsigned) w);
|
|
|
|
if (!isNormalWindow(display, w)) continue;
|
|
|
|
if (analyzeWindow(display, w, mPid, ename.local8Bit()))
|
|
|
|
{
|
|
|
|
TRACE("\t%s SOULMATE FOUND (1)", me());
|
|
|
|
setDockedWindow(w);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 TQt (see below)
|
|
|
|
* 3) Checks health of the process whose windows we are docking
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::realityCheck(void)
|
|
|
|
{
|
|
|
|
if (mSysTray == None)
|
|
|
|
{
|
|
|
|
// Check the system tray status if we were docked
|
|
|
|
if (sysTrayStatus(TQPaintDevice::x11AppDisplay(), &mSysTray)
|
|
|
|
!= SysTrayPresent) return; // no luck
|
|
|
|
|
|
|
|
TRACE("%s System tray present", me());
|
|
|
|
dock();
|
|
|
|
subscribe(TQPaintDevice::x11AppDisplay(), mSysTray,
|
|
|
|
StructureNotifyMask, true);
|
|
|
|
mRealityMonitor.stop();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* I am not sure when, but TQt 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
|
|
|
|
* TQt 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 TQt 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 = TQPaintDevice::x11AppDisplay();
|
|
|
|
XWindowAttributes attr;
|
|
|
|
XGetWindowAttributes(display, tqt_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 TQTrayLabel::showOnAllDesktops(void)
|
|
|
|
{
|
|
|
|
TRACE("Showing on all desktops");
|
|
|
|
Display *d = TQPaintDevice::x11AppDisplay();
|
|
|
|
long l[5] = { -1, 0, 0, 0, 0 }; // -1 = all, 0 = Desktop1, 1 = Desktop2 ...
|
|
|
|
sendMessage(d, tqt_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 TQTrayLabel::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 = TQPaintDevice::x11AppDisplay();
|
|
|
|
Window wid = winId();
|
|
|
|
|
|
|
|
// 1. GNOME and NET WM Specification
|
|
|
|
unsigned 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);
|
|
|
|
}
|
|
|
|
mDockWhenRestored->setChecked(true);
|
|
|
|
setDockWhenRestored(true);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
TQTimer::singleShot(500, this, TQ_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 TQTrayLabel::undock(void)
|
|
|
|
{
|
|
|
|
TRACE("%s stopping reality monitor", me());
|
|
|
|
mRealityMonitor.stop();
|
|
|
|
XUnmapWindow(TQPaintDevice::x11AppDisplay(), winId());
|
|
|
|
emit undocked(this);
|
|
|
|
emit undocked();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maps the window from the same place it was withdrawn from
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::map(void)
|
|
|
|
{
|
|
|
|
TRACE("%s", me());
|
|
|
|
mWithdrawn = false;
|
|
|
|
if (mDockedWindow == None) return;
|
|
|
|
|
|
|
|
Display *display = TQPaintDevice::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().
|
|
|
|
*/
|
|
|
|
TQTimer::singleShot(200, this, TQ_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, tqt_xrootwin(), mDockedWindow, "_NET_ACTIVE_WINDOW", 32,
|
|
|
|
SubstructureNotifyMask | SubstructureRedirectMask, l, sizeof(l));
|
|
|
|
// skipTaskbar modifies _NET_WM_STATE. Make sure we dont override WMs value
|
|
|
|
TQTimer::singleShot(230, this, TQ_SLOT(skipTaskbar()));
|
|
|
|
// disable "dock when minized" (if enable) for a short while since we went to Iconic state
|
|
|
|
// (when the window is mapped, often an IconicState WM_STATE message is sent too
|
|
|
|
// just before the NormalState)
|
|
|
|
mSavedDWM = mDockWhenMinimized->isChecked(); // store for later use
|
|
|
|
mDockWhenMinimized->setChecked(false);
|
|
|
|
TQTimer::singleShot(500, this, TQ_SLOT(toggleDockWhenMinimized()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::withdraw(void)
|
|
|
|
{
|
|
|
|
TRACE("%s", me());
|
|
|
|
mWithdrawn = true;
|
|
|
|
if (mDockedWindow == None) return;
|
|
|
|
|
|
|
|
Display *display = TQPaintDevice::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 = tqt_xrootwin();
|
|
|
|
ev.window = mDockedWindow;
|
|
|
|
ev.from_configure = false;
|
|
|
|
XSendEvent(display, tqt_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 TQTrayLabel::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 = TQPaintDevice::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 (mSkipTaskbar->isChecked())
|
|
|
|
{
|
|
|
|
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(),
|
|
|
|
mSkipTaskbar->isChecked(), append, replace);
|
|
|
|
|
|
|
|
if (mSkipTaskbar->isChecked())
|
|
|
|
{
|
|
|
|
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 TQTrayLabel::setSkipTaskbar(bool skip)
|
|
|
|
{
|
|
|
|
TRACE("%s skip=%i", me(), skip);
|
|
|
|
if (skip != mSkipTaskbar->isChecked())
|
|
|
|
{
|
|
|
|
// Make sure the toggle action state is updated in case this function
|
|
|
|
// is called directly from code.
|
|
|
|
mSkipTaskbar->setChecked(skip);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mDockedWindow != None && !mWithdrawn)
|
|
|
|
{
|
|
|
|
skipTaskbar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::toggleShow(void)
|
|
|
|
{
|
|
|
|
if (mWithdrawn)
|
|
|
|
{
|
|
|
|
map();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
withdraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Closes a window by sending _NET_CLOSE_WINDOW. For reasons best unknown we
|
|
|
|
* need to first map and then send the request.
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::close(void)
|
|
|
|
{
|
|
|
|
TRACE("%s", me());
|
|
|
|
undock();
|
|
|
|
Display *display = TQPaintDevice::x11AppDisplay();
|
|
|
|
long l[5] = { 0, 0, 0, 0, 0 };
|
|
|
|
map();
|
|
|
|
sendMessage(display, tqt_xrootwin(), mDockedWindow, "_NET_CLOSE_WINDOW", 32,
|
|
|
|
SubstructureNotifyMask | SubstructureRedirectMask,
|
|
|
|
l, sizeof(l));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function is called when TQTrayLabel wants to know whether it can
|
|
|
|
* unsubscribe from the root window. This is because it doesn't know if someone
|
|
|
|
* else is interested in root window events
|
|
|
|
*/
|
|
|
|
bool TQTrayLabel::canUnsubscribeFromRoot(void)
|
|
|
|
{
|
|
|
|
return (TrayLabelMgr::instance())->hiddenLabelsCount() == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets the tray icon. If the icon failed to load, we revert to application icon
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::setTrayIcon(const TQString& icon)
|
|
|
|
{
|
|
|
|
mCustomIcon = icon;
|
|
|
|
if (TQPixmap(mCustomIcon).isNull()) mCustomIcon = TQString::null;
|
|
|
|
TRACE("%s mCustomIcon=%s", me(), mCustomIcon.local8Bit());
|
|
|
|
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 TQTrayLabel::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)
|
|
|
|
mDockedWindow = None;
|
|
|
|
if (w != None)
|
|
|
|
{
|
|
|
|
if (!(TrayLabelMgr::instance()->isWindowDocked(w)))
|
|
|
|
{
|
|
|
|
mDockedWindow = w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mDockedWindow == None) mRealityMonitor.start(500); else mRealityMonitor.stop();
|
|
|
|
|
|
|
|
Display *d = TQPaintDevice::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
|
|
|
|
TQTimer::singleShot(500, this, TQ_SLOT(withdraw()));
|
|
|
|
}
|
|
|
|
else map();
|
|
|
|
dock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Balloon text. Overload this if you dont like the way things are ballooned
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::balloonText()
|
|
|
|
{
|
|
|
|
TRACE("%s BalloonText=%s ToolTipText=%s", me(),
|
|
|
|
mBalloon->text().local8Bit(), TQToolTip::textFor(this).local8Bit());
|
|
|
|
|
|
|
|
if (mBalloon->text() == TQToolTip::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.local8Bit();
|
|
|
|
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 TQt ToolTip code
|
|
|
|
TQString oldText = mBalloon->text();
|
|
|
|
mBalloon->setText(TQToolTip::textFor(this));
|
|
|
|
if (oldText.isEmpty()) return; // dont tool tip the first time
|
|
|
|
TQPoint p = mapToGlobal(TQPoint(0, -1 - mBalloon->height()));
|
|
|
|
if (p.x() + mBalloon->width() > TQApplication::desktop()->width())
|
|
|
|
p.setX(p.x() + width() - mBalloon->width());
|
|
|
|
|
|
|
|
if (p.y() < 0) p.setY(height() + 1);
|
|
|
|
|
|
|
|
mBalloon->move(p);
|
|
|
|
mBalloon->show();
|
|
|
|
TQTimer::singleShot(mBalloonTimeout, mBalloon, TQ_SLOT(hide()));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the title in the menu. Balloon the title change if necessary
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::handleTitleChange(void)
|
|
|
|
{
|
|
|
|
Display *display = TQPaintDevice::x11AppDisplay();
|
|
|
|
char *window_name = NULL;
|
|
|
|
|
|
|
|
XFetchName(display, mDockedWindow, &window_name);
|
|
|
|
mTitle = window_name;
|
|
|
|
TRACE("%s has title [%s]", me(), mTitle.local8Bit());
|
|
|
|
if (window_name) XFree(window_name);
|
|
|
|
|
|
|
|
XClassHint ch;
|
|
|
|
if (XGetClassHint(display, mDockedWindow, &ch))
|
|
|
|
{
|
|
|
|
if (ch.res_class) mClass = TQString(ch.res_class);
|
|
|
|
else if (ch.res_name) mClass = TQString(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 TQTrayLabel::updateTitle()
|
|
|
|
{
|
|
|
|
TRACE("%s", me());
|
|
|
|
TQString text = mTitle + " [" + mClass + "]";
|
|
|
|
TQToolTip::remove(this);
|
|
|
|
TQToolTip::add(this, text);
|
|
|
|
|
|
|
|
if (mBalloonTimeout) balloonText();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::handleIconChange(void)
|
|
|
|
{
|
|
|
|
char **window_icon = NULL;
|
|
|
|
|
|
|
|
TRACE("%s", me());
|
|
|
|
if (mDockedWindow == None) return;
|
|
|
|
|
|
|
|
Display *display = TQPaintDevice::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);
|
|
|
|
}
|
|
|
|
TQImage image;
|
|
|
|
if (!window_icon)
|
|
|
|
{
|
|
|
|
image = TDEGlobal::iconLoader()->loadIcon("question", TDEIcon::NoGroup, TDEIcon::SizeMedium);
|
|
|
|
}
|
|
|
|
else image = TQPixmap((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 TQTrayLabel::updateIcon()
|
|
|
|
{
|
|
|
|
TRACE("%s", me());
|
|
|
|
setPixmap(mCustomIcon.isEmpty() ? mAppIcon : mCustomIcon);
|
|
|
|
erase();
|
|
|
|
TQPaintEvent pe(rect());
|
|
|
|
paintEvent(&pe);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mouse activity on our label. RightClick = Menu. LeftClick = Toggle Map
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::mouseReleaseEvent(TQMouseEvent *ev)
|
|
|
|
{
|
|
|
|
if (ev->button() == TQt::RightButton)
|
|
|
|
{
|
|
|
|
mMainMenu->popup(ev->globalPos());
|
|
|
|
/* contextMenuAboutToShow(contextMenu());
|
|
|
|
contextMenu()->popup(e->globalPos());
|
|
|
|
e->accept();
|
|
|
|
return;*/
|
|
|
|
}
|
|
|
|
else
|
|
|
|
toggleShow();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Track drag event
|
|
|
|
*/
|
|
|
|
void TQTrayLabel::dragEnterEvent(TQDragEnterEvent *ev)
|
|
|
|
{
|
|
|
|
ev->accept();
|
|
|
|
map();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::dropEvent(TQDropEvent *)
|
|
|
|
{
|
|
|
|
KMessageBox::error(NULL, i18n("You cannot drop an item into the tray icon. Drop it on the window\n"
|
|
|
|
"that is brought in front when you hover the item over the tray icon"), i18n("TDEDocker"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Event dispatcher
|
|
|
|
*/
|
|
|
|
bool TQTrayLabel::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 = TQPaintDevice::x11AppDisplay();
|
|
|
|
Window w = XmuClientWindow(display, ((XMapEvent *) event)->window);
|
|
|
|
if (!isNormalWindow(display, w)) return FALSE;
|
|
|
|
if (!analyzeWindow(display, w, mPid, TQFileInfo(appName()).fileName().local8Bit()))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// All right. Lets dock this baby
|
|
|
|
setDockedWindow(w);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::destroyEvent(void)
|
|
|
|
{
|
|
|
|
TRACE("%s destroyEvent", me());
|
|
|
|
mUndockWhenDead = true;
|
|
|
|
setDockedWindow(None);
|
|
|
|
if (!mPid)
|
|
|
|
{
|
|
|
|
undock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::focusLostEvent()
|
|
|
|
{
|
|
|
|
if (mDockWhenFocusLost->isChecked())
|
|
|
|
{
|
|
|
|
withdraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::mapEvent(void)
|
|
|
|
{
|
|
|
|
TRACE("mapEvent");
|
|
|
|
if (mDockWhenObscured->isChecked())
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We get a obscured event for the time between the map and focus in of
|
|
|
|
* the window. So we disable it for sometime and reanable.
|
|
|
|
*/
|
|
|
|
mDockWhenObscured->setChecked(false);
|
|
|
|
TQTimer::singleShot(800, mDockWhenObscured, TQ_SLOT(toggle()));
|
|
|
|
TRACE("Turning off DWO for some time");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::minimizeEvent(void)
|
|
|
|
{
|
|
|
|
TRACE("minimizeEvent");
|
|
|
|
if (mDockWhenMinimized->isChecked())
|
|
|
|
{
|
|
|
|
withdraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::obscureEvent(void)
|
|
|
|
{
|
|
|
|
TRACE("obscureEvent");
|
|
|
|
if (mDockWhenObscured->isChecked() && !mWithdrawn)
|
|
|
|
{
|
|
|
|
withdraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::unmapEvent(void)
|
|
|
|
{
|
|
|
|
// NO OP
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::propertyChangeEvent(Atom property)
|
|
|
|
{
|
|
|
|
Display *display = TQPaintDevice::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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::processDead(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This is a ugly hack but worth every but of ugliness IMO ;).
|
|
|
|
* Lets say, an instance of xmms, already exists. You type tdedocker xmms.
|
|
|
|
* TDEDocker launches xmms. xmms cowardly exists seeing its previous instance.
|
|
|
|
* Wouldnt it be nice now to dock the previous instance of xmms automatically.
|
|
|
|
* This is more common than you think (think of session restoration)
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!mUndockWhenDead)
|
|
|
|
{
|
|
|
|
scanClients();
|
|
|
|
if (dockedWindow() != None) return;
|
|
|
|
}
|
|
|
|
undock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TQTrayLabel::setDockWhenRestored(bool dwr)
|
|
|
|
{
|
|
|
|
if (dwr && !mSessionManaged)
|
|
|
|
{
|
|
|
|
// Make sure the TDE action is off if session management was initially disabled by command line
|
|
|
|
mSessionManaged = true;
|
|
|
|
mDockWhenRestored->setChecked(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dwr && appName().isEmpty())
|
|
|
|
{
|
|
|
|
KMessageBox::error(NULL, i18n("No valid application executable file known. \"Dock When Restore\" is not possible."),
|
|
|
|
i18n("TDEDocker"));
|
|
|
|
mDockWhenRestored->setChecked(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get icon from user, load it and if successful load it.
|
|
|
|
void TQTrayLabel::setCustomIcon(void)
|
|
|
|
{
|
|
|
|
TQString icon;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
// Nag the user to give us a valid icon or press cancel
|
|
|
|
icon = KFileDialog::getOpenFileName();
|
|
|
|
if (icon.isEmpty()) return; // user cancelled
|
|
|
|
if (!TQPixmap(icon).isNull()) break;
|
|
|
|
TRACE("Attempting to set icon to %s", icon.local8Bit());
|
|
|
|
KMessageBox::error(this, i18n("%1 is not a valid icon").arg(icon), i18n("TDEDocker"));
|
|
|
|
}
|
|
|
|
|
|
|
|
setTrayIcon(icon);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get balloon timeout from the user
|
|
|
|
void TQTrayLabel::slotSetBalloonTimeout(void)
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
int timeout = TQInputDialog::getInteger(i18n("TDEDocker"),
|
|
|
|
i18n("Enter balloon timeout (secs). 0 to disable ballooning"),
|
|
|
|
balloonTimeout()/1000, 0, 60, 1, &ok);
|
|
|
|
|
|
|
|
if (!ok) return;
|
|
|
|
setBalloonTimeout(timeout * 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Installs a popup menu on the tray label
|
|
|
|
void TQTrayLabel::installMenu()
|
|
|
|
{
|
|
|
|
TQPixmap tdedocker_png(TDEGlobal::iconLoader()->loadIcon("tdedocker", TDEIcon::NoGroup, TDEIcon::SizeSmall));
|
|
|
|
setIcon(tdedocker_png);
|
|
|
|
TrayLabelMgr *tlMgr = TrayLabelMgr::instance();
|
|
|
|
|
|
|
|
mOptionsMenu = new TDEPopupMenu(this);
|
|
|
|
mDockWhenRestored = new TDEToggleAction(i18n("Dock when session restored"), 0, this);
|
|
|
|
connect(mDockWhenRestored, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(setDockWhenRestored(bool)));
|
|
|
|
mDockWhenRestored->plug(mOptionsMenu);
|
|
|
|
|
|
|
|
mOptionsMenu->insertItem(i18n("Set Icon"), this, TQ_SLOT(setCustomIcon()));
|
|
|
|
|
|
|
|
mBalloonTimeoutAction = new TDEAction(i18n("Set balloon timeout"), 0, this);
|
|
|
|
connect(mBalloonTimeoutAction, TQ_SIGNAL(activated()), this, TQ_SLOT(slotSetBalloonTimeout()));
|
|
|
|
mBalloonTimeoutAction->plug(mOptionsMenu);
|
|
|
|
|
|
|
|
mDockWhenObscured = new TDEToggleAction(i18n("Dock when obscured"), 0, this);
|
|
|
|
mDockWhenObscured->plug(mOptionsMenu);
|
|
|
|
|
|
|
|
mDockWhenMinimized = new TDEToggleAction(i18n("Dock when minimized"), 0, this);
|
|
|
|
mDockWhenMinimized->plug(mOptionsMenu);
|
|
|
|
|
|
|
|
mDockWhenFocusLost = new TDEToggleAction(i18n("Dock when focus lost"), 0, this);
|
|
|
|
mDockWhenFocusLost->plug(mOptionsMenu);
|
|
|
|
|
|
|
|
mSkipTaskbar = new TDEToggleAction(i18n("Skip taskbar"), 0, this);
|
|
|
|
connect(mSkipTaskbar, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(setSkipTaskbar(bool)));
|
|
|
|
mSkipTaskbar->plug(mOptionsMenu);
|
|
|
|
|
|
|
|
mMainMenu = new TDEPopupMenu(this);
|
|
|
|
mMainMenu->insertItem(i18n("Options"), mOptionsMenu);
|
|
|
|
mMainMenu->insertItem(i18n("Dock Another"), tlMgr, TQ_SLOT(dockAnother()));
|
|
|
|
mMainMenu->insertItem(i18n("Undock All"), tlMgr, TQ_SLOT(undockAll()));
|
|
|
|
mMainMenu->insertItem(i18n("Quit All"), tlMgr, TQ_SLOT(quitAll()));
|
|
|
|
mMainMenu->insertSeparator();
|
|
|
|
|
|
|
|
mShowId = mMainMenu->insertItem(TQString("Show/Hide [untitled]"), this, TQ_SLOT(toggleShow()));
|
|
|
|
mMainMenu->insertItem(TQString(i18n("Undock")), this, TQ_SLOT(undock()));
|
|
|
|
mMainMenu->insertSeparator();
|
|
|
|
|
|
|
|
mMainMenu->insertItem(SmallIcon("help"),KStdGuiItem::help().text(), (new KHelpMenu(this, TDEGlobal::instance()->aboutData()))->menu(), false);
|
|
|
|
TDEAction *quitAction = KStdAction::quit(this, TQ_SLOT(close()), NULL);
|
|
|
|
quitAction->plug(mMainMenu);
|
|
|
|
|
|
|
|
connect(mMainMenu, TQ_SIGNAL(aboutToShow()), this, TQ_SLOT(updateMenu()));
|
|
|
|
|
|
|
|
// Apply defaults here
|
|
|
|
mDockWhenObscured->setChecked(false);
|
|
|
|
mSessionManaged = true;
|
|
|
|
mDockWhenMinimized->setChecked(true);
|
|
|
|
mSkipTaskbar->setChecked(false);
|
|
|
|
setAcceptDrops(true); // and you thought this function only installs the menu
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called when we are just about to display the menu
|
|
|
|
void TQTrayLabel::updateMenu(void)
|
|
|
|
{
|
|
|
|
TQString title = mClass; // + "(" + mTitle + ")";
|
|
|
|
mMainMenu->changeItem(mShowId, TQIconSet(*pixmap()),
|
|
|
|
TQString((mWithdrawn ? i18n("Show %1") : i18n("Hide %1")).arg(title)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Session Management
|
|
|
|
bool TQTrayLabel::saveState(TDEConfig *config)
|
|
|
|
{
|
|
|
|
TRACE("%s saving state", me());
|
|
|
|
|
|
|
|
if (!mDockWhenRestored->isChecked())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->writeEntry("Application", mProgName.join(" "));
|
|
|
|
config->writeEntry("BalloonTimeout", mBalloonTimeout);
|
|
|
|
config->writeEntry("CustomIcon", mCustomIcon);
|
|
|
|
config->writeEntry("DockWhenFocusLost", mDockWhenFocusLost->isChecked());
|
|
|
|
config->writeEntry("DockWhenMinimized", mDockWhenMinimized->isChecked());
|
|
|
|
config->writeEntry("DockWhenObscured", mDockWhenObscured->isChecked());
|
|
|
|
config->writeEntry("SkipTaskbar", mSkipTaskbar->isChecked());
|
|
|
|
config->writeEntry("Withdraw", mWithdrawn);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TQTrayLabel::restoreState(TDEConfig *config)
|
|
|
|
{
|
|
|
|
TRACE("%s restoring state", me());
|
|
|
|
setBalloonTimeout(config->readNumEntry("BalloonTimeout", 4000));
|
|
|
|
mCustomIcon = config->readEntry("CustomIcon", TQString::null);
|
|
|
|
mDockWhenFocusLost->setChecked(config->readBoolEntry("DockWhenFocusLost", false));
|
|
|
|
mDockWhenMinimized->setChecked(config->readBoolEntry("DockWhenMinimized", true));
|
|
|
|
mDockWhenObscured->setChecked(config->readBoolEntry("DockWhenObscured", false));
|
|
|
|
mSkipTaskbar->setChecked(config->readBoolEntry("SkipTaskbar", false));
|
|
|
|
mWithdrawn = config->readBoolEntry("Withdraw", false);
|
|
|
|
|
|
|
|
dock();
|
|
|
|
scanClients(); // Grab window
|
|
|
|
if (mDockedWindow == None)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (mWithdrawn)
|
|
|
|
{
|
|
|
|
withdraw();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
map();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// End kicking butt
|
|
|
|
|
|
|
|
|
|
|
|
#include "tqtraylabel.moc"
|