diff --git a/src/editortab.cpp b/src/editortab.cpp index f600373..a46a3a6 100644 --- a/src/editortab.cpp +++ b/src/editortab.cpp @@ -1,1780 +1,1782 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2014 by Nick Shaforostoff 2018-2019 by Simon Depiets This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **************************************************************************** */ #include "editortab.h" #include "xlifftextedit.h" #include "lokalize_debug.h" #include "actionproxy.h" #include "editorview.h" #include "catalog.h" #include "pos.h" #include "cmd.h" #include "completionstorage.h" #define WEBQUERY_ENABLE //views #include "msgctxtview.h" #include "alttransview.h" #include "mergeview.h" #include "cataloglistview.h" #include "glossaryview.h" #ifdef WEBQUERY_ENABLE #include "webqueryview.h" #endif #include "tmview.h" #include "binunitsview.h" #include "phaseswindow.h" #include "projectlocal.h" #include "project.h" #include "prefs.h" #include "prefs_lokalize.h" #include "languagelistmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include EditorTab::EditorTab(QWidget* parent, bool valid) : LokalizeSubwindowBase2(parent) , m_project(Project::instance()) , m_catalog(new Catalog(this)) , m_view(new EditorView(this, m_catalog/*,new keyEventHandler(this,m_catalog)*/)) , m_pologyProcessInProgress(false) , m_sonnetDialog(0) , m_spellcheckStartUndoIndex(0) , m_spellcheckStop(false) , m_currentIsApproved(true) , m_currentIsUntr(true) , m_fullPathShown(false) , m_doReplaceCalled(false) , m_find(0) , m_replace(0) , m_syncView(0) , m_syncViewSecondary(0) , m_valid(valid) , m_dbusId(-1) { //QTime chrono;chrono.start(); setAcceptDrops(true); setCentralWidget(m_view); setupStatusBar(); //--NOT called from initLater() ! setupActions(); dbusObjectPath(); connect(m_view, &EditorView::signalChanged, this, &EditorTab::msgStrChanged); msgStrChanged(); connect(SettingsController::instance(), &SettingsController::generalSettingsChanged, m_view, &EditorView::settingsChanged); connect(m_view->tabBar(), &QTabBar::currentChanged, this, &EditorTab::switchForm); connect(m_view, QOverload::of(&EditorView::gotoEntryRequested), this, QOverload::of(&EditorTab::gotoEntry)); connect(m_view, &EditorView::tmLookupRequested, this, &EditorTab::lookupSelectionInTranslationMemory); connect(this, &EditorTab::fileOpened, this, &EditorTab::indexWordsForCompletion, Qt::QueuedConnection); connect(m_catalog, &Catalog::signalFileAutoSaveFailed, this, &EditorTab::fileAutoSaveFailedWarning); //defer some work to make window appear earlier (~200 msec on my Core Duo) //QTimer::singleShot(0,this,SLOT(initLater())); //qCWarning(LOKALIZE_LOG)<isEmpty()) { emit fileAboutToBeClosed(); emit fileClosed(); emit fileClosed(currentFile()); } ids.removeAll(m_dbusId); + + delete m_catalog; } void EditorTab::setupStatusBar() { statusBarItems.insert(ID_STATUS_CURRENT, i18nc("@info:status message entry", "Current: %1", 0)); statusBarItems.insert(ID_STATUS_TOTAL, i18nc("@info:status message entries", "Total: %1", 0)); statusBarItems.insert(ID_STATUS_FUZZY, i18nc("@info:status message entries\n'fuzzy' in gettext terminology", "Not ready: %1", 0)); statusBarItems.insert(ID_STATUS_UNTRANS, i18nc("@info:status message entries", "Untranslated: %1", 0)); statusBarItems.insert(ID_STATUS_ISFUZZY, QString()); connect(m_catalog, &Catalog::signalNumberOfFuzziesChanged, this, &EditorTab::numberOfFuzziesChanged); connect(m_catalog, &Catalog::signalNumberOfEmptyChanged, this, &EditorTab::numberOfUntranslatedChanged); } void LokalizeSubwindowBase::reflectNonApprovedCount(int count, int total) { QString text = i18nc("@info:status message entries\n'fuzzy' in gettext terminology", "Not ready: %1", count); if (count && total) text += i18nc("percentages in statusbar", " (%1%)", int(100.0 * count / total)); statusBarItems.insert(ID_STATUS_FUZZY, text); } void LokalizeSubwindowBase::reflectUntranslatedCount(int count, int total) { QString text = i18nc("@info:status message entries", "Untranslated: %1", count); if (count && total) text += i18nc("percentages in statusbar", " (%1%)", int(100.0 * count / total)); statusBarItems.insert(ID_STATUS_UNTRANS, text); } void EditorTab::numberOfFuzziesChanged() { reflectNonApprovedCount(m_catalog->numberOfNonApproved(), m_catalog->numberOfEntries()); } void EditorTab::numberOfUntranslatedChanged() { reflectUntranslatedCount(m_catalog->numberOfUntranslated(), m_catalog->numberOfEntries()); } void EditorTab::setupActions() { //all operations that can be done after initial setup //(via QTimer::singleShot) go to initLater() setXMLFile(QStringLiteral("editorui.rc")); setUpdatedXMLFile(); QAction *action; KActionCollection* ac = actionCollection(); KActionCategory* actionCategory; KActionCategory* file = new KActionCategory(i18nc("@title actions category", "File"), ac); KActionCategory* nav = new KActionCategory(i18nc("@title actions category", "Navigation"), ac); KActionCategory* edit = new KActionCategory(i18nc("@title actions category", "Editing"), ac); KActionCategory* sync1 = new KActionCategory(i18n("Synchronization 1"), ac); KActionCategory* sync2 = new KActionCategory(i18n("Synchronization 2"), ac); KActionCategory* tm = new KActionCategory(i18n("Translation Memory"), ac); KActionCategory* glossary = new KActionCategory(i18nc("@title actions category", "Glossary"), ac); //KActionCategory* tools=new KActionCategory(i18nc("@title actions category","Tools"), ac); #ifndef Q_OS_DARWIN QLocale::Language systemLang = QLocale::system().language(); #endif //BEGIN dockwidgets int i = 0; QVector altactions(ALTTRANS_SHORTCUTS); Qt::Key altlist[ALTTRANS_SHORTCUTS] = { Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4, Qt::Key_5, Qt::Key_6, Qt::Key_7, Qt::Key_8, Qt::Key_9 }; QAction* altaction; for (i = 0; i < ALTTRANS_SHORTCUTS; ++i) { altaction = tm->addAction(QStringLiteral("alttrans_insert_%1").arg(i)); ac->setDefaultShortcut(altaction, QKeySequence(Qt::ALT + altlist[i])); altaction->setText(i18nc("@action:inmenu", "Insert alternate translation #%1", QString::number(i))); altactions[i] = altaction; } m_altTransView = new AltTransView(this, m_catalog, altactions); addDockWidget(Qt::BottomDockWidgetArea, m_altTransView); ac->addAction(QStringLiteral("showmsgiddiff_action"), m_altTransView->toggleViewAction()); connect(this, QOverload::of(&EditorTab::signalNewEntryDisplayed), m_altTransView, QOverload::of(&AltTransView::slotNewEntryDisplayed)); connect(m_altTransView, &AltTransView::textInsertRequested, m_view, &EditorView::insertTerm); connect(m_altTransView, &AltTransView::refreshRequested, m_view, QOverload<>::of(&EditorView::gotoEntry), Qt::QueuedConnection); connect(m_catalog, QOverload<>::of(&Catalog::signalFileLoaded), m_altTransView, &AltTransView::fileLoaded); m_syncView = new MergeView(this, m_catalog, true); addDockWidget(Qt::BottomDockWidgetArea, m_syncView); sync1->addAction(QStringLiteral("showmergeview_action"), m_syncView->toggleViewAction()); connect(this, QOverload::of(&EditorTab::signalNewEntryDisplayed), m_syncView, QOverload::of(&MergeView::slotNewEntryDisplayed)); connect(m_catalog, QOverload<>::of(&Catalog::signalFileLoaded), m_syncView, &MergeView::cleanup); connect(m_syncView, &MergeView::gotoEntry, this, QOverload::of(&EditorTab::gotoEntry)); m_syncViewSecondary = new MergeView(this, m_catalog, false); addDockWidget(Qt::BottomDockWidgetArea, m_syncViewSecondary); sync2->addAction(QStringLiteral("showmergeviewsecondary_action"), m_syncViewSecondary->toggleViewAction()); connect(this, QOverload::of(&EditorTab::signalNewEntryDisplayed), m_syncViewSecondary, QOverload::of(&MergeView::slotNewEntryDisplayed)); connect(m_catalog, QOverload<>::of(&Catalog::signalFileLoaded), m_syncViewSecondary, &MergeView::cleanup); connect(m_catalog, QOverload::of(&Catalog::signalFileLoaded), m_syncViewSecondary, QOverload::of(&MergeView::mergeOpen), Qt::QueuedConnection); connect(m_syncViewSecondary, &MergeView::gotoEntry, this, QOverload::of(&EditorTab::gotoEntry)); m_transUnitsView = new CatalogView(this, m_catalog); addDockWidget(Qt::LeftDockWidgetArea, m_transUnitsView); ac->addAction(QStringLiteral("showcatalogtreeview_action"), m_transUnitsView->toggleViewAction()); connect(this, QOverload::of(&EditorTab::signalNewEntryDisplayed), m_transUnitsView, QOverload::of(&CatalogView::slotNewEntryDisplayed)); connect(m_transUnitsView, &CatalogView::gotoEntry, this, QOverload::of(&EditorTab::gotoEntry)); connect(m_transUnitsView, &CatalogView::escaped, this, &EditorTab::setProperFocus); connect(m_syncView, &MergeView::mergeCatalogPointerChanged, m_transUnitsView, &CatalogView::setMergeCatalogPointer); m_notesView = new MsgCtxtView(this, m_catalog); addDockWidget(Qt::LeftDockWidgetArea, m_notesView); ac->addAction(QStringLiteral("showmsgctxt_action"), m_notesView->toggleViewAction()); connect(m_catalog, QOverload<>::of(&Catalog::signalFileLoaded), m_notesView, &MsgCtxtView::cleanup); connect(m_notesView, &MsgCtxtView::srcFileOpenRequested, this, &EditorTab::dispatchSrcFileOpenRequest); connect(m_view, &EditorView::signalChanged, m_notesView, &MsgCtxtView::removeErrorNotes); connect(m_notesView, &MsgCtxtView::escaped, this, &EditorTab::setProperFocus); action = edit->addAction(QStringLiteral("edit_addnote"), m_notesView, SLOT(addNoteUI())); //action->setShortcut(Qt::CTRL+glist[i]); action->setText(i18nc("@action:inmenu", "Add a note")); QVector tmactions_insert(TM_SHORTCUTS); QVector tmactions_remove(TM_SHORTCUTS); Qt::Key tmlist[TM_SHORTCUTS] = { Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4, Qt::Key_5, Qt::Key_6, Qt::Key_7, Qt::Key_8, Qt::Key_9, Qt::Key_0 }; QAction* tmaction; for (i = 0; i < TM_SHORTCUTS; ++i) { // action->setVisible(false); tmaction = tm->addAction(QStringLiteral("tmquery_insert_%1").arg(i)); ac->setDefaultShortcut(tmaction, QKeySequence(Qt::CTRL + tmlist[i])); tmaction->setText(i18nc("@action:inmenu", "Insert TM suggestion #%1", i + 1)); tmactions_insert[i] = tmaction; tmaction = tm->addAction(QStringLiteral("tmquery_remove_%1").arg(i)); ac->setDefaultShortcut(tmaction, QKeySequence(Qt::CTRL + Qt::ALT + tmlist[i])); tmaction->setText(i18nc("@action:inmenu", "Remove TM suggestion #%1", i + 1)); tmactions_remove[i] = tmaction; } #ifndef Q_OS_DARWIN if (systemLang == QLocale::Czech) { ac->setDefaultShortcuts(tmactions_insert[0], QList() << QKeySequence(Qt::CTRL + tmlist[0]) << QKeySequence(Qt::CTRL + Qt::Key_Plus)); ac->setDefaultShortcuts(tmactions_remove[0], QList() << QKeySequence(Qt::CTRL + Qt::ALT + tmlist[0]) << QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Plus)); } #endif TM::TMView* _tmView = new TM::TMView(this, m_catalog, tmactions_insert, tmactions_remove); addDockWidget(Qt::BottomDockWidgetArea, _tmView); tm->addAction(QStringLiteral("showtmqueryview_action"), _tmView->toggleViewAction()); connect(_tmView, &TM::TMView::refreshRequested, m_view, QOverload<>::of(&EditorView::gotoEntry), Qt::QueuedConnection); connect(_tmView, &TM::TMView::refreshRequested, this, &EditorTab::msgStrChanged, Qt::QueuedConnection); connect(_tmView, &TM::TMView::textInsertRequested, m_view, &EditorView::insertTerm); connect(_tmView, &TM::TMView::fileOpenRequested, this, &EditorTab::fileOpenRequested); connect(this, &EditorTab::fileAboutToBeClosed, m_catalog, &Catalog::flushUpdateDBBuffer); connect(this, &EditorTab::signalNewEntryDisplayed, m_catalog, &Catalog::flushUpdateDBBuffer); connect(this, &EditorTab::signalNewEntryDisplayed, _tmView, QOverload::of(&TM::TMView::slotNewEntryDisplayed)); //do this after flushUpdateDBBuffer QVector gactions(GLOSSARY_SHORTCUTS); Qt::Key glist[GLOSSARY_SHORTCUTS] = { Qt::Key_E, Qt::Key_H, // Qt::Key_G, // Qt::Key_I, // Qt::Key_J, // Qt::Key_K, Qt::Key_K, Qt::Key_P, Qt::Key_N, // Qt::Key_Q, // Qt::Key_R, // Qt::Key_U, // Qt::Key_V, // Qt::Key_W, // Qt::Key_X, Qt::Key_Y, // Qt::Key_Z, Qt::Key_BraceLeft, Qt::Key_BraceRight, Qt::Key_Semicolon, Qt::Key_Apostrophe }; QAction* gaction; // int i=0; for (i = 0; i < GLOSSARY_SHORTCUTS; ++i) { // action->setVisible(false); gaction = glossary->addAction(QStringLiteral("glossary_insert_%1").arg(i)); ac->setDefaultShortcut(gaction, QKeySequence(Qt::CTRL + glist[i])); gaction->setText(i18nc("@action:inmenu", "Insert term translation #%1", QString::number(i))); gactions[i] = gaction; } GlossaryNS::GlossaryView* _glossaryView = new GlossaryNS::GlossaryView(this, m_catalog, gactions); addDockWidget(Qt::BottomDockWidgetArea, _glossaryView); glossary->addAction(QStringLiteral("showglossaryview_action"), _glossaryView->toggleViewAction()); connect(this, &EditorTab::signalNewEntryDisplayed, _glossaryView, QOverload::of(&GlossaryNS::GlossaryView::slotNewEntryDisplayed)); connect(_glossaryView, &GlossaryNS::GlossaryView::termInsertRequested, m_view, &EditorView::insertTerm); gaction = glossary->addAction(QStringLiteral("glossary_define"), this, SLOT(defineNewTerm())); gaction->setText(i18nc("@action:inmenu", "Define new term")); _glossaryView->addAction(gaction); _glossaryView->setContextMenuPolicy(Qt::ActionsContextMenu); BinUnitsView* binUnitsView = new BinUnitsView(m_catalog, this); addDockWidget(Qt::BottomDockWidgetArea, binUnitsView); edit->addAction(QStringLiteral("showbinunitsview_action"), binUnitsView->toggleViewAction()); connect(m_view, &EditorView::binaryUnitSelectRequested, binUnitsView, &BinUnitsView::selectUnit); //#ifdef WEBQUERY_ENABLE #if 0 QVector wqactions(WEBQUERY_SHORTCUTS); Qt::Key wqlist[WEBQUERY_SHORTCUTS] = { Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4, Qt::Key_5, Qt::Key_6, Qt::Key_7, Qt::Key_8, Qt::Key_9, Qt::Key_0, }; QAction* wqaction; for (i = 0; i < WEBQUERY_SHORTCUTS; ++i) { // action->setVisible(false); wqaction = ac->addAction(QString("webquery_insert_%1").arg(i)); wqaction->setShortcut(Qt::CTRL + Qt::ALT + wqlist[i]); //wqaction->setShortcut(Qt::META+wqlist[i]); wqaction->setText(i18nc("@action:inmenu", "Insert WebQuery result #%1", i)); wqactions[i] = wqaction; } WebQueryView* _webQueryView = new WebQueryView(this, m_catalog, wqactions); addDockWidget(Qt::BottomDockWidgetArea, _webQueryView); ac->addAction(QStringLiteral("showwebqueryview_action"), _webQueryView->toggleViewAction()); connect(this, &EditorTab::signalNewEntryDisplayed, _webQueryView, SLOT(slotNewEntryDisplayed(DocPosition))); connect(_webQueryView, SIGNAL(textInsertRequested(QString)), m_view, SLOT(insertTerm(QString))); #endif //END dockwidgets actionCategory = file; // File action = file->addAction(KStandardAction::Save, this, SLOT(saveFile())); // action->setEnabled(false); // connect (m_catalog,SIGNAL(cleanChanged(bool)),action,SLOT(setDisabled(bool))); connect(m_catalog, &Catalog::cleanChanged, this, &EditorTab::setModificationSign); file->addAction(KStandardAction::SaveAs, this, SLOT(saveFileAs())); //action = KStandardAction::quit(qApp, SLOT(quit()), ac); //action->setText(i18nc("@action:inmenu","Close all Lokalize windows")); //KStandardAction::quit(kapp, SLOT(quit()), ac); //KStandardAction::quit(this, SLOT(deleteLater()), ac); #define ADD_ACTION_SHORTCUT_ICON(_name,_text,_shortcut,_icon)\ action = actionCategory->addAction(QStringLiteral(_name));\ action->setText(_text);\ action->setIcon(QIcon::fromTheme(QStringLiteral(_icon)));\ ac->setDefaultShortcut(action, QKeySequence( _shortcut )); #define ADD_ACTION_SHORTCUT(_name,_text,_shortcut)\ action = actionCategory->addAction(QStringLiteral(_name));\ action->setText(_text);\ ac->setDefaultShortcut(action, QKeySequence( _shortcut )); action = actionCategory->addAction(QStringLiteral("file_phases")); action->setText(i18nc("@action:inmenu", "Phases...")); connect(action, &QAction::triggered, this, &EditorTab::openPhasesWindow); ADD_ACTION_SHORTCUT("file_wordcount", i18nc("@action:inmenu", "Word count"), Qt::CTRL + Qt::ALT + Qt::Key_C) connect(action, &QAction::triggered, this, &EditorTab::displayWordCount); ADD_ACTION_SHORTCUT("file_cleartarget", i18nc("@action:inmenu", "Clear all translated entries"), Qt::CTRL + Qt::ALT + Qt::Key_D) connect(action, &QAction::triggered, this, &EditorTab::clearTranslatedEntries); ADD_ACTION_SHORTCUT("file_pology", i18nc("@action:inmenu", "Launch the Pology command on this file"), Qt::CTRL + Qt::ALT + Qt::Key_P) action->setEnabled(Settings::self()->pologyEnabled()); connect(action, &QAction::triggered, this, &EditorTab::launchPology); ADD_ACTION_SHORTCUT("file_xliff2odf", i18nc("@action:inmenu", "Merge translation into OpenDocument"), Qt::CTRL + Qt::Key_Backslash) connect(action, &QAction::triggered, this, &EditorTab::mergeIntoOpenDocument); connect(this, &EditorTab::xliffFileOpened, action, &QAction::setVisible); action->setVisible(false); //Edit actionCategory = edit; action = edit->addAction(KStandardAction::Undo, this, SLOT(undo())); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::undoRequested, this, &EditorTab::undo); connect(m_catalog, &Catalog::canUndoChanged, action, &QAction::setEnabled); action->setEnabled(false); action = edit->addAction(KStandardAction::Redo, this, SLOT(redo())); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::redoRequested, this, &EditorTab::redo); connect(m_catalog, &Catalog::canRedoChanged, action, &QAction::setEnabled); action->setEnabled(false); action = nav->addAction(KStandardAction::Find, this, SLOT(find())); action = nav->addAction(KStandardAction::FindNext, this, SLOT(findNext())); action = nav->addAction(KStandardAction::FindPrev, this, SLOT(findPrev())); action->setText(i18nc("@action:inmenu", "Change searching direction")); action = edit->addAction(KStandardAction::Replace, this, SLOT(replace())); connect(m_view, &EditorView::findRequested, this, &EditorTab::find); connect(m_view, &EditorView::findNextRequested, this, QOverload<>::of(&EditorTab::findNext)); connect(m_view, &EditorView::replaceRequested, this, &EditorTab::replace); action = actionCategory->addAction(QStringLiteral("edit_approve"), new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("approved")), i18nc("@option:check whether message is marked as translated/reviewed/approved (depending on your role)", "Approved"), this)); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_U)); action->setCheckable(true); connect(action, &QAction::triggered, m_view, &EditorView::toggleApprovement); connect(m_view, &EditorView::signalApprovedEntryDisplayed, this, &EditorTab::signalApprovedEntryDisplayed); connect(this, &EditorTab::signalApprovedEntryDisplayed, action, &QAction::setChecked); connect(this, &EditorTab::signalApprovedEntryDisplayed, this, &EditorTab::msgStrChanged, Qt::QueuedConnection); m_approveAction = action; m_stateAction = action; connect(Project::local(), &ProjectLocal::configChanged, this, &EditorTab::setApproveActionTitle); connect(m_catalog, &Catalog::activePhaseChanged, this, &EditorTab::setApproveActionTitle); connect(m_stateAction->menu(), &QMenu::aboutToShow, this, &EditorTab::showStatesMenu); connect(m_stateAction->menu(), &QMenu::triggered, this, &EditorTab::setState); action = actionCategory->addAction(QStringLiteral("edit_approve_go_fuzzyUntr")); action->setText(i18nc("@action:inmenu", "Approve and go to next")); connect(action, &QAction::triggered, this, &EditorTab::toggleApprovementGotoNextFuzzyUntr); m_approveAndGoAction = action; setApproveActionTitle(); action = actionCategory->addAction(QStringLiteral("edit_nonequiv"), m_view, SLOT(setEquivTrans(bool))); action->setText(i18nc("@action:inmenu", "Equivalent translation")); action->setCheckable(true); connect(this, &EditorTab::signalEquivTranslatedEntryDisplayed, action, &QAction::setChecked); #ifndef Q_OS_DARWIN int copyShortcut = Qt::CTRL + Qt::Key_Space; if (Q_UNLIKELY(systemLang == QLocale::Korean || systemLang == QLocale::Japanese || systemLang == QLocale::Chinese )) copyShortcut = Qt::ALT + Qt::Key_Space; #else int copyShortcut = Qt::META + Qt::Key_Space; #endif ADD_ACTION_SHORTCUT_ICON("edit_msgid2msgstr", i18nc("@action:inmenu", "Copy source to target"), copyShortcut, "msgid2msgstr") connect(action, &QAction::triggered, (const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::source2target); ADD_ACTION_SHORTCUT("edit_unwrap-target", i18nc("@action:inmenu", "Unwrap target"), Qt::CTRL + Qt::Key_I) connect(action, &QAction::triggered, m_view, QOverload<>::of(&EditorView::unwrap)); action = edit->addAction(QStringLiteral("edit_clear-target"), m_view->viewPort(), SLOT(removeTargetSubstring())); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_D)); action->setText(i18nc("@action:inmenu", "Clear")); action = edit->addAction(QStringLiteral("edit_tagmenu"), m_view->viewPort(), SLOT(tagMenu())); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_T)); action->setText(i18nc("@action:inmenu", "Insert Tag")); action = edit->addAction(QStringLiteral("edit_tagimmediate"), m_view->viewPort(), SLOT(tagImmediate())); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_M)); action->setText(i18nc("@action:inmenu", "Insert Next Tag")); action = edit->addAction(QStringLiteral("edit_completion"), m_view, SIGNAL(doExplicitCompletion())); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Space)); action->setText(i18nc("@action:inmenu", "Completion")); action = edit->addAction(QStringLiteral("edit_spellreplace"), m_view->viewPort(), SLOT(spellReplace())); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Equal)); action->setText(i18nc("@action:inmenu", "Replace with best spellcheck suggestion")); // action = ac->addAction("glossary_define",m_view,SLOT(defineNewTerm())); // action->setText(i18nc("@action:inmenu","Define new term")); // Go actionCategory = nav; action = nav->addAction(KStandardAction::Next, this, SLOT(gotoNext())); action->setText(i18nc("@action:inmenu entry", "&Next")); connect(this, &EditorTab::signalLastDisplayed, action, &QAction::setDisabled); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoNextRequested, this, &EditorTab::gotoNext); action = nav->addAction(KStandardAction::Prior, this, SLOT(gotoPrev())); action->setText(i18nc("@action:inmenu entry", "&Previous")); connect(this, &EditorTab::signalFirstDisplayed, action, &QAction::setDisabled); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoPrevRequested, this, &EditorTab::gotoPrev); action = nav->addAction(KStandardAction::FirstPage, this, SLOT(gotoFirst())); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoFirstRequested, this, &EditorTab::gotoFirst); action->setText(i18nc("@action:inmenu", "&First Entry")); action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Home)); connect(this, &EditorTab::signalFirstDisplayed, action, &QAction::setDisabled); action = nav->addAction(KStandardAction::LastPage, this, SLOT(gotoLast())); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoLastRequested, this, &EditorTab::gotoLast); action->setText(i18nc("@action:inmenu", "&Last Entry")); action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_End)); connect(this, &EditorTab::signalLastDisplayed, action, &QAction::setDisabled); action = nav->addAction(KStandardAction::GotoPage, this, SLOT(gotoEntry())); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_G)); action->setText(i18nc("@action:inmenu", "Entry by number")); ADD_ACTION_SHORTCUT_ICON("go_prev_fuzzy", i18nc("@action:inmenu\n'not ready' means 'fuzzy' in gettext terminology", "Previous non-empty but not ready"), Qt::CTRL + Qt::Key_PageUp, "prevfuzzy") connect(action, &QAction::triggered, this, &EditorTab::gotoPrevFuzzy); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoPrevFuzzyRequested, this, &EditorTab::gotoPrevFuzzy); connect(this, &EditorTab::signalPriorFuzzyAvailable, action, &QAction::setEnabled); ADD_ACTION_SHORTCUT_ICON("go_next_fuzzy", i18nc("@action:inmenu\n'not ready' means 'fuzzy' in gettext terminology", "Next non-empty but not ready"), Qt::CTRL + Qt::Key_PageDown, "nextfuzzy") connect(action, &QAction::triggered, this, &EditorTab::gotoNextFuzzy); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoNextFuzzyRequested, this, &EditorTab::gotoNextFuzzy); connect(this, &EditorTab::signalNextFuzzyAvailable, action, &QAction::setEnabled); ADD_ACTION_SHORTCUT_ICON("go_prev_untrans", i18nc("@action:inmenu", "Previous untranslated"), Qt::ALT + Qt::Key_PageUp, "prevuntranslated") connect(action, &QAction::triggered, this, &EditorTab::gotoPrevUntranslated); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoPrevUntranslatedRequested, this, &EditorTab::gotoPrevUntranslated); connect(this, &EditorTab::signalPriorUntranslatedAvailable, action, &QAction::setEnabled); ADD_ACTION_SHORTCUT_ICON("go_next_untrans", i18nc("@action:inmenu", "Next untranslated"), Qt::ALT + Qt::Key_PageDown, "nextuntranslated") connect(action, &QAction::triggered, this, &EditorTab::gotoNextUntranslated); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoNextUntranslatedRequested, this, &EditorTab::gotoNextUntranslated); connect(this, &EditorTab::signalNextUntranslatedAvailable, action, &QAction::setEnabled); ADD_ACTION_SHORTCUT_ICON("go_prev_fuzzyUntr", i18nc("@action:inmenu\n'not ready' means 'fuzzy' in gettext terminology", "Previous not ready"), Qt::CTRL + Qt::SHIFT/*ALT*/ + Qt::Key_PageUp, "prevfuzzyuntrans") connect(action, &QAction::triggered, this, &EditorTab::gotoPrevFuzzyUntr); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoPrevFuzzyUntrRequested, this, &EditorTab::gotoPrevFuzzyUntr); connect(this, &EditorTab::signalPriorFuzzyOrUntrAvailable, action, &QAction::setEnabled); ADD_ACTION_SHORTCUT_ICON("go_next_fuzzyUntr", i18nc("@action:inmenu\n'not ready' means 'fuzzy' in gettext terminology", "Next not ready"), Qt::CTRL + Qt::SHIFT + Qt::Key_PageDown, "nextfuzzyuntrans") connect(action, &QAction::triggered, this, QOverload<>::of(&EditorTab::gotoNextFuzzyUntr)); connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoNextFuzzyUntrRequested, this, QOverload<>::of(&EditorTab::gotoNextFuzzyUntr)); connect(this, &EditorTab::signalNextFuzzyOrUntrAvailable, action, &QAction::setEnabled); action = nav->addAction(QStringLiteral("go_focus_earch_line"), m_transUnitsView, SLOT(setFocus())); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_L)); action->setText(i18nc("@action:inmenu", "Focus the search line of Translation Units view")); //Bookmarks action = nav->addAction(KStandardAction::AddBookmark, m_view, SLOT(toggleBookmark(bool))); //action = ac->addAction("bookmark_do"); action->setText(i18nc("@option:check", "Bookmark message")); action->setCheckable(true); //connect(action, SIGNAL(triggered(bool)),m_view,SLOT(toggleBookmark(bool))); connect(this, &EditorTab::signalBookmarkDisplayed, action, &QAction::setChecked); action = nav->addAction(QStringLiteral("bookmark_prior"), this, SLOT(gotoPrevBookmark())); action->setText(i18nc("@action:inmenu", "Previous bookmark")); connect(this, &EditorTab::signalPriorBookmarkAvailable, action, &QAction::setEnabled); action = nav->addAction(QStringLiteral("bookmark_next"), this, SLOT(gotoNextBookmark())); action->setText(i18nc("@action:inmenu", "Next bookmark")); connect(this, &EditorTab::signalNextBookmarkAvailable, action, &QAction::setEnabled); //Tools edit->addAction(KStandardAction::Spelling, this, SLOT(spellcheck())); actionCategory = tm; // xgettext: no-c-format ADD_ACTION_SHORTCUT("tools_tm_batch", i18nc("@action:inmenu", "Fill in all exact suggestions"), Qt::CTRL + Qt::ALT + Qt::Key_B) connect(action, &QAction::triggered, _tmView, &TM::TMView::slotBatchTranslate); // xgettext: no-c-format ADD_ACTION_SHORTCUT("tools_tm_batch_fuzzy", i18nc("@action:inmenu", "Fill in all exact suggestions and mark as fuzzy"), Qt::CTRL + Qt::ALT + Qt::Key_N) connect(action, &QAction::triggered, _tmView, &TM::TMView::slotBatchTranslateFuzzy); //MergeMode action = sync1->addAction(QStringLiteral("merge_open"), m_syncView, SLOT(mergeOpen())); action->setText(i18nc("@action:inmenu", "Open file for sync/merge")); action->setStatusTip(i18nc("@info:status", "Open catalog to be merged into the current one / replicate base file changes to")); action->setToolTip(action->statusTip()); action->setWhatsThis(action->statusTip()); m_syncView->addAction(action); action = sync1->addAction(QStringLiteral("merge_prev"), m_syncView, SLOT(gotoPrevChanged())); action->setText(i18nc("@action:inmenu", "Previous different")); action->setStatusTip(i18nc("@info:status", "Previous entry which is translated differently in the file being merged, including empty translations in merge source")); action->setToolTip(action->statusTip()); action->setWhatsThis(action->statusTip()); ac->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Up)); connect(m_syncView, &MergeView::signalPriorChangedAvailable, action, &QAction::setEnabled); m_syncView->addAction(action); action = sync1->addAction(QStringLiteral("merge_next"), m_syncView, SLOT(gotoNextChanged())); action->setText(i18nc("@action:inmenu", "Next different")); action->setStatusTip(i18nc("@info:status", "Next entry which is translated differently in the file being merged, including empty translations in merge source")); action->setToolTip(action->statusTip()); action->setWhatsThis(action->statusTip()); ac->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Down)); connect(m_syncView, &MergeView::signalNextChangedAvailable, action, &QAction::setEnabled); m_syncView->addAction(action); action = sync1->addAction(QStringLiteral("merge_nextapproved"), m_syncView, SLOT(gotoNextChangedApproved())); action->setText(i18nc("@action:inmenu", "Next different approved")); ac->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::META + Qt::Key_Down)); connect(m_syncView, &MergeView::signalNextChangedAvailable, action, &QAction::setEnabled); m_syncView->addAction(action); action = sync1->addAction(QStringLiteral("merge_accept"), m_syncView, SLOT(mergeAccept())); action->setText(i18nc("@action:inmenu", "Copy from merging source")); action->setEnabled(false); ac->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Return)); connect(m_syncView, &MergeView::signalEntryWithMergeDisplayed, action, &QAction::setEnabled); m_syncView->addAction(action); action = sync1->addAction(QStringLiteral("merge_acceptnew"), m_syncView, SLOT(mergeAcceptAllForEmpty())); action->setText(i18nc("@action:inmenu", "Copy all new translations")); action->setStatusTip(i18nc("@info:status", "This changes only empty and non-ready entries in base file")); action->setToolTip(action->statusTip()); action->setWhatsThis(action->statusTip()); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_A)); connect(m_syncView, &MergeView::mergeCatalogAvailable, action, &QAction::setEnabled); m_syncView->addAction(action); //action->setShortcut(Qt::ALT+Qt::Key_E); action = sync1->addAction(QStringLiteral("merge_back"), m_syncView, SLOT(mergeBack())); action->setText(i18nc("@action:inmenu", "Copy to merging source")); connect(m_syncView, &MergeView::mergeCatalogAvailable, action, &QAction::setEnabled); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Return)); m_syncView->addAction(action); //Secondary merge action = sync2->addAction(QStringLiteral("mergesecondary_open"), m_syncViewSecondary, SLOT(mergeOpen())); action->setText(i18nc("@action:inmenu", "Open file for secondary sync")); action->setStatusTip(i18nc("@info:status", "Open catalog to be merged into the current one / replicate base file changes to")); action->setToolTip(action->statusTip()); action->setWhatsThis(action->statusTip()); m_syncViewSecondary->addAction(action); action = sync2->addAction(QStringLiteral("mergesecondary_prev"), m_syncViewSecondary, SLOT(gotoPrevChanged())); action->setText(i18nc("@action:inmenu", "Previous different")); action->setStatusTip(i18nc("@info:status", "Previous entry which is translated differently in the file being merged, including empty translations in merge source")); action->setToolTip(action->statusTip()); action->setWhatsThis(action->statusTip()); connect(m_syncView, &MergeView::signalPriorChangedAvailable, action, &QAction::setEnabled); m_syncViewSecondary->addAction(action); action = sync2->addAction(QStringLiteral("mergesecondary_next"), m_syncViewSecondary, SLOT(gotoNextChanged())); action->setText(i18nc("@action:inmenu", "Next different")); action->setStatusTip(i18nc("@info:status", "Next entry which is translated differently in the file being merged, including empty translations in merge source")); action->setToolTip(action->statusTip()); action->setWhatsThis(action->statusTip()); connect(m_syncView, &MergeView::signalNextChangedAvailable, action, &QAction::setEnabled); m_syncViewSecondary->addAction(action); action = sync2->addAction(QStringLiteral("mergesecondary_accept"), m_syncViewSecondary, SLOT(mergeAccept())); action->setText(i18nc("@action:inmenu", "Copy from merging source")); connect(m_syncView, &MergeView::signalEntryWithMergeDisplayed, action, &QAction::setEnabled); m_syncViewSecondary->addAction(action); action = sync2->addAction(QStringLiteral("mergesecondary_acceptnew"), m_syncViewSecondary, SLOT(mergeAcceptAllForEmpty())); action->setText(i18nc("@action:inmenu", "Copy all new translations")); action->setStatusTip(i18nc("@info:status", "This changes only empty entries")); action->setToolTip(action->statusTip()); action->setWhatsThis(action->statusTip()); m_syncViewSecondary->addAction(action); action = sync2->addAction(QStringLiteral("mergesecondary_back"), m_syncViewSecondary, SLOT(mergeBack())); action->setText(i18nc("@action:inmenu", "Copy to merging source")); m_syncViewSecondary->addAction(action); } void EditorTab::setProperFocus() { m_view->setProperFocus(); } void EditorTab::hideDocks() { if (m_transUnitsView->isFloating()) m_transUnitsView->hide(); } void EditorTab::showDocks() { return; if (m_transUnitsView->isFloating()) m_transUnitsView->show(); } void EditorTab::setProperCaption(QString title, bool modified) { if (m_catalog->autoSaveRecovered()) title += ' ' + i18nc("editor tab name", "(recovered)"); setWindowTitle(title + QStringLiteral(" [*]")); setWindowModified(modified); } void EditorTab::setFullPathShown(bool fullPathShown) { m_fullPathShown = fullPathShown; updateCaptionPath(); setModificationSign(); } void EditorTab::setModificationSign() { bool clean = m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified(); setProperCaption(_captionPath, !clean); } void EditorTab::updateCaptionPath() { QString url = m_catalog->url(); if (!m_project->isLoaded()) { _captionPath = url; return; } if (!m_fullPathShown) { _captionPath = QFileInfo(url).fileName(); return; } _captionPath = QDir(QFileInfo(m_project->path()).absolutePath()).relativeFilePath(url); if (_captionPath.contains(QLatin1String("../.."))) _captionPath = url; else if (_captionPath.startsWith(QLatin1String("./"))) _captionPath = _captionPath.mid(2); } bool EditorTab::fileOpen(QString filePath, QString suggestedDirPath, bool silent) { if (!m_catalog->isClean()) { switch (KMessageBox::warningYesNoCancel(SettingsController::instance()->mainWindowPtr(), i18nc("@info", "The document contains unsaved changes.\n" "Do you want to save your changes or discard them?"), i18nc("@title:window", "Warning"), KStandardGuiItem::save(), KStandardGuiItem::discard()) ) { case KMessageBox::Yes: if (!saveFile()) return false; break; case KMessageBox::Cancel: return false; default:; } } if (suggestedDirPath.isEmpty()) suggestedDirPath = m_catalog->url(); QString saidPath; if (filePath.isEmpty()) { //Prevent crashes //Project::instance()->model()->weaver()->suspend(); //KDE5PORT use mutex if the crash is still there with kfilemetadata library filePath = QFileDialog::getOpenFileName(SettingsController::instance()->mainWindowPtr(), i18nc("@title:window", "Select translation file"), suggestedDirPath, Catalog::supportedFileTypes(true));//" text/x-gettext-translation-template"); //Project::instance()->model()->weaver()->resume(); //TODO application/x-xliff, windows: just extensions //originalPath=url.path(); never used } else if (!QFile::exists(filePath) && Project::instance()->isLoaded()) { //check if we are opening template QString newPath = filePath; newPath.replace(Project::instance()->poDir(), Project::instance()->potDir()); if (QFile::exists(newPath) || QFile::exists(newPath += 't')) { saidPath = filePath; filePath = newPath; } } if (filePath.isEmpty()) return false; QApplication::setOverrideCursor(Qt::WaitCursor); QString prevFilePath = currentFilePath(); bool wasOpen = !m_catalog->isEmpty(); if (wasOpen) emit fileAboutToBeClosed(); int errorLine = m_catalog->loadFromUrl(filePath, saidPath); if (wasOpen && errorLine == 0) { emit fileClosed(); emit fileClosed(prevFilePath); } QApplication::restoreOverrideCursor(); if (errorLine == 0) { statusBarItems.insert(ID_STATUS_TOTAL, i18nc("@info:status message entries", "Total: %1", m_catalog->numberOfEntries())); numberOfUntranslatedChanged(); numberOfFuzziesChanged(); m_currentPos.entry = -1; //so the signals are emitted DocPosition pos(0); //we delay gotoEntry(pos) until project is loaded; //Project if (!m_project->isLoaded()) { QFileInfo fileInfo(filePath); //search for it int i = 4; QDir dir = fileInfo.dir(); QStringList proj(QStringLiteral("*.lokalize")); dir.setNameFilters(proj); while (--i && !dir.isRoot() && !m_project->isLoaded()) { if (dir.entryList().isEmpty()) { if (!dir.cdUp()) break; } else m_project->load(dir.absoluteFilePath(dir.entryList().first())); } //enforce autosync m_syncViewSecondary->mergeOpen(filePath); if (!m_project->isLoaded()) { if (m_project->desirablePath().isEmpty()) m_project->setDesirablePath(fileInfo.absolutePath() + QStringLiteral("/index.lokalize")); if (m_catalog->targetLangCode().isEmpty() /*&& m_project->targetLangCode().length()*/) m_catalog->setTargetLangCode(getTargetLangCode(fileInfo.fileName())); //_project->setLangCode(m_catalog->targetLangCode()); } } if (m_catalog->targetLangCode().isEmpty() /*&& m_project->targetLangCode().length()*/) m_catalog->setTargetLangCode(Project::instance()->targetLangCode()); gotoEntry(pos); updateCaptionPath(); setModificationSign(); //OK!!! emit xliffFileOpened(m_catalog->type() == Xliff); emit fileOpened(); return true; } if (!silent) { if (errorLine > 0) KMessageBox::error(this, i18nc("@info", "Error opening the file %1, line: %2", filePath, errorLine)); else KMessageBox::error(this, i18nc("@info", "Error opening the file %1", filePath)); } return false; } bool EditorTab::saveFileAs(const QString& defaultPath) { QString filePath = QFileDialog::getSaveFileName(this, i18nc("@title:window", "Save File As"), QFileInfo(defaultPath.isEmpty() ? m_catalog->url() : defaultPath).absoluteFilePath(), m_catalog->fileType()); if (filePath.isEmpty()) return false; if (!Catalog::extIsSupported(filePath) && m_catalog->url().contains('.')) filePath += m_catalog->url().midRef(m_catalog->url().lastIndexOf('.')); return saveFile(filePath); } bool EditorTab::saveFile(const QString& filePath) { bool clean = m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified() && filePath == m_catalog->url(); if (clean) return true; if (m_catalog->isClean() && filePath.isEmpty()) { emit m_catalog->signalFileSaved(); return true; } if (m_catalog->saveToUrl(filePath)) { updateCaptionPath(); setModificationSign(); emit fileSaved(filePath); return true; } const QString errorFilePath = filePath.isEmpty() ? m_catalog->url() : filePath; if (KMessageBox::Continue == KMessageBox::warningContinueCancel(this, i18nc("@info", "Error saving the file %1\n" "Do you want to save to another file or cancel?", errorFilePath), i18nc("@title", "Error"), KStandardGuiItem::save()) ) return saveFileAs(errorFilePath); return false; } void EditorTab::fileAutoSaveFailedWarning(const QString& fileName) { KMessageBox::information(this, i18nc("@info", "Could not perform file autosaving.\n" "The target file was %1.", fileName)); } EditorState EditorTab::state() { EditorState state; state.dockWidgets = saveState(); state.filePath = m_catalog->url(); state.mergeFilePath = m_syncView->filePath(); state.entry = m_currentPos.entry; //state.offset=_currentPos.offset; return state; } bool EditorTab::queryClose() { bool clean = m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified(); if (clean) return true; //TODO caption switch (KMessageBox::warningYesNoCancel(this, i18nc("@info", "The document contains unsaved changes.\n" "Do you want to save your changes or discard them?"), i18nc("@title:window", "Warning"), KStandardGuiItem::save(), KStandardGuiItem::discard())) { case KMessageBox::Yes: return saveFile(); case KMessageBox::No: return true; default: return false; } } void EditorTab::undo() { gotoEntry(m_catalog->undo(), 0); msgStrChanged(); } void EditorTab::redo() { gotoEntry(m_catalog->redo(), 0); msgStrChanged(); } void EditorTab::gotoEntry() { bool ok = false; DocPosition pos = m_currentPos; pos.entry = QInputDialog::getInt(this, i18nc("@title", "Jump to Entry"), i18nc("@label:spinbox", "Enter entry number:"), pos.entry, 1, m_catalog->numberOfEntries(), 1, &ok); if (ok) { --(pos.entry); gotoEntry(pos); } } void EditorTab::gotoEntry(DocPosition pos) { return gotoEntry(pos, 0); } void EditorTab::gotoEntry(DocPosition pos, int selection) { //specially for dbus users if (pos.entry >= m_catalog->numberOfEntries() || pos.entry < 0) return; if (!m_catalog->isPlural(pos)) pos.form = 0; m_currentPos.part = pos.part; //for searching; //UndefPart => called on fuzzy toggle bool newEntry = m_currentPos.entry != pos.entry || m_currentPos.form != pos.form; if (newEntry || pos.part == DocPosition::Comment) { m_notesView->gotoEntry(pos, pos.part == DocPosition::Comment ? selection : 0); if (pos.part == DocPosition::Comment) { pos.form = 0; pos.offset = 0; selection = 0; } } m_view->gotoEntry(pos, selection); if (pos.part == DocPosition::UndefPart) m_currentPos.part = DocPosition::Target; //QTime time; time.start(); if (newEntry) { m_currentPos = pos; if (true) { emit signalNewEntryDisplayed(pos); emit entryDisplayed(); emit signalFirstDisplayed(pos.entry == m_transUnitsView->firstEntryNumber()); emit signalLastDisplayed(pos.entry == m_transUnitsView->lastEntryNumber()); emit signalPriorFuzzyAvailable(pos.entry > m_catalog->firstFuzzyIndex()); emit signalNextFuzzyAvailable(pos.entry < m_catalog->lastFuzzyIndex()); emit signalPriorUntranslatedAvailable(pos.entry > m_catalog->firstUntranslatedIndex()); emit signalNextUntranslatedAvailable(pos.entry < m_catalog->lastUntranslatedIndex()); emit signalPriorFuzzyOrUntrAvailable(pos.entry > m_catalog->firstFuzzyIndex() || pos.entry > m_catalog->firstUntranslatedIndex() ); emit signalNextFuzzyOrUntrAvailable(pos.entry < m_catalog->lastFuzzyIndex() || pos.entry < m_catalog->lastUntranslatedIndex()); emit signalPriorBookmarkAvailable(pos.entry > m_catalog->firstBookmarkIndex()); emit signalNextBookmarkAvailable(pos.entry < m_catalog->lastBookmarkIndex()); emit signalBookmarkDisplayed(m_catalog->isBookmarked(pos.entry)); emit signalEquivTranslatedEntryDisplayed(m_catalog->isEquivTrans(pos)); emit signalApprovedEntryDisplayed(m_catalog->isApproved(pos)); } } statusBarItems.insert(ID_STATUS_CURRENT, i18nc("@info:status", "Current: %1", m_currentPos.entry + 1)); //qCDebug(LOKALIZE_LOG)<<"ELA "<msgstr(m_currentPos).isEmpty(); bool isApproved = m_catalog->isApproved(m_currentPos); if (isUntr == m_currentIsUntr && isApproved == m_currentIsApproved) return; QString msg; if (isUntr) msg = i18nc("@info:status", "Untranslated"); else if (isApproved)msg = i18nc("@info:status 'non-fuzzy' in gettext terminology", "Ready"); else msg = i18nc("@info:status 'fuzzy' in gettext terminology", "Needs review"); /* else statusBar()->changeItem("",ID_STATUS_ISFUZZY);*/ statusBarItems.insert(ID_STATUS_ISFUZZY, msg); m_currentIsUntr = isUntr; m_currentIsApproved = isApproved; } void EditorTab::switchForm(int newForm) { if (m_currentPos.form == newForm) return; DocPosition pos = m_currentPos; pos.form = newForm; gotoEntry(pos); } void EditorTab::gotoNextUnfiltered() { DocPosition pos = m_currentPos; if (switchNext(m_catalog, pos)) gotoEntry(pos); } void EditorTab::gotoPrevUnfiltered() { DocPosition pos = m_currentPos; if (switchPrev(m_catalog, pos)) gotoEntry(pos); } void EditorTab::gotoFirstUnfiltered() { gotoEntry(DocPosition(0)); } void EditorTab::gotoLastUnfiltered() { gotoEntry(DocPosition(m_catalog->numberOfEntries() - 1)); } void EditorTab::gotoFirst() { DocPosition pos = DocPosition(m_transUnitsView->firstEntryNumber()); if (pos.entry != -1) gotoEntry(pos); } void EditorTab::gotoLast() { DocPosition pos = DocPosition(m_transUnitsView->lastEntryNumber()); if (pos.entry != -1) gotoEntry(pos); } void EditorTab::gotoNext() { DocPosition pos = m_currentPos; if (m_catalog->isPlural(pos) && pos.form + 1 < m_catalog->numberOfPluralForms()) pos.form++; else pos = DocPosition(m_transUnitsView->nextEntryNumber()); if (pos.entry != -1) gotoEntry(pos); } void EditorTab::gotoPrev() { DocPosition pos = m_currentPos; if (m_catalog->isPlural(pos) && pos.form > 0) pos.form--; else pos = DocPosition(m_transUnitsView->prevEntryNumber()); if (pos.entry != -1) gotoEntry(pos); } void EditorTab::gotoPrevFuzzy() { DocPosition pos; if ((pos.entry = m_catalog->prevFuzzyIndex(m_currentPos.entry)) == -1) return; gotoEntry(pos); } void EditorTab::gotoNextFuzzy() { DocPosition pos; if ((pos.entry = m_catalog->nextFuzzyIndex(m_currentPos.entry)) == -1) return; gotoEntry(pos); } void EditorTab::gotoPrevUntranslated() { DocPosition pos; if ((pos.entry = m_catalog->prevUntranslatedIndex(m_currentPos.entry)) == -1) return; gotoEntry(pos); } void EditorTab::gotoNextUntranslated() { DocPosition pos; if ((pos.entry = m_catalog->nextUntranslatedIndex(m_currentPos.entry)) == -1) return; gotoEntry(pos); } void EditorTab::gotoPrevFuzzyUntr() { DocPosition pos; short fu = m_catalog->prevFuzzyIndex(m_currentPos.entry); short un = m_catalog->prevUntranslatedIndex(m_currentPos.entry); pos.entry = fu > un ? fu : un; if (pos.entry == -1) return; gotoEntry(pos); } bool EditorTab::gotoNextFuzzyUntr() { return gotoNextFuzzyUntr(DocPosition()); } bool EditorTab::gotoNextFuzzyUntr(const DocPosition& p) { int index = (p.entry == -1) ? m_currentPos.entry : p.entry; DocPosition pos; short fu = m_catalog->nextFuzzyIndex(index); short un = m_catalog->nextUntranslatedIndex(index); if ((fu == -1) && (un == -1)) return false; if (fu == -1) fu = un; else if (un == -1) un = fu; pos.entry = fu < un ? fu : un; gotoEntry(pos); return true; } void EditorTab::toggleApprovementGotoNextFuzzyUntr() { if (!m_catalog->isApproved(m_currentPos.entry)) m_view->toggleApprovement(); if (!gotoNextFuzzyUntr()) gotoNextFuzzyUntr(DocPosition(-2));//so that we don't skip the first } void EditorTab::setApproveActionTitle() { const char* const titles[] = { I18N_NOOP2("@option:check trans-unit state", "Translated"), I18N_NOOP2("@option:check trans-unit state", "Signed-off"), I18N_NOOP2("@option:check trans-unit state", "Approved") }; const char* const helpText[] = { I18N_NOOP2("@info:tooltip", "Translation is done (although still may need a review)"), I18N_NOOP2("@info:tooltip", "Translation has received positive review"), I18N_NOOP2("@info:tooltip", "Entry is fully localized (i.e. final)") }; int role = m_catalog->activePhaseRole(); if (role == ProjectLocal::Undefined) role = Project::local()->role(); m_approveAction->setText(i18nc("@option:check trans-unit state", titles[role])); m_approveAction->setToolTip(i18nc("@info:tooltip", helpText[role])); m_approveAndGoAction->setVisible(role == ProjectLocal::Approver); } void EditorTab::showStatesMenu() { m_stateAction->menu()->clear(); if (!(m_catalog->capabilities()&ExtendedStates)) { QAction* a = m_stateAction->menu()->addAction(i18nc("@info:status 'fuzzy' in gettext terminology", "Needs review")); a->setData(QVariant(-1)); a->setCheckable(true); a->setChecked(!m_catalog->isApproved(m_currentPos)); a = m_stateAction->menu()->addAction(i18nc("@info:status 'non-fuzzy' in gettext terminology", "Ready")); a->setData(QVariant(-2)); a->setCheckable(true); a->setChecked(m_catalog->isApproved(m_currentPos)); return; } TargetState state = m_catalog->state(m_currentPos); const char* const* states = Catalog::states(); for (int i = 0; i < StateCount; ++i) { QAction* a = m_stateAction->menu()->addAction(i18n(states[i])); a->setData(QVariant(i)); a->setCheckable(true); a->setChecked(state == i); if (i == New || i == Translated || i == Final) m_stateAction->menu()->addSeparator(); } } void EditorTab::setState(QAction* a) { if (!(m_catalog->capabilities()&ExtendedStates)) m_view->toggleApprovement(); else m_view->setState(TargetState(a->data().toInt())); m_stateAction->menu()->clear(); } void EditorTab::openPhasesWindow() { PhasesWindow w(m_catalog, this); w.exec(); } void EditorTab::gotoPrevBookmark() { DocPosition pos; if ((pos.entry = m_catalog->prevBookmarkIndex(m_currentPos.entry)) == -1) return; gotoEntry(pos); } void EditorTab::gotoNextBookmark() { DocPosition pos; if ((pos.entry = m_catalog->nextBookmarkIndex(m_currentPos.entry)) == -1) return; gotoEntry(pos); } //wrapper for cmdline handling... void EditorTab::mergeOpen(QString mergeFilePath) { m_syncView->mergeOpen(mergeFilePath); } //HACK to prevent redundant repaintings when widget isn't visible void EditorTab::paintEvent(QPaintEvent* event) { if (QMdiSubWindow* sw = qobject_cast(parent())) { if (sw->mdiArea()->currentSubWindow() != sw) return; } LokalizeSubwindowBase2::paintEvent(event); } void EditorTab::indexWordsForCompletion() { CompletionStorage::instance()->scanCatalog(m_catalog); } void EditorTab::launchPology() { if (!m_pologyProcessInProgress) { QString command = Settings::self()->pologyCommandFile().replace(QStringLiteral("%f"), QStringLiteral("\"") + currentFilePath() + QStringLiteral("\"")); m_pologyProcess = new KProcess; m_pologyProcess->setOutputChannelMode(KProcess::SeparateChannels); qCWarning(LOKALIZE_LOG) << "Launching pology command: " << command; connect(m_pologyProcess, QOverload::of(&KProcess::finished), this, &EditorTab::pologyHasFinished); m_pologyProcess->setShellCommand(command); m_pologyProcessInProgress = true; m_pologyProcess->start(); } else { KMessageBox::error(this, i18n("A Pology check is already in progress."), i18n("Pology error")); } } void EditorTab::pologyHasFinished(int exitCode, QProcess::ExitStatus exitStatus) { const QString pologyError = m_pologyProcess->readAllStandardError(); if (exitStatus == QProcess::CrashExit) { KMessageBox::error(this, i18n("The Pology check has crashed unexpectedly:\n%1", pologyError), i18n("Pology error")); } else if (exitCode == 0) { KMessageBox::information(this, i18n("The Pology check has succeeded."), i18n("Pology success")); } else { KMessageBox::error(this, i18n("The Pology check has returned an error:\n%1", pologyError), i18n("Pology error")); } m_pologyProcess->deleteLater(); m_pologyProcessInProgress = false; } void EditorTab::clearTranslatedEntries() { switch (KMessageBox::warningYesNoCancel(this, i18nc("@info", "This will delete all the translations from the file.\n" "Do you really want to clear all translated entries?"), i18nc("@title:window", "Warning"), KStandardGuiItem::yes(), KStandardGuiItem::no())) { case KMessageBox::Yes: { DocPosition pos(0); do { removeTargetSubstring(m_catalog, pos); } while (switchNext(m_catalog, pos)); msgStrChanged(); gotoEntry(m_currentPos); } default:; } } void EditorTab::displayWordCount() { //TODO in trans and fuzzy separately int sourceCount = 0; int targetCount = 0; QRegExp rxClean(Project::instance()->markup() % '|' % Project::instance()->accel()); //cleaning regexp; NOTE isEmpty()? QRegExp rxSplit(QStringLiteral("\\W|\\d"));//splitting regexp DocPosition pos(0); do { QString msg = m_catalog->source(pos); msg.remove(rxClean); QStringList words = msg.split(rxSplit, QString::SkipEmptyParts); sourceCount += words.size(); msg = m_catalog->target(pos); msg.remove(rxClean); words = msg.split(rxSplit, QString::SkipEmptyParts); targetCount += words.size(); } while (switchNext(m_catalog, pos)); KMessageBox::information(this, i18nc("@info words count", "Source text words: %1
Target text words: %2", sourceCount, targetCount), i18nc("@title", "Word Count")); } bool EditorTab::findEntryBySourceContext(const QString& source, const QString& ctxt) { DocPosition pos(0); do { if (m_catalog->source(pos) == source && m_catalog->context(pos.entry) == QStringList(ctxt)) { gotoEntry(pos); return true; } } while (switchNext(m_catalog, pos)); return false; } //see also termlabel.h void EditorTab::defineNewTerm() { //TODO just a word under cursor? QString en(m_view->selectionInSource().toLower()); if (en.isEmpty()) en = m_catalog->msgid(m_currentPos).toLower(); QString target(m_view->selectionInTarget().toLower()); if (target.isEmpty()) target = m_catalog->msgstr(m_currentPos).toLower(); m_project->defineNewTerm(en, target); } void EditorTab::reloadFile() { QString mergeFilePath = m_syncView->filePath(); DocPosition p = m_currentPos; if (!fileOpen(currentFilePath())) return; gotoEntry(p); if (!mergeFilePath.isEmpty()) mergeOpen(mergeFilePath); } static void openLxrSearch(const QString& srcFileRelPath) { QDesktopServices::openUrl(QUrl(QStringLiteral("https://lxr.kde.org/search?_filestring=") + QString::fromLatin1(QUrl::toPercentEncoding(srcFileRelPath)))); } static void openLocalSource(const QString& file, int line) { if (Settings::self()->customEditorEnabled()) QProcess::startDetached(QString(Settings::self()->customEditorCommand()).arg(file).arg(line)); else QDesktopServices::openUrl(QUrl::fromLocalFile(file)); } void EditorTab::dispatchSrcFileOpenRequest(const QString& srcFileRelPath, int line) { // Try project scripts first. m_srcFileOpenRequestAccepted = false; emit srcFileOpenRequested(srcFileRelPath, line); if (m_srcFileOpenRequestAccepted) return; // If project scripts do not handle opening the source file, check if the // path exists relative to the current translation file path. QDir relativePath(currentFilePath()); relativePath.cdUp(); QString srcAbsolutePath(relativePath.absoluteFilePath(srcFileRelPath)); if (QFile::exists(srcAbsolutePath)) { openLocalSource(srcAbsolutePath, line); return; } QString dir = Project::instance()->local()->sourceDir(); if (dir.isEmpty()) { switch (KMessageBox::questionYesNoCancel(SettingsController::instance()->mainWindowPtr(), i18nc("@info", "Would you like to search for the source file locally or via lxr.kde.org?"), i18nc("@title:window", "Source file lookup"), KGuiItem(i18n("Locally")), KGuiItem("lxr.kde.org") )) { case KMessageBox::Yes: break; case KMessageBox::No: openLxrSearch(srcFileRelPath); case KMessageBox::Cancel: default: return; } } //hard fallback if (dir.isEmpty()) { dir = QFileDialog::getExistingDirectory(this, i18n("Select project's base folder for source file lookup"), QDir::homePath()); if (dir.length()) Project::instance()->local()->setSourceDir(dir); } if (dir.length()) { auto doOpen = [srcFileRelPath, dir, line]() { auto sourceFilePaths = Project::instance()->sourceFilePaths(); QString absFilePath = QString("%1/%2").arg(dir, srcFileRelPath); if (QFileInfo::exists(absFilePath)) { openLocalSource(absFilePath, line); return; } bool found = false; QByteArray fn = srcFileRelPath.midRef(srcFileRelPath.lastIndexOf('/') + 1).toUtf8(); auto it = sourceFilePaths.constFind(fn); while (it != sourceFilePaths.constEnd() && it.key() == fn) { const QString absFilePath = QString::fromUtf8(it.value() + '/' + fn); if (!absFilePath.endsWith(srcFileRelPath) || !QFileInfo::exists(absFilePath)) { //for the case when link contained also folders it++; continue; } found = true; openLocalSource(absFilePath, line); it++; } if (!found) { switch (KMessageBox::warningYesNoCancel(SettingsController::instance()->mainWindowPtr(), i18nc("@info", "Could not find source file in the folder specified.\n" "Do you want to change source files folder?"), i18nc("@title:window", "Source file lookup"), KStandardGuiItem::yes(), KStandardGuiItem::no(), KGuiItem(i18n("lxr.kde.org")))) { case KMessageBox::Cancel: Project::instance()->local()->setSourceDir(QString()); Project::instance()->resetSourceFilePaths(); openLxrSearch(srcFileRelPath); case KMessageBox::No: return; default: ; //fall through to dir selection } QString dir = QFileDialog::getExistingDirectory(0, i18n("Select project's base folder for source file lookup"), Project::instance()->local()->sourceDir()); if (dir.length()) { Project::instance()->local()->setSourceDir(dir); Project::instance()->resetSourceFilePaths(); } } }; if (!Project::instance()->sourceFilePaths().isEmpty()) doOpen(); else connect(Project::instance(), &Project::sourceFilePathsAreReady, doOpen); return; } // Otherwise, let the user know how to create a project script to handle // opening source file paths that are not relative to translation files. KMessageBox::information(this, i18nc("@info", "Cannot open the target source file: The target source file is not " "relative to the current translation file, and there are currently no " "scripts loaded to handle opening source files in custom paths. Refer " "to the Lokalize handbook for script examples and how to plug them " "into your project.")); } void EditorTab::mergeIntoOpenDocument() { if (!m_catalog || m_catalog->type() != Xliff) return; QString xliff2odf = QStringLiteral("xliff2odf"); if (QProcess::execute(xliff2odf, QStringList(QLatin1String("--version"))) == -2) { KMessageBox::error(SettingsController::instance()->mainWindowPtr(), i18n("Install translate-toolkit package and retry.")); return; } QString xliffFolder = QFileInfo(m_catalog->url()).absolutePath(); QString originalOdfFilePath = m_catalog->originalOdfFilePath(); if (originalOdfFilePath.isEmpty() || !QFile::exists(originalOdfFilePath)) { originalOdfFilePath = QFileDialog::getOpenFileName( SettingsController::instance()->mainWindowPtr(), i18n("Select original OpenDocument on which current XLIFF file is based"), xliffFolder, i18n("OpenDocument files (*.odt *.ods)")/*"text/x-lokalize-project"*/); if (originalOdfFilePath.length()) m_catalog->setOriginalOdfFilePath(originalOdfFilePath); } if (originalOdfFilePath.isEmpty()) return; saveFile(); //TODO check if odt did update (merge with new template is needed) QFileInfo originalOdfFileInfo(originalOdfFilePath); QString targetLangCode = m_catalog->targetLangCode(); QStringList args(m_catalog->url()); args.append(xliffFolder % '/' % originalOdfFileInfo.baseName() % '-' % targetLangCode % '.' % originalOdfFileInfo.suffix()); args.append(QStringLiteral("-t")); args.append(originalOdfFilePath); qCDebug(LOKALIZE_LOG) << args; QProcess::execute(xliff2odf, args); if (!QFile::exists(args.at(1))) return; //if (originalOdfFileInfo.suffix().toLower()==QLatin1String(".odt")) { QString lowriter = QStringLiteral("soffice"); if (QProcess::execute(lowriter, QStringList("--version")) == -2) { //TODO //KMessageBox::error(SettingsController::instance()->mainWindowPtr(), i18n("Install translate-toolkit package and retry")); return; } QProcess::startDetached(lowriter, QStringList(args.at(1))); QString reloaderScript = QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("scripts/odf/xliff2odf-standalone.py")); if (reloaderScript.length()) { QString python = QStringLiteral("python"); QStringList unoArgs(QStringLiteral("-c")); unoArgs.append(QStringLiteral("import uno")); if (QProcess::execute(python, unoArgs) != 0) { python = QStringLiteral("python3"); QStringList unoArgs(QStringLiteral("-c")); unoArgs.append(QStringLiteral("import uno")); if (QProcess::execute(python, unoArgs) != 0) { KMessageBox::information(SettingsController::instance()->mainWindowPtr(), i18n("Install python-uno package for additional functionality.")); return; } } QStringList reloaderArgs(reloaderScript); reloaderArgs.append(args.at(1)); reloaderArgs.append(currentEntryId()); QProcess::execute(python, reloaderArgs); } } } //BEGIN DBus interface #include "editoradaptor.h" QList EditorTab::ids; QString EditorTab::dbusObjectPath() { const QString EDITOR_PATH = QStringLiteral("/ThisIsWhatYouWant/Editor/"); if (m_dbusId == -1) { m_adaptor = new EditorAdaptor(this); int i = 0; while (i < ids.size() && i == ids.at(i)) ++i; ids.insert(i, i); m_dbusId = i; QDBusConnection::sessionBus().registerObject(EDITOR_PATH + QString::number(m_dbusId), this); } return EDITOR_PATH + QString::number(m_dbusId); } QString EditorTab::currentFilePath() { return m_catalog->url(); } QByteArray EditorTab::currentFileContents() { return m_catalog->contents(); } QString EditorTab::currentEntryId() { return m_catalog->id(m_currentPos); } QString EditorTab::selectionInTarget() { return m_view->selectionInTarget(); } QString EditorTab::selectionInSource() { return m_view->selectionInSource(); } void EditorTab::lookupSelectionInTranslationMemory() { emit tmLookupRequested(selectionInSource(), selectionInTarget()); } void EditorTab::setEntryFilteredOut(int entry, bool filteredOut) { m_transUnitsView->setEntryFilteredOut(entry, filteredOut); } void EditorTab::setEntriesFilteredOut(bool filteredOut) { m_transUnitsView->setEntriesFilteredOut(filteredOut); } int EditorTab::entryCount() { return m_catalog->numberOfEntries(); } QString EditorTab::entrySource(int entry, int form) { return m_catalog->sourceWithTags(DocPosition(entry, form)).string; } QString EditorTab::entryTarget(int entry, int form) { return m_catalog->targetWithTags(DocPosition(entry, form)).string; } int EditorTab::entryPluralFormCount(int entry) { return m_catalog->isPlural(entry) ? m_catalog->numberOfPluralForms() : 1; } bool EditorTab::entryReady(int entry) { return m_catalog->isApproved(entry); } QString EditorTab::sourceLangCode() { return m_catalog->sourceLangCode(); } QString EditorTab::targetLangCode() { return m_catalog->targetLangCode(); } void EditorTab::addEntryNote(int entry, const QString& note) { m_notesView->addNote(entry, note); } void EditorTab::addTemporaryEntryNote(int entry, const QString& note) { m_notesView->addTemporaryEntryNote(entry, note); } void EditorTab::addAlternateTranslation(int entry, const QString& translation) { m_altTransView->addAlternateTranslation(entry, translation); } void EditorTab::addTemporaryAlternateTranslation(int entry, const QString& translation) { m_altTransView->addAlternateTranslation(entry, translation); } void EditorTab::attachAlternateTranslationFile(const QString& path) { m_altTransView->attachAltTransFile(path); } void EditorTab::setEntryTarget(int entry, int form, const QString& content) { DocPosition pos(entry, form); m_catalog->beginMacro(i18nc("@item Undo action item", "Set unit text")); removeTargetSubstring(m_catalog, pos); insertCatalogString(m_catalog, pos, CatalogString(content)); m_catalog->endMacro(); if (m_currentPos == pos) m_view->gotoEntry(); } //END DBus interface