diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cfc8aae..ed02c24 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,207 +1,207 @@ project(lokalize) if(NOT WIN32) find_package(HUNSPELL REQUIRED) else(NOT WIN32) find_package(HUNSPELL) endif(NOT WIN32) if(HUNSPELL_FOUND) add_definitions(-DHAVE_HUNSPELL) include_directories( ${HUNSPELL_INCLUDE_DIRS} ) endif(HUNSPELL_FOUND) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/prefs ${CMAKE_CURRENT_SOURCE_DIR}/common ${CMAKE_CURRENT_SOURCE_DIR}/catalog ${CMAKE_CURRENT_SOURCE_DIR}/catalog/gettext ${CMAKE_CURRENT_SOURCE_DIR}/catalog/xliff ${CMAKE_CURRENT_SOURCE_DIR}/catalog/ts ${CMAKE_CURRENT_SOURCE_DIR}/cataloglistview ${CMAKE_CURRENT_SOURCE_DIR}/project ${CMAKE_CURRENT_SOURCE_DIR}/glossary ${CMAKE_CURRENT_SOURCE_DIR}/webquery ${CMAKE_CURRENT_SOURCE_DIR}/tm ${CMAKE_CURRENT_SOURCE_DIR}/filesearch ${CMAKE_CURRENT_SOURCE_DIR}/mergemode ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) set(lokalize_SRCS main.cpp lokalizemainwindow.cpp actionproxy.cpp editortab.cpp editortab_findreplace.cpp editorview.cpp xlifftextedit.cpp syntaxhighlighter.cpp completionstorage.cpp phaseswindow.cpp noteeditor.cpp msgctxtview.cpp binunitsview.cpp cataloglistview/cataloglistview.cpp cataloglistview/catalogmodel.cpp common/headerviewmenu.cpp common/domroutines.cpp common/htmlhelpers.cpp common/fastsizehintitemdelegate.cpp common/flowlayout.cpp common/termlabel.cpp common/languagelistmodel.cpp common/stemming.cpp glossary/glossaryview.cpp glossary/glossary.cpp glossary/glossarywindow.cpp mergemode/mergecatalog.cpp mergemode/mergeview.cpp alttransview.cpp common/diff.cpp project/project.cpp project/projectmodel.cpp project/projectwidget.cpp project/projecttab.cpp project/poextractor.cpp project/xliffextractor.cpp prefs/prefs.cpp webquery/webqueryview.cpp webquery/webquerycontroller.cpp webquery/myactioncollectionview.cpp tools/widgettextcaptureconfig.cpp filesearch/filesearchtab.cpp tm/tmview.cpp tm/tmscanapi.cpp tm/jobs.cpp tm/dbfilesmodel.cpp tm/tmmanager.cpp tm/tmtab.cpp tm/qaview.cpp tm/qamodel.cpp catalog/phase.cpp catalog/cmd.cpp catalog/pos.cpp catalog/catalog.cpp catalog/catalogstring.cpp catalog/gettextheader.cpp catalog/gettext/gettextstorage.cpp catalog/gettext/catalogitem.cpp catalog/gettext/importplugin.cpp catalog/gettext/gettextimport.cpp catalog/gettext/gettextexport.cpp catalog/xliff/xliffstorage.cpp catalog/ts/tsstorage.cpp ) if(WIN32) list (APPEND lokalize_SRCS common/winhelpers.cpp) else(WIN32) list (APPEND lokalize_SRCS common/unixhelpers.cpp) endif(WIN32) ecm_qt_declare_logging_category(lokalize_SRCS HEADER lokalize_debug.h IDENTIFIER LOKALIZE_LOG CATEGORY_NAME org.kde.lokalize DEFAULT_SEVERITY Warning ) ki18n_wrap_ui(lokalize_SRCS prefs/prefs_identity.ui prefs/prefs_general.ui prefs/prefs_editor.ui prefs/prefs_appearance.ui prefs/prefs_tm.ui + prefs/prefs_pology.ui project/prefs_project_advanced.ui project/prefs_project_local.ui - project/prefs_project_pology.ui project/prefs_projectmain.ui glossary/termedit.ui filesearch/filesearchoptions.ui filesearch/massreplaceoptions.ui tm/queryoptions.ui tm/managedatabases.ui tm/dbparams.ui kaider_findextension.ui webquery/querycontrol.ui tools/widgettextcaptureconfig.ui ) kconfig_add_kcfg_files(lokalize_SRCS prefs/prefs_lokalize.kcfgc project/projectbase.kcfgc project/projectlocal.kcfgc ) qt5_add_dbus_adaptor(lokalize_SRCS org.kde.lokalize.MainWindow.xml lokalizemainwindow.h LokalizeMainWindow) qt5_add_dbus_adaptor(lokalize_SRCS org.kde.lokalize.Editor.xml editortab.h EditorTab) qt5_add_dbus_adaptor(lokalize_SRCS filesearch/org.kde.lokalize.FileSearch.xml filesearch/filesearchtab.h FileSearchTab) qt5_add_dbus_adaptor(lokalize_SRCS tm/org.kde.lokalize.TranslationMemory.xml tm/tmtab.h TM::TMTab) qt5_add_dbus_adaptor(lokalize_SRCS project/org.kde.lokalize.Project.xml project/project.h Project) qt5_add_dbus_adaptor(lokalize_SRCS project/org.kde.lokalize.ProjectOverview.xml project/projecttab.h ProjectTab) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/../icons/global/*-apps-lokalize.png") ecm_add_app_icon(lokalize_SRCS ICONS ${ICONS_SRCS}) add_executable(lokalize ${lokalize_SRCS}) target_link_libraries(lokalize KF5::KIOFileWidgets KF5::ItemViews KF5::Notifications KF5::SonnetCore KF5::SonnetUi KF5::KrossCore KF5::KrossUi KF5::DBusAddons KF5::Crash Qt5::Sql ) if(HUNSPELL_FOUND) target_link_libraries(lokalize ${HUNSPELL_LIBRARIES}) endif(HUNSPELL_FOUND) install(TARGETS lokalize ${INSTALL_TARGETS_DEFAULT_ARGS} ) ########### install files ############### install( PROGRAMS org.kde.lokalize.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install( FILES prefs/lokalize.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) install( FILES lokalize.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR} ) install( FILES lokalize.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) install( FILES editorui.rc lokalizemainwindowui.rc scriptsui.rc project/projectmanagerui.rc tm/translationmemoryrui.rc filesearch/filesearchtabui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/lokalize ) diff --git a/src/editortab.cpp b/src/editortab.cpp index 8308295..6680346 100644 --- a/src/editortab.cpp +++ b/src/editortab.cpp @@ -1,1774 +1,1811 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2014 by Nick Shaforostoff 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" #ifndef NOKDE #define WEBQUERY_ENABLE #endif //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" #ifndef NOKDE -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #endif -#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) #ifndef NOKDE , m_sonnetDialog(0) , m_spellcheckStartUndoIndex(0) , m_spellcheckStop(false) #endif , m_currentIsApproved(true) , m_currentIsUntr(true) , m_fullPathShown(false) #ifndef NOKDE , m_doReplaceCalled(false) , m_find(0) , m_replace(0) #endif , m_syncView(0) , m_syncViewSecondary(0) #ifndef NOKDE , m_valid(valid) , m_dbusId(-1) #endif { //QTime chrono;chrono.start(); setAcceptDrops(true); setCentralWidget(m_view); setupStatusBar(); //--NOT called from initLater() ! setupActions(); #ifndef NOKDE dbusObjectPath(); #endif 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()); } #ifndef NOKDE ids.removeAll(m_dbusId); #endif } 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); } #ifndef NOKDE 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); } #endif 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; #ifdef NOKDE QMenu* am = new QMenu(i18nc("@option:check whether message is marked as translated/reviewed/approved (depending on your role)", "State"), this); action = am->menuAction(); ac->addAction(QStringLiteral("edit_state"), action); #endif 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")); #ifndef NOKDE 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")); #endif // 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); #ifndef NOKDE //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())); } #endif //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) { #ifndef NOKDE 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)); #else KMessageBox::error(this, i18nc("@info", "Error opening the file")); #endif } 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; #ifndef NOKDE 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()) ) #else if (QMessageBox::Yes == QMessageBox::warning(this, QString(), i18nc("@info", "Error saving the file %1\n" "Do you want to save to another file or cancel?").arg(errorFilePath), QMessageBox::Yes | QMessageBox::No) ) #endif 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); #ifdef NOKDE m_stateAction->setVisible(m_catalog->capabilities()&ExtendedStates); #endif } 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)))); } 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)) { QDesktopServices::openUrl(QUrl::fromLocalFile(srcAbsolutePath)); 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]() { auto sourceFilePaths = Project::instance()->sourceFilePaths(); 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; QDesktopServices::openUrl(QUrl::fromLocalFile(absFilePath)); 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 #ifndef NOKDE #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); } #endif 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 diff --git a/src/editortab.h b/src/editortab.h index a501181..e6bb210 100644 --- a/src/editortab.h +++ b/src/editortab.h @@ -1,417 +1,424 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2014 by Nick Shaforostoff 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 . **************************************************************************** */ #ifndef EDITORTAB_H #define EDITORTAB_H #ifdef HAVE_CONFIG_H #include #endif #include "pos.h" #include "lokalizesubwindowbase.h" #include +#include #ifndef NOKDE namespace Sonnet { class Dialog; } namespace Sonnet { class BackgroundChecker; } #include class KFind; class KReplace; class KActionCategory; #endif class Project; class Catalog; class EditorView; class MergeView; class CatalogView; class MsgCtxtView; class AltTransView; namespace GlossaryNS { class GlossaryView; } struct EditorState { public: EditorState(): entry(0) {} EditorState(const EditorState& s): dockWidgets(s.dockWidgets), filePath(s.filePath), entry(0) {} ~EditorState() {} QByteArray dockWidgets; QString filePath; QString mergeFilePath; int entry; //int offset; }; /** * @short Editor tab * * This class can be called a dispatcher for one message catalog. * * It is accessible via Lokalize.currentEditor() from kross scripts and via * '/ThisIsWhatYouWant/Editor/# : org.kde.Lokalize.Editor' from qdbusviewer * * @author Nick Shaforostoff */ class EditorTab: public LokalizeSubwindowBase2 { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.Lokalize.Editor") //qdbuscpp2xml -m -s editortab.h -o org.kde.lokalize.Editor.xml #define qdbuscpp2xml public: EditorTab(QWidget* parent, bool valid = true); ~EditorTab(); //interface for LokalizeMainWindow void hideDocks(); void showDocks(); QString currentFilePath(); void setFullPathShown(bool); void setProperCaption(QString, bool); //reimpl to remove ' - Lokalize' public slots: void setProperFocus(); public: bool queryClose(); EditorState state(); #ifndef NOKDE KXMLGUIClient* guiClient() { return (KXMLGUIClient*)this; } QString dbusObjectPath(); int dbusId() { return m_dbusId; } QObject* adaptor() { return m_adaptor; } #endif //wrapper for cmdline handling void mergeOpen(QString mergeFilePath); bool fileOpen(QString filePath = QString(), QString suggestedDirPath = QString(), bool silent = false); public slots: //for undo/redo, views void gotoEntry(DocPosition pos); void gotoEntry(DocPosition pos, int selection); #ifdef qdbuscpp2xml Q_SCRIPTABLE void gotoEntry(int entry) { gotoEntry(DocPosition(entry)); } Q_SCRIPTABLE void gotoEntryForm(int entry, int form) { gotoEntry(DocPosition(entry, form)); } Q_SCRIPTABLE void gotoEntryFormOffset(int entry, int form, int offset) { gotoEntry(DocPosition(entry, form, offset)); } Q_SCRIPTABLE void gotoEntryFormOffsetSelection(int entry, int form, int offset, int selection) { gotoEntry(DocPosition(entry, form, offset), selection); } Q_SCRIPTABLE QString currentEntryId(); Q_SCRIPTABLE int currentEntry() { return m_currentPos.entry; } Q_SCRIPTABLE int currentForm() { return m_currentPos.form; } Q_SCRIPTABLE QString selectionInTarget(); Q_SCRIPTABLE QString selectionInSource(); Q_SCRIPTABLE void setEntryFilteredOut(int entry, bool filteredOut); Q_SCRIPTABLE void setEntriesFilteredOut(bool filteredOut); Q_SCRIPTABLE int entryCount(); Q_SCRIPTABLE QString entrySource(int entry, int form); Q_SCRIPTABLE QString entryTarget(int entry, int form); Q_SCRIPTABLE void setEntryTarget(int entry, int form, const QString& content); Q_SCRIPTABLE int entryPluralFormCount(int entry); Q_SCRIPTABLE bool entryReady(int entry); Q_SCRIPTABLE void addEntryNote(int entry, const QString& note); Q_SCRIPTABLE void addTemporaryEntryNote(int entry, const QString& note); Q_SCRIPTABLE void addAlternateTranslation(int entry, const QString& translation); Q_SCRIPTABLE void addTemporaryAlternateTranslation(int entry, const QString& translation); Q_SCRIPTABLE QString currentFile() { return currentFilePath(); } Q_SCRIPTABLE QByteArray currentFileContents(); Q_SCRIPTABLE QString sourceLangCode(); Q_SCRIPTABLE QString targetLangCode(); Q_SCRIPTABLE void attachAlternateTranslationFile(const QString& path); Q_SCRIPTABLE void openSyncSource(QString path) { mergeOpen(path); } Q_SCRIPTABLE void reloadFile(); #endif Q_SCRIPTABLE bool saveFile(const QString& filePath = QString()); Q_SCRIPTABLE bool saveFileAs(const QString& defaultPath = QString()); Q_SCRIPTABLE void close() { return parent()->deleteLater(); } Q_SCRIPTABLE void gotoNextUnfiltered(); Q_SCRIPTABLE void gotoPrevUnfiltered(); Q_SCRIPTABLE void gotoFirstUnfiltered(); Q_SCRIPTABLE void gotoLastUnfiltered(); Q_SCRIPTABLE void gotoNext(); Q_SCRIPTABLE void gotoPrev(); Q_SCRIPTABLE void gotoFirst(); Q_SCRIPTABLE void gotoLast(); Q_SCRIPTABLE void mergeIntoOpenDocument(); Q_SCRIPTABLE bool findEntryBySourceContext(const QString& source, const QString& ctxt); #ifndef NOKDE Q_SCRIPTABLE bool isValid() { return m_valid; } Q_SCRIPTABLE void setSrcFileOpenRequestAccepted(bool a) { m_srcFileOpenRequestAccepted = a; } #endif private slots: #ifndef NOKDE void highlightFound(const QString &, int, int); //for find/replace void highlightFound_(const QString &, int, int); //for find/replace #endif void lookupSelectionInTranslationMemory(); //statusbar indication void numberOfFuzziesChanged(); void numberOfUntranslatedChanged(); //fuzzy, untr [statusbar] indication void msgStrChanged(); //modif [caption] indication void setModificationSign(); void updateCaptionPath(); //gui void switchForm(int); void undo(); void redo(); #ifndef NOKDE void findNext(); void findPrev(); void find(); void replace(); void replaceNext();//internal void doReplace(const QString&, int, int, int); //internal void cleanupReplace();//internal #endif // void selectAll(); // void deselectAll(); // void clear(); // void search2msgstr(); // void plural2msgstr(); void gotoEntry(); void gotoPrevFuzzyUntr(); bool gotoNextFuzzyUntr(const DocPosition& pos); bool gotoNextFuzzyUntr(); void gotoNextFuzzy(); void gotoPrevFuzzy(); void gotoNextUntranslated(); void gotoPrevUntranslated(); void toggleApprovementGotoNextFuzzyUntr(); void setApproveActionTitle(); #ifndef NOKDE void spellcheck(); void spellcheckNext(); void spellcheckShow(const QString&, int); void spellcheckReplace(QString, int, const QString&); void spellcheckStop(); void spellcheckCancel(); #endif void gotoNextBookmark(); void gotoPrevBookmark(); void displayWordCount(); void clearTranslatedEntries(); + void launchPology(); void openPhasesWindow(); void defineNewTerm(); void initLater(); void showStatesMenu(); void setState(QAction*); void dispatchSrcFileOpenRequest(const QString& srcPath, int line); void indexWordsForCompletion(); void fileAutoSaveFailedWarning(const QString&); + void pologyHasFinished(int exitCode, QProcess::ExitStatus exitStatus); + protected: void paintEvent(QPaintEvent* event); private: void setupAccel(); void setupActions(); void setupStatusBar(); void findNext(const DocPosition& startingPos); void replaceNext(const DocPosition&); private: Project* m_project; Catalog* m_catalog; EditorView* m_view; QAction* m_approveAndGoAction; QAction* m_approveAction; QAction* m_stateAction; //is = m_approveAction ifndef NOKDE + KProcess* m_pologyProcess; + bool m_pologyProcessInProgress; + DocPosition m_currentPos; DocPosition _searchingPos; //for find/replace DocPosition _replacingPos; DocPosition _spellcheckPos; DocPosition _spellcheckStartPos; #ifndef NOKDE Sonnet::BackgroundChecker* m_sonnetChecker; Sonnet::Dialog* m_sonnetDialog; int m_spellcheckStartUndoIndex; bool m_spellcheckStop: 1; #endif bool m_currentIsApproved: 1; //for statusbar animation bool m_currentIsUntr: 1; //for statusbar animation bool m_fullPathShown: 1; #ifndef NOKDE bool m_doReplaceCalled: 1; //used to prevent non-clean catalog status KFind* m_find; KReplace* m_replace; #endif //BEGIN views MergeView* m_syncView; MergeView* m_syncViewSecondary; CatalogView* m_transUnitsView; MsgCtxtView* m_notesView; AltTransView* m_altTransView; //END views QString _captionPath; bool m_srcFileOpenRequestAccepted; //BEGIN dbus #ifndef NOKDE bool m_valid; QObject* m_adaptor; int m_dbusId; static QList ids; #endif //END dbus signals: void tmLookupRequested(DocPosition::Part, const QString&); void tmLookupRequested(const QString& source, const QString& target); Q_SCRIPTABLE void srcFileOpenRequested(const QString& srcPath, int line); void fileOpenRequested(const QString& filePath, const QString& str, const QString& ctxt, const bool setAsActive); //emitted when mainwindow is closed or another file is opened void fileClosed(); Q_SCRIPTABLE void fileClosed(const QString& path); Q_SCRIPTABLE void fileSaved(const QString& path); Q_SCRIPTABLE void fileAboutToBeClosed();//old catalog is still accessible Q_SCRIPTABLE void fileOpened(); Q_SCRIPTABLE void entryDisplayed(); void signalNewEntryDisplayed(const DocPosition&); void signalEntryWithMergeDisplayed(bool, const DocPosition&); void signalFirstDisplayed(bool); void signalLastDisplayed(bool); void signalEquivTranslatedEntryDisplayed(bool); void signalApprovedEntryDisplayed(bool); void signalFuzzyEntryDisplayed(bool); void signalPriorFuzzyAvailable(bool); void signalNextFuzzyAvailable(bool); void signalPriorUntranslatedAvailable(bool); void signalNextUntranslatedAvailable(bool); void signalPriorFuzzyOrUntrAvailable(bool); void signalNextFuzzyOrUntrAvailable(bool); // merge mode signals gone to the view //NOTE move these to catalog tree view? void signalPriorBookmarkAvailable(bool); void signalNextBookmarkAvailable(bool); void signalBookmarkDisplayed(bool); Q_SCRIPTABLE void xliffFileOpened(bool); }; #endif diff --git a/src/editorui.rc b/src/editorui.rc index f7e4f68..59dde0c 100644 --- a/src/editorui.rc +++ b/src/editorui.rc @@ -1,270 +1,271 @@ - + &File + &Edit &Glossary Translation &Memory Alternative Translations &Go &Bookmarks S&ync &Secondary sync source Tool&views Main Toolbar diff --git a/src/filesearch/filesearchtab.cpp b/src/filesearch/filesearchtab.cpp index 610f1d4..b010844 100644 --- a/src/filesearch/filesearchtab.cpp +++ b/src/filesearch/filesearchtab.cpp @@ -1,929 +1,929 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2014 by Nick Shaforostoff 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 "filesearchtab.h" #include "lokalize_debug.h" #include "ui_filesearchoptions.h" #include "ui_massreplaceoptions.h" #include "project.h" #include "prefs.h" #include "tmscanapi.h" //TODO separate some decls into new header #include "state.h" #include "qaview.h" #include "catalog.h" #include "fastsizehintitemdelegate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #ifndef NOKDE -#include -#include -#include +#include +#include +#include #endif static QStringList doScanRecursive(const QDir& dir); class FileListModel: public QStringListModel { public: FileListModel(QObject* parent): QStringListModel(parent) {} QVariant data(const QModelIndex& item, int role = Qt::DisplayRole) const; Qt::ItemFlags flags(const QModelIndex&) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } }; QVariant FileListModel::data(const QModelIndex& item, int role) const { if (role == Qt::DisplayRole) return shorterFilePath(stringList().at(item.row())); if (role == Qt::UserRole) return stringList().at(item.row()); return QVariant(); } SearchFileListView::SearchFileListView(QWidget* parent) : QDockWidget(i18nc("@title:window", "File List"), parent) , m_browser(new QTreeView(this)) , m_background(new QLabel(i18n("Drop translation files here..."), this)) , m_model(new FileListModel(this)) { setWidget(m_background); m_background->setMinimumWidth(QFontMetrics(font()).averageCharWidth() * 30); m_background->setAlignment(Qt::AlignCenter); m_browser->hide(); m_browser->setModel(m_model); m_browser->setRootIsDecorated(false); m_browser->setHeaderHidden(true); m_browser->setUniformRowHeights(true); m_browser->setAlternatingRowColors(true); m_browser->setContextMenuPolicy(Qt::ActionsContextMenu); QAction* action = new QAction(i18nc("@action:inmenu", "Clear"), m_browser); connect(action, &QAction::triggered, this, &SearchFileListView::clear); m_browser->addAction(action); connect(m_browser, &QTreeView::activated, this, &SearchFileListView::requestFileOpen); } void SearchFileListView::requestFileOpen(const QModelIndex& item) { emit fileOpenRequested(item.data(Qt::UserRole).toString(), true); } void SearchFileListView::addFiles(const QStringList& files) { if (files.isEmpty()) return; m_background->hide(); setWidget(m_browser); m_browser->show(); //ensure unquiness, sorting the list along the way QMap map; foreach (const QString& filepath, m_model->stringList()) map[filepath] = true; foreach (const QString& filepath, files) map[filepath] = true; m_model->setStringList(map.keys()); } void SearchFileListView::addFilesFast(const QStringList& files) { if (files.size()) m_model->setStringList(m_model->stringList() + files); } void SearchFileListView::clear() { m_model->setStringList(QStringList()); } QStringList SearchFileListView::files() const { return m_model->stringList(); } void SearchFileListView::scrollTo(const QString& file) { if (file.isEmpty()) { m_browser->scrollToTop(); return; } int idx = m_model->stringList().indexOf(file); if (idx != -1) m_browser->scrollTo(m_model->index(idx, 0), QAbstractItemView::PositionAtCenter); } bool SearchParams::isEmpty() const { return sourcePattern.pattern().isEmpty() && targetPattern.pattern().isEmpty(); } SearchJob::SearchJob(const QStringList& f, const SearchParams& sp, const QVector& r, int sn, QObject*) : QRunnable() , files(f) , searchParams(sp) , rules(r) , searchNumber(sn) , m_size(0) { setAutoDelete(false); } void SearchJob::run() { QTime a; a.start(); bool removeAmpFromSource = searchParams.sourcePattern.patternSyntax() == QRegExp::FixedString && !searchParams.sourcePattern.pattern().contains(QLatin1Char('&')); bool removeAmpFromTarget = searchParams.targetPattern.patternSyntax() == QRegExp::FixedString && !searchParams.targetPattern.pattern().contains(QLatin1Char('&')); foreach (const QString& filePath, files) { Catalog catalog(0); if (Q_UNLIKELY(catalog.loadFromUrl(filePath, QString(), &m_size, true) != 0)) continue; //QVector catalogResults; int numberOfEntries = catalog.numberOfEntries(); DocPosition pos(0); for (; pos.entry < numberOfEntries; pos.entry++) { //if (!searchParams.states[catalog.state(pos)]) // return false; int lim = catalog.isPlural(pos.entry) ? catalog.numberOfPluralForms() : 1; for (pos.form = 0; pos.form < lim; pos.form++) { int sp = 0; int tp = 0; if (!searchParams.sourcePattern.isEmpty()) sp = searchParams.sourcePattern.indexIn(removeAmpFromSource ? catalog.source(pos).remove(QLatin1Char('&')) : catalog.source(pos)); if (!searchParams.targetPattern.isEmpty()) tp = searchParams.targetPattern.indexIn(removeAmpFromTarget ? catalog.target(pos).remove(QLatin1Char('&')) : catalog.target(pos)); //int np=searchParams.notesPattern.indexIn(catalog.notes(pos)); if ((sp != -1) != searchParams.invertSource && (tp != -1) != searchParams.invertTarget) { //TODO handle multiple results in same column //FileSearchResult r; SearchResult r; r.filepath = filePath; r.docPos = pos; if (!searchParams.sourcePattern.isEmpty() && !searchParams.invertSource) r.sourcePositions << StartLen(searchParams.sourcePattern.pos(), searchParams.sourcePattern.matchedLength()); if (!searchParams.targetPattern.isEmpty() && !searchParams.invertTarget) r.targetPositions << StartLen(searchParams.targetPattern.pos(), searchParams.targetPattern.matchedLength()); r.source = catalog.source(pos); r.target = catalog.target(pos); r.state = catalog.state(pos); r.isApproved = catalog.isApproved(pos); //r.activePhase=catalog.activePhase(); if (rules.size()) { QVector positions(2); int matchedQaRule = findMatchingRule(rules, r.source, r.target, positions); if (matchedQaRule == -1) continue; if (positions.at(0).len) r.sourcePositions << positions.at(0); if (positions.at(1).len) r.targetPositions << positions.at(1); } r.sourcePositions.squeeze(); r.targetPositions.squeeze(); //catalogResults< map; for (int i = 0; i < searchResults.count(); ++i) map.insertMulti(searchResults.at(i).filepath, i); foreach (const QString& filepath, map.keys()) { Catalog catalog(QThread::currentThread()); if (catalog.loadFromUrl(filepath, QString()) != 0) continue; foreach (int index, map.values(filepath)) { SearchResult& sr = searchResults[index]; DocPosition docPos = sr.docPos.toDocPosition(); if (catalog.target(docPos) != sr.target) { qCWarning(LOKALIZE_LOG) << "skipping replace because" << catalog.target(docPos) << "!=" << sr.target; continue; } CatalogString s = catalog.targetWithTags(docPos); int pos = replaceWhat.indexIn(s.string); while (pos != -1) { if (!s.string.midRef(pos, replaceWhat.matchedLength()).contains(TAGRANGE_IMAGE_SYMBOL)) { docPos.offset = pos; catalog.targetDelete(docPos, replaceWhat.matchedLength()); catalog.targetInsert(docPos, replaceWith); s.string.replace(pos, replaceWhat.matchedLength(), replaceWith); pos += replaceWith.length(); } else { pos += replaceWhat.matchedLength(); qCWarning(LOKALIZE_LOG) << "skipping replace because matched text contains markup" << s.string; } if (pos > s.string.length() || replaceWhat.pattern().startsWith('^')) break; pos = replaceWhat.indexIn(s.string, pos); } } catalog.save(); } } //BEGIN FileSearchModel FileSearchModel::FileSearchModel(QObject* parent) : QAbstractListModel(parent) { } QVariant FileSearchModel::headerData(int section, Qt::Orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); switch (section) { case FileSearchModel::Source: return i18nc("@title:column Original text", "Source"); case FileSearchModel::Target: return i18nc("@title:column Text in target language", "Target"); //case FileSearchModel::Context: return i18nc("@title:column","Context"); case FileSearchModel::Filepath: return i18nc("@title:column", "File"); case FileSearchModel::TranslationStatus: return i18nc("@title:column", "Translation Status"); } return QVariant(); } void FileSearchModel::appendSearchResults(const SearchResults& results) { beginInsertRows(QModelIndex(), m_searchResults.size(), m_searchResults.size() + results.size() - 1); m_searchResults += results; endInsertRows(); } void FileSearchModel::clear() { beginResetModel(); m_searchResults.clear();; endResetModel(); } QVariant FileSearchModel::data(const QModelIndex& item, int role) const { bool doHtml = (role == FastSizeHintItemDelegate::HtmlDisplayRole); if (doHtml) role = Qt::DisplayRole; if (role == Qt::DisplayRole) { QString result; const SearchResult& sr = m_searchResults.at(item.row()); if (item.column() == Source) result = sr.source; if (item.column() == Target) result = sr.target; if (item.column() == Filepath) result = shorterFilePath(sr.filepath); if (doHtml && item.column() <= FileSearchModel::Target) { if (result.isEmpty()) return result; const QString startBld = QStringLiteral("_ST_"); const QString endBld = QStringLiteral("_END_"); const QString startBldTag = QStringLiteral(""); const QString endBldTag = QStringLiteral(""); if (item.column() == FileSearchModel::Target && !m_replaceWhat.isEmpty()) { result.replace(m_replaceWhat, m_replaceWith); QString escaped = convertToHtml(result, !sr.isApproved); escaped.replace(startBld, startBldTag); escaped.replace(endBld, endBldTag); return escaped; } const QVector& occurrences = item.column() == FileSearchModel::Source ? sr.sourcePositions : sr.targetPositions; int occ = occurrences.count(); while (--occ >= 0) { const StartLen& sl = occurrences.at(occ); result.insert(sl.start + sl.len, endBld); result.insert(sl.start, startBld); } /* !isApproved(sr.state, Project::instance()->local()->role())*/ QString escaped = convertToHtml(result, item.column() == FileSearchModel::Target && !sr.isApproved); escaped.replace(startBld, startBldTag); escaped.replace(endBld, endBldTag); return escaped; } return result; } if (role == Qt::UserRole) { const SearchResult& sr = m_searchResults.at(item.row()); if (item.column() == Filepath) return sr.filepath; } return QVariant(); } void FileSearchModel::setReplacePreview(const QRegExp& s, const QString& r) { m_replaceWhat = s; m_replaceWith = QLatin1String("_ST_") % r % QLatin1String("_END_"); emit dataChanged(index(0, Target), index(rowCount() - 1, Target)); } //END FileSearchModel //BEGIN FileSearchTab FileSearchTab::FileSearchTab(QWidget *parent) : LokalizeSubwindowBase2(parent) // , m_proxyModel(new TMResultsSortFilterProxyModel(this)) , m_model(new FileSearchModel(this)) , m_lastSearchNumber(0) , m_dbusId(-1) { setWindowTitle(i18nc("@title:window", "Search and replace in files")); setAcceptDrops(true); QWidget* w = new QWidget(this); ui_fileSearchOptions = new Ui_FileSearchOptions; ui_fileSearchOptions->setupUi(w); setCentralWidget(w); QShortcut* sh = new QShortcut(Qt::CTRL + Qt::Key_L, this); connect(sh, &QShortcut::activated, ui_fileSearchOptions->querySource, QOverload<>::of(&QLineEdit::setFocus)); setFocusProxy(ui_fileSearchOptions->querySource); sh = new QShortcut(Qt::Key_Escape, this, SLOT(stopSearch()), 0, Qt::WidgetWithChildrenShortcut); QTreeView* view = ui_fileSearchOptions->treeView; QVector singleLineColumns(FileSearchModel::ColumnCount, false); singleLineColumns[FileSearchModel::Filepath] = true; singleLineColumns[FileSearchModel::TranslationStatus] = true; //singleLineColumns[TMDBModel::Context]=true; QVector richTextColumns(FileSearchModel::ColumnCount, false); richTextColumns[FileSearchModel::Source] = true; richTextColumns[FileSearchModel::Target] = true; view->setItemDelegate(new FastSizeHintItemDelegate(this, singleLineColumns, richTextColumns)); connect(m_model, &FileSearchModel::modelReset, (FastSizeHintItemDelegate*)view->itemDelegate(), &FastSizeHintItemDelegate::reset); connect(m_model, &FileSearchModel::dataChanged, (FastSizeHintItemDelegate*)view->itemDelegate(), &FastSizeHintItemDelegate::reset); //connect(m_model,SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),view->itemDelegate(),SLOT(reset())); //connect(m_proxyModel,SIGNAL(layoutChanged()),view->itemDelegate(),SLOT(reset())); //connect(m_proxyModel,SIGNAL(layoutChanged()),this,SLOT(displayTotalResultCount())); view->setContextMenuPolicy(Qt::ActionsContextMenu); QAction* a = new QAction(i18n("Copy source to clipboard"), view); a->setShortcut(Qt::CTRL + Qt::Key_S); a->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(a, &QAction::triggered, this, &FileSearchTab::copySourceToClipboard); view->addAction(a); a = new QAction(i18n("Copy target to clipboard"), view); a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return)); a->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(a, &QAction::triggered, this, &FileSearchTab::copyTargetToClipboard); view->addAction(a); a = new QAction(i18n("Open file"), view); a->setShortcut(QKeySequence(Qt::Key_Return)); a->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(a, &QAction::triggered, this, &FileSearchTab::openFile); connect(view, &QTreeView::activated, this, &FileSearchTab::openFile); view->addAction(a); connect(ui_fileSearchOptions->querySource, &QLineEdit::returnPressed, this, &FileSearchTab::performSearch); connect(ui_fileSearchOptions->queryTarget, &QLineEdit::returnPressed, this, &FileSearchTab::performSearch); connect(ui_fileSearchOptions->doFind, &QPushButton::clicked, this, &FileSearchTab::performSearch); // m_proxyModel->setDynamicSortFilter(true); // m_proxyModel->setSourceModel(m_model); view->setModel(m_model); // view->setModel(m_proxyModel); // view->sortByColumn(FileSearchModel::Filepath,Qt::AscendingOrder); // view->setSortingEnabled(true); // view->setItemDelegate(new FastSizeHintItemDelegate(this)); // connect(m_model,SIGNAL(resultsFetched()),view->itemDelegate(),SLOT(reset())); // connect(m_model,SIGNAL(modelReset()),view->itemDelegate(),SLOT(reset())); // connect(m_proxyModel,SIGNAL(layoutChanged()),view->itemDelegate(),SLOT(reset())); // connect(m_proxyModel,SIGNAL(layoutChanged()),this,SLOT(displayTotalResultCount())); //BEGIN resizeColumnToContents static const int maxInitialWidths[] = {QApplication::desktop()->availableGeometry().width() / 3, QApplication::desktop()->availableGeometry().width() / 3}; int column = sizeof(maxInitialWidths) / sizeof(int); while (--column >= 0) view->setColumnWidth(column, maxInitialWidths[column]); //END resizeColumnToContents int i = 6; while (--i > ID_STATUS_PROGRESS) statusBarItems.insert(i, QString()); #ifndef NOKDE setXMLFile(QStringLiteral("filesearchtabui.rc"), true); dbusObjectPath(); #endif KActionCollection* ac = actionCollection(); KActionCategory* srf = new KActionCategory(i18nc("@title actions category", "Search and replace in files"), ac); m_searchFileListView = new SearchFileListView(this); //m_searchFileListView->hide(); addDockWidget(Qt::RightDockWidgetArea, m_searchFileListView); srf->addAction(QStringLiteral("showfilelist_action"), m_searchFileListView->toggleViewAction()); connect(m_searchFileListView, &SearchFileListView::fileOpenRequested, this, QOverload::of(&FileSearchTab::fileOpenRequested)); m_massReplaceView = new MassReplaceView(this); addDockWidget(Qt::RightDockWidgetArea, m_massReplaceView); srf->addAction(QStringLiteral("showmassreplace_action"), m_massReplaceView->toggleViewAction()); connect(m_massReplaceView, &MassReplaceView::previewRequested, m_model, &FileSearchModel::setReplacePreview); connect(m_massReplaceView, &MassReplaceView::replaceRequested, this, &FileSearchTab::massReplace); //m_massReplaceView->hide(); m_qaView = new QaView(this); m_qaView->hide(); addDockWidget(Qt::RightDockWidgetArea, m_qaView); srf->addAction(QStringLiteral("showqa_action"), m_qaView->toggleViewAction()); connect(m_qaView, &QaView::rulesChanged, this, &FileSearchTab::performSearch); connect(m_qaView->toggleViewAction(), &QAction::toggled, this, &FileSearchTab::performSearch, Qt::QueuedConnection); view->header()->restoreState(readUiState("FileSearchResultsHeaderState")); } FileSearchTab::~FileSearchTab() { stopSearch(); writeUiState("FileSearchResultsHeaderState", ui_fileSearchOptions->treeView->header()->saveState()); #ifndef NOKDE ids.removeAll(m_dbusId); #endif } void FileSearchTab::performSearch() { if (m_searchFileListView->files().isEmpty()) { addFilesToSearch(doScanRecursive(QDir(Project::instance()->poDir()))); if (m_searchFileListView->files().isEmpty()) return; } m_model->clear(); statusBarItems.insert(1, QString()); m_searchFileListView->scrollTo(); m_lastSearchNumber++; SearchParams sp; sp.sourcePattern.setPattern(ui_fileSearchOptions->querySource->text()); sp.targetPattern.setPattern(ui_fileSearchOptions->queryTarget->text()); sp.invertSource = ui_fileSearchOptions->invertSource->isChecked(); sp.invertTarget = ui_fileSearchOptions->invertTarget->isChecked(); QVector rules = m_qaView->isVisible() ? m_qaView->rules() : QVector(); if (sp.isEmpty() && rules.isEmpty()) return; if (!ui_fileSearchOptions->regEx->isChecked()) { sp.sourcePattern.setPatternSyntax(QRegExp::FixedString); sp.targetPattern.setPatternSyntax(QRegExp::FixedString); } /* else { sp.sourcePattern.setMinimal(true); sp.targetPattern.setMinimal(true); } */ stopSearch(); m_massReplaceView->deactivatePreview(); QStringList files = m_searchFileListView->files(); for (int i = 0; i < files.size(); i += 100) { QStringList batch; int lim = qMin(files.size(), i + 100); for (int j = i; j < lim; j++) batch.append(files.at(j)); SearchJob* job = new SearchJob(batch, sp, rules, m_lastSearchNumber); QObject::connect(job, &SearchJob::done, this, &FileSearchTab::searchJobDone); QThreadPool::globalInstance()->start(job); m_runningJobs.append(job); } } void FileSearchTab::stopSearch() { #if QT_VERSION >= 0x050500 int i = m_runningJobs.size(); while (--i >= 0) QThreadPool::globalInstance()->cancel(m_runningJobs.at(i)); #endif m_runningJobs.clear(); } void FileSearchTab::massReplace(const QRegExp &what, const QString& with) { #define BATCH_SIZE 20 SearchResults searchResults = m_model->searchResults(); for (int i = 0; i < searchResults.count(); i += BATCH_SIZE) { int last = qMin(i + BATCH_SIZE, searchResults.count() - 1); QString filepath = searchResults.at(last).filepath; while (last + 1 < searchResults.count() && filepath == searchResults.at(last + 1).filepath) ++last; MassReplaceJob* job = new MassReplaceJob(searchResults.mid(i, last + 1 - i), i, what, with); QObject::connect(job, &MassReplaceJob::done, this, &FileSearchTab::replaceJobDone); QThreadPool::globalInstance()->start(job); m_runningJobs.append(job); } } static void copy(QTreeView* view, int column) { QApplication::clipboard()->setText(view->currentIndex().sibling(view->currentIndex().row(), column).data().toString()); } void FileSearchTab::copySourceToClipboard() { copy(ui_fileSearchOptions->treeView, FileSearchModel::Source); } void FileSearchTab::copyTargetToClipboard() { copy(ui_fileSearchOptions->treeView, FileSearchModel::Target); } void FileSearchTab::openFile() { QModelIndex item = ui_fileSearchOptions->treeView->currentIndex(); SearchResult sr = m_model->searchResult(item); DocPosition docPos = sr.docPos.toDocPosition(); int selection = 0; if (sr.targetPositions.size()) { docPos.offset = sr.targetPositions.first().start; selection = sr.targetPositions.first().len; } qCDebug(LOKALIZE_LOG) << "fileOpenRequest" << docPos.offset << selection; emit fileOpenRequested(sr.filepath, docPos, selection, true); } void FileSearchTab::fileSearchNext() { QModelIndex item = ui_fileSearchOptions->treeView->currentIndex(); int row = item.row(); int rowCount = m_model->rowCount(); if (++row >= rowCount) //ok if row was -1 (no solection) return; ui_fileSearchOptions->treeView->setCurrentIndex(item.sibling(row, item.column())); openFile(); } QStringList scanRecursive(const QList& urls) { QStringList result; int i = urls.size(); while (--i >= 0) { if (urls.at(i).isEmpty() || urls.at(i).path().isEmpty()) //NOTE is this a Qt bug? continue; QString path = urls.at(i).toLocalFile(); if (Catalog::extIsSupported(path)) result.append(path); else result += doScanRecursive(QDir(path)); } return result; } //returns gross number of jobs started static QStringList doScanRecursive(const QDir& dir) { QStringList result; QStringList subDirs(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable)); int i = subDirs.size(); while (--i >= 0) result += doScanRecursive(QDir(dir.filePath(subDirs.at(i)))); QStringList filters = Catalog::supportedExtensions(); i = filters.size(); while (--i >= 0) filters[i].prepend('*'); QStringList files(dir.entryList(filters, QDir::Files | QDir::NoDotAndDotDot | QDir::Readable)); i = files.size(); while (--i >= 0) result.append(dir.filePath(files.at(i))); return result; } void FileSearchTab::dragEnterEvent(QDragEnterEvent* event) { if (dragIsAcceptable(event->mimeData()->urls())) event->acceptProposedAction(); } void FileSearchTab::dropEvent(QDropEvent *event) { event->acceptProposedAction(); addFilesToSearch(scanRecursive(event->mimeData()->urls())); } void FileSearchTab::addFilesToSearch(const QStringList& files) { m_searchFileListView->addFiles(files); performSearch(); } void FileSearchTab::setSourceQuery(const QString& query) { ui_fileSearchOptions->querySource->setText(query); } void FileSearchTab::setTargetQuery(const QString& query) { ui_fileSearchOptions->queryTarget->setText(query); } void FileSearchTab::searchJobDone(SearchJob* j) { j->deleteLater(); if (j->searchNumber != m_lastSearchNumber) return; /* SearchResults searchResults; FileSearchResults::const_iterator i = j->results.constBegin(); while (i != j->results.constEnd()) { foreach(const FileSearchResult& fsr, i.value()) { SearchResult sr(fsr); sr.filepath=i.key(); searchResults<appendSearchResults(searchResults); */ if (j->results.size()) { m_model->appendSearchResults(j->results); m_searchFileListView->scrollTo(j->results.last().filepath); } statusBarItems.insert(1, i18nc("@info:status message entries", "Total: %1", m_model->rowCount())); //ui_fileSearchOptions->treeView->setFocus(); } void FileSearchTab::replaceJobDone(MassReplaceJob* j) { j->deleteLater(); ui_fileSearchOptions->treeView->scrollTo(m_model->index(j->globalPos + j->searchResults.count(), 0)); } //END FileSearchTab //BEGIN MASS REPLACE MassReplaceView::MassReplaceView(QWidget* parent) : QDockWidget(i18nc("@title:window", "Mass replace"), parent) , ui(new Ui_MassReplaceOptions) { QWidget* base = new QWidget(this); setWidget(base); ui->setupUi(base); connect(ui->doPreview, &QPushButton::toggled, this, &MassReplaceView::requestPreview); connect(ui->doReplace, &QPushButton::clicked, this, &MassReplaceView::requestReplace); /* QLabel* rl=new QLabel(i18n("Replace:"), base); QLineEdit* searchEdit=new QLineEdit(base); QHBoxLayout* searchL=new QHBoxLayout(); searchL->addWidget(rl); searchL->addWidget(searchEdit); QLabel* wl=new QLabel(i18n("With:"), base); wl->setAlignment(Qt::AlignRight); wl->setMinimumSize(rl->minimumSizeHint()); QLineEdit* replacementEdit=new QLineEdit(base); QHBoxLayout* replacementL=new QHBoxLayout(); replacementL->addWidget(wl); replacementL->addWidget(replacementEdit); FlowLayout* fl=new FlowLayout(); fl->addItem(searchL); fl->addItem(replacementL); base->setLayout(fl); */ } MassReplaceView::~MassReplaceView() { delete ui; } static QRegExp regExpFromUi(const QString& s, Ui_MassReplaceOptions* ui) { return QRegExp(s, ui->matchCase->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive, ui->useRegExps->isChecked() ? QRegExp::FixedString : QRegExp::RegExp); } void MassReplaceView::requestPreviewUpdate() { QString s = ui->searchText->text(); QString r = ui->replaceText->text(); if (s.length()) ui->doReplace->setEnabled(true); emit previewRequested(regExpFromUi(s, ui), r); } void MassReplaceView::requestPreview(bool enable) { if (enable) { connect(ui->searchText, &QLineEdit::textEdited, this, &MassReplaceView::requestPreviewUpdate); connect(ui->replaceText, &QLineEdit::textEdited, this, &MassReplaceView::requestPreviewUpdate); connect(ui->useRegExps, &QCheckBox::toggled, this, &MassReplaceView::requestPreviewUpdate); connect(ui->matchCase, &QCheckBox::toggled, this, &MassReplaceView::requestPreviewUpdate); requestPreviewUpdate(); } else { disconnect(ui->searchText, &QLineEdit::textEdited, this, &MassReplaceView::requestPreviewUpdate); disconnect(ui->replaceText, &QLineEdit::textEdited, this, &MassReplaceView::requestPreviewUpdate); disconnect(ui->useRegExps, &QCheckBox::toggled, this, &MassReplaceView::requestPreviewUpdate); disconnect(ui->matchCase, &QCheckBox::toggled, this, &MassReplaceView::requestPreviewUpdate); emit previewRequested(QRegExp(), QString()); } } void MassReplaceView::requestReplace() { QString s = ui->searchText->text(); QString r = ui->replaceText->text(); if (s.isEmpty()) return; emit replaceRequested(regExpFromUi(s, ui), r); } void MassReplaceView::deactivatePreview() { ui->doPreview->setChecked(false); ui->doReplace->setEnabled(false); } #ifndef NOKDE #include "filesearchadaptor.h" #include QList FileSearchTab::ids; //BEGIN DBus interface QString FileSearchTab::dbusObjectPath() { QString FILESEARCH_PATH = QStringLiteral("/ThisIsWhatYouWant/FileSearch/"); if (m_dbusId == -1) { new FileSearchAdaptor(this); int i = 0; while (i < ids.size() && i == ids.at(i)) ++i; ids.insert(i, i); m_dbusId = i; QDBusConnection::sessionBus().registerObject(FILESEARCH_PATH + QString::number(m_dbusId), this); } return FILESEARCH_PATH + QString::number(m_dbusId); } bool FileSearchTab::findGuiTextPackage(QString text, QString package) { setSourceQuery(text); performSearch(); return true; } //END DBus interface #endif diff --git a/src/lokalizemainwindow.cpp b/src/lokalizemainwindow.cpp index aafbbeb..9adc114 100644 --- a/src/lokalizemainwindow.cpp +++ b/src/lokalizemainwindow.cpp @@ -1,1064 +1,1064 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2008-2015 by Nick Shaforostoff 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 "lokalizemainwindow.h" #include "lokalize_debug.h" #include "actionproxy.h" #include "editortab.h" #include "projecttab.h" #include "tmtab.h" #include "jobs.h" #include "filesearchtab.h" #include "prefs_lokalize.h" // #define WEBQUERY_ENABLE #include "project.h" #include "projectmodel.h" #include "projectlocal.h" #include "prefs.h" #include "tools/widgettextcaptureconfig.h" #include "multieditoradaptor.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include #include #include #include #include #include LokalizeMainWindow::LokalizeMainWindow() : KXmlGuiWindow() , m_mdiArea(new LokalizeMdiArea) , m_prevSubWindow(0) , m_projectSubWindow(0) , m_translationMemorySubWindow(0) , m_editorActions(new QActionGroup(this)) , m_managerActions(new QActionGroup(this)) , m_spareEditor(new EditorTab(this, false)) , m_multiEditorAdaptor(new MultiEditorAdaptor(m_spareEditor)) , m_projectScriptingPlugin(0) { m_spareEditor->hide(); m_mdiArea->setViewMode(QMdiArea::TabbedView); m_mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder); m_mdiArea->setDocumentMode(true); m_mdiArea->setTabsMovable(true); m_mdiArea->setTabsClosable(true); setCentralWidget(m_mdiArea); connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &LokalizeMainWindow::slotSubWindowActivated); setupActions(); //prevent relayout of dockwidgets m_mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation, true); connect(Project::instance(), QOverload::of(&Project::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen_), Qt::QueuedConnection); connect(Project::instance(), &Project::configChanged, this, &LokalizeMainWindow::projectSettingsChanged); connect(Project::instance(), &Project::closed, this, &LokalizeMainWindow::closeProject); showProjectOverview(); showTranslationMemory(); //fix for #342558 for (int i = ID_STATUS_CURRENT; i <= ID_STATUS_ISFUZZY; i++) { m_statusBarLabels.append(new QLabel()); statusBar()->insertWidget(i, m_statusBarLabels.last(), 2); } setAttribute(Qt::WA_DeleteOnClose, true); if (!qApp->isSessionRestored()) { KConfig config; KConfigGroup stateGroup(&config, "State"); readProperties(stateGroup); } registerDBusAdaptor(); QTimer::singleShot(0, this, &LokalizeMainWindow::initLater); } void LokalizeMainWindow::initLater() { if (!m_prevSubWindow && m_projectSubWindow) slotSubWindowActivated(m_projectSubWindow); if (!Project::instance()->isTmSupported()) { KNotification* notification = new KNotification("NoSqlModulesAvailable", this); notification->setText(i18nc("@info", "No Qt Sql modules were found. Translation memory will not work.")); notification->sendEvent(); } } LokalizeMainWindow::~LokalizeMainWindow() { TM::cancelAllJobs(); KConfig config; KConfigGroup stateGroup(&config, "State"); saveProjectState(stateGroup); m_multiEditorAdaptor->deleteLater(); //Disconnect the signals pointing to this MainWindow object QMdiSubWindow* sw; for (int i = 0; i < m_fileToEditor.values().count(); i++) { sw = m_fileToEditor.values().at(i); disconnect(sw, &QMdiSubWindow::destroyed, this, &LokalizeMainWindow::editorClosed); EditorTab* w = static_cast(sw->widget()); disconnect(w, &EditorTab::aboutToBeClosed, this, &LokalizeMainWindow::resetMultiEditorAdaptor); disconnect(w, QOverload::of(&EditorTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen)); disconnect(w, QOverload::of(&EditorTab::tmLookupRequested), this, QOverload::of(&LokalizeMainWindow::lookupInTranslationMemory)); } qCWarning(LOKALIZE_LOG) << "MainWindow destroyed"; } void LokalizeMainWindow::slotSubWindowActivated(QMdiSubWindow* w) { //QTime aaa;aaa.start(); if (!w || m_prevSubWindow == w) return; w->setUpdatesEnabled(true); //QTBUG-23289 if (m_prevSubWindow) { m_prevSubWindow->setUpdatesEnabled(false); LokalizeSubwindowBase* prevEditor = static_cast(m_prevSubWindow->widget()); prevEditor->hideDocks(); guiFactory()->removeClient(prevEditor->guiClient()); prevEditor->statusBarItems.unregisterStatusBar(); if (qobject_cast(prevEditor)) { EditorTab* w = static_cast(prevEditor); EditorState state = w->state(); m_lastEditorState = state.dockWidgets.toBase64(); } /* QMenu* projectActions=static_cast(factory()->container("project_actions",this)); QList actionz=projectActions->actions(); int i=actionz.size(); //projectActions->menuAction()->setVisible(i); //qCWarning(LOKALIZE_LOG)<<"adding object"<=0) { disconnect(w, SIGNAL(signalNewEntryDisplayed()),actionz.at(i),SLOT(signalNewEntryDisplayed())); //static_cast(actionz.at(i))->addObject(static_cast( editor )->adaptor(),"Editor",Kross::ChildrenInterface::AutoConnectSignals); //static_cast(actionz.at(i))->trigger(); } } */ } LokalizeSubwindowBase* editor = static_cast(w->widget()); editor->reloadUpdatedXML(); if (qobject_cast(editor)) { EditorTab* w = static_cast(editor); w->setProperFocus(); EditorState state = w->state(); m_lastEditorState = state.dockWidgets.toBase64(); m_multiEditorAdaptor->setEditorTab(w); // connect(m_multiEditorAdaptor,SIGNAL(srcFileOpenRequested(QString,int)),this,SLOT(showTM())); /* QMenu* projectActions=static_cast(factory()->container("project_actions",this)); QList actionz=projectActions->actions(); int i=actionz.size(); //projectActions->menuAction()->setVisible(i); //qCWarning(LOKALIZE_LOG)<<"adding object"<=0) { connect(w, SIGNAL(signalNewEntryDisplayed()),actionz.at(i),SLOT(signalNewEntryDisplayed())); //static_cast(actionz.at(i))->addObject(static_cast( editor )->adaptor(),"Editor",Kross::ChildrenInterface::AutoConnectSignals); //static_cast(actionz.at(i))->trigger(); }*/ QTabBar* tw = m_mdiArea->findChild(); if (tw) tw->setTabToolTip(tw->currentIndex(), w->currentFilePath()); emit editorActivated(); } else if (w == m_projectSubWindow && m_projectSubWindow) { QTabBar* tw = m_mdiArea->findChild(); if (tw) tw->setTabToolTip(tw->currentIndex(), Project::instance()->path()); } editor->showDocks(); editor->statusBarItems.registerStatusBar(statusBar(), m_statusBarLabels); guiFactory()->addClient(editor->guiClient()); m_prevSubWindow = w; //qCWarning(LOKALIZE_LOG)<<"finished"< editors = m_mdiArea->subWindowList(); int i = editors.size(); while (--i >= 0) { //if (editors.at(i)==m_projectSubWindow) if (!qobject_cast(editors.at(i)->widget())) continue; if (!static_cast(editors.at(i)->widget())->queryClose()) return false; } bool ok = Project::instance()->queryCloseForAuxiliaryWindows(); if (ok) { QThreadPool::globalInstance()->clear(); Project::instance()->model()->threadPool()->clear(); } return ok; } EditorTab* LokalizeMainWindow::fileOpen_(QString filePath, const bool setAsActive) { return fileOpen(filePath, 0, setAsActive); } EditorTab* LokalizeMainWindow::fileOpen(QString filePath, int entry, bool setAsActive, const QString& mergeFile, bool silent) { if (filePath.length()) { FileToEditor::const_iterator it = m_fileToEditor.constFind(filePath); if (it != m_fileToEditor.constEnd()) { qCWarning(LOKALIZE_LOG) << "already opened:" << filePath; if (QMdiSubWindow* sw = it.value()) { m_mdiArea->setActiveSubWindow(sw); return static_cast(sw->widget()); } } } QByteArray state = m_lastEditorState; EditorTab* w = new EditorTab(this); QMdiSubWindow* sw = 0; //create QMdiSubWindow BEFORE fileOpen() because it causes some strange QMdiArea behaviour otherwise if (filePath.length()) sw = m_mdiArea->addSubWindow(w); QString suggestedDirPath; QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow(); if (activeSW && qobject_cast(activeSW->widget())) { QString fp = static_cast(activeSW->widget())->currentFilePath(); if (fp.length()) suggestedDirPath = QFileInfo(fp).absolutePath(); } if (!w->fileOpen(filePath, suggestedDirPath, silent)) { if (sw) { m_mdiArea->removeSubWindow(sw); sw->deleteLater(); } w->deleteLater(); return 0; } if (!sw) sw = m_mdiArea->addSubWindow(w); w->showMaximized(); sw->showMaximized(); if (!state.isEmpty()) w->restoreState(QByteArray::fromBase64(state)); if (entry/* || offset*/) w->gotoEntry(DocPosition(entry/*, DocPosition::Target, 0, offset*/)); if (setAsActive) { m_toBeActiveSubWindow = sw; QTimer::singleShot(0, this, &LokalizeMainWindow::applyToBeActiveSubWindow); } else { m_mdiArea->setActiveSubWindow(activeSW); sw->setUpdatesEnabled(false); //QTBUG-23289 } if (!mergeFile.isEmpty()) w->mergeOpen(mergeFile); m_openRecentFileAction->addUrl(QUrl::fromLocalFile(filePath));//(w->currentUrl()); connect(sw, &QMdiSubWindow::destroyed, this, &LokalizeMainWindow::editorClosed); connect(w, &EditorTab::aboutToBeClosed, this, &LokalizeMainWindow::resetMultiEditorAdaptor); connect(w, QOverload::of(&EditorTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen)); connect(w, QOverload::of(&EditorTab::tmLookupRequested), this, QOverload::of(&LokalizeMainWindow::lookupInTranslationMemory)); filePath = w->currentFilePath(); QStringRef fnSlashed = filePath.midRef(filePath.lastIndexOf('/')); FileToEditor::const_iterator i = m_fileToEditor.constBegin(); while (i != m_fileToEditor.constEnd()) { if (i.key().endsWith(fnSlashed)) { static_cast(i.value()->widget())->setFullPathShown(true); w->setFullPathShown(true); } ++i; } m_fileToEditor.insert(filePath, sw); sw->setAttribute(Qt::WA_DeleteOnClose, true); emit editorAdded(); return w; } void LokalizeMainWindow::resetMultiEditorAdaptor() { m_multiEditorAdaptor->setEditorTab(m_spareEditor); //it will be reparented shortly if there are other editors } void LokalizeMainWindow::editorClosed(QObject* obj) { m_fileToEditor.remove(m_fileToEditor.key(static_cast(obj))); } EditorTab* LokalizeMainWindow::fileOpen(const QString& filePath, const QString& source, const QString& ctxt, const bool setAsActive) { EditorTab* w = fileOpen(filePath, 0, setAsActive); if (!w) return 0;//TODO message w->findEntryBySourceContext(source, ctxt); return w; } EditorTab* LokalizeMainWindow::fileOpen(const QString& filePath, DocPosition docPos, int selection, const bool setAsActive) { EditorTab* w = fileOpen(filePath, 0, setAsActive); if (!w) return 0;//TODO message w->gotoEntry(docPos, selection); return w; } QObject* LokalizeMainWindow::projectOverview() { if (!m_projectSubWindow) { ProjectTab* w = new ProjectTab(this); m_projectSubWindow = m_mdiArea->addSubWindow(w); w->showMaximized(); m_projectSubWindow->showMaximized(); connect(w, QOverload::of(&ProjectTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen_)); connect(w, QOverload::of(&ProjectTab::projectOpenRequested), this, QOverload::of(&LokalizeMainWindow::openProject)); connect(w, QOverload<>::of(&ProjectTab::projectOpenRequested), this, QOverload<>::of(&LokalizeMainWindow::openProject)); connect(w, QOverload::of(&ProjectTab::searchRequested), this, QOverload::of(&LokalizeMainWindow::addFilesToSearch)); } if (m_mdiArea->currentSubWindow() == m_projectSubWindow) return m_projectSubWindow->widget(); return 0; } void LokalizeMainWindow::showProjectOverview() { projectOverview(); m_mdiArea->setActiveSubWindow(m_projectSubWindow); } TM::TMTab* LokalizeMainWindow::showTM() { if (!Project::instance()->isTmSupported()) { KMessageBox::information(0, i18n("TM facility requires SQLite Qt module."), i18n("No SQLite module available")); return 0; } if (!m_translationMemorySubWindow) { TM::TMTab* w = new TM::TMTab(this); m_translationMemorySubWindow = m_mdiArea->addSubWindow(w); w->showMaximized(); m_translationMemorySubWindow->showMaximized(); connect(w, QOverload::of(&TM::TMTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen)); } m_mdiArea->setActiveSubWindow(m_translationMemorySubWindow); return static_cast(m_translationMemorySubWindow->widget()); } FileSearchTab* LokalizeMainWindow::showFileSearch(bool activate) { EditorTab* precedingEditor = qobject_cast(activeEditor()); if (!m_fileSearchSubWindow) { FileSearchTab* w = new FileSearchTab(this); m_fileSearchSubWindow = m_mdiArea->addSubWindow(w); w->showMaximized(); m_fileSearchSubWindow->showMaximized(); connect(w, QOverload::of(&FileSearchTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen)); connect(w, QOverload::of(&FileSearchTab::fileOpenRequested), this, QOverload::of(&LokalizeMainWindow::fileOpen_)); } if (activate) { m_mdiArea->setActiveSubWindow(m_fileSearchSubWindow); if (precedingEditor) { if (precedingEditor->selectionInSource().length()) static_cast(m_fileSearchSubWindow->widget())->setSourceQuery(precedingEditor->selectionInSource()); if (precedingEditor->selectionInTarget().length()) static_cast(m_fileSearchSubWindow->widget())->setTargetQuery(precedingEditor->selectionInTarget()); } } return static_cast(m_fileSearchSubWindow->widget()); } void LokalizeMainWindow::fileSearchNext() { FileSearchTab* w = showFileSearch(/*activate=*/false); //TODO fill search params based on current selection w->fileSearchNext(); } void LokalizeMainWindow::addFilesToSearch(const QStringList& files) { FileSearchTab* w = showFileSearch(); w->addFilesToSearch(files); } void LokalizeMainWindow::applyToBeActiveSubWindow() { m_mdiArea->setActiveSubWindow(m_toBeActiveSubWindow); } void LokalizeMainWindow::setupActions() { //all operations that can be done after initial setup //(via QTimer::singleShot) go to initLater() QTime aaa; aaa.start(); setStandardToolBarMenuEnabled(true); QAction *action; KActionCollection* ac = actionCollection(); KActionCategory* actionCategory; KActionCategory* file = new KActionCategory(i18nc("@title actions category", "File"), ac); //KActionCategory* config=new KActionCategory(i18nc("@title actions category","Settings"), ac); KActionCategory* glossary = new KActionCategory(i18nc("@title actions category", "Glossary"), ac); KActionCategory* tm = new KActionCategory(i18nc("@title actions category", "Translation Memory"), ac); KActionCategory* proj = new KActionCategory(i18nc("@title actions category", "Project"), ac); actionCategory = file; // File //KStandardAction::open(this, SLOT(fileOpen()), ac); file->addAction(KStandardAction::Open, this, SLOT(fileOpen())); m_openRecentFileAction = KStandardAction::openRecent(this, SLOT(fileOpen(QUrl)), ac); file->addAction(KStandardAction::Quit, qApp, SLOT(closeAllWindows())); //Settings SettingsController* sc = SettingsController::instance(); KStandardAction::preferences(sc, &SettingsController::showSettingsDialog, ac); #define ADD_ACTION_SHORTCUT(_name,_text,_shortcut)\ action = actionCategory->addAction(QStringLiteral(_name));\ ac->setDefaultShortcut(action, QKeySequence( _shortcut ));\ action->setText(_text); //Window //documentBack //KStandardAction::close(m_mdiArea, SLOT(closeActiveSubWindow()), ac); actionCategory = file; ADD_ACTION_SHORTCUT("next-tab", i18n("Next tab"), Qt::CTRL + Qt::Key_Tab) connect(action, &QAction::triggered, m_mdiArea, &LokalizeMdiArea::activateNextSubWindow); ADD_ACTION_SHORTCUT("prev-tab", i18n("Previous tab"), Qt::CTRL + Qt::SHIFT + Qt::Key_Tab) connect(action, &QAction::triggered, m_mdiArea, &LokalizeMdiArea::activatePreviousSubWindow); ADD_ACTION_SHORTCUT("prev-active-tab", i18n("Previously active tab"), Qt::CTRL + Qt::Key_BracketLeft) connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow); //Tools actionCategory = glossary; Project* project = Project::instance(); ADD_ACTION_SHORTCUT("tools_glossary", i18nc("@action:inmenu", "Glossary"), Qt::CTRL + Qt::ALT + Qt::Key_G) connect(action, &QAction::triggered, project, &Project::showGlossary); actionCategory = tm; ADD_ACTION_SHORTCUT("tools_tm", i18nc("@action:inmenu", "Translation memory"), Qt::Key_F7) connect(action, &QAction::triggered, this, &LokalizeMainWindow::showTM); action = tm->addAction("tools_tm_manage", project, SLOT(showTMManager())); action->setText(i18nc("@action:inmenu", "Manage translation memories")); //Project actionCategory = proj; ADD_ACTION_SHORTCUT("project_overview", i18nc("@action:inmenu", "Project overview"), Qt::Key_F4) connect(action, &QAction::triggered, this, &LokalizeMainWindow::showProjectOverview); action = proj->addAction(QStringLiteral("project_configure"), sc, SLOT(projectConfigure())); action->setText(i18nc("@action:inmenu", "Configure project...")); action = proj->addAction(QStringLiteral("project_create"), sc, SLOT(projectCreate())); action->setText(i18nc("@action:inmenu", "Create software translation project...")); action = proj->addAction(QStringLiteral("project_create_odf"), Project::instance(), SLOT(projectOdfCreate())); action->setText(i18nc("@action:inmenu", "Create OpenDocument translation project...")); action = proj->addAction(QStringLiteral("project_open"), this, SLOT(openProject())); action->setText(i18nc("@action:inmenu", "Open project...")); action->setIcon(QIcon::fromTheme("project-open")); m_openRecentProjectAction = new KRecentFilesAction(i18nc("@action:inmenu", "Open recent project"), this); action = proj->addAction(QStringLiteral("project_open_recent"), m_openRecentProjectAction); connect(m_openRecentProjectAction, QOverload::of(&KRecentFilesAction::urlSelected), this, QOverload::of(&LokalizeMainWindow::openProject)); //Qt::QueuedConnection: defer until event loop is running to eliminate QWidgetPrivate::showChildren(bool) startup crash connect(Project::instance(), &Project::loaded, this, &LokalizeMainWindow::projectLoaded, Qt::QueuedConnection); ADD_ACTION_SHORTCUT("tools_filesearch", i18nc("@action:inmenu", "Search and replace in files"), Qt::Key_F6) connect(action, &QAction::triggered, this, &LokalizeMainWindow::showFileSearch); ADD_ACTION_SHORTCUT("tools_filesearch_next", i18nc("@action:inmenu", "Find next in files"), Qt::META + Qt::Key_F3) connect(action, &QAction::triggered, this, &LokalizeMainWindow::fileSearchNext); action = ac->addAction(QStringLiteral("tools_widgettextcapture"), this, SLOT(widgetTextCapture())); action->setText(i18nc("@action:inmenu", "Widget text capture")); setupGUI(Default, QStringLiteral("lokalizemainwindowui.rc")); //qCDebug(LOKALIZE_LOG)<<"action setup finished"<subWindowList()) { if (subwindow == m_translationMemorySubWindow && m_translationMemorySubWindow) subwindow->deleteLater(); else if (qobject_cast(subwindow->widget())) { m_fileToEditor.remove(static_cast(subwindow->widget())->currentFilePath());//safety m_mdiArea->removeSubWindow(subwindow); subwindow->deleteLater(); } } Project::instance()->load(QString()); //TODO scripts return true; } void LokalizeMainWindow::openProject(QString path) { path = SettingsController::instance()->projectOpen(path, false); //dry run if (path.isEmpty()) return; if (closeProject()) SettingsController::instance()->projectOpen(path, true);//really open } void LokalizeMainWindow::saveProperties(KConfigGroup& stateGroup) { saveProjectState(stateGroup); } void LokalizeMainWindow::saveProjectState(KConfigGroup& stateGroup) { QList editors = m_mdiArea->subWindowList(); QStringList files; QStringList mergeFiles; QList dockWidgets; //QList offsets; QList entries; QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow(); int activeSWIndex = -1; int i = editors.size(); while (--i >= 0) { //if (editors.at(i)==m_projectSubWindow) if (!editors.at(i) || !qobject_cast(editors.at(i)->widget())) continue; if (editors.at(i) == activeSW) activeSWIndex = files.size(); EditorState state = static_cast(editors.at(i)->widget())->state(); files.append(state.filePath); mergeFiles.append(state.mergeFilePath); dockWidgets.append(state.dockWidgets.toBase64()); entries.append(state.entry); //offsets.append(state.offset); //qCWarning(LOKALIZE_LOG)<(editors.at(i)->widget() )->state().url; } //if (activeSWIndex==-1 && activeSW==m_projectSubWindow) if (files.size() == 0 && !m_lastEditorState.isEmpty()) dockWidgets.append(m_lastEditorState); // save last state if no editor open if (stateGroup.isValid()) stateGroup.writeEntry("Project", Project::instance()->path()); KConfig config; KConfigGroup projectStateGroup(&config, "State-" + Project::instance()->path()); projectStateGroup.writeEntry("Active", activeSWIndex); projectStateGroup.writeEntry("Files", files); projectStateGroup.writeEntry("MergeFiles", mergeFiles); projectStateGroup.writeEntry("DockWidgets", dockWidgets); //stateGroup.writeEntry("Offsets",offsets); projectStateGroup.writeEntry("Entries", entries); if (m_projectSubWindow) { ProjectTab *w = static_cast(m_projectSubWindow->widget()); if (w->unitsCount() > 0) projectStateGroup.writeEntry("UnitsCount", w->unitsCount()); } QString nameSpecifier = Project::instance()->path(); if (!nameSpecifier.isEmpty()) nameSpecifier.prepend('-'); KConfig* c = stateGroup.isValid() ? stateGroup.config() : &config; m_openRecentFileAction->saveEntries(KConfigGroup(c, "RecentFiles" + nameSpecifier)); m_openRecentProjectAction->saveEntries(KConfigGroup(&config, "RecentProjects")); } void LokalizeMainWindow::readProperties(const KConfigGroup& stateGroup) { KConfig config; const KConfig* c = stateGroup.isValid() ? stateGroup.config() : &config; m_openRecentProjectAction->loadEntries(KConfigGroup(c, "RecentProjects")); QString path = stateGroup.readEntry("Project", QString()); if (Project::instance()->isLoaded() || path.isEmpty()) { //1. we weren't existing yet when the signal was emitted //2. defer until event loop is running to eliminate QWidgetPrivate::showChildren(bool) startup crash QTimer::singleShot(0, this, &LokalizeMainWindow::projectLoaded); } else Project::instance()->load(path); } void LokalizeMainWindow::projectLoaded() { QString projectPath = Project::instance()->path(); qCDebug(LOKALIZE_LOG) << projectPath; m_openRecentProjectAction->addUrl(QUrl::fromLocalFile(projectPath)); KConfig config; QString nameSpecifier = projectPath; if (!nameSpecifier.isEmpty()) nameSpecifier.prepend('-'); m_openRecentFileAction->loadEntries(KConfigGroup(&config, "RecentFiles" + nameSpecifier)); //if project isn't loaded, still restore opened files from "State-" KConfigGroup projectStateGroup(&config, "State-" + projectPath); QStringList files; QStringList mergeFiles; QList dockWidgets; //QList offsets; QList entries; projectOverview(); if (m_projectSubWindow) { ProjectTab *w = static_cast(m_projectSubWindow->widget()); w->setLegacyUnitsCount(projectStateGroup.readEntry("UnitsCount", 0)); QTabBar* tw = m_mdiArea->findChild(); if (tw) for (int i = 0; i < tw->count(); i++) if (tw->tabText(i) == w->windowTitle()) tw->setTabToolTip(i, Project::instance()->path()); } entries = projectStateGroup.readEntry("Entries", entries); if (Settings::self()->restoreRecentFilesOnStartup()) files = projectStateGroup.readEntry("Files", files); mergeFiles = projectStateGroup.readEntry("MergeFiles", mergeFiles); dockWidgets = projectStateGroup.readEntry("DockWidgets", dockWidgets); int i = files.size(); int activeSWIndex = projectStateGroup.readEntry("Active", -1); QStringList failedFiles; while (--i >= 0) { if (i < dockWidgets.size()) m_lastEditorState = dockWidgets.at(i); if (!fileOpen(files.at(i), entries.at(i)/*, offsets.at(i)*//*,&activeSW11*/, activeSWIndex == i, mergeFiles.at(i),/*silent*/true)) failedFiles.append(files.at(i)); } if (!failedFiles.isEmpty()) { qCDebug(LOKALIZE_LOG) << "failedFiles" << failedFiles; // KMessageBox::error(this, i18nc("@info","Error opening the following files:")+ // "
  • "+failedFiles.join("
  • ")+"
  • " ); KNotification* notification = new KNotification("FilesOpenError", this); notification->setText(i18nc("@info", "Error opening the following files:\n\n") % "" % failedFiles.join("
    ") % ""); notification->sendEvent(); } if (files.isEmpty() && dockWidgets.size() > 0) m_lastEditorState = dockWidgets.first(); // restore last state if no editor open if (activeSWIndex == -1) { m_toBeActiveSubWindow = m_projectSubWindow; QTimer::singleShot(0, this, &LokalizeMainWindow::applyToBeActiveSubWindow); } projectSettingsChanged(); QTimer::singleShot(0, this, &LokalizeMainWindow::loadProjectScripts); } void LokalizeMainWindow::projectSettingsChanged() { //TODO show langs setCaption(Project::instance()->projectID()); } void LokalizeMainWindow::widgetTextCapture() { WidgetTextCaptureConfig* w = new WidgetTextCaptureConfig(this); w->show(); } //BEGIN DBus interface //#include "plugin.h" #include "mainwindowadaptor.h" #include #include using namespace Kross; class MyScriptingPlugin: public Kross::ScriptingPlugin { public: MyScriptingPlugin(QObject* lokalize, QObject* editor) : Kross::ScriptingPlugin(lokalize) { addObject(lokalize, "Lokalize"); addObject(Project::instance(), "Project"); addObject(editor, "Editor"); setXMLFile("scriptsui.rc", true); } ~MyScriptingPlugin() {} }; #define PROJECTRCFILE "scripts.rc" #define PROJECTRCFILEDIR Project::instance()->projectDir()+"/lokalize-scripts" #define PROJECTRCFILEPATH Project::instance()->projectDir()+"/lokalize-scripts" "/" PROJECTRCFILE //TODO be lazy creating scripts dir ProjectScriptingPlugin::ProjectScriptingPlugin(QObject* lokalize, QObject* editor) : Kross::ScriptingPlugin(Project::instance()->kind(), PROJECTRCFILEPATH, Project::instance()->kind(), lokalize) { if (Project::instance()->projectDir().isEmpty()) return; QString filepath = PROJECTRCFILEPATH; // Remove directory "scripts.rc" if it is empty. It could be // mistakenly created by Lokalize 15.04.x. if (QFileInfo(filepath).isDir() && !QDir().rmdir(filepath)) { qCCritical(LOKALIZE_LOG) << "Failed to remove directory" << filepath << "to create scripting configuration file with at the same path. " << "The directory may be not empty."; return; } if (!QFile::exists(filepath)) { QDir().mkdir(QFileInfo(filepath).dir().path()); QFile f(filepath); if (!f.open(QIODevice::WriteOnly)) return; QTextStream out(&f); out << ""; f.close(); } //qCWarning(LOKALIZE_LOG)<collection(Project::instance()->kind()); if (!collection) return; foreach (const QString &collectionname, collection->collections()) { Kross::ActionCollection* c = collection->collection(collectionname); if (!c->isEnabled()) continue; foreach (Kross::Action* action, c->actions()) { if (action->property("autorun").toBool()) action->trigger(); if (action->property("first-run").toBool() && Project::local()->firstRun()) action->trigger(); } } } ProjectScriptingPlugin::~ProjectScriptingPlugin() { Kross::ActionCollection* collection = Kross::Manager::self().actionCollection()->collection(Project::instance()->kind()); if (!collection) return; QString scriptsrc = PROJECTRCFILE; QDir rcdir(PROJECTRCFILEDIR); qCWarning(LOKALIZE_LOG) << rcdir.entryList(QStringList("*.rc"), QDir::Files); foreach (const QString& rc, QDir(PROJECTRCFILEDIR).entryList(QStringList("*.rc"), QDir::Files)) if (rc != scriptsrc) qCWarning(LOKALIZE_LOG) << rc << collection->readXmlFile(rcdir.absoluteFilePath(rc)); } /* void LokalizeMainWindow::checkForProjectAlreadyOpened() { QStringList services=QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); int i=services.size(); while(--i>=0) { if (services.at(i).startsWith("org.kde.lokalize")) //QDBusReply QDBusConnectionInterface::servicePid ( const QString & serviceName ) const; QDBusConnection::callWithCallback(QDBusMessage::createMethodCall(services.at(i),"/ThisIsWhatYouWant","org.kde.Lokalize.MainWindow","currentProject"), this, SLOT(), const char * errorMethod); } } */ void LokalizeMainWindow::registerDBusAdaptor() { new MainWindowAdaptor(this); QDBusConnection::sessionBus().registerObject(QLatin1String("/ThisIsWhatYouWant"), this); //qCWarning(LOKALIZE_LOG)<registeredServiceNames().value(); #ifndef Q_OS_MAC //TODO really fix!!! guiFactory()->addClient(new MyScriptingPlugin(this, m_multiEditorAdaptor)); #endif //QMenu* projectActions=static_cast(factory()->container("project_actions",this)); /* KActionCollection* actionCollection = mWindow->actionCollection(); actionCollection->action("file_save")->setEnabled(canSave); actionCollection->action("file_save_as")->setEnabled(canSave); */ } void LokalizeMainWindow::loadProjectScripts() { if (m_projectScriptingPlugin) { guiFactory()->removeClient(m_projectScriptingPlugin); delete m_projectScriptingPlugin; } //a HACK to get new .rc files shown w/o requiring a restart m_projectScriptingPlugin = new ProjectScriptingPlugin(this, m_multiEditorAdaptor); //guiFactory()->addClient(m_projectScriptingPlugin); //guiFactory()->removeClient(m_projectScriptingPlugin); delete m_projectScriptingPlugin; m_projectScriptingPlugin = new ProjectScriptingPlugin(this, m_multiEditorAdaptor); guiFactory()->addClient(m_projectScriptingPlugin); } int LokalizeMainWindow::lookupInTranslationMemory(DocPosition::Part part, const QString& text) { TM::TMTab* w = showTM(); if (!text.isEmpty()) w->lookup(part == DocPosition::Source ? text : QString(), part == DocPosition::Target ? text : QString()); return w->dbusId(); } int LokalizeMainWindow::lookupInTranslationMemory(const QString& source, const QString& target) { TM::TMTab* w = showTM(); w->lookup(source, target); return w->dbusId(); } int LokalizeMainWindow::showTranslationMemory() { /*activateWindow(); raise(); show();*/ return lookupInTranslationMemory(DocPosition::UndefPart, QString()); } int LokalizeMainWindow::openFileInEditorAt(const QString& path, const QString& source, const QString& ctxt) { EditorTab* w = fileOpen(path, source, ctxt, true); if (!w) return -1; return w->dbusId(); } int LokalizeMainWindow::openFileInEditor(const QString& path) { return openFileInEditorAt(path, QString(), QString()); } QObject* LokalizeMainWindow::activeEditor() { //QList editors=m_mdiArea->subWindowList(); QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow(); if (!activeSW || !qobject_cast(activeSW->widget())) return 0; return activeSW->widget(); } QObject* LokalizeMainWindow::editorForFile(const QString& path) { FileToEditor::const_iterator it = m_fileToEditor.constFind(QFileInfo(path).canonicalFilePath()); if (it == m_fileToEditor.constEnd()) return 0; QMdiSubWindow* w = it.value(); if (!w) return 0; return static_cast(w->widget()); } int LokalizeMainWindow::editorIndexForFile(const QString& path) { EditorTab* editor = static_cast(editorForFile(path)); if (!editor) return -1; return editor->dbusId(); } QString LokalizeMainWindow::currentProject() { return Project::instance()->path(); } #ifdef Q_OS_WIN #include int LokalizeMainWindow::pid() { return GetCurrentProcessId(); } #else #include int LokalizeMainWindow::pid() { return getpid(); } #endif QString LokalizeMainWindow::dbusName() { return QStringLiteral("org.kde.lokalize-%1").arg(pid()); } void LokalizeMainWindow::busyCursor(bool busy) { busy ? QApplication::setOverrideCursor(Qt::WaitCursor) : QApplication::restoreOverrideCursor(); } void LokalizeMdiArea::activateNextSubWindow() { this->setActivationOrder((QMdiArea::WindowOrder)Settings::tabSwitch()); this->QMdiArea::activateNextSubWindow(); this->setActivationOrder(QMdiArea::ActivationHistoryOrder); } void LokalizeMdiArea::activatePreviousSubWindow() { this->setActivationOrder((QMdiArea::WindowOrder)Settings::tabSwitch()); this->QMdiArea::activatePreviousSubWindow(); this->setActivationOrder(QMdiArea::ActivationHistoryOrder); } MultiEditorAdaptor::MultiEditorAdaptor(EditorTab *parent) : EditorAdaptor(parent) { setObjectName(QStringLiteral("MultiEditorAdaptor")); connect(parent, QOverload::of(&EditorTab::destroyed), this, QOverload::of(&MultiEditorAdaptor::handleParentDestroy)); } void MultiEditorAdaptor::setEditorTab(EditorTab* e) { if (parent()) disconnect(parent(), QOverload::of(&EditorTab::destroyed), this, QOverload::of(&MultiEditorAdaptor::handleParentDestroy)); if (e) connect(e, QOverload::of(&EditorTab::destroyed), this, QOverload::of(&MultiEditorAdaptor::handleParentDestroy)); setParent(e); setAutoRelaySignals(false); setAutoRelaySignals(true); } void MultiEditorAdaptor::handleParentDestroy(QObject* p) { Q_UNUSED(p); setParent(0); } //END DBus interface DelayedFileOpener::DelayedFileOpener(const QVector& urls, LokalizeMainWindow* lmw) : QObject() , m_urls(urls) , m_lmw(lmw) { //do the work just after project loading is finished //(i.e. all the files from previous project session are loaded) QTimer::singleShot(1, this, &DelayedFileOpener::doOpen); } void DelayedFileOpener::doOpen() { int lastIndex = m_urls.count() - 1; for (int i = 0; i <= lastIndex; i++) m_lmw->fileOpen(m_urls.at(i), 0, /*set as active*/i == lastIndex); deleteLater(); } #include "moc_lokalizesubwindowbase.cpp" #include "moc_multieditoradaptor.cpp" diff --git a/src/msgctxtview.cpp b/src/msgctxtview.cpp index 1f3a712..7063a3a 100644 --- a/src/msgctxtview.cpp +++ b/src/msgctxtview.cpp @@ -1,331 +1,331 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2014 by Nick Shaforostoff 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 "msgctxtview.h" #include "noteeditor.h" #include "catalog.h" #include "cmd.h" #include "prefs_lokalize.h" #include "project.h" #include "lokalize_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include MsgCtxtView::MsgCtxtView(QWidget* parent, Catalog* catalog) : QDockWidget(i18nc("@title toolview name", "Unit metadata"), parent) , m_browser(new QTextBrowser(this)) , m_editor(0) , m_catalog(catalog) , m_selection(0) , m_offset(0) , m_hasInfo(false) , m_hasErrorNotes(false) , m_pologyProcessInProgress(0) , m_pologyStartedReceivingOutput(false) { setObjectName(QStringLiteral("msgCtxtView")); QWidget* main = new QWidget(this); setWidget(main); m_stackedLayout = new QStackedLayout(main); m_stackedLayout->addWidget(m_browser); m_browser->viewport()->setBackgroundRole(QPalette::Background); m_browser->setOpenLinks(false); connect(m_browser, &QTextBrowser::anchorClicked, this, &MsgCtxtView::anchorClicked); } MsgCtxtView::~MsgCtxtView() { } const QString MsgCtxtView::BR = "
    "; void MsgCtxtView::cleanup() { m_unfinishedNotes.clear(); m_tempNotes.clear(); } void MsgCtxtView::gotoEntry(const DocPosition& pos, int selection) { m_entry = DocPos(pos); m_selection = selection; m_offset = pos.offset; QTimer::singleShot(0, this, &MsgCtxtView::process); QTimer::singleShot(0, this, &MsgCtxtView::pology); } void MsgCtxtView::process() { if (m_catalog->numberOfEntries() <= m_entry.entry) return;//because of Qt::QueuedConnection if (m_stackedLayout->currentIndex()) m_unfinishedNotes[m_prevEntry] = qMakePair(m_editor->note(), m_editor->noteIndex()); if (m_unfinishedNotes.contains(m_entry)) { addNoteUI(); m_editor->setNote(m_unfinishedNotes.value(m_entry).first, m_unfinishedNotes.value(m_entry).second); } else m_stackedLayout->setCurrentIndex(0); m_prevEntry = m_entry; m_browser->clear(); if (m_tempNotes.contains(m_entry.entry)) { QString html = i18nc("@info notes to translation unit which expire when the catalog is closed", "Temporary notes:"); html += MsgCtxtView::BR; foreach (const QString& note, m_tempNotes.values(m_entry.entry)) html += note.toHtmlEscaped() + MsgCtxtView::BR; html += MsgCtxtView::BR; m_browser->insertHtml(html.replace('\n', MsgCtxtView::BR)); } QString phaseName = m_catalog->phase(m_entry.toDocPosition()); if (!phaseName.isEmpty()) { Phase phase = m_catalog->phase(phaseName); QString html = i18nc("@info translation unit metadata", "Phase:
    "); if (phase.date.isValid()) html += QString(QStringLiteral("%1: ")).arg(phase.date.toString(Qt::ISODate)); html += phase.process.toHtmlEscaped(); if (!phase.contact.isEmpty()) html += QString(QStringLiteral(" (%1)")).arg(phase.contact.toHtmlEscaped()); m_browser->insertHtml(html + MsgCtxtView::BR); } const QVector notes = m_catalog->notes(m_entry.toDocPosition()); m_hasErrorNotes = false; foreach (const Note& note, notes) m_hasErrorNotes = m_hasErrorNotes || note.content.contains(QLatin1String("[ERROR]")); int realOffset = displayNotes(m_browser, m_catalog->notes(m_entry.toDocPosition()), m_entry.form, m_catalog->capabilities()&MultipleNotes); QString html; foreach (const Note& note, m_catalog->developerNotes(m_entry.toDocPosition())) { html += MsgCtxtView::BR + escapeWithLinks(note.content); } QStringList sourceFiles = m_catalog->sourceFiles(m_entry.toDocPosition()); if (!sourceFiles.isEmpty()) { html += i18nc("@info PO comment parsing", "
    Files:
    "); foreach (const QString &sourceFile, sourceFiles) html += QString(QStringLiteral("%2
    ")).arg(sourceFile, sourceFile); html.chop(6); } QString msgctxt = m_catalog->context(m_entry.entry).first(); if (!msgctxt.isEmpty()) html += i18nc("@info PO comment parsing", "
    Context:
    ") + msgctxt.toHtmlEscaped(); QTextCursor t = m_browser->textCursor(); t.movePosition(QTextCursor::End); m_browser->setTextCursor(t); m_browser->insertHtml(html); t.movePosition(QTextCursor::Start); t.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, realOffset + m_offset); t.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m_selection); m_browser->setTextCursor(t); } void MsgCtxtView::pology() { - if (Project::instance()->local()->pologyEnabled() && m_pologyProcessInProgress == 0) { - QString command = Project::instance()->local()->pologyCommandEntry(); - command = command.replace(QStringLiteral("%u"), QString::number(m_entry.entry + 1)).replace(QStringLiteral("%f"), m_catalog->url()).replace(QStringLiteral("\n"), QStringLiteral(" ")); + if (Settings::self()->pologyEnabled() && m_pologyProcessInProgress == 0) { + QString command = Settings::self()->pologyCommandEntry(); + command = command.replace(QStringLiteral("%u"), QString::number(m_entry.entry + 1)).replace(QStringLiteral("%f"), QStringLiteral("\"") + m_catalog->url() + QStringLiteral("\"")).replace(QStringLiteral("\n"), QStringLiteral(" ")); m_pologyProcess = new KProcess; m_pologyProcess->setShellCommand(command); m_pologyProcess->setOutputChannelMode(KProcess::SeparateChannels); m_pologyStartedReceivingOutput = false; connect(m_pologyProcess, &KProcess::readyReadStandardOutput, this, &MsgCtxtView::pologyReceivedStandardOutput); connect(m_pologyProcess, &KProcess::readyReadStandardError, this, &MsgCtxtView::pologyReceivedStandardError); connect(m_pologyProcess, QOverload::of(&KProcess::finished), this, &MsgCtxtView::pologyHasFinished); m_pologyData = QStringLiteral("[pology] "); m_pologyProcessInProgress = m_entry.entry + 1; m_pologyProcess->start(); - } else if (Project::instance()->local()->pologyEnabled() && m_pologyProcessInProgress > 0) { + } else if (Settings::self()->pologyEnabled() && m_pologyProcessInProgress > 0) { QTimer::singleShot(1000, this, &MsgCtxtView::pology); } } void MsgCtxtView::pologyReceivedStandardOutput() { if (m_pologyProcessInProgress == m_entry.entry + 1) { if (!m_pologyStartedReceivingOutput) { m_pologyStartedReceivingOutput = true; } const QString grossPologyOutput = m_pologyProcess->readAllStandardOutput(); const QStringList pologyTmpLines = grossPologyOutput.split('\n', QString::SkipEmptyParts); foreach (const QString pologyTmp, pologyTmpLines) { if (pologyTmp.startsWith(QStringLiteral("[note]"))) m_pologyData += pologyTmp; } } } void MsgCtxtView::pologyReceivedStandardError() { if (m_pologyProcessInProgress == m_entry.entry + 1) { if (!m_pologyStartedReceivingOutput) { m_pologyStartedReceivingOutput = true; } m_pologyData += m_pologyProcess->readAllStandardError().replace('\n', MsgCtxtView::BR); } } void MsgCtxtView::pologyHasFinished() { if (m_pologyProcessInProgress == m_entry.entry + 1) { if (!m_pologyStartedReceivingOutput) { m_pologyStartedReceivingOutput = true; const QString grossPologyOutput = m_pologyProcess->readAllStandardOutput(); const QStringList pologyTmpLines = grossPologyOutput.split('\n', QString::SkipEmptyParts); if (pologyTmpLines.count() == 0) { m_pologyData += i18nc("@info The pology command didn't return anything", "(empty)"); } else { foreach (const QString pologyTmp, pologyTmpLines) { if (pologyTmp.startsWith(QStringLiteral("[note]"))) m_pologyData += pologyTmp; } } } if (!m_tempNotes.value(m_entry.entry).startsWith(QStringLiteral("Failed rules:"))) { //This was not opened by pology //Delete the previous pology notes if (m_tempNotes.value(m_entry.entry).startsWith(QStringLiteral("[pology] "))) { m_tempNotes.remove(m_entry.entry); } addTemporaryEntryNote(m_entry.entry, m_pologyData); } } m_pologyProcess->deleteLater(); m_pologyProcessInProgress = 0; } void MsgCtxtView::addNoteUI() { anchorClicked(QUrl(QStringLiteral("note:/add"))); } void MsgCtxtView::anchorClicked(const QUrl& link) { QString path = link.path().mid(1); // minus '/' if (link.scheme() == QLatin1String("note")) { int capabilities = m_catalog->capabilities(); if (!m_editor) { m_editor = new NoteEditor(this); m_stackedLayout->addWidget(m_editor); connect(m_editor, &NoteEditor::accepted, this, &MsgCtxtView::noteEditAccepted); connect(m_editor, &NoteEditor::rejected, this, &MsgCtxtView::noteEditRejected); } m_editor->setNoteAuthors(m_catalog->noteAuthors()); QVector notes = m_catalog->notes(m_entry.toDocPosition()); int noteIndex = -1; //means add new note Note note; if (!path.endsWith(QLatin1String("add"))) { noteIndex = path.toInt(); note = notes.at(noteIndex); } else if (!(capabilities & MultipleNotes) && notes.size()) { noteIndex = 0; //so we don't overwrite the only possible note note = notes.first(); } m_editor->setNote(note, noteIndex); m_editor->setFromFieldVisible(capabilities & KeepsNoteAuthors); m_stackedLayout->setCurrentIndex(1); } else if (link.scheme() == QLatin1String("src")) { int pos = path.lastIndexOf(':'); emit srcFileOpenRequested(path.left(pos), path.midRef(pos + 1).toInt()); } else if (link.scheme().contains(QLatin1String("tp"))) QDesktopServices::openUrl(link); } void MsgCtxtView::noteEditAccepted() { DocPosition pos = m_entry.toDocPosition(); pos.form = m_editor->noteIndex(); m_catalog->push(new SetNoteCmd(m_catalog, pos, m_editor->note())); m_prevEntry.entry = -1; process(); //m_stackedLayout->setCurrentIndex(0); //m_unfinishedNotes.remove(m_entry); noteEditRejected(); } void MsgCtxtView::noteEditRejected() { m_stackedLayout->setCurrentIndex(0); m_unfinishedNotes.remove(m_entry); emit escaped(); } void MsgCtxtView::addNote(DocPosition p, const QString& text) { p.form = -1; m_catalog->push(new SetNoteCmd(m_catalog, p, Note(text))); if (m_entry.entry == p.entry) { m_prevEntry.entry = -1; process(); } } void MsgCtxtView::addTemporaryEntryNote(int entry, const QString& text) { m_tempNotes.insertMulti(entry, text); m_prevEntry.entry = -1; process(); } void MsgCtxtView::removeErrorNotes() { if (!m_hasErrorNotes) return; DocPosition p = m_entry.toDocPosition(); const QVector notes = m_catalog->notes(p); p.form = notes.size(); while (--(p.form) >= 0) { if (notes.at(p.form).content.contains(QLatin1String("[ERROR]"))) m_catalog->push(new SetNoteCmd(m_catalog, p, Note())); } m_prevEntry.entry = -1; process(); } diff --git a/src/prefs/lokalize.kcfg b/src/prefs/lokalize.kcfg index ec3768e..9cb64b0 100644 --- a/src/prefs/lokalize.kcfg +++ b/src/prefs/lokalize.kcfg @@ -1,135 +1,146 @@ QLocale QFontDatabase kemailsettings.h kde-i18n-lists.h KEMailSettings().getSetting(KEMailSettings::RealName) KEMailSettings().getSetting(KEMailSettings::RealName) KEMailSettings().getSetting(KEMailSettings::EmailAddress) QLocale::system().name() getMailingList() #99CCFF #FF9999 true QFontDatabase::systemFont(QFontDatabase::GeneralFont) false 0 true true true false false 4 false false 7 0 true false false + + + false + + + posieve -u %u check-rules %f + + + posieve -s lokalize check-rules %f + + diff --git a/src/prefs/prefs.cpp b/src/prefs/prefs.cpp index 9eee7b2..a24cfd5 100644 --- a/src/prefs/prefs.cpp +++ b/src/prefs/prefs.cpp @@ -1,394 +1,395 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2011 by Nick Shaforostoff 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 "prefs.h" #include "lokalize_debug.h" #include "prefs_lokalize.h" #include "project.h" #include "projectlocal.h" #include "projectmodel.h" #include "languagelistmodel.h" #include "dbfilesmodel.h" #include "ui_prefs_identity.h" #include "ui_prefs_editor.h" #include "ui_prefs_general.h" #include "ui_prefs_appearance.h" +#include "ui_prefs_pology.h" #include "ui_prefs_tm.h" #include "ui_prefs_projectmain.h" #include "ui_prefs_project_advanced.h" #include "ui_prefs_project_local.h" -#include "ui_prefs_project_pology.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include SettingsController* SettingsController::_instance = 0; void SettingsController::cleanupSettingsController() { delete SettingsController::_instance; SettingsController::_instance = 0; } SettingsController* SettingsController::instance() { if (_instance == 0) { _instance = new SettingsController; qAddPostRoutine(SettingsController::cleanupSettingsController); } return _instance; } SettingsController::SettingsController() : QObject(Project::instance()) , dirty(false) , m_projectActionsView(0) , m_mainWindowPtr(0) {} SettingsController::~SettingsController() {} void SettingsController::showSettingsDialog() { if (KConfigDialog::showDialog("lokalize_settings")) return; KConfigDialog *dialog = new KConfigDialog(m_mainWindowPtr, "lokalize_settings", Settings::self()); dialog->setFaceType(KPageDialog::List); // Identity QWidget *w = new QWidget(dialog); Ui_prefs_identity ui_prefs_identity; ui_prefs_identity.setupUi(w); KConfigGroup grp = Settings::self()->config()->group("Identity"); ui_prefs_identity.DefaultLangCode->setModel(LanguageListModel::instance()->sortModel()); ui_prefs_identity.DefaultLangCode->setCurrentIndex(LanguageListModel::instance()->sortModelRowForLangCode(grp.readEntry("DefaultLangCode", QLocale::system().name()))); connect(ui_prefs_identity.DefaultLangCode, QOverload::of(&KComboBox::activated), ui_prefs_identity.kcfg_DefaultLangCode, &LangCodeSaver::setLangCode); ui_prefs_identity.kcfg_DefaultLangCode->hide(); dialog->addPage(w, i18nc("@title:tab", "Identity"), "preferences-desktop-user"); //General w = new QWidget(dialog); Ui_prefs_general ui_prefs_general; ui_prefs_general.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "General"), "preferences-system-windows"); //Editor w = new QWidget(dialog); Ui_prefs_editor ui_prefs_editor; ui_prefs_editor.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "Editing"), "accessories-text-editor"); //Font w = new QWidget(dialog); Ui_prefs_appearance ui_prefs_appearance; ui_prefs_appearance.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "Appearance"), "preferences-desktop-font"); //TM w = new QWidget(dialog); Ui_prefs_tm ui_prefs_tm; ui_prefs_tm.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "Translation Memory"), "configure"); +//Pology + w = new QWidget(dialog); + Ui_prefs_pology ui_prefs_pology; + ui_prefs_pology.setupUi(w); + dialog->addPage(w, i18nc("@title:tab", "Pology"), "preferences-desktop-filetype-association"); + connect(dialog, &KConfigDialog::settingsChanged, this, &SettingsController::generalSettingsChanged); //Spellcheck #if 0 w = new Sonnet::ConfigWidget(Settings::self()->config(), dialog); w->setParent(this); dialog->addPage(w, i18nc("@title:tab", "Spellcheck"), "spellcheck_setting"); connect(dialog, SIGNAL(okClicked()), w, SLOT(save())); connect(dialog, SIGNAL(applyClicked()), w, SLOT(save())); connect(dialog, SIGNAL(defaultClicked()), w, SLOT(slotDefault())); #endif //connect(dialog,SIGNAL(settingsChanged(const QString&)),m_view, SLOT(settingsChanged())); dialog->show(); // dialog->addPage(new General(0, "General"), i18n("General") ); // dialog->addPage(new Appearance(0, "Style"), i18n("Appearance") ); // connect(dialog, SIGNAL(settingsChanged(const QString&)), mainWidget, SLOT(loadSettings())); // connect(dialog, SIGNAL(settingsChanged(const QString&)), this, SLOT(loadSettings())); } ScriptsView::ScriptsView(QWidget* parent): Kross::ActionCollectionView(parent) { setAcceptDrops(true); } void ScriptsView::dragEnterEvent(QDragEnterEvent* event) { if (!event->mimeData()->urls().isEmpty() && event->mimeData()->urls().first().path().endsWith(QLatin1String(".rc"))) event->accept(); } void ScriptsView::dropEvent(QDropEvent* event) { Kross::ActionCollectionModel* scriptsModel = static_cast(model()); foreach (const QUrl& url, event->mimeData()->urls()) if (url.path().endsWith(".rc")) scriptsModel->rootCollection()->readXmlFile(url.path()); } bool SettingsController::ensureProjectIsLoaded() { if (Project::instance()->isLoaded()) return true; int answer = KMessageBox::questionYesNoCancel(m_mainWindowPtr, i18n("You have accessed a feature that requires a project to be loaded. Do you want to create a new project or open an existing project?"), QString(), KGuiItem(i18nc("@action", "New"), QIcon::fromTheme("document-new")), KGuiItem(i18nc("@action", "Open"), QIcon::fromTheme("project-open")) ); if (answer == KMessageBox::Yes) return projectCreate(); if (answer == KMessageBox::No) return !projectOpen().isEmpty(); return false; } QString SettingsController::projectOpen(QString path, bool doOpen) { if (path.isEmpty()) { //Project::instance()->model()->weaver()->suspend(); //KDE5PORT mutex if needed path = QFileDialog::getOpenFileName(m_mainWindowPtr, QString(), QDir::homePath()/*_catalog->url().directory()*/, i18n("Lokalize translation project (*.lokalize)")/*"text/x-lokalize-project"*/); //Project::instance()->model()->weaver()->resume(); } if (!path.isEmpty() && doOpen) Project::instance()->load(path); return path; } bool SettingsController::projectCreate() { //Project::instance()->model()->weaver()->suspend(); //KDE5PORT mutex if needed QString desirablePath = Project::instance()->desirablePath(); if (desirablePath.isEmpty()) desirablePath = QDir::homePath() + "/index.lokalize"; QString path = QFileDialog::getSaveFileName(m_mainWindowPtr, i18nc("@window:title", "Select folder with Gettext .po files to translate"), desirablePath, i18n("Lokalize translation project (*.lokalize)") /*"text/x-lokalize-project"*/); //Project::instance()->model()->weaver()->resume(); if (path.isEmpty()) return false; if (m_projectActionsView && m_projectActionsView->model()) { //ActionCollectionModel is known to be have bad for the usecase of reinitializing krossplugin m_projectActionsView->model()->deleteLater(); m_projectActionsView->setModel(0); } //TODO ask-n-save QDir projectFolder = QFileInfo(path).absoluteDir(); QString projectId = projectFolder.dirName(); if (projectFolder.cdUp()) projectId = projectFolder.dirName() % '-' % projectId;; Project::instance()->load(path, QString(), projectId); //Project::instance()->setDefaults(); //NOTE will this be an obstacle? //Project::instance()->setProjectID(); QTimer::singleShot(500, this, &SettingsController::projectConfigure); return true; } void SettingsController::projectConfigure() { if (Project::instance()->path().isEmpty()) { KMessageBox::error(mainWindowPtr(), i18n("Create software or OpenDocument translation project first.")); return; } if (KConfigDialog::showDialog("project_settings")) { if (!m_projectActionsView->model()) m_projectActionsView->setModel(new Kross::ActionCollectionModel(m_projectActionsView, Kross::Manager::self().actionCollection()->collection(Project::instance()->kind()))); return; } KConfigDialog *dialog = new KConfigDialog(m_mainWindowPtr, "project_settings", Project::instance()); dialog->setFaceType(KPageDialog::List); // Main QWidget *w = new QWidget(dialog); Ui_prefs_projectmain ui_prefs_projectmain; ui_prefs_projectmain.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "General"), "preferences-desktop-locale"); ui_prefs_projectmain.kcfg_LangCode->hide(); ui_prefs_projectmain.kcfg_PoBaseDir->hide(); ui_prefs_projectmain.kcfg_GlossaryTbx->hide(); Project& p = *(Project::instance()); ui_prefs_projectmain.LangCode->setModel(LanguageListModel::instance()->sortModel()); ui_prefs_projectmain.LangCode->setCurrentIndex(LanguageListModel::instance()->sortModelRowForLangCode(p.langCode())); connect(ui_prefs_projectmain.LangCode, QOverload::of(&KComboBox::activated), ui_prefs_projectmain.kcfg_LangCode, &LangCodeSaver::setLangCode); ui_prefs_projectmain.poBaseDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); ui_prefs_projectmain.glossaryTbx->setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly); ui_prefs_projectmain.glossaryTbx->setFilter("*.tbx\n*.xml"); connect(ui_prefs_projectmain.poBaseDir, &KUrlRequester::textChanged, ui_prefs_projectmain.kcfg_PoBaseDir, &RelPathSaver::setText); connect(ui_prefs_projectmain.glossaryTbx, &KUrlRequester::textChanged, ui_prefs_projectmain.kcfg_GlossaryTbx, &RelPathSaver::setText); ui_prefs_projectmain.poBaseDir->setUrl(QUrl::fromLocalFile(p.poDir())); ui_prefs_projectmain.glossaryTbx->setUrl(QUrl::fromLocalFile(p.glossaryPath())); // RegExps w = new QWidget(dialog); Ui_project_advanced ui_project_advanced; ui_project_advanced.setupUi(w); ui_project_advanced.kcfg_PotBaseDir->hide(); ui_project_advanced.kcfg_BranchDir->hide(); ui_project_advanced.kcfg_AltDir->hide(); ui_project_advanced.potBaseDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); ui_project_advanced.branchDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); ui_project_advanced.altDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); connect(ui_project_advanced.potBaseDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_PotBaseDir, &RelPathSaver::setText); connect(ui_project_advanced.branchDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_BranchDir, &RelPathSaver::setText); connect(ui_project_advanced.altDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_AltDir, &RelPathSaver::setText); ui_project_advanced.potBaseDir->setUrl(QUrl::fromLocalFile(p.potDir())); ui_project_advanced.branchDir->setUrl(QUrl::fromLocalFile(p.branchDir())); ui_project_advanced.altDir->setUrl(QUrl::fromLocalFile(p.altTransDir())); dialog->addPage(w, i18nc("@title:tab", "Advanced"), "applications-development-translation"); //Scripts w = new QWidget(dialog); QVBoxLayout* layout = new QVBoxLayout(w); layout->setSpacing(6); layout->setMargin(11); //m_projectActionsEditor=new Kross::ActionCollectionEditor(Kross::Manager::self().actionCollection()->collection(Project::instance()->projectID()),w); m_projectActionsView = new ScriptsView(w); layout->addWidget(m_projectActionsView); m_projectActionsView->setModel(new Kross::ActionCollectionModel(w, Kross::Manager::self().actionCollection()->collection(Project::instance()->kind()))); QHBoxLayout* btns = new QHBoxLayout(); layout->addLayout(btns); btns->addWidget(m_projectActionsView->createButton(w, "edit")); dialog->addPage(w, i18nc("@title:tab", "Scripts"), "preferences-system-windows-actions"); w = new QWidget(dialog); Ui_prefs_project_local ui_prefs_project_local; ui_prefs_project_local.setupUi(w); dialog->addPage(w, Project::local(), i18nc("@title:tab", "Personal"), "preferences-desktop-user"); - w = new QWidget(dialog); - Ui_prefs_project_pology ui_prefs_project_pology; - ui_prefs_project_pology.setupUi(w); - dialog->addPage(w, Project::local(), i18nc("@title:tab", "Pology"), "preferences-desktop-filetype-association"); - connect(dialog, &KConfigDialog::settingsChanged, Project::instance(), &Project::reinit); connect(dialog, &KConfigDialog::settingsChanged, Project::instance(), &Project::save, Qt::QueuedConnection); connect(dialog, &KConfigDialog::settingsChanged, TM::DBFilesModel::instance(), &TM::DBFilesModel::updateProjectTmIndex); connect(dialog, &KConfigDialog::settingsChanged, this, &SettingsController::reflectProjectConfigChange); dialog->show(); } void SettingsController::reflectProjectConfigChange() { //TODO check target language change: reflect changes in TM and glossary TM::DBFilesModel::instance()->openDB(Project::instance()->projectID()); } void SettingsController::reflectRelativePathsHack() { //m_scriptsRelPrefWidget->clear(); QStringList actionz(m_scriptsPrefWidget->items()); QString projectDir(Project::instance()->projectDir()); int i = actionz.size(); while (--i >= 0) actionz[i] = QDir(projectDir).relativeFilePath(actionz.at(i)); m_scriptsRelPrefWidget->setItems(actionz); } void LangCodeSaver::setLangCode(int index) { setText(LanguageListModel::instance()->langCodeForSortModelRow(index)); } void RelPathSaver::setText(const QString& txt) { QLineEdit::setText(QDir(Project::instance()->projectDir()).relativeFilePath(txt)); } void writeUiState(const char* elementName, const QByteArray& state) { KConfig config; KConfigGroup cg(&config, "MainWindow"); cg.writeEntry(elementName, state.toBase64()); } QByteArray readUiState(const char* elementName) { KConfig config; KConfigGroup cg(&config, "MainWindow"); return QByteArray::fromBase64(cg.readEntry(elementName, QByteArray())); } diff --git a/src/prefs/prefs_pology.ui b/src/prefs/prefs_pology.ui new file mode 100644 index 0000000..4092f2c --- /dev/null +++ b/src/prefs/prefs_pology.ui @@ -0,0 +1,83 @@ + + prefs_pology + + + + 0 + 0 + 611 + 439 + + + + + + + QFormLayout::ExpandingFieldsGrow + + + + + Enable Pology verification + + + + + + + The Pology command to run in order to check a single entry. Use the following placeholders in order to set-up the commands: %u is the entry number, %f is the file name. For instance: posieve -u %u check-rules %f + + + true + + + + + + + + 611 + 100 + + + + The Pology command to run in order to check a single entry. + + + Please enter here the Pology command to be run in order to check a single entry + + + + + + + The Pology command to run in order to check a whole file. This command should include "-s lokalize", placeholder %f is the file name. For instance: posieve -s lokalize check-rules %f + + + true + + + + + + + + 611 + 100 + + + + The Pology command to run in order to check a whole file. + + + Please enter here the Pology command to be run in order to check a whole file + + + + + + + + + + diff --git a/src/project/prefs_project_pology.ui b/src/project/prefs_project_pology.ui deleted file mode 100644 index bfc5b88..0000000 --- a/src/project/prefs_project_pology.ui +++ /dev/null @@ -1,64 +0,0 @@ - - prefs_project_pology - - - - 0 - 0 - 611 - 439 - - - - - - - QFormLayout::ExpandingFieldsGrow - - - - - Enable Pology verification - - - - - - - Pology command at entry level: - - - false - - - kcfg_PologyCommandEntry - - - - - - - The Pology command to run in order to check a single entry - - - Please enter here the Pology command to be run in order to check a single entry - - - - - - - Use the following placeholders in order to set-up the commands: %u is the entry number, %f is the file name. For instance: python /home/translator/i18n/pology/scripts/posieve.py -u %u check_rules %f - - - true - - - - - - - - - - diff --git a/src/project/projectlocal.kcfg b/src/project/projectlocal.kcfg index 997265a..f7461ca 100644 --- a/src/project/projectlocal.kcfg +++ b/src/project/projectlocal.kcfg @@ -1,30 +1,24 @@ Approver true - - - false - - - diff --git a/src/project/projecttab.cpp b/src/project/projecttab.cpp index aa92d57..8e40e52 100644 --- a/src/project/projecttab.cpp +++ b/src/project/projecttab.cpp @@ -1,422 +1,470 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2014 by Nick Shaforostoff 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 "projecttab.h" #include "project.h" #include "projectwidget.h" #include "tmscanapi.h" #include "prefs.h" +#include "prefs_lokalize.h" #include "catalog.h" +#include "lokalize_debug.h" -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include ProjectTab::ProjectTab(QWidget *parent) : LokalizeSubwindowBase2(parent) , m_browser(new ProjectWidget(this)) , m_filterEdit(new QLineEdit(this)) + , m_pologyProcessInProgress(false) , m_legacyUnitsCount(-1) , m_currentUnitsCount(0) { setWindowTitle(i18nc("@title:window", "Project Overview")); //setCaption(i18nc("@title:window","Project"),false); //BEGIN setup welcome widget QWidget* welcomeWidget = new QWidget(this); QVBoxLayout* wl = new QVBoxLayout(welcomeWidget); QLabel* about = new QLabel(i18n("" //copied from kaboutkdedialog_p.cpp "You do not have to be a software developer to be a member of the " "KDE team. You can join the national teams that translate " "program interfaces. You can provide graphics, themes, sounds, and " "improved documentation. You decide!" "

    " "Visit " "%1 " "for information on some projects in which you can participate." "

    " "If you need more information or documentation, then a visit to " "%2 " "will provide you with what you need.", QLatin1String("http://community.kde.org/Get_Involved"), QLatin1String("http://techbase.kde.org/")), welcomeWidget); about->setAlignment(Qt::AlignCenter); about->setWordWrap(true); about->setOpenExternalLinks(true); about->setTextInteractionFlags(Qt::TextBrowserInteraction); about->setTextFormat(Qt::RichText); QPushButton* conf = new QPushButton(i18n("&Configure Lokalize"), welcomeWidget); QPushButton* openProject = new QPushButton(i18nc("@action:inmenu", "Open project"), welcomeWidget); QPushButton* createProject = new QPushButton(i18nc("@action:inmenu", "Translate software"), welcomeWidget); QPushButton* createOdfProject = new QPushButton(i18nc("@action:inmenu", "Translate OpenDocument"), welcomeWidget); connect(conf, &QPushButton::clicked, SettingsController::instance(), &SettingsController::showSettingsDialog); connect(openProject, &QPushButton::clicked, this, QOverload<>::of(&ProjectTab::projectOpenRequested)); connect(createProject, &QPushButton::clicked, SettingsController::instance(), &SettingsController::projectCreate); connect(createOdfProject, &QPushButton::clicked, Project::instance(), &Project::projectOdfCreate); QHBoxLayout* wbtnl = new QHBoxLayout(); wbtnl->addStretch(1); wbtnl->addWidget(conf); wbtnl->addWidget(openProject); wbtnl->addWidget(createProject); wbtnl->addWidget(createOdfProject); wbtnl->addStretch(1); wl->addStretch(1); wl->addWidget(about); wl->addStretch(1); wl->addLayout(wbtnl); wl->addStretch(1); //END setup welcome widget QWidget* baseWidget = new QWidget(this); m_stackedLayout = new QStackedLayout(baseWidget); QWidget* w = new QWidget(this); m_stackedLayout->addWidget(welcomeWidget); m_stackedLayout->addWidget(w); connect(Project::instance(), &Project::loaded, this, &ProjectTab::showRealProjectOverview); if (Project::instance()->isLoaded()) //for --project cmd option showRealProjectOverview(); QVBoxLayout* l = new QVBoxLayout(w); m_filterEdit->setClearButtonEnabled(true); m_filterEdit->setPlaceholderText(i18n("Quick search...")); m_filterEdit->setToolTip(i18nc("@info:tooltip", "Activated by Ctrl+L.") % ' ' % i18nc("@info:tooltip", "Accepts regular expressions")); connect(m_filterEdit, &QLineEdit::textChanged, this, &ProjectTab::setFilterRegExp, Qt::QueuedConnection); new QShortcut(Qt::CTRL + Qt::Key_L, this, SLOT(setFocus()), 0, Qt::WidgetWithChildrenShortcut); l->addWidget(m_filterEdit); l->addWidget(m_browser); connect(m_browser, &ProjectWidget::fileOpenRequested, this, &ProjectTab::fileOpenRequested); connect(Project::instance()->model(), &ProjectModel::totalsChanged, this, &ProjectTab::updateStatusBar); connect(Project::instance()->model(), &ProjectModel::loadingAboutToStart, this, &ProjectTab::initStatusBarProgress); setCentralWidget(baseWidget); QStatusBar* statusBar = static_cast(parent)->statusBar(); m_progressBar = new QProgressBar(0); m_progressBar->setVisible(false); statusBar->insertWidget(ID_STATUS_PROGRESS, m_progressBar, 1); setXMLFile(QStringLiteral("projectmanagerui.rc"), true); setUpdatedXMLFile(); //QAction* action = KStandardAction::find(Project::instance(),SLOT(showTM()),actionCollection()); #define ADD_ACTION_SHORTCUT_ICON(_name,_text,_shortcut,_icon)\ action = nav->addAction(QStringLiteral(_name));\ action->setText(_text);\ action->setIcon(QIcon::fromTheme(_icon));\ ac->setDefaultShortcut(action, QKeySequence( _shortcut )); QAction *action; KActionCollection* ac = actionCollection(); KActionCategory* nav = new KActionCategory(i18nc("@title actions category", "Navigation"), ac); 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 + Qt::Key_PageUp, "prevfuzzyuntrans") connect(action, &QAction::triggered, this, &ProjectTab::gotoPrevFuzzyUntr); 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, &ProjectTab::gotoNextFuzzyUntr); 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, &ProjectTab::gotoPrevFuzzy); 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, &ProjectTab::gotoNextFuzzy); ADD_ACTION_SHORTCUT_ICON("go_prev_untrans", i18nc("@action:inmenu", "Previous untranslated"), Qt::ALT + Qt::Key_PageUp, "prevuntranslated") connect(action, &QAction::triggered, this, &ProjectTab::gotoPrevUntranslated); ADD_ACTION_SHORTCUT_ICON("go_next_untrans", i18nc("@action:inmenu", "Next untranslated"), Qt::ALT + Qt::Key_PageDown, "nextuntranslated") connect(action, &QAction::triggered, this, &ProjectTab::gotoNextUntranslated); ADD_ACTION_SHORTCUT_ICON("go_prev_templateOnly", i18nc("@action:inmenu", "Previous template only"), Qt::CTRL + Qt::Key_Up, "prevtemplate") connect(action, &QAction::triggered, this, &ProjectTab::gotoPrevTemplateOnly); ADD_ACTION_SHORTCUT_ICON("go_next_templateOnly", i18nc("@action:inmenu", "Next template only"), Qt::CTRL + Qt::Key_Down, "nexttemplate") connect(action, &QAction::triggered, this, &ProjectTab::gotoNextTemplateOnly); ADD_ACTION_SHORTCUT_ICON("go_prev_transOnly", i18nc("@action:inmenu", "Previous translation only"), Qt::ALT + Qt::Key_Up, "prevpo") connect(action, &QAction::triggered, this, &ProjectTab::gotoPrevTransOnly); ADD_ACTION_SHORTCUT_ICON("go_next_transOnly", i18nc("@action:inmenu", "Next translation only"), Qt::ALT + Qt::Key_Down, "nextpo") connect(action, &QAction::triggered, this, &ProjectTab::gotoNextTransOnly); action = nav->addAction(QStringLiteral("toggle_translated_files")); action->setText(i18nc("@action:inmenu", "Hide completed items")); action->setToolTip(i18nc("@action:inmenu", "Hide fully translated files and folders")); action->setIcon(QIcon::fromTheme("hide_table_row")); action->setCheckable(true); ac->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_T)); connect(action, &QAction::triggered, this, &ProjectTab::toggleTranslatedFiles); // ADD_ACTION_SHORTCUT_ICON("edit_find",i18nc("@action:inmenu","Find in files"),Qt::ALT+Qt::Key_Down,"nextpo") //connect(action, &QAction::triggered, this, &ProjectTab::gotoNextTransOnly); action = nav->addAction(KStandardAction::Find, this, SLOT(searchInFiles())); KActionCategory* proj = new KActionCategory(i18nc("@title actions category", "Project"), ac); action = proj->addAction(QStringLiteral("project_open"), this, SIGNAL(projectOpenRequested())); action->setText(i18nc("@action:inmenu", "Open project")); action->setIcon(QIcon::fromTheme("project-open")); int i = 6; while (--i > ID_STATUS_PROGRESS) statusBarItems.insert(i, QString()); } ProjectTab::~ProjectTab() { //qCWarning(LOKALIZE_LOG)<<"destroyed"; } void ProjectTab::showRealProjectOverview() { m_stackedLayout->setCurrentIndex(1); } void ProjectTab::toggleTranslatedFiles() { m_browser->toggleTranslatedFiles(); } QString ProjectTab::currentFilePath() { return Project::instance()->path(); } void ProjectTab::setFocus() { m_filterEdit->setFocus(); m_filterEdit->selectAll(); } void ProjectTab::setFilterRegExp() { QString newPattern = m_filterEdit->text(); if (m_browser->proxyModel()->filterRegExp().pattern() == newPattern) return; m_browser->proxyModel()->setFilterRegExp(newPattern); if (newPattern.size() > 2) m_browser->expandItems(); } void ProjectTab::contextMenuEvent(QContextMenuEvent *event) { QMenu* menu = new QMenu(this); connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater); if (m_browser->selectedItems().size() > 1 || (m_browser->selectedItems().size() == 1 && !m_browser->currentIsTranslationFile())) { menu->addAction(i18nc("@action:inmenu", "Open selected files"), this, SLOT(openFile())); menu->addSeparator(); } else if (m_browser->currentIsTranslationFile()) { menu->addAction(i18nc("@action:inmenu", "Open"), this, SLOT(openFile())); menu->addSeparator(); } /*menu.addAction(i18nc("@action:inmenu","Find in files"),this,SLOT(findInFiles())); menu.addAction(i18nc("@action:inmenu","Replace in files"),this,SLOT(replaceInFiles())); menu.addAction(i18nc("@action:inmenu","Spellcheck files"),this,SLOT(spellcheckFiles())); menu.addSeparator(); menu->addAction(i18nc("@action:inmenu","Get statistics for subfolders"),m_browser,SLOT(expandItems())); */ menu->addAction(i18nc("@action:inmenu", "Add to translation memory"), this, SLOT(scanFilesToTM())); menu->addAction(i18nc("@action:inmenu", "Search in files"), this, SLOT(searchInFiles())); + if (Settings::self()->pologyEnabled()) { + menu->addAction(i18nc("@action:inmenu", "Launch Pology on files"), this, &ProjectTab::pologyOnFiles); + } if (QDir(Project::instance()->templatesRoot()).exists()) menu->addAction(i18nc("@action:inmenu", "Search in files (including templates)"), this, SLOT(searchInFilesInclTempl())); // else if (Project::instance()->model()->hasChildren(/*m_proxyModel->mapToSource(*/(m_browser->currentIndex())) // ) // { // menu.addSeparator(); // menu.addAction(i18n("Force Scanning"),this,SLOT(slotForceStats())); // // } menu->popup(event->globalPos()); } void ProjectTab::scanFilesToTM() { TM::scanRecursive(m_browser->selectedItems(), Project::instance()->projectID()); } void ProjectTab::searchInFiles(bool templ) { QStringList files = m_browser->selectedItems(); if (!templ) { QString templatesRoot = Project::instance()->templatesRoot(); int i = files.size(); while (--i >= 0) { if (files.at(i).startsWith(templatesRoot)) files.removeAt(i); } } emit searchRequested(files); } +void ProjectTab::pologyOnFiles() +{ + if (!m_pologyProcessInProgress) { + QStringList files = m_browser->selectedItems(); + QString templatesRoot = Project::instance()->templatesRoot(); + QString filesAsString; + int i = files.size(); + while (--i >= 0) { + if (files.at(i).endsWith(QStringLiteral(".po"))) + filesAsString += QStringLiteral("\"") + files.at(i) + QStringLiteral("\" "); + } + + QString command = Settings::self()->pologyCommandFile().replace(QStringLiteral("%f"), filesAsString); + m_pologyProcess = new KProcess; + m_pologyProcess->setOutputChannelMode(KProcess::SeparateChannels); + qCWarning(LOKALIZE_LOG) << "Launching pology command: " << command; + connect(m_pologyProcess, QOverload::of(&KProcess::finished), + this, &ProjectTab::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 ProjectTab::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 ProjectTab::searchInFilesInclTempl() { searchInFiles(true); } void ProjectTab::openFile() { QStringList files = m_browser->selectedItems(); int i = files.size(); while (--i >= 0) { if (Catalog::extIsSupported(files.at(i))) { emit fileOpenRequested(files.at(i), true); } } } void ProjectTab::findInFiles() { emit searchRequested(m_browser->selectedItems()); } void ProjectTab::replaceInFiles() { emit replaceRequested(m_browser->selectedItems()); } void ProjectTab::spellcheckFiles() { emit spellcheckRequested(m_browser->selectedItems()); } void ProjectTab::gotoPrevFuzzyUntr() { m_browser->gotoPrevFuzzyUntr(); } void ProjectTab::gotoNextFuzzyUntr() { m_browser->gotoNextFuzzyUntr(); } void ProjectTab::gotoPrevFuzzy() { m_browser->gotoPrevFuzzy(); } void ProjectTab::gotoNextFuzzy() { m_browser->gotoNextFuzzy(); } void ProjectTab::gotoPrevUntranslated() { m_browser->gotoPrevUntranslated(); } void ProjectTab::gotoNextUntranslated() { m_browser->gotoNextUntranslated(); } void ProjectTab::gotoPrevTemplateOnly() { m_browser->gotoPrevTemplateOnly(); } void ProjectTab::gotoNextTemplateOnly() { m_browser->gotoNextTemplateOnly(); } void ProjectTab::gotoPrevTransOnly() { m_browser->gotoPrevTransOnly(); } void ProjectTab::gotoNextTransOnly() { m_browser->gotoNextTransOnly(); } bool ProjectTab::currentItemIsTranslationFile() const { return m_browser->currentIsTranslationFile(); } void ProjectTab::setCurrentItem(const QString& url) { m_browser->setCurrentItem(url); } QString ProjectTab::currentItem() const { return m_browser->currentItem(); } QStringList ProjectTab::selectedItems() const { return m_browser->selectedItems(); } void ProjectTab::updateStatusBar(int fuzzy, int translated, int untranslated, bool done) { int total = fuzzy + translated + untranslated; m_currentUnitsCount = total; if (m_progressBar->value() != total && m_legacyUnitsCount > 0) m_progressBar->setValue(total); if (m_progressBar->maximum() < qMax(total, m_legacyUnitsCount)) m_progressBar->setMaximum(qMax(total, m_legacyUnitsCount)); m_progressBar->setVisible(!done); if (done) m_legacyUnitsCount = total; statusBarItems.insert(ID_STATUS_TOTAL, i18nc("@info:status message entries", "Total: %1", total)); reflectNonApprovedCount(fuzzy, total); reflectUntranslatedCount(untranslated, total); } void ProjectTab::initStatusBarProgress() { if (m_legacyUnitsCount > 0) { if (m_progressBar->value() != 0) m_progressBar->setValue(0); if (m_progressBar->maximum() != m_legacyUnitsCount) m_progressBar->setMaximum(m_legacyUnitsCount); updateStatusBar(); } } void ProjectTab::setLegacyUnitsCount(int to) { m_legacyUnitsCount = to; m_currentUnitsCount = to; initStatusBarProgress(); } //bool ProjectTab::isShown() const {return isVisible();} diff --git a/src/project/projecttab.h b/src/project/projecttab.h index 1e08cc3..23d452c 100644 --- a/src/project/projecttab.h +++ b/src/project/projecttab.h @@ -1,124 +1,131 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2009 by Nick Shaforostoff 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 . **************************************************************************** */ #ifndef PROJECTTAB_H #define PROJECTTAB_H #include "lokalizesubwindowbase.h" #include +#include #include class QStackedLayout; class ProjectWidget; class QLineEdit; class QContextMenuEvent; class QProgressBar; /** * Project Overview Tab */ class ProjectTab: public LokalizeSubwindowBase2 { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.Lokalize.ProjectOverview") //qdbuscpp2xml -m -s projecttab.h -o org.kde.lokalize.ProjectOverview.xml public: ProjectTab(QWidget *parent); ~ProjectTab(); void contextMenuEvent(QContextMenuEvent *event); void hideDocks() {}; void showDocks() {}; KXMLGUIClient* guiClient() { return (KXMLGUIClient*)this; } QString currentFilePath(); int unitsCount() { return m_currentUnitsCount; } void setLegacyUnitsCount(int to); signals: void projectOpenRequested(QString path); void projectOpenRequested(); void fileOpenRequested(const QString&, const bool setAsActive); void searchRequested(const QStringList&); void replaceRequested(const QStringList&); void spellcheckRequested(const QStringList&); public slots: Q_SCRIPTABLE void setCurrentItem(const QString& url); Q_SCRIPTABLE QString currentItem() const; ///@returns list of selected files recursively Q_SCRIPTABLE QStringList selectedItems() const; Q_SCRIPTABLE bool currentItemIsTranslationFile() const; void showRealProjectOverview(); //Q_SCRIPTABLE bool isShown() const; private slots: void setFilterRegExp(); void setFocus(); void scanFilesToTM(); + void pologyOnFiles(); void searchInFiles(bool templ = false); void searchInFilesInclTempl(); void openFile(); void findInFiles(); void replaceInFiles(); void spellcheckFiles(); void gotoPrevFuzzyUntr(); void gotoNextFuzzyUntr(); void gotoPrevFuzzy(); void gotoNextFuzzy(); void gotoPrevUntranslated(); void gotoNextUntranslated(); void gotoPrevTemplateOnly(); void gotoNextTemplateOnly(); void gotoPrevTransOnly(); void gotoNextTransOnly(); void toggleTranslatedFiles(); void updateStatusBar(int fuzzy = 0, int translated = 0, int untranslated = 0, bool done = false); void initStatusBarProgress(); + void pologyHasFinished(int exitCode, QProcess::ExitStatus exitStatus); + private: ProjectWidget* m_browser; QLineEdit* m_filterEdit; QProgressBar* m_progressBar; QStackedLayout *m_stackedLayout; + KProcess* m_pologyProcess; + bool m_pologyProcessInProgress; + int m_legacyUnitsCount, m_currentUnitsCount; }; #endif diff --git a/src/tm/tmtab.cpp b/src/tm/tmtab.cpp index fba868d..baa838f 100644 --- a/src/tm/tmtab.cpp +++ b/src/tm/tmtab.cpp @@ -1,806 +1,806 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2014 by Nick Shaforostoff 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 "tmtab.h" #include "lokalize_debug.h" #include "ui_queryoptions.h" #include "project.h" #include "dbfilesmodel.h" #include "tmscanapi.h" #include "qaview.h" #include "prefs_lokalize.h" #include "jobs.h" #include "fastsizehintitemdelegate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NOKDE -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #endif #if defined(Q_OS_WIN) && defined(QStringLiteral) #undef QStringLiteral #define QStringLiteral QLatin1String #endif using namespace TM; //static int BIG_COUNTER=0; //TODO do things for case when user explicitly wants to find & accel mark //BEGIN TMDBModel TMDBModel::TMDBModel(QObject* parent) : QSqlQueryModel(parent) , m_queryType(WordOrder) , m_totalResultCount(0) { setHeaderData(TMDBModel::Source, Qt::Horizontal, i18nc("@title:column Original text", "Source")); setHeaderData(TMDBModel::Target, Qt::Horizontal, i18nc("@title:column Text in target language", "Target")); setHeaderData(TMDBModel::Context, Qt::Horizontal, i18nc("@title:column", "Context")); setHeaderData(TMDBModel::Filepath, Qt::Horizontal, i18nc("@title:column", "File")); setHeaderData(TMDBModel::TransationStatus, Qt::Horizontal, i18nc("@title:column", "Translation Status")); } void TMDBModel::setDB(const QString& str) { m_dbName = str; QString sourceLangCode = DBFilesModel::instance()->m_configurations.value(str).sourceLangCode; QString targetLangCode = DBFilesModel::instance()->m_configurations.value(str).targetLangCode; if (sourceLangCode.length()) setHeaderData(TMDBModel::Source, Qt::Horizontal, QString(i18nc("@title:column Original text", "Source") % QStringLiteral(": ") % sourceLangCode)); if (targetLangCode.length()) setHeaderData(TMDBModel::Target, Qt::Horizontal, QString(i18nc("@title:column Text in target language", "Target") % QStringLiteral(": ") % targetLangCode)); } void TMDBModel::setQueryType(int type) { m_queryType = (QueryType)type; } void TMDBModel::setFilter(const QString& source, const QString& target, bool invertSource, bool invertTarget, const QString& filemask ) { QString escapedSource(source); escapedSource.replace('\'', QStringLiteral("''")); QString escapedTarget(target); escapedTarget.replace('\'', QStringLiteral("''")); QString invertSourceStr; if (invertSource) invertSourceStr = QStringLiteral("NOT "); QString invertTargetStr; if (invertTarget) invertTargetStr = QStringLiteral("NOT "); QString escapedFilemask(filemask); escapedFilemask.replace('\'', QStringLiteral("''")); QString sourceQuery; QString targetQuery; QString fileQuery; if (m_queryType == SubStr) { escapedSource.replace('%', QStringLiteral("\b%")); escapedSource.replace('_', QStringLiteral("\b_")); escapedTarget.replace('%', QStringLiteral("\b%")); escapedTarget.replace('_', QStringLiteral("\b_")); if (!escapedSource.isEmpty()) sourceQuery = QStringLiteral("AND source_strings.source ") % invertSourceStr % QStringLiteral("LIKE '%") % escapedSource % QStringLiteral("%' ESCAPE '\b' "); if (!escapedTarget.isEmpty()) targetQuery = QStringLiteral("AND target_strings.target ") % invertTargetStr % QStringLiteral("LIKE '%") % escapedTarget % QStringLiteral("%' ESCAPE '\b' "); } else if (m_queryType == WordOrder) { /*escapedSource.replace('%',"\b%");escapedSource.replace('_',"\b_"); escapedTarget.replace('%',"\b%");escapedTarget.replace('_',"\b_");*/ QRegExp wre(QStringLiteral("\\W")); QStringList sourceList = escapedSource.split(wre, QString::SkipEmptyParts); QStringList targetList = escapedTarget.split(wre, QString::SkipEmptyParts); if (!sourceList.isEmpty()) sourceQuery = QStringLiteral("AND source_strings.source ") % invertSourceStr % QStringLiteral("LIKE '%") % sourceList.join(QStringLiteral("%' AND source_strings.source ") % invertSourceStr % QStringLiteral("LIKE '%")) % QStringLiteral("%' "); if (!targetList.isEmpty()) targetQuery = QStringLiteral("AND target_strings.target ") % invertTargetStr % QStringLiteral("LIKE '%") % targetList.join(QStringLiteral("%' AND target_strings.target ") % invertTargetStr % QStringLiteral("LIKE '%")) % QStringLiteral("%' "); } else { if (!escapedSource.isEmpty()) sourceQuery = QStringLiteral("AND source_strings.source ") % invertSourceStr % QStringLiteral("GLOB '") % escapedSource % QStringLiteral("' "); if (!escapedTarget.isEmpty()) targetQuery = QStringLiteral("AND target_strings.target ") % invertTargetStr % QStringLiteral("GLOB '") % escapedTarget % QStringLiteral("' "); } if (!filemask.isEmpty()) fileQuery = QStringLiteral("AND files.path GLOB '") % escapedFilemask % QStringLiteral("' "); QString fromPart = QStringLiteral("FROM main JOIN source_strings ON (source_strings.id=main.source) " "JOIN target_strings ON (target_strings.id=main.target), files " "WHERE files.id=main.file ") % sourceQuery % targetQuery % fileQuery; ExecQueryJob* job = new ExecQueryJob(QStringLiteral( "SELECT source_strings.source, target_strings.target, " "main.ctxt, files.path, " "source_strings.source_accel, target_strings.target_accel, main.bits ") + fromPart, m_dbName, &m_dbOperationMutex); connect(job, &ExecQueryJob::done, this, &TMDBModel::slotQueryExecuted); threadPool()->start(job); job = new ExecQueryJob(QStringLiteral("SELECT count(*) ") + fromPart, m_dbName, &m_dbOperationMutex); connect(job, &ExecQueryJob::done, this, &TMDBModel::slotQueryExecuted); threadPool()->start(job); m_totalResultCount = 0; } void TMDBModel::slotQueryExecuted(ExecQueryJob* job) { job->deleteLater(); m_dbOperationMutex.lock(); if (job->query->lastQuery().startsWith(QLatin1String("SELECT count(*) "))) { m_totalResultCount = job->query->next() ? job->query->value(0).toInt() : -1; m_dbOperationMutex.unlock(); emit finalResultCountFetched(m_totalResultCount); return; } query().finish(); query().clear(); setQuery(*(job->query)); m_dbOperationMutex.unlock(); emit resultsFetched(); } bool TMDBModel::rowIsApproved(int row) const { bool ok; qlonglong bits = record(row).value(TMDBModel::_Bits).toLongLong(&ok); return !(ok && bits & 4); } int TMDBModel::translationStatus(const QModelIndex& item) const { //QMutexLocker locker(&m_dbOperationMutex); if (QSqlQueryModel::data(item.sibling(item.row(), Target), Qt::DisplayRole).toString().isEmpty()) return 2; return int(!rowIsApproved(item.row())); } #define TM_DELIMITER '\v' QVariant TMDBModel::data(const QModelIndex& item, int role) const { //QMutexLocker locker(&m_dbOperationMutex); bool doHtml = (role == FastSizeHintItemDelegate::HtmlDisplayRole); if (doHtml) role = Qt::DisplayRole; else if (role == Qt::FontRole && item.column() == TMDBModel::Target) { //TODO Qt::ForegroundRole -- brush for orphaned entries QFont font = QApplication::font(); font.setItalic(!rowIsApproved(item.row())); return font; } else if (role == FullPathRole && item.column() == TMDBModel::Filepath) return QSqlQueryModel::data(item, Qt::DisplayRole); else if (role == TransStateRole) return translationStatus(item); QVariant result = QSqlQueryModel::data(item, role); /* if (role==Qt::SizeHintRole && !result.isValid()) BIG_COUNTER++;*/ if (role != Qt::DisplayRole) return result; if (item.column() == TMDBModel::Context) { //context QString r = result.toString(); int pos = r.indexOf(TM_DELIMITER); if (pos != -1) result = r.remove(pos, r.size()); } else if (item.column() < TMDBModel::Context && !record(item.row()).isNull(TMDBModel::_SourceAccel + item.column())) { //source, target const QVariant& posVar = record(item.row()).value(TMDBModel::_SourceAccel + item.column()); int pos = -1; bool ok = false; if (posVar.isValid()) pos = posVar.toInt(&ok); if (ok && pos != -1) { QString r = result.toString(); r.insert(pos, Project::instance()->accel()); result = r; } } else if (item.column() == TMDBModel::Filepath) { return shorterFilePath(result.toString()); } else if (item.column() == TMDBModel::TransationStatus) { static QString statuses[] = {i18nc("@info:status 'non-fuzzy' in gettext terminology", "Ready"), i18nc("@info:status 'fuzzy' in gettext terminology", "Needs review"), i18nc("@info:status", "Untranslated") }; return statuses[translationStatus(item)]; } if (doHtml && item.column() < TMDBModel::Context) return convertToHtml(result.toString(), item.column() == TMDBModel::Target && !rowIsApproved(item.row())); else return result; } //END TMDBModel //BEGIN TMResultsSortFilterProxyModel class TMResultsSortFilterProxyModel: public QSortFilterProxyModel { public: TMResultsSortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) {} void setRules(const QVector& rules); void fetchMore(const QModelIndex& parent); QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; protected: bool lessThan(const QModelIndex& left, const QModelIndex& right) const; bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; private: QVector m_rules; mutable QMap m_matchingRulesForSourceRow; //mutable QMap > m_highlightDataForSourceRow; }; bool TMResultsSortFilterProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { if (left.column() == TMDBModel::TransationStatus) { int l = left.data(TMDBModel::TransStateRole).toInt(); int r = right.data(TMDBModel::TransStateRole).toInt(); return l < r; } return QSortFilterProxyModel::lessThan(left, right); } void TMResultsSortFilterProxyModel::fetchMore(const QModelIndex& parent) { int oldSourceRowCount = sourceModel()->rowCount(); int oldRowCount = rowCount(); QSortFilterProxyModel::fetchMore(parent); if (m_rules.isEmpty()) return; while (oldRowCount == rowCount()) { QSortFilterProxyModel::fetchMore(parent); if (sourceModel()->rowCount() == oldSourceRowCount) break; oldSourceRowCount = sourceModel()->rowCount(); } qCDebug(LOKALIZE_LOG) << "row count" << sourceModel()->rowCount() << " filtered:" << rowCount(); emit layoutChanged(); } void TMResultsSortFilterProxyModel::setRules(const QVector& rules) { m_rules = rules; m_matchingRulesForSourceRow.clear(); invalidateFilter(); } QVariant TMResultsSortFilterProxyModel::data(const QModelIndex& index, int role) const { QVariant result = QSortFilterProxyModel::data(index, role); if (m_rules.isEmpty() || role != FastSizeHintItemDelegate::HtmlDisplayRole) return result; if (index.column() != TMDBModel::Source && index.column() != TMDBModel::Target) return result; int source_row = mapToSource(index).row(); QString string = result.toString(); QVector regExps; if (index.column() == TMDBModel::Source) regExps = m_rules[m_matchingRulesForSourceRow[source_row]].sources; else regExps = m_rules[m_matchingRulesForSourceRow[source_row]].falseFriends; foreach (const QRegExp& re, regExps) { int pos = re.indexIn(string); if (pos != -1) return string.replace(pos, re.matchedLength(), QStringLiteral("") % re.cap(0) % QStringLiteral("")); } //StartLen sl=m_highlightDataForSourceRow.value(source_row).at(index.column()); return result; } bool TMResultsSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { if (m_rules.isEmpty()) return true; QString source = sourceModel()->index(source_row, TMDBModel::Source, source_parent).data().toString(); QString target = sourceModel()->index(source_row, TMDBModel::Target, source_parent).data().toString(); static QVector dummy_positions; int i = findMatchingRule(m_rules, source, target, dummy_positions); bool accept = (i != -1); if (accept) m_matchingRulesForSourceRow[source_row] = i; return accept; } //END TMResultsSortFilterProxyModel class QueryStylesModel: public QStringListModel { public: explicit QueryStylesModel(QObject* parent = 0); QVariant data(const QModelIndex& item, int role) const; }; QueryStylesModel::QueryStylesModel(QObject* parent): QStringListModel(parent) { setStringList(QStringList(i18n("Substring")) << i18n("Google-like") << i18n("Wildcard")); } QVariant QueryStylesModel::data(const QModelIndex& item, int role) const { if (role == Qt::ToolTipRole) { static QString tooltips[] = {i18n("Case insensitive"), i18n("Space is AND operator. Case insensitive."), i18n("Shell globs (* and ?). Case sensitive.") }; return tooltips[item.row()]; } return QStringListModel::data(item, role); } //BEGIN TMWindow TMTab::TMTab(QWidget *parent) : LokalizeSubwindowBase2(parent) , m_proxyModel(new TMResultsSortFilterProxyModel(this)) , m_partToAlsoTryLater(DocPosition::UndefPart) , m_dbusId(-1) { //setCaption(i18nc("@title:window","Translation Memory"),false); setWindowTitle(i18nc("@title:window", "Translation Memory")); setAcceptDrops(true); ui_queryOptions = new Ui_QueryOptions; QWidget* w = new QWidget(this); ui_queryOptions->setupUi(w); setCentralWidget(w); ui_queryOptions->queryLayout->setStretchFactor(ui_queryOptions->mainQueryLayout, 42); connect(ui_queryOptions->querySource, &QLineEdit::returnPressed, this, &TMTab::performQuery); connect(ui_queryOptions->queryTarget, &QLineEdit::returnPressed, this, &TMTab::performQuery); connect(ui_queryOptions->filemask, &QLineEdit::returnPressed, this, &TMTab::performQuery); connect(ui_queryOptions->doFind, &QPushButton::clicked, this, &TMTab::performQuery); connect(ui_queryOptions->doUpdateTM, &QPushButton::clicked, this, &TMTab::updateTM); QShortcut* sh = new QShortcut(Qt::CTRL + Qt::Key_L, this); connect(sh, &QShortcut::activated, ui_queryOptions->querySource, QOverload<>::of(&QLineEdit::setFocus)); setFocusProxy(ui_queryOptions->querySource); QTreeView* view = ui_queryOptions->treeView; view->setContextMenuPolicy(Qt::ActionsContextMenu); QAction* a = new QAction(i18n("Copy source to clipboard"), view); a->setShortcut(Qt::CTRL + Qt::Key_S); a->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(a, &QAction::triggered, this, &TMTab::copySource); view->addAction(a); a = new QAction(i18n("Copy target to clipboard"), view); a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return)); a->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(a, &QAction::triggered, this, &TMTab::copyTarget); view->addAction(a); a = new QAction(i18n("Open file"), view); a->setShortcut(QKeySequence(Qt::Key_Return)); a->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(a, &QAction::triggered, this, &TMTab::openFile); connect(view, &QTreeView::activated, this, &TMTab::openFile); view->addAction(a); //view->addAction(KStandardAction::copy(this),this,SLOT(),this); //QKeySequence::Copy? //QShortcut* shortcut = new QShortcut(Qt::CTRL + Qt::Key_P,view,0,0,Qt::WidgetWithChildrenShortcut); //connect(shortcut,SIGNAL(activated()), this, SLOT(copyText())); m_model = new TMDBModel(this); m_model->setDB(Project::instance()->projectID()); m_proxyModel->setDynamicSortFilter(true); m_proxyModel->setSourceModel(m_model); view->setModel(m_proxyModel); view->sortByColumn(TMDBModel::Filepath, Qt::AscendingOrder); view->setSortingEnabled(true); view->setColumnHidden(TMDBModel::_SourceAccel, true); view->setColumnHidden(TMDBModel::_TargetAccel, true); view->setColumnHidden(TMDBModel::_Bits, true); QVector singleLineColumns(TMDBModel::ColumnCount, false); singleLineColumns[TMDBModel::Filepath] = true; singleLineColumns[TMDBModel::TransationStatus] = true; singleLineColumns[TMDBModel::Context] = true; QVector richTextColumns(TMDBModel::ColumnCount, false); richTextColumns[TMDBModel::Source] = true; richTextColumns[TMDBModel::Target] = true; view->setItemDelegate(new FastSizeHintItemDelegate(this, singleLineColumns, richTextColumns)); connect(m_model, &TMDBModel::resultsFetched, (FastSizeHintItemDelegate*)view->itemDelegate(), &FastSizeHintItemDelegate::reset); connect(m_model, &TMDBModel::modelReset, (FastSizeHintItemDelegate*)view->itemDelegate(), &FastSizeHintItemDelegate::reset); //connect(m_model,SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),view->itemDelegate(),SLOT(reset())); connect(m_proxyModel, &TMResultsSortFilterProxyModel::layoutChanged, (FastSizeHintItemDelegate*)view->itemDelegate(), &FastSizeHintItemDelegate::reset); connect(m_proxyModel, &TMResultsSortFilterProxyModel::layoutChanged, this, &TMTab::displayTotalResultCount); connect(m_model, &TMDBModel::resultsFetched, this, &TMTab::handleResults); connect(m_model, &TMDBModel::finalResultCountFetched, this, &TMTab::displayTotalResultCount); ui_queryOptions->queryStyle->setModel(new QueryStylesModel(this)); connect(ui_queryOptions->queryStyle, QOverload::of(&KComboBox::currentIndexChanged), m_model, &TMDBModel::setQueryType); ui_queryOptions->dbName->setModel(DBFilesModel::instance()); ui_queryOptions->dbName->setRootModelIndex(DBFilesModel::instance()->rootIndex()); int pos = ui_queryOptions->dbName->findData(Project::instance()->projectID(), DBFilesModel::NameRole); if (pos >= 0) ui_queryOptions->dbName->setCurrentIndex(pos); connect(ui_queryOptions->dbName, QOverload::of(&QComboBox::currentIndexChanged), m_model, &TMDBModel::setDB); //connect(ui_queryOptions->dbName, SIGNAL(activated(QString)), this, SLOT(performQuery())); //BEGIN resizeColumnToContents static const int maxInitialWidths[4] = {QApplication::desktop()->availableGeometry().width() / 3, QApplication::desktop()->availableGeometry().width() / 3, 50, 200}; int column = sizeof(maxInitialWidths) / sizeof(int); while (--column >= 0) view->setColumnWidth(column, maxInitialWidths[column]); //END resizeColumnToContents int i = 6; while (--i > ID_STATUS_PROGRESS) statusBarItems.insert(i, QString()); setXMLFile(QStringLiteral("translationmemoryrui.rc"), true); setUpdatedXMLFile(); dbusObjectPath(); QAction *action; KActionCollection* ac = actionCollection(); KActionCategory* tm = new KActionCategory(i18nc("@title actions category", "Translation Memory"), ac); action = tm->addAction(QStringLiteral("tools_tm_manage"), Project::instance(), SLOT(showTMManager())); action->setText(i18nc("@action:inmenu", "Manage translation memories")); m_qaView = new QaView(this); m_qaView->hide(); addDockWidget(Qt::RightDockWidgetArea, m_qaView); tm->addAction(QStringLiteral("showqa_action"), m_qaView->toggleViewAction()); connect(m_qaView, &QaView::rulesChanged, this, QOverload<>::of(&TMTab::setQAMode)); connect(m_qaView->toggleViewAction(), &QAction::toggled, this, QOverload::of(&TMTab::setQAMode)); #ifndef NOKDE KConfig config; KConfigGroup cg(&config, "MainWindow"); view->header()->restoreState(QByteArray::fromBase64(cg.readEntry("TMSearchResultsHeaderState", QByteArray()))); #endif } TMTab::~TMTab() { #ifndef NOKDE KConfig config; KConfigGroup cg(&config, "MainWindow"); cg.writeEntry("TMSearchResultsHeaderState", ui_queryOptions->treeView->header()->saveState().toBase64()); ids.removeAll(m_dbusId); #endif delete ui_queryOptions; } void TMTab::updateTM() { scanRecursive(QStringList(Project::instance()->poDir()), Project::instance()->projectID()); if (Settings::deleteFromTMOnMissing()) { RemoveMissingFilesJob* job = new RemoveMissingFilesJob(Project::instance()->projectID()); TM::threadPool()->start(job, REMOVEMISSINGFILES); } } void TMTab::performQuery() { if (ui_queryOptions->dbName->currentText().isEmpty()) { int pos = ui_queryOptions->dbName->findData(Project::instance()->projectID(), DBFilesModel::NameRole); if (pos >= 0) ui_queryOptions->dbName->setCurrentIndex(pos); //m_model->setDB(Project::instance()->projectID()); } m_model->m_dbOperationMutex.lock(); m_model->setFilter(ui_queryOptions->querySource->text(), ui_queryOptions->queryTarget->text(), ui_queryOptions->invertSource->isChecked(), ui_queryOptions->invertTarget->isChecked(), ui_queryOptions->filemask->text() ); m_model->m_dbOperationMutex.unlock(); QApplication::setOverrideCursor(Qt::BusyCursor); } void TMTab::handleResults() { QApplication::restoreOverrideCursor(); QString filemask = ui_queryOptions->filemask->text(); //ui_queryOptions->regexSource->text(),ui_queryOptions->regexTarget->text() m_model->m_dbOperationMutex.lock(); int rowCount = m_model->rowCount(); m_model->m_dbOperationMutex.unlock(); if (rowCount == 0) { qCDebug(LOKALIZE_LOG) << "m_model->rowCount()==0"; //try harder if (m_partToAlsoTryLater != DocPosition::UndefPart) { if (m_partToAlsoTryLater == DocPosition::Comment) { QString text = ui_queryOptions->queryTarget->text(); if (text.isEmpty()) text = ui_queryOptions->querySource->text(); if (text.isEmpty()) m_partToAlsoTryLater = DocPosition::UndefPart; else findGuiText(text); return; } QLineEdit* const source_target_query[] = {ui_queryOptions->queryTarget, ui_queryOptions->querySource}; source_target_query[m_partToAlsoTryLater == DocPosition::Source]->setText(source_target_query[m_partToAlsoTryLater != DocPosition::Source]->text()); source_target_query[m_partToAlsoTryLater != DocPosition::Source]->clear(); m_partToAlsoTryLater = ui_queryOptions->filemask->text().isEmpty() ? DocPosition::UndefPart : DocPosition::Comment; //leave a note that we should also try w/o package if the current one doesn't succeed return performQuery(); } if (!filemask.isEmpty() && !filemask.contains('*')) { ui_queryOptions->filemask->setText('*' % filemask % '*'); return performQuery(); } } qCDebug(LOKALIZE_LOG) << "=DocPosition::UndefPart"; m_partToAlsoTryLater = DocPosition::UndefPart; ui_queryOptions->treeView->setFocus(); } void TMTab::displayTotalResultCount() { m_model->m_dbOperationMutex.lock(); int total = m_model->totalResultCount(); int filtered = m_proxyModel->rowCount(); if (filtered == m_model->rowCount()) statusBarItems.insert(1, i18nc("@info:status message entries", "Total: %1", total)); else statusBarItems.insert(1, i18nc("@info:status message entries", "Total: %1 (%2)", filtered, total)); m_model->m_dbOperationMutex.unlock(); } static void copy(Ui_QueryOptions* ui_queryOptions, int column) { QApplication::clipboard()->setText(ui_queryOptions->treeView->currentIndex().sibling(ui_queryOptions->treeView->currentIndex().row(), column).data().toString()); } void TMTab::copySource() { copy(ui_queryOptions, TMDBModel::Source); } void TMTab::copyTarget() { copy(ui_queryOptions, TMDBModel::Target); } void TMTab::openFile() { QModelIndex item = ui_queryOptions->treeView->currentIndex(); if (Settings::deleteFromTMOnMissing()) { //Check if the file exists and delete it if it doesn't QString filePath = item.sibling(item.row(), TMDBModel::Filepath).data(Qt::UserRole).toString(); if (Project::instance()->isFileMissing(filePath)) { //File doesn't exist RemoveFileJob* job = new RemoveFileJob(filePath, ui_queryOptions->dbName->currentText()); TM::threadPool()->start(job, REMOVEFILE); KMessageBox::information(this, i18nc("@info", "The file %1 does not exist, it has been removed from the translation memory.", filePath)); return performQuery();//We relaunch the query } } emit fileOpenRequested(item.sibling(item.row(), TMDBModel::Filepath).data(Qt::UserRole).toString(), item.sibling(item.row(), TMDBModel::Source).data().toString(), item.sibling(item.row(), TMDBModel::Context).data().toString(), true); } void TMTab::setQAMode() { return setQAMode(true); } void TMTab::setQAMode(bool enable) { static_cast(ui_queryOptions->treeView->itemDelegate())->reset(); if (!enable) { m_proxyModel->setRules(QVector()); return; } m_proxyModel->setRules(m_qaView->rules()); /*QDomElement docElem = m_categories.at(0).toElement(); QDomNode n = docElem.firstChildElement(); while(!n.isNull()) { QDomElement e = n.toElement(); qCDebug(LOKALIZE_LOG) << e.tagName(); n = n.nextSiblingElement(); }*/ performQuery(); } //END TMWindow #if 0 bool QueryResultDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& /*option*/, const QModelIndex& index) { qCWarning(LOKALIZE_LOG) << "QEvent" << event; if (event->type() == QEvent::Shortcut) { qCWarning(LOKALIZE_LOG) << "QEvent::Shortcut" << index.data().canConvert(QVariant::String); if (static_cast(event)->key().matches(QKeySequence::Copy) && index.data().canConvert(QVariant::String)) { QApplication::clipboard()->setText(index.data().toString()); qCWarning(LOKALIZE_LOG) << "index.data().toString()"; } } else if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent* mEvent = static_cast(event); if (mEvent->button() == Qt::MidButton) { } } else if (event->type() == QEvent::KeyPress) { QKeyEvent* kEvent = static_cast(event); if (kEvent->key() == Qt::Key_Return) { if (kEvent->modifiers() == Qt::NoModifier) { } } } else return false; event->accept(); return true; } #endif void TMTab::dragEnterEvent(QDragEnterEvent* event) { if (dragIsAcceptable(event->mimeData()->urls())) event->acceptProposedAction(); } void TMTab::dropEvent(QDropEvent *event) { QStringList files; foreach (const QUrl& url, event->mimeData()->urls()) files.append(url.toLocalFile()); if (scanRecursive(files, Project::instance()->projectID())) event->acceptProposedAction(); } #ifndef NOKDE #include "translationmemoryadaptor.h" #endif //BEGIN DBus interface QList TMTab::ids; QString TMTab::dbusObjectPath() { #ifndef NOKDE const QString TM_PATH = QStringLiteral("/ThisIsWhatYouWant/TranslationMemory/"); if (m_dbusId == -1) { new TranslationMemoryAdaptor(this); int i = 0; while (i < ids.size() && i == ids.at(i)) ++i; ids.insert(i, i); m_dbusId = i; QDBusConnection::sessionBus().registerObject(TM_PATH + QString::number(m_dbusId), this); } return TM_PATH + QString::number(m_dbusId); #else return QString(); #endif } void TMTab::lookup(QString source, QString target) { source.remove(Project::instance()->accel()); target.remove(Project::instance()->accel()); ui_queryOptions->querySource->setText(source); ui_queryOptions->queryTarget->setText(target); ui_queryOptions->invertSource->setChecked(false); ui_queryOptions->invertTarget->setChecked(false); ui_queryOptions->queryStyle->setCurrentIndex(TMDBModel::SubStr); performQuery(); } // void TMTab::lookup(DocPosition::Part part, QString text) // { // lookup(part==DocPosition::Source?text:QString(),part==DocPosition::Target?text:QString()); // } bool TMTab::findGuiTextPackage(QString text, QString package) { qCWarning(LOKALIZE_LOG) << package << text; QLineEdit* const source_target_query[] = {ui_queryOptions->queryTarget, ui_queryOptions->querySource}; static const DocPosition::Part source_target[] = {DocPosition::Target, DocPosition::Source}; QTextCodec* latin1 = QTextCodec::codecForMib(4); DocPosition::Part tryNowPart = source_target[latin1->canEncode(text)]; m_partToAlsoTryLater = source_target[tryNowPart == DocPosition::Target]; text.remove(Project::instance()->accel()); ui_queryOptions->querySource->clear(); ui_queryOptions->queryTarget->clear(); source_target_query[tryNowPart == DocPosition::Source]->setText(text); ui_queryOptions->invertSource->setChecked(false); ui_queryOptions->invertTarget->setChecked(false); if (!package.isEmpty()) package = '*' % package % '*'; ui_queryOptions->filemask->setText(package); ui_queryOptions->queryStyle->setCurrentIndex(TMDBModel::Glob); performQuery(); return true; } //END DBus interface