mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
455 lines
11 KiB
C++
455 lines
11 KiB
C++
![]() |
/*
|
||
|
* Copyright (C) 2009, 2010 Ivan Cukic <ivan.cukic(at)kde.org>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser/Library General Public License version 2,
|
||
|
* or (at your option) any later version, as published by the Free
|
||
|
* Software Foundation
|
||
|
*
|
||
|
* This program 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/Library General Public License for more details
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser/Library General Public
|
||
|
* License along with this program; if not, write to the
|
||
|
* Free Software Foundation, Inc.,
|
||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "PopupList.h"
|
||
|
#include "PopupList_p.h"
|
||
|
|
||
|
#include <QApplication>
|
||
|
#include <QDesktopWidget>
|
||
|
|
||
|
#include <Plasma/Theme>
|
||
|
|
||
|
#include <KMessageBox>
|
||
|
// #include <KWindowSystem>
|
||
|
|
||
|
#define MINIMUM_ITEM_HEIGHT 24
|
||
|
#define PREFERRED_ITEM_HEIGHT 32
|
||
|
#define MAXIMUM_ITEM_HEIGHT 32
|
||
|
#define ICON_SIZE QSize(24, 24)
|
||
|
#define MENU_WIDTH 100
|
||
|
#define POP_BORDER_OFFSET 16
|
||
|
#define POP_MINIMUM_OFFSET 64
|
||
|
|
||
|
namespace Lancelot {
|
||
|
|
||
|
PopupListMarginCache * PopupListMarginCache::m_instance = NULL;
|
||
|
|
||
|
PopupListMarginCache::PopupListMarginCache()
|
||
|
: m_width(-1), m_height(-1)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
PopupListMarginCache * PopupListMarginCache::self()
|
||
|
{
|
||
|
if (!m_instance) {
|
||
|
m_instance = new PopupListMarginCache();
|
||
|
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),
|
||
|
m_instance, SLOT(plasmaThemeChanged()));
|
||
|
}
|
||
|
return m_instance;
|
||
|
}
|
||
|
|
||
|
int PopupListMarginCache::width()
|
||
|
{
|
||
|
if (m_width == -1) {
|
||
|
updateSizes();
|
||
|
}
|
||
|
return m_width;
|
||
|
}
|
||
|
|
||
|
int PopupListMarginCache::height()
|
||
|
{
|
||
|
if (m_height == -1) {
|
||
|
updateSizes();
|
||
|
}
|
||
|
return m_height;
|
||
|
}
|
||
|
|
||
|
void PopupListMarginCache::plasmaThemeChanged()
|
||
|
{
|
||
|
m_width = m_height = -1;
|
||
|
}
|
||
|
|
||
|
void PopupListMarginCache::updateSizes()
|
||
|
{
|
||
|
Plasma::FrameSvg * bgsvg = new Plasma::FrameSvg(this);
|
||
|
bgsvg->setImagePath("dialogs/background");
|
||
|
|
||
|
m_width = bgsvg->marginSize(Plasma::LeftMargin)
|
||
|
+ bgsvg->marginSize(Plasma::RightMargin) + 4;
|
||
|
m_height = bgsvg->marginSize(Plasma::TopMargin)
|
||
|
+ bgsvg->marginSize(Plasma::BottomMargin) + 4;
|
||
|
}
|
||
|
|
||
|
PopupList::Private::Private(PopupList * parent)
|
||
|
: listModel(NULL),
|
||
|
treeModel(NULL),
|
||
|
openAction(PopupList::PopupNew),
|
||
|
closeTimeout(1000),
|
||
|
child(NULL),
|
||
|
parentList(NULL),
|
||
|
q(parent),
|
||
|
hovered(false)
|
||
|
{
|
||
|
scene = new QGraphicsScene();
|
||
|
list = new ActionListView();
|
||
|
|
||
|
scene->addItem(list);
|
||
|
parent->setGraphicsWidget(list);
|
||
|
parent->resize(MENU_WIDTH, 384);
|
||
|
|
||
|
list->setShowsExtendersOutside(false);
|
||
|
list->setGroupByName("PopupList");
|
||
|
list->setItemsGroup(Global::self()
|
||
|
->group("PopupList-Items"));
|
||
|
list->setCategoriesGroup(Global::self()
|
||
|
->group("PopupList-Categories"));
|
||
|
|
||
|
list->setItemHeight(MINIMUM_ITEM_HEIGHT, Qt::MinimumSize);
|
||
|
list->setItemHeight(PREFERRED_ITEM_HEIGHT, Qt::PreferredSize);
|
||
|
list->setItemHeight(MAXIMUM_ITEM_HEIGHT, Qt::MaximumSize);
|
||
|
|
||
|
list->setCategoryHeight(MINIMUM_ITEM_HEIGHT, Qt::MinimumSize);
|
||
|
list->setCategoryHeight(PREFERRED_ITEM_HEIGHT, Qt::PreferredSize);
|
||
|
list->setCategoryHeight(MAXIMUM_ITEM_HEIGHT, Qt::MaximumSize);
|
||
|
|
||
|
list->setItemIconSize(ICON_SIZE);
|
||
|
list->setCategoryIconSize(ICON_SIZE);
|
||
|
|
||
|
}
|
||
|
|
||
|
void PopupList::Private::connectSignals()
|
||
|
{
|
||
|
connect(list->list()->itemFactory(), SIGNAL(updated()),
|
||
|
q, SLOT(updateSize()));
|
||
|
connect(list->list()->itemFactory(), SIGNAL(itemInserted(int)),
|
||
|
q, SLOT(updateSize()));
|
||
|
connect(list->list()->itemFactory(), SIGNAL(itemDeleted(int)),
|
||
|
q, SLOT(updateSize()));
|
||
|
connect(list->list()->itemFactory(), SIGNAL(itemAltered(int)),
|
||
|
q, SLOT(updateSize()));
|
||
|
connect(list->list()->itemFactory(), SIGNAL(activated(int)),
|
||
|
this, SLOT(listItemActivated(int)));
|
||
|
}
|
||
|
|
||
|
PopupList::Private::~Private()
|
||
|
{
|
||
|
delete list;
|
||
|
delete scene;
|
||
|
}
|
||
|
|
||
|
void PopupList::Private::listItemActivated(int index)
|
||
|
{
|
||
|
if (treeModel && treeModel->isCategory(index)) {
|
||
|
switch (openAction) {
|
||
|
case OpenInside:
|
||
|
list->setModel(treeModel->child(index));
|
||
|
break;
|
||
|
case PopupNew:
|
||
|
if (!child) {
|
||
|
child = new PopupList(q);
|
||
|
}
|
||
|
child->setModel(treeModel->child(index));
|
||
|
child->exec(QCursor::pos(), q);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
// nothing
|
||
|
}
|
||
|
} else {
|
||
|
hidePopupAndParents();
|
||
|
}
|
||
|
|
||
|
emit q->activated(index);
|
||
|
}
|
||
|
|
||
|
void PopupList::Private::hidePopupAndParents()
|
||
|
{
|
||
|
PopupList * list = q;
|
||
|
|
||
|
while (list) {
|
||
|
list->hide();
|
||
|
list = list->parentList();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PopupList::PopupList(QWidget * parent, Qt::WindowFlags f)
|
||
|
: Plasma::Dialog(parent, f),
|
||
|
d(new Private(this))
|
||
|
{
|
||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||
|
d->list->setDisplayMode(ActionListView::SingleLineNameFirst);
|
||
|
|
||
|
d->animation = new QPropertyAnimation(this, "pos", this);
|
||
|
d->animation->setEasingCurve(QEasingCurve::InOutQuad);
|
||
|
d->animation->setDuration(250);
|
||
|
}
|
||
|
|
||
|
PopupList::~PopupList()
|
||
|
{
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
void PopupList::setModel(ActionListModel * model)
|
||
|
{
|
||
|
if (!model) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (d->child) {
|
||
|
d->child->hide();
|
||
|
}
|
||
|
|
||
|
d->treeModel = dynamic_cast < ActionTreeModel * > (model);
|
||
|
|
||
|
if (!d->treeModel) {
|
||
|
d->listModel = model;
|
||
|
d->list->setCategoriesGroup(Global::self()
|
||
|
->group("PopupList-CategoriesPass"));
|
||
|
} else {
|
||
|
d->listModel = NULL;
|
||
|
d->list->setCategoriesActivable(true);
|
||
|
d->list->setCategoriesGroup(Global::self()
|
||
|
->group("PopupList-CategoriesPass"));
|
||
|
}
|
||
|
|
||
|
d->list->setModel(model);
|
||
|
d->connectSignals();
|
||
|
}
|
||
|
|
||
|
void PopupList::showEvent(QShowEvent * event)
|
||
|
{
|
||
|
Plasma::Dialog::showEvent(event);
|
||
|
d->list->setFocus();
|
||
|
d->timer.stop();
|
||
|
}
|
||
|
|
||
|
// void PopupList::show()
|
||
|
// {
|
||
|
// Plasma::Dialog::show();
|
||
|
//
|
||
|
// KWindowSystem::forceActiveWindow(winId());
|
||
|
// }
|
||
|
|
||
|
void PopupList::hideEvent(QHideEvent * event)
|
||
|
{
|
||
|
Plasma::Dialog::hideEvent(event);
|
||
|
|
||
|
if (d->child) {
|
||
|
d->child->hide();
|
||
|
}
|
||
|
|
||
|
if (parentList()) {
|
||
|
if (!parentList()->d->hovered) {
|
||
|
parentList()->close();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PopupList::setCloseTimeout(int timeout)
|
||
|
{
|
||
|
d->closeTimeout = timeout;
|
||
|
}
|
||
|
|
||
|
int PopupList::closeTimeout() const
|
||
|
{
|
||
|
return d->closeTimeout;
|
||
|
}
|
||
|
|
||
|
PopupList::SublevelOpenAction PopupList::sublevelOpenAction() const
|
||
|
{
|
||
|
return d->openAction;
|
||
|
}
|
||
|
|
||
|
void PopupList::setSublevelOpenAction(SublevelOpenAction action)
|
||
|
{
|
||
|
d->openAction = action;
|
||
|
}
|
||
|
|
||
|
void PopupList::enterEvent(QEvent * event)
|
||
|
{
|
||
|
d->timer.stop();
|
||
|
Plasma::Dialog::enterEvent(event);
|
||
|
|
||
|
d->hovered = true;
|
||
|
}
|
||
|
|
||
|
void PopupList::leaveEvent(QEvent * event)
|
||
|
{
|
||
|
if (d->closeTimeout) {
|
||
|
d->timer.start(d->closeTimeout, this);
|
||
|
}
|
||
|
Plasma::Dialog::leaveEvent(event);
|
||
|
|
||
|
d->hovered = false;
|
||
|
}
|
||
|
|
||
|
void PopupList::timerEvent(QTimerEvent * event)
|
||
|
{
|
||
|
if (d->timer.timerId() == event->timerId()) {
|
||
|
if (!d->child || d->child->isHidden()) {
|
||
|
close();
|
||
|
|
||
|
d->timer.stop();
|
||
|
}
|
||
|
}
|
||
|
Plasma::Dialog::timerEvent(event);
|
||
|
}
|
||
|
|
||
|
void PopupList::updateSize()
|
||
|
{
|
||
|
qreal width = d->list->preferredWidth();
|
||
|
if (width < MENU_WIDTH) {
|
||
|
width = MENU_WIDTH;
|
||
|
}
|
||
|
|
||
|
qreal height =
|
||
|
(d->list->list()->itemFactory()->itemCount()) * PREFERRED_ITEM_HEIGHT;
|
||
|
d->list->resize(width, height);
|
||
|
|
||
|
resize(width + PopupListMarginCache::self()->width(),
|
||
|
height + PopupListMarginCache::self()->height());
|
||
|
}
|
||
|
|
||
|
void PopupList::Private::prepareToShow()
|
||
|
{
|
||
|
q->updateSize();
|
||
|
timer.stop();
|
||
|
}
|
||
|
|
||
|
void PopupList::exec(const QPoint & p)
|
||
|
{
|
||
|
d->prepareToShow();
|
||
|
|
||
|
d->parentList = NULL;
|
||
|
|
||
|
QRect g = geometry();
|
||
|
g.moveTopLeft(p);
|
||
|
|
||
|
QRect screen = QApplication::desktop()->screenGeometry(
|
||
|
QApplication::desktop()->screenNumber(p)
|
||
|
);
|
||
|
|
||
|
if (g.right() > screen.right()) {
|
||
|
g.moveRight(screen.right());
|
||
|
} else if (g.left() < screen.left()) {
|
||
|
g.moveLeft(screen.left());
|
||
|
}
|
||
|
|
||
|
if (g.bottom() > screen.bottom()) {
|
||
|
g.moveBottom(screen.bottom());
|
||
|
} else if (g.top() < screen.top()) {
|
||
|
g.moveTop(screen.top());
|
||
|
}
|
||
|
|
||
|
moveTo(g.topLeft());
|
||
|
|
||
|
show();
|
||
|
}
|
||
|
|
||
|
void PopupList::exec(const QPoint & p, PopupList * parent)
|
||
|
{
|
||
|
d->prepareToShow();
|
||
|
|
||
|
d->parentList = parent;
|
||
|
|
||
|
QRect selfGeometry = geometry();
|
||
|
selfGeometry.moveTopLeft(p);
|
||
|
|
||
|
QRect screenGeometry = QApplication::desktop()->screenGeometry(
|
||
|
QApplication::desktop()->screenNumber(p)
|
||
|
);
|
||
|
|
||
|
QRect rootParentGeometry;
|
||
|
int numberOfParentLists = 0;
|
||
|
|
||
|
PopupList * list = this;
|
||
|
|
||
|
while (list->parentList()) {
|
||
|
list = list->parentList();
|
||
|
numberOfParentLists++;
|
||
|
}
|
||
|
|
||
|
rootParentGeometry = list->geometry();
|
||
|
|
||
|
// Moving...
|
||
|
selfGeometry.moveLeft(rootParentGeometry.right() - POP_BORDER_OFFSET);
|
||
|
if (selfGeometry.right() > screenGeometry.right()) {
|
||
|
selfGeometry.moveRight(screenGeometry.right());
|
||
|
|
||
|
if (selfGeometry.left() - rootParentGeometry.left() <
|
||
|
numberOfParentLists * POP_MINIMUM_OFFSET) {
|
||
|
rootParentGeometry.moveLeft(selfGeometry.left()
|
||
|
- numberOfParentLists * POP_MINIMUM_OFFSET);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (selfGeometry.bottom() > screenGeometry.bottom()) {
|
||
|
selfGeometry.moveBottom(screenGeometry.bottom());
|
||
|
} else if (selfGeometry.top() < screenGeometry.top()) {
|
||
|
selfGeometry.moveTop(screenGeometry.top());
|
||
|
}
|
||
|
|
||
|
moveTo(selfGeometry.topLeft());
|
||
|
|
||
|
list = this;
|
||
|
|
||
|
int shift = (selfGeometry.left() - rootParentGeometry.left())
|
||
|
/ numberOfParentLists;
|
||
|
int left = selfGeometry.left();
|
||
|
|
||
|
while (list->parentList()) {
|
||
|
list = list->parentList();
|
||
|
|
||
|
QPoint parentPosition = list->pos();
|
||
|
|
||
|
left -= shift;
|
||
|
parentPosition.setX(left);
|
||
|
list->moveTo(parentPosition);
|
||
|
|
||
|
}
|
||
|
|
||
|
show();
|
||
|
}
|
||
|
|
||
|
void PopupList::moveTo(const QPoint & to)
|
||
|
{
|
||
|
if (!isVisible() ||
|
||
|
!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
|
||
|
move(to);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (d->animation->state() == QAbstractAnimation::Running) {
|
||
|
d->animation->stop();
|
||
|
}
|
||
|
|
||
|
d->animation->setEndValue(to);
|
||
|
d->animation->start();
|
||
|
}
|
||
|
|
||
|
bool PopupList::eventFilter(QObject * object, QEvent * event)
|
||
|
{
|
||
|
if (event->type() == QEvent::KeyPress) {
|
||
|
QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
|
||
|
if (keyEvent->key() == Qt::Key_Escape) {
|
||
|
close();
|
||
|
}
|
||
|
}
|
||
|
return Plasma::Dialog::eventFilter(object, event);
|
||
|
}
|
||
|
|
||
|
PopupList * PopupList::parentList() const
|
||
|
{
|
||
|
return d->parentList;
|
||
|
}
|
||
|
|
||
|
} // namespace Lancelot
|
||
|
|