#include "support/lassert.h"
#include "support/lstrings.h"
#include "support/lyxalgo.h" // sorted
+#include "support/mute_warning.h"
#include "support/textutils.h"
#include "support/Messages.h"
#include "support/os.h"
#include <QByteArray>
#include <QBitmap>
#include <QDateTime>
-#if QT_VERSION < 0x060000
-#include <QDesktopWidget>
-#endif
#include <QEvent>
#include <QFileOpenEvent>
#include <QFileInfo>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
#include <QRandomGenerator>
#endif
-#include <QRegExp>
+#include <QScreen>
#include <QSessionManager>
#include <QSettings>
#include <QSocketNotifier>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
+#include <QSvgRenderer>
#include <QTimer>
#include <QTranslator>
#include <QThreadPool>
#include <QWidget>
-#ifdef Q_WS_X11
-#include <X11/Xatom.h>
-#include <X11/Xlib.h>
-#include <QX11Info>
-#undef CursorShape
-#undef None
-#elif defined(QPA_XCB)
+#if defined(QPA_XCB)
#include <xcb/xcb.h>
#ifdef HAVE_QT5_X11_EXTRAS
#include <QtX11Extras/QX11Info>
#endif
#endif
-#if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400)
+#if (QT_VERSION >= 0x050400)
#if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
-#if (QT_VERSION < 0x060000)
-#if (QT_VERSION < 0x050000)
-#include <QWindowsMime>
+#if (QT_VERSION >= 0x060000)
+#if (QT_VERSION >= 0x060500)
+#include <QtGui/QWindowsMimeConverter>
+#define QWINDOWSMIME QWindowsMimeConverter
+#define QVARIANTTYPE QMetaType
+#else
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qwindowsmime_p.h>
+#include <QtGui/qpa/qplatformintegration.h>
#define QWINDOWSMIME QWindowsMime
+#define QVARIANTTYPE QMetaType
+using QWindowsMime = QNativeInterface::Private::QWindowsMime;
+using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
+#endif
#else
#include <QWinMime>
#define QWINDOWSMIME QWinMime
-#endif
+#define QVARIANTTYPE QVariant::Type
#endif
#ifdef Q_CC_GNU
#include <wtypes.h>
frontend::Application * createApplication(int & argc, char * argv[])
{
-#if !defined(Q_WS_X11) && !defined(QPA_XCB)
+#if !defined(QPA_XCB)
// prune -geometry argument(s) by shifting
// the following ones 2 places down.
for (int i = 0 ; i < argc ; ++i) {
#endif
+#if defined(Q_OS_MAC)
+ int const cursor_time_on = NSTextInsertionPointBlinkPeriodOn();
+ int const cursor_time_off = NSTextInsertionPointBlinkPeriodOff();
+ if (cursor_time_on > 0 && cursor_time_off > 0) {
+ QApplication::setCursorFlashTime(cursor_time_on + cursor_time_off);
+ } else if (cursor_time_on <= 0 && cursor_time_off > 0) {
+ // Off is set and On is undefined of zero
+ QApplication::setCursorFlashTime(0);
+ } else if (cursor_time_off <= 0 && cursor_time_on > 0) {
+ // On is set and Off is undefined of zero
+ QApplication::setCursorFlashTime(0);
+ }
+#endif
+
// Setup high DPI handling. This is a bit complicated, but will be default in Qt6.
// macOS does it by itself.
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && !defined(Q_OS_MAC)
}
+namespace {
+
+/* Get aliases for icon name. This allows to avoid duplication of
+ * icons when new versions of functions are introduced for the
+ * toolbar. A good example is the introduction of layout-toggle in
+ * #9864.
+ * The file is parsed by Lexer. Each line is of the form
+ * <original substring> <replacement substring>
+ *
+ * The return value is another icon file name that can be queried.
+ */
+// FIXME: consider using regular expressions.
+QString getAlias(QString name) {
+ static bool has_aliases = false;
+ static vector <pair<QString,QString>> aliases;
+ // Initialize aliases list (once).
+ if (!has_aliases) {
+ FileName alfile = libFileSearch("images", "icon.aliases");
+ if (alfile.exists()) {
+ Lexer lex;
+ lex.setFile(alfile);
+ while(lex.isOK()) {
+ string from, to;
+ lex >> from >> to;
+ if (!from.empty())
+ aliases.push_back({toqstr(from), toqstr(to)});
+ }
+ }
+ has_aliases = true;
+ }
+ // check for the following aliases
+ for (auto const & alias : aliases) {
+ if (name.contains(alias.first))
+ name.replace(alias.first, alias.second);
+ }
+ return name;
+}
+
+} // namespace
+
+
IconInfo iconInfo(FuncRequest const & f, bool unknown, bool rtl)
{
IconInfo res;
name.replace(' ', '_');
name.replace(';', '_');
name.replace('\\', "backslash");
- // avoid duplication for these
- name.replace("dialog-toggle", "dialog-show");
names << name;
+ QString alias = getAlias(name);
+ if (alias != name)
+ names << alias;
// then special default icon for some lfuns
switch (f.action()) {
// next thing to try is function name alone
names << lfunname;
+ QString alias = getAlias(lfunname);
+ if (alias != lfunname)
+ names << alias;
// and finally maybe the unknown icon
if (unknown)
names << "unknown";
- search_mode const mode = theGuiApp()->imageSearchMode();
+ search_mode const mode = theGuiApp() ? theGuiApp()->imageSearchMode() : support::must_exist;
// The folders where icons are searched for
QStringList imagedirs;
imagedirs << "images/ipa/" << "images/";
- // This is used to search for rtl version of icons which have the +rrtl suffix.
+ // This is used to search for rtl version of icons which have the +rtl suffix.
QStringList suffixes;
if (rtl)
suffixes << "+rtl";
QIcon getIcon(FuncRequest const & f, bool unknown, bool rtl)
{
-#if (QT_VERSION >= 0x040600)
if (lyxrc.use_system_theme_icons) {
// use the icons from system theme that are available
QString action = toqstr(lyxaction.getActionName(f.action()));
return thmicn;
}
}
-#endif
IconInfo icondata = iconInfo(f, unknown, rtl);
if (icondata.filepath.isEmpty())
QString translate(const char * /* context */,
const char *sourceText,
-#if QT_VERSION >= 0x050000
const char * /* disambiguation */ = nullptr, int /* n */ = -1) const override
-#else
- const char * /*comment*/ = 0) const override
-#endif
{
// Here we declare the strings that need to be translated from Qt own GUI
// This is needed to include these strings to po files
////////////////////////////////////////////////////////////////////////
// Windows specific stuff goes here...
-#if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400 && QT_VERSION < 0x060000)
+#if (QT_VERSION >= 0x050400)
#if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
// QWindowsMimeMetafile can only be compiled on Windows.
}
QVariant convertToMime(QString const & mimetype, IDataObject * pDataObj,
- QVariant::Type /*preferredType*/) const override
+ QVARIANTTYPE /*preferredType*/) const override
{
QByteArray data;
if (!canConvertToMime(mimetype, pDataObj))
struct GuiApplication::Private
{
Private(): language_model_(nullptr), meta_fake_bit(NoModifier),
- global_menubar_(nullptr)
- #if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
- , last_state_(Qt::ApplicationInactive)
- #endif
+ global_menubar_(nullptr), last_state_(Qt::ApplicationInactive)
{
- #if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400 && QT_VERSION < 0x060000)
+ #if (QT_VERSION >= 0x050400)
#if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
/// WMF Mime handler for Windows clipboard.
wmf_mime_ = new QWindowsMimeMetafile;
+ #if (QT_VERSION >= 0x060000 && QT_VERSION < 0x060500)
+ win_app_ = dynamic_cast<QWindowsApplication *>
+ (QGuiApplicationPrivate::platformIntegration());
+ win_app_->registerMime(wmf_mime_);
+ #endif
#endif
#endif
initKeySequences(&theTopLevelKeymap());
}
+ #if (QT_VERSION >= 0x060000 && QT_VERSION < 0x060500)
+ #if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
+ ~Private()
+ {
+ win_app_->unregisterMime(wmf_mime_);
+ }
+ #endif
+ #endif
+
void initKeySequences(KeyMap * kb)
{
keyseq = KeySequence(kb, kb);
/// Only used on mac.
QMenuBar * global_menubar_;
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
/// Holds previous application state on Mac
Qt::ApplicationState last_state_;
-#endif
#if defined(Q_OS_MAC) && (QT_VERSION < 0x060000)
/// Linkback mime handler for MacOSX.
QMacPasteboardMimeGraphics mac_pasteboard_mime_;
#endif
-#if (QT_VERSION < 0x050000) || (QT_VERSION >= 0x050400 && QT_VERSION < 0x060000)
+#if (QT_VERSION >= 0x050400)
#if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
/// WMF Mime handler for Windows clipboard.
QWindowsMimeMetafile * wmf_mime_;
+#if (QT_VERSION >= 0x060000 && QT_VERSION < 0x060500)
+ QWindowsApplication * win_app_;
+#endif
#endif
#endif
QCoreApplication::setOrganizationName(app_name);
QCoreApplication::setOrganizationDomain("lyx.org");
QCoreApplication::setApplicationName(lyx_package);
-#if QT_VERSION >= 0x050000 && QT_VERSION < 0x060000
+#if QT_VERSION < 0x060000
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
setupApplescript();
appleCleanupEditMenu();
appleCleanupViewMenu();
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
this, SLOT(onApplicationStateChanged(Qt::ApplicationState)));
#endif
-#endif
-#if defined(Q_WS_X11) || defined(QPA_XCB)
+#if defined(QPA_XCB)
// doubleClickInterval() is 400 ms on X11 which is just too long.
// On Windows and Mac OS X, the operating system's value is used.
// On Microsoft Windows, calling this function sets the double
if (lyxrc.typewriter_font_name.empty())
lyxrc.typewriter_font_name = fromqstr(typewriterFontName());
-#if (QT_VERSION >= 0x050000)
- // Qt4 does this in event(), see below.
// Track change of keyboard
connect(inputMethod(), SIGNAL(localeChanged()), this, SLOT(onLocaleChanged()));
-#endif
d->general_timer_.setInterval(500);
connect(&d->general_timer_, SIGNAL(timeout()),
}
-#if QT_VERSION < 0x050000
-// Emulate platformName() for Qt4
-
-// FIXME: when ditching this method, remove all tests
-// platformName() == "qt4x11"
-// in the code
-QString GuiApplication::platformName() const
-{
-# if defined(Q_WS_X11)
- // Note that this one does not really exist
- return "qt4x11";
-# elif defined(Q_OS_MAC)
- return "cocoa";
-# elif defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
- return "windows";
-# else
- LYXERR0("Unknown platform!");
- return "unknown";
-# endif
-}
-#endif
-
-
double GuiApplication::pixelRatio() const
{
-#if QT_VERSION >= 0x050000
return qt_scale_factor * devicePixelRatio();
-#else
- return 1.0;
-#endif
}
return qstring_to_ucs4(findImg(toqstr(c)));
}
+void Application::applyPrefs()
+{
+ if (lyxrc.ui_style != "default")
+ lyx::frontend::GuiApplication::setStyle(toqstr(lyxrc.ui_style));
+}
FuncStatus GuiApplication::getStatus(FuncRequest const & cmd) const
{
createView(QString(), false); // keep hidden
current_view_->newDocument(to_utf8(cmd.argument()));
current_view_->show();
- setActiveWindow(current_view_);
+ current_view_->activateWindow();
} else {
current_view_->newDocument(to_utf8(cmd.argument()));
}
// We want the ui session to be saved per document and not per
// window number. The filename crc is a good enough identifier.
createView(support::checksum(fname));
- current_view_->openDocument(fname);
+ current_view_->openDocuments(fname, cmd.origin());
if (!current_view_->documentBufferView())
current_view_->close();
else if (cmd.origin() == FuncRequest::LYXSERVER) {
current_view_->showNormal();
}
} else {
- current_view_->openDocument(fname);
+ current_view_->openDocuments(fname, cmd.origin());
if (cmd.origin() == FuncRequest::LYXSERVER) {
current_view_->raise();
current_view_->activateWindow();
}
case LFUN_LYXFILES_OPEN: {
- // This is the actual reason for this method (#12106).
- validateCurrentView();
- if (!current_view_
- || (!lyxrc.open_buffers_in_tabs
- && current_view_->documentBufferView() != nullptr))
- createView();
string arg = to_utf8(cmd.argument());
if (arg.empty())
// set default
case LFUN_DIALOG_SHOW: {
string const name = cmd.getArg(0);
-
- if ( name == "aboutlyx"
- || name == "prefs"
- || name == "texinfo"
- || name == "progress"
- || name == "compare")
+ // Workaround: on Mac OS the application
+ // is not terminated when closing the last view.
+ // With the following dialogs which should still
+ // be usable, create a new one to be able
+ // to dispatch LFUN_DIALOG_SHOW to this view.
+ if (name == "aboutlyx" || name == "compare"
+ || name == "lyxfiles" || name == "prefs"
+ || name == "progress" || name == "texinfo")
{
- // work around: on Mac OS the application
- // is not terminated when closing the last view.
- // Create a new one to be able to dispatch the
- // LFUN_DIALOG_SHOW to this view.
if (current_view_ == nullptr)
createView();
}
string GuiApplication::inputLanguageCode() const
{
-#if (QT_VERSION < 0x050000)
- QLocale loc = keyboardInputLocale();
-#else
QLocale loc = inputMethod()->locale();
-#endif
//LYXERR0("input lang = " << fromqstr(loc.name()));
return loc.name() == "C" ? "en_US" : fromqstr(loc.name());
}
}
+void GuiApplication::onPaletteChanged()
+{
+ colorCache().setPalette(palette());
+}
+
+
void GuiApplication::handleKeyFunc(FuncCode action)
{
char_type c = 0;
if (autoShow) {
view->show();
- setActiveWindow(view);
+ view->activateWindow();
}
if (!geometry_arg.isEmpty()) {
int x, y;
int w, h;
QChar sx, sy;
-#if QT_VERSION < 0x060000
- QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)){0,1}(?:([+-][0-9]*)){0,1}" );
- re.indexIn(geometry_arg);
- w = re.cap(1).toInt();
- h = re.cap(2).toInt();
- x = re.cap(3).toInt();
- y = re.cap(4).toInt();
- sx = re.cap(3).isEmpty() ? '+' : re.cap(3).at(0);
- sy = re.cap(4).isEmpty() ? '+' : re.cap(4).at(0);
-#else
QRegularExpression re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)){0,1}(?:([+-][0-9]*)){0,1}" );
QRegularExpressionMatch match = re.match(geometry_arg);
w = match.captured(1).toInt();
y = match.captured(4).toInt();
sx = match.captured(3).isEmpty() ? '+' : match.captured(3).at(0);
sy = match.captured(4).isEmpty() ? '+' : match.captured(4).at(0);
-#endif
+
// Set initial geometry such that we can get the frame size.
view->setGeometry(x, y, w, h);
int framewidth = view->geometry().x() - view->x();
// Negative displacements must be interpreted as distances
// from the right or bottom screen borders.
if (sx == '-' || sy == '-') {
-#if QT_VERSION < 0x060000
- QRect rec = QApplication::desktop()->screenGeometry();
-#else
QRect rec = QGuiApplication::primaryScreen()->geometry();
-#endif
if (sx == '-')
x += rec.width() - w - framewidth;
if (sy == '-')
}
+QPixmap GuiApplication::getScaledPixmap(QString imagedir, QString name) const
+{
+ qreal dpr = 1.0;
+ // Consider device/pixel ratio (HiDPI)
+ if (currentView())
+ dpr = currentView()->devicePixelRatio();
+ // We render SVG directly for HiDPI scalability
+ QPixmap pm = getPixmap(imagedir, name, "svgz,png");
+ FileName fname = imageLibFileSearch(imagedir, name, "svgz,png");
+ QString fpath = toqstr(fname.absFileName());
+ if (!fpath.isEmpty() && !fpath.endsWith(".png")) {
+ QSvgRenderer svgRenderer(fpath);
+ if (svgRenderer.isValid()) {
+ pm = QPixmap(pm.size() * dpr);
+ pm.fill(Qt::transparent);
+ QPainter painter(&pm);
+ svgRenderer.render(&painter);
+ pm.setDevicePixelRatio(dpr);
+ }
+ }
+ return pm;
+}
+
+
Clipboard & GuiApplication::clipboard()
{
return d->clipboard_;
}
+bool GuiApplication::needsBackingStore() const
+{
+ /* Qt on macOS and Wayland does not respect the
+ * Qt::WA_OpaquePaintEvent attribute and resets the widget backing
+ * store at each update. Therefore, we use our own backing store
+ * in these two cases. It is also possible to force the use of the
+ * backing store for cases like x11 with transparent WM themes.
+ */
+ return platformName() == "cocoa" || platformName().contains("wayland");
+}
+
+
QList<int> GuiApplication::viewIds() const
{
return d->views_.keys();
return;
#ifdef Q_OS_MAC
-#if QT_VERSION > 0x040600
setAttribute(Qt::AA_MacDontSwapCtrlAndMeta,lyxrc.mac_dontswap_ctrl_meta);
-#endif
-#if QT_VERSION > 0x050100
+# if QT_VERSION < 0x060000
setAttribute(Qt::AA_UseHighDpiPixmaps,true);
-#endif
+# endif
// Create the global default menubar which is shown for the dialogs
// and if no GuiView is visible.
// This must be done after the session was recovered to know the "last files".
QStandardItemModel * lang_model = new QStandardItemModel(this);
lang_model->insertColumns(0, 3);
- QIcon speller(getPixmap("images/", "dialog-show_spellchecker", "svgz,png"));
- QIcon saurus(getPixmap("images/", "thesaurus-entry", "svgz,png"));
+ QIcon speller(guiApp ? guiApp->getScaledPixmap("images/", "dialog-show_spellchecker")
+ : getPixmap("images/", "dialog-show_spellchecker", "svgz,png"));
+ QIcon saurus(guiApp ? guiApp->getScaledPixmap("images/", "thesaurus-entry")
+ : getPixmap("images/", "thesaurus-entry", "svgz,png"));
Languages::const_iterator it = lyx::languages.begin();
Languages::const_iterator end = lyx::languages.end();
for (; it != end; ++it) {
QFont const GuiApplication::typewriterSystemFont()
{
-#if QT_VERSION >= 0x050200
QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
-#else
- QFont font("monospace");
-#endif
if (!isFixedPitch(font)) {
// try to enforce a real monospaced font
font.setStyleHint(QFont::Monospace);
e->accept();
return true;
}
-#if (QT_VERSION < 0x050000)
- // Qt5 uses a signal for that, see above.
- case QEvent::KeyboardLayoutChange:
- //LYXERR0("keyboard change");
- if (currentView() && currentView()->currentBufferView())
- currentView()->currentBufferView()->cursor().setLanguageFromInput();
- e->accept();
- return true;
-#endif
+ case QEvent::ApplicationPaletteChange: {
+ // runtime switch from/to dark mode
+ onPaletteChanged();
+ return QApplication::event(e);
+ }
default:
return QApplication::event(e);
}
}
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
void GuiApplication::onApplicationStateChanged(Qt::ApplicationState state)
{
std::string name = "unknown";
LYXERR(Debug::GUI, "onApplicationStateChanged..." << name);
d->last_state_ = state;
}
-#endif
void GuiApplication::startLongOperation() {
//
// X11 specific stuff goes here...
-#ifdef Q_WS_X11
-bool GuiApplication::x11EventFilter(XEvent * xev)
-{
- if (!current_view_)
- return false;
-
- switch (xev->type) {
- case SelectionRequest: {
- if (xev->xselectionrequest.selection != XA_PRIMARY)
- break;
- LYXERR(Debug::SELECTION, "X requested selection.");
- BufferView * bv = current_view_->currentBufferView();
- if (bv) {
- docstring const sel = bv->requestSelection();
- if (!sel.empty()) {
- d->selection_.put(sel);
- // Refresh the selection request timestamp.
- // We have to do this by ourselves as Qt seems
- // not doing that, maybe because of our
- // "persistent selection" implementation
- // (see comments in GuiSelection.cpp).
- XSelectionEvent nev;
- nev.type = SelectionNotify;
- nev.display = xev->xselectionrequest.display;
- nev.requestor = xev->xselectionrequest.requestor;
- nev.selection = xev->xselectionrequest.selection;
- nev.target = xev->xselectionrequest.target;
- nev.property = 0L; // None
- nev.time = CurrentTime;
- XSendEvent(QX11Info::display(),
- nev.requestor, False, 0,
- reinterpret_cast<XEvent *>(&nev));
- return true;
- }
- }
- break;
- }
- case SelectionClear: {
- if (xev->xselectionclear.selection != XA_PRIMARY)
- break;
- LYXERR(Debug::SELECTION, "Lost selection.");
- BufferView * bv = current_view_->currentBufferView();
- if (bv)
- bv->clearSelection();
- break;
- }
- }
- return false;
-}
-#elif defined(QPA_XCB)
+#if defined(QPA_XCB)
bool GuiApplication::nativeEventFilter(const QByteArray & eventType,
- void * message, long *)
+ void * message, QINTPTR *)
{
if (!current_view_ || eventType != "xcb_generic_event_t")
return false;