/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; isetChecked(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"