diff --git a/src/core/management_layer/content/project/project_manager.cpp b/src/core/management_layer/content/project/project_manager.cpp index 506c7eea2..719afa845 100644 --- a/src/core/management_layer/content/project/project_manager.cpp +++ b/src/core/management_layer/content/project/project_manager.cpp @@ -526,199 +526,222 @@ void ProjectManager::Implementation::updateNavigatorContextMenu(const QModelInde const auto enabled = editingMode == DocumentEditingMode::Edit; // - // Формируем список действий для конкретных элементов структуры проекта - // - if (currentItem->type() == Domain::DocumentObjectType::Characters) { - auto findAllCharacters = new QAction(tr("Find all characters")); - findAllCharacters->setIconText(u8"\U000F0016"); - findAllCharacters->setEnabled(enabled); - connect(findAllCharacters, &QAction::triggered, q, [this] { this->findAllCharacters(); }); - menuActions.append(findAllCharacters); - // - auto addCharacter = new QAction(tr("Add character")); - addCharacter->setIconText(u8"\U000F0014"); - connect(addCharacter, &QAction::triggered, q, [this] { addDocument(); }); - menuActions.append(addCharacter); - } else if (currentItem->type() == Domain::DocumentObjectType::Locations) { - auto findAllLocations = new QAction(tr("Find all locations")); - findAllLocations->setIconText(u8"\U000F13B0"); - findAllLocations->setEnabled(enabled); - connect(findAllLocations, &QAction::triggered, q, [this] { this->findAllLocations(); }); - menuActions.append(findAllLocations); - // - auto addLocation = new QAction(tr("Add location")); - addLocation->setIconText(u8"\U000F0975"); - connect(addLocation, &QAction::triggered, q, [this] { addDocument(); }); - menuActions.append(addLocation); - } else if (currentItem->type() == Domain::DocumentObjectType::RecycleBin) { - if (currentItem->hasChildren()) { - auto emptyRecycleBin = new QAction(tr("Empty recycle bin")); - emptyRecycleBin->setIconText(u8"\U000f05e8"); - emptyRecycleBin->setEnabled(enabled); - connect(emptyRecycleBin, &QAction::triggered, q, [this] { this->emptyRecycleBin(); }); - menuActions.append(emptyRecycleBin); + // Если все выделенные итемы находятся в корзине, то их можно только удалить навсегда + // + bool allInRecycleBin = true; + for (const auto index : selectedIndexes) { + const auto currentItemIndex = projectStructureProxyModel->mapToSource(index); + const auto currentItem = projectStructureModel->itemForIndex(currentItemIndex); + if (!isInRecycleBin(currentItem)) { + allInRecycleBin = false; + break; } } - // - // ... для остальных элементов - // - else { - auto addFolder = new QAction(tr("Add folder")); - addFolder->setIconText(u8"\U000F0257"); - addFolder->setEnabled(enabled); - connect(addFolder, &QAction::triggered, q, - [this] { addDocument(Domain::DocumentObjectType::Folder); }); - menuActions.append(addFolder); + if (allInRecycleBin) { + auto removeDocument = new QAction(tr("Remove permanently")); + removeDocument->setIconText(u8"\U000f01b4"); + removeDocument->setEnabled(enabled); + connect(removeDocument, &QAction::triggered, q, [this] { removeSelectedDocuments(); }); + menuActions.append(removeDocument); + } else { // - auto addDocument = new QAction(tr("Add document")); - addDocument->setIconText(u8"\U000F0415"); - addDocument->setEnabled(enabled); - connect(addDocument, &QAction::triggered, q, [this] { this->addDocument(); }); - menuActions.append(addDocument); - - if (_index.isValid()) { + // Иначе формируем список действий для конкретных элементов структуры проекта + // + if (currentItem->type() == Domain::DocumentObjectType::Characters) { + auto findAllCharacters = new QAction(tr("Find all characters")); + findAllCharacters->setIconText(u8"\U000F0016"); + findAllCharacters->setEnabled(enabled); + connect(findAllCharacters, &QAction::triggered, q, + [this] { this->findAllCharacters(); }); + menuActions.append(findAllCharacters); // - // Если хотя бы один выделенный элемент можно удалить, добавим действие + auto addCharacter = new QAction(tr("Add character")); + addCharacter->setIconText(u8"\U000F0014"); + connect(addCharacter, &QAction::triggered, q, [this] { addDocument(); }); + menuActions.append(addCharacter); + } else if (currentItem->type() == Domain::DocumentObjectType::Locations) { + auto findAllLocations = new QAction(tr("Find all locations")); + findAllLocations->setIconText(u8"\U000F13B0"); + findAllLocations->setEnabled(enabled); + connect(findAllLocations, &QAction::triggered, q, [this] { this->findAllLocations(); }); + menuActions.append(findAllLocations); // - bool addRemoveAction = false; - for (const auto index : selectedIndexes) { - const auto currentItemIndex = projectStructureProxyModel->mapToSource(index); - const auto currentItem = projectStructureModel->itemForIndex(currentItemIndex); - if (canDocumentBeRemoved(currentItem)) { - addRemoveAction = true; - break; - } - } - if (addRemoveAction) { - auto removeDocument = new QAction( - selectedIndexes.size() > 1 ? tr("Remove documents") : tr("Remove document")); - removeDocument->setSeparator(true); - removeDocument->setIconText(u8"\U000f01b4"); - removeDocument->setEnabled(enabled); - connect(removeDocument, &QAction::triggered, q, - [this] { removeSelectedDocuments(); }); - menuActions.append(removeDocument); + auto addLocation = new QAction(tr("Add location")); + addLocation->setIconText(u8"\U000F0975"); + connect(addLocation, &QAction::triggered, q, [this] { addDocument(); }); + menuActions.append(addLocation); + } else if (currentItem->type() == Domain::DocumentObjectType::RecycleBin) { + if (currentItem->hasChildren()) { + auto emptyRecycleBin = new QAction(tr("Empty recycle bin")); + emptyRecycleBin->setIconText(u8"\U000f05e8"); + emptyRecycleBin->setEnabled(enabled); + connect(emptyRecycleBin, &QAction::triggered, q, + [this] { this->emptyRecycleBin(); }); + menuActions.append(emptyRecycleBin); } } - } + // + // ... для остальных элементов + // + else { + auto addFolder = new QAction(tr("Add folder")); + addFolder->setIconText(u8"\U000F0257"); + addFolder->setEnabled(enabled); + connect(addFolder, &QAction::triggered, q, + [this] { addDocument(Domain::DocumentObjectType::Folder); }); + menuActions.append(addFolder); + // + auto addDocument = new QAction(tr("Add document")); + addDocument->setIconText(u8"\U000F0415"); + addDocument->setEnabled(enabled); + connect(addDocument, &QAction::triggered, q, [this] { this->addDocument(); }); + menuActions.append(addDocument); - // - // Персонажей и локации можно отсортировать по алфавиту - // - if (currentItem->type() == Domain::DocumentObjectType::Characters - || currentItem->type() == Domain::DocumentObjectType::Locations) { - auto sortAlphabetically = new QAction(tr("Sort alphabetically")); - sortAlphabetically->setIconText(u8"\U000F05BD"); - connect(sortAlphabetically, &QAction::triggered, q, - [this] { sortChildrenAlphabetically(); }); - menuActions.append(sortAlphabetically); - } - - bool isDocumentActionAdded = false; - // - // Для текстовых документов можно создать версию - // - if (_index.isValid() && selectedIndexes.size() == 1 && isTextItem(currentItem)) { - auto createNewVersion = new QAction(tr("Create new draft")); - createNewVersion->setSeparator(true); - createNewVersion->setIconText(u8"\U000F00FB"); - createNewVersion->setEnabled(enabled); - connect(createNewVersion, &QAction::triggered, q, - [this, currentItemIndex] { this->createNewVersion(currentItemIndex); }); - menuActions.append(createNewVersion); - isDocumentActionAdded = true; - } - - // - // Каждый из элементов можно открыть в своём окне, кроме корзины - // - if (_index.isValid() && selectedIndexes.size() == 1 - && currentItem->type() != Domain::DocumentObjectType::RecycleBin) { - auto openInNewWindow = new QAction(tr("Open in new window")); - openInNewWindow->setSeparator(!isDocumentActionAdded && !menuActions.isEmpty()); - openInNewWindow->setIconText(u8"\U000F03CC"); - connect(openInNewWindow, &QAction::triggered, q, - [this] { openCurrentDocumentInNewWindow(); }); - menuActions.append(openInNewWindow); - isDocumentActionAdded = true; - } - - // - // Для документов, имеющих разрешение на экспорт, можно вызвать экспорт - // - if (_index.isValid() && selectedIndexes.size() == 1 && isCurrentDocumentExportAvailable) { - auto exportCurrentFileAction = new QAction(tr("Export...")); - exportCurrentFileAction->setSeparator(true); - exportCurrentFileAction->setIconText(u8"\U000f0207"); - connect(exportCurrentFileAction, &QAction::triggered, q, - &ProjectManager::exportCurrentDocumentRequested); - menuActions.append(exportCurrentFileAction); - } - - // - // Документы облачного проекта можно расшарить - // - if (isProjectRemote && allowGrantAccessToProject && _index.isValid() - && selectedIndexes.size() == 1 - && currentItem->type() != Domain::DocumentObjectType::RecycleBin) { - QModelIndex documentIndexToShare; - const auto documentUuidToShare = [currentItem, _index, &documentIndexToShare] { - switch (currentItem->type()) { - case Domain::DocumentObjectType::Character: - case Domain::DocumentObjectType::Location: - case Domain::DocumentObjectType::World: - case Domain::DocumentObjectType::AudioplayTitlePage: - case Domain::DocumentObjectType::AudioplaySynopsis: - case Domain::DocumentObjectType::AudioplayText: - case Domain::DocumentObjectType::AudioplayStatistics: - case Domain::DocumentObjectType::ComicBookTitlePage: - case Domain::DocumentObjectType::ComicBookSynopsis: - case Domain::DocumentObjectType::ComicBookText: - case Domain::DocumentObjectType::ComicBookStatistics: - case Domain::DocumentObjectType::NovelTitlePage: - case Domain::DocumentObjectType::NovelSynopsis: - case Domain::DocumentObjectType::NovelOutline: - case Domain::DocumentObjectType::NovelText: - case Domain::DocumentObjectType::NovelStatistics: - case Domain::DocumentObjectType::ScreenplayTitlePage: - case Domain::DocumentObjectType::ScreenplaySynopsis: - case Domain::DocumentObjectType::ScreenplayTreatment: - case Domain::DocumentObjectType::ScreenplayText: - case Domain::DocumentObjectType::ScreenplayStatistics: - case Domain::DocumentObjectType::StageplayTitlePage: - case Domain::DocumentObjectType::StageplaySynopsis: - case Domain::DocumentObjectType::StageplayText: - case Domain::DocumentObjectType::StageplayStatistics: { - documentIndexToShare = _index.parent(); - return currentItem->parent()->uuid(); - } - default: { - documentIndexToShare = _index; - return currentItem->uuid(); - } + if (_index.isValid()) { + // + // Если хотя бы один выделенный элемент можно удалить, добавим действие + // + bool addRemoveAction = false; + for (const auto index : selectedIndexes) { + const auto currentItemIndex = projectStructureProxyModel->mapToSource(index); + const auto currentItem = projectStructureModel->itemForIndex(currentItemIndex); + if (canDocumentBeRemoved(currentItem)) { + addRemoveAction = true; + break; + } + } + if (addRemoveAction) { + auto removeDocument + = new QAction(selectedIndexes.size() > 1 ? tr("Remove documents") + : tr("Remove document")); + removeDocument->setSeparator(true); + removeDocument->setIconText(u8"\U000f01b4"); + removeDocument->setEnabled(enabled); + connect(removeDocument, &QAction::triggered, q, + [this] { removeSelectedDocuments(); }); + menuActions.append(removeDocument); + } } - }(); + } + // - // ... если пользователь владелец, или может изменять этот документ + // Персонажей и локации можно отсортировать по алфавиту // - if ((isProjectOwner || editingMode == DocumentEditingMode::Edit - || editingPermissions.value(documentUuidToShare, DocumentEditingMode::Read) - == DocumentEditingMode::Edit)) { - auto shareAccess = new QAction(tr("Share access")); - shareAccess->setSeparator(!menuActions.isEmpty()); - shareAccess->setIconText(u8"\U000F0010"); - connect(shareAccess, &QAction::triggered, q, [this, documentIndexToShare] { - auto projectCollaboratorsView = pluginsBuilder.projectCollaboratorsView( - modelsFacade.modelFor(Domain::DocumentObjectType::Project)); - view.left->addEditor(projectCollaboratorsView->asQWidget()); - - QMetaObject::invokeMethod(projectCollaboratorsView->asQWidget(), - "configureDocumentAccessPressed", - Q_ARG(QModelIndex, documentIndexToShare)); - }); - menuActions.append(shareAccess); + if (currentItem->type() == Domain::DocumentObjectType::Characters + || currentItem->type() == Domain::DocumentObjectType::Locations) { + auto sortAlphabetically = new QAction(tr("Sort alphabetically")); + sortAlphabetically->setIconText(u8"\U000F05BD"); + connect(sortAlphabetically, &QAction::triggered, q, + [this] { sortChildrenAlphabetically(); }); + menuActions.append(sortAlphabetically); + } + + bool isDocumentActionAdded = false; + // + // Для текстовых документов можно создать версию + // + if (_index.isValid() && selectedIndexes.size() == 1 && isTextItem(currentItem)) { + auto createNewVersion = new QAction(tr("Create new draft")); + createNewVersion->setSeparator(true); + createNewVersion->setIconText(u8"\U000F00FB"); + createNewVersion->setEnabled(enabled); + connect(createNewVersion, &QAction::triggered, q, + [this, currentItemIndex] { this->createNewVersion(currentItemIndex); }); + menuActions.append(createNewVersion); + isDocumentActionAdded = true; + } + + // + // Каждый из элементов можно открыть в своём окне, кроме корзины + // + if (_index.isValid() && selectedIndexes.size() == 1 + && currentItem->type() != Domain::DocumentObjectType::RecycleBin) { + auto openInNewWindow = new QAction(tr("Open in new window")); + openInNewWindow->setSeparator(!isDocumentActionAdded && !menuActions.isEmpty()); + openInNewWindow->setIconText(u8"\U000F03CC"); + connect(openInNewWindow, &QAction::triggered, q, + [this] { openCurrentDocumentInNewWindow(); }); + menuActions.append(openInNewWindow); isDocumentActionAdded = true; } + + // + // Для документов, имеющих разрешение на экспорт, можно вызвать экспорт + // + if (_index.isValid() && selectedIndexes.size() == 1 && isCurrentDocumentExportAvailable) { + auto exportCurrentFileAction = new QAction(tr("Export...")); + exportCurrentFileAction->setSeparator(true); + exportCurrentFileAction->setIconText(u8"\U000f0207"); + connect(exportCurrentFileAction, &QAction::triggered, q, + &ProjectManager::exportCurrentDocumentRequested); + menuActions.append(exportCurrentFileAction); + } + + // + // Документы облачного проекта можно расшарить + // + if (isProjectRemote && allowGrantAccessToProject && _index.isValid() + && selectedIndexes.size() == 1 + && currentItem->type() != Domain::DocumentObjectType::RecycleBin) { + QModelIndex documentIndexToShare; + const auto documentUuidToShare = [currentItem, _index, &documentIndexToShare] { + switch (currentItem->type()) { + case Domain::DocumentObjectType::Character: + case Domain::DocumentObjectType::Location: + case Domain::DocumentObjectType::World: + case Domain::DocumentObjectType::AudioplayTitlePage: + case Domain::DocumentObjectType::AudioplaySynopsis: + case Domain::DocumentObjectType::AudioplayText: + case Domain::DocumentObjectType::AudioplayStatistics: + case Domain::DocumentObjectType::ComicBookTitlePage: + case Domain::DocumentObjectType::ComicBookSynopsis: + case Domain::DocumentObjectType::ComicBookText: + case Domain::DocumentObjectType::ComicBookStatistics: + case Domain::DocumentObjectType::NovelTitlePage: + case Domain::DocumentObjectType::NovelSynopsis: + case Domain::DocumentObjectType::NovelOutline: + case Domain::DocumentObjectType::NovelText: + case Domain::DocumentObjectType::NovelStatistics: + case Domain::DocumentObjectType::ScreenplayTitlePage: + case Domain::DocumentObjectType::ScreenplaySynopsis: + case Domain::DocumentObjectType::ScreenplayTreatment: + case Domain::DocumentObjectType::ScreenplayText: + case Domain::DocumentObjectType::ScreenplayStatistics: + case Domain::DocumentObjectType::StageplayTitlePage: + case Domain::DocumentObjectType::StageplaySynopsis: + case Domain::DocumentObjectType::StageplayText: + case Domain::DocumentObjectType::StageplayStatistics: { + documentIndexToShare = _index.parent(); + return currentItem->parent()->uuid(); + } + default: { + documentIndexToShare = _index; + return currentItem->uuid(); + } + } + }(); + // + // ... если пользователь владелец, или может изменять этот документ + // + if ((isProjectOwner || editingMode == DocumentEditingMode::Edit + || editingPermissions.value(documentUuidToShare, DocumentEditingMode::Read) + == DocumentEditingMode::Edit)) { + auto shareAccess = new QAction(tr("Share access")); + shareAccess->setSeparator(!menuActions.isEmpty()); + shareAccess->setIconText(u8"\U000F0010"); + connect(shareAccess, &QAction::triggered, q, [this, documentIndexToShare] { + auto projectCollaboratorsView = pluginsBuilder.projectCollaboratorsView( + modelsFacade.modelFor(Domain::DocumentObjectType::Project)); + view.left->addEditor(projectCollaboratorsView->asQWidget()); + + QMetaObject::invokeMethod(projectCollaboratorsView->asQWidget(), + "configureDocumentAccessPressed", + Q_ARG(QModelIndex, documentIndexToShare)); + }); + menuActions.append(shareAccess); + isDocumentActionAdded = true; + } + } } //