/* 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 * Maciej PÅ‚aza * Kuzma Shapran * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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 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 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 tmp = KWindowSystem::stackingOrder(); QMutableHashIterator 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 i(mButtonsHash); while (i.hasNext()) { i.next(); i.value()->setAutoRotation(autoRotate, panelPosition); } } /************************************************ ************************************************/ void LxQtTaskBar::refreshButtonVisibility() { bool haveVisibleWindow = false; QHashIterator 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 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 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 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); }