]> git.lyx.org Git - lyx.git/blob - src/frontends/qt3/QContentPane.C
clean up Abdel's cleanup
[lyx.git] / src / frontends / qt3 / 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 #include "BufferView.h"
14 #include "frontends/LyXView.h"
15 #include "frontends/WorkArea.h"
16
17 // Qt defines a macro 'signals' that clashes with a boost namespace.
18 // All is well if the namespace is visible first.
19 #include "QWorkArea.h"
20
21 #include "QContentPane.h"
22 #include "QLyXKeySym.h"
23
24 #include <qapplication.h>
25 #include <qpainter.h>
26
27 #include <boost/bind.hpp>
28
29 namespace {
30
31 /// return the LyX key state from Qt's
32 key_modifier::state q_key_state(Qt::ButtonState state)
33 {
34         key_modifier::state k = key_modifier::none;
35         if (state & Qt::ControlButton)
36                 k |= key_modifier::ctrl;
37         if (state & Qt::ShiftButton)
38                 k |= key_modifier::shift;
39         if (state & Qt::AltButton || state & Qt::MetaButton)
40                 k |= key_modifier::alt;
41         return k;
42 }
43
44
45 /// return the LyX mouse button state from Qt's
46 mouse_button::state q_button_state(Qt::ButtonState button)
47 {
48         mouse_button::state b = mouse_button::none;
49         switch (button) {
50                 case Qt::LeftButton:
51                         b = mouse_button::button1;
52                         break;
53                 case Qt::MidButton:
54                         b = mouse_button::button2;
55                         break;
56                 case Qt::RightButton:
57                         b = mouse_button::button3;
58                         break;
59                 default:
60                         break;
61         }
62         return b;
63 }
64
65
66 /// return the LyX mouse button state from Qt's
67 mouse_button::state q_motion_state(Qt::ButtonState state)
68 {
69         mouse_button::state b = mouse_button::none;
70         if (state & Qt::LeftButton)
71                 b |= mouse_button::button1;
72         if (state & Qt::MidButton)
73                 b |= mouse_button::button2;
74         if (state & Qt::RightButton)
75                 b |= mouse_button::button3;
76         return b;
77 }
78
79 } // namespace anon
80
81
82 namespace lyx {
83 namespace frontend {
84
85 // This is a 'heartbeat' generating synthetic mouse move events when the
86 // cursor is at the top or bottom edge of the viewport. One scroll per 0.2 s
87 SyntheticMouseEvent::SyntheticMouseEvent()
88         : timeout(200), restart_timeout(true),
89           x_old(-1), y_old(-1), scrollbar_value_old(-1.0)
90 {}
91
92
93 QContentPane::QContentPane(QWorkArea * parent)
94         : QWidget(parent, "content_pane", WRepaintNoErase),
95           track_scrollbar_(true), wa_(parent)
96 {
97         synthetic_mouse_event_.timeout.timeout.connect(
98                 boost::bind(&QContentPane::generateSyntheticMouseEvent,
99                             this));
100
101         connect(&step_timer_, SIGNAL(timeout()), SLOT(keyeventTimeout()));
102
103         setFocusPolicy(QWidget::WheelFocus);
104         setFocus();
105         setCursor(ibeamCursor);
106 #if USE_INPUT_METHODS
107         // to make qt-immodule work
108         setInputMethodEnabled(true);
109 #endif
110
111         // stupid moc strikes again
112         connect(wa_->scrollbar_, SIGNAL(valueChanged(int)),
113                 this, SLOT(scrollBarChanged(int)));
114
115         // Start the timer, one-shot.
116         step_timer_.start(50, true);
117 }
118
119
120 #if USE_INPUT_METHODS
121 // to make qt-immodule work
122 void QContentPane::imStartEvent(QIMEvent *e)
123 {
124         e->accept();
125 }
126
127
128 void QContentPane::imComposeEvent(QIMEvent *e)
129 {
130         e->accept();
131 }
132
133
134 void QContentPane::imEndEvent(QIMEvent *e)
135 {
136         QString const text = e->text();
137         if (!text.isEmpty()) {
138                 int key = 0;
139                 // needed to make math superscript work on some systems
140                 // ideally, such special coding should not be necessary
141                 if (text == "^")
142                         key = Qt::Key_AsciiCircum;
143                 QKeyEvent ev(QEvent::KeyPress, key, *text.ascii(), 0, text);
144                 keyPressEvent(&ev);
145         }
146         e->accept();
147 }
148 #endif
149
150
151 void QContentPane::generateSyntheticMouseEvent()
152 {
153         // Set things off to generate the _next_ 'pseudo' event.
154         if (synthetic_mouse_event_.restart_timeout)
155                 synthetic_mouse_event_.timeout.start();
156
157         // Has anything changed on-screen since the last timeout signal
158         // was received?
159         double const scrollbar_value = wa_->scrollbar_->value();
160         if (scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
161                 // Yes it has. Store the params used to check this.
162                 synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
163
164                 // ... and dispatch the event to the LyX core.
165                 wa_->view().workArea()->dispatch(synthetic_mouse_event_.cmd);
166         }
167 }
168
169
170 void QContentPane::scrollBarChanged(int val)
171 {
172         if (track_scrollbar_)
173                 wa_->view().workArea()->scrollBufferView(val);
174 }
175
176
177 void QContentPane::mousePressEvent(QMouseEvent * e)
178 {
179         if (dc_event_.active && dc_event_ == *e) {
180                 dc_event_.active = false;
181                 FuncRequest cmd(LFUN_MOUSE_TRIPLE,
182                         dc_event_.x, dc_event_.y,
183                         q_button_state(dc_event_.state));
184                 wa_->view().workArea()->dispatch(cmd);
185                 return;
186         }
187
188         FuncRequest const cmd(LFUN_MOUSE_PRESS, e->x(), e->y(),
189                               q_button_state(e->button()));
190         wa_->view().workArea()->dispatch(cmd);
191 }
192
193
194 void QContentPane::mouseReleaseEvent(QMouseEvent * e)
195 {
196         if (synthetic_mouse_event_.timeout.running())
197                 synthetic_mouse_event_.timeout.stop();
198
199         FuncRequest const cmd(LFUN_MOUSE_RELEASE, e->x(), e->y(),
200                               q_button_state(e->button()));
201         wa_->view().workArea()->dispatch(cmd);
202 }
203
204
205 void QContentPane::mouseMoveEvent(QMouseEvent * e)
206 {
207         FuncRequest cmd(LFUN_MOUSE_MOTION, e->x(), e->y(),
208                               q_motion_state(e->state()));
209
210         // If we're above or below the work area...
211         if (e->y() <= 20 || e->y() >= height() - 20) {
212                 // Make sure only a synthetic event can cause a page scroll,
213                 // so they come at a steady rate:
214                 if (e->y() <= 20)
215                         // _Force_ a scroll up:
216                         cmd.y = -40;
217                 else
218                         cmd.y = height();
219                 // Store the event, to be handled when the timeout expires.
220                 synthetic_mouse_event_.cmd = cmd;
221
222                 if (synthetic_mouse_event_.timeout.running())
223                         // Discard the event. Note that it _may_ be handled
224                         // when the timeout expires if
225                         // synthetic_mouse_event_.cmd has not been overwritten.
226                         // Ie, when the timeout expires, we handle the
227                         // most recent event but discard all others that
228                         // occurred after the one used to start the timeout
229                         // in the first place.
230                         return;
231                 else {
232                         synthetic_mouse_event_.restart_timeout = true;
233                         synthetic_mouse_event_.timeout.start();
234                         // Fall through to handle this event...
235                 }
236
237         } else if (synthetic_mouse_event_.timeout.running()) {
238                 // Store the event, to be possibly handled when the timeout
239                 // expires.
240                 // Once the timeout has expired, normal control is returned
241                 // to mouseMoveEvent (restart_timeout = false).
242                 // This results in a much smoother 'feel' when moving the
243                 // mouse back into the work area.
244                 synthetic_mouse_event_.cmd = cmd;
245                 synthetic_mouse_event_.restart_timeout = false;
246                 return;
247         }
248
249         // Has anything changed on-screen since the last QMouseEvent
250         // was received?
251         double const scrollbar_value = wa_->scrollbar_->value();
252         if (e->x() != synthetic_mouse_event_.x_old ||
253             e->y() != synthetic_mouse_event_.y_old ||
254             scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
255                 // Yes it has. Store the params used to check this.
256                 synthetic_mouse_event_.x_old = e->x();
257                 synthetic_mouse_event_.y_old = e->y();
258                 synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
259
260                 // ... and dispatch the event to the LyX core.
261                 wa_->view().workArea()->dispatch(cmd);
262         }
263 }
264
265
266 void QContentPane::wheelEvent(QWheelEvent * e)
267 {
268         // Wheel rotation by one notch results in a delta() of 120 (see
269         // documentation of QWheelEvent)
270         int const lines = QApplication::wheelScrollLines() * e->delta() / 120;
271         wa_->scrollbar_->setValue(wa_->scrollbar_->value() -
272                                   lines *  wa_->scrollbar_->lineStep());
273 }
274
275
276 void QContentPane::keyPressEvent(QKeyEvent * e)
277 {
278         keyeventQueue_.push(boost::shared_ptr<QKeyEvent>(new QKeyEvent(*e)));
279 }
280
281
282 void QContentPane::keyeventTimeout()
283 {
284         bool handle_autos = true;
285
286         while (!keyeventQueue_.empty()) {
287                 boost::shared_ptr<QKeyEvent> ev = keyeventQueue_.front();
288
289                 // We never handle more than one auto repeated
290                 // char in a list of queued up events.
291                 if (!handle_autos && ev->isAutoRepeat()) {
292                         keyeventQueue_.pop();
293                         continue;
294                 }
295
296         boost::shared_ptr<QLyXKeySym> sym(new QLyXKeySym);
297                 sym->set(ev.get());
298
299                 wa_->view().workArea()->processKeySym(sym, q_key_state(ev->state()));
300                 keyeventQueue_.pop();
301
302                 handle_autos = false;
303         }
304
305         // Restart the timer.
306         step_timer_.start(25, true);
307 }
308
309
310 void QContentPane::doubleClickTimeout()
311 {
312         if (!dc_event_.active)
313                 return;
314
315         dc_event_.active = false;
316
317         FuncRequest cmd(LFUN_MOUSE_DOUBLE,
318                 dc_event_.x, dc_event_.y,
319                 q_button_state(dc_event_.state));
320         wa_->view().workArea()->dispatch(cmd);
321 }
322
323
324 void QContentPane::mouseDoubleClickEvent(QMouseEvent * e)
325 {
326         dc_event_ = double_click(e);
327
328         // doubleClickInterval() is just too long.
329         QTimer::singleShot(int(QApplication::doubleClickInterval() / 1.5),
330                 this, SLOT(doubleClickTimeout()));
331 }
332
333
334 void QContentPane::resizeEvent(QResizeEvent *)
335 {
336         if (!pixmap_.get()) {
337                 pixmap_.reset(new QPixmap(width(), height()));
338         }
339
340         pixmap_->resize(width(), height());
341         wa_->view().workArea()->resizeBufferView();
342 }
343
344
345 void QContentPane::paintEvent(QPaintEvent * e)
346 {
347         if (!pixmap_.get()) {
348                 pixmap_.reset(new QPixmap(width(), height()));
349                 wa_->view().workArea()->resizeBufferView();
350                 return;
351         }
352
353         QRect r(e->rect());
354
355         QPainter q(this);
356         q.drawPixmap(QPoint(r.x(), r.y()),
357                 *pixmap_.get(), r);
358 }
359
360
361 void QContentPane::trackScrollbar(bool track_on)
362 {
363         track_scrollbar_ = track_on;
364 }
365
366 } // namespace frontend
367 } // namespace lyx
368
369 #include "QContentPane_moc.cpp"