Updated German translation
[dasher.git] / Src / Gtk2 / dasher_main.cpp
blobea1a161e314a141a083d63618121b8f5bab639b9
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
5 #include <cstring>
6 #include <functional>
7 #include <utility>
9 #include <gdk/gdk.h>
10 #include <gdk/gdkx.h>
11 #include <glib/gi18n.h>
12 #include <gtk/gtk.h>
13 #ifdef WITH_MAEMOFULLSCREEN
14 #include <hildon-widgets/hildon-program.h>
15 #endif
16 #include <unistd.h>
18 #include "GtkDasherControl.h"
19 #include "dasher_lock_dialogue.h"
20 #ifdef WITH_MAEMO
21 #include "dasher_maemo_helper.h"
22 #endif
23 #include "dasher_main.h"
25 #include "dasher_editor.h"
26 #include "math.h"
28 #include "dasher_main_private.h"
29 #include "XmlSettingsStore.h"
30 #include "FileUtils.h"
32 enum {
33 REALIZED,
34 SIGNAL_NUM
37 static guint dasher_main_signals[SIGNAL_NUM] = { 0 };
39 G_DEFINE_TYPE(DasherMain, dasher_main, G_TYPE_OBJECT);
41 static void dasher_main_finalize(GObject *pObject);
43 /* Private member functions */
44 static void dasher_main_setup_window_state(DasherMain *pSelf);
45 static void dasher_main_setup_window_style(DasherMain *pSelf);
46 static void dasher_main_setup_internal_layout(DasherMain *pSelf);
47 static void dasher_main_set_window_title(DasherMain *pSelf);
49 /* ... Table based menu/toolbar commands */
50 static void dasher_main_command_import(DasherMain *pSelf);
51 static void dasher_main_command_quit(DasherMain *pSelf);
52 static void dasher_main_command_preferences(DasherMain *pSelf);
53 static void dasher_main_command_preferences_alphabet(DasherMain *pSelf);
54 static void dasher_main_command_help(DasherMain *pSelf);
55 static void dasher_main_command_about(DasherMain *pSelf);
56 static void dasher_main_command_toggle_direct_mode(DasherMain*);
57 static void dasher_main_command_toggle_game_mode(DasherMain*);
59 /* c.f. WRAP_CPP_CB below */
60 extern "C" void dasher_main_cb_import(GtkAction*, DasherMain*);
61 extern "C" void dasher_main_cb_quit(GtkAction*, DasherMain*);
62 extern "C" void dasher_main_cb_preferences(GtkAction*, DasherMain*);
63 extern "C" void dasher_main_cb_help(GtkAction*, DasherMain*);
64 extern "C" void dasher_main_cb_about(GtkAction*, DasherMain*);
65 extern "C" void dasher_main_cb_toggle_direct_mode(GtkAction*, DasherMain*);
66 extern "C" void dasher_main_cb_toggle_game_mode(GtkAction*, DasherMain*);
68 static gboolean dasher_main_speed_changed(DasherMain *pSelf);
69 static void dasher_main_alphabet_combo_changed(DasherMain *pSelf);
70 // TODO: populate speed slider
71 static void dasher_main_populate_alphabet_combo(DasherMain *pSelf);
73 /* TODO: order these in file */
74 static GtkBuilder *dasher_main_open_gui_xml(DasherMain *, const char *);
75 static void dasher_main_load_interface(DasherMain *pSelf);
76 static void dasher_main_create_preferences(DasherMain *pSelf);
77 static void dasher_main_handle_parameter_change(DasherMain *pSelf, int iParameter);
78 static void dasher_main_load_state(DasherMain *pSelf);
79 static void dasher_main_save_state(DasherMain *pSelf);
80 static void dasher_main_setup_window(DasherMain *pSelf);
81 static void dasher_main_populate_controls(DasherMain *pSelf);
82 static gint dasher_main_lookup_key(DasherMain *pSelf, guint iKeyVal);
84 /* TODO: Various functions which haven't yet been rationalised */
85 gboolean grab_focus();
87 /* ... Message handling from main window widgets */
88 extern "C" void speed_changed(GtkWidget *pWidget, gpointer user_data);
89 extern "C" void alphabet_combo_changed(GtkWidget *pWidget, gpointer pUserData);
90 extern "C" void dasher_main_cb_filename_changed(DasherEditor *pEditor, gpointer pUserData);
91 extern "C" void dasher_main_cb_buffer_changed(DasherEditor *pEditor, gpointer pUserData);
92 extern "C" void dasher_main_cb_context_changed(DasherEditor *pEditor, gpointer pUserData);
93 extern "C" gboolean dasher_main_cb_window_close(GtkWidget *pWidget, GdkEvent *event, gpointer pUserData);
94 extern "C" void parameter_notification(GtkDasherControl *pDasherControl, gint iParameter, gpointer data);
96 /* ... Focus management and event forwarding */
97 extern "C" bool focus_in_event(GtkWidget *widget, GdkEventFocus *event, gpointer data);
98 extern "C" bool edit_focus_in_event(GtkWidget *widget, GdkEventFocus *event, gpointer data);
99 extern "C" gboolean take_real_focus(GtkWidget *widget, GdkEventFocus *event, gpointer user_data);
101 extern "C" gboolean edit_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
102 extern "C" gboolean edit_key_release(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
104 /* ... Temporary test/debug functions */
105 extern "C" gboolean test_focus_handler(GtkWidget *pWidget, GtkDirectionType iDirection, gpointer *pUserData);
107 extern "C" void handle_start_event(GtkDasherControl *pDasherControl, gpointer data);
108 extern "C" gint dasher_main_key_snooper(GtkWidget *pWidget, GdkEventKey *pEvent, gpointer pUserData);
110 /* Boilerplate code */
111 static void
112 dasher_main_class_init(DasherMainClass *pClass) {
113 g_type_class_add_private(pClass, sizeof(DasherMainPrivate));
115 dasher_main_signals[REALIZED] =
116 g_signal_new("realized", G_TYPE_FROM_CLASS(pClass),
117 (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
118 G_STRUCT_OFFSET(DasherMainClass, realized),
119 NULL, NULL, g_cclosure_marshal_VOID__VOID,
120 G_TYPE_NONE, 0);
122 GObjectClass *pObjectClass = (GObjectClass *)pClass;
123 pObjectClass->finalize = dasher_main_finalize;
126 static void
127 dasher_main_init(DasherMain *pDasherMain) {
128 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pDasherMain);
130 pPrivate->pAppSettings = NULL;
131 pPrivate->pEditor = NULL;
132 pPrivate->pPreferencesDialogue = NULL;
134 pPrivate->pKeyboardHelper = new CKeyboardHelper(NULL);
136 pPrivate->bWidgetsInitialised = false;
139 static void
140 dasher_main_finalize(GObject *pObject) {
142 DasherMain *pDasherMain = DASHER_MAIN(pObject);
143 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pDasherMain);
145 pPrivate->pAppSettings->UnregisterParameterChangeCallback(pPrivate->parameter_callback_id_);
147 /* TODO: Does unref really do the right thing - check the whole ref counting situation */
148 // if(pPrivate->pEditor)
149 // g_object_unref(pPrivate->pEditor);
151 if(pPrivate->pPreferencesDialogue)
152 g_object_unref(pPrivate->pPreferencesDialogue);
154 if(pPrivate->pAppSettings)
155 g_object_unref(pPrivate->pAppSettings);
157 gtk_widget_destroy(GTK_WIDGET(pPrivate->pMainWindow));
159 /* TODO: Do we need to take down anything else? */
162 class XmlErrorDisplay : public CMessageDisplay {
163 public:
164 void Message(const std::string &strText, bool bInterrupt) override {
165 // TODO: decide if a pop-up dialog should be shown instead.
166 fputs(strText.c_str(), stderr);
167 fputs("\n", stderr);
171 /* Public methods */
172 DasherMain *
173 dasher_main_new(int *argc, char ***argv, SCommandLine *pCommandLine) {
174 DasherMain *pDasherMain = (DasherMain *)(g_object_new(dasher_main_get_type(), NULL));
175 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pDasherMain);
176 string configFileName = "settings.xml";
177 if (pCommandLine->szConfigFile != nullptr) {
178 configFileName = "settings.";
179 configFileName += pCommandLine->szConfigFile;
180 configFileName += ".xml";
182 static XmlErrorDisplay display;
183 // TODO Pass that instance of fileutils to DasherControl, instead of creating new one.
184 static FileUtils fileUtils;
185 auto settings = new Dasher::XmlSettingsStore(configFileName, &fileUtils, &display);
186 settings->Load();
187 // Save the defaults if needed.
188 settings->Save();
190 DasherAppSettings::Create(settings);
191 pPrivate->pAppSettings = DasherAppSettings::Get();
192 pPrivate->parameter_callback_id_ =
193 pPrivate->pAppSettings->RegisterParameterChangeCallback(
194 std::bind(dasher_main_handle_parameter_change, pDasherMain, std::placeholders::_1));
196 /* Load the user interface from the GUI file */
197 if(pCommandLine && pCommandLine->szAppStyle) {
198 if(!strcmp(pCommandLine->szAppStyle, "traditional")) {
199 pPrivate->pAppSettings->SetLong(APP_LP_STYLE, APP_STYLE_TRAD);
201 else if(!strcmp(pCommandLine->szAppStyle, "compose")) {
202 pPrivate->pAppSettings->SetLong(APP_LP_STYLE, APP_STYLE_COMPOSE);
204 else if(!strcmp(pCommandLine->szAppStyle, "direct")) {
205 pPrivate->pAppSettings->SetLong(APP_LP_STYLE, APP_STYLE_DIRECT);
207 else if(!strcmp(pCommandLine->szAppStyle, "fullscreen")) {
208 pPrivate->pAppSettings->SetLong(APP_LP_STYLE, APP_STYLE_FULLSCREEN);
210 else {
211 g_critical("Application style %s is not supported", pCommandLine->szAppStyle);
212 return 0;
216 dasher_main_load_interface(pDasherMain);
218 pPrivate->pAppSettings->SetWidget(GTK_DASHER_CONTROL(pPrivate->pDasherWidget));
221 /* TODO: This parsing code should really be tidied up */
222 if(pCommandLine && pCommandLine->szOptions) {
223 gchar **pszOptionTerms;
224 pszOptionTerms = g_strsplit(pCommandLine->szOptions, ",", 0);
226 gchar **pszCurrent = pszOptionTerms;
228 while(*pszCurrent) {
229 gchar *szJoin = g_strrstr(*pszCurrent, "=");
230 // Note to translators: This message will be output for command line errors when the "=" in --options=foo is missing.
231 const gchar *errorMessage = _("option setting is missing \"=\".");
233 if(szJoin) {
234 int iLength = szJoin - *pszCurrent;
236 gchar *szKey = g_new(gchar, iLength + 1);
237 memcpy(szKey, *pszCurrent, iLength);
238 szKey[iLength] = '\0';
240 errorMessage = pPrivate->pAppSettings->ClSet(szKey, szJoin + 1);
242 g_free(szKey);
245 if (errorMessage) {
246 // Note to translators: This string will be output when --options= specifies an unknown option.
247 g_critical("%s: '%s', %s", _("Invalid option string specified"), *pszCurrent, errorMessage);
248 return 0;
251 ++pszCurrent;
254 g_strfreev(pszOptionTerms);
256 /* --- */
259 dasher_editor_initialise(pPrivate->pEditor, pPrivate->pAppSettings, GTK_DASHER_CONTROL(pPrivate->pDasherWidget), pPrivate->pXML, NULL);
262 dasher_main_setup_window(pDasherMain);
264 /* Create the editor */
265 gchar *szFullPath = NULL;
267 if(pCommandLine) {
268 if(pCommandLine->szFilename) {
269 if(!g_path_is_absolute(pCommandLine->szFilename)) {
270 char *cwd;
271 cwd = (char *)malloc(1024 * sizeof(char));
272 getcwd(cwd, 1024);
273 szFullPath = g_build_path("/", cwd, pCommandLine->szFilename, NULL);
275 else {
276 szFullPath = g_strdup(pCommandLine->szFilename);
281 // TODO: Fix this
282 // pPrivate->pEditor = GTK_EDITOR(
285 // dasher_editor_initialise(pPrivate->pAppSettings, pDasherMain, pPrivate->pXML, szFullPath);
287 g_free(szFullPath);
289 /* Create the preferences window */
290 dasher_main_create_preferences(pDasherMain);
292 /* Create the lock dialogue (to be removed in future versions) */
293 #ifndef WITH_MAEMO
294 dasher_lock_dialogue_new(pPrivate->pXML, pPrivate->pMainWindow);
295 #else
296 dasher_lock_dialogue_new(pPrivate->pXML, 0);
297 #endif
299 /* Set up various bits and pieces */
300 dasher_main_set_window_title(pDasherMain);
301 dasher_main_populate_controls(pDasherMain);
303 gtk_key_snooper_install(dasher_main_key_snooper, pDasherMain);
305 return pDasherMain;
308 static GtkBuilder *
309 dasher_main_open_gui_xml(DasherMain *pSelf, const char *szGUIFilename) {
310 GError *e = NULL;
311 GtkBuilder *xml = gtk_builder_new();
313 g_message("Opening GUI file: %s", szGUIFilename);
315 if (!gtk_builder_add_from_file(xml, szGUIFilename, &e)) {
316 g_message("Can't find GUI file: %s. Dasher is unlikely to be correctly "
317 "installed. (%s)", szGUIFilename, e->message);
318 exit(1);
321 gtk_builder_connect_signals(xml, pSelf);
323 return xml;
327 #define WRAP_CPP_CB(item) \
328 extern "C" void \
329 dasher_main_cb_##item(GtkAction *obj, DasherMain *p)\
331 dasher_main_command_##item(p);\
335 * Editor passes on the action strings to dasher_editor_command which
336 * land in dasher_editor.
338 WRAP_CPP_CB(import)
339 WRAP_CPP_CB(quit)
340 WRAP_CPP_CB(preferences)
341 WRAP_CPP_CB(help)
342 WRAP_CPP_CB(about)
343 WRAP_CPP_CB(toggle_direct_mode)
344 WRAP_CPP_CB(toggle_game_mode)
346 extern "C" void
347 dasher_main_cb_editor(GtkAction *obj, DasherMain *pSelf)
349 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
350 DASHER_ASSERT(pPrivate->pEditor != NULL);
351 const gchar *action = gtk_action_get_name(obj);
352 dasher_editor_command(pPrivate->pEditor, action);
355 static void
356 dasher_main_load_interface(DasherMain *pSelf) {
357 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
358 const char *szGUIFilename = NULL;
359 const char *szPrefGUIFilename = NULL;
361 #if WITH_MAEMO
362 #ifdef WITH_MAEMOFULLSCREEN
363 szGUIFilename = PROGDATA "/dashermaemofullscreen.ui";
364 #else
365 szGUIFilename = PROGDATA "/dashermaemo.ui";
366 #endif
367 szPrefGUIFilename = PROGDATA "/dashermaemo.preferences.ui";
368 #else
369 szGUIFilename = PROGDATA "/dasher.traditional.ui";
370 szPrefGUIFilename = PROGDATA "/dasher.preferences.ui";
371 #endif
373 if(!szGUIFilename) {
374 g_error("Failure to determine GUI filename");
377 pPrivate->pXML = dasher_main_open_gui_xml(pSelf, szGUIFilename);
378 pPrivate->pPrefXML = dasher_main_open_gui_xml(pSelf, szPrefGUIFilename);
380 #ifndef HAVE_GTK_SHOW_URI
381 GtkAction *helpact =
382 GTK_ACTION(gtk_builder_get_object(pPrivate->pXML, "action_help"));
383 gtk_action_set_sensitive(helpact, false);
384 gtk_action_set_visible(helpact, false);
385 #endif
387 // Save the details of some of the widgets for later
388 // pPrivate->pActionPane = gtk_builder_get_object(pPrivate->pXML, "vbox39");
389 // pPrivate->pBufferView = gtk_builder_get_object(pPrivate->pXML, "the_text_view");
390 pPrivate->pDivider = GTK_PANED(gtk_builder_get_object(pPrivate->pXML, "main_divider"));
391 // pPrivate->pEditPane = gtk_builder_get_object(pPrivate->pXML, "vbox40");
392 pPrivate->pMainWindow = GTK_WINDOW(gtk_builder_get_object(pPrivate->pXML, "window"));
393 pPrivate->pToolbar = GTK_WIDGET(gtk_builder_get_object(pPrivate->pXML, "dasher_tool_bar"));
394 // pPrivate->pMenuBar = gtk_builder_get_object(pPrivate->pXML, "dasher_menu_bar");
395 pPrivate->pDasherWidget = GTK_WIDGET(gtk_builder_get_object(pPrivate->pXML, "DasherControl"));
396 pPrivate->pEditor = DASHER_EDITOR(gtk_builder_get_object(pPrivate->pXML, "DasherEditor"));
397 gtk_dasher_control_set_editor(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), pPrivate->pEditor);
399 #ifndef WITH_MAEMO
400 pPrivate->pSpeedBox = GTK_SPIN_BUTTON(gtk_builder_get_object(pPrivate->pXML, "spinbutton1"));
401 pPrivate->pAlphabetCombo = GTK_WIDGET(gtk_builder_get_object(pPrivate->pXML, "combobox1"));
402 pPrivate->pStatusControl = GTK_WIDGET(gtk_builder_get_object(pPrivate->pXML, "hbox8"));
404 pPrivate->pAlphabetList = gtk_list_store_new(1, G_TYPE_STRING);
405 gtk_combo_box_set_model(GTK_COMBO_BOX(pPrivate->pAlphabetCombo),
406 GTK_TREE_MODEL(pPrivate->pAlphabetList));
408 GtkCellRenderer *pRenderer;
409 pRenderer = gtk_cell_renderer_text_new();
410 g_object_set(G_OBJECT(pRenderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
411 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(pPrivate->pAlphabetCombo), pRenderer, true);
412 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(pPrivate->pAlphabetCombo), pRenderer, "text", 0, NULL);
415 // gtk_widget_add_events(pPrivate->pDragHandle, GDK_POINTER_MOTION_MASK);
416 #else
418 #ifdef WITH_MAEMOFULLSCREEN
419 // TODO: This is horrible - no need to get it from the glade file if we're not going to use it
421 pPrivate->pProgram = HILDON_PROGRAM(hildon_program_get_instance());
422 // hildon_app_set_title(pPrivate->pApp, "Dasher");
424 pPrivate->pHWindow = HILDON_WINDOW(hildon_window_new());
425 hildon_program_add_window(pPrivate->pProgram, pPrivate->pHWindow);
427 gtk_widget_reparent(pPrivate->pInnerFrame, GTK_WIDGET(pPrivate->pHWindow));
428 // gtk_paned_set_position(GTK_PANED(window), 100);
430 /* Do menu setup */
431 GtkMenu *main_menu;
432 GtkWidget *file_menu;
433 GtkWidget *file_menu_item;
434 GtkWidget *options_menu;
435 GtkWidget *options_menu_item;
436 GtkWidget *help_menu;
437 GtkWidget *help_menu_item;
440 // main_menu = hildon_appview_get_menu(appview);
442 main_menu = GTK_MENU(gtk_menu_new());
443 file_menu = gtk_builder_get_object(pPrivate->pXML, "file_menu");
444 options_menu = gtk_builder_get_object(pPrivate->pXML, "options1_menu");
445 help_menu = gtk_builder_get_object(pPrivate->pXML, "help_menu");
446 file_menu_item = gtk_menu_item_new_with_label ("File");
447 options_menu_item = gtk_menu_item_new_with_label ("Options");
448 help_menu_item = gtk_menu_item_new_with_label ("Help");
450 g_object_ref(file_menu);
451 g_object_ref(options_menu);
452 g_object_ref(help_menu);
454 gtk_menu_item_set_submenu(GTK_MENU_ITEM(gtk_builder_get_object(pPrivate->pXML, "file_menu")), NULL);
455 gtk_menu_item_set_submenu(GTK_MENU_ITEM(gtk_builder_get_object(pPrivate->pXML, "options1")), NULL);
456 gtk_menu_item_set_submenu(GTK_MENU_ITEM(gtk_builder_get_object(pPrivate->pXML, "help_menu")), NULL);
458 gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_menu_item),file_menu);
459 gtk_menu_item_set_submenu(GTK_MENU_ITEM(options_menu_item),options_menu);
460 gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_menu_item),help_menu);
461 gtk_menu_shell_append((GtkMenuShell *)main_menu, file_menu_item);
462 gtk_menu_shell_append((GtkMenuShell *)main_menu, options_menu_item);
463 gtk_menu_shell_append((GtkMenuShell *)main_menu, help_menu_item);
465 g_object_unref(file_menu);
466 g_object_unref(options_menu);
467 g_object_unref(help_menu);
469 hildon_program_set_common_menu(pPrivate->pProgram, main_menu);
471 gtk_widget_show_all( GTK_WIDGET( main_menu ) );
473 // /* And toolbar */
474 // GtkWidget *toolbar;
475 // toolbar = gtk_builder_get_object(pPrivate->pXML, "toolbar");
476 // g_print("Got %p\n",toolbar);
477 // gtk_widget_reparent (toolbar, appview->vbox);
479 gtk_widget_show_all(GTK_WIDGET(pPrivate->pHWindow));
481 gtk_widget_destroy(GTK_WIDGET(pPrivate->pMainWindow));
482 pPrivate->pMainWindow = pPrivate->pHWindow;
484 g_signal_connect(G_OBJECT(pPrivate->pHWindow), "delete_event", G_CALLBACK(ask_save_before_exit), NULL);
486 #endif // Maemo fullscreen
487 #endif // Maemo
489 // pPrivate->bHidden = false;
490 // pPrivate->bGrabbed = false;
492 // pPrivate->iPosition = 100; // FIXME - make this persistant
494 // TODO: Specify callbacks in glade file
495 // TODO: Rationalise focus
496 // g_signal_connect(G_OBJECT(pPrivate->pBufferView), "button-release-event", G_CALLBACK(take_real_focus), NULL);
497 // g_signal_connect(G_OBJECT(pPrivate->pBufferView), "key-press-event", G_CALLBACK(edit_key_press), NULL);
498 //g_signal_connect(G_OBJECT(pPrivate->pBufferView), "key-release-event", G_CALLBACK(edit_key_release), NULL);
500 // dasher_main_build_context_menu(pSelf);
502 // Create a Maemo helper if necessary
503 #if defined WITH_MAEMO && !defined WITH_MAEMOFULLSCREEN
504 pPrivate->pMaemoHelper = dasher_maemo_helper_new(pPrivate->pMainWindow);
505 #endif
507 // Set up any non-registry-dependent options
508 #ifdef WITH_GPE
509 gtk_window_set_decorated(pPrivate->pMainWindow, false);
510 #endif
512 // Hide any widgets which aren't appropriate for this mode
513 if(pPrivate->pAppSettings->GetLong(APP_LP_STYLE) == APP_STYLE_DIRECT) {
514 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(pPrivate->pXML, "DasherEditor")));
517 // TODO: szFullPath
518 pPrivate->bWidgetsInitialised = true;
521 static void
522 dasher_main_create_preferences(DasherMain *pSelf) {
523 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
524 pPrivate->pPreferencesDialogue = dasher_preferences_dialogue_new(pPrivate->pPrefXML, pPrivate->pEditor, pPrivate->pAppSettings, pPrivate->pMainWindow);
528 * Start game mode: prompt user for the text to play with, put this in SP_GAME_TEXT_FILE;
529 * clear out any text in the dasher editor; call CDasherControl::EnterGameMode().
531 * Event handler which displays a standard GTK file dialog. The dialog allows the user
532 * to specify a text file to play game mode with.
534 * @param pButton the button that fired the event
535 * @param pWidget reference needed by GTK for callback signature
536 * @param pData pointer to a an std::pair<GtkWindow*, DasherMain*> containing references
537 * to the dialog's parent window and an instance of DasherMain
539 void show_game_file_dialog(GtkWidget *pButton, GtkWidget *pWidget, gpointer pData) {
541 std::pair<GtkWindow*, DasherMain*> *objRefs = (std::pair<GtkWindow*, DasherMain*>*)pData;
543 DasherMain *pSelf = objRefs->second;
544 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
546 GtkWidget *pFileDialog = gtk_file_chooser_dialog_new("Choose a Game Text",
547 GTK_WINDOW(objRefs->first),
548 GTK_FILE_CHOOSER_ACTION_OPEN,
549 _("_Cancel"), GTK_RESPONSE_CANCEL,
550 _("_Open"), GTK_RESPONSE_ACCEPT,
551 NULL);
553 gtk_window_set_destroy_with_parent(GTK_WINDOW(pFileDialog), true);
555 if (gtk_dialog_run(GTK_DIALOG(pFileDialog)) == GTK_RESPONSE_ACCEPT) {
557 char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileDialog));
559 pPrivate->pAppSettings->SetString(SP_GAME_TEXT_FILE, filename);
560 gtk_dasher_control_set_game_mode(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), true);
562 gtk_widget_destroy(GTK_WIDGET(objRefs->first));
565 void dasher_main_command_toggle_direct_mode(DasherMain *pSelf) {
566 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
568 // Question of style: we could hide/show in
569 // dasher_main_handle_parameter_change()
570 if (pPrivate->pAppSettings->GetLong(APP_LP_STYLE) == APP_STYLE_DIRECT) {
571 // Opposite of direct mode
572 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(pPrivate->pXML, "DasherEditor")));
573 gtk_window_set_keep_above(GTK_WINDOW(pPrivate->pMainWindow), false);
574 gtk_window_set_accept_focus(GTK_WINDOW(pPrivate->pMainWindow), true);
575 gtk_window_unstick(GTK_WINDOW(pPrivate->pMainWindow));
576 pPrivate->pAppSettings->SetLong(APP_LP_STYLE, APP_STYLE_TRAD);
577 } else {
578 // Hide text window
579 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(pPrivate->pXML, "DasherEditor")));
580 // Direct mode - set always on top
581 gtk_window_set_keep_above(GTK_WINDOW(pPrivate->pMainWindow), true);
582 // Refuse focus
583 gtk_window_set_accept_focus(GTK_WINDOW(pPrivate->pMainWindow), false);
584 // Stick on all desktops
585 gtk_window_stick(GTK_WINDOW(pPrivate->pMainWindow));
586 pPrivate->pAppSettings->SetLong(APP_LP_STYLE, APP_STYLE_DIRECT);
589 dasher_editor_toggle_direct_mode(pPrivate->pEditor);
593 * Toggle game mode on and off. Toggling on causes a dialog box to be displayed
594 * welcoming the user to game mode and prompting them to specify a file to play with.
595 * Toggling off just calls LeaveGameMode().
597 * @param pSelf a reference to an instance of DasherMain
599 void dasher_main_command_toggle_game_mode(DasherMain *pSelf) {
601 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
603 if(!gtk_dasher_control_get_game_mode(GTK_DASHER_CONTROL(pPrivate->pDasherWidget))) {
605 GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pPrivate->pMainWindow), GTK_DIALOG_MODAL,
606 GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE,
607 _("Welcome to Dasher Game Mode! Game Mode is a fun way to practice entering text in Dasher. Please select a training text to play with:"));
609 gtk_dialog_add_button(GTK_DIALOG(pDialog), _("Use Default"), GTK_RESPONSE_ACCEPT);
610 GtkWidget *pFileButton = gtk_dialog_add_button(GTK_DIALOG(pDialog), _("Choose File..."), 2);
611 gtk_dialog_add_button(GTK_DIALOG(pDialog), _("Cancel"), GTK_RESPONSE_REJECT);
613 //make a pair with references to the the DasherMain and parent window instances that
614 //handler will need - kind of disgusting, but looks like only way to pass multiple
615 //parameters in g_signal_connect
616 std::pair<GtkWindow*, DasherMain*> objRefs = std::make_pair(GTK_WINDOW(pDialog), pSelf);
617 //ACL surprisingly this works: the signal handler (show_game_file_dialog) is called
618 // before gtk_dialog_run returns, and the pair is in this, calling, method's stack frame,
619 // so exists until _this_ method finishes...
621 g_signal_connect(pFileButton, "button-press-event", G_CALLBACK(show_game_file_dialog),
622 (gpointer)&objRefs);
624 if (gtk_dialog_run(GTK_DIALOG(pDialog))==GTK_RESPONSE_ACCEPT) {
625 gtk_dasher_control_set_game_mode(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), true);
626 //Tick menu?
628 //have to do this check because we might have destroyed the dialog already in show_game_file_dialog
629 if(GTK_IS_WIDGET(pDialog))
630 gtk_widget_destroy(pDialog);
631 } else {
632 GtkWidget *pDialog = gtk_message_dialog_new(GTK_WINDOW(pPrivate->pMainWindow), GTK_DIALOG_MODAL,
633 GTK_MESSAGE_OTHER, GTK_BUTTONS_NONE,
634 _("Are you sure you wish to turn off game mode? All unsaved changes will be lost."));
636 gtk_dialog_add_button(GTK_DIALOG(pDialog), _("No"), GTK_RESPONSE_REJECT);
637 gtk_dialog_add_button(GTK_DIALOG(pDialog), _("Yes"), GTK_RESPONSE_ACCEPT);
639 if(gtk_dialog_run(GTK_DIALOG(pDialog))==GTK_RESPONSE_ACCEPT) {
640 gtk_dasher_control_set_game_mode(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), false);
642 DASHER_ASSERT(GTK_IS_WIDGET(pDialog));
643 gtk_widget_destroy(GTK_WIDGET(pDialog));
648 static void
649 dasher_main_handle_parameter_change(DasherMain *pSelf, int iParameter) {
650 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
652 switch( iParameter ) {
653 case APP_BP_SHOW_TOOLBAR:
654 if( pPrivate->pAppSettings->GetBool(APP_BP_SHOW_TOOLBAR))
655 gtk_widget_show(pPrivate->pToolbar);
656 else
657 gtk_widget_hide(pPrivate->pToolbar);
658 break;
659 case APP_BP_SHOW_STATUSBAR:
660 if (pPrivate->pAppSettings->GetBool(APP_BP_SHOW_STATUSBAR))
661 gtk_widget_show(pPrivate->pStatusControl);
662 else
663 gtk_widget_hide(pPrivate->pStatusControl);
664 break;
665 #ifndef WITH_MAEMO
666 case LP_MAX_BITRATE:
667 gtk_spin_button_set_value(pPrivate->pSpeedBox, pPrivate->pAppSettings->GetLong(LP_MAX_BITRATE) / 100.0);
668 break;
669 #endif
670 case SP_ALPHABET_ID:
671 dasher_main_populate_alphabet_combo(pSelf);
672 break;
673 case BP_GLOBAL_KEYBOARD:
674 dasher_main_setup_window(pSelf);
675 break;
676 #if defined WITH_MAEMO && !defined WITH_MAEMOFULLSCREEN
677 case APP_LP_MAEMO_SIZE: {
678 g_message("Maemo size");
680 bool bVisible = GTK_WIDGET_VISIBLE(pPrivate->pMainWindow);
681 gtk_widget_hide(pPrivate->pMainWindow);
682 if(pPrivate->pAppSettings->GetLong(APP_LP_MAEMO_SIZE) == 0) {
683 int iWidth;
684 gtk_window_get_size(GTK_WINDOW(pPrivate->pMainWindow), &iWidth, NULL);
685 gtk_widget_set_size_request(pPrivate->pMainWindow, -1, 150);
686 gtk_window_resize(GTK_WINDOW(pPrivate->pMainWindow), iWidth, 150);
687 gtk_widget_set_size_request(pPrivate->pDasherWidget, 175, -1);
689 else {
690 int iWidth;
691 gtk_window_get_size(GTK_WINDOW(pPrivate->pMainWindow), &iWidth, NULL);
692 gtk_widget_set_size_request(pPrivate->pMainWindow, -1, 250);
693 gtk_window_resize(GTK_WINDOW(pPrivate->pMainWindow), iWidth, 250);
694 gtk_widget_set_size_request(pPrivate->pDasherWidget, 280, -1);
696 if(bVisible)
697 gtk_widget_show(pPrivate->pMainWindow);
698 break;
700 #endif
703 if(pPrivate->pPreferencesDialogue)
704 dasher_preferences_dialogue_handle_parameter_change(pPrivate->pPreferencesDialogue, iParameter);
706 if(pPrivate->pEditor)
707 dasher_editor_handle_parameter_change(pPrivate->pEditor, iParameter);
710 static void
711 dasher_main_load_state(DasherMain *pSelf) {
712 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
714 int iEditSize = pPrivate->pAppSettings->GetLong(APP_LP_EDIT_SIZE);
715 int iWindowWidth = pPrivate->pAppSettings->GetLong(APP_LP_SCREEN_WIDTH);
716 int iWindowHeight = pPrivate->pAppSettings->GetLong(APP_LP_SCREEN_HEIGHT);
718 #ifndef WITH_MAEMO
719 gtk_window_resize(GTK_WINDOW(pPrivate->pMainWindow), iWindowWidth, iWindowHeight);
720 #endif
722 gtk_paned_set_position(pPrivate->pDivider, iEditSize);
724 pPrivate->iWidth = iWindowWidth;
725 pPrivate->iHeight = iWindowHeight;
728 int iWindowX;
729 int iWindowY;
731 iWindowX = pPrivate->pAppSettings->GetLong(APP_LP_X);
732 iWindowY = pPrivate->pAppSettings->GetLong(APP_LP_Y);
734 gtk_window_move(GTK_WINDOW(pPrivate->pMainWindow), iWindowX, iWindowY);
737 static void
738 dasher_main_save_state(DasherMain *pSelf) {
739 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
741 if(!pPrivate->bWidgetsInitialised)
742 return;
744 int iWindowWidth;
745 int iWindowHeight;
747 gtk_window_get_size(GTK_WINDOW(pPrivate->pMainWindow), &iWindowWidth, &iWindowHeight);
748 int iEditSize = gtk_paned_get_position(pPrivate->pDivider);
750 // APP_STYLE_DIRECT doesn't have an edit window.
751 if (pPrivate->pAppSettings->GetLong(APP_LP_STYLE) != APP_STYLE_DIRECT)
752 pPrivate->pAppSettings->SetLong(APP_LP_EDIT_SIZE, iEditSize);
753 pPrivate->pAppSettings->SetLong(APP_LP_SCREEN_WIDTH, iWindowWidth);
754 pPrivate->pAppSettings->SetLong(APP_LP_SCREEN_HEIGHT, iWindowHeight);
756 int iWindowX;
757 int iWindowY;
758 gtk_window_get_position(GTK_WINDOW(pPrivate->pMainWindow), &iWindowX, &iWindowY);
760 pPrivate->pAppSettings->SetLong(APP_LP_X, iWindowX);
761 pPrivate->pAppSettings->SetLong(APP_LP_Y, iWindowY);
764 void
765 dasher_main_show(DasherMain *pSelf) {
766 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
767 gtk_widget_show(GTK_WIDGET(pPrivate->pMainWindow));
770 static void
771 dasher_main_setup_window(DasherMain *pSelf) {
772 dasher_main_setup_window_style(pSelf);
773 dasher_main_setup_window_state(pSelf);
774 dasher_main_setup_internal_layout(pSelf);
776 // DasherMainPrivate *pPrivate = (DasherMainPrivate *)(pSelf->private_data);
778 // if(dasher_app_settings_get_bool(pPrivate->pAppSettings, BP_GLOBAL_KEYBOARD))
779 // gdk_window_add_filter(0, keyboard_filter_cb, 0);
780 // else
781 // gdk_window_remove_filter(0, keyboard_filter_cb, 0);
784 static void
785 dasher_main_populate_controls(DasherMain *pSelf) {
786 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
788 // Populate the alphabet chooser
789 dasher_main_populate_alphabet_combo(pSelf);
791 // Set the value of the speed spinner
792 gtk_spin_button_set_value(pPrivate->pSpeedBox,
793 pPrivate->pAppSettings->GetLong(LP_MAX_BITRATE) / 100.0);
796 /* Private methods */
798 /* Window state is basically size and position */
799 static void
800 dasher_main_setup_window_state(DasherMain *pSelf) {
801 dasher_main_load_state(pSelf);
803 // TODO: Setup positioning here - need to think up a policy on this
807 /* Setup the window style - this is defined to be window manager hints and the like */
808 static void
809 dasher_main_setup_window_style(DasherMain *pSelf) {
810 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
812 switch(pPrivate->pAppSettings->GetLong(APP_LP_STYLE)) {
813 case APP_STYLE_TRAD:
814 // Nothing to do
815 break;
816 case APP_STYLE_COMPOSE:
817 // Nothing to do
818 break;
819 case APP_STYLE_DIRECT:
820 // Direct mode - set always on top
821 gtk_window_set_keep_above(GTK_WINDOW(pPrivate->pMainWindow), true);
823 // Refuse focus
824 gtk_window_set_accept_focus(GTK_WINDOW(pPrivate->pMainWindow), false);
826 // Stick on all desktops
827 gtk_window_stick(GTK_WINDOW(pPrivate->pMainWindow));
828 break;
829 case APP_STYLE_FULLSCREEN:
830 // Fullscreen mode - set fullscreen
831 gtk_window_fullscreen(GTK_WINDOW(pPrivate->pMainWindow));
832 break;
833 default:
834 g_error("Inconsistent application style specified.");
838 /* Internal layout is the visibility of various widgets */
839 static void
840 dasher_main_setup_internal_layout(DasherMain *pSelf) {
841 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
843 if(pPrivate->pToolbar) {
844 if(pPrivate->pAppSettings->GetBool(APP_BP_SHOW_TOOLBAR))
845 gtk_widget_show(pPrivate->pToolbar);
846 else
847 gtk_widget_hide(pPrivate->pToolbar);
850 if(pPrivate->pStatusControl) {
851 if (pPrivate->pAppSettings->GetBool(APP_BP_SHOW_STATUSBAR))
852 gtk_widget_show(pPrivate->pStatusControl);
853 else
854 gtk_widget_hide(pPrivate->pStatusControl);
858 // TODO: Fold into setup controls?
859 static void
860 dasher_main_set_window_title(DasherMain *pSelf) {
861 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
863 const gchar *szFilename = dasher_editor_get_filename(pPrivate->pEditor);
865 // Note to translators: This is the name of the dasher program as it appears
866 // in a window title.
867 gchar * dasher = _("Dasher");
868 if(szFilename == 0) {
869 gtk_window_set_title(GTK_WINDOW(pPrivate->pMainWindow), dasher);
871 else {
872 gchar *title = g_strdup_printf("%s - %s", dasher, szFilename);
873 gtk_window_set_title(GTK_WINDOW(pPrivate->pMainWindow), title);
874 g_free (title);
878 static void
879 dasher_main_command_import(DasherMain *pSelf) {
880 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
882 GtkWidget *pFileSel = gtk_file_chooser_dialog_new(_("Select File"),
883 GTK_WINDOW(pPrivate->pMainWindow),
884 GTK_FILE_CHOOSER_ACTION_OPEN,
885 _("_Open"), GTK_RESPONSE_ACCEPT,
886 _("_Cancel"), GTK_RESPONSE_CANCEL,
887 NULL);
889 #ifdef TEACH_TRAINING_HELPER_LOAD_FILE_ABOUT_URI
890 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(pFileSel), FALSE);
891 #endif
893 if(gtk_dialog_run(GTK_DIALOG(pFileSel)) == GTK_RESPONSE_ACCEPT) {
895 #ifdef TEACH_TRAINING_HELPER_LOAD_FILE_ABOUT_URI
896 gchar *szFilename = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(pFileSel));
897 #else
898 gchar *szFilename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pFileSel));
899 #endif
901 gtk_dasher_control_train(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), szFilename);
903 g_free(szFilename);
906 gtk_widget_destroy(pFileSel);
909 static void dasher_main_command_quit(DasherMain *pSelf) {
910 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
912 GtkWidget *pDialogue = NULL;
913 dasher_main_save_state(pSelf);
914 if(dasher_editor_file_changed(pPrivate->pEditor)) {
915 // XXX PRLW: Just open the save dialogue box.
916 #if 0
917 const gchar *szFilename = dasher_editor_get_filename(pPrivate->pEditor);
919 if(szFilename) {
920 pDialogue = gtk_message_dialog_new(GTK_WINDOW(pPrivate->pMainWindow), GTK_DIALOG_MODAL,
921 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
922 _("Do you want to save your changes to %s?\n\nYour changes will be lost if you don't save them."),
923 szFilename);
925 else {
926 #endif
927 pDialogue = gtk_message_dialog_new(GTK_WINDOW(pPrivate->pMainWindow), GTK_DIALOG_MODAL,
928 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
929 _("Do you want to save your changes?\n\nYour changes will be lost if you don't save them."));
930 #if 0
932 #endif
934 gtk_dialog_add_buttons(GTK_DIALOG(pDialogue),
935 _("Quit without saving"), GTK_RESPONSE_REJECT,
936 _("Don't quit"), GTK_RESPONSE_CANCEL,
937 _("Save and quit"), GTK_RESPONSE_ACCEPT,
938 NULL);
940 switch (gtk_dialog_run(GTK_DIALOG(pDialogue))) {
941 case GTK_RESPONSE_REJECT:
942 gtk_main_quit();
943 break;
944 case GTK_RESPONSE_CANCEL:
945 gtk_widget_destroy(GTK_WIDGET(pDialogue));
946 break;
947 case GTK_RESPONSE_ACCEPT:
948 dasher_editor_command(pPrivate->pEditor, "action_save");
949 gtk_main_quit();
950 break;
953 else {
954 pDialogue = gtk_message_dialog_new(GTK_WINDOW(pPrivate->pMainWindow), GTK_DIALOG_MODAL,
955 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
956 _("Are you sure you wish to quit?"));
958 gtk_dialog_add_buttons(GTK_DIALOG(pDialogue),
959 _("Don't quit"), GTK_RESPONSE_REJECT,
960 _("Quit"), GTK_RESPONSE_ACCEPT,
961 NULL);
963 switch (gtk_dialog_run(GTK_DIALOG(pDialogue))) {
964 case GTK_RESPONSE_REJECT:
965 gtk_widget_destroy(GTK_WIDGET(pDialogue));
966 break;
967 case GTK_RESPONSE_ACCEPT:
968 gtk_main_quit();
973 static void
974 dasher_main_command_preferences(DasherMain *pSelf) {
975 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
976 dasher_preferences_dialogue_show(pPrivate->pPreferencesDialogue, 0);
979 static void
980 dasher_main_command_preferences_alphabet(DasherMain *pSelf) {
981 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
982 dasher_preferences_dialogue_show(pPrivate->pPreferencesDialogue, 1);
985 static void
986 dasher_main_command_help(DasherMain *pSelf) {
987 #ifdef HAVE_GTK_SHOW_URI
988 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
989 GdkScreen *scr;
990 GError *err = NULL;
992 scr = gtk_widget_get_screen(GTK_WIDGET(pPrivate->pMainWindow));
993 if (!gtk_show_uri(scr, "ghelp:dasher", gtk_get_current_event_time(), &err)) {
994 GtkWidget *d;
995 d = gtk_message_dialog_new(GTK_WINDOW(pPrivate->pMainWindow),
996 GTK_DIALOG_MODAL,
997 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
998 "%s", _("Unable to open help file"));
999 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(d),
1000 "%s", err->message);
1001 g_signal_connect(d, "response", G_CALLBACK(gtk_widget_destroy), NULL);
1002 gtk_window_present(GTK_WINDOW(d));
1004 g_error_free (err);
1006 #endif
1009 static void
1010 dasher_main_command_about(DasherMain *pSelf) {
1011 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
1013 // In alphabetical order - please keep this in sync with the AUTHORS
1014 // file at root of the package tree
1015 const gchar *authors[] = {
1016 "Chris Ball",
1017 "Ignas Budvytis",
1018 "Peter Conlon",
1019 "Phil Cowans",
1020 "Frederik Eaton",
1021 "Behdad Esfahbod",
1022 "Matthew Garrett",
1023 "Chris Hack",
1024 "Takashi Kaburagi",
1025 "Sega Kazue",
1026 "Alan Lawrence",
1027 "David MacKay",
1028 "Iain Murray",
1029 "Martijn van Veen",
1030 "Keith Vertanen",
1031 "Hanna Wallach",
1032 "David Ward",
1033 "Patrick Welche",
1034 "Brian Williams",
1035 "Seb Wills",
1036 "Will Zou",
1037 NULL
1040 // Yeah, should really do some Gnome documentation for it...
1041 const gchar *documenters[] = {
1042 "Chris Ball",
1043 "Matthew Garrett",
1044 "David MacKay",
1045 NULL
1048 gtk_show_about_dialog(GTK_WINDOW(pPrivate->pMainWindow),
1049 "authors", authors,
1050 "comments", _("Dasher is a predictive text entry application"),
1051 "copyright", "Copyright \xC2\xA9 1998-2011 The Dasher Project",
1052 "documenters", documenters,
1053 "license", "GPL 2+",
1054 "logo-icon-name", "dasher",
1055 "translator-credits", _("translator-credits"),
1056 "version", PACKAGE_VERSION,
1057 "website", PACKAGE_URL,
1058 "wrap-license", true,
1059 NULL);
1062 static gboolean
1063 dasher_main_speed_changed(DasherMain *pSelf) {
1064 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
1066 int iNewValue( static_cast<int>(round(gtk_spin_button_get_value(pPrivate->pSpeedBox) * 100)));
1068 if(pPrivate->pAppSettings->GetLong(LP_MAX_BITRATE) != iNewValue)
1069 pPrivate->pAppSettings->SetLong(LP_MAX_BITRATE, iNewValue);
1071 return true;
1074 static void
1075 dasher_main_alphabet_combo_changed(DasherMain *pSelf) {
1076 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
1078 GtkTreeIter sIter;
1080 if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(pPrivate->pAlphabetCombo), &sIter)) {
1081 char *szSelected;
1082 gtk_tree_model_get(GTK_TREE_MODEL(pPrivate->pAlphabetList), &sIter, 0, &szSelected, -1);
1084 if(!strcmp("More Alphabets...", szSelected)) {
1085 gtk_combo_box_set_active(GTK_COMBO_BOX(pPrivate->pAlphabetCombo), 0);
1086 // dasher_preferences_dialogue_show(pPrivate->pPreferencesDialogue);
1087 dasher_main_command_preferences_alphabet(pSelf);
1089 else
1090 pPrivate->pAppSettings->SetString(SP_ALPHABET_ID, szSelected);
1092 g_free(szSelected);
1096 static void
1097 dasher_main_populate_alphabet_combo(DasherMain *pSelf) {
1098 #ifndef WITH_MAEMO
1099 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
1101 // Disconnect the event handler temporarily, otherwise this will
1102 // trigger alphabet changes
1104 g_signal_handlers_block_by_func(pPrivate->pAlphabetCombo, (gpointer)alphabet_combo_changed, pSelf);
1106 gtk_list_store_clear(pPrivate->pAlphabetList);
1109 GtkTreeIter sIter;
1110 const char *szValue;
1112 szValue = pPrivate->pAppSettings->GetString(SP_ALPHABET_ID).c_str();
1114 if(strlen(szValue) > 0) {
1115 gtk_list_store_append(pPrivate->pAlphabetList, &sIter);
1116 gtk_list_store_set(pPrivate->pAlphabetList, &sIter, 0, szValue, -1);
1117 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(pPrivate->pAlphabetCombo), &sIter);
1120 szValue = pPrivate->pAppSettings->GetString(SP_ALPHABET_1).c_str();
1121 if(strlen(szValue) > 0) {
1122 gtk_list_store_append(pPrivate->pAlphabetList, &sIter);
1123 gtk_list_store_set(pPrivate->pAlphabetList, &sIter, 0, szValue, -1);
1126 szValue = pPrivate->pAppSettings->GetString(SP_ALPHABET_2).c_str();
1127 if(strlen(szValue) > 0) {
1128 gtk_list_store_append(pPrivate->pAlphabetList, &sIter);
1129 gtk_list_store_set(pPrivate->pAlphabetList, &sIter, 0, szValue, -1);
1132 szValue = pPrivate->pAppSettings->GetString(SP_ALPHABET_3).c_str();
1133 if(strlen(szValue) > 0) {
1134 gtk_list_store_append(pPrivate->pAlphabetList, &sIter);
1135 gtk_list_store_set(pPrivate->pAlphabetList, &sIter, 0, szValue, -1);
1138 szValue = pPrivate->pAppSettings->GetString(SP_ALPHABET_4).c_str();
1139 if(strlen(szValue) > 0) {
1140 gtk_list_store_append(pPrivate->pAlphabetList, &sIter);
1141 gtk_list_store_set(pPrivate->pAlphabetList, &sIter, 0, szValue, -1);
1144 gtk_list_store_append(pPrivate->pAlphabetList, &sIter);
1145 gtk_list_store_set(pPrivate->pAlphabetList, &sIter, 0, "More Alphabets...", -1);
1147 g_signal_handlers_unblock_by_func(pPrivate->pAlphabetCombo, (gpointer)alphabet_combo_changed, pSelf);
1149 #endif
1152 static gint
1153 dasher_main_lookup_key(DasherMain *pSelf, guint iKeyVal) {
1154 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
1156 if(pPrivate->pKeyboardHelper)
1157 return pPrivate->pKeyboardHelper->ConvertKeycode(iKeyVal);
1158 else
1159 return -1;
1163 gboolean
1164 grab_focus() {
1166 // TODO: reimplement (text view member of class)
1167 // gtk_widget_grab_focus(the_text_view);
1168 // g_bForwardKeyboard = true;
1169 return true;
1172 /* Callbacks */
1174 extern "C" void
1175 speed_changed(GtkWidget *pWidget, gpointer pUserData) {
1176 DasherMain *pDasherMain = DASHER_MAIN(pUserData);
1177 dasher_main_speed_changed(pDasherMain);
1180 extern "C" void
1181 alphabet_combo_changed(GtkWidget *pWidget, gpointer pUserData) {
1182 DasherMain *pDasherMain = DASHER_MAIN(pUserData);
1183 dasher_main_alphabet_combo_changed(pDasherMain);
1186 extern "C" void
1187 dasher_main_cb_filename_changed(DasherEditor *pEditor, gpointer pUserData) {
1188 DasherMain *pDasherMain = DASHER_MAIN(pUserData);
1189 dasher_main_set_window_title(pDasherMain);
1192 extern "C" void
1193 dasher_main_cb_buffer_changed(DasherEditor *pEditor, gpointer pUserData) {
1194 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pUserData);
1196 gtk_dasher_control_set_buffer(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), dasher_editor_get_offset(pPrivate->pEditor));
1199 extern "C" void
1200 dasher_main_cb_context_changed(DasherEditor *pEditor, gpointer pUserData) {
1201 DasherMain *pDasherMain = DASHER_MAIN(pUserData);
1202 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pDasherMain);
1204 gtk_dasher_control_set_offset(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), dasher_editor_get_offset(pPrivate->pEditor));
1207 extern "C" gboolean
1208 dasher_main_cb_window_close(GtkWidget *pWidget, GdkEvent *event, gpointer pUserData) {
1209 DasherMain *pDasherMain = DASHER_MAIN(pUserData);
1210 dasher_main_command_quit(pDasherMain);
1212 /* Returning true stops further propagation */
1213 return TRUE;
1216 extern "C" void
1217 parameter_notification(GtkDasherControl *pDasherControl, gint iParameter, gpointer pUserData) {
1218 DasherMain *pDasherMain = DASHER_MAIN(pUserData);
1219 dasher_main_handle_parameter_change(pDasherMain, iParameter);
1222 // TODO: Not really sure what happens here - need to sort out focus behaviour in general
1223 extern "C" bool
1224 focus_in_event(GtkWidget *widget, GdkEventFocus *event, gpointer data) {
1225 return grab_focus();
1228 extern "C" bool
1229 edit_focus_in_event(GtkWidget *widget, GdkEventFocus *event, gpointer data) {
1230 return true;
1233 extern "C" gboolean
1234 take_real_focus(GtkWidget *widget, GdkEventFocus *event, gpointer user_data) {
1235 // g_bForwardKeyboard = false;
1236 return false;
1239 extern "C" gboolean
1240 edit_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) {
1241 // TODO: Reimplement
1243 // if(g_bForwardKeyboard) {
1244 // gboolean *returnType;
1245 // g_signal_emit_by_name(GTK_WIDGET(pPrivate->pDasherWidget), "key_press_event", event, &returnType);
1246 // return true;
1247 // }
1248 // else {
1249 // return false;
1250 // }
1252 // TODO: Check callback return functions
1253 return false;
1256 extern "C" gboolean
1257 edit_key_release(GtkWidget *widget, GdkEventKey *event, gpointer user_data) {
1258 // TODO: reimplement
1260 // if(g_bForwardKeyboard) {
1261 // gboolean *returnType;
1262 // g_signal_emit_by_name(GTK_WIDGET(pPrivate->pDasherWidget), "key_release_event", event, &returnType);
1263 // return true;
1264 // }
1265 // else {
1266 // return false;
1267 // }
1269 // TODO: Check callback return functions
1270 return false;
1273 extern "C" gboolean
1274 test_focus_handler(GtkWidget *pWidget, GtkDirectionType iDirection, gpointer *pUserData) {
1275 return FALSE;
1278 extern "C" void
1279 handle_start_event(GtkDasherControl *pDasherControl, gpointer data) {
1280 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(data);
1282 dasher_editor_grab_focus(pPrivate->pEditor);
1286 // TODO: Make this only work for children of the main window
1287 extern "C" gint
1288 dasher_main_key_snooper(GtkWidget *pWidget, GdkEventKey *pEvent,
1289 gpointer pUserData) {
1290 DasherMain *pSelf = DASHER_MAIN(pUserData);
1292 gint iButton = dasher_main_lookup_key(pSelf, pEvent->keyval);
1294 if(iButton != -1) {
1295 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pSelf);
1297 if (pWidget == GTK_WIDGET(pPrivate->pMainWindow) ||
1298 gtk_widget_is_ancestor(pWidget, GTK_WIDGET(pPrivate->pMainWindow))) {
1299 if(pPrivate->pDasherWidget) {
1300 if(pEvent->type == GDK_KEY_PRESS)
1301 gtk_dasher_control_external_key_down(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), iButton);
1302 else
1303 gtk_dasher_control_external_key_up(GTK_DASHER_CONTROL(pPrivate->pDasherWidget), iButton);
1306 return TRUE;
1308 else {
1309 return FALSE;
1312 else {
1313 return FALSE;
1317 // Callbacks from the Dasher widget
1319 extern "C" void
1320 handle_stop_event(GtkDasherControl *pDasherControl, gpointer data) {
1321 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(data);
1323 if(pPrivate->pEditor)
1324 dasher_editor_handle_stop(pPrivate->pEditor);
1327 // TODO: The following two should probably be made the same
1328 extern "C" void
1329 handle_request_settings(GtkDasherControl * pDasherControl, gpointer data) {
1330 // TODO: reimplement
1331 // dasher_preferences_dialogue_show(g_pPreferencesDialogue);
1334 extern "C" void
1335 gtk2_edit_delete_callback(GtkDasherControl *pDasherControl, const gchar *szText, int iOffset, gpointer user_data) {
1336 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(user_data);
1338 if(pPrivate->pEditor) {
1339 gint displaylength = g_utf8_strlen(szText, -1);
1340 dasher_editor_delete(pPrivate->pEditor, displaylength, iOffset);
1344 extern "C" void
1345 gtk2_edit_output_callback(GtkDasherControl *pDasherControl, const gchar *szText, int iOffset, gpointer user_data) {
1346 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(user_data);
1348 if(pPrivate->pEditor) {
1349 dasher_editor_output(pPrivate->pEditor, szText, iOffset);
1353 extern "C" void
1354 convert_cb(GtkDasherControl *pDasherControl, gpointer pUserData) {
1355 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pUserData);
1357 if(pPrivate->pEditor) {
1358 dasher_editor_edit_convert(pPrivate->pEditor);
1362 extern "C" void
1363 protect_cb(GtkDasherControl *pDasherControl, gpointer pUserData) {
1364 DasherMainPrivate *pPrivate = DASHER_MAIN_GET_PRIVATE(pUserData);
1366 if(pPrivate->pEditor) {
1367 dasher_editor_edit_protect(pPrivate->pEditor);