summaryrefslogtreecommitdiffstats
path: root/kicker/kicker/core/extensionmanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kicker/kicker/core/extensionmanager.cpp')
-rw-r--r--kicker/kicker/core/extensionmanager.cpp773
1 files changed, 773 insertions, 0 deletions
diff --git a/kicker/kicker/core/extensionmanager.cpp b/kicker/kicker/core/extensionmanager.cpp
new file mode 100644
index 000000000..d6342b8f9
--- /dev/null
+++ b/kicker/kicker/core/extensionmanager.cpp
@@ -0,0 +1,773 @@
+/*****************************************************************
+
+Copyright (c) 2000 Matthias Elter <elter@kde.org>
+Copyright (c) 2004-2005 Aaron Seigo <aseigo@kde.org>
+
+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.
+
+******************************************************************/
+
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <kaboutdata.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kwinmodule.h>
+#include <dcopref.h>
+
+#include "container_extension.h"
+#include "global.h"
+#include "kicker.h"
+#include "panelextension.h"
+#include "pluginmanager.h"
+
+#include "extensionmanager.h"
+
+ExtensionManager* ExtensionManager::m_self = 0;
+
+ExtensionManager* ExtensionManager::the()
+{
+ if (!m_self)
+ {
+ m_self = new ExtensionManager;
+ }
+
+ return m_self;
+}
+
+ExtensionManager::ExtensionManager()
+ : QObject(0, "ExtensionManager"),
+ m_menubarPanel(0),
+ m_mainPanel(0),
+ m_panelCounter(-1)
+{
+}
+
+ExtensionManager::~ExtensionManager()
+{
+ if (this == m_self)
+ {
+ m_self = 0;
+ }
+
+ ExtensionList::iterator itEnd = _containers.end();
+ for (ExtensionList::iterator it = _containers.begin(); it != itEnd; ++it)
+ {
+ delete *it;
+ }
+ _containers.clear();
+
+ delete m_menubarPanel;
+ delete m_mainPanel;
+}
+
+void ExtensionManager::initialize()
+{
+// kdDebug(1210) << "ExtensionManager::loadContainerConfig()" << endl;
+ KConfig* config = KGlobal::config();
+ PluginManager* pm = PluginManager::the();
+
+ // set up the "main" panel
+ if (config->hasGroup("Main Panel"))
+ {
+ config->setGroup("Main Panel");
+ if (config->hasKey("DesktopFile"))
+ {
+ m_mainPanel = pm->createExtensionContainer(config->readPathEntry("DesktopFile"),
+ true, config->readPathEntry("ConfigFile"),
+ "Main Panel");
+ }
+ }
+
+ if (!m_mainPanel)
+ {
+ // fall back to a regular ol' PanelExtension
+ m_mainPanel = pm->createExtensionContainer(
+ "childpanelextension.desktop",
+ true,
+ QString(kapp->aboutData()->appName()) + "rc",
+ "Main Panel");
+ }
+
+ if (!m_mainPanel)
+ {
+ KMessageBox::error(0, i18n("The KDE panel (kicker) could not load the main panel "
+ "due to a problem with your installation. "),
+ i18n("Fatal Error!"));
+ exit(1);
+ }
+
+ configureMenubar(true);
+
+ Kicker::the()->setMainWidget(m_mainPanel);
+
+ m_mainPanel->readConfig();
+ m_mainPanel->show();
+ kapp->processEvents();
+
+ // read extension list
+ config->setGroup("General");
+ QStringList elist = config->readListEntry("Extensions2");
+
+ // now restore the extensions
+ QStringList::iterator itEnd = elist.end();
+ for (QStringList::iterator it = elist.begin(); it != elist.end(); ++it)
+ {
+ // extension id
+ QString extensionId(*it);
+
+ // create a matching applet container
+ if (extensionId.find("Extension") == -1)
+ {
+ continue;
+ }
+
+ // is there a config group for this extension?
+ if (!config->hasGroup(extensionId))
+ {
+ continue;
+ }
+
+ // set config group
+ config->setGroup(extensionId);
+
+ ExtensionContainer* e = pm->createExtensionContainer(config->readPathEntry("DesktopFile"),
+ true, // is startup
+ config->readPathEntry("ConfigFile"),
+ extensionId);
+ if (e)
+ {
+ addContainer(e);
+ e->readConfig();
+ e->show();
+ kapp->processEvents();
+ }
+ }
+
+ pm->clearUntrustedLists();
+ connect(Kicker::the(), SIGNAL(configurationChanged()), SLOT(configurationChanged()));
+ DCOPRef r( "ksmserver", "ksmserver" );
+ r.send( "resumeStartup", QCString( "kicker" ));
+}
+
+void ExtensionManager::configureMenubar(bool duringInit)
+{
+ KConfig menuConfig( "kdesktoprc", true );
+ if( KConfigGroup( &menuConfig, "KDE" ).readBoolEntry("macStyle", false)
+ || KConfigGroup( &menuConfig, "Menubar" ).readBoolEntry( "ShowMenubar", false ))
+ {
+ if (KGlobal::dirs()->findResource("applets", "menuapplet.desktop").isEmpty() ||
+ m_menubarPanel)
+ {
+ return;
+ }
+
+ if (duringInit)
+ {
+ AppletInfo menubarInfo("menuapplet.desktop", QString::null, AppletInfo::Applet);
+ if (PluginManager::the()->hasInstance(menubarInfo))
+ {
+ // it's already there, in the main panel!
+ return;
+ }
+ migrateMenubar();
+ }
+
+ AppletInfo info("childpanelextension.desktop",
+ "kicker_menubarpanelrc",
+ AppletInfo::Extension);
+ KPanelExtension* menubar = new MenubarExtension(info);
+ m_menubarPanel = new ExtensionContainer(menubar, info, "Menubar Panel");
+ m_menubarPanel->setPanelOrder(-1);
+ m_menubarPanel->readConfig();
+ m_menubarPanel->setPosition(KPanelExtension::Top);
+ m_menubarPanel->setXineramaScreen(XineramaAllScreens);
+ m_menubarPanel->setHideButtons(false, false);
+
+ // this takes care of resizing the panel so it shows with the right height
+ updateMenubar();
+
+ m_menubarPanel->show();
+ connect(kapp, SIGNAL(kdisplayFontChanged()), SLOT(updateMenubar()));
+ }
+ else if (m_menubarPanel)
+ {
+ int screen = m_menubarPanel->xineramaScreen();
+ delete m_menubarPanel;
+ m_menubarPanel = 0;
+
+ emit desktopIconsAreaChanged(desktopIconsArea(screen), screen);
+ }
+}
+
+void ExtensionManager::migrateMenubar()
+{
+ // lame, lame, lame.
+ // the menubar applet was just plunked into kicker and not much
+ // thought was put into how it should be used. great idea, but no
+ // integration. >:-(
+ // so now we have to check to see if we HAVE another extension that
+ // will have a menubar in it, and if so, abort creating one of our
+ // own.
+ //
+ // the reason i didn't do this as a kconfig_update script is that
+ // most people don't use this feature, so no reason to penalize
+ // everyone, and moreover the user may added this to their main
+ // panel, meaning kickerrc itself would have to be vastly modified
+ // with lots of complications. not work it IMHO.
+
+ KConfig* config = KGlobal::config();
+ config->setGroup("General");
+
+ if (config->readBoolEntry("CheckedForMenubar", false))
+ {
+ return;
+ }
+
+ if (!locate("config", "kicker_menubarpanelrc").isEmpty())
+ {
+ // don't overwrite/override something that's already there
+ return;
+ }
+
+ QStringList elist = config->readListEntry("Extensions2");
+ QStringList::iterator itEnd = elist.end();
+ for (QStringList::iterator it = elist.begin(); it != elist.end(); ++it)
+ {
+ QString extensionId(*it);
+
+ if (extensionId.find("Extension") == -1)
+ {
+ continue;
+ }
+
+ // is there a config group for this extension?
+ if (!config->hasGroup(extensionId))
+ {
+ continue;
+ }
+
+ config->setGroup(extensionId);
+ QString extension = config->readPathEntry("ConfigFile");
+ KConfig extensionConfig(locate("config", extension));
+ extensionConfig.setGroup("General");
+
+ if (extensionConfig.hasKey("Applets2"))
+ {
+ QStringList containers = extensionConfig.readListEntry("Applets2");
+ QStringList::iterator cit = containers.begin();
+ QStringList::iterator citEnd = containers.end();
+ for (; cit != citEnd; ++cit)
+ {
+ QString appletId(*cit);
+
+ // is there a config group for this applet?
+ if (!extensionConfig.hasGroup(appletId))
+ {
+ continue;
+ }
+
+ KConfigGroup group(&extensionConfig, appletId.latin1());
+ QString appletType = appletId.left(appletId.findRev('_'));
+
+ if (appletType == "Applet")
+ {
+ QString appletFile = group.readPathEntry("DesktopFile");
+ if (appletFile.find("menuapplet.desktop") != -1)
+ {
+ QString menubarConfig = locate("config", extension);
+ KIO::NetAccess::copy(menubarConfig,
+ locateLocal("config",
+ "kicker_menubarpanelrc"), 0);
+ elist.remove(it);
+ config->setGroup("General");
+ config->writeEntry("Extensions2", elist);
+ config->writeEntry("CheckedForMenubar", true);
+ config->sync();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ config->setGroup("General");
+ config->writeEntry("CheckedForMenubar", true);
+}
+
+void ExtensionManager::saveContainerConfig()
+{
+// kdDebug(1210) << "ExtensionManager::saveContainerConfig()" << endl;
+
+ KConfig *config = KGlobal::config();
+
+ // build the extension list
+ QStringList elist;
+ ExtensionList::iterator itEnd = _containers.end();
+ for (ExtensionList::iterator it = _containers.begin(); it != itEnd; ++it)
+ {
+ elist.append((*it)->extensionId());
+ }
+
+ // write extension list
+ config->setGroup("General");
+ config->writeEntry("Extensions2", elist);
+
+ config->sync();
+}
+
+void ExtensionManager::configurationChanged()
+{
+ if (m_mainPanel)
+ {
+ m_mainPanel->readConfig();
+ }
+
+ if (m_menubarPanel)
+ {
+ m_menubarPanel->readConfig();
+ }
+
+ ExtensionList::iterator itEnd = _containers.end();
+ for (ExtensionList::iterator it = _containers.begin(); it != itEnd; ++it)
+ {
+ (*it)->readConfig();
+ }
+}
+
+void ExtensionManager::updateMenubar()
+{
+ if (!m_menubarPanel)
+ {
+ return;
+ }
+
+ //kdDebug(0) << "ExtensionManager::updateMenubar()" << endl;
+ // we need to make sure the panel is tall enough to accomodate the
+ // menubar! an easy way to find out the height of a menu: make one ;)
+ KMenuBar tmpmenu;
+ tmpmenu.insertItem("KDE Rocks!");
+ m_menubarPanel->setSize(KPanelExtension::SizeCustom,
+ tmpmenu.sizeHint().height());
+ m_menubarPanel->writeConfig();
+
+ emit desktopIconsAreaChanged(desktopIconsArea(m_menubarPanel->xineramaScreen()),
+ m_menubarPanel->xineramaScreen());
+}
+
+bool ExtensionManager::isMainPanel(const QWidget* panel) const
+{
+ return m_mainPanel == panel;
+}
+
+bool ExtensionManager::isMenuBar(const QWidget* panel) const
+{
+ return m_menubarPanel == panel;
+}
+
+void ExtensionManager::addExtension( const QString& desktopFile )
+{
+ PluginManager* pm = PluginManager::the();
+ ExtensionContainer *e = pm->createExtensionContainer(desktopFile,
+ false, // is not startup
+ QString::null, // no config
+ uniqueId());
+
+
+ kdDebug(1210) << "ExtensionManager::addExtension" << endl;
+
+ if (e)
+ {
+ e->readConfig();
+ // as a new panel, the position will be set to the preferred position
+ // we just need to make sure this works with the rest of the panel layout
+ e->setPosition(initialPanelPosition(e->position()));
+ kdDebug(1210)<<"after e->readConfig(): pos="<<e->position()<<endl;
+ addContainer(e);
+ e->show();
+ e->writeConfig();
+ saveContainerConfig();
+ }
+}
+
+void ExtensionManager::addContainer(ExtensionContainer* e)
+{
+ if (!e)
+ {
+ return;
+ }
+
+ _containers.append(e);
+
+ connect(e, SIGNAL(removeme(ExtensionContainer*)),
+ this, SLOT(removeContainer(ExtensionContainer*)));
+
+ emit desktopIconsAreaChanged(desktopIconsArea(e->xineramaScreen()),
+ e->xineramaScreen());
+}
+
+void ExtensionManager::removeContainer(ExtensionContainer* e)
+{
+ if (!e)
+ {
+ return;
+ }
+
+ e->removeSessionConfigFile();
+ _containers.remove(e);
+ e->deleteLater(); // Wait till we return to the main event loop
+ saveContainerConfig();
+
+ emit desktopIconsAreaChanged(desktopIconsArea(e->xineramaScreen()),
+ e->xineramaScreen());
+}
+
+void ExtensionManager::removeAllContainers()
+{
+ while (!_containers.isEmpty())
+ {
+ ExtensionContainer* e = _containers.first();
+ _containers.remove(e);
+ e->deleteLater(); // Wait till we return to the main event loop
+ }
+
+ saveContainerConfig();
+}
+
+QString ExtensionManager::uniqueId()
+{
+ QString idBase = "Extension_%1";
+ QString newId;
+ int i = 0;
+ bool unique = false;
+
+ while (!unique)
+ {
+ i++;
+ newId = idBase.arg(i);
+
+ unique = true;
+ ExtensionList::iterator itEnd = _containers.end();
+ for (ExtensionList::iterator it = _containers.begin(); it != itEnd; ++it)
+ {
+ if ((*it)->extensionId() == newId)
+ {
+ unique = false;
+ break;
+ }
+ }
+ }
+
+ return newId;
+}
+
+KPanelExtension::Position ExtensionManager::initialPanelPosition(KPanelExtension::Position preferred)
+{
+ // Guess a good position
+ bool positions[KPanelExtension::Bottom+1];
+ for( int i = 0; i <= (int) KPanelExtension::Bottom; ++i )
+ {
+ positions[i] = true;
+ }
+
+ ExtensionList::iterator itEnd = _containers.end();
+ for (ExtensionList::iterator it = _containers.begin(); it != itEnd; ++it)
+ {
+ positions[(int) (*it)->position()] = false;
+ }
+
+ KPanelExtension::Position pos = preferred;
+ if (positions[(int)pos])
+ return pos;
+
+ pos = (KPanelExtension::Position) (pos ^ 1);
+ if (positions[(int)pos])
+ return pos;
+
+ pos = (KPanelExtension::Position) (pos ^ 3);
+ if (positions[(int)pos])
+ return pos;
+
+ pos = (KPanelExtension::Position) (pos ^ 1);
+ if (positions[(int)pos])
+ return pos;
+
+ return preferred;
+}
+
+bool ExtensionManager::shouldExclude(int XineramaScreen,
+ const ExtensionContainer* extension,
+ const ExtensionContainer* exclude) const
+{
+ // Rules of Exclusion:
+ // 0. Exclude ourselves
+ // 1. Exclude panels not on our Xinerama screen
+ // 2. Exclude panels on the same side of the screen as ourselves that are above us
+ // 3. Exclude panels on the opposite side of the screen. Breaks down if the user
+ // dabbles in insane layouts where a top/bottom or left/right pair overlap?
+ // 4. Exclude panels on adjacent sides of the screen that do not overlap with us
+
+ if (exclude->winId() == extension->winId())
+ {
+ // Rule 0 Exclusion
+ return true;
+ }
+
+ if (extension->xineramaScreen()!= XineramaAllScreens &&
+ exclude->xineramaScreen() != XineramaAllScreens &&
+ exclude->xineramaScreen() != XineramaScreen)
+ {
+ // Rule 1 exclusion
+ return true;
+ }
+
+ if (!exclude->reserveStrut())
+ {
+ return true;
+ }
+
+ bool lowerInStack = extension->panelOrder() < exclude->panelOrder();
+ if (exclude->position() == extension->position())
+ {
+ // Rule 2 Exclusion
+ if (extension->position() == KPanelExtension::Bottom &&
+ exclude->geometry().bottom() == extension->geometry().bottom() &&
+ !exclude->geometry().intersects(extension->geometry()))
+ {
+ return false;
+ }
+ else if (extension->position() == KPanelExtension::Top &&
+ exclude->geometry().top() == extension->geometry().top() &&
+ !exclude->geometry().intersects(extension->geometry()))
+ {
+ return false;
+ }
+ else if (extension->position() == KPanelExtension::Left &&
+ exclude->geometry().left() == extension->geometry().left() &&
+ !exclude->geometry().intersects(extension->geometry()))
+ {
+ return false;
+ }
+ else if (extension->position() == KPanelExtension::Right &&
+ exclude->geometry().right() == extension->geometry().right() &&
+ !exclude->geometry().intersects(extension->geometry()))
+ {
+ return false;
+ }
+
+ return lowerInStack;
+ }
+
+ // Rule 3 exclusion
+ if (exclude->orientation() == extension->orientation())
+ {
+ // on the opposite side of the screen from us.
+ return true;
+ }
+
+ // Rule 4 exclusion
+ if (extension->position() == KPanelExtension::Bottom)
+ {
+ if (exclude->geometry().bottom() > extension->geometry().top())
+ {
+ return lowerInStack;
+ }
+ }
+ else if (extension->position() == KPanelExtension::Top)
+ {
+ if (exclude->geometry().top() < extension->geometry().bottom())
+ {
+ return lowerInStack;
+ }
+ }
+ else if (extension->position() == KPanelExtension::Left)
+ {
+ if (exclude->geometry().left() < extension->geometry().right())
+ {
+ return lowerInStack;
+ }
+ }
+ else /* if (extension->position() == KPanelExtension::Right) */
+ {
+ if (exclude->geometry().right() > extension->geometry().left())
+ {
+ return lowerInStack;
+ }
+ }
+
+ return true;
+}
+
+QRect ExtensionManager::workArea(int XineramaScreen, const ExtensionContainer* extension)
+{
+ if (!extension)
+ {
+ return Kicker::the()->kwinModule()->workArea(XineramaScreen);
+ }
+
+ QValueList<WId> list;
+
+ ExtensionList::iterator itEnd = _containers.end();
+ ExtensionList::iterator it = _containers.begin();
+
+ // If the hide mode is Manual, exclude the struts of
+ // panels below this one in the list. Else exclude the
+ // struts of all panels.
+ if (extension->reserveStrut() &&
+ extension != m_menubarPanel &&
+ extension->hideMode() == ExtensionContainer::ManualHide)
+ {
+ if (m_mainPanel && shouldExclude(XineramaScreen, extension, m_mainPanel))
+ {
+ list.append(m_mainPanel->winId());
+ }
+
+ for (; it != itEnd; ++it)
+ {
+ if (shouldExclude(XineramaScreen, extension, *it))
+ {
+ list.append((*it)->winId());
+ }
+ }
+ }
+ else
+ {
+ // auto hide panel? just ignore everything else for now.
+ if (extension == m_menubarPanel)
+ {
+ list.append(m_menubarPanel->winId());
+ }
+
+ if (m_mainPanel)
+ {
+ list.append(m_mainPanel->winId());
+ }
+
+ for (; it != itEnd; ++it)
+ {
+ list.append((*it)->winId());
+ }
+ }
+
+ QRect workArea;
+ if (XineramaScreen == XineramaAllScreens)
+ {
+ /* special value for all screens */
+ workArea = Kicker::the()->kwinModule()->workArea(list);
+ }
+ else
+ {
+ workArea = Kicker::the()->kwinModule()->workArea(list, XineramaScreen)
+ .intersect(QApplication::desktop()->screenGeometry(XineramaScreen));
+ }
+
+ return workArea;
+}
+
+int ExtensionManager::nextPanelOrder()
+{
+ ++m_panelCounter;
+ return m_panelCounter;
+}
+
+void ExtensionManager::reduceArea(QRect &area, const ExtensionContainer *extension) const
+{
+ if (!extension ||
+ extension->hideMode() == ExtensionContainer::AutomaticHide ||
+ !extension->reserveStrut())
+ {
+ return;
+ }
+
+ QRect geom = extension->initialGeometry(extension->position(), extension->alignment(),
+ extension->xineramaScreen());
+
+ // reduce given area (QRect) to the space not covered by the given extension
+ // As simplification: the length of the extension is not taken into account
+ // which means that even a small extension e.g. on the left side of the desktop
+ // will remove the available area with its with
+
+ switch (extension->position())
+ {
+ case KPanelExtension::Left:
+ {
+ area.setLeft(QMAX(area.left(), geom.right()));
+ break;
+ }
+ case KPanelExtension::Right:
+ {
+ area.setRight(QMIN(area.right(), geom.left()));
+ break;
+ }
+ case KPanelExtension::Top:
+ {
+ area.setTop(QMAX(area.top(), geom.bottom()));
+ break;
+ }
+ case KPanelExtension::Bottom:
+ {
+ area.setBottom(QMIN(area.bottom(), geom.top()));
+ break;
+ }
+ default: ; // ignore KPanelExtension::Floating ... at least for now
+ }
+}
+
+QRect ExtensionManager::desktopIconsArea(int screen) const
+{
+ // This is pretty broken, mixes Xinerama and non-Xinerama multihead
+ // and generally doesn't seem to be required anyway => ignore screen.
+// QRect area = QApplication::desktop()->screenGeometry(screen);
+ QRect area = QApplication::desktop()->geometry();
+
+ reduceArea(area, m_mainPanel);
+ reduceArea(area, m_menubarPanel);
+
+ for (ExtensionList::const_iterator it = _containers.constBegin();
+ it != _containers.constEnd();
+ ++it)
+ {
+ reduceArea(area, (*it));
+ }
+
+ kdDebug(1210) << "ExtensionManager::desktopIconsArea() = " << area
+ << " screen = " << screen << endl;
+ return area;
+}
+
+void ExtensionManager::extensionSizeChanged(ExtensionContainer *extension)
+{
+ // we have to recalc the available space for desktop icons
+ if (!extension)
+ {
+ return;
+ }
+
+ emit desktopIconsAreaChanged(desktopIconsArea(extension->xineramaScreen()),
+ extension->xineramaScreen());
+}
+
+#include "extensionmanager.moc"