Initial commit

This commit is contained in:
dalto
2022-02-05 11:15:38 -06:00
commit a0ab1887e5
11 changed files with 602 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
CMakeLists.txt.user
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe
+31
View File
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.16)
project(eosquickstart VERSION 0.1 LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 6.2 COMPONENTS Quick REQUIRED)
qt_add_executable(appeosquickstart
main.cpp packagemodel.h packagemodel.cpp packagemanager.h packagemanager.cpp
)
qt_add_qml_module(appeosquickstart
URI eosquickstart
VERSION 1.0
QML_FILES main.qml
)
set_target_properties(appeosquickstart PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
target_compile_definitions(appeosquickstart
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(appeosquickstart
PRIVATE Qt6::Quick)
+14
View File
@@ -0,0 +1,14 @@
[General]
PackageManager="/usr/bin/pacman"
#PreferredTerminalPath="/usr/bin/xterm"
#PreferredTerminalArgs="-e"
[Browsers]
firefox="Standalone web browser from mozilla.org"
vivaldi="An advanced browser made with the power user in mind"
bob="This should never be installed because it doesn't exist"
chromium="A web browser built for speed, simplicity, and security"
[Graphics]
gimp="GNU Image Manipulation Program"
+10
View File
@@ -0,0 +1,10 @@
[Desktop Entry]
Encoding=UTF-8
Name=EndeavourOS Quickstart Installer
Comment=Get started by installing some common applications
Exec=eosquickstart
Terminal=false
Type=Application
Icon=system-software-install
Categories=System;
NoDisplay=false
+3
View File
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE"></TS>
+51
View File
@@ -0,0 +1,51 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QLocale>
#include <QThread>
#include <QTranslator>
#include "packagemanager.h"
#include "packagemodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
const QString baseName = "eosquickstart_" + QLocale(locale).name();
if (translator.load(":/i18n/" + baseName)) {
app.installTranslator(&translator);
break;
}
}
QSettings settings("/etc/eos-quickstart.conf", QSettings::NativeFormat);
const Terminal preferredTerminal = {settings.value("PreferredTerminalPath").toString(), settings.value("PreferredTerminalArgs").toString()};
PackageModel model;
PackageManager packageManager(settings.value("packagemanager", "/usr/bin/pacman").toString(), &model, preferredTerminal);
const QStringList packageList = packageManager.installedPackages();
model.loadModel(settings, packageList);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("packageModel"), &model);
engine.rootContext()->setContextProperty(QStringLiteral("packageManager"), &packageManager);
const QUrl url(u"qrc:/eosquickstart/main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
+122
View File
@@ -0,0 +1,122 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
//import Packages 1.0
ApplicationWindow {
width: 600
height: 800
visible: true
title: qsTr("EndeavourOS QuickStart Installer")
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
ListView {
id: lvPackages
property var expandMap: ({})
model: packageModel
delegate: Item {
property bool expanded: lvPackages.isSectionExpanded( model.category )
width: parent.width - sbListview.width
implicitHeight: expanded ? check.implicitHeight : 0
Row {
height: parent.height
width: parent.width
clip: true
CheckDelegate {
id: check
width: parent.width
text: " " + model.name + " - " + model.description + (model.isInstalled ? " (Installed)" : "")
checkable: model.isInstalled ? false : true
checked: model.isChecked
font.bold: model.isInstalled ? true : false
onCheckStateChanged: model.isChecked = checked
}
}
Behavior on height {
NumberAnimation { duration: 200 }
}
}
width: Window.width
height: Window.height - installButton.height - 10
focus: true
clip: true
section.property: "category"
section.criteria: ViewSection.FullString
section.delegate: Rectangle {
width: parent.width - sbListview.width
implicitHeight: text.implicitHeight + 2
color: myPalette.window
Text {
id: text
anchors.left: parent.left
font.bold: true
text: section
padding: 5
color: myPalette.windowText
}
MouseArea {
anchors.fill: parent
onClicked: lvPackages.toggleSection( section );
}
}
function isSectionExpanded( section ) {
return (section in expandMap);
}
function hideSection( section ) {
delete expandMap[section]
expandMapChanged();
}
function showSection( section ) {
expandMap[section] = true
expandMapChanged();
}
function toggleSection( section ) {
if ( isSectionExpanded( section ) ) {
hideSection( section )
} else {
showSection( section )
}
}
// We need to make sure our selections don't become unchecked as we scroll
cacheBuffer: 50000
ScrollBar.vertical: ScrollBar {
id: sbListview
active: true
}
}
Button {
id: installButton
height: 40
anchors {
bottom: parent.bottom
bottomMargin: 5
horizontalCenter: parent.horizontalCenter;
}
onClicked: packageManager.installPackages()
text: "Install Now"
}
}
+103
View File
@@ -0,0 +1,103 @@
#include "packagemanager.h"
#include <QFile>
PackageManager::PackageManager(const QString &binaryPath, PackageModel *packageModel, Terminal terminal, QObject *parent)
: QObject{parent}
{
m_preferredTerminal = terminal;
m_pacmanBinary = binaryPath;
m_model = packageModel;
populateInstalledPackages();
}
const QStringList PackageManager::installedPackages() const
{
return m_installedPackages;
}
bool PackageManager::installPackages()
{
Terminal terminal = getTerminal();
if (terminal.binary.isEmpty()) {
return false;
}
QList<PackageData> *packages = m_model->model();
QStringList packageList;
for (const PackageData &package : *packages) {
if (package.isChecked) {
packageList.append(package.packageName);
}
}
if (packageList.isEmpty()) {
return false;
}
QProcess proc;
proc.setProgram(terminal.binary);
proc.setArguments(QStringList() << terminal.args << "pkexec bash -c \"" + m_pacmanBinary + " -Syu " + packageList.join(" ") + " ; read -p '" + tr("Press enter to resume") + "...'\"");
proc.start();
proc.waitForFinished(1000 * 60);
if( proc.exitCode() == 0) {
return true;
} else {
return false;
}
}
bool PackageManager::populateInstalledPackages()
{
if (m_pacmanBinary.isEmpty()) {
qWarning() << tr("ERROR: Package Manager not set");
return false;
}
m_installedPackages.clear();
QProcess proc;
proc.setProgram(m_pacmanBinary);
proc.setArguments({"-Qq"});
proc.start();
if (!proc.waitForFinished()) {
qWarning() << tr("Failed to start process ") << proc.program() << proc.arguments();
return false;
}
QString output = proc.readAllStandardOutput();
const QStringList lines = output.split('\n');
for (const QString &line : lines) {
if (!line.trimmed().isEmpty()) {
m_installedPackages.append(line.trimmed());
}
}
return true;
}
Terminal PackageManager::getTerminal()
{
if (QFile::exists(m_preferredTerminal.binary)) {
return m_preferredTerminal;
}
// Iterate over the list until we find an installed terminal
for (const Terminal &terminal : m_terminalList) {
if (QFile::exists(terminal.binary)) {
return terminal;
}
}
qWarning() << tr("Preferred terminal not available and no terminal found on system");
return Terminal();
}
+45
View File
@@ -0,0 +1,45 @@
#ifndef PACKAGEMANAGER_H
#define PACKAGEMANAGER_H
#include <QObject>
#include <QProcess>
#include "packagemodel.h"
struct Terminal {
QString binary;
QString args;
};
class PackageManager : public QObject
{
Q_OBJECT
public:
explicit PackageManager(const QString &binaryPath, PackageModel *packageModel, Terminal terminal, QObject *parent = nullptr);
const QStringList installedPackages() const;
public slots:
bool installPackages();
private:
bool populateInstalledPackages();
Terminal getTerminal();
QString m_pacmanBinary;
Terminal m_preferredTerminal;
QStringList m_installedPackages;
PackageModel *m_model;
const QVector<Terminal> m_terminalList = {
{"/usr/bin/konsole", "-e"},
{"/usr/bin/gnome-terminal", "--"},
{"/usr/bin/xfce4-terminal", "-x"},
{"/usr/bin/lxterminal", "-e"},
{"/usr/bin/xterm", "-e"},
{"/usr/bin/alacritty", "-e"}
};
};
#endif // PACKAGEMANAGER_H
+98
View File
@@ -0,0 +1,98 @@
#include "packagemodel.h"
PackageModel::PackageModel(QObject *parent)
: QAbstractListModel(parent)
{
}
int PackageModel::rowCount(const QModelIndex &parent) const
{
// For list models only the root node (an invalid parent) should return the list's size. For all
// other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
if (parent.isValid())
return 0;
return m_model.size();
}
QVariant PackageModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
switch(role) {
case NameRole:
return m_model.at(index.row()).packageName;
case DescriptionRole:
return m_model.at(index.row()).packageDesc;
case CategoryRole:
return m_model.at(index.row()).category;
case IsInstalledRole:
return m_model.at(index.row()).isInstalled;
case IsCheckedRole:
return m_model.at(index.row()).isChecked;
default:
qWarning() << tr("Package model can't retrieve data from invalid role");
}
return QVariant();
}
bool PackageModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (data(index, role) != value) {
// Only allow the checked status to be updated
if (role==IsCheckedRole && index.row() < m_model.size() && value.typeId() == QMetaType::Bool) {
m_model[index.row()].isChecked = value.toBool();
}
emit dataChanged(index, index, QVector<int>() << role);
return true;
}
return false;
}
Qt::ItemFlags PackageModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEditable;
}
QHash<int, QByteArray> PackageModel::roleNames() const
{
return {{NameRole, "name"},
{DescriptionRole, "description"},
{CategoryRole, "category"},
{IsInstalledRole, "isInstalled"},
{IsCheckedRole, "isChecked"}
};
}
bool PackageModel::loadModel(QSettings &settings, const QStringList &installedPackageList)
{
if(settings.allKeys().isEmpty()) {
return false;
}
m_model.clear();
// Iterate over the settings file and populate the model
const QStringList groups = settings.childGroups();
for (const QString &group : groups) {
settings.beginGroup(group);
const QStringList keys = settings.allKeys();
for (const QString &key : keys) {
PackageData packageData;
packageData.packageName = key;
packageData.packageDesc = settings.value(key).toString();
packageData.category = group;
packageData.isInstalled = installedPackageList.contains(key);
packageData.isChecked = false;
m_model.append(packageData);
}
settings.endGroup();
}
return true;
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef PACKAGEMODEL_H
#define PACKAGEMODEL_H
#include <QAbstractListModel>
#include <QSettings>
struct PackageData {
QString packageName;
QString packageDesc;
QString category;
bool isInstalled;
bool isChecked;
};
class PackageModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit PackageModel(QObject *parent = nullptr);
enum {
NameRole = Qt::UserRole,
DescriptionRole,
CategoryRole,
IsInstalledRole,
IsCheckedRole
};
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// Editable:
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
virtual QHash<int, QByteArray> roleNames() const override;
bool loadModel(QSettings &settings, const QStringList &installedPackageList);
QList<PackageData>* model() { return &m_model; }
private:
QList<PackageData> m_model;
};
#endif // PACKAGEMODEL_H