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.
lxqt-panel-packaging/plugin-taskbar/lxqttaskbar.cpp

596 lines
18 KiB

/* BEGIN_COMMON_COPYRIGHT_HEADER
* (c)LGPL2+
*
* LXDE-Qt - a lightweight, Qt based, desktop toolset
* http://razor-qt.org
* http://lxqt.org
*
* Copyright: 2011 Razor team
* 2014 LXQt team
* Authors:
* Alexander Sokoloff <sokoloff.a@gmail.com>
* Maciej Płaza <plaza.maciej@gmail.com>
* Kuzma Shapran <kuzma.shapran@gmail.com>
*
* This program or library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* END_COMMON_COPYRIGHT_HEADER */
#include <QApplication>
#include <QDebug>
#include <QToolButton>
#include <QSettings>
#include <LXQt/GridLayout>
#include <XdgIcon>
#include <QList>
#include <QMimeData>
#include <QDesktopWidget>
#include <QWheelEvent>
#include <QFlag>
#include <QX11Info>
#include <QDebug>
#include "lxqttaskbar.h"
#include "lxqttaskbutton.h"
#include "../panel/ilxqtpanelplugin.h"
using namespace LxQt;
/************************************************
************************************************/
LxQtTaskBar::LxQtTaskBar(ILxQtPanelPlugin *plugin, QWidget *parent) :
QFrame(parent),
mButtonStyle(Qt::ToolButtonTextBesideIcon),
mCheckedBtn(NULL),
mCloseOnMiddleClick(true),
mShowOnlyCurrentDesktopTasks(false),
mAutoRotate(true),
mPlugin(plugin),
mPlaceHolder(new QWidget(this)),
mStyle(new ElidedButtonStyle())
{
mLayout = new LxQt::GridLayout(this);
setLayout(mLayout);
mLayout->setMargin(0);
realign();
mPlaceHolder->setStyle(mStyle);
mPlaceHolder->setMinimumSize(1, 1);
mPlaceHolder->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
mPlaceHolder->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
mLayout->addWidget(mPlaceHolder);
settingsChanged();
setAcceptDrops(true);
connect(KWindowSystem::self(), SIGNAL(stackingOrderChanged()), SLOT(refreshTaskList()));
connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), SLOT(refreshTaskList()));
connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), SLOT(activeWindowChanged(WId)));
connect(KWindowSystem::self(), SIGNAL(windowChanged(WId, NET::Properties, NET::Properties2)),
SLOT(windowChanged(WId, NET::Properties, NET::Properties2)));
}
/************************************************
************************************************/
LxQtTaskBar::~LxQtTaskBar()
{
delete mStyle;
}
/************************************************
************************************************/
LxQtTaskButton* LxQtTaskBar::buttonByWindow(WId window) const
{
if (mButtonsHash.contains(window))
return mButtonsHash.value(window);
return 0;
}
/************************************************
************************************************/
bool LxQtTaskBar::windowOnActiveDesktop(WId window) const
{
if (!mShowOnlyCurrentDesktopTasks)
return true;
int desktop = KWindowInfo(window, NET::WMDesktop).desktop();
if (desktop == NET::OnAllDesktops)
return true;
return desktop == KWindowSystem::currentDesktop();
}
/************************************************
************************************************/
bool LxQtTaskBar::acceptWindow(WId window) const
{
QFlags<NET::WindowTypeMask> ignoreList;
ignoreList |= NET::DesktopMask;
ignoreList |= NET::DockMask;
ignoreList |= NET::SplashMask;
ignoreList |= NET::ToolbarMask;
ignoreList |= NET::MenuMask;
ignoreList |= NET::PopupMenuMask;
ignoreList |= NET::NotificationMask;
KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor);
if (!info.valid())
return false;
if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList))
return false;
if (info.state() & NET::SkipTaskbar)
return false;
// WM_TRANSIENT_FOR hint not set - normal window
WId transFor = info.transientFor();
if (transFor == 0 || transFor == window || transFor == (WId) QX11Info::appRootWindow())
return true;
info = KWindowInfo(transFor, NET::WMWindowType);
QFlags<NET::WindowTypeMask> normalFlag;
normalFlag |= NET::NormalMask;
normalFlag |= NET::DialogMask;
normalFlag |= NET::UtilityMask;
return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag);
}
/************************************************
************************************************/
void LxQtTaskBar::dragEnterEvent(QDragEnterEvent* event)
{
if (event->mimeData()->hasFormat("lxqt/lxqttaskbutton"))
event->acceptProposedAction();
else
event->ignore();
QWidget::dragEnterEvent(event);
}
/************************************************
************************************************/
void LxQtTaskBar::dropEvent(QDropEvent* event)
{
if (!event->mimeData()->hasFormat("lxqt/lxqttaskbutton"))
return;
QDataStream stream(event->mimeData()->data("lxqt/lxqttaskbutton"));
// window id for dropped button
qlonglong temp;
stream >> temp;
WId droppedWid = (WId) temp;
qDebug() << QString("Dropped window: %1").arg(droppedWid);
int droppedIndex = mLayout->indexOf(mButtonsHash[droppedWid]);
int newPos = -1;
const int size = mLayout->count();
for (int i = 0; i < droppedIndex && newPos == -1; i++)
if (mLayout->itemAt(i)->widget()->x() + mLayout->itemAt(i)->widget()->width() / 2 > event->pos().x())
newPos = i;
for (int i = size - 1; i > droppedIndex && newPos == -1; i--)
if (mLayout->itemAt(i)->widget()->x() + mLayout->itemAt(i)->widget()->width() / 2 < event->pos().x())
newPos = i;
if (newPos == -1 || droppedIndex == newPos)
return;
qDebug() << QString("Dropped button shoud go to position %1").arg(newPos);
mLayout->moveItem(droppedIndex, newPos);
mLayout->invalidate();
QWidget::dropEvent(event);
}
/************************************************
************************************************/
void LxQtTaskBar::refreshTaskList()
{
QList<WId> tmp = KWindowSystem::stackingOrder();
QMutableHashIterator<WId, LxQtTaskButton*> i(mButtonsHash);
while (i.hasNext())
{
i.next();
int n = tmp.removeAll(i.key());
if (!n)
{
// if the button we're removing is the currently selected app
if(i.value() == mCheckedBtn)
mCheckedBtn = NULL;
delete i.value();
i.remove();
}
}
foreach (WId wnd, tmp)
{
if (acceptWindow(wnd))
{
LxQtTaskButton* btn = new LxQtTaskButton(wnd, this);
btn->setStyle(mStyle);
btn->setToolButtonStyle(mButtonStyle);
mButtonsHash.insert(wnd, btn);
mLayout->addWidget(btn);
}
}
refreshButtonVisibility();
mLayout->invalidate();
activeWindowChanged();
realign();
}
/************************************************
************************************************/
void LxQtTaskBar::refreshButtonRotation()
{
bool autoRotate = mAutoRotate && (mButtonStyle != Qt::ToolButtonIconOnly);
ILxQtPanel::Position panelPosition = mPlugin->panel()->position();
QHashIterator<WId, LxQtTaskButton*> i(mButtonsHash);
while (i.hasNext())
{
i.next();
i.value()->setAutoRotation(autoRotate, panelPosition);
}
}
/************************************************
************************************************/
void LxQtTaskBar::refreshButtonVisibility()
{
bool haveVisibleWindow = false;
QHashIterator<WId, LxQtTaskButton*> i(mButtonsHash);
while (i.hasNext())
{
i.next();
bool isVisible = windowOnActiveDesktop(i.key());
haveVisibleWindow |= isVisible;
i.value()->setVisible(isVisible);
}
mPlaceHolder->setVisible(!haveVisibleWindow);
if (haveVisibleWindow)
mPlaceHolder->setFixedSize(0, 0);
else
{
mPlaceHolder->setMinimumSize(1, 1);
mPlaceHolder->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
}
}
/************************************************
************************************************/
void LxQtTaskBar::refreshIconGeometry()
{
// FIXME: sometimes we get wrong globalPos here, especially
// after changing the pos or size of the panel.
// this might be caused by bugs in lxqtpanel.cpp.
QHashIterator<WId, LxQtTaskButton*> i(mButtonsHash);
while (i.hasNext())
{
i.next();
LxQtTaskButton* button = i.value();
QRect rect = button->geometry();
QPoint globalPos = mapToGlobal(button->pos());
rect.moveTo(globalPos);
NETWinInfo info(QX11Info::connection(), button->windowId(),
(WId) QX11Info::appRootWindow(), NET::WMIconGeometry, 0);
NETRect nrect;
nrect.pos.x = rect.x();
nrect.pos.y = rect.y();
nrect.size.height = rect.height();
nrect.size.width = rect.width();
info.setIconGeometry(nrect);
}
}
/************************************************
************************************************/
void LxQtTaskBar::activeWindowChanged(WId window)
{
if (!window)
window = KWindowSystem::activeWindow();
LxQtTaskButton* btn = buttonByWindow(window);
if (mCheckedBtn != btn)
{
if (mCheckedBtn)
mCheckedBtn->setChecked(false);
if (btn)
{
btn->setChecked(true);
if (btn->hasUrgencyHint())
btn->setUrgencyHint(false);
}
mCheckedBtn = btn;
}
}
/************************************************
************************************************/
void LxQtTaskBar::windowChanged(WId window, NET::Properties prop, NET::Properties2 prop2)
{
LxQtTaskButton* button = buttonByWindow(window);
if (!button)
return;
// window changed virtual desktop
if (prop.testFlag(NET::WMDesktop))
{
if (mShowOnlyCurrentDesktopTasks)
{
int desktop = button->desktopNum();
button->setHidden(desktop != NET::OnAllDesktops && desktop != KWindowSystem::currentDesktop());
}
}
if (prop.testFlag(NET::WMVisibleName) || prop.testFlag(NET::WMName))
button->updateText();
// FIXME: NET::WMIconGeometry is causing high CPU and memory usage
if (prop.testFlag(NET::WMIcon) /*|| prop.testFlag(NET::WMIconGeometry)*/)
button->updateIcon();
if (prop.testFlag(NET::WMState))
button->setUrgencyHint(KWindowInfo(window, NET::WMState).hasState(NET::DemandsAttention));
}
/************************************************
************************************************/
void LxQtTaskBar::setButtonStyle(Qt::ToolButtonStyle buttonStyle)
{
mButtonStyle = buttonStyle;
QHashIterator<WId, LxQtTaskButton*> i(mButtonsHash);
while (i.hasNext())
{
i.next();
i.value()->setToolButtonStyle(mButtonStyle);
}
}
/************************************************
************************************************/
void LxQtTaskBar::settingsChanged()
{
mButtonWidth = mPlugin->settings()->value("buttonWidth", 400).toInt();
QString s = mPlugin->settings()->value("buttonStyle").toString().toUpper();
if (s == "ICON")
setButtonStyle(Qt::ToolButtonIconOnly);
else if (s == "TEXT")
setButtonStyle(Qt::ToolButtonTextOnly);
else
setButtonStyle(Qt::ToolButtonTextBesideIcon);
mShowOnlyCurrentDesktopTasks = mPlugin->settings()->value("showOnlyCurrentDesktopTasks", mShowOnlyCurrentDesktopTasks).toBool();
mAutoRotate = mPlugin->settings()->value("autoRotate", true).toBool();
mCloseOnMiddleClick = mPlugin->settings()->value("closeOnMiddleClick", true).toBool();
refreshTaskList();
}
/************************************************
************************************************/
void LxQtTaskBar::realign()
{
mLayout->setEnabled(false);
refreshButtonRotation();
ILxQtPanel *panel = mPlugin->panel();
QSize maxSize = QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
QSize minSize = QSize(0, 0);
bool rotated = false;
if (panel->isHorizontal())
{
if (mButtonStyle == Qt::ToolButtonIconOnly)
{
// Horizontal + Icons **************
mLayout->setRowCount(panel->lineCount());
mLayout->setColumnCount(0);
mLayout->setStretch(LxQt::GridLayout::StretchVertical);
minSize.rheight() = 0;
minSize.rwidth() = 0;
maxSize.rheight() = QWIDGETSIZE_MAX;
maxSize.rwidth() = mButtonWidth;
}
else
{
// Horizontal + Text ***************
mLayout->setRowCount(panel->lineCount());
mLayout->setColumnCount(0);
mLayout->setStretch(LxQt::GridLayout::StretchHorizontal | LxQt::GridLayout::StretchVertical);
minSize.rheight() = 0;
minSize.rwidth() = 0;
maxSize.rheight() = QWIDGETSIZE_MAX;
maxSize.rwidth() = mButtonWidth;
}
}
else
{
if (mButtonStyle == Qt::ToolButtonIconOnly)
{
// Vertical + Icons ****************
mLayout->setRowCount(0);
mLayout->setColumnCount(panel->lineCount());
mLayout->setStretch(LxQt::GridLayout::StretchHorizontal);
minSize.rheight() = 0;
minSize.rwidth() = 0;
maxSize.rheight() = QWIDGETSIZE_MAX;
maxSize.rwidth() = QWIDGETSIZE_MAX;
}
else
{
if (mAutoRotate)
{
switch (panel->position())
{
case ILxQtPanel::PositionLeft:
case ILxQtPanel::PositionRight:
rotated = true;
break;
default:;
}
}
// Vertical + Text *****************
if (rotated)
{
mLayout->setColumnCount(panel->lineCount());
mLayout->setRowCount(0);
mLayout->setStretch(LxQt::GridLayout::StretchHorizontal | LxQt::GridLayout::StretchVertical);
minSize.rheight() = 0;
minSize.rwidth() = 0;
maxSize.rheight() = mButtonWidth;
maxSize.rwidth() = QWIDGETSIZE_MAX;
}
else
{
mLayout->setColumnCount(1);
mLayout->setRowCount(0);
mLayout->setStretch(LxQt::GridLayout::StretchHorizontal);
minSize.rheight() = 0;
minSize.rwidth() = mButtonWidth;
maxSize.rheight() = QWIDGETSIZE_MAX;
maxSize.rwidth() = QWIDGETSIZE_MAX;
}
}
}
mLayout->setCellMinimumSize(minSize);
mLayout->setCellMaximumSize(maxSize);
mLayout->setDirection(rotated ? LxQt::GridLayout::TopToBottom : LxQt::GridLayout::LeftToRight);
mLayout->setEnabled(true);
refreshIconGeometry();
}
/************************************************
************************************************/
void LxQtTaskBar::mousePressEvent(QMouseEvent *event)
{
// close the app on mouse middle click
if (mCloseOnMiddleClick && event->button() == Qt::MidButton)
{
// find the button at current cursor pos
QHashIterator<WId, LxQtTaskButton*> i(mButtonsHash);
while (i.hasNext())
{
i.next();
LxQtTaskButton* btn = i.value();
if (btn->geometry().contains(event->pos()) &&
(!mShowOnlyCurrentDesktopTasks || KWindowSystem::currentDesktop() == KWindowInfo(i.key(), NET::WMDesktop).desktop()))
{
btn->closeApplication();
break;
}
}
}
QFrame::mousePressEvent(event);
}
/************************************************
************************************************/
void LxQtTaskBar::wheelEvent(QWheelEvent* event)
{
if (!mCheckedBtn)
return;
int current = mLayout->indexOf(mCheckedBtn);
if (current == -1)
return;
int delta = event->delta() < 0 ? 1 : -1;
for (int ix = current + delta; 0 <= ix && ix < mLayout->count(); ix += delta)
{
QLayoutItem *item = mLayout->itemAt(ix);
if (!item)
continue;
WId window = ((LxQtTaskButton *) item->widget())->windowId();
if (acceptWindow(window) && windowOnActiveDesktop(window))
{
KWindowSystem::activateWindow(window);
break;
}
}
}
/************************************************
************************************************/
void LxQtTaskBar::resizeEvent(QResizeEvent* event)
{
refreshIconGeometry();
return QWidget::resizeEvent(event);
}
/************************************************
************************************************/
void LxQtTaskBar::changeEvent(QEvent* event)
{
// if current style is changed, reset the base style of the proxy style
// so we can apply the new style correctly to task buttons.
if(event->type() == QEvent::StyleChange)
mStyle->setBaseStyle(NULL);
QFrame::changeEvent(event);
}