3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "BufferView.h"
14 #include "frontends/LyXView.h"
15 #include "frontends/WorkArea.h"
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"
21 #include "QContentPane.h"
22 #include "QLyXKeySym.h"
24 #include <qapplication.h>
27 #include <boost/bind.hpp>
31 /// return the LyX key state from Qt's
32 key_modifier::state q_key_state(Qt::ButtonState state)
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;
45 /// return the LyX mouse button state from Qt's
46 mouse_button::state q_button_state(Qt::ButtonState button)
48 mouse_button::state b = mouse_button::none;
51 b = mouse_button::button1;
54 b = mouse_button::button2;
57 b = mouse_button::button3;
66 /// return the LyX mouse button state from Qt's
67 mouse_button::state q_motion_state(Qt::ButtonState state)
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;
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)
93 QContentPane::QContentPane(QWorkArea * parent)
94 : QWidget(parent, "content_pane", WRepaintNoErase),
95 track_scrollbar_(true), wa_(parent)
97 synthetic_mouse_event_.timeout.timeout.connect(
98 boost::bind(&QContentPane::generateSyntheticMouseEvent,
101 connect(&step_timer_, SIGNAL(timeout()), SLOT(keyeventTimeout()));
103 setFocusPolicy(QWidget::WheelFocus);
105 setCursor(ibeamCursor);
106 #if USE_INPUT_METHODS
107 // to make qt-immodule work
108 setInputMethodEnabled(true);
111 // stupid moc strikes again
112 connect(wa_->scrollbar_, SIGNAL(valueChanged(int)),
113 this, SLOT(scrollBarChanged(int)));
115 // Start the timer, one-shot.
116 step_timer_.start(50, true);
120 #if USE_INPUT_METHODS
121 // to make qt-immodule work
122 void QContentPane::imStartEvent(QIMEvent *e)
128 void QContentPane::imComposeEvent(QIMEvent *e)
134 void QContentPane::imEndEvent(QIMEvent *e)
136 QString const text = e->text();
137 if (!text.isEmpty()) {
139 // needed to make math superscript work on some systems
140 // ideally, such special coding should not be necessary
142 key = Qt::Key_AsciiCircum;
143 QKeyEvent ev(QEvent::KeyPress, key, *text.ascii(), 0, text);
151 void QContentPane::generateSyntheticMouseEvent()
153 // Set things off to generate the _next_ 'pseudo' event.
154 if (synthetic_mouse_event_.restart_timeout)
155 synthetic_mouse_event_.timeout.start();
157 // Has anything changed on-screen since the last timeout signal
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;
164 // ... and dispatch the event to the LyX core.
165 wa_->view().workArea()->dispatch(synthetic_mouse_event_.cmd);
170 void QContentPane::scrollBarChanged(int val)
172 if (track_scrollbar_)
173 wa_->view().workArea()->scrollBufferView(val);
177 void QContentPane::mousePressEvent(QMouseEvent * e)
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);
188 FuncRequest const cmd(LFUN_MOUSE_PRESS, e->x(), e->y(),
189 q_button_state(e->button()));
190 wa_->view().workArea()->dispatch(cmd);
194 void QContentPane::mouseReleaseEvent(QMouseEvent * e)
196 if (synthetic_mouse_event_.timeout.running())
197 synthetic_mouse_event_.timeout.stop();
199 FuncRequest const cmd(LFUN_MOUSE_RELEASE, e->x(), e->y(),
200 q_button_state(e->button()));
201 wa_->view().workArea()->dispatch(cmd);
205 void QContentPane::mouseMoveEvent(QMouseEvent * e)
207 FuncRequest cmd(LFUN_MOUSE_MOTION, e->x(), e->y(),
208 q_motion_state(e->state()));
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:
215 // _Force_ a scroll up:
219 // Store the event, to be handled when the timeout expires.
220 synthetic_mouse_event_.cmd = cmd;
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.
232 synthetic_mouse_event_.restart_timeout = true;
233 synthetic_mouse_event_.timeout.start();
234 // Fall through to handle this event...
237 } else if (synthetic_mouse_event_.timeout.running()) {
238 // Store the event, to be possibly handled when the timeout
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;
249 // Has anything changed on-screen since the last QMouseEvent
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;
260 // ... and dispatch the event to the LyX core.
261 wa_->view().workArea()->dispatch(cmd);
266 void QContentPane::wheelEvent(QWheelEvent * e)
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());
276 void QContentPane::keyPressEvent(QKeyEvent * e)
278 keyeventQueue_.push(boost::shared_ptr<QKeyEvent>(new QKeyEvent(*e)));
282 void QContentPane::keyeventTimeout()
284 bool handle_autos = true;
286 while (!keyeventQueue_.empty()) {
287 boost::shared_ptr<QKeyEvent> ev = keyeventQueue_.front();
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();
296 boost::shared_ptr<QLyXKeySym> sym(new QLyXKeySym);
299 wa_->view().workArea()->processKeySym(sym, q_key_state(ev->state()));
300 keyeventQueue_.pop();
302 handle_autos = false;
305 // Restart the timer.
306 step_timer_.start(25, true);
310 void QContentPane::doubleClickTimeout()
312 if (!dc_event_.active)
315 dc_event_.active = false;
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);
324 void QContentPane::mouseDoubleClickEvent(QMouseEvent * e)
326 dc_event_ = double_click(e);
328 // doubleClickInterval() is just too long.
329 QTimer::singleShot(int(QApplication::doubleClickInterval() / 1.5),
330 this, SLOT(doubleClickTimeout()));
334 void QContentPane::resizeEvent(QResizeEvent *)
336 if (!pixmap_.get()) {
337 pixmap_.reset(new QPixmap(width(), height()));
340 pixmap_->resize(width(), height());
341 wa_->view().workArea()->resizeBufferView();
345 void QContentPane::paintEvent(QPaintEvent * e)
347 if (!pixmap_.get()) {
348 pixmap_.reset(new QPixmap(width(), height()));
349 wa_->view().workArea()->resizeBufferView();
356 q.drawPixmap(QPoint(r.x(), r.y()),
361 void QContentPane::trackScrollbar(bool track_on)
363 track_scrollbar_ = track_on;
366 } // namespace frontend
369 #include "QContentPane_moc.cpp"