2006-03-05 17:24:44 +00:00
|
|
|
/**
|
2007-04-26 03:53:02 +00:00
|
|
|
* \file GuiWorkArea.cpp
|
2006-03-05 17:24:44 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
* Licence details can be found in the file COPYING.
|
|
|
|
*
|
|
|
|
* \author John Levon
|
|
|
|
* \author Abdelrazak Younes
|
|
|
|
*
|
|
|
|
* Full author contact details are available in file CREDITS.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
#include "GuiWorkArea.h"
|
2011-10-16 22:48:26 +00:00
|
|
|
#include "GuiWorkArea_Private.h"
|
2006-07-13 16:37:55 +00:00
|
|
|
|
2008-05-23 08:43:07 +00:00
|
|
|
#include "ColorCache.h"
|
|
|
|
#include "FontLoader.h"
|
|
|
|
#include "Menus.h"
|
|
|
|
|
2007-10-06 15:48:58 +00:00
|
|
|
#include "Buffer.h"
|
2008-11-22 14:45:47 +00:00
|
|
|
#include "BufferList.h"
|
2007-11-12 22:15:51 +00:00
|
|
|
#include "BufferParams.h"
|
2006-04-13 20:39:42 +00:00
|
|
|
#include "BufferView.h"
|
2007-11-12 22:15:51 +00:00
|
|
|
#include "CoordCache.h"
|
2007-10-11 09:59:01 +00:00
|
|
|
#include "Cursor.h"
|
2007-11-12 22:15:51 +00:00
|
|
|
#include "Font.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "FuncRequest.h"
|
2007-11-12 22:15:51 +00:00
|
|
|
#include "GuiApplication.h"
|
2008-04-21 19:12:15 +00:00
|
|
|
#include "GuiCompleter.h"
|
2007-11-12 22:15:51 +00:00
|
|
|
#include "GuiKeySymbol.h"
|
|
|
|
#include "GuiPainter.h"
|
2007-11-13 09:52:28 +00:00
|
|
|
#include "GuiView.h"
|
2007-11-12 22:15:51 +00:00
|
|
|
#include "KeySymbol.h"
|
|
|
|
#include "Language.h"
|
2010-02-09 16:11:13 +00:00
|
|
|
#include "LyX.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "LyXRC.h"
|
2008-08-24 20:36:23 +00:00
|
|
|
#include "LyXVC.h"
|
2007-11-12 22:15:51 +00:00
|
|
|
#include "qt_helpers.h"
|
2008-04-06 11:52:11 +00:00
|
|
|
#include "Text.h"
|
2010-06-14 22:14:05 +00:00
|
|
|
#include "TextMetrics.h"
|
2007-08-14 13:14:47 +00:00
|
|
|
#include "version.h"
|
2006-08-17 17:15:17 +00:00
|
|
|
|
2006-10-23 08:46:09 +00:00
|
|
|
#include "graphics/GraphicsImage.h"
|
|
|
|
#include "graphics/GraphicsLoader.h"
|
|
|
|
|
2009-04-04 09:14:38 +00:00
|
|
|
#include "support/convert.h"
|
2009-03-09 19:12:02 +00:00
|
|
|
#include "support/debug.h"
|
2007-11-29 19:19:39 +00:00
|
|
|
#include "support/gettext.h"
|
2011-07-14 17:00:35 +00:00
|
|
|
#include "support/lassert.h"
|
2014-06-09 11:05:50 +00:00
|
|
|
#include "support/TempFile.h"
|
2007-11-12 22:15:51 +00:00
|
|
|
|
|
|
|
#include "frontends/Application.h"
|
|
|
|
#include "frontends/FontMetrics.h"
|
|
|
|
#include "frontends/WorkAreaManager.h"
|
|
|
|
|
2007-12-25 20:12:07 +00:00
|
|
|
#include <QContextMenuEvent>
|
2013-02-03 11:23:31 +00:00
|
|
|
#if (QT_VERSION < 0x050000)
|
2007-08-14 13:14:47 +00:00
|
|
|
#include <QInputContext>
|
2013-02-03 11:23:31 +00:00
|
|
|
#endif
|
2012-11-10 12:36:40 +00:00
|
|
|
#include <QDrag>
|
2007-12-25 20:12:07 +00:00
|
|
|
#include <QHelpEvent>
|
2014-08-25 18:08:59 +00:00
|
|
|
#ifdef Q_OS_MAC
|
2014-08-22 09:17:34 +00:00
|
|
|
#include <QProxyStyle>
|
2008-03-04 18:53:44 +00:00
|
|
|
#endif
|
2006-03-05 17:24:44 +00:00
|
|
|
#include <QMainWindow>
|
2012-11-10 12:36:40 +00:00
|
|
|
#include <QMimeData>
|
2008-03-07 00:21:23 +00:00
|
|
|
#include <QMenu>
|
2006-03-05 17:24:44 +00:00
|
|
|
#include <QPainter>
|
2007-10-11 19:00:18 +00:00
|
|
|
#include <QPalette>
|
2008-03-08 16:15:10 +00:00
|
|
|
#include <QPixmapCache>
|
2006-03-05 17:24:44 +00:00
|
|
|
#include <QScrollBar>
|
2012-11-10 12:36:40 +00:00
|
|
|
#include <QStyleOption>
|
2012-05-26 08:16:20 +00:00
|
|
|
#include <QStylePainter>
|
2007-03-27 17:26:11 +00:00
|
|
|
#include <QTimer>
|
2007-12-25 20:12:07 +00:00
|
|
|
#include <QToolButton>
|
2007-12-26 06:44:22 +00:00
|
|
|
#include <QToolTip>
|
2008-05-16 06:35:31 +00:00
|
|
|
#include <QMenuBar>
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2010-04-22 11:16:58 +00:00
|
|
|
#include "support/bind.h"
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2008-06-30 17:18:41 +00:00
|
|
|
#include <cmath>
|
2016-07-04 09:04:53 +00:00
|
|
|
#include <iostream>
|
2008-06-30 17:18:41 +00:00
|
|
|
|
2008-02-28 12:41:57 +00:00
|
|
|
int const TabIndicatorWidth = 3;
|
2006-03-12 17:29:34 +00:00
|
|
|
|
2007-08-19 09:44:34 +00:00
|
|
|
#undef KeyPress
|
2008-10-13 11:25:37 +00:00
|
|
|
#undef NoModifier
|
2006-10-23 11:19:17 +00:00
|
|
|
|
2007-12-12 10:16:00 +00:00
|
|
|
using namespace std;
|
2007-12-12 18:57:56 +00:00
|
|
|
using namespace lyx::support;
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
namespace lyx {
|
2006-03-12 17:29:34 +00:00
|
|
|
|
2006-11-26 21:30:39 +00:00
|
|
|
|
2006-03-05 17:24:44 +00:00
|
|
|
/// return the LyX mouse button state from Qt's
|
2006-10-21 00:16:43 +00:00
|
|
|
static mouse_button::state q_button_state(Qt::MouseButton button)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-23 11:19:17 +00:00
|
|
|
/// return the LyX mouse button state from Qt's
|
2006-11-02 22:23:26 +00:00
|
|
|
mouse_button::state q_motion_state(Qt::MouseButtons state)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
namespace frontend {
|
|
|
|
|
2006-11-12 07:50:18 +00:00
|
|
|
class CursorWidget {
|
2006-10-23 11:19:17 +00:00
|
|
|
public:
|
2015-10-10 19:04:28 +00:00
|
|
|
CursorWidget() : rtl_(false), l_shape_(false), completable_(false),
|
|
|
|
show_(false), x_(0), cursor_width_(0)
|
|
|
|
{
|
2011-03-11 06:11:55 +00:00
|
|
|
recomputeWidth();
|
|
|
|
}
|
2006-11-12 07:50:18 +00:00
|
|
|
|
|
|
|
void draw(QPainter & painter)
|
2006-10-23 11:19:17 +00:00
|
|
|
{
|
2008-02-28 12:41:43 +00:00
|
|
|
if (!show_ || !rect_.isValid())
|
|
|
|
return;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-02-28 12:41:57 +00:00
|
|
|
int y = rect_.top();
|
2008-02-28 12:41:43 +00:00
|
|
|
int l = x_ - rect_.left();
|
|
|
|
int r = rect_.right() - x_;
|
2008-02-28 12:41:57 +00:00
|
|
|
int bot = rect_.bottom();
|
2008-02-28 12:41:43 +00:00
|
|
|
|
2010-11-21 18:07:04 +00:00
|
|
|
// draw vertical line
|
2011-03-11 06:11:55 +00:00
|
|
|
painter.fillRect(x_, y, cursor_width_, rect_.height(), color_);
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-02-28 12:41:57 +00:00
|
|
|
// draw RTL/LTR indication
|
2008-02-28 12:41:43 +00:00
|
|
|
painter.setPen(color_);
|
|
|
|
if (l_shape_) {
|
|
|
|
if (rtl_)
|
2008-02-28 12:41:57 +00:00
|
|
|
painter.drawLine(x_, bot, x_ - l, bot);
|
2008-02-28 12:41:43 +00:00
|
|
|
else
|
2011-03-11 06:11:55 +00:00
|
|
|
painter.drawLine(x_, bot, x_ + cursor_width_ + r, bot);
|
2008-02-28 12:41:43 +00:00
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-02-28 12:41:57 +00:00
|
|
|
// draw completion triangle
|
2008-02-28 12:41:43 +00:00
|
|
|
if (completable_) {
|
2008-02-28 12:41:57 +00:00
|
|
|
int m = y + rect_.height() / 2;
|
2008-02-28 12:41:43 +00:00
|
|
|
int d = TabIndicatorWidth - 1;
|
|
|
|
if (rtl_) {
|
2008-02-28 12:41:57 +00:00
|
|
|
painter.drawLine(x_ - 1, m - d, x_ - 1 - d, m);
|
|
|
|
painter.drawLine(x_ - 1, m + d, x_ - 1 - d, m);
|
2008-02-28 12:41:43 +00:00
|
|
|
} else {
|
2011-03-11 06:11:55 +00:00
|
|
|
painter.drawLine(x_ + cursor_width_, m - d, x_ + cursor_width_ + d, m);
|
|
|
|
painter.drawLine(x_ + cursor_width_, m + d, x_ + cursor_width_ + d, m);
|
2007-06-04 06:21:33 +00:00
|
|
|
}
|
|
|
|
}
|
2006-10-23 11:19:17 +00:00
|
|
|
}
|
|
|
|
|
2008-02-28 12:41:43 +00:00
|
|
|
void update(int x, int y, int h, bool l_shape,
|
|
|
|
bool rtl, bool completable)
|
2006-10-23 11:19:17 +00:00
|
|
|
{
|
2007-10-25 12:41:02 +00:00
|
|
|
color_ = guiApp->colorCache().get(Color_cursor);
|
2008-02-28 12:41:43 +00:00
|
|
|
l_shape_ = l_shape;
|
|
|
|
rtl_ = rtl;
|
|
|
|
completable_ = completable;
|
|
|
|
x_ = x;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-02-28 12:41:57 +00:00
|
|
|
// extension to left and right
|
2008-02-28 12:41:43 +00:00
|
|
|
int l = 0;
|
|
|
|
int r = 0;
|
|
|
|
|
2008-02-28 12:41:57 +00:00
|
|
|
// RTL/LTR indication
|
2008-02-28 12:41:43 +00:00
|
|
|
if (l_shape_) {
|
|
|
|
if (rtl)
|
|
|
|
l += h / 3;
|
|
|
|
else
|
|
|
|
r += h / 3;
|
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-02-28 12:41:57 +00:00
|
|
|
// completion triangle
|
2008-02-28 12:41:43 +00:00
|
|
|
if (completable_) {
|
|
|
|
if (rtl)
|
2008-02-28 12:41:57 +00:00
|
|
|
l = max(l, TabIndicatorWidth);
|
2008-02-28 12:41:43 +00:00
|
|
|
else
|
2008-02-28 12:41:57 +00:00
|
|
|
r = max(r, TabIndicatorWidth);
|
2007-06-04 06:21:33 +00:00
|
|
|
}
|
2008-02-28 12:41:43 +00:00
|
|
|
|
2008-02-28 12:41:57 +00:00
|
|
|
// compute overall rectangle
|
2011-03-11 06:11:55 +00:00
|
|
|
rect_ = QRect(x - l, y, cursor_width_ + r + l, h);
|
2006-11-12 07:50:18 +00:00
|
|
|
}
|
2006-10-23 11:19:17 +00:00
|
|
|
|
2006-11-12 08:29:05 +00:00
|
|
|
void show(bool set_show = true) { show_ = set_show; }
|
2006-11-12 07:50:18 +00:00
|
|
|
void hide() { show_ = false; }
|
2011-03-11 06:11:55 +00:00
|
|
|
int cursorWidth() const { return cursor_width_; }
|
|
|
|
void recomputeWidth() {
|
|
|
|
cursor_width_ = lyxrc.cursor_width
|
|
|
|
? lyxrc.cursor_width
|
2016-10-29 08:28:34 +00:00
|
|
|
: 1 + int((lyxrc.currentZoom + 50) / 200.0);
|
2011-03-11 06:11:55 +00:00
|
|
|
}
|
2006-10-23 11:19:17 +00:00
|
|
|
|
2006-11-12 08:29:05 +00:00
|
|
|
QRect const & rect() { return rect_; }
|
|
|
|
|
2006-11-12 07:50:18 +00:00
|
|
|
private:
|
2008-02-28 12:41:57 +00:00
|
|
|
/// cursor is in RTL or LTR text
|
2008-02-28 12:41:43 +00:00
|
|
|
bool rtl_;
|
2008-02-28 12:41:57 +00:00
|
|
|
/// indication for RTL or LTR
|
2008-02-28 12:41:43 +00:00
|
|
|
bool l_shape_;
|
2008-02-28 12:41:57 +00:00
|
|
|
/// triangle to show that a completion is available
|
2008-02-28 12:41:43 +00:00
|
|
|
bool completable_;
|
2006-11-02 22:53:10 +00:00
|
|
|
///
|
2006-11-12 07:50:18 +00:00
|
|
|
bool show_;
|
2006-11-02 22:53:10 +00:00
|
|
|
///
|
2006-11-12 07:50:18 +00:00
|
|
|
QColor color_;
|
2008-02-28 12:41:57 +00:00
|
|
|
/// rectangle, possibly with l_shape and completion triangle
|
2006-11-12 07:50:18 +00:00
|
|
|
QRect rect_;
|
2008-02-28 12:41:57 +00:00
|
|
|
/// x position (were the vertical line is drawn)
|
2008-02-28 12:41:43 +00:00
|
|
|
int x_;
|
2011-03-11 06:11:55 +00:00
|
|
|
|
|
|
|
int cursor_width_;
|
2006-10-23 11:19:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-03-05 17:24:44 +00:00
|
|
|
// 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()
|
2010-06-14 22:14:05 +00:00
|
|
|
: timeout(200), restart_timeout(true)
|
2006-03-05 17:24:44 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
GuiWorkArea::Private::Private(GuiWorkArea * parent)
|
2016-10-19 09:55:08 +00:00
|
|
|
: p(parent), screen_(0), buffer_view_(0), lyx_view_(0),
|
|
|
|
cursor_visible_(false), cursor_(0),
|
|
|
|
need_resize_(false), schedule_redraw_(false), preedit_lines_(1),
|
|
|
|
pixel_ratio_(1.0),
|
|
|
|
completer_(new GuiCompleter(p, p)), dialog_mode_(false),
|
2017-02-28 21:58:44 +00:00
|
|
|
read_only_(false), clean_(true), externally_modified_(false)
|
2011-10-16 22:48:26 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-17 08:57:28 +00:00
|
|
|
GuiWorkArea::GuiWorkArea(QWidget * /* w */)
|
2011-10-16 22:48:26 +00:00
|
|
|
: d(new Private(this))
|
2008-11-15 22:34:52 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
|
2008-11-15 22:34:52 +00:00
|
|
|
GuiWorkArea::GuiWorkArea(Buffer & buffer, GuiView & gv)
|
2011-10-16 22:48:26 +00:00
|
|
|
: d(new Private(this))
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2008-11-15 22:34:52 +00:00
|
|
|
setGuiView(gv);
|
2014-10-18 13:52:43 +00:00
|
|
|
buffer.params().display_pixel_ratio = theGuiApp()->pixelRatio();
|
2008-11-15 22:34:52 +00:00
|
|
|
setBuffer(buffer);
|
2008-11-22 14:45:47 +00:00
|
|
|
init();
|
2008-11-15 22:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-12 17:23:13 +00:00
|
|
|
double GuiWorkArea::pixelRatio() const
|
|
|
|
{
|
2014-10-18 13:52:43 +00:00
|
|
|
#if QT_VERSION >= 0x050000
|
2017-01-22 00:06:00 +00:00
|
|
|
return qt_scale_factor * devicePixelRatio();
|
2014-10-12 17:23:13 +00:00
|
|
|
#else
|
|
|
|
return 1.0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-15 22:34:52 +00:00
|
|
|
void GuiWorkArea::init()
|
|
|
|
{
|
2007-11-12 22:15:51 +00:00
|
|
|
// Setup the signals
|
2011-10-16 22:48:26 +00:00
|
|
|
connect(&d->cursor_timeout_, SIGNAL(timeout()),
|
2007-11-21 21:15:23 +00:00
|
|
|
this, SLOT(toggleCursor()));
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-02-02 03:05:54 +00:00
|
|
|
int const time = QApplication::cursorFlashTime() / 2;
|
|
|
|
if (time > 0) {
|
2011-10-16 22:48:26 +00:00
|
|
|
d->cursor_timeout_.setInterval(time);
|
|
|
|
d->cursor_timeout_.start();
|
2008-04-06 11:52:11 +00:00
|
|
|
} else {
|
2008-02-02 03:05:54 +00:00
|
|
|
// let's initialize this just to be safe
|
2011-10-16 22:48:26 +00:00
|
|
|
d->cursor_timeout_.setInterval(500);
|
2008-04-06 11:52:11 +00:00
|
|
|
}
|
2008-02-02 03:05:54 +00:00
|
|
|
|
2011-10-23 06:52:03 +00:00
|
|
|
d->resetScreen();
|
2009-05-01 13:11:22 +00:00
|
|
|
// With Qt4.5 a mouse event will happen before the first paint event
|
|
|
|
// so make sure that the buffer view has an up to date metrics.
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->resize(viewport()->width(), viewport()->height());
|
|
|
|
d->cursor_ = new frontend::CursorWidget();
|
|
|
|
d->cursor_->hide();
|
2006-10-23 11:19:17 +00:00
|
|
|
|
2006-03-05 17:24:44 +00:00
|
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
setAcceptDrops(true);
|
2006-12-04 04:31:18 +00:00
|
|
|
setMouseTracking(true);
|
2006-03-05 17:24:44 +00:00
|
|
|
setMinimumSize(100, 70);
|
2008-10-13 11:25:37 +00:00
|
|
|
setFrameStyle(QFrame::NoFrame);
|
2007-11-11 22:30:21 +00:00
|
|
|
updateWindowTitle();
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2006-11-13 17:28:37 +00:00
|
|
|
viewport()->setAutoFillBackground(false);
|
2006-12-21 14:50:46 +00:00
|
|
|
// We don't need double-buffering nor SystemBackground on
|
|
|
|
// the viewport because we have our own backing pixmap.
|
|
|
|
viewport()->setAttribute(Qt::WA_NoSystemBackground);
|
|
|
|
|
2008-11-15 22:34:52 +00:00
|
|
|
setFocusPolicy(Qt::StrongFocus);
|
2006-03-12 17:29:34 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
d->setCursorShape(Qt::IBeamCursor);
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
d->synthetic_mouse_event_.timeout.timeout.connect(
|
2010-04-22 11:16:58 +00:00
|
|
|
bind(&GuiWorkArea::generateSyntheticMouseEvent,
|
2007-11-12 22:15:51 +00:00
|
|
|
this));
|
2006-04-05 23:56:29 +00:00
|
|
|
|
2006-03-12 17:29:34 +00:00
|
|
|
// Initialize the vertical Scroll Bar
|
2008-01-29 12:54:23 +00:00
|
|
|
QObject::connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
|
|
|
|
this, SLOT(scrollTo(int)));
|
2006-03-12 17:29:34 +00:00
|
|
|
|
2007-11-29 21:10:35 +00:00
|
|
|
LYXERR(Debug::GUI, "viewport width: " << viewport()->width()
|
|
|
|
<< " viewport height: " << viewport()->height());
|
2006-03-12 17:29:34 +00:00
|
|
|
|
2006-06-26 16:55:35 +00:00
|
|
|
// Enables input methods for asian languages.
|
|
|
|
// Must be set when creating custom text editing widgets.
|
|
|
|
setAttribute(Qt::WA_InputMethodEnabled, true);
|
2008-11-15 22:34:52 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
d->dialog_mode_ = false;
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2006-08-17 17:15:17 +00:00
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
GuiWorkArea::~GuiWorkArea()
|
|
|
|
{
|
2017-03-31 09:16:08 +00:00
|
|
|
// If something is wrong with the buffer, we can ignore it safely
|
|
|
|
try {
|
|
|
|
d->buffer_view_->buffer().workAreaManager().remove(this);
|
|
|
|
} catch(...) {}
|
2011-12-17 21:53:46 +00:00
|
|
|
delete d->screen_;
|
2011-10-16 22:48:26 +00:00
|
|
|
delete d->buffer_view_;
|
|
|
|
delete d->cursor_;
|
2008-07-10 22:14:29 +00:00
|
|
|
// Completer has a QObject parent and is thus automatically destroyed.
|
2010-01-10 18:14:50 +00:00
|
|
|
// See #4758.
|
2008-07-10 22:14:29 +00:00
|
|
|
// delete completer_;
|
2011-10-16 22:48:26 +00:00
|
|
|
delete d;
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-23 10:49:45 +00:00
|
|
|
Qt::CursorShape GuiWorkArea::cursorShape() const
|
|
|
|
{
|
|
|
|
return viewport()->cursor().shape();
|
|
|
|
}
|
|
|
|
|
2010-10-24 22:14:31 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::setCursorShape(Qt::CursorShape shape)
|
2010-10-23 10:49:45 +00:00
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
p->viewport()->setCursor(shape);
|
2010-10-23 10:49:45 +00:00
|
|
|
}
|
|
|
|
|
2010-10-24 22:14:31 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::updateCursorShape()
|
2010-10-24 22:14:31 +00:00
|
|
|
{
|
|
|
|
setCursorShape(buffer_view_->clickableInset()
|
|
|
|
? Qt::PointingHandCursor : Qt::IBeamCursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-15 22:34:52 +00:00
|
|
|
void GuiWorkArea::setGuiView(GuiView & gv)
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
d->lyx_view_ = &gv;
|
2008-11-15 22:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkArea::setBuffer(Buffer & buffer)
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
delete d->buffer_view_;
|
|
|
|
d->buffer_view_ = new BufferView(buffer);
|
2008-11-15 22:34:52 +00:00
|
|
|
buffer.workAreaManager().add(this);
|
|
|
|
|
|
|
|
// HACK: Prevents an additional redraw when the scrollbar pops up
|
|
|
|
// which regularily happens on documents with more than one page.
|
|
|
|
// The policy should be set to "Qt::ScrollBarAsNeeded" soon.
|
|
|
|
// Since we have no geometry information yet, we assume that
|
|
|
|
// a document needs a scrollbar if there is more then four
|
|
|
|
// paragraph in the outermost text.
|
|
|
|
if (buffer.text().paragraphs().size() > 4)
|
|
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
|
|
|
QTimer::singleShot(50, this, SLOT(fixVerticalScrollBar()));
|
2016-09-28 19:33:44 +00:00
|
|
|
Q_EMIT bufferViewChanged();
|
2008-11-15 22:34:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-06 11:52:11 +00:00
|
|
|
void GuiWorkArea::fixVerticalScrollBar()
|
|
|
|
{
|
2008-04-06 14:43:28 +00:00
|
|
|
if (!isFullScreen())
|
2008-06-20 13:29:25 +00:00
|
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
2008-04-06 11:52:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
void GuiWorkArea::close()
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
d->lyx_view_->removeWorkArea(this);
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-09 18:03:53 +00:00
|
|
|
void GuiWorkArea::setFullScreen(bool full_screen)
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->setFullScreen(full_screen);
|
2009-05-26 22:37:14 +00:00
|
|
|
setFrameStyle(QFrame::NoFrame);
|
2008-02-20 14:27:52 +00:00
|
|
|
if (full_screen) {
|
|
|
|
setFrameStyle(QFrame::NoFrame);
|
|
|
|
if (lyxrc.full_screen_scrollbar)
|
|
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
2009-05-26 22:37:14 +00:00
|
|
|
} else
|
2008-02-20 14:27:52 +00:00
|
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
2008-02-09 18:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
BufferView & GuiWorkArea::bufferView()
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
return *d->buffer_view_;
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BufferView const & GuiWorkArea::bufferView() const
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
return *d->buffer_view_;
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkArea::stopBlinkingCursor()
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
d->cursor_timeout_.stop();
|
|
|
|
d->hideCursor();
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkArea::startBlinkingCursor()
|
|
|
|
{
|
2010-06-04 13:24:03 +00:00
|
|
|
// do not show the cursor if the view is busy
|
|
|
|
if (view().busy())
|
|
|
|
return;
|
|
|
|
|
2010-03-20 15:11:25 +00:00
|
|
|
Point p;
|
|
|
|
int h = 0;
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->cursorPosAndHeight(p, h);
|
2010-03-20 15:11:25 +00:00
|
|
|
// Don't start blinking if the cursor isn't on screen.
|
2011-10-16 22:48:26 +00:00
|
|
|
if (!d->buffer_view_->cursorInView(p, h))
|
2010-03-20 15:11:25 +00:00
|
|
|
return;
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
d->showCursor();
|
2010-03-19 16:11:58 +00:00
|
|
|
|
2008-02-02 03:05:54 +00:00
|
|
|
//we're not supposed to cache this value.
|
|
|
|
int const time = QApplication::cursorFlashTime() / 2;
|
|
|
|
if (time <= 0)
|
|
|
|
return;
|
2011-10-16 22:48:26 +00:00
|
|
|
d->cursor_timeout_.setInterval(time);
|
|
|
|
d->cursor_timeout_.start();
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-08 02:03:54 +00:00
|
|
|
void GuiWorkArea::redraw(bool update_metrics)
|
2007-11-12 22:15:51 +00:00
|
|
|
{
|
|
|
|
if (!isVisible())
|
|
|
|
// No need to redraw in this case.
|
|
|
|
return;
|
|
|
|
|
|
|
|
// No need to do anything if this is the current view. The BufferView
|
|
|
|
// metrics are already up to date.
|
2011-10-16 22:48:26 +00:00
|
|
|
if (update_metrics || d->lyx_view_ != guiApp->currentView()
|
|
|
|
|| d->lyx_view_->currentWorkArea() != this) {
|
2007-11-12 22:15:51 +00:00
|
|
|
// FIXME: it would be nice to optimize for the off-screen case.
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->cursor().fixIfBroken();
|
|
|
|
d->buffer_view_->updateMetrics();
|
|
|
|
d->buffer_view_->cursor().fixIfBroken();
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// update cursor position, because otherwise it has to wait until
|
|
|
|
// the blinking interval is over
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->cursor_visible_) {
|
|
|
|
d->hideCursor();
|
|
|
|
d->showCursor();
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::WORKAREA, "WorkArea::redraw screen");
|
2011-10-16 22:48:26 +00:00
|
|
|
d->updateScreen();
|
2007-11-17 11:27:03 +00:00
|
|
|
update(0, 0, viewport()->width(), viewport()->height());
|
2007-11-12 22:15:51 +00:00
|
|
|
|
2007-12-21 09:43:23 +00:00
|
|
|
/// \warning: scrollbar updating *must* be done after the BufferView is drawn
|
|
|
|
/// because \c BufferView::updateScrollbar() is called in \c BufferView::draw().
|
2011-10-16 22:48:26 +00:00
|
|
|
d->updateScrollbar();
|
|
|
|
d->lyx_view_->updateStatusBar();
|
2007-11-20 22:03:56 +00:00
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
if (lyxerr.debugging(Debug::WORKAREA))
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->coordCache().dump();
|
2010-01-25 13:31:07 +00:00
|
|
|
|
2011-10-17 21:19:00 +00:00
|
|
|
updateWindowTitle();
|
2010-10-24 22:14:31 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
d->updateCursorShape();
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-04 02:23:32 +00:00
|
|
|
// Keep in sync with GuiWorkArea::processKeySym below
|
|
|
|
bool GuiWorkArea::queryKeySym(KeySymbol const & key, KeyModifier mod) const
|
|
|
|
{
|
|
|
|
return guiApp->queryKeySym(key, mod);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Keep in sync with GuiWorkArea::queryKeySym above
|
2007-11-12 22:15:51 +00:00
|
|
|
void GuiWorkArea::processKeySym(KeySymbol const & key, KeyModifier mod)
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->lyx_view_->isFullScreen() && d->lyx_view_->menuBar()->isVisible()
|
2009-05-26 21:41:45 +00:00
|
|
|
&& lyxrc.full_screen_menubar) {
|
2008-05-16 08:11:37 +00:00
|
|
|
// FIXME HACK: we should not have to do this here. See related comment
|
|
|
|
// in GuiView::event() (QEvent::ShortcutOverride)
|
2011-10-16 22:48:26 +00:00
|
|
|
d->lyx_view_->menuBar()->hide();
|
2008-05-16 08:11:37 +00:00
|
|
|
}
|
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
// In order to avoid bad surprise in the middle of an operation,
|
2008-02-05 00:37:30 +00:00
|
|
|
// we better stop the blinking cursor...
|
2008-02-05 13:10:42 +00:00
|
|
|
// the cursor gets restarted in GuiView::restartCursor()
|
2007-11-12 22:15:51 +00:00
|
|
|
stopBlinkingCursor();
|
2009-10-04 20:58:20 +00:00
|
|
|
guiApp->processKeySym(key, mod);
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-20 19:10:14 +00:00
|
|
|
void GuiWorkArea::Private::dispatch(FuncRequest const & cmd)
|
2007-11-12 22:15:51 +00:00
|
|
|
{
|
|
|
|
// Handle drag&drop
|
2016-06-20 19:10:14 +00:00
|
|
|
if (cmd.action() == LFUN_FILE_OPEN) {
|
2010-01-29 10:42:39 +00:00
|
|
|
DispatchResult dr;
|
2016-06-20 19:10:14 +00:00
|
|
|
lyx_view_->dispatch(cmd, dr);
|
2007-11-12 22:15:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-10-13 11:25:37 +00:00
|
|
|
bool const notJustMovingTheMouse =
|
2010-04-09 19:00:42 +00:00
|
|
|
cmd.action() != LFUN_MOUSE_MOTION || cmd.button() != mouse_button::none;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
// In order to avoid bad surprise in the middle of an operation, we better stop
|
2008-02-05 13:10:42 +00:00
|
|
|
// the blinking cursor.
|
|
|
|
if (notJustMovingTheMouse)
|
2011-10-16 22:48:26 +00:00
|
|
|
p->stopBlinkingCursor();
|
2007-11-12 22:15:51 +00:00
|
|
|
|
|
|
|
buffer_view_->mouseEventDispatch(cmd);
|
|
|
|
|
|
|
|
// Skip these when selecting
|
2011-10-23 20:21:01 +00:00
|
|
|
// FIXME: let GuiView take care of those.
|
2010-04-09 19:00:42 +00:00
|
|
|
if (cmd.action() != LFUN_MOUSE_MOTION) {
|
2008-04-21 19:12:15 +00:00
|
|
|
completer_->updateVisibility(false, false);
|
2008-05-02 20:58:25 +00:00
|
|
|
lyx_view_->updateDialogs();
|
2008-06-12 16:00:36 +00:00
|
|
|
lyx_view_->updateStatusBar();
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GUI tweaks except with mouse motion with no button pressed.
|
2008-02-05 00:37:30 +00:00
|
|
|
if (notJustMovingTheMouse) {
|
2007-11-12 22:15:51 +00:00
|
|
|
// Slight hack: this is only called currently when we
|
|
|
|
// clicked somewhere, so we force through the display
|
|
|
|
// of the new status here.
|
2011-10-23 20:21:01 +00:00
|
|
|
// FIXME: let GuiView take care of those.
|
2007-11-12 22:15:51 +00:00
|
|
|
lyx_view_->clearMessage();
|
|
|
|
|
2008-02-05 00:37:30 +00:00
|
|
|
// Show the cursor immediately after any operation
|
2011-10-16 22:48:26 +00:00
|
|
|
p->startBlinkingCursor();
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
2010-10-24 21:41:47 +00:00
|
|
|
|
2010-10-24 22:14:31 +00:00
|
|
|
updateCursorShape();
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::resizeBufferView()
|
2007-11-12 22:15:51 +00:00
|
|
|
{
|
|
|
|
// WARNING: Please don't put any code that will trigger a repaint here!
|
|
|
|
// We are already inside a paint event.
|
2011-10-23 20:21:01 +00:00
|
|
|
p->stopBlinkingCursor();
|
|
|
|
// Warn our container (GuiView).
|
|
|
|
p->busy(true);
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
Point point;
|
2009-04-03 21:39:06 +00:00
|
|
|
int h = 0;
|
2011-10-16 22:48:26 +00:00
|
|
|
buffer_view_->cursorPosAndHeight(point, h);
|
|
|
|
bool const cursor_in_view = buffer_view_->cursorInView(point, h);
|
|
|
|
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
|
2009-04-03 21:39:06 +00:00
|
|
|
if (cursor_in_view)
|
|
|
|
buffer_view_->scrollToCursor();
|
2010-03-29 16:00:49 +00:00
|
|
|
updateScreen();
|
2008-02-20 15:37:17 +00:00
|
|
|
|
|
|
|
// Update scrollbars which might have changed due different
|
2008-10-13 11:25:37 +00:00
|
|
|
// BufferView dimension. This is especially important when the
|
2008-02-20 15:37:17 +00:00
|
|
|
// BufferView goes from zero-size to the real-size for the first time,
|
|
|
|
// as the scrollbar paramters are then set for the first time.
|
|
|
|
updateScrollbar();
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2007-11-19 10:04:14 +00:00
|
|
|
need_resize_ = false;
|
2011-10-23 20:21:01 +00:00
|
|
|
p->busy(false);
|
|
|
|
// Eventually, restart the cursor after the resize event.
|
|
|
|
// We might be resizing even if the focus is on another widget so we only
|
|
|
|
// restart the cursor if we have the focus.
|
|
|
|
if (p->hasFocus())
|
|
|
|
QTimer::singleShot(50, p, SLOT(startBlinkingCursor()));
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::showCursor()
|
2007-11-12 22:15:51 +00:00
|
|
|
{
|
|
|
|
if (cursor_visible_)
|
|
|
|
return;
|
|
|
|
|
2010-03-19 16:11:58 +00:00
|
|
|
Point p;
|
|
|
|
int h = 0;
|
|
|
|
buffer_view_->cursorPosAndHeight(p, h);
|
|
|
|
if (!buffer_view_->cursorInView(p, h))
|
|
|
|
return;
|
|
|
|
|
2008-02-28 12:41:43 +00:00
|
|
|
// RTL or not RTL
|
|
|
|
bool l_shape = false;
|
2007-11-12 22:15:51 +00:00
|
|
|
Font const & realfont = buffer_view_->cursor().real_current_font;
|
|
|
|
BufferParams const & bp = buffer_view_->buffer().params();
|
|
|
|
bool const samelang = realfont.language() == bp.language;
|
|
|
|
bool const isrtl = realfont.isVisibleRightToLeft();
|
|
|
|
|
2008-02-28 12:41:43 +00:00
|
|
|
if (!samelang || isrtl != bp.language->rightToLeft())
|
|
|
|
l_shape = true;
|
2007-11-12 22:15:51 +00:00
|
|
|
|
|
|
|
// The ERT language hack needs fixing up
|
|
|
|
if (realfont.language() == latex_language)
|
2008-02-28 12:41:43 +00:00
|
|
|
l_shape = false;
|
2007-11-12 22:15:51 +00:00
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
// show cursor on screen
|
2009-04-03 21:39:06 +00:00
|
|
|
Cursor & cur = buffer_view_->cursor();
|
2008-03-16 17:07:10 +00:00
|
|
|
bool completable = cur.inset().showCompletionCursor()
|
2008-04-21 19:12:15 +00:00
|
|
|
&& completer_->completionAvailable()
|
|
|
|
&& !completer_->popupVisible()
|
|
|
|
&& !completer_->inlineVisible();
|
2010-03-19 16:11:58 +00:00
|
|
|
cursor_visible_ = true;
|
2011-03-11 06:11:55 +00:00
|
|
|
cursor_->recomputeWidth();
|
Keyboard based horizontal scrolling for wide insets
[This commit is the output of the "horizontal scrolling" GSoC 2013
project, by Hashini Senaratne. The code has been cleaned up, some
variables have been renamed and moved from the Cursor class to
BufferView::Private. This is the base from which I (jmarc) will polish
the feature for landing on master.
Below is the original commit log of Hashini, updated to reflect the
changes that have been done.]
This feature also applicable for other insets; graphics and labels.
This implementation is capable of scrolling a single row when reaching
its content which is beyond the screen limits, using left and right
arrow keys.
The attribute 'horiz_scroll_offset_' introduced in the
BufferView::Private class plays a main role in horizontal scrolling of
the wide rows that grow beyond the screen limits. This attribute
represents by how much pixels the current row that the text cursor
lies in should be get scrolled.
The main logic that is responsible for drawing the scrolled rows is
within the BufferView class, BufferView::checkCursorScrollOffset.
* The main logic is called via BufferView::draw.
* What this does is set the horiz_scroll_offset_ attribute in in order to
show the position that the text cursor lies in.
* To make sure that BufferView::draw gets involved when Update flag is
FitCursor, necessary changes are made in BufferView::processUpdateFlags.
Basically what the logic that used to set the horiz_scroll_offset_
does is,
* The row which the text cursor lies in is identified by a
CursorSlice that points to the beginning of the row. This is the
'rowSlice' variable used in BufferView::checkCursorScrollOffset. Acessors
are added to obtain this variable. Here row objects were not used to
identify the current row, because it appears that row objects can
disappear when doing a decoration update for example. This means that
comparing row pointers is not a good idea, because they can change
without notice.
* Stop calculations of horiz_scroll_offset_ variable, if metrics have not been
computed yet. Otherwise the calls to TextMetrics::parMetrics, calls
redoParagraph and may change the row heigths. Therefore vertical scrolling
feature may get disturbed. This is avoided.
* Using BufferView::::setCurrentRowSlice resets horiz_scroll_offset_
when changing cursor row. This is done in order to prevent unwanted
scrolling that happens when changing the selected row using up and
down arrow keys.
* Recompute inset positions before checking scoll offset of the row, by
painting the row insets with drawing disabled. This is done because the
position of insets is computed within the drawing procedure.
* Current x position of the text cursor is compared with the
horiz_scroll_offset_ value and the other variables like row.width(),
bv.workWidth(). Compute the new horiz_scroll_offset_ value in order
to show where the text cursor lies in. The basics conditions that we
check before recomputing it are, if the text cursor lies rightward to
the current right screen boundary, if the text cursor lies leftward
to the current left screen boundary, if the text cursor lies within
screen boundaries but the length of the row is less than the left
boundary of the screen (this happens when we delete some content of
the row using delete key or backspace key).
* Change update strategy when scrooll offset has changed. This allows to
redraw the row when no drawing was scheduled. By doing so, it was
possible to redraw a wide row when moving to the leftmost position of the
wide row, from the leftmost position of the row below, using the left
arrow key.
In TextMetrics::drawParagraph it is checked whether the current row is
what is drawing now. If it is so, the value used to the x value of the row
for drawing is adapted according to BufferView::horizScrollOffset.
The method used to pass boundary() was fixed to get row when cursor was in
a nested inset. This matter is considered in Cursor::textRow and it is
modified accordingly.
GuiWorkArea::Private::showCursor() is modified to show the cursor position
in a scrolled row.
2014-07-26 11:17:28 +00:00
|
|
|
|
|
|
|
//int cur_x = buffer_view_->getPos(cur).x_;
|
|
|
|
// We may have decided to slide the cursor row so that cursor
|
|
|
|
// is visible.
|
|
|
|
p.x_ -= buffer_view_->horizScrollOffset();
|
|
|
|
|
2010-03-19 16:11:58 +00:00
|
|
|
showCursor(p.x_, p.y_, h, l_shape, isrtl, completable);
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::hideCursor()
|
2007-11-12 22:15:51 +00:00
|
|
|
{
|
|
|
|
if (!cursor_visible_)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cursor_visible_ = false;
|
|
|
|
removeCursor();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkArea::toggleCursor()
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->cursor_visible_)
|
|
|
|
d->hideCursor();
|
2007-11-12 22:15:51 +00:00
|
|
|
else
|
2011-10-16 22:48:26 +00:00
|
|
|
d->showCursor();
|
2008-02-02 03:05:54 +00:00
|
|
|
}
|
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::updateScrollbar()
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2008-01-17 17:47:01 +00:00
|
|
|
ScrollbarParameters const & scroll_ = buffer_view_->scrollbarParameters();
|
2016-07-29 20:27:13 +00:00
|
|
|
// Block signals to prevent setRange() and setSliderPosition from causing
|
|
|
|
// recursive calls via the signal valueChanged. (#10311)
|
|
|
|
QSignalBlocker blocker(p->verticalScrollBar());
|
2011-10-16 22:48:26 +00:00
|
|
|
p->verticalScrollBar()->setRange(scroll_.min, scroll_.max);
|
|
|
|
p->verticalScrollBar()->setPageStep(scroll_.page_step);
|
|
|
|
p->verticalScrollBar()->setSingleStep(scroll_.single_step);
|
2016-06-01 20:28:26 +00:00
|
|
|
p->verticalScrollBar()->setSliderPosition(0);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2006-06-26 16:55:35 +00:00
|
|
|
|
2008-01-29 12:54:23 +00:00
|
|
|
void GuiWorkArea::scrollTo(int value)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2007-09-15 13:59:26 +00:00
|
|
|
stopBlinkingCursor();
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->scrollDocView(value, true);
|
2007-09-15 13:59:26 +00:00
|
|
|
|
|
|
|
if (lyxrc.cursor_follows_scrollbar) {
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->setCursorFromScrollbar();
|
2011-10-23 20:21:01 +00:00
|
|
|
// FIXME: let GuiView take care of those.
|
2011-10-16 22:48:26 +00:00
|
|
|
d->lyx_view_->updateLayoutList();
|
2007-09-15 13:59:26 +00:00
|
|
|
}
|
|
|
|
// Show the cursor immediately after any operation.
|
|
|
|
startBlinkingCursor();
|
2015-01-11 18:36:41 +00:00
|
|
|
// FIXME QT5
|
2013-02-03 11:23:31 +00:00
|
|
|
#ifdef Q_WS_X11
|
2007-06-02 07:58:11 +00:00
|
|
|
QApplication::syncX();
|
2013-02-03 11:23:31 +00:00
|
|
|
#endif
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-25 18:53:38 +00:00
|
|
|
bool GuiWorkArea::event(QEvent * e)
|
|
|
|
{
|
2008-01-10 08:19:54 +00:00
|
|
|
switch (e->type()) {
|
|
|
|
case QEvent::ToolTip: {
|
|
|
|
QHelpEvent * helpEvent = static_cast<QHelpEvent *>(e);
|
|
|
|
if (lyxrc.use_tooltip) {
|
|
|
|
QPoint pos = helpEvent->pos();
|
|
|
|
if (pos.x() < viewport()->width()) {
|
2011-10-16 22:48:26 +00:00
|
|
|
QString s = toqstr(d->buffer_view_->toolTip(pos.x(), pos.y()));
|
2016-06-11 11:57:18 +00:00
|
|
|
QToolTip::showText(helpEvent->globalPos(), formatToolTip(s,35));
|
2008-01-10 08:19:54 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
QToolTip::hideText();
|
|
|
|
}
|
|
|
|
// Don't forget to accept the event!
|
|
|
|
e->accept();
|
|
|
|
return true;
|
|
|
|
}
|
2008-01-10 09:04:30 +00:00
|
|
|
|
2016-07-04 02:23:32 +00:00
|
|
|
case QEvent::ShortcutOverride:
|
|
|
|
// keyPressEvent is ShortcutOverride-aware and only accepts the event in
|
|
|
|
// this case
|
|
|
|
keyPressEvent(static_cast<QKeyEvent *>(e));
|
|
|
|
return e->isAccepted();
|
|
|
|
|
2016-06-13 06:39:04 +00:00
|
|
|
case QEvent::KeyPress: {
|
2008-01-10 09:04:30 +00:00
|
|
|
// We catch this event in order to catch the Tab or Shift+Tab key press
|
|
|
|
// which are otherwise reserved to focus switching between controls
|
|
|
|
// within a dialog.
|
|
|
|
QKeyEvent * ke = static_cast<QKeyEvent*>(e);
|
2010-10-08 05:23:59 +00:00
|
|
|
if ((ke->key() == Qt::Key_Tab && ke->modifiers() == Qt::NoModifier)
|
|
|
|
|| (ke->key() == Qt::Key_Backtab && (
|
|
|
|
ke->modifiers() == Qt::ShiftModifier
|
|
|
|
|| ke->modifiers() == Qt::NoModifier))) {
|
2016-06-13 06:39:04 +00:00
|
|
|
keyPressEvent(ke);
|
2010-10-08 05:23:59 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return QAbstractScrollArea::event(e);
|
2008-01-10 09:04:30 +00:00
|
|
|
}
|
|
|
|
|
2008-01-10 08:19:54 +00:00
|
|
|
default:
|
|
|
|
return QAbstractScrollArea::event(e);
|
|
|
|
}
|
|
|
|
return false;
|
2007-12-25 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-25 20:12:07 +00:00
|
|
|
void GuiWorkArea::contextMenuEvent(QContextMenuEvent * e)
|
|
|
|
{
|
2011-10-29 14:48:55 +00:00
|
|
|
string name;
|
2009-07-30 18:12:29 +00:00
|
|
|
if (e->reason() == QContextMenuEvent::Mouse)
|
2011-02-14 21:30:12 +00:00
|
|
|
// the menu name is set on mouse press
|
2011-10-16 22:48:26 +00:00
|
|
|
name = d->context_menu_name_;
|
2011-02-14 21:30:12 +00:00
|
|
|
else {
|
|
|
|
QPoint pos = e->pos();
|
2011-10-16 22:48:26 +00:00
|
|
|
Cursor const & cur = d->buffer_view_->cursor();
|
2011-02-14 21:30:12 +00:00
|
|
|
if (e->reason() == QContextMenuEvent::Keyboard && cur.inTexted()) {
|
|
|
|
// Do not access the context menu of math right in front of before
|
|
|
|
// the cursor. This does not work when the cursor is in text.
|
|
|
|
Inset * inset = cur.paragraph().getInset(cur.pos());
|
|
|
|
if (inset && inset->asInsetMath())
|
|
|
|
--pos.rx();
|
|
|
|
else if (cur.pos() > 0) {
|
|
|
|
Inset * inset = cur.paragraph().getInset(cur.pos() - 1);
|
|
|
|
if (inset)
|
|
|
|
++pos.rx();
|
|
|
|
}
|
2009-07-27 07:39:14 +00:00
|
|
|
}
|
2011-10-16 22:48:26 +00:00
|
|
|
name = d->buffer_view_->contextMenu(pos.x(), pos.y());
|
2009-07-27 07:39:14 +00:00
|
|
|
}
|
2011-02-14 21:30:12 +00:00
|
|
|
|
2007-12-26 12:40:58 +00:00
|
|
|
if (name.empty()) {
|
|
|
|
QAbstractScrollArea::contextMenuEvent(e);
|
|
|
|
return;
|
|
|
|
}
|
2011-02-15 16:53:37 +00:00
|
|
|
// always show mnemonics when the keyboard is used to show the context menu
|
|
|
|
// FIXME: This should be fixed in Qt itself
|
|
|
|
bool const keyboard = (e->reason() == QContextMenuEvent::Keyboard);
|
2011-10-16 22:48:26 +00:00
|
|
|
QMenu * menu = guiApp->menus().menu(toqstr(name), *d->lyx_view_, keyboard);
|
2007-12-26 12:40:58 +00:00
|
|
|
if (!menu) {
|
|
|
|
QAbstractScrollArea::contextMenuEvent(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Position the menu to the right.
|
|
|
|
// FIXME: menu position should be different for RTL text.
|
2008-02-08 23:51:30 +00:00
|
|
|
menu->exec(e->globalPos());
|
2007-12-26 12:48:36 +00:00
|
|
|
e->accept();
|
2007-12-25 20:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-21 13:42:49 +00:00
|
|
|
void GuiWorkArea::focusInEvent(QFocusEvent * e)
|
2006-10-27 23:26:52 +00:00
|
|
|
{
|
2010-01-21 17:11:17 +00:00
|
|
|
LYXERR(Debug::DEBUG, "GuiWorkArea::focusInEvent(): " << this << endl);
|
2012-01-13 03:31:48 +00:00
|
|
|
if (d->lyx_view_->currentWorkArea() != this) {
|
2011-10-16 22:48:26 +00:00
|
|
|
d->lyx_view_->setCurrentWorkArea(this);
|
2012-01-13 03:31:48 +00:00
|
|
|
d->lyx_view_->currentWorkArea()->bufferView().buffer().updateBuffer();
|
|
|
|
}
|
2008-03-03 11:04:17 +00:00
|
|
|
|
2006-12-21 14:50:46 +00:00
|
|
|
startBlinkingCursor();
|
2008-02-21 13:42:49 +00:00
|
|
|
QAbstractScrollArea::focusInEvent(e);
|
2006-10-27 23:26:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-21 13:42:49 +00:00
|
|
|
void GuiWorkArea::focusOutEvent(QFocusEvent * e)
|
2006-10-27 23:26:52 +00:00
|
|
|
{
|
2010-01-21 17:11:17 +00:00
|
|
|
LYXERR(Debug::DEBUG, "GuiWorkArea::focusOutEvent(): " << this << endl);
|
2006-10-27 23:26:52 +00:00
|
|
|
stopBlinkingCursor();
|
2008-02-21 13:42:49 +00:00
|
|
|
QAbstractScrollArea::focusOutEvent(e);
|
2006-10-27 23:26:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
void GuiWorkArea::mousePressEvent(QMouseEvent * e)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->dc_event_.active && d->dc_event_ == *e) {
|
|
|
|
d->dc_event_.active = false;
|
2007-10-17 18:28:45 +00:00
|
|
|
FuncRequest cmd(LFUN_MOUSE_TRIPLE, e->x(), e->y(),
|
2016-06-27 22:54:14 +00:00
|
|
|
q_button_state(e->button()), q_key_state(e->modifiers()));
|
2011-10-16 22:48:26 +00:00
|
|
|
d->dispatch(cmd);
|
2008-02-21 13:42:49 +00:00
|
|
|
e->accept();
|
2006-03-05 17:24:44 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-05 02:44:12 +00:00
|
|
|
#if (QT_VERSION < 0x050000) && !defined(__HAIKU__)
|
2011-03-19 13:14:13 +00:00
|
|
|
inputContext()->reset();
|
2013-02-03 11:23:31 +00:00
|
|
|
#endif
|
2011-03-19 13:14:13 +00:00
|
|
|
|
|
|
|
FuncRequest const cmd(LFUN_MOUSE_PRESS, e->x(), e->y(),
|
2016-06-27 22:54:14 +00:00
|
|
|
q_button_state(e->button()), q_key_state(e->modifiers()));
|
2016-06-20 19:10:14 +00:00
|
|
|
d->dispatch(cmd);
|
2011-03-19 13:14:13 +00:00
|
|
|
|
2011-02-14 21:30:12 +00:00
|
|
|
// Save the context menu on mouse press, because also the mouse
|
|
|
|
// cursor is set on mouse press. Afterwards, we can either release
|
|
|
|
// the mousebutton somewhere else, or the cursor might have moved
|
2011-03-19 13:14:13 +00:00
|
|
|
// due to the DEPM. We need to do this after the mouse has been
|
|
|
|
// set in dispatch(), because the selection state might change.
|
2009-07-30 18:12:29 +00:00
|
|
|
if (e->button() == Qt::RightButton)
|
2011-10-16 22:48:26 +00:00
|
|
|
d->context_menu_name_ = d->buffer_view_->contextMenu(e->x(), e->y());
|
2009-07-30 18:12:29 +00:00
|
|
|
|
2008-02-21 13:42:49 +00:00
|
|
|
e->accept();
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
void GuiWorkArea::mouseReleaseEvent(QMouseEvent * e)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->synthetic_mouse_event_.timeout.running())
|
|
|
|
d->synthetic_mouse_event_.timeout.stop();
|
2006-03-05 17:24:44 +00:00
|
|
|
|
|
|
|
FuncRequest const cmd(LFUN_MOUSE_RELEASE, e->x(), e->y(),
|
2016-06-27 22:54:14 +00:00
|
|
|
q_button_state(e->button()), q_key_state(e->modifiers()));
|
2011-10-16 22:48:26 +00:00
|
|
|
d->dispatch(cmd);
|
2008-02-21 13:42:49 +00:00
|
|
|
e->accept();
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
void GuiWorkArea::mouseMoveEvent(QMouseEvent * e)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2007-03-27 17:31:29 +00:00
|
|
|
// we kill the triple click if we move
|
2007-03-27 17:26:11 +00:00
|
|
|
doubleClickTimeout();
|
2006-03-05 17:24:44 +00:00
|
|
|
FuncRequest cmd(LFUN_MOUSE_MOTION, e->x(), e->y(),
|
2016-06-27 22:54:14 +00:00
|
|
|
q_motion_state(e->buttons()), q_key_state(e->modifiers()));
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2008-02-21 13:42:49 +00:00
|
|
|
e->accept();
|
|
|
|
|
2006-03-05 17:24:44 +00:00
|
|
|
// If we're above or below the work area...
|
2010-06-14 22:14:05 +00:00
|
|
|
if ((e->y() <= 20 || e->y() >= viewport()->height() - 20)
|
|
|
|
&& e->buttons() == mouse_button::button1) {
|
2006-03-05 17:24:44 +00:00
|
|
|
// 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:
|
2010-06-14 22:14:05 +00:00
|
|
|
cmd.set_y(e->y() - 21);
|
2006-03-05 17:24:44 +00:00
|
|
|
else
|
2010-06-14 22:14:05 +00:00
|
|
|
cmd.set_y(e->y() + 21);
|
2006-03-05 17:24:44 +00:00
|
|
|
// Store the event, to be handled when the timeout expires.
|
2011-10-16 22:48:26 +00:00
|
|
|
d->synthetic_mouse_event_.cmd = cmd;
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->synthetic_mouse_event_.timeout.running()) {
|
2006-03-05 17:24:44 +00:00
|
|
|
// 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;
|
2010-06-14 22:14:05 +00:00
|
|
|
}
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
d->synthetic_mouse_event_.restart_timeout = true;
|
|
|
|
d->synthetic_mouse_event_.timeout.start();
|
2008-02-07 13:40:31 +00:00
|
|
|
// Fall through to handle this event...
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
} else if (d->synthetic_mouse_event_.timeout.running()) {
|
2006-03-05 17:24:44 +00:00
|
|
|
// 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.
|
2011-10-16 22:48:26 +00:00
|
|
|
d->synthetic_mouse_event_.cmd = cmd;
|
|
|
|
d->synthetic_mouse_event_.restart_timeout = false;
|
2006-03-05 17:24:44 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-10-16 22:48:26 +00:00
|
|
|
d->dispatch(cmd);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-08 16:15:10 +00:00
|
|
|
void GuiWorkArea::wheelEvent(QWheelEvent * ev)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2006-03-16 20:34:20 +00:00
|
|
|
// Wheel rotation by one notch results in a delta() of 120 (see
|
|
|
|
// documentation of QWheelEvent)
|
2010-06-30 12:38:39 +00:00
|
|
|
double const delta = ev->delta() / 120.0;
|
2010-07-17 22:29:42 +00:00
|
|
|
bool zoom = false;
|
2010-07-22 14:51:35 +00:00
|
|
|
switch (lyxrc.scroll_wheel_zoom) {
|
2010-07-17 22:29:42 +00:00
|
|
|
case LyXRC::SCROLL_WHEEL_ZOOM_CTRL:
|
2010-07-18 12:09:06 +00:00
|
|
|
zoom = ev->modifiers() & Qt::ControlModifier;
|
2010-07-18 12:18:22 +00:00
|
|
|
zoom &= !(ev->modifiers() & (Qt::ShiftModifier | Qt::AltModifier));
|
2010-07-17 22:29:42 +00:00
|
|
|
break;
|
|
|
|
case LyXRC::SCROLL_WHEEL_ZOOM_SHIFT:
|
2010-07-18 12:09:06 +00:00
|
|
|
zoom = ev->modifiers() & Qt::ShiftModifier;
|
2010-07-18 12:18:22 +00:00
|
|
|
zoom &= !(ev->modifiers() & (Qt::ControlModifier | Qt::AltModifier));
|
2010-07-17 22:29:42 +00:00
|
|
|
break;
|
2010-07-18 10:04:59 +00:00
|
|
|
case LyXRC::SCROLL_WHEEL_ZOOM_ALT:
|
2010-07-18 12:09:06 +00:00
|
|
|
zoom = ev->modifiers() & Qt::AltModifier;
|
2010-07-18 12:18:22 +00:00
|
|
|
zoom &= !(ev->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier));
|
2010-07-17 22:29:42 +00:00
|
|
|
break;
|
|
|
|
case LyXRC::SCROLL_WHEEL_ZOOM_OFF:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (zoom) {
|
2010-06-30 12:38:39 +00:00
|
|
|
docstring arg = convert<docstring>(int(5 * delta));
|
2009-04-04 09:14:38 +00:00
|
|
|
lyx::dispatch(FuncRequest(LFUN_BUFFER_ZOOM_IN, arg));
|
2008-07-01 07:28:37 +00:00
|
|
|
return;
|
2008-03-08 16:15:10 +00:00
|
|
|
}
|
2008-07-01 07:28:37 +00:00
|
|
|
|
|
|
|
// Take into account the desktop wide settings.
|
|
|
|
int const lines = qApp->wheelScrollLines();
|
|
|
|
int const page_step = verticalScrollBar()->pageStep();
|
|
|
|
// Test if the wheel mouse is set to one screen at a time.
|
2016-05-27 22:59:25 +00:00
|
|
|
// This is according to
|
|
|
|
// https://doc.qt.io/qt-5/qapplication.html#wheelScrollLines-prop
|
|
|
|
int scroll_value =
|
|
|
|
min(lines * verticalScrollBar()->singleStep(), page_step);
|
2008-07-01 07:28:37 +00:00
|
|
|
|
2010-06-30 12:38:39 +00:00
|
|
|
// Take into account the rotation and the user preferences.
|
|
|
|
scroll_value = int(scroll_value * delta * lyxrc.mouse_wheel_speed);
|
2008-07-01 07:28:37 +00:00
|
|
|
LYXERR(Debug::SCROLLING, "wheelScrollLines = " << lines
|
|
|
|
<< " delta = " << delta << " scroll_value = " << scroll_value
|
|
|
|
<< " page_step = " << page_step);
|
|
|
|
// Now scroll.
|
|
|
|
verticalScrollBar()->setValue(verticalScrollBar()->value() - scroll_value);
|
|
|
|
|
2008-03-08 16:15:10 +00:00
|
|
|
ev->accept();
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2006-03-16 20:34:20 +00:00
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
void GuiWorkArea::generateSyntheticMouseEvent()
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
int const e_y = d->synthetic_mouse_event_.cmd.y();
|
|
|
|
int const wh = d->buffer_view_->workHeight();
|
2010-06-14 22:14:05 +00:00
|
|
|
bool const up = e_y < 0;
|
|
|
|
bool const down = e_y > wh;
|
|
|
|
|
2008-02-05 13:26:22 +00:00
|
|
|
// Set things off to generate the _next_ 'pseudo' event.
|
2010-06-14 22:14:05 +00:00
|
|
|
int step = 50;
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->synthetic_mouse_event_.restart_timeout) {
|
2010-06-14 22:14:05 +00:00
|
|
|
// This is some magic formulae to determine the speed
|
|
|
|
// of scrolling related to the position of the mouse.
|
|
|
|
int time = 200;
|
|
|
|
if (up || down) {
|
|
|
|
int dist = up ? -e_y : e_y - wh;
|
|
|
|
time = max(min(200, 250000 / (dist * dist)), 1) ;
|
|
|
|
|
|
|
|
if (time < 40) {
|
|
|
|
step = 80000 / (time * time);
|
|
|
|
time = 40;
|
|
|
|
}
|
|
|
|
}
|
2011-10-16 22:48:26 +00:00
|
|
|
d->synthetic_mouse_event_.timeout.setTimeout(time);
|
|
|
|
d->synthetic_mouse_event_.timeout.start();
|
2010-06-14 22:14:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Can we scroll further ?
|
|
|
|
int const value = verticalScrollBar()->value();
|
|
|
|
if (value == verticalScrollBar()->maximum()
|
|
|
|
|| value == verticalScrollBar()->minimum()) {
|
2011-10-16 22:48:26 +00:00
|
|
|
d->synthetic_mouse_event_.timeout.stop();
|
2010-06-14 22:14:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll
|
|
|
|
if (step <= 2 * wh) {
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->scroll(up ? -step : step);
|
|
|
|
d->buffer_view_->updateMetrics();
|
2010-06-14 22:14:05 +00:00
|
|
|
} else {
|
2013-05-02 14:38:25 +00:00
|
|
|
d->buffer_view_->scrollDocView(value + (up ? -step : step), false);
|
2010-06-14 22:14:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// In which paragraph do we have to set the cursor ?
|
2011-10-16 22:48:26 +00:00
|
|
|
Cursor & cur = d->buffer_view_->cursor();
|
2011-03-13 16:41:47 +00:00
|
|
|
// FIXME: we don't know howto handle math.
|
|
|
|
Text * text = cur.text();
|
|
|
|
if (!text)
|
|
|
|
return;
|
2011-10-16 22:48:26 +00:00
|
|
|
TextMetrics const & tm = d->buffer_view_->textMetrics(text);
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
pair<pit_type, const ParagraphMetrics *> pp = up ? tm.first() : tm.last();
|
|
|
|
ParagraphMetrics const & pm = *pp.second;
|
|
|
|
pit_type const pit = pp.first;
|
2010-06-14 22:14:05 +00:00
|
|
|
|
|
|
|
if (pm.rows().empty())
|
2009-04-24 21:04:51 +00:00
|
|
|
return;
|
2010-06-14 22:14:05 +00:00
|
|
|
|
|
|
|
// Find the row at which we set the cursor.
|
|
|
|
RowList::const_iterator rit = pm.rows().begin();
|
|
|
|
RowList::const_iterator rlast = pm.rows().end();
|
|
|
|
int yy = pm.position() - pm.ascent();
|
|
|
|
for (--rlast; rit != rlast; ++rit) {
|
|
|
|
int h = rit->height();
|
|
|
|
if ((up && yy + h > 0)
|
|
|
|
|| (!up && yy + h > wh - defaultRowHeight()))
|
|
|
|
break;
|
|
|
|
yy += h;
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
2014-05-02 13:55:10 +00:00
|
|
|
|
2010-06-14 22:14:05 +00:00
|
|
|
// Find the position of the cursor
|
|
|
|
bool bound;
|
2011-10-16 22:48:26 +00:00
|
|
|
int x = d->synthetic_mouse_event_.cmd.x();
|
2014-05-02 13:55:10 +00:00
|
|
|
pos_type const pos = tm.getPosNearX(*rit, x, bound);
|
2010-06-14 22:14:05 +00:00
|
|
|
|
|
|
|
// Set the cursor
|
|
|
|
cur.pit() = pit;
|
|
|
|
cur.pos() = pos;
|
|
|
|
cur.boundary(bound);
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->buffer().changed(false);
|
2010-06-14 22:14:05 +00:00
|
|
|
return;
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
|
2007-09-17 18:41:03 +00:00
|
|
|
void GuiWorkArea::keyPressEvent(QKeyEvent * ev)
|
2006-03-12 17:29:34 +00:00
|
|
|
{
|
2016-07-04 02:23:32 +00:00
|
|
|
// this is also called for ShortcutOverride events. In this case, one must
|
|
|
|
// not act but simply accept the event explicitly.
|
|
|
|
bool const act = (ev->type() != QEvent::ShortcutOverride);
|
|
|
|
|
2008-11-15 22:58:19 +00:00
|
|
|
// Do not process here some keys if dialog_mode_ is set
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->dialog_mode_
|
2008-11-15 22:34:52 +00:00
|
|
|
&& (ev->modifiers() == Qt::NoModifier
|
|
|
|
|| ev->modifiers() == Qt::ShiftModifier)
|
|
|
|
&& (ev->key() == Qt::Key_Escape
|
|
|
|
|| ev->key() == Qt::Key_Enter
|
|
|
|
|| ev->key() == Qt::Key_Return)
|
|
|
|
) {
|
|
|
|
ev->ignore();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-02-21 19:42:34 +00:00
|
|
|
// intercept some keys if completion popup is visible
|
2011-10-16 22:48:26 +00:00
|
|
|
if (d->completer_->popupVisible()) {
|
2008-02-21 19:42:34 +00:00
|
|
|
switch (ev->key()) {
|
|
|
|
case Qt::Key_Enter:
|
|
|
|
case Qt::Key_Return:
|
2016-07-04 02:23:32 +00:00
|
|
|
if (act)
|
|
|
|
d->completer_->activate();
|
2008-02-21 19:42:34 +00:00
|
|
|
ev->accept();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2008-02-26 19:19:34 +00:00
|
|
|
|
2007-06-02 07:58:11 +00:00
|
|
|
// do nothing if there are other events
|
|
|
|
// (the auto repeated events come too fast)
|
2010-06-03 07:26:19 +00:00
|
|
|
// it looks like this is only needed on X11
|
2015-01-11 18:36:41 +00:00
|
|
|
#if defined(Q_WS_X11) || defined(QPA_XCB)
|
2016-07-04 02:23:32 +00:00
|
|
|
// FIXME: this is a weird way to implement event compression. Also, this is
|
|
|
|
// broken with IBus.
|
|
|
|
if (act && qApp->hasPendingEvents() && ev->isAutoRepeat()) {
|
2011-04-08 22:22:52 +00:00
|
|
|
switch (ev->key()) {
|
|
|
|
case Qt::Key_PageDown:
|
|
|
|
case Qt::Key_PageUp:
|
2011-05-29 14:49:58 +00:00
|
|
|
case Qt::Key_Left:
|
|
|
|
case Qt::Key_Right:
|
|
|
|
case Qt::Key_Up:
|
|
|
|
case Qt::Key_Down:
|
2011-04-08 22:22:52 +00:00
|
|
|
LYXERR(Debug::KEY, "system is busy: scroll key event ignored");
|
|
|
|
ev->ignore();
|
|
|
|
return;
|
|
|
|
}
|
2007-06-02 07:58:11 +00:00
|
|
|
}
|
|
|
|
#endif
|
2007-05-17 08:24:44 +00:00
|
|
|
|
2016-07-04 02:23:32 +00:00
|
|
|
KeyModifier const m = q_key_state(ev->modifiers());
|
2011-02-23 14:30:41 +00:00
|
|
|
|
|
|
|
std::string str;
|
|
|
|
if (m & ShiftModifier)
|
|
|
|
str += "Shift-";
|
|
|
|
if (m & ControlModifier)
|
|
|
|
str += "Control-";
|
|
|
|
if (m & AltModifier)
|
|
|
|
str += "Alt-";
|
|
|
|
if (m & MetaModifier)
|
|
|
|
str += "Meta-";
|
2016-07-04 02:23:32 +00:00
|
|
|
|
|
|
|
if (act)
|
|
|
|
LYXERR(Debug::KEY, " count: " << ev->count() << " text: " << ev->text()
|
|
|
|
<< " isAutoRepeat: " << ev->isAutoRepeat() << " key: " << ev->key()
|
|
|
|
<< " keyState: " << str);
|
2006-03-12 17:29:34 +00:00
|
|
|
|
2007-09-17 18:41:03 +00:00
|
|
|
KeySymbol sym;
|
|
|
|
setKeySymbol(&sym, ev);
|
2009-07-06 16:14:20 +00:00
|
|
|
if (sym.isOK()) {
|
2016-07-04 02:23:32 +00:00
|
|
|
if (act) {
|
|
|
|
processKeySym(sym, m);
|
|
|
|
ev->accept();
|
|
|
|
} else
|
|
|
|
ev->setAccepted(queryKeySym(sym, m));
|
2009-07-06 16:14:20 +00:00
|
|
|
} else {
|
|
|
|
ev->ignore();
|
|
|
|
}
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2007-08-31 05:53:55 +00:00
|
|
|
|
|
|
|
void GuiWorkArea::doubleClickTimeout()
|
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
d->dc_event_.active = false;
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2007-08-31 05:53:55 +00:00
|
|
|
|
2007-09-17 18:41:03 +00:00
|
|
|
void GuiWorkArea::mouseDoubleClickEvent(QMouseEvent * ev)
|
2007-03-27 08:05:31 +00:00
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
d->dc_event_ = DoubleClick(ev);
|
2007-03-27 17:31:29 +00:00
|
|
|
QTimer::singleShot(QApplication::doubleClickInterval(), this,
|
2016-06-27 22:54:14 +00:00
|
|
|
SLOT(doubleClickTimeout()));
|
|
|
|
FuncRequest cmd(LFUN_MOUSE_DOUBLE, ev->x(), ev->y(),
|
2016-06-20 19:10:14 +00:00
|
|
|
q_button_state(ev->button()), q_key_state(ev->modifiers()));
|
2011-10-16 22:48:26 +00:00
|
|
|
d->dispatch(cmd);
|
2008-02-21 13:42:49 +00:00
|
|
|
ev->accept();
|
2007-03-27 08:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-23 08:46:09 +00:00
|
|
|
void GuiWorkArea::resizeEvent(QResizeEvent * ev)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2006-10-23 08:46:09 +00:00
|
|
|
QAbstractScrollArea::resizeEvent(ev);
|
2011-10-16 22:48:26 +00:00
|
|
|
d->need_resize_ = true;
|
2008-02-21 13:42:49 +00:00
|
|
|
ev->accept();
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::update(int x, int y, int w, int h)
|
2006-03-20 17:25:02 +00:00
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
p->viewport()->update(x, y, w, h);
|
2006-03-20 17:25:02 +00:00
|
|
|
}
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
|
2006-10-23 08:46:09 +00:00
|
|
|
void GuiWorkArea::paintEvent(QPaintEvent * ev)
|
|
|
|
{
|
2014-10-12 17:23:13 +00:00
|
|
|
QRectF const rc = ev->rect();
|
2007-11-29 21:10:35 +00:00
|
|
|
// LYXERR(Debug::PAINTING, "paintEvent begin: x: " << rc.x()
|
|
|
|
// << " y: " << rc.y() << " w: " << rc.width() << " h: " << rc.height());
|
2006-11-02 22:53:10 +00:00
|
|
|
|
2014-10-12 17:23:13 +00:00
|
|
|
if (d->needResize()) {
|
2011-10-23 06:52:03 +00:00
|
|
|
d->resetScreen();
|
2011-10-16 22:48:26 +00:00
|
|
|
d->resizeBufferView();
|
|
|
|
if (d->cursor_visible_) {
|
|
|
|
d->hideCursor();
|
|
|
|
d->showCursor();
|
2009-12-31 13:46:10 +00:00
|
|
|
}
|
2007-01-04 12:36:17 +00:00
|
|
|
}
|
|
|
|
|
2006-11-02 22:53:10 +00:00
|
|
|
QPainter pain(viewport());
|
2014-10-12 17:23:13 +00:00
|
|
|
double const pr = pixelRatio();
|
|
|
|
QRectF const rcs = QRectF(rc.x() * pr, rc.y() * pr, rc.width() * pr, rc.height() * pr);
|
|
|
|
|
2011-12-17 21:53:46 +00:00
|
|
|
if (lyxrc.use_qimage) {
|
2014-10-12 17:23:13 +00:00
|
|
|
QImage const & image = static_cast<QImage const &>(*d->screen_);
|
|
|
|
pain.drawImage(rc, image, rcs);
|
2011-12-17 21:53:46 +00:00
|
|
|
} else {
|
2014-10-12 17:23:13 +00:00
|
|
|
QPixmap const & pixmap = static_cast<QPixmap const &>(*d->screen_);
|
|
|
|
pain.drawPixmap(rc, pixmap, rcs);
|
2011-12-17 21:53:46 +00:00
|
|
|
}
|
2011-10-16 22:48:26 +00:00
|
|
|
d->cursor_->draw(pain);
|
2008-02-21 13:42:49 +00:00
|
|
|
ev->accept();
|
2006-11-02 22:53:10 +00:00
|
|
|
}
|
2006-10-23 08:46:09 +00:00
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::updateScreen()
|
2006-11-02 22:53:10 +00:00
|
|
|
{
|
2014-10-12 17:23:13 +00:00
|
|
|
GuiPainter pain(screen_, p->pixelRatio());
|
2007-08-27 22:53:16 +00:00
|
|
|
buffer_view_->draw(pain);
|
2006-06-02 12:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::showCursor(int x, int y, int h,
|
2008-02-28 12:41:43 +00:00
|
|
|
bool l_shape, bool rtl, bool completable)
|
2006-06-02 12:01:28 +00:00
|
|
|
{
|
2007-01-18 21:47:27 +00:00
|
|
|
if (schedule_redraw_) {
|
2008-06-20 12:31:03 +00:00
|
|
|
// This happens when a graphic conversion is finished. As we don't know
|
|
|
|
// the size of the new graphics, it's better the update everything.
|
|
|
|
// We can't use redraw() here because this would trigger a infinite
|
|
|
|
// recursive loop with showCursor().
|
2011-10-16 22:48:26 +00:00
|
|
|
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
|
2007-08-21 07:33:46 +00:00
|
|
|
updateScreen();
|
2008-06-20 12:31:03 +00:00
|
|
|
updateScrollbar();
|
2011-10-16 22:48:26 +00:00
|
|
|
p->viewport()->update(QRect(0, 0, p->viewport()->width(), p->viewport()->height()));
|
2007-01-18 21:47:27 +00:00
|
|
|
schedule_redraw_ = false;
|
2007-01-16 15:16:09 +00:00
|
|
|
// Show the cursor immediately after the update.
|
|
|
|
hideCursor();
|
2011-10-16 22:48:26 +00:00
|
|
|
p->toggleCursor();
|
2007-01-16 15:16:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-02-28 12:41:43 +00:00
|
|
|
cursor_->update(x, y, h, l_shape, rtl, completable);
|
2006-10-23 11:19:17 +00:00
|
|
|
cursor_->show();
|
2011-10-16 22:48:26 +00:00
|
|
|
p->viewport()->update(cursor_->rect());
|
2006-06-02 12:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::Private::removeCursor()
|
2006-06-02 12:01:28 +00:00
|
|
|
{
|
2006-10-23 11:19:17 +00:00
|
|
|
cursor_->hide();
|
2006-11-12 08:29:05 +00:00
|
|
|
//if (!qApp->focusWidget())
|
2011-10-16 22:48:26 +00:00
|
|
|
p->viewport()->update(cursor_->rect());
|
2006-06-02 12:01:28 +00:00
|
|
|
}
|
2006-03-12 17:29:34 +00:00
|
|
|
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
|
2006-03-12 17:29:34 +00:00
|
|
|
{
|
2007-04-01 09:14:08 +00:00
|
|
|
QString const & commit_string = e->commitString();
|
|
|
|
docstring const & preedit_string
|
|
|
|
= qstring_to_ucs4(e->preeditString());
|
|
|
|
|
|
|
|
if (!commit_string.isEmpty()) {
|
2006-06-26 16:55:35 +00:00
|
|
|
|
2008-04-20 21:05:35 +00:00
|
|
|
LYXERR(Debug::KEY, "preeditString: " << e->preeditString()
|
|
|
|
<< " commitString: " << e->commitString());
|
2006-06-26 16:55:35 +00:00
|
|
|
|
2006-03-12 17:29:34 +00:00
|
|
|
int key = 0;
|
2007-02-10 16:30:11 +00:00
|
|
|
|
2007-04-01 09:14:08 +00:00
|
|
|
// FIXME Iwami 04/01/07: we should take care also of UTF16 surrogates here.
|
2007-11-29 21:10:35 +00:00
|
|
|
for (int i = 0; i != commit_string.size(); ++i) {
|
2007-04-01 09:14:08 +00:00
|
|
|
QKeyEvent ev(QEvent::KeyPress, key, Qt::NoModifier, commit_string[i]);
|
2007-02-10 16:30:11 +00:00
|
|
|
keyPressEvent(&ev);
|
|
|
|
}
|
2006-03-12 17:29:34 +00:00
|
|
|
}
|
2007-04-01 09:14:08 +00:00
|
|
|
|
2007-05-28 22:27:45 +00:00
|
|
|
// Hide the cursor during the kana-kanji transformation.
|
2007-04-01 09:14:08 +00:00
|
|
|
if (preedit_string.empty())
|
|
|
|
startBlinkingCursor();
|
|
|
|
else
|
|
|
|
stopBlinkingCursor();
|
|
|
|
|
2007-04-01 15:12:19 +00:00
|
|
|
// last_width : for checking if last preedit string was/wasn't empty.
|
2016-07-30 19:28:44 +00:00
|
|
|
// FIXME THREAD && FIXME
|
2013-10-07 22:59:05 +00:00
|
|
|
// We could have more than one work area, right?
|
2007-04-01 14:56:55 +00:00
|
|
|
static bool last_width = false;
|
2007-04-01 09:14:08 +00:00
|
|
|
if (!last_width && preedit_string.empty()) {
|
2007-05-28 22:27:45 +00:00
|
|
|
// if last_width is last length of preedit string.
|
2007-04-01 09:14:08 +00:00
|
|
|
e->accept();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-12 17:23:13 +00:00
|
|
|
GuiPainter pain(d->screen_, pixelRatio());
|
2011-10-16 22:48:26 +00:00
|
|
|
d->buffer_view_->updateMetrics();
|
|
|
|
d->buffer_view_->draw(pain);
|
2016-11-04 11:01:43 +00:00
|
|
|
// FIXME: shall we use real_current_font here? (see #10478)
|
2011-10-16 22:48:26 +00:00
|
|
|
FontInfo font = d->buffer_view_->cursor().getFont().fontInfo();
|
2007-04-01 09:14:08 +00:00
|
|
|
FontMetrics const & fm = theFontMetrics(font);
|
|
|
|
int height = fm.maxHeight();
|
2011-10-16 22:48:26 +00:00
|
|
|
int cur_x = d->cursor_->rect().left();
|
|
|
|
int cur_y = d->cursor_->rect().bottom();
|
2007-04-01 09:14:08 +00:00
|
|
|
|
|
|
|
// redraw area of preedit string.
|
2007-11-14 14:34:55 +00:00
|
|
|
update(0, cur_y - height, viewport()->width(),
|
2011-10-16 22:48:26 +00:00
|
|
|
(height + 1) * d->preedit_lines_);
|
2007-04-01 09:14:08 +00:00
|
|
|
|
|
|
|
if (preedit_string.empty()) {
|
2007-04-01 14:56:55 +00:00
|
|
|
last_width = false;
|
2011-10-16 22:48:26 +00:00
|
|
|
d->preedit_lines_ = 1;
|
2007-04-01 09:14:08 +00:00
|
|
|
e->accept();
|
|
|
|
return;
|
|
|
|
}
|
2007-04-01 14:56:55 +00:00
|
|
|
last_width = true;
|
2007-04-01 09:14:08 +00:00
|
|
|
|
2007-04-01 15:12:19 +00:00
|
|
|
// att : stores an IM attribute.
|
2007-04-01 14:56:55 +00:00
|
|
|
QList<QInputMethodEvent::Attribute> const & att = e->attributes();
|
2007-04-01 09:14:08 +00:00
|
|
|
|
|
|
|
// get attributes of input method cursor.
|
2007-04-01 15:12:19 +00:00
|
|
|
// cursor_pos : cursor position in preedit string.
|
2007-04-01 14:56:55 +00:00
|
|
|
size_t cursor_pos = 0;
|
|
|
|
bool cursor_is_visible = false;
|
2007-11-29 21:10:35 +00:00
|
|
|
for (int i = 0; i != att.size(); ++i) {
|
2007-04-01 09:14:08 +00:00
|
|
|
if (att.at(i).type == QInputMethodEvent::Cursor) {
|
2007-04-01 14:56:55 +00:00
|
|
|
cursor_pos = att.at(i).start;
|
|
|
|
cursor_is_visible = att.at(i).length != 0;
|
2007-04-01 09:14:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t preedit_length = preedit_string.length();
|
|
|
|
|
|
|
|
// get position of selection in input method.
|
|
|
|
// FIXME: isn't there a way to do this simplier?
|
2007-04-01 15:12:19 +00:00
|
|
|
// rStart : cursor position in selected string in IM.
|
2007-04-01 14:56:55 +00:00
|
|
|
size_t rStart = 0;
|
2007-04-01 15:12:19 +00:00
|
|
|
// rLength : selected string length in IM.
|
2007-04-01 14:56:55 +00:00
|
|
|
size_t rLength = 0;
|
|
|
|
if (cursor_pos < preedit_length) {
|
2007-11-29 21:10:35 +00:00
|
|
|
for (int i = 0; i != att.size(); ++i) {
|
2007-04-01 09:14:08 +00:00
|
|
|
if (att.at(i).type == QInputMethodEvent::TextFormat) {
|
2007-04-01 14:56:55 +00:00
|
|
|
if (att.at(i).start <= int(cursor_pos)
|
|
|
|
&& int(cursor_pos) < att.at(i).start + att.at(i).length) {
|
2007-04-01 09:14:08 +00:00
|
|
|
rStart = att.at(i).start;
|
|
|
|
rLength = att.at(i).length;
|
2007-04-01 14:56:55 +00:00
|
|
|
if (!cursor_is_visible)
|
|
|
|
cursor_pos += rLength;
|
2007-04-01 09:14:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2007-04-01 14:56:55 +00:00
|
|
|
rStart = cursor_pos;
|
2007-04-01 09:14:08 +00:00
|
|
|
rLength = 0;
|
|
|
|
}
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
int const right_margin = d->buffer_view_->rightMargin();
|
2007-04-01 09:14:08 +00:00
|
|
|
Painter::preedit_style ps;
|
|
|
|
// Most often there would be only one line:
|
2011-10-16 22:48:26 +00:00
|
|
|
d->preedit_lines_ = 1;
|
2007-04-01 09:14:08 +00:00
|
|
|
for (size_t pos = 0; pos != preedit_length; ++pos) {
|
|
|
|
char_type const typed_char = preedit_string[pos];
|
|
|
|
// reset preedit string style
|
|
|
|
ps = Painter::preedit_default;
|
|
|
|
|
|
|
|
// if we reached the right extremity of the screen, go to next line.
|
2007-11-14 14:34:55 +00:00
|
|
|
if (cur_x + fm.width(typed_char) > viewport()->width() - right_margin) {
|
2007-04-01 09:14:08 +00:00
|
|
|
cur_x = right_margin;
|
|
|
|
cur_y += height + 1;
|
2011-10-16 22:48:26 +00:00
|
|
|
++d->preedit_lines_;
|
2007-04-01 09:14:08 +00:00
|
|
|
}
|
|
|
|
// preedit strings are displayed with dashed underline
|
|
|
|
// and partial strings are displayed white on black indicating
|
|
|
|
// that we are in selecting mode in the input method.
|
|
|
|
// FIXME: rLength == preedit_length is not a changing condition
|
|
|
|
// FIXME: should be put out of the loop.
|
2007-05-28 22:27:45 +00:00
|
|
|
if (pos >= rStart
|
2007-04-01 09:14:08 +00:00
|
|
|
&& pos < rStart + rLength
|
2007-04-01 14:56:55 +00:00
|
|
|
&& !(cursor_pos < rLength && rLength == preedit_length))
|
2007-04-01 09:14:08 +00:00
|
|
|
ps = Painter::preedit_selecting;
|
|
|
|
|
2007-04-01 14:56:55 +00:00
|
|
|
if (pos == cursor_pos
|
|
|
|
&& (cursor_pos < rLength && rLength == preedit_length))
|
2007-04-01 09:14:08 +00:00
|
|
|
ps = Painter::preedit_cursor;
|
|
|
|
|
|
|
|
// draw one character and update cur_x.
|
|
|
|
cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the preedit string screen area.
|
2011-10-16 22:48:26 +00:00
|
|
|
update(0, cur_y - d->preedit_lines_*height, viewport()->width(),
|
|
|
|
(height + 1) * d->preedit_lines_);
|
2007-04-01 09:14:08 +00:00
|
|
|
|
|
|
|
// Don't forget to accept the event!
|
2006-03-12 17:29:34 +00:00
|
|
|
e->accept();
|
|
|
|
}
|
2006-06-26 16:55:35 +00:00
|
|
|
|
2007-04-01 09:14:08 +00:00
|
|
|
|
|
|
|
QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const
|
|
|
|
{
|
2008-09-08 01:18:33 +00:00
|
|
|
QRect cur_r(0, 0, 0, 0);
|
2007-04-01 09:14:08 +00:00
|
|
|
switch (query) {
|
2009-07-27 09:48:40 +00:00
|
|
|
// this is the CJK-specific composition window position and
|
|
|
|
// the context menu position when the menu key is pressed.
|
2007-04-01 09:14:08 +00:00
|
|
|
case Qt::ImMicroFocus:
|
2011-10-16 22:48:26 +00:00
|
|
|
cur_r = d->cursor_->rect();
|
|
|
|
if (d->preedit_lines_ != 1)
|
2007-04-01 09:14:08 +00:00
|
|
|
cur_r.moveLeft(10);
|
2009-07-27 07:39:14 +00:00
|
|
|
cur_r.moveBottom(cur_r.bottom()
|
2011-10-16 22:48:26 +00:00
|
|
|
+ cur_r.height() * (d->preedit_lines_ - 1));
|
2007-04-01 09:14:08 +00:00
|
|
|
// return lower right of cursor in LyX.
|
|
|
|
return cur_r;
|
|
|
|
default:
|
|
|
|
return QWidget::inputMethodQuery(query);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-17 18:28:45 +00:00
|
|
|
|
2007-11-12 22:15:51 +00:00
|
|
|
void GuiWorkArea::updateWindowTitle()
|
|
|
|
{
|
2016-10-19 09:55:08 +00:00
|
|
|
Buffer const & buf = bufferView().buffer();
|
2017-02-28 21:58:44 +00:00
|
|
|
if (buf.fileName() != d->file_name_
|
2017-03-01 21:03:44 +00:00
|
|
|
|| buf.hasReadonlyFlag() != d->read_only_
|
2017-02-28 21:58:44 +00:00
|
|
|
|| buf.lyxvc().vcstatus() != d->vc_status_
|
|
|
|
|| buf.isClean() != d->clean_
|
|
|
|
|| buf.notifiesExternalModification() != d->externally_modified_) {
|
2016-10-19 09:55:08 +00:00
|
|
|
d->file_name_ = buf.fileName();
|
2017-03-01 21:03:44 +00:00
|
|
|
d->read_only_ = buf.hasReadonlyFlag();
|
2016-10-19 09:55:08 +00:00
|
|
|
d->vc_status_ = buf.lyxvc().vcstatus();
|
|
|
|
d->clean_ = buf.isClean();
|
2017-02-28 21:58:44 +00:00
|
|
|
d->externally_modified_ = buf.notifiesExternalModification();
|
2016-10-23 11:48:18 +00:00
|
|
|
Q_EMIT titleChanged(this);
|
2016-10-19 09:55:08 +00:00
|
|
|
}
|
2007-11-12 22:15:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
bool GuiWorkArea::isFullScreen() const
|
2008-02-27 21:04:33 +00:00
|
|
|
{
|
2011-10-16 22:48:26 +00:00
|
|
|
return d->lyx_view_ && d->lyx_view_->isFullScreen();
|
2008-02-27 21:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-16 22:48:26 +00:00
|
|
|
void GuiWorkArea::scheduleRedraw()
|
|
|
|
{
|
|
|
|
d->schedule_redraw_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool GuiWorkArea::inDialogMode() const
|
|
|
|
{
|
|
|
|
return d->dialog_mode_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkArea::setDialogMode(bool mode)
|
|
|
|
{
|
|
|
|
d->dialog_mode_ = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GuiCompleter & GuiWorkArea::completer()
|
|
|
|
{
|
|
|
|
return *d->completer_;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuiView const & GuiWorkArea::view() const
|
|
|
|
{
|
|
|
|
return *d->lyx_view_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GuiView & GuiWorkArea::view()
|
|
|
|
{
|
|
|
|
return *d->lyx_view_;
|
|
|
|
}
|
|
|
|
|
2008-11-22 14:45:47 +00:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// EmbeddedWorkArea
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2008-11-22 17:35:17 +00:00
|
|
|
EmbeddedWorkArea::EmbeddedWorkArea(QWidget * w): GuiWorkArea(w)
|
2008-11-22 14:45:47 +00:00
|
|
|
{
|
2014-06-09 11:05:50 +00:00
|
|
|
support::TempFile tempfile("embedded.internal");
|
|
|
|
tempfile.setAutoRemove(false);
|
|
|
|
buffer_ = theBufferList().newInternalBuffer(tempfile.name().absFileName());
|
2008-11-22 14:45:47 +00:00
|
|
|
buffer_->setUnnamed(true);
|
|
|
|
buffer_->setFullyLoaded(true);
|
|
|
|
setBuffer(*buffer_);
|
|
|
|
setDialogMode(true);
|
|
|
|
}
|
|
|
|
|
2008-11-22 15:21:45 +00:00
|
|
|
|
2008-11-22 14:45:47 +00:00
|
|
|
EmbeddedWorkArea::~EmbeddedWorkArea()
|
|
|
|
{
|
|
|
|
// No need to destroy buffer and bufferview here, because it is done
|
2010-10-18 14:20:03 +00:00
|
|
|
// in theBufferList() destruction loop at application exit
|
2008-11-22 14:45:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-22 17:35:17 +00:00
|
|
|
void EmbeddedWorkArea::closeEvent(QCloseEvent * ev)
|
2008-11-22 15:21:45 +00:00
|
|
|
{
|
2008-11-22 17:35:17 +00:00
|
|
|
disable();
|
|
|
|
GuiWorkArea::closeEvent(ev);
|
2008-11-22 15:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-22 17:35:17 +00:00
|
|
|
void EmbeddedWorkArea::hideEvent(QHideEvent * ev)
|
2008-11-22 15:21:45 +00:00
|
|
|
{
|
|
|
|
disable();
|
2008-11-22 17:35:17 +00:00
|
|
|
GuiWorkArea::hideEvent(ev);
|
2008-11-22 15:21:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-21 11:50:13 +00:00
|
|
|
QSize EmbeddedWorkArea::sizeHint () const
|
|
|
|
{
|
|
|
|
// FIXME(?):
|
|
|
|
// GuiWorkArea sets the size to the screen's viewport
|
|
|
|
// by returning a value this gets overridden
|
|
|
|
// EmbeddedWorkArea is now sized to fit in the layout
|
|
|
|
// of the parent, and has a minimum size set in GuiWorkArea
|
|
|
|
// which is what we return here
|
|
|
|
return QSize(100, 70);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-22 15:21:45 +00:00
|
|
|
void EmbeddedWorkArea::disable()
|
|
|
|
{
|
|
|
|
stopBlinkingCursor();
|
2008-11-22 17:35:17 +00:00
|
|
|
if (view().currentWorkArea() != this)
|
|
|
|
return;
|
2010-01-09 22:49:35 +00:00
|
|
|
// No problem if currentMainWorkArea() is 0 (setCurrentWorkArea()
|
|
|
|
// tolerates it and shows the background logo), what happens if
|
|
|
|
// an EmbeddedWorkArea is closed after closing all document WAs
|
2008-11-22 17:35:17 +00:00
|
|
|
view().setCurrentWorkArea(view().currentMainWorkArea());
|
2008-11-22 15:21:45 +00:00
|
|
|
}
|
|
|
|
|
2007-10-06 15:48:58 +00:00
|
|
|
////////////////////////////////////////////////////////////////////
|
2007-10-17 18:28:45 +00:00
|
|
|
//
|
2008-10-13 11:25:37 +00:00
|
|
|
// TabWorkArea
|
2007-10-17 18:28:45 +00:00
|
|
|
//
|
2007-10-06 15:48:58 +00:00
|
|
|
////////////////////////////////////////////////////////////////////
|
2007-10-17 18:28:45 +00:00
|
|
|
|
2014-08-25 18:08:59 +00:00
|
|
|
#ifdef Q_OS_MAC
|
2014-08-22 09:17:34 +00:00
|
|
|
class NoTabFrameMacStyle : public QProxyStyle {
|
2008-03-04 18:53:44 +00:00
|
|
|
public:
|
|
|
|
///
|
|
|
|
QRect subElementRect(SubElement element, const QStyleOption * option,
|
2008-03-21 09:30:19 +00:00
|
|
|
const QWidget * widget = 0) const
|
2008-03-04 18:53:44 +00:00
|
|
|
{
|
2014-08-22 09:17:34 +00:00
|
|
|
QRect rect = QProxyStyle::subElementRect(element, option, widget);
|
2008-03-04 18:53:44 +00:00
|
|
|
bool noBar = static_cast<QTabWidget const *>(widget)->count() <= 1;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-03-04 18:53:44 +00:00
|
|
|
// The Qt Mac style puts the contents into a 3 pixel wide box
|
|
|
|
// which looks very ugly and not like other Mac applications.
|
|
|
|
// Hence we remove this here, and moreover the 16 pixel round
|
|
|
|
// frame above if the tab bar is hidden.
|
|
|
|
if (element == QStyle::SE_TabWidgetTabContents) {
|
|
|
|
rect.adjust(- rect.left(), 0, rect.left(), 0);
|
|
|
|
if (noBar)
|
|
|
|
rect.setTop(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2008-03-14 16:35:44 +00:00
|
|
|
NoTabFrameMacStyle noTabFrameMacStyle;
|
2008-03-04 18:53:44 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-03-15 01:00:25 +00:00
|
|
|
TabWorkArea::TabWorkArea(QWidget * parent)
|
2016-07-13 06:42:35 +00:00
|
|
|
: QTabWidget(parent), clicked_tab_(-1), midpressed_tab_(-1)
|
2007-10-06 15:48:58 +00:00
|
|
|
{
|
2014-08-25 18:08:59 +00:00
|
|
|
#ifdef Q_OS_MAC
|
2008-03-14 16:35:44 +00:00
|
|
|
setStyle(&noTabFrameMacStyle);
|
2008-03-04 18:53:44 +00:00
|
|
|
#endif
|
|
|
|
|
2007-10-11 19:00:18 +00:00
|
|
|
QPalette pal = palette();
|
2007-11-12 22:15:51 +00:00
|
|
|
pal.setColor(QPalette::Active, QPalette::Button,
|
|
|
|
pal.color(QPalette::Active, QPalette::Window));
|
|
|
|
pal.setColor(QPalette::Disabled, QPalette::Button,
|
|
|
|
pal.color(QPalette::Disabled, QPalette::Window));
|
|
|
|
pal.setColor(QPalette::Inactive, QPalette::Button,
|
|
|
|
pal.color(QPalette::Inactive, QPalette::Window));
|
2007-10-11 19:00:18 +00:00
|
|
|
|
2008-02-21 12:29:26 +00:00
|
|
|
QObject::connect(this, SIGNAL(currentChanged(int)),
|
|
|
|
this, SLOT(on_currentTabChanged(int)));
|
|
|
|
|
2009-03-08 10:21:33 +00:00
|
|
|
closeBufferButton = new QToolButton(this);
|
2008-03-05 15:45:43 +00:00
|
|
|
closeBufferButton->setPalette(pal);
|
2008-02-21 12:29:26 +00:00
|
|
|
// FIXME: rename the icon to closebuffer.png
|
2015-03-12 23:47:21 +00:00
|
|
|
closeBufferButton->setIcon(QIcon(getPixmap("images/", "closetab", "svgz,png")));
|
2008-02-21 22:19:22 +00:00
|
|
|
closeBufferButton->setText("Close File");
|
2008-02-21 12:29:26 +00:00
|
|
|
closeBufferButton->setAutoRaise(true);
|
|
|
|
closeBufferButton->setCursor(Qt::ArrowCursor);
|
2008-02-21 22:19:22 +00:00
|
|
|
closeBufferButton->setToolTip(qt_("Close File"));
|
2008-02-21 12:29:26 +00:00
|
|
|
closeBufferButton->setEnabled(true);
|
|
|
|
QObject::connect(closeBufferButton, SIGNAL(clicked()),
|
|
|
|
this, SLOT(closeCurrentBuffer()));
|
|
|
|
setCornerWidget(closeBufferButton, Qt::TopRightCorner);
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-03-15 02:13:01 +00:00
|
|
|
// setup drag'n'drop
|
|
|
|
QTabBar* tb = new DragTabBar;
|
|
|
|
connect(tb, SIGNAL(tabMoveRequested(int, int)),
|
|
|
|
this, SLOT(moveTab(int, int)));
|
2008-04-29 15:45:06 +00:00
|
|
|
tb->setElideMode(Qt::ElideNone);
|
2008-03-15 02:13:01 +00:00
|
|
|
setTabBar(tb);
|
2008-02-21 12:29:26 +00:00
|
|
|
|
2008-03-15 01:00:25 +00:00
|
|
|
// make us responsible for the context menu of the tabbar
|
2008-03-15 02:13:01 +00:00
|
|
|
tb->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
connect(tb, SIGNAL(customContextMenuRequested(const QPoint &)),
|
2008-03-15 01:00:25 +00:00
|
|
|
this, SLOT(showContextMenu(const QPoint &)));
|
2009-05-02 12:43:42 +00:00
|
|
|
connect(tb, SIGNAL(tabCloseRequested(int)),
|
2009-10-31 18:10:18 +00:00
|
|
|
this, SLOT(closeTab(int)));
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2007-10-06 15:48:58 +00:00
|
|
|
setUsesScrollButtons(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-13 06:42:35 +00:00
|
|
|
void TabWorkArea::mousePressEvent(QMouseEvent *me)
|
|
|
|
{
|
|
|
|
if (me->button() == Qt::MidButton)
|
|
|
|
midpressed_tab_ = tabBar()->tabAt(me->pos());
|
|
|
|
else
|
|
|
|
QTabWidget::mousePressEvent(me);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TabWorkArea::mouseReleaseEvent(QMouseEvent *me)
|
|
|
|
{
|
|
|
|
if (me->button() == Qt::MidButton) {
|
|
|
|
int const midreleased_tab = tabBar()->tabAt(me->pos());
|
|
|
|
if (midpressed_tab_ == midreleased_tab && posIsTab(me->pos()))
|
|
|
|
closeTab(midreleased_tab);
|
|
|
|
} else
|
|
|
|
QTabWidget::mouseReleaseEvent(me);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-25 17:05:38 +00:00
|
|
|
void TabWorkArea::paintEvent(QPaintEvent * event)
|
|
|
|
{
|
|
|
|
if (tabBar()->isVisible()) {
|
|
|
|
QTabWidget::paintEvent(event);
|
|
|
|
} else {
|
|
|
|
// Prevent the selected tab to influence the
|
|
|
|
// painting of the frame of the tab widget.
|
|
|
|
// This is needed for gtk style in Qt.
|
|
|
|
QStylePainter p(this);
|
2012-12-28 12:39:03 +00:00
|
|
|
#if QT_VERSION < 0x050000
|
2012-05-25 17:05:38 +00:00
|
|
|
QStyleOptionTabWidgetFrameV2 opt;
|
2012-12-28 12:39:03 +00:00
|
|
|
#else
|
|
|
|
QStyleOptionTabWidgetFrame opt;
|
|
|
|
#endif
|
2012-05-25 17:05:38 +00:00
|
|
|
initStyleOption(&opt);
|
|
|
|
opt.rect = style()->subElementRect(QStyle::SE_TabWidgetTabPane,
|
|
|
|
&opt, this);
|
|
|
|
opt.selectedTabRect = QRect();
|
|
|
|
p.drawPrimitive(QStyle::PE_FrameTabWidget, opt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-21 23:16:19 +00:00
|
|
|
bool TabWorkArea::posIsTab(QPoint position)
|
|
|
|
{
|
2016-07-29 17:06:49 +00:00
|
|
|
// tabAt returns -1 if tab does not covers position
|
|
|
|
return tabBar()->tabAt(position) > -1;
|
2016-07-21 23:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-18 11:30:20 +00:00
|
|
|
void TabWorkArea::mouseDoubleClickEvent(QMouseEvent * event)
|
|
|
|
{
|
|
|
|
if (event->button() != Qt::LeftButton)
|
|
|
|
return;
|
|
|
|
|
2016-07-21 23:14:16 +00:00
|
|
|
// this code chunk is unnecessary because it seems the event only makes
|
|
|
|
// it this far if it is not on a tab. I'm not sure why this is (maybe
|
|
|
|
// it is handled and ended in DragTabBar?), and thus I'm not sure if
|
|
|
|
// this is true in all cases and if it will be true in the future so I
|
|
|
|
// leave this code for now. (skostysh, 2016-07-21)
|
|
|
|
//
|
2010-04-18 11:30:20 +00:00
|
|
|
// return early if double click on existing tabs
|
2016-07-21 23:16:19 +00:00
|
|
|
if (posIsTab(event->pos()))
|
|
|
|
return;
|
2010-04-18 11:30:20 +00:00
|
|
|
|
|
|
|
dispatch(FuncRequest(LFUN_BUFFER_NEW));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-09 18:29:15 +00:00
|
|
|
void TabWorkArea::setFullScreen(bool full_screen)
|
|
|
|
{
|
|
|
|
for (int i = 0; i != count(); ++i) {
|
2010-10-23 10:49:45 +00:00
|
|
|
if (GuiWorkArea * wa = workArea(i))
|
2008-02-09 18:29:15 +00:00
|
|
|
wa->setFullScreen(full_screen);
|
|
|
|
}
|
2008-02-19 21:51:55 +00:00
|
|
|
|
|
|
|
if (lyxrc.full_screen_tabbar)
|
2009-05-26 21:52:57 +00:00
|
|
|
showBar(!full_screen && count() > 1);
|
2012-01-03 14:21:48 +00:00
|
|
|
else
|
|
|
|
showBar(count() > 1);
|
2008-02-09 18:29:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-06 15:48:58 +00:00
|
|
|
void TabWorkArea::showBar(bool show)
|
|
|
|
{
|
2007-11-19 11:21:45 +00:00
|
|
|
tabBar()->setEnabled(show);
|
2007-10-06 15:48:58 +00:00
|
|
|
tabBar()->setVisible(show);
|
2009-10-05 23:06:29 +00:00
|
|
|
closeBufferButton->setVisible(show && lyxrc.single_close_tab_button);
|
|
|
|
setTabsClosable(!lyxrc.single_close_tab_button);
|
2007-10-06 15:48:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-18 14:08:20 +00:00
|
|
|
GuiWorkAreaContainer * TabWorkArea::widget(int index) const
|
2007-10-16 06:50:09 +00:00
|
|
|
{
|
2017-03-18 14:08:20 +00:00
|
|
|
QWidget * w = QTabWidget::widget(index);
|
|
|
|
if (!w)
|
|
|
|
return nullptr;
|
|
|
|
GuiWorkAreaContainer * wac = dynamic_cast<GuiWorkAreaContainer *>(w);
|
2017-03-01 23:41:02 +00:00
|
|
|
LATTEST(wac);
|
2017-03-18 14:08:20 +00:00
|
|
|
return wac;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GuiWorkAreaContainer * TabWorkArea::currentWidget() const
|
|
|
|
{
|
|
|
|
return widget(currentIndex());
|
2007-10-16 06:50:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-18 14:08:20 +00:00
|
|
|
GuiWorkArea * TabWorkArea::workArea(int index) const
|
2017-03-01 23:41:02 +00:00
|
|
|
{
|
2017-03-18 14:08:20 +00:00
|
|
|
GuiWorkAreaContainer * w = widget(index);
|
|
|
|
if (!w)
|
|
|
|
return nullptr;
|
|
|
|
return w->workArea();
|
2017-03-01 23:41:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-18 14:08:20 +00:00
|
|
|
GuiWorkArea * TabWorkArea::currentWorkArea() const
|
2010-10-23 10:49:45 +00:00
|
|
|
{
|
2017-03-18 14:08:20 +00:00
|
|
|
return workArea(currentIndex());
|
2010-10-23 10:49:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-18 14:08:20 +00:00
|
|
|
GuiWorkArea * TabWorkArea::workArea(Buffer & buffer) const
|
2007-10-16 06:50:09 +00:00
|
|
|
{
|
2017-03-18 14:08:20 +00:00
|
|
|
// FIXME: this method doesn't work if we have more than one work area
|
2010-04-05 14:24:56 +00:00
|
|
|
// showing the same buffer.
|
2007-10-16 06:50:09 +00:00
|
|
|
for (int i = 0; i != count(); ++i) {
|
2010-10-23 10:49:45 +00:00
|
|
|
GuiWorkArea * wa = workArea(i);
|
2008-04-15 18:04:49 +00:00
|
|
|
LASSERT(wa, return 0);
|
2007-10-16 06:50:09 +00:00
|
|
|
if (&wa->bufferView().buffer() == &buffer)
|
|
|
|
return wa;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-07 08:12:20 +00:00
|
|
|
void TabWorkArea::closeAll()
|
|
|
|
{
|
|
|
|
while (count()) {
|
2017-03-01 23:41:02 +00:00
|
|
|
QWidget * wac = widget(0);
|
|
|
|
LASSERT(wac, return);
|
2007-10-07 08:12:20 +00:00
|
|
|
removeTab(0);
|
2017-03-01 23:41:02 +00:00
|
|
|
delete wac;
|
2007-10-07 08:12:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 23:41:02 +00:00
|
|
|
int TabWorkArea::indexOfWorkArea(GuiWorkArea * w) const
|
|
|
|
{
|
|
|
|
for (int index = 0; index < count(); ++index)
|
|
|
|
if (workArea(index) == w)
|
|
|
|
return index;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-07 08:05:02 +00:00
|
|
|
bool TabWorkArea::setCurrentWorkArea(GuiWorkArea * work_area)
|
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(work_area, return false);
|
2017-03-01 23:41:02 +00:00
|
|
|
int index = indexOfWorkArea(work_area);
|
2007-10-07 08:05:02 +00:00
|
|
|
if (index == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (index == currentIndex())
|
|
|
|
// Make sure the work area is up to date.
|
|
|
|
on_currentTabChanged(index);
|
|
|
|
else
|
|
|
|
// Switch to the work area.
|
|
|
|
setCurrentIndex(index);
|
|
|
|
work_area->setFocus();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-19 10:04:14 +00:00
|
|
|
GuiWorkArea * TabWorkArea::addWorkArea(Buffer & buffer, GuiView & view)
|
|
|
|
{
|
|
|
|
GuiWorkArea * wa = new GuiWorkArea(buffer, view);
|
2017-03-01 23:41:02 +00:00
|
|
|
GuiWorkAreaContainer * wac = new GuiWorkAreaContainer(wa);
|
2007-11-19 10:04:14 +00:00
|
|
|
wa->setUpdatesEnabled(false);
|
2007-11-19 12:03:38 +00:00
|
|
|
// Hide tabbar if there's no tab (avoid a resize and a flashing tabbar
|
|
|
|
// when hiding it again below).
|
2008-02-27 21:04:33 +00:00
|
|
|
if (!(currentWorkArea() && currentWorkArea()->isFullScreen()))
|
|
|
|
showBar(count() > 0);
|
2017-03-01 23:41:02 +00:00
|
|
|
addTab(wac, wa->windowTitle());
|
2007-11-19 10:04:14 +00:00
|
|
|
QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
|
2008-04-29 15:44:07 +00:00
|
|
|
this, SLOT(updateTabTexts()));
|
2008-02-27 21:04:33 +00:00
|
|
|
if (currentWorkArea() && currentWorkArea()->isFullScreen())
|
|
|
|
setFullScreen(true);
|
|
|
|
else
|
|
|
|
// Hide tabbar if there's only one tab.
|
|
|
|
showBar(count() > 1);
|
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
updateTabTexts();
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2007-11-19 10:04:14 +00:00
|
|
|
return wa;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-07 08:05:02 +00:00
|
|
|
bool TabWorkArea::removeWorkArea(GuiWorkArea * work_area)
|
|
|
|
{
|
2008-04-15 18:04:49 +00:00
|
|
|
LASSERT(work_area, return false);
|
2017-03-01 23:41:02 +00:00
|
|
|
int index = indexOfWorkArea(work_area);
|
2007-10-07 08:05:02 +00:00
|
|
|
if (index == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
work_area->setUpdatesEnabled(false);
|
2017-03-01 23:41:02 +00:00
|
|
|
QWidget * wac = widget(index);
|
2007-10-07 08:05:02 +00:00
|
|
|
removeTab(index);
|
2017-03-01 23:41:02 +00:00
|
|
|
delete wac;
|
2007-10-07 08:05:02 +00:00
|
|
|
|
|
|
|
if (count()) {
|
|
|
|
// make sure the next work area is enabled.
|
|
|
|
currentWidget()->setUpdatesEnabled(true);
|
2008-03-21 09:30:19 +00:00
|
|
|
if (currentWorkArea() && currentWorkArea()->isFullScreen())
|
2008-02-27 21:04:33 +00:00
|
|
|
setFullScreen(true);
|
|
|
|
else
|
2009-05-05 06:24:43 +00:00
|
|
|
// Show tabbar only if there's more than one tab.
|
2009-05-05 06:22:55 +00:00
|
|
|
showBar(count() > 1);
|
2009-05-26 22:37:14 +00:00
|
|
|
} else
|
2008-03-14 23:24:59 +00:00
|
|
|
lastWorkAreaRemoved();
|
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
updateTabTexts();
|
|
|
|
|
2007-10-07 08:05:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-10-06 15:48:58 +00:00
|
|
|
void TabWorkArea::on_currentTabChanged(int i)
|
|
|
|
{
|
2008-04-15 17:00:53 +00:00
|
|
|
// returns e.g. on application destruction
|
|
|
|
if (i == -1)
|
|
|
|
return;
|
2010-10-23 10:49:45 +00:00
|
|
|
GuiWorkArea * wa = workArea(i);
|
2008-04-15 18:04:49 +00:00
|
|
|
LASSERT(wa, return);
|
2007-10-06 15:48:58 +00:00
|
|
|
wa->setUpdatesEnabled(true);
|
2010-01-08 02:03:54 +00:00
|
|
|
wa->redraw(true);
|
2007-10-06 15:48:58 +00:00
|
|
|
wa->setFocus();
|
|
|
|
///
|
|
|
|
currentWorkAreaChanged(wa);
|
|
|
|
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::GUI, "currentTabChanged " << i
|
2010-01-11 18:01:36 +00:00
|
|
|
<< " File: " << wa->bufferView().buffer().absFileName());
|
2007-10-06 15:48:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-21 12:29:26 +00:00
|
|
|
void TabWorkArea::closeCurrentBuffer()
|
2007-10-06 15:48:58 +00:00
|
|
|
{
|
2009-08-17 22:33:49 +00:00
|
|
|
GuiWorkArea * wa;
|
|
|
|
if (clicked_tab_ == -1)
|
|
|
|
wa = currentWorkArea();
|
|
|
|
else {
|
2010-10-23 10:49:45 +00:00
|
|
|
wa = workArea(clicked_tab_);
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(wa, return);
|
2009-08-17 22:33:49 +00:00
|
|
|
}
|
|
|
|
wa->view().closeWorkArea(wa);
|
2007-10-06 15:48:58 +00:00
|
|
|
}
|
|
|
|
|
2007-11-11 22:30:21 +00:00
|
|
|
|
2009-10-31 18:14:21 +00:00
|
|
|
void TabWorkArea::hideCurrentTab()
|
2008-02-21 12:29:26 +00:00
|
|
|
{
|
2009-08-14 23:12:21 +00:00
|
|
|
GuiWorkArea * wa;
|
2008-03-15 01:00:25 +00:00
|
|
|
if (clicked_tab_ == -1)
|
2009-08-14 23:12:21 +00:00
|
|
|
wa = currentWorkArea();
|
2008-03-15 01:00:25 +00:00
|
|
|
else {
|
2010-10-23 10:49:45 +00:00
|
|
|
wa = workArea(clicked_tab_);
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(wa, return);
|
2008-03-15 01:00:25 +00:00
|
|
|
}
|
2009-08-15 14:07:05 +00:00
|
|
|
wa->view().hideWorkArea(wa);
|
2008-02-21 12:29:26 +00:00
|
|
|
}
|
|
|
|
|
2009-10-31 18:10:18 +00:00
|
|
|
|
|
|
|
void TabWorkArea::closeTab(int index)
|
|
|
|
{
|
|
|
|
on_currentTabChanged(index);
|
|
|
|
GuiWorkArea * wa;
|
|
|
|
if (index == -1)
|
|
|
|
wa = currentWorkArea();
|
|
|
|
else {
|
2010-10-23 10:49:45 +00:00
|
|
|
wa = workArea(index);
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(wa, return);
|
2009-10-31 18:10:18 +00:00
|
|
|
}
|
2009-10-31 18:14:21 +00:00
|
|
|
wa->view().closeWorkArea(wa);
|
2009-10-31 18:10:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
///
|
|
|
|
class DisplayPath {
|
|
|
|
public:
|
|
|
|
/// make vector happy
|
2017-03-02 14:20:46 +00:00
|
|
|
DisplayPath() : tab_(-1), dottedPrefix_(false) {}
|
2008-04-29 15:44:07 +00:00
|
|
|
///
|
|
|
|
DisplayPath(int tab, FileName const & filename)
|
|
|
|
: tab_(tab)
|
|
|
|
{
|
2009-08-11 12:34:06 +00:00
|
|
|
filename_ = (filename.extension() == "lyx") ?
|
|
|
|
toqstr(filename.onlyFileNameWithoutExt())
|
|
|
|
: toqstr(filename.onlyFileName());
|
2008-05-02 16:59:39 +00:00
|
|
|
postfix_ = toqstr(filename.absoluteFilePath()).
|
|
|
|
split("/", QString::SkipEmptyParts);
|
2008-04-29 15:44:07 +00:00
|
|
|
postfix_.pop_back();
|
|
|
|
abs_ = toqstr(filename.absoluteFilePath());
|
|
|
|
dottedPrefix_ = false;
|
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
/// Absolute path for debugging.
|
|
|
|
QString abs() const
|
|
|
|
{
|
|
|
|
return abs_;
|
|
|
|
}
|
|
|
|
/// Add the first segment from the postfix or three dots to the prefix.
|
|
|
|
/// Merge multiple dot tripples. In fact dots are added lazily, i.e. only
|
|
|
|
/// when really needed.
|
|
|
|
void shiftPathSegment(bool dotted)
|
|
|
|
{
|
2008-05-02 16:59:39 +00:00
|
|
|
if (postfix_.count() <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!dotted) {
|
|
|
|
if (dottedPrefix_ && !prefix_.isEmpty())
|
2015-10-04 18:38:47 +00:00
|
|
|
prefix_ += ellipsisSlash_;
|
2008-05-02 16:59:39 +00:00
|
|
|
prefix_ += postfix_.front() + "/";
|
2008-04-29 15:44:07 +00:00
|
|
|
}
|
2008-05-02 16:59:39 +00:00
|
|
|
dottedPrefix_ = dotted && !prefix_.isEmpty();
|
|
|
|
postfix_.pop_front();
|
2008-04-29 15:44:07 +00:00
|
|
|
}
|
|
|
|
///
|
|
|
|
QString displayString() const
|
|
|
|
{
|
|
|
|
if (prefix_.isEmpty())
|
|
|
|
return filename_;
|
2008-05-02 16:59:39 +00:00
|
|
|
|
|
|
|
bool dots = dottedPrefix_ || !postfix_.isEmpty();
|
2015-10-04 18:38:47 +00:00
|
|
|
return prefix_ + (dots ? ellipsisSlash_ : "") + filename_;
|
2008-04-29 15:44:07 +00:00
|
|
|
}
|
|
|
|
///
|
2008-04-29 15:45:06 +00:00
|
|
|
QString forecastPathString() const
|
2008-04-29 15:44:07 +00:00
|
|
|
{
|
|
|
|
if (postfix_.count() == 0)
|
|
|
|
return displayString();
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
return prefix_
|
2015-10-04 18:38:47 +00:00
|
|
|
+ (dottedPrefix_ ? ellipsisSlash_ : "")
|
2008-04-29 15:45:06 +00:00
|
|
|
+ postfix_.front() + "/";
|
2008-04-29 15:44:07 +00:00
|
|
|
}
|
|
|
|
///
|
2008-05-02 16:59:39 +00:00
|
|
|
bool final() const { return postfix_.empty(); }
|
2008-04-29 15:44:07 +00:00
|
|
|
///
|
2008-05-02 16:59:39 +00:00
|
|
|
int tab() const { return tab_; }
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
private:
|
2015-10-04 18:38:47 +00:00
|
|
|
/// ".../"
|
|
|
|
static QString const ellipsisSlash_;
|
2008-04-29 15:44:07 +00:00
|
|
|
///
|
|
|
|
QString prefix_;
|
|
|
|
///
|
|
|
|
QStringList postfix_;
|
|
|
|
///
|
|
|
|
QString filename_;
|
|
|
|
///
|
|
|
|
QString abs_;
|
|
|
|
///
|
|
|
|
int tab_;
|
|
|
|
///
|
|
|
|
bool dottedPrefix_;
|
|
|
|
};
|
2008-02-21 12:29:26 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
|
2015-10-04 18:38:47 +00:00
|
|
|
QString const DisplayPath::ellipsisSlash_ = QString(QChar(0x2026)) + "/";
|
|
|
|
|
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
///
|
|
|
|
bool operator<(DisplayPath const & a, DisplayPath const & b)
|
2007-11-11 22:30:21 +00:00
|
|
|
{
|
2008-04-29 15:44:07 +00:00
|
|
|
return a.displayString() < b.displayString();
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
bool operator==(DisplayPath const & a, DisplayPath const & b)
|
|
|
|
{
|
|
|
|
return a.displayString() == b.displayString();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TabWorkArea::updateTabTexts()
|
|
|
|
{
|
|
|
|
size_t n = count();
|
|
|
|
if (n == 0)
|
2007-11-11 22:30:21 +00:00
|
|
|
return;
|
2008-04-29 15:44:07 +00:00
|
|
|
std::list<DisplayPath> paths;
|
|
|
|
typedef std::list<DisplayPath>::iterator It;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
|
|
|
// collect full names first: path into postfix, empty prefix and
|
2008-04-29 15:44:07 +00:00
|
|
|
// filename without extension
|
|
|
|
for (size_t i = 0; i < n; ++i) {
|
2010-10-23 10:49:45 +00:00
|
|
|
GuiWorkArea * i_wa = workArea(i);
|
2008-04-29 15:44:07 +00:00
|
|
|
FileName const fn = i_wa->bufferView().buffer().fileName();
|
|
|
|
paths.push_back(DisplayPath(i, fn));
|
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
// go through path segments and see if it helps to make the path more unique
|
|
|
|
bool somethingChanged = true;
|
|
|
|
bool allFinal = false;
|
|
|
|
while (somethingChanged && !allFinal) {
|
|
|
|
// adding path segments changes order
|
|
|
|
paths.sort();
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
LYXERR(Debug::GUI, "updateTabTexts() iteration start");
|
|
|
|
somethingChanged = false;
|
|
|
|
allFinal = true;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
// find segments which are not unique (i.e. non-atomic)
|
|
|
|
It it = paths.begin();
|
|
|
|
It segStart = it;
|
|
|
|
QString segString = it->displayString();
|
|
|
|
for (; it != paths.end(); ++it) {
|
|
|
|
// look to the next item
|
|
|
|
It next = it;
|
|
|
|
++next;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
// final?
|
|
|
|
allFinal = allFinal && it->final();
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-05-02 22:07:51 +00:00
|
|
|
LYXERR(Debug::GUI, "it = " << it->abs()
|
|
|
|
<< " => " << it->displayString());
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
// still the same segment?
|
|
|
|
QString nextString;
|
|
|
|
if ((next != paths.end()
|
|
|
|
&& (nextString = next->displayString()) == segString))
|
|
|
|
continue;
|
|
|
|
LYXERR(Debug::GUI, "segment ended");
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
// only a trivial one with one element?
|
|
|
|
if (it == segStart) {
|
|
|
|
// start new segment
|
|
|
|
segStart = next;
|
|
|
|
segString = nextString;
|
|
|
|
continue;
|
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2017-04-05 08:20:01 +00:00
|
|
|
// We found a non-atomic segment
|
|
|
|
// We know that segStart <= it < next <= paths.end().
|
|
|
|
// The assertion below tells coverity about it.
|
|
|
|
LATTEST(segStart != paths.end());
|
|
|
|
QString dspString = segStart->forecastPathString();
|
|
|
|
LYXERR(Debug::GUI, "first forecast found for "
|
|
|
|
<< segStart->abs() << " => " << dspString);
|
|
|
|
It sit = segStart;
|
|
|
|
++sit;
|
2008-04-29 15:44:07 +00:00
|
|
|
// Shift path segments and hope for the best
|
|
|
|
// that it makes the path more unique.
|
|
|
|
somethingChanged = true;
|
|
|
|
bool moreUnique = false;
|
|
|
|
for (; sit != next; ++sit) {
|
2008-04-29 15:45:06 +00:00
|
|
|
if (sit->forecastPathString() != dspString) {
|
2008-04-29 15:44:07 +00:00
|
|
|
LYXERR(Debug::GUI, "different forecast found for "
|
2008-05-02 22:07:51 +00:00
|
|
|
<< sit->abs() << " => " << sit->forecastPathString());
|
2008-04-29 15:44:07 +00:00
|
|
|
moreUnique = true;
|
|
|
|
break;
|
|
|
|
}
|
2008-05-02 16:59:39 +00:00
|
|
|
LYXERR(Debug::GUI, "same forecast found for "
|
2008-05-02 22:07:51 +00:00
|
|
|
<< sit->abs() << " => " << dspString);
|
2008-04-29 15:44:07 +00:00
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
// if the path segment helped, add it. Otherwise add dots
|
|
|
|
bool dots = !moreUnique;
|
|
|
|
LYXERR(Debug::GUI, "using dots = " << dots);
|
|
|
|
for (sit = segStart; sit != next; ++sit) {
|
|
|
|
sit->shiftPathSegment(dots);
|
2008-05-02 22:07:51 +00:00
|
|
|
LYXERR(Debug::GUI, "shifting "
|
|
|
|
<< sit->abs() << " => " << sit->displayString());
|
2008-04-29 15:44:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// start new segment
|
|
|
|
segStart = next;
|
|
|
|
segString = nextString;
|
|
|
|
}
|
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-04-29 15:44:07 +00:00
|
|
|
// set new tab titles
|
|
|
|
for (It it = paths.begin(); it != paths.end(); ++it) {
|
2011-01-04 09:41:27 +00:00
|
|
|
int const tab_index = it->tab();
|
|
|
|
Buffer const & buf = workArea(tab_index)->bufferView().buffer();
|
2013-07-19 13:55:56 +00:00
|
|
|
QString tab_text = it->displayString().replace("&", "&&");
|
2008-04-29 15:44:07 +00:00
|
|
|
if (!buf.fileName().empty() && !buf.isClean())
|
2011-01-04 09:41:27 +00:00
|
|
|
tab_text += "*";
|
2015-05-22 08:41:12 +00:00
|
|
|
QString tab_tooltip = it->abs();
|
2017-03-01 21:03:44 +00:00
|
|
|
if (buf.hasReadonlyFlag()) {
|
2015-05-22 08:41:12 +00:00
|
|
|
setTabIcon(tab_index, QIcon(getPixmap("images/", "emblem-readonly", "svgz,png")));
|
2017-02-28 21:58:44 +00:00
|
|
|
tab_tooltip = qt_("%1 (read only)").arg(tab_tooltip);
|
2015-05-22 08:41:12 +00:00
|
|
|
} else
|
|
|
|
setTabIcon(tab_index, QIcon());
|
2017-02-28 21:58:44 +00:00
|
|
|
if (buf.notifiesExternalModification()) {
|
|
|
|
QString const warn = qt_("%1 (modified externally)");
|
|
|
|
tab_tooltip = warn.arg(tab_tooltip);
|
|
|
|
tab_text += QChar(0x26a0);
|
|
|
|
}
|
2011-01-04 09:41:27 +00:00
|
|
|
setTabText(tab_index, tab_text);
|
2015-05-22 08:41:12 +00:00
|
|
|
setTabToolTip(tab_index, tab_tooltip);
|
2008-04-29 15:44:07 +00:00
|
|
|
}
|
2007-11-11 22:30:21 +00:00
|
|
|
}
|
|
|
|
|
2008-03-15 01:00:25 +00:00
|
|
|
|
|
|
|
void TabWorkArea::showContextMenu(const QPoint & pos)
|
|
|
|
{
|
|
|
|
// which tab?
|
2008-03-15 02:13:01 +00:00
|
|
|
clicked_tab_ = static_cast<DragTabBar *>(tabBar())->tabAt(pos);
|
2008-05-02 16:59:39 +00:00
|
|
|
if (clicked_tab_ == -1)
|
|
|
|
return;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-05-02 16:59:39 +00:00
|
|
|
// show tab popup
|
|
|
|
QMenu popup;
|
2015-03-12 23:47:21 +00:00
|
|
|
popup.addAction(QIcon(getPixmap("images/", "hidetab", "svgz,png")),
|
2009-10-31 18:14:21 +00:00
|
|
|
qt_("Hide tab"), this, SLOT(hideCurrentTab()));
|
2015-03-12 23:47:21 +00:00
|
|
|
popup.addAction(QIcon(getPixmap("images/", "closetab", "svgz,png")),
|
2008-05-02 16:59:39 +00:00
|
|
|
qt_("Close tab"), this, SLOT(closeCurrentBuffer()));
|
|
|
|
popup.exec(tabBar()->mapToGlobal(pos));
|
|
|
|
|
|
|
|
clicked_tab_ = -1;
|
2008-03-15 01:00:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-15 02:13:01 +00:00
|
|
|
void TabWorkArea::moveTab(int fromIndex, int toIndex)
|
|
|
|
{
|
|
|
|
QWidget * w = widget(fromIndex);
|
|
|
|
QIcon icon = tabIcon(fromIndex);
|
|
|
|
QString text = tabText(fromIndex);
|
|
|
|
|
|
|
|
setCurrentIndex(fromIndex);
|
|
|
|
removeTab(fromIndex);
|
|
|
|
insertTab(toIndex, w, icon, text);
|
|
|
|
setCurrentIndex(toIndex);
|
|
|
|
}
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-03-15 02:13:01 +00:00
|
|
|
|
|
|
|
DragTabBar::DragTabBar(QWidget* parent)
|
|
|
|
: QTabBar(parent)
|
|
|
|
{
|
|
|
|
setAcceptDrops(true);
|
2009-10-05 23:06:29 +00:00
|
|
|
setTabsClosable(!lyxrc.single_close_tab_button);
|
2009-05-02 12:43:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-15 02:13:01 +00:00
|
|
|
void DragTabBar::mousePressEvent(QMouseEvent * event)
|
|
|
|
{
|
|
|
|
if (event->button() == Qt::LeftButton)
|
|
|
|
dragStartPos_ = event->pos();
|
|
|
|
QTabBar::mousePressEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DragTabBar::mouseMoveEvent(QMouseEvent * event)
|
|
|
|
{
|
|
|
|
// If the left button isn't pressed anymore then return
|
|
|
|
if (!(event->buttons() & Qt::LeftButton))
|
|
|
|
return;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-03-15 02:13:01 +00:00
|
|
|
// If the distance is too small then return
|
|
|
|
if ((event->pos() - dragStartPos_).manhattanLength()
|
|
|
|
< QApplication::startDragDistance())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// did we hit something after all?
|
|
|
|
int tab = tabAt(dragStartPos_);
|
|
|
|
if (tab == -1)
|
|
|
|
return;
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-03-15 02:13:01 +00:00
|
|
|
// simulate button release to remove highlight from button
|
|
|
|
int i = currentIndex();
|
|
|
|
QMouseEvent me(QEvent::MouseButtonRelease, dragStartPos_,
|
|
|
|
event->button(), event->buttons(), 0);
|
|
|
|
QTabBar::mouseReleaseEvent(&me);
|
|
|
|
setCurrentIndex(i);
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-03-15 02:13:01 +00:00
|
|
|
// initiate Drag
|
|
|
|
QDrag * drag = new QDrag(this);
|
|
|
|
QMimeData * mimeData = new QMimeData;
|
|
|
|
// a crude way to distinguish tab-reodering drops from other ones
|
|
|
|
mimeData->setData("action", "tab-reordering") ;
|
|
|
|
drag->setMimeData(mimeData);
|
2008-10-13 11:25:37 +00:00
|
|
|
|
2008-03-15 02:13:01 +00:00
|
|
|
// get tab pixmap as cursor
|
|
|
|
QRect r = tabRect(tab);
|
|
|
|
QPixmap pixmap(r.size());
|
2008-03-15 02:42:48 +00:00
|
|
|
render(&pixmap, - r.topLeft());
|
2008-03-15 02:13:01 +00:00
|
|
|
drag->setPixmap(pixmap);
|
2008-03-15 10:19:12 +00:00
|
|
|
drag->exec();
|
2008-03-15 02:13:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DragTabBar::dragEnterEvent(QDragEnterEvent * event)
|
|
|
|
{
|
|
|
|
// Only accept if it's an tab-reordering request
|
|
|
|
QMimeData const * m = event->mimeData();
|
|
|
|
QStringList formats = m->formats();
|
2008-10-13 11:25:37 +00:00
|
|
|
if (formats.contains("action")
|
2008-03-15 02:13:01 +00:00
|
|
|
&& m->data("action") == "tab-reordering")
|
|
|
|
event->acceptProposedAction();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DragTabBar::dropEvent(QDropEvent * event)
|
|
|
|
{
|
|
|
|
int fromIndex = tabAt(dragStartPos_);
|
|
|
|
int toIndex = tabAt(event->pos());
|
2008-10-13 11:25:37 +00:00
|
|
|
|
|
|
|
// Tell interested objects that
|
2008-03-15 02:13:01 +00:00
|
|
|
if (fromIndex != toIndex)
|
|
|
|
tabMoveRequested(fromIndex, toIndex);
|
|
|
|
event->acceptProposedAction();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-01 23:41:02 +00:00
|
|
|
GuiWorkAreaContainer::GuiWorkAreaContainer(GuiWorkArea * wa, QWidget * parent)
|
|
|
|
: QWidget(parent), wa_(wa)
|
|
|
|
{
|
|
|
|
LASSERT(wa, return);
|
|
|
|
Ui::WorkAreaUi::setupUi(this);
|
|
|
|
layout()->addWidget(wa);
|
|
|
|
connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
|
|
|
|
this, SLOT(updateDisplay()));
|
|
|
|
connect(reloadPB, SIGNAL(clicked()), this, SLOT(reload()));
|
|
|
|
connect(ignorePB, SIGNAL(clicked()), this, SLOT(ignore()));
|
2017-05-15 00:01:58 +00:00
|
|
|
setMessageColour({notificationFrame}, {reloadPB, ignorePB});
|
2017-03-01 23:41:02 +00:00
|
|
|
updateDisplay();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkAreaContainer::updateDisplay()
|
|
|
|
{
|
|
|
|
Buffer const & buf = wa_->bufferView().buffer();
|
|
|
|
notificationFrame->setHidden(!buf.notifiesExternalModification());
|
|
|
|
QString const label = QString("<b>The file \"%1\" changed on disk.</b>")
|
|
|
|
.arg(toqstr(buf.fileName().displayName()));
|
|
|
|
externalModificationLabel->setText(label);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkAreaContainer::dispatch(FuncRequest f) const
|
|
|
|
{
|
|
|
|
lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH,
|
|
|
|
wa_->bufferView().buffer().absFileName()));
|
|
|
|
lyx::dispatch(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkAreaContainer::reload() const
|
|
|
|
{
|
|
|
|
dispatch(FuncRequest(LFUN_BUFFER_RELOAD));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkAreaContainer::ignore() const
|
|
|
|
{
|
|
|
|
dispatch(FuncRequest(LFUN_BUFFER_EXTERNAL_MODIFICATION_CLEAR));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiWorkAreaContainer::mouseDoubleClickEvent(QMouseEvent * event)
|
|
|
|
{
|
|
|
|
// prevent TabWorkArea from opening a new buffer on double click
|
|
|
|
event->accept();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
} // namespace frontend
|
|
|
|
} // namespace lyx
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2008-11-14 14:28:50 +00:00
|
|
|
#include "moc_GuiWorkArea.cpp"
|