]> git.lyx.org Git - lyx.git/blob - src/frontends/qt2/QContentPane.C
ceadb7e2c6bb1ccb3f1a23a4cc10c1d7d08cd449
[lyx.git] / src / frontends / qt2 / QContentPane.C
1 /**
2  * \file QContentPane.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 // Qt defines a macro 'signals' that clashes with a boost namespace.
14 // All is well if the namespace is visible first.
15 #include "QWorkArea.h"
16
17 #include "QContentPane.h"
18 #include "QLyXKeySym.h"
19
20 #include <qapplication.h>
21 #include <qpainter.h>
22 #include <qtimer.h>
23
24 #include <boost/bind.hpp>
25
26 namespace {
27
28 /// return the LyX key state from Qt's
29 key_modifier::state q_key_state(Qt::ButtonState state)
30 {
31         key_modifier::state k = key_modifier::none;
32         if (state & Qt::ControlButton)
33                 k |= key_modifier::ctrl;
34         if (state & Qt::ShiftButton)
35                 k |= key_modifier::shift;
36         if (state & Qt::AltButton)
37                 k |= key_modifier::alt;
38         return k;
39 }
40
41
42 /// return the LyX mouse button state from Qt's
43 mouse_button::state q_button_state(Qt::ButtonState button)
44 {
45         mouse_button::state b = mouse_button::none;
46         switch (button) {
47                 case Qt::LeftButton:
48                         b = mouse_button::button1;
49                         break;
50                 case Qt::MidButton:
51                         b = mouse_button::button2;
52                         break;
53                 case Qt::RightButton:
54                         b = mouse_button::button3;
55                         break;
56                 default:
57                         break;
58         }
59         return b;
60 }
61
62
63 /// return the LyX mouse button state from Qt's
64 mouse_button::state q_motion_state(Qt::ButtonState state)
65 {
66         mouse_button::state b = mouse_button::none;
67         if (state & Qt::LeftButton)
68                 b |= mouse_button::button1;
69         if (state & Qt::MidButton)
70                 b |= mouse_button::button2;
71         if (state & Qt::RightButton)
72                 b |= mouse_button::button3;
73         return b;
74 }
75
76 } // namespace anon
77
78
79 SyntheticMouseEvent::SyntheticMouseEvent()
80         : timeout(200), restart_timeout(true),
81           x_old(-1), y_old(-1), scrollbar_value_old(-1.0)
82 {}
83
84
85 QContentPane::QContentPane(QWorkArea * parent)
86         : QWidget(parent, "content_pane", WRepaintNoErase),
87           track_scrollbar_(true), wa_(parent)
88 {
89         synthetic_mouse_event_.timeout.timeout.connect(
90                 boost::bind(&QContentPane::generateSyntheticMouseEvent,
91                             this));
92
93         setFocusPolicy(QWidget::WheelFocus);
94         setFocus();
95         setCursor(ibeamCursor);
96 #if QT_VERSION >= 0x030200
97         // to make qt-immodule work
98         setInputMethodEnabled(true);
99 #endif
100
101         // stupid moc strikes again
102         connect(wa_->scrollbar_, SIGNAL(valueChanged(int)),
103                 this, SLOT(scrollBarChanged(int)));
104 }
105
106
107 #if QT_VERSION >= 0x030200
108 // to make qt-immodule work
109 void QContentPane::imStartEvent(QIMEvent *e)
110 {
111         e->accept();
112 }
113
114
115 void QContentPane::imComposeEvent(QIMEvent *e)
116 {
117         e->accept();
118 }
119
120
121 void QContentPane::imEndEvent(QIMEvent *e)
122 {
123         QString const text = e->text();
124         if (!text.isEmpty()) {
125                 int key = 0;
126                 // needed to make math superscript work on some systems
127                 // ideally, such special coding should not be necessary
128                 if (text == "^")
129                         key = Qt::Key_AsciiCircum;
130                 QKeyEvent ev(QEvent::KeyPress, key, *text.ascii(), 0, text);
131                 keyPressEvent(&ev);
132         }
133         e->accept();
134 }
135 #endif
136
137
138 void QContentPane::generateSyntheticMouseEvent()
139 {
140         // Set things off to generate the _next_ 'pseudo' event.
141         if (synthetic_mouse_event_.restart_timeout)
142                 synthetic_mouse_event_.timeout.start();
143
144         // Has anything changed on-screen since the last timeout signal
145         // was received?
146         double const scrollbar_value = wa_->scrollbar_->value();
147         if (scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
148                 // Yes it has. Store the params used to check this.
149                 synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
150
151                 // ... and dispatch the event to the LyX core.
152                 wa_->dispatch(synthetic_mouse_event_.cmd);
153         }
154 }
155
156
157 void QContentPane::scrollBarChanged(int val)
158 {
159         if (track_scrollbar_)
160                 wa_->scrollDocView(val);
161 }
162
163
164 void QContentPane::mousePressEvent(QMouseEvent * e)
165 {
166         if (dc_event_.active && dc_event_ == *e) {
167                 dc_event_.active = false;
168                 FuncRequest cmd(LFUN_MOUSE_TRIPLE,
169                         dc_event_.x, dc_event_.y,
170                         q_button_state(dc_event_.state));
171                 wa_->dispatch(cmd);
172                 return;
173         }
174
175         FuncRequest const cmd(LFUN_MOUSE_PRESS, e->x(), e->y(),
176                               q_button_state(e->button()));
177         wa_->dispatch(cmd);
178 }
179
180
181 void QContentPane::mouseReleaseEvent(QMouseEvent * e)
182 {
183         if (synthetic_mouse_event_.timeout.running())
184                 synthetic_mouse_event_.timeout.stop();
185
186         FuncRequest const cmd(LFUN_MOUSE_RELEASE, e->x(), e->y(),
187                               q_button_state(e->button()));
188         wa_->dispatch(cmd);
189 }
190
191
192 void QContentPane::mouseMoveEvent(QMouseEvent * e)
193 {
194         FuncRequest const cmd(LFUN_MOUSE_MOTION, e->x(), e->y(),
195                               q_motion_state(e->state()));
196
197         // If we're above or below the work area...
198         if (e->y() <= 0 || e->y() >= height()) {
199                 // Store the event, to be handled when the timeout expires.
200                 synthetic_mouse_event_.cmd = cmd;
201
202                 if (synthetic_mouse_event_.timeout.running())
203                         // Discard the event. Note that it _may_ be handled
204                         // when the timeout expires if
205                         // synthetic_mouse_event_.cmd has not been overwritten.
206                         // Ie, when the timeout expires, we handle the
207                         // most recent event but discard all others that
208                         // occurred after the one used to start the timeout
209                         // in the first place.
210                         return;
211                 else {
212                         synthetic_mouse_event_.restart_timeout = true;
213                         synthetic_mouse_event_.timeout.start();
214                         // Fall through to handle this event...
215                 }
216
217         } else if (synthetic_mouse_event_.timeout.running()) {
218                 // Store the event, to be possibly handled when the timeout
219                 // expires.
220                 // Once the timeout has expired, normal control is returned
221                 // to mouseMoveEvent (restart_timeout = false).
222                 // This results in a much smoother 'feel' when moving the
223                 // mouse back into the work area.
224                 synthetic_mouse_event_.cmd = cmd;
225                 synthetic_mouse_event_.restart_timeout = false;
226                 return;
227         }
228
229         // Has anything changed on-screen since the last QMouseEvent
230         // was received?
231         double const scrollbar_value = wa_->scrollbar_->value();
232         if (e->x() != synthetic_mouse_event_.x_old ||
233             e->y() != synthetic_mouse_event_.y_old ||
234             scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
235                 // Yes it has. Store the params used to check this.
236                 synthetic_mouse_event_.x_old = e->x();
237                 synthetic_mouse_event_.y_old = e->y();
238                 synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
239
240                 // ... and dispatch the event to the LyX core.
241                 wa_->dispatch(cmd);
242         }
243 }
244
245
246 void QContentPane::wheelEvent(QWheelEvent * e)
247 {
248         wa_->scrollbar_->setValue(wa_->scrollbar_->value() - e->delta());
249 }
250
251
252 void QContentPane::keyPressEvent(QKeyEvent * e)
253 {
254         boost::shared_ptr<QLyXKeySym> sym(new QLyXKeySym);
255         sym->set(e);
256         wa_->workAreaKeyPress(sym, q_key_state(e->state()));
257 }
258
259
260 void QContentPane::doubleClickTimeout()
261 {
262         if (!dc_event_.active)
263                 return;
264
265         dc_event_.active = false;
266
267         FuncRequest cmd(LFUN_MOUSE_DOUBLE,
268                 dc_event_.x, dc_event_.y,
269                 q_button_state(dc_event_.state));
270         wa_->dispatch(cmd);
271 }
272
273
274 void QContentPane::mouseDoubleClickEvent(QMouseEvent * e)
275 {
276         dc_event_ = double_click(e);
277
278         // doubleClickInterval() is just too long.
279         QTimer::singleShot(int(QApplication::doubleClickInterval() / 1.5),
280                 this, SLOT(doubleClickTimeout()));
281 }
282
283
284 void QContentPane::resizeEvent(QResizeEvent *)
285 {
286         if (!pixmap_.get()) {
287                 pixmap_.reset(new QPixmap(width(), height()));
288         }
289
290         pixmap_->resize(width(), height());
291         wa_->workAreaResize();
292 }
293
294
295 void QContentPane::paintEvent(QPaintEvent * e)
296 {
297         if (!pixmap_.get()) {
298                 pixmap_.reset(new QPixmap(width(), height()));
299                 wa_->workAreaResize();
300                 return;
301         }
302
303         QRect r(e->rect());
304
305         QPainter q(this);
306         q.drawPixmap(QPoint(r.x(), r.y()),
307                 *pixmap_.get(), r);
308 }
309
310
311 void QContentPane::trackScrollbar(bool track_on)
312 {
313         track_scrollbar_ = track_on;
314 }