1 // Copyright (C) 2007-2018 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #include "QtxNotify.h"
27 #include <QMouseEvent>
28 #include <QApplication>
29 #include <QTextDocument>
30 #include <QDesktopWidget>
31 #include <QSharedPointer>
32 #include <QPropertyAnimation>
33 #include <QParallelAnimationGroup>
36 \brief QtxNotify::NotifyWidget
37 Class represented notification widget
40 class QtxNotify::NotifyWidget : public QWidget
42 typedef QSharedPointer<QTextDocument> TextDocument;
45 NotifyWidget(QtxNotify*, const QString& title,
46 const QString& text, const int timeout, QWidget* parent = 0);
47 virtual ~NotifyWidget();
49 QtxNotify* notifyMgr() const;
53 QString title() const;
56 void setId(const int);
57 void setText(const QString&);
58 void setTitle(const QString&);
59 void setTimeout(const int);
61 virtual QSize sizeHint() const;
62 virtual QSize minimumSizeHint() const;
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*);
72 int frameWidth() const;
73 int notificationWidth() const;
75 QRect textRect() const;
76 QRect titleRect() const;
77 QRect closeRect() const;
78 TextDocument textDocument() const;
87 QtxNotify* myNotifyMgr;
89 bool myIsClosePressed;
93 \brief Constructor [private]
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),
100 myTimer(new QTimer(this)),
102 myIsClosePressed(false)
104 myTimer->setSingleShot(true);
106 connect(myTimer, &QTimer::timeout, this, &QtxNotify::NotifyWidget::onTimeout);
109 setMouseTracking(true);
115 QtxNotify::NotifyWidget::~NotifyWidget()
120 \brief Gets the notification manager.
121 \return notification manager instance
123 QtxNotify* QtxNotify::NotifyWidget::notifyMgr() const
129 \brief Gets the notification identifier.
130 \return notification identifier
132 int QtxNotify::NotifyWidget::id() const
138 \brief Gets the notification text.
139 \return notification text string
141 QString QtxNotify::NotifyWidget::text() const
147 \brief Gets the notification title.
148 \return notification title string
150 QString QtxNotify::NotifyWidget::title() const
156 \brief Gets the notification timeout.
157 \return notification timeout
159 int QtxNotify::NotifyWidget::timeout() const
161 return myTimer->interval();
165 \brief Sets the notification timeout.
166 \param id - notification identifier
168 void QtxNotify::NotifyWidget::setId(const int id)
174 \brief Sets the notification text.
175 \param text - notification text string
177 void QtxNotify::NotifyWidget::setText(const QString& text)
185 \brief Sets the notification title.
186 \param title - notification title string
188 void QtxNotify::NotifyWidget::setTitle(const QString& title)
195 \brief Sets the notification timeout.
196 \param timeout - notification timeout
198 void QtxNotify::NotifyWidget::setTimeout(const int timeout)
200 if (myTimer->isActive())
202 myTimer->setInterval(timeout);
204 if (isVisible() && myTimer->interval() > 0)
209 \brief Gets the notification widget size hint.
212 QSize QtxNotify::NotifyWidget::sizeHint() const
214 return minimumSizeHint();
218 \brief Gets the notification widget minimum size hint.
219 \return minimum size hint
221 QSize QtxNotify::NotifyWidget::minimumSizeHint() const
223 int frame = frameWidth();
224 int width = notificationWidth();
225 TextDocument doc = textDocument();
226 int height = (int)doc->size().height() + 2 * frame;
228 height += fontMetrics().height() + 2 * frame;
230 return QSize(width, height);
233 void QtxNotify::NotifyWidget::showEvent(QShowEvent* e)
235 QWidget::showEvent(e);
237 if (!myTimer->isActive() && myTimer->interval() > 0)
242 \brief Reimplemented for notification drawing.
244 void QtxNotify::NotifyWidget::paintEvent(QPaintEvent*)
246 QPainter painter(this);
248 QRect clsRect = closeRect();
249 QRect ttlRect = titleRect();
250 QRect txtRect = textRect();
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);
260 painter.setPen(Qt::white);
263 painter.setFont(fnt);
264 ttlRect.setRight(clsRect.left());
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);
276 if (myIsClosePressed && clsRect.contains(mapFromGlobal(QCursor::pos())))
278 painter.fillRect(clsRect.adjusted(0, 0, -1, -1), Qt::gray);
279 clsRect = clsRect.adjusted(1, 1, 0, 0);
283 painter.fillRect(clsRect.adjusted(1, 1, 0, 0), Qt::lightGray);
284 clsRect = clsRect.adjusted(0, 0, -1, -1);
286 painter.fillRect(clsRect, Qt::red);
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());
299 painter.setPen(Qt::black);
300 TextDocument doc = textDocument();
301 painter.translate(txtRect.topLeft());
302 doc->drawContents(&painter, QRectF(QPointF(0, 0), txtRect.size()));
310 \brief Reimplemented for handling close button.
312 void QtxNotify::NotifyWidget::mouseMoveEvent(QMouseEvent*)
314 if (myIsClosePressed)
319 \brief Reimplemented for handling close button.
321 void QtxNotify::NotifyWidget::mousePressEvent(QMouseEvent* e)
323 if (e->button() == Qt::LeftButton && closeRect().contains(e->pos()))
325 myIsClosePressed = true;
331 \brief Reimplemented for handling close button.
333 void QtxNotify::NotifyWidget::mouseReleaseEvent(QMouseEvent* e)
335 if (e->button() == Qt::LeftButton)
337 if (myIsClosePressed && closeRect().contains(e->pos()))
339 notifyMgr()->hideNotification(id());
341 myIsClosePressed = false;
347 \brief Gets the notification frame with
348 \return Frame width in pixels
350 int QtxNotify::NotifyWidget::frameWidth() const
356 \brief Gets the notification window with
357 \return width in pixels
359 int QtxNotify::NotifyWidget::notificationWidth() const
361 return notifyMgr()->notificationWidth();
365 \brief Gets the text document with actual text and size.
366 \return text document instance
368 QtxNotify::NotifyWidget::TextDocument QtxNotify::NotifyWidget::textDocument() const
370 TextDocument doc(new QTextDocument());
371 if (Qt::mightBeRichText(text()))
372 doc->setHtml(text());
374 doc->setPlainText(text());
375 doc->setTextWidth(notificationWidth() - 2 * frameWidth());
380 \brief Gets the notification message text rectangle.
381 \return text rectangle
383 QRect QtxNotify::NotifyWidget::textRect() const
385 int frame = frameWidth();
386 return QRect(frame, 3 * frame + fontMetrics().height(),
387 width() - 2 * frame, height() - 4 * frame - fontMetrics().height());
391 \brief Gets the notification title rectangle
392 \return title rectangle
394 QRect QtxNotify::NotifyWidget::titleRect() const
396 int frame = frameWidth();
397 return QRect(frame, frame, width() - 2 * frame, fontMetrics().height() + 2 * frame);
401 \brief Gets the notification close button rectangle.
402 \return close rectangle
404 QRect QtxNotify::NotifyWidget::closeRect() const
406 int frame = frameWidth();
407 int size = fontMetrics().height();
408 return QRect(width() - 2 * frame - size, 2 * frame, size, size);
412 \brief Invoked when notification close timeout and hide the notification.
414 void QtxNotify::NotifyWidget::onTimeout()
416 notifyMgr()->hideNotification(id());
421 Class that manages all notification instances. Performs show, hide, placing, etc
427 QtxNotify::QtxNotify(QObject* parent)
431 myPlacement(TopToBottom),
432 myAlignment(Qt::AlignRight|Qt::AlignTop),
436 myArrangeTimer = new QTimer(this);
437 myArrangeTimer->setInterval(0);
438 myArrangeTimer->setSingleShot(true);
439 connect(myArrangeTimer, SIGNAL(timeout()), this, SLOT(onArrangeTimeout()));
442 QWidgetList windows = QApplication::topLevelWidgets();
443 for ( QWidgetList::iterator it = windows.begin(); it != windows.end() && !window; ++it )
446 if (win->isVisible() &&
447 (win->windowType() == Qt::Widget || win->windowType() == Qt::Window))
451 setWindow(window ? window : QApplication::desktop());
457 QtxNotify::QtxNotify(QWidget* win, QObject* parent)
461 myPlacement(TopToBottom),
462 myAlignment(Qt::AlignRight|Qt::AlignTop),
466 myArrangeTimer = new QTimer(this);
467 myArrangeTimer->setInterval(0);
468 myArrangeTimer->setSingleShot(true);
469 connect(myArrangeTimer, SIGNAL(timeout()), this, SLOT(onArrangeTimeout()));
477 QtxNotify::~QtxNotify()
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
490 int QtxNotify::showNotification(const QString& text, const QString& title, int timeout)
492 QtxNotify::NotifyWidget* notify = notification(text);
495 notify = new QtxNotify::NotifyWidget(this, title, text, timeout, window());
496 notify->setId(generateId());
500 myNotifications.removeAll(notify);
501 notify->setTimeout(timeout);
502 notify->setTitle(title);
506 myNotifications.append(notify);
513 \brief Closes the notifications with spectified text.
514 \param text - Notification text
516 void QtxNotify::hideNotification(const QString& text)
518 removeNotification(notification(text));
522 \brief Closes the notifications with spectified identifier.
523 \param text - Notification identifier
525 void QtxNotify::hideNotification(const int id)
527 removeNotification(notification(id));
531 \brief Gets the reference widget for all notifications.
532 \return reference widget
534 QWidget* QtxNotify::window() const
540 \brief Sets the reference widget for all notifications.
541 \param win - reference widget
543 void QtxNotify::setWindow(QWidget* window)
545 if (myWindow != window)
548 myWindow->removeEventFilter(this);
551 myWindow->installEventFilter(this);
557 \brief Gets the animation time in msec for notification modifications.
558 \return animation time
560 int QtxNotify::animationTime() const
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
570 void QtxNotify::setAnimationTime(const int time)
576 \brief Gets the notification placement policy.
577 \return notification placement policy
579 QtxNotify::PlacementPolicy QtxNotify::placementPolicy() const
585 \brief Sets the notification placement policy.
586 \param placement - notification placement policy
588 void QtxNotify::setPlacementPolicy(const QtxNotify::PlacementPolicy& placement)
590 if (myPlacement != placement)
592 myPlacement = placement;
598 \brief Gets the notification placement base point alignment.
599 \return alignment flags
601 int QtxNotify::alignment() const
607 \brief Sets the notification placement base point alignment.
608 \param falgs - alignment flags
610 void QtxNotify::setAlignment(const int flags)
612 if (myAlignment != flags)
620 \brief Reimplemented for tracking reference widget size changing and
621 update notification geometries.
622 \param o - handled object
623 \param e - handled event
625 bool QtxNotify::eventFilter(QObject* o, QEvent* e)
627 if (e->type() == QEvent::Resize && o == window())
631 return QObject::eventFilter(o, e);
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
641 double QtxNotify::notificationSize() const
647 \brief Sets the notification width size of each notification.
648 \param size - notification size
650 void QtxNotify::setNotificationSize(const double size)
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
664 void QtxNotify::arrangeNotifications(bool anim)
666 if (myNotifications.isEmpty())
669 QWidget* ref = window() ? window() : QApplication::desktop();
670 QPoint refPoint = referencePoint();
671 if (myNotifications.first()->isWindow())
672 refPoint = ref->mapToGlobal(refPoint);
675 NotifyList notifications;
676 bool reverse = placementPolicy() == QtxNotify::BottomToTop;
677 if (alignment() & Qt::AlignBottom)
682 for ( NotifyList::reverse_iterator it = myNotifications.rbegin(); it != myNotifications.rend(); ++it )
683 notifications.append(*it);
687 for ( NotifyList::iterator it = myNotifications.begin(); it != myNotifications.end(); ++it )
688 notifications.append(*it);
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 )
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 ));
702 QParallelAnimationGroup* animGroup = 0;
703 if (isAnimated() && anim)
704 animGroup = new QParallelAnimationGroup();
706 for ( NotifyList::iterator iter = notifications.begin(); iter != notifications.end(); ++iter )
708 QtxNotify::NotifyWidget* notify = *iter;
709 QRect geom = geoms[notify];
710 if (notify->parentWidget())
711 geom = QRect(notify->parentWidget()->mapFromGlobal(geom.topLeft()), geom.size());
713 bool isvis = notify->isVisible();
717 QPropertyAnimation* animation = new QPropertyAnimation(notify, "geometry");
718 animation->setDuration(animationTime());
719 animGroup->addAnimation(animation);
722 animation->setStartValue(notify->geometry());
723 animation->setEndValue(geom);
728 animation->setStartValue(QRect(QPoint(geom.center().x(), geom.top()), QSize(0, geom.height())));
729 animation->setEndValue(geom);
734 notify->setGeometry(geom);
739 startAnimation(animGroup);
742 QPoint QtxNotify::referencePoint() const
745 QWidget* ref = window() ? window() : QApplication::desktop();
748 int notifywidth = notificationWidth();
749 int align = alignment() > 0 ? alignment() : Qt::AlignLeft | Qt::AlignTop;
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);
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);
765 return ref->mapToGlobal(refPoint);
769 \brief Gets the notification with specified identifier.
770 \param id - notification identifier
771 \return notification instance
773 QtxNotify::NotifyWidget* QtxNotify::notification(const int id)
775 QtxNotify::NotifyWidget* notify = 0;
776 for (NotifyList::const_iterator it = myNotifications.begin(); it != myNotifications.end() && !notify; ++it)
778 if ((*it)->id() == id)
785 \brief Gets the notification with specified text.
786 \param text - notification text
787 \return notification instance
789 QtxNotify::NotifyWidget* QtxNotify::notification(const QString& text)
791 QtxNotify::NotifyWidget* notify = 0;
792 for (NotifyList::const_iterator it = myNotifications.begin(); it != myNotifications.end() && !notify; ++it)
794 if ((*it)->text() == text)
801 \brief Removes the specified notification.
802 \param notify - notification instance
804 void QtxNotify::removeNotification(QtxNotify::NotifyWidget* notify)
809 myNotifications.removeAll(notify);
810 notify->setTimeout(0);
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())));
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);
827 notify->deleteLater();
828 arrangeNotifications();
833 \brief Generates the new free notification identifier.
834 \return generated identifier
836 int QtxNotify::generateId() const
839 for (NotifyList::const_iterator it = myNotifications.begin(); it != myNotifications.end(); ++it)
840 map.insert((*it)->id(), true);
843 while (map.contains(id))
849 \brief Gets the notification window with
850 \return width in pixels
852 int QtxNotify::notificationWidth() const
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);
862 \brief Schedule delayed notification arrangement.
864 void QtxNotify::triggerArrange()
866 if (myArrangeTimer->isActive())
867 myArrangeTimer->stop();
868 myArrangeTimer->start();
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
876 bool QtxNotify::isAnimated() const
878 return myAnimTime > 0;
882 \brief Gets the active animation state. If there is exist active animation then return true otherwise false.
883 \return active animation state
885 bool QtxNotify::hasAcitveAnimation() const
888 for (AnimationList::const_iterator it = myAnimations.begin(); it != myAnimations.end() && !has; ++it)
889 has = (*it)->state() == QAbstractAnimation::Running;
894 \brief Starts the given animation.
895 \param animation - animation instance
897 void QtxNotify::startAnimation(QAbstractAnimation* animation)
899 myAnimations.append(animation);
900 connect(animation, SIGNAL(destroyed(QObject*)), this, SLOT(onAnimationDestroyed(QObject*)));
902 animation->start(QAbstractAnimation::DeleteWhenStopped);
906 \brief Stops the given animation.
907 \param animation - animation instance
909 void QtxNotify::stopAnimation(QAbstractAnimation* animation)
912 animation->deleteLater();
916 \brief Invoked when animation is destroyed. Removes the destroyed animation reference from list.
917 \param obj - destroyed animation reference
919 void QtxNotify::onAnimationDestroyed(QObject* obj)
921 myAnimations.removeAll((QAbstractAnimation*)obj);
925 \brief Performs the scheduled delayed notification arrangment.
927 void QtxNotify::onArrangeTimeout()
929 if (hasAcitveAnimation())
933 arrangeNotifications(!myAnimBlock);
939 \brief Performs the notification arrangment with disabled animation.
941 void QtxNotify::updateArrangement()