Salome HOME
0023564: [EDF] AsterStudy: introduce a feature to show popup notifications
[modules/gui.git] / src / Qtx / QtxNotify.cxx
1 // Copyright (C) 2007-2018  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #include "QtxNotify.h"
24
25 #include <QTimer>
26 #include <QPainter>
27 #include <QMouseEvent>
28 #include <QApplication>
29 #include <QTextDocument>
30 #include <QDesktopWidget>
31 #include <QSharedPointer>
32 #include <QPropertyAnimation>
33 #include <QParallelAnimationGroup>
34
35 /*!
36     \brief QtxNotify::NotifyWidget
37     Class represented notification widget
38 */
39
40 class QtxNotify::NotifyWidget : public QWidget
41 {
42     typedef QSharedPointer<QTextDocument> TextDocument;
43
44 public:
45     NotifyWidget(QtxNotify*, const QString& title,
46                  const QString& text, const int timeout, QWidget* parent = 0);
47     virtual ~NotifyWidget();
48
49     QtxNotify*             notifyMgr() const;
50
51     int                    id() const;
52     QString                text() const;
53     QString                title() const;
54     int                    timeout() const;
55
56     void                   setId(const int);
57     void                   setText(const QString&);
58     void                   setTitle(const QString&);
59     void                   setTimeout(const int);
60
61     virtual QSize          sizeHint() const;
62     virtual QSize          minimumSizeHint() const;
63
64 protected:
65     virtual void           showEvent(QShowEvent*);
66     virtual void           paintEvent(QPaintEvent*);
67     virtual void           mouseMoveEvent(QMouseEvent*);
68     virtual void           mousePressEvent(QMouseEvent*);
69     virtual void           mouseReleaseEvent(QMouseEvent*);
70
71 private:
72     int                    frameWidth() const;
73     int                    notificationWidth() const;
74
75     QRect                  textRect() const;
76     QRect                  titleRect() const;
77     QRect                  closeRect() const;
78     TextDocument           textDocument() const;
79
80     void                   onTimeout();
81
82 private:
83     int                    myId;
84     QString                myText;
85     QString                myTitle;
86     QTimer*                myTimer;
87     QtxNotify*             myNotifyMgr;
88
89     bool                   myIsClosePressed;
90 };
91
92 /*!
93     \brief Constructor [private]
94 */
95 QtxNotify::NotifyWidget::NotifyWidget(QtxNotify* mgr, const QString& title, const QString& text,
96                                       const int timeout, QWidget* parent)
97     : QWidget(parent, (parent ? Qt::Widget : Qt::Tool) | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint),
98       myTitle(title),
99       myText(text),
100       myTimer(new QTimer(this)),
101       myNotifyMgr(mgr),
102       myIsClosePressed(false)
103 {
104     myTimer->setSingleShot(true);
105
106     connect(myTimer, &QTimer::timeout, this, &QtxNotify::NotifyWidget::onTimeout);
107
108     setTimeout(timeout);
109     setMouseTracking(true);
110 }
111
112 /*!
113     \brief Destructor
114 */
115 QtxNotify::NotifyWidget::~NotifyWidget()
116 {
117 }
118
119 /*!
120     \brief Gets the notification manager.
121     \return notification manager instance
122 */
123 QtxNotify* QtxNotify::NotifyWidget::notifyMgr() const
124 {
125     return myNotifyMgr;
126 }
127
128 /*!
129     \brief Gets the notification identifier.
130     \return notification identifier
131 */
132 int QtxNotify::NotifyWidget::id() const
133 {
134     return myId;
135 }
136
137 /*!
138     \brief Gets the notification text.
139     \return notification text string
140 */
141 QString QtxNotify::NotifyWidget::text() const
142 {
143     return myText;
144 }
145
146 /*!
147     \brief Gets the notification title.
148     \return notification title string
149 */
150 QString QtxNotify::NotifyWidget::title() const
151 {
152     return myTitle;
153 }
154
155 /*!
156     \brief Gets the notification timeout.
157     \return notification timeout
158 */
159 int QtxNotify::NotifyWidget::timeout() const
160 {
161     return myTimer->interval();
162 }
163
164 /*!
165     \brief Sets the notification timeout.
166     \param id - notification identifier
167 */
168 void QtxNotify::NotifyWidget::setId(const int id)
169 {
170     myId = id;
171 }
172
173 /*!
174     \brief Sets the notification text.
175     \param text - notification text string
176 */
177 void QtxNotify::NotifyWidget::setText(const QString& text)
178 {
179     myText = text;
180     updateGeometry();
181     update();
182 }
183
184 /*!
185     \brief Sets the notification title.
186     \param title - notification title string
187 */
188 void QtxNotify::NotifyWidget::setTitle(const QString& title)
189 {
190     myTitle = title;
191     update();
192 }
193
194 /*!
195     \brief Sets the notification timeout.
196     \param timeout - notification timeout
197 */
198 void QtxNotify::NotifyWidget::setTimeout(const int timeout)
199 {
200     if (myTimer->isActive())
201         myTimer->stop();
202     myTimer->setInterval(timeout);
203
204     if (isVisible() && myTimer->interval() > 0)
205         myTimer->start();
206 }
207
208 /*!
209     \brief Gets the notification widget size hint.
210     \return size hint
211 */
212 QSize QtxNotify::NotifyWidget::sizeHint() const
213 {
214     return minimumSizeHint();
215 }
216
217 /*!
218     \brief Gets the notification widget minimum size hint.
219     \return minimum size hint
220 */
221 QSize QtxNotify::NotifyWidget::minimumSizeHint() const
222 {
223     int frame = frameWidth();
224     int width = notificationWidth();
225     TextDocument doc = textDocument();
226     int height = (int)doc->size().height() + 2 * frame;
227
228     height += fontMetrics().height() + 2 * frame;
229
230     return QSize(width, height);
231 }
232
233 void QtxNotify::NotifyWidget::showEvent(QShowEvent* e)
234 {
235   QWidget::showEvent(e);
236
237   if (!myTimer->isActive() && myTimer->interval() > 0)
238     myTimer->start();
239 }
240
241 /*!
242     \brief Reimplemented for notification drawing.
243 */
244 void QtxNotify::NotifyWidget::paintEvent(QPaintEvent*)
245 {
246     QPainter painter(this);
247
248     QRect clsRect = closeRect();
249     QRect ttlRect = titleRect();
250     QRect txtRect = textRect();
251
252     // Fills background and frame
253     painter.setRenderHint(QPainter::TextAntialiasing);
254     painter.fillRect(QRect(QPoint(0,0), size()), Qt::darkGray);
255     painter.fillRect(txtRect, Qt::white);
256
257     // Draw title
258     painter.save();
259
260     painter.setPen(Qt::white);
261     QFont fnt = font();
262     fnt.setBold(true);
263     painter.setFont(fnt);
264     ttlRect.setRight(clsRect.left());
265
266     QString titleText = title().trimmed();
267     if (QFontMetrics(painter.font()).width(titleText) > ttlRect.width())
268         titleText = QFontMetrics(painter.font()).elidedText(titleText, Qt::ElideRight, ttlRect.width() - 5);
269     painter.drawText(ttlRect.adjusted(2, 0, 0, 0), Qt::AlignVCenter, titleText);
270
271     painter.restore();
272
273     // Draw close button
274     painter.save();
275
276     if (myIsClosePressed && clsRect.contains(mapFromGlobal(QCursor::pos())))
277     {
278         painter.fillRect(clsRect.adjusted(0, 0, -1, -1), Qt::gray);
279         clsRect = clsRect.adjusted(1, 1, 0, 0);
280     }
281     else
282     {
283         painter.fillRect(clsRect.adjusted(1, 1, 0, 0), Qt::lightGray);
284         clsRect = clsRect.adjusted(0, 0, -1, -1);
285     }
286     painter.fillRect(clsRect, Qt::red);
287
288     int m = 2;
289     painter.setPen(Qt::white);
290     QRect rect = clsRect.adjusted(m, m, -m, -m);
291     painter.drawLine(rect.topLeft(), rect.bottomRight());
292     painter.drawLine(rect.topRight(), rect.bottomLeft());
293
294     painter.restore();
295
296     // Draw text
297     painter.save();
298
299     painter.setPen(Qt::black);
300     TextDocument doc = textDocument();
301     painter.translate(txtRect.topLeft());
302     doc->drawContents(&painter, QRectF(QPointF(0, 0), txtRect.size()));
303
304     painter.restore();
305
306     painter.end();
307 }
308
309 /*!
310     \brief Reimplemented for handling close button.
311 */
312 void QtxNotify::NotifyWidget::mouseMoveEvent(QMouseEvent*)
313 {
314     if (myIsClosePressed)
315         update();
316 }
317
318 /*!
319     \brief Reimplemented for handling close button.
320 */
321 void QtxNotify::NotifyWidget::mousePressEvent(QMouseEvent* e)
322 {
323     if (e->button() == Qt::LeftButton && closeRect().contains(e->pos()))
324     {
325         myIsClosePressed = true;
326         update();
327     }
328 }
329
330 /*!
331     \brief Reimplemented for handling close button.
332 */
333 void QtxNotify::NotifyWidget::mouseReleaseEvent(QMouseEvent* e)
334 {
335     if (e->button() == Qt::LeftButton)
336     {
337         if (myIsClosePressed && closeRect().contains(e->pos()))
338         {
339             notifyMgr()->hideNotification(id());
340         }
341         myIsClosePressed = false;
342         update();
343     }
344 }
345
346 /*!
347     \brief Gets the notification frame with
348     \return Frame width in pixels
349 */
350 int QtxNotify::NotifyWidget::frameWidth() const
351 {
352     return 2;
353 }
354
355 /*!
356     \brief Gets the notification window with
357     \return width in pixels
358 */
359 int QtxNotify::NotifyWidget::notificationWidth() const
360 {
361     return notifyMgr()->notificationWidth();
362 }
363
364 /*!
365     \brief Gets the text document with actual text and size.
366     \return text document instance
367 */
368 QtxNotify::NotifyWidget::TextDocument QtxNotify::NotifyWidget::textDocument() const
369 {
370     TextDocument doc(new QTextDocument());
371     if (Qt::mightBeRichText(text()))
372         doc->setHtml(text());
373     else
374         doc->setPlainText(text());
375     doc->setTextWidth(notificationWidth() - 2 * frameWidth());
376     return doc;
377 }
378
379 /*!
380     \brief Gets the notification message text rectangle.
381     \return text rectangle
382 */
383 QRect QtxNotify::NotifyWidget::textRect() const
384 {
385     int frame = frameWidth();
386     return QRect(frame, 3 * frame + fontMetrics().height(),
387                  width() - 2 * frame, height() - 4 * frame - fontMetrics().height());
388 }
389
390 /*!
391     \brief Gets the notification title rectangle
392     \return title rectangle
393 */
394 QRect QtxNotify::NotifyWidget::titleRect() const
395 {
396     int frame = frameWidth();
397     return QRect(frame, frame, width() - 2 * frame, fontMetrics().height() + 2 * frame);
398 }
399
400 /*!
401     \brief Gets the notification close button rectangle.
402     \return close rectangle
403 */
404 QRect QtxNotify::NotifyWidget::closeRect() const
405 {
406     int frame = frameWidth();
407     int size = fontMetrics().height();
408     return QRect(width() - 2 * frame - size, 2 * frame, size, size);
409 }
410
411 /*!
412     \brief Invoked when notification close timeout and hide the notification.
413 */
414 void QtxNotify::NotifyWidget::onTimeout()
415 {
416     notifyMgr()->hideNotification(id());
417 }
418
419 /*!
420     \brief QtxNotify
421     Class that manages all notification instances. Performs show, hide, placing, etc
422 */
423
424 /*!
425     \brief Constructor.
426 */
427 QtxNotify::QtxNotify(QObject* parent)
428     : QObject(parent),
429     mySize(250),
430     myWindow(0),
431     myPlacement(TopToBottom),
432     myAlignment(Qt::AlignRight|Qt::AlignTop),
433     myAnimTime(0),
434     myAnimBlock(false)
435 {
436     myArrangeTimer = new QTimer(this);
437     myArrangeTimer->setInterval(0);
438     myArrangeTimer->setSingleShot(true);
439     connect(myArrangeTimer, SIGNAL(timeout()), this, SLOT(onArrangeTimeout()));
440
441     QWidget* window = 0;
442     QWidgetList windows = QApplication::topLevelWidgets();
443     for ( QWidgetList::iterator it = windows.begin(); it != windows.end() && !window; ++it )
444     {
445         QWidget* win = *it;
446         if (win->isVisible() &&
447             (win->windowType() == Qt::Widget || win->windowType() == Qt::Window))
448             window = win;
449     }
450
451     setWindow(window ? window : QApplication::desktop());
452 }
453
454 /*!
455     \brief Constructor.
456 */
457 QtxNotify::QtxNotify(QWidget* win, QObject* parent)
458     : QObject(parent),
459     mySize(250),
460     myWindow(0),
461     myPlacement(TopToBottom),
462     myAlignment(Qt::AlignRight|Qt::AlignTop),
463     myAnimTime(0),
464     myAnimBlock(false)
465 {
466     myArrangeTimer = new QTimer(this);
467     myArrangeTimer->setInterval(0);
468     myArrangeTimer->setSingleShot(true);
469     connect(myArrangeTimer, SIGNAL(timeout()), this, SLOT(onArrangeTimeout()));
470
471     setWindow(win);
472 }
473
474 /*!
475     \brief Destructor.
476 */
477 QtxNotify::~QtxNotify()
478 {
479 }
480
481 /*!
482     \brief Shows the notifications with spectified text, title and automatic close timeout.
483     Notification will be automatically closed after specified timeout in msec. If
484     timeout is zero then automatic closing doesn't performed.
485     \param text - Notification text
486     \param title - Notification title
487     \param timeout - Notification close timeout in msec
488     \return notification identifier
489 */
490 int QtxNotify::showNotification(const QString& text, const QString& title, int timeout)
491 {
492     QtxNotify::NotifyWidget* notify = notification(text);
493     if (!notify)
494     {
495         notify = new QtxNotify::NotifyWidget(this, title, text, timeout, window());
496         notify->setId(generateId());
497     }
498     else
499     {
500         myNotifications.removeAll(notify);
501         notify->setTimeout(timeout);
502         notify->setTitle(title);
503         notify->hide();
504     }
505
506     myNotifications.append(notify);
507     triggerArrange();
508
509     return notify->id();
510 }
511
512 /*!
513     \brief Closes the notifications with spectified text.
514     \param text - Notification text
515 */
516 void QtxNotify::hideNotification(const QString& text)
517 {
518     removeNotification(notification(text));
519 }
520
521 /*!
522     \brief Closes the notifications with spectified identifier.
523     \param text - Notification identifier
524 */
525 void QtxNotify::hideNotification(const int id)
526 {
527     removeNotification(notification(id));
528 }
529
530 /*!
531     \brief Gets the reference widget for all notifications.
532     \return reference widget
533 */
534 QWidget* QtxNotify::window() const
535 {
536     return myWindow;
537 }
538
539 /*!
540     \brief Sets the reference widget for all notifications.
541     \param win - reference widget
542 */
543 void QtxNotify::setWindow(QWidget* window)
544 {
545     if (myWindow != window)
546     {
547         if (myWindow)
548             myWindow->removeEventFilter(this);
549         myWindow = window;
550         if (myWindow)
551             myWindow->installEventFilter(this);
552         updateArrangement();
553     }
554 }
555
556 /*!
557     \brief Gets the animation time in msec for notification modifications.
558     \return animation time
559 */
560 int QtxNotify::animationTime() const
561 {
562     return myAnimTime;
563 }
564
565 /*!
566     \brief Sets the animation time in msec for notification modifications.
567     If animation time is zero then animation is disabled.
568     \param time - animation time
569 */
570 void QtxNotify::setAnimationTime(const int time)
571 {
572     myAnimTime = time;
573 }
574
575 /*!
576     \brief Gets the notification placement policy.
577     \return notification placement policy
578 */
579 QtxNotify::PlacementPolicy QtxNotify::placementPolicy() const
580 {
581     return myPlacement;
582 }
583
584 /*!
585     \brief Sets the notification placement policy.
586     \param placement - notification placement policy
587 */
588 void QtxNotify::setPlacementPolicy(const QtxNotify::PlacementPolicy& placement)
589 {
590     if (myPlacement != placement)
591     {
592         myPlacement = placement;
593         updateArrangement();
594     }
595 }
596
597 /*!
598     \brief Gets the notification placement base point alignment.
599     \return alignment flags
600 */
601 int QtxNotify::alignment() const
602 {
603     return myAlignment;
604 }
605
606 /*!
607     \brief Sets the notification placement base point alignment.
608     \param falgs - alignment flags
609 */
610 void QtxNotify::setAlignment(const int flags)
611 {
612     if (myAlignment != flags)
613     {
614         myAlignment = flags;
615         updateArrangement();
616     }
617 }
618
619 /*!
620     \brief Reimplemented for tracking reference widget size changing and
621     update notification geometries.
622     \param o - handled object
623     \param e - handled event
624 */
625 bool QtxNotify::eventFilter(QObject* o, QEvent* e)
626 {
627     if (e->type() == QEvent::Resize && o == window())
628     {
629         updateArrangement();
630     }
631     return QObject::eventFilter(o, e);
632 }
633
634 /*!
635     \brief Gets the notification width size of each notification. If size value more than 1
636            then it will be interpreted as absolute width in pixels. If size value in range
637            between 0 and 1 then notification width will be calculated as relative part of
638            reference window width.
639     \return notification size
640 */
641 double QtxNotify::notificationSize() const
642 {
643     return mySize;
644 }
645
646 /*!
647     \brief Sets the notification width size of each notification.
648     \param size - notification size
649 */
650 void QtxNotify::setNotificationSize(const double size)
651 {
652     if (mySize != size)
653     {
654         mySize = size;
655         updateArrangement();
656     }
657 }
658
659 /*!
660     \brief Performs the update of all notification geometries.
661     If parameter anim is true then arrangment will be animated otherwise not.
662     \param anim - use animation
663 */
664 void QtxNotify::arrangeNotifications(bool anim)
665 {
666     if (myNotifications.isEmpty())
667         return;
668
669     QWidget* ref = window() ? window() : QApplication::desktop();
670     QPoint refPoint = referencePoint();
671     if (myNotifications.first()->isWindow())
672         refPoint = ref->mapToGlobal(refPoint);
673
674     int space = 10;
675     NotifyList notifications;
676     bool reverse = placementPolicy() == QtxNotify::BottomToTop;
677     if (alignment() & Qt::AlignBottom)
678         reverse = !reverse;
679
680     if (reverse)
681     {
682         for ( NotifyList::reverse_iterator it = myNotifications.rbegin(); it != myNotifications.rend(); ++it )
683             notifications.append(*it);
684     }
685     else
686     {
687         for ( NotifyList::iterator it = myNotifications.begin(); it != myNotifications.end(); ++it )
688             notifications.append(*it);
689     }
690
691     int sign = alignment() & Qt::AlignBottom ? -1 : 1;
692     QPoint pos = refPoint;
693     QMap<QtxNotify::NotifyWidget*, QRect> geoms;
694     for ( NotifyList::iterator it = notifications.begin(); it != notifications.end(); ++it )
695     {
696         QtxNotify::NotifyWidget* notify = *it;
697         QSize size = notify->sizeHint();
698         geoms.insert(notify, QRect(QPoint(pos.x(), pos.y() - (sign < 0 ? size.height() : 0)), size));
699         pos = QPoint(pos.x(), pos.y() + sign * ( size.height() + space ));
700     }
701
702     QParallelAnimationGroup* animGroup = 0;
703     if (isAnimated() && anim)
704         animGroup = new QParallelAnimationGroup();
705
706     for ( NotifyList::iterator iter = notifications.begin(); iter != notifications.end(); ++iter )
707     {
708         QtxNotify::NotifyWidget* notify = *iter;
709         QRect geom = geoms[notify];
710         if (notify->parentWidget())
711             geom = QRect(notify->parentWidget()->mapFromGlobal(geom.topLeft()), geom.size());
712
713         bool isvis = notify->isVisible();
714
715         if (animGroup)
716         {
717             QPropertyAnimation* animation = new QPropertyAnimation(notify, "geometry");
718             animation->setDuration(animationTime());
719             animGroup->addAnimation(animation);
720             if (isvis)
721             {
722                 animation->setStartValue(notify->geometry());
723                 animation->setEndValue(geom);
724             }
725             else
726             {
727                 notify->show();
728                 animation->setStartValue(QRect(QPoint(geom.center().x(), geom.top()), QSize(0, geom.height())));
729                 animation->setEndValue(geom);
730             }
731         }
732         else
733         {
734             notify->setGeometry(geom);
735             notify->show();
736         }
737     }
738     if (animGroup)
739         startAnimation(animGroup);
740 }
741
742 QPoint QtxNotify::referencePoint() const
743 {
744     int margin = 5;
745     QWidget* ref = window() ? window() : QApplication::desktop();
746
747     QPoint refPoint;
748     int notifywidth = notificationWidth();
749     int align = alignment() > 0 ? alignment() : Qt::AlignLeft | Qt::AlignTop;
750
751     if (align & Qt::AlignLeft)
752         refPoint.setX(margin);
753     else if (align & Qt::AlignRight)
754         refPoint.setX(ref->width() - notifywidth - margin);
755     else if (align & Qt::AlignHCenter)
756         refPoint.setX((ref->width() - notifywidth) / 2);
757
758     if (align & Qt::AlignTop)
759         refPoint.setY(margin);
760     else if (align & Qt::AlignBottom)
761         refPoint.setY(ref->height() - margin);
762     else if (align & Qt::AlignVCenter)
763         refPoint.setY(ref->height() / 2);
764
765     return ref->mapToGlobal(refPoint);
766 }
767
768 /*!
769     \brief Gets the notification with specified identifier.
770     \param id - notification identifier
771     \return notification instance
772 */
773 QtxNotify::NotifyWidget* QtxNotify::notification(const int id)
774 {
775     QtxNotify::NotifyWidget* notify = 0;
776     for (NotifyList::const_iterator it = myNotifications.begin(); it != myNotifications.end() && !notify; ++it)
777     {
778         if ((*it)->id() == id)
779             notify = *it;
780     }
781     return notify;
782 }
783
784 /*!
785     \brief Gets the notification with specified text.
786     \param text - notification text
787     \return notification instance
788 */
789 QtxNotify::NotifyWidget* QtxNotify::notification(const QString& text)
790 {
791     QtxNotify::NotifyWidget* notify = 0;
792     for (NotifyList::const_iterator it = myNotifications.begin(); it != myNotifications.end() && !notify; ++it)
793     {
794         if ((*it)->text() == text)
795             notify = *it;
796     }
797     return notify;
798 }
799
800 /*!
801     \brief Removes the specified notification.
802     \param notify - notification instance
803 */
804 void QtxNotify::removeNotification(QtxNotify::NotifyWidget* notify)
805 {
806     if (!notify)
807         return;
808
809     myNotifications.removeAll(notify);
810     notify->setTimeout(0);
811
812     if (isAnimated()) {
813         QPropertyAnimation* animation = new QPropertyAnimation(notify, "geometry");
814         animation->setDuration(animationTime());
815         animation->setStartValue(notify->geometry());
816         animation->setEndValue(QRect(QPoint(notify->geometry().center().x(), notify->geometry().top()),
817                                      QSize(0, notify->geometry().height())));
818
819         connect(animation, SIGNAL(finished()), notify, SLOT(hide()));
820         connect(animation, SIGNAL(finished()), notify, SLOT(deleteLater()));
821         connect(animation, SIGNAL(finished()), this, SLOT(onArrangeTimeout()));
822         startAnimation(animation);
823     }
824     else
825     {
826         notify->hide();
827         notify->deleteLater();
828         arrangeNotifications();
829     }
830 }
831
832 /*!
833     \brief Generates the new free notification identifier.
834     \return generated identifier
835 */
836 int QtxNotify::generateId() const
837 {
838     QMap<int, bool> map;
839     for (NotifyList::const_iterator it = myNotifications.begin(); it != myNotifications.end(); ++it)
840         map.insert((*it)->id(), true);
841
842     int id = 0;
843     while (map.contains(id))
844         id++;
845     return id;
846 }
847
848 /*!
849     \brief Gets the notification window with
850     \return width in pixels
851 */
852 int QtxNotify::notificationWidth() const
853 {
854     QWidget* refWin = window();
855     double size = notificationSize();
856     int width = (int)( size > 1 ? size : size * refWin->width() );
857     width = qMin(width, refWin->width() - 10);
858     return width;
859 }
860
861 /*!
862     \brief Schedule delayed notification arrangement.
863 */
864 void QtxNotify::triggerArrange()
865 {
866     if (myArrangeTimer->isActive())
867         myArrangeTimer->stop();
868     myArrangeTimer->start();
869 }
870
871 /*!
872     \brief Gets the animation using state.
873     If it's true then animation will be used during notification modifcations.
874     \return animation using state
875 */
876 bool QtxNotify::isAnimated() const
877 {
878     return myAnimTime > 0;
879 }
880
881 /*!
882     \brief Gets the active animation state. If there is exist active animation then return true otherwise false.
883     \return active animation state
884 */
885 bool QtxNotify::hasAcitveAnimation() const
886 {
887     bool has = false;
888     for (AnimationList::const_iterator it = myAnimations.begin(); it != myAnimations.end() && !has; ++it)
889         has = (*it)->state() == QAbstractAnimation::Running;
890     return has;
891 }
892
893 /*!
894     \brief Starts the given animation.
895     \param animation - animation instance
896 */
897 void QtxNotify::startAnimation(QAbstractAnimation* animation)
898 {
899     myAnimations.append(animation);
900     connect(animation, SIGNAL(destroyed(QObject*)), this, SLOT(onAnimationDestroyed(QObject*)));
901
902     animation->start(QAbstractAnimation::DeleteWhenStopped);
903 }
904
905 /*!
906     \brief Stops the given animation.
907     \param animation - animation instance
908 */
909 void QtxNotify::stopAnimation(QAbstractAnimation* animation)
910 {
911     animation->stop();
912     animation->deleteLater();
913 }
914
915 /*!
916     \brief Invoked when animation is destroyed. Removes the destroyed animation reference from list.
917     \param obj - destroyed animation reference
918 */
919 void QtxNotify::onAnimationDestroyed(QObject* obj)
920 {
921     myAnimations.removeAll((QAbstractAnimation*)obj);
922 }
923
924 /*!
925     \brief Performs the scheduled delayed notification arrangment.
926 */
927 void QtxNotify::onArrangeTimeout()
928 {
929     if (hasAcitveAnimation())
930         triggerArrange();
931     else
932     {
933         arrangeNotifications(!myAnimBlock);
934         myAnimBlock = false;
935     }
936 }
937
938 /*!
939     \brief Performs the notification arrangment with disabled animation.
940 */
941 void QtxNotify::updateArrangement()
942 {
943     myAnimBlock = true;
944     triggerArrange();
945 }