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"
16 // Qt defines a macro 'signals' that clashes with a boost namespace.
17 // All is well if the namespace is visible first.
18 #include "QWorkArea.h"
20 #include "QContentPane.h"
21 #include "QLyXKeySym.h"
23 #include <qapplication.h>
26 #include <boost/bind.hpp>
30 /// return the LyX key state from Qt's
31 key_modifier::state q_key_state(Qt::ButtonState state)
33 key_modifier::state k = key_modifier::none;
34 if (state & Qt::ControlButton)
35 k |= key_modifier::ctrl;
36 if (state & Qt::ShiftButton)
37 k |= key_modifier::shift;
38 if (state & Qt::AltButton)
39 k |= key_modifier::alt;
44 /// return the LyX mouse button state from Qt's
45 mouse_button::state q_button_state(Qt::ButtonState button)
47 mouse_button::state b = mouse_button::none;
50 b = mouse_button::button1;
53 b = mouse_button::button2;
56 b = mouse_button::button3;
65 /// return the LyX mouse button state from Qt's
66 mouse_button::state q_motion_state(Qt::ButtonState state)
68 mouse_button::state b = mouse_button::none;
69 if (state & Qt::LeftButton)
70 b |= mouse_button::button1;
71 if (state & Qt::MidButton)
72 b |= mouse_button::button2;
73 if (state & Qt::RightButton)
74 b |= mouse_button::button3;
84 // This is a 'heartbeat' generating synthetic mouse move events when the
85 // cursor is at the top or bottom edge of the viewport. One scroll per 0.2 s
86 SyntheticMouseEvent::SyntheticMouseEvent()
87 : timeout(200), restart_timeout(true),
88 x_old(-1), y_old(-1), scrollbar_value_old(-1.0)
92 QContentPane::QContentPane(QWorkArea * parent)
93 : QWidget(parent, "content_pane", WRepaintNoErase),
94 track_scrollbar_(true), wa_(parent)
96 synthetic_mouse_event_.timeout.timeout.connect(
97 boost::bind(&QContentPane::generateSyntheticMouseEvent,
100 connect(&step_timer_, SIGNAL(timeout()), SLOT(keyeventTimeout()));
102 setFocusPolicy(QWidget::WheelFocus);
104 setCursor(ibeamCursor);
105 #if USE_INPUT_METHODS
106 // to make qt-immodule work
107 setInputMethodEnabled(true);
110 // stupid moc strikes again
111 connect(wa_->scrollbar_, SIGNAL(valueChanged(int)),
112 this, SLOT(scrollBarChanged(int)));
114 // Start the timer, one-shot.
115 step_timer_.start(50, true);
119 #if USE_INPUT_METHODS
120 // to make qt-immodule work
121 void QContentPane::imStartEvent(QIMEvent *e)
127 void QContentPane::imComposeEvent(QIMEvent *e)
133 void QContentPane::imEndEvent(QIMEvent *e)
135 QString const text = e->text();
136 if (!text.isEmpty()) {
138 // needed to make math superscript work on some systems
139 // ideally, such special coding should not be necessary
141 key = Qt::Key_AsciiCircum;
142 QKeyEvent ev(QEvent::KeyPress, key, *text.ascii(), 0, text);
150 void QContentPane::generateSyntheticMouseEvent()
152 // Set things off to generate the _next_ 'pseudo' event.
153 if (synthetic_mouse_event_.restart_timeout)
154 synthetic_mouse_event_.timeout.start();
156 // Has anything changed on-screen since the last timeout signal
158 double const scrollbar_value = wa_->scrollbar_->value();
159 if (scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
160 // Yes it has. Store the params used to check this.
161 synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
163 // ... and dispatch the event to the LyX core.
164 wa_->view().view()->workAreaDispatch(synthetic_mouse_event_.cmd);
169 void QContentPane::scrollBarChanged(int val)
171 if (track_scrollbar_)
172 wa_->view().view()->scrollDocView(val);
176 void QContentPane::mousePressEvent(QMouseEvent * e)
178 if (dc_event_.active && dc_event_ == *e) {
179 dc_event_.active = false;
180 FuncRequest cmd(LFUN_MOUSE_TRIPLE,
181 dc_event_.x, dc_event_.y,
182 q_button_state(dc_event_.state));
183 wa_->view().view()->workAreaDispatch(cmd);
187 FuncRequest const cmd(LFUN_MOUSE_PRESS, e->x(), e->y(),
188 q_button_state(e->button()));
189 wa_->view().view()->workAreaDispatch(cmd);
193 void QContentPane::mouseReleaseEvent(QMouseEvent * e)
195 if (synthetic_mouse_event_.timeout.running())
196 synthetic_mouse_event_.timeout.stop();
198 FuncRequest const cmd(LFUN_MOUSE_RELEASE, e->x(), e->y(),
199 q_button_state(e->button()));
200 wa_->view().view()->workAreaDispatch(cmd);
204 void QContentPane::mouseMoveEvent(QMouseEvent * e)
206 FuncRequest cmd(LFUN_MOUSE_MOTION, e->x(), e->y(),
207 q_motion_state(e->state()));
209 // If we're above or below the work area...
210 if (e->y() <= 20 || e->y() >= height() - 20) {
211 // Make sure only a synthetic event can cause a page scroll,
212 // so they come at a steady rate:
214 // _Force_ a scroll up:
218 // Store the event, to be handled when the timeout expires.
219 synthetic_mouse_event_.cmd = cmd;
221 if (synthetic_mouse_event_.timeout.running())
222 // Discard the event. Note that it _may_ be handled
223 // when the timeout expires if
224 // synthetic_mouse_event_.cmd has not been overwritten.
225 // Ie, when the timeout expires, we handle the
226 // most recent event but discard all others that
227 // occurred after the one used to start the timeout
228 // in the first place.
231 synthetic_mouse_event_.restart_timeout = true;
232 synthetic_mouse_event_.timeout.start();
233 // Fall through to handle this event...
236 } else if (synthetic_mouse_event_.timeout.running()) {
237 // Store the event, to be possibly handled when the timeout
239 // Once the timeout has expired, normal control is returned
240 // to mouseMoveEvent (restart_timeout = false).
241 // This results in a much smoother 'feel' when moving the
242 // mouse back into the work area.
243 synthetic_mouse_event_.cmd = cmd;
244 synthetic_mouse_event_.restart_timeout = false;
248 // Has anything changed on-screen since the last QMouseEvent
250 double const scrollbar_value = wa_->scrollbar_->value();
251 if (e->x() != synthetic_mouse_event_.x_old ||
252 e->y() != synthetic_mouse_event_.y_old ||
253 scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
254 // Yes it has. Store the params used to check this.
255 synthetic_mouse_event_.x_old = e->x();
256 synthetic_mouse_event_.y_old = e->y();
257 synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
259 // ... and dispatch the event to the LyX core.
260 wa_->view().view()->workAreaDispatch(cmd);
265 void QContentPane::wheelEvent(QWheelEvent * e)
267 // Wheel rotation by one notch results in a delta() of 120 (see
268 // documentation of QWheelEvent)
269 int const lines = QApplication::wheelScrollLines() * e->delta() / 120;
270 wa_->scrollbar_->setValue(wa_->scrollbar_->value() -
271 lines * wa_->scrollbar_->lineStep());
275 void QContentPane::keyPressEvent(QKeyEvent * e)
277 keyeventQueue_.push(boost::shared_ptr<QKeyEvent>(new QKeyEvent(*e)));
281 void QContentPane::keyeventTimeout()
283 bool handle_autos = true;
285 while (!keyeventQueue_.empty()) {
286 boost::shared_ptr<QKeyEvent> ev = keyeventQueue_.front();
288 // We never handle more than one auto repeated
289 // char in a list of queued up events.
290 if (!handle_autos && ev->isAutoRepeat()) {
291 keyeventQueue_.pop();
295 boost::shared_ptr<QLyXKeySym> sym(new QLyXKeySym);
298 wa_->view().view()->workAreaKeyPress(sym, q_key_state(ev->state()));
299 keyeventQueue_.pop();
301 handle_autos = false;
304 // Restart the timer.
305 step_timer_.start(25, true);
309 void QContentPane::doubleClickTimeout()
311 if (!dc_event_.active)
314 dc_event_.active = false;
316 FuncRequest cmd(LFUN_MOUSE_DOUBLE,
317 dc_event_.x, dc_event_.y,
318 q_button_state(dc_event_.state));
319 wa_->view().view()->workAreaDispatch(cmd);
323 void QContentPane::mouseDoubleClickEvent(QMouseEvent * e)
325 dc_event_ = double_click(e);
327 // doubleClickInterval() is just too long.
328 QTimer::singleShot(int(QApplication::doubleClickInterval() / 1.5),
329 this, SLOT(doubleClickTimeout()));
333 void QContentPane::resizeEvent(QResizeEvent *)
335 if (!pixmap_.get()) {
336 pixmap_.reset(new QPixmap(width(), height()));
339 pixmap_->resize(width(), height());
340 wa_->view().view()->workAreaResize(width(), height());
344 void QContentPane::paintEvent(QPaintEvent * e)
346 BufferView * buffer_view_ = wa_->view().view();
348 if (!pixmap_.get()) {
349 pixmap_.reset(new QPixmap(width(), height()));
350 buffer_view_->workAreaResize(width(), height());
357 q.drawPixmap(QPoint(r.x(), r.y()),
360 buffer_view_->updateScrollbar();
361 ScrollbarParameters const & scroll_ = buffer_view_->scrollbarParameters();
363 wa_->scrollbar_->setTracking(false);
364 wa_->setScrollbarParams(scroll_.height, scroll_.position,
365 scroll_.lineScrollHeight);
366 wa_->scrollbar_->setTracking(true);
370 void QContentPane::trackScrollbar(bool track_on)
372 track_scrollbar_ = track_on;
375 } // namespace frontend