9 Commits

25 changed files with 742 additions and 14 deletions

View File

@@ -3,9 +3,17 @@ project(SOM LANGUAGES CXX)
find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets)
option(DEV_BUILD "Enable developer-mode build" OFF)
if(DEV_BUILD)
add_compile_definitions(DEV_BUILD=1)
else()
add_compile_definitions(DEV_BUILD=0)
endif()
qt_standard_project_setup()
qt_add_executable(SOM
qt_add_executable(som
WIN32 MACOSX_BUNDLE
src/main.cpp
src/mainwindow.cpp
@@ -14,7 +22,11 @@ qt_add_executable(SOM
src/interface.h
)
target_link_libraries(SOM
set_target_properties(som PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out"
)
target_link_libraries(som
PRIVATE
Qt::Core
Qt::Widgets
@@ -24,14 +36,14 @@ add_subdirectory(plugins)
include(GNUInstallDirs)
install(TARGETS SOM
install(TARGETS som
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
qt_generate_deploy_app_script(
TARGET SOM
TARGET som
OUTPUT_SCRIPT deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)

41
PKGBUILD Normal file
View File

@@ -0,0 +1,41 @@
# Maintainer: Your Name <youremail@example.com>
pkgname=som
pkgver=1.0.0
pkgrel=1
pkgdesc="My Qt6 C++ CMake application"
arch=('x86_64')
url="https://example.com"
license=('GPL')
depends=('qt6-base') # add other dependencies your app needs
makedepends=('cmake' 'gcc' 'make' 'qt6-base')
source=()
build() {
cd ..
mkdir build
cd build
# Run CMake
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make
}
package() {
echo $PWD
cd ../build
echo $PWD
# Install the app binary
install -Dm755 out/som "$pkgdir/usr/bin/som"
# Install plugins
install -d "$pkgdir/usr/lib/som"
cp -r out/plugins/* "$pkgdir/usr/lib/som/"
# Install the .desktop file
install -Dm644 ../som.desktop "$pkgdir/usr/share/applications/som.desktop"
# Optionally, install an icon if you have one
# install -Dm644 ../myapp.png "$pkgdir/usr/share/icons/hicolor/128x128/apps/myapp.png"
}

View File

@@ -3,3 +3,4 @@ add_subdirectory(CalculatorPlugin)
add_subdirectory(IrcClientPlugin)
add_subdirectory(TerminalPlugin)
add_subdirectory(ScreenShotPlugin)
add_subdirectory(FileExplorerPlugin)

View File

@@ -14,6 +14,10 @@ target_sources(CalculatorPlugin PRIVATE
button.cpp button.h
)
set_target_properties(CalculatorPlugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/plugins"
)
target_link_libraries(CalculatorPlugin PRIVATE
Qt::Core
Qt::Gui

View File

@@ -332,3 +332,9 @@ void CalculatorPlugin::multiplicativeOperatorClicked()
pendingMultiplicativeOperator = clickedOperator;
waitingForOperand = true;
}
void CalculatorPlugin::connectToHost(QObject* host)
{
Q_UNUSED(host);
// Calculator plugin doesn't need host signals
}

View File

@@ -27,6 +27,8 @@ public:
QString pname() override;
QString pdesc() override;
QWidget *pcontent() override;
void connectToHost(QObject* host) override;
private:
QLineEdit *display;
enum { NumDigitButtons = 10 };

View File

@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.16)
project(FileExplorerPlugin VERSION 1.0 LANGUAGES C CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(GNUInstallDirs)
find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Widgets)
qt_add_plugin(FileExplorerPlugin)
target_sources(FileExplorerPlugin PRIVATE
FileExplorerPlugin.cpp FileExplorerPlugin.h
)
set_target_properties(FileExplorerPlugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/plugins"
)
target_link_libraries(FileExplorerPlugin PRIVATE
Qt::Core
Qt::Gui
Qt::Widgets
)
install(TARGETS FileExplorerPlugin
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

View File

@@ -0,0 +1,401 @@
#include "FileExplorerPlugin.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QInputDialog>
#include <QMessageBox>
#include <QApplication>
#include <QDialog>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QLabel>
QString FileExplorerPlugin::pname() {
return "File Explorer Plugin";
}
QString FileExplorerPlugin::pdesc() {
return "Two-pane file manager (Thunar-like) with create/edit/rename/delete.";
}
QWidget* FileExplorerPlugin::pcontent() {
// If already created, return it (plugin may call multiple times)
if (mainWidget) return mainWidget;
projectRoot = QDir::currentPath(); // default project directory; adjust if needed
mainWidget = new QWidget();
mainWidget->setWindowTitle("File Explorer Plugin");
mainWidget->resize(1000, 600);
QVBoxLayout *vLayout = new QVBoxLayout(mainWidget);
// Horizontal splitter: left = dir tree, right = file list
QSplitter *splitter = new QSplitter(Qt::Horizontal, mainWidget);
// ----------------------------
// LEFT: directory tree (dirs only)
// ----------------------------
dirModel = new QFileSystemModel(this);
dirModel->setRootPath(projectRoot);
dirModel->setFilter(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Drives);
dirTree = new QTreeView(splitter);
dirTree->setModel(dirModel);
dirTree->setRootIndex(dirModel->index(projectRoot));
dirTree->setHeaderHidden(true);
dirTree->setContextMenuPolicy(Qt::CustomContextMenu);
dirTree->setAnimated(true);
dirTree->setExpandsOnDoubleClick(true);
dirTree->setSelectionMode(QAbstractItemView::SingleSelection);
// collapse columns other than the name (column 0)
for (int c = 1; c < dirModel->columnCount(); ++c)
dirTree->setColumnHidden(c, true);
// ----------------------------
// RIGHT: files in current directory
// ----------------------------
fileModel = new QFileSystemModel(this);
fileModel->setFilter(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System);
fileModel->setRootPath(projectRoot);
fileList = new QListView(splitter);
fileList->setModel(fileModel);
fileList->setRootIndex(fileModel->index(projectRoot));
fileList->setViewMode(QListView::ListMode);
fileList->setSelectionMode(QAbstractItemView::ExtendedSelection);
fileList->setContextMenuPolicy(Qt::CustomContextMenu);
fileList->setUniformItemSizes(true);
splitter->setStretchFactor(0, 0);
splitter->setStretchFactor(1, 1);
splitter->setSizes({250, 750});
vLayout->addWidget(splitter);
// ----------------------------
// Connections
// ----------------------------
connect(dirTree, &QTreeView::clicked,
this, &FileExplorerPlugin::onDirSelected);
connect(dirTree, &QTreeView::customContextMenuRequested,
this, &FileExplorerPlugin::showDirContextMenu);
connect(fileList, &QListView::customContextMenuRequested,
this, &FileExplorerPlugin::showFileContextMenu);
connect(fileList, &QListView::activated,
this, &FileExplorerPlugin::onFileActivated);
return mainWidget;
}
// ---------- Helpers ----------
QString FileExplorerPlugin::selectedFilePath(const QModelIndex &index) const {
if (!index.isValid()) return QString();
return fileModel->filePath(index);
}
QString FileExplorerPlugin::selectedDirPath(const QModelIndex &index) const {
if (!index.isValid()) return QString();
return dirModel->filePath(index);
}
// ---------- Slot: when a directory is selected in left pane ----------
void FileExplorerPlugin::onDirSelected(const QModelIndex &index) {
QString path = selectedDirPath(index);
if (path.isEmpty()) return;
// Show contents of selected dir on the right
QModelIndex fileRoot = fileModel->setRootPath(path);
fileList->setRootIndex(fileRoot);
emit fileSelected(path);
}
// ---------- Double-click / activate on file: open editor for text files ----------
void FileExplorerPlugin::onFileActivated(const QModelIndex &index) {
QString path = selectedFilePath(index);
if (path.isEmpty()) return;
QFileInfo info(path);
if (info.isDir()) {
// navigate into directory
QModelIndex fileRoot = fileModel->setRootPath(path);
fileList->setRootIndex(fileRoot);
return;
}
// Try to open text files in simple editor; binary files will fail to load as text
//editFile(path);
}
// ---------- Context menu: right pane (files + folders) ----------
void FileExplorerPlugin::showFileContextMenu(const QPoint &pos) {
// Determine clicked index relative to the fileList root
QModelIndex clicked = fileList->indexAt(pos);
QString currentDir = fileModel->filePath(fileList->rootIndex());
QMenu menu(mainWidget);
QAction *actCreateFile = menu.addAction("Create File");
QAction *actCreateFolder = menu.addAction("Create Folder");
QAction *actEdit = nullptr;
QAction *actRename = nullptr;
QAction *actDelete = nullptr;
if (clicked.isValid()) {
actEdit = menu.addAction("Edit");
actRename = menu.addAction("Rename");
actDelete = menu.addAction("Delete");
}
QAction *selected = menu.exec(fileList->viewport()->mapToGlobal(pos));
if (!selected) return;
if (selected == actCreateFile) {
bool ok;
QString name = QInputDialog::getText(mainWidget, "Create File",
"File name:", QLineEdit::Normal,
QString(), &ok);
if (!ok || name.isEmpty()) return;
QString newPath = QDir(currentDir).filePath(name);
QFile f(newPath);
if (!f.open(QIODevice::WriteOnly)) {
QMessageBox::warning(mainWidget, "Error", "Unable to create file:\n" + newPath);
return;
}
f.close();
// refresh model
fileModel->setRootPath(currentDir);
fileList->setRootIndex(fileModel->index(currentDir));
return;
}
if (selected == actCreateFolder) {
bool ok;
QString name = QInputDialog::getText(mainWidget, "Create Folder",
"Folder name:", QLineEdit::Normal,
QString(), &ok);
if (!ok || name.isEmpty()) return;
QDir dir(currentDir);
if (!dir.mkdir(name)) {
QMessageBox::warning(mainWidget, "Error", "Unable to create folder:\n" + dir.filePath(name));
return;
}
fileModel->setRootPath(currentDir);
fileList->setRootIndex(fileModel->index(currentDir));
return;
}
if (!clicked.isValid()) return;
QString path = selectedFilePath(clicked);
QFileInfo info(path);
if (selected == actEdit) {
if (info.isDir()) {
// nothing to edit
QMessageBox::information(mainWidget, "Edit", "Cannot edit a directory.");
return;
}
editFile(path);
return;
}
if (selected == actRename) {
bool ok;
QString newName = QInputDialog::getText(mainWidget, "Rename",
"New name:", QLineEdit::Normal,
fileModel->fileName(clicked), &ok);
if (!ok || newName.isEmpty()) return;
QString newPath = info.dir().filePath(newName);
if (!QFile::rename(path, newPath)) {
// try QDir::rename for directories
if (info.isDir() && !QDir().rename(path, newPath)) {
QMessageBox::warning(mainWidget, "Error", "Rename failed.");
} else if (!info.isDir()) {
QMessageBox::warning(mainWidget, "Error", "Rename failed.");
}
}
// refresh
QString currentDirPath = fileModel->filePath(fileList->rootIndex());
fileModel->setRootPath(currentDirPath);
fileList->setRootIndex(fileModel->index(currentDirPath));
return;
}
if (selected == actDelete) {
if (QMessageBox::question(mainWidget, "Delete",
"Delete selected item(s)?",
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
!= QMessageBox::Yes) {
return;
}
bool okAny = true;
// support multi-selection
QModelIndexList sel = fileList->selectionModel()->selectedIndexes();
if (sel.isEmpty()) sel = { clicked };
for (const QModelIndex &mi : sel) {
QString p = fileModel->filePath(mi);
QFileInfo fi(p);
bool success = fi.isDir() ? QDir(p).removeRecursively() : QFile::remove(p);
if (!success) okAny = false;
}
if (!okAny) QMessageBox::warning(mainWidget, "Error", "One or more items could not be deleted.");
QString currentDirPath = fileModel->filePath(fileList->rootIndex());
fileModel->setRootPath(currentDirPath);
fileList->setRootIndex(fileModel->index(currentDirPath));
return;
}
}
// ---------- Context menu: left pane (directory tree) ----------
void FileExplorerPlugin::showDirContextMenu(const QPoint &pos) {
QModelIndex idx = dirTree->indexAt(pos);
if (!idx.isValid()) return;
QString path = selectedDirPath(idx);
if (path.isEmpty()) return;
QMenu menu(mainWidget);
QAction *actSetAsProjectRoot = menu.addAction("Set as Project Root");
QAction *actCreateFolder = menu.addAction("Create Folder");
QAction *actRename = menu.addAction("Rename");
QAction *actDelete = menu.addAction("Delete");
QAction *selected = menu.exec(dirTree->viewport()->mapToGlobal(pos));
if (!selected) return;
if (selected == actSetAsProjectRoot) {
projectRoot = path;
// re-root dirModel and fileModel
dirTree->setRootIndex(dirModel->setRootPath(projectRoot));
fileList->setRootIndex(fileModel->setRootPath(projectRoot));
return;
}
if (selected == actCreateFolder) {
bool ok;
QString name = QInputDialog::getText(mainWidget, "Create Folder",
"Folder name:", QLineEdit::Normal,
QString(), &ok);
if (!ok || name.isEmpty()) return;
QDir d(path);
if (!d.mkdir(name)) {
QMessageBox::warning(mainWidget, "Error", "Could not create folder.");
}
// refresh view
dirModel->setRootPath(projectRoot);
dirTree->setRootIndex(dirModel->index(projectRoot));
return;
}
if (selected == actRename) {
bool ok;
QString newName = QInputDialog::getText(mainWidget, "Rename Folder",
"New name:", QLineEdit::Normal,
dirModel->fileName(idx), &ok);
if (!ok || newName.isEmpty()) return;
QFileInfo info(path);
QString newPath = info.dir().filePath(newName);
if (!QDir().rename(path, newPath)) {
QMessageBox::warning(mainWidget, "Error", "Rename failed.");
}
dirModel->setRootPath(projectRoot);
dirTree->setRootIndex(dirModel->index(projectRoot));
return;
}
if (selected == actDelete) {
if (QMessageBox::question(mainWidget, "Delete Folder",
"Delete this folder and its contents?",
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
!= QMessageBox::Yes) {
return;
}
if (!QDir(path).removeRecursively()) {
QMessageBox::warning(mainWidget, "Error", "Delete failed.");
} else {
dirModel->setRootPath(projectRoot);
dirTree->setRootIndex(dirModel->index(projectRoot));
// if deleted path == current fileList root, move fileList to projectRoot
QString currentFileRoot = fileModel->filePath(fileList->rootIndex());
if (currentFileRoot.startsWith(path)) {
fileList->setRootIndex(fileModel->index(projectRoot));
}
}
return;
}
}
// ---------- Simple text editor dialog for editing files ----------
void FileExplorerPlugin::editFile(const QString &path) {
QFileInfo fi(path);
if (!fi.exists() || !fi.isFile()) {
QMessageBox::warning(mainWidget, "Edit", "File does not exist or is not a regular file.");
return;
}
QFile f(path);
if (!f.open(QIODevice::ReadOnly)) {
QMessageBox::warning(mainWidget, "Edit", "Unable to open file for reading.");
return;
}
QByteArray data = f.readAll();
f.close();
// Create modal dialog with QTextEdit
QDialog dlg(mainWidget);
dlg.setWindowTitle(QString("Edit: %1").arg(fi.fileName()));
dlg.resize(800, 600);
QVBoxLayout *lay = new QVBoxLayout(&dlg);
QLabel *info = new QLabel(QString("Path: %1").arg(path), &dlg);
lay->addWidget(info);
QTextEdit *editor = new QTextEdit(&dlg);
// attempt to interpret as UTF-8; binary files will look garbled
editor->setPlainText(QString::fromUtf8(data));
lay->addWidget(editor);
QDialogButtonBox *btns = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel, &dlg);
lay->addWidget(btns);
connect(btns, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(btns, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
if (dlg.exec() == QDialog::Accepted) {
QString text = editor->toPlainText();
QFile out(path);
if (!out.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
QMessageBox::warning(mainWidget, "Save", "Unable to open file for writing.");
return;
}
QByteArray outData = text.toUtf8();
qint64 written = out.write(outData);
out.close();
if (written != outData.size()) {
QMessageBox::warning(mainWidget, "Save", "Not all data could be written.");
}
// refresh
QString cur = fileModel->filePath(fileList->rootIndex());
fileModel->setRootPath(cur);
fileList->setRootIndex(fileModel->index(cur));
}
}
void FileExplorerPlugin::connectToHost(QObject* host)
{
Q_UNUSED(host);
// Calculator plugin doesn't need host signals
}

View File

@@ -0,0 +1,61 @@
#ifndef TESTPLUGIN_H
#define TESTPLUGIN_H
#include <../../src/interface.h>
#include <QObject>
#include <QtPlugin>
#include <QString>
#include <QWidget>
#include <QFileSystemModel>
#include <QTreeView>
#include <QListView>
#include <QSplitter>
#include <QMenu>
#include <QTextEdit>
class FileExplorerPlugin : public QObject, public Interface {
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.SOM.Interface" FILE "FileExplorerPlugin.json")
Q_INTERFACES(Interface)
public:
QString pname() override;
QString pdesc() override;
QWidget *pcontent() override;
void connectToHost(QObject* host) override;
private slots:
void onDirSelected(const QModelIndex &index);
void onFileActivated(const QModelIndex &index);
void showFileContextMenu(const QPoint &pos);
void showDirContextMenu(const QPoint &pos);
private:
QWidget *mainWidget = nullptr;
// Models + views
QFileSystemModel *dirModel = nullptr; // directories only, left pane
QTreeView *dirTree = nullptr;
QFileSystemModel *fileModel = nullptr; // files+dirs, right pane
QListView *fileList = nullptr;
// Project root (left pane root). Default: application current path
QString projectRoot;
// Helpers
QString selectedFilePath(const QModelIndex &index) const;
QString selectedDirPath(const QModelIndex &index) const;
void createFileInCurrentRoot();
void createFolderInCurrentRoot();
void editFile(const QString &path);
bool removePath(const QString &path);
bool renamePath(const QString &oldPath, const QString &newName);
signals:
void fileSelected(const QString& path);
};
#endif

View File

@@ -0,0 +1 @@
{}

View File

@@ -23,6 +23,10 @@ target_sources(IrcClientPlugin PRIVATE
topichandler.cpp topichandler.h
)
set_target_properties(IrcClientPlugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/plugins"
)
target_link_libraries(IrcClientPlugin PRIVATE
Qt::Core
Qt::Gui

View File

@@ -267,3 +267,9 @@ void IrcClientPlugin::append(const QString &line)
{
msgs->append(line);
}
void IrcClientPlugin::connectToHost(QObject* host)
{
Q_UNUSED(host);
// Calculator plugin doesn't need host signals
}

View File

@@ -30,6 +30,8 @@ public:
QString pname() override;
QString pdesc() override;
QWidget *pcontent() override;
void connectToHost(QObject* host) override;
private:
void append(const QString& line);

View File

@@ -13,6 +13,10 @@ target_sources(ScreenShotPlugin PRIVATE
ScreenShotPlugin.cpp ScreenShotPlugin.h
)
set_target_properties(ScreenShotPlugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/plugins"
)
target_link_libraries(ScreenShotPlugin PRIVATE
Qt::Core
Qt::Gui

View File

@@ -155,3 +155,9 @@ void ScreenShotPlugin::updateceheck()
hideThisWindowCheckBox->setDisabled(false);
}
}
void ScreenShotPlugin::connectToHost(QObject* host)
{
Q_UNUSED(host);
// Calculator plugin doesn't need host signals
}

View File

@@ -27,6 +27,8 @@ public:
QString pname() override;
QString pdesc() override;
QWidget *pcontent() override ;
void connectToHost(QObject* host) override;
public slots:
void updateceheck();

View File

@@ -13,6 +13,10 @@ target_sources(TerminalPlugin PRIVATE
TerminalPlugin.cpp TerminalPlugin.h
)
set_target_properties(TerminalPlugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/plugins"
)
target_link_libraries(TerminalPlugin PRIVATE
Qt::Core
Qt::Gui

View File

@@ -2,7 +2,8 @@
#include <QVBoxLayout>
#include <QWidget>
#include <QApplication>
#include <QMenu>
#include <QAction>
QString TerminalPlugin::pname()
{
@@ -18,21 +19,67 @@ QWidget* TerminalPlugin::pcontent()
{
QWidget *test = new QWidget();
QVBoxLayout *mainLayout = new QVBoxLayout;
chdir(path.toUtf8().constData());
console = new QTermWidget(test);
QFont font = QApplication::font();
font.setFamily("Monospace");
font.setPointSize(12);
console->setTerminalFont(font);
console->setScrollBarPosition(QTermWidget::ScrollBarRight);
console->setColorScheme("WhiteOnBlack");
console->setContextMenuPolicy(Qt::CustomContextMenu);
connect(console, &QWidget::customContextMenuRequested, this, [=](const QPoint &pos) {
QMenu menu;
QAction *copyAct = new QAction(tr("Copy"), &menu);
QAction *pasteAct = new QAction(tr("Paste"), &menu);
if (console->selectedText().isEmpty()) {
copyAct->setEnabled(false);
}
connect(copyAct, &QAction::triggered, [=]() {
console->copyClipboard();
});
connect(pasteAct, &QAction::triggered, [=]() {
console->pasteClipboard();
});
menu.addAction(copyAct);
menu.addAction(pasteAct);
QPoint globalPos = console->mapToGlobal(pos);
menu.exec(globalPos);
});
test->setWindowTitle(tr("Terminal Plugin"));
test->resize(600, 400);
mainLayout->addWidget(console);
test->setLayout(mainLayout);
QIcon icon = QIcon(":/images/monkey.png");
test->setWindowIcon(icon);
return test;
}
void TerminalPlugin::connectToHost(QObject* host)
{
connect(host, SIGNAL(currentPathChanged(QString)),
this, SLOT(onHostPathChanged(QString)));
}
void TerminalPlugin::onHostPathChanged(const QString &newpath)
{
path = newpath;
QMessageBox::about(pcontent(), tr("About Terminal"),
tr(newpath.toUtf8().constData()));
}

View File

@@ -4,12 +4,14 @@
#include <../../src/interface.h>
#include <qtermwidget6/qtermwidget.h>
#include <unistd.h>
#include <QObject>
#include <QtPlugin>
#include <QString>
#include <QWidget>
#include <QImage>
#include <QMessageBox>
class TerminalPlugin : public QObject, public Interface
{
@@ -21,9 +23,15 @@ public:
QString pname() override;
QString pdesc() override;
QWidget *pcontent() override;
void connectToHost(QObject* host) override;
private:
QTermWidget *console;
QString path;
private slots:
void onHostPathChanged(const QString &newpath);
};
#endif

View File

@@ -13,6 +13,10 @@ target_sources(WebBrowserPlugin PRIVATE
WebBrowserPlugin.cpp WebBrowserPlugin.h
)
set_target_properties(WebBrowserPlugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/out/plugins"
)
target_link_libraries(WebBrowserPlugin PRIVATE
Qt::Core
Qt::Gui

View File

@@ -76,3 +76,9 @@ void WebBrowserPlugin::forwardpage()
{
view->forward();
}
void WebBrowserPlugin::connectToHost(QObject* host)
{
Q_UNUSED(host);
// Calculator plugin doesn't need host signals
}

View File

@@ -16,6 +16,8 @@ public:
QString pname() override;
QString pdesc() override;
QWidget *pcontent() override;
void connectToHost(QObject* host) override;
private:
QGroupBox *horizontalGroupBox;

8
som.desktop Normal file
View File

@@ -0,0 +1,8 @@
[Desktop Entry]
Name=SOM
Comment=Description of My Qt6 App
Exec=/usr/bin/som
Icon=myapp
Terminal=false
Type=Application
Categories=Utility;Development;

View File

@@ -63,21 +63,24 @@ void MainWindow::about()
void MainWindow::initSettings()
{
QSettings settings("SOM", "SOM");
QSettings settings("som", "som");
if (!settings.contains("window/geometry")) {
this->resize(QSize(800, 600));
this->move(this->screen()->geometry().center() - this->frameGeometry().center());
settings.setValue("window/geometry", this->saveGeometry());
projdir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/som-projects";
settings.setValue("window/projdir", this->projdir);
settings.sync();
}
this->restoreGeometry(settings.value("window/geometry").toByteArray());
this->projdir = settings.value("window/projdir").toString();
settings.beginGroup("Plugins");
for (const QString &group : settings.childGroups()) {
settings.beginGroup(group);
QString pluginName = group.section('_', 0, 0);
QString pluginName = group.split("_").first();
pluginName = QUrl::fromPercentEncoding(pluginName.toUtf8());
Interface *iPlugin = nullptr;
@@ -92,9 +95,11 @@ void MainWindow::initSettings()
if (iPlugin) {
QWidget *pluginWidget = iPlugin->pcontent();
QString uniqueId = QUuid::createUuid().toString();
if (pluginWidget) {
QMdiSubWindow *sub = mdi->addSubWindow(pluginWidget);
sub->setProperty("uniqueId", group);
sub->setProperty("name", iPlugin->pname());
sub->setProperty("uniqueId", uniqueId);
sub->restoreGeometry(settings.value("geometry").toByteArray());
sub->setAttribute(Qt::WA_DeleteOnClose);
sub->show();
@@ -109,7 +114,7 @@ void MainWindow::initSettings()
void MainWindow::saveSettings()
{
QSettings settings("SOM", "SOM");
QSettings settings("som", "som");
settings.setValue("window/geometry", this->saveGeometry());
settings.beginGroup("Plugins");
@@ -119,7 +124,7 @@ void MainWindow::saveSettings()
settings.beginGroup("Plugins");
QList<QMdiSubWindow*> subWindows = mdi->subWindowList();
for (QMdiSubWindow* sub : subWindows) {
QString id = sub->windowTitle() + "_" + sub->property("uniqueId").toString();
QString id = sub->property("name").toString() + "_" + sub->property("uniqueId").toString();
settings.beginGroup(id);
settings.setValue("geometry", sub->saveGeometry());
settings.endGroup();
@@ -139,14 +144,50 @@ void MainWindow::settings()
QWidget *settingsWin = new QWidget();
settingsWin->setWindowTitle("Settings");
settingsWin->resize(300, 200);
QVBoxLayout *layout = new QVBoxLayout(settingsWin);
QLabel *dirLabel = new QLabel(projdir, settingsWin);
layout->addWidget(dirLabel);
QPushButton *browseButton = new QPushButton("Browse...", settingsWin);
layout->addWidget(browseButton);
// Connect the button to open the directory dialog
connect(browseButton, &QPushButton::clicked, this,
[this, dirLabel, settingsWin]() {
QString selectedDir = QFileDialog::getExistingDirectory(
this,
tr("Select Directory"),
projdir,
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
);
if (!selectedDir.isEmpty()) {
dirLabel->setText(selectedDir);
QSettings settings("som", "som");
settings.setValue("window/projdir", selectedDir);
settingsWin->close(); // Close the settings window
}
});
settingsWin->setLayout(layout);
settingsWin->show();
}
void MainWindow::loadPlugins()
{
#if DEV_BUILD
pluginsDir = QDir(QCoreApplication::applicationDirPath());
pluginsDir.cd("plugins");
#else
pluginsDir = QDir("/usr/lib");
pluginsDir.cd("som");
#endif
const auto entryList = pluginsDir.entryList(QDir::Files);
for (const QString &fileName : entryList) {
@@ -154,6 +195,15 @@ void MainWindow::loadPlugins()
QObject *plugin = loader.instance();
if (plugin) {
auto iPlugin = qobject_cast<Interface*>(plugin);
if (iPlugin) {
iPlugin->connectToHost(this);
// *** NEW: connect plugin.signal → MainWindow.slot ***
connect(plugin, SIGNAL(fileSelected(QString)),
this, SLOT(fileSelected(QString)));
}
populateMenu(plugin);
}
}
@@ -174,6 +224,7 @@ void MainWindow::changePlugin()
QWidget* pluginContent = iPlugin->pcontent();
auto *sub = mdi->addSubWindow(pluginContent);
sub->setProperty("uniqueId", uniqueId);
sub->setProperty("name", iPlugin->pname());
sub->setAttribute(Qt::WA_DeleteOnClose);
sub->show();
}
@@ -194,3 +245,10 @@ void MainWindow::addToMenu(QObject *plugin, const QString &text,
connect(action, &QAction::triggered, this, member);
menu->addAction(action);
}
void MainWindow::fileSelected(const QString &path)
{
emit currentPathChanged(path);
QMessageBox::about(this, tr("About SOM"),
tr(path.toUtf8().constData()));
}

View File

@@ -43,14 +43,22 @@ private:
QAction *settingsAct;
QDir pluginsDir;
QMdiArea *mdi;
QString projdir;
private slots:
void about();
void settings();
void changePlugin();
public slots:
void fileSelected(const QString& path);
protected:
void closeEvent(QCloseEvent *event) override;
signals:
void currentPathChanged(const QString& path);
};
#endif // MAINWINDOW_H