diff --git a/src/common/settings/settings.cpp b/src/common/settings/settings.cpp index f324ea134..801fe5087 100644 --- a/src/common/settings/settings.cpp +++ b/src/common/settings/settings.cpp @@ -190,7 +190,7 @@ bool Settings::contains(const QString &group, const QString &key) const return d->defaultData.values.value(group).contains(key); } -QVariant Settings::value(const QString &group, const QString &key, const QVariant &defaultValue) +QVariant Settings::value(const QString &group, const QString &key, const QVariant &defaultValue) const { auto value = d->settingData.value(group, key, QVariant::Invalid); if (value.isValid()) @@ -199,7 +199,7 @@ QVariant Settings::value(const QString &group, const QString &key, const QVarian return d->defaultData.value(group, key, defaultValue); } -QVariant Settings::defaultValue(const QString &group, const QString &key, const QVariant &defaultValue) +QVariant Settings::defaultValue(const QString &group, const QString &key, const QVariant &defaultValue) const { return d->defaultData.value(group, key, defaultValue); } diff --git a/src/common/settings/settings.h b/src/common/settings/settings.h index f4ea47308..7aa515a30 100644 --- a/src/common/settings/settings.h +++ b/src/common/settings/settings.h @@ -20,8 +20,8 @@ class Settings : public QObject QStringList groupList() const; QStringList keyList(const QString &group) const; bool contains(const QString &group, const QString &key) const; - QVariant value(const QString &group, const QString &key, const QVariant &defaultValue = QVariant()); - QVariant defaultValue(const QString &group, const QString &key, const QVariant &defaultValue = QVariant()); + QVariant value(const QString &group, const QString &key, const QVariant &defaultValue = QVariant()) const; + QVariant defaultValue(const QString &group, const QString &key, const QVariant &defaultValue = QVariant()) const; void setValue(const QString &group, const QString &key, const QVariant &value, bool notify = false); void removeGroup(const QString &group); void remove(const QString &group, const QString &key); diff --git a/src/common/util/eventdefinitions.h b/src/common/util/eventdefinitions.h index d7cf7cd3e..285e45ca9 100644 --- a/src/common/util/eventdefinitions.h +++ b/src/common/util/eventdefinitions.h @@ -125,6 +125,13 @@ OPI_OBJECT(workspace, OPI_INTERFACE(expandAll) OPI_INTERFACE(foldAll) ) +OPI_OBJECT(session, + OPI_INTERFACE(readyToSaveSession) + OPI_INTERFACE(sessionLoaded, "session") + OPI_INTERFACE(sessionCreated, "session") + OPI_INTERFACE(sessionRenamed, "oldName", "newName") + OPI_INTERFACE(sessionRemoved, "session") + ) struct AnalysedData { diff --git a/src/plugins/codeeditor/gui/private/tabbar_p.h b/src/plugins/codeeditor/gui/private/tabbar_p.h index a62869cd9..127c16235 100644 --- a/src/plugins/codeeditor/gui/private/tabbar_p.h +++ b/src/plugins/codeeditor/gui/private/tabbar_p.h @@ -24,7 +24,6 @@ class TabBarPrivate : public QObject void updateBackgroundColor(); bool isModified(int index) const; int showConfirmDialog(const QString &filePath); - void closeAllTab(const QStringList &exceptList); public slots: void onCurrentTabChanged(int index); diff --git a/src/plugins/codeeditor/gui/private/workspacewidget_p.h b/src/plugins/codeeditor/gui/private/workspacewidget_p.h index e5bff9f66..609239aa6 100644 --- a/src/plugins/codeeditor/gui/private/workspacewidget_p.h +++ b/src/plugins/codeeditor/gui/private/workspacewidget_p.h @@ -11,6 +11,7 @@ #include "base/abstracteditwidget.h" #include "common/util/eventdefinitions.h" #include "services/window/windowservice.h" +#include "services/session/sessionservice.h" #include "Qsci/qscicommand.h" #include @@ -64,6 +65,8 @@ public slots: void handleSetModifiedAutoReload(const QString &fileName, bool flag); void handleSetComment(); void handleShowOpenedFiles(); + void handleSaveSession(); + void handleSessionLoaded(); public: WorkspaceWidget *q; @@ -78,6 +81,8 @@ public slots: QStringList modifiedFileList; QStringList removedFileList; QTimer fileCheckTimer; + + dpfservice::SessionService *sessionSrv { nullptr }; }; #endif // WORKSPACEWIDGET_P_H diff --git a/src/plugins/codeeditor/gui/tabbar.cpp b/src/plugins/codeeditor/gui/tabbar.cpp index 21e67036a..f2874effe 100644 --- a/src/plugins/codeeditor/gui/tabbar.cpp +++ b/src/plugins/codeeditor/gui/tabbar.cpp @@ -81,29 +81,6 @@ int TabBarPrivate::showConfirmDialog(const QString &filePath) return dialog.exec(); } -void TabBarPrivate::closeAllTab(const QStringList &exceptList) -{ - QStringList tabList; - for (int i = 0; i < tabBar->count(); ++i) { - auto file = tabBar->tabToolTip(i); - if (exceptList.contains(file)) - continue; - - if (isModified(i)) { - int ret = showConfirmDialog(file); - if (ret == 0) // save - emit q->saveFileRequested(file); - else if (ret == 2 || ret == -1) // cancel or close - return; - } - - tabList << file; - } - - for (const auto &tab : tabList) - q->removeTab(tab, true); -} - void TabBarPrivate::initConnection() { connect(tabBar, &DTabBar::currentChanged, this, &TabBarPrivate::onCurrentTabChanged); @@ -149,12 +126,12 @@ void TabBarPrivate::showMenu(QPoint pos) q->removeTab(file); }); menu.addAction(tr("Close All Files"), [=]() { - closeAllTab({}); + q->closeAllTab({}); }); menu.addAction(tr("Close All Files Except This"), [=]() { auto curFile = tabBar->tabToolTip(curIndex); QStringList exceptList { curFile }; - closeAllTab(exceptList); + q->closeAllTab(exceptList); }); menu.addSeparator(); @@ -248,8 +225,7 @@ void TabBar::removeTab(const QString &fileName, bool silent) if (-1 == index) return; - QFileInfo info(fileName); - if (!silent && info.exists() && d->isModified(index)) { + if (!silent && QFile::exists(fileName) && d->isModified(index)) { int ret = d->showConfirmDialog(fileName); if (ret == 0) // save emit saveFileRequested(fileName); @@ -263,6 +239,21 @@ void TabBar::removeTab(const QString &fileName, bool silent) editor.switchedFile(this->currentFileName()); } +void TabBar::closeAllTab(const QStringList &exceptList, bool silent) +{ + QStringList tabList; + for (int i = 0; i < d->tabBar->count(); ++i) { + auto file = d->tabBar->tabToolTip(i); + if (exceptList.contains(file)) + continue; + + tabList << file; + } + + for (const auto &tab : tabList) + removeTab(tab, silent); +} + void TabBar::setCloseButtonVisible(bool visible) { d->closeBtn->setVisible(visible); diff --git a/src/plugins/codeeditor/gui/tabbar.h b/src/plugins/codeeditor/gui/tabbar.h index d072a82d7..bb6381e21 100644 --- a/src/plugins/codeeditor/gui/tabbar.h +++ b/src/plugins/codeeditor/gui/tabbar.h @@ -24,6 +24,7 @@ class TabBar : public QWidget void switchTab(const QString &fileName); void removeTab(const QString &fileName, bool silent = false); + void closeAllTab(const QStringList &exceptList, bool silent = false); void setCloseButtonVisible(bool visible); void setSplitButtonVisible(bool visible); diff --git a/src/plugins/codeeditor/gui/tabwidget.cpp b/src/plugins/codeeditor/gui/tabwidget.cpp index 259b410eb..76edf25a8 100644 --- a/src/plugins/codeeditor/gui/tabwidget.cpp +++ b/src/plugins/codeeditor/gui/tabwidget.cpp @@ -697,6 +697,11 @@ void TabWidget::closeFileEditor() closeFileEditor(editor->getFile()); } +void TabWidget::closeAllEditor() +{ + d->tabBar->closeAllTab({}, true); +} + void TabWidget::switchHeaderSource() { auto editor = d->currentTextEditor(); diff --git a/src/plugins/codeeditor/gui/tabwidget.h b/src/plugins/codeeditor/gui/tabwidget.h index ce3ed9d50..c9df41355 100644 --- a/src/plugins/codeeditor/gui/tabwidget.h +++ b/src/plugins/codeeditor/gui/tabwidget.h @@ -42,6 +42,7 @@ class TabWidget : public QWidget void setFileModified(const QString &fileName, bool isModified); void closeFileEditor(const QString &fileName); void closeFileEditor(); + void closeAllEditor(); void switchHeaderSource(); void followSymbolUnderCursor(); void findUsage(); diff --git a/src/plugins/codeeditor/gui/workspacewidget.cpp b/src/plugins/codeeditor/gui/workspacewidget.cpp index 33cdf55e1..fc200dba6 100644 --- a/src/plugins/codeeditor/gui/workspacewidget.cpp +++ b/src/plugins/codeeditor/gui/workspacewidget.cpp @@ -18,7 +18,9 @@ #include #include -constexpr char TextEditorContext[] { "Text Editor" }; +constexpr char kTextEditorContext[] { "Text Editor" }; +constexpr char kOpenedFileList[] { "OpenFileList" }; +constexpr char kCurrentFile[] { "CurrentFile" }; using namespace dpfservice; DWIDGET_USE_NAMESPACE @@ -27,6 +29,7 @@ WorkspaceWidgetPrivate::WorkspaceWidgetPrivate(WorkspaceWidget *qq) : QObject(qq), q(qq) { + sessionSrv = dpfGetService(SessionService); fileCheckTimer.setInterval(500); fileCheckTimer.setSingleShot(true); } @@ -69,13 +72,13 @@ void WorkspaceWidgetPrivate::initActions() // add/del comment QAction *commentAction = new QAction(tr("Add/Delete Comment"), q); - auto cmd = ActionManager::instance()->registerAction(commentAction, "TextEditor.AddAndRemoveComment", { TextEditorContext }); + auto cmd = ActionManager::instance()->registerAction(commentAction, "TextEditor.AddAndRemoveComment", { kTextEditorContext }); cmd->setDefaultKeySequence(Qt::CTRL | Qt::Key_Slash); connect(commentAction, &QAction::triggered, this, &WorkspaceWidgetPrivate::handleSetComment); // show opened files QAction *showOpenedAction = new QAction(tr("Show opened files"), q); - cmd = ActionManager::instance()->registerAction(showOpenedAction, "TextEditor.ShowOpenedFiles", { TextEditorContext }); + cmd = ActionManager::instance()->registerAction(showOpenedAction, "TextEditor.ShowOpenedFiles", { kTextEditorContext }); cmd->setDefaultKeySequence(Qt::CTRL | Qt::Key_Tab); connect(showOpenedAction, &QAction::triggered, this, &WorkspaceWidgetPrivate::handleShowOpenedFiles); @@ -438,7 +441,7 @@ void WorkspaceWidgetPrivate::initActions() if (!actionText.isEmpty()) { auto id = QString("TextEditor.%1").arg(me.valueToKey(val)); auto act = new QAction(actionText, q); - auto cmd = ActionManager::instance()->registerAction(act, id, { TextEditorContext }); + auto cmd = ActionManager::instance()->registerAction(act, id, { kTextEditorContext }); if (!ksList.isEmpty()) cmd->setDefaultKeySequences(ksList); connect(act, &QAction::triggered, this, [val, this] { @@ -469,6 +472,32 @@ void WorkspaceWidgetPrivate::handleShowOpenedFiles() currentTabWidget()->handleShowOpenedFiles(q->pos().x() - q->mapFromGlobal(q->pos()).x(), q->pos().y() + q->mapToGlobal(q->pos()).y() - 100, q->size()); } +void WorkspaceWidgetPrivate::handleSaveSession() +{ + sessionSrv->setValue(kOpenedFileList, q->openedFiles()); + sessionSrv->setValue(kCurrentFile, q->currentFile()); +} + +void WorkspaceWidgetPrivate::handleSessionLoaded() +{ + while (tabWidgetList.size() > 1) { + auto tabWidget = tabWidgetList.takeLast(); + tabWidget->deleteLater(); + } + + tabWidgetList.first()->closeAllEditor(); + focusTabWidget = tabWidgetList.first(); + auto symbolWidget = SymbolWidgetGenerator::instance()->symbolWidget(); + symbolWidget->setEditor(nullptr); + + const auto &openedFiles = sessionSrv->value(kOpenedFileList).toStringList(); + const auto ¤tFile = sessionSrv->value(kCurrentFile).toString(); + for (const auto &file : openedFiles) + handleOpenFile("", file); + if (!currentFile.isEmpty()) + handleOpenFile("", currentFile); +} + void WorkspaceWidgetPrivate::initConnection() { connect(&fileCheckTimer, &QTimer::timeout, this, &WorkspaceWidgetPrivate::checkFileState); @@ -495,6 +524,8 @@ void WorkspaceWidgetPrivate::initConnection() connect(EditorCallProxy::instance(), &EditorCallProxy::reqRenameSymbol, this, &WorkspaceWidgetPrivate::handleRenameSymbol); connect(EditorCallProxy::instance(), &EditorCallProxy::reqToggleBreakpoint, this, &WorkspaceWidgetPrivate::handleToggleBreakpoint); connect(EditorCallProxy::instance(), &EditorCallProxy::reqSetModifiedAutoReload, this, &WorkspaceWidgetPrivate::handleSetModifiedAutoReload); + connect(EditorCallProxy::instance(), &EditorCallProxy::reqSaveSession, this, &WorkspaceWidgetPrivate::handleSaveSession); + connect(EditorCallProxy::instance(), &EditorCallProxy::reqSessionLoaded, this, &WorkspaceWidgetPrivate::handleSessionLoaded); } void WorkspaceWidgetPrivate::connectTabWidgetSignals(TabWidget *tabWidget) @@ -904,18 +935,18 @@ void WorkspaceWidgetPrivate::onFocusChanged(QWidget *old, QWidget *now) Q_UNUSED(old) if (!now) { - ActionManager::instance()->removeContext({ TextEditorContext }); + ActionManager::instance()->removeContext({ kTextEditorContext }); return; } // the `now` is TextEditor auto tabWidget = qobject_cast(now->parentWidget()); if (!tabWidget) { - ActionManager::instance()->removeContext({ TextEditorContext }); + ActionManager::instance()->removeContext({ kTextEditorContext }); return; } - ActionManager::instance()->addContext({ TextEditorContext }); + ActionManager::instance()->addContext({ kTextEditorContext }); focusTabWidget = tabWidget; editor.switchedFile(focusTabWidget->currentFile()); auto symbolWidget = SymbolWidgetGenerator::instance()->symbolWidget(); diff --git a/src/plugins/codeeditor/lsp/languageclienthandler.cpp b/src/plugins/codeeditor/lsp/languageclienthandler.cpp index 030269fe3..cf6d5dbb1 100644 --- a/src/plugins/codeeditor/lsp/languageclienthandler.cpp +++ b/src/plugins/codeeditor/lsp/languageclienthandler.cpp @@ -84,9 +84,12 @@ void LanguageClientHandlerPrivate::initConnection() void LanguageClientHandlerPrivate::initLspConnection() { + isInited = true; auto client = getClient(); - if (!editor || !client) + if (!editor || !client) { + isInited = false; return; + } auto referencesResult = qOverload(&newlsp::Client::requestResult); connect(client, referencesResult, CodeLens::instance(), &CodeLens::displayReference, Qt::UniqueConnection); @@ -168,6 +171,14 @@ bool LanguageClientHandlerPrivate::shouldStartCompletion(const QString &inserted return false; } +bool LanguageClientHandlerPrivate::documentIsOpened() +{ + if (!isOpened) + q->updateTokens(); + + return isOpened; +} + int LanguageClientHandlerPrivate::wordPostion() { int pos = editor->cursorPosition(); @@ -185,6 +196,11 @@ newlsp::Client *LanguageClientHandlerPrivate::getClient() if (projectKey.isValid()) return LSPClientManager::instance()->get(projectKey); + if (!isInited) { + initLspConnection(); + return nullptr; + } + auto prjSrv = dpfGetService(dpfservice::ProjectService); const auto &filePath = editor->getFile(); const auto &allProject = prjSrv->getAllProjectInfo(); @@ -384,7 +400,7 @@ void LanguageClientHandlerPrivate::setDefinitionSelectedStyle(int start, int end void LanguageClientHandlerPrivate::delayTextChanged() { - if (!editor) + if (!editor || !documentIsOpened()) return; cleanDiagnostics(); @@ -398,7 +414,7 @@ void LanguageClientHandlerPrivate::delayTextChanged() void LanguageClientHandlerPrivate::delayPositionChanged() { - if (!editor || !getClient()) + if (!editor || !documentIsOpened()) return; lsp::Position pos; @@ -408,7 +424,7 @@ void LanguageClientHandlerPrivate::delayPositionChanged() void LanguageClientHandlerPrivate::handleHoveredStart(int position) { - if (!editor || !getClient()) + if (!editor || !documentIsOpened()) return; hoverCache.setPosition(position); @@ -453,7 +469,7 @@ void LanguageClientHandlerPrivate::handleHoverEnd(int position) void LanguageClientHandlerPrivate::handleFollowTypeStart(int position) { - if (!editor || editor->wordAtPosition(position).isEmpty()) { + if (!editor || !documentIsOpened() || editor->wordAtPosition(position).isEmpty()) { handleFollowTypeEnd(); return; } @@ -531,13 +547,13 @@ void LanguageClientHandlerPrivate::handleShowContextMenu(QMenu *menu) void LanguageClientHandlerPrivate::handleFileClosed(const QString &file) { - if (getClient()) + if (documentIsOpened() && getClient()) getClient()->closeRequest(file); } void LanguageClientHandlerPrivate::handleRename(const QString &text) { - if (!editor || !getClient() || !renameCache.isValid()) + if (!editor || !documentIsOpened() || !renameCache.isValid()) return; lsp::Position pos { renameCache.line, renameCache.column }; @@ -685,7 +701,7 @@ LanguageClientHandler::~LanguageClientHandler() void LanguageClientHandler::requestCompletion(int line, int column) { - if (!d->getClient()) + if (!d->documentIsOpened()) return; lsp::CompletionContext context; @@ -711,18 +727,22 @@ void LanguageClientHandler::requestCompletion(int line, int column) void LanguageClientHandler::updateTokens() { + d->isOpened = true; if (auto client = d->getClient()) { client->openRequest(d->editor->getFile()); client->docSemanticTokensFull(d->editor->getFile()); client->symbolRequest(d->editor->getFile()); + } else { + d->isOpened = false; } } lsp::SemanticTokenType::type_value LanguageClientHandler::tokenToDefine(int token) { - auto client = d->getClient(); - if (!client) + if (!d->documentIsOpened()) return {}; + + auto client = d->getClient(); auto initSecTokensProvider = client->initSecTokensProvider(); if (0 <= token && token < initSecTokensProvider.legend.tokenTypes.size()) return initSecTokensProvider.legend.tokenTypes[token]; @@ -743,7 +763,7 @@ QColor LanguageClientHandler::symbolIndicColor(lsp::SemanticTokenType::type_valu void LanguageClientHandler::refreshTokens() { - if (!d->editor || !d->getClient()) + if (!d->editor || !d->documentIsOpened()) return; d->getClient()->docSemanticTokensFull(d->editor->getFile()); @@ -751,7 +771,7 @@ void LanguageClientHandler::refreshTokens() void LanguageClientHandler::switchHeaderSource(const QString &file) { - if (!d->getClient()) + if (!d->documentIsOpened()) return; d->getClient()->switchHeaderSource(file); @@ -759,7 +779,7 @@ void LanguageClientHandler::switchHeaderSource(const QString &file) void LanguageClientHandler::followSymbolUnderCursor() { - if (!d->editor || !d->editor->hasFocus() || !d->getClient()) + if (!d->editor || !d->editor->hasFocus() || !d->documentIsOpened()) return; d->definitionCache.setSwitchMode(DefinitionCache::ActionMode); @@ -771,7 +791,7 @@ void LanguageClientHandler::followSymbolUnderCursor() void LanguageClientHandler::findUsagesActionTriggered() { - if (!d->editor || !d->getClient()) + if (!d->editor || !d->documentIsOpened()) return; lsp::Position pos; @@ -781,7 +801,7 @@ void LanguageClientHandler::findUsagesActionTriggered() void LanguageClientHandler::renameActionTriggered() { - if (!d->editor) + if (!d->editor || !d->documentIsOpened()) return; int pos = d->editor->cursorPosition(); @@ -799,7 +819,7 @@ void LanguageClientHandler::renameActionTriggered() void LanguageClientHandler::formatSelections() { - if (!d->getClient() || !d->editor || !d->editor->hasSelectedText()) + if (!d->documentIsOpened() || !d->editor || !d->editor->hasSelectedText()) return; int lineFrom, indexFrom, lineTo, indexTo; diff --git a/src/plugins/codeeditor/lsp/lspclientmanager.cpp b/src/plugins/codeeditor/lsp/lspclientmanager.cpp index 7a0a52e6d..0c581c3d6 100644 --- a/src/plugins/codeeditor/lsp/lspclientmanager.cpp +++ b/src/plugins/codeeditor/lsp/lspclientmanager.cpp @@ -33,7 +33,6 @@ newlsp::Client *LSPClientManager::get(const newlsp::ProjectKey &key) } else { auto client = new newlsp::Client(); qApp->metaObject()->invokeMethod(client, "selectLspServer", Q_ARG(const newlsp::ProjectKey &, key)); - QString complieDB_Path = QString::fromStdString(key.workspace) + QDir::separator() + ".unioncode"; qApp->metaObject()->invokeMethod(client, "initRequest"); clientHash.insert(key, client); } diff --git a/src/plugins/codeeditor/lsp/private/languageclienthandler_p.h b/src/plugins/codeeditor/lsp/private/languageclienthandler_p.h index 48b82d644..3f25a200a 100644 --- a/src/plugins/codeeditor/lsp/private/languageclienthandler_p.h +++ b/src/plugins/codeeditor/lsp/private/languageclienthandler_p.h @@ -174,6 +174,7 @@ class LanguageClientHandlerPrivate : public QObject void initIndicStyle(); bool shouldStartCompletion(const QString &insertedText); + bool documentIsOpened(); int wordPostion(); newlsp::Client *getClient(); @@ -225,6 +226,8 @@ public slots: QThread thread; LanguageWorker *languageWorker { nullptr }; + bool isInited { false }; + bool isOpened { false }; }; #endif // LANGUAGECLIENTHANDLER_P_H diff --git a/src/plugins/codeeditor/transceiver/codeeditorreceiver.cpp b/src/plugins/codeeditor/transceiver/codeeditorreceiver.cpp index 940d26b55..be2e00eae 100644 --- a/src/plugins/codeeditor/transceiver/codeeditorreceiver.cpp +++ b/src/plugins/codeeditor/transceiver/codeeditorreceiver.cpp @@ -25,16 +25,18 @@ CodeEditorReceiver::CodeEditorReceiver(QObject *parent) eventHandleMap.insert(editor.setBreakpointEnabled.name, std::bind(&CodeEditorReceiver::processSetBreakpointEnabledEvent, this, _1)); eventHandleMap.insert(editor.clearAllBreakpoint.name, std::bind(&CodeEditorReceiver::processClearAllBreakpointsEvent, this, _1)); eventHandleMap.insert(editor.setModifiedAutoReload.name, std::bind(&CodeEditorReceiver::processSetModifiedAutoReloadEvent, this, _1)); + eventHandleMap.insert(session.readyToSaveSession.name, std::bind(&CodeEditorReceiver::processReadyToSaveSessionEvent, this, _1)); + eventHandleMap.insert(session.sessionLoaded.name, std::bind(&CodeEditorReceiver::processSessionLoadedEvent, this, _1)); } dpf::EventHandler::Type CodeEditorReceiver::type() { - return dpf::EventHandler::Type::Async; + return dpf::EventHandler::Type::Sync; } QStringList CodeEditorReceiver::topics() { - return { editor.topic }; + return { editor.topic, session.topic }; } void CodeEditorReceiver::eventProcess(const dpf::Event &event) @@ -140,6 +142,18 @@ void CodeEditorReceiver::processRemoveDebugLineEvent(const dpf::Event &event) Q_EMIT EditorCallProxy::instance()->reqRemoveDebugLine(); } +void CodeEditorReceiver::processSessionLoadedEvent(const dpf::Event &event) +{ + Q_UNUSED(event) + Q_EMIT EditorCallProxy::instance()->reqSessionLoaded(); +} + +void CodeEditorReceiver::processReadyToSaveSessionEvent(const dpf::Event &event) +{ + Q_UNUSED(event) + Q_EMIT EditorCallProxy::instance()->reqSaveSession(); +} + EditorCallProxy::EditorCallProxy() { } diff --git a/src/plugins/codeeditor/transceiver/codeeditorreceiver.h b/src/plugins/codeeditor/transceiver/codeeditorreceiver.h index 33604527d..f1834d8c1 100644 --- a/src/plugins/codeeditor/transceiver/codeeditorreceiver.h +++ b/src/plugins/codeeditor/transceiver/codeeditorreceiver.h @@ -35,6 +35,10 @@ class CodeEditorReceiver : public dpf::EventHandler, dpf::AutoEventHandlerRegist void processSetDebugLineEvent(const dpf::Event &event); void processRemoveDebugLineEvent(const dpf::Event &event); + // session + void processSessionLoadedEvent(const dpf::Event &event); + void processReadyToSaveSessionEvent(const dpf::Event &event); + private: QHash> eventHandleMap; }; @@ -71,6 +75,10 @@ class EditorCallProxy : public QObject void reqClearAllBreakpoints(); void reqSetDebugLine(const QString &fileName, int line); void reqRemoveDebugLine(); + + // session + void reqSaveSession(); + void reqSessionLoaded(); }; #endif // CODEEDITORRECEIVER_H diff --git a/src/plugins/core/builtin/texts/uc_add_16px.svg b/src/plugins/core/builtin/texts/uc_add_16px.svg new file mode 100644 index 000000000..515d35ff8 --- /dev/null +++ b/src/plugins/core/builtin/texts/uc_add_16px.svg @@ -0,0 +1,9 @@ + + + ICON / action /add + + + + + + \ No newline at end of file diff --git a/src/plugins/core/builtin/texts/uc_clone_16px.svg b/src/plugins/core/builtin/texts/uc_clone_16px.svg new file mode 100644 index 000000000..3d2902937 --- /dev/null +++ b/src/plugins/core/builtin/texts/uc_clone_16px.svg @@ -0,0 +1,7 @@ + + + ICON / action /clone + + + + \ No newline at end of file diff --git a/src/plugins/core/builtin/texts/uc_delete_16px.svg b/src/plugins/core/builtin/texts/uc_delete_16px.svg new file mode 100644 index 000000000..b685950fe --- /dev/null +++ b/src/plugins/core/builtin/texts/uc_delete_16px.svg @@ -0,0 +1,11 @@ + + + ICON / action /delete + + + + + + + + \ No newline at end of file diff --git a/src/plugins/core/builtin/texts/uc_edit_16px.svg b/src/plugins/core/builtin/texts/uc_edit_16px.svg new file mode 100644 index 000000000..0b0f3beab --- /dev/null +++ b/src/plugins/core/builtin/texts/uc_edit_16px.svg @@ -0,0 +1,9 @@ + + + ICON / action /edit + + + + + + \ No newline at end of file diff --git a/src/plugins/core/builtin/texts/uc_help_16px.svg b/src/plugins/core/builtin/texts/uc_help_16px.svg new file mode 100644 index 000000000..65a6cad27 --- /dev/null +++ b/src/plugins/core/builtin/texts/uc_help_16px.svg @@ -0,0 +1,7 @@ + + + ICON / action /help + + + + \ No newline at end of file diff --git a/src/plugins/core/builtin/texts/uc_open_16px.svg b/src/plugins/core/builtin/texts/uc_open_16px.svg new file mode 100644 index 000000000..e4070595e --- /dev/null +++ b/src/plugins/core/builtin/texts/uc_open_16px.svg @@ -0,0 +1,7 @@ + + + ICON / action /open + + + + \ No newline at end of file diff --git a/src/plugins/core/core.cpp b/src/plugins/core/core.cpp index a80bc1a90..99ee2cd94 100644 --- a/src/plugins/core/core.cpp +++ b/src/plugins/core/core.cpp @@ -5,6 +5,7 @@ #include "core.h" #include "uicontroller//controller.h" #include "services/window/windowservice.h" +#include "services/session/sessionservice.h" #include "locator/locatormanager.h" #include "locator/actionlocator.h" @@ -22,6 +23,10 @@ void Core::initialize() qCritical() << errStr; abort(); } + if (!ctx.load(SessionService::name(), &errStr)) { + qCritical() << errStr; + abort(); + } } bool Core::start() diff --git a/src/plugins/core/core.qrc b/src/plugins/core/core.qrc index 6ae831f7e..7d8467a84 100644 --- a/src/plugins/core/core.qrc +++ b/src/plugins/core/core.qrc @@ -30,5 +30,11 @@ builtin/dark/icons/notification_error_20px.svg builtin/dark/icons/notification_info_20px.svg builtin/dark/icons/notification_warning_20px.svg + builtin/texts/uc_open_16px.svg + builtin/texts/uc_edit_16px.svg + builtin/texts/uc_delete_16px.svg + builtin/texts/uc_clone_16px.svg + builtin/texts/uc_add_16px.svg + builtin/texts/uc_help_16px.svg diff --git a/src/plugins/core/modules/sessionmanagermodule.cpp b/src/plugins/core/modules/sessionmanagermodule.cpp new file mode 100644 index 000000000..d517efd41 --- /dev/null +++ b/src/plugins/core/modules/sessionmanagermodule.cpp @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "sessionmanagermodule.h" +#include "session/sessionmanager.h" + +#include "services/session/sessionservice.h" +#include "common/util/eventdefinitions.h" + +using namespace dpfservice; + +void SessionManagerModule::initialize(Controller *_uiController) +{ + connect(&dpf::Listener::instance(), &dpf::Listener::pluginsStarted, this, [this] { + auto ins = SessionManager::instance(); + if (ins->isAutoLoadLastSession()) { + const auto session = ins->lastSession(); + QTimer::singleShot(100, this, std::bind(&SessionManager::loadSession, ins, session)); + } + }); + initInterfaces(); + initOutputEvents(); +} + +void SessionManagerModule::initInterfaces() +{ + auto sessionSrv = dpfGetService(SessionService); + Q_ASSERT(sessionSrv); + + using namespace std::placeholders; + auto ins = SessionManager::instance(); + sessionSrv->currentSession = std::bind(&SessionManager::currentSession, ins); + sessionSrv->lastSession = std::bind(&SessionManager::lastSession, ins); + sessionSrv->sessionList = std::bind(&SessionManager::sessionList, ins); + sessionSrv->sessionDateTime = std::bind(&SessionManager::sessionDateTime, ins, _1); + sessionSrv->lastActiveTime = std::bind(&SessionManager::lastActiveTime, ins, _1); + sessionSrv->createSession = std::bind(&SessionManager::createSession, ins, _1); + sessionSrv->removeSession = std::bind(&SessionManager::removeSession, ins, _1); + sessionSrv->removeSessions = std::bind(&SessionManager::removeSessions, ins, _1); + sessionSrv->renameSession = std::bind(&SessionManager::renameSession, ins, _1, _2); + sessionSrv->cloneSession = std::bind(&SessionManager::cloneSession, ins, _1, _2); + sessionSrv->showSessionManager = std::bind(&SessionManager::showSessionManager, ins); + sessionSrv->setValue = std::bind(&SessionManager::setValue, ins, _1, _2); + sessionSrv->value = std::bind(&SessionManager::value, ins, _1); + sessionSrv->loadSession = std::bind(&SessionManager::loadSession, ins, _1); + sessionSrv->saveSession = std::bind(&SessionManager::saveSession, ins); + sessionSrv->isDefaultSession = std::bind(&SessionManager::isDefaultSession, ins, _1); + sessionSrv->isSessionLoading = std::bind(&SessionManager::isSessionLoading, ins); + sessionSrv->isDefaultVirgin = std::bind(&SessionManager::isDefaultVirgin, ins); + sessionSrv->sessionFile = std::bind(&SessionManager::sessionFile, ins, _1); +} + +void SessionManagerModule::initOutputEvents() +{ + auto ins = SessionManager::instance(); + connect(ins, &SessionManager::readyToSaveSession, this, + [] { + session.readyToSaveSession(); + }); + connect(ins, &SessionManager::sessionLoaded, this, + [](const QString &name) { + session.sessionLoaded(name); + }); + connect(ins, &SessionManager::sessionCreated, this, + [](const QString &name) { + session.sessionCreated(name); + }); + connect(ins, &SessionManager::sessionRenamed, this, + [](const QString &oldName, const QString &newName) { + session.sessionRenamed(oldName, newName); + }); + connect(ins, &SessionManager::sessionRemoved, this, + [](const QString &name) { + session.sessionRemoved(name); + }); +} diff --git a/src/plugins/core/modules/sessionmanagermodule.h b/src/plugins/core/modules/sessionmanagermodule.h new file mode 100644 index 000000000..af61c620e --- /dev/null +++ b/src/plugins/core/modules/sessionmanagermodule.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SESSIONMANAGERMODULE_H +#define SESSIONMANAGERMODULE_H + +#include "abstractmodule.h" + +class SessionManagerModule : public AbstractModule +{ + Q_OBJECT +public: + virtual void initialize(Controller *_uiController) override; + +private: + void initInterfaces(); + void initOutputEvents(); +}; + +#endif // SESSIONMANAGERMODULE_H diff --git a/src/plugins/core/session/sessiondialog.cpp b/src/plugins/core/session/sessiondialog.cpp new file mode 100644 index 000000000..8dbf81e95 --- /dev/null +++ b/src/plugins/core/session/sessiondialog.cpp @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "sessiondialog.h" +#include "sessionlistview.h" +#include "sessionmanager.h" + +#include +#include +#include + +#include +#include + +DWIDGET_USE_NAMESPACE + +SessionDialog::SessionDialog(QWidget *parent) + : DDialog(parent) +{ + initUI(); + initConnect(); +} + +void SessionDialog::initUI() +{ + setFixedSize(550, 440); + setIcon(QIcon::fromTheme("ide")); + setWindowTitle(tr("Session Manager")); + setContentLayoutContentsMargins(QMargins(20, 0, 20, 0)); + + QWidget *contentWidget = new QWidget(this); + QVBoxLayout *mainLayout = new QVBoxLayout(contentWidget); + mainLayout->setSpacing(10); + mainLayout->setContentsMargins(0, 0, 0, 0); + + DFrame *frame = new DFrame(this); + QVBoxLayout *layout = new QVBoxLayout(frame); + layout->setContentsMargins(3, 3, 3, 3); + layout->setSpacing(0); + + view = new SessionListView(frame); + view->setFrameShape(QFrame::NoFrame); + view->setAlternatingRowColors(true); + autoLoadCB = new DCheckBox(tr("Restore last session on startup"), this); + helpLabel = new DLabel(this); + helpLabel->installEventFilter(this); + helpLabel->setWordWrap(true); + helpLabel->setFixedSize(36, 36); + helpLabel->setAlignment(Qt::AlignCenter); + helpLabel->setPixmap(QIcon::fromTheme("uc_help").pixmap(16, 16)); + QString tipFormat("

%1

%2

"); + helpLabel->setToolTip(tipFormat.arg(tr("Session management is a crucial feature in modern IDEs that allows developers " + "to preserve and restore their working environment across different coding sessions."), + tr("This functionality significantly improves productivity by eliminating the need " + "to manually reconstruct working environments and allowing developers to maintain " + "separate contexts for different tasks or projects."))); + closeBtn = new DPushButton(tr("Close", "button"), this); + closeBtn->setMinimumWidth(170); + + QHBoxLayout *btnLayout = new QHBoxLayout; + btnLayout->setContentsMargins(0, 0, 0, 0); + btnLayout->addWidget(helpLabel, 0, Qt::AlignLeft); + btnLayout->addWidget(closeBtn, 0, Qt::AlignRight); + + layout->addWidget(view, 1); + layout->addWidget(new DHorizontalLine(frame)); + layout->addWidget(createOptionWidget()); + + mainLayout->addWidget(frame); + mainLayout->addWidget(autoLoadCB, 0, Qt::AlignLeft); + mainLayout->addLayout(btnLayout); + + addContent(contentWidget); +} + +void SessionDialog::initConnect() +{ + connect(view, &SessionListView::sessionsSelected, this, &SessionDialog::updateOptions); + connect(addBtn, &DIconButton::clicked, view, &SessionListView::createSession); + connect(renameBtn, &DIconButton::clicked, view, &SessionListView::renameCurrentSession); + connect(cloneBtn, &DIconButton::clicked, view, &SessionListView::cloneCurrentSession); + connect(removeBtn, &DIconButton::clicked, view, &SessionListView::removeSelectedSessions); + connect(openBtn, &DIconButton::clicked, view, &SessionListView::switchToCurrentSession); + connect(closeBtn, &DPushButton::clicked, this, &SessionDialog::close); +} + +QWidget *SessionDialog::createOptionWidget() +{ + auto createButton = [this](const QString &icon, const QString &tip) { + DIconButton *iconBtn = new DIconButton(this); + iconBtn->setIcon(QIcon::fromTheme(icon)); + iconBtn->setIconSize({ 16, 16 }); + iconBtn->setToolTip(tip); + iconBtn->setFlat(true); + return iconBtn; + }; + + QWidget *widget = new QWidget(this); + QHBoxLayout *layout = new QHBoxLayout(widget); + layout->setAlignment(Qt::AlignLeft); + layout->setContentsMargins(7, 3, 3, 3); + layout->setSpacing(10); + + addBtn = createButton("uc_add", tr("New Session")); + openBtn = createButton("uc_open", tr("Open Session")); + renameBtn = createButton("uc_edit", tr("Rename Session")); + cloneBtn = createButton("uc_clone", tr("Clone Session")); + removeBtn = createButton("uc_delete", tr("Remove Session")); + + DVerticalLine *vLine = new DVerticalLine(this); + vLine->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + layout->addWidget(addBtn); + layout->addWidget(vLine); + layout->addWidget(openBtn); + layout->addWidget(renameBtn); + layout->addWidget(cloneBtn); + layout->addWidget(removeBtn); + + return widget; +} + +void SessionDialog::setAutoLoadSession(bool autoLoad) +{ + autoLoadCB->setChecked(autoLoad); +} + +bool SessionDialog::autoLoadSession() const +{ + return autoLoadCB->isChecked(); +} + +bool SessionDialog::eventFilter(QObject *obj, QEvent *e) +{ + if (obj == helpLabel && e->type() == QEvent::Paint) { + QPainter painter(helpLabel); + painter.setRenderHint(QPainter::Antialiasing); + + QPainterPath path; + path.addRoundedRect(helpLabel->rect(), 8, 8); + + painter.setClipPath(path); + painter.fillRect(helpLabel->rect(), helpLabel->palette().button()); + } + + return DDialog::eventFilter(obj, e); +} + +void SessionDialog::updateOptions(const QStringList &sessions) +{ + if (sessions.isEmpty()) { + openBtn->setEnabled(false); + renameBtn->setEnabled(false); + cloneBtn->setEnabled(false); + removeBtn->setEnabled(false); + return; + } + + bool defaultIsSelected = sessions.contains("default"); + bool activeIsSelected = std::any_of(sessions.cbegin(), sessions.cend(), + [](const QString &session) { + return session == SessionManager::instance()->currentSession(); + }); + openBtn->setEnabled(sessions.size() == 1); + renameBtn->setEnabled(sessions.size() == 1 && !defaultIsSelected); + cloneBtn->setEnabled(sessions.size() == 1); + removeBtn->setEnabled(!defaultIsSelected && !activeIsSelected); +} + +SessionNameInputDialog::SessionNameInputDialog(QWidget *parent) + : DDialog(parent) +{ + initUI(); +} + +void SessionNameInputDialog::initUI() +{ + setSpacing(10); + setIcon(QIcon::fromTheme("ide")); + lineEdit = new DLineEdit(this); + lineEdit->setPlaceholderText(tr("Please input session name")); + connect(lineEdit, &DLineEdit::textChanged, this, [this](const QString &text) { + getButton(1)->setEnabled(!text.isEmpty()); + getButton(2)->setEnabled(!text.isEmpty()); + }); + addContent(lineEdit); + + addButton(tr("Cancel", "button")); + addButton("2"); + addButton("3", true, DDialog::ButtonRecommend); + getButton(1)->setEnabled(false); + getButton(2)->setEnabled(false); +} + +void SessionNameInputDialog::setSessionName(const QString &name) +{ + lineEdit->setText(name); +} + +QString SessionNameInputDialog::sessionName() const +{ + return lineEdit->text(); +} + +void SessionNameInputDialog::setActionText(const QString &actText, const QString &openActText) +{ + getButton(1)->setText(actText); + getButton(2)->setText(openActText); +} diff --git a/src/plugins/core/session/sessiondialog.h b/src/plugins/core/session/sessiondialog.h new file mode 100644 index 000000000..c138439d6 --- /dev/null +++ b/src/plugins/core/session/sessiondialog.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SESSIONDIALOG_H +#define SESSIONDIALOG_H + +#include +#include +#include +#include +#include + +class SessionListView; +class SessionDialog : public DTK_WIDGET_NAMESPACE::DDialog +{ + Q_OBJECT +public: + explicit SessionDialog(QWidget *parent = nullptr); + + void setAutoLoadSession(bool autoLoad); + bool autoLoadSession() const; + +protected: + bool eventFilter(QObject *obj, QEvent *e) override; + +private: + void initUI(); + void initConnect(); + QWidget *createOptionWidget(); + void updateOptions(const QStringList &sessions); + +private: + SessionListView *view { nullptr }; + DTK_WIDGET_NAMESPACE::DIconButton *addBtn { nullptr }; + DTK_WIDGET_NAMESPACE::DIconButton *openBtn { nullptr }; + DTK_WIDGET_NAMESPACE::DIconButton *renameBtn { nullptr }; + DTK_WIDGET_NAMESPACE::DIconButton *cloneBtn { nullptr }; + DTK_WIDGET_NAMESPACE::DIconButton *removeBtn { nullptr }; + DTK_WIDGET_NAMESPACE::DCheckBox *autoLoadCB { nullptr }; + DTK_WIDGET_NAMESPACE::DLabel *helpLabel { nullptr }; + DTK_WIDGET_NAMESPACE::DPushButton *closeBtn { nullptr }; +}; + +class SessionNameInputDialog : public DTK_WIDGET_NAMESPACE::DDialog +{ + Q_OBJECT +public: + explicit SessionNameInputDialog(QWidget *parent = nullptr); + + void setSessionName(const QString &name); + QString sessionName() const; + void setActionText(const QString &actText, const QString &openActText); + +private: + void initUI(); + + DTK_WIDGET_NAMESPACE::DLineEdit *lineEdit { nullptr }; +}; + +#endif // SESSIONDIALOG_H diff --git a/src/plugins/core/session/sessionlistview.cpp b/src/plugins/core/session/sessionlistview.cpp new file mode 100644 index 000000000..f95455097 --- /dev/null +++ b/src/plugins/core/session/sessionlistview.cpp @@ -0,0 +1,194 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "sessionlistview.h" +#include "sessiondialog.h" +#include "sessionmanager.h" + +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +class SessionItemDelegate : public QStyledItemDelegate +{ +public: + SessionItemDelegate(QObject *parent = nullptr) + : QStyledItemDelegate(parent) {} + +protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; +}; + +void SessionItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + painter->setRenderHint(QPainter::Antialiasing); + QStyleOptionViewItem opt = option; + opt.state &= ~QStyle::State_HasFocus; + QStyledItemDelegate::paint(painter, opt, index); +} + +QSize SessionItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Q_UNUSED(index) + auto rect = option.rect; + return { rect.width(), 30 }; +} + +SessionListView::SessionListView(QWidget *parent) + : QTreeView(parent) +{ + initUI(); + initConnections(); +} + +void SessionListView::initUI() +{ + setUniformRowHeights(true); + setItemDelegate(new SessionItemDelegate(this)); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setEditTriggers(QAbstractItemView::NoEditTriggers); + setWordWrap(false); + setRootIsDecorated(false); + setSortingEnabled(true); + setModel(&model); + sortByColumn(0, Qt::AscendingOrder); + header()->setSectionResizeMode(QHeaderView::Stretch); +} + +void SessionListView::initConnections() +{ + connect(this, &SessionListView::activated, this, &SessionListView::switchToCurrentSession); + connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, [this] { + Q_EMIT sessionsSelected(selectedSessions()); + }); + + connect(&model, &SessionModel::modelReset, this, &SessionListView::selectActiveSession); +} + +QString SessionListView::currentSession() const +{ + return model.sessionAt(selectionModel()->currentIndex().row()); +} + +void SessionListView::selectSession(const QString &session) +{ + int row = model.rowOfSession(session); + selectionModel()->setCurrentIndex(model.index(row, 0), + QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +void SessionListView::selectActiveSession() +{ + selectSession(SessionManager::instance()->currentSession()); +} + +void SessionListView::createSession() +{ + SessionNameInputDialog dlg; + dlg.setTitle(tr("New Session Name")); + dlg.setActionText(tr("Create", "button"), + tr("Create and Open", "button")); + + runInputDialog(&dlg, [](const QString &newName) { + SessionManager::instance()->createSession(newName); + }); +} + +void SessionListView::removeSelectedSessions() +{ + const auto &sessions = selectedSessions(); + + DDialog dlg(this); + dlg.setIcon(QIcon::fromTheme("dialog-warning")); + if (sessions.size() > 1) + dlg.setTitle(tr("Are you sure to delete these sessions?")); + else + dlg.setTitle(tr("Are you sure to remove this session?")); + dlg.addButton(tr("Cancel", "button")); + dlg.addButton(tr("Remove", "button"), true, DDialog::ButtonWarning); + + if (dlg.exec() == 1) { + SessionManager::instance()->removeSessions(selectedSessions()); + model.reset(); + } +} + +void SessionListView::cloneCurrentSession() +{ + SessionNameInputDialog dlg; + dlg.setTitle(tr("New Session Name")); + dlg.setActionText(tr("Clone", "button"), + tr("Clone and Open", "button")); + const auto session = currentSession(); + dlg.setSessionName(session + " (2)"); + + runInputDialog(&dlg, [session](const QString &newName) { + SessionManager::instance()->renameSession(session, newName); + }); +} + +void SessionListView::renameCurrentSession() +{ + SessionNameInputDialog dlg; + dlg.setTitle(tr("Rename Session")); + dlg.setActionText(tr("Rename", "button"), + tr("Rename and Open", "button")); + const auto session = currentSession(); + dlg.setSessionName(session); + + runInputDialog(&dlg, [session](const QString &newName) { + SessionManager::instance()->renameSession(session, newName); + }); +} + +void SessionListView::switchToCurrentSession() +{ + const auto session = currentSession(); + SessionManager::instance()->loadSession(session); +} + +void SessionListView::showEvent(QShowEvent *event) +{ + QTreeView::showEvent(event); + selectActiveSession(); + setFocus(); +} + +void SessionListView::keyPressEvent(QKeyEvent *event) +{ +} + +QStringList SessionListView::selectedSessions() const +{ + const auto &indexList = selectionModel()->selectedRows(); + QStringList selectedSessions; + std::transform(indexList.cbegin(), indexList.cend(), std::back_inserter(selectedSessions), + [this](const QModelIndex &index) { + return model.sessionAt(index.row()); + }); + return selectedSessions; +} + +void SessionListView::runInputDialog(SessionNameInputDialog *dialog, + std::function handler) +{ + int ret = dialog->exec(); + if (ret < 1) + return; + + const auto name = dialog->sessionName(); + if (name.isEmpty() || SessionManager::instance()->sessionList().contains(name)) + return; + + handler(name); + model.reset(); + if (ret == 2) + SessionManager::instance()->loadSession(name); +} diff --git a/src/plugins/core/session/sessionlistview.h b/src/plugins/core/session/sessionlistview.h new file mode 100644 index 000000000..b6c38ea7a --- /dev/null +++ b/src/plugins/core/session/sessionlistview.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SESSIONLISTVIEW_H +#define SESSIONLISTVIEW_H + +#include "sessionmodel.h" + +#include + +class SessionNameInputDialog; +class SessionListView : public QTreeView +{ + Q_OBJECT +public: + explicit SessionListView(QWidget *parent = nullptr); + + QString currentSession() const; + void selectSession(const QString &session); + void selectActiveSession(); + +public Q_SLOTS: + void createSession(); + void removeSelectedSessions(); + void cloneCurrentSession(); + void renameCurrentSession(); + void switchToCurrentSession(); + +Q_SIGNALS: + void sessionsSelected(const QStringList &sessions); + +private: + void initUI(); + void initConnections(); + + void showEvent(QShowEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + + QStringList selectedSessions() const; + void runInputDialog(SessionNameInputDialog *dialog, + std::function handler); + +private: + SessionModel model; +}; + +#endif // SESSIONLISTVIEW_H diff --git a/src/plugins/core/session/sessionmanager.cpp b/src/plugins/core/session/sessionmanager.cpp new file mode 100644 index 000000000..5926ece27 --- /dev/null +++ b/src/plugins/core/session/sessionmanager.cpp @@ -0,0 +1,320 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "sessionmanager.h" +#include "sessiondialog.h" +#include "uicontroller/controller.h" + +#include "common/settings/settings.h" +#include "common/util/custompaths.h" + +#include +#include + +constexpr char kDefaultSession[] { "default" }; +constexpr char kSessionGroup[] { "Session" }; +constexpr char kGeneralGroup[] { "General" }; + +constexpr char kLastActiveTimes[] { "LastActiveTimes" }; +constexpr char kIsAutoLoadLastSession[] { "AutoLoadLastSession" }; +constexpr char kLastSession[] { "LastSession" }; + +class SessionManagerPrivate +{ +public: + explicit SessionManagerPrivate(SessionManager *qq); + + QString sessionConfigDir(); + QString settingFile(); + void readSettings(); + void saveSettings(); + void restoreValues(const QString &session); + void setAutoLoadLastSession(bool autoLoad); + +public: + SessionManager *q; + + QString currentSession { kDefaultSession }; + bool isSessionLoading { false }; + bool isAutoLoad { false }; + bool virginSession { true }; + + QStringList sessionList; + QMap values; + QHash sessionDateTimes; + QHash lastActiveTimes; + + Settings settings; +}; + +SessionManagerPrivate::SessionManagerPrivate(SessionManager *qq) + : q(qq) +{ +} + +QString SessionManagerPrivate::sessionConfigDir() +{ + const QString configDir = CustomPaths::user(CustomPaths::Flags::Configures); + return configDir + QDir::separator() + "sessions"; +} + +QString SessionManagerPrivate::settingFile() +{ + return sessionConfigDir() + QDir::separator() + "session.json"; +} + +void SessionManagerPrivate::readSettings() +{ + settings.load("", settingFile()); + isAutoLoad = settings.value(kGeneralGroup, kIsAutoLoadLastSession).toBool(); +} + +void SessionManagerPrivate::saveSettings() +{ + QVariantMap times; + for (auto it = lastActiveTimes.cbegin(); it != lastActiveTimes.cend(); ++it) { + times.insert(it.key(), it.value()); + } + settings.setValue(kGeneralGroup, kLastActiveTimes, times); + settings.setValue(kGeneralGroup, kLastSession, currentSession); + settings.setValue(kGeneralGroup, kIsAutoLoadLastSession, isAutoLoad); +} + +void SessionManagerPrivate::restoreValues(const QString &session) +{ + Settings settings("", q->sessionFile(session)); + const auto keys = settings.keyList(kSessionGroup); + for (const auto &key : keys) { + values.insert(key, settings.value(kSessionGroup, key)); + } +} + +void SessionManagerPrivate::setAutoLoadLastSession(bool autoLoad) +{ + if (isAutoLoad == autoLoad) + return; + isAutoLoad = autoLoad; + settings.setValue(kGeneralGroup, kIsAutoLoadLastSession, autoLoad); + settings.sync(); +} + +SessionManager::SessionManager(QObject *parent) + : QObject(parent), + d(new SessionManagerPrivate(this)) +{ + d->readSettings(); + connect(qApp, &QApplication::aboutToQuit, this, [this] { + d->saveSettings(); + saveSession(); + }); +} + +SessionManager::~SessionManager() +{ + delete d; +} + +SessionManager *SessionManager::instance() +{ + static SessionManager ins; + return &ins; +} + +QString SessionManager::currentSession() +{ + return d->currentSession; +} + +QString SessionManager::lastSession() +{ + return d->settings.value(kGeneralGroup, kLastSession).toString(); +} + +QStringList SessionManager::sessionList() +{ + if (d->sessionList.isEmpty()) { + QDir sessionDir(d->sessionConfigDir()); + const auto &sessionInfos = sessionDir.entryInfoList({ "*.session" }, QDir::NoDotAndDotDot | QDir::Files, + QDir::Time | QDir::Reversed); + const QVariantMap lastActiveTimes = d->settings.value(kGeneralGroup, kLastActiveTimes).toMap(); + for (const auto &info : sessionInfos) { + const auto &name = info.baseName(); + d->sessionDateTimes.insert(name, info.lastModified()); + const auto lastActiveTime = lastActiveTimes.find(name); + // clang-format off + d->lastActiveTimes.insert(name, lastActiveTime != lastActiveTimes.end() + ? lastActiveTime->toDateTime() + : info.lastModified()); + // clang-format on + if (info.baseName() != kDefaultSession) + d->sessionList << info.baseName(); + } + d->sessionList.prepend(kDefaultSession); + } + return d->sessionList; +} + +QDateTime SessionManager::sessionDateTime(const QString &session) +{ + return d->sessionDateTimes.value(session); +} + +QDateTime SessionManager::lastActiveTime(const QString &session) +{ + return d->lastActiveTimes.value(session); +} + +bool SessionManager::createSession(const QString &session) +{ + if (sessionList().contains(session)) + return false; + + Q_ASSERT(d->sessionList.size() > 0); + d->sessionList.insert(1, session); + d->lastActiveTimes.insert(session, QDateTime::currentDateTime()); + Q_EMIT sessionCreated(session); + return true; +} + +bool SessionManager::removeSession(const QString &session) +{ + if (!d->sessionList.contains(session)) + return false; + + d->sessionList.removeOne(session); + d->lastActiveTimes.remove(session); + Q_EMIT sessionRemoved(session); + + const auto &file = sessionFile(session); + if (QFile::exists(file)) + return QFile::remove(file); + return false; +} + +void SessionManager::removeSessions(const QStringList &sessions) +{ + for (const auto &session : sessions) + removeSession(session); +} + +bool SessionManager::renameSession(const QString &oldName, const QString &newName) +{ + if (!cloneSession(oldName, newName)) + return false; + if (oldName == currentSession()) + loadSession(newName); + Q_EMIT sessionRenamed(oldName, newName); + return removeSession(oldName); +} + +bool SessionManager::cloneSession(const QString &select, const QString &clone) +{ + if (!d->sessionList.contains(select)) + return false; + + QFile file(sessionFile(select)); + if (!file.exists() || file.copy(sessionFile(clone))) { + d->sessionList.insert(1, clone); + d->sessionDateTimes.insert(clone, QFileInfo(sessionFile(clone)).lastModified()); + Q_EMIT sessionCreated(clone); + return true; + } + return false; +} + +void SessionManager::showSessionManager() +{ + saveSession(); + SessionDialog dlg(Controller::instance()->mainWindow()); + dlg.setAutoLoadSession(d->isAutoLoad); + dlg.exec(); + d->setAutoLoadLastSession(dlg.autoLoadSession()); +} + +void SessionManager::setValue(const QString &key, const QVariant &value) +{ + if (d->values.value(key) == value) + return; + d->values.insert(key, value); +} + +QVariant SessionManager::value(const QString &key) +{ + return d->values.value(key, QVariant()); +} + +bool SessionManager::loadSession(const QString &session) +{ + if (session == d->currentSession && !isDefaultVirgin()) + return true; + + bool isEmptySession = session.isEmpty(); + if (!isEmptySession && !sessionList().contains(session)) + return false; + + const auto &cfgFile = sessionFile(isEmptySession ? kDefaultSession : session); + if (QFile::exists(cfgFile) && isEmptySession) { + d->restoreValues(kDefaultSession); + Q_EMIT sessionLoaded(kDefaultSession); + return true; + } else if (isEmptySession) { + Q_EMIT sessionLoaded(kDefaultSession); + return true; + } + + d->isSessionLoading = true; + if (!isDefaultVirgin()) + saveSession(); + + d->virginSession = false; + d->values.clear(); + d->currentSession = session; + if (QFile::exists(cfgFile)) + d->restoreValues(session); + d->lastActiveTimes.insert(session, QDateTime::currentDateTime()); + Q_EMIT sessionLoaded(session); + + d->isSessionLoading = false; + return true; +} + +bool SessionManager::saveSession() +{ + Q_EMIT readyToSaveSession(); + + const auto &cfgFile = sessionFile(d->currentSession); + Settings settings("", cfgFile); + auto iter = d->values.cbegin(); + for (; iter != d->values.cend(); ++iter) { + settings.setValue(kSessionGroup, iter.key(), iter.value()); + } + + return true; +} + +bool SessionManager::isDefaultSession(const QString &session) +{ + return session == kDefaultSession; +} + +bool SessionManager::isSessionLoading() +{ + return d->isSessionLoading; +} + +bool SessionManager::isDefaultVirgin() +{ + return isDefaultSession(d->currentSession) && d->virginSession; +} + +bool SessionManager::isAutoLoadLastSession() +{ + return d->isAutoLoad; +} + +QString SessionManager::sessionFile(const QString &session) +{ + QString format = "%1/%2.session"; + return format.arg(d->sessionConfigDir(), session); +} diff --git a/src/plugins/core/session/sessionmanager.h b/src/plugins/core/session/sessionmanager.h new file mode 100644 index 000000000..b0656fdf0 --- /dev/null +++ b/src/plugins/core/session/sessionmanager.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SESSIONMANAGER_H +#define SESSIONMANAGER_H + +#include + +class SessionManagerPrivate; +class SessionManager : public QObject +{ + Q_OBJECT +public: + static SessionManager *instance(); + + QString currentSession(); + QString lastSession(); + QStringList sessionList(); + QDateTime sessionDateTime(const QString &session); + QDateTime lastActiveTime(const QString &session); + + bool createSession(const QString &session); + bool removeSession(const QString &session); + void removeSessions(const QStringList &sessions); + bool renameSession(const QString &oldName, const QString &newName); + bool cloneSession(const QString &select, const QString &clone); + void showSessionManager(); + + void setValue(const QString &key, const QVariant &value); + QVariant value(const QString &key); + + bool loadSession(const QString &session); + bool saveSession(); + bool isDefaultSession(const QString &session); + bool isSessionLoading(); + bool isDefaultVirgin(); + bool isAutoLoadLastSession(); + + QString sessionFile(const QString &session); + +Q_SIGNALS: + void readyToSaveSession(); + void sessionLoaded(const QString &session); + + void sessionCreated(const QString &session); + void sessionRenamed(const QString &oldName, const QString &newName); + void sessionRemoved(const QString &session); + +private: + SessionManager(QObject *parent = nullptr); + ~SessionManager(); + + SessionManagerPrivate *const d; +}; + +#endif // SESSIONMANAGER_H diff --git a/src/plugins/core/session/sessionmodel.cpp b/src/plugins/core/session/sessionmodel.cpp new file mode 100644 index 000000000..e606b6bea --- /dev/null +++ b/src/plugins/core/session/sessionmodel.cpp @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "sessionmodel.h" + +#include "sessionmanager.h" + +#include +#include + +SessionModel::SessionModel(QObject *parent) + : QAbstractTableModel(parent) +{ + sessionList = SessionManager::instance()->sessionList(); +} + +int SessionModel::rowOfSession(const QString &session) const +{ + return sessionList.indexOf(session); +} + +QString SessionModel::sessionAt(int row) const +{ + return sessionList.value(row, QString()); +} + +int SessionModel::rowCount(const QModelIndex &parent) const +{ + return sessionList.size(); +} + +int SessionModel::columnCount(const QModelIndex &parent) const +{ + return 2; +} + +QVariant SessionModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + const auto &sessionName = sessionList.at(index.row()); + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case 0: + return sessionName; + case 1: + return SessionManager::instance()->sessionDateTime(sessionName); + } + break; + case Qt::FontRole: { + auto instance = SessionManager::instance(); + QFont font; + font.setItalic(instance->isDefaultSession(sessionName)); + if (instance->currentSession() == sessionName && !instance->isDefaultVirgin()) + font.setBold(true); + else + font.setBold(false); + return font; + } + default: + break; + } + return {}; +} + +QVariant SessionModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) { + if (role == Qt::DisplayRole) { + return section == 0 ? tr("Session") : tr("Last Modified"); + } + } + return QVariant(); +} + +void SessionModel::sort(int column, Qt::SortOrder order) +{ + beginResetModel(); + qSort(sessionList.begin(), sessionList.end(), + [column, order](const QString &s1, const QString &s2) { + bool ret = false; + if (column == 0) { + ret = s1 < s2; + } else { + const auto &time1 = SessionManager::instance()->sessionDateTime(s1); + const auto &time2 = SessionManager::instance()->sessionDateTime(s2); + ret = time1 < time2; + } + return order == Qt::AscendingOrder ? ret : !ret; + }); + + sortColumn = column; + sortOrder = order; + endResetModel(); +} + +void SessionModel::reset() +{ + beginResetModel(); + sessionList = SessionManager::instance()->sessionList(); + endResetModel(); + sort(sortColumn, sortOrder); +} diff --git a/src/plugins/core/session/sessionmodel.h b/src/plugins/core/session/sessionmodel.h new file mode 100644 index 000000000..a50686214 --- /dev/null +++ b/src/plugins/core/session/sessionmodel.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SESSIONMODEL_H +#define SESSIONMODEL_H + +#include + +class SessionModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit SessionModel(QObject *parent = nullptr); + + int rowOfSession(const QString &session) const; + QString sessionAt(int row) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + +public Q_SLOTS: + void reset(); + +private: + QStringList sessionList; + int sortColumn { 0 }; + Qt::SortOrder sortOrder { Qt::AscendingOrder }; +}; + +#endif // SESSIONMODEL_H diff --git a/src/plugins/core/uicontroller/controller.cpp b/src/plugins/core/uicontroller/controller.cpp index aef2477ef..17abfe23d 100644 --- a/src/plugins/core/uicontroller/controller.cpp +++ b/src/plugins/core/uicontroller/controller.cpp @@ -17,6 +17,7 @@ #include "modules/contextmodule.h" #include "modules/notificationmodule.h" #include "modules/dependencemodule.h" +#include "modules/sessionmanagermodule.h" #include "locator/locatormanager.h" #include "find/placeholdermanager.h" @@ -198,6 +199,7 @@ Controller::Controller(QObject *parent) registerModule("contextModule", new ContextModule()); registerModule("notifyModule", new NotificationModule()); registerModule("dependenceModule", new DependenceModule()); + registerModule("sessionManagerModule", new SessionManagerModule()); initModules(); } diff --git a/src/plugins/core/uicontroller/mainwindow.cpp b/src/plugins/core/uicontroller/mainwindow.cpp index 39b2ffcf0..cc916acfb 100644 --- a/src/plugins/core/uicontroller/mainwindow.cpp +++ b/src/plugins/core/uicontroller/mainwindow.cpp @@ -63,8 +63,6 @@ MainWindow::MainWindow(QWidget *parent) titlebar()->setFocusPolicy(Qt::NoFocus); setWindowIcon(QIcon::fromTheme("ide")); - setAttribute(Qt::WA_DeleteOnClose); - setStyle(new CustomStyle()); addTopToolBar(); setContextMenuPolicy(Qt::NoContextMenu); //donot show left toolbar`s contextmenu diff --git a/src/plugins/debugger/dap/dapdebugger.cpp b/src/plugins/debugger/dap/dapdebugger.cpp index 1ddaf04de..5fe6536cc 100644 --- a/src/plugins/debugger/dap/dapdebugger.cpp +++ b/src/plugins/debugger/dap/dapdebugger.cpp @@ -23,6 +23,7 @@ #include "services/option/optionmanager.h" #include "services/language/languageservice.h" #include "services/window/windowservice.h" +#include "services/session/sessionservice.h" #include "unistd.h" #include "base/baseitemdelegate.h" @@ -49,6 +50,11 @@ #define PER_WAIT_MSEC (1000) #define MAX_WAIT_TIMES (10) +constexpr char kBreakpointList[] { "BreakpointList" }; +constexpr char kFileName[] { "FileName" }; +constexpr char kLineNumber[] { "LineNumber" }; +constexpr char kEnable[] { "Enabel" }; +constexpr char kBreakpointCondition[] { "Condition" }; /** * @brief Debugger::Debugger * For serial debugging service @@ -58,7 +64,7 @@ using namespace DEBUG_NAMESPACE; using namespace dpfservice; using DTK_WIDGET_NAMESPACE::DSpinner; -void notify(uint type, const QString &message) // type 0-infomation, 1-warning, 2-error +void notify(uint type, const QString &message) // type 0-infomation, 1-warning, 2-error { auto wdService = dpfGetService(WindowService); wdService->notify(type, QObject::tr("Debug"), message, {}); @@ -121,11 +127,11 @@ class DebuggerPrivate QProcess backend; - QMultiMap bps; bool isRemote = false; RemoteInfo remoteInfo; DAPDebugger::debugState debugState = DAPDebugger::Normal; + SessionService *sessionSrv = nullptr; }; DebuggerPrivate::~DebuggerPrivate() @@ -135,8 +141,7 @@ DebuggerPrivate::~DebuggerPrivate() } DAPDebugger::DAPDebugger(QObject *parent) - : AbstractDebugger(parent) - , d(new DebuggerPrivate()) + : AbstractDebugger(parent), d(new DebuggerPrivate()) { qRegisterMetaType("OutputPane::OutputFormat"); qRegisterMetaType("StackFrameData"); @@ -147,6 +152,7 @@ DAPDebugger::DAPDebugger(QObject *parent) qRegisterMetaType("dpf::Event"); qRegisterMetaType("RunState"); + d->sessionSrv = dpfGetService(SessionService); d->localSession = new DebugSession(debugService->getModel(), this); d->currentSession = d->localSession; connect(d->currentSession, &DebugSession::sigRegisterHandlers, this, &DAPDebugger::registerDapHandlers); @@ -165,7 +171,7 @@ DAPDebugger::DAPDebugger(QObject *parent) "/path", "com.deepin.unioncode.interface", "output", - this, SLOT(slotOutputMsg(const QString&, const QString&))); + this, SLOT(slotOutputMsg(const QString &, const QString &))); initializeView(); @@ -231,7 +237,7 @@ void DAPDebugger::startRerverseDebug(const QString &target) d->currentSession = d->localSession; updateRunState(kPreparing); - + QMap param; param.insert("program", "rr"); @@ -241,9 +247,9 @@ void DAPDebugger::startRerverseDebug(const QString &target) "getDebugPort"); msg << d->requestDAPPortPpid - << "cmake" //rr only support c/c++ + << "cmake" //rr only support c/c++ << "" - << QStringList{target}; + << QStringList { target }; bool ret = QDBusConnection::sessionBus().send(msg); if (!ret) { @@ -256,18 +262,18 @@ void DAPDebugger::startDebugRemote(const RemoteInfo &info) { d->remoteInfo = info; d->isRemote = true; - + if (d->remoteSession) delete d->remoteSession; - + d->remoteSession = new DebugSession(debugService->getModel(), this); d->remoteSession->setRemote(true); d->remoteSession->setLocalProjectPath(getActiveProjectInfo().workspaceFolder()); d->remoteSession->setRemoteProjectPath(info.projectPath); - + d->currentSession = d->remoteSession; connect(d->currentSession, &DebugSession::sigRegisterHandlers, this, &DAPDebugger::registerDapHandlers, Qt::DirectConnection); - + QMap param; param.insert("ip", info.ip); param.insert("workspace", info.projectPath); @@ -275,7 +281,7 @@ void DAPDebugger::startDebugRemote(const RemoteInfo &info) prepareDebug(); launchSession(info.port, param, d->activeProjectKitName); - + updateRunState(kPreparing); } @@ -301,7 +307,7 @@ void DAPDebugger::attachDebug(const QString &processId) QString debuggerTool = prjInfo.debugProgram(); if (!debuggerTool.contains("gdb")) { auto msg = tr("The gdb is required, please install it in console with \"sudo apt install gdb\", " - "and then restart the tool, reselect the CMake Debugger in Options Dialog..."); + "and then restart the tool, reselect the CMake Debugger in Options Dialog..."); printOutput(msg, OutputPane::OutputFormat::ErrorMessage); return; } @@ -431,6 +437,14 @@ DAPDebugger::RunState DAPDebugger::getRunState() const void DAPDebugger::addBreakpoint(const QString &filePath, int lineNumber) { + const auto &bps = d->breakpointModel.breakpointList(); + bool ret = std::any_of(bps.cbegin(), bps.cend(), + [&](const BreakpointItem &bp) { + return filePath == bp.filePath() && lineNumber == bp.lineNumber(); + }); + if (ret) + return; + // update model here. Internal::Breakpoint bp; bp.filePath = filePath; @@ -470,6 +484,13 @@ void DAPDebugger::removeBreakpoint(const QString &filePath, int lineNumber) } } +void DAPDebugger::removeAllBreakpoints() +{ + const auto &bps = d->breakpointModel.breakpointList(); + for (const auto &bp : bps) + removeBreakpoint(bp.filePath(), bp.lineNumber()); +} + void DAPDebugger::switchBreakpointsStatus(const QString &filePath, int lineNumber, bool enabled) { Internal::Breakpoint bp; @@ -495,7 +516,7 @@ void DAPDebugger::setBreakpointCondition(const QString &filePath, int lineNumber bp.lineNumber = lineNumber; bp.condition = expression; d->breakpointModel.setBreakpointCondition(bp); - + // send to backend. if (d->runState == kStopped || d->runState == kRunning) { debugService->setBreakpointCondition(filePath, lineNumber, expression, d->currentSession); @@ -628,7 +649,7 @@ void DAPDebugger::registerDapHandlers() if (event.reason == "signal-received" && event.description.has_value()) { signalStopped = true; auto signalName = QString::fromStdString(event.description.value().c_str()); - if (signalName == "SIGSEGV") { // Segmentation fault + if (signalName == "SIGSEGV") { // Segmentation fault auto signalMeaning = event.text.has_value() ? event.text.value().c_str() : ""; QMetaObject::invokeMethod(this, "showStoppedBySignalMessageBox", Q_ARG(QString, QString::fromStdString(signalMeaning)), Q_ARG(QString, signalName)); @@ -640,15 +661,15 @@ void DAPDebugger::registerDapHandlers() bool attaching = d->debugState == Attaching; // ui focus on the active frame. if (event.reason == "function breakpoint" - || event.reason == "breakpoint" - || event.reason == "step" - || event.reason == "breakpoint-hit" - || event.reason == "function-finished" - || event.reason == "end-stepping-range" - || event.reason == "goto" - || event.reason == "pause" // python send it when pauseing - || signalStopped - || (event.reason == "unknown" && attaching)) { + || event.reason == "breakpoint" + || event.reason == "step" + || event.reason == "breakpoint-hit" + || event.reason == "function-finished" + || event.reason == "end-stepping-range" + || event.reason == "goto" + || event.reason == "pause" // python send it when pauseing + || signalStopped + || (event.reason == "unknown" && attaching)) { //when attaching to running program . it won`t receive initialized event and event`s reason is "unknwon" //so initial breakpoints in here if (attaching) { @@ -756,7 +777,7 @@ void DAPDebugger::registerDapHandlers() QString output = event.output.c_str(); if (output.contains("received signal") - || output.contains("Program")) { + || output.contains("Program")) { format = OutputPane::OutputFormat::StdErr; } printOutput(output, format); @@ -891,14 +912,10 @@ void DAPDebugger::handleEvents(const dpf::Event &event) } else if (event.data() == editor.breakpointAdded.name) { QString filePath = event.property(editor.breakpointAdded.pKeys[0]).toString(); int line = event.property(editor.breakpointAdded.pKeys[1]).toInt() + 1; - if (d->bps.contains(filePath) && d->bps.values(filePath).contains(line)) - return; - d->bps.insert(filePath, line); addBreakpoint(filePath, line); } else if (event.data() == editor.breakpointRemoved.name) { QString filePath = event.property(editor.breakpointRemoved.pKeys[0]).toString(); int line = event.property(editor.breakpointRemoved.pKeys[1]).toInt() + 1; - d->bps.remove(filePath, line); removeBreakpoint(filePath, line); } else if (event.data() == editor.breakpointStatusChanged.name) { QString filePath = event.property("fileName").toString(); @@ -908,7 +925,7 @@ void DAPDebugger::handleEvents(const dpf::Event &event) } else if (event.data() == editor.setBreakpointCondition.name) { QString filePath = event.property("fileName").toString(); int line = event.property("line").toInt() + 1; - + DDialog condition; DLineEdit *edit = new DLineEdit(d->breakpointView); edit->setPlaceholderText(tr("Input Condition Expression")); @@ -932,6 +949,33 @@ void DAPDebugger::handleEvents(const dpf::Event &event) int line = event.property("line").toInt() + 1; runToLine(filePath, line); + } else if (event.data() == session.readyToSaveSession.name) { + QVariantList bpList; + const auto &bps = d->breakpointModel.breakpointList(); + for (const auto &bp : bps) { + QVariantMap map; + map.insert(kFileName, bp.filePath()); + map.insert(kLineNumber, bp.lineNumber()); + map.insert(kEnable, bp.isEnabled()); + map.insert(kBreakpointCondition, bp.condition()); + bpList << map; + } + d->sessionSrv->setValue(kBreakpointList, bpList); + } else if (event.data() == session.sessionLoaded.name) { + removeAllBreakpoints(); + const auto bps = d->sessionSrv->value(kBreakpointList).toList(); + for (const auto &bp : bps) { + const auto &map = bp.toMap(); + const auto fileName = map.value(kFileName).toString(); + const int lineNumber = map.value(kLineNumber).toInt(); + const bool enable = map.value(kEnable).toBool(); + const auto condition = map.value(kBreakpointCondition).toString(); + addBreakpoint(fileName, lineNumber); + if (!enable) + switchBreakpointsStatus(fileName, lineNumber, enable); + if (!condition.isEmpty()) + setBreakpointCondition(fileName, lineNumber, condition); + } } } @@ -974,13 +1018,13 @@ void DAPDebugger::handleFrames(const StackFrames &stackFrames) d->localsModel.clear(); d->currentValidFrame = curFrame; - if(d->getLocalsFuture.isRunning()) + if (d->getLocalsFuture.isRunning()) d->getLocalsFuture.cancel(); // update local variables. - d->processingVariablesTimer.start(50); // if processing time < 50ms, do not show spinner + d->processingVariablesTimer.start(50); // if processing time < 50ms, do not show spinner d->processingVariablesCount.ref(); - d->getLocalsFuture = QtConcurrent::run([=](){ + d->getLocalsFuture = QtConcurrent::run([=]() { IVariables locals; getLocals(curFrame.frameId, &locals); d->localsModel.clearHighlightItems(); @@ -996,7 +1040,7 @@ void DAPDebugger::updateThreadList(int curr, const dap::array &thre { d->threadSelector->clear(); int currIdx = -1; - for (const auto& e: threads) { + for (const auto &e : threads) { QString itemText = "#" + QString::number(e.id) + " " + e.name.c_str(); d->threadSelector->addItem(itemText); if (curr == e.id) @@ -1086,7 +1130,7 @@ bool DAPDebugger::showStoppedBySignalMessageBox(QString meaning, QString name) "signal from the operating system.

" "" "
Signal name : %1
Signal meaning : %2
") - .arg(name, meaning); + .arg(name, meaning); d->alertBox = Internal::information(tr("Signal Received"), msg); return true; @@ -1098,7 +1142,7 @@ void DAPDebugger::slotFrameSelected() d->processingVariablesTimer.start(50); d->processingVariablesCount.ref(); auto curFrame = d->stackModel.currentFrame(); - QtConcurrent::run([=](){ + QtConcurrent::run([=]() { IVariables locals; getLocals(curFrame.frameId, &locals); d->localsModel.clearHighlightItems(); @@ -1117,20 +1161,20 @@ void DAPDebugger::slotBreakpointSelected(const QModelIndex &index) void DAPDebugger::slotGetChildVariable(const QModelIndex &index) { - auto treeItem = static_cast(index.internalPointer()); - if(!treeItem->canFetchChildren() || (!d->localsView->isExpanded(index) && !d->watchsView->isExpanded(index))) + auto treeItem = static_cast(index.internalPointer()); + if (!treeItem->canFetchChildren() || (!d->localsView->isExpanded(index) && !d->watchsView->isExpanded(index))) return; treeItem->setChildrenFetched(true); - if (treeItem->childReference() == 0) { //clear child variables + if (treeItem->childReference() == 0) { //clear child variables emit childVariablesUpdated(treeItem, {}); return; } d->processingVariablesTimer.start(50); d->processingVariablesCount.ref(); - QtConcurrent::run([=](){ + QtConcurrent::run([=]() { IVariables variables; d->currentSession->getVariables(treeItem->childReference(), &variables, 0); @@ -1202,12 +1246,12 @@ void DAPDebugger::initializeView() initializeVairablesPane(); - connect(&d->processingVariablesTimer, &QTimer::timeout, this, [=](){ + connect(&d->processingVariablesTimer, &QTimer::timeout, this, [=]() { d->variablesSpinner->show(); d->variablesSpinner->raise(); d->variablesSpinner->move(d->localsView->width() / 2 - d->variablesSpinner->width(), d->localsView->height() / 3); }); - connect(this, &DAPDebugger::processingVariablesDone, this, [=](){ + connect(this, &DAPDebugger::processingVariablesDone, this, [=]() { if (d->processingVariablesCount != 0) return; d->processingVariablesTimer.stop(); @@ -1229,15 +1273,15 @@ void DAPDebugger::initializeView() connect(&d->stackModel, &StackFrameModel::currentIndexChanged, this, &DAPDebugger::slotFrameSelected); connect(d->breakpointView, &QTreeView::doubleClicked, this, &DAPDebugger::slotBreakpointSelected); - + connect(d->localsView, &QTreeView::expanded, this, &DAPDebugger::slotGetChildVariable); - connect(this, &DAPDebugger::childVariablesUpdated, d->localsView, [=](LocalTreeItem *treeItem, IVariables vars){ + connect(this, &DAPDebugger::childVariablesUpdated, d->localsView, [=](LocalTreeItem *treeItem, IVariables vars) { d->localsModel.appendItem(treeItem, vars); }); connect(&d->localsModel, &LocalTreeModel::updateChildVariables, this, &DAPDebugger::slotGetChildVariable); connect(d->watchsView, &QTreeView::expanded, this, &DAPDebugger::slotGetChildVariable); - connect(this, &DAPDebugger::childVariablesUpdated, d->watchsView, [=](LocalTreeItem *treeItem, IVariables vars){ + connect(this, &DAPDebugger::childVariablesUpdated, d->watchsView, [=](LocalTreeItem *treeItem, IVariables vars) { d->watchsModel.appendItem(treeItem, vars); }); connect(&d->watchsModel, &LocalTreeModel::updateChildVariables, this, &DAPDebugger::slotGetChildVariable); @@ -1269,7 +1313,7 @@ void DAPDebugger::initializeVairablesPane() menu->addAction(evaluateWatchVariable); menu->addAction(remove); - connect(d->watchsView, &DTreeView::customContextMenuRequested, this, [=](const QPoint &pos){ + connect(d->watchsView, &DTreeView::customContextMenuRequested, this, [=](const QPoint &pos) { auto index = d->watchsView->indexAt(pos); if (index.isValid() && d->watchsView->selectionModel()->selectedRows().size() == 1) remove->setEnabled(true); @@ -1281,7 +1325,7 @@ void DAPDebugger::initializeVairablesPane() connect(evaluateWatchVariable, &QAction::triggered, this, &DAPDebugger::slotEvaluateWatchVariable); connect(remove, &QAction::triggered, this, &DAPDebugger::slotRemoveEvaluator); - QStringList headers { tr("Name"), tr("Value"), tr("Type")/*, "Reference" */}; + QStringList headers { tr("Name"), tr("Value"), tr("Type") /*, "Reference" */ }; d->localsModel.setHeaders(headers); d->watchsModel.setHeaders(headers); @@ -1485,7 +1529,7 @@ void DAPDebugger::launchSession(int port, const QMap ¶m, printOutput(tr("Debugging starts")); QString launchTip = tr("Launch dap session with port %1 ...") - .arg(port); + .arg(port); printOutput(launchTip); d->currentDebugKit = kitName; @@ -1493,12 +1537,12 @@ void DAPDebugger::launchSession(int port, const QMap ¶m, bool bSuccess = false; if (!d->isRemote) bSuccess = d->currentSession->initialize(d->rtCfgProvider->ip(), - port, - iniRequet); + port, + iniRequet); else bSuccess = d->currentSession->initialize(param.value("ip").toString().toLatin1(), - port, - iniRequet); + port, + iniRequet); if (!bSuccess) { updateRunState(DAPDebugger::RunState::kNoRun); @@ -1509,53 +1553,53 @@ void DAPDebugger::launchSession(int port, const QMap ¶m, // Launch debuggee. switch (d->debugState) { - case Normal: { - auto &ctx = dpfInstance.serviceContext(); - LanguageService *service = ctx.service(LanguageService::name()); - if (service) { - auto generator = service->create(kitName); - if (generator) { - if (generator->isLaunchNotAttach()) { - dap::LaunchRequest request = generator->launchDAP(param); - bSuccess &= d->currentSession->launch(request); - } else { - dap::AttachRequest request = generator->attachDAP(port, param); - bSuccess &= d->currentSession->attach(request); - } + case Normal: { + auto &ctx = dpfInstance.serviceContext(); + LanguageService *service = ctx.service(LanguageService::name()); + if (service) { + auto generator = service->create(kitName); + if (generator) { + if (generator->isLaunchNotAttach()) { + dap::LaunchRequest request = generator->launchDAP(param); + bSuccess &= d->currentSession->launch(request); + } else { + dap::AttachRequest request = generator->attachDAP(port, param); + bSuccess &= d->currentSession->attach(request); } - } else { - bSuccess &= false; } - break; - } - case Attaching: { - dap::object obj; - obj["processId"] = param.value("targetPath").toString().toStdString(); - dap::AttachRequest request; - request.name = kitName.toStdString(); //kitName: gdb - request.connect = obj; - bSuccess &= d->currentSession->attach(request); - break; + } else { + bSuccess &= false; } - case Reverse: { - dap::LaunchRequest request; - request.name = "rr"; - request.type = "cppdbg"; - request.request = "launch"; - request.program = ""; // targetPath.toStdString(); - request.stopAtEntry = false; - dap::array arrayArg; - foreach (QString arg, param["arguments"].toStringList()) { - arrayArg.push_back(arg.toStdString()); - } - request.args = arrayArg; - request.externalConsole = false; - request.MIMode = "gdb"; - request.__sessionId = QUuid::createUuid().toString().toStdString(); - bSuccess &= d->currentSession->launch(request); + break; + } + case Attaching: { + dap::object obj; + obj["processId"] = param.value("targetPath").toString().toStdString(); + dap::AttachRequest request; + request.name = kitName.toStdString(); //kitName: gdb + request.connect = obj; + bSuccess &= d->currentSession->attach(request); + break; + } + case Reverse: { + dap::LaunchRequest request; + request.name = "rr"; + request.type = "cppdbg"; + request.request = "launch"; + request.program = ""; // targetPath.toStdString(); + request.stopAtEntry = false; + dap::array arrayArg; + foreach (QString arg, param["arguments"].toStringList()) { + arrayArg.push_back(arg.toStdString()); } - default: - break; + request.args = arrayArg; + request.externalConsole = false; + request.MIMode = "gdb"; + request.__sessionId = QUuid::createUuid().toString().toStdString(); + bSuccess &= d->currentSession->launch(request); + } + default: + break; } if (!bSuccess) { @@ -1565,10 +1609,10 @@ void DAPDebugger::launchSession(int port, const QMap ¶m, } else { debugService->getModel()->clear(); debugService->getModel()->addSession(d->currentSession); - + auto appOutPutPane = AppOutputPane::instance(); appOutPutPane->createApplicationPane("debugPane", "debugTarget"); - appOutPutPane->setStopHandler("debugPane", [=](){ + appOutPutPane->setStopHandler("debugPane", [=]() { abortDebug(); d->outputPane = appOutPutPane->defaultPane(); }); @@ -1580,7 +1624,7 @@ void DAPDebugger::launchSession(int port, const QMap ¶m, void DAPDebugger::currentThreadChanged(const QString &text) { - QtConcurrent::run([&]() { // run in thread to avoid blocked when get variables. + QtConcurrent::run([&]() { // run in thread to avoid blocked when get variables. QStringList l = text.split("#"); QString threadNumber = l.last().split(" ").first(); switchCurrentThread(threadNumber.toInt()); @@ -1609,7 +1653,7 @@ bool DAPDebugger::runCoredump(const QString &target, const QString &core, const QMap param; param.insert("targetPath", target); - param.insert("arguments", QStringList{core}); + param.insert("arguments", QStringList { core }); d->userKitName = kit; return requestDebugPort(param, d->userKitName, true); @@ -1627,8 +1671,7 @@ void DAPDebugger::handleAssemble(const QString &content) if (content.isEmpty()) return; - QString assemblerPath = CustomPaths::user(CustomPaths::Flags::Configures) + - QDir::separator() + QString("Disassembler"); + QString assemblerPath = CustomPaths::user(CustomPaths::Flags::Configures) + QDir::separator() + QString("Disassembler"); QFile file(assemblerPath); if (file.open(QIODevice::WriteOnly)) { @@ -1655,7 +1698,7 @@ void DAPDebugger::handleUpdateDebugLine() localFile = d->isRemote ? transformRemotePath(curFrame.file) : curFrame.file; if (QFileInfo(localFile).exists()) { - editor.setDebugLine(localFile, curFrame.line - 1); + editor.setDebugLine(localFile, curFrame.line - 1); } else if (!curFrame.address.isEmpty()) { disassemble(curFrame.address); } @@ -1666,14 +1709,14 @@ QString DAPDebugger::transformRemotePath(const QString &remotePath) { if (!d->isRemote || d->remoteInfo.projectPath.isEmpty()) return remotePath; - + auto prjInfo = getActiveProjectInfo(); auto workSpace = prjInfo.workspaceFolder(); - + QString ret = remotePath; if (remotePath.startsWith(d->remoteInfo.projectPath)) { ret.replace(0, d->remoteInfo.projectPath.size(), workSpace); - + QFileInfo info(ret); if (!info.exists()) qWarning() << ret << " is not exists!"; diff --git a/src/plugins/debugger/dap/dapdebugger.h b/src/plugins/debugger/dap/dapdebugger.h index 14a0ba57e..4df9150c5 100644 --- a/src/plugins/debugger/dap/dapdebugger.h +++ b/src/plugins/debugger/dap/dapdebugger.h @@ -113,6 +113,7 @@ public slots: void addBreakpoint(const QString &filepath, int lineNumber); void removeBreakpoint(const QString &filepath, int lineNumber); + void removeAllBreakpoints(); void switchBreakpointsStatus(const QString &filePath, int lineNumber, bool enabled); void setBreakpointCondition(const QString &filePath, int lineNumber, const QString &expression); void jumpToLine(const QString &filePath, int lineNumber); diff --git a/src/plugins/debugger/event/eventreceiver.cpp b/src/plugins/debugger/event/eventreceiver.cpp index 77c4ea86f..ea2b08c8a 100644 --- a/src/plugins/debugger/event/eventreceiver.cpp +++ b/src/plugins/debugger/event/eventreceiver.cpp @@ -19,10 +19,31 @@ dpf::EventHandler::Type DebugEventReceiver::type() QStringList DebugEventReceiver::topics() { - return {T_BUILDER, project.topic, debugger.topic, editor.topic}; + return { T_BUILDER, project.topic, debugger.topic, editor.topic }; } void DebugEventReceiver::eventProcess(const dpf::Event &event) { emit debuggerSignals->receivedEvent(event); } + +SyncDebugEventReceiver::SyncDebugEventReceiver(QObject *parent) + : dpf::EventHandler(parent), + dpf::AutoEventHandlerRegister() +{ +} + +dpf::EventHandler::Type SyncDebugEventReceiver::type() +{ + return dpf::EventHandler::Type::Sync; +} + +QStringList SyncDebugEventReceiver::topics() +{ + return { session.topic }; +} + +void SyncDebugEventReceiver::eventProcess(const dpf::Event &event) +{ + emit debuggerSignals->receivedEvent(event); +} diff --git a/src/plugins/debugger/event/eventreceiver.h b/src/plugins/debugger/event/eventreceiver.h index 667c5210f..deaf7bd07 100644 --- a/src/plugins/debugger/event/eventreceiver.h +++ b/src/plugins/debugger/event/eventreceiver.h @@ -17,9 +17,18 @@ class DebugEventReceiver : public dpf::EventHandler, dpf::AutoEventHandlerRegist static Type type(); static QStringList topics(); -signals: +private: + virtual void eventProcess(const dpf::Event &event) override; +}; + +class SyncDebugEventReceiver : public dpf::EventHandler, dpf::AutoEventHandlerRegister +{ + friend class dpf::AutoEventHandlerRegister; -public slots: +public: + explicit SyncDebugEventReceiver(QObject *parent = nullptr); + static Type type(); + static QStringList topics(); private: virtual void eventProcess(const dpf::Event &event) override; diff --git a/src/plugins/debugger/interface/breakpointitem.cpp b/src/plugins/debugger/interface/breakpointitem.cpp index 0ddf7eeeb..e1008bbc5 100644 --- a/src/plugins/debugger/interface/breakpointitem.cpp +++ b/src/plugins/debugger/interface/breakpointitem.cpp @@ -15,7 +15,6 @@ const QString empty(QLatin1Char('-')); BreakpointItem::BreakpointItem(const Internal::Breakpoint &_bp) : bp(_bp) { - } BreakpointItem::BreakpointItem(const BreakpointItem &item) @@ -29,25 +28,24 @@ BreakpointItem::BreakpointItem() BreakpointItem::~BreakpointItem() { - } QVariant BreakpointItem::data(int row, int column, int role) const { if (role == Qt::DisplayRole) { switch (column) { - case kIndexColumn: - return QString::number(row + 1); - case kFunctionNameColumn: - return empty; - case kFileNameColumn: - return bp.fileName.isEmpty() ? empty : QDir::toNativeSeparators(bp.fileName); - case kLineNumberColumn: - return bp.lineNumber > 0 ? QString::number(bp.lineNumber) : empty; - case kCondition: - return bp.condition; - case kAddressColumn: - return bp.address; + case kIndexColumn: + return QString::number(row + 1); + case kFunctionNameColumn: + return empty; + case kFileNameColumn: + return bp.fileName.isEmpty() ? empty : QDir::toNativeSeparators(bp.fileName); + case kLineNumberColumn: + return bp.lineNumber > 0 ? QString::number(bp.lineNumber) : empty; + case kCondition: + return bp.condition; + case kAddressColumn: + return bp.address; } } @@ -84,23 +82,23 @@ static QString msgBreakpointAtSpecialFunc(const QString &func) static QString typeToString(BreakpointType type) { switch (type) { - case BreakpointByFileAndLine: - return BreakpointItem::tr("Breakpoint by File and Line"); - case BreakpointByFunction: - return BreakpointItem::tr("Breakpoint by Function"); - case BreakpointByAddress: - return BreakpointItem::tr("Breakpoint by Address"); - case BreakpointAtThrow: - return msgBreakpointAtSpecialFunc("throw"); - case BreakpointAtCatch: - return msgBreakpointAtSpecialFunc("catch"); - case BreakpointAtExec: - return msgBreakpointAtSpecialFunc("exec"); - //case BreakpointAtVFork: - // return msgBreakpointAtSpecialFunc("vfork"); - case UnknownBreakpointType: - case LastBreakpointType: - break; + case BreakpointByFileAndLine: + return BreakpointItem::tr("Breakpoint by File and Line"); + case BreakpointByFunction: + return BreakpointItem::tr("Breakpoint by Function"); + case BreakpointByAddress: + return BreakpointItem::tr("Breakpoint by Address"); + case BreakpointAtThrow: + return msgBreakpointAtSpecialFunc("throw"); + case BreakpointAtCatch: + return msgBreakpointAtSpecialFunc("catch"); + case BreakpointAtExec: + return msgBreakpointAtSpecialFunc("exec"); + //case BreakpointAtVFork: + // return msgBreakpointAtSpecialFunc("vfork"); + case UnknownBreakpointType: + case LastBreakpointType: + break; } return BreakpointItem::tr("Unknown Breakpoint Type"); } @@ -121,8 +119,8 @@ QString BreakpointItem::toolTip() const if (bp.type == BreakpointByFunction) { str << "" << BreakpointItem::tr("Function Name:") - << "" << bp.functionName - << ""; + << "" << bp.functionName + << ""; } if (bp.type == BreakpointByFileAndLine) { str << "" << BreakpointItem::tr("File Name:") @@ -140,10 +138,15 @@ QString BreakpointItem::toolTip() const str << bp.address; str << ""; - str << "


"; + str << "
"; return rc; } +QString BreakpointItem::filePath() const +{ + return bp.filePath; +} + int BreakpointItem::lineNumber() const { return bp.lineNumber; @@ -163,3 +166,8 @@ void BreakpointItem::setCondition(const QString &expression) { bp.condition = expression; } + +QString BreakpointItem::condition() const +{ + return bp.condition; +} diff --git a/src/plugins/debugger/interface/breakpointitem.h b/src/plugins/debugger/interface/breakpointitem.h index 17d3caefa..6059e7e2d 100644 --- a/src/plugins/debugger/interface/breakpointitem.h +++ b/src/plugins/debugger/interface/breakpointitem.h @@ -38,11 +38,13 @@ class BreakpointItem : public QObject QString displayName() const; QString toolTip() const; + QString filePath() const; int lineNumber() const; bool isEnabled() const; void setEnabled(bool on); void setCondition(const QString &expression); + QString condition() const; const Internal::Breakpoint &breakpoint() const {return bp;} diff --git a/src/plugins/debugger/interface/breakpointmodel.cpp b/src/plugins/debugger/interface/breakpointmodel.cpp index f1bde1bef..372477808 100644 --- a/src/plugins/debugger/interface/breakpointmodel.cpp +++ b/src/plugins/debugger/interface/breakpointmodel.cpp @@ -121,6 +121,11 @@ Internal::Breakpoint BreakpointModel::currentBreakpoint() const return bps.at(currentIndex).breakpoint(); } +const BreakpointItems &BreakpointModel::breakpointList() const +{ + return bps; +} + int BreakpointModel::rowCount(const QModelIndex &parent) const { // Since the stack is not a tree, row count is 0 for any valid parent diff --git a/src/plugins/debugger/interface/breakpointmodel.h b/src/plugins/debugger/interface/breakpointmodel.h index ceaef2b51..16e28cbb1 100644 --- a/src/plugins/debugger/interface/breakpointmodel.h +++ b/src/plugins/debugger/interface/breakpointmodel.h @@ -32,6 +32,7 @@ class BreakpointModel : public QAbstractTableModel QAbstractItemModel *model() { return this; } bool isContentsValid() const { return contentsValid; } Internal::Breakpoint currentBreakpoint() const; + const BreakpointItems &breakpointList() const; signals: void breakpointChanged(); void currentIndexChanged(); diff --git a/src/plugins/project/mainframe/projecttree.cpp b/src/plugins/project/mainframe/projecttree.cpp index b6ac28666..222ec3f10 100644 --- a/src/plugins/project/mainframe/projecttree.cpp +++ b/src/plugins/project/mainframe/projecttree.cpp @@ -105,6 +105,13 @@ ProjectTree::~ProjectTree() } } +void ProjectTree::clear() +{ + while (auto item = d->itemModel->item(0)) { + removeRootItem(item); + } +} + void ProjectTree::activeProjectInfo(const ProjectInfo &info) { int rowCount = d->itemModel->rowCount(); @@ -121,18 +128,14 @@ void ProjectTree::activeProjectInfo(const ProjectInfo &info) } } -void ProjectTree::activeProjectInfo(const QString &kitName, - const QString &language, - const QString &workspace) +void ProjectTree::activeProjectInfo(const QString &workspace) { int rowCount = d->itemModel->rowCount(); for (int currRow = 0; currRow < rowCount; currRow++) { auto currItem = d->itemModel->item(currRow, 0); if (currItem) { auto currInfo = ProjectInfo::get(ProjectGenerator::root(currItem)); - if (currInfo.language() == language - && currInfo.workspaceFolder() == workspace - && currInfo.kitName() == kitName) { + if (currInfo.workspaceFolder() == workspace) { doActiveProject(currItem); } } diff --git a/src/plugins/project/mainframe/projecttree.h b/src/plugins/project/mainframe/projecttree.h index a8c29d3bf..52faa6129 100644 --- a/src/plugins/project/mainframe/projecttree.h +++ b/src/plugins/project/mainframe/projecttree.h @@ -21,9 +21,9 @@ class ProjectTree : public DTreeView public: explicit ProjectTree(QWidget *parent = nullptr); ~ProjectTree() override; + void clear(); void activeProjectInfo(const dpfservice::ProjectInfo &info); - void activeProjectInfo(const QString &kitName, const QString &language, - const QString &workspace); + void activeProjectInfo(const QString &workspace); void appendRootItem(QStandardItem *root); void removeRootItem(QStandardItem *root); void takeRootItem(QStandardItem *root); diff --git a/src/plugins/project/transceiver/projectcorereceiver.cpp b/src/plugins/project/transceiver/projectcorereceiver.cpp index 0c6fd1691..5695dffa9 100644 --- a/src/plugins/project/transceiver/projectcorereceiver.cpp +++ b/src/plugins/project/transceiver/projectcorereceiver.cpp @@ -8,10 +8,24 @@ #include "mainframe/projectkeeper.h" #include "services/project/projectservice.h" #include "services/window/windowelement.h" +#include "services/session/sessionservice.h" + +using namespace dpfservice; ProjectCoreReceiver::ProjectCoreReceiver(QObject *parent) : dpf::EventHandler(parent), dpf::AutoEventHandlerRegister() { + using namespace std::placeholders; + eventHandleMap.insert(project.activeProject.name, std::bind(&ProjectCoreReceiver::processActiveProjectEvent, this, _1)); + eventHandleMap.insert(project.openProject.name, std::bind(&ProjectCoreReceiver::processOpenProjectEvent, this, _1)); + eventHandleMap.insert(project.activatedProject.name, std::bind(&ProjectCoreReceiver::processActivatedProjectEvent, this, _1)); + eventHandleMap.insert(project.openProjectByPath.name, std::bind(&ProjectCoreReceiver::processOpenProjectByPathEvent, this, _1)); + eventHandleMap.insert(workspace.expandAll.name, std::bind(&ProjectCoreReceiver::processExpandAllEvent, this, _1)); + eventHandleMap.insert(workspace.foldAll.name, std::bind(&ProjectCoreReceiver::processFoldAllEvent, this, _1)); + eventHandleMap.insert(editor.switchedFile.name, std::bind(&ProjectCoreReceiver::processSwitchedFileEvent, this, _1)); + eventHandleMap.insert(uiController.modeRaised.name, std::bind(&ProjectCoreReceiver::processModeRaisedEvent, this, _1)); + eventHandleMap.insert(session.sessionLoaded.name, std::bind(&ProjectCoreReceiver::processSessionLoadedEvent, this, _1)); + eventHandleMap.insert(session.readyToSaveSession.name, std::bind(&ProjectCoreReceiver::processReadyToSaveSessionEvent, this, _1)); } dpf::EventHandler::Type ProjectCoreReceiver::type() @@ -21,55 +35,123 @@ dpf::EventHandler::Type ProjectCoreReceiver::type() QStringList ProjectCoreReceiver::topics() { - return { project.topic, workspace.topic, editor.topic, uiController.topic }; //绑定menu 事件 + return { project.topic, workspace.topic, editor.topic, uiController.topic, session.topic }; //绑定menu 事件 } void ProjectCoreReceiver::eventProcess(const dpf::Event &event) { - using namespace dpfservice; - if (event.data() == project.activeProject.name) { - QString kitName = event.property("kitName").toString(); - QString language = event.property("language").toString(); - QString workspace = event.property("workspace").toString(); - ProjectKeeper::instance()->treeView()->activeProjectInfo(kitName, language, workspace); - } else if (event.data() == project.openProject.name) { - uiController.doSwitch(dpfservice::MWNA_EDIT); - auto &ctx = dpfInstance.serviceContext(); - ProjectService *projectService = ctx.service(ProjectService::name()); - if (projectService) { - // "kitName", "language", "workspace" - QString kitName = event.property(project.openProject.pKeys[0]).toString(); - QString language = event.property(project.openProject.pKeys[1]).toString(); - QString workspace = event.property(project.openProject.pKeys[2]).toString(); - auto generator = projectService->createGenerator(kitName); - if (!generator) - return; - QStringList supportLangs = generator->supportLanguages(); - if (supportLangs.contains(language)) { - if (generator->canOpenProject(kitName, language, workspace)) { - generator->doProjectOpen(kitName, language, workspace); - } else if (generator->isOpenedProject(kitName, language, workspace)) { - project.activeProject(kitName, language, workspace); - } + const auto &eventName = event.data().toString(); + if (!eventHandleMap.contains(eventName)) + return; + + eventHandleMap[eventName](event); +} + +void ProjectCoreReceiver::processActiveProjectEvent(const dpf::Event &event) +{ + QString workspace = event.property("workspace").toString(); + ProjectKeeper::instance()->treeView()->activeProjectInfo(workspace); +} + +void ProjectCoreReceiver::processOpenProjectEvent(const dpf::Event &event) +{ + uiController.doSwitch(dpfservice::MWNA_EDIT); + auto &ctx = dpfInstance.serviceContext(); + ProjectService *projectService = ctx.service(ProjectService::name()); + if (projectService) { + // "kitName", "language", "workspace" + QString kitName = event.property(project.openProject.pKeys[0]).toString(); + QString language = event.property(project.openProject.pKeys[1]).toString(); + QString workspace = event.property(project.openProject.pKeys[2]).toString(); + auto generator = projectService->createGenerator(kitName); + if (!generator) + return; + QStringList supportLangs = generator->supportLanguages(); + if (supportLangs.contains(language)) { + if (generator->canOpenProject(kitName, language, workspace)) { + generator->doProjectOpen(kitName, language, workspace); + } else if (generator->isOpenedProject(kitName, language, workspace)) { + project.activeProject(kitName, language, workspace); } } - } else if (event.data() == workspace.expandAll.name) { - ProjectKeeper::instance()->treeView()->expandAll(); - } else if (event.data() == workspace.foldAll.name) { - ProjectKeeper::instance()->treeView()->collapseAll(); - } else if (event.data() == project.activatedProject.name) { - QVariant proInfoVar = event.property("projectInfo"); - dpfservice::ProjectInfo projectInfo = qvariant_cast(proInfoVar); - - emit ProjectProxy::instance()->projectActivated(projectInfo); - } else if (event.data() == editor.switchedFile.name) { - emit ProjectProxy::instance()->switchedFile(event.property("fileName").toString()); - ProjectKeeper::instance()->treeView()->selectProjectFile(event.property("fileName").toString()); - } else if (event.data() == uiController.modeRaised.name) { - auto mode = event.property("mode").toString(); - emit ProjectProxy::instance()->modeRaised(mode); - } else if (event.data() == project.openProjectByPath.name) { - auto directory = event.property("directory").toString(); - emit ProjectProxy::instance()->openProject(directory); } } + +void ProjectCoreReceiver::processExpandAllEvent(const dpf::Event &event) +{ + ProjectKeeper::instance()->treeView()->expandAll(); +} + +void ProjectCoreReceiver::processFoldAllEvent(const dpf::Event &event) +{ + ProjectKeeper::instance()->treeView()->collapseAll(); +} + +void ProjectCoreReceiver::processActivatedProjectEvent(const dpf::Event &event) +{ + QVariant proInfoVar = event.property("projectInfo"); + dpfservice::ProjectInfo projectInfo = qvariant_cast(proInfoVar); + + emit ProjectProxy::instance()->projectActivated(projectInfo); +} + +void ProjectCoreReceiver::processSwitchedFileEvent(const dpf::Event &event) +{ + emit ProjectProxy::instance()->switchedFile(event.property("fileName").toString()); + ProjectKeeper::instance()->treeView()->selectProjectFile(event.property("fileName").toString()); +} + +void ProjectCoreReceiver::processModeRaisedEvent(const dpf::Event &event) +{ + auto mode = event.property("mode").toString(); + emit ProjectProxy::instance()->modeRaised(mode); +} + +void ProjectCoreReceiver::processOpenProjectByPathEvent(const dpf::Event &event) +{ + auto directory = event.property("directory").toString(); + emit ProjectProxy::instance()->openProject(directory); +} + +void ProjectCoreReceiver::processSessionLoadedEvent(const dpf::Event &event) +{ + auto sessionSrv = dpfGetService(SessionService); + auto view = ProjectKeeper::instance()->treeView(); + view->clear(); + + auto prjList = sessionSrv->value("ProjectList").toList(); + for (const auto &info : prjList) { + auto map = info.toMap(); + dpf::Event e(project.topic); + e.setProperty("kitName", map.value("KitName")); + e.setProperty("language", map.value("Language")); + e.setProperty("workspace", map.value("Workspace")); + + processOpenProjectEvent(e); + } + + if (prjList.size() > 1) { + auto activePrj = sessionSrv->value("ActiveProject").toString(); + view->activeProjectInfo(activePrj); + } +} + +void ProjectCoreReceiver::processReadyToSaveSessionEvent(const dpf::Event &event) +{ + auto sessionSrv = dpfGetService(SessionService); + Q_ASSERT(sessionSrv); + + QVariantList infoList; + auto view = ProjectKeeper::instance()->treeView(); + const auto &prjList = view->getAllProjectInfo(); + for (const auto &prj : prjList) { + QVariantMap map; + map.insert("KitName", prj.kitName()); + map.insert("Workspace", prj.workspaceFolder()); + map.insert("Language", prj.language()); + + infoList << map; + } + sessionSrv->setValue("ProjectList", infoList); + sessionSrv->setValue("ActiveProject", view->getActiveProjectInfo().workspaceFolder()); +} diff --git a/src/plugins/project/transceiver/projectcorereceiver.h b/src/plugins/project/transceiver/projectcorereceiver.h index 3c31d0540..4b9ba9e7b 100644 --- a/src/plugins/project/transceiver/projectcorereceiver.h +++ b/src/plugins/project/transceiver/projectcorereceiver.h @@ -12,24 +12,38 @@ class ProjectCoreReceiver : public dpf::EventHandler, dpf::AutoEventHandlerRegis { Q_OBJECT friend class dpf::AutoEventHandlerRegister; + public: - explicit ProjectCoreReceiver(QObject * parent = nullptr); + explicit ProjectCoreReceiver(QObject *parent = nullptr); static Type type(); - static QStringList topics(); - - virtual void eventProcess(const dpf::Event& event) override; + virtual void eventProcess(const dpf::Event &event) override; + +private: + void processActiveProjectEvent(const dpf::Event &event); + void processOpenProjectEvent(const dpf::Event &event); + void processExpandAllEvent(const dpf::Event &event); + void processFoldAllEvent(const dpf::Event &event); + void processActivatedProjectEvent(const dpf::Event &event); + void processSwitchedFileEvent(const dpf::Event &event); + void processModeRaisedEvent(const dpf::Event &event); + void processOpenProjectByPathEvent(const dpf::Event &event); + void processSessionLoadedEvent(const dpf::Event &event); + void processReadyToSaveSessionEvent(const dpf::Event &event); + +private: + QHash> eventHandleMap; }; class ProjectProxy : public QObject { Q_OBJECT - ProjectProxy(){} - ProjectProxy(const ProjectProxy&) = delete; + ProjectProxy() {} + ProjectProxy(const ProjectProxy &) = delete; public: - static ProjectProxy* instance() + static ProjectProxy *instance() { static ProjectProxy ins; return &ins; @@ -41,4 +55,4 @@ class ProjectProxy : public QObject void switchedFile(const QString &file); }; -#endif // PROJECTCORERECEIVER_H +#endif // PROJECTCORERECEIVER_H diff --git a/src/plugins/recent/builtin/icons/uc_session_24px.svg b/src/plugins/recent/builtin/icons/uc_session_24px.svg new file mode 100644 index 000000000..c16030abb --- /dev/null +++ b/src/plugins/recent/builtin/icons/uc_session_24px.svg @@ -0,0 +1,30 @@ + + + ICON / list/session + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/plugins/recent/builtin/texts/uc_clone_16px.svg b/src/plugins/recent/builtin/texts/uc_clone_16px.svg new file mode 100644 index 000000000..3d2902937 --- /dev/null +++ b/src/plugins/recent/builtin/texts/uc_clone_16px.svg @@ -0,0 +1,7 @@ + + + ICON / action /clone + + + + \ No newline at end of file diff --git a/src/plugins/recent/builtin/texts/uc_delete_16px.svg b/src/plugins/recent/builtin/texts/uc_delete_16px.svg new file mode 100644 index 000000000..b685950fe --- /dev/null +++ b/src/plugins/recent/builtin/texts/uc_delete_16px.svg @@ -0,0 +1,11 @@ + + + ICON / action /delete + + + + + + + + \ No newline at end of file diff --git a/src/plugins/recent/builtin/texts/uc_edit_16px.svg b/src/plugins/recent/builtin/texts/uc_edit_16px.svg new file mode 100644 index 000000000..0b0f3beab --- /dev/null +++ b/src/plugins/recent/builtin/texts/uc_edit_16px.svg @@ -0,0 +1,9 @@ + + + ICON / action /edit + + + + + + \ No newline at end of file diff --git a/src/plugins/recent/builtin/texts/uc_settings_16px.svg b/src/plugins/recent/builtin/texts/uc_settings_16px.svg new file mode 100644 index 000000000..12c4f4016 --- /dev/null +++ b/src/plugins/recent/builtin/texts/uc_settings_16px.svg @@ -0,0 +1,7 @@ + + + ICON / list /settings + + + + \ No newline at end of file diff --git a/src/plugins/recent/mainframe/recentdisplaywidget.cpp b/src/plugins/recent/mainframe/recentdisplaywidget.cpp index 60fa429ad..d181521f6 100644 --- a/src/plugins/recent/mainframe/recentdisplaywidget.cpp +++ b/src/plugins/recent/mainframe/recentdisplaywidget.cpp @@ -4,9 +4,11 @@ #include "recentdisplaywidget.h" #include "recentlistview.h" +#include "sessionitemwidget.h" #include "services/window/windowservice.h" #include "services/project/projectservice.h" #include "services/project/projectgenerator.h" +#include "services/session/sessionservice.h" #include "common/settings/settings.h" #include @@ -36,13 +38,23 @@ static RecentDisplayWidget *ins { nullptr }; class RecentDisplayWidgetPrivate { - friend class RecentDisplayWidget; +public: + explicit RecentDisplayWidgetPrivate(RecentDisplayWidget *qq); + + void createProjectWidget(); + void createDocumentWidget(); + void createSessionWidget(); + +public: + RecentDisplayWidget *q; + QHBoxLayout *hLayout { nullptr }; QVBoxLayout *vLayoutDoc { nullptr }; QVBoxLayout *vLayoutPro { nullptr }; DWidget *recentOpen { nullptr }; RecentProjectView *proView { nullptr }; RecentDocemntView *docView { nullptr }; + SessionItemListWidget *sessionListWidget { nullptr }; QList viewList; DLabel *proLabel { nullptr }; DToolButton *proClear { nullptr }; @@ -52,19 +64,129 @@ class RecentDisplayWidgetPrivate DToolButton *docClear { nullptr }; DDialog *clearDocConfirm { nullptr }; + DToolButton *sessionSetBtn { nullptr }; + DFrame *navFrame { nullptr }; DFrame *docFrame { nullptr }; DFrame *proFrame { nullptr }; + DFrame *sessionFrame { nullptr }; DPushButton *btnOpenFile { nullptr }; DPushButton *btnOpenProject { nullptr }; DPushButton *btnNewFileOrPro { nullptr }; DLabel *nullRecentText { nullptr }; Settings recentSettings; + SessionService *sessionSrv { nullptr }; }; +RecentDisplayWidgetPrivate::RecentDisplayWidgetPrivate(RecentDisplayWidget *qq) + : q(qq) +{ + sessionSrv = dpfGetService(SessionService); +} + +void RecentDisplayWidgetPrivate::createProjectWidget() +{ + proFrame = new DFrame(q); + proFrame->setLineWidth(0); + DStyle::setFrameRadius(proFrame, 0); + proView = new RecentProjectView(q); + + proLabel = new DLabel(RecentDisplayWidget::tr("Projects"), q); + proLabel->setForegroundRole(QPalette::BrightText); + proLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + proLabel->setContentsMargins(10, 5, 0, 10); + + proClear = new DToolButton(q); + proClear->setIcon(QIcon::fromTheme("ide_recent_delete")); + proClear->setToolTip(RecentDisplayWidget::tr("clear all")); + + clearProConfirm = new DDialog(q); + clearProConfirm->setIcon(QIcon::fromTheme("dialog-warning")); + clearProConfirm->setMessage(RecentDisplayWidget::tr("Confirm to clear the record of the opened project?")); + clearProConfirm->insertButton(0, RecentDisplayWidget::tr("Cancel", "button")); + clearProConfirm->insertButton(1, RecentDisplayWidget::tr("Delete", "button"), true, DDialog::ButtonWarning); + + QHBoxLayout *proHlayout = new QHBoxLayout; + proHlayout->addWidget(proLabel); + proHlayout->addWidget(proClear); + + DFontSizeManager::instance()->bind(proLabel, DFontSizeManager::T4, QFont::Medium); + vLayoutPro = new QVBoxLayout(); + vLayoutPro->setContentsMargins(10, 10, 10, 10); + vLayoutPro->addLayout(proHlayout); + vLayoutPro->setSpacing(0); + vLayoutPro->addWidget(proView); + proFrame->setLayout(vLayoutPro); +} + +void RecentDisplayWidgetPrivate::createDocumentWidget() +{ + docFrame = new DFrame(); + docFrame->setLineWidth(0); + DStyle::setFrameRadius(docFrame, 0); + docView = new RecentDocemntView(q); + + docLabel = new DLabel(RecentDisplayWidget::tr("Documents")); + docLabel->setForegroundRole(QPalette::BrightText); + docLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + docLabel->setContentsMargins(10, 5, 0, 10); + + docClear = new DToolButton(q); + docClear->setIcon(QIcon::fromTheme("ide_recent_delete")); + docClear->setToolTip(RecentDisplayWidget::tr("clear all")); + + clearDocConfirm = new DDialog(q); + clearDocConfirm->setIcon(QIcon::fromTheme("dialog-warning")); + clearDocConfirm->setMessage(RecentDisplayWidget::tr("Confirm to clear the record of the opened file?")); + clearDocConfirm->insertButton(0, RecentDisplayWidget::tr("Cancel", "button")); + clearDocConfirm->insertButton(1, RecentDisplayWidget::tr("Delete", "button"), true, DDialog::ButtonWarning); + + QHBoxLayout *docHlayout = new QHBoxLayout; + docHlayout->addWidget(docLabel); + docHlayout->addWidget(docClear); + + DFontSizeManager::instance()->bind(docLabel, DFontSizeManager::T4, QFont::Medium); + vLayoutDoc = new QVBoxLayout(); + vLayoutDoc->setContentsMargins(10, 10, 10, 10); + vLayoutDoc->addLayout(docHlayout); + vLayoutDoc->setSpacing(0); + vLayoutDoc->addWidget(docView); + docFrame->setLayout(vLayoutDoc); +} + +void RecentDisplayWidgetPrivate::createSessionWidget() +{ + sessionFrame = new DFrame(q); + sessionFrame->setLineWidth(0); + sessionFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + DStyle::setFrameRadius(sessionFrame, 0); + sessionListWidget = new SessionItemListWidget(q); + + DLabel *sessionLabel = new DLabel(RecentDisplayWidget::tr("Session"), q); + sessionLabel->setForegroundRole(QPalette::BrightText); + sessionLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + sessionLabel->setContentsMargins(10, 5, 0, 10); + DFontSizeManager::instance()->bind(sessionLabel, DFontSizeManager::T4, QFont::Medium); + + sessionSetBtn = new DToolButton(q); + sessionSetBtn->setIcon(QIcon::fromTheme("uc_settings")); + sessionSetBtn->setToolTip(RecentDisplayWidget::tr("session manager")); + + QHBoxLayout *headerLayout = new QHBoxLayout; + headerLayout->addWidget(sessionLabel); + headerLayout->addWidget(sessionSetBtn); + + QVBoxLayout *vLayout = new QVBoxLayout(sessionFrame); + vLayout->setContentsMargins(10, 10, 10, 10); + vLayout->setSpacing(0); + vLayout->addLayout(headerLayout); + vLayout->addWidget(sessionListWidget); +} + RecentDisplayWidget::RecentDisplayWidget(DWidget *parent) - : DWidget(parent), d(new RecentDisplayWidgetPrivate()) + : DWidget(parent), + d(new RecentDisplayWidgetPrivate(this)) { initializeUi(); initConnect(); @@ -115,6 +237,21 @@ void RecentDisplayWidget::addProject(const QString &kitName, d->proView->setItemList(itemList); } +void RecentDisplayWidget::addSession(const QString &session) +{ + d->sessionListWidget->addSessionList({ session }); +} + +void RecentDisplayWidget::removeSession(const QString &session) +{ + d->sessionListWidget->removeSession(session); +} + +void RecentDisplayWidget::updateSessions() +{ + d->sessionListWidget->updateSessions(); +} + void RecentDisplayWidget::doDoubleClickedProject(const QModelIndex &index) { QString kitName = index.data(RecentProjectView::KitNameRole).toString(); @@ -201,8 +338,6 @@ void RecentDisplayWidget::clearDocList() void RecentDisplayWidget::initializeUi() { d->navFrame = new DFrame(); - d->docFrame = new DFrame(); - d->proFrame = new DFrame(); d->navFrame->setLineWidth(0); d->navFrame->setContentsMargins(0, 0, 0, 0); @@ -233,74 +368,19 @@ void RecentDisplayWidget::initializeUi() recentTitle->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); DFontSizeManager::instance()->bind(recentTitle, DFontSizeManager::T4, QFont::Medium); - //recent open document - d->docFrame->setLineWidth(0); - DStyle::setFrameRadius(d->docFrame, 0); - d->docView = new RecentDocemntView(this); - - d->docLabel = new DLabel(tr("Documents")); - d->docLabel->setForegroundRole(QPalette::BrightText); - d->docLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - d->docLabel->setContentsMargins(10, 5, 0, 10); - - d->docClear = new DToolButton(this); - d->docClear->setIcon(QIcon::fromTheme("ide_recent_delete")); - d->docClear->setToolTip(tr("clear all")); + d->createDocumentWidget(); + d->createProjectWidget(); + d->createSessionWidget(); - d->clearDocConfirm = new DDialog(this); - d->clearDocConfirm->setIcon(QIcon::fromTheme("dialog-warning")); - d->clearDocConfirm->setMessage(tr("Confirm to clear the record of the opened file?")); - d->clearDocConfirm->insertButton(0, tr("Cancel", "button")); - d->clearDocConfirm->insertButton(1, tr("Delete", "button"), true, DDialog::ButtonWarning); - - QHBoxLayout *docHlayout = new QHBoxLayout; - docHlayout->addWidget(d->docLabel); - docHlayout->addWidget(d->docClear); - - DFontSizeManager::instance()->bind(d->docLabel, DFontSizeManager::T4, QFont::Medium); - d->vLayoutDoc = new QVBoxLayout(); - d->vLayoutDoc->setContentsMargins(10, 10, 10, 10); - d->vLayoutDoc->addLayout(docHlayout); - d->vLayoutDoc->setSpacing(0); - d->vLayoutDoc->addWidget(d->docView); - d->docFrame->setLayout(d->vLayoutDoc); - - //recent open projects - d->proFrame->setLineWidth(0); - DStyle::setFrameRadius(d->proFrame, 0); - d->proView = new RecentProjectView(this); - - d->proLabel = new DLabel(tr("Projects"), this); - d->proLabel->setForegroundRole(QPalette::BrightText); - d->proLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - d->proLabel->setContentsMargins(10, 5, 0, 10); - - d->proClear = new DToolButton(this); - d->proClear->setIcon(QIcon::fromTheme("ide_recent_delete")); - d->proClear->setToolTip(tr("clear all")); - - d->clearProConfirm = new DDialog(this); - d->clearProConfirm->setIcon(QIcon::fromTheme("dialog-warning")); - d->clearProConfirm->setMessage(tr("Confirm to clear the record of the opened project?")); - d->clearProConfirm->insertButton(0, tr("Cancel", "button")); - d->clearProConfirm->insertButton(1, tr("Delete", "button"), true, DDialog::ButtonWarning); - - QHBoxLayout *proHlayout = new QHBoxLayout; - proHlayout->addWidget(d->proLabel); - proHlayout->addWidget(d->proClear); - - DFontSizeManager::instance()->bind(d->proLabel, DFontSizeManager::T4, QFont::Medium); - d->vLayoutPro = new QVBoxLayout(); - d->vLayoutPro->setContentsMargins(10, 10, 10, 10); - d->vLayoutPro->addLayout(proHlayout); - d->vLayoutPro->setSpacing(0); - d->vLayoutPro->addWidget(d->proView); - d->proFrame->setLayout(d->vLayoutPro); + QVBoxLayout *sessionDocLayout = new QVBoxLayout; + sessionDocLayout->setSpacing(2); + sessionDocLayout->addWidget(d->docFrame); + sessionDocLayout->addWidget(d->sessionFrame); QHBoxLayout *proAndDocLayout = new QHBoxLayout(); proAndDocLayout->addWidget(d->proFrame); proAndDocLayout->setSpacing(2); - proAndDocLayout->addWidget(d->docFrame); + proAndDocLayout->addLayout(sessionDocLayout); d->recentOpen = new DWidget(this); QVBoxLayout *recentNavLayout = new QVBoxLayout(d->recentOpen); @@ -366,6 +446,9 @@ void RecentDisplayWidget::initConnect() else if (index == 1) clearProList(); }); + QObject::connect(d->sessionSetBtn, &DToolButton::clicked, this, [this] { + d->sessionSrv->showSessionManager(); + }); } [[deprecated("-------------存在兼容代码需要删除")]] void RecentDisplayWidget::initData() @@ -402,6 +485,7 @@ void RecentDisplayWidget::initConnect() d->proView->setItemList(d->recentSettings.value(kRecentGroup, d->proView->configKey()).toList()); d->docView->setItemList(d->recentSettings.value(kRecentGroup, d->docView->configKey()).toList()); + d->sessionListWidget->addSessionList(d->sessionSrv->sessionList()); } void RecentDisplayWidget::showEvent(QShowEvent *event) diff --git a/src/plugins/recent/mainframe/recentdisplaywidget.h b/src/plugins/recent/mainframe/recentdisplaywidget.h index 5a9467300..a18b0cac6 100644 --- a/src/plugins/recent/mainframe/recentdisplaywidget.h +++ b/src/plugins/recent/mainframe/recentdisplaywidget.h @@ -24,6 +24,9 @@ public slots: void addProject(const QString &kitName, const QString &language, const QString &workspace); + void addSession(const QString &session); + void removeSession(const QString &session); + void updateSessions(); private slots: void doDoubleClickedProject(const QModelIndex &index); diff --git a/src/plugins/recent/mainframe/sessionitemwidget.cpp b/src/plugins/recent/mainframe/sessionitemwidget.cpp new file mode 100644 index 000000000..550a470a0 --- /dev/null +++ b/src/plugins/recent/mainframe/sessionitemwidget.cpp @@ -0,0 +1,417 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "sessionitemwidget.h" + +#include "services/session/sessionservice.h" +#include "common/settings/settings.h" + +#include +#include +#include +#include + +#include +#include + +DWIDGET_USE_NAMESPACE +using namespace dpfservice; + +ArrowHeaderLine::ArrowHeaderLine(QWidget *parent) + : QWidget(parent) +{ + QHBoxLayout *mainLayout = new QHBoxLayout(this); + mainLayout->setContentsMargins(10, 0, 10, 0); + mainLayout->setSpacing(0); + + DLabel *iconLabel = new DLabel(this); + iconLabel->setPixmap(QIcon::fromTheme("uc_session").pixmap({ 20, 20 })); + arrowButton = new DToolButton(this); + arrowButton->setIcon(DStyle::standardIcon(style(), DStyle::SP_ReduceElement)); + arrowButton->setIconSize({ 12, 12 }); + arrowButton->setFixedSize(24, 24); + titleLabel = new DLabel(this); + titleLabel->installEventFilter(this); + titleLabel->setCursor(Qt::PointingHandCursor); + DFontSizeManager::instance()->bind(titleLabel, DFontSizeManager::T5, QFont::Medium); + + connect(arrowButton, &DToolButton::clicked, this, &ArrowHeaderLine::expandChanged); + mainLayout->addWidget(iconLabel); + mainLayout->addSpacing(10); + mainLayout->addWidget(titleLabel, 1); + mainLayout->addWidget(arrowButton); + setFixedHeight(40); + setExpand(false); +} + +void ArrowHeaderLine::setExpand(bool value) +{ + if (value) { + arrowButton->setIcon(DStyle::standardIcon(style(), DStyle::SP_ExpandElement)); + } else { + arrowButton->setIcon(DStyle::standardIcon(style(), DStyle::SP_ReduceElement)); + } + isExpanded = value; +} + +void ArrowHeaderLine::setTitle(const QString &title) +{ + titleLabel->setText(title); +} + +QString ArrowHeaderLine::title() const +{ + return titleLabel->text(); +} + +void ArrowHeaderLine::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::FontChange) + setFixedHeight(qMax(EXPAND_HEADER_HEIGHT, this->fontMetrics().height())); + + QWidget::changeEvent(e); +} + +bool ArrowHeaderLine::eventFilter(QObject *obj, QEvent *e) +{ + if (obj == titleLabel) { + switch (e->type()) { + case QEvent::Enter: { + QFont font = titleLabel->font(); + font.setUnderline(true); + titleLabel->setFont(font); + break; + } + case QEvent::Leave: { + QFont font = titleLabel->font(); + font.setUnderline(false); + titleLabel->setFont(font); + break; + } + case QEvent::MouseButtonPress: { + QMouseEvent *mouseEvent = static_cast(e); + if (mouseEvent->button() == Qt::LeftButton) { + Q_EMIT itemClicked(); + } + break; + } + default: + break; + } + } + + return QWidget::eventFilter(obj, e); +} + +void ArrowHeaderLine::reverseArrowDirection() +{ + setExpand(!isExpanded); +} + +class SessionItemWidgetPrivate : public QObject +{ +public: + explicit SessionItemWidgetPrivate(SessionItemWidget *qq); + + void initUI(); + void initConnection(); + QWidget *createContent(); + DIconButton *createOptionButton(const QString &icon, const QString &tip); + QString createProjectInfo(const QVariantList &projects); + void removeSession(); + void renameSession(); + void cloneSession(); + void openSession(); + void runInputDialog(const QString &title, const QStringList &actList, + const QString &editText, std::function handler); + +public: + SessionItemWidget *q; + + QString sessionName; + SessionService *sessionSrv { nullptr }; + ArrowHeaderLine *headerLine { nullptr }; + DIconButton *cloneBtn { nullptr }; + DIconButton *renameBtn { nullptr }; + DIconButton *removeBtn { nullptr }; + DLabel *prjInfoLabel { nullptr }; +}; + +SessionItemWidgetPrivate::SessionItemWidgetPrivate(SessionItemWidget *qq) + : q(qq) +{ + sessionSrv = dpfGetService(SessionService); +} + +void SessionItemWidgetPrivate::initUI() +{ + headerLine = new ArrowHeaderLine(q); + headerLine->setExpand(q->expand()); + q->setHeader(headerLine); + q->setContent(createContent()); + q->setSeparatorVisible(false); + q->setExpandedSeparatorVisible(false); +} + +void SessionItemWidgetPrivate::initConnection() +{ + connect(headerLine, &ArrowHeaderLine::expandChanged, q, [=] { + q->setExpand(!q->expand()); + }); + connect(headerLine, &ArrowHeaderLine::itemClicked, this, &SessionItemWidgetPrivate::openSession); + connect(cloneBtn, &DIconButton::clicked, this, &SessionItemWidgetPrivate::cloneSession); + connect(renameBtn, &DIconButton::clicked, this, &SessionItemWidgetPrivate::renameSession); + connect(removeBtn, &DIconButton::clicked, this, &SessionItemWidgetPrivate::removeSession); +} + +QWidget *SessionItemWidgetPrivate::createContent() +{ + QWidget *widget = new QWidget(q); + QVBoxLayout *wLayout = new QVBoxLayout(widget); + wLayout->setContentsMargins(0, 0, 0, 10); + wLayout->setSpacing(8); + + prjInfoLabel = new DLabel(q); + prjInfoLabel->setWordWrap(true); + prjInfoLabel->setContentsMargins(40, 0, 0, 0); + + cloneBtn = createOptionButton("uc_clone", SessionItemWidget::tr("Clone")); + renameBtn = createOptionButton("uc_edit", SessionItemWidget::tr("Rename")); + removeBtn = createOptionButton("uc_delete", SessionItemWidget::tr("Remove")); + + QHBoxLayout *btnLayout = new QHBoxLayout; + btnLayout->setSpacing(10); + btnLayout->setContentsMargins(10, 0, 10, 0); + btnLayout->setAlignment(Qt::AlignRight); + btnLayout->addWidget(cloneBtn); + btnLayout->addWidget(renameBtn); + btnLayout->addWidget(removeBtn); + + wLayout->addWidget(prjInfoLabel); + wLayout->addLayout(btnLayout); + return widget; +} + +DIconButton *SessionItemWidgetPrivate::createOptionButton(const QString &icon, const QString &tip) +{ + DIconButton *btn = new DIconButton(q); + btn->setIconSize({ 16, 16 }); + btn->setIcon(QIcon::fromTheme(icon)); + btn->setToolTip(tip); + btn->setFlat(true); + return btn; +} + +QString SessionItemWidgetPrivate::createProjectInfo(const QVariantList &projects) +{ + static QString formt("
%1
" + "
%2
"); + QString msg; + for (const auto &p : projects) { + const auto &map = p.toMap(); + QFileInfo info(map.value("Workspace").toString()); + msg += formt.arg(info.fileName(), info.absoluteFilePath()); + } + + return msg; +} + +void SessionItemWidgetPrivate::removeSession() +{ + DDialog dlg(q); + dlg.setIcon(QIcon::fromTheme("dialog-warning")); + dlg.setTitle(tr("Are you sure to remove this session?")); + dlg.addButton(tr("Cancel", "button")); + dlg.addButton(tr("Remove", "button"), true, DDialog::ButtonWarning); + + if (dlg.exec() == 1) + sessionSrv->removeSession(sessionName); +} + +void SessionItemWidgetPrivate::renameSession() +{ + QStringList actList { tr("Rename", "button"), tr("Rename and Open", "button") }; + runInputDialog(tr("Rename Session"), actList, sessionName, + [this](const QString &newName) { + sessionSrv->renameSession(sessionName, newName); + }); +} + +void SessionItemWidgetPrivate::cloneSession() +{ + QStringList actList { tr("Clone", "button"), tr("Clone and Open", "button") }; + runInputDialog(tr("New Session Name"), actList, sessionName + " (2)", + [this](const QString &newName) { + sessionSrv->cloneSession(sessionName, newName); + }); +} + +void SessionItemWidgetPrivate::openSession() +{ + sessionSrv->loadSession(sessionName); +} + +void SessionItemWidgetPrivate::runInputDialog(const QString &title, const QStringList &actList, + const QString &editText, std::function handler) +{ + Q_ASSERT(actList.size() == 2); + + DDialog dlg(q); + dlg.setSpacing(10); + dlg.setTitle(title); + dlg.setIcon(QIcon::fromTheme("ide")); + DLineEdit *lineEdit = new DLineEdit(&dlg); + lineEdit->setPlaceholderText(tr("Please input session name")); + connect(lineEdit, &DLineEdit::textChanged, &dlg, [&dlg](const QString &text) { + dlg.getButton(1)->setEnabled(!text.isEmpty()); + dlg.getButton(2)->setEnabled(!text.isEmpty()); + }); + dlg.addContent(lineEdit); + + dlg.addButton(tr("Cancel", "button")); + dlg.addButton(actList[0]); + dlg.addButton(actList[1], true, DDialog::ButtonRecommend); + dlg.getButton(1)->setEnabled(false); + dlg.getButton(2)->setEnabled(false); + lineEdit->setText(editText); + + int ret = dlg.exec(); + if (ret < 1) + return; + + const auto name = lineEdit->text(); + if (name.isEmpty() || sessionSrv->sessionList().contains(name)) + return; + + handler(name); + if (ret == 2) + sessionSrv->loadSession(name); +} + +SessionItemWidget::SessionItemWidget(QWidget *parent) + : DDrawer(parent), + d(new SessionItemWidgetPrivate(this)) +{ + d->initUI(); + d->initConnection(); +} + +SessionItemWidget::~SessionItemWidget() +{ + delete d; +} + +void SessionItemWidget::setSessionName(const QString &session) +{ + d->sessionName = session; +} + +QString SessionItemWidget::sessionName() const +{ + return d->sessionName; +} + +void SessionItemWidget::setExpand(bool value) +{ + d->headerLine->setExpand(value); + DDrawer::setExpand(value); +} + +void SessionItemWidget::updateSession() +{ + bool isLastSession = d->sessionSrv->lastSession() == d->sessionName; + bool isCurrentSession = d->sessionSrv->currentSession() == d->sessionName; + bool isDefaultSession = d->sessionSrv->isDefaultSession(d->sessionName); + bool isDefaultVirgin = d->sessionSrv->isDefaultVirgin(); + + d->renameBtn->setEnabled(!isDefaultSession); + d->removeBtn->setEnabled(!isDefaultSession && !isCurrentSession); + + auto title = d->sessionName; + if (isLastSession && isDefaultVirgin) + title = tr("%1 (last session)").arg(title); + if (isCurrentSession && !isDefaultVirgin) + title = tr("%1 (current session)").arg(title); + d->headerLine->setTitle(title); + + const auto &sessionCfg = d->sessionSrv->sessionFile(d->sessionName); + if (!QFile::exists(sessionCfg)) + return d->prjInfoLabel->setVisible(false); + + Settings st("", sessionCfg); + const auto &prjList = st.value("Session", "ProjectList").toList(); + const auto &info = d->createProjectInfo(prjList); + d->prjInfoLabel->setVisible(!info.isEmpty()); + d->prjInfoLabel->setText(info); +} + +void SessionItemWidget::resizeEvent(QResizeEvent *e) +{ + d->headerLine->setFixedWidth(e->size().width()); + DDrawer::resizeEvent(e); +} + +SessionItemListWidget::SessionItemListWidget(QWidget *parent) + : QScrollArea(parent) +{ + QWidget *widget = new QWidget(this); + mainLayout = new QVBoxLayout(widget); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(2); + mainLayout->setAlignment(Qt::AlignTop); + + setFrameShape(QFrame::NoFrame); + setAutoFillBackground(true); + setBackgroundRole(QPalette::Base); + setWidgetResizable(true); + setWidget(widget); +} + +void SessionItemListWidget::addSessionList(const QStringList &sessionList) +{ + for (const auto &session : sessionList) { + auto item = new SessionItemWidget(this); + item->setSessionName(session); + this->sessionList.append(item); + mainLayout->addWidget(item); + } + updateSessions(); +} + +void SessionItemListWidget::removeSession(const QString &session) +{ + auto iter = std::find_if(sessionList.begin(), sessionList.end(), + [&session](SessionItemWidget *item) { + return item->sessionName() == session; + }); + if (iter == sessionList.end()) + return; + + sessionList.removeOne(*iter); + mainLayout->removeWidget(*iter); + (*iter)->deleteLater(); + updateSessions(); +} + +void SessionItemListWidget::updateSessions() +{ + for (int i = 0; i < sessionList.size(); ++i) { + auto item = sessionList[i]; + item->updateSession(); + item->setFixedWidth(width()); + if (i % 2 == 0) { + item->setBackgroundRole(DPalette::ItemBackground); + } else { + item->setBackgroundRole(QPalette::Base); + } + } +} + +void SessionItemListWidget::resizeEvent(QResizeEvent *e) +{ + for (auto *item : qAsConst(sessionList)) { + item->setFixedWidth(e->size().width()); + } + QScrollArea::resizeEvent(e); +} diff --git a/src/plugins/recent/mainframe/sessionitemwidget.h b/src/plugins/recent/mainframe/sessionitemwidget.h new file mode 100644 index 000000000..96863745c --- /dev/null +++ b/src/plugins/recent/mainframe/sessionitemwidget.h @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SESSIONITEMWIDGET_H +#define SESSIONITEMWIDGET_H + +#include +#include +#include +#include + +#include + +class ArrowHeaderLine : public QWidget +{ + Q_OBJECT +public: + ArrowHeaderLine(QWidget *parent = nullptr); + void setExpand(bool value); + void setTitle(const QString &title); + QString title() const; + +Q_SIGNALS: + void expandChanged(); + void itemClicked(); + +protected: + void changeEvent(QEvent *e) override; + bool eventFilter(QObject *obj, QEvent *e) override; + +private: + void reverseArrowDirection(); + bool isExpanded { false }; + DTK_WIDGET_NAMESPACE::DToolButton *arrowButton { nullptr }; + DTK_WIDGET_NAMESPACE::DLabel *titleLabel { nullptr }; +}; + +class SessionItemWidgetPrivate; +class SessionItemWidget : public DTK_WIDGET_NAMESPACE::DDrawer +{ + Q_OBJECT +public: + explicit SessionItemWidget(QWidget *parent = nullptr); + ~SessionItemWidget(); + + void setSessionName(const QString &session); + QString sessionName() const; + void setExpand(bool value) override; + void updateSession(); + +private: + void resizeEvent(QResizeEvent *e) override; + + SessionItemWidgetPrivate *const d; +}; + +class SessionItemListWidget : public QScrollArea +{ + Q_OBJECT +public: + explicit SessionItemListWidget(QWidget *parent = nullptr); + + void addSessionList(const QStringList &sessionList); + void removeSession(const QString &session); + void updateSessions(); + +private: + void resizeEvent(QResizeEvent *e) override; + + QList sessionList; + QVBoxLayout *mainLayout { nullptr }; +}; + +#endif // SESSIONITEMWIDGET_H diff --git a/src/plugins/recent/recent.qrc b/src/plugins/recent/recent.qrc index cacc28606..1c0718b85 100644 --- a/src/plugins/recent/recent.qrc +++ b/src/plugins/recent/recent.qrc @@ -2,7 +2,12 @@ images/recentLogo.png - - builtin/texts/ide_recent_delete_32px.svg - + + builtin/texts/ide_recent_delete_32px.svg + builtin/icons/uc_session_24px.svg + builtin/texts/uc_clone_16px.svg + builtin/texts/uc_delete_16px.svg + builtin/texts/uc_edit_16px.svg + builtin/texts/uc_settings_16px.svg + diff --git a/src/plugins/recent/transceiver/recentreceiver.cpp b/src/plugins/recent/transceiver/recentreceiver.cpp index 05562dee9..1c2556c41 100644 --- a/src/plugins/recent/transceiver/recentreceiver.cpp +++ b/src/plugins/recent/transceiver/recentreceiver.cpp @@ -4,12 +4,17 @@ #include "recentreceiver.h" #include "common/common.h" +#include "mainframe/recentdisplaywidget.h" RecentReceiver::RecentReceiver(QObject *parent) - : dpf::EventHandler (parent) - , dpf::AutoEventHandlerRegister () + : dpf::EventHandler(parent), dpf::AutoEventHandlerRegister() { - + using namespace std::placeholders; + eventHandleMap.insert(recent.saveOpenedProject.name, std::bind(&RecentReceiver::processSaveOpenedProjectEvent, this, _1)); + eventHandleMap.insert(recent.saveOpenedFile.name, std::bind(&RecentReceiver::processSaveOpenedFileEvent, this, _1)); + eventHandleMap.insert(session.sessionCreated.name, std::bind(&RecentReceiver::processSessionCreatedEvent, this, _1)); + eventHandleMap.insert(session.sessionRemoved.name, std::bind(&RecentReceiver::processSessionRemovedEvent, this, _1)); + eventHandleMap.insert(session.sessionLoaded.name, std::bind(&RecentReceiver::processSessionLoadedEvent, this, _1)); } dpf::EventHandler::Type RecentReceiver::type() @@ -19,22 +24,49 @@ dpf::EventHandler::Type RecentReceiver::type() QStringList RecentReceiver::topics() { - return { recent.topic }; + return { recent.topic, session.topic }; } void RecentReceiver::eventProcess(const dpf::Event &event) { - if (event.data() == recent.saveOpenedProject.name) { - QString kitName = event.property(recent.saveOpenedProject.pKeys[0]).toString(); - QString language = event.property(recent.saveOpenedProject.pKeys[1]).toString(); - QString workspace = event.property(recent.saveOpenedProject.pKeys[2]).toString(); - if (QDir(workspace).exists()) - RecentProxy::instance()->saveOpenedProject(kitName, language, workspace); - } else if (event.data() == recent.saveOpenedFile.name) { - QString filePath = event.property(recent.saveOpenedFile.pKeys[0]).toString(); - if (QFileInfo(filePath).exists()) - RecentProxy::instance()->saveOpenedFile(filePath); - } + const auto &eventName = event.data().toString(); + if (!eventHandleMap.contains(eventName)) + return; + + eventHandleMap[eventName](event); +} + +void RecentReceiver::processSaveOpenedProjectEvent(const dpf::Event &event) +{ + QString kitName = event.property("kitName").toString(); + QString language = event.property("language").toString(); + QString workspace = event.property("workspace").toString(); + if (QDir(workspace).exists()) + Q_EMIT RecentProxy::instance()->saveOpenedProject(kitName, language, workspace); +} + +void RecentReceiver::processSaveOpenedFileEvent(const dpf::Event &event) +{ + QString filePath = event.property("filePath").toString(); + if (QFileInfo(filePath).exists()) + Q_EMIT RecentProxy::instance()->saveOpenedFile(filePath); +} + +void RecentReceiver::processSessionCreatedEvent(const dpf::Event &event) +{ + const QString session = event.property("session").toString(); + RecentDisplayWidget::instance()->addSession(session); +} + +void RecentReceiver::processSessionRemovedEvent(const dpf::Event &event) +{ + const QString session = event.property("session").toString(); + RecentDisplayWidget::instance()->removeSession(session); +} + +void RecentReceiver::processSessionLoadedEvent(const dpf::Event &event) +{ + RecentDisplayWidget::instance()->updateSessions(); } RecentProxy *RecentProxy::instance() diff --git a/src/plugins/recent/transceiver/recentreceiver.h b/src/plugins/recent/transceiver/recentreceiver.h index 9796b3c52..13292fdf2 100644 --- a/src/plugins/recent/transceiver/recentreceiver.h +++ b/src/plugins/recent/transceiver/recentreceiver.h @@ -15,10 +15,18 @@ class RecentReceiver : public dpf::EventHandler, dpf::AutoEventHandlerRegister> eventHandleMap; }; class RecentProxy : public QObject diff --git a/src/services/CMakeLists.txt b/src/services/CMakeLists.txt index 74d630938..cb57370ff 100644 --- a/src/services/CMakeLists.txt +++ b/src/services/CMakeLists.txt @@ -25,6 +25,7 @@ FILE(GLOB SERVICES_FILES "${CMAKE_CURRENT_SOURCE_DIR}/terminal/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/locator/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/ai/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/session/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/*.h" ) diff --git a/src/services/session/sessionservice.cpp b/src/services/session/sessionservice.cpp new file mode 100644 index 000000000..07d54d158 --- /dev/null +++ b/src/services/session/sessionservice.cpp @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "sessionservice.h" + +SessionService::SessionService() {} diff --git a/src/services/session/sessionservice.h b/src/services/session/sessionservice.h new file mode 100644 index 000000000..161d24a3c --- /dev/null +++ b/src/services/session/sessionservice.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SESSIONSERVICE_H +#define SESSIONSERVICE_H + +#include "services/services_global.h" +#include + +namespace dpfservice { +class SERVICE_EXPORT SessionService final : public dpf::PluginService, dpf::AutoServiceRegister +{ + Q_OBJECT + Q_DISABLE_COPY(SessionService) +public: + static QString name() + { + return "org.deepin.service.SessionService"; + } + + explicit SessionService(QObject *parent = nullptr) + : dpf::PluginService(parent) + { + } + + DPF_INTERFACE(QString, currentSession); + DPF_INTERFACE(QString, lastSession); + DPF_INTERFACE(QStringList, sessionList); + DPF_INTERFACE(QDateTime, sessionDateTime, const QString &session); + DPF_INTERFACE(QDateTime, lastActiveTime, const QString &session); + + DPF_INTERFACE(bool, createSession, const QString &session); + DPF_INTERFACE(bool, removeSession, const QString &session); + DPF_INTERFACE(void, removeSessions, const QStringList &sessions); + DPF_INTERFACE(bool, renameSession, const QString &oldName, const QString &newName); + DPF_INTERFACE(bool, cloneSession, const QString &select, const QString &clone); + DPF_INTERFACE(void, showSessionManager); + + DPF_INTERFACE(void, setValue, const QString &key, const QVariant &value); + DPF_INTERFACE(QVariant, value, const QString &key); + + DPF_INTERFACE(bool, loadSession, const QString &session); + DPF_INTERFACE(bool, saveSession); + DPF_INTERFACE(bool, isDefaultSession, const QString &session); + DPF_INTERFACE(bool, isSessionLoading); + DPF_INTERFACE(bool, isDefaultVirgin); + + DPF_INTERFACE(QString, sessionFile, const QString &session); +}; +} + +#endif // SESSIONSERVICE_H diff --git a/src/tools/languageadapter/main.cpp b/src/tools/languageadapter/main.cpp index 811a2dc95..25d4b980a 100644 --- a/src/tools/languageadapter/main.cpp +++ b/src/tools/languageadapter/main.cpp @@ -49,7 +49,7 @@ static QString packageInstallPath(const QString &python) // setting from clangd trismit QProcess *createCxxServ(const newlsp::ProjectKey &key) { - lspServOut << __FUNCTION__ << qApp->thread() << QThread::currentThread(); + lspServOut << __FUNCTION__ << QCoreApplication::instance()->thread() << QThread::currentThread(); if (key.language != newlsp::Cxx) return nullptr;