kde-workspace/plasma/containments/newspaper/newspaper.cpp

593 lines
18 KiB
C++
Raw Normal View History

2014-11-15 03:32:08 +02:00
/*
* Copyright 2007 by Alex Merry <alex.merry@kdemail.net>
* Copyright 2008 by Alexis Ménard <darktears31@gmail.com>
* Copyright 2008 by Aaron Seigo <aseigo@kde.org>
* Copyright 2009 by Marco Martin <notmart@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2,
* or (at your option) any later version.
*
* 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 General Public License for more details
*
* You should have received a copy of the GNU 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 "newspaper.h"
#include "appletoverlay.h"
#include "applettitlebar.h"
#include "appletscontainer.h"
#include "appletsview.h"
#include <limits>
#include <QAction>
#include <QGraphicsLinearLayout>
#include <QTimer>
#include <QtGui/qgraphicssceneevent.h>
2014-11-15 03:32:08 +02:00
#include <KDebug>
#include <KIcon>
#include <Plasma/AbstractToolBox>
#include <Plasma/Corona>
#include <Plasma/FrameSvg>
#include <Plasma/Theme>
#include <Plasma/View>
#include <Plasma/PopupApplet>
#include <Plasma/Frame>
#include <Plasma/ToolButton>
using namespace Plasma;
Newspaper::Newspaper(QObject *parent, const QVariantList &args)
: Containment(parent, args),
m_orientation(Qt::Vertical),
m_expandAll(false),
m_appletOverlay(0)
{
setContainmentType(Containment::CustomContainment);
m_scrollWidget = new AppletsView(this);
m_container = new AppletsContainer(m_scrollWidget);
connect(this, SIGNAL(appletRemoved(Plasma::Applet*)),
this, SLOT(refreshLayout()));
connect (m_scrollWidget, SIGNAL(dropRequested(QGraphicsSceneDragDropEvent *)),
this, SLOT(viewRequestedDrop(QGraphicsSceneDragDropEvent *)));
connect(this, SIGNAL(toolBoxVisibilityChanged(bool)), this, SLOT(updateConfigurationMode(bool)));
}
Newspaper::~Newspaper()
{
delete m_appletOverlay;
config().writeEntry("orientation", (int)m_orientation);
}
void Newspaper::init()
{
m_externalLayout = new QGraphicsLinearLayout(this);
m_externalLayout->setContentsMargins(0, 0, 0, 0);
m_externalLayout->addItem(m_scrollWidget);
m_scrollWidget->setAppletsContainer(m_container);
connect(m_container, SIGNAL(appletActivated(Plasma::Applet *)), this, SLOT(appletActivated(Plasma::Applet *)));
m_updateSizeTimer = new QTimer(this);
m_updateSizeTimer->setSingleShot(true);
connect(m_updateSizeTimer, SIGNAL(timeout()), m_container, SLOT(updateSize()));
m_relayoutTimer = new QTimer(this);
m_relayoutTimer->setSingleShot(true);
connect(m_relayoutTimer, SIGNAL(timeout()), m_container, SLOT(updateSize()));
connect(m_relayoutTimer, SIGNAL(timeout()), m_container, SLOT(cleanupColumns()));
connect(m_container, SIGNAL(appletSizeHintChanged()), this, SLOT(appletSizeHintChanged()));
configChanged();
m_container->addColumn();
setOrientation(m_orientation);
Plasma::Svg *borderSvg = new Plasma::Svg(this);
borderSvg->setImagePath("newspaper/border");
Containment::init();
setHasConfigurationInterface(true);
setToolBox(Plasma::AbstractToolBox::load(corona()->preferredToolBoxPlugin(Plasma::Containment::DesktopContainment), QVariantList(), this));
QAction *a = action("add widgets");
if (a) {
addToolBoxAction(a);
}
if (toolBox()) {
connect(toolBox(), SIGNAL(toggled()), this, SIGNAL(toolBoxToggled()));
connect(toolBox(), SIGNAL(visibilityChanged(bool)), this, SIGNAL(toolBoxVisibilityChanged(bool)));
toolBox()->show();
}
a = new QAction(KIcon("view-fullscreen"), i18n("Expand widgets"), this);
addAction("expand widgets", a);
addToolBoxAction(a);
connect(a, SIGNAL(triggered()), this, SLOT(toggleExpandAllApplets()));
if (m_expandAll) {
a->setIcon(KIcon("view-restore"));
a->setText(i18n("Collapse widgets"));
}
a->setEnabled(m_orientation == Qt::Vertical);
a->setVisible(m_orientation == Qt::Vertical);
a = action("configure");
if (a) {
a->setText(i18n("Configure page"));
addToolBoxAction(a);
}
QAction *lockAction = 0;
if (corona()) {
lockAction = corona()->action("lock widgets");
}
if (!lockAction || !lockAction->isEnabled()) {
lockAction = new QAction(this);
addAction("lock page", lockAction);
lockAction->setText(i18n("Lock page"));
lockAction->setIcon(KIcon("object-locked"));
QObject::connect(lockAction, SIGNAL(triggered(bool)), this, SLOT(toggleImmutability()));
}
addToolBoxAction(lockAction);
//FIXME: two different use cases for the desktop and the newspaper, another reason to move the toolbox management out of here
QAction *activityAction = 0;
if (corona()) {
activityAction = corona()->action("manage activities");
}
if (activityAction) {
addToolBoxAction(activityAction);
} else {
a = action("remove");
if (a) {
a->setText(i18n("Remove page"));
addToolBoxAction(a);
}
}
a = new QAction(i18n("Next activity"), this);
addAction("next containment", a);
a = new QAction(i18n("Previous activity"), this);
addAction("previous containment", a);
if (corona()) {
connect(corona(), SIGNAL(availableScreenRegionChanged()), this, SLOT(availableScreenRegionChanged()));
availableScreenRegionChanged();
}
}
void Newspaper::configChanged()
{
m_orientation = (Qt::Orientation)config().readEntry("orientation", (int)Qt::Vertical);
m_container->setOrientation(m_orientation);
m_expandAll = config().readEntry("ExpandAllApplets", false);
m_container->setExpandAll(m_expandAll);
}
void Newspaper::updateSize()
{
m_container->updateSize();
}
void Newspaper::toggleImmutability()
{
if (immutability() == Plasma::UserImmutable) {
setImmutability(Plasma::Mutable);
} else if (immutability() == Plasma::Mutable) {
setImmutability(Plasma::UserImmutable);
}
}
void Newspaper::toggleExpandAllApplets()
{
m_expandAll = !m_expandAll;
QAction *a = action("expand widgets");
if (a) {
if (m_expandAll) {
a->setIcon(KIcon("view-restore"));
a->setText(i18n("Collapse widgets"));
} else {
a->setIcon(KIcon("view-fullscreen"));
a->setText(i18n("Expand widgets"));
}
}
m_container->setExpandAll(m_expandAll);
config().writeEntry("ExpandAllApplets", m_expandAll);
}
void Newspaper::refreshLayout()
{
if (!m_relayoutTimer->isActive()) {
m_relayoutTimer->start(200);
}
}
void Newspaper::setOrientation(Qt::Orientation orientation)
{
m_orientation = orientation;
m_container->setOrientation(m_orientation);
m_externalLayout->setOrientation(m_orientation);
QAction *expandAction = action("expand widgets");
if (expandAction) {
expandAction->setEnabled(orientation == Qt::Vertical);
expandAction->setVisible(orientation == Qt::Vertical);
}
if (m_orientation == Qt::Vertical) {
m_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
} else {
m_container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
}
for (int i = 0; i < m_container->count(); ++i) {
QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(m_container->itemAt(i));
if (!lay) {
continue;
}
lay->setOrientation(orientation);
}
}
Plasma::Applet *Newspaper::addApplet(const QString &appletName, const int row, const int column)
{
m_container->setAutomaticAppletLayout(false);
Plasma::Applet *applet = Containment::addApplet(appletName);
m_container->addApplet(applet, row, column);
m_container->setAutomaticAppletLayout(true);
return applet;
}
Plasma::Applet *Newspaper::addApplet(Plasma::Applet *applet, const int row, const int column)
{
m_container->setAutomaticAppletLayout(false);
Containment::addApplet(applet);
m_container->addApplet(applet, row, column);
m_container->setAutomaticAppletLayout(true);
return applet;
}
Qt::Orientation Newspaper::orientation() const
{
return m_orientation;
}
void Newspaper::appletSizeHintChanged()
{
if (m_updateSizeTimer) {
m_updateSizeTimer->start(200);
}
}
void Newspaper::constraintsEvent(Plasma::Constraints constraints)
{
kDebug() << "constraints updated with" << constraints << "!!!!!!";
if (constraints & Plasma::StartupCompletedConstraint) {
connect(this, SIGNAL(appletAdded(Plasma::Applet*,QPointF)),
m_container, SLOT(layoutApplet(Plasma::Applet*,QPointF)));
Plasma::Corona *c = corona();
if (c) {
connect(c, SIGNAL(containmentAdded(Plasma::Containment *)),
this, SLOT(containmentAdded(Plasma::Containment *)));
foreach (Plasma::Containment *containment, corona()->containments()) {
Newspaper *news = qobject_cast<Newspaper *>(containment);
if (news) {
connect(news, SIGNAL(destroyed(QObject *)),
this, SLOT(containmentRemoved(QObject *)));
}
}
QTimer::singleShot(100, this, SLOT(updateRemoveActionVisibility()));
}
}
if (constraints & Plasma::SizeConstraint && m_appletOverlay) {
m_appletOverlay->resize(size());
}
if (constraints & Plasma::SizeConstraint) {
availableScreenRegionChanged();
m_container->syncColumnSizes();
}
if (constraints & Plasma::ImmutableConstraint) {
QAction *a = action("lock page");
if (a) {
switch (immutability()) {
case Plasma::SystemImmutable:
a->setEnabled(false);
a->setVisible(false);
break;
case Plasma::UserImmutable:
a->setText(i18n("Unlock Page"));
a->setIcon(KIcon("object-unlocked"));
a->setEnabled(true);
a->setVisible(true);
break;
case Plasma::Mutable:
a->setText(i18n("Lock Page"));
a->setIcon(KIcon("object-locked"));
a->setEnabled(true);
a->setVisible(true);
break;
}
}
a = action("add page");
if (a) {
if (immutability() == Plasma::Mutable) {
a->setEnabled(true);
a->setVisible(true);
} else {
a->setEnabled(false);
a->setVisible(false);
}
}
if (immutability() == Plasma::Mutable && !m_appletOverlay && toolBox() && toolBox()->isShowing()) {
m_appletOverlay = new AppletOverlay(this);
m_appletOverlay->resize(size());
m_scrollWidget->setImmediateDrag(true);
} else if (immutability() != Plasma::Mutable && m_appletOverlay && toolBox() && toolBox()->isShowing()) {
m_appletOverlay->deleteLater();
m_appletOverlay = 0;
m_scrollWidget->setImmediateDrag(false);
}
updateRemoveActionVisibility();
}
}
void Newspaper::updateConfigurationMode(bool config)
{
if (config && !m_appletOverlay && immutability() == Plasma::Mutable) {
m_appletOverlay = new AppletOverlay(this);
m_appletOverlay->resize(size());
m_scrollWidget->setImmediateDrag(true);
} else if (!config) {
delete m_appletOverlay;
m_appletOverlay = 0;
m_scrollWidget->setImmediateDrag(false);
}
if (!config) {
m_container->cleanupColumns();
}
}
void Newspaper::changeEvent(QEvent *event)
{
if (event->type() == QEvent::ContentsRectChange) {
if (toolBox() && toolBox()->isShowing()) {
updateConfigurationMode(true);
}
}
}
void Newspaper::restore(KConfigGroup &group)
{
Containment::restore(group);
KConfigGroup appletsConfig(&group, "Applets");
//the number of items in orderedApplets is the number of columns
QMap<int, QMap<int, Applet *> > orderedApplets;
QList<Applet *> unorderedApplets;
foreach (Applet *applet, applets()) {
KConfigGroup appletConfig(&appletsConfig, QString::number(applet->id()));
KConfigGroup layoutConfig(&appletConfig, "LayoutInformation");
int column = layoutConfig.readEntry("Column", -1);
int order = layoutConfig.readEntry("Order", -1);
if (order > -1) {
if (column > -1) {
orderedApplets[column][order] = applet;
} else {
unorderedApplets.append(applet);
}
//if LayoutInformation is not available use the usual way, as a bonus makes it retrocompatible with oler configs
} else {
unorderedApplets.append(applet);
}
connect(applet, SIGNAL(sizeHintChanged(Qt::SizeHint)), this, SLOT(appletSizeHintChanged()));
}
//if the required columns does not exist, create them
if (m_container->count() < orderedApplets.count()) {
int columnsToAdd = orderedApplets.count()-m_container->count();
for (int i = 0; i < columnsToAdd; ++i) {
m_container->addColumn();
}
}
//finally add all the applets that had a layout information
int column = 0;
QMap<int, QMap<int, Applet *> >::const_iterator it = orderedApplets.constBegin();
while (it != orderedApplets.constEnd()) {
QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(m_container->itemAt(column));
++column;
//this should never happen
if (!lay) {
++it;
continue;
}
foreach (Applet *applet, it.value()) {
lay->insertItem(lay->count()-1, applet);
m_container->createAppletTitle(applet);
}
++it;
}
//add all the remaining applets
foreach (Applet *applet, unorderedApplets) {
m_container->layoutApplet(applet, applet->pos());
}
m_container->updateSize();
}
void Newspaper::saveContents(KConfigGroup &group) const
{
Containment::saveContents(group);
KConfigGroup appletsConfig(&group, "Applets");
for (int column = 0; column < m_container->count(); ++column) {
QGraphicsLinearLayout *lay = static_cast<QGraphicsLinearLayout *>(m_container->itemAt(column));
for (int row = 0; row < lay->count(); ++row) {
const Applet *applet = dynamic_cast<Applet *>(lay->itemAt(row));
if (applet) {
KConfigGroup appletConfig(&appletsConfig, QString::number(applet->id()));
appletConfig.deleteEntry("geometry");
appletConfig.deleteEntry("zvalue");
appletConfig.deleteEntry("transform");
KConfigGroup layoutConfig(&appletConfig, "LayoutInformation");
layoutConfig.writeEntry("Column", column);
layoutConfig.writeEntry("Order", row);
}
}
}
}
void Newspaper::updateRemoveActionVisibility()
{
int newspapers = 0;
foreach (Plasma::Containment *containment, corona()->containments()) {
if (qobject_cast<Newspaper *>(containment)) {
++newspapers;
}
}
QAction *a = action("remove");
if (a) {
a->setEnabled(newspapers > 1);
a->setVisible(newspapers > 1);
}
}
void Newspaper::containmentAdded(Plasma::Containment *containment)
{
//we now are sure there are at least two pages
Newspaper *news = qobject_cast<Newspaper *>(containment);
if (news) {
connect(news, SIGNAL(destroyed(QObject *)), this, SLOT(containmentRemoved(QObject *)));
QAction *a = action("remove");
if (a) {
a->setEnabled(true);
a->setVisible(true);
}
}
}
void Newspaper::availableScreenRegionChanged()
{
if (!corona()) {
return;
}
QRect maxRect;
int maxArea = 0;
//we don't want the bounding rect (that could include panels too), but the maximumone representing the desktop
foreach (const QRect& rect, corona()->availableScreenRegion(screen()).rects()) {
int area = rect.width() * rect.height();
if (area > maxArea) {
maxRect = rect;
maxArea = area;
}
}
QGraphicsView *ownView = view();
//FIXME: the second check is a workaround to a qt bug: when a qwidget has just been created, maptoglobal and mapfromglobal aren't symmetryc. remove as soon as the bug is fixed
if (ownView && (ownView->mapFromGlobal(QPoint(0,0)) == -ownView->mapToGlobal(QPoint(0,0)))) {
maxRect.moveTopLeft(ownView->mapFromGlobal(maxRect.topLeft()));
}
maxRect.moveTopLeft(QPoint(qMax(0, maxRect.left()), qMax(0, maxRect.top())));
setContentsMargins(maxRect.left(), maxRect.top(), qMax((qreal)0.0, size().width() - maxRect.right()), qMax((qreal)0.0, size().height() - maxRect.bottom()));
}
void Newspaper::containmentRemoved(QObject *containment)
{
if (!corona()) {
return;
}
if (qobject_cast<Newspaper *>(containment)) {
updateRemoveActionVisibility();
}
}
void Newspaper::viewRequestedDrop(QGraphicsSceneDragDropEvent *event)
{
event->setPos(mapFromScene(event->scenePos()));
dropEvent(event);
}
//They all have to be reimplemented in order to accept them
void Newspaper::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
Containment::dragEnterEvent(event);
}
void Newspaper::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
Containment::dragLeaveEvent(event);
}
void Newspaper::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
Containment::dragMoveEvent(event);
event->accept();
}
void Newspaper::dropEvent(QGraphicsSceneDragDropEvent *event)
{
Containment::dropEvent(event);
event->accept();
}
K_EXPORT_PLASMA_APPLET(newspaper, Newspaper)
2015-02-27 09:28:46 +00:00
#include "moc_newspaper.cpp"
2014-11-15 03:32:08 +02:00