1 /***************************************************************************
2  *   Copyright (c) 2008  Jeff Mitchell <mitchell@kde.org>                  *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
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.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   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.         *
18  ***************************************************************************/
19 
20 #include "PopupDropper.h"
21 #include "PopupDropper_p.h"
22 #include "PopupDropperItem.h"
23 
24 #include <QtDebug>
25 #include <QGraphicsItem>
26 #include <QGraphicsScene>
27 #include <QSvgRenderer>
28 #include <QAction>
29 #include <QMenu>
30 #include <QPalette>
31 #include <QTimeLine>
32 #include <QWidget>
33 
PopupDropperPrivate(PopupDropper * parent,bool sa,QWidget * widget)34 PopupDropperPrivate::PopupDropperPrivate( PopupDropper* parent, bool sa, QWidget* widget )
35     : QObject( parent )
36     , standalone( sa )
37     , widget( widget )
38     , scene( 0 )
39     , view( 0 )
40     , fade( PopupDropper::FadeInOut )
41     , fadeHideTimer()
42     , fadeShowTimer()
43     , fadeInTime( 800 )
44     , fadeOutTime( 300 )
45     , deleteTimer()
46     , deleteTimeout( 1000 )
47     , frameMax( 30 )
48     , windowColor( 0, 0, 0, 64 )
49     , windowBackgroundBrush()
50     , baseTextColor( Qt::white )
51     , hoveredTextColor( Qt::blue )
52     , hoveredBorderPen()
53     , hoveredFillBrush()
54     , file()
55     , sharedRenderer( 0 )
56     , horizontalOffset( 30 )
57     , pdiItems()
58     , overlayLevel( 1 )
59     , entered( false )
60     , submenuMap()
61     , submenu( false )
62     , allItems()
63     , quitOnDragLeave( false )
64     , onTop( true )
65     , widgetRect()
66     , queuedHide( false )
67     , q( parent )
68 {
69     if( widget )
70         widgetRect = widget->rect();
71 
72     windowBackgroundBrush.setColor( windowColor );
73     hoveredBorderPen.setColor( Qt::blue );
74     hoveredBorderPen.setWidth( 2 );
75     hoveredBorderPen.setStyle( Qt::SolidLine );
76     QColor hoveredFillColor = QColor( Qt::blue );
77     hoveredFillColor.setAlpha( 32 );
78     hoveredFillBrush.setColor( hoveredFillColor );
79     hoveredFillBrush.setStyle( Qt::SolidPattern );
80     scene = new QGraphicsScene( ( sa ? 0 : parent ) );
81     view = new PopupDropperView( parent, scene, ( sa ? 0 : widget ) );
82     //qDebug() << "on create, view size = " << view->size();
83     deleteTimer.setSingleShot( true );
84     fadeHideTimer.setDirection( QTimeLine::Backward );
85     connect( &fadeHideTimer, &QTimeLine::frameChanged, this, &PopupDropperPrivate::fadeHideTimerFrameChanged );
86     connect( &fadeShowTimer, &QTimeLine::frameChanged, this, &PopupDropperPrivate::fadeShowTimerFrameChanged );
87     connect( &fadeHideTimer, &QTimeLine::finished, this, &PopupDropperPrivate::fadeHideTimerFinished );
88     connect( &fadeShowTimer, &QTimeLine::finished, this, &PopupDropperPrivate::fadeShowTimerFinished );
89     connect( &deleteTimer, &QTimer::timeout, this, &PopupDropperPrivate::deleteTimerFinished );
90 }
91 
~PopupDropperPrivate()92 PopupDropperPrivate::~PopupDropperPrivate()
93 {
94 }
95 
newSceneView(PopupDropper * pud)96 void PopupDropperPrivate::newSceneView( PopupDropper* pud )
97 {
98     scene->deleteLater();
99     scene = new QGraphicsScene( pud );
100     //qDebug() << "new scene width in newSceneView = " << scene->width();
101     view = new PopupDropperView( pud, scene, widget );
102     //qDebug() << "on create, view size = " << view->size();
103 }
104 
setParent(QObject * parent)105 void PopupDropperPrivate::setParent( QObject* parent )
106 {
107     QObject::setParent( parent );
108     q = static_cast<PopupDropper*>( parent );
109 }
110 
fadeHideTimerFrameChanged(int frame)111 void PopupDropperPrivate::fadeHideTimerFrameChanged( int frame ) //SLOT
112 {
113     if( fadeHideTimer.state() == QTimeLine::Running )
114     {
115         qreal val = ( frame * 1.0 ) / frameMax;
116         QColor color = windowColor;
117         int alpha = (int)( color.alpha() * val );
118         color.setAlpha( alpha );
119         q->setPalette( color );
120         foreach( PopupDropperItem* pdi, pdiItems )
121             pdi->setSubitemOpacity( val );
122     }
123 }
124 
fadeShowTimerFrameChanged(int frame)125 void PopupDropperPrivate::fadeShowTimerFrameChanged( int frame ) //SLOT
126 {
127     if( fadeShowTimer.state() == QTimeLine::Running )
128     {
129         qreal val = ( frame * 1.0 ) / frameMax;
130         QColor color = windowColor;
131         int alpha = (int)( color.alpha() * val );
132         color.setAlpha( alpha );
133         q->setPalette( color );
134         foreach( PopupDropperItem* pdi, pdiItems )
135             pdi->setSubitemOpacity( val );
136     }
137 }
138 
fadeHideTimerFinished()139 void PopupDropperPrivate::fadeHideTimerFinished() //SLOT
140 {
141     view->hide();
142     //qDebug() << "Emitting fadeHideFinished in d pointer " << this;
143     Q_EMIT q->fadeHideFinished();
144 }
145 
fadeShowTimerFinished()146 void PopupDropperPrivate::fadeShowTimerFinished() //SLOT
147 {
148     q->setPalette( windowColor );
149     queuedHide = false;
150     foreach( PopupDropperItem* pdi, pdiItems )
151         pdi->setSubitemOpacity( 1.0 );
152 }
153 
dragEntered()154 void PopupDropperPrivate::dragEntered()
155 {
156     //qDebug() << "PopupDropperPrivate::dragEntered";
157     q->updateAllOverlays();
158 }
159 
dragLeft()160 void PopupDropperPrivate::dragLeft()
161 {
162     //qDebug() << "PopupDropperPrivate::dragLeft()";
163     //qDebug() << "PUD to be hidden or not hidden = " << q;
164     if( view->entered() && quitOnDragLeave )
165     {
166         view->setAcceptDrops( false );
167         //qDebug() << "View entered, hiding";
168         connect( q, &PopupDropper::fadeHideFinished, q, &PopupDropper::subtractOverlay );
169         q->hide();
170     }
171     q->updateAllOverlays();
172 }
173 
startDeleteTimer()174 void PopupDropperPrivate::startDeleteTimer()
175 {
176     if( deleteTimeout == 0 )
177         return;
178     view->setEntered( false );
179     //qDebug() << "Starting delete timer";
180     deleteTimer.start( deleteTimeout );
181 }
182 
deleteTimerFinished()183 void PopupDropperPrivate::deleteTimerFinished() //SLOT
184 {
185     //qDebug() << "Delete Timer Finished";
186     if( !view->entered() && quitOnDragLeave )
187     {
188         connect( q, &PopupDropper::fadeHideFinished, q, &PopupDropper::subtractOverlay );
189         q->hide();
190     }
191 }
192 
reposItems()193 void PopupDropperPrivate::reposItems()
194 {
195     qreal partitionsize, my_min, my_max;
196     //qDebug() << "allItems.size() = " << allItems.size();
197     int counter = 0;
198     for( int i = 0; i < allItems.size(); i++ )
199     {
200         //qDebug() << "item " << i;
201         int verticalmargin = 5;
202         partitionsize = scene->height() / pdiItems.size(); //gives partition size...now center in this area
203         my_min = ( counter * partitionsize ) + verticalmargin;
204         my_max = ( ( counter + 1 ) * partitionsize ) - verticalmargin;
205         //qDebug() << "my_min = " << my_min << ", my_max = " << my_max;
206         PopupDropperItem* pItem = dynamic_cast<PopupDropperItem*>( allItems.at( i ) );
207         QGraphicsLineItem* qglItem = 0;
208         if( pItem )
209         {
210             pItem->setPopupDropper( q ); //safety
211             //qDebug() << "item " << i << " is a PDI ";
212             //If the svgElementRect is too high, resize it to fit
213             pItem->svgElementRect().setHeight( my_max - my_min - ( 2 * verticalmargin ) );
214             pItem->setPos( 0, my_min );
215             pItem->borderRectItem()->setRect( 0 - pItem->borderWidth(), 0, scene->width() + 2*pItem->borderWidth(), my_max - my_min );
216             pItem->scaleAndReposSvgItem();
217             pItem->reposTextItem();
218             pItem->reposHoverFillRects();
219             pItem->update();
220             //qDebug() << "size of view frame = " << view->size();
221             ++counter;
222         }
223         else if( ( qglItem = dynamic_cast<QGraphicsLineItem*>( allItems.at( i ) ) ) )
224         {
225             //qDebug() << "item " << i << " is a QGLI";
226             qglItem->setLine( horizontalOffset, (my_max-partitionsize), scene->width() - horizontalOffset, (my_max-partitionsize) );
227         }
228     }
229 }
230 
amIOnTop(PopupDropperView * pdv)231 bool PopupDropperPrivate::amIOnTop( PopupDropperView* pdv )
232 {
233     if( onTop && pdv == view )
234         return true;
235     return false;
236 }
237 
238 //////////////////////////////////////////////////////////////
239 
PopupDropper(QWidget * parent,bool standalone)240 PopupDropper::PopupDropper( QWidget *parent, bool standalone )
241     : QObject( parent )
242     , d( new PopupDropperPrivate( this, standalone, parent ) )
243 {
244     if( !parent )
245     {
246         parent = d->view;
247         d->widget = parent;
248     }
249     QObject::setParent( parent );
250     initOverlay( parent );
251     setColors( d->windowColor, d->baseTextColor, d->hoveredTextColor, d->hoveredBorderPen.color(), d->hoveredFillBrush.color() );
252     d->sharedRenderer = new QSvgRenderer( this );
253     d->overlayLevel = 1;
254     //qDebug() << "Popup Dropper created!";
255 }
256 
~PopupDropper()257 PopupDropper::~PopupDropper()
258 {
259     //qDebug() << "Popup Dropper destroyed!";
260 }
261 
overlayLevel() const262 int PopupDropper::overlayLevel() const
263 {
264     return d->overlayLevel;
265 }
266 
initOverlay(QWidget * parent,PopupDropperPrivate * priv)267 void PopupDropper::initOverlay( QWidget* parent, PopupDropperPrivate* priv )
268 {
269     PopupDropperPrivate *pdp = priv ? priv : d;
270     //qDebug() << "PUD Overlay being created, d pointer is " << d;
271     pdp->scene->setSceneRect( QRectF( parent->rect() ) );
272     //qDebug() << "Scene width = " << pdp->scene->width();
273     pdp->scene->setItemIndexMethod( QGraphicsScene::NoIndex );
274     pdp->view->setFixedSize( parent->size() );
275     pdp->view->setLineWidth( 0 );
276     pdp->view->setFrameStyle( QFrame::NoFrame );
277     pdp->view->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
278     pdp->view->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
279     pdp->view->setBackgroundRole( QPalette::Window );
280     pdp->view->setAutoFillBackground( true );
281     pdp->fadeHideTimer.setFrameRange( 0, pdp->frameMax );
282     pdp->fadeHideTimer.setUpdateInterval( 20 ); // 50 fps
283     pdp->fadeShowTimer.setFrameRange( 0, pdp->frameMax );
284     pdp->fadeShowTimer.setUpdateInterval( 20 ); // 50 fps
285 }
286 
addOverlay()287 void PopupDropper::addOverlay()
288 {
289     d->onTop = false;
290     m_viewStack.push( d );
291     PopupDropperPrivate* old_d = d;
292     d = new PopupDropperPrivate( this, false, old_d->view );
293     d->sharedRenderer = old_d->sharedRenderer;
294     //qDebug() << "Adding overlay: ";
295     initOverlay( old_d->view );
296     setColors( d->windowColor, d->baseTextColor, d->hoveredTextColor, d->hoveredBorderPen.color(), d->hoveredFillBrush.color() );
297     d->quitOnDragLeave = true;
298     d->overlayLevel = old_d->overlayLevel + 1;
299     old_d->view->deactivateHover();
300 }
301 
302 //note: Used when activating a submenu; does not set default colors, should have done that when creating submenu
addOverlay(PopupDropperPrivate * newD)303 void PopupDropper::addOverlay( PopupDropperPrivate* newD )
304 {
305     d->onTop = false;
306     //qDebug() << "right before push, m_viewStack.size() is " << m_viewStack.size();
307     m_viewStack.push( d );
308     //qDebug() << "right after push, m_viewStack.size() is " << m_viewStack.size();
309     PopupDropperPrivate* old_d = d;
310     d = newD;
311     d->onTop = true;
312     d->sharedRenderer = old_d->sharedRenderer;
313     d->quitOnDragLeave = true;
314     d->overlayLevel = old_d->overlayLevel + 1;
315     //qDebug() << "new d d->overlayLevel = " << d->overlayLevel;
316     //qDebug() << "in PopupDropper " << this;
317 }
318 
319 //NOTE: just like you have to show the overlay when adding, this function does not hide the overlay; you must do that yourself
320 //with hide() (which will hide just one level)
subtractOverlay()321 bool PopupDropper::subtractOverlay()
322 {
323     //qDebug() << "subtractOverlay, with d pointer " << d;
324     disconnect( this, &PopupDropper::fadeHideFinished, this, &PopupDropper::subtractOverlay );
325     while( !isHidden() && d->fadeHideTimer.state() == QTimeLine::Running )
326     {
327         //qDebug() << "singleshotting subtractOverlay";
328         QTimer::singleShot( 0, this, &PopupDropper::subtractOverlay );
329         return false;
330     }
331     //qDebug() << "in PopupDropper " << this;
332     //qDebug() << "overlay d->overlayLevel = " << d->overlayLevel;
333     if( d->overlayLevel == 1 )
334         return false;
335     PopupDropper::Fading currFadeValue = d->fade;
336     d->fade = PopupDropper::NoFade;
337     d->onTop = false;
338     PopupDropperPrivate* old_d = d;
339     //qDebug() << "right before pop, m_viewStack.size() is " << m_viewStack.size();
340     d = m_viewStack.pop();
341     d->onTop = true;
342     if( !old_d->submenu )
343     {
344         //qDebug() << "not submenu, deleting";
345         old_d->deleteLater();
346     }
347     else
348     {
349         foreach( QGraphicsItem* item, old_d->pdiItems )
350             old_d->scene->removeItem( item );
351         //qDebug() << "not deleting, submenu";
352         old_d->fade = currFadeValue;
353         old_d->view->resetView();
354     }
355     d->startDeleteTimer();
356     return true;
357 }
358 
addSubmenu(PopupDropper ** pd,const QString & text)359 PopupDropperItem* PopupDropper::addSubmenu( PopupDropper** pd, const QString &text )
360 {
361     //qDebug() << "addSubmenu, this is " << this << " and passed-in PopupDropper is " << (*pd);
362     if( !(*pd) )
363     {
364         qWarning() << "Did not pass in a valid PUD!";
365         return 0;
366     }
367     PopupDropperPrivate* newD = (*pd)->d;
368     newD->submenu = true;
369     newD->widget = d->widget;
370     newD->setParent( this );
371     foreach( PopupDropperItem* item, newD->pdiItems )
372         newD->scene->removeItem( item );
373     newD->newSceneView( this );
374     initOverlay( d->widget, newD );
375     PopupDropperItem* pdi = new PopupDropperItem();
376 
377     QAction* action = new QAction( text, this );
378 
379     connect( action, &QAction::hovered, this, &PopupDropper::activateSubmenu );
380     pdi->setAction( action );
381     pdi->setSubmenuTrigger( true );
382     pdi->setHoverIndicatorShowStyle( PopupDropperItem::OnHover );
383     d->submenuMap[action] = newD;
384     delete (*pd);
385     (*pd) = 0;
386     foreach( PopupDropperItem* item, newD->pdiItems )
387         item->setPopupDropper( this );
388     //qDebug() << "d->submenuMap[pda] = " << d->submenuMap[pda];
389     addItem( pdi );
390     return pdi;
391 }
392 
activateSubmenu()393 void PopupDropper::activateSubmenu()
394 {
395     //qDebug() << "Sender is " << QObject::sender();
396     if( isHidden() || d->fadeHideTimer.state() == QTimeLine::Running )
397         return;
398     PopupDropperPrivate* oldd = d;
399     addOverlay( d->submenuMap[static_cast<QAction*>(QObject::sender())] );
400     //qDebug() << "d->pdiItems.size() = " << d->pdiItems.size() << " for " << d;
401     foreach( PopupDropperItem* item, d->pdiItems )
402         addItem( item, false, false );
403     oldd->view->deactivateHover();
404     show();
405 }
406 
addMenu(const QMenu * menu)407 bool PopupDropper::addMenu( const QMenu *menu )
408 {
409     Q_UNUSED(menu)
410     if( !menu )
411         return false;
412 
413     if( menu->actions().isEmpty() )
414         return true;
415 
416     PopupDropperItem *pdi = nullptr;
417     foreach( QAction *action, menu->actions() )
418     {
419         if( !action->menu() )
420         {
421             pdi = new PopupDropperItem();
422             pdi->setAction( action );
423             addItem( pdi );
424         }
425         else
426         {
427             PopupDropper *pd = new PopupDropper( nullptr );
428             bool success = pd->addMenu( action->menu() );
429             if( success )
430                 pdi = addSubmenu( &pd, action->text() );
431         }
432         pdi = nullptr;
433     }
434 
435     return true;
436 }
437 
standalone() const438 bool PopupDropper::standalone() const
439 {
440     return d->standalone;
441 }
442 
show()443 void PopupDropper::show()
444 {
445     if( !d->sharedRenderer )
446     {
447         //qDebug() << "No shared renderer set!";
448         return;
449     }
450     if( d->widget && d->widget->rect() != d->widgetRect )
451     {
452         d->widgetRect = d->widget->rect();
453         d->scene->setSceneRect( d->widget->rect() );
454         d->view->setFixedSize( d->widget->size() );
455         update();
456     }
457     //qDebug() << "Showing PopupDropper";
458     d->fadeShowTimer.stop();
459     if( ( d->fade == PopupDropper::FadeIn || d->fade == PopupDropper::FadeInOut ) && d->fadeInTime > 0 )
460     {
461         //qDebug() << "Animating!";
462         d->fadeShowTimer.setDuration( d->fadeInTime );
463         d->fadeShowTimer.setCurrentTime( 0 );
464         d->fadeShowTimer.setCurveShape( QTimeLine::EaseOutCurve );
465         QColor color = d->windowColor;
466         color.setAlpha( 0 );
467         setPalette( color );
468         foreach( PopupDropperItem* pdi, d->pdiItems )
469             pdi->setSubitemOpacity( 0.0 );
470         d->fadeShowTimer.start();
471         //qDebug() << "Timer started";
472     }
473     d->view->show();
474 }
475 
showAllOverlays()476 void PopupDropper::showAllOverlays()
477 {
478     show();
479     for( int i = m_viewStack.size() - 1; i >= 0; --i )
480     {
481         PopupDropperPrivate* pdi = m_viewStack.at( i );
482         if( pdi != d )
483             d->view->show();
484     }
485 }
486 
487 //returns immediately!
hide()488 void PopupDropper::hide()
489 {
490     //qDebug() << "PopupDropper::hide entered, d pointer is = " << d;
491 
492     //if hide is called and the view is already hidden, it's likely spurious
493     if( isHidden() )
494     {
495         //qDebug() << "ishidden, returning";
496         return;
497     }
498 
499     //queuedHide is to make sure that fadeShowTimerFinished executes before this next hide()
500     if( d->fadeShowTimer.state() == QTimeLine::Running )
501     {
502         //qDebug() << "show timer running, queueing hide";
503         d->fadeShowTimer.stop();
504         d->queuedHide = true;
505         QTimer::singleShot( 0, d, &PopupDropperPrivate::fadeShowTimerFinished );
506         QTimer::singleShot( 0, this, &PopupDropper::hide );
507         return;
508     }
509 
510     //queuedHide will be set to false from fadeShowTimerFinished...so if this came up first,
511     //then wait
512     if( d->fadeHideTimer.state() == QTimeLine::Running || d->queuedHide )
513     {
514         //qDebug() << "hide timer running or queued hide";
515         QTimer::singleShot( 0, this, &PopupDropper::hide );
516         return;
517     }
518 
519     if( ( d->fade == PopupDropper::FadeOut || d->fade == PopupDropper::FadeInOut ) && d->fadeOutTime > 0 )
520     {
521         //qDebug() << "Starting fade out";
522         d->fadeHideTimer.setDuration( d->fadeOutTime );
523         d->fadeHideTimer.setCurveShape( QTimeLine::LinearCurve );
524         d->fadeHideTimer.start();
525         //qDebug() << "Timer started";
526         return;
527     }
528     else  //time is zero, or no fade
529     {
530         //qDebug() << "time is zero, or no fade";
531         QTimer::singleShot( 0, d, &PopupDropperPrivate::fadeHideTimerFinished );
532         return;
533     }
534 }
535 
hideAllOverlays()536 void PopupDropper::hideAllOverlays()
537 {
538     //qDebug() << "Entered hideAllOverlays";
539     connect( this, &PopupDropper::fadeHideFinished, this, &PopupDropper::slotHideAllOverlays );
540     hide();
541     //qDebug() << "Leaving hideAllOverlays";
542 }
543 
slotHideAllOverlays()544 void PopupDropper::slotHideAllOverlays()
545 {
546     //qDebug() << "Entered slotHideAllOverlays()";
547     disconnect( this, &PopupDropper::fadeHideFinished, this, &PopupDropper::slotHideAllOverlays );
548     //qDebug() << "m_viewStack.size() = " << m_viewStack.size();
549     for( int i = m_viewStack.size() - 1; i >= 0; --i )
550     {
551         PopupDropperPrivate* pdp = m_viewStack.at( i );
552         //qDebug() << "checking pdp = " << (QObject*)pdp << ", d is " << (QObject*)d;
553         if( pdp != d )
554             pdp->view->hide();
555     }
556     //qDebug() << "Leaving slotHideAllOverlays";
557 }
558 
update()559 void PopupDropper::update()
560 {
561     d->reposItems();
562     d->view->update();
563 }
564 
updateAllOverlays()565 void PopupDropper::updateAllOverlays()
566 {
567     for( int i = m_viewStack.size() - 1; i >= 0; --i )
568     {
569         PopupDropperPrivate* pdp = m_viewStack.at( i );
570         pdp->view->update();
571     }
572     d->view->update();
573 }
574 
isHidden() const575 bool PopupDropper::isHidden() const
576 {
577     return d->view->isHidden();
578 }
579 
clear()580 void PopupDropper::clear()
581 {
582     while( !isHidden() && d->fadeHideTimer.state() == QTimeLine::Running )
583     {
584         QTimer::singleShot(0, this, &PopupDropper::clear );
585         return;
586     }
587     //qDebug() << "Clear happening!";
588 //     disconnect( this, 0, this, &PopupDropper::clear ); Unused at this point.
589     do
590     {
591         foreach( QGraphicsItem* item, d->allItems )
592         {
593             if( dynamic_cast<PopupDropperItem*>(item) )
594             {
595                 if( dynamic_cast<PopupDropperItem*>(item)->isSubmenuTrigger() )
596                 {
597                     //qDebug() << "Disconnecting action";
598                     disconnect( dynamic_cast<PopupDropperItem*>(item)->action(), &QAction::hovered, this, &PopupDropper::activateSubmenu );
599                 }
600                 dynamic_cast<PopupDropperItem*>(item)->deleteLater();
601             }
602             else
603                 delete item;
604         }
605         d->pdiItems.clear();
606         d->allItems.clear();
607         //qDebug() << "Size of pdiItems is now " << d->pdiItems.size();
608         //qDebug() << "Size of allItems is now " << d->allItems.size();
609         d->view->hide();
610         d->view->resetView();
611     } while ( subtractOverlay() );
612 }
613 
isEmpty(bool allItems) const614 bool PopupDropper::isEmpty( bool allItems ) const
615 {
616     if( allItems )
617         return d->allItems.empty();
618     else
619         return d->pdiItems.empty();
620 }
621 
quitOnDragLeave() const622 bool PopupDropper::quitOnDragLeave() const
623 {
624     return d->quitOnDragLeave;
625 }
626 
setQuitOnDragLeave(bool quit)627 void PopupDropper::setQuitOnDragLeave( bool quit )
628 {
629     d->quitOnDragLeave = quit;
630 }
631 
fadeInTime() const632 int PopupDropper::fadeInTime() const
633 {
634     return d->fadeInTime;
635 }
636 
setFadeInTime(const int msecs)637 void PopupDropper::setFadeInTime( const int msecs )
638 {
639     d->fadeInTime = msecs;
640 }
641 
fadeOutTime() const642 int PopupDropper::fadeOutTime() const
643 {
644     return d->fadeOutTime;
645 }
646 
setFadeOutTime(const int msecs)647 void PopupDropper::setFadeOutTime( const int msecs )
648 {
649     d->fadeOutTime = msecs;
650 }
651 
fading() const652 PopupDropper::Fading PopupDropper::fading() const
653 {
654     return d->fade;
655 }
656 
setFading(PopupDropper::Fading fade)657 void PopupDropper::setFading( PopupDropper::Fading fade )
658 {
659     d->fade = fade;
660 }
661 
fadeHideTimer() const662 const QTimeLine* PopupDropper::fadeHideTimer() const
663 {
664     return &d->fadeHideTimer;
665 }
666 
fadeShowTimer() const667 const QTimeLine* PopupDropper::fadeShowTimer() const
668 {
669     return &d->fadeShowTimer;
670 }
671 
deleteTimeout() const672 int PopupDropper::deleteTimeout() const
673 {
674     return d->deleteTimeout;
675 }
676 
setDeleteTimeout(int msecs)677 void PopupDropper::setDeleteTimeout( int msecs )
678 {
679     d->deleteTimeout = msecs;
680 }
681 
windowColor() const682 QColor PopupDropper::windowColor() const
683 {
684     return d->windowColor;
685 }
686 
setWindowColor(const QColor & window)687 void PopupDropper::setWindowColor( const QColor &window )
688 {
689     d->windowColor = window;
690     setPalette( window );
691 }
692 
windowBackgroundBrush() const693 QBrush PopupDropper::windowBackgroundBrush() const
694 {
695     return d->windowBackgroundBrush;
696 }
697 
setWindowBackgroundBrush(const QBrush & window)698 void PopupDropper::setWindowBackgroundBrush( const QBrush &window )
699 {
700     d->windowBackgroundBrush = window;
701     d->view->setBackgroundBrush( window );
702 }
703 
baseTextColor() const704 QColor PopupDropper::baseTextColor() const
705 {
706     return d->baseTextColor;
707 }
708 
setBaseTextColor(const QColor & text)709 void PopupDropper::setBaseTextColor( const QColor &text )
710 {
711     d->baseTextColor = text;
712     foreach( PopupDropperItem *item, d->pdiItems )
713         item->setBaseTextColor( text );
714 }
715 
hoveredTextColor() const716 QColor PopupDropper::hoveredTextColor() const
717 {
718     return d->hoveredTextColor;
719 }
720 
setHoveredTextColor(const QColor & text)721 void PopupDropper::setHoveredTextColor( const QColor &text )
722 {
723     d->hoveredTextColor = text;
724     foreach( PopupDropperItem *item, d->pdiItems )
725         item->setHoveredTextColor( text );
726 }
727 
hoveredBorderPen() const728 QPen PopupDropper::hoveredBorderPen() const
729 {
730     return d->hoveredBorderPen;
731 }
732 
setHoveredBorderPen(const QPen & border)733 void PopupDropper::setHoveredBorderPen( const QPen &border )
734 {
735     d->hoveredBorderPen = border;
736     foreach( PopupDropperItem *item, d->pdiItems )
737         item->setHoveredBorderPen( border );
738 }
739 
hoveredFillBrush() const740 QBrush PopupDropper::hoveredFillBrush() const
741 {
742     return d->hoveredFillBrush;
743 }
744 
setHoveredFillBrush(const QBrush & fill)745 void PopupDropper::setHoveredFillBrush( const QBrush &fill )
746 {
747     d->hoveredFillBrush = fill;
748     foreach( PopupDropperItem *item, d->pdiItems )
749         item->setHoveredFillBrush( fill );
750 }
751 
setColors(const QColor & window,const QColor & baseText,const QColor & hoveredText,const QColor & hoveredBorder,const QColor & hoveredFill)752 void PopupDropper::setColors( const QColor &window, const QColor &baseText, const QColor &hoveredText, const QColor &hoveredBorder, const QColor &hoveredFill )
753 {
754     d->windowColor = window;
755     d->baseTextColor = baseText;
756     d->hoveredTextColor = hoveredText;
757     d->hoveredBorderPen.setColor( hoveredBorder );
758     d->hoveredFillBrush.setColor( hoveredFill );
759     setPalette( window, baseText, hoveredText, hoveredBorder, hoveredFill );
760 }
761 
setPalette(const QColor & window)762 void PopupDropper::setPalette( const QColor &window )
763 {
764     QPalette p = d->view->palette();
765     p.setColor( QPalette::Window, window );
766     d->view->setPalette( p );
767     updateAllOverlays();
768 }
769 
setPalette(const QColor & window,const QColor & baseText,const QColor & hoveredText,const QColor & hoveredBorder,const QColor & hoveredFill)770 void PopupDropper::setPalette( const QColor &window, const QColor &baseText, const QColor &hoveredText, const QColor &hoveredBorder, const QColor &hoveredFill )
771 {
772     QPalette p = d->view->palette();
773     p.setColor( QPalette::Window, window );
774     d->view->setPalette( p );
775     QPen pen;
776     QBrush brush;
777     foreach( PopupDropperItem *item, d->pdiItems )
778     {
779         item->setBaseTextColor( baseText );
780         item->setHoveredTextColor( hoveredText );
781         pen = item->hoveredBorderPen();
782         pen.setColor( hoveredBorder );
783         item->setHoveredBorderPen( pen );
784         brush = item->hoveredFillBrush();
785         brush.setColor( hoveredFill );
786         item->setHoveredFillBrush( brush );
787     }
788     updateAllOverlays();
789 }
790 
windowTitle() const791 QString PopupDropper::windowTitle() const
792 {
793     return d->view->windowTitle();
794 }
795 
setWindowTitle(const QString & title)796 void PopupDropper::setWindowTitle( const QString &title )
797 {
798     d->view->setWindowTitle( title );
799     d->view->update();
800 }
801 
svgFile() const802 QString PopupDropper::svgFile() const
803 {
804     return d->file;
805 }
806 
setSvgFile(const QString & file)807 void PopupDropper::setSvgFile( const QString &file )
808 {
809     if( d->sharedRenderer )
810     {
811         if( !d->sharedRenderer->load( file ) )
812             qWarning() << "Could not load SVG file " << file;
813         else
814         {
815             d->file = file;
816             //qDebug() << "Loaded SVG file!";
817         }
818     }
819     else
820         qWarning() << "No shared renderer!";
821 }
822 
svgRenderer()823 QSvgRenderer* PopupDropper::svgRenderer()
824 {
825     return d->sharedRenderer;
826 }
827 
setSvgRenderer(QSvgRenderer * renderer)828 void PopupDropper::setSvgRenderer( QSvgRenderer *renderer )
829 {
830     d->sharedRenderer = renderer;
831 }
832 
horizontalOffset() const833 int PopupDropper::horizontalOffset() const
834 {
835     return d->horizontalOffset;
836 }
837 
setHorizontalOffset(int pixels)838 void PopupDropper::setHorizontalOffset( int pixels )
839 {
840     d->horizontalOffset = pixels;
841 }
842 
viewSize() const843 const QSize PopupDropper::viewSize() const
844 {
845     if( d && d->view )
846         return d->view->size();
847     else
848         return QSize( 0, 0 );
849 }
850 
addItem(PopupDropperItem * item,bool useSharedRenderer)851 void PopupDropper::addItem( PopupDropperItem *item, bool useSharedRenderer )
852 {
853     addItem( item, useSharedRenderer, true );
854 }
855 
addItem(PopupDropperItem * item,bool useSharedRenderer,bool appendToList)856 void PopupDropper::addItem( PopupDropperItem *item, bool useSharedRenderer, bool appendToList )
857 {
858     //qDebug() << "adding item";
859     //FIXME: Make separators do something graphical instead of just ignoring them
860     PopupDropperItem *pItem = static_cast<PopupDropperItem*>( item );
861     if( pItem->isSeparator() )
862         return;
863     if( useSharedRenderer )
864         pItem->setSharedRenderer( d->sharedRenderer );
865     //qDebug() << "Checking appendToList";
866     if( appendToList )
867     {
868         d->pdiItems.append( pItem );
869         d->allItems.append( pItem );
870         //qDebug() << "pdiItems list is now size " << d->pdiItems.size() << " for " << d;
871         //qDebug() << "allItems list is now size " << d->allItems.size() << " for " << d;
872     }
873     if( !pItem->textItem() )
874     {
875         QGraphicsTextItem *textItem = new QGraphicsTextItem( pItem->text(), pItem );
876         pItem->setTextItem( textItem );
877         if( !pItem->customBaseTextColor() || !pItem->baseTextColor().isValid() )
878         {
879             pItem->setBaseTextColor( d->baseTextColor );
880             //qDebug() << "Using PD's base text color";
881         }
882         else
883         {
884             //qDebug() << "Using the item's base text color";
885             pItem->textItem()->setDefaultTextColor( pItem->baseTextColor() );
886         }
887         if( !pItem->customHoveredTextColor() )
888             pItem->setHoveredTextColor( d->hoveredTextColor );
889     }
890     if( !pItem->borderRectItem() )
891     {
892         QGraphicsRectItem *borderRectItem = new QGraphicsRectItem( pItem );
893         borderRectItem->setZValue( -5 );
894         pItem->setBorderRectItem( borderRectItem );
895         if( !pItem->customHoveredBorderPen() )
896             pItem->setHoveredBorderPen( d->hoveredBorderPen );
897         if( !pItem->customHoveredFillBrush() )
898             pItem->setHoveredFillBrush( d->hoveredFillBrush );
899     }
900     d->reposItems();
901     pItem->setPopupDropper( this );
902     d->scene->addItem( pItem );
903 }
904 
items() const905 QList<PopupDropperItem*> PopupDropper::items() const
906 {
907     QList<PopupDropperItem*> list;
908     foreach( PopupDropperItem *item, d->pdiItems )
909         list.append( item );
910 
911     return list;
912 }
913 
914 //Won't currently work for > 1 level of submenu!
915 //TODO: Figure out a better way. (Does anything else work > 1 level?)
submenuItems(const PopupDropperItem * item) const916 QList<PopupDropperItem*> PopupDropper::submenuItems( const PopupDropperItem *item ) const
917 {
918     QList<PopupDropperItem*> list;
919     if( !item || !item->isSubmenuTrigger() || !d->submenuMap.contains( item->action() ) )
920         return list;
921 
922     PopupDropperPrivate *pdp = d->submenuMap[item->action()];
923     list.reserve(pdp->pdiItems.count());
924     foreach( PopupDropperItem *pdi, pdp->pdiItems )
925         list.append( pdi );
926 
927     return list;
928 }
929 
930 //Goes through and calls the callback on all items, including submenuItems
931 //which can be adjusted differently by checking isSubmenuTrigger()
forEachItem(void callback (void *))932 void PopupDropper::forEachItem( void callback(void*) )
933 {
934     forEachItemPrivate( d, callback );
935 }
936 
forEachItemPrivate(PopupDropperPrivate * pdp,void callback (void * item))937 void PopupDropper::forEachItemPrivate( PopupDropperPrivate *pdp, void callback(void* item) )
938 {
939     foreach( PopupDropperItem *item, pdp->pdiItems )
940         callback( item );
941     foreach( QAction *action, pdp->submenuMap.keys() )
942         forEachItemPrivate( pdp->submenuMap[action], callback );
943 }
944 
addSeparator(PopupDropperItem * separator)945 void PopupDropper::addSeparator( PopupDropperItem* separator )
946 {
947 
948     if( !separator )
949     {
950         //qDebug() << "Action is not a separator!";
951         return;
952     }
953 
954     separator->setSeparator( true );
955 
956     if( separator->separatorStyle() == PopupDropperItem::TextSeparator )
957     {
958         //qDebug() << "Separator style is text";
959         addItem( separator );
960     }
961 
962     //qDebug() << "Separator style is line";
963     QPen linePen;
964     if( separator && separator->hasLineSeparatorPen() )
965         linePen = separator->lineSeparatorPen();
966     else
967     {
968         linePen.setWidth( 2 );
969         linePen.setCapStyle( Qt::RoundCap );
970         linePen.setStyle( Qt::DotLine );
971         linePen.setColor( QColor( 255, 255, 255 ) );
972     }
973 
974     //qDebug() << "scene width = " << d->scene->width() << ", horizontalOffset = " << d->horizontalOffset;
975     //qDebug() << "right side = " << qreal(d->scene->width() - d->horizontalOffset);
976     QGraphicsLineItem* lineItem = new QGraphicsLineItem( 0, 0, 0, 0 );
977     d->allItems.append( lineItem );
978     lineItem->setPen( linePen );
979     d->reposItems();
980     d->scene->addItem( lineItem );
981 }
982