/* * 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: customtraylabel.cpp,v 1.14 2005/06/21 10:04:35 cs19713 Exp $ #include #include #include #include #include #include #include #include #include #include "trace.h" #include "customtraylabel.h" #include "traylabelmgr.h" #include "kdocker.h" CustomTrayLabel::CustomTrayLabel(Window w, QWidget* p, const QString& t) : QTrayLabel(w, p, t), mUndockWhenDead(false) { installMenu(); } CustomTrayLabel::CustomTrayLabel(const QStringList& argv, pid_t pid, QWidget* parent) : QTrayLabel(argv, pid, parent), mUndockWhenDead(false) { installMenu(); } /* * Installs a popup menu on the tray label */ void CustomTrayLabel::installMenu() { QPixmap kdocker_png(QString(ICONS_PATH) + "/kdocker.png"); if (kdocker_png.isNull()) kdocker_png.load(qApp->applicationDirPath() + "/icons/kdocker.png"); setIcon(kdocker_png); TrayLabelMgr *tlMgr = TrayLabelMgr::instance(); mOptionsMenu = new QPopupMenu(this); mSessionManagement = new QAction(tr("Dock when session restored"), 0, this); mSessionManagement->setToggleAction(true); connect(mSessionManagement, SIGNAL(toggled(bool)), this, SLOT(enableSessionManagement(bool))); mSessionManagement->addTo(mOptionsMenu); mAutoLaunch = new QAction(tr("Launch on startup"), 0, this); mAutoLaunch->setToggleAction(true); connect(mAutoLaunch, SIGNAL(activated()), this, SLOT(slotSetLaunchOnStartup())); mAutoLaunch->addTo(mOptionsMenu); mOptionsMenu->insertItem(tr("Set Icon"), this, SLOT(setCustomIcon())); mBalloonTimeout = new QAction(tr("Set balloon timeout"), 0, this); connect(mBalloonTimeout, SIGNAL(activated()), this, SLOT(slotSetBalloonTimeout())); mBalloonTimeout->addTo(mOptionsMenu); mDockWhenObscured = new QAction(tr("Dock when obscured"), 0, this); mDockWhenObscured->setToggleAction(true); connect(mDockWhenObscured, SIGNAL(toggled(bool)), this, SLOT(setDockWhenObscured(bool))); mDockWhenObscured->addTo(mOptionsMenu); mDockWhenMinimized = new QAction(tr("Dock when minimized"), 0, this); mDockWhenMinimized->setToggleAction(true); connect(mDockWhenMinimized, SIGNAL(toggled(bool)), this, SLOT(setDockWhenMinimized(bool))); mDockWhenMinimized->addTo(mOptionsMenu); mDockWhenFocusLost = new QAction(tr("Dock when focus lost"), 0, this); mDockWhenFocusLost->setToggleAction(true); connect(mDockWhenFocusLost, SIGNAL(toggled(bool)), this, SLOT(setDockWhenFocusLost(bool))); mDockWhenFocusLost->addTo(mOptionsMenu); mSkipTaskbar = new QAction(tr("Skip taskbar"), 0, this); mSkipTaskbar->setToggleAction(true); connect(mSkipTaskbar, SIGNAL(toggled(bool)), this, SLOT(setSkipTaskbar(bool))); mSkipTaskbar->addTo(mOptionsMenu); mMainMenu = new QPopupMenu(this); mMainMenu->insertItem(QIconSet(kdocker_png), tr("About KDocker"), tlMgr, SLOT(about())); mMainMenu->insertSeparator(); mMainMenu->insertItem(tr("Options"), mOptionsMenu); mMainMenu->insertItem(tr("Dock Another"), tlMgr, SLOT(dockAnother())); mMainMenu->insertItem(tr("Undock All"), tlMgr, SLOT(undockAll())); mMainMenu->insertSeparator(); mShowId = mMainMenu->insertItem(QString("Show/Hide [untitled]"), this, SLOT(toggleShow())); mMainMenu->insertItem(QString(tr("Undock")), this, SLOT(undock())); mMainMenu->insertItem(QString(tr("Close")), this, SLOT(close())); connect(mMainMenu, SIGNAL(aboutToShow()), this, SLOT(updateMenu())); // Apply defaults here setLaunchOnStartup(false); setDockWhenObscured(false); enableSessionManagement(true); mDockWhenMinimized->setOn(isDockWhenMinimized()); mSkipTaskbar->setOn(isSkippingTaskbar()); setAcceptDrops(true); // and you thought this function only installs the menu } /* * Session Management */ bool CustomTrayLabel::restoreState(QSettings& settings) { mAutoLaunch->setOn(settings.readBoolEntry("/LaunchOnStartup")); setDockWhenObscured(settings.readBoolEntry("/DockWhenObscured")); TRACE("AutoLaunch=%i DWM=%i DWO=%i", isLaunchOnStartup(), isDockWhenMinimized(), isDockWhenObscured()); return QTrayLabel::restoreState(settings); } bool CustomTrayLabel::saveState(QSettings& settings) { if (!mSessionManagement->isOn()) return false; QTrayLabel::saveState(settings); settings.writeEntry("/LaunchOnStartup", isLaunchOnStartup()); settings.writeEntry("/DockWhenObscured", isDockWhenObscured()); TRACE("AutoLaunch=%i DWM=%i DWO=%i", isLaunchOnStartup(), isDockWhenMinimized(), isDockWhenObscured()); return true; } static bool which(const char *app) { if (access(app, X_OK) == 0) return true; // Check if the program exist in the $PATH char *path = strdup(getenv("PATH")); char prog[300]; if (path == NULL) return false; TRACE("PATH=%s", path); char *p = strtok(path, ":"); while (p != NULL) { snprintf(prog, sizeof(prog), "%s/%s", p, app); if (access(prog, X_OK) == 0) break; p = strtok(NULL, ":"); } free(path); TRACE("Located at (%s)", p); return p != NULL; } // Overridden to update our menu void CustomTrayLabel::setDockWhenMinimized(bool dwm) { QTrayLabel::setDockWhenMinimized(dwm); mDockWhenMinimized->setOn(isDockWhenMinimized()); } void CustomTrayLabel::setSkipTaskbar(bool skip) { QTrayLabel::setSkipTaskbar(skip); mSkipTaskbar->setOn(isSkippingTaskbar()); } void CustomTrayLabel::setAppName(const QString& name) { QTrayLabel::setAppName(name.lower()); } /* * This function is called when QTrayLabel wants to know whether it can * unsubscribe from root window. This is because it doesnt know if someone * else is interested in root window events */ bool CustomTrayLabel::canUnsubscribeFromRoot(void) { return (TrayLabelMgr::instance())->hiddenLabelsCount() == 0; } // Get icon from user, load it and if successful load it. void CustomTrayLabel::setCustomIcon(void) { QString icon; while (true) { // Nag the user to give us a valid icon or press cancel icon = QFileDialog::getOpenFileName(); if (icon.isNull()) return; // user cancelled if (!QPixmap(icon).isNull()) break; TRACE("Attempting to set icon to %s", icon.latin1()); QMessageBox::critical(this, tr("KDocker"), tr("%1 is not a valid icon").arg(icon)); } setTrayIcon(icon); } // Get balloon timeout from the user void CustomTrayLabel::slotSetBalloonTimeout(void) { bool ok; int timeout = QInputDialog::getInteger(tr("KDocker"), tr("Enter balloon timeout (secs). 0 to disable ballooning"), balloonTimeout()/1000, 0, 60, 1, &ok); if (!ok) return; setBalloonTimeout(timeout * 1000); } void CustomTrayLabel::setLaunchOnStartup(bool launch) { mAutoLaunch->setOn(launch); slotSetLaunchOnStartup(); // fake an "activated" signal } void CustomTrayLabel::slotSetLaunchOnStartup() { TRACE("%i", mAutoLaunch->isOn()); if (!mAutoLaunch->isOn()) return; QString app = appName(); TRACE("Validating %s", app.latin1()); while (true) { if (which(app.latin1())) { TRACE("Autolaunch enabled to %s", app.latin1()); setAppName(app); mAutoLaunch->setOn(true); return; } // Request user to provide file name himself if (QMessageBox::critical(NULL, tr("KDocker"), tr("\"%1\" is not a valid executable " "or was not found in your $PATH").arg(app), tr("Select program"), tr("Cancel")) == 1) { mAutoLaunch->setOn(false); return; // cancelled } app = QFileDialog::getOpenFileName(); if (app.isNull()) { TRACE("Disabling auto launch"); mAutoLaunch->setOn(false); return; } } } // Called when we are just about to display the menu void CustomTrayLabel::updateMenu(void) { QString title = appClass(); // + "(" + appTitle() + ")"; mMainMenu->changeItem(mShowId, QIconSet(*pixmap()), QString((isWithdrawn() ? tr("Show %1") : tr("Hide %1")).arg(title))); } void CustomTrayLabel::mapEvent(void) { TRACE("mapEvent"); if (mDockWhenObscured->isOn()) { /* * 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->setOn(false); QTimer::singleShot(800, mDockWhenObscured, SLOT(toggle())); TRACE("Turning off DWO for some time"); } } void CustomTrayLabel::obscureEvent(void) { TRACE("obscureEvent"); if (mDockWhenObscured->isOn() && !isWithdrawn()) withdraw(); } void CustomTrayLabel::focusLostEvent() { if (mDockWhenFocusLost->isOn()) withdraw(); } void CustomTrayLabel::mouseReleaseEvent(QMouseEvent * ev) { if (ev->button() == Qt::RightButton) mMainMenu->popup(ev->globalPos()); else toggleShow(); } void CustomTrayLabel::destroyEvent(void) { mUndockWhenDead = true; QTrayLabel::destroyEvent(); } void CustomTrayLabel::processDead(void) { /* * This is a ugly hack but worth every but of ugliness IMO ;). * Lets say, an instance of xmms, already exists. You type kdocker xmms. * KDocker 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(); } /* * Can dock this window iff not docked by another one tray label already */ bool CustomTrayLabel::canDockWindow(Window w) { TRACE("Checking if 0x%x is already docked", (unsigned) w); return !(TrayLabelMgr::instance()->isWindowDocked(w)); } void CustomTrayLabel::dropEvent(QDropEvent *) { QMessageBox::information(NULL, "KDocker", tr("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")); }