Warning, file /sdk/lokalize/src/editortab.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 This file is part of Lokalize 0003 0004 SPDX-FileCopyrightText: 2007-2014 Nick Shaforostoff <shafff@ukr.net> 0005 SPDX-FileCopyrightText: 2018-2019 Simon Depiets <sdepiets@gmail.com> 0006 SPDX-FileCopyrightText: 2023 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0007 0008 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0009 */ 0010 0011 #include "editortab.h" 0012 0013 #include "xlifftextedit.h" 0014 0015 #include "lokalize_debug.h" 0016 0017 #include "actionproxy.h" 0018 #include "editorview.h" 0019 #include "catalog.h" 0020 #include "pos.h" 0021 #include "cmd.h" 0022 0023 #include "completionstorage.h" 0024 0025 #define WEBQUERY_ENABLE 0026 0027 //views 0028 #include "msgctxtview.h" 0029 #include "alttransview.h" 0030 #include "mergeview.h" 0031 #include "cataloglistview.h" 0032 #include "glossaryview.h" 0033 #ifdef WEBQUERY_ENABLE 0034 #include "webqueryview.h" 0035 #endif 0036 #include "tmview.h" 0037 #include "binunitsview.h" 0038 0039 #include "phaseswindow.h" 0040 #include "projectlocal.h" 0041 0042 0043 #include "project.h" 0044 #include "prefs.h" 0045 #include "prefs_lokalize.h" 0046 #include "languagelistmodel.h" 0047 0048 #include <KToolBarPopupAction> 0049 #include <KActionCollection> 0050 #include <KStandardAction> 0051 #include <KStandardShortcut> 0052 #include <KXMLGUIFactory> 0053 #include <KActionCategory> 0054 #include <KMessageBox> 0055 #include <KLocalizedString> 0056 #include <KShell> 0057 0058 #include <QDesktopServices> 0059 #include <QIcon> 0060 #include <QActionGroup> 0061 #include <QMdiArea> 0062 #include <QMdiSubWindow> 0063 #include <QMenuBar> 0064 #include <QFileDialog> 0065 #include <QInputDialog> 0066 #include <QApplication> 0067 #include <QDir> 0068 #include <QTime> 0069 #include <QStringBuilder> 0070 #include <QProcess> 0071 #include <QStandardPaths> 0072 0073 0074 EditorTab::EditorTab(QWidget* parent, bool valid) 0075 : LokalizeSubwindowBase2(parent) 0076 , m_project(Project::instance()) 0077 , m_catalog(new Catalog(this)) 0078 , m_view(new EditorView(this, m_catalog/*,new keyEventHandler(this,m_catalog)*/)) 0079 , m_pologyProcessInProgress(false) 0080 , m_sonnetDialog(nullptr) 0081 , m_spellcheckStartUndoIndex(0) 0082 , m_spellcheckStop(false) 0083 , m_currentIsApproved(true) 0084 , m_currentIsUntr(true) 0085 , m_fullPathShown(false) 0086 , m_doReplaceCalled(false) 0087 , m_find(nullptr) 0088 , m_replace(nullptr) 0089 , m_syncView(nullptr) 0090 , m_syncViewSecondary(nullptr) 0091 , m_valid(valid) 0092 , m_dbusId(-1) 0093 { 0094 //QTime chrono;chrono.start(); 0095 0096 setAcceptDrops(true); 0097 setCentralWidget(m_view); 0098 setupStatusBar(); //--NOT called from initLater() ! 0099 setupActions(); 0100 0101 dbusObjectPath(); 0102 0103 connect(m_view, &EditorView::signalChanged, this, &EditorTab::msgStrChanged); 0104 msgStrChanged(); 0105 connect(SettingsController::instance(), &SettingsController::generalSettingsChanged, m_view, &EditorView::settingsChanged); 0106 connect(m_view->tabBar(), &QTabBar::currentChanged, this, &EditorTab::switchForm); 0107 0108 connect(m_view, QOverload<const DocPosition &>::of(&EditorView::gotoEntryRequested), this, QOverload<DocPosition>::of(&EditorTab::gotoEntry)); 0109 connect(m_view, &EditorView::tmLookupRequested, this, &EditorTab::lookupSelectionInTranslationMemory); 0110 0111 connect(this, &EditorTab::fileOpened, this, &EditorTab::indexWordsForCompletion, Qt::QueuedConnection); 0112 0113 connect(m_catalog, &Catalog::signalFileAutoSaveFailed, this, &EditorTab::fileAutoSaveFailedWarning); 0114 0115 0116 //defer some work to make window appear earlier (~200 msec on my Core Duo) 0117 //QTimer::singleShot(0,this,SLOT(initLater())); 0118 //qCWarning(LOKALIZE_LOG)<<chrono.elapsed(); 0119 } 0120 0121 void EditorTab::initLater() 0122 { 0123 } 0124 0125 EditorTab::~EditorTab() 0126 { 0127 disconnect(m_catalog, &Catalog::signalNumberOfFuzziesChanged, this, &EditorTab::numberOfFuzziesChanged); 0128 disconnect(m_catalog, &Catalog::signalNumberOfEmptyChanged, this, &EditorTab::numberOfUntranslatedChanged); 0129 0130 if (!m_catalog->isEmpty()) { 0131 Q_EMIT fileAboutToBeClosed(); 0132 Q_EMIT fileClosed(); 0133 Q_EMIT fileClosed(currentFile()); 0134 } 0135 0136 ids.removeAll(m_dbusId); 0137 0138 delete m_catalog; 0139 } 0140 0141 0142 void EditorTab::setupStatusBar() 0143 { 0144 statusBarItems.insert(ID_STATUS_CURRENT, i18nc("@info:status message entry", "Current: %1", 0)); 0145 statusBarItems.insert(ID_STATUS_TOTAL, i18nc("@info:status message entries", "Total: %1", 0)); 0146 statusBarItems.insert(ID_STATUS_FUZZY, i18nc("@info:status message entries\n'fuzzy' in gettext terminology", "Not ready: %1", 0)); 0147 statusBarItems.insert(ID_STATUS_UNTRANS, i18nc("@info:status message entries", "Untranslated: %1", 0)); 0148 statusBarItems.insert(ID_STATUS_ISFUZZY, QString()); 0149 0150 connect(m_catalog, &Catalog::signalNumberOfFuzziesChanged, this, &EditorTab::numberOfFuzziesChanged); 0151 connect(m_catalog, &Catalog::signalNumberOfEmptyChanged, this, &EditorTab::numberOfUntranslatedChanged); 0152 } 0153 0154 void LokalizeSubwindowBase::reflectNonApprovedCount(int count, int total) 0155 { 0156 QString text = i18nc("@info:status message entries\n'fuzzy' in gettext terminology", "Not ready: %1", count); 0157 if (count && total) 0158 text += i18nc("percentages in statusbar", " (%1%)", int(100.0 * count / total)); 0159 statusBarItems.insert(ID_STATUS_FUZZY, text); 0160 } 0161 0162 void LokalizeSubwindowBase::reflectUntranslatedCount(int count, int total) 0163 { 0164 QString text = i18nc("@info:status message entries", "Untranslated: %1", count); 0165 if (count && total) 0166 text += i18nc("percentages in statusbar", " (%1%)", int(100.0 * count / total)); 0167 statusBarItems.insert(ID_STATUS_UNTRANS, text); 0168 } 0169 0170 void EditorTab::numberOfFuzziesChanged() 0171 { 0172 reflectNonApprovedCount(m_catalog->numberOfNonApproved(), m_catalog->numberOfEntries()); 0173 } 0174 0175 void EditorTab::numberOfUntranslatedChanged() 0176 { 0177 reflectUntranslatedCount(m_catalog->numberOfUntranslated(), m_catalog->numberOfEntries()); 0178 } 0179 0180 void EditorTab::setupActions() 0181 { 0182 //all operations that can be done after initial setup 0183 //(via QTimer::singleShot) go to initLater() 0184 0185 setXMLFile(QStringLiteral("editorui.rc")); 0186 setUpdatedXMLFile(); 0187 0188 QAction *action; 0189 KActionCollection* ac = actionCollection(); 0190 KActionCategory* actionCategory; 0191 0192 KActionCategory* file = new KActionCategory(i18nc("@title actions category", "File"), ac); 0193 KActionCategory* nav = new KActionCategory(i18nc("@title actions category", "Navigation"), ac); 0194 KActionCategory* edit = new KActionCategory(i18nc("@title actions category", "Editing"), ac); 0195 KActionCategory* sync1 = new KActionCategory(i18n("Synchronization 1"), ac); 0196 KActionCategory* sync2 = new KActionCategory(i18n("Synchronization 2"), ac); 0197 KActionCategory* tm = new KActionCategory(i18n("Translation Memory"), ac); 0198 KActionCategory* glossary = new KActionCategory(i18nc("@title actions category", "Glossary"), ac); 0199 //KActionCategory* tools=new KActionCategory(i18nc("@title actions category","Tools"), ac); 0200 0201 #ifndef Q_OS_DARWIN 0202 QLocale::Language systemLang = QLocale::system().language(); 0203 #endif 0204 0205 0206 //BEGIN dockwidgets 0207 int i = 0; 0208 0209 QVector<QAction*> altactions(ALTTRANS_SHORTCUTS); 0210 Qt::Key altlist[ALTTRANS_SHORTCUTS] = { 0211 Qt::Key_1, 0212 Qt::Key_2, 0213 Qt::Key_3, 0214 Qt::Key_4, 0215 Qt::Key_5, 0216 Qt::Key_6, 0217 Qt::Key_7, 0218 Qt::Key_8, 0219 Qt::Key_9 0220 }; 0221 QAction* altaction; 0222 for (i = 0; i < ALTTRANS_SHORTCUTS; ++i) { 0223 altaction = tm->addAction(QStringLiteral("alttrans_insert_%1").arg(i)); 0224 ac->setDefaultShortcut(altaction, QKeySequence(Qt::ALT + altlist[i])); 0225 altaction->setText(i18nc("@action:inmenu", "Insert alternate translation #%1", QString::number(i))); 0226 altactions[i] = altaction; 0227 } 0228 0229 m_altTransView = new AltTransView(this, m_catalog, altactions); 0230 addDockWidget(Qt::BottomDockWidgetArea, m_altTransView); 0231 ac->addAction(QStringLiteral("showmsgiddiff_action"), m_altTransView->toggleViewAction()); 0232 connect(this, QOverload<const DocPosition &>::of(&EditorTab::signalNewEntryDisplayed), m_altTransView, QOverload<const DocPosition &>::of(&AltTransView::slotNewEntryDisplayed)); 0233 connect(m_altTransView, &AltTransView::textInsertRequested, m_view, &EditorView::insertTerm); 0234 connect(m_altTransView, &AltTransView::refreshRequested, m_view, QOverload<>::of(&EditorView::gotoEntry), Qt::QueuedConnection); 0235 connect(m_catalog, QOverload<>::of(&Catalog::signalFileLoaded), m_altTransView, &AltTransView::fileLoaded); 0236 0237 m_syncView = new MergeView(this, m_catalog, true); 0238 addDockWidget(Qt::BottomDockWidgetArea, m_syncView); 0239 sync1->addAction(QStringLiteral("showmergeview_action"), m_syncView->toggleViewAction()); 0240 connect(this, QOverload<const DocPosition &>::of(&EditorTab::signalNewEntryDisplayed), m_syncView, QOverload<const DocPosition &>::of(&MergeView::slotNewEntryDisplayed)); 0241 connect(m_catalog, QOverload<>::of(&Catalog::signalFileLoaded), m_syncView, &MergeView::cleanup); 0242 connect(m_syncView, &MergeView::gotoEntry, this, QOverload<DocPosition, int>::of(&EditorTab::gotoEntry)); 0243 0244 m_syncViewSecondary = new MergeView(this, m_catalog, false); 0245 addDockWidget(Qt::BottomDockWidgetArea, m_syncViewSecondary); 0246 sync2->addAction(QStringLiteral("showmergeviewsecondary_action"), m_syncViewSecondary->toggleViewAction()); 0247 connect(this, QOverload<const DocPosition &>::of(&EditorTab::signalNewEntryDisplayed), m_syncViewSecondary, QOverload<const DocPosition &>::of(&MergeView::slotNewEntryDisplayed)); 0248 connect(m_catalog, QOverload<>::of(&Catalog::signalFileLoaded), m_syncViewSecondary, &MergeView::cleanup); 0249 connect(m_catalog, QOverload<const QString &>::of(&Catalog::signalFileLoaded), m_syncViewSecondary, QOverload<QString>::of(&MergeView::mergeOpen), Qt::QueuedConnection); 0250 connect(m_syncViewSecondary, &MergeView::gotoEntry, this, QOverload<DocPosition, int>::of(&EditorTab::gotoEntry)); 0251 0252 m_transUnitsView = new CatalogView(this, m_catalog); 0253 addDockWidget(Qt::LeftDockWidgetArea, m_transUnitsView); 0254 ac->addAction(QStringLiteral("showcatalogtreeview_action"), m_transUnitsView->toggleViewAction()); 0255 connect(this, QOverload<const DocPosition &>::of(&EditorTab::signalNewEntryDisplayed), m_transUnitsView, QOverload<const DocPosition &>::of(&CatalogView::slotNewEntryDisplayed)); 0256 connect(m_transUnitsView, &CatalogView::gotoEntry, this, QOverload<DocPosition, int>::of(&EditorTab::gotoEntry)); 0257 connect(m_transUnitsView, &CatalogView::escaped, this, &EditorTab::setProperFocus); 0258 connect(m_syncView, &MergeView::mergeCatalogPointerChanged, m_transUnitsView, &CatalogView::setMergeCatalogPointer); 0259 0260 m_notesView = new MsgCtxtView(this, m_catalog); 0261 addDockWidget(Qt::LeftDockWidgetArea, m_notesView); 0262 ac->addAction(QStringLiteral("showmsgctxt_action"), m_notesView->toggleViewAction()); 0263 connect(m_catalog, QOverload<>::of(&Catalog::signalFileLoaded), m_notesView, &MsgCtxtView::cleanup); 0264 connect(m_notesView, &MsgCtxtView::srcFileOpenRequested, this, &EditorTab::dispatchSrcFileOpenRequest); 0265 connect(m_view, &EditorView::signalChanged, m_notesView, &MsgCtxtView::removeErrorNotes); 0266 connect(m_notesView, &MsgCtxtView::escaped, this, &EditorTab::setProperFocus); 0267 0268 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::languageToolChanged, m_notesView, &MsgCtxtView::languageTool); 0269 0270 action = edit->addAction(QStringLiteral("edit_addnote"), m_notesView, SLOT(addNoteUI())); 0271 //action->setShortcut(Qt::CTRL+glist[i]); 0272 action->setText(i18nc("@action:inmenu", "Add a note")); 0273 0274 QVector<QAction*> tmactions_insert(TM_SHORTCUTS); 0275 QVector<QAction*> tmactions_remove(TM_SHORTCUTS); 0276 Qt::Key tmlist[TM_SHORTCUTS] = { 0277 Qt::Key_1, 0278 Qt::Key_2, 0279 Qt::Key_3, 0280 Qt::Key_4, 0281 Qt::Key_5, 0282 Qt::Key_6, 0283 Qt::Key_7, 0284 Qt::Key_8, 0285 Qt::Key_9, 0286 Qt::Key_0 0287 }; 0288 QAction* tmaction; 0289 for (i = 0; i < TM_SHORTCUTS; ++i) { 0290 // action->setVisible(false); 0291 tmaction = tm->addAction(QStringLiteral("tmquery_insert_%1").arg(i)); 0292 ac->setDefaultShortcut(tmaction, QKeySequence(Qt::CTRL + tmlist[i])); 0293 tmaction->setText(i18nc("@action:inmenu", "Insert TM suggestion #%1", i + 1)); 0294 tmactions_insert[i] = tmaction; 0295 0296 tmaction = tm->addAction(QStringLiteral("tmquery_remove_%1").arg(i)); 0297 ac->setDefaultShortcut(tmaction, QKeySequence(Qt::CTRL + Qt::ALT + tmlist[i])); 0298 tmaction->setText(i18nc("@action:inmenu", "Remove TM suggestion #%1", i + 1)); 0299 tmactions_remove[i] = tmaction; 0300 } 0301 #ifndef Q_OS_DARWIN 0302 if (systemLang == QLocale::Czech) { 0303 ac->setDefaultShortcuts(tmactions_insert[0], QList<QKeySequence>() << QKeySequence(Qt::CTRL + tmlist[0]) << QKeySequence(Qt::CTRL + Qt::Key_Plus)); 0304 ac->setDefaultShortcuts(tmactions_remove[0], QList<QKeySequence>() << QKeySequence(Qt::CTRL + Qt::ALT + tmlist[0]) << QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Plus)); 0305 } 0306 #endif 0307 TM::TMView* _tmView = new TM::TMView(this, m_catalog, tmactions_insert, tmactions_remove); 0308 addDockWidget(Qt::BottomDockWidgetArea, _tmView); 0309 tm->addAction(QStringLiteral("showtmqueryview_action"), _tmView->toggleViewAction()); 0310 connect(_tmView, &TM::TMView::refreshRequested, m_view, QOverload<>::of(&EditorView::gotoEntry), Qt::QueuedConnection); 0311 connect(_tmView, &TM::TMView::refreshRequested, this, &EditorTab::msgStrChanged, Qt::QueuedConnection); 0312 connect(_tmView, &TM::TMView::textInsertRequested, m_view, &EditorView::insertTerm); 0313 connect(_tmView, &TM::TMView::fileOpenRequested, this, &EditorTab::fileOpenRequested); 0314 connect(this, &EditorTab::fileAboutToBeClosed, m_catalog, &Catalog::flushUpdateDBBuffer); 0315 connect(this, &EditorTab::signalNewEntryDisplayed, m_catalog, &Catalog::flushUpdateDBBuffer); 0316 connect(this, &EditorTab::signalNewEntryDisplayed, _tmView, QOverload<const DocPosition &>::of(&TM::TMView::slotNewEntryDisplayed)); //do this after flushUpdateDBBuffer 0317 0318 QVector<QAction*> gactions(GLOSSARY_SHORTCUTS); 0319 Qt::Key glist[GLOSSARY_SHORTCUTS] = { 0320 Qt::Key_E, 0321 Qt::Key_H, 0322 // Qt::Key_G, 0323 // Qt::Key_I, 0324 // Qt::Key_J, 0325 // Qt::Key_K, 0326 Qt::Key_K, 0327 Qt::Key_P, 0328 Qt::Key_N, 0329 // Qt::Key_Q, 0330 // Qt::Key_R, 0331 // Qt::Key_U, 0332 // Qt::Key_V, 0333 // Qt::Key_W, 0334 // Qt::Key_X, 0335 Qt::Key_Y, 0336 // Qt::Key_Z, 0337 Qt::Key_BraceLeft, 0338 Qt::Key_BraceRight, 0339 Qt::Key_Semicolon, 0340 Qt::Key_Apostrophe 0341 }; 0342 QAction* gaction; 0343 // int i=0; 0344 for (i = 0; i < GLOSSARY_SHORTCUTS; ++i) { 0345 // action->setVisible(false); 0346 gaction = glossary->addAction(QStringLiteral("glossary_insert_%1").arg(i)); 0347 ac->setDefaultShortcut(gaction, QKeySequence(Qt::CTRL + glist[i])); 0348 gaction->setText(i18nc("@action:inmenu", "Insert term translation #%1", QString::number(i))); 0349 gactions[i] = gaction; 0350 } 0351 0352 GlossaryNS::GlossaryView* _glossaryView = new GlossaryNS::GlossaryView(this, m_catalog, gactions); 0353 addDockWidget(Qt::BottomDockWidgetArea, _glossaryView); 0354 glossary->addAction(QStringLiteral("showglossaryview_action"), _glossaryView->toggleViewAction()); 0355 connect(this, &EditorTab::signalNewEntryDisplayed, _glossaryView, QOverload<DocPosition>::of(&GlossaryNS::GlossaryView::slotNewEntryDisplayed)); 0356 connect(_glossaryView, &GlossaryNS::GlossaryView::termInsertRequested, m_view, &EditorView::insertTerm); 0357 0358 gaction = glossary->addAction(QStringLiteral("glossary_define"), this, SLOT(defineNewTerm())); 0359 gaction->setText(i18nc("@action:inmenu", "Define new term")); 0360 _glossaryView->addAction(gaction); 0361 _glossaryView->setContextMenuPolicy(Qt::ActionsContextMenu); 0362 0363 0364 BinUnitsView* binUnitsView = new BinUnitsView(m_catalog, this); 0365 addDockWidget(Qt::BottomDockWidgetArea, binUnitsView); 0366 edit->addAction(QStringLiteral("showbinunitsview_action"), binUnitsView->toggleViewAction()); 0367 connect(m_view, &EditorView::binaryUnitSelectRequested, binUnitsView, &BinUnitsView::selectUnit); 0368 0369 0370 //#ifdef WEBQUERY_ENABLE 0371 #if 0 0372 QVector<QAction*> wqactions(WEBQUERY_SHORTCUTS); 0373 Qt::Key wqlist[WEBQUERY_SHORTCUTS] = { 0374 Qt::Key_1, 0375 Qt::Key_2, 0376 Qt::Key_3, 0377 Qt::Key_4, 0378 Qt::Key_5, 0379 Qt::Key_6, 0380 Qt::Key_7, 0381 Qt::Key_8, 0382 Qt::Key_9, 0383 Qt::Key_0, 0384 }; 0385 QAction* wqaction; 0386 for (i = 0; i < WEBQUERY_SHORTCUTS; ++i) { 0387 // action->setVisible(false); 0388 wqaction = ac->addAction(QString("webquery_insert_%1").arg(i)); 0389 wqaction->setShortcut(Qt::CTRL + Qt::ALT + wqlist[i]); 0390 //wqaction->setShortcut(Qt::META+wqlist[i]); 0391 wqaction->setText(i18nc("@action:inmenu", "Insert WebQuery result #%1", i)); 0392 wqactions[i] = wqaction; 0393 } 0394 WebQueryView* _webQueryView = new WebQueryView(this, m_catalog, wqactions); 0395 addDockWidget(Qt::BottomDockWidgetArea, _webQueryView); 0396 ac->addAction(QStringLiteral("showwebqueryview_action"), _webQueryView->toggleViewAction()); 0397 connect(this, &EditorTab::signalNewEntryDisplayed, _webQueryView, SLOT(slotNewEntryDisplayed(DocPosition))); 0398 connect(_webQueryView, SIGNAL(textInsertRequested(QString)), m_view, SLOT(insertTerm(QString))); 0399 #endif 0400 0401 0402 //END dockwidgets 0403 0404 0405 0406 0407 actionCategory = file; 0408 0409 // File 0410 action = file->addAction(KStandardAction::Save, this, SLOT(saveFile())); 0411 // action->setEnabled(false); 0412 // connect (m_catalog,SIGNAL(cleanChanged(bool)),action,SLOT(setDisabled(bool))); 0413 connect(m_catalog, &Catalog::cleanChanged, this, &EditorTab::setModificationSign); 0414 file->addAction(KStandardAction::SaveAs, this, SLOT(saveFileAs())); 0415 //action = KStandardAction::quit(qApp, SLOT(quit()), ac); 0416 //action->setText(i18nc("@action:inmenu","Close all Lokalize windows")); 0417 0418 //KStandardAction::quit(kapp, SLOT(quit()), ac); 0419 //KStandardAction::quit(this, SLOT(deleteLater()), ac); 0420 #define ADD_ACTION_SHORTCUT_ICON(_name,_text,_shortcut,_icon)\ 0421 action = actionCategory->addAction(QStringLiteral(_name));\ 0422 action->setText(_text);\ 0423 action->setIcon(QIcon::fromTheme(QStringLiteral(_icon)));\ 0424 ac->setDefaultShortcut(action, QKeySequence( _shortcut )); 0425 0426 #define ADD_ACTION_SHORTCUT(_name,_text,_shortcut)\ 0427 action = actionCategory->addAction(QStringLiteral(_name));\ 0428 action->setText(_text);\ 0429 ac->setDefaultShortcut(action, QKeySequence( _shortcut )); 0430 0431 action = actionCategory->addAction(QStringLiteral("file_phases")); 0432 action->setText(i18nc("@action:inmenu", "Phases...")); 0433 connect(action, &QAction::triggered, this, &EditorTab::openPhasesWindow); 0434 0435 ADD_ACTION_SHORTCUT("file_wordcount", i18nc("@action:inmenu", "Word count"), Qt::CTRL + Qt::ALT + Qt::Key_C) 0436 connect(action, &QAction::triggered, this, &EditorTab::displayWordCount); 0437 0438 ADD_ACTION_SHORTCUT("file_cleartarget", i18nc("@action:inmenu", "Clear all translated entries"), Qt::CTRL + Qt::ALT + Qt::Key_D) 0439 connect(action, &QAction::triggered, this, &EditorTab::clearTranslatedEntries); 0440 0441 ADD_ACTION_SHORTCUT("file_pology", i18nc("@action:inmenu", "Launch the Pology command on this file"), Qt::CTRL + Qt::ALT + Qt::Key_P) 0442 action->setEnabled(Settings::self()->pologyEnabled()); 0443 connect(action, &QAction::triggered, this, &EditorTab::launchPology); 0444 0445 ADD_ACTION_SHORTCUT("file_xliff2odf", i18nc("@action:inmenu", "Merge translation into OpenDocument"), Qt::CTRL + Qt::Key_Backslash) 0446 connect(action, &QAction::triggered, this, &EditorTab::mergeIntoOpenDocument); 0447 connect(this, &EditorTab::xliffFileOpened, action, &QAction::setVisible); 0448 action->setVisible(false); 0449 0450 0451 //Edit 0452 actionCategory = edit; 0453 action = edit->addAction(KStandardAction::Undo, this, SLOT(undo())); 0454 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::undoRequested, this, &EditorTab::undo); 0455 connect(m_catalog, &Catalog::canUndoChanged, action, &QAction::setEnabled); 0456 action->setEnabled(false); 0457 0458 action = edit->addAction(KStandardAction::Redo, this, SLOT(redo())); 0459 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::redoRequested, this, &EditorTab::redo); 0460 connect(m_catalog, &Catalog::canRedoChanged, action, &QAction::setEnabled); 0461 action->setEnabled(false); 0462 0463 action = nav->addAction(KStandardAction::Find, this, SLOT(find())); 0464 action = nav->addAction(KStandardAction::FindNext, this, SLOT(findNext())); 0465 action = nav->addAction(KStandardAction::FindPrev, this, SLOT(findPrev())); 0466 action->setText(i18nc("@action:inmenu", "Change searching direction")); 0467 action = edit->addAction(KStandardAction::Replace, this, SLOT(replace())); 0468 0469 connect(m_view, &EditorView::findRequested, this, &EditorTab::find); 0470 connect(m_view, &EditorView::findNextRequested, this, QOverload<>::of(&EditorTab::findNext)); 0471 connect(m_view, &EditorView::replaceRequested, this, &EditorTab::replace); 0472 0473 0474 0475 action = actionCategory->addAction(QStringLiteral("edit_approve"), 0476 new KToolBarPopupAction(QIcon::fromTheme(QStringLiteral("approved")), 0477 i18nc("@option:check whether message is marked as translated/reviewed/approved (depending on your role)", "Approved"), this)); 0478 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_U)); 0479 0480 action->setCheckable(true); 0481 connect(action, &QAction::triggered, m_view, &EditorView::toggleApprovement); 0482 connect(m_view, &EditorView::signalApprovedEntryDisplayed, this, &EditorTab::signalApprovedEntryDisplayed); 0483 connect(this, &EditorTab::signalApprovedEntryDisplayed, action, &QAction::setChecked); 0484 connect(this, &EditorTab::signalApprovedEntryDisplayed, this, &EditorTab::msgStrChanged, Qt::QueuedConnection); 0485 0486 m_approveAction = action; 0487 m_stateAction = action; 0488 connect(Project::local(), &ProjectLocal::configChanged, this, &EditorTab::setApproveActionTitle); 0489 connect(m_catalog, &Catalog::activePhaseChanged, this, &EditorTab::setApproveActionTitle); 0490 connect(m_stateAction->menu(), &QMenu::aboutToShow, this, &EditorTab::showStatesMenu); 0491 connect(m_stateAction->menu(), &QMenu::triggered, this, &EditorTab::setState); 0492 0493 0494 action = actionCategory->addAction(QStringLiteral("edit_approve_go_fuzzyUntr")); 0495 action->setText(i18nc("@action:inmenu", "Approve and go to next")); 0496 connect(action, &QAction::triggered, this, &EditorTab::toggleApprovementGotoNextFuzzyUntr); 0497 m_approveAndGoAction = action; 0498 0499 setApproveActionTitle(); 0500 0501 0502 0503 action = actionCategory->addAction(QStringLiteral("edit_nonequiv"), m_view, SLOT(setEquivTrans(bool))); 0504 action->setText(i18nc("@action:inmenu", "Equivalent translation")); 0505 action->setCheckable(true); 0506 connect(this, &EditorTab::signalEquivTranslatedEntryDisplayed, action, &QAction::setChecked); 0507 0508 0509 #ifndef Q_OS_DARWIN 0510 int copyShortcut = Qt::CTRL + Qt::Key_Space; 0511 if (Q_UNLIKELY(systemLang == QLocale::Korean 0512 || systemLang == QLocale::Japanese 0513 || systemLang == QLocale::Chinese 0514 )) 0515 copyShortcut = Qt::ALT + Qt::Key_Space; 0516 #else 0517 int copyShortcut = Qt::META + Qt::Key_Space; 0518 #endif 0519 ADD_ACTION_SHORTCUT_ICON("edit_msgid2msgstr", i18nc("@action:inmenu", "Copy source to target"), copyShortcut, "msgid2msgstr") 0520 connect(action, &QAction::triggered, (const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::source2target); 0521 0522 ADD_ACTION_SHORTCUT("edit_unwrap-target", i18nc("@action:inmenu", "Unwrap target"), Qt::CTRL + Qt::Key_I) 0523 connect(action, &QAction::triggered, m_view, QOverload<>::of(&EditorView::unwrap)); 0524 0525 action = edit->addAction(QStringLiteral("edit_clear-target"), m_view->viewPort(), SLOT(removeTargetSubstring())); 0526 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_D)); 0527 action->setText(i18nc("@action:inmenu", "Clear")); 0528 0529 action = edit->addAction(QStringLiteral("edit_tagmenu"), m_view->viewPort(), SLOT(tagMenu())); 0530 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_T)); 0531 action->setText(i18nc("@action:inmenu", "Insert Tag")); 0532 0533 action = edit->addAction(QStringLiteral("edit_languagetool"), m_view->viewPort(), SLOT(launchLanguageTool())); 0534 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_J)); 0535 action->setText(i18nc("@action:inmenu", "Check this unit using LanguageTool")); 0536 0537 action = edit->addAction(QStringLiteral("edit_tagimmediate"), m_view->viewPort(), SLOT(tagImmediate())); 0538 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_M)); 0539 action->setText(i18nc("@action:inmenu", "Insert Next Tag")); 0540 0541 action = edit->addAction(QStringLiteral("edit_completion"), m_view, SIGNAL(doExplicitCompletion())); 0542 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Space)); 0543 action->setText(i18nc("@action:inmenu", "Completion")); 0544 0545 action = edit->addAction(QStringLiteral("edit_spellreplace"), m_view->viewPort(), SLOT(spellReplace())); 0546 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Equal)); 0547 action->setText(i18nc("@action:inmenu", "Replace with best spellcheck suggestion")); 0548 // action = ac->addAction("glossary_define",m_view,SLOT(defineNewTerm())); 0549 // action->setText(i18nc("@action:inmenu","Define new term")); 0550 0551 // Go 0552 actionCategory = nav; 0553 action = nav->addAction(KStandardAction::Next, this, SLOT(gotoNext())); 0554 action->setText(i18nc("@action:inmenu entry", "&Next")); 0555 connect(this, &EditorTab::signalLastDisplayed, action, &QAction::setDisabled); 0556 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoNextRequested, this, &EditorTab::gotoNext); 0557 0558 action = nav->addAction(KStandardAction::Prior, this, SLOT(gotoPrev())); 0559 action->setText(i18nc("@action:inmenu entry", "&Previous")); 0560 connect(this, &EditorTab::signalFirstDisplayed, action, &QAction::setDisabled); 0561 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoPrevRequested, this, &EditorTab::gotoPrev); 0562 0563 action = nav->addAction(KStandardAction::FirstPage, this, SLOT(gotoFirst())); 0564 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoFirstRequested, this, &EditorTab::gotoFirst); 0565 action->setText(i18nc("@action:inmenu", "&First Entry")); 0566 action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Home)); 0567 connect(this, &EditorTab::signalFirstDisplayed, action, &QAction::setDisabled); 0568 0569 action = nav->addAction(KStandardAction::LastPage, this, SLOT(gotoLast())); 0570 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoLastRequested, this, &EditorTab::gotoLast); 0571 action->setText(i18nc("@action:inmenu", "&Last Entry")); 0572 action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_End)); 0573 connect(this, &EditorTab::signalLastDisplayed, action, &QAction::setDisabled); 0574 0575 action = nav->addAction(KStandardAction::GotoPage, this, SLOT(gotoEntry())); 0576 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_G)); 0577 action->setText(i18nc("@action:inmenu", "Entry by number")); 0578 0579 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") 0580 connect(action, &QAction::triggered, this, &EditorTab::gotoPrevFuzzy); 0581 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoPrevFuzzyRequested, this, &EditorTab::gotoPrevFuzzy); 0582 connect(this, &EditorTab::signalPriorFuzzyAvailable, action, &QAction::setEnabled); 0583 0584 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") 0585 connect(action, &QAction::triggered, this, &EditorTab::gotoNextFuzzy); 0586 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoNextFuzzyRequested, this, &EditorTab::gotoNextFuzzy); 0587 connect(this, &EditorTab::signalNextFuzzyAvailable, action, &QAction::setEnabled); 0588 0589 ADD_ACTION_SHORTCUT_ICON("go_prev_untrans", i18nc("@action:inmenu", "Previous untranslated"), Qt::ALT + Qt::Key_PageUp, "prevuntranslated") 0590 connect(action, &QAction::triggered, this, &EditorTab::gotoPrevUntranslated); 0591 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoPrevUntranslatedRequested, this, &EditorTab::gotoPrevUntranslated); 0592 connect(this, &EditorTab::signalPriorUntranslatedAvailable, action, &QAction::setEnabled); 0593 0594 ADD_ACTION_SHORTCUT_ICON("go_next_untrans", i18nc("@action:inmenu", "Next untranslated"), Qt::ALT + Qt::Key_PageDown, "nextuntranslated") 0595 connect(action, &QAction::triggered, this, &EditorTab::gotoNextUntranslated); 0596 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoNextUntranslatedRequested, this, &EditorTab::gotoNextUntranslated); 0597 connect(this, &EditorTab::signalNextUntranslatedAvailable, action, &QAction::setEnabled); 0598 0599 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") 0600 connect(action, &QAction::triggered, this, &EditorTab::gotoPrevFuzzyUntr); 0601 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoPrevFuzzyUntrRequested, this, &EditorTab::gotoPrevFuzzyUntr); 0602 connect(this, &EditorTab::signalPriorFuzzyOrUntrAvailable, action, &QAction::setEnabled); 0603 0604 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") 0605 connect(action, &QAction::triggered, this, QOverload<>::of(&EditorTab::gotoNextFuzzyUntr)); 0606 connect((const TranslationUnitTextEdit*)m_view->viewPort(), &TranslationUnitTextEdit::gotoNextFuzzyUntrRequested, this, QOverload<>::of(&EditorTab::gotoNextFuzzyUntr)); 0607 connect(this, &EditorTab::signalNextFuzzyOrUntrAvailable, action, &QAction::setEnabled); 0608 0609 action = nav->addAction(QStringLiteral("go_focus_earch_line"), m_transUnitsView, SLOT(setFocus())); 0610 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_L)); 0611 action->setText(i18nc("@action:inmenu", "Focus the search line of Translation Units view")); 0612 0613 0614 //Bookmarks 0615 action = nav->addAction(KStandardAction::AddBookmark, m_view, SLOT(toggleBookmark(bool))); 0616 //action = ac->addAction("bookmark_do"); 0617 action->setText(i18nc("@option:check", "Bookmark message")); 0618 action->setCheckable(true); 0619 //connect(action, SIGNAL(triggered(bool)),m_view,SLOT(toggleBookmark(bool))); 0620 connect(this, &EditorTab::signalBookmarkDisplayed, action, &QAction::setChecked); 0621 0622 action = nav->addAction(QStringLiteral("bookmark_prior"), this, SLOT(gotoPrevBookmark())); 0623 action->setText(i18nc("@action:inmenu", "Previous bookmark")); 0624 connect(this, &EditorTab::signalPriorBookmarkAvailable, action, &QAction::setEnabled); 0625 0626 action = nav->addAction(QStringLiteral("bookmark_next"), this, SLOT(gotoNextBookmark())); 0627 action->setText(i18nc("@action:inmenu", "Next bookmark")); 0628 connect(this, &EditorTab::signalNextBookmarkAvailable, action, &QAction::setEnabled); 0629 0630 //Tools 0631 edit->addAction(KStandardAction::Spelling, this, SLOT(spellcheck())); 0632 0633 actionCategory = tm; 0634 // xgettext: no-c-format 0635 ADD_ACTION_SHORTCUT("tools_tm_batch", i18nc("@action:inmenu", "Fill in all exact suggestions"), Qt::CTRL + Qt::ALT + Qt::Key_B) 0636 connect(action, &QAction::triggered, _tmView, &TM::TMView::slotBatchTranslate); 0637 0638 // xgettext: no-c-format 0639 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) 0640 connect(action, &QAction::triggered, _tmView, &TM::TMView::slotBatchTranslateFuzzy); 0641 0642 //MergeMode 0643 action = sync1->addAction(QStringLiteral("merge_open"), m_syncView, SLOT(mergeOpen())); 0644 action->setText(i18nc("@action:inmenu", "Open file for sync/merge")); 0645 action->setStatusTip(i18nc("@info:status", "Open catalog to be merged into the current one / replicate base file changes to")); 0646 action->setToolTip(action->statusTip()); 0647 action->setWhatsThis(action->statusTip()); 0648 m_syncView->addAction(action); 0649 0650 action = sync1->addAction(QStringLiteral("merge_prev"), m_syncView, SLOT(gotoPrevChanged())); 0651 action->setText(i18nc("@action:inmenu", "Previous different")); 0652 action->setStatusTip(i18nc("@info:status", "Previous entry which is translated differently in the file being merged, including empty translations in merge source")); 0653 action->setToolTip(action->statusTip()); 0654 action->setWhatsThis(action->statusTip()); 0655 ac->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Up)); 0656 0657 connect(m_syncView, &MergeView::signalPriorChangedAvailable, action, &QAction::setEnabled); 0658 m_syncView->addAction(action); 0659 0660 action = sync1->addAction(QStringLiteral("merge_next"), m_syncView, SLOT(gotoNextChanged())); 0661 action->setText(i18nc("@action:inmenu", "Next different")); 0662 action->setStatusTip(i18nc("@info:status", "Next entry which is translated differently in the file being merged, including empty translations in merge source")); 0663 action->setToolTip(action->statusTip()); 0664 action->setWhatsThis(action->statusTip()); 0665 ac->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Down)); 0666 connect(m_syncView, &MergeView::signalNextChangedAvailable, action, &QAction::setEnabled); 0667 m_syncView->addAction(action); 0668 0669 action = sync1->addAction(QStringLiteral("merge_nextapproved"), m_syncView, SLOT(gotoNextChangedApproved())); 0670 action->setText(i18nc("@action:inmenu", "Next different approved")); 0671 ac->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::META + Qt::Key_Down)); 0672 connect(m_syncView, &MergeView::signalNextChangedAvailable, action, &QAction::setEnabled); 0673 m_syncView->addAction(action); 0674 0675 action = sync1->addAction(QStringLiteral("merge_accept"), m_syncView, SLOT(mergeAccept())); 0676 action->setText(i18nc("@action:inmenu", "Copy from merging source")); 0677 action->setEnabled(false); 0678 ac->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Return)); 0679 connect(m_syncView, &MergeView::signalEntryWithMergeDisplayed, action, &QAction::setEnabled); 0680 m_syncView->addAction(action); 0681 0682 action = sync1->addAction(QStringLiteral("merge_acceptnew"), m_syncView, SLOT(mergeAcceptAllForEmpty())); 0683 action->setText(i18nc("@action:inmenu", "Copy all new translations")); 0684 action->setStatusTip(i18nc("@info:status", "This changes only empty and non-ready entries in base file")); 0685 action->setToolTip(action->statusTip()); 0686 action->setWhatsThis(action->statusTip()); 0687 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_A)); 0688 connect(m_syncView, &MergeView::mergeCatalogAvailable, action, &QAction::setEnabled); 0689 m_syncView->addAction(action); 0690 //action->setShortcut(Qt::ALT+Qt::Key_E); 0691 0692 action = sync1->addAction(QStringLiteral("merge_back"), m_syncView, SLOT(mergeBack())); 0693 action->setText(i18nc("@action:inmenu", "Copy to merging source")); 0694 connect(m_syncView, &MergeView::mergeCatalogAvailable, action, &QAction::setEnabled); 0695 ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Return)); 0696 m_syncView->addAction(action); 0697 0698 0699 //Secondary merge 0700 action = sync2->addAction(QStringLiteral("mergesecondary_open"), m_syncViewSecondary, SLOT(mergeOpen())); 0701 action->setText(i18nc("@action:inmenu", "Open file for secondary sync")); 0702 action->setStatusTip(i18nc("@info:status", "Open catalog to be merged into the current one / replicate base file changes to")); 0703 action->setToolTip(action->statusTip()); 0704 action->setWhatsThis(action->statusTip()); 0705 m_syncViewSecondary->addAction(action); 0706 0707 action = sync2->addAction(QStringLiteral("mergesecondary_prev"), m_syncViewSecondary, SLOT(gotoPrevChanged())); 0708 action->setText(i18nc("@action:inmenu", "Previous different")); 0709 action->setStatusTip(i18nc("@info:status", "Previous entry which is translated differently in the file being merged, including empty translations in merge source")); 0710 action->setToolTip(action->statusTip()); 0711 action->setWhatsThis(action->statusTip()); 0712 connect(m_syncView, &MergeView::signalPriorChangedAvailable, action, &QAction::setEnabled); 0713 m_syncViewSecondary->addAction(action); 0714 0715 action = sync2->addAction(QStringLiteral("mergesecondary_next"), m_syncViewSecondary, SLOT(gotoNextChanged())); 0716 action->setText(i18nc("@action:inmenu", "Next different")); 0717 action->setStatusTip(i18nc("@info:status", "Next entry which is translated differently in the file being merged, including empty translations in merge source")); 0718 action->setToolTip(action->statusTip()); 0719 action->setWhatsThis(action->statusTip()); 0720 connect(m_syncView, &MergeView::signalNextChangedAvailable, action, &QAction::setEnabled); 0721 m_syncViewSecondary->addAction(action); 0722 0723 action = sync2->addAction(QStringLiteral("mergesecondary_accept"), m_syncViewSecondary, SLOT(mergeAccept())); 0724 action->setText(i18nc("@action:inmenu", "Copy from merging source")); 0725 connect(m_syncView, &MergeView::signalEntryWithMergeDisplayed, action, &QAction::setEnabled); 0726 m_syncViewSecondary->addAction(action); 0727 0728 action = sync2->addAction(QStringLiteral("mergesecondary_acceptnew"), m_syncViewSecondary, SLOT(mergeAcceptAllForEmpty())); 0729 action->setText(i18nc("@action:inmenu", "Copy all new translations")); 0730 action->setStatusTip(i18nc("@info:status", "This changes only empty entries")); 0731 action->setToolTip(action->statusTip()); 0732 action->setWhatsThis(action->statusTip()); 0733 m_syncViewSecondary->addAction(action); 0734 0735 action = sync2->addAction(QStringLiteral("mergesecondary_back"), m_syncViewSecondary, SLOT(mergeBack())); 0736 action->setText(i18nc("@action:inmenu", "Copy to merging source")); 0737 m_syncViewSecondary->addAction(action); 0738 } 0739 0740 void EditorTab::setProperFocus() 0741 { 0742 m_view->setProperFocus(); 0743 } 0744 0745 void EditorTab::hideDocks() 0746 { 0747 if (m_transUnitsView->isFloating()) 0748 m_transUnitsView->hide(); 0749 } 0750 0751 void EditorTab::showDocks() 0752 { 0753 return; 0754 if (m_transUnitsView->isFloating()) 0755 m_transUnitsView->show(); 0756 } 0757 0758 void EditorTab::setProperCaption(QString title, bool modified) 0759 { 0760 if (m_catalog->autoSaveRecovered()) title += ' ' + i18nc("editor tab name", "(recovered)"); 0761 setWindowTitle(title + QStringLiteral(" [*]")); 0762 setWindowModified(modified); 0763 } 0764 0765 void EditorTab::setFullPathShown(bool fullPathShown) 0766 { 0767 m_fullPathShown = fullPathShown; 0768 0769 updateCaptionPath(); 0770 setModificationSign(); 0771 } 0772 0773 void EditorTab::setModificationSign() 0774 { 0775 bool clean = m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified(); 0776 setProperCaption(_captionPath, !clean); 0777 } 0778 0779 0780 void EditorTab::updateCaptionPath() 0781 { 0782 QString url = m_catalog->url(); 0783 if (!m_project->isLoaded()) { 0784 _captionPath = url; 0785 return; 0786 } 0787 if (!m_fullPathShown) { 0788 _captionPath = QFileInfo(url).fileName(); 0789 return; 0790 } 0791 _captionPath = QDir(QFileInfo(m_project->path()).absolutePath()).relativeFilePath(url); 0792 if (_captionPath.contains(QLatin1String("../.."))) 0793 _captionPath = url; 0794 else if (_captionPath.startsWith(QLatin1String("./"))) 0795 _captionPath = _captionPath.mid(2); 0796 } 0797 0798 bool EditorTab::fileOpen(QString filePath, QString suggestedDirPath, QMap<QString, QMdiSubWindow*> openedFiles, bool silent) 0799 { 0800 if (!m_catalog->isClean()) { 0801 switch (KMessageBox::warningYesNoCancel(SettingsController::instance()->mainWindowPtr(), 0802 i18nc("@info", "The document contains unsaved changes.\n" 0803 "Do you want to save your changes or discard them?"), i18nc("@title:window", "Warning"), 0804 KStandardGuiItem::save(), KStandardGuiItem::discard()) 0805 ) { 0806 case KMessageBox::Yes: if (!saveFile()) return false; break; 0807 case KMessageBox::Cancel: return false; 0808 default:; 0809 } 0810 } 0811 if (suggestedDirPath.isEmpty()) 0812 suggestedDirPath = m_catalog->url(); 0813 0814 QString saidPath; 0815 if (filePath.isEmpty()) { 0816 //Prevent crashes 0817 //Project::instance()->model()->weaver()->suspend(); 0818 //KDE5PORT use mutex if the crash is still there with kfilemetadata library 0819 filePath = QFileDialog::getOpenFileName(SettingsController::instance()->mainWindowPtr(), i18nc("@title:window", "Select translation file"), 0820 suggestedDirPath, Catalog::supportedFileTypes(true));//" text/x-gettext-translation-template"); 0821 //Project::instance()->model()->weaver()->resume(); 0822 //TODO application/x-xliff, windows: just extensions 0823 //originalPath=url.path(); never used 0824 } else if (!QFile::exists(filePath) && Project::instance()->isLoaded()) { 0825 //check if we are opening template 0826 QString newPath = filePath; 0827 newPath.replace(Project::instance()->poDir(), Project::instance()->potDir()); 0828 if (QFile::exists(newPath) || QFile::exists(newPath += 't')) { 0829 saidPath = filePath; 0830 filePath = newPath; 0831 } 0832 } 0833 if (filePath.isEmpty()) 0834 return false; 0835 QMap<QString, QMdiSubWindow*>::const_iterator it = openedFiles.constFind(filePath); 0836 if (it != openedFiles.constEnd()) { 0837 qCWarning(LOKALIZE_LOG) << "already opened:" << filePath; 0838 return false; 0839 } 0840 0841 QApplication::setOverrideCursor(Qt::WaitCursor); 0842 0843 QString prevFilePath = currentFilePath(); 0844 bool wasOpen = !m_catalog->isEmpty(); 0845 if (wasOpen) Q_EMIT fileAboutToBeClosed(); 0846 int errorLine = m_catalog->loadFromUrl(filePath, saidPath); 0847 if (wasOpen && errorLine == 0) { 0848 Q_EMIT fileClosed(); 0849 Q_EMIT fileClosed(prevFilePath); 0850 } 0851 0852 QApplication::restoreOverrideCursor(); 0853 0854 if (errorLine == 0) { 0855 statusBarItems.insert(ID_STATUS_TOTAL, i18nc("@info:status message entries", "Total: %1", m_catalog->numberOfEntries())); 0856 numberOfUntranslatedChanged(); 0857 numberOfFuzziesChanged(); 0858 0859 m_currentPos.entry = -1; //so the signals are emitted 0860 DocPosition pos(0); 0861 //we delay gotoEntry(pos) until project is loaded; 0862 0863 //Project 0864 if (!m_project->isLoaded()) { 0865 QFileInfo fileInfo(filePath); 0866 //search for it 0867 int i = 4; 0868 QDir dir = fileInfo.dir(); 0869 QStringList proj(QStringLiteral("*.lokalize")); 0870 dir.setNameFilters(proj); 0871 while (--i && !dir.isRoot() && !m_project->isLoaded()) { 0872 if (dir.entryList().isEmpty()) { 0873 if (!dir.cdUp()) break; 0874 } else m_project->load(dir.absoluteFilePath(dir.entryList().first())); 0875 } 0876 //enforce autosync 0877 m_syncViewSecondary->mergeOpen(filePath); 0878 0879 if (!m_project->isLoaded()) { 0880 if (m_project->desirablePath().isEmpty()) 0881 m_project->setDesirablePath(fileInfo.absolutePath() + QStringLiteral("/index.lokalize")); 0882 0883 if (m_catalog->targetLangCode().isEmpty() /*&& m_project->targetLangCode().length()*/) 0884 m_catalog->setTargetLangCode(getTargetLangCode(fileInfo.fileName())); 0885 0886 //_project->setLangCode(m_catalog->targetLangCode()); 0887 } 0888 } 0889 if (m_catalog->targetLangCode().isEmpty() /*&& m_project->targetLangCode().length()*/) 0890 m_catalog->setTargetLangCode(Project::instance()->targetLangCode()); 0891 0892 gotoEntry(pos); 0893 0894 updateCaptionPath(); 0895 setModificationSign(); 0896 0897 //OK!!! 0898 Q_EMIT xliffFileOpened(m_catalog->type() == Xliff); 0899 Q_EMIT fileOpened(); 0900 return true; 0901 } 0902 0903 if (!silent) { 0904 if (errorLine > 0) KMessageBox::error(this, i18nc("@info", "Error opening the file %1, line: %2", filePath, errorLine)); 0905 else KMessageBox::error(this, i18nc("@info", "Error opening the file %1", filePath)); 0906 } 0907 return false; 0908 } 0909 0910 bool EditorTab::saveFileAs(const QString& defaultPath) 0911 { 0912 QString filePath = QFileDialog::getSaveFileName(this, i18nc("@title:window", "Save File As"), 0913 QFileInfo(defaultPath.isEmpty() ? m_catalog->url() : defaultPath).absoluteFilePath(), m_catalog->fileType()); 0914 if (filePath.isEmpty()) return false; 0915 if (!Catalog::extIsSupported(filePath) && m_catalog->url().contains('.')) 0916 filePath += m_catalog->url().midRef(m_catalog->url().lastIndexOf('.')); 0917 0918 return saveFile(filePath); 0919 } 0920 0921 bool EditorTab::saveFile(const QString& filePath) 0922 { 0923 bool clean = m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified() && filePath == m_catalog->url(); 0924 if (clean) return true; 0925 if (m_catalog->isClean() && filePath.isEmpty()) { 0926 Q_EMIT m_catalog->signalFileSaved(); 0927 return true; 0928 } 0929 if (m_catalog->saveToUrl(filePath)) { 0930 updateCaptionPath(); 0931 setModificationSign(); 0932 Q_EMIT fileSaved(filePath); 0933 return true; 0934 } 0935 const QString errorFilePath = filePath.isEmpty() ? m_catalog->url() : filePath; 0936 if (KMessageBox::Continue == KMessageBox::warningContinueCancel(this, 0937 i18nc("@info", "Error saving the file %1\n" 0938 "Do you want to save to another file or cancel?", errorFilePath), 0939 i18nc("@title", "Error"), KStandardGuiItem::save()) 0940 ) 0941 return saveFileAs(errorFilePath); 0942 return false; 0943 } 0944 0945 void EditorTab::fileAutoSaveFailedWarning(const QString& fileName) 0946 { 0947 KMessageBox::information(this, i18nc("@info", "Could not perform file autosaving.\n" 0948 "The target file was %1.", fileName)); 0949 } 0950 0951 EditorState EditorTab::state() 0952 { 0953 EditorState state; 0954 state.dockWidgets = saveState(); 0955 state.filePath = m_catalog->url(); 0956 state.mergeFilePath = m_syncView->filePath(); 0957 state.entry = m_currentPos.entry; 0958 //state.offset=_currentPos.offset; 0959 return state; 0960 } 0961 0962 0963 bool EditorTab::queryClose() 0964 { 0965 bool clean = m_catalog->isClean() && !m_syncView->isModified() && !m_syncViewSecondary->isModified(); 0966 if (clean) return true; 0967 0968 //TODO caption 0969 switch (KMessageBox::warningYesNoCancel(this, 0970 i18nc("@info", "The document contains unsaved changes.\n" 0971 "Do you want to save your changes or discard them?"), i18nc("@title:window", "Warning"), 0972 KStandardGuiItem::save(), KStandardGuiItem::discard())) { 0973 case KMessageBox::Yes: return saveFile(); 0974 case KMessageBox::No: return true; 0975 default: return false; 0976 } 0977 } 0978 0979 0980 void EditorTab::undo() 0981 { 0982 gotoEntry(m_catalog->undo(), 0); 0983 msgStrChanged(); 0984 } 0985 0986 void EditorTab::redo() 0987 { 0988 gotoEntry(m_catalog->redo(), 0); 0989 msgStrChanged(); 0990 } 0991 0992 void EditorTab::gotoEntry() 0993 { 0994 bool ok = false; 0995 DocPosition pos = m_currentPos; 0996 pos.entry = QInputDialog::getInt(this, i18nc("@title", "Jump to Entry"), 0997 i18nc("@label:spinbox", "Enter entry number:"), 0998 pos.entry, 1, m_catalog->numberOfEntries(), 1, &ok); 0999 if (ok) { 1000 --(pos.entry); 1001 gotoEntry(pos); 1002 } 1003 } 1004 void EditorTab::gotoEntry(DocPosition pos) 1005 { 1006 return gotoEntry(pos, 0); 1007 } 1008 void EditorTab::gotoEntry(DocPosition pos, int selection) 1009 { 1010 //specially for dbus users 1011 if (pos.entry >= m_catalog->numberOfEntries() || pos.entry < 0) 1012 return; 1013 if (!m_catalog->isPlural(pos)) 1014 pos.form = 0; 1015 1016 m_currentPos.part = pos.part; //for searching; 1017 //UndefPart => called on fuzzy toggle 1018 1019 1020 bool newEntry = m_currentPos.entry != pos.entry || m_currentPos.form != pos.form; 1021 if (newEntry || pos.part == DocPosition::Comment) { 1022 m_notesView->gotoEntry(pos, pos.part == DocPosition::Comment ? selection : 0); 1023 if (pos.part == DocPosition::Comment) { 1024 pos.form = 0; 1025 pos.offset = 0; 1026 selection = 0; 1027 } 1028 } 1029 1030 1031 m_view->gotoEntry(pos, selection); 1032 if (pos.part == DocPosition::UndefPart) 1033 m_currentPos.part = DocPosition::Target; 1034 1035 //QTime time; time.start(); 1036 1037 if (newEntry) { 1038 m_currentPos = pos; 1039 if (true) { 1040 Q_EMIT signalNewEntryDisplayed(pos); 1041 Q_EMIT entryDisplayed(); 1042 1043 Q_EMIT signalFirstDisplayed(pos.entry == m_transUnitsView->firstEntryNumber()); 1044 Q_EMIT signalLastDisplayed(pos.entry == m_transUnitsView->lastEntryNumber()); 1045 1046 Q_EMIT signalPriorFuzzyAvailable(pos.entry > m_catalog->firstFuzzyIndex()); 1047 Q_EMIT signalNextFuzzyAvailable(pos.entry < m_catalog->lastFuzzyIndex()); 1048 1049 Q_EMIT signalPriorUntranslatedAvailable(pos.entry > m_catalog->firstUntranslatedIndex()); 1050 Q_EMIT signalNextUntranslatedAvailable(pos.entry < m_catalog->lastUntranslatedIndex()); 1051 1052 Q_EMIT signalPriorFuzzyOrUntrAvailable(pos.entry > m_catalog->firstFuzzyIndex() 1053 || pos.entry > m_catalog->firstUntranslatedIndex() 1054 ); 1055 Q_EMIT signalNextFuzzyOrUntrAvailable(pos.entry < m_catalog->lastFuzzyIndex() 1056 || pos.entry < m_catalog->lastUntranslatedIndex()); 1057 1058 Q_EMIT signalPriorBookmarkAvailable(pos.entry > m_catalog->firstBookmarkIndex()); 1059 Q_EMIT signalNextBookmarkAvailable(pos.entry < m_catalog->lastBookmarkIndex()); 1060 Q_EMIT signalBookmarkDisplayed(m_catalog->isBookmarked(pos.entry)); 1061 1062 Q_EMIT signalEquivTranslatedEntryDisplayed(m_catalog->isEquivTrans(pos)); 1063 Q_EMIT signalApprovedEntryDisplayed(m_catalog->isApproved(pos)); 1064 } 1065 1066 } 1067 1068 statusBarItems.insert(ID_STATUS_CURRENT, i18nc("@info:status", "Current: %1", m_currentPos.entry + 1)); 1069 //qCDebug(LOKALIZE_LOG)<<"ELA "<<time.elapsed(); 1070 } 1071 1072 void EditorTab::msgStrChanged() 1073 { 1074 bool isUntr = m_catalog->msgstr(m_currentPos).isEmpty(); 1075 bool isApproved = m_catalog->isApproved(m_currentPos); 1076 if (isUntr == m_currentIsUntr && isApproved == m_currentIsApproved) 1077 return; 1078 1079 QString msg; 1080 if (isUntr) msg = i18nc("@info:status", "Untranslated"); 1081 else if (isApproved)msg = i18nc("@info:status 'non-fuzzy' in gettext terminology", "Ready"); 1082 else msg = i18nc("@info:status 'fuzzy' in gettext terminology", "Needs review"); 1083 1084 /* else 1085 statusBar()->changeItem("",ID_STATUS_ISFUZZY);*/ 1086 1087 statusBarItems.insert(ID_STATUS_ISFUZZY, msg); 1088 1089 m_currentIsUntr = isUntr; 1090 m_currentIsApproved = isApproved; 1091 } 1092 1093 void EditorTab::switchForm(int newForm) 1094 { 1095 if (m_currentPos.form == newForm) return; 1096 1097 DocPosition pos = m_currentPos; 1098 pos.form = newForm; 1099 gotoEntry(pos); 1100 } 1101 1102 void EditorTab::gotoNextUnfiltered() 1103 { 1104 DocPosition pos = m_currentPos; 1105 1106 if (switchNext(m_catalog, pos)) 1107 gotoEntry(pos); 1108 } 1109 1110 1111 void EditorTab::gotoPrevUnfiltered() 1112 { 1113 DocPosition pos = m_currentPos; 1114 1115 if (switchPrev(m_catalog, pos)) 1116 gotoEntry(pos); 1117 } 1118 1119 void EditorTab::gotoFirstUnfiltered() 1120 { 1121 gotoEntry(DocPosition(0)); 1122 } 1123 void EditorTab::gotoLastUnfiltered() 1124 { 1125 gotoEntry(DocPosition(m_catalog->numberOfEntries() - 1)); 1126 } 1127 1128 void EditorTab::gotoFirst() 1129 { 1130 DocPosition pos = DocPosition(m_transUnitsView->firstEntryNumber()); 1131 if (pos.entry != -1) 1132 gotoEntry(pos); 1133 } 1134 1135 void EditorTab::gotoLast() 1136 { 1137 DocPosition pos = DocPosition(m_transUnitsView->lastEntryNumber()); 1138 if (pos.entry != -1) 1139 gotoEntry(pos); 1140 } 1141 1142 1143 void EditorTab::gotoNext() 1144 { 1145 DocPosition pos = m_currentPos; 1146 if (m_catalog->isPlural(pos) && pos.form + 1 < m_catalog->numberOfPluralForms()) 1147 pos.form++; 1148 else 1149 pos = DocPosition(m_transUnitsView->nextEntryNumber()); 1150 1151 if (pos.entry != -1) 1152 gotoEntry(pos); 1153 } 1154 1155 void EditorTab::gotoPrev() 1156 { 1157 DocPosition pos = m_currentPos; 1158 if (m_catalog->isPlural(pos) && pos.form > 0) 1159 pos.form--; 1160 else 1161 pos = DocPosition(m_transUnitsView->prevEntryNumber()); 1162 1163 if (pos.entry != -1) 1164 gotoEntry(pos); 1165 } 1166 1167 void EditorTab::gotoPrevFuzzy() 1168 { 1169 DocPosition pos; 1170 1171 if ((pos.entry = m_catalog->prevFuzzyIndex(m_currentPos.entry)) == -1) 1172 return; 1173 1174 gotoEntry(pos); 1175 } 1176 1177 void EditorTab::gotoNextFuzzy() 1178 { 1179 DocPosition pos; 1180 1181 if ((pos.entry = m_catalog->nextFuzzyIndex(m_currentPos.entry)) == -1) 1182 return; 1183 1184 gotoEntry(pos); 1185 } 1186 1187 void EditorTab::gotoPrevUntranslated() 1188 { 1189 DocPosition pos; 1190 1191 if ((pos.entry = m_catalog->prevUntranslatedIndex(m_currentPos.entry)) == -1) 1192 return; 1193 1194 gotoEntry(pos); 1195 } 1196 1197 void EditorTab::gotoNextUntranslated() 1198 { 1199 DocPosition pos; 1200 1201 if ((pos.entry = m_catalog->nextUntranslatedIndex(m_currentPos.entry)) == -1) 1202 return; 1203 1204 gotoEntry(pos); 1205 } 1206 1207 void EditorTab::gotoPrevFuzzyUntr() 1208 { 1209 DocPosition pos; 1210 1211 short fu = m_catalog->prevFuzzyIndex(m_currentPos.entry); 1212 short un = m_catalog->prevUntranslatedIndex(m_currentPos.entry); 1213 1214 pos.entry = fu > un ? fu : un; 1215 if (pos.entry == -1) 1216 return; 1217 1218 gotoEntry(pos); 1219 } 1220 1221 bool EditorTab::gotoNextFuzzyUntr() 1222 { 1223 return gotoNextFuzzyUntr(DocPosition()); 1224 } 1225 1226 bool EditorTab::gotoNextFuzzyUntr(const DocPosition& p) 1227 { 1228 int index = (p.entry == -1) ? m_currentPos.entry : p.entry; 1229 1230 DocPosition pos; 1231 1232 short fu = m_catalog->nextFuzzyIndex(index); 1233 short un = m_catalog->nextUntranslatedIndex(index); 1234 if ((fu == -1) && (un == -1)) 1235 return false; 1236 1237 if (fu == -1) fu = un; 1238 else if (un == -1) un = fu; 1239 1240 pos.entry = fu < un ? fu : un; 1241 gotoEntry(pos); 1242 return true; 1243 } 1244 1245 1246 void EditorTab::toggleApprovementGotoNextFuzzyUntr() 1247 { 1248 if (!m_catalog->isApproved(m_currentPos.entry)) 1249 m_view->toggleApprovement(); 1250 if (!gotoNextFuzzyUntr()) 1251 gotoNextFuzzyUntr(DocPosition(-2));//so that we don't skip the first 1252 } 1253 1254 void EditorTab::setApproveActionTitle() 1255 { 1256 const char* const titles[] = { 1257 I18N_NOOP2("@option:check trans-unit state", "Translated"), 1258 I18N_NOOP2("@option:check trans-unit state", "Signed-off"), 1259 I18N_NOOP2("@option:check trans-unit state", "Approved") 1260 }; 1261 const char* const helpText[] = { 1262 I18N_NOOP2("@info:tooltip", "Translation is done (although still may need a review)"), 1263 I18N_NOOP2("@info:tooltip", "Translation has received positive review"), 1264 I18N_NOOP2("@info:tooltip", "Entry is fully localized (i.e. final)") 1265 }; 1266 1267 int role = m_catalog->activePhaseRole(); 1268 if (role == ProjectLocal::Undefined) 1269 role = Project::local()->role(); 1270 m_approveAction->setText(i18nc("@option:check trans-unit state", titles[role])); 1271 m_approveAction->setToolTip(i18nc("@info:tooltip", helpText[role])); 1272 m_approveAndGoAction->setVisible(role == ProjectLocal::Approver); 1273 } 1274 1275 void EditorTab::showStatesMenu() 1276 { 1277 m_stateAction->menu()->clear(); 1278 if (!(m_catalog->capabilities()&ExtendedStates)) { 1279 QAction* a = m_stateAction->menu()->addAction(i18nc("@info:status 'fuzzy' in gettext terminology", "Needs review")); 1280 a->setData(QVariant(-1)); 1281 a->setCheckable(true); 1282 a->setChecked(!m_catalog->isApproved(m_currentPos)); 1283 1284 a = m_stateAction->menu()->addAction(i18nc("@info:status 'non-fuzzy' in gettext terminology", "Ready")); 1285 a->setData(QVariant(-2)); 1286 a->setCheckable(true); 1287 a->setChecked(m_catalog->isApproved(m_currentPos)); 1288 1289 return; 1290 } 1291 1292 TargetState state = m_catalog->state(m_currentPos); 1293 1294 const char* const* states = Catalog::states(); 1295 for (int i = 0; i < StateCount; ++i) { 1296 QAction* a = m_stateAction->menu()->addAction(i18n(states[i])); 1297 a->setData(QVariant(i)); 1298 a->setCheckable(true); 1299 a->setChecked(state == i); 1300 1301 if (i == New || i == Translated || i == Final) 1302 m_stateAction->menu()->addSeparator(); 1303 } 1304 } 1305 1306 void EditorTab::setState(QAction* a) 1307 { 1308 if (!(m_catalog->capabilities()&ExtendedStates)) 1309 m_view->toggleApprovement(); 1310 else 1311 m_view->setState(TargetState(a->data().toInt())); 1312 1313 m_stateAction->menu()->clear(); 1314 } 1315 1316 void EditorTab::openPhasesWindow() 1317 { 1318 PhasesWindow w(m_catalog, this); 1319 w.exec(); 1320 } 1321 1322 void EditorTab::gotoPrevBookmark() 1323 { 1324 DocPosition pos; 1325 1326 if ((pos.entry = m_catalog->prevBookmarkIndex(m_currentPos.entry)) == -1) 1327 return; 1328 1329 gotoEntry(pos); 1330 } 1331 1332 void EditorTab::gotoNextBookmark() 1333 { 1334 DocPosition pos; 1335 1336 if ((pos.entry = m_catalog->nextBookmarkIndex(m_currentPos.entry)) == -1) 1337 return; 1338 1339 gotoEntry(pos); 1340 } 1341 1342 //wrapper for cmdline handling... 1343 void EditorTab::mergeOpen(QString mergeFilePath) 1344 { 1345 m_syncView->mergeOpen(mergeFilePath); 1346 } 1347 1348 //HACK to prevent redundant repaintings when widget isn't visible 1349 void EditorTab::paintEvent(QPaintEvent* event) 1350 { 1351 if (QMdiSubWindow* sw = qobject_cast<QMdiSubWindow*>(parent())) { 1352 if (sw->mdiArea()->currentSubWindow() != sw) 1353 return; 1354 } 1355 LokalizeSubwindowBase2::paintEvent(event); 1356 } 1357 1358 void EditorTab::indexWordsForCompletion() 1359 { 1360 CompletionStorage::instance()->scanCatalog(m_catalog); 1361 } 1362 1363 void EditorTab::launchPology() 1364 { 1365 if (!m_pologyProcessInProgress) { 1366 QString command = Settings::self()->pologyCommandFile().replace(QStringLiteral("%f"), QStringLiteral("\"") + currentFilePath() + QStringLiteral("\"")); 1367 m_pologyProcess = new KProcess; 1368 m_pologyProcess->setOutputChannelMode(KProcess::SeparateChannels); 1369 qCWarning(LOKALIZE_LOG) << "Launching pology command: " << command; 1370 connect(m_pologyProcess, QOverload<int, QProcess::ExitStatus>::of(&KProcess::finished), 1371 this, &EditorTab::pologyHasFinished); 1372 m_pologyProcess->setShellCommand(command); 1373 m_pologyProcessInProgress = true; 1374 m_pologyProcess->start(); 1375 } else { 1376 KMessageBox::error(this, i18n("A Pology check is already in progress."), i18n("Pology error")); 1377 } 1378 } 1379 1380 void EditorTab::pologyHasFinished(int exitCode, QProcess::ExitStatus exitStatus) 1381 { 1382 const QString pologyError = m_pologyProcess->readAllStandardError(); 1383 if (exitStatus == QProcess::CrashExit) { 1384 KMessageBox::error(this, i18n("The Pology check has crashed unexpectedly:\n%1", pologyError), i18n("Pology error")); 1385 } else if (exitCode == 0) { 1386 KMessageBox::information(this, i18n("The Pology check has succeeded."), i18n("Pology success")); 1387 } else { 1388 KMessageBox::error(this, i18n("The Pology check has returned an error:\n%1", pologyError), i18n("Pology error")); 1389 } 1390 m_pologyProcess->deleteLater(); 1391 m_pologyProcessInProgress = false; 1392 } 1393 1394 void EditorTab::clearTranslatedEntries() 1395 { 1396 switch (KMessageBox::warningYesNoCancel(this, 1397 i18nc("@info", "This will delete all the translations from the file.\n" 1398 "Do you really want to clear all translated entries?"), i18nc("@title:window", "Warning"), 1399 KStandardGuiItem::yes(), KStandardGuiItem::no())) { 1400 case KMessageBox::Yes: { 1401 DocPosition pos(0); 1402 do { 1403 removeTargetSubstring(m_catalog, pos); 1404 } while (switchNext(m_catalog, pos)); 1405 msgStrChanged(); 1406 gotoEntry(m_currentPos); 1407 } 1408 default:; 1409 } 1410 } 1411 1412 void EditorTab::displayWordCount() 1413 { 1414 //TODO in trans and fuzzy separately 1415 int sourceCount = 0; 1416 int targetCount = 0; 1417 QRegExp rxClean(Project::instance()->markup() + '|' + Project::instance()->accel()); //cleaning regexp; NOTE isEmpty()? 1418 QRegExp rxSplit(QStringLiteral("\\W|\\d"));//splitting regexp 1419 DocPosition pos(0); 1420 do { 1421 QString msg = m_catalog->source(pos); 1422 msg.remove(rxClean); 1423 QStringList words = msg.split(rxSplit, Qt::SkipEmptyParts); 1424 sourceCount += words.size(); 1425 1426 msg = m_catalog->target(pos); 1427 msg.remove(rxClean); 1428 words = msg.split(rxSplit, Qt::SkipEmptyParts); 1429 targetCount += words.size(); 1430 } while (switchNext(m_catalog, pos)); 1431 1432 KMessageBox::information(this, i18nc("@info words count", 1433 "Source text words: %1<br/>Target text words: %2", 1434 sourceCount, targetCount), i18nc("@title", "Word Count")); 1435 } 1436 bool EditorTab::findEntryBySourceContext(const QString& source, const QString& ctxt) 1437 { 1438 DocPosition pos(0); 1439 do { 1440 if (m_catalog->source(pos) == source && m_catalog->context(pos.entry) == QStringList(ctxt)) { 1441 gotoEntry(pos); 1442 return true; 1443 } 1444 } while (switchNext(m_catalog, pos)); 1445 return false; 1446 } 1447 1448 //see also termlabel.h 1449 void EditorTab::defineNewTerm() 1450 { 1451 //TODO just a word under cursor? 1452 QString en(m_view->selectionInSource().toLower()); 1453 if (en.isEmpty()) 1454 en = m_catalog->msgid(m_currentPos).toLower(); 1455 1456 QString target(m_view->selectionInTarget().toLower()); 1457 if (target.isEmpty()) 1458 target = m_catalog->msgstr(m_currentPos).toLower(); 1459 1460 m_project->defineNewTerm(en, target); 1461 } 1462 1463 1464 void EditorTab::reloadFile() 1465 { 1466 QString mergeFilePath = m_syncView->filePath(); 1467 DocPosition p = m_currentPos; 1468 if (!fileOpen(currentFilePath())) 1469 return; 1470 1471 gotoEntry(p); 1472 if (!mergeFilePath.isEmpty()) 1473 mergeOpen(mergeFilePath); 1474 } 1475 1476 static void openLxrSearch(const QString& srcFileRelPath) 1477 { 1478 QDesktopServices::openUrl(QUrl(QStringLiteral("https://lxr.kde.org/search?_filestring=") 1479 + QString::fromLatin1(QUrl::toPercentEncoding(srcFileRelPath)))); 1480 } 1481 1482 static void openLocalSource(const QString& file, int line) 1483 { 1484 if (!Settings::self()->customEditorEnabled()) { 1485 QDesktopServices::openUrl(QUrl::fromLocalFile(file)); 1486 return; 1487 } 1488 1489 const QString cmd = Settings::self()->customEditorCommand().arg(file).arg(line); 1490 QStringList args = KShell::splitArgs(cmd); 1491 if (args.isEmpty()) 1492 return; 1493 const QString prog = args.takeFirst(); 1494 1495 // Make sure prog is in PATH and not just in the CWD 1496 const QString progFullPath = QStandardPaths::findExecutable(prog); 1497 if (progFullPath.isEmpty()) { 1498 qWarning() << "Could not find application in PATH." << prog; 1499 return; 1500 } 1501 1502 QProcess::startDetached(progFullPath, args); 1503 } 1504 1505 void EditorTab::dispatchSrcFileOpenRequest(const QString& srcFileRelPath, int line) 1506 { 1507 // Try project scripts first. 1508 m_srcFileOpenRequestAccepted = false; 1509 Q_EMIT srcFileOpenRequested(srcFileRelPath, line); 1510 if (m_srcFileOpenRequestAccepted) 1511 return; 1512 1513 // If project scripts do not handle opening the source file, check if the 1514 // path exists relative to the current translation file path. 1515 QDir relativePath(currentFilePath()); 1516 relativePath.cdUp(); 1517 QString srcAbsolutePath(relativePath.absoluteFilePath(srcFileRelPath)); 1518 if (QFile::exists(srcAbsolutePath)) { 1519 openLocalSource(srcAbsolutePath, line); 1520 return; 1521 } 1522 1523 QString dir = Project::instance()->local()->sourceDir(); 1524 if (dir.isEmpty()) { 1525 switch (KMessageBox::questionYesNoCancel(SettingsController::instance()->mainWindowPtr(), 1526 i18nc("@info", "Would you like to search for the source file locally or via lxr.kde.org?"), i18nc("@title:window", "Source file lookup"), 1527 KGuiItem(i18n("Locally")), KGuiItem("lxr.kde.org") 1528 )) { 1529 case KMessageBox::Yes: break; 1530 case KMessageBox::No: openLxrSearch(srcFileRelPath); 1531 case KMessageBox::Cancel: 1532 default: 1533 return; 1534 } 1535 } 1536 1537 //hard fallback 1538 if (dir.isEmpty()) { 1539 dir = QFileDialog::getExistingDirectory(this, i18n("Select project's base folder for source file lookup"), QDir::homePath()); 1540 if (dir.length()) 1541 Project::instance()->local()->setSourceDir(dir); 1542 } 1543 if (dir.length()) { 1544 auto doOpen = [srcFileRelPath, dir, line]() { 1545 auto sourceFilePaths = Project::instance()->sourceFilePaths(); 1546 QString absFilePath = QString("%1/%2").arg(dir, srcFileRelPath); 1547 if (QFileInfo::exists(absFilePath)) { 1548 openLocalSource(absFilePath, line); 1549 return; 1550 } 1551 bool found = false; 1552 QByteArray fn = srcFileRelPath.midRef(srcFileRelPath.lastIndexOf('/') + 1).toUtf8(); 1553 auto it = sourceFilePaths.constFind(fn); 1554 while (it != sourceFilePaths.constEnd() && it.key() == fn) { 1555 const QString absFilePath = QString::fromUtf8(it.value() + '/' + fn); 1556 if (!absFilePath.endsWith(srcFileRelPath) || !QFileInfo::exists(absFilePath)) { //for the case when link contained also folders 1557 it++; 1558 continue; 1559 } 1560 found = true; 1561 openLocalSource(absFilePath, line); 1562 it++; 1563 } 1564 if (!found) { 1565 switch (KMessageBox::warningYesNoCancel(SettingsController::instance()->mainWindowPtr(), 1566 i18nc("@info", "Could not find source file in the folder specified.\n" 1567 "Do you want to change source files folder?"), i18nc("@title:window", "Source file lookup"), 1568 KStandardGuiItem::yes(), KStandardGuiItem::no(), KGuiItem(i18n("lxr.kde.org")))) { 1569 case KMessageBox::Cancel: 1570 Project::instance()->local()->setSourceDir(QString()); 1571 Project::instance()->resetSourceFilePaths(); 1572 openLxrSearch(srcFileRelPath); 1573 case KMessageBox::No: 1574 return; 1575 default: ; //fall through to dir selection 1576 } 1577 1578 QString dir = QFileDialog::getExistingDirectory(nullptr, i18n("Select project's base folder for source file lookup"), Project::instance()->local()->sourceDir()); 1579 if (dir.length()) { 1580 Project::instance()->local()->setSourceDir(dir); 1581 Project::instance()->resetSourceFilePaths(); 1582 } 1583 1584 } 1585 }; 1586 if (Project::instance()->isSourceFilePathsReady()) 1587 doOpen(); 1588 else { 1589 Project::instance()->sourceFilePaths(); 1590 auto conn = std::make_shared<QMetaObject::Connection>(); 1591 *conn = connect(Project::instance(), &Project::sourceFilePathsAreReady, [this, conn, doOpen](){ 1592 this->disconnect(*conn); 1593 doOpen(); 1594 }); 1595 } 1596 return; 1597 } 1598 1599 1600 // Otherwise, let the user know how to create a project script to handle 1601 // opening source file paths that are not relative to translation files. 1602 KMessageBox::information(this, i18nc("@info", 1603 "Cannot open the target source file: The target source file is not " 1604 "relative to the current translation file, and there are currently no " 1605 "scripts loaded to handle opening source files in custom paths. Refer " 1606 "to the Lokalize handbook for script examples and how to plug them " 1607 "into your project.")); 1608 } 1609 1610 void EditorTab::mergeIntoOpenDocument() 1611 { 1612 if (!m_catalog || m_catalog->type() != Xliff) 1613 return; 1614 1615 const QString xliff2odf = QStandardPaths::findExecutable(QStringLiteral("xliff2odf")); 1616 if (xliff2odf.isEmpty()) { 1617 KMessageBox::error(SettingsController::instance()->mainWindowPtr(), i18n("Install translate-toolkit package and retry")); 1618 return; 1619 } 1620 1621 if (QProcess::execute(xliff2odf, QStringList(QLatin1String("--version"))) == -2) { 1622 KMessageBox::error(SettingsController::instance()->mainWindowPtr(), 1623 i18n("Install translate-toolkit package and retry.")); 1624 return; 1625 } 1626 QString xliffFolder = QFileInfo(m_catalog->url()).absolutePath(); 1627 1628 QString originalOdfFilePath = m_catalog->originalOdfFilePath(); 1629 if (originalOdfFilePath.isEmpty() || !QFile::exists(originalOdfFilePath)) { 1630 originalOdfFilePath = QFileDialog::getOpenFileName( 1631 SettingsController::instance()->mainWindowPtr(), 1632 i18n("Select original OpenDocument on which current XLIFF file is based"), 1633 xliffFolder, 1634 i18n("OpenDocument files (*.odt *.ods)")/*"text/x-lokalize-project"*/); 1635 if (originalOdfFilePath.length()) m_catalog->setOriginalOdfFilePath(originalOdfFilePath); 1636 } 1637 if (originalOdfFilePath.isEmpty()) 1638 return; 1639 1640 saveFile(); 1641 1642 //TODO check if odt did update (merge with new template is needed) 1643 1644 QFileInfo originalOdfFileInfo(originalOdfFilePath); 1645 QString targetLangCode = m_catalog->targetLangCode(); 1646 1647 QStringList args(m_catalog->url()); 1648 args.append(xliffFolder + '/' + originalOdfFileInfo.baseName() + '-' + targetLangCode + '.' + originalOdfFileInfo.suffix()); 1649 args.append(QStringLiteral("-t")); 1650 args.append(originalOdfFilePath); 1651 qCDebug(LOKALIZE_LOG) << args; 1652 QProcess::execute(xliff2odf, args); 1653 1654 if (!QFile::exists(args.at(1))) 1655 return; 1656 1657 //if (originalOdfFileInfo.suffix().toLower()==QLatin1String(".odt")) 1658 { 1659 const QString lowriter = QStandardPaths::findExecutable(QStringLiteral("soffice")); 1660 if (lowriter.isEmpty()) { 1661 return; 1662 } 1663 1664 if (QProcess::execute(lowriter, QStringList("--version")) == -2) { 1665 //TODO 1666 //KMessageBox::error(SettingsController::instance()->mainWindowPtr(), i18n("Install translate-toolkit package and retry")); 1667 return; 1668 } 1669 QProcess::startDetached(lowriter, QStringList(args.at(1))); 1670 QString reloaderScript = QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("scripts/odf/xliff2odf-standalone.py")); 1671 if (reloaderScript.length()) { 1672 QString python = QStandardPaths::findExecutable(QStringLiteral("python")); 1673 QStringList unoArgs(QStringLiteral("-c")); unoArgs.append(QStringLiteral("import uno")); 1674 if (python.isEmpty() || QProcess::execute(python, unoArgs) != 0) { 1675 python = QStandardPaths::findExecutable(QStringLiteral("python3")); 1676 QStringList unoArgs(QStringLiteral("-c")); unoArgs.append(QStringLiteral("import uno")); 1677 if (python.isEmpty() || QProcess::execute(python, unoArgs) != 0) { 1678 KMessageBox::information(SettingsController::instance()->mainWindowPtr(), 1679 i18n("Install python-uno package for additional functionality.")); 1680 return; 1681 } 1682 } 1683 1684 QStringList reloaderArgs(reloaderScript); 1685 reloaderArgs.append(args.at(1)); 1686 reloaderArgs.append(currentEntryId()); 1687 QProcess::execute(python, reloaderArgs); 1688 } 1689 } 1690 } 1691 1692 1693 //BEGIN DBus interface 1694 #include "editoradaptor.h" 1695 QList<int> EditorTab::ids; 1696 1697 QString EditorTab::dbusObjectPath() 1698 { 1699 const QString EDITOR_PATH = QStringLiteral("/ThisIsWhatYouWant/Editor/"); 1700 if (m_dbusId == -1) { 1701 m_adaptor = new EditorAdaptor(this); 1702 1703 int i = 0; 1704 while (i < ids.size() && i == ids.at(i)) 1705 ++i; 1706 ids.insert(i, i); 1707 m_dbusId = i; 1708 QDBusConnection::sessionBus().registerObject(EDITOR_PATH + QString::number(m_dbusId), this); 1709 } 1710 return EDITOR_PATH + QString::number(m_dbusId); 1711 } 1712 1713 1714 QString EditorTab::currentFilePath() 1715 { 1716 return m_catalog->url(); 1717 } 1718 QByteArray EditorTab::currentFileContents() 1719 { 1720 return m_catalog->contents(); 1721 } 1722 QString EditorTab::currentEntryId() 1723 { 1724 return m_catalog->id(m_currentPos); 1725 } 1726 QString EditorTab::selectionInTarget() 1727 { 1728 return m_view->selectionInTarget(); 1729 } 1730 QString EditorTab::selectionInSource() 1731 { 1732 return m_view->selectionInSource(); 1733 } 1734 1735 void EditorTab::lookupSelectionInTranslationMemory() 1736 { 1737 Q_EMIT tmLookupRequested(selectionInSource(), selectionInTarget()); 1738 } 1739 1740 1741 void EditorTab::setEntryFilteredOut(int entry, bool filteredOut) 1742 { 1743 m_transUnitsView->setEntryFilteredOut(entry, filteredOut); 1744 } 1745 void EditorTab::setEntriesFilteredOut(bool filteredOut) 1746 { 1747 m_transUnitsView->setEntriesFilteredOut(filteredOut); 1748 } 1749 int EditorTab::entryCount() 1750 { 1751 return m_catalog->numberOfEntries(); 1752 } 1753 1754 QString EditorTab::entrySource(int entry, int form) 1755 { 1756 return m_catalog->sourceWithTags(DocPosition(entry, form)).string; 1757 } 1758 QString EditorTab::entryTarget(int entry, int form) 1759 { 1760 return m_catalog->targetWithTags(DocPosition(entry, form)).string; 1761 } 1762 int EditorTab::entryPluralFormCount(int entry) 1763 { 1764 return m_catalog->isPlural(entry) ? m_catalog->numberOfPluralForms() : 1; 1765 } 1766 bool EditorTab::entryReady(int entry) 1767 { 1768 return m_catalog->isApproved(entry); 1769 } 1770 QString EditorTab::sourceLangCode() 1771 { 1772 return m_catalog->sourceLangCode(); 1773 } 1774 QString EditorTab::targetLangCode() 1775 { 1776 return m_catalog->targetLangCode(); 1777 } 1778 void EditorTab::addEntryNote(int entry, const QString& note) 1779 { 1780 m_notesView->addNote(entry, note); 1781 } 1782 void EditorTab::addTemporaryEntryNote(int entry, const QString& note) 1783 { 1784 m_notesView->addTemporaryEntryNote(entry, note); 1785 } 1786 1787 void EditorTab::addAlternateTranslation(int entry, const QString& translation) 1788 { 1789 m_altTransView->addAlternateTranslation(entry, translation); 1790 } 1791 void EditorTab::addTemporaryAlternateTranslation(int entry, const QString& translation) 1792 { 1793 m_altTransView->addAlternateTranslation(entry, translation); 1794 } 1795 void EditorTab::attachAlternateTranslationFile(const QString& path) 1796 { 1797 m_altTransView->attachAltTransFile(path); 1798 } 1799 1800 void EditorTab::setEntryTarget(int entry, int form, const QString& content) 1801 { 1802 DocPosition pos(entry, form); 1803 m_catalog->beginMacro(i18nc("@item Undo action item", "Set unit text")); 1804 removeTargetSubstring(m_catalog, pos); 1805 insertCatalogString(m_catalog, pos, CatalogString(content)); 1806 m_catalog->endMacro(); 1807 if (m_currentPos == pos) 1808 m_view->gotoEntry(); 1809 } 1810 //END DBus interface