#include <QByteArray>
#include <QBitmap>
#include <QDateTime>
+#if QT_VERSION < 0x060000
#include <QDesktopWidget>
+#endif
#include <QEvent>
#include <QFileOpenEvent>
#include <QFileInfo>
#include <QObject>
#include <QPainter>
#include <QPixmap>
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+#include <QRandomGenerator>
+#endif
#include <QRegExp>
#include <QSessionManager>
#include <QSettings>
#if (QT_VERSION < 0x050000)
#include <QWindowsMime>
#define QWINDOWSMIME QWindowsMime
+#define QVARIANTTYPE QVariant::Type
+#elif (QT_VERSION >= 0x060000)
+#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;
#else
#include <QWinMime>
#define QWINDOWSMIME QWinMime
+#define QVARIANTTYPE QVariant::Type
#endif
#ifdef Q_CC_GNU
#include <wtypes.h>
#endif
#endif
-#ifdef Q_OS_MAC
+#if defined(Q_OS_MAC) && (QT_VERSION < 0x060000)
#include <QMacPasteboardMime>
#endif // Q_OS_MAC
AllowSetForegroundWindow(ASFW_ANY);
#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)
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
// Attribute Qt::AA_EnableHighDpiScaling must be set before QCoreApplication is created
if (getEnv("QT_ENABLE_HIGHDPI_SCALING").empty()
// HighDPI scale factor policy must be set before QGuiApplication is created
if (getEnv("QT_SCALE_FACTOR_ROUNDING_POLICY").empty())
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
+#endif
#endif
frontend::GuiApplication * guiApp = new frontend::GuiApplication(argc, argv);
}
+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";
//
////////////////////////////////////////////////////////////////////////
-#ifdef Q_OS_MAC
+#if defined(Q_OS_MAC) && (QT_VERSION < 0x060000)
// QMacPasteboardMimeGraphics can only be compiled on Mac.
class QMacPasteboardMimeGraphics : public QMacPasteboardMime
}
QVariant convertToMime(QString const & mimetype, IDataObject * pDataObj,
- QVariant::Type /*preferredType*/) const override
+ QVARIANTTYPE /*preferredType*/) const override
{
QByteArray data;
if (!canConvertToMime(mimetype, pDataObj))
#if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
/// WMF Mime handler for Windows clipboard.
wmf_mime_ = new QWindowsMimeMetafile;
+ #if (QT_VERSION >= 0x060000)
+ win_app_ = dynamic_cast<QWindowsApplication *>
+ (QGuiApplicationPrivate::platformIntegration());
+ win_app_->registerMime(wmf_mime_);
+ #endif
#endif
#endif
initKeySequences(&theTopLevelKeymap());
}
+ #if (QT_VERSION >= 0x060000)
+ #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);
Qt::ApplicationState last_state_;
#endif
-#ifdef Q_OS_MAC
+#if defined(Q_OS_MAC) && (QT_VERSION < 0x060000)
/// Linkback mime handler for MacOSX.
QMacPasteboardMimeGraphics mac_pasteboard_mime_;
#endif
#if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
/// WMF Mime handler for Windows clipboard.
QWindowsMimeMetafile * wmf_mime_;
+#if (QT_VERSION >= 0x060000)
+ QWindowsApplication * win_app_;
+#endif
#endif
#endif
QCoreApplication::setOrganizationName(app_name);
QCoreApplication::setOrganizationDomain("lyx.org");
QCoreApplication::setApplicationName(lyx_package);
-#if QT_VERSION >= 0x050000
+#if QT_VERSION >= 0x050100 && QT_VERSION < 0x060000
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
setDesktopFileName(lyx_package);
#endif
- // FIXME Deprecated. Should use QRandomGenerator since 5.10
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+ QRandomGenerator(QDateTime::currentDateTime().toSecsSinceEpoch());
+#else
qsrand(QDateTime::currentDateTime().toTime_t());
+#endif
// Install LyX translator for missing Qt translations
installTranslator(&d->gui_trans_);
// needs to be done before reading lyxrc
QWidget w;
- lyxrc.dpi = (w.physicalDpiX() + w.physicalDpiY()) / 2;
+ lyxrc.dpi = (w.logicalDpiX() + w.logicalDpiY()) / 2;
guiApp = this;
case LFUN_REPEAT:
case LFUN_PREFERENCES_SAVE:
case LFUN_BUFFER_SAVE_AS_DEFAULT:
- case LFUN_DEBUG_LEVEL_SET:
// these are handled in our dispatch()
break;
+ case LFUN_DEBUG_LEVEL_SET: {
+ string bad = Debug::badValue(to_utf8(cmd.argument()));
+ enable = bad.empty();
+ if (!bad.empty())
+ flag.message(bformat(_("Bad debug value `%1$s'."), from_utf8(bad)));
+ break;
+ }
+
case LFUN_WINDOW_CLOSE:
enable = !d->views_.empty();
break;
BufferView * bv = current_view_->currentBufferView();
if (bv) {
- if (dr.needBufferUpdate()) {
+ if (dr.needBufferUpdate() || bv->buffer().needUpdate()) {
bv->cursor().clearBufferUpdate();
bv->buffer().updateBuffer();
}
void GuiApplication::reconfigure(string const & option)
{
// emit message signal.
- if (current_view_)
+ if (current_view_) {
current_view_->message(_("Running configure..."));
+ current_view_->setCursor(Qt::WaitCursor);
+ }
// Run configure in user lyx directory
string const lock_file = package().getConfigureLockName();
LaTeXPackages::getAvailable();
fileUnlock(fd, lock_file.c_str());
+ if (current_view_)
+ current_view_->unsetCursor();
+
if (ret)
Alert::information(_("System reconfiguration failed"),
_("The system reconfiguration has failed.\n"
case LFUN_BOOKMARK_CLEAR:
theSession().bookmarks().clear();
+ dr.screenUpdate(Update::Force);
break;
case LFUN_DEBUG_LEVEL_SET:
}
+void GuiApplication::onPaletteChanged()
+{
+ colorCache().setPalette(palette());
+}
+
+
void GuiApplication::handleKeyFunc(FuncCode action)
{
char_type c = 0;
// seq has been reset at this point
func = seq.addkey(keysym, NoModifier);
- LYXERR(Debug::KEY, " Key (queried) [action=" << func.action() << "]["
+ LYXERR(Debug::KEY, " Key (queried) [action="
+ << lyxaction.getActionName(func.action()) << "]["
<< seq.print(KeySequence::Portable) << ']');
return func.action() != LFUN_UNKNOWN_ACTION;
}
d->cancel_meta_seq.reset();
FuncRequest func = d->cancel_meta_seq.addkey(keysym, state);
- LYXERR(Debug::KEY, "action first set to [" << func.action() << ']');
+ LYXERR(Debug::KEY, "action first set to ["
+ << lyxaction.getActionName(func.action()) << ']');
// When not cancel or meta-fake, do the normal lookup.
// Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
if ((func.action() != LFUN_CANCEL) && (func.action() != LFUN_META_PREFIX)) {
// remove Caps Lock and Mod2 as a modifiers
func = d->keyseq.addkey(keysym, (state | d->meta_fake_bit));
- LYXERR(Debug::KEY, "action now set to [" << func.action() << ']');
+ LYXERR(Debug::KEY, "action now set to ["
+ << lyxaction.getActionName(func.action()) << ']');
}
// Don't remove this unless you know what you are doing.
if (func.action() == LFUN_NOACTION)
func = FuncRequest(LFUN_COMMAND_PREFIX);
- LYXERR(Debug::KEY, " Key [action=" << func.action() << "]["
- << d->keyseq.print(KeySequence::Portable) << ']');
+ LYXERR(Debug::KEY, " Key [action="
+ << lyxaction.getActionName(func.action()) << "]["
+ << d->keyseq.print(KeySequence::Portable) << ']');
// already here we know if it any point in going further
// why not return already here if action == -1 and
// If addkey looked up a command and did not find further commands then
// seq has been reset at this point
func = d->keyseq.addkey(keysym, NoModifier);
- LYXERR(Debug::KEY, "Action now " << func.action());
+ LYXERR(Debug::KEY, "Action now " << lyxaction.getActionName(func.action()));
}
if (func.action() == LFUN_UNKNOWN_ACTION) {
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();
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();
+ h = match.captured(2).toInt();
+ x = match.captured(3).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 == '-')
}
+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();
// opposite. Therefore, long name should be used without truncation.
// c.f. http://doc.trolltech.com/4.1/qtranslator.html#load
if (!d->qt_trans_.load(language_name,
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ QLibraryInfo::path(QLibraryInfo::TranslationsPath))) {
+#else
QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
+#endif
LYXERR(Debug::LOCALE, "Could not find Qt translations for locale "
<< language_name);
} else {
#if QT_VERSION > 0x040600
setAttribute(Qt::AA_MacDontSwapCtrlAndMeta,lyxrc.mac_dontswap_ctrl_meta);
#endif
-#if QT_VERSION > 0x050100
+#if QT_VERSION >= 0x050000 && QT_VERSION < 0x060000
setAttribute(Qt::AA_UseHighDpiPixmaps,true);
#endif
// Create the global default menubar which is shown for the dialogs
e->accept();
return true;
#endif
+ case QEvent::ApplicationPaletteChange: {
+ // runtime switch from/to dark mode
+ onPaletteChanged();
+ return QApplication::event(e);
+ }
default:
return QApplication::event(e);
}