You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/kicker/taskbar/taskcontainer.cpp

1874 lines
50 KiB

/*****************************************************************
Copyright (c) 2001 Matthias Elter <elter@kde.org>
Copyright (c) 2002 John Firebaugh <jfirebaugh@kde.org>
Copyright (c) 2005 Aaron Seigo <aseigo@kde.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.#
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*****************************************************************
*/
#include <assert.h>
#include <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)
{