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/customtraylabel.cpp

361 lines
11 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: customtraylabel.cpp,v 1.14 2005/06/21 10:04:35 cs19713 Exp $
#include <qsettings.h>
#include <qpopupmenu.h>
#include <qmessagebox.h>
#include <qfiledialog.h>
#include <qinputdialog.h>
#include <qaction.h>
#include <qtimer.h>
#include <qsize.h>
#include <stdlib.h>
#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"));
}