Allow to lookup Italian verbs and show pronouns.
[mverbiste] / mainwindow.cpp
1 #include "mainwindow.h"
2 #include "ui_mainwindow.h"
3 #include "gui/conjugation.h"
4
5 #include <QtCore/QCoreApplication>
6
7 MainWindow::MainWindow(QWidget *parent)
8     : QMainWindow(parent), ui(new Ui::MainWindow)
9 {
10 #ifdef Q_WS_MAEMO_5
11     this->setAttribute(Qt::WA_Maemo5StackedWindow);
12     this->setWindowFlags(Qt::Window);
13 #endif
14     ui->setupUi(this);
15     setupcodedUI();
16 }
17
18 void MainWindow::setupcodedUI()
19 {
20     cent = centralWidget();
21     mlayout = new QVBoxLayout;
22     btlayout = new QHBoxLayout;
23
24     resultPages = new QTabWidget;
25     resultPages->setStyleSheet("QTabBar::tab { height: 50px }");
26     mlayout->addWidget(resultPages);
27
28     btnPron = new QCheckBox();
29     btnPron->setIcon(QIcon("/usr/share/icons/hicolor/48x48/hildon/general_conference_avatar.png"));
30     btnClear = new QPushButton;   /* Clearbutton */
31     btnClear->setIcon(QIcon("/usr/share/icons/hicolor/64x64/hildon/general_delete.png"));
32     wordinput = new QLineEdit;    /* Word input */
33     btlayout->addWidget(btnPron);
34     btlayout->addWidget(btnClear);
35     btlayout->addWidget(wordinput);
36     btnLookup = new QPushButton;  /* Lookup button */
37     btnLookup->setIcon(QIcon("/usr/share/icons/hicolor/64x64/hildon/general_search.png"));
38     btlayout->addWidget(btnLookup);
39
40     mlayout->addLayout(btlayout);
41     cent->setLayout(mlayout);
42
43     // Clear the word input when Clear button is tapped
44     connect(btnClear, SIGNAL(clicked()), this, SLOT(startAgain()));
45
46     connect(wordinput, SIGNAL(returnPressed()), this, SLOT(startLookup()));
47     connect(btnLookup, SIGNAL(clicked()), this, SLOT(startLookup()));
48     connect(btnPron, SIGNAL(clicked()), this, SLOT(startLookup()));
49
50     /* Icon */
51     QIcon *icon = new QIcon();
52     icon->addFile(ICONFILE);
53     setWindowIcon(*icon);
54
55     /* About Dialog */
56     aboutDialog = new AboutDialog(ICONFILE, QString("MVerbiste v%1").arg(VERSTR));
57     aboutDialog->setIntro(trUtf8("A French conjugation utility for Maemo and MeeGo"));
58     aboutDialog->addAuthor(QString::fromUtf8("Nguyễn Hồng Quân <ng.hong.quan@gmail.com>\nPierre Sarrazin <sarrazip@sarrazip.com>"));
59
60     /* Menu */
61     QMenu *menu = ui->menuBar->addMenu(tr("Top menu"));
62     QAction *act_about = menu->addAction(tr("About"));
63     connect(act_about, SIGNAL(triggered()), aboutDialog, SLOT(show()));
64     /* Menu filters */
65     QActionGroup *filterGroup = new QActionGroup(this);
66     filterGroup->setExclusive(true);
67     filFrench = new QAction(tr("Search French"), filterGroup);
68     filFrench->setCheckable(true);
69     filFrench->setChecked(true);
70     filItalian = new QAction(tr("Search Italian"), filterGroup);
71     filItalian->setCheckable(true);
72     menu->addActions(filterGroup->actions());
73     connect(filItalian, SIGNAL(changed()), this, SLOT(switchLang()));
74     connect(filFrench, SIGNAL(changed()), this, SLOT(switchLang()));
75 }
76
77 MainWindow::~MainWindow()
78 {
79     delete ui;
80     delete freVerbDic;
81     delete aboutDialog;
82 }
83
84 void MainWindow::setOrientation(ScreenOrientation orientation)
85 {
86 #if defined(Q_OS_SYMBIAN)
87     // If the version of Qt on the device is < 4.7.2, that attribute won't work
88     if (orientation != ScreenOrientationAuto) {
89         const QStringList v = QString::fromAscii(qVersion()).split(QLatin1Char('.'));
90         if (v.count() == 3 && (v.at(0).toInt() << 16 | v.at(1).toInt() << 8 | v.at(2).toInt()) < 0x040702) {
91             qWarning("Screen orientation locking only supported with Qt 4.7.2 and above");
92             return;
93         }
94     }
95 #endif // Q_OS_SYMBIAN
96
97     Qt::WidgetAttribute attribute;
98     switch (orientation) {
99 #if QT_VERSION < 0x040702
100     // Qt < 4.7.2 does not yet have the Qt::WA_*Orientation attributes
101     case ScreenOrientationLockPortrait:
102         attribute = static_cast<Qt::WidgetAttribute>(128);
103         break;
104     case ScreenOrientationLockLandscape:
105         attribute = static_cast<Qt::WidgetAttribute>(129);
106         break;
107     default:
108     case ScreenOrientationAuto:
109         attribute = static_cast<Qt::WidgetAttribute>(130);
110         break;
111 #else // QT_VERSION < 0x040702
112     case ScreenOrientationLockPortrait:
113         attribute = Qt::WA_LockPortraitOrientation;
114         break;
115     case ScreenOrientationLockLandscape:
116         attribute = Qt::WA_LockLandscapeOrientation;
117         break;
118     default:
119     case ScreenOrientationAuto:
120         attribute = Qt::WA_AutoOrientation;
121         break;
122 #endif // QT_VERSION < 0x040702
123     };
124     setAttribute(attribute, true);
125 }
126
127 void MainWindow::showExpanded()
128 {
129 #if defined(Q_OS_SYMBIAN) || defined(Q_WS_SIMULATOR)
130     showFullScreen();
131 #elif defined(Q_WS_MAEMO_5)
132     showMaximized();
133 #else
134     show();
135 #endif
136     initverbiste();
137     wordinput->setFocus();
138 }
139
140 void  MainWindow::initverbiste()
141 {
142     langCode = "fr";
143
144     FrenchVerbDictionary::Language lang = FrenchVerbDictionary::parseLanguageCode(langCode);
145     if (lang != FrenchVerbDictionary::FRENCH)
146     {
147         // TODO: If lang code is not supported?
148     }
149
150     /* Create verb dictionary, accept non-accent input */
151     freVerbDic = new FrenchVerbDictionary(true);
152 }
153
154 void MainWindow::switchLang()
155 {
156     FrenchVerbDictionary::Language curlang = freVerbDic->getLanguage();
157     FrenchVerbDictionary::Language targetlang = filItalian->isChecked() ? FrenchVerbDictionary::ITALIAN
158                                                                         : FrenchVerbDictionary::FRENCH;
159     if (curlang == targetlang) {
160         return;
161     }
162     /* If lang change */
163     std::string conjFN, verbsFN;
164     FrenchVerbDictionary::getXMLFilenames(conjFN, verbsFN, targetlang);
165     delete freVerbDic;
166     freVerbDic = new FrenchVerbDictionary(conjFN, verbsFN, true, targetlang);
167 }
168
169 void MainWindow::startLookup()
170 {
171     QString input = wordinput->text().trimmed();
172     if (input.isEmpty()) {
173         return;
174     }
175
176     btnLookup->setText(tr("Please wait..."));
177     btnLookup->setEnabled(false);
178     clearResults();
179     /* Pending the lookup job to the next event loop (redraw the button right now) */
180     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
181
182     /* Get input word to look up */
183     const std::string word = input.toLower().toUtf8().constData();
184
185     /*
186      *  For each possible deconjugation, take the infinitive form and
187      *  obtain its complete conjugation.
188      */
189     std::vector<InflectionDesc> infles;
190     bool includePronouns = btnPron->isChecked();
191     bool isItalian = filItalian->isChecked();          // TODO: Will get this value from external
192
193 #ifndef QT_NO_DEBUG
194     timer.start();
195     qDebug() << "Start " << timer.elapsed();
196 #endif
197     freVerbDic->deconjugate(word, infles);
198
199     resultPages->setUpdatesEnabled(false);
200     std::string prevUTF8Infinitive, prevTemplateName; /* Remember found word
201     to avoid conjugating again */
202
203     for (std::vector<InflectionDesc>::const_iterator it = infles.begin();
204          it != infles.end(); it++)
205     {
206         const InflectionDesc &d = *it;
207
208 #ifndef QT_NO_DEBUG
209         qDebug() << ">> Infinitive " << d.infinitive.c_str();
210         qDebug() << "   Template " << d.templateName.c_str();
211 #endif
212         /* If this infinitive has been conjugated, we skip to the next infinitive */
213         if (d.infinitive == prevUTF8Infinitive && d.templateName == prevTemplateName) {
214             continue;
215         }
216         /* FIXME:
217          * In original source (Verbiste), this checking is done later,
218          * after getConjugation(). I place it here to avoid calling again
219          * multitimes getConjugation(), which is very slow.
220          * We need to test more to see which place is more correct.
221          */
222
223         VVVS conjug;
224 #ifndef QT_NO_DEBUG
225         qDebug() << "   START getConjugation " << timer.elapsed();
226 #endif
227         getConjugation(*freVerbDic, d.infinitive, d.templateName, conjug,
228                #ifndef QT_NO_DEBUG
229                        timer,
230                #endif
231                        includePronouns);
232
233 #ifndef QT_NO_DEBUG
234         qDebug() << "   getConjugation() returns: " << timer.elapsed();
235 #endif
236
237         if (conjug.size() == 0           // if no tenses
238             || conjug[0].size() == 0     // if no infinitive tense
239             || conjug[0][0].size() == 0  // if no person in inf. tense
240             || conjug[0][0][0].empty())  // if infinitive string empty
241         {
242             continue;
243         }
244
245         std::string utf8Infinitive = conjug[0][0][0];
246 #ifndef QT_NO_DEBUG
247         qDebug() << "     Infinitive " << utf8Infinitive.c_str();
248         qDebug() << "     Template " << d.templateName.c_str();
249 #endif
250
251         /* Add result to GUI (not show yet) */
252         ResultPage *rsp = addResultPage(utf8Infinitive);
253
254         /* Get modes and tenses of the verb */
255         int i = 0;
256         for (VVVS::const_iterator t = conjug.begin();
257              t != conjug.end(); t++, i++) {
258             if (i == 1)
259                 i = 4;
260             else if (i == 11)
261                 i = 12;
262             assert(i >= 0 && i < 16);
263
264             int row = i / 4;
265             int col = i % 4;
266
267             std::string utf8TenseName = getTenseNameForTableCell(row, col, isItalian);
268             if (utf8TenseName.empty())
269                 continue;
270
271             QVBoxLayout *cell = makeResultCell(*t, utf8TenseName, word, freVerbDic);
272             rsp->grid->addLayout(cell, row, col);
273         }
274
275         /* Show the result on GUI */
276         rsp->packContent();
277         prevUTF8Infinitive = utf8Infinitive;
278         prevTemplateName = d.templateName;
279     }
280
281     /* Enable the button again */
282     btnLookup->setEnabled(true);
283     btnLookup->setText("");
284     resultPages->setUpdatesEnabled(true);
285 }
286
287 ResultPage* MainWindow::addResultPage(const std::string &labelText)
288 {
289     ResultPage *rp = new ResultPage();
290     QString label = QString::fromUtf8(labelText.c_str());
291     resultPages->addTab(rp->page, label);
292     return rp;
293 }
294
295 void MainWindow::clearResults()
296 {
297     while (resultPages->count()) {
298         int lastIndex = resultPages->count() - 1;
299         resultPages->widget(lastIndex)->deleteLater();
300         resultPages->removeTab(lastIndex);
301     }
302 }
303
304 void MainWindow::startAgain()
305 {
306     wordinput->clear();
307     clearResults();
308     wordinput->setFocus();
309     btnLookup->setEnabled(true);
310 }
311
312 QVBoxLayout* MainWindow::makeResultCell(const VVS &tenseIterator,
313                                         const std::string &tenseName,
314                                         const std::string &inputWord,
315                                         FrenchVerbDictionary *verbDict)
316 {
317     /* Mode & Tense name */
318     QLabel *tenseLabel = new QLabel();
319     tenseLabel->setText(QString::fromUtf8(tenseName.c_str()));
320     tenseLabel->setStyleSheet("QLabel {background-color: #44A51C; "
321                               "padding-left: 10px; padding-right: 10px}");
322
323     /* Conjugaison */
324     QVBoxLayout *vbox = new QVBoxLayout();
325     vbox->addWidget(tenseLabel);
326     QVector<QString> persons = qgetConjugates(*verbDict, tenseIterator,inputWord,
327                                               "<font color='#D20020'>", "</font>");
328     for (int i = 0; i < persons.size(); ++i) {
329         QLabel *lb = new QLabel(persons.at(i));
330         lb->setMargin(4);
331         vbox->addWidget(lb, 1);
332     }
333     return vbox;
334 }
335
336 /**** For ResultPage class ****/
337 ResultPage::ResultPage()
338     : page(new QScrollArea),
339       grid(new QGridLayout)
340 {
341 }
342
343 void ResultPage::packContent()
344 {
345     QWidget *immediate = new QWidget();
346     immediate->setLayout(grid);
347     page->setWidget(immediate);
348 }
349