kde-workspace/plasma/containments/newspaper/appletscontainer.cpp
Ivailo Monev 08bd97aff5 generic: compiler warning fixes
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2021-06-13 17:59:56 +03:00

674 lines
21 KiB
C++

////////////////////////////////////////////////////////////////////////
// appletscontainer.cpp //
// //
// 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> //
// Copyright 2010 by Igor Oliveira <igor.oliveira@>openbossa.org> //
// //
// This 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 //
/////////////////////////////////////////////////////////////////////////
#include "appletscontainer.h"
#include "applettitlebar.h"
#include "appletsview.h"
#include <QFont>
#include <QFontMetrics>
#include <QGraphicsLinearLayout>
#include <QtGui/qgraphicssceneevent.h>
#include <QTimer>
#include <QPainter>
#include <KIconLoader>
#include <Plasma/AbstractToolBox>
#include <Plasma/Applet>
#include <Plasma/Containment>
#include <Plasma/ScrollWidget>
#include <Plasma/Theme>
using namespace Plasma;
AppletsContainer::AppletsContainer(AppletsView *parent)
: QGraphicsWidget(parent),
m_scrollWidget(parent),
m_orientation(Qt::Vertical),
m_viewportSize(size()),
m_containment(0),
m_automaticAppletLayout(true),
m_expandAll(false),
m_appletsPerColumn(1),
m_appletsPerRow(1),
m_viewScrollState(QAbstractAnimation::Stopped),
m_toolBox(0)
{
setFlag(QGraphicsItem::ItemHasNoContents);
m_mainLayout = new QGraphicsLinearLayout(this);
m_mainLayout->setContentsMargins(0,0,0,0);
m_viewSyncTimer = new QTimer(this);
m_viewSyncTimer->setSingleShot(true);
connect(m_viewSyncTimer, SIGNAL(timeout()), this, SLOT(syncView()));
m_viewportGeometryUpdateTimer = new QTimer(this);
m_viewportGeometryUpdateTimer->setSingleShot(true);
connect(m_viewportGeometryUpdateTimer, SIGNAL(timeout()), this, SLOT(updateViewportGeometry()));
connect(m_scrollWidget, SIGNAL(viewportGeometryChanged(const QRectF &)),
this, SLOT(viewportGeometryChanged(const QRectF &)));
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
connect(m_scrollWidget, SIGNAL(scrollStateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), this, SLOT(scrollStateChanged(QAbstractAnimation::State, QAbstractAnimation::State)));
themeChanged();
m_background = new Plasma::FrameSvg(this);
m_background->setImagePath("widgets/newspaper-background");
syncBorders();
connect(m_background, SIGNAL(repaintNeeded()), SLOT(syncBorders()));
}
AppletsContainer::~AppletsContainer()
{
}
void AppletsContainer::setAutomaticAppletLayout(const bool automatic)
{
m_automaticAppletLayout = automatic;
}
bool AppletsContainer::automaticAppletLayout() const
{
return m_automaticAppletLayout;
}
void AppletsContainer::themeChanged()
{
QFont font = Plasma::Theme::defaultTheme()->font(Plasma::Theme::DefaultFont);
QFontMetrics fm(font);
m_mSize = fm.boundingRect("M").size();
updateViewportGeometry();
}
void AppletsContainer::syncColumnSizes()
{
const int margin = 4 + (m_mainLayout->count() - 1) * m_mainLayout->spacing();
QSizeF viewportSize = m_scrollWidget->viewportGeometry().size();
QSizeF maxSize;
//try to figure out the column size from the applets size hints
if (m_orientation == Qt::Vertical && m_containment) {
foreach (Plasma::Applet *applet, m_containment->applets()) {
QSizeF appletSize = applet->effectiveSizeHint(Qt::PreferredSize);
if (appletSize.width() > maxSize.width()) {
maxSize.setWidth(appletSize.width());
}
if (appletSize.height() > maxSize.height()) {
maxSize.setHeight(appletSize.height());
}
}
const QSizeF sizeFromHints = maxSize * m_mainLayout->count();
//a bit of snap to avoid contents just too large
if (qAbs(sizeFromHints.width() - viewportSize.width()) > 128) {
viewportSize = sizeFromHints;
}
}
for (int i = 0; i < m_mainLayout->count(); ++i) {
QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(m_mainLayout->itemAt(i));
if (m_orientation == Qt::Vertical) {
lay->setMaximumWidth(viewportSize.width()/m_mainLayout->count()-margin);
lay->setMinimumWidth(viewportSize.width()/m_mainLayout->count()-margin);
lay->setMaximumHeight(-1);
lay->setMinimumHeight(-1);
} else {
lay->setMaximumHeight(viewportSize.height()/m_mainLayout->count()-margin);
lay->setMinimumHeight(viewportSize.height()/m_mainLayout->count()-margin);
lay->setMaximumWidth(-1);
lay->setMinimumWidth(-1);
}
}
}
void AppletsContainer::updateSize()
{
//Layouts are to happy to cache their hints, invalidate everything
for (int i = 0; i < m_mainLayout->count(); ++i) {
QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(m_mainLayout->itemAt(i));
lay->invalidate();
}
m_mainLayout->invalidate();
QSizeF hint = sizeHint(Qt::PreferredSize, QSize());
int toolBoxSpace = 0;
if (m_orientation == Qt::Horizontal) {
if (m_toolBox) {
toolBoxSpace = m_viewportSize.width()/m_appletsPerColumn;
}
resize(hint.width() + toolBoxSpace, qMin(size().height(), m_scrollWidget->viewportGeometry().height()));
if (m_toolBox) {
m_toolBox->setPos(QPoint(size().width() - toolBoxSpace/2, size().height()/2) -
QPoint(m_toolBox->size().width()/2, m_toolBox->size().height()/2));
}
} else {
if (m_toolBox) {
toolBoxSpace = m_viewportSize.height()/m_appletsPerRow;
}
resize(qMin(size().width(), m_scrollWidget->viewportGeometry().width()), hint.height() + toolBoxSpace);
if (m_toolBox) {
m_toolBox->setPos(QPoint(size().width()/2, size().height() - toolBoxSpace/2) -
QPoint(m_toolBox->size().width()/2, m_toolBox->size().height()/2));
}
}
}
void AppletsContainer::setExpandAll(const bool expand)
{
if (m_expandAll == expand) {
return;
}
m_expandAll = expand;
if (!m_containment) {
return;
}
if (expand) {
foreach (Plasma::Applet *applet, m_containment->applets()) {
if (applet->effectiveSizeHint(Qt::MinimumSize).height() > KIconLoader::SizeSmall) {
applet->setPreferredHeight(-1);
}
QList<AppletTitleBar *> titles = applet->findChildren<AppletTitleBar *>("TitleBar");
if (!titles.isEmpty()) {
titles.first()->setActive(true);
}
}
} else {
foreach (Plasma::Applet *applet, m_containment->applets()) {
if (m_orientation == Qt::Vertical) {
if (applet == m_currentApplet.data()) {
applet->setPreferredHeight(optimalAppletSize(applet, true).height());
} else {
applet->setPreferredHeight(optimalAppletSize(applet, false).height());
}
} else {
applet->setPreferredSize(-1, -1);
applet->setPreferredWidth((m_scrollWidget->viewportGeometry().size().width()/2)-8);
}
QList<AppletTitleBar *> titles = applet->findChildren<AppletTitleBar *>("TitleBar");
if (!titles.isEmpty()) {
titles.first()->setActive(applet == m_currentApplet.data());
}
}
}
updateSnapSize();
updateSize();
}
bool AppletsContainer::expandAll() const
{
return m_expandAll;
}
Qt::Orientation AppletsContainer::orientation() const
{
return m_orientation;
}
Plasma::Containment *AppletsContainer::containment() const
{
return m_containment;
}
QGraphicsLinearLayout *AppletsContainer::addColumn()
{
QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(m_orientation);
lay->setContentsMargins(0,0,0,0);
lay->setSpacing(0);
m_mainLayout->addItem(lay);
QGraphicsWidget *spacer = new QGraphicsWidget(this);
spacer->setPreferredSize(0, 0);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
lay->addItem(spacer);
syncColumnSizes();
return lay;
}
void AppletsContainer::cleanupColumns()
{
//clean up all empty columns
for (int i = 0; i < count(); ++i) {
QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(m_mainLayout->itemAt(i));
if (!lay) {
continue;
}
if (lay->count() == 1) {
removeColumn(i);
}
}
}
void AppletsContainer::removeColumn(int column)
{
QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout *>(m_mainLayout->itemAt(column));
if (!lay) {
return;
}
m_mainLayout->removeAt(column);
for (int i = 0; i < lay->count(); ++i) {
QGraphicsLayoutItem *item = lay->itemAt(i);
QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget *>(item);
Plasma::Applet *applet = qobject_cast<Plasma::Applet *>(widget);
//find a new home for the applet
if (applet) {
layoutApplet(applet, applet->pos());
//delete spacers
} else if (widget) {
widget->deleteLater();
}
}
syncColumnSizes();
delete lay;
}
void AppletsContainer::layoutApplet(Plasma::Applet* applet, const QPointF &pos)
{
if (!m_automaticAppletLayout) {
return;
}
QGraphicsLinearLayout *lay = 0;
for (int i = 0; i < m_mainLayout->count(); ++i) {
QGraphicsLinearLayout *candidateLay = dynamic_cast<QGraphicsLinearLayout *>(m_mainLayout->itemAt(i));
//normally should never happen
if (!candidateLay) {
continue;
}
if (m_orientation == Qt::Horizontal) {
if (pos.y() < candidateLay->geometry().bottom()) {
lay = candidateLay;
break;
}
//vertical
} else {
if (pos.x() < candidateLay->geometry().right()) {
lay = candidateLay;
break;
}
}
}
//couldn't decide: is the last column empty?
if (!lay) {
QGraphicsLinearLayout *candidateLay = dynamic_cast<QGraphicsLinearLayout *>(m_mainLayout->itemAt(m_mainLayout->count()-1));
if (candidateLay && candidateLay->count() == 1) {
lay = candidateLay;
}
}
//give up, make a new column
if (!lay) {
lay = addColumn();
}
int insertIndex = -1;
QPointF localPos = mapToItem(this, pos);
//if localPos is (-1,-1) insert at the end of the Newspaper
if (localPos != QPoint(-1, -1)) {
for (int i = 0; i < lay->count(); ++i) {
QGraphicsLayoutItem *li = lay->itemAt(i);
QRectF siblingGeometry = li->geometry();
if (m_orientation == Qt::Horizontal) {
qreal middle = (siblingGeometry.left() + siblingGeometry.right()) / 2.0;
if (localPos.x() < middle) {
insertIndex = i;
break;
} else if (localPos.x() <= siblingGeometry.right()) {
insertIndex = i + 1;
break;
}
} else { //Vertical
qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
if (localPos.y() < middle) {
insertIndex = i;
break;
} else if (localPos.y() <= siblingGeometry.bottom()) {
insertIndex = i + 1;
break;
}
}
}
}
if (insertIndex == -1) {
lay->insertItem(lay->count()-1, applet);
} else {
lay->insertItem(qMin(insertIndex, lay->count()-1), applet);
}
connect(applet, SIGNAL(sizeHintChanged(Qt::SizeHint)), this, SIGNAL(appletSizeHintChanged()));
updateSize();
createAppletTitle(applet);
syncColumnSizes();
}
void AppletsContainer::addApplet(Plasma::Applet* applet, const int row, const int column)
{
QGraphicsLinearLayout *lay;
//give up, make a new column
if (column < 0 || column >= m_mainLayout->count()) {
lay = addColumn();
} else {
lay = static_cast<QGraphicsLinearLayout *>(m_mainLayout->itemAt(column));
}
if (row <= 0) {
lay->insertItem(lay->count()-1, applet);
} else {
lay->insertItem(qMin(row, lay->count()-1), applet);
}
connect(applet, SIGNAL(sizeHintChanged(Qt::SizeHint)), this, SIGNAL(appletSizeHintChanged()));
updateSize();
createAppletTitle(applet);
syncColumnSizes();
}
void AppletsContainer::createAppletTitle(Plasma::Applet *applet)
{
QList<AppletTitleBar *> titles = applet->findChildren<AppletTitleBar *>("TitleBar");
if (!titles.isEmpty()) {
return;
}
AppletTitleBar *appletTitleBar = new AppletTitleBar(applet);
appletTitleBar->setParent(applet);
appletTitleBar->show();
appletTitleBar->setActive(m_expandAll);
if (!m_containment) {
m_containment = applet->containment();
if (m_containment) {
m_toolBox = Plasma::AbstractToolBox::load("org.kde.mobiletoolbox", QVariantList(), m_containment);
if (m_toolBox) {
m_toolBox->setParentItem(this);
m_toolBox->show();
m_toolBox->addTool(m_containment->action("add widgets"));
}
}
}
if (m_orientation == Qt::Horizontal) {
applet->setPreferredSize(-1, -1);
applet->setPreferredWidth((m_scrollWidget->viewportGeometry().size().width()/2)-8);
} else if (m_expandAll) {
if (applet->effectiveSizeHint(Qt::MinimumSize).height() > KIconLoader::SizeSmall) {
applet->setPreferredHeight(-1);
}
} else {
applet->setPreferredHeight(optimalAppletSize(applet, false).height());
}
}
void AppletsContainer::setOrientation(Qt::Orientation orientation)
{
m_orientation = orientation;
m_mainLayout->setOrientation(orientation==Qt::Vertical?Qt::Horizontal:Qt::Vertical);
}
int AppletsContainer::count() const
{
return m_mainLayout->count();
}
QGraphicsLayoutItem *AppletsContainer::itemAt(int i)
{
return m_mainLayout->itemAt(i);
}
QSizeF AppletsContainer::optimalAppletSize(Plasma::Applet *applet, const bool maximized) const
{
if (!applet) {
return QSizeF();
}
const QSizeF minNormalAppletSize(m_viewportSize.width() / m_appletsPerColumn,
m_viewportSize.height() / m_appletsPerRow);
//FIXME: this change of fixed preferred height could cause a relayout, unfortunately there is no other way
int preferred = applet->preferredHeight();
applet->setPreferredHeight(-1);
QSizeF normalAppletSize = QSizeF(applet->effectiveSizeHint(Qt::PreferredSize));
if (normalAppletSize.height() <= minNormalAppletSize.height()/2) {
normalAppletSize.setHeight(minNormalAppletSize.height()/2);
} else {
normalAppletSize.setHeight(minNormalAppletSize.height());
}
if (normalAppletSize.width() <= minNormalAppletSize.width()/2) {
normalAppletSize.setWidth(minNormalAppletSize.width()/2);
} else {
normalAppletSize.setWidth(minNormalAppletSize.width());
}
//4,4 is the size of the margins of the scroll view
normalAppletSize -= QSize(4/m_appletsPerColumn, 4/m_appletsPerRow);
if (maximized) {
QSizeF size = QSizeF(applet->effectiveSizeHint(Qt::PreferredSize)).boundedTo(m_viewportSize).expandedTo(normalAppletSize);
applet->setPreferredHeight(preferred-4);
return size;
} else {
applet->setPreferredHeight(preferred);
return normalAppletSize;
}
}
QSizeF AppletsContainer::viewportSize() const
{
return m_viewportSize;
}
void AppletsContainer::viewportGeometryChanged(const QRectF &geometry)
{
Q_UNUSED(geometry)
m_viewportGeometryUpdateTimer->start(250);
}
void AppletsContainer::scrollStateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
{
Q_UNUSED(oldState)
m_viewScrollState = newState;
}
void AppletsContainer::updateViewportGeometry()
{
m_viewportSize = m_scrollWidget->viewportGeometry().size();
//each applet with an optimal of 40 columns, 15 lines of text
m_appletsPerColumn = qMax((qreal)1, m_viewportSize.width() / (m_mSize.width()*40));
m_appletsPerRow = qMax((qreal)1, m_viewportSize.height() / (m_mSize.height()*15));
if (!m_containment || (m_expandAll && m_orientation != Qt::Horizontal)) {
syncColumnSizes();
return;
}
foreach (Plasma::Applet *applet, m_containment->applets()) {
if (m_orientation == Qt::Vertical) {
if (applet == m_currentApplet.data()) {
applet->setPreferredHeight(optimalAppletSize(applet, true).height());
} else {
applet->setPreferredHeight(optimalAppletSize(applet, false).height());
}
} else {
applet->setPreferredSize(-1, -1);
applet->setPreferredWidth(optimalAppletSize(applet, false).width());
}
}
updateSnapSize();
syncColumnSizes();
}
void AppletsContainer::updateSnapSize()
{
if (m_orientation == Qt::Horizontal || (!m_expandAll && !m_currentApplet)) {
m_scrollWidget->setSnapSize(QSize(m_viewportSize.width()/m_appletsPerColumn, m_viewportSize.height()/m_appletsPerRow));
} else {
m_scrollWidget->setSnapSize(QSizeF());
}
}
void AppletsContainer::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
event->accept();
}
void AppletsContainer::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event)
if (m_orientation == Qt::Horizontal) {
return;
}
setCurrentApplet(0);
QGraphicsWidget::mouseReleaseEvent(event);
}
void AppletsContainer::setCurrentApplet(Plasma::Applet *applet)
{
if (m_currentApplet.data() == applet) {
return;
}
if (m_currentApplet) {
m_currentApplet.data()->setPreferredHeight(optimalAppletSize(m_currentApplet.data(), false).height());
QList<AppletTitleBar *> titles = m_currentApplet.data()->findChildren<AppletTitleBar *>("TitleBar");
if (!titles.isEmpty()) {
titles.first()->setActive(false);
}
}
m_currentApplet = applet;
if (applet) {
applet->setPreferredHeight(optimalAppletSize(applet, true).height());
updateSize();
m_viewSyncTimer->start(200);
QList<AppletTitleBar *> titles = applet->findChildren<AppletTitleBar *>("TitleBar");
if (!titles.isEmpty()) {
titles.first()->setActive(true);
}
}
updateSnapSize();
emit appletActivated(m_currentApplet.data());
}
void AppletsContainer::syncView()
{
Plasma::Applet *applet = m_currentApplet.data();
if (applet && m_viewScrollState == QAbstractAnimation::Stopped) {
m_scrollWidget->ensureRectVisible(QRectF(applet->pos(), QSizeF(applet->size().width(), applet->preferredHeight())));
}
}
Plasma::Applet *AppletsContainer::currentApplet() const
{
return m_currentApplet.data();
}
void AppletsContainer::resizeEvent(QGraphicsSceneResizeEvent *event)
{
m_background->resizeFrame(event->newSize());
QGraphicsWidget::resizeEvent(event);
}
void AppletsContainer::syncBorders()
{
qreal left, top, right, bottom = 0.0;
if (m_background->isValid()) {
setFlag(QGraphicsItem::ItemHasNoContents, false);
m_background->getMargins(left, top, right, bottom);
} else {
setFlag(QGraphicsItem::ItemHasNoContents);
}
setContentsMargins(left, top, right, bottom);
}
void AppletsContainer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
m_background->paintFrame(painter);
}
#include "moc_appletscontainer.cpp"