summaryrefslogtreecommitdiffstats
path: root/kicker/extensions/dockbar/dockbarextension.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kicker/extensions/dockbar/dockbarextension.cpp')
-rw-r--r--kicker/extensions/dockbar/dockbarextension.cpp423
1 files changed, 423 insertions, 0 deletions
diff --git a/kicker/extensions/dockbar/dockbarextension.cpp b/kicker/extensions/dockbar/dockbarextension.cpp
new file mode 100644
index 000000000..71b583f35
--- /dev/null
+++ b/kicker/extensions/dockbar/dockbarextension.cpp
@@ -0,0 +1,423 @@
+/*****************************************************************
+
+Copyright (c) 2000 Matthias Elter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#undef Bool // For enable-final
+#include <klocale.h>
+#include <kwinmodule.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kprocess.h>
+#include <kshell.h>
+#include <kwin.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kglobal.h>
+
+#include "dockbarextension.h"
+#include "dockbarextension.moc"
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+extern "C"
+{
+ KDE_EXPORT KPanelExtension* init(QWidget *parent, const QString& configFile)
+ {
+ KGlobal::locale()->insertCatalogue("dockbarextension");
+ return new DockBarExtension(configFile, KPanelExtension::Normal,
+ 0, parent, "dockbarextension");
+ }
+}
+
+DockBarExtension::DockBarExtension(const QString& configFile, Type type,
+ int actions, QWidget *parent, const char *name)
+ : KPanelExtension(configFile, type, actions, parent, name)
+{
+ dragging_container = 0;
+ kwin_module = new KWinModule(this);
+ connect( kwin_module, SIGNAL( windowAdded(WId) ), SLOT( windowAdded(WId) ) );
+ setMinimumSize(DockContainer::sz(), DockContainer::sz());
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ loadContainerConfig();
+}
+
+DockBarExtension::~DockBarExtension()
+{
+ // kill nicely the applets
+ for (DockContainer::Vector::const_iterator it = containers.constBegin();
+ it != containers.constEnd();
+ ++it)
+ {
+ (*it)->kill();
+ }
+
+ if (dragging_container) delete dragging_container;
+}
+
+QSize DockBarExtension::sizeHint(Position p, QSize) const
+{
+ if (p == Left || p == Right)
+ return QSize(DockContainer::sz(), DockContainer::sz() * containers.count());
+ else
+ return QSize(DockContainer::sz() * containers.count(), DockContainer::sz());
+}
+
+void DockBarExtension::resizeEvent(QResizeEvent*)
+{
+ layoutContainers();
+}
+
+
+void DockBarExtension::windowAdded(WId win)
+{
+ // try to read WM_COMMAND
+ int argc;
+ char **argv;
+ QString command;
+ if (XGetCommand(qt_xdisplay(), win, &argv, &argc)) {
+ command = KShell::joinArgs(argv, argc);
+ XFreeStringList(argv);
+ }
+
+ // try to read wm hints
+ WId resIconwin = 0;
+ XWMHints *wmhints = XGetWMHints(qt_xdisplay(), win);
+ if (0 != wmhints) { // we managed to read wm hints
+ // read IconWindowHint
+ bool is_valid = false;
+ /* a good dockapp set the icon hint and the state hint,
+ if it uses its icon, the window initial state must be "withdrawn"
+ if not, then the initial state must be "normal"
+ this filters the problematic Eterm whose initial state is "normal"
+ and which has an iconwin.
+ */
+ if ((wmhints->flags & IconWindowHint) &&
+ (wmhints->flags & StateHint)) {
+ resIconwin = wmhints->icon_window;
+ is_valid = (resIconwin && wmhints->initial_state == 0) ||
+ (resIconwin == 0 && wmhints->initial_state == 1);
+
+ /* an alternative is a window who does not have an icon,
+ but whose initial state is set to "withdrawn". This has been
+ added for wmxmms... I hope it won't swallow to much windows :-/
+ */
+ } else if ((wmhints->flags & IconWindowHint) == 0 &&
+ (wmhints->flags & StateHint)) {
+ is_valid = (wmhints->initial_state == 0);
+ }
+ XFree(wmhints);
+ if (!is_valid)
+ return; // we won't swallow this one
+ }
+ else
+ return;
+
+ // The following if statement was at one point commented out,
+ // without a comment as to why. This caused problems like
+ // Eterm windows getting swallowed whole. So, perhaps now I'll
+ // get bug reports about whatever commenting it out was supposed
+ // to fix.
+ if (resIconwin == 0)
+ resIconwin = win;
+
+ // try to read class hint
+ XClassHint hint;
+ QString resClass, resName;
+ if (XGetClassHint(qt_xdisplay(), win, &hint)) {
+ resName = hint.res_name;
+ resClass = hint.res_class;
+ }
+ else {
+ kdDebug() << "Could not read XClassHint of window " << win << endl;
+ return;
+ }
+ /* withdrawing the window prevents kwin from managing the window,
+ which causes the double-launch bug (one instance from the kwin
+ session, and one from the dockbar) bug when kde is restarted */
+ if (resIconwin != win) {
+ XWithdrawWindow( qt_xdisplay(), win, qt_xscreen() );
+ while( KWin::windowInfo(win, NET::XAWMState).mappingState() != NET::Withdrawn );
+ }
+
+ // add a container
+ embedWindow(resIconwin, command, resName, resClass);
+ saveContainerConfig();
+}
+
+void DockBarExtension::layoutContainers()
+{
+ int i = 0;
+ for (DockContainer::Vector::const_iterator it = containers.constBegin();
+ it != containers.constEnd();
+ ++it)
+ {
+ if (orientation() == Horizontal)
+ (*it)->move(DockContainer::sz() * i, 0);
+ else
+ (*it)->move(0, DockContainer::sz() * i);
+ i++;
+ }
+}
+
+void DockBarExtension::embedWindow(WId win, QString command, QString resName, QString resClass)
+{
+ if (win == 0) return;
+ DockContainer* container = 0;
+ bool ncmd = false;
+
+ for (DockContainer::Vector::const_iterator it = containers.constBegin();
+ it != containers.constEnd();
+ ++it)
+ {
+ DockContainer* c = *it;
+ if (c->embeddedWinId() == 0 &&
+ c->resName() == resName &&
+ c->resClass() == resClass &&
+ (command.isNull() || c->command() == command))
+ {
+ container = c;
+ break;
+ }
+ }
+
+ if (container == 0) {
+ QString cmd = command.isNull() ? resClass : command;
+ if (KStandardDirs::findExe(KShell::splitArgs(cmd).front()).isEmpty())
+ ncmd = true;
+ container = new DockContainer(cmd, this, resName, resClass);
+ addContainer(container);
+ }
+
+ container->embed(win);
+ layoutContainers();
+ emit updateLayout();
+ if (ncmd)
+ container->askNewCommand();
+ }
+
+void DockBarExtension::addContainer(DockContainer* c, int pos)
+{
+ if (pos == -1)
+ {
+ containers.append(c);
+ }
+ else
+ {
+ DockContainer::Vector::iterator it = containers.begin();
+
+ for (int i = 0; i < pos && it != containers.end(); ++i)
+ {
+ ++it;
+ }
+ ++it;
+
+ containers.insert(it, c);
+ }
+ connect(c, SIGNAL(embeddedWindowDestroyed(DockContainer*)),
+ SLOT(embeddedWindowDestroyed(DockContainer*)));
+ connect(c, SIGNAL(settingsChanged(DockContainer*)),
+ SLOT(settingsChanged(DockContainer*)));
+ c->resize(DockContainer::sz(), DockContainer::sz());
+ c->show();
+}
+
+void DockBarExtension::removeContainer(DockContainer* c)
+{
+ DockContainer::Vector::iterator it = qFind(containers.begin(), containers.end(), c);
+
+ if (it == containers.end())
+ {
+ return;
+ }
+
+ containers.erase(it);
+ delete c;
+ layoutContainers();
+}
+
+void DockBarExtension::embeddedWindowDestroyed(DockContainer* c)
+{
+ removeContainer(c);
+ saveContainerConfig();
+ emit updateLayout();
+}
+
+void DockBarExtension::settingsChanged(DockContainer *)
+{
+ saveContainerConfig();
+}
+
+void DockBarExtension::saveContainerConfig()
+{
+ QStringList applet_list;
+ KConfig *conf = config();
+ unsigned count = 0;
+
+ for (DockContainer::Vector::const_iterator it = containers.constBegin();
+ it != containers.constEnd();
+ ++it)
+ {
+ DockContainer* c = *it;
+ if (!c->command().isEmpty())
+ {
+ QString applet_gid = QString("Applet_%1").arg(QString::number(count));
+ applet_list.append(applet_gid);
+ conf->setGroup(applet_gid);
+ conf->writePathEntry("Command", c->command());
+ conf->writePathEntry("resName", c->resName());
+ conf->writeEntry("resClass", c->resClass());
+ ++count;
+ }
+ }
+ conf->setGroup("General");
+ conf->writeEntry("Applets", applet_list);
+ conf->deleteEntry("Commands"); // cleanup old config
+ conf->sync();
+}
+
+void DockBarExtension::loadContainerConfig()
+{
+ KConfig *conf = config();
+ conf->setGroup("General");
+ QStringList applets = conf->readListEntry("Applets");
+
+ QStringList fail_list;
+ for (QStringList::Iterator it = applets.begin(); it != applets.end(); ++it) {
+ if (!conf->hasGroup(*it)) continue;
+ conf->setGroup(*it);
+ QString cmd = conf->readPathEntry("Command");
+ QString resName = conf->readPathEntry("resName");
+ QString resClass = conf->readEntry("resClass");
+ if (cmd.isEmpty() || resName.isEmpty() || resClass.isEmpty()) continue;
+
+ DockContainer* c = new DockContainer(cmd, this, resName, resClass );
+ addContainer(c);
+
+ KProcess proc;
+ proc << KShell::splitArgs( cmd );
+ if (!proc.start(KProcess::DontCare)) {
+ fail_list.append(cmd);
+ removeContainer(c);
+ }
+ }
+ if (!fail_list.isEmpty())
+ KMessageBox::queuedMessageBox(0, KMessageBox::Information, i18n("The following dockbar applets could not be started: %1").arg(fail_list.join(", ")), i18n("kicker: information"), 0);
+ saveContainerConfig();
+}
+
+int DockBarExtension::findContainerAtPoint(const QPoint& p)
+{
+ int i = 0;
+ for (DockContainer::Vector::const_iterator it = containers.constBegin();
+ it != containers.constEnd();
+ ++it, ++i)
+ {
+ if ((*it)->geometry().contains(p))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void DockBarExtension::mousePressEvent(QMouseEvent *e ) {
+ if (e->button() == LeftButton) {
+ // Store the position of the mouse clic.
+ mclic_pos = e->pos();
+ } else if (e->button() == RightButton) {
+ int pos = findContainerAtPoint(e->pos());
+ if (pos != -1) containers.at(pos)->popupMenu(e->globalPos());
+ }
+}
+
+void DockBarExtension::mouseReleaseEvent(QMouseEvent *e ) {
+ if (e->button() != LeftButton) return;
+ if (dragging_container) {
+ releaseMouse();
+ original_container->embed(dragging_container->embeddedWinId());
+ delete dragging_container; dragging_container = 0;
+ layoutContainers();
+ saveContainerConfig();
+ }
+}
+
+void DockBarExtension::mouseMoveEvent(QMouseEvent *e) {
+ if (! (e->state() & LeftButton) ) return;
+ if (dragging_container == 0) {
+ // Check whether the user has moved far enough.
+ int delay = QApplication::startDragDistance();
+ if ( (mclic_pos - e->pos()).manhattanLength() > delay ) {
+ int pos = findContainerAtPoint(e->pos());
+ original_container = 0;
+ if (pos > -1) {
+ original_container = containers.at(pos);
+ mclic_dock_pos = e->pos() - original_container->pos();
+ dragged_container_original_pos = pos;
+ dragging_container = new DockContainer(original_container->command(), 0, original_container->resName(), original_container->resClass(), true);
+ dragging_container->show();
+ dragging_container->embed(original_container->embeddedWinId());
+ grabMouse();
+ }
+ }
+ }
+ if (dragging_container) {
+ dragging_container->move(e->globalPos() - mclic_dock_pos);
+
+ // change layout of other containers
+ QPoint dragpos(dragging_container->pos()),
+ barpos(mapToGlobal(pos()));
+ int pdrag1,pdrag2,psz;
+ pdrag1 = dragpos.x() - barpos.x() + DockContainer::sz()/2;
+ pdrag2 = dragpos.y() - barpos.y() + DockContainer::sz()/2;
+ if (orientation() == Vertical) {
+ int tmp = pdrag1; pdrag1 = pdrag2; pdrag2 = tmp;
+ psz = height();
+ } else psz = width();
+ if (pdrag2 >= 0 && pdrag2 < DockContainer::sz() && pdrag1 >= 0 && pdrag1 < psz)
+ pdrag1 /= DockContainer::sz();
+ else
+ pdrag1 = dragged_container_original_pos;
+
+
+ DockContainer::Vector::iterator it = qFind(containers.begin(), containers.end(), original_container);
+
+ if (it == containers.end())
+ {
+ return;
+ }
+
+ DockContainer::Vector::iterator target = containers.begin();
+ for (int i = 0; i < pdrag1 && target != containers.end(); ++i)
+ {
+ ++target;
+ }
+
+ containers.erase(it);
+ containers.insert(target, original_container);
+ layoutContainers();
+ }
+}