1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Linguist of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "globals.h"
30#include "mainwindow.h"
31#include "messagemodel.h"
32#include "phrase.h"
33#include "phraseview.h"
34#include "phrasemodel.h"
35#include "simtexth.h"
36
37#include <QHeaderView>
38#include <QKeyEvent>
39#include <QSettings>
40#include <QTreeView>
41#include <QWidget>
42#include <QDebug>
43
44
45QT_BEGIN_NAMESPACE
46
47static QString phraseViewHeaderKey()
48{
49 return settingPath("PhraseViewHeader");
50}
51
52PhraseView::PhraseView(MultiDataModel *model, QList<QHash<QString, QList<Phrase *> > > *phraseDict, QWidget *parent)
53 : QTreeView(parent),
54 m_dataModel(model),
55 m_phraseDict(phraseDict),
56 m_modelIndex(-1),
57 m_doGuesses(true)
58{
59 setObjectName(QLatin1String("phrase list view"));
60
61 m_phraseModel = new PhraseModel(this);
62
63 setModel(m_phraseModel);
64 setAlternatingRowColors(true);
65 setSelectionBehavior(QAbstractItemView::SelectRows);
66 setSelectionMode(QAbstractItemView::SingleSelection);
67 setRootIsDecorated(false);
68 setItemsExpandable(false);
69
70 for (int i = 0; i < 10; i++)
71 (void) new GuessShortcut(i, this, SLOT(guessShortcut(int)));
72
73 header()->setSectionResizeMode(QHeaderView::Interactive);
74 header()->setSectionsClickable(true);
75 header()->restoreState(state: QSettings().value(key: phraseViewHeaderKey()).toByteArray());
76
77 connect(sender: this, SIGNAL(activated(QModelIndex)), receiver: this, SLOT(selectPhrase(QModelIndex)));
78}
79
80PhraseView::~PhraseView()
81{
82 QSettings().setValue(key: phraseViewHeaderKey(), value: header()->saveState());
83 deleteGuesses();
84}
85
86void PhraseView::toggleGuessing()
87{
88 m_doGuesses = !m_doGuesses;
89 update();
90}
91
92void PhraseView::update()
93{
94 setSourceText(model: m_modelIndex, sourceText: m_sourceText);
95}
96
97
98void PhraseView::contextMenuEvent(QContextMenuEvent *event)
99{
100 QModelIndex index = indexAt(p: event->pos());
101 if (!index.isValid())
102 return;
103
104 QMenu *contextMenu = new QMenu(this);
105
106 QAction *insertAction = new QAction(tr(s: "Insert"), contextMenu);
107 connect(sender: insertAction, SIGNAL(triggered()), receiver: this, SLOT(selectPhrase()));
108
109 QAction *editAction = new QAction(tr(s: "Edit"), contextMenu);
110 connect(sender: editAction, SIGNAL(triggered()), receiver: this, SLOT(editPhrase()));
111 Qt::ItemFlags isFromPhraseBook = model()->flags(index) & Qt::ItemIsEditable;
112 editAction->setEnabled(isFromPhraseBook);
113
114 QAction *gotoAction = new QAction(tr(s: "Go to"), contextMenu);
115 connect(sender: gotoAction, SIGNAL(triggered()), receiver: this, SLOT(gotoMessageFromGuess()));
116 gotoAction->setEnabled(!isFromPhraseBook);
117
118 contextMenu->addAction(action: insertAction);
119 contextMenu->addAction(action: editAction);
120 contextMenu->addAction(action: gotoAction);
121
122 contextMenu->exec(pos: event->globalPos());
123 event->accept();
124}
125
126void PhraseView::mouseDoubleClickEvent(QMouseEvent *event)
127{
128 QModelIndex index = indexAt(p: event->pos());
129 if (!index.isValid())
130 return;
131
132 emit phraseSelected(latestModel: m_modelIndex, phrase: m_phraseModel->phrase(index)->target());
133 event->accept();
134}
135
136void PhraseView::guessShortcut(int key)
137{
138 foreach (const Phrase *phrase, m_phraseModel->phraseList())
139 if (phrase->shortcut() == key) {
140 emit phraseSelected(latestModel: m_modelIndex, phrase: phrase->target());
141 return;
142 }
143}
144
145void PhraseView::selectPhrase(const QModelIndex &index)
146{
147 emit phraseSelected(latestModel: m_modelIndex, phrase: m_phraseModel->phrase(index)->target());
148}
149
150void PhraseView::selectPhrase()
151{
152 emit phraseSelected(latestModel: m_modelIndex, phrase: m_phraseModel->phrase(index: currentIndex())->target());
153}
154
155void PhraseView::editPhrase()
156{
157 edit(index: currentIndex());
158}
159
160void PhraseView::gotoMessageFromGuess()
161{
162 emit setCurrentMessageFromGuess(modelIndex: m_modelIndex,
163 cand: m_phraseModel->phrase(index: currentIndex())->candidate());
164}
165
166void PhraseView::setMaxCandidates(const int max)
167{
168 m_maxCandidates = max;
169 emit showFewerGuessesAvailable(canShow: m_maxCandidates > DefaultMaxCandidates);
170}
171
172void PhraseView::moreGuesses()
173{
174 setMaxCandidates(m_maxCandidates + DefaultMaxCandidates);
175 setSourceText(model: m_modelIndex, sourceText: m_sourceText);
176}
177
178void PhraseView::fewerGuesses()
179{
180 setMaxCandidates(m_maxCandidates - DefaultMaxCandidates);
181 setSourceText(model: m_modelIndex, sourceText: m_sourceText);
182}
183
184void PhraseView::resetNumGuesses()
185{
186 setMaxCandidates(DefaultMaxCandidates);
187 setSourceText(model: m_modelIndex, sourceText: m_sourceText);
188}
189
190static CandidateList similarTextHeuristicCandidates(MultiDataModel *model, int mi,
191 const char *text, int maxCandidates)
192{
193 QList<int> scores;
194 CandidateList candidates;
195
196 StringSimilarityMatcher stringmatcher(QString::fromLatin1(str: text));
197
198 for (MultiDataModelIterator it(model, mi); it.isValid(); ++it) {
199 MessageItem *m = it.current();
200 if (!m)
201 continue;
202
203 TranslatorMessage mtm = m->message();
204 if (mtm.type() == TranslatorMessage::Unfinished
205 || mtm.translation().isEmpty())
206 continue;
207
208 QString s = m->text();
209
210 int score = stringmatcher.getSimilarityScore(strCandidate: s);
211
212 if (candidates.count() == maxCandidates && score > scores[maxCandidates - 1])
213 candidates.removeLast();
214 if (candidates.count() < maxCandidates && score >= textSimilarityThreshold ) {
215 Candidate cand(mtm.context(), s, mtm.comment(), mtm.translation());
216
217 int i;
218 for (i = 0; i < candidates.size(); ++i) {
219 if (score >= scores.at(i)) {
220 if (score == scores.at(i)) {
221 if (candidates.at(i) == cand)
222 goto continue_outer_loop;
223 } else {
224 break;
225 }
226 }
227 }
228 scores.insert(i, t: score);
229 candidates.insert(i, t: cand);
230 }
231 continue_outer_loop:
232 ;
233 }
234 return candidates;
235}
236
237
238void PhraseView::setSourceText(int model, const QString &sourceText)
239{
240 m_modelIndex = model;
241 m_sourceText = sourceText;
242 m_phraseModel->removePhrases();
243 deleteGuesses();
244
245 if (model < 0)
246 return;
247
248 foreach (Phrase *p, getPhrases(model, sourceText))
249 m_phraseModel->addPhrase(p);
250
251 if (!sourceText.isEmpty() && m_doGuesses) {
252 CandidateList cl = similarTextHeuristicCandidates(model: m_dataModel, mi: model,
253 text: sourceText.toLatin1(), maxCandidates: m_maxCandidates);
254 int n = 0;
255 foreach (const Candidate &candidate, cl) {
256 QString def;
257 if (n < 9)
258 def = tr(s: "Guess from '%1' (%2)")
259 .arg(args: candidate.context, args: QKeySequence(Qt::CTRL | (Qt::Key_0 + (n + 1)))
260 .toString(format: QKeySequence::NativeText));
261 else
262 def = tr(s: "Guess from '%1'").arg(a: candidate.context);
263 Phrase *guess = new Phrase(candidate.source, candidate.translation, def, candidate, n);
264 m_guesses.append(t: guess);
265 m_phraseModel->addPhrase(p: guess);
266 ++n;
267 }
268 }
269}
270
271QList<Phrase *> PhraseView::getPhrases(int model, const QString &source)
272{
273 QList<Phrase *> phrases;
274 QString f = MainWindow::friendlyString(str: source);
275 QStringList lookupWords = f.split(sep: QLatin1Char(' '));
276
277 foreach (const QString &s, lookupWords) {
278 if (m_phraseDict->at(i: model).contains(akey: s)) {
279 foreach (Phrase *p, m_phraseDict->at(model).value(s)) {
280 if (f.contains(s: MainWindow::friendlyString(str: p->source())))
281 phrases.append(t: p);
282 }
283 }
284 }
285 return phrases;
286}
287
288void PhraseView::deleteGuesses()
289{
290 qDeleteAll(c: m_guesses);
291 m_guesses.clear();
292}
293
294QT_END_NAMESPACE
295

source code of qttools/src/linguist/linguist/phraseview.cpp