diff options
Diffstat (limited to 'kicker/taskbar')
-rw-r--r-- | kicker/taskbar/CMakeLists.txt | 44 | ||||
-rw-r--r-- | kicker/taskbar/Makefile.am | 20 | ||||
-rw-r--r-- | kicker/taskbar/taskbar.cpp | 1465 | ||||
-rw-r--r-- | kicker/taskbar/taskbar.h | 178 | ||||
-rw-r--r-- | kicker/taskbar/taskbar.kcfg | 215 | ||||
-rw-r--r-- | kicker/taskbar/taskbarbindings.cpp | 37 | ||||
-rw-r--r-- | kicker/taskbar/taskbarcontainer.cpp | 366 | ||||
-rw-r--r-- | kicker/taskbar/taskbarcontainer.h | 84 | ||||
-rw-r--r-- | kicker/taskbar/taskbarsettings.kcfgc | 7 | ||||
-rw-r--r-- | kicker/taskbar/taskcontainer.cpp | 1878 | ||||
-rw-r--r-- | kicker/taskbar/taskcontainer.h | 165 |
11 files changed, 4459 insertions, 0 deletions
diff --git a/kicker/taskbar/CMakeLists.txt b/kicker/taskbar/CMakeLists.txt new file mode 100644 index 000000000..c01fad149 --- /dev/null +++ b/kicker/taskbar/CMakeLists.txt @@ -0,0 +1,44 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/kicker/libkicker + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kicker/libkicker + ${CMAKE_SOURCE_DIR}/kicker/taskmanager + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES taskbar.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) + + +##### taskbar (shared) ########################## + +set( target taskbar ) + +set( ${target}_SRCS + taskbarsettings.kcfgc taskcontainer.cpp taskbar.cpp + taskbarcontainer.cpp taskbarcontainer.skel +) + +tde_add_library( ${target} SHARED AUTOMOC + SOURCES ${${target}_SRCS} + VERSION 1.2.0 + LINK kickermain-shared taskmanager-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/kicker/taskbar/Makefile.am b/kicker/taskbar/Makefile.am new file mode 100644 index 000000000..e1495dbc3 --- /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_TDEUI) $(LIB_TDEUTILS) ../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..cae24916d --- /dev/null +++ b/kicker/taskbar/taskbar.cpp @@ -0,0 +1,1465 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <[email protected]> +Copyright (c) 2004 Sebastian Wolff +Copyright (c) 2005 Aaron Seigo <[email protected]> + +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. +***************************************************************** + + Additional changes: + - 2013/10/22 Michele Calgaro + * added support for display mode (Icons and Text, Text only, Icons only) + and removed "Show application icons" +*/ + +#include <math.h> + +#include <tqapplication.h> +#include <tqbitmap.h> +#include <tqdesktopwidget.h> +#include <tqlayout.h> +#include <tqpainter.h> +#include <tqstringlist.h> + +#include <dcopclient.h> +#include <tdeapplication.h> +#include <kdebug.h> +#include <tdeglobal.h> +#include <kglobalaccel.h> +#include <kimageeffect.h> +#include <tdelocale.h> +#include <kstandarddirs.h> + +#include "kickerSettings.h" +#include "taskbarsettings.h" +#include "taskcontainer.h" +#include "taskmanager.h" + +#include "taskbar.h" +#include "taskbar.moc" + +#define READ_MERGED_TASBKAR_SETTING(x) ((m_settingsObject->useGlobalSettings())?m_globalSettingsObject->x():m_settingsObject->x()) + +TaskBar::TaskBar( TaskBarSettings* settingsObject, TaskBarSettings* globalSettingsObject, TQWidget *parent, const char *name ) + : Panner( parent, name ), + m_showAllWindows(false), + m_cycleWheel(false), + m_currentScreen(-1), + m_showOnlyCurrentScreen(false), + m_sortByDesktop(false), + m_displayIconsNText(settingsObject->DisplayIconsAndText), + m_showOnlyIconified(false), + m_showTaskStates(0), + m_textShadowEngine(0), + m_ignoreUpdates(false), + m_relayoutTimer(0, "TaskBar::m_relayoutTimer") +{ + arrowType = LeftArrow; + blocklayout = true; + + m_settingsObject = settingsObject; + if (m_settingsObject) + { + m_settingsObject->readConfig(); + } + m_globalSettingsObject = globalSettingsObject; + if (m_globalSettingsObject) + { + m_globalSettingsObject->readConfig(); + } + + // init + setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding ) ); + m_sortByAppPrev = READ_MERGED_TASBKAR_SETTING(sortByApp); + + // setup animation frames + for (int i = 1; i < 11; i++) + { + frames.append(new TQPixmap(locate("data", "kicker/pics/disk" + TQString::number(i) + ".png"))); + } + + // configure + configure(); + + connect(&m_relayoutTimer, TQT_SIGNAL(timeout()), + this, TQT_SLOT(reLayout())); + + connect(this, TQT_SIGNAL(contentsMoving(int, int)), TQT_SLOT(setBackground())); + + // connect manager + connect(TaskManager::the(), TQT_SIGNAL(taskAdded(Task::Ptr)), + this, TQT_SLOT(add(Task::Ptr))); + connect(TaskManager::the(), TQT_SIGNAL(taskRemoved(Task::Ptr)), + this, TQT_SLOT(remove(Task::Ptr))); + connect(TaskManager::the(), TQT_SIGNAL(startupAdded(Startup::Ptr)), + this, TQT_SLOT(add(Startup::Ptr))); + connect(TaskManager::the(), TQT_SIGNAL(startupRemoved(Startup::Ptr)), + this, TQT_SLOT(remove(Startup::Ptr))); + connect(TaskManager::the(), TQT_SIGNAL(desktopChanged(int)), + this, TQT_SLOT(desktopChanged(int))); + connect(TaskManager::the(), TQT_SIGNAL(windowChanged(Task::Ptr)), + this, TQT_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, TQT_SIGNAL(settingsChanged(int)), TQT_SLOT(slotSettingsChanged(int))); + keys = new TDEGlobalAccel( TQT_TQOBJECT(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; +} + + +TQSize TaskBar::sizeHint() const +{ + // get our minimum height based on the minimum button height or the + // height of the font in use, which is largest + TQFontMetrics fm(TDEGlobalSettings::taskbarFont()); + int minButtonHeight = fm.height() > READ_MERGED_TASBKAR_SETTING(minimumButtonHeight) ? + fm.height() : READ_MERGED_TASBKAR_SETTING(minimumButtonHeight); + + return TQSize(BUTTON_MIN_WIDTH, minButtonHeight); +} + +TQSize TaskBar::sizeHint( KPanelExtension::Position p, TQSize maxSize) const +{ + // get our minimum height based on the minimum button height or the + // height of the font in use, which is largest + TQFontMetrics fm(TDEGlobalSettings::taskbarFont()); + int minButtonHeight = fm.height() > READ_MERGED_TASBKAR_SETTING(minimumButtonHeight) ? + fm.height() : READ_MERGED_TASBKAR_SETTING(minimumButtonHeight); + + if ( p == KPanelExtension::Left || p == KPanelExtension::Right ) + { + // Vertical layout + // Minimum space allows for one icon, the window list button and the up/down scrollers + int minHeight = minButtonHeight*3; + if (minHeight > maxSize.height()) + return maxSize; + return TQSize(maxSize.width(), minHeight); + } + else + { + // Horizontal layout + // Minimum space allows for one column of icons, the window list button and the left/right scrollers + int min_width=BUTTON_MIN_WIDTH*3; + if (min_width > maxSize.width()) + return maxSize; + return TQSize(min_width, maxSize.height()); + } +} + +bool TaskBar::showIcons() const +{ + return (m_displayIconsNText==m_settingsObject->DisplayIconsAndText || + m_displayIconsNText==m_settingsObject->DisplayIconsOnly); +} +bool TaskBar::showText() const +{ + return (m_displayIconsNText==m_settingsObject->DisplayIconsAndText || + m_displayIconsNText==m_settingsObject->DisplayTextOnly); +} + +void TaskBar::configure() +{ + bool wasShowWindows = m_showAllWindows; + bool wasSortByDesktop = m_sortByDesktop; + bool wasCycleWheel = m_cycleWheel; + bool wasDisplayIconsNText = m_displayIconsNText; + bool wasShowOnlyIconified = m_showOnlyIconified; + int wasShowTaskStates = m_showTaskStates; + + m_showAllWindows = READ_MERGED_TASBKAR_SETTING(showAllWindows); + m_sortByDesktop = m_showAllWindows && READ_MERGED_TASBKAR_SETTING(sortByDesktop); + m_displayIconsNText = READ_MERGED_TASBKAR_SETTING(displayIconsNText); + m_showOnlyIconified = READ_MERGED_TASBKAR_SETTING(showOnlyIconified); + m_cycleWheel = READ_MERGED_TASBKAR_SETTING(cycleWheel); + m_showTaskStates = READ_MERGED_TASBKAR_SETTING(showTaskStates); + + m_currentScreen = -1; // Show all screens or re-get our screen + m_showOnlyCurrentScreen = (READ_MERGED_TASBKAR_SETTING(showCurrentScreenOnly) && + TQApplication::desktop()->isVirtualDesktop() && + TQApplication::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 + // disconnect first in case we've been here before + // to avoid multiple connections + disconnect(TaskManager::the(), TQT_SIGNAL(windowChangedGeometry(Task::Ptr)), + this, TQT_SLOT(windowChangedGeometry(Task::Ptr))); + if (m_showOnlyCurrentScreen) + { + connect(TaskManager::the(), TQT_SIGNAL(windowChangedGeometry(Task::Ptr)), + this, TQT_SLOT(windowChangedGeometry(Task::Ptr))); + } + TaskManager::the()->trackGeometry(m_showOnlyCurrentScreen); + + if (wasShowWindows != m_showAllWindows || + wasSortByDesktop != m_sortByDesktop || + wasDisplayIconsNText != m_displayIconsNText || + wasCycleWheel != m_cycleWheel || + wasShowOnlyIconified != m_showOnlyIconified || + wasShowTaskStates != m_showTaskStates) + { + // relevant settings changed, update our task containers + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + (*it)->settingsChanged(); + } + } + + if (m_sortByAppPrev != READ_MERGED_TASBKAR_SETTING(sortByApp)) { + m_sortByAppPrev = READ_MERGED_TASBKAR_SETTING(sortByApp); + reSort(); + } + + TaskManager::the()->setXCompositeEnabled(READ_MERGED_TASBKAR_SETTING(showThumbnails)); + + reLayoutEventually(); +} + +void TaskBar::setOrientation( Orientation o ) +{ + Panner::setOrientation( o ); + reLayoutEventually(); +} + +void TaskBar::moveEvent( TQMoveEvent* e ) +{ + Panner::moveEvent(e); + setViewportBackground(); +} + +void TaskBar::resizeEvent( TQResizeEvent* e ) +{ + if (m_showOnlyCurrentScreen) + { + TQPoint topLeft = mapToGlobal(this->geometry().topLeft()); + if (m_currentScreen != TQApplication::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, m_settingsObject, m_globalSettingsObject, 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, m_settingsObject, m_globalSettingsObject, viewport()); + m_hiddenContainers.append(container); + connect(container, TQT_SIGNAL(showMe(TaskContainer*)), this, TQT_SLOT(showTaskContainer(TaskContainer*))); +} + +void TaskBar::reSort() +{ + TaskContainer::List originalContainers = containers; + TaskContainer::Iterator it = originalContainers.begin(); + for (; it != originalContainers.end(); ++it) + { + removeChild(*it); + } + containers.clear(); + it = originalContainers.begin(); + for (; it != originalContainers.end(); ++it) + { + showTaskContainer(*it); + } + reLayoutEventually(); + emit containerCountChanged(); +} + +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 (READ_MERGED_TASBKAR_SETTING(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; + } + } + + // alphabetize containers + it = containers.begin(); + for (; it != containers.end(); ++it) + { + TaskContainer* c = *it; + if (TQString::localeAwareCompare(container->id().lower(), c->id().lower()) < 0) { + 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 TQPopupMenu::exec() creates its own event loop, deferred deletes + // via TQObject::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 us 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 + TQFontMetrics fm(TDEGlobalSettings::taskbarFont()); + int minButtonHeight = fm.height() > READ_MERGED_TASBKAR_SETTING(minimumButtonHeight) ? + fm.height() : READ_MERGED_TASBKAR_SETTING(minimumButtonHeight); + + // horizontal layout + if (orientation() == Qt::Horizontal) + { + int bwidth=BUTTON_MIN_WIDTH; + int rows = contentsRect().height() / minButtonHeight; + if (rows<1) + rows=1; + + // actual button height + int bheight = contentsRect().height() / rows; + if (bheight<1) // avoid zero devision later + bheight=1; + + // buttons per row + int bpr = static_cast<int>(ceil(static_cast<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 and the taskbar is not in 'icons only' mode + if (mbpr > bpr) + { + if (!showIcons() || showText()) + bwidth = contentsRect().width() / bpr; + int maxWidth = READ_MERGED_TASBKAR_SETTING(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 = TQApplication::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 min button height to keep gaps into account + int minButtonHeightAdjusted=minButtonHeight+4; + // adjust content size + if (contentsRect().height() < (int)list.count() * minButtonHeightAdjusted) + { + resizeContents(contentsRect().width(), list.count() * minButtonHeightAdjusted); + } + + // 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() != minButtonHeightAdjusted) + c->resize(contentsRect().width(), minButtonHeightAdjusted); + + if (childX(c) != 0 || childY(c) != (i * minButtonHeightAdjusted)) + moveChild(c, 0, i * minButtonHeightAdjusted); + + c->setBackground(); + i++; + } + } + + TQTimer::singleShot(100, this, TQT_SLOT(publishIconGeometry())); +} + +void TaskBar::setViewportBackground() +{ + const TQPixmap *bg = parentWidget()->backgroundPixmap(); + + if (bg) + { + TQPixmap 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(TQt::ArrowType at) +{ + if (arrowType == at) + { + return; + } + + arrowType = at; + for (TaskContainer::Iterator it = containers.begin(); + it != containers.end(); + ++it) + { + (*it)->setArrowType(arrowType); + } +} + +void TaskBar::publishIconGeometry() +{ + TQPoint p = mapToGlobal(TQPoint(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( TQMouseEvent* e ) +{ + propagateMouseEvent( e ); +} + +void TaskBar::viewportMouseReleaseEvent( TQMouseEvent* e ) +{ + propagateMouseEvent( e ); +} + +void TaskBar::viewportMouseDoubleClickEvent( TQMouseEvent* e ) +{ + propagateMouseEvent( e ); +} + +void TaskBar::viewportMouseMoveEvent( TQMouseEvent* e ) +{ + propagateMouseEvent( e ); +} + +void TaskBar::propagateMouseEvent( TQMouseEvent* e ) +{ + if ( !isTopLevel() ) + { + TQMouseEvent me( e->type(), mapTo( topLevelWidget(), e->pos() ), + e->globalPos(), e->button(), e->state() ); + TQApplication::sendEvent( topLevelWidget(), &me ); + } +} + +bool TaskBar::idMatch( const TQString& id1, const TQString& 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()))) + { + if (!(*it)->isHidden()) + { + 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()))) + { + if (!(*it)->isHidden()) + { + i += (*it)->filteredTaskCount(); + } + } + } + + return i; +} + +int TaskBar::maximumButtonsWithoutShrinking() const +{ + TQFontMetrics fm(TDEGlobalSettings::taskbarFont()); + int minButtonHeight = fm.height() > READ_MERGED_TASBKAR_SETTING(minimumButtonHeight) ? + fm.height() : READ_MERGED_TASBKAR_SETTING(minimumButtonHeight); + int rows = contentsRect().height() / minButtonHeight; + + if (rows < 1) + { + rows = 1; + } + + if ( orientation() == Qt::Horizontal ) { + // maxWidth of 0 means no max width, drop back to default + int maxWidth = READ_MERGED_TASBKAR_SETTING(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 READ_MERGED_TASBKAR_SETTING(groupTasks) == m_settingsObject->GroupAlways || + ((READ_MERGED_TASBKAR_SETTING(groupTasks) == m_settingsObject->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()) && + (!c->isHidden())) + { + 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(TQWheelEvent* e) +{ + + if(READ_MERGED_TASBKAR_SETTING(cycleWheel)) { + + 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) TDEApplication::SETTINGS_SHORTCUTS ) + { + keys->readSettings(); + keys->updateConnections(); + } +} + +int TaskBar::showScreen() const +{ + if (m_showOnlyCurrentScreen && m_currentScreen == -1) + { + const_cast<TaskBar*>(this)->m_currentScreen = + TQApplication::desktop()->screenNumber(mapToGlobal(this->geometry().topLeft())); + } + + return m_currentScreen; +} + +TQImage* TaskBar::blendGradient(const TQSize& size) +{ + if (m_blendGradient.isNull() || m_blendGradient.size() != size) + { + TQPixmap bgpm(size); + TQPainter bgp(&bgpm); + bgpm.fill(black); + + if (TQApplication::reverseLayout()) + { + TQImage gradient = KImageEffect::gradient( + TQSize(30, size.height()), + TQColor(255,255,255), + TQColor(0,0,0), + KImageEffect::HorizontalGradient); + bgp.drawImage(0, 0, gradient); + } + else + { + TQImage gradient = KImageEffect::gradient( + TQSize(30, size.height()), + TQColor(0,0,0), + TQColor(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 TQValueVector<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); + } +} + +TaskMoveDestination::TaskMoveDestination TaskBar::taskMoveCapabilities(TaskContainer* movingContainer) { + TaskMoveDestination::TaskMoveDestination ret = TaskMoveDestination::Null; + + bool before = false; + bool after = false; + bool movingFound = false; + + if (movingContainer) { + // Check to see if there are any visible containers before or after the movingContainer + TaskContainer::Iterator it = containers.begin(); + for (; it != containers.end(); ++it) + { + TaskContainer* c = *it; + if (!c->isVisibleTo(this)) { + continue; + } + if (c == movingContainer) { + movingFound = true; + } + else { + if (movingFound) { + after = true; + } + else { + before = true; + } + } + } + if (before) { + ret = ret | TaskMoveDestination::Left; + } + if (after) { + ret = ret | TaskMoveDestination::Right; + } + } + + return ret; +} + +int TaskBar::taskMoveHandler(TaskMoveDestination::TaskMoveDestination dest, Task::List taskList, const TQPoint pos) { + TaskContainer* movingContainer = NULL; + TaskContainer* destContainer = NULL; + bool movingRight = true; + + TaskContainer::Iterator it = containers.begin(); + for (; it != containers.end(); ++it) + { + TaskContainer* c = *it; + if (!c->isVisibleTo(this)) { + continue; + } + if (c->taskList() == taskList) { + movingContainer = c; + break; + } + } + + if (movingContainer) { + if (dest == TaskMoveDestination::Position) { + // Find the best place for the container to go... + it = containers.begin(); + for (; it != containers.end(); ++it) + { + TaskContainer* c = *it; + if (!c->isVisibleTo(this)) { + continue; + } + TQPoint containerPos = c->pos(); + TQSize containerSize = c->size(); + TQRect containerRect(containerPos.x(), containerPos.y(), containerSize.width(), containerSize.height()); + if (containerRect.contains(pos)) { + destContainer = c; + // Figure out if the mobile container is moving towards the end of the container list (i.e. right or down) + for (; it != containers.end(); ++it) + { + if (movingContainer == (*it)) { + movingRight = false; + } + } + break; + } + } + } + else if (dest == TaskMoveDestination::Beginning) { + // Move to beginning + it = containers.begin(); + while ((it != containers.end()) && (!(*it)->isVisibleTo(this))) { + it++; + } + if (it == containers.end()) { + return false; + } + destContainer = *it; + movingRight = false; + } + else if (dest == TaskMoveDestination::Left) { + // Move left + it = containers.begin(); + while ((it != containers.end()) && (!(*it)->isVisibleTo(this))) { + it++; + } + if (it == containers.end()) { + return false; + } + TaskContainer* prev = *it; + destContainer = prev; + for (; it != containers.end(); ++it) + { + TaskContainer* c = *it; + if (!c->isVisibleTo(this)) { + continue; + } + if (movingContainer == c) { + destContainer = prev; + break; + } + prev = c; + } + movingRight = false; + } + else if (dest == TaskMoveDestination::Right) { + // Move right + it = containers.begin(); + destContainer = NULL; + for (; it != containers.end(); ++it) + { + TaskContainer* c = *it; + if (!c->isVisibleTo(this)) { + continue; + } + if (movingContainer == c) { + if (it != containers.end()) { + it++; + while ((it != containers.end()) && (!(*it)->isVisibleTo(this))) { + it++; + } + } + if ((it != containers.end()) && ((*it)->isVisibleTo(this))) { + destContainer = *it; + } + break; + } + } + movingRight = true; + } + else if (dest == TaskMoveDestination::End) { + // Move to end + destContainer = NULL; + movingRight = true; + } + + if (destContainer == movingContainer) { + return false; + } + + removeChild(movingContainer); + containers.remove(movingContainer); + + if (destContainer) { + it = containers.find(destContainer); + if ((it != containers.end()) && (movingRight)) { + it++; + while ((it != containers.end()) && (!(*it)->isVisibleTo(this))) { + it++; + } + } + if ((it != containers.end()) && ((*it)->isVisibleTo(this))) { + containers.insert(it, movingContainer); + } + else { + containers.append(movingContainer); + } + } + else { + containers.append(movingContainer); + } + + addChild(movingContainer); + reLayoutEventually(); + emit containerCountChanged(); + + return true; + } + + return false; +} diff --git a/kicker/taskbar/taskbar.h b/kicker/taskbar/taskbar.h new file mode 100644 index 000000000..111554014 --- /dev/null +++ b/kicker/taskbar/taskbar.h @@ -0,0 +1,178 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <[email protected]> + +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. + +***************************************************************** + + Additional changes: + - 2013/10/22 Michele Calgaro + * added support for display mode (Icons and Text, Text only, Icons only) + and removed "Show application icons" +*/ + +#ifndef __taskbar_h__ +#define __taskbar_h__ + +#include <kpanelextension.h> +#include <taskmanager.h> + +#include "taskcontainer.h" +#include "panner.h" +#include "kshadowengine.h" + +const int WINDOWLISTBUTTON_SIZE = 15; +const int BUTTON_MAX_WIDTH = 200; +const int BUTTON_MIN_WIDTH = 24; // 24 = 4+2+16+2 -> Space for borders, application icon and gaps + +class Startup; +class Task; +class TDEGlobalAccel; + +namespace TaskMoveDestination +{ + enum TaskMoveDestination + { + Null = 0x00, + Position = 0x01, + Left = 0x02, + Right = 0x04, + Beginning = 0x08, + End = 0x10 + }; + + inline TaskMoveDestination operator|(TaskMoveDestination a, TaskMoveDestination b) + { + return static_cast<TaskMoveDestination>(static_cast<int>(a) | static_cast<int>(b)); + } + + inline TaskMoveDestination operator&(TaskMoveDestination a, TaskMoveDestination b) + { + return static_cast<TaskMoveDestination>(static_cast<int>(a) & static_cast<int>(b)); + } + + inline TaskMoveDestination operator~(TaskMoveDestination a) + { + return static_cast<TaskMoveDestination>(~static_cast<int>(a)); + } +}; + +class TaskBar : public Panner +{ + Q_OBJECT + +public: + TaskBar( TaskBarSettings* settingsObject, TaskBarSettings* globalSettingsObject, TQWidget *parent = 0, const char *name = 0 ); + ~TaskBar(); + + TQSize sizeHint() const; + TQSize sizeHint( KPanelExtension::Position, TQSize maxSize ) const; + + void setOrientation( Orientation ); + void setArrowType( TQt::ArrowType at ); + + int containerCount() const; + int taskCount() const; + int showScreen() const; + + bool showIcons() const; + bool showText() const; + bool sortByDesktop() const { return m_sortByDesktop; } + bool showAllWindows() const { return m_showAllWindows; } + + TQImage* blendGradient(const TQSize& size); + + KTextShadowEngine *textShadowEngine(); + + int taskMoveHandler(TaskMoveDestination::TaskMoveDestination dest, Task::List taskList, const TQPoint pos = TQPoint(0,0)); + TaskMoveDestination::TaskMoveDestination taskMoveCapabilities(TaskContainer* movingContainer); + +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(); + void reSort(); + +protected: + void reLayoutEventually(); + void viewportMousePressEvent( TQMouseEvent* ); + void viewportMouseReleaseEvent( TQMouseEvent* ); + void viewportMouseDoubleClickEvent( TQMouseEvent* ); + void viewportMouseMoveEvent( TQMouseEvent* ); + void wheelEvent(TQWheelEvent*); + void propagateMouseEvent( TQMouseEvent* ); + void resizeEvent( TQResizeEvent* ); + void moveEvent( TQMoveEvent* ); + bool idMatch( const TQString& id1, const TQString& id2 ); + TaskContainer::List filteredContainers(); + +private: + void sortContainersByDesktop(TaskContainer::List& list); + void setViewportBackground(); + + bool blocklayout; + bool m_showAllWindows; + bool m_cycleWheel; + int m_currentScreen; // The screen to show, -1 for all screens + bool m_showOnlyCurrentScreen; + bool m_sortByDesktop; + int m_displayIconsNText; + bool m_showOnlyIconified; + int m_showTaskStates; + 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(); + TDEGlobalAccel* keys; + KTextShadowEngine* m_textShadowEngine; + bool m_ignoreUpdates; + bool m_sortByAppPrev; + TQTimer m_relayoutTimer; + TQImage m_blendGradient; + TaskBarSettings* m_settingsObject; + TaskBarSettings* m_globalSettingsObject; +}; + +#endif diff --git a/kicker/taskbar/taskbar.kcfg b/kicker/taskbar/taskbar.kcfg new file mode 100644 index 000000000..95596ad34 --- /dev/null +++ b/kicker/taskbar/taskbar.kcfg @@ -0,0 +1,215 @@ +<?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 arg="true"/> + + <group name="General"> + <entry key="UseGlobalSettings" type="Bool" > + <default>true</default> + <label>Use the global taskbar configuration</label> + <whatsthis>Turning this option off will cause the taskbar to ignore the global taskbar configuration, instead using a specific configuration for that particular taskbar.</whatsthis> + </entry> + <entry key="AllowDragAndDropReArrange" type="Bool" > + <default>true</default> + <label>Allow taskbar items to be rearranged using drag and drop</label> + <whatsthis>Turning this option on will allow tasks on the taskbar to be manually rearranged using drag and drop.</whatsthis> + </entry> + <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. By default, this option is selected and all windows are shown.</whatsthis> + </entry> + <entry key="CycleWheel" type="Bool" > + <default>true</default> + <label>Cycle through windows with mouse wheel</label> + <whatsthis>Enabling this option causes the taskbar to cycle through the current list of windows when using the mouse wheel</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. By default, this option is not selected and the taskbar will show all windows.</whatsthis> + </entry> + <entry key="DisplayIconsNText" type="Enum" > + <choices> + <choice name="DisplayIconsAndText"> + <label>Never</label> + </choice> + <choice name="DisplayTextOnly"> + <label>When Taskbar Full</label> + </choice> + <choice name="DisplayIconsOnly"> + <label>Always</label> + </choice> + </choices> + <default>DisplayIconsAndText</default> + <label>Display:</label> + <whatsthis>Choose taskbar display mode among <strong>Icons and text</strong>, <strong>Text only</strong> and <strong>Icons only</strong></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. You 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>. By default the taskbar groups windows when it is full.</whatsthis> + </entry> + <entry key="ShowTaskStates" type="Enum" > + <choices> + <choice name="ShowAll"> + <label>Any</label> + </choice> + <choice name="ShowStopped"> + <label>Only Stopped</label> + </choice> + <choice name="ShowRunning"> + <label>Only Running</label> + </choice> + </choices> + <default>ShowAll</default> + <label>Show tasks with state:</label> + <whatsthis>The taskbar can show and/or hide tasks based on their current process state. Select <em>Any</em> to show all tasks regardless of current state.</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. By 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. By 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. By 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. By 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..364faac97 --- /dev/null +++ b/kicker/taskbar/taskbarbindings.cpp @@ -0,0 +1,37 @@ +/***************************************************************** + +Copyright (c) 2001-2004 Matthias Elter <[email protected]> + +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), TQString(), key3, key4, TQT_TQOBJECT(this), TQT_SLOT(fnSlot) ) +#else +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), TQString(), 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..a891cf64e --- /dev/null +++ b/kicker/taskbar/taskbarcontainer.cpp @@ -0,0 +1,366 @@ +/***************************************************************** + +Copyright (c) 2001 John Firebaugh <[email protected]> + +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 <tqlayout.h> +#include <tqtimer.h> +#include <tqfile.h> + +#include <dcopclient.h> +#include <tdeapplication.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kstandarddirs.h> +#include <twindowlistmenu.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" + +#define GLOBAL_TASKBAR_CONFIG_FILE_NAME "ktaskbarrc" + +#define READ_MERGED_TASBKAR_SETTING(x) ((settingsObject->useGlobalSettings())?globalSettingsObject->x():settingsObject->x()) + +TaskBarContainer::TaskBarContainer( bool enableFrame, TQString configFileOverride, TQWidget *parent, const char *name ) + : TQFrame(parent, name), + configFile(configFileOverride), + direction( KPanelApplet::Up ), + showWindowListButton( true ), + windowListButton(0), + windowListMenu(0), + settingsObject(NULL), + globalSettingsObject(NULL) +{ + if (configFile == "") + { + configFile = GLOBAL_TASKBAR_CONFIG_FILE_NAME; + } + TQFile configFileObject(locateLocal("config", configFile)); + if (!configFileObject.exists()) + { + TDEConfig globalConfig(GLOBAL_TASKBAR_CONFIG_FILE_NAME, TRUE, TRUE); + TDEConfig localConfig(configFile); + globalConfig.copyTo(configFile, &localConfig); + localConfig.writeEntry("UseGlobalSettings", TRUE); + localConfig.sync(); + } + settingsObject = new TaskBarSettings(TDESharedConfig::openConfig(configFile)); + globalSettingsObject = new TaskBarSettings(TDESharedConfig::openConfig(GLOBAL_TASKBAR_CONFIG_FILE_NAME)); + + setAcceptDrops(true); // Always enabled to activate task during drag&drop. + + setBackgroundOrigin( AncestorOrigin ); + + uint margin; + if ( enableFrame ) + { + setFrameStyle( Sunken | StyledPanel ); + margin = frameWidth(); + } + else + { + setFrameStyle( NoFrame ); + margin = 0; + } + + layout = new TQBoxLayout( this, TQApplication::reverseLayout() ? + TQBoxLayout::RightToLeft : + TQBoxLayout::LeftToRight ); + layout->setMargin( margin ); + + // scrollable taskbar + taskBar = new TaskBar(settingsObject, globalSettingsObject, this); + layout->addWidget( taskBar ); + + connect( taskBar, TQT_SIGNAL( containerCountChanged() ), TQT_SIGNAL( containerCountChanged() ) ); + + setBackground(); + + // read settings and setup layout + configure(); + + connectDCOPSignal("", "", "kdeTaskBarConfigChanged()", + "configChanged()", false); +} + +TaskBarContainer::~TaskBarContainer() +{ + if (windowListMenu) delete windowListMenu; + if (settingsObject) delete settingsObject; + if (globalSettingsObject) delete globalSettingsObject; +} + +void TaskBarContainer::configure() +{ + setFont(READ_MERGED_TASBKAR_SETTING(taskbarFont)); + showWindowListButton = READ_MERGED_TASBKAR_SETTING(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, TQT_SIGNAL(pressed()), + TQT_SLOT(showWindowListMenu())); + connect(windowListMenu, TQT_SIGNAL(aboutToHide()), + TQT_SLOT(windowListMenuAboutToHide())); + + // geometry + TQString 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, + TDEIcon::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 + settingsObject->readConfig(); + globalSettingsObject->readConfig(); + + configure(); + taskBar->configure(); +} + +void TaskBarContainer::preferences() +{ + TQByteArray data; + + if (!kapp->dcopClient()->isAttached()) + { + kapp->dcopClient()->attach(); + } + + if (configFile == GLOBAL_TASKBAR_CONFIG_FILE_NAME) + { + kapp->dcopClient()->send("kicker", "kicker", "showTaskBarConfig()", data); + } + else + { + TQDataStream args( data, IO_WriteOnly ); + args << configFile; + kapp->dcopClient()->send("kicker", "kicker", "showTaskBarConfig(TQString)", data); + } +} + +void TaskBarContainer::orientationChange(Orientation o) +{ + if (o == Qt::Horizontal) + { + if (windowListButton) + { + windowListButton->setFixedWidth(WINDOWLISTBUTTON_SIZE); + windowListButton->setMaximumHeight(BUTTON_MAX_WIDTH); + } + layout->setDirection(TQApplication::reverseLayout() ? + TQBoxLayout::RightToLeft : + TQBoxLayout::LeftToRight); + } + else + { + if (windowListButton) + { + windowListButton->setMaximumWidth(BUTTON_MAX_WIDTH); + windowListButton->setFixedHeight(WINDOWLISTBUTTON_SIZE); + } + layout->setDirection(TQBoxLayout::TopToBottom); + } + + taskBar->setOrientation(o); + if (windowListButton) + { + windowListButton->setOrientation(o); + } + layout->activate(); +} + +void TaskBarContainer::popupDirectionChange(KPanelApplet::Direction d) +{ + direction = d; + ArrowType at = UpArrow; + + TQString 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, + TDEIcon::Panel, + 16)); + windowListButton->setMinimumSize(windowListButton->sizeHint()); + } +} + +void TaskBarContainer::showWindowListMenu() +{ + if (!windowListMenu) + return; + + windowListMenu->init(); + + // calc popup menu position + TQPoint pos( mapToGlobal( TQPoint(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, TQT_SIGNAL( pressed() ), this, TQT_SLOT( showWindowListMenu() ) ); + windowListMenu->exec( pos ); + TQTimer::singleShot(100, this, TQT_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, TQT_SIGNAL( pressed() ), TQT_SLOT( showWindowListMenu() ) ); +} + +TQSize TaskBarContainer::sizeHint( KPanelExtension::Position p, TQSize maxSize) const +{ + TQSize size = taskBar->sizeHint( p, maxSize ); + if ( (p == KPanelExtension::Left || p == KPanelExtension::Right) && showWindowListButton ) { + return TQSize( size.width(), size.height() + WINDOWLISTBUTTON_SIZE ); + } + return size; +} + +void TaskBarContainer::setBackground() +{ + taskBar->setBackground(); +} + +void TaskBarContainer::dragEnterEvent( TQDragEnterEvent* e ) +{ + // ignore all drags other than tasks + if (!TaskDrag::canDecode(e)) + { + return; + } + + if ((e->source()->parent() == taskBar->viewport()) && TaskDrag::canDecode(e) && READ_MERGED_TASBKAR_SETTING(allowDragAndDropReArrange) && (!READ_MERGED_TASBKAR_SETTING(sortByApp))) + { + e->accept(); + } +} + +void TaskBarContainer::dragLeaveEvent( TQDragLeaveEvent* e ) +{ + TQFrame::dragLeaveEvent( e ); +} + +void TaskBarContainer::dropEvent( TQDropEvent* e ) +{ + // ignore all drags other than tasks + if (!TaskDrag::canDecode(e)) + { + return; + } + + if ((e->source()->parent() == taskBar->viewport()) && TaskDrag::canDecode(e) && READ_MERGED_TASBKAR_SETTING(allowDragAndDropReArrange) && (!READ_MERGED_TASBKAR_SETTING(sortByApp))) + { + if (taskBar->taskMoveHandler(TaskMoveDestination::Position, TaskDrag::decode(e), taskBar->mapFrom(this, e->pos()))) { + e->accept(); + } + } +} diff --git a/kicker/taskbar/taskbarcontainer.h b/kicker/taskbar/taskbarcontainer.h new file mode 100644 index 000000000..7fc13241f --- /dev/null +++ b/kicker/taskbar/taskbarcontainer.h @@ -0,0 +1,84 @@ +/***************************************************************** + +Copyright (c) 2001 John Firebaugh <[email protected]> + +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 <tqframe.h> + +#include <dcopobject.h> +#include <kpanelapplet.h> +#include <kpanelextension.h> + +class TQBoxLayout; +class SimpleButton; +class KWindowListMenu; +class TaskBar; +class TaskBarSettings; + +class KDE_EXPORT TaskBarContainer : public TQFrame, public DCOPObject +{ + Q_OBJECT + K_DCOP + +public: + TaskBarContainer( bool enableFrame, TQString configFileOverride = TQString::null, TQWidget* parent = 0, const char* name = 0 ); + ~TaskBarContainer(); + + void orientationChange( Orientation ); + void popupDirectionChange( KPanelApplet::Direction ); + void preferences(); + //FIXME: don't use Position, use Orientation + TQSize sizeHint( KPanelExtension::Position, TQSize maxSize ) const; + void setBackground(); + +protected: + void dragEnterEvent(TQDragEnterEvent*); + void dragLeaveEvent(TQDragLeaveEvent*); + void dropEvent(TQDropEvent*); + +k_dcop: + void configChanged(); + +signals: + void containerCountChanged(); + +protected slots: + void configure(); + void showWindowListMenu(); + void windowListMenuAboutToHide(); + void reconnectWindowListButton(); + +private: + TQString configFile; + KPanelApplet::Direction direction; + bool showWindowListButton; + TQBoxLayout * layout; + TaskBar * taskBar; + SimpleButton * windowListButton; + KWindowListMenu * windowListMenu; + TaskBarSettings * settingsObject; + TaskBarSettings * globalSettingsObject; +}; + +#endif diff --git a/kicker/taskbar/taskbarsettings.kcfgc b/kicker/taskbar/taskbarsettings.kcfgc new file mode 100644 index 000000000..c5219f64e --- /dev/null +++ b/kicker/taskbar/taskbarsettings.kcfgc @@ -0,0 +1,7 @@ +File=taskbar.kcfg +Singleton=false +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..860c231bb --- /dev/null +++ b/kicker/taskbar/taskcontainer.cpp @@ -0,0 +1,1878 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <[email protected]> +Copyright (c) 2002 John Firebaugh <[email protected]> +Copyright (c) 2005 Aaron Seigo <[email protected]> + +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. + +***************************************************************** + + Additional changes: + - 2013/10/22 Michele Calgaro + * added support for display mode (Icons and Text, Text only, Icons only) + and removed "Show application icons" +*/ + +#include <assert.h> + +#include <tqbitmap.h> +#include <tqcolor.h> +#include <tqcursor.h> +#include <tqimage.h> +#include <tqpainter.h> +#include <tqpixmap.h> +#include <tqstyle.h> +#include <tqstylesheet.h> +#include <tqtooltip.h> +#include <tqfile.h> + +#include <tdeapplication.h> +#include <kdebug.h> +#include <tdeglobalsettings.h> +#include <tdelocale.h> +#include <kiconeffect.h> +#include <kiconloader.h> +#include <kimageeffect.h> + +#ifdef Q_WS_X11 +#include <X11/Xlib.h> +#include <netwm.h> +#include <fixx11h.h> +#endif + +#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" + +#define READ_MERGED_TASBKAR_SETTING(x) ((m_settingsObject->useGlobalSettings())?m_globalSettingsObject->x():m_settingsObject->x()) +#define READ_MERGED_TASBKAR_ACTION(x) ((m_settingsObject->useGlobalSettings())?m_globalSettingsObject->action(x):m_settingsObject->action(x)) + +static Bool netwm_atoms_created = False; +static Atom net_wm_pid = 0; + +static const int netAtomCount = 1; +static void create_atoms(Display *d) { + static const char * const names[netAtomCount] = + { + "_NET_WM_PID" + }; + + Atom atoms[netAtomCount], *atomsp[netAtomCount] = + { + &net_wm_pid + }; + + assert( !netwm_atoms_created ); + + int i = netAtomCount; + while (i--) + atoms[i] = 0; + + XInternAtoms(d, (char **) names, netAtomCount, False, atoms); + + i = netAtomCount; + while (i--) + *atomsp[i] = atoms[i]; + + netwm_atoms_created = True; +} + +bool is_process_resumable(pid_t pid) { + TQFile procStatFile(TQString("/proc/%1/stat").arg(pid)); + if (procStatFile.open(IO_ReadOnly)) { + TQByteArray statRaw = procStatFile.readAll(); + procStatFile.close(); + TQString statString(statRaw); + TQStringList statFields = TQStringList::split(" ", statString, TRUE); + TQString tcomm = statFields[1]; + TQString state = statFields[2]; + if( state == "T" ) { + return true; + } + else { + return false; + } + } + else { + return false; + } +} + +TaskContainer::TaskContainer(Task::Ptr task, TaskBar* bar, TaskBarSettings* settingsObject, TaskBarSettings* globalSettingsObject, TQWidget *parent, const char *name) + : TQToolButton(parent, name), + animationTimer(0, "TaskContainer::animationTimer"), + dragSwitchTimer(0, "TaskContainer::dragSwitchTimer"), + attentionTimer(0, "TaskContainer::attentionTimer"), + m_paintEventCompressionTimer(0, "TaskContainer::paintEventCompressionTimer"), + currentFrame(0), + attentionState(-1), + lastActivated(0), + m_menu(0), + m_startup(0), + arrowType(TQt::UpArrow), + taskBar(bar), + discardNextMouseEvent(false), + aboutToActivate(false), + m_mouseOver(false), + m_paintEventCompression(false), + m_settingsObject(settingsObject), + m_globalSettingsObject(globalSettingsObject) +{ + 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, TaskBarSettings* settingsObject, TaskBarSettings* globalSettingsObject, TQWidget *parent, const char *name) + : TQToolButton(parent, name), + animationTimer(0, "TaskContainer::animationTimer"), + dragSwitchTimer(0, "TaskContainer::dragSwitchTimer"), + attentionTimer(0, "TaskContainer::attentionTimer"), + m_paintEventCompressionTimer(0, "TaskContainer::paintEventCompressionTimer"), + currentFrame(0), + frames(startupFrames), + attentionState(-1), + lastActivated(0), + m_menu(0), + m_startup(startup), + arrowType(TQt::LeftArrow), + taskBar(bar), + discardNextMouseEvent(false), + aboutToActivate(false), + m_mouseOver(false), + m_paintEventCompression(false), + m_settingsObject(settingsObject), + m_globalSettingsObject(globalSettingsObject) +{ + init(); + setEnabled(false); + + sid = m_startup->bin(); + + connect(m_startup, TQT_SIGNAL(changed()), TQT_SLOT(update())); + + dragSwitchTimer.start(333, true); +} + +void TaskContainer::init() +{ + if (m_settingsObject) + { + m_settingsObject->readConfig(); + } + if (m_globalSettingsObject) + { + m_globalSettingsObject->readConfig(); + } + + if (!netwm_atoms_created) create_atoms(TQPaintDevice::x11AppDisplay()); + + setWFlags(TQt::WNoAutoErase); + setBackgroundMode(NoBackground); + animBg = TQPixmap(16, 16); + + installEventFilter(KickerTip::the()); + + connect(&animationTimer, TQT_SIGNAL(timeout()), TQT_SLOT(animationTimerFired())); + connect(&dragSwitchTimer, TQT_SIGNAL(timeout()), TQT_SLOT(showMe())); + connect(&attentionTimer, TQT_SIGNAL(timeout()), TQT_SLOT(attentionTimerFired())); + connect(&m_paintEventCompressionTimer, TQT_SIGNAL(timeout()), TQT_SLOT(updateNow())); +} + +TaskContainer::~TaskContainer() +{ + if (m_menu) + { + delete m_menu; + m_menu = 0; + } + + stopTimers(); +} + +void TaskContainer::showMe() +{ + if(!frames.isEmpty() && taskBar->showIcons()) + animationTimer.start(100); + + emit showMe(this); + disconnect(&dragSwitchTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(showMe())); + connect(&dragSwitchTimer, TQT_SIGNAL(timeout()), TQT_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 TQObject* source = TQT_TQOBJECT_CONST(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 TQObject* source = TQT_TQOBJECT_CONST(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(); + TQToolButton::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->showIcons() && frames.at(currentFrame) != frames.end()) + { + TQPixmap *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 + TQPixmap 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 < READ_MERGED_TASBKAR_SETTING(attentionBlinkIterations)*2) + { + ++attentionState; + } + else if (READ_MERGED_TASBKAR_SETTING(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(); +} + +TQSizePolicy TaskContainer::sizePolicy() const +{ + return TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding ); +} + +void TaskContainer::resizeEvent( TQResizeEvent * ) +{ + // calculate the icon rect + TQRect br( style().subRect( TQStyle::SR_PushButtonContents, this ) ); + iconRect = TQStyle::visualRect( TQRect(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, TQT_SIGNAL(changed(bool)), TQT_SLOT(taskChanged(bool))); + connect(task, TQT_SIGNAL(iconChanged()), TQT_SLOT(iconChanged())); + connect(task, TQT_SIGNAL(activated()), TQT_SLOT(setLastActivated())); +} + +void TaskContainer::remove(Task::Ptr task) +{ + if (!task) + { + return; + } + + task->publishIconGeometry(TQRect()); + 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); +} + +bool TaskContainer::isHidden() +{ + return ((m_filteredTasks.count() < 1) && !m_startup); +} + +TQString TaskContainer::id() +{ + return sid; +} + +void TaskContainer::updateNow() +{ + m_paintEventCompression = true; + update(); +} + +void TaskContainer::setBackground() +{ + updateNow(); +} + +void TaskContainer::paintEvent( TQPaintEvent* ) +{ + if (!m_paintEventCompression) + { + if (!m_paintEventCompressionTimer.isActive()) + { + m_paintEventCompressionTimer.start(30, true); + } + return; + } + + m_paintEventCompression = false; + TQPixmap* pm = new TQPixmap(size()); + + const TQPixmap* background = taskBar->backgroundPixmap(); + + if (background) + { + TQPoint pt = mapTo(taskBar, TQPoint(0, 0)) + taskBar->backgroundOffset(); + TQPainter p(pm); + p.drawTiledPixmap(0, 0, width(), height(), *background, pt.x(), pt.y()); + p.end(); + } + else + { + pm->fill(taskBar->paletteBackgroundColor()); + } + + TQPainter p; + p.begin(pm ,this); + drawButton(&p); + p.end(); + + TQPixmap iconPixmapToSet = *pm; + if (TQPaintDevice::x11AppDepth() == 32) iconPixmapToSet.convertFromImage(KImageEffect::convertToPremultipliedAlpha( iconPixmapToSet.convertToImage() )); + + bitBlt(this, 0, 0, &iconPixmapToSet); + delete pm; +} + +void TaskContainer::drawButton(TQPainter *p) +{ + if (isEmpty()) + { + return; + } + + // get a pointer to the pixmap we're drawing on + TQPixmap *pm((TQPixmap*)p->device()); + TQPixmap pixmap; // icon + Task::Ptr task = 0; + bool iconified = !READ_MERGED_TASBKAR_SETTING(showOnlyIconified); + bool halo = READ_MERGED_TASBKAR_SETTING(haloText); + bool alwaysDrawButtons = READ_MERGED_TASBKAR_SETTING(drawButtons); + bool drawButton = alwaysDrawButtons || + (m_mouseOver && !halo && isEnabled() && + READ_MERGED_TASBKAR_SETTING(showButtonOnHover)); + TQFont font(TDEGlobalSettings::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 == READ_MERGED_TASBKAR_SETTING(attentionBlinkIterations) || + attentionState % 2 == 0; + } + } + + font.setBold(active); + + TQColorGroup colors = palette().active(); + + if (READ_MERGED_TASBKAR_SETTING(useCustomColors)) + { + colors.setColor( TQColorGroup::Button, READ_MERGED_TASBKAR_SETTING(taskBackgroundColor)); + colors.setColor( TQColorGroup::Background, READ_MERGED_TASBKAR_SETTING(taskBackgroundColor) ); + colors.setColor( TQColorGroup::ButtonText, READ_MERGED_TASBKAR_SETTING(inactiveTaskTextColor) ); + colors.setColor( TQColorGroup::Text, READ_MERGED_TASBKAR_SETTING(inactiveTaskTextColor) ); + } + + if (demandsAttention) + { + if (!drawButton) + { + halo = true; + + TQRect r = rect(); + TQColor 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(TQPen(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( TQColorGroup::Button, colors.highlight() ); + colors.setColor( TQColorGroup::Background, colors.highlight() ); + colors.setColor( TQColorGroup::ButtonText, colors.highlightedText() ); + colors.setColor( TQColorGroup::Text, colors.highlightedText() ); + } + + if (active || aboutToActivate) + { + colors.setColor(TQColorGroup::Button, colors.button().dark(110)); + } + + // get the task icon + if (task) + { + pixmap = task->pixmap(); + } + + bool sunken = isDown() || (alwaysDrawButtons && (active || aboutToActivate)); + bool reverse = TQApplication::reverseLayout(); + TQRect br(style().subRect(TQStyle::SR_PushButtonContents, this)); + TQPoint shift = TQPoint(style().pixelMetric(TQStyle::PM_ButtonShiftHorizontal), + style().pixelMetric(TQStyle::PM_ButtonShiftVertical)); + + // draw button background + if (drawButton) + { + if (READ_MERGED_TASBKAR_SETTING(drawButtons) && KickerSettings::showDeepButtons()) { + style().tqdrawPrimitive(TQStyle::PE_ButtonBevel, p, + TQRect(1, 1, width()-3, height()-2), + colors, sunken ? TQStyle::Style_On : TQStyle::Style_Raised); + } + else { + style().tqdrawPrimitive(TQStyle::PE_ButtonTool, p, + TQRect(1, 1, width()-2, height()-2), + colors, sunken ? TQStyle::Style_Down : TQStyle::Style_Raised); + } + } + + // shift button label on sunken buttons + if (sunken) + { + p->translate(shift.x(), shift.y()); + } + + TQString text = name(); // find text + int textPos = ( taskBar->showIcons() && (!pixmap.isNull() || m_startup)) ? 2 + 16 + 2 : 0; + + // show icons + if (taskBar->showIcons()) + { + 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 ) + { + TQImage tmp = pixmap.convertToImage(); + pixmap.convertFromImage( tmp.smoothScale( 16, 16 ) ); + } + + // fade out the icon when minimized + if (iconified) + TDEIconEffect::semiTransparent( pixmap ); + + // draw icon + TQRect pmr(0, 0, pixmap.width(), pixmap.height()); + pmr.moveCenter(iconRect.center()); + p->drawPixmap(pmr, pixmap); + } + + // modified overlay icon + if (taskBar->showText()) + { + static TQString modStr = "[" + i18n( "modified" ) + "]"; + int modStrPos = text.find( modStr ); + if (modStrPos >= 0) + { + // +1 because we include a space after the closing brace. + text.remove(modStrPos, modStr.length() + 1); + TQPixmap modPixmap = SmallIcon("modified"); + + // draw modified overlay + if (!modPixmap.isNull()) + { + TQRect r = TQStyle::visualRect(TQRect(br.x() + textPos,(height() - 16) / 2, 16, 16), this); + if (iconified) + { + TDEIconEffect::semiTransparent(modPixmap); + } + p->drawPixmap(r, modPixmap); + textPos += 16 + 2; + } + } + } + } + + // draw text + if (taskBar->showText()) + { + if (!text.isEmpty()) + { + TQRect tr = TQStyle::visualRect(TQRect(br.x() + textPos + 1, 0, + width() - textPos, height()), this); + int textFlags = AlignVCenter | SingleLine; + textFlags |= reverse ? AlignRight : AlignLeft; + TQPen textPen; + + // get the color for the text label + if (iconified) + { + textPen = TQPen(KickerLib::blendColors(colors.button(), colors.buttonText())); + } + else if (!active) + { + textPen = TQPen(colors.buttonText()); + } + else // hack for the dotNET style and others + { + if (READ_MERGED_TASBKAR_SETTING(useCustomColors)) + { + textPen = TQPen(READ_MERGED_TASBKAR_SETTING(activeTaskTextColor)); + } + else + { + textPen = TQPen(colors.buttonText()); // textPen = p->pen(); + } + } + + int availableWidth = width() - (br.x() * 2) - textPos - 2 - (READ_MERGED_TASBKAR_SETTING(drawButtons) && KickerSettings::showDeepButtons())?2:0; + if (m_filteredTasks.count() > 1) + { + availableWidth -= 8; + } + + if (TQFontMetrics(font).width(text) > availableWidth) + { + // draw text into overlay pixmap + TQPixmap tpm(*pm); + TQPainter 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 + TQImage img = pm->convertToImage(); + TQImage timg = tpm.convertToImage(); + KImageEffect::blend(img, timg, *taskBar->blendGradient(size()), KImageEffect::Red); + + // End painting before assigning the pixmap + TQPaintDevice* opd = p->device(); + p->end(); + pm->convertFromImage(img); + p->begin(opd ,this); + } + 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()) + { + TQPixmap *anim = *frames.at(currentFrame); + + if (anim && !anim->isNull()) + { + // save the background for the other frames + bitBlt(&animBg, TQPoint(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) && (!(READ_MERGED_TASBKAR_SETTING(drawButtons) && KickerSettings::showDeepButtons()))) + { + TQStyle::PrimitiveElement e = TQStyle::PE_ArrowLeft; + + switch (arrowType) + { + case Qt::LeftArrow: e = TQStyle::PE_ArrowLeft; break; + case Qt::RightArrow: e = TQStyle::PE_ArrowRight; break; + case Qt::UpArrow: e = TQStyle::PE_ArrowUp; break; + case Qt::DownArrow: e = TQStyle::PE_ArrowDown; break; + } + + int flags = TQStyle::Style_Enabled; + TQRect ar = TQStyle::visualRect(TQRect(br.x() + br.width() - 8 - 2, + br.y(), 8, br.height()), this); + if (sunken) + { + flags |= TQStyle::Style_Down; + } + + style().tqdrawPrimitive(e, p, ar, colors, flags); + } + + // draw mouse over frame in transparent mode + if (m_mouseOver && halo) + KickerLib::drawBlendedRect(p, TQRect(0, 0, width(), height()), colorGroup().foreground()); + + if (aboutToActivate) + { + aboutToActivate = false; + } +} + +TQString TaskContainer::name() +{ + // default to container id + TQString 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 TQString 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) + { + TQChar 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 -> TQString 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 += TQString::number(m_filteredTasks.count()); + text += "]"; + } + + return text; +} + +void TaskContainer::mousePressEvent( TQMouseEvent* e ) +{ + if (discardNextMouseEvent) + { + discardNextMouseEvent = false; + return; + } + + if (e->button() == Qt::LeftButton) + { + m_dragStartPos = e->pos(); + } + else + { + m_dragStartPos = TQPoint(); + } + + int buttonAction = 0; + + // On left button, only do actions that invoke a menu. + // Other actions will be handled in mouseReleaseEvent + switch (e->button()) + { + case Qt::LeftButton: + buttonAction = READ_MERGED_TASBKAR_ACTION(m_settingsObject->LeftButton); + break; + case Qt::MidButton: + buttonAction = READ_MERGED_TASBKAR_ACTION(m_settingsObject->MiddleButton); + break; + case Qt::RightButton: + default: + buttonAction = READ_MERGED_TASBKAR_ACTION(m_settingsObject->RightButton); + break; + } + + if ((buttonAction == m_settingsObject->ShowTaskList && + m_filteredTasks.count() > 1) || + buttonAction == m_settingsObject->ShowOperationsMenu) + { + performAction(buttonAction); + } +} + +void TaskContainer::mouseReleaseEvent(TQMouseEvent *e) +{ + m_dragStartPos = TQPoint(); + + if (!READ_MERGED_TASBKAR_SETTING(drawButtons)) + { + setDown(false); + } + + // This is to avoid the flicker caused by redrawing the + // button as unpressed just before it's activated. + if (!TQT_TQRECT_OBJECT(rect()).contains(e->pos())) + { + TQToolButton::mouseReleaseEvent(e); + return; + } + + int buttonAction = 0; + + switch (e->button()) + { + case Qt::LeftButton: + buttonAction = READ_MERGED_TASBKAR_ACTION(m_settingsObject->LeftButton); + break; + case Qt::MidButton: + buttonAction = READ_MERGED_TASBKAR_ACTION(m_settingsObject->MiddleButton); + break; + case Qt::RightButton: + default: + buttonAction = READ_MERGED_TASBKAR_ACTION(m_settingsObject->RightButton); + break; + } + + if ((buttonAction == m_settingsObject->ShowTaskList && + m_filteredTasks.count() > 1) || + buttonAction == m_settingsObject->ShowOperationsMenu) + { + return; + } + + if (buttonAction == TaskBarSettings::ActivateRaiseOrMinimize || + buttonAction == TaskBarSettings::Activate) + { + aboutToActivate = true; + } + + performAction( buttonAction ); + TQTimer::singleShot(0, this, TQT_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( m_settingsObject->ShowTaskList ); + } else { + performAction( TaskBarSettings::ActivateRaiseOrMinimize ); + } + break; + case TaskBarSettings::ShowOperationsMenu: + popupMenu( m_settingsObject->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 == m_settingsObject->ShowTaskList ) + { + m_menu = new TaskLMBMenu(m_filteredTasks); + } + else if (action == m_settingsObject->ShowOperationsMenu) + { + if (!kapp->authorizeTDEAction("twin_rmb")) + { + return; + } + + m_menu = new TaskRMBMenu(m_filteredTasks, taskBar->showAllWindows(), (READ_MERGED_TASBKAR_SETTING(allowDragAndDropReArrange))?makeTaskMoveMenu():NULL); + } + else + { + return; + } + + // calc popup menu position + TQPoint pos(mapToGlobal(TQPoint(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 ( TQApplication::reverseLayout() ) + pos.setX( pos.x() + width() - m_menu->sizeHint().width() ); + pos.setY( pos.y() + height() ); + break; + case UpArrow: + if ( TQApplication::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 ); + + if (m_menu) { + delete m_menu; + } + m_menu = 0; +} + +TQPopupMenu* TaskContainer::makeTaskMoveMenu() +{ + TaskMoveDestination::TaskMoveDestination capabilities = taskBar->taskMoveCapabilities(this); + + int id; + TQPopupMenu* menu = new TQPopupMenu(); + + menu->setCheckable(false); + + id = menu->insertItem(SmallIconSet("start"), + i18n("Move to Beginning"), + this, TQT_SLOT(slotTaskMoveBeginning())); + menu->setItemEnabled(id, (capabilities & TaskMoveDestination::Left)); + + id = menu->insertItem(SmallIconSet("back"), + i18n("Move Left"), + this, TQT_SLOT(slotTaskMoveLeft())); + menu->setItemEnabled(id, (capabilities & TaskMoveDestination::Left)); + + id = menu->insertItem(SmallIconSet("forward"), + i18n("Move Right"), + this, TQT_SLOT(slotTaskMoveRight())); + menu->setItemEnabled(id, (capabilities & TaskMoveDestination::Right)); + + id = menu->insertItem(SmallIconSet("finish"), + i18n("Move to End"), + this, TQT_SLOT(slotTaskMoveEnd())); + menu->setItemEnabled(id, (capabilities & TaskMoveDestination::Right)); + + return menu; +} + +void TaskContainer::slotTaskMoveBeginning() +{ + taskBar->taskMoveHandler(TaskMoveDestination::Beginning, taskList()); +} + +void TaskContainer::slotTaskMoveLeft() +{ + taskBar->taskMoveHandler(TaskMoveDestination::Left, taskList()); +} + +void TaskContainer::slotTaskMoveRight() +{ + taskBar->taskMoveHandler(TaskMoveDestination::Right, taskList()); +} + +void TaskContainer::slotTaskMoveEnd() +{ + taskBar->taskMoveHandler(TaskMoveDestination::End, taskList()); +} + +void TaskContainer::mouseMoveEvent( TQMouseEvent* e ) +{ + kdDebug() << "regular move" << endl; + if (!m_dragStartPos.isNull()) + { + startDrag(e->pos()); + } + + TQToolButton::mouseMoveEvent(e); +} + +bool TaskContainer::startDrag(const TQPoint& pos) +{ + if (m_filteredTasks.count() != 1) + { + return false; + } + + int delay = TDEGlobalSettings::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 TQToolButton. Unfortunately Qt is lame +// as usual and makes interesting stuff private or +// non-virtual, so we have to copy code. +bool TaskContainer::eventFilter(TQObject *o, TQEvent *e) +{ + switch ( e->type() ) + { + case TQEvent::MouseButtonPress: + case TQEvent::MouseButtonDblClick: + { + TQMouseEvent *me = (TQMouseEvent*)e; + TQPoint p = me->globalPos(); + if ( TQApplication::widgetAt( p, true ) == this ) + { + if (me->type() == TQEvent::MouseButtonPress && + me->button() == Qt::LeftButton) + { + m_dragStartPos = mapFromGlobal(p); + } + + discardNextMouseEvent = true; + } + break; + } + case TQEvent::MouseButtonRelease: + { + m_dragStartPos = TQPoint(); + break; + } + case TQEvent::MouseMove: + { + if (!m_dragStartPos.isNull()) + { + TQMouseEvent* me = TQT_TQMOUSEEVENT(e); + TQPoint p(me->globalPos()); + + if (me->state() & Qt::LeftButton && + TQApplication::widgetAt(p, true) == this) + { + kdDebug() << "event move" << endl; + if (startDrag(mapFromGlobal(p))) + { + TQPopupMenu* menu = dynamic_cast<TQPopupMenu*>(o); + + if (menu) + { + menu->hide(); + } + } + } + } + break; + } + + default: + break; + } + + return TQToolButton::eventFilter( o, e ); +} + +void TaskContainer::setArrowType( TQt::ArrowType at ) +{ + if (arrowType == at) + { + return; + } + + arrowType = at; + update(); +} + +void TaskContainer::publishIconGeometry( TQPoint global ) +{ + TQPoint 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(TQRect(p.x(), p.y(), width(), height())); + } +} + +void TaskContainer::dragEnterEvent( TQDragEnterEvent* e ) +{ + // ignore applet drags + if (PanelDrag::canDecode(e)) + { + return; + } + + if ((e->source()->parent() == this->parent()) && TaskDrag::canDecode(e) && READ_MERGED_TASBKAR_SETTING(allowDragAndDropReArrange) && (!READ_MERGED_TASBKAR_SETTING(sortByApp))) + { + e->accept(); + } + + // 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); + } + + TQToolButton::dragEnterEvent( e ); +} + +void TaskContainer::dropEvent( TQDropEvent* e ) +{ + // Ignore all drops except tasks + if (!TaskDrag::canDecode(e)) { + return; + } + + if ((e->source()->parent() == this->parent()) && TaskDrag::canDecode(e) && READ_MERGED_TASBKAR_SETTING(allowDragAndDropReArrange) && (!READ_MERGED_TASBKAR_SETTING(sortByApp))) + { + if (taskBar->taskMoveHandler(TaskMoveDestination::Position, TaskDrag::decode(e), TQWidget::mapTo(taskBar, e->pos()))) { + e->accept(); + } + } + + dragSwitchTimer.stop(); + + TQToolButton::dropEvent( e ); +} + +void TaskContainer::dragLeaveEvent( TQDragLeaveEvent* e ) +{ + dragSwitchTimer.stop(); + + TQToolButton::dragLeaveEvent( e ); +} + +void TaskContainer::enterEvent(TQEvent* e) +{ + TQToolTip::remove(this); + m_mouseOver = true; + updateNow(); + + if (tasks.isEmpty()) + { + TQToolButton::enterEvent(e); + return; + } + + if (!KickerSettings::showMouseOverEffects()) + { + TQString tooltip = "<qt>" + TQStyleSheet::escape(name()) + "</qt>"; + TQToolTip::add(this, tooltip); + return; + } +} + +void TaskContainer::leaveEvent(TQEvent*) +{ + m_mouseOver = false; + updateNow(); +} + +void TaskContainer::dragSwitch() +{ + if (m_filteredTasks.isEmpty()) + { + return; + } + + if (m_filteredTasks.count() == 1) + { + m_filteredTasks.first()->activate(); + } + else + { + popupMenu(m_settingsObject->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()) && + (!READ_MERGED_TASBKAR_SETTING(showOnlyIconified) || t->isIconified())) + { + pid_t pid = 0; +#ifdef Q_WS_X11 + Atom type_ret; + int format_ret; + unsigned long nitems_ret = 0, unused = 0; + unsigned char *data_ret = 0; + if (XGetWindowProperty(TQPaintDevice::x11AppDisplay(), t->window(), net_wm_pid, 0l, 1l, + False, XA_CARDINAL, &type_ret, &format_ret, + &nitems_ret, &unused, &data_ret) == Success) { + if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1) { + pid = *((long *) data_ret); + } + if ( data_ret ) + XFree(data_ret); + } +#endif + if (pid < 0) { + m_filteredTasks.append(t); + } + else if (READ_MERGED_TASBKAR_SETTING(showTaskStates) != m_settingsObject->ShowAll) { + if (is_process_resumable(pid)) { + if (READ_MERGED_TASBKAR_SETTING(showTaskStates) == m_settingsObject->ShowAll) { + m_filteredTasks.append(t); + } + else if (READ_MERGED_TASBKAR_SETTING(showTaskStates) == m_settingsObject->ShowStopped) { + m_filteredTasks.append(t); + } + else if (READ_MERGED_TASBKAR_SETTING(showTaskStates) == m_settingsObject->ShowRunning) { + t->publishIconGeometry( TQRect()); + } + else { + m_filteredTasks.append(t); + } + } + else { + if (READ_MERGED_TASBKAR_SETTING(showTaskStates) == m_settingsObject->ShowAll) { + m_filteredTasks.append(t); + } + else if (READ_MERGED_TASBKAR_SETTING(showTaskStates) == m_settingsObject->ShowStopped) { + t->publishIconGeometry( TQRect()); + } + else if (READ_MERGED_TASBKAR_SETTING(showTaskStates) == m_settingsObject->ShowRunning) { + m_filteredTasks.append(t); + } + else { + m_filteredTasks.append(t); + } + } + } + else { + m_filteredTasks.append(t); + } + } + else + { + t->publishIconGeometry( TQRect()); + } + } + + // sort container list by desktop + if (taskBar->sortByDesktop() && m_filteredTasks.count() > 1) + { + TQValueVector<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 (TQValueVector<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 = TDEGlobal::iconLoader()->loadIcon(m_startup->icon(), + TDEIcon::Small, + TDEIcon::SizeMedium, + TDEIcon::DefaultState, + 0, true); + return; + } + + TQPixmap pixmap; + TQString name; + TQString details; + + if (m_filteredTasks.count() > 0) + { + if (READ_MERGED_TASBKAR_SETTING(showThumbnails) && + m_filteredTasks.count() == 1) + { + Task::Ptr t = m_filteredTasks.first(); + + pixmap = t->thumbnail(READ_MERGED_TASBKAR_SETTING(thumbnailMaxDimension)); + } + + if (pixmap.isNull() && tasks.count()) + { + // try to load icon via net_wm + pixmap = KWin::icon(tasks.last()->window(), + TDEIcon::SizeMedium, + TDEIcon::SizeMedium, + true); + } + + // Collect all desktops the tasks are on. Sort naturally. + TQMap<int, TQString> 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 (READ_MERGED_TASBKAR_SETTING(showAllWindows) && KWin::numberOfDesktops() > 1) + { + if (desktopMap.isEmpty()) + { + details.append(i18n("On all desktops")); + } + else + { + TQStringList desktopNames = desktopMap.values(); + details.append(i18n("On %1").arg(TQStyleSheet::escape(desktopNames.join(", "))) + "<br>"); + } + } + + if (demandsAttention) + { + details.append(i18n("Requesting attention") + "<br>"); + } + + name = this->name(); + if (modified) + { + details.append(i18n("Has unsaved changes")); + + static TQString 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 = TQStyleSheet::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 TQGuardedPtr 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..edaf337b4 --- /dev/null +++ b/kicker/taskbar/taskcontainer.h @@ -0,0 +1,165 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <[email protected]> +Copyright (c) 2002 John Firebaugh <[email protected]> + +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 <tqpixmap.h> +#include <tqtimer.h> +#include <tqtoolbutton.h> + +#include "kickertip.h" +#include "taskmanager.h" + +class TaskBar; +class TaskBarSettings; + +typedef TQValueList<TQPixmap*> PixmapList; + +class TaskContainer : public TQToolButton, public KickerTip::Client +{ + Q_OBJECT + +public: + typedef TQValueList<TaskContainer*> List; + typedef TQValueList<TaskContainer*>::iterator Iterator; + + TaskContainer(Task::Ptr, TaskBar*, TaskBarSettings* settingsObject, TaskBarSettings* globalSettingsObject, TQWidget *parent = 0, const char *name = 0); + TaskContainer(Startup::Ptr, PixmapList&, TaskBar*, TaskBarSettings* settingsObject, TaskBarSettings* globalSettingsObject, TQWidget *parent = 0, const char *name = 0); + virtual ~TaskContainer(); + + void setArrowType( TQt::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(); + bool isHidden(); + + TQString id(); + int desktop(); + TQString name(); + + virtual TQSizePolicy sizePolicy () const; + + void publishIconGeometry( TQPoint ); + void desktopChanged( int ); + void windowChanged(Task::Ptr); + void settingsChanged(); + bool eventFilter( TQObject *o, TQEvent *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(); + + Task::List taskList() const { return tasks; } + +public slots: + void updateNow(); + +signals: + void showMe(TaskContainer*); + +protected: + void paintEvent(TQPaintEvent*); + void drawButton(TQPainter*); + void resizeEvent(TQResizeEvent*); + void mousePressEvent(TQMouseEvent*); + void mouseReleaseEvent(TQMouseEvent*); + void mouseMoveEvent(TQMouseEvent*); + void dragEnterEvent(TQDragEnterEvent*); + void dragLeaveEvent(TQDragLeaveEvent*); + void dropEvent(TQDropEvent*); + void enterEvent(TQEvent*); + void leaveEvent(TQEvent*); + bool startDrag(const TQPoint& 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(); + + void slotTaskMoveBeginning(); + void slotTaskMoveLeft(); + void slotTaskMoveRight(); + void slotTaskMoveEnd(); + +private: + void checkAttention(const Task::Ptr changed_task = NULL); + TQPopupMenu* makeTaskMoveMenu(); + TQString sid; + TQTimer animationTimer; + TQTimer dragSwitchTimer; + TQTimer attentionTimer; + TQTimer m_paintEventCompressionTimer; + int currentFrame; + PixmapList frames; + int attentionState; + TQRect iconRect; + TQPixmap animBg; + Task::List tasks; + Task::List m_filteredTasks; + Task::Ptr lastActivated; + TQPopupMenu* 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 }; + TQPoint m_dragStartPos; + TaskBarSettings* m_settingsObject; + TaskBarSettings* m_globalSettingsObject; +}; + +#endif |