Some bugs fixed
[qstardict] / qstardict / resizablepopup.cpp
1 /*****************************************************************************
2  * resizablepopup.cpp - QStarDict, a StarDict clone written with using Qt    *
3  * Copyright (C) 2007 Alexander Rodin                                        *
4  *                                                                           *
5  * This program is free software; you can redistribute it and/or modify      *
6  * it under the terms of the GNU General Public License as published by      *
7  * the Free Software Foundation; either version 2 of the License, or         *
8  * (at your option) any later version.                                       *
9  *                                                                           *
10  * This program is distributed in the hope that it will be useful,           *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
13  * GNU General Public License for more details.                              *
14  *                                                                           *
15  * You should have received a copy of the GNU General Public License along   *
16  * with this program; if not, write to the Free Software Foundation, Inc.,   *
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.               *
18  *****************************************************************************/
19
20 #include "resizablepopup.h"
21
22 #include <QApplication>
23 #include <QCursor>
24 #include <QDesktopWidget>
25 #include <QMouseEvent>
26 #include <QTimerEvent>
27
28 namespace
29 {
30 const int CornerSize = 10;
31 }
32
33 namespace QStarDict
34 {
35
36 ResizablePopup::ResizablePopup(QWidget *parent)
37     : QFrame(parent, Qt::Popup)
38 {
39     m_isMoving = false;
40     m_resizeDirection = None;
41     m_timeoutBeforeHide = 0;
42     m_timerCloseId = 0;
43     m_timerResizeId = 0;
44     m_isPopuped = false;
45     setMouseTracking(true);
46     setLineWidth(1);
47     setMidLineWidth(2);
48     setFrameStyle(QFrame::Box);
49     setFrameShadow(QFrame::Raised);
50 }
51
52 void ResizablePopup::popup()
53 {
54     if (m_defaultSize != size())
55         resize(m_defaultSize);
56     QPoint newPosition = QCursor::pos() - QPoint(30, 30);
57     if (newPosition.x() < 0)
58         newPosition.setX(0);
59     else if (newPosition.x() + width() > QApplication::desktop()->width())
60         newPosition.setX(QApplication::desktop()->width() - width());
61     if (newPosition.y() < 0)
62         newPosition.setY(0);
63     else if (newPosition.y() + height() > QApplication::desktop()->height())
64         newPosition.setY(QApplication::desktop()->height() - height());
65     move(newPosition);
66     show();
67     m_isPopuped = true;
68 }
69
70 void ResizablePopup::enterEvent(QEvent*)
71 {
72     if (m_timerCloseId)
73     {
74         killTimer(m_timerCloseId);
75         m_timerCloseId = 0;
76     }
77 }
78
79 void ResizablePopup::leaveEvent(QEvent*)
80 {
81     if (geometry().contains(QCursor::pos()))
82         return;
83     if (m_resizeDirection)
84         return;
85     if (m_timeoutBeforeHide < 0)
86         return;
87     if (m_timeoutBeforeHide == 0)
88     {
89         m_isPopuped = false;
90         hide();
91     }
92     else if (! m_timerCloseId)
93         m_timerCloseId = startTimer(m_timeoutBeforeHide);
94 }
95
96 void ResizablePopup::mouseMoveEvent(QMouseEvent *event)
97 {
98     Qt::CursorShape cursorShape = Qt::ArrowCursor;
99     if ((event->x() >= 0 && event->x() < CornerSize &&
100             event->y() >= 0 && event->y() < CornerSize) ||
101         (event->x() < width() && event->x() >= width() - CornerSize &&
102             event->y() < height() && event->y() >= height() - CornerSize))
103         cursorShape = Qt::SizeFDiagCursor;
104     else if ((event->x() < width() && event->x() >= width() - CornerSize &&
105                 event->y() >= 0 && event->y() < CornerSize) ||
106              (event->x() >= 0 && event->x() < CornerSize &&
107                 event->y() < height() && event->y() >= height() - CornerSize))
108         cursorShape = Qt::SizeBDiagCursor;
109     else if ((event->x() >= 0 && event->x() < frameWidth()) ||
110              (event->x() < width() && event->x() >= width() - frameWidth()))
111         cursorShape = Qt::SizeHorCursor;
112     else if ((event->y() >= 0 && event->y() < frameWidth()) ||
113              (event->y() < height() && event->y() >= height() - frameWidth()))
114         cursorShape = Qt::SizeVerCursor;
115     
116     if (cursor().shape() != cursorShape)
117         setCursor(cursorShape);
118     else
119         if (event->buttons().testFlag(Qt::LeftButton))
120         {
121             if (m_isMoving)
122                 move(pos() + (event->globalPos() - m_oldCursorPos));
123             m_oldCursorPos = event->globalPos();
124             return;
125         }
126     m_isMoving = false;
127 }
128
129 void ResizablePopup::mousePressEvent(QMouseEvent *event)
130 {
131     if (! geometry().contains(event->globalPos()))
132     {
133         if (m_timerCloseId)
134         {
135             killTimer(m_timerCloseId);
136             m_timerCloseId = 0;
137         }
138         m_isPopuped = false;
139         hide();
140         return;
141     }
142
143     if (event->buttons().testFlag(Qt::LeftButton))
144     {
145         if (event->x() < CornerSize && event->y() < CornerSize)
146             m_resizeDirection = TopLeft;
147         else if (event->x() >= width() - CornerSize && event->y() < CornerSize)
148             m_resizeDirection = TopRight;
149         else if (event->x() < CornerSize && event->y() >= height() - CornerSize)
150             m_resizeDirection = BottomLeft;
151         else if (event->x() >= width() - CornerSize && event->y() >= height() - CornerSize)
152             m_resizeDirection = BottomRight;
153         else if (event->x() < frameWidth())
154             m_resizeDirection = Left;
155         else if (event->x() >= width() - frameWidth())
156             m_resizeDirection = Right;
157         else if (event->y() < frameWidth())
158             m_resizeDirection = Top;
159         else if (event->y() >= height() - frameWidth())
160             m_resizeDirection = Bottom;
161         else
162             m_resizeDirection = None;
163         if (m_resizeDirection)
164             m_timerResizeId = startTimer(8);
165     }
166
167     m_isMoving = true;
168     m_oldCursorPos = event->globalPos();
169 }
170
171 void ResizablePopup::mouseReleaseEvent(QMouseEvent*)
172 {
173     stopResize();
174 }
175
176 void ResizablePopup::mouseDoubleClickEvent(QMouseEvent*)
177 {
178     m_isPopuped = false;
179     if (m_timerCloseId)
180     {
181         killTimer(m_timerCloseId);
182         m_timerCloseId = 0;
183     }
184     hide();
185 }
186
187 void ResizablePopup::timerEvent(QTimerEvent *event)
188 {
189     if (event->timerId() == m_timerResizeId)
190     {
191         doResize();
192     }
193     else if (event->timerId() == m_timerCloseId)
194     {
195         m_isPopuped = false;
196         hide();
197         killTimer(m_timerCloseId);
198         m_timerCloseId = 0;
199         stopResize();
200     }
201 }
202
203 void ResizablePopup::doResize()
204 {
205     if (! QApplication::mouseButtons().testFlag(Qt::LeftButton))
206         stopResize();
207     
208     if (m_resizeDirection)
209     {
210         QRect newGeometry = geometry();
211         switch (m_resizeDirection)
212         {
213             case TopLeft:
214                 newGeometry.setTopLeft(QCursor::pos());
215                 break;
216             case TopRight:
217                 newGeometry.setTopRight(QCursor::pos());
218                 break;
219             case BottomLeft:
220                 newGeometry.setBottomLeft(QCursor::pos());
221                 break;
222             case BottomRight:
223                 newGeometry.setBottomRight(QCursor::pos());
224                 break;
225             case Left:
226                 newGeometry.setLeft(QCursor::pos().x());
227                 break;
228             case Right:
229                 newGeometry.setRight(QCursor::pos().x());
230                 break;
231             case Top:
232                 newGeometry.setTop(QCursor::pos().y());
233                 break;
234             case Bottom:
235                 newGeometry.setBottom(QCursor::pos().y());
236                 break;
237             default:
238                 ; // Nothing
239         }
240         if (newGeometry.width() < minimumSize().width())
241         {
242             newGeometry.setWidth(width());
243             newGeometry.moveLeft(geometry().left());
244         }
245         if (newGeometry.height() < minimumSize().height())
246         {
247             newGeometry.setHeight(height());
248             newGeometry.moveTop(geometry().top());
249         }
250         if (newGeometry != geometry())
251             setGeometry(newGeometry);
252     }
253 }
254
255 void ResizablePopup::stopResize()
256 {
257     if (m_resizeDirection)
258     {
259         m_resizeDirection = None;
260         killTimer(m_timerResizeId);
261         m_timerResizeId = 0;
262     }
263 }
264
265 bool ResizablePopup::event(QEvent *event)
266 {
267     if (event->type() == QEvent::WindowUnblocked && m_isPopuped)
268     {
269         if (m_timerCloseId)
270         {
271             killTimer(m_timerCloseId);
272             m_timerCloseId = 0;
273         }
274         show();
275         return true;
276     }
277     else
278         return QFrame::event(event);
279 }
280
281 }
282
283 // vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab cindent textwidth=120 formatoptions=tc
284