1ea7cb4dc82585f9233cdd9cdd7c07682ba9ef20
[mstardict] / src / mstardict.cpp
1 /*
2  *  MStarDict - International dictionary for Maemo.
3  *  Copyright (C) 2010 Roman Moravcik
4  *
5  *  base on code of stardict:
6  *  Copyright (C) 2003-2007 Hu Zheng <huzheng_001@163.com>
7  *
8  *  based on code of sdcv:
9  *  Copyright (C) 2005-2006 Evgeniy <dushistov@mail.ru>
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include <cerrno>
31 #include <cstring>
32 #include <cstdlib>
33 #include <cstdio>
34 #include <clocale>
35
36 #include <glib.h>
37 #include <glib/gi18n.h>
38 #include <glib/gstdio.h>
39
40 #include <gtk/gtk.h>
41 #include <hildon/hildon.h>
42
43 #include <getopt.h>
44 #include <string>
45 #include <vector>
46 #include <memory>
47 #include <list>
48
49 #include "conf.hpp"
50 #include "dictmngr.hpp"
51 #include "libwrapper.hpp"
52 #include "mstardict.hpp"
53
54 enum {
55     DEF_COLUMN,
56     N_COLUMNS
57 };
58
59 MStarDict::MStarDict()
60 {
61     main_window = NULL;
62     label_widget = NULL;
63     results_widget = NULL;
64     results_view = NULL;
65     results_view_scroll = NULL;
66
67     /* create list of ressults */
68     results_list = gtk_list_store_new(N_COLUMNS,
69                                       G_TYPE_STRING);   /* DEF_COLUMN */
70
71     /* initialize configuration */
72     oConf = new Conf();
73
74     /* initialize dict manager */
75     oDict = new DictMngr(this);
76
77     /* initialize stardict library */
78     oLibs = new Library(this);
79 }
80
81 MStarDict::~MStarDict()
82 {
83     /* destroy list of results */
84     g_object_unref(results_list);
85
86     /* deinitialize stardict library */
87     delete oLibs;
88
89     /* deinitialize dict manager */
90     delete oDict;
91
92     /* deinitialize configuration */
93     delete oConf;
94 }
95
96 gboolean
97 MStarDict::onResultsViewSelectionChanged(GtkTreeSelection *selection,
98                                          MStarDict *mStarDict)
99 {
100     GtkTreeModel *model;
101     GtkTreeIter iter;
102     char *bookname, *def, *exp;
103     const gchar *sWord;
104     bool bFound = false;
105
106     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
107         /* unselect selected rows */
108         gtk_tree_selection_unselect_all(selection);
109
110         gtk_tree_model_get(model, &iter, DEF_COLUMN, &sWord, -1);
111
112         /* clear previous search results */
113         mStarDict->results.clear();
114
115         for (size_t iLib = 0; iLib < mStarDict->oLibs->query_dictmask.size(); iLib++) {
116             bFound =
117                 mStarDict->oLibs->BuildResultData(mStarDict->oLibs->query_dictmask, sWord,
118                                                   mStarDict->oLibs->iCurrentIndex, iLib,
119                                                   mStarDict->results);
120         }
121
122         bookname =
123             g_markup_printf_escaped
124             ("<span color=\"dimgray\" size=\"x-small\">%s</span>",
125              mStarDict->results[0].bookname.c_str());
126         def =
127             g_markup_printf_escaped
128             ("<span color=\"darkred\" weight=\"heavy\" size=\"large\">%s</span>",
129              mStarDict->results[0].def.c_str());
130         exp = g_strdup(mStarDict->results[0].exp.c_str());
131
132         /* create translation window */
133         mStarDict->CreateTranslationWindow(bookname, def, exp);
134
135         g_free(bookname);
136         g_free(def);
137         g_free(exp);
138     }
139
140     /* grab focus to search entry */
141     gtk_widget_grab_focus(GTK_WIDGET(mStarDict->search));
142
143     return true;
144 }
145
146 gboolean
147 MStarDict::onSearchEntryChanged(GtkEditable* editable,
148                                 MStarDict* mStarDict)
149 {
150     const gchar *sWord;
151     bool bFound = false;
152     std::string query;
153
154     if (mStarDict->oLibs->query_dictmask.empty())
155         return true;
156
157     sWord = gtk_entry_get_text(GTK_ENTRY(editable));
158     if (strcmp(sWord, "") == 0) {
159         mStarDict->ShowNoResults(true);
160     } else {
161         mStarDict->ShowProgressIndicator(true);
162         mStarDict->ResultsUnselectAll(GTK_SELECTION_NONE);
163
164         switch (analyse_query(sWord, query)) {
165         case qtFUZZY:
166             bFound = mStarDict->oLibs->LookupWithFuzzy(query.c_str());
167             break;
168
169         case qtPATTERN:
170             bFound = mStarDict->oLibs->LookupWithRule(query.c_str());
171             break;
172
173         case qtREGEX:
174             bFound = mStarDict->oLibs->LookupWithRegex(query.c_str());
175             break;
176
177         case qtSIMPLE:
178             bFound = mStarDict->oLibs->SimpleLookup(query.c_str(), mStarDict->oLibs->iCurrentIndex);
179             if (!bFound) {
180                 const gchar *sugWord = mStarDict->oLibs->GetSuggestWord(query.c_str(),
181                                                                         mStarDict->
182                                                                         oLibs->iCurrentIndex,
183                                                                         mStarDict->
184                                                                         oLibs->query_dictmask, 0);
185                 if (sugWord) {
186                     gchar *sSugWord = g_strdup(sugWord);
187                     bFound =
188                         mStarDict->oLibs->SimpleLookup(sSugWord, mStarDict->oLibs->iCurrentIndex);
189                     g_free(sSugWord);
190                 }
191             }
192             mStarDict->oLibs->ListWords(mStarDict->oLibs->iCurrentIndex);
193             break;
194
195         default:
196             break;
197         }
198
199         if (bFound)
200             mStarDict->ShowNoResults(false);
201         else
202             mStarDict->ShowNoResults(true);
203
204         mStarDict->ResultsUnselectAll(GTK_SELECTION_SINGLE);
205         mStarDict->ShowProgressIndicator(false);
206     }
207
208     return true;
209 }
210
211 gboolean
212 MStarDict::onDictionariesMenuItemClicked(GtkButton *button,
213                                          MStarDict *mStarDict)
214 {
215     mStarDict->oDict->CreateDictMngrDialog();
216     return true;
217 }
218
219 gboolean
220 MStarDict::onQuitMenuItemClicked(GtkButton *button,
221                                  MStarDict *mStarDict)
222 {
223     gtk_main_quit();
224     return true;
225 }
226
227 gboolean
228 MStarDict::onLookupProgressDialogResponse(GtkDialog *dialog,
229                                           gint response_id,
230                                           bool *cancel)
231 {
232     *cancel = true;
233     return true;
234 }
235
236 gboolean
237 MStarDict::onMainWindowKeyPressEvent(GtkWidget *window,
238                                      GdkEventKey *event,
239                                      MStarDict *mStarDict)
240 {
241     if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KP_Enter) {
242         mStarDict->SearchWord();
243     }
244     return false;
245 }
246
247 GtkWidget *
248 MStarDict::CreateLookupProgressDialog(bool *cancel)
249 {
250     GtkWidget *dialog, *progress;
251
252     /* create dialog */
253     dialog = gtk_dialog_new();
254     gtk_window_set_title(GTK_WINDOW(dialog), _("Searching"));
255     gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_window));
256     gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_OK);
257
258     g_signal_connect(dialog, "response", G_CALLBACK(onLookupProgressDialogResponse), cancel);
259
260     /* add progress bar */
261     progress = gtk_progress_bar_new();
262     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), progress);
263     g_object_set_data(G_OBJECT(dialog), "progress", progress);
264
265     /* show dialog */
266     gtk_widget_show_all(dialog);
267
268     while (gtk_events_pending())
269         gtk_main_iteration();
270
271     return dialog;
272 }
273
274 void
275 MStarDict::DestroyLookupProgressDialog(GtkWidget *dialog)
276 {
277     gtk_widget_destroy(GTK_WIDGET(dialog));
278 }
279
280 void
281 MStarDict::CreateTranslationWindow(const gchar *bookname,
282                                    const gchar *def,
283                                    const gchar *exp)
284 {
285     GtkWidget *window, *alignment, *pannable, *vbox, *label;
286
287     window = hildon_stackable_window_new();
288     gtk_window_set_title(GTK_WINDOW(window), _("Translation"));
289
290     alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
291     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
292                               HILDON_MARGIN_DEFAULT,
293                               HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DOUBLE, HILDON_MARGIN_DEFAULT);
294     gtk_container_add(GTK_CONTAINER(window), alignment);
295
296     pannable = hildon_pannable_area_new();
297     g_object_set(G_OBJECT(pannable), "mov-mode", HILDON_MOVEMENT_MODE_BOTH, NULL);
298     gtk_container_add(GTK_CONTAINER(alignment), pannable);
299
300     vbox = gtk_vbox_new(FALSE, 0);
301     hildon_pannable_area_add_with_viewport(HILDON_PANNABLE_AREA(pannable), vbox);
302
303     label = gtk_label_new("Bookname");
304     gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
305     gtk_label_set_markup(GTK_LABEL(label), bookname);
306     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
307
308     label = gtk_label_new("Definition");
309     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
310     gtk_label_set_markup(GTK_LABEL(label), def);
311     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
312
313     label = gtk_label_new("Expresion");
314     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
315     gtk_label_set_markup(GTK_LABEL(label), exp);
316     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
317
318     gtk_widget_show_all(window);
319 }
320
321 void
322 MStarDict::CreateMainWindow()
323 {
324     HildonProgram *program = NULL;
325     GtkWidget *alignment, *vbox;
326     GtkCellRenderer *renderer;
327     GtkTreeSelection *selection;
328
329     /* hildon program */
330     program = hildon_program_get_instance();
331     g_set_application_name(_("MStardict"));
332
333     /* main window */
334     main_window = hildon_stackable_window_new();
335     hildon_program_add_window(program, HILDON_WINDOW(main_window));
336
337     /* aligment */
338     alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
339     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
340                               HILDON_MARGIN_HALF, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
341     gtk_container_add(GTK_CONTAINER(main_window), alignment);
342
343     /* main vbox */
344     vbox = gtk_vbox_new(FALSE, 0);
345     gtk_container_add(GTK_CONTAINER(alignment), vbox);
346
347     /* no_search_result label */
348     label_widget = gtk_label_new(_("No search result"));
349     hildon_helper_set_logical_color(label_widget, GTK_RC_FG,
350                                     GTK_STATE_NORMAL, "SecondaryTextColor");
351     hildon_helper_set_logical_font(label_widget, "LargeSystemFont");
352     gtk_box_pack_start(GTK_BOX(vbox), label_widget, TRUE, TRUE, 0);
353
354     /* alignment for pannable area */
355     results_widget = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
356     gtk_alignment_set_padding(GTK_ALIGNMENT(results_widget),
357                               0, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
358     gtk_box_pack_start(GTK_BOX(vbox), results_widget, TRUE, TRUE, 0);
359
360     /* pannable for tree view */
361     results_view_scroll = hildon_pannable_area_new();
362     gtk_container_add(GTK_CONTAINER(results_widget), results_view_scroll);
363
364     /* result tree view */
365     results_view = hildon_gtk_tree_view_new(HILDON_UI_MODE_EDIT);
366     gtk_tree_view_set_model(GTK_TREE_VIEW(results_view), GTK_TREE_MODEL(results_list));
367     gtk_container_add(GTK_CONTAINER(results_view_scroll), results_view);
368
369     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(results_view));
370     g_signal_connect(selection, "changed", G_CALLBACK(onResultsViewSelectionChanged), this);
371
372     /* def column */
373     renderer = gtk_cell_renderer_text_new();
374     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW
375                                                 (results_view), -1, "Def",
376                                                 renderer, "text", DEF_COLUMN, NULL);
377     g_object_set(G_OBJECT(renderer), "xpad", 10, NULL);
378
379     /* search entry */
380     search = hildon_entry_new(HILDON_SIZE_FINGER_HEIGHT);
381     gtk_box_pack_end(GTK_BOX(vbox), search, FALSE, TRUE, 0);
382     g_signal_connect(search, "changed", G_CALLBACK(onSearchEntryChanged), this);
383
384     /* window signals */
385     g_signal_connect(G_OBJECT(main_window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
386     g_signal_connect(G_OBJECT(main_window), "key_press_event", G_CALLBACK(onMainWindowKeyPressEvent), this);
387
388     /* show all widget instead of alignment */
389     gtk_widget_show_all(GTK_WIDGET(main_window));
390
391     /* grab focus to search entry */
392     gtk_widget_grab_focus(GTK_WIDGET(search));
393 }
394
395 void
396 MStarDict::CreateMainMenu()
397 {
398     HildonAppMenu *menu;
399     GtkWidget *item;
400
401     menu = HILDON_APP_MENU(hildon_app_menu_new());
402     hildon_window_set_app_menu(HILDON_WINDOW(main_window), menu);
403
404     /* dictionaries menu item */
405     item = hildon_gtk_button_new(HILDON_SIZE_AUTO);
406     gtk_button_set_label(GTK_BUTTON(item), _("Dictionaries"));
407     hildon_app_menu_append(menu, GTK_BUTTON(item));
408     g_signal_connect(item, "clicked", G_CALLBACK(onDictionariesMenuItemClicked), this);
409
410     /* quit menu item */
411     item = hildon_gtk_button_new(HILDON_SIZE_AUTO);
412     gtk_button_set_label(GTK_BUTTON(item), _("Quit"));
413     hildon_app_menu_append(menu, GTK_BUTTON(item));
414     g_signal_connect(item, "clicked", G_CALLBACK(onQuitMenuItemClicked), this);
415
416     /* show main menu */
417     gtk_widget_show_all(GTK_WIDGET(menu));
418 }
419
420 void
421 MStarDict::SearchWord()
422 {
423     const gchar *sWord;
424     bool bFound = false;
425     std::string query;
426
427     if (oLibs->query_dictmask.empty())
428         return;
429
430     sWord = gtk_entry_get_text(GTK_ENTRY(search));
431     if (strcmp(sWord, "") == 0) {
432         ShowNoResults(true);
433     } else {
434         /* unselect rows */
435         ResultsUnselectAll(GTK_SELECTION_NONE);
436
437         switch (analyse_query(sWord, query)) {
438         case qtDATA:
439             bFound = oLibs->LookupData(query.c_str());
440             break;
441         default:
442             /* nothing */ ;
443         }
444
445         /* unselect selected rows */
446         ResultsUnselectAll(GTK_SELECTION_SINGLE);
447
448         if (bFound)
449             ShowNoResults(false);
450         else
451             ShowNoResults(true);
452     }
453 }
454
455 void
456 MStarDict::ResultsListClear()
457 {
458     gtk_list_store_clear(results_list);
459 }
460
461 void
462 MStarDict::ResultsListInsertLast(const gchar *word)
463 {
464     GtkTreeIter iter;
465     gtk_list_store_append(results_list, &iter);
466     gtk_list_store_set(results_list, &iter, DEF_COLUMN, word, -1);
467 }
468
469 void
470 MStarDict::ResultsReScroll()
471 {
472     hildon_pannable_area_scroll_to(HILDON_PANNABLE_AREA(results_view_scroll), -1, 0);
473 }
474
475 void
476 MStarDict::ResultsUnselectAll(GtkSelectionMode mode)
477 {
478     GtkTreeSelection *selection;
479
480     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(results_view));
481     gtk_tree_selection_set_mode(selection, mode);
482     gtk_tree_selection_unselect_all(selection);
483 }
484
485 void
486 MStarDict::ShowNoResults(bool bNoResults)
487 {
488     if (bNoResults) {
489         gtk_label_set_text(GTK_LABEL(label_widget), _("No search result"));
490         gtk_widget_show(label_widget);
491         gtk_widget_hide(results_widget);
492     } else {
493         gtk_widget_hide(label_widget);
494         gtk_widget_show(results_widget);
495     }
496 }
497
498 void
499 MStarDict::ShowNoDictionary(bool bNoDictionary)
500 {
501     if (bNoDictionary) {
502         gtk_label_set_text(GTK_LABEL(label_widget), _("No loaded dictionary"));
503         gtk_widget_show(label_widget);
504         gtk_widget_hide(results_widget);
505     } else {
506         gtk_widget_hide(label_widget);
507         gtk_widget_show(results_widget);
508     }
509 }
510
511 void
512 MStarDict::ShowProgressIndicator(bool bShow)
513 {
514     if (bShow)
515         hildon_gtk_window_set_progress_indicator(GTK_WINDOW(main_window), 1);
516     else
517         hildon_gtk_window_set_progress_indicator(GTK_WINDOW(main_window), 0);
518 }
519
520 int
521 main(int argc,
522      char **argv)
523 {
524     /* initialize hildon */
525     hildon_gtk_init(&argc, &argv);
526
527     /* initialize localization */
528     setlocale(LC_ALL, "");
529     bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
530     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
531     textdomain(GETTEXT_PACKAGE);
532
533     /* create main window */
534     MStarDict mStarDict;
535     mStarDict.CreateMainWindow();
536     mStarDict.CreateMainMenu();
537     mStarDict.ShowNoResults(true);
538
539     /* load dictionaries */
540     mStarDict.oDict->LoadDictionaries();
541
542     gtk_main();
543     return 0;
544 }