diff --git a/data/pics/splash-background.png b/data/pics/splash-background.png new file mode 100644 index 000000000..b7b6a6f15 Binary files /dev/null and b/data/pics/splash-background.png differ diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt index 8dbe14c60..e8b36dd10 100644 --- a/src/dialogs/CMakeLists.txt +++ b/src/dialogs/CMakeLists.txt @@ -1,12 +1,13 @@ set(kdenlive_SRCS ${kdenlive_SRCS} dialogs/clipcreationdialog.cpp dialogs/encodingprofilesdialog.cpp dialogs/kdenlivesettingsdialog.cpp dialogs/markerdialog.cpp dialogs/profilesdialog.cpp dialogs/renderwidget.cpp + dialogs/splash.cpp dialogs/titletemplatedialog.cpp dialogs/wizard.cpp PARENT_SCOPE) diff --git a/src/dialogs/splash.cpp b/src/dialogs/splash.cpp new file mode 100644 index 000000000..5156cd5c2 --- /dev/null +++ b/src/dialogs/splash.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2017 by Nicolas Carion * + * This file is part of Kdenlive. See www.kdenlive.org. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3 or any later version accepted by the * + * membership of KDE e.V. (or its successor approved by the membership * + * of KDE e.V.), which shall act as a proxy defined in Section 14 of * + * version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#include "splash.hpp" + +#include +#include +#include +#include + +Splash::Splash() + : QObject() + , m_engine(new QQmlEngine()) +{ + KDeclarative::KDeclarative kdeclarative; + kdeclarative.setDeclarativeEngine(m_engine); + kdeclarative.setupEngine(m_engine); + kdeclarative.setupContext(); + QQmlComponent component(m_engine); + component.loadUrl(QUrl(QStringLiteral("qrc:/qml/splash.qml"))); + if (component.isReady()) { + auto *root = component.create(); + connect(this, SIGNAL(sigEndSplash()), root, SIGNAL(endSplash())); + } else { + qWarning() << component.errorString(); + } +} +Splash::~Splash() +{ + delete m_engine; +} + +void Splash::endSplash() +{ + emit sigEndSplash(); +} diff --git a/src/dialogs/splash.hpp b/src/dialogs/splash.hpp new file mode 100644 index 000000000..25c1f38d5 --- /dev/null +++ b/src/dialogs/splash.hpp @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2017 by Nicolas Carion * + * This file is part of Kdenlive. See www.kdenlive.org. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3 or any later version accepted by the * + * membership of KDE e.V. (or its successor approved by the membership * + * of KDE e.V.), which shall act as a proxy defined in Section 14 of * + * version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef SPLASH_H +#define SPLASH_H + +#include + +class QQmlEngine; + +class Splash : public QObject +{ + Q_OBJECT + +public: + Splash(); + ~Splash(); + + void endSplash(); + +signals: + void sigEndSplash(); + +protected: + QQmlEngine *m_engine; +}; + +#endif diff --git a/src/icons.qrc b/src/icons.qrc index 0fedfdc8b..9c6ae1cce 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -1,37 +1,39 @@ ../data/pics/breeze-light/lt_timeline-insert.svg ../data/pics/breeze-light/lt_timeline-extract.svg ../data/pics/breeze-light/lt_timeline-lift.svg ../data/pics/breeze-light/lt_timeline-overwrite.svg ../data/pics/breeze-light/lt_timeline-use-zone-on.svg ../data/pics/breeze-light/lt_timeline-use-zone-off.svg ../data/pics/breeze-light/lt_favorite.svg ../data/pics/breeze-light/lt_zone-in.svg ../data/pics/breeze-light/lt_zone-out.svg ../data/pics/breeze-light/lt_preview-add-zone.svg ../data/pics/breeze-light/lt_preview-remove-zone.svg ../data/pics/breeze-light/lt_preview-render-on.svg ../data/pics/breeze-light/lt_preview-render-off.svg ../data/pics/breeze-light/lt_view-split-effect.svg ../data/pics/breeze-light/lt_composite-track-on.svg ../data/pics/breeze-light/lt_composite-track-preview.svg ../data/pics/breeze-light/lt_composite-track-off.svg ../data/pics/breeze-light/lt_project-defaults.svg ../data/pics/breeze-light/lt_jog-dial.svg ../data/pics/breeze-light/lt_show-all-effects.svg ../data/pics/breeze-light/lt_show-gpu-effects.svg ../data/pics/breeze-light/lt_auto-transition.svg ../data/pics/breeze-light/lt_preview-remove-all.svg ../data/pics/breeze-light/lt_linear.svg ../data/pics/breeze-light/lt_discrete.svg ../data/pics/breeze-light/lt_smooth.svg ../data/icons/48-apps-kdenlive.png + ../data/pics/kdenlive-logo.png + ../data/pics/splash-background.png ../data/pics/breeze-light/lt_kdenlive-lock.svg ../data/pics/breeze-light/lt_kdenlive-unlock.svg ../data/pics/breeze-light/lt_kdenlive-hide-video.svgz ../data/pics/breeze-light/lt_kdenlive-show-video.svgz ../data/pics/breeze-light/lt_kdenlive-show-audio.svgz ../data/pics/breeze-light/lt_kdenlive-hide-audio.svgz diff --git a/src/main.cpp b/src/main.cpp index 60902037e..616fe13a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,208 +1,222 @@ /*************************************************************************** * Copyright (C) 2007 by Marco Gittler (g.marco@freenet.de) * * Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "core.h" +#include "dialogs/splash.hpp" #include "logger.hpp" #include #include #include "kxmlgui_version.h" #include #include #ifdef USE_DRMINGW #include #elif defined(KF5_USE_CRASH) #include #endif #include #include #include "definitions.h" #include "kdenlive_debug.h" #include #include #include #include #include #include #include #include #include #include //new #include +int fact(int n) +{ + return n < 2 ? n : fact(n - 1) + fact(n - 2); +} int main(int argc, char *argv[]) { #ifdef USE_DRMINGW ExcHndlInit(); #endif // Force QDomDocument to use a deterministic XML attribute order qSetGlobalQHashSeed(0); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) QCoreApplication::setAttribute(Qt::AA_X11InitThreads); #endif #ifdef Q_OS_WIN qputenv("KDE_FORK_SLAVES", "1"); QString path = qApp->applicationDirPath() + QLatin1Char(';') + qgetenv("PATH"); qputenv("PATH", path.toUtf8().constData()); #endif Logger::init(); QApplication app(argc, argv); app.setApplicationName(QStringLiteral("kdenlive")); app.setOrganizationDomain(QStringLiteral("kde.org")); app.setWindowIcon(QIcon(QStringLiteral(":/pics/kdenlive.png"))); KLocalizedString::setApplicationDomain("kdenlive"); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup grp(config, "unmanaged"); KConfigGroup initialGroup(config, "version"); if (!initialGroup.exists()) { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); if (env.contains(QStringLiteral("XDG_CURRENT_DESKTOP")) && env.value(QStringLiteral("XDG_CURRENT_DESKTOP")).toLower() == QLatin1String("kde")) { qCDebug(KDENLIVE_LOG) << "KDE Desktop detected, using system icons"; } else { // We are not on a KDE desktop, force breeze icon theme // Check if breeze theme is available QStringList iconThemes = KIconTheme::list(); if (iconThemes.contains(QStringLiteral("breeze"))) { grp.writeEntry("force_breeze", true); grp.writeEntry("use_dark_breeze", true); qCDebug(KDENLIVE_LOG) << "Non KDE Desktop detected, forcing Breeze icon theme"; } } // Set breeze dark as default on first opening KConfigGroup cg(config, "UiSettings"); cg.writeEntry("ColorScheme", "Breeze Dark"); } // Init DBus services KDBusService programDBusService; bool forceBreeze = grp.readEntry("force_breeze", QVariant(false)).toBool(); if (forceBreeze) { bool darkBreeze = grp.readEntry("use_dark_breeze", QVariant(false)).toBool(); QIcon::setThemeName(darkBreeze ? QStringLiteral("breeze-dark") : QStringLiteral("breeze")); } // Create KAboutData KAboutData aboutData(QByteArray("kdenlive"), i18n("Kdenlive"), KDENLIVE_VERSION, i18n("An open source video editor."), KAboutLicense::GPL, i18n("Copyright © 2007–2019 Kdenlive authors"), i18n("Please report bugs to http://bugs.kde.org"), QStringLiteral("https://kdenlive.org")); aboutData.addAuthor(i18n("Jean-Baptiste Mardelle"), i18n("MLT and KDE SC 4 / KF5 port, main developer and maintainer"), QStringLiteral("jb@kdenlive.org")); aboutData.addAuthor(i18n("Nicolas Carion"), i18n("Code re-architecture & timeline rewrite"), QStringLiteral("french.ebook.lover@gmail.com")); aboutData.addAuthor(i18n("Vincent Pinon"), i18n("KF5 port, Windows cross-build, bugs fixing"), QStringLiteral("vpinon@kde.org")); aboutData.addAuthor(i18n("Laurent Montel"), i18n("Bugs fixing, clean up code, optimization etc."), QStringLiteral("montel@kde.org")); aboutData.addAuthor(i18n("Till Theato"), i18n("Bug fixing, etc."), QStringLiteral("root@ttill.de")); aboutData.addAuthor(i18n("Simon A. Eugster"), i18n("Color scopes, bug fixing, etc."), QStringLiteral("simon.eu@gmail.com")); aboutData.addAuthor(i18n("Marco Gittler"), i18n("MLT transitions and effects, timeline, audio thumbs"), QStringLiteral("g.marco@freenet.de")); aboutData.addAuthor(i18n("Dan Dennedy"), i18n("Bug fixing, etc."), QStringLiteral("dan@dennedy.org")); aboutData.addAuthor(i18n("Alberto Villa"), i18n("Bug fixing, logo, etc."), QStringLiteral("avilla@FreeBSD.org")); aboutData.addAuthor(i18n("Jean-Michel Poure"), i18n("Rendering profiles customization"), QStringLiteral("jm@poure.com")); aboutData.addAuthor(i18n("Ray Lehtiniemi"), i18n("Bug fixing, etc."), QStringLiteral("rayl@mail.com")); aboutData.addAuthor(i18n("Steve Guilford"), i18n("Bug fixing, etc."), QStringLiteral("s.guilford@dbplugins.com")); aboutData.addAuthor(i18n("Jason Wood"), i18n("Original KDE 3 version author (not active anymore)"), QStringLiteral("jasonwood@blueyonder.co.uk")); aboutData.addCredit(i18n("Nara Oliveira and Farid Abdelnour | Estúdio Gunga"), i18n("Kdenlive 16.08 icon")); aboutData.setTranslator(i18n("NAME OF TRANSLATORS"), i18n("EMAIL OF TRANSLATORS")); aboutData.setOrganizationDomain(QByteArray("kde.org")); aboutData.setOtherText( i18n("Using:\nMLT version %1\nFFmpeg libraries", mlt_version_get_string())); aboutData.setDesktopFileName(QStringLiteral("org.kde.kdenlive")); // Register about data KAboutData::setApplicationData(aboutData); // Add rcc stored icons to the search path so that we always find our icons KIconLoader *loader = KIconLoader::global(); loader->reconfigure("kdenlive", QStringList() << QStringLiteral(":/pics")); // Set app stuff from about data app.setApplicationDisplayName(aboutData.displayName()); app.setOrganizationDomain(aboutData.organizationDomain()); app.setApplicationVersion(aboutData.version()); app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); // Create command line parser with options QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.setApplicationDescription(aboutData.shortDescription()); parser.addVersionOption(); parser.addHelpOption(); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("config"), i18n("Set a custom config file name"), QStringLiteral("config"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("mlt-path"), i18n("Set the path for MLT environment"), QStringLiteral("mlt-path"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("mlt-log"), i18n("MLT log level"), QStringLiteral("verbose/debug"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("i"), i18n("Comma separated list of clips to add"), QStringLiteral("clips"))); parser.addPositionalArgument(QStringLiteral("file"), i18n("Document to open")); // Parse command line parser.process(app); aboutData.processCommandLine(&parser); #ifdef USE_DRMINGW ExcHndlInit(); #elif defined(KF5_USE_CRASH) KCrash::initialize(); #endif + auto splash = new Splash(); + // splash->show(); + qApp->processEvents(); + qmlRegisterUncreatableMetaObject(PlaylistState::staticMetaObject, // static meta object "com.enums", // import statement 1, 0, // major and minor version of the import "ClipState", // name in QML "Error: only enums"); qmlRegisterUncreatableMetaObject(ClipType::staticMetaObject, // static meta object "com.enums", // import statement 1, 0, // major and minor version of the import "ProducerType", // name in QML "Error: only enums"); QString mltPath = parser.value(QStringLiteral("mlt-path")); if (parser.value(QStringLiteral("mlt-log")) == QStringLiteral("verbose")) { mlt_log_set_level(MLT_LOG_VERBOSE); } else if (parser.value(QStringLiteral("mlt-log")) == QStringLiteral("debug")) { mlt_log_set_level(MLT_LOG_DEBUG); } QUrl url; if (parser.positionalArguments().count() != 0) { url = QUrl::fromLocalFile(parser.positionalArguments().at(0)); // Make sure we get an absolute URL so that we can autosave correctly QString currentPath = QDir::currentPath(); QUrl startup = QUrl::fromLocalFile(currentPath.endsWith(QDir::separator()) ? currentPath : currentPath + QDir::separator()); url = startup.resolved(url); } + qApp->processEvents(); Core::build(mltPath); + qApp->processEvents(); pCore->initGUI(url); + splash->endSplash(); + qApp->processEvents(); int result = app.exec(); Core::clean(); + delete splash; if (EXIT_RESTART == result) { qCDebug(KDENLIVE_LOG) << "restarting app"; auto *restart = new QProcess; restart->start(app.applicationFilePath(), QStringList()); restart->waitForReadyRead(); restart->waitForFinished(1000); result = EXIT_SUCCESS; } return result; } diff --git a/src/qml/splash.qml b/src/qml/splash.qml new file mode 100644 index 000000000..1c3d70dd1 --- /dev/null +++ b/src/qml/splash.qml @@ -0,0 +1,217 @@ +/*************************************************************************** + * Copyright (C) 2017 by Nicolas Carion * + * This file is part of Kdenlive. See www.kdenlive.org. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3 or any later version accepted by the * + * membership of KDE e.V. (or its successor approved by the membership * + * of KDE e.V.), which shall act as a proxy defined in Section 14 of * + * version 3 of the license. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.3 +import QtQuick.Controls.Styles 1.4 + + +Window { + id: splash + objectName: "splash" + color: "transparent" + title: "Splash Window" + SystemPalette { id: activePalette } + modality: Qt.ApplicationModal + flags: Qt.SplashScreen + property int timeoutInterval: 2000 + signal timeout + x: (Screen.width - splashContent.width) / 2 + y: (Screen.height - splashContent.height) / 2 + width: splashContent.width + height: splashContent.height + + property int border: 10 + property bool splashing: true + + signal endSplash + + onEndSplash: { + console.log("ending splash") + splash.splashing = false; + } + + Rectangle { + id:splashContent + height: Screen.height / 2 + width: Screen.width / 3 + border.width:splash.border + border.color:"#bfbfbf" + color: "#31363b" + Image { + id:logo + anchors.left: splashContent.left + anchors.top: splashContent.top + anchors.margins: 50 + // anchors.horizontalCenter: splashContent.horizontalCenter + source: "../pics/kdenlive-logo.png" + fillMode: Image.PreserveAspectFit + height: splashContent.height / 5 - 100 + } + RowLayout { + //anchors.horizontalCenter: splashContent.horizontalCenter + anchors.bottom: logo.bottom + anchors.right: splashContent.right + anchors.rightMargin: logo.x + spacing: 100 + Text { + color: "white" + text: "Website" + font.bold: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + } + Text { + color: "white" + text: "Donate" + font.bold: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + } + Text { + color: "white" + text: "Forum" + font.bold: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + } + } + Rectangle { + id:recentProjects + y: splashContent.height / 5 + anchors.left: splashContent.left + anchors.leftMargin: splash.border + anchors.right: splashContent.right + anchors.rightMargin: splash.border + color:"#232629" + height: 3 * splashContent.height / 5 + width: splashContent.width + visible: !splashing + Text { + id:txtProject + color: "#f38577" + text: "Recent Projects" + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: 50 + anchors.leftMargin: 100 + font.bold: true + } + } + Image { + id:splash_img + y: splashContent.height / 5 + anchors.left: splashContent.left + anchors.leftMargin: splash.border + anchors.right: splashContent.right + anchors.rightMargin: splash.border + height: 3 * splashContent.height / 5 + width: splashContent.width + source: "../pics/splash-background.png" + fillMode: Image.PreserveAspectFit + visible: splashing + } + /*Text { + id:txtProject + color: "#f38577" + text: "Recent Projects" + anchors.horizontalCenter: splashContent.horizontalCenter + anchors.top: logo.bottom + anchors.topMargin: 50 + font.bold: true + } + Rectangle { + id:recentProjects + border.width:5 + border.color:"#efefef" + color: "#fafafa" + height: splashContent.height / 4 + width: 5*splashContent.width/6 + anchors.horizontalCenter: splashContent.horizontalCenter + anchors.top: txtProject.bottom + anchors.topMargin: 5 + }*/ + Item { + anchors.left: splashContent.left + anchors.right: splashContent.right + anchors.bottom: splashContent.bottom + anchors.top: recentProjects.bottom + anchors.margins: 50 + visible: !splashing + CheckBox { + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + style: CheckBoxStyle { + indicator: Rectangle { + implicitWidth: 32 + implicitHeight: 32 + radius: 3 + //border.color: control.activeFocus ? "darkblue" : "gray" + border.width: 1 + border.color:"white" + color: "#4d4d4d" + Rectangle { + visible: control.checked + color: "#555" + border.color: "#333" + radius: 1 + anchors.margins: 4 + anchors.fill: parent + } + } + label: Text { + text: "Hide on startup" + color: "white" + } + } + } + Row { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + spacing: 50 + visible: !splashing + Button { + iconSource:"image://icon/document-new" + text:"New" + //style: CustomButton { + // backColor: splashContent.color + // } + } + Button { + iconSource:"image://icon/document-open" + text:"Open" + //style: CustomButton { + // backColor: splashContent.color + // } + } + } + } + + /*MouseArea { + anchors.fill: parent + onClicked: { + console.log("clic"); + splash.close(); + } + }*/ + } + Component.onCompleted: visible = true + +} diff --git a/src/uiresources.qrc b/src/uiresources.qrc index 6d87eedd0..151218e43 100644 --- a/src/uiresources.qrc +++ b/src/uiresources.qrc @@ -1,52 +1,53 @@ kdenliveui.rc monitor/view/kdenlivemonitor.qml monitor/view/kdenliveclipmonitor.qml monitor/view/kdenlivemonitoreffectscene.qml monitor/view/kdenlivemonitorcornerscene.qml monitor/view/kdenlivemonitorrotoscene.qml monitor/view/kdenlivemonitorsplit.qml monitor/view/kdenlivemonitorripple.qml monitor/view/SceneToolBar.qml monitor/view/EffectToolBar.qml monitor/view/MonitorRuler.qml monitor/view/OverlayStandard.qml monitor/view/OverlayMinimal.qml monitor/view/OverlayCenter.qml monitor/view/OverlayCenterDiagonal.qml monitor/view/OverlayThirds.qml timeline2/view/qml/timeline.qml timeline2/view/qml/TrackHead.qml timeline2/view/qml/AudioLevels.qml timeline2/view/qml/Track.qml timeline2/view/qml/Ruler.qml timeline2/view/qml/Clip.qml timeline2/view/qml/ClipThumbs.qml timeline2/view/qml/ClipAudioThumbs.qml timeline2/view/qml/KeyframeView.qml timeline2/view/qml/Composition.qml timeline2/view/qml/ClipMenu.qml timeline2/view/qml/CompositionMenu.qml timeline2/view/qml/PulsingAnimation.qml timeline2/view/qml/CornerSelectionShadow.qml timeline2/view/qml/Timeline.js assets/assetlist/view/qml/assetList.qml assets/view/qml/AssetView.qml transitions/view/qml/transitionView.qml timeline2/view/qml/AssetMenu.qml effects/effectstack/view/qml/BuiltStack.qml effects/effectstack/view/qml/EffectSlider.qml effects/effectstack/view/qml/LiftGammaGain.qml + qml/splash.qml ../data/blacklisted_effects.txt ../data/blacklisted_transitions.txt ../data/kdenlive_renderprofiles.knsrc ../data/kdenlive_titles.knsrc ../data/kdenlive_wipes.knsrc ../data/kdenlive_keyboardschemes.knsrc