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