Warning, file /sdk/lokalize/src/phaseswindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002   This file is part of Lokalize
0003 
0004   SPDX-FileCopyrightText: 2009-2014 Nick Shaforostoff <shafff@ukr.net>
0005   SPDX-FileCopyrightText: 2018-2019 Simon Depiets <sdepiets@gmail.com>
0006 
0007   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0008 */
0009 
0010 #include "phaseswindow.h"
0011 #include "catalog.h"
0012 #include "cmd.h"
0013 #include "noteeditor.h"
0014 #include "project.h"
0015 
0016 #include <kcombobox.h>
0017 #include <klocalizedstring.h>
0018 #include <kstandardguiitem.h>
0019 
0020 #include <QStringBuilder>
0021 #include <QPushButton>
0022 #include <QTextBrowser>
0023 #include <QTreeView>
0024 #include <QStringListModel>
0025 #include <QVBoxLayout>
0026 #include <QFormLayout>
0027 #include <QApplication>
0028 #include <QAbstractListModel>
0029 #include <QSplitter>
0030 #include <QStackedLayout>
0031 #include <QDialogButtonBox>
0032 
0033 //BEGIN PhasesModel
0034 class PhasesModel: public QAbstractListModel
0035 {
0036 public:
0037     enum PhasesModelColumns {
0038         Date = 0,
0039         Process,
0040         Company,
0041         Contact,
0042         ToolName,
0043         ColumnCount
0044     };
0045 
0046     PhasesModel(Catalog* catalog, QObject* parent);
0047     ~PhasesModel() {}
0048     QModelIndex addPhase(const Phase& phase);
0049     QModelIndex activePhaseIndex()const
0050     {
0051         return index(m_activePhase);
0052     }
0053     QList<Phase> addedPhases()const;
0054 
0055     int rowCount(const QModelIndex& parent = QModelIndex()) const override;
0056     int columnCount(const QModelIndex& parent = QModelIndex()) const override
0057     {
0058         Q_UNUSED(parent);
0059         return ColumnCount;
0060     }
0061     QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override;
0062     QVariant headerData(int section, Qt::Orientation, int role = Qt::DisplayRole) const override;
0063 
0064 
0065 private:
0066     Catalog* m_catalog;
0067     QList<Phase> m_phases;
0068     QMap<QString, Tool> m_tools;
0069     int m_activePhase;
0070 };
0071 
0072 PhasesModel::PhasesModel(Catalog* catalog, QObject* parent)
0073     : QAbstractListModel(parent)
0074     , m_catalog(catalog)
0075     , m_phases(catalog->allPhases())
0076     , m_tools(catalog->allTools())
0077 {
0078     m_activePhase = m_phases.size();
0079     while (--m_activePhase >= 0 && m_phases.at(m_activePhase).name != catalog->activePhase())
0080         ;
0081 }
0082 
0083 QModelIndex PhasesModel::addPhase(const Phase& phase)
0084 {
0085     m_activePhase = m_phases.size();
0086     beginInsertRows(QModelIndex(), m_activePhase, m_activePhase);
0087     m_phases.append(phase);
0088     endInsertRows();
0089     return index(m_activePhase);
0090 }
0091 
0092 QList<Phase> PhasesModel::addedPhases()const
0093 {
0094     QList<Phase> result;
0095     for (int i = m_catalog->allPhases().size(); i < m_phases.size(); ++i)
0096         result.append(m_phases.at(i));
0097 
0098     return result;
0099 }
0100 
0101 int PhasesModel::rowCount(const QModelIndex& parent) const
0102 {
0103     if (parent.isValid())
0104         return 0;
0105     return m_phases.size();
0106 }
0107 
0108 QVariant PhasesModel::data(const QModelIndex& index, int role) const
0109 {
0110     if (role == Qt::FontRole && index.row() == m_activePhase) {
0111         QFont font = QApplication::font();
0112         font.setBold(true);
0113         return font;
0114     }
0115     if (role == Qt::UserRole)
0116         return m_phases.at(index.row()).name;
0117     if (role != Qt::DisplayRole)
0118         return QVariant();
0119 
0120     const Phase& phase = m_phases.at(index.row());
0121     switch (index.column()) {
0122     case Date:       return phase.date.toString();
0123     case Process:    return phase.process;
0124     case Company:    return phase.company;
0125     case Contact:    return QString(phase.contact
0126                                         % (phase.email.isEmpty() ? QString() : QStringLiteral(" <%1> ").arg(phase.email))
0127                                         % (phase.phone.isEmpty() ? QString() : QStringLiteral(", %1").arg(phase.phone)));
0128     case ToolName:       return m_tools.value(phase.tool).name;
0129     }
0130     return QVariant();
0131 }
0132 
0133 QVariant PhasesModel::headerData(int section, Qt::Orientation, int role) const
0134 {
0135     if (role != Qt::DisplayRole)
0136         return QVariant();
0137 
0138     switch (section) {
0139     case Date:       return i18nc("@title:column", "Date");
0140     case Process:    return i18nc("@title:column", "Process");
0141     case Company:    return i18nc("@title:column", "Company");
0142     case Contact:    return i18nc("@title:column", "Person");
0143     case ToolName:   return i18nc("@title:column", "Tool");
0144     }
0145     return QVariant();
0146 }
0147 //END PhasesModel
0148 
0149 
0150 //BEGIN PhaseEditDialog
0151 class PhaseEditDialog: public QDialog
0152 {
0153 public:
0154     PhaseEditDialog(QWidget *parent);
0155     ~PhaseEditDialog() {}
0156 
0157     Phase phase()const;
0158     ProjectLocal::PersonRole role()const;
0159 private:
0160     KComboBox* m_process;
0161 };
0162 
0163 
0164 PhaseEditDialog::PhaseEditDialog(QWidget *parent)
0165     : QDialog(parent)
0166     , m_process(new KComboBox(this))
0167 {
0168     QStringList processes;
0169     processes << i18n("Translation") << i18n("Review") << i18n("Approval");
0170     m_process->setModel(new QStringListModel(processes, this));
0171 
0172     QFormLayout* l = new QFormLayout(this);
0173     l->addRow(i18nc("noun", "Process (this will also change your role):"), m_process);
0174 
0175     QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
0176     connect(buttonBox, &QDialogButtonBox::accepted, this, &PhaseEditDialog::accept);
0177     connect(buttonBox, &QDialogButtonBox::rejected, this, &PhaseEditDialog::reject);
0178     l->addRow(buttonBox);
0179 }
0180 
0181 Phase PhaseEditDialog::phase() const
0182 {
0183     Phase phase;
0184     phase.process = processes()[m_process->currentIndex()];
0185     return phase;
0186 }
0187 
0188 ProjectLocal::PersonRole PhaseEditDialog::role() const
0189 {
0190     return (ProjectLocal::PersonRole)m_process->currentIndex();
0191 }
0192 
0193 
0194 PhasesWindow::PhasesWindow(Catalog* catalog, QWidget *parent)
0195     : QDialog(parent)
0196     , m_catalog(catalog)
0197     , m_model(new PhasesModel(catalog, this))
0198     , m_view(new MyTreeView(this))
0199     , m_browser(new QTextBrowser(this))
0200     , m_editor(nullptr)
0201 {
0202     connect(this, &PhasesWindow::accepted, this, &PhasesWindow::handleResult);
0203     //setAttribute(Qt::WA_DeleteOnClose, true);
0204     QVBoxLayout* l = new QVBoxLayout(this);
0205     QHBoxLayout* btns = new QHBoxLayout;
0206     l->addLayout(btns);
0207 
0208     QPushButton* add = new QPushButton(this);
0209     KGuiItem::assign(add, KStandardGuiItem::add());
0210 
0211     connect(add, &QPushButton::clicked, this, &PhasesWindow::addPhase);
0212     btns->addWidget(add);
0213     btns->addStretch(5);
0214 
0215     QSplitter* splitter = new QSplitter(this);
0216     l->addWidget(splitter);
0217 
0218     m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
0219     connect(m_buttonBox, &QDialogButtonBox::accepted, this, &PhasesWindow::accept);
0220     connect(m_buttonBox, &QDialogButtonBox::rejected, this, &PhasesWindow::reject);
0221     l->addWidget(m_buttonBox);
0222 
0223 
0224     m_view->setRootIsDecorated(false);
0225     m_view->setModel(m_model);
0226     splitter->addWidget(m_view);
0227     int column = m_model->columnCount();
0228     while (--column >= 0)
0229         m_view->resizeColumnToContents(column);
0230     if (m_model->rowCount())
0231         m_view->setCurrentIndex(m_model->activePhaseIndex());
0232     connect(m_view, &MyTreeView::currentIndexChanged, this, &PhasesWindow::displayPhaseNotes);
0233 
0234 
0235     m_noteView = new QWidget(this);
0236     m_noteView->hide();
0237     splitter->addWidget(m_noteView);
0238     m_stackedLayout = new QStackedLayout(m_noteView);
0239     m_stackedLayout->addWidget(m_browser);
0240 
0241     m_browser->viewport()->setBackgroundRole(QPalette::Window);
0242     m_browser->setOpenLinks(false);
0243     connect(m_browser, &QTextBrowser::anchorClicked, this, &PhasesWindow::anchorClicked);
0244 
0245     splitter->setStretchFactor(0, 15);
0246     splitter->setStretchFactor(1, 5);
0247     resize(QSize(700, 400));
0248 }
0249 
0250 void PhasesWindow::handleResult()
0251 {
0252     m_catalog->beginMacro(i18nc("@item Undo action item", "Edit phases"));
0253 
0254     Phase last;
0255     const auto addedPhases = m_model->addedPhases();
0256     for (const Phase& phase : addedPhases)
0257         static_cast<QUndoStack*>(m_catalog)->push(new UpdatePhaseCmd(m_catalog, last = phase));
0258     Project::instance()->local()->setRole(roleForProcess(last.process));
0259     m_catalog->setActivePhase(last.name, roleForProcess(last.process));
0260 
0261     QMapIterator<QString, QVector<Note> > i(m_phaseNotes);
0262     while (i.hasNext()) {
0263         i.next();
0264         m_catalog->setPhaseNotes(i.key(), i.value());
0265     }
0266 
0267     m_catalog->endMacro();
0268 }
0269 
0270 void PhasesWindow::addPhase()
0271 {
0272     PhaseEditDialog d(this);
0273     if (!d.exec())
0274         return;
0275 
0276     Phase phase = d.phase();
0277     initPhaseForCatalog(m_catalog, phase, ForceAdd);
0278     m_view->setCurrentIndex(m_model->addPhase(phase));
0279     m_phaseNotes.insert(phase.name, QVector<Note>());
0280 
0281     m_buttonBox->button(QDialogButtonBox::Ok)->setFocus();
0282 }
0283 
0284 static QString phaseNameFromView(QTreeView* view)
0285 {
0286     return view->currentIndex().data(Qt::UserRole).toString();
0287 }
0288 
0289 void PhasesWindow::anchorClicked(QUrl link)
0290 {
0291     QString path = link.path().mid(1); // minus '/'
0292 
0293     if (link.scheme() == QLatin1String("note")) {
0294         if (!m_editor) {
0295             m_editor = new NoteEditor(this);
0296             m_stackedLayout->addWidget(m_editor);
0297             connect(m_editor, &NoteEditor::accepted, this, &PhasesWindow::noteEditAccepted);
0298             connect(m_editor, &NoteEditor::rejected, this, &PhasesWindow::noteEditRejected);
0299         }
0300         m_editor->setNoteAuthors(m_catalog->noteAuthors());
0301         if (path.endsWith(QLatin1String("add")))
0302             m_editor->setNote(Note(), -1);
0303         else {
0304             int pos = path.toInt();
0305             QString phaseName = phaseNameFromView(m_view);
0306             QVector<Note> notes = m_phaseNotes.contains(phaseName) ?
0307                                   m_phaseNotes.value(phaseName)
0308                                   : m_catalog->phaseNotes(phaseName);
0309             m_editor->setNote(notes.at(pos), pos);
0310         }
0311         m_stackedLayout->setCurrentIndex(1);
0312     }
0313 }
0314 
0315 void PhasesWindow::noteEditAccepted()
0316 {
0317     QString phaseName = phaseNameFromView(m_view);
0318     if (!m_phaseNotes.contains(phaseName))
0319         m_phaseNotes.insert(phaseName, m_catalog->phaseNotes(phaseName));
0320 
0321     //QVector<Note> notes=m_phaseNotes.value(phaseName);
0322     if (m_editor->noteIndex() == -1)
0323         m_phaseNotes[phaseName].append(m_editor->note());
0324     else
0325         m_phaseNotes[phaseName][m_editor->noteIndex()] = m_editor->note();
0326 
0327     m_stackedLayout->setCurrentIndex(0);
0328     displayPhaseNotes(m_view->currentIndex());
0329 }
0330 
0331 void PhasesWindow::noteEditRejected()
0332 {
0333     m_stackedLayout->setCurrentIndex(0);
0334 }
0335 
0336 void PhasesWindow::displayPhaseNotes(const QModelIndex& current)
0337 {
0338     m_browser->clear();
0339     QString phaseName = current.data(Qt::UserRole).toString();
0340     QVector<Note> notes = m_phaseNotes.contains(phaseName) ?
0341                           m_phaseNotes.value(phaseName)
0342                           : m_catalog->phaseNotes(phaseName);
0343     displayNotes(m_browser, notes);
0344     m_noteView->show();
0345     m_stackedLayout->setCurrentIndex(0);
0346 }
0347