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.
299 lines
9.5 KiB
299 lines
9.5 KiB
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
|
* (c)LGPL2+
|
|
*
|
|
* LXDE-Qt - a lightweight, Qt based, desktop toolset
|
|
* http://razor-qt.org
|
|
*
|
|
* Copyright: 2011 Razor team
|
|
* Authors:
|
|
* Petr Vanek <petr@scribus.info>
|
|
*
|
|
* 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 <QButtonGroup>
|
|
#include <QWheelEvent>
|
|
#include <QtDebug>
|
|
#include <QSignalMapper>
|
|
#include <QTimer>
|
|
#include <lxqt-globalkeys.h>
|
|
#include <LXQt/GridLayout>
|
|
#include <KWindowSystem/KWindowSystem>
|
|
#include <QX11Info>
|
|
#include <cmath>
|
|
|
|
#include "desktopswitch.h"
|
|
#include "desktopswitchbutton.h"
|
|
#include "desktopswitchconfiguration.h"
|
|
|
|
static const QString DEFAULT_SHORTCUT_TEMPLATE("Control+F%1");
|
|
|
|
DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) :
|
|
QObject(),
|
|
ILXQtPanelPlugin(startupInfo),
|
|
m_pSignalMapper(new QSignalMapper(this)),
|
|
m_desktopCount(KWindowSystem::numberOfDesktops()),
|
|
mRows(-1),
|
|
mDesktops(new NETRootInfo(QX11Info::connection(), NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopNames, NET::WM2DesktopLayout)),
|
|
mLabelType(static_cast<DesktopSwitchButton::LabelType>(-1))
|
|
{
|
|
m_buttons = new QButtonGroup(this);
|
|
connect (m_pSignalMapper, SIGNAL(mapped(int)), this, SLOT(setDesktop(int)));
|
|
|
|
mLayout = new LXQt::GridLayout(&mWidget);
|
|
mWidget.setLayout(mLayout);
|
|
|
|
settingsChanged();
|
|
|
|
onCurrentDesktopChanged(KWindowSystem::currentDesktop());
|
|
QTimer::singleShot(0, this, SLOT(registerShortcuts()));
|
|
|
|
connect(m_buttons, SIGNAL(buttonClicked(int)), this, SLOT(setDesktop(int)));
|
|
|
|
connect(KWindowSystem::self(), SIGNAL(numberOfDesktopsChanged(int)), SLOT(onNumberOfDesktopsChanged(int)));
|
|
connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), SLOT(onCurrentDesktopChanged(int)));
|
|
connect(KWindowSystem::self(), SIGNAL(desktopNamesChanged()), SLOT(onDesktopNamesChanged()));
|
|
|
|
connect(KWindowSystem::self(), static_cast<void (KWindowSystem::*)(WId, NET::Properties, NET::Properties2)>(&KWindowSystem::windowChanged),
|
|
this, &DesktopSwitch::onWindowChanged);
|
|
}
|
|
|
|
void DesktopSwitch::registerShortcuts()
|
|
{
|
|
// Register shortcuts to change desktop
|
|
GlobalKeyShortcut::Action * gshortcut;
|
|
QString path;
|
|
QString description;
|
|
for (int i = 0; i < 12; ++i)
|
|
{
|
|
path = QString("/panel/%1/desktop_%2").arg(settings()->group()).arg(i + 1);
|
|
description = tr("Switch to desktop %1").arg(i + 1);
|
|
|
|
gshortcut = GlobalKeyShortcut::Client::instance()->addAction(QString(), path, description, this);
|
|
if (nullptr != gshortcut)
|
|
{
|
|
m_keys << gshortcut;
|
|
connect(gshortcut, &GlobalKeyShortcut::Action::registrationFinished, this, &DesktopSwitch::shortcutRegistered);
|
|
connect(gshortcut, SIGNAL(activated()), m_pSignalMapper, SLOT(map()));
|
|
m_pSignalMapper->setMapping(gshortcut, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DesktopSwitch::shortcutRegistered()
|
|
{
|
|
GlobalKeyShortcut::Action * const shortcut = qobject_cast<GlobalKeyShortcut::Action*>(sender());
|
|
|
|
disconnect(shortcut, &GlobalKeyShortcut::Action::registrationFinished, this, &DesktopSwitch::shortcutRegistered);
|
|
|
|
const int i = m_keys.indexOf(shortcut);
|
|
Q_ASSERT(-1 != i);
|
|
|
|
if (shortcut->shortcut().isEmpty())
|
|
{
|
|
shortcut->changeShortcut(DEFAULT_SHORTCUT_TEMPLATE.arg(i + 1));
|
|
}
|
|
}
|
|
|
|
void DesktopSwitch::onWindowChanged(WId id, NET::Properties properties, NET::Properties2 properties2)
|
|
{
|
|
if (properties.testFlag(NET::WMState) && isWindowHighlightable(id))
|
|
{
|
|
KWindowInfo info = KWindowInfo(id, NET::WMDesktop | NET::WMState);
|
|
int desktop = info.desktop();
|
|
if (!info.valid() || info.onAllDesktops())
|
|
return;
|
|
else
|
|
{
|
|
DesktopSwitchButton *button = static_cast<DesktopSwitchButton *>(m_buttons->button(desktop - 1));
|
|
if(button)
|
|
button->setUrgencyHint(id, info.hasState(NET::DemandsAttention));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DesktopSwitch::refresh()
|
|
{
|
|
QList<QAbstractButton*> btns = m_buttons->buttons();
|
|
|
|
int i = 0;
|
|
const int current_cnt = btns.count();
|
|
const int border = qMin(btns.count(), m_desktopCount);
|
|
//update existing buttons
|
|
for ( ; i < border; ++i)
|
|
{
|
|
((DesktopSwitchButton*)m_buttons->button(i))->update(i, mLabelType,
|
|
KWindowSystem::desktopName(i + 1).isEmpty() ?
|
|
tr("Desktop %1").arg(i + 1) :
|
|
KWindowSystem::desktopName(i + 1));
|
|
}
|
|
|
|
//create new buttons (if neccessary)
|
|
QAbstractButton *b;
|
|
for ( ; i < m_desktopCount; ++i)
|
|
{
|
|
b = new DesktopSwitchButton(&mWidget, i, mLabelType,
|
|
KWindowSystem::desktopName(i+1).isEmpty() ?
|
|
tr("Desktop %1").arg(i+1) :
|
|
KWindowSystem::desktopName(i+1));
|
|
mWidget.layout()->addWidget(b);
|
|
m_buttons->addButton(b, i);
|
|
}
|
|
|
|
//delete unneeded buttons (if neccessary)
|
|
for ( ; i < current_cnt; ++i)
|
|
{
|
|
b = m_buttons->buttons().last();
|
|
m_buttons->removeButton(b);
|
|
mWidget.layout()->removeWidget(b);
|
|
delete b;
|
|
}
|
|
}
|
|
|
|
bool DesktopSwitch::isWindowHighlightable(WId window)
|
|
{
|
|
// this method was borrowed from the taskbar plugin
|
|
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);
|
|
}
|
|
|
|
DesktopSwitch::~DesktopSwitch()
|
|
{
|
|
}
|
|
|
|
void DesktopSwitch::setDesktop(int desktop)
|
|
{
|
|
KWindowSystem::setCurrentDesktop(desktop + 1);
|
|
}
|
|
|
|
void DesktopSwitch::onNumberOfDesktopsChanged(int count)
|
|
{
|
|
qDebug() << "Desktop count changed from" << m_desktopCount << "to" << count;
|
|
m_desktopCount = count;
|
|
refresh();
|
|
}
|
|
|
|
void DesktopSwitch::onCurrentDesktopChanged(int current)
|
|
{
|
|
QAbstractButton *button = m_buttons->button(current - 1);
|
|
if (button)
|
|
button->setChecked(true);
|
|
}
|
|
|
|
void DesktopSwitch::onDesktopNamesChanged()
|
|
{
|
|
refresh();
|
|
}
|
|
|
|
void DesktopSwitch::settingsChanged()
|
|
{
|
|
int value = settings()->value("rows", 1).toInt();
|
|
if (mRows != value)
|
|
{
|
|
mRows = value;
|
|
realign();
|
|
}
|
|
value = settings()->value("labelType", DesktopSwitchButton::LABEL_TYPE_NUMBER).toInt();
|
|
if (mLabelType != static_cast<DesktopSwitchButton::LabelType>(value))
|
|
{
|
|
mLabelType = static_cast<DesktopSwitchButton::LabelType>(value);
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
void DesktopSwitch::realign()
|
|
{
|
|
int columns = static_cast<int>(ceil(static_cast<float>(m_desktopCount) / mRows));
|
|
mLayout->setEnabled(false);
|
|
if (panel()->isHorizontal())
|
|
{
|
|
mLayout->setRowCount(mRows);
|
|
mLayout->setColumnCount(0);
|
|
mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft);
|
|
}
|
|
else
|
|
{
|
|
mLayout->setColumnCount(mRows);
|
|
mLayout->setRowCount(0);
|
|
mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft);
|
|
}
|
|
mLayout->setEnabled(true);
|
|
}
|
|
|
|
QDialog *DesktopSwitch::configureDialog()
|
|
{
|
|
return new DesktopSwitchConfiguration(settings());
|
|
}
|
|
|
|
DesktopSwitchWidget::DesktopSwitchWidget():
|
|
QFrame(),
|
|
m_mouseWheelThresholdCounter(0)
|
|
{
|
|
}
|
|
|
|
void DesktopSwitchWidget::wheelEvent(QWheelEvent *e)
|
|
{
|
|
// Without some sort of threshold which has to be passed, scrolling is too sensitive
|
|
m_mouseWheelThresholdCounter -= e->delta();
|
|
// If the user hasn't scrolled far enough in one direction (positive or negative): do nothing
|
|
if(abs(m_mouseWheelThresholdCounter) < 100)
|
|
return;
|
|
|
|
int max = KWindowSystem::numberOfDesktops();
|
|
int delta = e->delta() < 0 ? 1 : -1;
|
|
int current = KWindowSystem::currentDesktop() + delta;
|
|
|
|
if (current > max){
|
|
current = 1;
|
|
}
|
|
else if (current < 1)
|
|
current = max;
|
|
|
|
m_mouseWheelThresholdCounter = 0;
|
|
KWindowSystem::setCurrentDesktop(current);
|
|
}
|