mirror of
https://github.com/endeavouros-team/eos-quickstart.git
synced 2026-07-29 06:55:24 +00:00
Initial commit
This commit is contained in:
+74
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="de_DE"></TS>
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user