add more spacing
[personal-kdebase.git] / workspace / plasma / containments / panel / panel.cpp
blob2a84f460f5fd92058711afef4ba442f8f1c081b3
1 /*
2 * Copyright 2007 by Alex Merry <alex.merry@kdemail.net>
3 * Copyright 2008 by Alexis Ménard <darktears31@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library General Public License version 2,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "panel.h"
22 #include <limits>
24 #include <QApplication>
25 #include <QGraphicsLinearLayout>
26 #include <QPainter>
27 #include <QBitmap>
28 #include <QDesktopWidget>
29 #include <QGridLayout>
30 #include <QLabel>
31 #include <QComboBox>
32 #include <QAction>
33 #include <QGraphicsLayout>
34 #include <QGraphicsSceneDragDropEvent>
37 #include <KDebug>
38 #include <KIcon>
39 #include <KDialog>
40 #include <KIntNumInput>
41 #include <KMessageBox>
43 #include <Plasma/Corona>
44 #include <Plasma/FrameSvg>
45 #include <Plasma/Theme>
46 #include <Plasma/View>
47 #include <Plasma/PaintUtils>
49 #include <kephal/screens.h>
51 using namespace Plasma;
53 class Spacer : public QGraphicsWidget
55 public:
56 Spacer(QGraphicsWidget *parent)
57 : QGraphicsWidget(parent)
59 setAcceptDrops(true);
62 ~Spacer()
65 Panel *panel;
67 protected:
68 void dropEvent(QGraphicsSceneDragDropEvent *event)
70 event->setPos(mapToParent(event->pos()));
71 panel->dropEvent(event);
74 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget = 0)
76 Q_UNUSED(option)
77 Q_UNUSED(widget)
79 //TODO: make this a pretty gradient?
80 painter->setRenderHint(QPainter::Antialiasing);
81 QPainterPath p = Plasma::PaintUtils::roundedRectangle(contentsRect().adjusted(1, 1, -2, -2), 4);
82 QColor c = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
83 c.setAlphaF(0.3);
85 painter->fillPath(p, c);
89 Panel::Panel(QObject *parent, const QVariantList &args)
90 : Containment(parent, args),
91 m_configureAction(0),
92 m_addPanelAction(0),
93 m_currentSize(QSize(Kephal::ScreenUtils::screenSize(screen()).width(), 35)),
94 m_maskDirty(true),
95 m_spacerIndex(-1),
96 m_spacer(0)
98 m_background = new Plasma::FrameSvg(this);
99 m_background->setImagePath("widgets/panel-background");
100 m_background->setEnabledBorders(Plasma::FrameSvg::AllBorders);
101 connect(m_background, SIGNAL(repaintNeeded()), this, SLOT(backgroundChanged()));
102 setZValue(150);
103 setContainmentType(Containment::PanelContainment);
104 resize(m_currentSize);
105 setMinimumSize(m_currentSize);
106 setMaximumSize(m_currentSize);
108 connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeUpdated()));
109 connect(this, SIGNAL(appletAdded(Plasma::Applet*,QPointF)),
110 this, SLOT(layoutApplet(Plasma::Applet*,QPointF)));
111 connect(this, SIGNAL(appletRemoved(Plasma::Applet*)),
112 this, SLOT(appletRemoved(Plasma::Applet*)));
115 Panel::~Panel()
119 void Panel::init()
121 Containment::init();
122 //FIXME: This should be enabled, but in that case proxywidgets won't get rendered
123 //setFlag(ItemClipsChildrenToShape, true);
125 KConfigGroup cg = config("Configuration");
127 m_currentSize = cg.readEntry("minimumSize", m_currentSize);
128 if (formFactor() == Plasma::Vertical) {
129 m_currentSize.expandedTo(QSize(0, 35));
130 } else {
131 m_currentSize.expandedTo(QSize(35, 0));
134 setMinimumSize(cg.readEntry("minimumSize", m_currentSize));
135 setMaximumSize(cg.readEntry("maximumSize", m_currentSize));
136 setDrawWallpaper(false);
139 QList<QAction*> Panel::contextualActions()
141 if (!m_configureAction) {
142 m_configureAction = new QAction(i18n("Panel Settings"), this);
143 m_configureAction->setIcon(KIcon("configure"));
144 connect(m_configureAction, SIGNAL(triggered()), this, SIGNAL(toolBoxToggled()));
146 m_addPanelAction = new QAction(i18n("Add Panel"), this);
147 connect(m_addPanelAction, SIGNAL(triggered(bool)), this, SLOT(addPanel()));
148 m_addPanelAction->setIcon(KIcon("list-add"));
149 constraintsEvent(Plasma::ImmutableConstraint);
152 QList<QAction*> actions;
153 actions << action("add widgets") << m_addPanelAction << action("lock widgets") << m_configureAction << action("remove");
154 return actions;
157 void Panel::backgroundChanged()
159 constraintsEvent(Plasma::LocationConstraint);
162 void Panel::layoutApplet(Plasma::Applet* applet, const QPointF &pos)
164 // this gets called whenever an applet is added, and we add it to our layout
165 QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout*>(layout());
167 if (!lay) {
168 return;
171 Plasma::FormFactor f = formFactor();
172 int insertIndex = -1;
174 //Enlarge the panel if possible and needed
175 QSizeF appletHint = applet->preferredSize();
176 QSizeF panelHint = layout()->preferredSize();
177 if (f == Plasma::Horizontal) {
178 if (panelHint.width() + appletHint.width() > size().width()) {
179 resize(panelHint.width() + appletHint.width(), size().height());
181 } else {
182 if (panelHint.height() + appletHint.height() > size().height()) {
183 resize(size().width(), panelHint.height() + appletHint.height());
186 layout()->setMaximumSize(size());
188 //if pos is (-1,-1) insert at the end of the panel
189 if (pos != QPoint(-1, -1)) {
190 for (int i = 0; i < lay->count(); ++i) {
191 QRectF siblingGeometry = lay->itemAt(i)->geometry();
192 if (f == Plasma::Horizontal) {
193 qreal middle = (siblingGeometry.left() + siblingGeometry.right()) / 2.0;
194 if (pos.x() < middle) {
195 insertIndex = i;
196 break;
197 } else if (pos.x() <= siblingGeometry.right()) {
198 insertIndex = i + 1;
199 break;
201 } else { // Plasma::Vertical
202 qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
203 if (pos.y() < middle) {
204 insertIndex = i;
205 break;
206 } else if (pos.y() <= siblingGeometry.bottom()) {
207 insertIndex = i + 1;
208 break;
214 if (insertIndex == -1) {
215 lay->addItem(applet);
216 } else {
217 lay->insertItem(insertIndex, applet);
220 connect(applet, SIGNAL(sizeHintChanged(Qt::SizeHint)), this, SLOT(updateSize()));
223 void Panel::appletRemoved(Plasma::Applet* applet)
225 QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout*>(layout());
226 lay->removeItem(applet);
228 //shrink the panel if possible
229 if (formFactor() == Plasma::Horizontal) {
230 resize(size().width() - applet->size().width(), size().height());
231 } else {
232 resize(size().width(), size().height() - applet->size().height());
234 layout()->setMaximumSize(size());
237 void Panel::updateSize()
239 Plasma::Applet *applet = qobject_cast<Plasma::Applet *>(sender());
241 if (applet) {
242 if (formFactor() == Plasma::Horizontal) {
243 const int delta = applet->preferredWidth() - applet->size().width();
244 //setting the preferred width when delta = 0 and preferredWidth() < minimumWidth()
245 // leads to the same thing as setPreferredWidth(minimumWidth())
246 if (delta != 0) {
247 setPreferredWidth(preferredWidth() + delta);
249 } else if (formFactor() == Plasma::Vertical) {
250 const int delta = applet->preferredHeight() - applet->size().height();
251 if (delta != 0) {
252 setPreferredHeight(preferredHeight() + delta);
256 resize(preferredSize());
260 void Panel::addPanel()
262 if (corona()) {
263 Containment* panel = corona()->addContainment("panel");
264 panel->showConfigurationInterface();
266 panel->setScreen(screen());
268 QList<Plasma::Location> freeEdges = corona()->freeEdges(screen());
269 kDebug() << freeEdges;
270 Plasma::Location destination;
271 if (freeEdges.contains(Plasma::TopEdge)) {
272 destination = Plasma::TopEdge;
273 } else if (freeEdges.contains(Plasma::BottomEdge)) {
274 destination = Plasma::BottomEdge;
275 } else if (freeEdges.contains(Plasma::LeftEdge)) {
276 destination = Plasma::LeftEdge;
277 } else if (freeEdges.contains(Plasma::RightEdge)) {
278 destination = Plasma::RightEdge;
279 } else destination = Plasma::TopEdge;
281 panel->setLocation(destination);
283 // trigger an instant layout so we immediately have a proper geometry
284 // rather than waiting around for the event loop
285 panel->updateConstraints(Plasma::StartupCompletedConstraint);
286 panel->flushPendingConstraintsEvents();
288 if (destination == Plasma::LeftEdge ||
289 destination == Plasma::RightEdge) {
290 panel->setMinimumSize(10, 35);
291 panel->setMaximumSize(35,Kephal::ScreenUtils::screenSize(screen()).height());
292 panel->resize(QSize(35, Kephal::ScreenUtils::screenSize(screen()).height()));
297 void Panel::updateBorders(const QRect &geom)
299 Plasma::Location loc = location();
300 FrameSvg::EnabledBorders enabledBorders = FrameSvg::AllBorders;
302 int s = screen();
303 //kDebug() << loc << s << formFactor() << geometry();
305 qreal topHeight = m_background->marginSize(Plasma::TopMargin);
306 qreal bottomHeight = m_background->marginSize(Plasma::BottomMargin);
307 qreal leftWidth = m_background->marginSize(Plasma::LeftMargin);
308 qreal rightWidth = m_background->marginSize(Plasma::RightMargin);
310 //remove unwanted borders
311 if (s < 0) {
312 // do nothing in this case, we want all the borders
313 } else if (loc == BottomEdge || loc == TopEdge) {
314 QRect r = Kephal::ScreenUtils::screenGeometry(s);
316 if (loc == BottomEdge) {
317 enabledBorders ^= FrameSvg::BottomBorder;
318 bottomHeight = 0;
319 } else {
320 enabledBorders ^= FrameSvg::TopBorder;
321 topHeight = 0;
324 if (geom.x() <= r.x()) {
325 enabledBorders ^= FrameSvg::LeftBorder;
326 leftWidth = 0;
328 if (geom.right() >= r.right()) {
329 enabledBorders ^= FrameSvg::RightBorder;
330 rightWidth = 0;
333 //kDebug() << "top/bottom: Width:" << width << ", height:" << height;
334 } else if (loc == LeftEdge || loc == RightEdge) {
335 QRect r = Kephal::ScreenUtils::screenGeometry(s);
337 if (loc == RightEdge) {
338 enabledBorders ^= FrameSvg::RightBorder;
339 rightWidth = 0;
340 } else {
341 enabledBorders ^= FrameSvg::LeftBorder;
342 leftWidth = 0;
344 if (geom.y() <= r.y()) {
345 enabledBorders ^= FrameSvg::TopBorder;
346 topHeight = 0;
348 if (geom.bottom() >= r.bottom()) {
349 enabledBorders ^= FrameSvg::BottomBorder;
350 bottomHeight = 0;
353 //kDebug() << "left/right: Width:" << width << ", height:" << height;
354 } else {
355 kDebug() << "no location!?";
358 //activate borders and fetch sizes again
359 m_background->setEnabledBorders(enabledBorders);
360 m_background->getMargins(leftWidth, topHeight, rightWidth, bottomHeight);
362 //calculation of extra margins has to be done after getMargins
363 const QGraphicsItem *box = toolBoxItem();
364 if (box && immutability() == Mutable) {
365 QSizeF s = box->boundingRect().size();
366 if (formFactor() == Vertical) {
367 //hardcoded extra margin for the toolbox right now
368 bottomHeight += s.height();;
369 //Default to horizontal for now
370 } else {
371 if (QApplication::layoutDirection() == Qt::RightToLeft) {
372 leftWidth += s.width();
373 } else {
374 rightWidth += s.width();
379 //invalidate the layout and set again
380 if (layout()) {
381 switch (location()) {
382 case LeftEdge:
383 rightWidth = qMin(rightWidth, qMax(qreal(1), size().width() - KIconLoader::SizeMedium));
384 break;
385 case RightEdge:
386 leftWidth = qMin(leftWidth, qMax(qreal(1), size().width() - KIconLoader::SizeMedium));
387 break;
388 case TopEdge:
389 bottomHeight = qMin(bottomHeight, qMax(qreal(1), size().height() - KIconLoader::SizeMedium));
390 break;
391 case BottomEdge:
392 topHeight = qMin(topHeight, qMax(qreal(1), size().height() - KIconLoader::SizeMedium));
393 break;
394 default:
395 break;
397 layout()->setContentsMargins(leftWidth, topHeight, rightWidth, bottomHeight);
398 layout()->invalidate();
401 update();
404 void Panel::constraintsEvent(Plasma::Constraints constraints)
406 kDebug() << "constraints updated with" << constraints << "!!!!!!";
408 m_maskDirty = true;
410 if (constraints & Plasma::FormFactorConstraint) {
411 Plasma::FormFactor form = formFactor();
412 Qt::Orientation layoutDirection = form == Plasma::Vertical ? Qt::Vertical : Qt::Horizontal;
413 // create our layout!
414 if (layout()) {
415 QGraphicsLayout *lay = layout();
416 QGraphicsLinearLayout * linearLay = dynamic_cast<QGraphicsLinearLayout *>(lay);
417 if (linearLay) {
418 linearLay->setOrientation(layoutDirection);
420 linearLay->setMaximumSize(size());
421 } else {
422 QGraphicsLinearLayout *lay = new QGraphicsLinearLayout(this);
423 lay->setOrientation(layoutDirection);
424 lay->setContentsMargins(0, 0, 0, 0);
425 lay->setSpacing(4);
426 lay->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding));
427 setLayout(lay);
428 updateBorders(geometry().toRect());
430 foreach (Applet *applet, applets()) {
431 lay->addItem(applet);
434 lay->setMaximumSize(size());
438 //we need to know if the width or height is 100%
439 if (constraints & Plasma::LocationConstraint || constraints & Plasma::SizeConstraint) {
440 m_currentSize = geometry().size().toSize();
441 QRectF screenRect = screen() >= 0 ? Kephal::ScreenUtils::screenGeometry(screen()) :
442 geometry();
444 if ((formFactor() == Horizontal && m_currentSize.width() >= screenRect.width()) ||
445 (formFactor() == Vertical && m_currentSize.height() >= screenRect.height())) {
446 m_background->setElementPrefix(location());
447 } else {
448 switch (location()) {
449 case LeftEdge:
450 //this call will automatically fallback at no prefix if the element isn't available
451 m_background->setElementPrefix("west-mini");
452 break;
453 case RightEdge:
454 m_background->setElementPrefix("east-mini");
455 break;
456 case TopEdge:
457 m_background->setElementPrefix("north-mini");
458 break;
459 case BottomEdge:
460 default:
461 m_background->setElementPrefix("south-mini");
462 break;
466 m_background->resizeFrame(m_currentSize);
469 //FIXME: this seems the only way to correctly resize the layout the first time when the
470 // saved panel size is less than the default is to setting a maximum size.
471 // this shouldn't happen. maybe even a qgraphicslayout bug?
472 if (layout() && (constraints & Plasma::SizeConstraint)) {
473 layout()->setMaximumSize(size());
476 if (constraints & Plasma::LocationConstraint) {
477 setFormFactorFromLocation(location());
480 if (constraints & Plasma::ImmutableConstraint) {
481 bool unlocked = immutability() == Plasma::Mutable;
483 if (m_addPanelAction) {
484 m_addPanelAction->setEnabled(unlocked);
485 m_addPanelAction->setVisible(unlocked);
488 if (m_configureAction) {
489 m_configureAction->setEnabled(unlocked);
490 m_configureAction->setVisible(unlocked);
493 QGraphicsView *panelView = view();
494 if (panelView) {
495 updateBorders(panelView->geometry());
500 void Panel::saveState(KConfigGroup &config) const
502 config.writeEntry("minimumSize", minimumSize());
503 config.writeEntry("maximumSize", maximumSize());
506 void Panel::themeUpdated()
508 //if the theme is changed all the calculations needs to be done again
509 //and resize based on the change in the theme bordersize
511 qreal oldLeftWidth;
512 qreal newLeftWidth;
513 qreal oldTopHeight;
514 qreal newTopHeight;
515 qreal oldRightWidth;
516 qreal newRightWidth;
517 qreal oldBottomHeight;
518 qreal newBottomHeight;
520 layout()->getContentsMargins(&oldLeftWidth, &oldTopHeight, &oldRightWidth, &oldBottomHeight);
521 m_background->getMargins(newLeftWidth, newTopHeight, newRightWidth, newBottomHeight);
523 QSize newSize(size().width()-(oldLeftWidth - newLeftWidth)-(oldRightWidth - newRightWidth),
524 size().height()-(oldTopHeight - newTopHeight)-(oldBottomHeight - newBottomHeight));
526 resize(newSize);
528 if (formFactor() == Plasma::Vertical) {
529 setMaximumWidth(newSize.width());
530 setMinimumWidth(newSize.width());
531 } else {
532 setMaximumHeight(newSize.height());
533 setMinimumHeight(newSize.height());
536 updateBorders(geometry().toRect());
539 void Panel::paintInterface(QPainter *painter,
540 const QStyleOptionGraphicsItem *option,
541 const QRect& contentsRect)
543 Q_UNUSED(contentsRect)
544 //FIXME: this background drawing is bad and ugly =)
545 // draw the background untransformed (saves lots of per-pixel-math)
546 painter->save();
547 painter->resetTransform();
549 const Containment::StyleOption *containmentOpt = qstyleoption_cast<const Containment::StyleOption *>(option);
551 QRect viewGeom;
552 if (containmentOpt && containmentOpt->view) {
553 viewGeom = containmentOpt->view->geometry();
556 if (m_maskDirty || m_lastViewGeom != viewGeom) {
557 m_maskDirty = false;
558 m_lastViewGeom = viewGeom;
560 updateBorders(viewGeom);
561 if (containmentOpt && containmentOpt->view) {
562 containmentOpt->view->setMask(m_background->mask());
566 // blit the background (saves all the per-pixel-products that blending does)
567 painter->setCompositionMode(QPainter::CompositionMode_Source);
568 painter->setRenderHint(QPainter::Antialiasing);
570 m_background->paintFrame(painter, option->exposedRect);
572 // restore transformation and composition mode
573 painter->restore();
576 void Panel::setFormFactorFromLocation(Plasma::Location loc) {
577 switch (loc) {
578 case BottomEdge:
579 case TopEdge:
580 //kDebug() << "setting horizontal form factor";
581 setFormFactor(Plasma::Horizontal);
582 break;
583 case RightEdge:
584 case LeftEdge:
585 //kDebug() << "setting vertical form factor";
586 setFormFactor(Plasma::Vertical);
587 break;
588 case Floating:
589 //TODO: implement a form factor for floating panels
590 kDebug() << "Floating is unimplemented.";
591 break;
592 default:
593 kDebug() << "invalid location!!";
597 void Panel::showDropZone(const QPoint pos)
599 if (!scene()) {
600 return;
603 // if the drop isn't happening on the outer edges and is instead
604 // actually poised over an applet, ignore it
605 if (((formFactor() == Plasma::Vertical && pos.y() > 1 && pos.y() > size().height() - 2) ||
606 (pos.x() > 1 && pos.x() < size().width() - 2)) &&
607 scene()->itemAt(mapToScene(pos)) != this) {
608 return;
611 QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout*>(layout());
613 if (!lay) {
614 return;
617 if (pos == QPoint()) {
618 if (m_spacer) {
619 lay->removeItem(m_spacer);
620 m_spacer->hide();
622 return;
625 //lucky case: the spacer is already in the right position
626 if (m_spacer && m_spacer->geometry().contains(pos)) {
627 return;
630 Plasma::FormFactor f = formFactor();
631 int insertIndex = -1;
633 //FIXME: needed in two places, make it a function?
634 for (int i = 0; i < lay->count(); ++i) {
635 QRectF siblingGeometry = lay->itemAt(i)->geometry();
637 if (f == Plasma::Horizontal) {
638 qreal middle = (siblingGeometry.left() + siblingGeometry.right()) / 2.0;
639 if (pos.x() < middle) {
640 insertIndex = i;
641 break;
642 } else if (pos.x() <= siblingGeometry.right()) {
643 insertIndex = i + 1;
644 break;
646 } else { // Plasma::Vertical
647 qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
648 if (pos.y() < middle) {
649 insertIndex = i;
650 break;
651 } else if (pos.y() <= siblingGeometry.bottom()) {
652 insertIndex = i + 1;
653 break;
658 m_spacerIndex = insertIndex;
659 if (insertIndex != -1) {
660 if (!m_spacer) {
661 m_spacer = new Spacer(this);
662 m_spacer->panel = this;
664 lay->removeItem(m_spacer);
665 m_spacer->show();
666 lay->insertItem(insertIndex, m_spacer);
671 K_EXPORT_PLASMA_APPLET(panel, Panel)
673 #include "panel.moc"