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