/* * Copyright 2007 by Alex Merry * Copyright 2008 by Alexis Ménard * Copyright 2008 by Aaron Seigo * Copyright 2009 by Marco Martin * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(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(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 > orderedApplets; QList 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 >::const_iterator it = orderedApplets.constBegin(); while (it != orderedApplets.constEnd()) { QGraphicsLinearLayout *lay = dynamic_cast(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(m_container->itemAt(column)); for (int row = 0; row < lay->count(); ++row) { const Applet *applet = dynamic_cast(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(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(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(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) #include "moc_newspaper.cpp"