Imported version 0.4-1
[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 MStarDict *pMStarDict;
55
56 enum {
57     DEF_COLUMN,
58     N_COLUMNS
59 };
60
61 MStarDict::MStarDict()
62 {
63     main_window = NULL;
64     label_widget = NULL;
65     results_widget = NULL;
66     results_view = NULL;
67     results_view_scroll = NULL;
68
69     /* create list of ressults */
70     results_list = gtk_list_store_new(N_COLUMNS,
71                                       G_TYPE_STRING);   /* DEF_COLUMN */
72
73     /* initialize configuration */
74     oConf = new Conf();
75
76     /* initialize stardict plugins */
77     std::list < std::string > plugin_order_list;
78     std::list < std::string > plugin_disable_list;
79     oStarDictPlugins = new StarDictPlugins("/usr/lib/mstardict/plugins",
80                                            plugin_order_list,
81                                            plugin_disable_list);
82
83     /* initialize dict manager */
84     oDict = new DictMngr(this);
85
86     /* initialize stardict library */
87     oLibs = new Library(this);
88 }
89
90 MStarDict::~MStarDict()
91 {
92     /* destroy list of results */
93     g_object_unref(results_list);
94
95     /* deinitialize stardict library */
96     delete oLibs;
97
98     /* deinitialize dict manager */
99     delete oDict;
100
101     /* deinitialize stardict plugins */
102     delete oStarDictPlugins;
103
104     /* deinitialize configuration */
105     delete oConf;
106 }
107
108 gboolean
109 MStarDict::onResultsViewSelectionChanged(GtkTreeSelection *selection,
110                                          MStarDict *mStarDict)
111 {
112     GtkTreeModel *model;
113     GtkTreeIter iter;
114     char *bookname, *def, *exp;
115     const gchar *sWord;
116     bool bFound = false;
117
118     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
119         /* unselect selected rows */
120         gtk_tree_selection_unselect_all(selection);
121
122         gtk_tree_model_get(model, &iter, DEF_COLUMN, &sWord, -1);
123
124         /* clear previous search results */
125         mStarDict->results.clear();
126
127         for (size_t iLib = 0; iLib < mStarDict->oLibs->query_dictmask.size(); iLib++) {
128             bFound =
129                 mStarDict->oLibs->BuildResultData(mStarDict->oLibs->query_dictmask, sWord,
130                                                   mStarDict->oLibs->iCurrentIndex, iLib,
131                                                   mStarDict->results);
132         }
133
134         bookname =
135             g_markup_printf_escaped
136             ("<span color=\"dimgray\" size=\"x-small\">%s</span>",
137              mStarDict->results[0].bookname.c_str());
138         def =
139             g_markup_printf_escaped
140             ("<span color=\"darkred\" weight=\"heavy\" size=\"large\">%s</span>",
141              mStarDict->results[0].def.c_str());
142         exp = g_strdup(mStarDict->results[0].exp.c_str());
143
144         /* create translation window */
145         mStarDict->CreateTranslationWindow(bookname, def, exp);
146
147         g_free(bookname);
148         g_free(def);
149         g_free(exp);
150     }
151
152     /* grab focus to search entry */
153     mStarDict->GrabFocus();
154
155     return true;
156 }
157
158 gboolean
159 MStarDict::onSearchEntryChanged(GtkEditable* editable,
160                                 MStarDict* mStarDict)
161 {
162     const gchar *sWord;
163     bool bFound = false;
164     std::string query;
165
166     if (mStarDict->oLibs->query_dictmask.empty())
167         return true;
168
169     sWord = gtk_entry_get_text(GTK_ENTRY(editable));
170     if (strcmp(sWord, "") == 0) {
171         mStarDict->ShowNoResults(true);
172     } else {
173         mStarDict->ShowProgressIndicator(true);
174         mStarDict->ResultsUnselectAll(GTK_SELECTION_NONE);
175
176         switch (analyse_query(sWord, query)) {
177         case qtFUZZY:
178             bFound = mStarDict->oLibs->LookupWithFuzzy(query.c_str());
179             break;
180
181         case qtPATTERN:
182             bFound = mStarDict->oLibs->LookupWithRule(query.c_str());
183             break;
184
185         case qtREGEX:
186             bFound = mStarDict->oLibs->LookupWithRegex(query.c_str());
187             break;
188
189         case qtSIMPLE:
190             bFound = mStarDict->oLibs->SimpleLookup(query.c_str(), mStarDict->oLibs->iCurrentIndex);
191             if (!bFound) {
192                 const gchar *sugWord = mStarDict->oLibs->GetSuggestWord(query.c_str(),
193                                                                         mStarDict->
194                                                                         oLibs->iCurrentIndex,
195                                                                         mStarDict->
196                                                                         oLibs->query_dictmask, 0);
197                 if (sugWord) {
198                     gchar *sSugWord = g_strdup(sugWord);
199                     bFound =
200                         mStarDict->oLibs->SimpleLookup(sSugWord, mStarDict->oLibs->iCurrentIndex);
201                     g_free(sSugWord);
202                 }
203             }
204             mStarDict->oLibs->ListWords(mStarDict->oLibs->iCurrentIndex);
205             break;
206
207         default:
208             break;
209         }
210
211         if (bFound)
212             mStarDict->ShowNoResults(false);
213         else
214             mStarDict->ShowNoResults(true);
215
216         mStarDict->ResultsUnselectAll(GTK_SELECTION_SINGLE);
217         mStarDict->ShowProgressIndicator(false);
218     }
219
220     return true;
221 }
222
223 gboolean
224 MStarDict::onDictionariesMenuItemClicked(GtkButton *button,
225                                          MStarDict *mStarDict)
226 {
227     mStarDict->oDict->CreateDictMngrDialog();
228     return true;
229 }
230
231 gboolean
232 MStarDict::onQuitMenuItemClicked(GtkButton *button,
233                                  MStarDict *mStarDict)
234 {
235     gtk_main_quit();
236     return true;
237 }
238
239 gboolean
240 MStarDict::onLookupProgressDialogResponse(GtkDialog *dialog,
241                                           gint response_id,
242                                           bool *cancel)
243 {
244     *cancel = true;
245     return true;
246 }
247
248 gboolean
249 MStarDict::onMainWindowKeyPressEvent(GtkWidget *window,
250                                      GdkEventKey *event,
251                                      MStarDict *mStarDict)
252 {
253     if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KP_Enter) {
254         mStarDict->SearchWord();
255     } else if (event->type == GDK_KEY_PRESS && event->keyval >= 0x21 && event->keyval <= 0x7E) {
256         mStarDict->GrabFocus();
257     }
258     return false;
259 }
260
261 GtkWidget *
262 MStarDict::CreateLookupProgressDialog(bool *cancel)
263 {
264     GtkWidget *dialog, *progress;
265
266     /* create dialog */
267     dialog = gtk_dialog_new();
268     gtk_window_set_title(GTK_WINDOW(dialog), _("Searching"));
269     gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_window));
270     gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_OK);
271
272     g_signal_connect(dialog, "response", G_CALLBACK(onLookupProgressDialogResponse), cancel);
273
274     /* add progress bar */
275     progress = gtk_progress_bar_new();
276     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), progress);
277     g_object_set_data(G_OBJECT(dialog), "progress_bar", progress);
278
279     /* show dialog */
280     gtk_widget_show_all(dialog);
281
282     while (gtk_events_pending())
283         gtk_main_iteration();
284
285     return dialog;
286 }
287
288 void
289 MStarDict::DestroyLookupProgressDialog(GtkWidget *dialog)
290 {
291     gtk_widget_destroy(GTK_WIDGET(dialog));
292 }
293
294 void
295 MStarDict::CreateTranslationWindow(const gchar *bookname,
296                                    const gchar *def,
297                                    const gchar *exp)
298 {
299     GtkWidget *window, *alignment, *pannable, *vbox, *label;
300
301     window = hildon_stackable_window_new();
302     gtk_window_set_title(GTK_WINDOW(window), _("Translation"));
303
304     alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
305     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
306                               HILDON_MARGIN_DEFAULT,
307                               HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DOUBLE, HILDON_MARGIN_DEFAULT);
308     gtk_container_add(GTK_CONTAINER(window), alignment);
309
310     pannable = hildon_pannable_area_new();
311     g_object_set(G_OBJECT(pannable), "mov-mode", HILDON_MOVEMENT_MODE_BOTH, NULL);
312     gtk_container_add(GTK_CONTAINER(alignment), pannable);
313
314     vbox = gtk_vbox_new(FALSE, 0);
315     hildon_pannable_area_add_with_viewport(HILDON_PANNABLE_AREA(pannable), vbox);
316
317     label = gtk_label_new("Bookname");
318     gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
319     gtk_label_set_markup(GTK_LABEL(label), bookname);
320     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
321
322     label = gtk_label_new("Definition");
323     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
324     gtk_label_set_markup(GTK_LABEL(label), def);
325     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
326
327     label = gtk_label_new("Expresion");
328     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
329     gtk_label_set_markup(GTK_LABEL(label), exp);
330     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
331
332     gtk_widget_show_all(window);
333 }
334
335 void
336 MStarDict::CreateMainWindow()
337 {
338     HildonProgram *program = NULL;
339     GtkWidget *alignment, *vbox;
340     GtkCellRenderer *renderer;
341     GtkTreeSelection *selection;
342
343     /* hildon program */
344     program = hildon_program_get_instance();
345     g_set_application_name(_("MStardict"));
346
347     /* main window */
348     main_window = hildon_stackable_window_new();
349     hildon_program_add_window(program, HILDON_WINDOW(main_window));
350
351     /* aligment */
352     alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
353     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
354                               HILDON_MARGIN_HALF, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
355     gtk_container_add(GTK_CONTAINER(main_window), alignment);
356
357     /* main vbox */
358     vbox = gtk_vbox_new(FALSE, 0);
359     gtk_container_add(GTK_CONTAINER(alignment), vbox);
360
361     /* no_search_result label */
362     label_widget = gtk_label_new(_("No search result"));
363     hildon_helper_set_logical_color(label_widget, GTK_RC_FG,
364                                     GTK_STATE_NORMAL, "SecondaryTextColor");
365     hildon_helper_set_logical_font(label_widget, "LargeSystemFont");
366     gtk_box_pack_start(GTK_BOX(vbox), label_widget, TRUE, TRUE, 0);
367
368     /* alignment for pannable area */
369     results_widget = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
370     gtk_alignment_set_padding(GTK_ALIGNMENT(results_widget),
371                               0, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
372     gtk_box_pack_start(GTK_BOX(vbox), results_widget, TRUE, TRUE, 0);
373
374     /* pannable for tree view */
375     results_view_scroll = hildon_pannable_area_new();
376     gtk_container_add(GTK_CONTAINER(results_widget), results_view_scroll);
377
378     /* result tree view */
379     results_view = hildon_gtk_tree_view_new(HILDON_UI_MODE_EDIT);
380     gtk_tree_view_set_model(GTK_TREE_VIEW(results_view), GTK_TREE_MODEL(results_list));
381     gtk_container_add(GTK_CONTAINER(results_view_scroll), results_view);
382
383     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(results_view));
384     g_signal_connect(selection, "changed", G_CALLBACK(onResultsViewSelectionChanged), this);
385
386     /* def column */
387     renderer = gtk_cell_renderer_text_new();
388     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW
389                                                 (results_view), -1, "Def",
390                                                 renderer, "text", DEF_COLUMN, NULL);
391     g_object_set(G_OBJECT(renderer),
392                  "xpad", 10,
393                  "ellipsize", PANGO_ELLIPSIZE_END,
394                  "ellipsize-set", TRUE,
395                  NULL);
396
397     /* search entry */
398     search = hildon_entry_new(HILDON_SIZE_FINGER_HEIGHT);
399     gtk_box_pack_end(GTK_BOX(vbox), search, FALSE, TRUE, 0);
400     g_signal_connect(search, "changed", G_CALLBACK(onSearchEntryChanged), this);
401
402     /* window signals */
403     g_signal_connect(G_OBJECT(main_window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
404     g_signal_connect(G_OBJECT(main_window), "key_press_event", G_CALLBACK(onMainWindowKeyPressEvent), this);
405
406     /* show all widget instead of alignment */
407     gtk_widget_show_all(GTK_WIDGET(main_window));
408
409     /* grab focus to search entry */
410     GrabFocus();
411 }
412
413 void
414 MStarDict::CreateMainMenu()
415 {
416     HildonAppMenu *menu;
417     GtkWidget *item;
418
419     menu = HILDON_APP_MENU(hildon_app_menu_new());
420     hildon_window_set_app_menu(HILDON_WINDOW(main_window), menu);
421
422     /* dictionaries menu item */
423     item = hildon_gtk_button_new(HILDON_SIZE_AUTO);
424     gtk_button_set_label(GTK_BUTTON(item), _("Dictionaries"));
425     hildon_app_menu_append(menu, GTK_BUTTON(item));
426     g_signal_connect(item, "clicked", G_CALLBACK(onDictionariesMenuItemClicked), this);
427
428     /* quit menu item */
429     item = hildon_gtk_button_new(HILDON_SIZE_AUTO);
430     gtk_button_set_label(GTK_BUTTON(item), _("Quit"));
431     hildon_app_menu_append(menu, GTK_BUTTON(item));
432     g_signal_connect(item, "clicked", G_CALLBACK(onQuitMenuItemClicked), this);
433
434     /* show main menu */
435     gtk_widget_show_all(GTK_WIDGET(menu));
436 }
437
438 void
439 MStarDict::SearchWord()
440 {
441     const gchar *sWord;
442     bool bFound = false;
443     std::string query;
444
445     if (oLibs->query_dictmask.empty())
446         return;
447
448     sWord = gtk_entry_get_text(GTK_ENTRY(search));
449     if (strcmp(sWord, "") == 0) {
450         ShowNoResults(true);
451     } else {
452         /* unselect rows */
453         ResultsUnselectAll(GTK_SELECTION_NONE);
454
455         switch (analyse_query(sWord, query)) {
456         case qtDATA:
457             bFound = oLibs->LookupData(query.c_str());
458             break;
459         default:
460             /* nothing */ ;
461         }
462
463         /* unselect selected rows */
464         ResultsUnselectAll(GTK_SELECTION_SINGLE);
465
466         if (bFound)
467             ShowNoResults(false);
468         else
469             ShowNoResults(true);
470     }
471 }
472
473 void
474 MStarDict::ResultsListClear()
475 {
476     gtk_list_store_clear(results_list);
477 }
478
479 void
480 MStarDict::ResultsListInsertLast(const gchar *word)
481 {
482     GtkTreeIter iter;
483     gtk_list_store_append(results_list, &iter);
484     gtk_list_store_set(results_list, &iter, DEF_COLUMN, word, -1);
485 }
486
487 void
488 MStarDict::ResultsReScroll()
489 {
490     hildon_pannable_area_scroll_to(HILDON_PANNABLE_AREA(results_view_scroll), -1, 0);
491 }
492
493 void
494 MStarDict::ResultsUnselectAll(GtkSelectionMode mode)
495 {
496     GtkTreeSelection *selection;
497
498     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(results_view));
499     gtk_tree_selection_set_mode(selection, mode);
500     gtk_tree_selection_unselect_all(selection);
501 }
502
503 void
504 MStarDict::ShowNoResults(bool bNoResults)
505 {
506     if (bNoResults) {
507         gtk_label_set_text(GTK_LABEL(label_widget), _("No search result"));
508         gtk_widget_show(label_widget);
509         gtk_widget_hide(results_widget);
510     } else {
511         gtk_widget_hide(label_widget);
512         gtk_widget_show(results_widget);
513     }
514 }
515
516 void
517 MStarDict::ShowNoDictionary(bool bNoDictionary)
518 {
519     if (bNoDictionary) {
520         gtk_label_set_text(GTK_LABEL(label_widget), _("No loaded dictionary"));
521         gtk_widget_show(label_widget);
522         gtk_widget_hide(results_widget);
523     } else {
524         gtk_widget_hide(label_widget);
525         gtk_widget_show(results_widget);
526     }
527 }
528
529 void
530 MStarDict::ShowProgressIndicator(bool bShow)
531 {
532     if (bShow)
533         hildon_gtk_window_set_progress_indicator(GTK_WINDOW(main_window), 1);
534     else
535         hildon_gtk_window_set_progress_indicator(GTK_WINDOW(main_window), 0);
536 }
537
538 void
539 MStarDict::GrabFocus()
540 {
541     gtk_widget_grab_focus(GTK_WIDGET(search));
542 }
543
544 int
545 main(int argc,
546      char **argv)
547 {
548     /* initialize hildon */
549     hildon_gtk_init(&argc, &argv);
550
551     /* initialize localization */
552     setlocale(LC_ALL, "");
553     bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
554     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
555     textdomain(GETTEXT_PACKAGE);
556
557     /* create main window */
558     MStarDict mStarDict;
559     pMStarDict = &mStarDict;
560     mStarDict.CreateMainWindow();
561     mStarDict.CreateMainMenu();
562     mStarDict.ShowNoResults(true);
563
564     /* load dictionaries */
565     mStarDict.oDict->LoadDictionaries();
566
567     gtk_main();
568     return 0;
569 }