diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 4aed2c8219774f5d797760606b8489a92ddc5163 (patch) | |
tree | 3f8c130f7d269626bf6a9447407ef6c35954426a /kicker/taskbar | |
download | tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kicker/taskbar')
-rw-r--r-- | kicker/taskbar/Makefile.am | 20 | ||||
-rw-r--r-- | kicker/taskbar/taskbar.cpp | 1231 | ||||
-rw-r--r-- | kicker/taskbar/taskbar.h | 135 | ||||
-rw-r--r-- | kicker/taskbar/taskbar.kcfg | 173 | ||||
-rw-r--r-- | kicker/taskbar/taskbarbindings.cpp | 37 | ||||
-rw-r--r-- | kicker/taskbar/taskbarcontainer.cpp | 292 | ||||
-rw-r--r-- | kicker/taskbar/taskbarcontainer.h | 75 | ||||
-rw-r--r-- | kicker/taskbar/taskbarsettings.kcfgc | 7 | ||||
-rw-r--r-- | kicker/taskbar/taskcontainer.cpp | 1643 | ||||
-rw-r--r-- | kicker/taskbar/taskcontainer.h | 153 |
10 files changed, 3766 insertions, 0 deletions
diff --git a/kicker/taskbar/Makefile.am b/kicker/taskbar/Makefile.am new file mode 100644 index 000000000..01b3af162 --- /dev/null +++ b/kicker/taskbar/Makefile.am @@ -0,0 +1,20 @@ +INCLUDES = -I$(srcdir)/../taskmanager -I$(srcdir)/../libkicker -I$(top_builddir)/kicker/libkicker $(all_includes) + +# KDE 4: rename to libkickertaskbar.la! + +lib_LTLIBRARIES = libtaskbar.la +libtaskbar_la_SOURCES = taskbarsettings.kcfgc taskcontainer.cpp taskbar.cpp \ + taskbarcontainer.cpp taskbarcontainer.skel + +libtaskbar_la_METASOURCES = AUTO + +libtaskbar_la_LDFLAGS = -version-info 3:0:2 $(all_libraries) -no-undefined +libtaskbar_la_LIBADD = $(LIB_KDEUI) $(LIB_KUTILS) ../libkicker/libkickermain.la ../taskmanager/libtaskmanager.la + +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/libtaskbar.pot + +taskbar.lo: ../libkicker/kickerSettings.h +taskbarcontainer.lo: ../libkicker/kickerSettings.h + +kde_kcfg_DATA = taskbar.kcfg diff --git a/kicker/taskbar/taskbar.cpp b/kicker/taskbar/taskbar.cpp new file mode 100644 index 000000000..8c5bb73c1 --- /dev/null +++ b/kicker/taskbar/taskbar.cpp @@ -0,0 +1,1231 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> +Copyright (c) 2004 Sebastian Wolff +Copyright (c) 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 <math.h> + +#include <qapplication.h> +#include <qbitmap.h> +#include <qdesktopwidget.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qstringlist.h> + +#include <dcopclient.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kglobalaccel.h> +#include <kimageeffect.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#include "kickerSettings.h" +#include "taskbarsettings.h" +#include "taskcontainer.h" +#include "taskmanager.h" + +#include "taskbar.h" +#include "taskbar.moc" + + +TaskBar::TaskBar( QWidget *parent, const char *name ) + : Panner( parent, name ), + m_showAllWindows(false), + m_currentScreen(-1), + m_showOnlyCurrentScreen(false), + m_sortByDesktop(false), + m_showIcon(false), + m_showOnlyIconified(false), + m_textShadowEngine(0), + m_ignoreUpdates(false) +{ + arrowType = LeftArrow; + blocklayout = true; + + // init + setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); + + // setup animation frames + for (int i = 1; i < 11; i++) + { + frames.append(new QPixmap(locate("data", "kicker/pics/disk" + QString::number(i) + ".png"))); + } + + // configure + configure(); + + connect(&m_relayoutTimer, SIGNAL(timeout()), + this, SLOT(reLayout())); + + connect(this, SIGNAL(contentsMoving(int, int)), SLOT(setBackground())); + + // connect manager + connect(TaskManager::the(), SIGNAL(taskAdded(Task::Ptr)), + this, SLOT(add(Task::Ptr))); + connect(TaskManager::the(), SIGNAL(taskRemoved(Task::Ptr)), + this, SLOT(remove(Task::Ptr))); + connect(TaskManager::the(), SIGNAL(startupAdded(Startup::Ptr)), + this, SLOT(add(Startup::Ptr))); + connect(TaskManager::the(), SIGNAL(startupRemoved(Startup::Ptr)), + this, SLOT(remove(Startup::Ptr))); + connect(TaskManager::the(), SIGNAL(desktopChanged(int)), + this, SLOT(desktopChanged(int))); + connect(TaskManager::the(), SIGNAL(windowChanged(Task::Ptr)), + this, SLOT(windowChanged(Task::Ptr))); + + isGrouping = shouldGroup(); + + // register existant tasks + Task::Dict tasks = TaskManager::the()->tasks(); + Task::Dict::iterator taskEnd = tasks.end(); + for (Task::Dict::iterator it = tasks.begin(); it != taskEnd; ++it) + { + add(it.data()); + } + + // register existant startups + Startup::List startups = TaskManager::the()->startups(); + Startup::List::iterator startupEnd = startups.end(); + for (Startup::List::iterator sIt = startups.begin(); sIt != startupEnd; ++sIt) + { + add((*sIt)); + } + + blocklayout = false; + + connect(kapp, SIGNAL(settingsChanged(int)), SLOT(slotSettingsChanged(int))); + keys = new KGlobalAccel( this ); +#include "taskbarbindings.cpp" + keys->readSettings(); + keys->updateConnections(); + + reLayout(); +} + +TaskBar::~TaskBar() +{ + for (TaskContainer::Iterator it = m_hiddenContainers.begin(); + it != m_hiddenContainers.end(); + ++it) + { + (*it)->deleteLater(); + } + + for (TaskContainer::List::const_iterator it = containers.constBegin(); + it != containers.constEnd(); + ++it) + { + (*it)->deleteLater(); + } + + for (PixmapList::const_iterator it = frames.constBegin(); + it != frames.constEnd(); + ++it) + { + delete *it; + } + + delete m_textShadowEngine; +} + +KTextShadowEngine *TaskBar::textShadowEngine() +{ + if (!m_textShadowEngine) + m_textShadowEngine = new KTextShadowEngine(); + + return m_textShadowEngine; +} + + +QSize TaskBar::sizeHint() const +{ + // get our minimum height based on the minimum button height or the + // height of the font in use, which is largest + QFontMetrics fm(KGlobalSettings::taskbarFont()); + int minButtonHeight = fm.height() > TaskBarSettings::minimumButtonHeight() ? + fm.height() : TaskBarSettings::minimumButtonHeight(); + + return QSize(BUTTON_MIN_WIDTH, minButtonHeight); +} + +QSize TaskBar::sizeHint( KPanelExtension::Position p, QSize maxSize) const +{ + // get our minimum height based on the minimum button height or the + // height of the font in use, which is largest + QFontMetrics fm(KGlobalSettings::taskbarFont()); + int minButtonHeight = fm.height() > TaskBarSettings::minimumButtonHeight() ? + fm.height() : TaskBarSettings::minimumButtonHeight(); + + if ( p == KPanelExtension::Left || p == KPanelExtension::Right ) + { + int actualMax = minButtonHeight * containerCount(); + + if (containerCount() == 0) + { + actualMax = minButtonHeight; + } + + if (actualMax > maxSize.height()) + { + return maxSize; + } + return QSize( maxSize.width(), actualMax ); + } + else + { + int rows = KickerSettings::conserveSpace() ? + contentsRect().height() / minButtonHeight : + 1; + if ( rows < 1 ) + { + rows = 1; + } + + int maxWidth = TaskBarSettings::maximumButtonWidth(); + if (maxWidth == 0) + { + maxWidth = BUTTON_MAX_WIDTH; + } + + int actualMax = maxWidth * (containerCount() / rows); + + if (containerCount() % rows > 0) + { + actualMax += maxWidth; + } + if (containerCount() == 0) + { + actualMax = maxWidth; + } + + if (actualMax > maxSize.width()) + { + return maxSize; + } + return QSize( actualMax, maxSize.height() ); + } +} + +void TaskBar::configure() +{ + bool wasShowWindows = m_showAllWindows; + bool wasSortByDesktop = m_sortByDesktop; + bool wasShowIcon = m_showIcon; + bool wasShowOnlyIconified = m_showOnlyIconified; + + m_showAllWindows = TaskBarSettings::showAllWindows(); + m_sortByDesktop = m_showAllWindows && TaskBarSettings::sortByDesktop(); + m_showIcon = TaskBarSettings::showIcon(); + m_showOnlyIconified = TaskBarSettings::showOnlyIconified(); + + m_currentScreen = -1; // Show all screens or re-get our screen + m_showOnlyCurrentScreen = TaskBarSettings::showCurrentScreenOnly() && + QApplication::desktop()->isVirtualDesktop() && + QApplication::desktop()->numScreens() > 1; + + // we need to watch geometry issues if we aren't showing windows when we + // are paying attention to the current Xinerama screen + if (m_showOnlyCurrentScreen) + { + // disconnect first in case we've been here before + // to avoid multiple connections + disconnect(TaskManager::the(), SIGNAL(windowChangedGeometry(Task::Ptr)), + this, SLOT(windowChangedGeometry(Task::Ptr))); + connect(TaskManager::the(), SIGNAL(windowChangedGeometry(Task::Ptr)), + this, SLOT(windowChangedGeometry(Task::Ptr))); + } + TaskManager::the()->trackGeometry(m_showOnlyCurrentScreen); + + if (wasShowWindows != m_showAllWindows || + wasSortByDesktop != m_sortByDesktop || + wasShowIcon != m_showIcon || + wasShowOnlyIconified != m_showOnlyIconified) + { + // relevant settings changed, update our task containers + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + (*it)->settingsChanged(); + } + } + + TaskManager::the()->setXCompositeEnabled(TaskBarSettings::showThumbnails()); + + reLayoutEventually(); +} + +void TaskBar::setOrientation( Orientation o ) +{ + Panner::setOrientation( o ); + reLayoutEventually(); +} + +void TaskBar::moveEvent( QMoveEvent* e ) +{ + Panner::moveEvent(e); + setViewportBackground(); +} + +void TaskBar::resizeEvent( QResizeEvent* e ) +{ + if (m_showOnlyCurrentScreen) + { + QPoint topLeft = mapToGlobal(this->geometry().topLeft()); + if (m_currentScreen != QApplication::desktop()->screenNumber(topLeft)) + { + // we have been moved to another screen! + m_currentScreen = -1; + reGroup(); + } + } + + Panner::resizeEvent(e); + reLayoutEventually(); + setViewportBackground(); +} + +void TaskBar::add(Task::Ptr task) +{ + if (!task || + (m_showOnlyCurrentScreen && + !TaskManager::isOnScreen(showScreen(), task->window()))) + { + return; + } + + // try to group + if (isGrouping) + { + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + TaskContainer* c = *it; + + if (idMatch(task->classClass(), c->id())) + { + c->add(task); + reLayoutEventually(); + return; + } + } + } + + // create new container + TaskContainer *container = new TaskContainer(task, this, viewport()); + m_hiddenContainers.append(container); + + // even though there is a signal to listen to, we have to add this + // immediately to ensure grouping doesn't break (primarily on startup) + // we still add the container to m_hiddenContainers in case the event + // loop gets re-entered here and something bizarre happens. call it + // insurance =) + showTaskContainer(container); +} + +void TaskBar::add(Startup::Ptr startup) +{ + if (!startup) + { + return; + } + + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + if ((*it)->contains(startup)) + { + return; + } + } + + // create new container + TaskContainer *container = new TaskContainer(startup, frames, this, viewport()); + m_hiddenContainers.append(container); + connect(container, SIGNAL(showMe(TaskContainer*)), this, SLOT(showTaskContainer(TaskContainer*))); +} + +void TaskBar::showTaskContainer(TaskContainer* container) +{ + TaskContainer::List::iterator it = m_hiddenContainers.find(container); + if (it != m_hiddenContainers.end()) + { + m_hiddenContainers.erase(it); + } + + if (container->isEmpty()) + { + return; + } + + // try to place the container after one of the same app + if (TaskBarSettings::sortByApp()) + { + TaskContainer::Iterator it = containers.begin(); + for (; it != containers.end(); ++it) + { + TaskContainer* c = *it; + + if (container->id().lower() == c->id().lower()) + { + // search for the last occurrence of this app + for (; it != containers.end(); ++it) + { + c = *it; + + if (container->id().lower() != c->id().lower()) + { + break; + } + } + break; + } + } + + if (it != containers.end()) + { + containers.insert(it, container); + } + else + { + containers.append(container); + } + } + else + { + containers.append(container); + } + + addChild(container); + reLayoutEventually(); + emit containerCountChanged(); +} + +void TaskBar::remove(Task::Ptr task, TaskContainer* container) +{ + for (TaskContainer::Iterator it = m_hiddenContainers.begin(); + it != m_hiddenContainers.end(); + ++it) + { + if ((*it)->contains(task)) + { + (*it)->finish(); + m_deletableContainers.append(*it); + m_hiddenContainers.erase(it); + break; + } + } + + if (!container) + { + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + if ((*it)->contains(task)) + { + container = *it; + break; + } + } + + if (!container) + { + return; + } + } + + container->remove(task); + + if (container->isEmpty()) + { + TaskContainer::List::iterator it = containers.find(container); + if (it != containers.end()) + { + containers.erase(it); + } + + removeChild(container); + container->finish(); + m_deletableContainers.append(container); + + reLayoutEventually(); + emit containerCountChanged(); + } + else if (container->filteredTaskCount() < 1) + { + reLayoutEventually(); + emit containerCountChanged(); + } +} + +void TaskBar::remove(Startup::Ptr startup, TaskContainer* container) +{ + for (TaskContainer::Iterator it = m_hiddenContainers.begin(); + it != m_hiddenContainers.end(); + ++it) + { + if ((*it)->contains(startup)) + { + (*it)->remove(startup); + + if ((*it)->isEmpty()) + { + (*it)->finish(); + m_deletableContainers.append(*it); + m_hiddenContainers.erase(it); + } + + break; + } + } + + if (!container) + { + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + if ((*it)->contains(startup)) + { + container = *it; + break; + } + } + + if (!container) + { + return; + } + } + + container->remove(startup); + if (!container->isEmpty()) + { + return; + } + + TaskContainer::List::iterator it = containers.find(container); + if (it != containers.end()) + { + containers.erase(it); + } + + // startup containers only ever contain that one item. so + // just delete the poor bastard. + container->finish(); + m_deletableContainers.append(container); + reLayoutEventually(); + emit containerCountChanged(); +} + +void TaskBar::desktopChanged(int desktop) +{ + if (m_showAllWindows) + { + return; + } + + m_relayoutTimer.stop(); + m_ignoreUpdates = true; + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + (*it)->desktopChanged(desktop); + } + + m_ignoreUpdates = false; + reLayout(); + emit containerCountChanged(); +} + +void TaskBar::windowChanged(Task::Ptr task) +{ + if (m_showOnlyCurrentScreen && + !TaskManager::isOnScreen(showScreen(), task->window())) + { + return; // we don't care about this window + } + + TaskContainer* container = 0; + for (TaskContainer::List::const_iterator it = containers.constBegin(); + it != containers.constEnd(); + ++it) + { + TaskContainer* c = *it; + + if (c->contains(task)) + { + container = c; + break; + } + } + + // if we don't have a container or we're showing only windows on this + // desktop and the container is neither on the desktop nor currently visible + // just skip it + if (!container || + (!m_showAllWindows && + !container->onCurrentDesktop() && + !container->isVisibleTo(this))) + { + return; + } + + container->windowChanged(task); + + if (!m_showAllWindows || m_showOnlyIconified) + { + emit containerCountChanged(); + } + + reLayoutEventually(); +} + +void TaskBar::windowChangedGeometry(Task::Ptr task) +{ + //TODO: this gets called every time a window's geom changes + // when we are in "show only on the same Xinerama screen" + // mode it would be Good(tm) to compress these events so this + // gets run less often, but always when needed + TaskContainer* container = 0; + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + TaskContainer* c = *it; + if (c->contains(task)) + { + container = c; + break; + } + } + + if ((!!container) == TaskManager::isOnScreen(showScreen(), task->window())) + { + // we have this window covered, so we don't need to do anything + return; + } + + if (container) + { + remove(task, container); + } + else + { + add(task); + } +} + +void TaskBar::reLayoutEventually() +{ + m_relayoutTimer.stop(); + + if (!blocklayout && !m_ignoreUpdates) + { + m_relayoutTimer.start(25, true); + } +} + +void TaskBar::reLayout() +{ + // Because QPopupMenu::exec() creates its own event loop, deferred deletes + // via QObject::deleteLater() may be prematurely executed when a container's + // popup menu is visible. + // + // To get around this, we collect the containers and delete them manually + // when doing a relayout. (kling) + if (!m_deletableContainers.isEmpty()) { + TaskContainer::List::iterator it = m_deletableContainers.begin(); + for (; it != m_deletableContainers.end(); ++it) + delete *it; + m_deletableContainers.clear(); + } + + // filter task container list + TaskContainer::List list = filteredContainers(); + + if (list.count() < 1) + { + resizeContents(contentsRect().width(), contentsRect().height()); + return; + } + + if (isGrouping != shouldGroup()) + { + reGroup(); + return; + } + + // sort container list by desktop + if (m_sortByDesktop) + { + sortContainersByDesktop(list); + } + + // needed because Panner doesn't know how big it's contents are so it's + // up to use to initialize it. =( + resizeContents(contentsRect().width(), contentsRect().height()); + + // number of rows simply depends on our height which is either the + // minimum button height or the height of the font in use, whichever is + // largest + QFontMetrics fm(KGlobalSettings::taskbarFont()); + int minButtonHeight = fm.height() > TaskBarSettings::minimumButtonHeight() ? + fm.height() : TaskBarSettings::minimumButtonHeight(); + + // horizontal layout + if (orientation() == Horizontal) + { + int bwidth = BUTTON_MIN_WIDTH; + int rows = contentsRect().height() / minButtonHeight; + + if ( rows < 1 ) + { + rows = 1; + } + + // actual button height + int bheight = contentsRect().height() / rows; + + // avoid zero devision later + if (bheight < 1) + { + bheight = 1; + } + + // buttons per row + int bpr = (int)ceil( (double)list.count() / rows); + + // adjust content size + if ( contentsRect().width() < bpr * BUTTON_MIN_WIDTH ) + { + resizeContents( bpr * BUTTON_MIN_WIDTH, contentsRect().height() ); + } + + // maximum number of buttons per row + int mbpr = contentsRect().width() / BUTTON_MIN_WIDTH; + + // expand button width if space permits + if (mbpr > bpr) + { + bwidth = contentsRect().width() / bpr; + int maxWidth = TaskBarSettings::maximumButtonWidth(); + if (maxWidth > 0 && bwidth > maxWidth) + { + bwidth = maxWidth; + } + } + + // layout containers + + // for taskbars at the bottom, we need to ensure that the bottom + // buttons touch the bottom of the screen. since we layout from + // top to bottom this means seeing if we have any padding and + // popping it on the top. this preserves Fitt's Law behaviour + // for taskbars on the bottom + int topPadding = 0; + if (arrowType == UpArrow) + { + topPadding = contentsRect().height() % (rows * bheight); + } + + int i = 0; + bool reverseLayout = QApplication::reverseLayout(); + for (TaskContainer::Iterator it = list.begin(); + it != list.end(); + ++it, i++) + { + TaskContainer* c = *it; + + int row = i % rows; + + int x = ( i / rows ) * bwidth; + if (reverseLayout) + { + x = contentsRect().width() - x - bwidth; + } + int y = (row * bheight) + topPadding; + + c->setArrowType(arrowType); + + if (childX(c) != x || childY(c) != y) + moveChild(c, x, y); + + if (c->width() != bwidth || c->height() != bheight) + c->resize( bwidth, bheight ); + + c->setBackground(); + } + } + else // vertical layout + { + // adjust content size + if (contentsRect().height() < (int)list.count() * minButtonHeight) + { + resizeContents(contentsRect().width(), list.count() * minButtonHeight); + } + + // layout containers + int i = 0; + for (TaskContainer::Iterator it = list.begin(); + it != list.end(); + ++it) + { + TaskContainer* c = *it; + + c->setArrowType(arrowType); + + if (c->width() != contentsRect().width() || c->height() != minButtonHeight) + c->resize(contentsRect().width(), minButtonHeight); + + if (childX(c) != 0 || childY(c) != (i * minButtonHeight)) + moveChild(c, 0, i * minButtonHeight); + + c->setBackground(); + i++; + } + } + + QTimer::singleShot(100, this, SLOT(publishIconGeometry())); +} + +void TaskBar::setViewportBackground() +{ + const QPixmap *bg = parentWidget()->backgroundPixmap(); + + if (bg) + { + QPixmap pm(parentWidget()->size()); + pm.fill(parentWidget(), pos() + viewport()->pos()); + viewport()->setPaletteBackgroundPixmap(pm); + viewport()->setBackgroundOrigin(WidgetOrigin); + } + else + viewport()->setPaletteBackgroundColor(paletteBackgroundColor()); +} + +void TaskBar::setBackground() +{ + setViewportBackground(); + + TaskContainer::List list = filteredContainers(); + + for (TaskContainer::Iterator it = list.begin(); + it != list.end(); + ++it) + { + TaskContainer* c = *it; + c->setBackground(); + } +} + +void TaskBar::setArrowType(Qt::ArrowType at) +{ + if (arrowType == at) + { + return; + } + + arrowType = at; + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + (*it)->setArrowType(arrowType); + } +} + +void TaskBar::publishIconGeometry() +{ + QPoint p = mapToGlobal(QPoint(0,0)); // roundtrip, don't do that too often + + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + (*it)->publishIconGeometry(p); + } +} + +void TaskBar::viewportMousePressEvent( QMouseEvent* e ) +{ + propagateMouseEvent( e ); +} + +void TaskBar::viewportMouseReleaseEvent( QMouseEvent* e ) +{ + propagateMouseEvent( e ); +} + +void TaskBar::viewportMouseDoubleClickEvent( QMouseEvent* e ) +{ + propagateMouseEvent( e ); +} + +void TaskBar::viewportMouseMoveEvent( QMouseEvent* e ) +{ + propagateMouseEvent( e ); +} + +void TaskBar::propagateMouseEvent( QMouseEvent* e ) +{ + if ( !isTopLevel() ) + { + QMouseEvent me( e->type(), mapTo( topLevelWidget(), e->pos() ), + e->globalPos(), e->button(), e->state() ); + QApplication::sendEvent( topLevelWidget(), &me ); + } +} + +bool TaskBar::idMatch( const QString& id1, const QString& id2 ) +{ + if ( id1.isEmpty() || id2.isEmpty() ) + return false; + + return id1.lower() == id2.lower(); +} + +int TaskBar::containerCount() const +{ + int i = 0; + + for (TaskContainer::List::const_iterator it = containers.constBegin(); + it != containers.constEnd(); + ++it) + { + if ((m_showAllWindows || (*it)->onCurrentDesktop()) && + ((showScreen() == -1) || ((*it)->isOnScreen()))) + { + i++; + } + } + + return i; +} + +int TaskBar::taskCount() const +{ + int i = 0; + + for (TaskContainer::List::const_iterator it = containers.constBegin(); + it != containers.constEnd(); + ++it) + { + if ((m_showAllWindows || (*it)->onCurrentDesktop()) && + ((showScreen() == -1) || ((*it)->isOnScreen()))) + { + i += (*it)->filteredTaskCount(); + } + } + + return i; +} + +int TaskBar::maximumButtonsWithoutShrinking() const +{ + QFontMetrics fm(KGlobalSettings::taskbarFont()); + int minButtonHeight = fm.height() > TaskBarSettings::minimumButtonHeight() ? + fm.height() : TaskBarSettings::minimumButtonHeight(); + int rows = contentsRect().height() / minButtonHeight; + + if (rows < 1) + { + rows = 1; + } + + if ( orientation() == Horizontal ) { + // maxWidth of 0 means no max width, drop back to default + int maxWidth = TaskBarSettings::maximumButtonWidth(); + if (maxWidth == 0) + { + maxWidth = BUTTON_MAX_WIDTH; + } + + // They squash a bit before they pop, hence the 2 + return rows * (contentsRect().width() / maxWidth) + 2; + } + else + { + // Overlap slightly and ugly arrows appear, hence -1 + return rows - 1; + } +} + +bool TaskBar::shouldGroup() const +{ + return TaskBarSettings::groupTasks() == TaskBarSettings::GroupAlways || + (TaskBarSettings::groupTasks() == TaskBarSettings::GroupWhenFull && + taskCount() > maximumButtonsWithoutShrinking()); +} + +void TaskBar::reGroup() +{ + isGrouping = shouldGroup(); + blocklayout = true; + + TaskContainer::Iterator lastContainer = m_hiddenContainers.end(); + for (TaskContainer::Iterator it = m_hiddenContainers.begin(); + it != lastContainer; + ++it) + { + (*it)->finish(); + m_deletableContainers.append(*it); + } + m_hiddenContainers.clear(); + + for (TaskContainer::List::const_iterator it = containers.constBegin(); + it != containers.constEnd(); + ++it) + { + (*it)->finish(); + m_deletableContainers.append(*it); + } + containers.clear(); + + Task::Dict tasks = TaskManager::the()->tasks(); + Task::Dict::iterator lastTask = tasks.end(); + for (Task::Dict::iterator it = tasks.begin(); it != lastTask; ++it) + { + Task::Ptr task = it.data(); + if (showScreen() == -1 || task->isOnScreen(showScreen())) + { + add(task); + } + } + + Startup::List startups = TaskManager::the()->startups(); + Startup::List::iterator itEnd = startups.end(); + for (Startup::List::iterator sIt = startups.begin(); sIt != itEnd; ++sIt) + { + add(*sIt); + } + + blocklayout = false; + reLayoutEventually(); +} + + +TaskContainer::List TaskBar::filteredContainers() +{ + // filter task container list + TaskContainer::List list; + + for (TaskContainer::List::const_iterator it = containers.constBegin(); + it != containers.constEnd(); + ++it) + { + TaskContainer* c = *it; + if ((m_showAllWindows || c->onCurrentDesktop()) && + (!m_showOnlyIconified || c->isIconified()) && + ((showScreen() == -1) || c->isOnScreen())) + { + list.append(c); + c->show(); + } + else + { + c->hide(); + } + } + + return list; +} + +void TaskBar::activateNextTask(bool forward) +{ + bool forcenext = false; + TaskContainer::List list = filteredContainers(); + + // this is necessary here, because 'containers' is unsorted and + // we want to iterate over the _shown_ task containers in a linear way + if (m_sortByDesktop) + { + sortContainersByDesktop(list); + } + + int numContainers = list.count(); + TaskContainer::List::iterator it; + for (int i = 0; i < numContainers; ++i) + { + it = forward ? list.at(i) : list.at(numContainers - i - 1); + + if (it != list.end() && (*it)->activateNextTask(forward, forcenext)) + { + return; + } + } + + if (forcenext) + { + // moving forward from the last, or backward from the first, loop around + for (int i = 0; i < numContainers; ++i) + { + it = forward ? list.at(i) : list.at(numContainers - i - 1); + + if (it != list.end() && (*it)->activateNextTask(forward, forcenext)) + { + return; + } + } + + return; + } + + forcenext = true; // select first + for (int i = 0; i < numContainers; ++i) + { + it = forward ? list.at(i) : list.at(numContainers - i - 1); + + if (it == list.end()) + { + break; + } + + TaskContainer* c = *it; + if (m_sortByDesktop) + { + if (forward ? c->desktop() < TaskManager::the()->currentDesktop() + : c->desktop() > TaskManager::the()->currentDesktop()) + { + continue; + } + } + + if (c->activateNextTask(forward, forcenext)) + { + return; + } + } +} + +void TaskBar::wheelEvent(QWheelEvent* e) +{ + if (e->delta() > 0) + { + // scroll away from user, previous task + activateNextTask(false); + } + else + { + // scroll towards user, next task + activateNextTask(true); + } +} + +void TaskBar::slotActivateNextTask() +{ + activateNextTask( true ); +} + +void TaskBar::slotActivatePreviousTask() +{ + activateNextTask( false ); +} + +void TaskBar::slotSettingsChanged( int category ) +{ + if( category == (int) KApplication::SETTINGS_SHORTCUTS ) + { + keys->readSettings(); + keys->updateConnections(); + } +} + +int TaskBar::showScreen() const +{ + if (m_showOnlyCurrentScreen && m_currentScreen == -1) + { + const_cast<TaskBar*>(this)->m_currentScreen = + QApplication::desktop()->screenNumber(mapToGlobal(this->geometry().topLeft())); + } + + return m_currentScreen; +} + +QImage* TaskBar::blendGradient(const QSize& size) +{ + if (m_blendGradient.isNull() || m_blendGradient.size() != size) + { + QPixmap bgpm(size); + QPainter bgp(&bgpm); + bgpm.fill(black); + + if (QApplication::reverseLayout()) + { + QImage gradient = KImageEffect::gradient( + QSize(30, size.height()), + QColor(255,255,255), + QColor(0,0,0), + KImageEffect::HorizontalGradient); + bgp.drawImage(0, 0, gradient); + } + else + { + QImage gradient = KImageEffect::gradient( + QSize(30, size.height()), + QColor(0,0,0), + QColor(255,255,255), + KImageEffect::HorizontalGradient); + bgp.drawImage(size.width() - 30, 0, gradient); + } + + m_blendGradient = bgpm.convertToImage(); + } + + return &m_blendGradient; +} + +void TaskBar::sortContainersByDesktop(TaskContainer::List& list) +{ + typedef QValueVector<QPair<int, QPair<int, TaskContainer*> > > SortVector; + SortVector sorted; + sorted.resize(list.count()); + int i = 0; + + TaskContainer::List::ConstIterator lastUnsorted(list.constEnd()); + for (TaskContainer::List::ConstIterator it = list.constBegin(); + it != lastUnsorted; + ++it) + { + sorted[i] = qMakePair((*it)->desktop(), qMakePair(i, *it)); + ++i; + } + + qHeapSort(sorted); + + list.clear(); + SortVector::const_iterator lastSorted(sorted.constEnd()); + for (SortVector::const_iterator it = sorted.constBegin(); + it != lastSorted; + ++it) + { + list.append((*it).second.second); + } +} + diff --git a/kicker/taskbar/taskbar.h b/kicker/taskbar/taskbar.h new file mode 100644 index 000000000..73c397670 --- /dev/null +++ b/kicker/taskbar/taskbar.h @@ -0,0 +1,135 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@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. + +******************************************************************/ + +#ifndef __taskbar_h__ +#define __taskbar_h__ + +#include <kpanelextension.h> +#include <taskmanager.h> + +#include "taskcontainer.h" +#include "panner.h" +#include "kshadowengine.h" + +#define WINDOWLISTBUTTON_SIZE 15 +#define BUTTON_MAX_WIDTH 200 +#define BUTTON_MIN_WIDTH 20 + +class Startup; +class Task; +class KGlobalAccel; + +class TaskBar : public Panner +{ + Q_OBJECT + +public: + TaskBar( QWidget *parent = 0, const char *name = 0 ); + ~TaskBar(); + + QSize sizeHint() const; + QSize sizeHint( KPanelExtension::Position, QSize maxSize ) const; + + void setOrientation( Orientation ); + void setArrowType( Qt::ArrowType at ); + + int containerCount() const; + int taskCount() const; + int showScreen() const; + + bool showIcon() const { return m_showIcon; } + bool sortByDesktop() const { return m_sortByDesktop; } + bool showAllWindows() const { return m_showAllWindows; } + + QImage* blendGradient(const QSize& size); + + KTextShadowEngine *textShadowEngine(); + +public slots: + void configure(); + void setBackground(); + +signals: + void containerCountChanged(); + +protected slots: + void add(Task::Ptr); + void add(Startup::Ptr); + void showTaskContainer(TaskContainer*); + void remove(Task::Ptr task, TaskContainer *container = 0); + void remove(Startup::Ptr startup, TaskContainer *container = 0); + + void desktopChanged( int ); + void windowChanged(Task::Ptr); + void windowChangedGeometry(Task::Ptr); + + void publishIconGeometry(); + + void activateNextTask( bool forward ); + void slotActivateNextTask(); + void slotActivatePreviousTask(); + void slotSettingsChanged(int); + void reLayout(); + +protected: + void reLayoutEventually(); + void viewportMousePressEvent( QMouseEvent* ); + void viewportMouseReleaseEvent( QMouseEvent* ); + void viewportMouseDoubleClickEvent( QMouseEvent* ); + void viewportMouseMoveEvent( QMouseEvent* ); + void wheelEvent(QWheelEvent*); + void propagateMouseEvent( QMouseEvent* ); + void resizeEvent( QResizeEvent* ); + void moveEvent( QMoveEvent* ); + bool idMatch( const QString& id1, const QString& id2 ); + TaskContainer::List filteredContainers(); + +private: + void sortContainersByDesktop(TaskContainer::List& list); + void setViewportBackground(); + + bool blocklayout; + bool m_showAllWindows; + // The screen to show, -1 for all screens + int m_currentScreen; + bool m_showOnlyCurrentScreen; + bool m_sortByDesktop; + bool m_showIcon; + bool m_showOnlyIconified; + ArrowType arrowType; + TaskContainer::List containers; + TaskContainer::List m_hiddenContainers; + TaskContainer::List m_deletableContainers; + PixmapList frames; + int maximumButtonsWithoutShrinking() const; + bool shouldGroup() const; + bool isGrouping; + void reGroup(); + KGlobalAccel* keys; + KTextShadowEngine* m_textShadowEngine; + QTimer m_relayoutTimer; + bool m_ignoreUpdates; + QImage m_blendGradient; +}; + +#endif diff --git a/kicker/taskbar/taskbar.kcfg b/kicker/taskbar/taskbar.kcfg new file mode 100644 index 000000000..173e5b8ec --- /dev/null +++ b/kicker/taskbar/taskbar.kcfg @@ -0,0 +1,173 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="ktaskbarrc"/> + + <group name="General"> + <entry key="ShowAllWindows" type="Bool" > + <default>true</default> + <label>Show windows from all desktops</label> + <whatsthis>Turning this option off will cause the taskbar to display <b>only</b> the windows on the current desktop. \n\nBy default, this option is selected and all windows are shown.</whatsthis> + </entry> + <entry key="ShowOnlyIconified" type="Bool" > + <default>false</default> + <label>Show only minimized windows</label> + <whatsthis>Select this option if you want the taskbar to display <b>only</b> minimized windows. \n\nBy default, this option is not selected and the taskbar will show all windows.</whatsthis> + </entry> + <entry key="GroupTasks" type="Enum" > + <choices> + <choice name="GroupNever"> + <label>Never</label> + </choice> + <choice name="GroupWhenFull"> + <label>When Taskbar Full</label> + </choice> + <choice name="GroupAlways"> + <label>Always</label> + </choice> + </choices> + <default>GroupWhenFull</default> + <label>Group similar tasks:</label> + <whatsthis>The taskbar can group similar windows into single buttons. When one of these window group buttons are clicked on a menu appears showing all the windows in that group. This can be especially useful with the <em>Show all windows</em> option.\n\nYou can set the taskbar to <strong>Never</strong> group windows, to <strong>Always</strong> group windows or to group windows only <strong>When the Taskbar is Full</strong>.\n\nBy default the taskbar groups windows when it is full.</whatsthis> + </entry> + <entry key="SortByDesktop" type="Bool" > + <default>true</default> + <label>Sort windows by desktop</label> + <whatsthis>Selecting this option causes the taskbar to show windows in order of the desktop they appear on.\n\nBy default this option is selected.</whatsthis> + </entry> + <entry key="SortByApp" type="Bool" > + <default>true</default> + <label>Sort windows by application</label> + <whatsthis>Selecting this option causes the taskbar to show windows ordered by application.\n\nBy default this option is selected.</whatsthis> + </entry> + <entry key="ShowIcon" type="Bool" > + <default>true</default> + <label>Show application icons</label> + <whatsthis>Select this option if you want window icons to appear along with their titles in the taskbar.\n\nBy default this option is selected.</whatsthis> + </entry> + <entry key="MaximumButtonWidth" type="Int" > + <default>200</default> + <min>0</min> + <label></label> + <whatsthis></whatsthis> + </entry> + <entry key="MinimumButtonHeight" type="Int" > + <default>18</default> + <label></label> + <min>1</min> + <whatsthis></whatsthis> + </entry> + <entry key="ShowCurrentScreenOnly" type="Bool" > + <default>false</default> + <label>Show windows from all screens</label> + <whatsthis>Turning this option off will cause the taskbar to display <b>only</b> windows which are on the same Xinerama screen as the taskbar.\n\nBy default, this option is selected and all windows are shown.</whatsthis> + </entry> + <entry key="taskbarFont" type="Font" > + <default></default> + <label></label> + <whatsthis></whatsthis> + </entry> + <entry key="ShowWindowListBtn" type="Bool" > + <default>false</default> + <label>Show window list button</label> + <whatsthis>Selecting this option causes the taskbar to display a button that, when clicked, shows a list of all windows in a popup menu.</whatsthis> + </entry> + <entry key="$(Button)Action" type="Enum" > + <parameter name="Button" type="Enum"> + <values> + <value>LeftButton</value> + <value>MiddleButton</value> + <value>RightButton</value> + </values> + </parameter> + <choices> + <choice name="ShowTaskList"> + <label>Show Task List</label> + </choice> + <choice name="ShowOperationsMenu"> + <label>Show Operations Menu</label> + </choice> + <choice name="ActivateRaiseOrMinimize"> + <label>Activate, Raise or Minimize Task</label> + </choice> + <choice name="Activate"> + <label>Activate Task</label> + </choice> + <choice name="Raise"> + <label>Raise Task</label> + </choice> + <choice name="Lower"> + <label>Lower Task</label> + </choice> + <choice name="Minimize"> + <label>Minimize Task</label> + </choice> + <choice name="ToCurrentDesktop"> + <label>Move To Current Desktop</label> + </choice> + <choice name="Close"> + <label>Close Task</label> + </choice> + </choices> + <default param="LeftButton">ShowTaskList</default> + <default param="MiddleButton">ActivateRaiseOrMinimize</default> + <default param="RightButton">ShowOperationsMenu</default> + <label>Mouse button actions</label> + <whatsthis></whatsthis> + </entry> + </group> + + <group name="Appearance"> + <entry key="AttentionBlinkIterations" type="Int"> + <default>4</default> + <label>The number of times to blink a taskbar button when a window asks for attention. Setting this to 1000 or greater causes the button to blink forever.</label> + <min>0</min> + </entry> + <entry key="DrawButtons" type="Bool" > + <default>false</default> + <label>Draw taskbar entries "flat" and not as a button</label> + <whatsthis>Turning this option on will cause the taskbar to draw visible button frames for each entry in the taskbar. \n\nBy default, this option is off.</whatsthis> + </entry> + <entry key="HaloText" type="Bool" > + <default>false</default> + <label>Draw taskbar text with a halo around it</label> + <whatsthis>Turning this option on will cause the taskbar to draw fancier text that has an outline around it. While this is useful for transparent panels or particularly dark panel backgrounds, it is slower.</whatsthis> + </entry> + <entry key="ShowButtonOnHover" type="Bool" > + <default>true</default> + <label>Show a visible button frame on the task the cursor is positioned over</label> + </entry> + <entry key="ShowThumbnails" type="Bool" > + <default>false</default> + <label>Show thumbnails instead of icons in the mouse-over effects</label> + <whatsthis>Enabling this option will draw a thumbnail of the window in its mouse-over effect.<p>If a window is minimized or resides on a different desktop while the taskbar is starting, an icon is shown until the window is restored or the appropriate desktop is activated, respectively.</p></whatsthis> + </entry> + <entry key="ThumbnailMaxDimension" type="UInt" > + <default>100</default> + <label>Maximum width/height of the thumbnail in pixels</label> + <whatsthis>A thumbnail is created by resizing the window. The scaling factor is determined by its largest dimension and this value. In doing so, the thumbnail's size will not exceed this value in any dimension.</whatsthis> + </entry> + <entry key="UseCustomColors" type="Bool" > + <default>false</default> + <label>Use custom colors for taskbar buttons text and background</label> + <whatsthis>Turning this option on will allow choosing your own colors for taskbar buttons text and background.</whatsthis> + </entry> + <entry name="ActiveTaskTextColor" type="Color" > + <label>Color to use for active task button text</label> + <default code="true">QColor()</default> + <whatsthis>This color is used for displaying text on taskbar button for task which is active at the moment.</whatsthis> + </entry> + <entry name="InactiveTaskTextColor" type="Color" > + <label>Color to use for inactive tasks button text</label> + <default code="true">QColor()</default> + <whatsthis>This color is used for displaying text on taskbar button for tasks other than active.</whatsthis> + </entry> + <entry name="TaskBackgroundColor" type="Color" > + <label>Color to use for taskbar buttons background</label> + <default code="true">QColor()</default> + <whatsthis>This color is used for displaying background of taskbar buttons.</whatsthis> + </entry> + </group> +</kcfg> diff --git a/kicker/taskbar/taskbarbindings.cpp b/kicker/taskbar/taskbarbindings.cpp new file mode 100644 index 000000000..63accd854 --- /dev/null +++ b/kicker/taskbar/taskbarbindings.cpp @@ -0,0 +1,37 @@ +/***************************************************************** + +Copyright (c) 2001-2004 Matthias Elter <elter@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. + +******************************************************************/ + +#ifndef NOSLOTS +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), QString::null, key3, key4, this, SLOT(fnSlot) ) +#else +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), QString::null, key3, key4 ) +#endif +#define WIN KKey::QtWIN + + DEF( I18N_NOOP( "Next Taskbar Entry" ), 0, 0, slotActivateNextTask() ); + DEF( I18N_NOOP( "Previous Taskbar Entry" ), 0, 0, slotActivatePreviousTask() ); + +#undef DEF +#undef WIN diff --git a/kicker/taskbar/taskbarcontainer.cpp b/kicker/taskbar/taskbarcontainer.cpp new file mode 100644 index 000000000..a548b7837 --- /dev/null +++ b/kicker/taskbar/taskbarcontainer.cpp @@ -0,0 +1,292 @@ +/***************************************************************** + +Copyright (c) 2001 John Firebaugh <jfirebaugh@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 <qlayout.h> +#include <qtimer.h> + +#include <dcopclient.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kwindowlistmenu.h> + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include "simplebutton.h" + +#include "taskbar.h" +#include "taskbarsettings.h" + +#include "taskbarcontainer.h" +#include "taskbarcontainer.moc" + +TaskBarContainer::TaskBarContainer( bool enableFrame, QWidget *parent, const char *name ) + : QFrame(parent, name), + direction( KPanelApplet::Up ), + showWindowListButton( true ), + windowListButton(0), + windowListMenu(0) +{ + setBackgroundOrigin( AncestorOrigin ); + + uint margin; + if ( enableFrame ) + { + setFrameStyle( Sunken | StyledPanel ); + margin = frameWidth(); + } + else + { + setFrameStyle( NoFrame ); + margin = 0; + } + + layout = new QBoxLayout( this, QApplication::reverseLayout() ? + QBoxLayout::RightToLeft : + QBoxLayout::LeftToRight ); + layout->setMargin( margin ); + + // scrollable taskbar + taskBar = new TaskBar(this); + layout->addWidget( taskBar ); + + connect( taskBar, SIGNAL( containerCountChanged() ), SIGNAL( containerCountChanged() ) ); + + setBackground(); + + // read settings and setup layout + configure(); + + connectDCOPSignal("", "", "kdeTaskBarConfigChanged()", + "configChanged()", false); +} + +TaskBarContainer::~TaskBarContainer() +{ + delete windowListMenu; +} + +void TaskBarContainer::configure() +{ + setFont(TaskBarSettings::taskbarFont()); + showWindowListButton = TaskBarSettings::showWindowListBtn(); + + if (!showWindowListButton) + { + delete windowListButton; + windowListButton = 0; + delete windowListMenu; + windowListMenu = 0; + } + else if (windowListButton == 0) + { + // window list button + windowListButton = new SimpleButton(this); + windowListMenu= new KWindowListMenu; + connect(windowListButton, SIGNAL(pressed()), + SLOT(showWindowListMenu())); + connect(windowListMenu, SIGNAL(aboutToHide()), + SLOT(windowListMenuAboutToHide())); + + // geometry + QString icon; + switch (direction) + { + case KPanelApplet::Up: + icon = "1uparrow"; + windowListButton->setMaximumHeight(BUTTON_MAX_WIDTH); + break; + case KPanelApplet::Down: + icon = "1downarrow"; + windowListButton->setMaximumHeight(BUTTON_MAX_WIDTH); + break; + case KPanelApplet::Left: + icon = "1leftarrow"; + windowListButton->setMaximumWidth(BUTTON_MAX_WIDTH); + break; + case KPanelApplet::Right: + icon = "1rightarrow"; + windowListButton->setMaximumWidth(BUTTON_MAX_WIDTH); + break; + } + + windowListButton->setPixmap(kapp->iconLoader()->loadIcon(icon, + KIcon::Panel, + 16)); + windowListButton->setMinimumSize(windowListButton->sizeHint()); + layout->insertWidget(0, windowListButton); + windowListButton->show(); + } +} + +void TaskBarContainer::configChanged() +{ + // we have a separate method here to connect to the DCOP signal + // instead of connecting direclty to taskbar so that Taskbar + // doesn't have to also connect to the DCOP signal (less places + // to change/fix it if/when it changes) without calling + // configure() twice on taskbar on start up + TaskBarSettings::self()->readConfig(); + + configure(); + taskBar->configure(); +} + +void TaskBarContainer::preferences() +{ + QByteArray data; + + if (!kapp->dcopClient()->isAttached()) + { + kapp->dcopClient()->attach(); + } + + kapp->dcopClient()->send("kicker", "kicker", "showTaskBarConfig()", data); +} + +void TaskBarContainer::orientationChange(Orientation o) +{ + if (o == Horizontal) + { + if (windowListButton) + { + windowListButton->setFixedWidth(WINDOWLISTBUTTON_SIZE); + windowListButton->setMaximumHeight(BUTTON_MAX_WIDTH); + } + layout->setDirection(QApplication::reverseLayout() ? + QBoxLayout::RightToLeft : + QBoxLayout::LeftToRight); + } + else + { + if (windowListButton) + { + windowListButton->setMaximumWidth(BUTTON_MAX_WIDTH); + windowListButton->setFixedHeight(WINDOWLISTBUTTON_SIZE); + } + layout->setDirection(QBoxLayout::TopToBottom); + } + + taskBar->setOrientation(o); + if (windowListButton) + { + windowListButton->setOrientation(o); + } + layout->activate(); +} + +void TaskBarContainer::popupDirectionChange(KPanelApplet::Direction d) +{ + direction = d; + ArrowType at = UpArrow; + + QString icon; + switch (d) + { + case KPanelApplet::Up: + icon = "1uparrow"; + at = UpArrow; + break; + case KPanelApplet::Down: + icon = "1downarrow"; + at = DownArrow; + break; + case KPanelApplet::Left: + icon = "1leftarrow"; + at = LeftArrow; + break; + case KPanelApplet::Right: + icon = "1rightarrow"; + at = RightArrow; + break; + } + + taskBar->setArrowType(at); + + if (windowListButton) + { + windowListButton->setPixmap(kapp->iconLoader()->loadIcon(icon, + KIcon::Panel, + 16)); + windowListButton->setMinimumSize(windowListButton->sizeHint()); + } +} + +void TaskBarContainer::showWindowListMenu() +{ + if (!windowListMenu) + return; + + windowListMenu->init(); + + // calc popup menu position + QPoint pos( mapToGlobal( QPoint(0,0) ) ); + + switch( direction ) { + case KPanelApplet::Right: + pos.setX( pos.x() + width() ); + break; + case KPanelApplet::Left: + pos.setX( pos.x() - windowListMenu->sizeHint().width() ); + break; + case KPanelApplet::Down: + pos.setY( pos.y() + height() ); + break; + case KPanelApplet::Up: + pos.setY( pos.y() - windowListMenu->sizeHint().height() ); + default: + break; + } + + disconnect( windowListButton, SIGNAL( pressed() ), this, SLOT( showWindowListMenu() ) ); + windowListMenu->exec( pos ); + QTimer::singleShot(100, this, SLOT(reconnectWindowListButton())); +} + +void TaskBarContainer::windowListMenuAboutToHide() +{ + // this ensures that when clicked AGAIN, the window list button doesn't cause the + // window list menu to show again. usability, you see. hoorah. + windowListButton->setDown( false ); +} + +void TaskBarContainer::reconnectWindowListButton() +{ + connect( windowListButton, SIGNAL( pressed() ), SLOT( showWindowListMenu() ) ); +} + +QSize TaskBarContainer::sizeHint( KPanelExtension::Position p, QSize maxSize) const +{ + QSize size = taskBar->sizeHint( p, maxSize ); + if ( (p == KPanelExtension::Left || p == KPanelExtension::Right) && showWindowListButton ) { + return QSize( size.width(), size.height() + WINDOWLISTBUTTON_SIZE ); + } + return size; +} + +void TaskBarContainer::setBackground() +{ + taskBar->setBackground(); +} diff --git a/kicker/taskbar/taskbarcontainer.h b/kicker/taskbar/taskbarcontainer.h new file mode 100644 index 000000000..c265036b6 --- /dev/null +++ b/kicker/taskbar/taskbarcontainer.h @@ -0,0 +1,75 @@ +/***************************************************************** + +Copyright (c) 2001 John Firebaugh <jfirebaugh@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. + +******************************************************************/ + +#ifndef __taskbarcontainer_h__ +#define __taskbarcontainer_h__ + +#include <qframe.h> + +#include <dcopobject.h> +#include <kpanelapplet.h> +#include <kpanelextension.h> + +class QBoxLayout; +class SimpleButton; +class KWindowListMenu; +class TaskBar; + +class KDE_EXPORT TaskBarContainer : public QFrame, public DCOPObject +{ + Q_OBJECT + K_DCOP + +public: + TaskBarContainer( bool enableFrame, QWidget* parent = 0, const char* name = 0 ); + ~TaskBarContainer(); + + void orientationChange( Orientation ); + void popupDirectionChange( KPanelApplet::Direction ); + void preferences(); + //FIXME: don't use Position, use Orientation + QSize sizeHint( KPanelExtension::Position, QSize maxSize ) const; + void setBackground(); + +k_dcop: + void configChanged(); + +signals: + void containerCountChanged(); + +protected slots: + void configure(); + void showWindowListMenu(); + void windowListMenuAboutToHide(); + void reconnectWindowListButton(); + +private: + KPanelApplet::Direction direction; + bool showWindowListButton; + QBoxLayout * layout; + TaskBar * taskBar; + SimpleButton * windowListButton; + KWindowListMenu * windowListMenu; +}; + +#endif diff --git a/kicker/taskbar/taskbarsettings.kcfgc b/kicker/taskbar/taskbarsettings.kcfgc new file mode 100644 index 000000000..45c0da407 --- /dev/null +++ b/kicker/taskbar/taskbarsettings.kcfgc @@ -0,0 +1,7 @@ +File=taskbar.kcfg +Singleton=true +ClassName=TaskBarSettings +Mutators=true +Visibility=KDE_EXPORT +SetUserTexts=true +GlobalEnums=true diff --git a/kicker/taskbar/taskcontainer.cpp b/kicker/taskbar/taskcontainer.cpp new file mode 100644 index 000000000..7025d643a --- /dev/null +++ b/kicker/taskbar/taskcontainer.cpp @@ -0,0 +1,1643 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> +Copyright (c) 2002 John Firebaugh <jfirebaugh@kde.org> +Copyright (c) 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 <qbitmap.h> +#include <qcolor.h> +#include <qcursor.h> +#include <qimage.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qstyle.h> +#include <qstylesheet.h> +#include <qtooltip.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kglobalsettings.h> +#include <klocale.h> +#include <kiconeffect.h> +#include <kiconloader.h> +#include <kimageeffect.h> + +#include "global.h" +#include "kickerSettings.h" +#include "paneldrag.h" +#include "taskbar.h" +#include "taskbarsettings.h" +#include "tasklmbmenu.h" +#include "taskrmbmenu.h" + +#include "taskcontainer.h" +#include "taskcontainer.moc" + +TaskContainer::TaskContainer(Task::Ptr task, TaskBar* bar, + QWidget *parent, const char *name) + : QToolButton(parent, name), + currentFrame(0), + attentionState(-1), + lastActivated(0), + m_menu(0), + m_startup(0), + arrowType(Qt::UpArrow), + taskBar(bar), + discardNextMouseEvent(false), + aboutToActivate(false), + m_mouseOver(false), + m_paintEventCompression(false) +{ + init(); + setAcceptDrops(true); // Always enabled to activate task during drag&drop. + + add(task); + + // we abuse this timer once to get shown + // no point in having another timer just for this, and + // a single shot won't do because we need to stop the timer + // in case our task is deleted out from under us + dragSwitchTimer.start(0, true); +} + +TaskContainer::TaskContainer(Startup::Ptr startup, PixmapList& startupFrames, + TaskBar* bar, QWidget *parent, const char *name) + : QToolButton(parent, name), + currentFrame(0), + frames(startupFrames), + attentionState(-1), + lastActivated(0), + m_menu(0), + m_startup(startup), + arrowType(Qt::LeftArrow), + taskBar(bar), + discardNextMouseEvent(false), + aboutToActivate(false), + m_mouseOver(false), + m_paintEventCompression(false) +{ + init(); + setEnabled(false); + + sid = m_startup->bin(); + + connect(m_startup, SIGNAL(changed()), SLOT(update())); + + dragSwitchTimer.start(333, true); +} + +void TaskContainer::init() +{ + setWFlags(WNoAutoErase); + setBackgroundMode(NoBackground); + animBg = QPixmap(16, 16); + + installEventFilter(KickerTip::the()); + + connect(&animationTimer, SIGNAL(timeout()), SLOT(animationTimerFired())); + connect(&dragSwitchTimer, SIGNAL(timeout()), SLOT(showMe())); + connect(&attentionTimer, SIGNAL(timeout()), SLOT(attentionTimerFired())); + connect(&m_paintEventCompressionTimer, SIGNAL(timeout()), SLOT(updateNow())); +} + +TaskContainer::~TaskContainer() +{ + if (m_menu) + { + delete m_menu; + m_menu = 0; + } + + stopTimers(); +} + +void TaskContainer::showMe() +{ + if(!frames.isEmpty() && taskBar->showIcon()) + animationTimer.start(100); + + emit showMe(this); + disconnect(&dragSwitchTimer, SIGNAL(timeout()), this, SLOT(showMe())); + connect(&dragSwitchTimer, SIGNAL(timeout()), SLOT(dragSwitch())); +} + +void TaskContainer::stopTimers() +{ + animationTimer.stop(); + dragSwitchTimer.stop(); + attentionTimer.stop(); +} + +void TaskContainer::taskChanged(bool geometryOnlyChange) +{ + if (geometryOnlyChange) + { + // we really don't care about those changes, which we may be getting + // thanks to the pager, for instance, turning it on in taskmanager. + // // let's ignore them so we don't end up with tons of processing going on + return; + } + + const QObject* source = sender(); + Task::Ptr task = 0; + Task::List::const_iterator itEnd = tasks.constEnd(); + for (Task::List::const_iterator it = tasks.constBegin(); it != itEnd; ++it) + { + if (*it == source) + { + task = *it; + break; + } + } + + if (task) + { + checkAttention(task); + } + + KickerTip::Client::updateKickerTip(); + update(); +} + +void TaskContainer::iconChanged() +{ + const QObject* source = sender(); + Task::Ptr task = 0; + Task::List::const_iterator itEnd = tasks.constEnd(); + for (Task::List::const_iterator it = tasks.constBegin(); it != itEnd; ++it) + { + if (*it == source) + { + task = *it; + break; + } + } + + if (task && !m_filteredTasks.empty() && task != m_filteredTasks.first()) + { + if (m_menu) + { + m_menu->update(); + } + return; + } + + KickerTip::Client::updateKickerTip(); + QToolButton::update(); +} + +void TaskContainer::setLastActivated() +{ + Task::List::const_iterator itEnd = m_filteredTasks.constEnd(); + for (Task::List::const_iterator it = m_filteredTasks.constBegin(); it != itEnd; ++it) + { + Task::Ptr t = *it; + if ( t->isActive() ) + { + lastActivated = t; + return; + } + } + lastActivated = 0L; +} + + +void TaskContainer::animationTimerFired() +{ + if (!frames.isEmpty() && taskBar->showIcon() && frames.at(currentFrame) != frames.end()) + { + QPixmap *pm = *frames.at(currentFrame); + + // draw pixmap + if ( pm && !pm->isNull() ) { + // we only have to redraw the background for frames 0, 8 and 9 + if ( currentFrame == 0 || currentFrame > 7 ) { + // double buffered painting + QPixmap composite( animBg ); + bitBlt( &composite, 0, 0, pm ); + bitBlt( this, iconRect.x(), iconRect.y(), &composite ); + } + else + bitBlt( this, iconRect.x(), iconRect.y(), pm ); + } + + // increment frame counter + if ( currentFrame >= 9) + currentFrame = 0; + else + currentFrame++; + } +} + +void TaskContainer::checkAttention(const Task::Ptr t) +{ + bool attention = t ? t->demandsAttention() : false; + if (attention && attentionState == -1) // was activated + { + attentionState = 0; + attentionTimer.start(500); + } + else if(!attention && attentionState >= 0) + { // need to check all + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + if ((*it)->demandsAttention()) + { + attention = true; + break; + } + } + + if (!attention) + { + attentionTimer.stop(); + attentionState = -1; + } + } +} + +void TaskContainer::attentionTimerFired() +{ + assert( attentionState != -1 ); + if (attentionState < TaskBarSettings::attentionBlinkIterations()*2) + { + ++attentionState; + } + else if (TaskBarSettings::attentionBlinkIterations() < 1000) + { + attentionTimer.stop(); + } + else + { + // we have a "forever" blinker (attentionBlinkIterations > 999) and have reached + // the upper limit. so we need to decrement the attentionState to make it blink + --attentionState; + } + update(); +} + +QSizePolicy TaskContainer::sizePolicy() const +{ + return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); +} + +void TaskContainer::resizeEvent( QResizeEvent * ) +{ + // calculate the icon rect + QRect br( style().subRect( QStyle::SR_PushButtonContents, this ) ); + iconRect = QStyle::visualRect( QRect(br.x() + 2, (height() - 16) / 2, 16, 16), this ); +} + +void TaskContainer::add(Task::Ptr task) +{ + if (!task) + { + return; + } + + tasks.append(task); + + if (sid.isEmpty()) + { + sid = task->classClass(); + } + + updateFilteredTaskList(); + checkAttention(task); + + KickerTip::Client::updateKickerTip(); + update(); + + connect(task, SIGNAL(changed(bool)), SLOT(taskChanged(bool))); + connect(task, SIGNAL(iconChanged()), SLOT(iconChanged())); + connect(task, SIGNAL(activated()), SLOT(setLastActivated())); +} + +void TaskContainer::remove(Task::Ptr task) +{ + if (!task) + { + return; + } + + task->publishIconGeometry(QRect()); + for (Task::List::Iterator it = tasks.begin(); it != tasks.end(); ++it) + { + if ((*it) == task) + { + tasks.erase(it); + break; + } + } + + updateFilteredTaskList(); + + if (isEmpty()) + { + stopTimers(); + return; + } + + checkAttention(); + KickerTip::Client::updateKickerTip(); + update(); +} + +void TaskContainer::remove(Startup::Ptr startup) +{ + if (!startup || startup != m_startup) + { + return; + } + + m_startup = 0; + animationTimer.stop(); + frames.clear(); + + if (!tasks.isEmpty()) + { + setEnabled(true); + } +} + +bool TaskContainer::contains(Task::Ptr task) +{ + if (!task) + { + return false; + } + + for (Task::List::Iterator it = tasks.begin(); it != tasks.end(); ++it) + { + if ((*it) == task) + { + return true; + } + } + + return false; +} + +bool TaskContainer::contains(Startup::Ptr startup) +{ + return startup && (m_startup == startup); +} + +bool TaskContainer::contains(WId win) +{ + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + if ((*it)->window() == win) + { + return true; + } + } + + return false; +} + +bool TaskContainer::isEmpty() +{ + return (tasks.isEmpty() && !m_startup); +} + +QString TaskContainer::id() +{ + return sid; +} + +void TaskContainer::updateNow() +{ + m_paintEventCompression = true; + update(); +} + +void TaskContainer::setBackground() +{ + updateNow(); +} + +void TaskContainer::paintEvent( QPaintEvent* ) +{ + if (!m_paintEventCompression) + { + if (!m_paintEventCompressionTimer.isActive()) + { + m_paintEventCompressionTimer.start(30, true); + } + return; + } + + m_paintEventCompression = false; + QPixmap* pm = new QPixmap(size()); + + const QPixmap* background = taskBar->backgroundPixmap(); + + if (background) + { + QPoint pt = mapTo(taskBar, QPoint(0, 0)) + taskBar->backgroundOffset(); + QPainter p(pm); + p.drawTiledPixmap(0, 0, width(), height(), *background, pt.x(), pt.y()); + p.end(); + } + else + { + pm->fill(taskBar->paletteBackgroundColor()); + } + + QPainter p; + p.begin(pm ,this); + drawButton(&p); + p.end(); + + bitBlt(this, 0, 0, pm); + delete pm; +} + +void TaskContainer::drawButton(QPainter *p) +{ + if (isEmpty()) + { + return; + } + + // get a pointer to the pixmap we're drawing on + QPixmap *pm((QPixmap*)p->device()); + QPixmap pixmap; // icon + Task::Ptr task = 0; + bool iconified = !TaskBarSettings::showOnlyIconified(); + bool halo = TaskBarSettings::haloText(); + bool alwaysDrawButtons = TaskBarSettings::drawButtons(); + bool drawButton = alwaysDrawButtons || + (m_mouseOver && !halo && isEnabled() && + TaskBarSettings::showButtonOnHover()); + QFont font(KGlobalSettings::taskbarFont()); + + // draw sunken if we contain the active task + bool active = false; + bool demandsAttention = false; + Task::List::iterator itEnd = m_filteredTasks.end(); + for (Task::List::iterator it = m_filteredTasks.begin(); it != itEnd; ++it) + { + task = *it; + if (iconified && !task->isIconified()) + { + iconified = false; + } + + if (task->isActive()) + { + active = true; + } + + if (task->demandsAttention()) + { + demandsAttention = attentionState == TaskBarSettings::attentionBlinkIterations() || + attentionState % 2 == 0; + } + } + + font.setBold(active); + + QColorGroup colors = palette().active(); + + if (TaskBarSettings::useCustomColors()) + { + colors.setColor( QColorGroup::Button, TaskBarSettings::taskBackgroundColor()); + colors.setColor( QColorGroup::Background, TaskBarSettings::taskBackgroundColor() ); + colors.setColor( QColorGroup::ButtonText, TaskBarSettings::inactiveTaskTextColor() ); + colors.setColor( QColorGroup::Text, TaskBarSettings::inactiveTaskTextColor() ); + } + + if (demandsAttention) + { + if (!drawButton) + { + halo = true; + + QRect r = rect(); + QColor line = colors.highlight(); + r.addCoords(2, 2, -2, -2); + p->fillRect(r, line); + for (int i = 0; i < 2; ++i) + { + line = KickerLib::blendColors(line, colors.background()); + p->setPen(QPen(line, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + r.addCoords(-1, -1, 1, 1); + p->drawRect(r); + } + } + + // blink until blink timeout, then display differently without blinking + colors.setColor( QColorGroup::Button, colors.highlight() ); + colors.setColor( QColorGroup::Background, colors.highlight() ); + colors.setColor( QColorGroup::ButtonText, colors.highlightedText() ); + colors.setColor( QColorGroup::Text, colors.highlightedText() ); + } + + if (active || aboutToActivate) + { + colors.setColor(QColorGroup::Button, colors.button().dark(110)); + } + + // get the task icon + if (task) + { + pixmap = task->pixmap(); + } + + bool sunken = isDown() || (alwaysDrawButtons && (active || aboutToActivate)); + bool reverse = QApplication::reverseLayout(); + QRect br(style().subRect(QStyle::SR_PushButtonContents, this)); + QPoint shift = QPoint(style().pixelMetric(QStyle::PM_ButtonShiftHorizontal), + style().pixelMetric(QStyle::PM_ButtonShiftVertical)); + + // draw button background + if (drawButton) + { + style().drawPrimitive(QStyle::PE_HeaderSection, p, + QRect(0, 0, width(), height()), + colors); + } + + // shift button label on sunken buttons + if (sunken) + { + p->translate(shift.x(), shift.y()); + } + + if (taskBar->showIcon()) + { + if (pixmap.isNull() && m_startup) + { + pixmap = SmallIcon(m_startup->icon()); + } + + if ( !pixmap.isNull() ) + { + // make sure it is no larger than 16x16 + if ( pixmap.width() > 16 || pixmap.height() > 16 ) + { + QImage tmp = pixmap.convertToImage(); + pixmap.convertFromImage( tmp.smoothScale( 16, 16 ) ); + } + + // fade out the icon when minimized + if (iconified) + { + KIconEffect::semiTransparent( pixmap ); + } + + // draw icon + QRect pmr(0, 0, pixmap.width(), pixmap.height()); + pmr.moveCenter(iconRect.center()); + p->drawPixmap(pmr, pixmap); + } + } + + // find text + QString text = name(); + + // modified overlay + static QString modStr = "[" + i18n( "modified" ) + "]"; + int modStrPos = text.find( modStr ); + int textPos = ( taskBar->showIcon() && (!pixmap.isNull() || m_startup)) ? 2 + 16 + 2 : 0; + + if (modStrPos >= 0) + { + // +1 because we include a space after the closing brace. + text.remove(modStrPos, modStr.length() + 1); + QPixmap modPixmap = SmallIcon("modified"); + + // draw modified overlay + if (!modPixmap.isNull()) + { + QRect r = QStyle::visualRect(QRect(br.x() + textPos, + (height() - 16) / 2, 16, 16), + this); + + if (iconified) + { + KIconEffect::semiTransparent(modPixmap); + } + + p->drawPixmap(r, modPixmap); + textPos += 16 + 2; + } + } + + // draw text + if (!text.isEmpty()) + { + QRect tr = QStyle::visualRect(QRect(br.x() + textPos + 1, 0, + width() - textPos, height()), + this); + int textFlags = AlignVCenter | SingleLine; + textFlags |= reverse ? AlignRight : AlignLeft; + QPen textPen; + + // get the color for the text label + if (iconified) + { + textPen = QPen(KickerLib::blendColors(colors.button(), colors.buttonText())); + } + else if (!active) + { + textPen = QPen(colors.buttonText()); + } + else // hack for the dotNET style and others + { + if (TaskBarSettings::useCustomColors()) + { + textPen = QPen(TaskBarSettings::activeTaskTextColor()); + } + else + { + textPen = p->pen(); + } + } + + int availableWidth = width() - (br.x() * 2) - textPos; + if (m_filteredTasks.count() > 1) + { + availableWidth -= 8; + } + + if (QFontMetrics(font).width(text) > availableWidth) + { + // draw text into overlay pixmap + QPixmap tpm(*pm); + QPainter tp(&tpm); + + if (sunken) + { + tp.translate(shift.x(), shift.y()); + } + + tp.setFont(font); + tp.setPen(textPen); + + if (halo) + { + taskBar->textShadowEngine()->drawText(tp, tr, textFlags, text, size()); + } + else + { + tp.drawText(tr, textFlags, text); + } + + // blend text into background image + QImage img = pm->convertToImage(); + QImage timg = tpm.convertToImage(); + KImageEffect::blend(img, timg, *taskBar->blendGradient(size()), KImageEffect::Red); + pm->convertFromImage(img); + } + else + { + p->setFont(font); + p->setPen(textPen); + + if (halo) + { + taskBar->textShadowEngine()->drawText(*p, tr, textFlags, text, size()); + } + else + { + p->drawText(tr, textFlags, text); + } + } + } + + if (!frames.isEmpty() && m_startup && frames.at(currentFrame) != frames.end()) + { + QPixmap *anim = *frames.at(currentFrame); + + if (anim && !anim->isNull()) + { + // save the background for the other frames + bitBlt(&animBg, QPoint(0,0), pm, iconRect); + // draw the animation frame + bitBlt(pm, iconRect.x(), iconRect.y(), anim); + } + } + + if (sunken) + { + // Change the painter back so the arrow, etc gets drawn in the right location + p->translate(-shift.x(), -shift.y()); + } + + // draw popup arrow + if (m_filteredTasks.count() > 1) + { + QStyle::PrimitiveElement e = QStyle::PE_ArrowLeft; + + switch (arrowType) + { + case Qt::LeftArrow: e = QStyle::PE_ArrowLeft; break; + case Qt::RightArrow: e = QStyle::PE_ArrowRight; break; + case Qt::UpArrow: e = QStyle::PE_ArrowUp; break; + case Qt::DownArrow: e = QStyle::PE_ArrowDown; break; + } + + int flags = QStyle::Style_Enabled; + QRect ar = QStyle::visualRect(QRect(br.x() + br.width() - 8 - 2, + br.y(), 8, br.height()), this); + if (sunken) + { + flags |= QStyle::Style_Down; + } + + style().drawPrimitive(e, p, ar, colors, flags); + } + + // draw mouse over frame in transparent mode + if (m_mouseOver && halo) + KickerLib::drawBlendedRect(p, QRect(0, 0, width(), height()), colorGroup().foreground()); + + if (aboutToActivate) + { + aboutToActivate = false; + } +} + +QString TaskContainer::name() +{ + // default to container id + QString text; + + // single task -> use mainwindow caption + if (m_filteredTasks.count() == 1) + { + text = m_filteredTasks.first()->visibleName(); + } + else if (m_filteredTasks.count() > 1) + { + // multiple tasks -> use the common part of all captions + // if it is more descriptive than the class name + const QString match = m_filteredTasks.first()->visibleName(); + unsigned int maxLength = match.length(); + unsigned int i = 0; + bool stop = false; + + // what we do is find the right-most letter than the names do NOT have + // in common, and then use everything UP TO that as the name in the button + while (i < maxLength) + { + QChar check = match.at(i).lower(); + Task::List::iterator itEnd = m_filteredTasks.end(); + for (Task::List::iterator it = m_filteredTasks.begin(); it != itEnd; ++it) + { + // we're doing a lot of Utf8 -> QString conversions here + // by repeatedly calling visibleIconicName() =/ + if (check != (*it)->visibleName().at(i).lower()) + { + if (i > 0) + { + --i; + } + stop = true; + break; + } + } + + if (stop) + { + break; + } + + ++i; + } + + // strip trailing crap + while (i > 0 && !match.at(i).isLetterOrNumber()) + { + --i; + } + + // more descriptive than id()? + if (i > 0 && (i + 1) >= id().length()) + { + text = match.left(i + 1); + } + } + else if (m_startup && !m_startup->text().isEmpty()) + { + // fall back to startup name + text = m_startup->text(); + } + + if (text.isEmpty()) + { + text = id(); + + // Upper case first letter: seems to be the right thing to do for most cases + text[0] = text[0].upper(); + } + + if (m_filteredTasks.count() > 1) + { + // this is faster than (" [%1]").arg() or + + // and it's as fast as using append, but cleaner looking + text += " ["; + text += QString::number(m_filteredTasks.count()); + text += "]"; + } + + return text; +} + +void TaskContainer::mousePressEvent( QMouseEvent* e ) +{ + if (discardNextMouseEvent) + { + discardNextMouseEvent = false; + return; + } + + if (e->button() == LeftButton) + { + m_dragStartPos = e->pos(); + } + else + { + m_dragStartPos = QPoint(); + } + + int buttonAction = 0; + + // On left button, only do actions that invoke a menu. + // Other actions will be handled in mouseReleaseEvent + switch (e->button()) + { + case LeftButton: + buttonAction = TaskBarSettings::action(TaskBarSettings::LeftButton); + break; + case MidButton: + buttonAction = TaskBarSettings::action(TaskBarSettings::MiddleButton); + break; + case RightButton: + default: + buttonAction = TaskBarSettings::action(TaskBarSettings::RightButton); + break; + } + + if ((buttonAction == TaskBarSettings::ShowTaskList && + m_filteredTasks.count() > 1) || + buttonAction == TaskBarSettings::ShowOperationsMenu) + { + performAction(buttonAction); + } +} + +void TaskContainer::mouseReleaseEvent(QMouseEvent *e) +{ + m_dragStartPos = QPoint(); + + if (!TaskBarSettings::drawButtons()) + { + setDown(false); + } + + // This is to avoid the flicker caused by redrawing the + // button as unpressed just before it's activated. + if (!rect().contains(e->pos())) + { + QToolButton::mouseReleaseEvent(e); + return; + } + + int buttonAction = 0; + + switch (e->button()) + { + case LeftButton: + buttonAction = TaskBarSettings::action(TaskBarSettings::LeftButton); + break; + case MidButton: + buttonAction = TaskBarSettings::action(TaskBarSettings::MiddleButton); + break; + case RightButton: + default: + buttonAction = TaskBarSettings::action(TaskBarSettings::RightButton); + break; + } + + if ((buttonAction == TaskBarSettings::ShowTaskList && + m_filteredTasks.count() > 1) || + buttonAction == TaskBarSettings::ShowOperationsMenu) + { + return; + } + + if (buttonAction == TaskBarSettings::ActivateRaiseOrMinimize || + buttonAction == TaskBarSettings::Activate) + { + aboutToActivate = true; + } + + performAction( buttonAction ); + QTimer::singleShot(0, this, SLOT(update())); +} + +void TaskContainer::performAction(int action) +{ + if (m_filteredTasks.isEmpty()) + { + return; + } + + switch( action ) { + case TaskBarSettings::ShowTaskList: + // If there is only one task, the correct behavior is + // to activate, raise, or iconify it, not show the task menu. + if( m_filteredTasks.count() > 1 ) { + popupMenu( TaskBarSettings::ShowTaskList ); + } else { + performAction( TaskBarSettings::ActivateRaiseOrMinimize ); + } + break; + case TaskBarSettings::ShowOperationsMenu: + popupMenu( TaskBarSettings::ShowOperationsMenu ); + break; + case TaskBarSettings::ActivateRaiseOrMinimize: + if (m_filteredTasks.isEmpty()) + { + break; + } + if (m_filteredTasks.count() == 1) + { + m_filteredTasks.first()->activateRaiseOrIconify(); + } + else + { + // multiple tasks -> cycle list + bool hasLastActivated = false; + Task::List::iterator itEnd = m_filteredTasks.end(); + for (Task::List::iterator it = m_filteredTasks.begin(); it != itEnd; ++it) + { + if ((*it) == lastActivated) + { + hasLastActivated = true; + } + + if ((*it)->isActive()) + { + // activate next + ++it; + if (it == itEnd) + { + it = m_filteredTasks.begin(); + } + (*it)->activateRaiseOrIconify(); + return; + } + } + + if (hasLastActivated) + { + lastActivated->activateRaiseOrIconify(); + } + else + { + m_filteredTasks[0]->activateRaiseOrIconify(); + } + } + break; + case TaskBarSettings::Activate: + m_filteredTasks.first()->activate(); + break; + case TaskBarSettings::Raise: + m_filteredTasks.first()->raise(); + break; + case TaskBarSettings::Lower: + m_filteredTasks.first()->lower(); + break; + case TaskBarSettings::Minimize: + m_filteredTasks.first()->toggleIconified(); + break; + case TaskBarSettings::Close: + m_filteredTasks.first()->close(); + break; + case TaskBarSettings::ToCurrentDesktop: + m_filteredTasks.first()->toCurrentDesktop(); + break; + default: + kdWarning(1210) << "Unknown taskbar action!" << endl; + break; + } +} + +// forcenext == true means the last entry in the previous +// taskcontainer was active -> activate first +bool TaskContainer::activateNextTask(bool forward, bool& forcenext) +{ + if (forcenext) + { + if (m_filteredTasks.isEmpty()) + { + return false; + } + + if (forward) + { + m_filteredTasks.first()->activate(); + } + else + { + m_filteredTasks.last()->activate(); + } + + forcenext = false; + return true; + } + + Task::List::iterator itEnd = m_filteredTasks.end(); + for (Task::List::iterator it = m_filteredTasks.begin(); + it != itEnd; + ++it) + { + if ((*it)->isActive()) + { + if (forward) + { + ++it; + if (it == itEnd) + { + forcenext = true; + return false; + } + + (*it)->activate(); + return true; + } + else if (it == m_filteredTasks.begin()) + { + forcenext = true; + return false; + } + + --it; + (*it)->activate(); + return true; + } + } + + return false; +} + +void TaskContainer::popupMenu(int action) +{ + if (action == TaskBarSettings::ShowTaskList ) + { + m_menu = new TaskLMBMenu(m_filteredTasks); + } + else if (action == TaskBarSettings::ShowOperationsMenu) + { + if (!kapp->authorizeKAction("kwin_rmb")) + { + return; + } + + m_menu = new TaskRMBMenu(m_filteredTasks, taskBar->showAllWindows()); + } + else + { + return; + } + + // calc popup menu position + QPoint pos(mapToGlobal(QPoint(0, 0))); + + switch( arrowType ) + { + case RightArrow: + pos.setX(pos.x() + width()); + break; + case LeftArrow: + pos.setX(pos.x() - m_menu->sizeHint().width()); + break; + case DownArrow: + if ( QApplication::reverseLayout() ) + pos.setX( pos.x() + width() - m_menu->sizeHint().width() ); + pos.setY( pos.y() + height() ); + break; + case UpArrow: + if ( QApplication::reverseLayout() ) + pos.setX( pos.x() + width() - m_menu->sizeHint().width() ); + pos.setY(pos.y() - m_menu->sizeHint().height()); + break; + default: + break; + } + m_menu->installEventFilter( this ); + m_menu->exec( pos ); + + delete m_menu; + m_menu = 0; +} + +void TaskContainer::mouseMoveEvent( QMouseEvent* e ) +{ + kdDebug() << "regular move" << endl; + if (!m_dragStartPos.isNull()) + { + startDrag(e->pos()); + } + + QToolButton::mouseMoveEvent(e); +} + +bool TaskContainer::startDrag(const QPoint& pos) +{ + if (m_filteredTasks.count() != 1) + { + return false; + } + + int delay = KGlobalSettings::dndEventDelay(); + + if ((m_dragStartPos - pos).manhattanLength() > delay) + { + if (!m_filteredTasks.first()->isActive()) + { + setDown(false); + } + + TaskDrag* drag = new TaskDrag(m_filteredTasks, this); + + if (!m_filteredTasks.isEmpty()) + { + kdDebug() << m_filteredTasks.first()->name() << endl; + drag->setPixmap(m_filteredTasks.first()->pixmap()); + } + + drag->dragMove(); + return true; + } + + return false; +} + +// This is the code that gives us the proper behavior +// when a popup menu is displayed and we are clicked: +// close the menu, and don't reopen it immediately. +// It's copied from QToolButton. Unfortunately Qt is lame +// as usual and makes interesting stuff private or +// non-virtual, so we have to copy code. +bool TaskContainer::eventFilter(QObject *o, QEvent *e) +{ + switch ( e->type() ) + { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + { + QMouseEvent *me = (QMouseEvent*)e; + QPoint p = me->globalPos(); + if ( QApplication::widgetAt( p, true ) == this ) + { + if (me->type() == QEvent::MouseButtonPress && + me->button() == LeftButton) + { + m_dragStartPos = mapFromGlobal(p); + } + + discardNextMouseEvent = true; + } + break; + } + case QEvent::MouseButtonRelease: + { + m_dragStartPos = QPoint(); + break; + } + case QEvent::MouseMove: + { + if (!m_dragStartPos.isNull()) + { + QMouseEvent* me = static_cast<QMouseEvent*>(e); + QPoint p(me->globalPos()); + + if (me->state() & LeftButton && + QApplication::widgetAt(p, true) == this) + { + kdDebug() << "event move" << endl; + if (startDrag(mapFromGlobal(p))) + { + QPopupMenu* menu = dynamic_cast<QPopupMenu*>(o); + + if (menu) + { + menu->hide(); + } + } + } + } + break; + } + + default: + break; + } + + return QToolButton::eventFilter( o, e ); +} + +void TaskContainer::setArrowType( Qt::ArrowType at ) +{ + if (arrowType == at) + { + return; + } + + arrowType = at; + update(); +} + +void TaskContainer::publishIconGeometry( QPoint global ) +{ + QPoint p = global + geometry().topLeft(); + + Task::List::const_iterator itEnd = tasks.constEnd(); + for (Task::List::const_iterator it = tasks.constBegin(); it != itEnd; ++it) + { + Task::Ptr t = *it; + t->publishIconGeometry(QRect(p.x(), p.y(), width(), height())); + } +} + +void TaskContainer::dragEnterEvent( QDragEnterEvent* e ) +{ + // ignore task drags and applet drags + if (TaskDrag::canDecode(e) || PanelDrag::canDecode(e)) + { + return; + } + + // if a dragitem is held for over a taskbutton for two seconds, + // activate corresponding window + if (m_filteredTasks.isEmpty()) + { + return; + } + + if (!m_filteredTasks.first()->isActive() || + m_filteredTasks.count() > 1) + { + dragSwitchTimer.start(1000, true); + } + + QToolButton::dragEnterEvent( e ); +} + +void TaskContainer::dragLeaveEvent( QDragLeaveEvent* e ) +{ + dragSwitchTimer.stop(); + + QToolButton::dragLeaveEvent( e ); +} + +void TaskContainer::enterEvent(QEvent* e) +{ + QToolTip::remove(this); + m_mouseOver = true; + updateNow(); + + if (tasks.isEmpty()) + { + QToolButton::enterEvent(e); + return; + } + + if (!KickerSettings::showMouseOverEffects()) + { + QString tooltip = "<qt>" + QStyleSheet::escape(name()) + "</qt>"; + QToolTip::add(this, tooltip); + return; + } +} + +void TaskContainer::leaveEvent(QEvent*) +{ + m_mouseOver = false; + updateNow(); +} + +void TaskContainer::dragSwitch() +{ + if (m_filteredTasks.isEmpty()) + { + return; + } + + if (m_filteredTasks.count() == 1) + { + m_filteredTasks.first()->activate(); + } + else + { + popupMenu(TaskBarSettings::ShowTaskList); + } +} + +int TaskContainer::desktop() +{ + if ( tasks.isEmpty() ) + return TaskManager::the()->currentDesktop(); + + if ( tasks.count() > 1 ) + return TaskManager::the()->numberOfDesktops(); + + return tasks.first()->desktop(); +} + +bool TaskContainer::onCurrentDesktop() +{ + if (m_startup) + { + return true; + } + + Task::List::const_iterator itEnd = tasks.constEnd(); + for (Task::List::const_iterator it = tasks.constBegin(); it != itEnd; ++it) + { + Task::Ptr t = *it; + if (t->isOnCurrentDesktop()) + { + return true; + } + } + + return false; +} + +bool TaskContainer::isOnScreen() +{ + if (isEmpty()) + { + return false; + } + + int screen = taskBar->showScreen(); + if ((tasks.isEmpty() && m_startup) || screen == -1) + { + return true; + } + + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + if ((*it)->isOnScreen( screen )) + { + return true; + } + } + + return false; +} + +bool TaskContainer::isIconified() +{ + if (isEmpty()) + { + return false; + } + + if (tasks.isEmpty() && m_startup) + { + return true; + } + + Task::List::const_iterator itEnd = tasks.constEnd(); + for (Task::List::const_iterator it = tasks.constBegin(); it != itEnd; ++it) + { + if ((*it)->isIconified()) + { + return true; + } + } + + return false; +} + +void TaskContainer::updateFilteredTaskList() +{ + m_filteredTasks.clear(); + + Task::List::const_iterator itEnd = tasks.constEnd(); + for (Task::List::const_iterator it = tasks.constBegin(); it != itEnd; ++it) + { + Task::Ptr t = *it; + if ((taskBar->showAllWindows() || t->isOnCurrentDesktop()) && + (!TaskBarSettings::showOnlyIconified() || t->isIconified())) + { + m_filteredTasks.append(t); + } + else + { + t->publishIconGeometry( QRect()); + } + } + + // sort container list by desktop + if (taskBar->sortByDesktop() && m_filteredTasks.count() > 1) + { + QValueVector<QPair<int, Task::Ptr> > sorted; + sorted.resize(m_filteredTasks.count()); + int i = 0; + + Task::List::const_iterator itEnd = m_filteredTasks.constEnd(); + for (Task::List::const_iterator it = m_filteredTasks.constBegin(); it != itEnd; ++it) + { + Task::Ptr t = *it; + sorted[i] = (qMakePair(t->desktop(), t)); + ++i; + } + + qHeapSort(sorted); + + m_filteredTasks.clear(); + for (QValueVector<QPair<int, Task::Ptr> >::iterator it = sorted.begin(); + it != sorted.end(); + ++it) + { + m_filteredTasks.append((*it).second); + } + } +} + +void TaskContainer::desktopChanged(int) +{ + updateFilteredTaskList(); + KickerTip::Client::updateKickerTip(); + update(); +} + +void TaskContainer::windowChanged(Task::Ptr) +{ + updateFilteredTaskList(); + KickerTip::Client::updateKickerTip(); + update(); +} + +void TaskContainer::settingsChanged() +{ + updateFilteredTaskList(); + KickerTip::Client::updateKickerTip(); + update(); +} + +void TaskContainer::updateKickerTip(KickerTip::Data& data) +{ + if (m_startup) + { + data.message = m_startup->text(); + data.duration = 4000; + data.subtext = i18n("Loading application ..."); + data.icon = KGlobal::iconLoader()->loadIcon(m_startup->icon(), + KIcon::Small, + KIcon::SizeMedium, + KIcon::DefaultState, + 0, true); + return; + } + + QPixmap pixmap; + QString name; + QString details; + + if (m_filteredTasks.count() > 0) + { + if (TaskBarSettings::showThumbnails() && + m_filteredTasks.count() == 1) + { + Task::Ptr t = m_filteredTasks.first(); + + pixmap = t->thumbnail(TaskBarSettings::thumbnailMaxDimension()); + } + + if (pixmap.isNull() && tasks.count()) + { + // try to load icon via net_wm + pixmap = KWin::icon(tasks.last()->window(), + KIcon::SizeMedium, + KIcon::SizeMedium, + true); + } + + // Collect all desktops the tasks are on. Sort naturally. + QMap<int, QString> desktopMap; + bool demandsAttention = false; + bool modified = false; + bool allDesktops = false; + Task::List::const_iterator itEnd = m_filteredTasks.constEnd(); + for (Task::List::const_iterator it = m_filteredTasks.constBegin(); it != itEnd; ++it) + { + Task::Ptr t = *it; + if (t->demandsAttention()) + { + demandsAttention = true; + } + + if (t->isModified()) + { + modified = true; + } + + if (t->isOnAllDesktops()) + { + allDesktops = true; + desktopMap.clear(); + } + else if (!allDesktops) + { + desktopMap.insert(t->desktop(), + TaskManager::the()->desktopName(t->desktop())); + } + } + + if (TaskBarSettings::showAllWindows() && KWin::numberOfDesktops() > 1) + { + if (desktopMap.isEmpty()) + { + details.append(i18n("On all desktops")); + } + else + { + QStringList desktopNames = desktopMap.values(); + details.append(i18n("On %1").arg(QStyleSheet::escape(desktopNames.join(", "))) + "<br>"); + } + } + + if (demandsAttention) + { + details.append(i18n("Requesting attention") + "<br>"); + } + + name = this->name(); + if (modified) + { + details.append(i18n("Has unsaved changes")); + + static QString modStr = "[" + i18n( "modified" ) + "]"; + int modStrPos = name.find(modStr); + + if (modStrPos >= 0) + { + // +1 because we include a space after the closing brace. + name.remove(modStrPos, modStr.length() + 1); + } + } + } + + data.message = QStyleSheet::escape(name); + data.subtext = details; + data.icon = pixmap; + data.direction = KickerLib::arrowToDirection(arrowType); +} + +void TaskContainer::finish() +{ + // Disconnect all signal/slot connections to avoid triggering a popupMenu() call, + // whose event loop is the root of all (or at least much) evil. + // Unfortunately, we can't just do "disconnect()", because that gets us a bunch + // of dangling QGuardedPtr objects (most notably in QTipManager.) (kling) + + animationTimer.disconnect(); + dragSwitchTimer.disconnect(); + attentionTimer.disconnect(); + + if (m_startup) + m_startup->disconnect(this); + + for (Task::List::Iterator it = tasks.begin(); it != tasks.end(); ++it) + { + (*it)->disconnect(this); + } + + if (m_menu) + m_menu->close(); +} diff --git a/kicker/taskbar/taskcontainer.h b/kicker/taskbar/taskcontainer.h new file mode 100644 index 000000000..4328c36d4 --- /dev/null +++ b/kicker/taskbar/taskcontainer.h @@ -0,0 +1,153 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> +Copyright (c) 2002 John Firebaugh <jfirebaugh@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. + +******************************************************************/ + +#ifndef __taskcontainer_h__ +#define __taskcontainer_h__ + +#include <qpixmap.h> +#include <qtimer.h> +#include <qtoolbutton.h> + +#include "kickertip.h" +#include "taskmanager.h" + +class TaskBar; + +typedef QValueList<QPixmap*> PixmapList; + +class TaskContainer : public QToolButton, public KickerTip::Client +{ + Q_OBJECT + +public: + typedef QValueList<TaskContainer*> List; + typedef QValueList<TaskContainer*>::iterator Iterator; + + TaskContainer(Task::Ptr, TaskBar*, QWidget *parent = 0, const char *name = 0); + TaskContainer(Startup::Ptr, PixmapList&, TaskBar*, + QWidget *parent = 0, const char *name = 0); + virtual ~TaskContainer(); + + void setArrowType( Qt::ArrowType at ); + + void init(); + + void add(Task::Ptr); + void remove(Task::Ptr); + void remove(Startup::Ptr); + + bool contains(Task::Ptr); + bool contains(Startup::Ptr); + bool contains(WId); + + bool isEmpty(); + bool onCurrentDesktop(); + bool isIconified(); + bool isOnScreen(); + + QString id(); + int desktop(); + QString name(); + + virtual QSizePolicy sizePolicy () const; + + void publishIconGeometry( QPoint ); + void desktopChanged( int ); + void windowChanged(Task::Ptr); + void settingsChanged(); + bool eventFilter( QObject *o, QEvent *e ); + + int taskCount() const { return tasks.count(); } + int filteredTaskCount() const { return m_filteredTasks.count(); } + + bool activateNextTask( bool forward, bool& forcenext ); + + void updateKickerTip(KickerTip::Data&); + + void finish(); + + void setBackground(); + +public slots: + void updateNow(); + +signals: + void showMe(TaskContainer*); + +protected: + void paintEvent(QPaintEvent*); + void drawButton(QPainter*); + void resizeEvent(QResizeEvent*); + void mousePressEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void dragEnterEvent(QDragEnterEvent*); + void dragLeaveEvent(QDragLeaveEvent*); + void enterEvent(QEvent*); + void leaveEvent(QEvent*); + bool startDrag(const QPoint& pos); + void stopTimers(); + + void performAction(int); + void popupMenu(int); + + void updateFilteredTaskList(); + +protected slots: + void animationTimerFired(); + void attentionTimerFired(); + void dragSwitch(); + void iconChanged(); + void setLastActivated(); + void taskChanged(bool geometryChangeOnly); + void showMe(); + +private: + void checkAttention(const Task::Ptr changed_task = NULL); + QString sid; + QTimer animationTimer; + QTimer dragSwitchTimer; + QTimer attentionTimer; + QTimer m_paintEventCompressionTimer; + int currentFrame; + PixmapList frames; + int attentionState; + QRect iconRect; + QPixmap animBg; + Task::List tasks; + Task::List m_filteredTasks; + Task::Ptr lastActivated; + QPopupMenu* m_menu; + Startup::Ptr m_startup; + ArrowType arrowType; + TaskBar* taskBar; + bool discardNextMouseEvent; + bool aboutToActivate; + bool m_mouseOver; + bool m_paintEventCompression; + enum { ATTENTION_BLINK_TIMEOUT = 4 }; + QPoint m_dragStartPos; +}; + +#endif |