main
initial git repo
This commit is contained in:
commit
6e1e41799e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.vscode
|
||||
build
|
||||
23
CMakeLists.txt
Normal file
23
CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(project-launcher)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK4 REQUIRED gtk4)
|
||||
find_package(nlohmann_json 3.2.0 REQUIRED)
|
||||
|
||||
include_directories(${GTK4_INCLUDE_DIRS})
|
||||
link_directories(${GTK4_LIBRARY_DIRS})
|
||||
|
||||
add_executable(project-launcher
|
||||
src/main.cpp
|
||||
src/project_manager.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(project-launcher
|
||||
PRIVATE
|
||||
${GTK4_LIBRARIES}
|
||||
nlohmann_json::nlohmann_json
|
||||
)
|
||||
103
resources/style.css
Normal file
103
resources/style.css
Normal file
@ -0,0 +1,103 @@
|
||||
/* * {
|
||||
background: red;
|
||||
} */
|
||||
|
||||
window {
|
||||
background-color: #181825;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
box {
|
||||
background: #181825;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.project-path {
|
||||
font-size: 12px;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.circular {
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
background: alpha(currentColor, 0.1);
|
||||
}
|
||||
|
||||
.circular:hover {
|
||||
background: alpha(currentColor, 0.2);
|
||||
}
|
||||
|
||||
listboxrow {
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
margin: 2px 0;
|
||||
background: #2d2d2d;
|
||||
}
|
||||
|
||||
listboxrow:hover {
|
||||
background: #3d3d3d;
|
||||
}
|
||||
|
||||
/* listboxrow box {
|
||||
background: transparent;
|
||||
} */
|
||||
|
||||
entry {
|
||||
background: #2d2d2d;
|
||||
border: 1px solid #3d3d3d;
|
||||
border-radius: 6px;
|
||||
color: #ffffff;
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #2d2d2d;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #ffffff;
|
||||
padding: 8px 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #3d3d3d;
|
||||
}
|
||||
|
||||
button.suggested-action {
|
||||
background: #2864b4;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
button.suggested-action:hover {
|
||||
background: #3275c5;
|
||||
}
|
||||
|
||||
button.destructive-action {
|
||||
background: #c01c28;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
button.destructive-action:hover {
|
||||
background: #d1293a;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
background-color: #181825;
|
||||
}
|
||||
|
||||
.list-box row {
|
||||
background-color: #181825;
|
||||
border-radius: 6px;
|
||||
margin: 8px;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.list-box row:selected {
|
||||
outline: #2864b4 solid 2px;
|
||||
}
|
||||
375
src/main.cpp
Normal file
375
src/main.cpp
Normal file
@ -0,0 +1,375 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include "project_manager.hpp"
|
||||
#include <memory>
|
||||
|
||||
struct AppData
|
||||
{
|
||||
ProjectManager *project_manager;
|
||||
GtkListBox *project_list;
|
||||
GtkWindow *main_window;
|
||||
GtkApplication *app;
|
||||
};
|
||||
|
||||
static GtkWidget *create_window(const char *title, GtkWindow *parent);
|
||||
|
||||
static void update_project_list(AppData *data);
|
||||
|
||||
static void on_project_activated(GtkListBox *box, GtkListBoxRow *row, AppData *data)
|
||||
{
|
||||
int index = gtk_list_box_row_get_index(row);
|
||||
|
||||
// Открываем проект
|
||||
if (data->project_manager->openProject(index))
|
||||
{
|
||||
// Добавляем небольшую задержку перед закрытием
|
||||
g_timeout_add(100, [](gpointer user_data) -> gboolean {
|
||||
auto *app = static_cast<GtkApplication*>(user_data);
|
||||
g_application_quit(G_APPLICATION(app));
|
||||
return G_SOURCE_REMOVE;
|
||||
}, data->app);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_dialog_response(GtkButton *button, AppData *data)
|
||||
{
|
||||
GtkWidget *dialog = gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_WINDOW);
|
||||
GtkWidget *main_box = gtk_window_get_child(GTK_WINDOW(dialog));
|
||||
GtkWidget *name_entry = gtk_widget_get_first_child(main_box);
|
||||
GtkWidget *path_entry = gtk_widget_get_next_sibling(name_entry);
|
||||
|
||||
const char *name = gtk_editable_get_text(GTK_EDITABLE(name_entry));
|
||||
const char *path = gtk_editable_get_text(GTK_EDITABLE(path_entry));
|
||||
|
||||
if (strlen(name) > 0 && strlen(path) > 0)
|
||||
{
|
||||
data->project_manager->addProject(name, path);
|
||||
update_project_list(data);
|
||||
}
|
||||
|
||||
gtk_window_destroy(GTK_WINDOW(dialog));
|
||||
}
|
||||
|
||||
static void on_add_clicked(GtkButton *button, AppData *data)
|
||||
{
|
||||
// Создаем диалог
|
||||
GtkWidget *dialog = create_window("Добавить проект", data->main_window);
|
||||
|
||||
// Создаем основной контейнер
|
||||
GtkWidget *main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
||||
gtk_window_set_child(GTK_WINDOW(dialog), main_box);
|
||||
|
||||
// Добавляем отступы
|
||||
gtk_widget_set_margin_start(main_box, 10);
|
||||
gtk_widget_set_margin_end(main_box, 10);
|
||||
gtk_widget_set_margin_top(main_box, 10);
|
||||
gtk_widget_set_margin_bottom(main_box, 10);
|
||||
|
||||
// Создаем поля ввода
|
||||
GtkWidget *name_entry = gtk_entry_new();
|
||||
gtk_entry_set_placeholder_text(GTK_ENTRY(name_entry), "Название проекта");
|
||||
gtk_box_append(GTK_BOX(main_box), name_entry);
|
||||
|
||||
GtkWidget *path_entry = gtk_entry_new();
|
||||
gtk_entry_set_placeholder_text(GTK_ENTRY(path_entry), "Путь к проекту");
|
||||
gtk_box_append(GTK_BOX(main_box), path_entry);
|
||||
|
||||
// Создаем контейнер для кнопок
|
||||
GtkWidget *button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
|
||||
gtk_widget_set_halign(button_box, GTK_ALIGN_END);
|
||||
gtk_box_append(GTK_BOX(main_box), button_box);
|
||||
|
||||
// Добавляем кнопки
|
||||
GtkWidget *cancel_button = gtk_button_new_with_label("Отмена");
|
||||
GtkWidget *accept_button = gtk_button_new_with_label("Добавить");
|
||||
gtk_box_append(GTK_BOX(button_box), cancel_button);
|
||||
gtk_box_append(GTK_BOX(button_box), accept_button);
|
||||
|
||||
// Подключаем сигналы для кнопок
|
||||
g_signal_connect_swapped(cancel_button, "clicked", G_CALLBACK(gtk_window_destroy), dialog);
|
||||
g_signal_connect(accept_button, "clicked", G_CALLBACK(on_dialog_response), data);
|
||||
|
||||
// Показываем диалог
|
||||
gtk_window_present(GTK_WINDOW(dialog));
|
||||
}
|
||||
|
||||
static void on_delete_clicked(GtkButton *button, AppData *data)
|
||||
{
|
||||
GtkWidget *row = gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_LIST_BOX_ROW);
|
||||
int index = gtk_list_box_row_get_index(GTK_LIST_BOX_ROW(row));
|
||||
data->project_manager->deleteProject(index);
|
||||
update_project_list(data);
|
||||
}
|
||||
|
||||
static void on_save_clicked(GtkButton *button, gpointer user_data)
|
||||
{
|
||||
auto *data = static_cast<std::pair<AppData *, int> *>(user_data);
|
||||
GtkWidget *dialog = gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_WINDOW);
|
||||
GtkWidget *box = gtk_window_get_child(GTK_WINDOW(dialog));
|
||||
GtkWidget *name_entry = gtk_widget_get_first_child(box);
|
||||
GtkWidget *path_entry = gtk_widget_get_next_sibling(name_entry);
|
||||
|
||||
const char *name = gtk_editable_get_text(GTK_EDITABLE(name_entry));
|
||||
const char *path = gtk_editable_get_text(GTK_EDITABLE(path_entry));
|
||||
|
||||
if (strlen(name) > 0 && strlen(path) > 0)
|
||||
{
|
||||
data->first->project_manager->editProject(data->second, name, path);
|
||||
update_project_list(data->first);
|
||||
}
|
||||
gtk_window_destroy(GTK_WINDOW(dialog));
|
||||
delete data;
|
||||
}
|
||||
|
||||
static void on_edit_clicked(GtkButton *button, AppData *data)
|
||||
{
|
||||
GtkWidget *row = gtk_widget_get_ancestor(GTK_WIDGET(button), GTK_TYPE_LIST_BOX_ROW);
|
||||
int index = gtk_list_box_row_get_index(GTK_LIST_BOX_ROW(row));
|
||||
const auto &projects = data->project_manager->getProjects();
|
||||
const auto &project = projects[index];
|
||||
|
||||
// Создаем диалог
|
||||
GtkWidget *dialog = create_window("Редактировать проект", data->main_window);
|
||||
|
||||
// Создаем основной контейнер
|
||||
GtkWidget *main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
||||
gtk_window_set_child(GTK_WINDOW(dialog), main_box);
|
||||
gtk_widget_set_margin_start(main_box, 10);
|
||||
gtk_widget_set_margin_end(main_box, 10);
|
||||
gtk_widget_set_margin_top(main_box, 10);
|
||||
gtk_widget_set_margin_bottom(main_box, 10);
|
||||
|
||||
// Создаем поля ввода
|
||||
GtkWidget *name_entry = gtk_entry_new();
|
||||
gtk_editable_set_text(GTK_EDITABLE(name_entry), project.name.c_str());
|
||||
gtk_box_append(GTK_BOX(main_box), name_entry);
|
||||
|
||||
GtkWidget *path_entry = gtk_entry_new();
|
||||
gtk_editable_set_text(GTK_EDITABLE(path_entry), project.path.c_str());
|
||||
gtk_box_append(GTK_BOX(main_box), path_entry);
|
||||
|
||||
// Создаем контейнер для кнопок
|
||||
GtkWidget *button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
|
||||
gtk_widget_set_halign(button_box, GTK_ALIGN_END);
|
||||
gtk_box_append(GTK_BOX(main_box), button_box);
|
||||
|
||||
// Добавляем кнопки
|
||||
GtkWidget *cancel_button = gtk_button_new_with_label("Отмена");
|
||||
GtkWidget *save_button = gtk_button_new_with_label("Сохранить");
|
||||
gtk_widget_add_css_class(save_button, "suggested-action");
|
||||
gtk_box_append(GTK_BOX(button_box), cancel_button);
|
||||
gtk_box_append(GTK_BOX(button_box), save_button);
|
||||
|
||||
// Подключаем сигналы
|
||||
g_signal_connect_swapped(cancel_button, "clicked", G_CALLBACK(gtk_window_destroy), dialog);
|
||||
auto *user_data = new std::pair<AppData *, int>(data, index);
|
||||
g_signal_connect(save_button, "clicked", G_CALLBACK(on_save_clicked), user_data);
|
||||
|
||||
gtk_window_present(GTK_WINDOW(dialog));
|
||||
}
|
||||
|
||||
static void on_search_changed(GtkSearchEntry *entry, AppData *data)
|
||||
{
|
||||
gtk_list_box_invalidate_filter(data->project_list);
|
||||
}
|
||||
|
||||
static void update_project_list(AppData *data)
|
||||
{
|
||||
// Очищаем текущий список
|
||||
GtkWidget *child;
|
||||
while ((child = gtk_widget_get_first_child(GTK_WIDGET(data->project_list))) != NULL)
|
||||
{
|
||||
gtk_list_box_remove(data->project_list, GTK_WIDGET(child));
|
||||
}
|
||||
|
||||
// Добавляем проекты
|
||||
for (const auto &project : data->project_manager->getProjects())
|
||||
{
|
||||
// Создаем основной контейнер для элемента списка
|
||||
GtkWidget *row_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
|
||||
gtk_widget_set_margin_start(row_box, 10);
|
||||
gtk_widget_set_margin_end(row_box, 10);
|
||||
gtk_widget_set_margin_top(row_box, 5);
|
||||
gtk_widget_set_margin_bottom(row_box, 5);
|
||||
|
||||
// Создаем контейнер для информации о проекте
|
||||
GtkWidget *info_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
|
||||
gtk_widget_set_hexpand(info_box, TRUE);
|
||||
gtk_box_append(GTK_BOX(row_box), info_box);
|
||||
|
||||
// Добавляем название проекта
|
||||
GtkWidget *name_label = gtk_label_new(project.name.c_str());
|
||||
gtk_widget_add_css_class(name_label, "project-name");
|
||||
gtk_label_set_xalign(GTK_LABEL(name_label), 0);
|
||||
gtk_box_append(GTK_BOX(info_box), name_label);
|
||||
|
||||
// Добавляем путь проекта
|
||||
GtkWidget *path_label = gtk_label_new(project.path.c_str());
|
||||
gtk_widget_add_css_class(path_label, "project-path");
|
||||
gtk_label_set_xalign(GTK_LABEL(path_label), 0);
|
||||
gtk_widget_set_opacity(path_label, 0.7);
|
||||
gtk_box_append(GTK_BOX(info_box), path_label);
|
||||
|
||||
// Добавляем кнопки действий
|
||||
GtkWidget *button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
|
||||
gtk_box_append(GTK_BOX(row_box), button_box);
|
||||
|
||||
// Кнопка редактирования
|
||||
GtkWidget *edit_button = gtk_button_new_from_icon_name("document-edit-symbolic");
|
||||
gtk_widget_add_css_class(edit_button, "circular");
|
||||
gtk_box_append(GTK_BOX(button_box), edit_button);
|
||||
g_signal_connect(edit_button, "clicked", G_CALLBACK(on_edit_clicked), data);
|
||||
|
||||
// Кнопка удаления
|
||||
GtkWidget *delete_button = gtk_button_new_from_icon_name("user-trash-symbolic");
|
||||
gtk_widget_add_css_class(delete_button, "circular");
|
||||
gtk_box_append(GTK_BOX(button_box), delete_button);
|
||||
g_signal_connect(delete_button, "clicked", G_CALLBACK(on_delete_clicked), data);
|
||||
|
||||
gtk_list_box_append(data->project_list, row_box);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean destroy_on_key_pressed(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, gpointer user_data)
|
||||
{
|
||||
if (keyval == GDK_KEY_Escape)
|
||||
{
|
||||
GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(controller));
|
||||
GtkWindow *window = GTK_WINDOW(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW));
|
||||
gtk_window_destroy(window);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void activate(GtkApplication *app, gpointer user_data)
|
||||
{
|
||||
auto *data = new AppData();
|
||||
data->project_manager = new ProjectManager();
|
||||
data->app = app;
|
||||
|
||||
// Загружаем CSS
|
||||
GtkCssProvider *provider = gtk_css_provider_new();
|
||||
const char *css_path = g_build_filename(g_get_home_dir(), ".config/project-launcher/style.css", NULL);
|
||||
gtk_css_provider_load_from_path(provider, css_path);
|
||||
gtk_style_context_add_provider_for_display(
|
||||
gdk_display_get_default(),
|
||||
GTK_STYLE_PROVIDER(provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
|
||||
// Создаем главное окно через нашу функцию
|
||||
GtkWidget *window = create_window("Менеджер проектов", nullptr);
|
||||
gtk_window_set_application(GTK_WINDOW(window), app); // Важно для главного окна!
|
||||
data->main_window = GTK_WINDOW(window);
|
||||
gtk_window_set_default_size(GTK_WINDOW(window), 1200, 800);
|
||||
|
||||
// Создаем основной контейнер
|
||||
GtkWidget *main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
|
||||
gtk_widget_set_margin_start(main_box, 15);
|
||||
gtk_widget_set_margin_end(main_box, 15);
|
||||
gtk_widget_set_margin_top(main_box, 15);
|
||||
gtk_widget_set_margin_bottom(main_box, 15);
|
||||
gtk_window_set_child(GTK_WINDOW(window), main_box);
|
||||
|
||||
// Создаем верхнюю панель
|
||||
GtkWidget *header_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
|
||||
gtk_box_append(GTK_BOX(main_box), header_box);
|
||||
|
||||
// Добавляем поле поиска
|
||||
GtkWidget *search_entry = gtk_search_entry_new();
|
||||
gtk_widget_set_hexpand(search_entry, TRUE);
|
||||
gtk_box_append(GTK_BOX(header_box), search_entry);
|
||||
|
||||
// Создаем кнопку добавления
|
||||
GtkWidget *add_button = gtk_button_new_with_label("Добавить проект");
|
||||
gtk_widget_add_css_class(add_button, "suggested-action");
|
||||
gtk_box_append(GTK_BOX(header_box), add_button);
|
||||
g_signal_connect(add_button, "clicked", G_CALLBACK(on_add_clicked), data);
|
||||
|
||||
// Создаем список проектов
|
||||
GtkWidget *scrolled = gtk_scrolled_window_new();
|
||||
gtk_widget_set_vexpand(scrolled, TRUE);
|
||||
gtk_widget_add_css_class(scrolled, "projects-container");
|
||||
gtk_box_append(GTK_BOX(main_box), scrolled);
|
||||
|
||||
GtkWidget *list_box = gtk_list_box_new();
|
||||
data->project_list = GTK_LIST_BOX(list_box);
|
||||
gtk_widget_add_css_class(list_box, "list-box");
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled), list_box);
|
||||
|
||||
// Функция фильтрации
|
||||
auto filter_func = [](GtkListBoxRow *row, gpointer data) -> gboolean
|
||||
{
|
||||
const char *search_text = gtk_editable_get_text(GTK_EDITABLE(data));
|
||||
if (strlen(search_text) == 0)
|
||||
return TRUE;
|
||||
|
||||
GtkWidget *box = gtk_list_box_row_get_child(row);
|
||||
GtkWidget *info_box = gtk_widget_get_first_child(box);
|
||||
GtkWidget *name_label = gtk_widget_get_first_child(info_box);
|
||||
|
||||
const char *name = gtk_label_get_text(GTK_LABEL(name_label));
|
||||
|
||||
// Преобразуем строки в нижний регистр для регистронезависимого поиска
|
||||
char *name_lower = g_utf8_strdown(name, -1);
|
||||
char *search_lower = g_utf8_strdown(search_text, -1);
|
||||
|
||||
gboolean found = strstr(name_lower, search_lower) != NULL;
|
||||
|
||||
g_free(name_lower);
|
||||
g_free(search_lower);
|
||||
|
||||
return found;
|
||||
};
|
||||
|
||||
// Устанавливаем функцию фильтрации
|
||||
gtk_list_box_set_filter_func(data->project_list,
|
||||
(GtkListBoxFilterFunc)filter_func, search_entry, NULL);
|
||||
|
||||
g_signal_connect(search_entry, "search-changed", G_CALLBACK(on_search_changed), data);
|
||||
|
||||
g_signal_connect(list_box, "row-activated", G_CALLBACK(on_project_activated), data);
|
||||
|
||||
// Обновляем список проектов
|
||||
update_project_list(data);
|
||||
|
||||
// Устанавливаем фокус на первый элемент списка
|
||||
GtkListBoxRow *first_row = gtk_list_box_get_row_at_index(data->project_list, 0);
|
||||
if (first_row)
|
||||
{
|
||||
gtk_list_box_select_row(data->project_list, first_row);
|
||||
gtk_widget_grab_focus(GTK_WIDGET(first_row));
|
||||
}
|
||||
|
||||
gtk_window_present(GTK_WINDOW(window));
|
||||
}
|
||||
|
||||
static GtkWidget *create_window(const char *title, GtkWindow *parent)
|
||||
{
|
||||
GtkWidget *window = gtk_window_new();
|
||||
if (title)
|
||||
{
|
||||
gtk_window_set_title(GTK_WINDOW(window), title);
|
||||
}
|
||||
if (parent)
|
||||
{
|
||||
gtk_window_set_transient_for(GTK_WINDOW(window), parent);
|
||||
gtk_window_set_modal(GTK_WINDOW(window), TRUE);
|
||||
}
|
||||
|
||||
GtkEventController *controller = gtk_event_controller_key_new();
|
||||
gtk_widget_add_controller(window, controller);
|
||||
g_signal_connect(controller, "key-pressed", G_CALLBACK(destroy_on_key_pressed), NULL);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
GtkApplication *app = gtk_application_new("org.example.projectlauncher", G_APPLICATION_DEFAULT_FLAGS);
|
||||
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
|
||||
|
||||
int status = g_application_run(G_APPLICATION(app), argc, argv);
|
||||
g_object_unref(app);
|
||||
|
||||
return status;
|
||||
}
|
||||
109
src/project_manager.cpp
Normal file
109
src/project_manager.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
#include "project_manager.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
|
||||
ProjectManager::ProjectManager()
|
||||
{
|
||||
// Создаем директорию если её нет
|
||||
std::string config_dir = std::string(getenv("HOME")) + "/.config/project-launcher";
|
||||
std::filesystem::create_directories(config_dir);
|
||||
|
||||
loadFromFile();
|
||||
}
|
||||
|
||||
ProjectManager::~ProjectManager()
|
||||
{
|
||||
saveToFile();
|
||||
}
|
||||
|
||||
const std::vector<Project> &ProjectManager::getProjects() const
|
||||
{
|
||||
return projects;
|
||||
}
|
||||
|
||||
void ProjectManager::addProject(const std::string &name, const std::string &path)
|
||||
{
|
||||
projects.push_back({name, path});
|
||||
saveToFile();
|
||||
}
|
||||
|
||||
void ProjectManager::deleteProject(size_t index)
|
||||
{
|
||||
if (index < projects.size())
|
||||
{
|
||||
projects.erase(projects.begin() + index);
|
||||
saveToFile();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectManager::editProject(size_t index, const std::string &name, const std::string &path)
|
||||
{
|
||||
if (index < projects.size())
|
||||
{
|
||||
projects[index].name = name;
|
||||
projects[index].path = path;
|
||||
saveToFile();
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectManager::openProject(size_t index)
|
||||
{
|
||||
if (index < projects.size())
|
||||
{
|
||||
std::string command = "cursor " + projects[index].path + " &";
|
||||
bool commandExecute = system(command.c_str());
|
||||
std::cout << "Command executed: " << commandExecute << std::endl;
|
||||
return commandExecute == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProjectManager::saveToFile()
|
||||
{
|
||||
nlohmann::json j = nlohmann::json::array();
|
||||
for (const auto &project : projects)
|
||||
{
|
||||
j.push_back({{"name", project.name},
|
||||
{"path", project.path}});
|
||||
}
|
||||
|
||||
std::ofstream file(CONFIG_FILE);
|
||||
if (file.is_open())
|
||||
{
|
||||
file << j.dump(4);
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Ошибка при сохранении файла!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectManager::loadFromFile()
|
||||
{
|
||||
std::ifstream file(CONFIG_FILE);
|
||||
if (file.is_open())
|
||||
{
|
||||
try
|
||||
{
|
||||
nlohmann::json j;
|
||||
file >> j;
|
||||
|
||||
projects.clear();
|
||||
for (const auto &item : j)
|
||||
{
|
||||
Project project;
|
||||
project.name = item["name"];
|
||||
project.path = item["path"];
|
||||
projects.push_back(project);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "Ошибка при чтении файла: " << e.what() << std::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
30
src/project_manager.hpp
Normal file
30
src/project_manager.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
struct Project
|
||||
{
|
||||
std::string name;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
class ProjectManager
|
||||
{
|
||||
public:
|
||||
ProjectManager();
|
||||
~ProjectManager();
|
||||
|
||||
const std::vector<Project> &getProjects() const;
|
||||
void addProject(const std::string &name, const std::string &path);
|
||||
void deleteProject(size_t index);
|
||||
void editProject(size_t index, const std::string &name, const std::string &path);
|
||||
bool openProject(size_t index);
|
||||
|
||||
private:
|
||||
std::vector<Project> projects;
|
||||
const std::string CONFIG_FILE = std::string(getenv("HOME")) + "/.config/project-launcher/projects.json";
|
||||
|
||||
void saveToFile();
|
||||
void loadFromFile();
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user