mirror of
https://git.lyx.org/repos/lyx.git
synced 2025-01-07 17:55:30 +00:00
9beb07356b
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@10623 a592a061-630c-0410-9148-cb99ea01b6c8
354 lines
8.3 KiB
C
354 lines
8.3 KiB
C
/**
|
|
* \file QContentPane.C
|
|
* This file is part of LyX, the document processor.
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* \author John Levon
|
|
*
|
|
* Full author contact details are available in file CREDITS.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
// Qt defines a macro 'signals' that clashes with a boost namespace.
|
|
// All is well if the namespace is visible first.
|
|
#include "QWorkArea.h"
|
|
|
|
#include "QContentPane.h"
|
|
#include "QLyXKeySym.h"
|
|
|
|
#include <qapplication.h>
|
|
#include <qpainter.h>
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
namespace {
|
|
|
|
/// return the LyX key state from Qt's
|
|
key_modifier::state q_key_state(Qt::ButtonState state)
|
|
{
|
|
key_modifier::state k = key_modifier::none;
|
|
if (state & Qt::ControlButton)
|
|
k |= key_modifier::ctrl;
|
|
if (state & Qt::ShiftButton)
|
|
k |= key_modifier::shift;
|
|
if (state & Qt::AltButton)
|
|
k |= key_modifier::alt;
|
|
return k;
|
|
}
|
|
|
|
|
|
/// return the LyX mouse button state from Qt's
|
|
mouse_button::state q_button_state(Qt::ButtonState button)
|
|
{
|
|
mouse_button::state b = mouse_button::none;
|
|
switch (button) {
|
|
case Qt::LeftButton:
|
|
b = mouse_button::button1;
|
|
break;
|
|
case Qt::MidButton:
|
|
b = mouse_button::button2;
|
|
break;
|
|
case Qt::RightButton:
|
|
b = mouse_button::button3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
/// return the LyX mouse button state from Qt's
|
|
mouse_button::state q_motion_state(Qt::ButtonState state)
|
|
{
|
|
mouse_button::state b = mouse_button::none;
|
|
if (state & Qt::LeftButton)
|
|
b |= mouse_button::button1;
|
|
if (state & Qt::MidButton)
|
|
b |= mouse_button::button2;
|
|
if (state & Qt::RightButton)
|
|
b |= mouse_button::button3;
|
|
return b;
|
|
}
|
|
|
|
} // namespace anon
|
|
|
|
|
|
// This is a 'heartbeat' generating synthetic mouse move events when the
|
|
// cursor is at the top or bottom edge of the viewport. One scroll per 0.2 s
|
|
SyntheticMouseEvent::SyntheticMouseEvent()
|
|
: timeout(200), restart_timeout(true),
|
|
x_old(-1), y_old(-1), scrollbar_value_old(-1.0)
|
|
{}
|
|
|
|
|
|
QContentPane::QContentPane(QWorkArea * parent)
|
|
: QWidget(parent, "content_pane", WRepaintNoErase),
|
|
track_scrollbar_(true), wa_(parent)
|
|
{
|
|
synthetic_mouse_event_.timeout.timeout.connect(
|
|
boost::bind(&QContentPane::generateSyntheticMouseEvent,
|
|
this));
|
|
|
|
connect(&step_timer_, SIGNAL(timeout()), SLOT(keyeventTimeout()));
|
|
|
|
setFocusPolicy(QWidget::WheelFocus);
|
|
setFocus();
|
|
setCursor(ibeamCursor);
|
|
#if USE_INPUT_METHODS
|
|
// to make qt-immodule work
|
|
setInputMethodEnabled(true);
|
|
#endif
|
|
|
|
// stupid moc strikes again
|
|
connect(wa_->scrollbar_, SIGNAL(valueChanged(int)),
|
|
this, SLOT(scrollBarChanged(int)));
|
|
|
|
// Start the timer, one-shot.
|
|
step_timer_.start(50, true);
|
|
}
|
|
|
|
|
|
#if USE_INPUT_METHODS
|
|
// to make qt-immodule work
|
|
void QContentPane::imStartEvent(QIMEvent *e)
|
|
{
|
|
e->accept();
|
|
}
|
|
|
|
|
|
void QContentPane::imComposeEvent(QIMEvent *e)
|
|
{
|
|
e->accept();
|
|
}
|
|
|
|
|
|
void QContentPane::imEndEvent(QIMEvent *e)
|
|
{
|
|
QString const text = e->text();
|
|
if (!text.isEmpty()) {
|
|
int key = 0;
|
|
// needed to make math superscript work on some systems
|
|
// ideally, such special coding should not be necessary
|
|
if (text == "^")
|
|
key = Qt::Key_AsciiCircum;
|
|
QKeyEvent ev(QEvent::KeyPress, key, *text.ascii(), 0, text);
|
|
keyPressEvent(&ev);
|
|
}
|
|
e->accept();
|
|
}
|
|
#endif
|
|
|
|
|
|
void QContentPane::generateSyntheticMouseEvent()
|
|
{
|
|
// Set things off to generate the _next_ 'pseudo' event.
|
|
if (synthetic_mouse_event_.restart_timeout)
|
|
synthetic_mouse_event_.timeout.start();
|
|
|
|
// Has anything changed on-screen since the last timeout signal
|
|
// was received?
|
|
double const scrollbar_value = wa_->scrollbar_->value();
|
|
if (scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
|
|
// Yes it has. Store the params used to check this.
|
|
synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
|
|
|
|
// ... and dispatch the event to the LyX core.
|
|
wa_->dispatch(synthetic_mouse_event_.cmd);
|
|
}
|
|
}
|
|
|
|
|
|
void QContentPane::scrollBarChanged(int val)
|
|
{
|
|
if (track_scrollbar_)
|
|
wa_->scrollDocView(val);
|
|
}
|
|
|
|
|
|
void QContentPane::mousePressEvent(QMouseEvent * e)
|
|
{
|
|
if (dc_event_.active && dc_event_ == *e) {
|
|
dc_event_.active = false;
|
|
FuncRequest cmd(LFUN_MOUSE_TRIPLE,
|
|
dc_event_.x, dc_event_.y,
|
|
q_button_state(dc_event_.state));
|
|
wa_->dispatch(cmd);
|
|
return;
|
|
}
|
|
|
|
FuncRequest const cmd(LFUN_MOUSE_PRESS, e->x(), e->y(),
|
|
q_button_state(e->button()));
|
|
wa_->dispatch(cmd);
|
|
}
|
|
|
|
|
|
void QContentPane::mouseReleaseEvent(QMouseEvent * e)
|
|
{
|
|
if (synthetic_mouse_event_.timeout.running())
|
|
synthetic_mouse_event_.timeout.stop();
|
|
|
|
FuncRequest const cmd(LFUN_MOUSE_RELEASE, e->x(), e->y(),
|
|
q_button_state(e->button()));
|
|
wa_->dispatch(cmd);
|
|
}
|
|
|
|
|
|
void QContentPane::mouseMoveEvent(QMouseEvent * e)
|
|
{
|
|
FuncRequest cmd(LFUN_MOUSE_MOTION, e->x(), e->y(),
|
|
q_motion_state(e->state()));
|
|
|
|
// If we're above or below the work area...
|
|
if (e->y() <= 20 || e->y() >= height() - 20) {
|
|
// Make sure only a synthetic event can cause a page scroll,
|
|
// so they come at a steady rate:
|
|
if (e->y() <= 20)
|
|
// _Force_ a scroll up:
|
|
cmd.y = -40;
|
|
else
|
|
cmd.y = height();
|
|
// Store the event, to be handled when the timeout expires.
|
|
synthetic_mouse_event_.cmd = cmd;
|
|
|
|
if (synthetic_mouse_event_.timeout.running())
|
|
// Discard the event. Note that it _may_ be handled
|
|
// when the timeout expires if
|
|
// synthetic_mouse_event_.cmd has not been overwritten.
|
|
// Ie, when the timeout expires, we handle the
|
|
// most recent event but discard all others that
|
|
// occurred after the one used to start the timeout
|
|
// in the first place.
|
|
return;
|
|
else {
|
|
synthetic_mouse_event_.restart_timeout = true;
|
|
synthetic_mouse_event_.timeout.start();
|
|
// Fall through to handle this event...
|
|
}
|
|
|
|
} else if (synthetic_mouse_event_.timeout.running()) {
|
|
// Store the event, to be possibly handled when the timeout
|
|
// expires.
|
|
// Once the timeout has expired, normal control is returned
|
|
// to mouseMoveEvent (restart_timeout = false).
|
|
// This results in a much smoother 'feel' when moving the
|
|
// mouse back into the work area.
|
|
synthetic_mouse_event_.cmd = cmd;
|
|
synthetic_mouse_event_.restart_timeout = false;
|
|
return;
|
|
}
|
|
|
|
// Has anything changed on-screen since the last QMouseEvent
|
|
// was received?
|
|
double const scrollbar_value = wa_->scrollbar_->value();
|
|
if (e->x() != synthetic_mouse_event_.x_old ||
|
|
e->y() != synthetic_mouse_event_.y_old ||
|
|
scrollbar_value != synthetic_mouse_event_.scrollbar_value_old) {
|
|
// Yes it has. Store the params used to check this.
|
|
synthetic_mouse_event_.x_old = e->x();
|
|
synthetic_mouse_event_.y_old = e->y();
|
|
synthetic_mouse_event_.scrollbar_value_old = scrollbar_value;
|
|
|
|
// ... and dispatch the event to the LyX core.
|
|
wa_->dispatch(cmd);
|
|
}
|
|
}
|
|
|
|
|
|
void QContentPane::wheelEvent(QWheelEvent * e)
|
|
{
|
|
wa_->scrollbar_->setValue(wa_->scrollbar_->value() - e->delta());
|
|
}
|
|
|
|
|
|
void QContentPane::keyPressEvent(QKeyEvent * e)
|
|
{
|
|
keyeventQueue_.push(boost::shared_ptr<QKeyEvent>(new QKeyEvent(*e)));
|
|
}
|
|
|
|
|
|
void QContentPane::keyeventTimeout()
|
|
{
|
|
bool handle_autos = true;
|
|
|
|
while (!keyeventQueue_.empty()) {
|
|
boost::shared_ptr<QKeyEvent> ev = keyeventQueue_.front();
|
|
|
|
// We never handle more than one auto repeated
|
|
// char in a list of queued up events.
|
|
if (!handle_autos && ev->isAutoRepeat()) {
|
|
keyeventQueue_.pop();
|
|
continue;
|
|
}
|
|
|
|
boost::shared_ptr<QLyXKeySym> sym(new QLyXKeySym);
|
|
sym->set(ev.get());
|
|
|
|
wa_->workAreaKeyPress(sym, q_key_state(ev->state()));
|
|
keyeventQueue_.pop();
|
|
|
|
handle_autos = false;
|
|
}
|
|
|
|
// Restart the timer.
|
|
step_timer_.start(25, true);
|
|
}
|
|
|
|
|
|
void QContentPane::doubleClickTimeout()
|
|
{
|
|
if (!dc_event_.active)
|
|
return;
|
|
|
|
dc_event_.active = false;
|
|
|
|
FuncRequest cmd(LFUN_MOUSE_DOUBLE,
|
|
dc_event_.x, dc_event_.y,
|
|
q_button_state(dc_event_.state));
|
|
wa_->dispatch(cmd);
|
|
}
|
|
|
|
|
|
void QContentPane::mouseDoubleClickEvent(QMouseEvent * e)
|
|
{
|
|
dc_event_ = double_click(e);
|
|
|
|
// doubleClickInterval() is just too long.
|
|
QTimer::singleShot(int(QApplication::doubleClickInterval() / 1.5),
|
|
this, SLOT(doubleClickTimeout()));
|
|
}
|
|
|
|
|
|
void QContentPane::resizeEvent(QResizeEvent *)
|
|
{
|
|
if (!pixmap_.get()) {
|
|
pixmap_.reset(new QPixmap(width(), height()));
|
|
}
|
|
|
|
pixmap_->resize(width(), height());
|
|
wa_->workAreaResize();
|
|
}
|
|
|
|
|
|
void QContentPane::paintEvent(QPaintEvent * e)
|
|
{
|
|
if (!pixmap_.get()) {
|
|
pixmap_.reset(new QPixmap(width(), height()));
|
|
wa_->workAreaResize();
|
|
return;
|
|
}
|
|
|
|
QRect r(e->rect());
|
|
|
|
QPainter q(this);
|
|
q.drawPixmap(QPoint(r.x(), r.y()),
|
|
*pixmap_.get(), r);
|
|
}
|
|
|
|
|
|
void QContentPane::trackScrollbar(bool track_on)
|
|
{
|
|
track_scrollbar_ = track_on;
|
|
}
|