1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/wtfpl/COPYING for more details.
12 #include <QApplication>
15 #include <QDesktopWidget>
17 #include <QMouseEvent>
26 const static QString
sLt("<");
27 const static QString
sAmp("&");
30 QString
escapeStr (const QString
&str
) {
32 int len
= str
.length();
33 res
.reserve(len
*11/10);
34 for (int f
= 0; f
< len
; f
++) {
36 switch (ch
.unicode()) {
37 case '<': res
+= sLt
; break;
38 case '&': res
+= sAmp
; break;
39 default: res
+= ch
; break;
48 ///////////////////////////////////////////////////////////////////////////////
49 QSize
K8PopupLayout::sizeHint () const {
50 QDesktopWidget
*desktop
= QApplication::desktop();
51 QRect geometry
= desktop
->availableGeometry();
53 int maxH
= (geometry
.bottom()-geometry
.top())*2/3;
54 QSize res
= QVBoxLayout::sizeHint();
57 if (h
< minH
) h
= minH
;
58 if (h
> maxH
) h
= maxH
;
65 ///////////////////////////////////////////////////////////////////////////////
67 K8PopupFader::K8PopupFader (QWidget
*parent
, bool aIn
) : QWidget(parent
), mPParent(parent
), mIn(aIn
) {
68 mStartColor
= parent
? parent
->palette().window().color() : Qt::white
;
72 mTimer
= new QTimer(this);
73 connect(mTimer
, SIGNAL(timeout()), this, SLOT(update()));
75 setAttribute(Qt::WA_DeleteOnClose
);
76 resize(parent
->size());
80 void K8PopupFader::start () {
81 mCurrentAlpha
= mIn
? 0 : 255;
83 //setMask(mPParent->mask());
88 void K8PopupFader::paintEvent (QPaintEvent
* /*event*/) {
91 QPainter
painter(this);
92 QColor semiTransparentColor
= mStartColor
;
94 semiTransparentColor
.setAlpha(mCurrentAlpha
);
95 painter
.fillRect(rect(), semiTransparentColor
);
97 mCurrentAlpha
+= 255*mTimer
->interval()/mDuration
;
98 end
= (mCurrentAlpha
>= 255);
100 mCurrentAlpha
-= 255*mTimer
->interval()/mDuration
;
101 end
= (mCurrentAlpha
<= 0);
111 ///////////////////////////////////////////////////////////////////////////////
112 K8PopupWin::K8PopupWin (K8PopupManager
*aMan
, int aWidth
, int aHeight
, K8PopupType
*aType
,
113 const QString
&aId
, const QString
&msg
, QWidget
*parent
) :
114 QWidget(parent
), mMan(aMan
),
115 mType(aType
), mShouldClose(false), mMouseInside(false), mShowTime(0),
118 , mFaderIn(0), mFaderOut(0), mFadingOut(false)
122 initUI(aWidth
, aHeight
);
127 K8PopupWin::~K8PopupWin () {
128 if (mMan
) mMan
->remove(this);
133 void K8PopupWin::onFaderInDies (QObject
* /*obj*/) {
138 void K8PopupWin::onFaderOutDies (QObject
* /*obj*/) {
140 if (mFadingOut
) close();
145 void K8PopupWin::initUI (int width
, int height
) {
146 setWindowFlags(windowFlags() | Qt::FramelessWindowHint
| Qt::WindowStaysOnTopHint
| Qt::ToolTip
);
147 setAttribute(Qt::WA_QuitOnClose
, false);
148 setAttribute(Qt::WA_DeleteOnClose
, true);
149 setAttribute(Qt::WA_KeyboardFocusChange
, false);
150 setAttribute(Qt::WA_AlwaysShowToolTips
, false);
151 setContextMenuPolicy(Qt::NoContextMenu
);
153 resize(width
, height
);
155 if (!mMan
|| !mMan
->floatHeight()) {
156 mLayout
= new QVBoxLayout(this);
157 setFixedSize(QSize(width
, height
));
159 mLayout
= new K8PopupLayout(width
, this);
160 mLayout
->setSizeConstraint(QLayout::SetFixedSize
);
163 //mLayout = new QVBoxLayout(this);
164 mLayout
->setSpacing(0);
165 mLayout
->setContentsMargins(0, 0, 0, 0);
166 //verticalLayout->setSpacing(4);
167 //verticalLayout->setMargin(2);
169 mContentLabel
= new QLabel(this);
170 mContentLabel
->setMinimumSize(QSize(0, 24));
171 mContentLabel
->setMaximumSize(QSize(16777215, 16777215));
172 QSizePolicy szp
= mContentLabel
->sizePolicy();
173 szp
.setVerticalPolicy(QSizePolicy::Expanding
);
174 mContentLabel
->setSizePolicy(szp
);
175 mContentLabel
->setContextMenuPolicy(Qt::NoContextMenu
);
176 mContentLabel
->setAlignment(Qt::AlignLeft
| Qt::AlignTop
);
177 //mContentLabel->setAlignment(Qt::AlignCenter);
178 mContentLabel
->setWordWrap(true);
179 mContentLabel
->setTextInteractionFlags(Qt::NoTextInteraction
);
180 mContentLabel
->setTextFormat(Qt::RichText
);
181 mLayout
->addWidget(mContentLabel
);
183 mContentLabel
->show();
185 mContentLabel
->installEventFilter(this);
186 installEventFilter(this);
190 bool K8PopupWin::eventFilter (QObject
* /*obj*/, QEvent
*event
) {
191 if (event
->type() == QEvent::MouseButtonRelease
) {
192 QMouseEvent
*mevent
= (QMouseEvent
*)event
;
194 if (mMan
) mMan
->emitClick(this, mevent
->button()); else close();
201 void K8PopupWin::enterEvent (QEvent
*event
) {
203 QWidget::enterEvent(event
);
207 void K8PopupWin::leaveEvent (QEvent
*event
) {
208 mMouseInside
= false;
209 QWidget::leaveEvent(event
);
210 if (mShouldClose
) close();
214 void K8PopupWin::buildAndSetMask (QWidget
*w
) {
215 //qDebug() << "size:" << size();
216 QPixmap
pixmap(size());
217 pixmap
.fill(Qt::transparent
);
218 render(&pixmap
, QPoint(), QRegion(), QWidget::DrawChildren
| QWidget::IgnoreMask
);
220 w
->setMask(pixmap
.mask());
224 void K8PopupWin::rebuildMask () {
230 buildAndSetMask(this);
236 buildAndSetMask(this);
244 void K8PopupWin::show () {
246 if (mFadingOut
) return;
248 // cut edges (if any)
253 mFaderIn
= new K8PopupFader(this, false);
254 connect(mFaderIn
, SIGNAL(destroyed(QObject
*)), this, SLOT(onFaderInDies(QObject
*)));
255 mFaderIn
->setFadeColor(Qt::white
);
262 mFaderIn
->resize(size());
263 buildAndSetMask(mFaderIn
);
269 void K8PopupWin::getOut () {
271 if (mFadingOut
) return;
272 if (mFaderIn
) mFaderIn
->close();
274 mFaderOut
= new K8PopupFader(this, true);
275 connect(mFaderOut
, SIGNAL(destroyed(QObject
*)), this, SLOT(onFaderOutDies(QObject
*)));
276 mFaderOut
->setFadeColor(QColor(255, 255, 255));
277 buildAndSetMask(mFaderOut
);
287 void K8PopupWin::setData (const QString
&msg
) {
289 if (!mType
->style
.isEmpty()) mContentLabel
->setStyleSheet(mType
->style
);
291 mContentLabel
->setText(msg
);
296 ///////////////////////////////////////////////////////////////////////////////
297 K8PopupManager::K8PopupManager (int width
, int height
, uint timeout
, Position pos
) :
298 mPos(pos
), mWidth(width
), mHeight(height
), mTimeout(timeout
), mFloatHeight(false)
300 mCheckTimer
= new QTimer(this);
301 connect(mCheckTimer
, SIGNAL(timeout()), this, SLOT(onTimer()));
303 if (mWidth
< 64) mWidth
= 64; else if (mWidth
> 750) mWidth
= 750;
304 if (mHeight
< 62) mHeight
= 64; else if (mHeight
> 550) mHeight
= 550;
305 if (mTimeout
< 1) mTimeout
= 1; else if (mTimeout
> 30) mTimeout
= 30;
309 K8PopupManager::~K8PopupManager () {
310 foreach (K8PopupWin
*w
, mList
) {
316 foreach (K8PopupType
*t
, mTypes
) delete t
;
320 void K8PopupManager::setTimeout (int timeout
) {
322 if (mTimeout
< 1) mTimeout
= 1; else if (mTimeout
> 30) mTimeout
= 30;
326 void K8PopupManager::addType (const QString
&aName
, const QString
&aStyle
) {
329 if (mTypes
.contains(aName
)) {
333 t
= new K8PopupType(aName
, aStyle
);
339 void K8PopupManager::delType (const QString
&aName
) {
340 if (!mTypes
.contains(aName
)) return;
341 K8PopupType
*t
= mTypes
[aName
];
342 foreach (K8PopupWin
*w
, mList
) if (w
&& w
->mType
== t
) w
->mType
= 0;
343 mTypes
.remove(aName
);
348 bool K8PopupManager::hasType (const QString
&aName
) {
349 return mTypes
.contains(aName
);
353 void K8PopupManager::showMessage (const QString
&type
, const QString
&id
, const QString
&msg
) {
354 if (!mTypes
.contains(type
)) return;
355 K8PopupType
*t
= mTypes
[type
];
356 K8PopupWin
*w
= new K8PopupWin(this, mWidth
, mHeight
, t
, id
, msg
);
359 w
->mShowTime
= QDateTime::currentDateTime().toTime_t();
360 if (mFloatHeight
) rearrange();
364 void K8PopupManager::updateMessage (const QString
&type
, const QString
&id
, const QString
&msg
) {
365 bool wasChanged
= false;
367 if (!type
.isEmpty()) {
368 if (!mTypes
.contains(type
)) return;
371 foreach (K8PopupWin
*w
, mList
) {
372 if (!w
->mMan
|| (t
&& w
->mType
!= t
) || w
->mId
!= id
) continue;
374 w
->mShowTime
= QDateTime::currentDateTime().toTime_t();
378 if (wasChanged
) rearrange(); else showMessage(type
, id
, msg
);
382 void K8PopupManager::onTimer () {
383 uint now
= QDateTime::currentDateTime().toTime_t();
384 for (int f
= mList
.size()-1; f
>= 0; f
--) {
385 K8PopupWin
*w
= mList
[f
];
387 if (w
->mShowTime
+mTimeout
> now
) continue;
388 if (w
->mMouseInside
) {
389 //w->mShouldClose = true;
390 w
->mShowTime
= QDateTime::currentDateTime().toTime_t();
396 void K8PopupManager::allocSlot (K8PopupWin
*w
) {
397 QDesktopWidget
*desktop
= QApplication::desktop();
398 QRect geometry
= desktop
->availableGeometry();
407 x
= geometry
.right()-mWidth
;
412 y
= geometry
.bottom();
415 x
= geometry
.right()-mWidth
;
416 y
= geometry
.bottom();
421 int idx
= mList
.indexOf(w
);
422 if (idx
< 0) mList
.append(w
);
425 QPoint
curpos(QCursor::pos());
427 foreach (K8PopupWin
*ww
, mList
) {
428 int hh
= ww
->height()-1;
430 if (ww
->mMouseInside
) ww
->mShowTime
= QDateTime::currentDateTime().toTime_t();
432 case LeftTop
: case RightTop
:
433 if (y
+hh
> geometry
.bottom()) {
435 if (mPos
== LeftTop
) x
+= mWidth
; else x
-= mWidth
;
440 case LeftBottom
: case RightBottom
:
442 if (y
< geometry
.top()) {
443 y
= geometry
.bottom();
444 if (mPos
== LeftBottom
) x
+= mWidth
; else x
-= mWidth
;
451 QPoint
wp(ww
->mapFromGlobal(curpos
));
453 ww
->mMouseInside
= (wp
.x() >= 0 && wp
.y() >= 0 && wp
.x() < ww
->width() && wp
.y() < ww
->height());
454 if (ww
->mMouseInside
) ww
->mShowTime
= QDateTime::currentDateTime().toTime_t();
456 if (mList
.size() == 0) {
457 if (mCheckTimer
->isActive()) mCheckTimer
->stop();
459 if (!mCheckTimer
->isActive()) mCheckTimer
->start(500);
464 void K8PopupManager::remove (K8PopupWin
*wnd
) {
466 int idx
= mList
.indexOf(wnd
);
474 void K8PopupManager::rearrange () {
479 void K8PopupManager::emitClick (K8PopupWin
*w
, Qt::MouseButton button
) {
480 if (!w
|| !w
->mMan
) return;
481 if (!w
->mType
) return;
483 if (w
->mFadingOut
) return;
485 emit
click(w
->mType
->name
, w
->mId
, button
);
490 void K8PopupManager::removeAll () {
491 foreach (K8PopupWin
*w
, mList
) {
496 if (mCheckTimer
->isActive()) mCheckTimer
->stop();
500 void K8PopupManager::removeById (const QString
&type
, const QString
&id
) {
502 if (!type
.isEmpty()) {
503 if (!mTypes
.contains(type
)) return;
507 for (int f
= 0; f
< mList
.size(); ) {
508 K8PopupWin
*w
= mList
[f
];
509 if (!w
->mMan
|| (t
&& w
->mType
!= t
) || w
->mId
!= id
) {