Sorry - big commit since I've been holding off until it worked again Fixed
[dasher.git] / Src / Gtk2 / dasher_editor.cpp
blob7de1fdc5976a37885c33131730d45b1f5c708359
1 #include <gtk/gtk.h>
2 #include <string.h>
4 #include "dasher.h"
5 #ifndef WITH_MAEMO
6 #include "dasher_action_copy.h"
7 #endif
8 #include "dasher_action_keyboard.h"
9 #ifdef WITH_MAEMO
10 #include "dasher_action_keyboard_maemo.h"
11 #else
12 #include "dasher_action_script.h"
13 #endif
14 #ifdef GNOME_SPEECH
15 #include "dasher_action_speech.h"
16 #endif
17 #include "dasher_editor.h"
18 #include "dasher_external_buffer.h"
19 #include "dasher_internal_buffer.h"
20 #include "dasher_lock_dialogue.h"
21 #include "fileops.h"
22 #include "game_mode_helper.h"
23 #include "FontDialogues.h"
24 #include "GtkDasherControl.h"
25 #include "Menu.h"
26 #include "Preferences.h"
28 // TODO: Maybe reimplement something along the lines of the following, which used to be in edit.cc
30 // void set_mark() {
31 // GtkTextIter oBufferEnd;
32 // GtkTextIter oBufferStart;
33 // gtk_text_buffer_get_bounds( the_text_buffer, &oBufferStart, &oBufferEnd);
34 // gtk_text_buffer_create_mark(the_text_buffer, "new_start", &oBufferEnd, true);
35 // }
37 // const gchar *get_new_text() {
38 // GtkTextIter oNewStart;
39 // GtkTextIter oNewEnd;
40 // GtkTextIter oDummy;
42 // gtk_text_buffer_get_bounds( the_text_buffer, &oDummy, &oNewEnd);
43 // gtk_text_buffer_get_iter_at_mark( the_text_buffer, &oNewStart, gtk_text_buffer_get_mark(the_text_buffer, "new_start"));
45 // return gtk_text_buffer_get_text( the_text_buffer, &oNewStart, &oNewEnd, false );
47 // }
49 // ---
51 #define ACTION_STATE_SHOW 1
52 #define ACTION_STATE_CONTROL 2
53 #define ACTION_STATE_AUTO 4
55 typedef struct _EditorAction EditorAction;
57 struct _EditorAction {
58 DasherAction *pAction;
59 EditorAction *pNext;
60 EditorAction *pPrevious;
61 gint iControlID;
62 gint iID; // TODO: does this need to be separate from iControlID?
63 gboolean bShow;
64 gboolean bControl;
65 gboolean bAuto;
68 static void dasher_editor_class_init(DasherEditorClass *pClass);
69 static void dasher_editor_init(DasherEditor *pEditor);
70 static void dasher_editor_destroy(GObject *pObject);
72 void dasher_editor_select_all(DasherEditor *pSelf);
73 void dasher_editor_setup_actions(DasherEditor *pSelf);
74 void dasher_editor_add_action(DasherEditor *pSelf, DasherAction *pNewAction);
75 EditorAction *dasher_editor_get_action_by_id(DasherEditor *pSelf, int iID);
76 void dasher_editor_rebuild_action_pane(DasherEditor *pSelf);
77 void dasher_editor_display_message(DasherEditor *pSelf, DasherMessageInfo *pMessageInfo);
78 void dasher_editor_handle_parameter_change(DasherEditor *pSelf, int iParameter);
79 void dasher_editor_handle_pre_parameter_change(DasherEditor *pSelf, int iParameter);
80 void dasher_editor_check_activity(DasherEditor *pSelf, EditorAction *pAction);
81 void dasher_editor_action_save_state(DasherEditor *pSelf, EditorAction *pAction);
83 // TODO: Should these be public?
84 void dasher_editor_convert(DasherEditor *pSelf);
85 void dasher_editor_protect(DasherEditor *pSelf);
87 // Private methods not in class
88 extern "C" void action_button_callback(GtkWidget *pWidget, gpointer pUserData);
89 extern "C" void context_changed_handler(GObject *pSource, gpointer pUserData);
90 extern "C" void main_window_realized(DasherMain *pMain, gpointer pUserData);
92 typedef struct _DasherEditorPrivate DasherEditorPrivate;
94 struct _DasherEditorPrivate {
95 GtkTextView *pTextView;
96 GtkTextBuffer *pBuffer;
97 GtkVBox *pActionPane;
98 GtkClipboard *pTextClipboard;
99 GtkClipboard *pPrimarySelection;
100 EditorAction *pActionRing;
101 EditorAction *pActionIter;
102 gboolean bActionIterStarted;
103 gint iNextActionID;
104 IDasherBufferSet *pBufferSet;
105 IDasherBufferSet *pExternalBuffer;
106 IDasherBufferSet *pInternalBuffer;
107 GameModeHelper *pGameModeHelper;
110 GType dasher_editor_get_type() {
111 static GType dasher_editor_type = 0;
113 if(!dasher_editor_type) {
114 static const GTypeInfo dasher_editor_info = {
115 sizeof(DasherEditorClass),
116 NULL,
117 NULL,
118 (GClassInitFunc) dasher_editor_class_init,
119 NULL,
120 NULL,
121 sizeof(DasherEditor),
123 (GInstanceInitFunc) dasher_editor_init,
124 NULL
127 dasher_editor_type = g_type_register_static(G_TYPE_OBJECT, "DasherEditor", &dasher_editor_info, static_cast < GTypeFlags > (0));
130 return dasher_editor_type;
133 static void dasher_editor_class_init(DasherEditorClass *pClass) {
134 GObjectClass *pObjectClass = (GObjectClass *) pClass;
135 pObjectClass->finalize = dasher_editor_destroy;
138 static void dasher_editor_init(DasherEditor *pDasherControl) {
139 pDasherControl->private_data = new DasherEditorPrivate;
141 ((DasherEditorPrivate *)(pDasherControl->private_data))->pBufferSet = 0;
142 ((DasherEditorPrivate *)(pDasherControl->private_data))->pInternalBuffer = 0;
143 ((DasherEditorPrivate *)(pDasherControl->private_data))->pExternalBuffer = 0;
146 static void dasher_editor_destroy(GObject *pObject) {
147 // FIXME - I think we need to chain up through the finalize methods
148 // of the parent classes here...
149 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(((DasherEditor *)pObject)->private_data);
151 EditorAction *pCurrentAction = pPrivate->pActionRing;
153 if(pCurrentAction) {
154 bool bStarted = false;
156 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
157 bStarted = true;
158 dasher_action_deactivate(pCurrentAction->pAction);
159 g_object_unref(G_OBJECT(pCurrentAction->pAction));
160 pCurrentAction = pCurrentAction->pNext;
164 if(pPrivate->pBufferSet)
165 g_object_unref(G_OBJECT(pPrivate->pBufferSet));
167 delete (DasherEditorPrivate *)(((DasherEditor *)pObject)->private_data);
170 DasherEditor *dasher_editor_new(int argc, char **argv) {
171 DasherEditor *pDasherControl;
172 pDasherControl = (DasherEditor *)(g_object_new(dasher_editor_get_type(), NULL));
174 return pDasherControl;
177 void dasher_editor_initialise(DasherEditor *pSelf) {
178 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
180 GladeXML *pGladeXML = dasher_main_get_glade(g_pDasherMain);
182 GtkTextView *pTextView = GTK_TEXT_VIEW(glade_xml_get_widget(pGladeXML, "the_text_view"));
183 GtkVBox *pActionPane = GTK_VBOX(glade_xml_get_widget(pGladeXML, "vbox39"));
185 pPrivate->pTextView = pTextView;
186 pPrivate->pBuffer = gtk_text_view_get_buffer(pTextView);
187 pPrivate->pActionPane = pActionPane;
188 pPrivate->pTextClipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
189 pPrivate->pPrimarySelection = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
190 pPrivate->pActionRing = NULL;
191 pPrivate->iNextActionID = 0;
192 pPrivate->pGameModeHelper = 0;
194 dasher_editor_create_buffer(pSelf);
195 dasher_editor_setup_actions(pSelf);
199 IDasherBufferSet *dasher_editor_get_buffer_set(DasherEditor *pSelf) {
200 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
201 return IDASHER_BUFFER_SET(dasher_internal_buffer_new(pPrivate->pTextView));
205 void dasher_editor_clipboard(DasherEditor *pSelf, clipboard_action act) {
206 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
208 GtkTextIter *start = new GtkTextIter;
209 GtkTextIter *end = new GtkTextIter;
211 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), start, 0);
212 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), end, -1);
214 gchar *the_text = gtk_text_buffer_get_text(pPrivate->pBuffer, start, end, TRUE);
216 switch (act) {
217 case CLIPBOARD_CUT:
218 gtk_text_buffer_cut_clipboard(pPrivate->pBuffer, pPrivate->pTextClipboard, TRUE);
219 break;
220 case CLIPBOARD_COPY:
221 gtk_text_buffer_copy_clipboard(pPrivate->pBuffer, pPrivate->pTextClipboard);
222 break;
223 case CLIPBOARD_PASTE:
224 gtk_text_buffer_paste_clipboard(pPrivate->pBuffer, pPrivate->pTextClipboard, NULL, TRUE);
225 break;
226 case CLIPBOARD_COPYALL:
227 gtk_clipboard_set_text(pPrivate->pTextClipboard, the_text, strlen(the_text));
228 gtk_clipboard_set_text(pPrivate->pPrimarySelection, the_text, strlen(the_text));
230 break;
231 case CLIPBOARD_SELECTALL:
232 dasher_editor_select_all(pSelf);
233 break;
234 case CLIPBOARD_CLEAR:
235 gtk_text_buffer_set_text(pPrivate->pBuffer, "", 0);
236 break;
238 g_free(the_text);
240 delete start;
241 delete end;
244 void dasher_editor_select_all(DasherEditor *pSelf) {
245 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
246 GtkTextIter *start, *end;
248 start = new GtkTextIter;
249 end = new GtkTextIter;
251 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), start, 0);
252 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), end, -1);
254 GtkTextMark *selection = gtk_text_buffer_get_mark(pPrivate->pBuffer, "selection_bound");
255 GtkTextMark *cursor = gtk_text_buffer_get_mark(pPrivate->pBuffer, "insert");
257 gtk_text_buffer_move_mark(pPrivate->pBuffer, selection, start);
258 gtk_text_buffer_move_mark(pPrivate->pBuffer, cursor, end);
260 delete start;
261 delete end;
264 void dasher_editor_handle_stop(DasherEditor *pSelf) {
265 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
267 // See if anything is set to auto:
268 EditorAction *pCurrentAction = pPrivate->pActionRing;
270 if(pCurrentAction) {
271 bool bStarted = false;
273 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
274 bStarted = true;
275 if(pCurrentAction->bAuto)
276 dasher_action_execute(pCurrentAction->pAction, dasher_editor_get_all_text(pSelf));
277 pCurrentAction = pCurrentAction->pNext;
282 void dasher_editor_handle_start(DasherEditor *pSelf) {
283 // The edit box keeps track of where we started
285 // TODO: This should be filtered through the buffer, rather than directly to the edit box
286 // set_mark();
289 void dasher_editor_handle_control(DasherEditor *pSelf, int iNodeID) {
290 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
292 if(iNodeID == Dasher::CControlManager::CTL_USER + 1)
293 dasher_editor_clear(pSelf, false); // Clear node is a special case (it shouldn't be)
294 else {
295 EditorAction *pCurrentAction = pPrivate->pActionRing;
296 bool bStarted = false;
298 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
299 bStarted = true;
300 if(pCurrentAction->iControlID == iNodeID) {
301 dasher_action_execute(pCurrentAction->pAction, dasher_editor_get_all_text(pSelf));
302 dasher_editor_clear(pSelf, true);
304 pCurrentAction = pCurrentAction->pNext;
309 // TODO: Think about changing signals so we don't need to do this translation
311 struct SControlMap {
312 int iEvent;
313 int iDir;
314 int iDist;
315 bool bDelete;
318 static struct SControlMap sMap[] = {
319 {Dasher::CControlManager::CTL_MOVE_FORWARD_CHAR, EDIT_FORWARDS, EDIT_CHAR, false},
320 {Dasher::CControlManager::CTL_MOVE_FORWARD_WORD, EDIT_FORWARDS, EDIT_WORD, false},
321 {Dasher::CControlManager::CTL_MOVE_FORWARD_LINE, EDIT_FORWARDS, EDIT_LINE, false},
322 {Dasher::CControlManager::CTL_MOVE_FORWARD_FILE, EDIT_FORWARDS, EDIT_FILE, false},
323 {Dasher::CControlManager::CTL_MOVE_BACKWARD_CHAR, EDIT_BACKWARDS, EDIT_CHAR, false},
324 {Dasher::CControlManager::CTL_MOVE_BACKWARD_WORD, EDIT_BACKWARDS, EDIT_WORD, false},
325 {Dasher::CControlManager::CTL_MOVE_BACKWARD_LINE, EDIT_BACKWARDS, EDIT_LINE, false},
326 {Dasher::CControlManager::CTL_MOVE_BACKWARD_FILE, EDIT_BACKWARDS, EDIT_FILE, false},
327 {Dasher::CControlManager::CTL_DELETE_FORWARD_CHAR, EDIT_FORWARDS, EDIT_CHAR, true},
328 {Dasher::CControlManager::CTL_DELETE_FORWARD_WORD, EDIT_FORWARDS, EDIT_WORD, true},
329 {Dasher::CControlManager::CTL_DELETE_FORWARD_LINE, EDIT_FORWARDS, EDIT_LINE, true},
330 {Dasher::CControlManager::CTL_DELETE_FORWARD_FILE, EDIT_FORWARDS, EDIT_FILE, true},
331 {Dasher::CControlManager::CTL_DELETE_BACKWARD_CHAR, EDIT_BACKWARDS, EDIT_CHAR, true},
332 {Dasher::CControlManager::CTL_DELETE_BACKWARD_WORD, EDIT_BACKWARDS, EDIT_WORD, true},
333 {Dasher::CControlManager::CTL_DELETE_BACKWARD_LINE, EDIT_BACKWARDS, EDIT_LINE, true},
334 {Dasher::CControlManager::CTL_DELETE_BACKWARD_FILE, EDIT_BACKWARDS, EDIT_FILE, true}
337 if(pPrivate->pBufferSet) {
338 for(unsigned int i(0); i < sizeof(sMap)/sizeof(struct SControlMap); ++i) {
339 if(sMap[i].iEvent == iNodeID) {
340 if(sMap[i].bDelete)
341 idasher_buffer_set_edit_delete(pPrivate->pBufferSet, sMap[i].iDir, sMap[i].iDist);
342 else
343 idasher_buffer_set_edit_move(pPrivate->pBufferSet, sMap[i].iDir, sMap[i].iDist);
349 void dasher_editor_handle_parameter_change(DasherEditor *pSelf, int iParameter) {
350 // switch(iParameter) {
351 // case APP_LP_STYLE:
352 // dasher_editor_create_buffer(pSelf);
353 // break;
354 // }
356 dasher_preferences_dialogue_handle_parameter_change(g_pPreferencesDialogue, iParameter);
357 dasher_main_handle_parameter_change(g_pDasherMain, iParameter);
360 void dasher_editor_handle_pre_parameter_change(DasherEditor *pSelf, int iParameter) {
361 dasher_main_handle_pre_parameter_change(g_pDasherMain, iParameter);
364 void dasher_editor_add_action(DasherEditor *pSelf, DasherAction *pNewAction) {
365 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
367 EditorAction *pNewEditorAction = new EditorAction;
368 pNewEditorAction->pAction = pNewAction;
369 pNewEditorAction->iID = pPrivate->iNextActionID;
370 ++pPrivate->iNextActionID;
372 gchar szRegistryName[256];
373 strncpy(szRegistryName, "Action_", 256);
374 strncat(szRegistryName, dasher_action_get_name(pNewEditorAction->pAction), 255 - strlen(szRegistryName));
376 for(unsigned int i(0); i < strlen(szRegistryName); ++i)
377 if(szRegistryName[i] == ' ')
378 szRegistryName[i] = '_';
380 gint iState;
382 if(!dasher_app_settings_get_free_long(g_pDasherAppSettings, szRegistryName, iState)) {
383 if(!strcmp(dasher_action_get_name(pNewEditorAction->pAction), "Speak"))
384 iState = 0;
385 else
386 iState = ACTION_STATE_SHOW | ACTION_STATE_CONTROL;
388 dasher_app_settings_set_free_long(g_pDasherAppSettings, szRegistryName, iState);
391 pNewEditorAction->bShow = iState & ACTION_STATE_SHOW;
392 pNewEditorAction->bControl = iState & ACTION_STATE_CONTROL;
393 pNewEditorAction->bAuto = iState & ACTION_STATE_AUTO;
395 dasher_editor_check_activity(pSelf, pNewEditorAction);
397 if(pPrivate->pActionRing) {
398 pNewEditorAction->pNext = pPrivate->pActionRing;
399 pNewEditorAction->pPrevious = pPrivate->pActionRing->pPrevious;
400 pPrivate->pActionRing->pPrevious->pNext = pNewEditorAction;
401 pPrivate->pActionRing->pPrevious = pNewEditorAction;
403 else {
404 pNewEditorAction->pNext = pNewEditorAction;
405 pNewEditorAction->pPrevious = pNewEditorAction;
408 pPrivate->pActionRing = pNewEditorAction;
410 if(iState & ACTION_STATE_SHOW)
411 gtk_dasher_control_add_action_button(GTK_DASHER_CONTROL(pDasherWidget), dasher_action_get_name(pNewEditorAction->pAction));
414 void dasher_editor_setup_actions(DasherEditor *pSelf) {
415 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
417 // TODO: Activate and deactivate methods for actions
418 // TODO: Clear shouldn't be a special case (include support for false in clear method)
420 #ifdef GNOME_SPEECH
421 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_speech_new()));
422 #endif
424 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_keyboard_new(pPrivate->pExternalBuffer)));
426 #ifdef WITH_MAEMO
427 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_keyboard_maemo_new()));
428 #else
429 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_copy_new(pSelf)));
431 GDir *pDirectory;
432 G_CONST_RETURN gchar *szFilename;
434 gchar *szUserScriptDir = new gchar[strlen(dasher_app_settings_get_string(g_pDasherAppSettings, SP_USER_LOC))+9];
435 strcpy(szUserScriptDir, dasher_app_settings_get_string(g_pDasherAppSettings, SP_USER_LOC));
436 strcat(szUserScriptDir, "scripts/");
438 pDirectory = g_dir_open(szUserScriptDir, 0, NULL);
440 if(pDirectory) {
441 while((szFilename = g_dir_read_name(pDirectory))) {
442 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_script_new(szUserScriptDir, szFilename)));
445 g_dir_close(pDirectory);
448 delete[] szUserScriptDir;
450 gchar *szSystemScriptDir = new gchar[strlen(dasher_app_settings_get_string(g_pDasherAppSettings, SP_SYSTEM_LOC))+9];
451 strcpy(szSystemScriptDir, dasher_app_settings_get_string(g_pDasherAppSettings, SP_SYSTEM_LOC));
452 strcat(szSystemScriptDir, "scripts/");
454 pDirectory = g_dir_open(szSystemScriptDir, 0, NULL);
456 if(pDirectory) {
457 while((szFilename = g_dir_read_name(pDirectory))) {
458 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_script_new(szSystemScriptDir, szFilename)));
461 g_dir_close(pDirectory);
464 delete[] szSystemScriptDir;
465 #endif
467 gtk_dasher_control_register_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER, "Actions", -1 );
468 gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER, Dasher::CControlManager::CTL_ROOT, -2);
469 int iControlOffset(1);
471 gtk_dasher_control_register_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, "Clear", -1 );
472 gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, Dasher::CControlManager::CTL_USER, -2);
473 gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), -1, Dasher::CControlManager::CTL_USER + iControlOffset, -2);
474 ++iControlOffset;
476 EditorAction *pCurrentAction = pPrivate->pActionRing;
477 bool bStarted = false;
479 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
480 bStarted = true;
482 gtk_dasher_control_register_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, dasher_action_get_name(pCurrentAction->pAction), -1 );
483 gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, Dasher::CControlManager::CTL_USER, -2);
484 gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), -1, Dasher::CControlManager::CTL_USER + iControlOffset, -2);
485 pCurrentAction->iControlID = Dasher::CControlManager::CTL_USER + iControlOffset;
486 ++iControlOffset;
488 pCurrentAction = pCurrentAction->pNext;
491 #ifndef WITH_MAEMOFULLSCREEN
492 // dasher_editor_rebuild_action_pane(pSelf);
493 #endif
496 extern "C" void delete_children_callback(GtkWidget *pWidget, gpointer pUserData) {
497 gtk_widget_destroy(pWidget);
500 extern "C" void main_window_realized(DasherMain *pMain, gpointer pUserData) {
504 void dasher_editor_rebuild_action_pane(DasherEditor *pSelf) {
505 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
507 // Delete any existing widgets
508 gtk_container_foreach(GTK_CONTAINER(pPrivate->pActionPane), delete_children_callback, 0);
510 // Add the cancel button
511 GtkButton *pNewButton = GTK_BUTTON(gtk_button_new_with_label("Clear"));
512 gtk_widget_show(GTK_WIDGET(pNewButton));
514 void **pUserData = new void *[2];
515 pUserData[0] = (void *)pSelf;
516 pUserData[1] = 0;
518 g_signal_connect(G_OBJECT(pNewButton), "clicked", G_CALLBACK(action_button_callback), pUserData);
519 #ifdef WITH_MAEMO
520 // For Maemo we want the packing to expand
521 gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), true, true, 0);
522 #else
523 gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), false, false, 0);
524 #endif
527 EditorAction *pCurrentAction = pPrivate->pActionRing;
528 bool bStarted = false;
530 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
531 bStarted = true;
532 if(pCurrentAction->bShow) {
533 GtkButton *pNewButton = GTK_BUTTON(gtk_button_new_with_label(dasher_action_get_name(pCurrentAction->pAction)));
534 gtk_widget_show(GTK_WIDGET(pNewButton));
536 pUserData = new void *[2];
537 pUserData[0] = (void *)pSelf;
538 pUserData[1] = (void *)(pCurrentAction->pAction);
540 g_signal_connect(G_OBJECT(pNewButton), "clicked", G_CALLBACK(action_button_callback), pUserData);
541 #ifdef WITH_MAEMO
542 // For Maemo we want the packing to expand
543 gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), true, true, 0);
544 #else
545 gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), false, false, 0);
546 #endif
548 pCurrentAction = pCurrentAction->pNext;
552 // TODO: We shouldn't need to know about the buffer here - make this a method of the buffer set
553 const gchar *dasher_editor_get_all_text(DasherEditor *pSelf) {
554 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
556 GtkTextIter oStart;
557 GtkTextIter oEnd;
559 gtk_text_buffer_get_start_iter(pPrivate->pBuffer, &oStart);
560 gtk_text_buffer_get_end_iter(pPrivate->pBuffer, &oEnd);
562 return gtk_text_buffer_get_text(pPrivate->pBuffer, &oStart, &oEnd, false );
565 void dasher_editor_action_button(DasherEditor *pSelf, DasherAction *pAction) {
566 if(pAction) {
567 dasher_action_execute(pAction, dasher_editor_get_all_text(pSelf));
568 dasher_editor_clear(pSelf, true);
570 else { // Clear button
571 dasher_editor_clear(pSelf, false);
575 void dasher_editor_clear(DasherEditor *pSelf, gboolean bStore) {
576 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
578 if(IS_DASHER_INTERNAL_BUFFER(pPrivate->pBufferSet))
579 dasher_internal_buffer_clear(DASHER_INTERNAL_BUFFER(pPrivate->pBufferSet));
583 void dasher_editor_actions_start(DasherEditor *pSelf) {
584 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
586 pPrivate->bActionIterStarted = false;
587 pPrivate->pActionIter = pPrivate->pActionRing;
590 bool dasher_editor_actions_more(DasherEditor *pSelf) {
591 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
593 return(!pPrivate->bActionIterStarted || (pPrivate->pActionIter != pPrivate->pActionRing));
596 void dasher_editor_actions_get_next(DasherEditor *pSelf, const gchar **szName, gint *iID, gboolean *bShow, gboolean *bControl, gboolean *bAuto) {
597 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
599 *szName = dasher_action_get_name(pPrivate->pActionIter->pAction);
600 *iID = pPrivate->pActionIter->iID;
601 *bShow = pPrivate->pActionIter->bShow;
602 *bControl = pPrivate->pActionIter->bControl;
603 *bAuto = pPrivate->pActionIter->bAuto;
605 pPrivate->pActionIter = pPrivate->pActionIter->pNext;
606 pPrivate->bActionIterStarted = true;
609 void dasher_editor_action_set_show(DasherEditor *pSelf, int iActionID, bool bValue) {
610 EditorAction *pAction;
611 pAction = dasher_editor_get_action_by_id(pSelf, iActionID);
613 if(pAction) {
614 pAction->bShow = bValue;
615 dasher_editor_check_activity(pSelf, pAction);
616 dasher_editor_rebuild_action_pane(pSelf);
618 dasher_editor_action_save_state(pSelf, pAction);
622 void dasher_editor_action_set_control(DasherEditor *pSelf, int iActionID, bool bValue) {
623 // TODO: Need to actually change behaviour in resonse to these calls
625 EditorAction *pAction;
626 pAction = dasher_editor_get_action_by_id(pSelf, iActionID);
628 if(pAction) {
629 pAction->bControl = bValue;
630 dasher_editor_check_activity(pSelf, pAction);
631 if(bValue)
632 gtk_dasher_control_connect_node(GTK_DASHER_CONTROL(pDasherWidget), pAction->iControlID, Dasher::CControlManager::CTL_USER, -2);
633 else
634 gtk_dasher_control_disconnect_node(GTK_DASHER_CONTROL(pDasherWidget), pAction->iControlID, Dasher::CControlManager::CTL_USER);
636 dasher_editor_action_save_state(pSelf, pAction);
640 void dasher_editor_action_set_auto(DasherEditor *pSelf, int iActionID, bool bValue) {
641 EditorAction *pAction;
642 pAction = dasher_editor_get_action_by_id(pSelf, iActionID);
644 if(pAction) {
645 pAction->bAuto = bValue;
646 dasher_editor_check_activity(pSelf, pAction);
648 dasher_editor_action_save_state(pSelf, pAction);
652 void dasher_editor_action_save_state(DasherEditor *pSelf, EditorAction *pAction) {
653 gchar szRegistryName[256];
654 strncpy(szRegistryName, "Action_", 256);
655 strncat(szRegistryName, dasher_action_get_name(pAction->pAction), 255 - strlen(szRegistryName));
657 for(unsigned int i(0); i < strlen(szRegistryName); ++i)
658 if(szRegistryName[i] == ' ')
659 szRegistryName[i] = '_';
661 gint iState = 0;
663 if(pAction->bShow)
664 iState += ACTION_STATE_SHOW;
666 if(pAction->bControl)
667 iState += ACTION_STATE_CONTROL;
669 if(pAction->bAuto)
670 iState += ACTION_STATE_AUTO;
672 dasher_app_settings_set_free_long(g_pDasherAppSettings, szRegistryName, iState);
675 void dasher_editor_output(DasherEditor *pSelf, const gchar *szText) {
676 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
678 if(pPrivate->pBufferSet)
679 idasher_buffer_set_insert(pPrivate->pBufferSet, szText);
681 if(pPrivate->pGameModeHelper)
682 game_mode_helper_output(pPrivate->pGameModeHelper, szText);
685 void dasher_editor_delete(DasherEditor *pSelf, int iLength) {
686 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
688 if(pPrivate->pBufferSet)
689 idasher_buffer_set_delete(pPrivate->pBufferSet, iLength);
691 if(pPrivate->pGameModeHelper)
692 game_mode_helper_delete(pPrivate->pGameModeHelper, iLength);
695 void dasher_editor_convert(DasherEditor *pSelf) {
696 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
698 if(pPrivate->pBufferSet)
699 idasher_buffer_set_edit_convert(pPrivate->pBufferSet);
702 void dasher_editor_protect(DasherEditor *pSelf) {
703 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
705 if(pPrivate->pBufferSet)
706 idasher_buffer_set_edit_protect(pPrivate->pBufferSet);
709 EditorAction *dasher_editor_get_action_by_id(DasherEditor *pSelf, int iID){
710 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
712 EditorAction *pCurrentAction = pPrivate->pActionRing;
713 bool bStarted = false;
715 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
716 bStarted = true;
717 if(pCurrentAction->iID == iID)
718 return pCurrentAction;
719 pCurrentAction = pCurrentAction->pNext;
722 return 0;
725 void dasher_editor_create_buffer(DasherEditor *pSelf) {
726 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
728 if(!(pPrivate->pExternalBuffer))
729 pPrivate->pExternalBuffer = IDASHER_BUFFER_SET(dasher_external_buffer_new());
731 if(!(pPrivate->pInternalBuffer)) {
732 pPrivate->pInternalBuffer = dasher_editor_get_buffer_set(pSelf);
733 idasher_buffer_set_conversion_mode(pPrivate->pInternalBuffer, gtk_dasher_control_get_parameter_bool(GTK_DASHER_CONTROL(pDasherWidget), BP_CONVERSION_MODE));
736 if(dasher_app_settings_get_long(g_pDasherAppSettings, APP_LP_STYLE) == 2)
737 pPrivate->pBufferSet = pPrivate->pExternalBuffer;
738 else {
739 pPrivate->pBufferSet = pPrivate->pInternalBuffer;
742 // TODO: Fix this
743 g_signal_connect(G_OBJECT(pPrivate->pBufferSet), "context_changed", G_CALLBACK(context_changed_handler), NULL);
747 void dasher_editor_refresh_context(DasherEditor *pSelf, int iMaxLength) {
748 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
750 gchar *szContext;
752 if(pPrivate->pBufferSet)
753 szContext = idasher_buffer_set_get_context(pPrivate->pBufferSet, iMaxLength);
754 else
755 szContext = "";
757 if(szContext && (strlen(szContext) > 0))
758 gtk_dasher_control_set_context( GTK_DASHER_CONTROL(pDasherWidget), szContext );
761 void dasher_editor_display_message(DasherEditor *pSelf, DasherMessageInfo *pMessageInfo) {
762 GtkMessageDialog *pDialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new(0, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, pMessageInfo->szMessage));
763 gtk_dialog_run(GTK_DIALOG(pDialog));
764 gtk_widget_destroy(GTK_WIDGET(pDialog));
767 void dasher_editor_generate_filename(DasherEditor *pSelf) {
768 if( dasher_app_settings_get_bool(g_pDasherAppSettings, APP_BP_TIME_STAMP )) {
769 // Build a filename based on the current time and date
770 tm *t_struct;
771 time_t ctime;
772 char cwd[1000];
773 char tbuffer[200];
775 ctime = time(NULL);
777 t_struct = localtime(&ctime);
779 if(filename) {
780 g_free((void *)filename);
783 getcwd(cwd, 1000);
784 snprintf(tbuffer, 200, "dasher-%04d%02d%02d-%02d%02d.txt", (t_struct->tm_year + 1900), (t_struct->tm_mon + 1), t_struct->tm_mday, t_struct->tm_hour, t_struct->tm_min);
786 filename = g_build_path("/", cwd, tbuffer, NULL);
788 else {
789 if(filename) {
790 g_free((void *)filename);
792 filename = NULL;
795 // TODO: Rationalise this - should probably be in 'new' function rather than here
796 dasher_main_set_filename(g_pDasherMain, filename);
799 // TODO: Rationalise this
800 void dasher_editor_open(DasherEditor *pSelf, const gchar *szFilename) {
801 open_file(szFilename);
804 // TODO: Rationalise this
805 bool dasher_editor_save_as(DasherEditor *pSelf, const gchar *szFilename, bool bAppend) {
806 return save_file_as(szFilename, bAppend);
809 void dasher_editor_check_activity(DasherEditor *pSelf, EditorAction *pAction) {
810 gboolean bNeedActive(pAction->bShow || pAction->bControl || pAction->bAuto);
811 gboolean bActive(dasher_action_get_active(pAction->pAction));
813 if(bNeedActive && !bActive)
814 dasher_action_activate(pAction->pAction);
815 else if(!bNeedActive && bActive)
816 dasher_action_deactivate(pAction->pAction);
819 void dasher_editor_start_tutorial(DasherEditor *pSelf) {
820 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
822 pPrivate->pGameModeHelper = GAME_MODE_HELPER(game_mode_helper_new(GTK_DASHER_CONTROL(pDasherWidget)));
825 void dasher_editor_command(DasherEditor *pSelf, const gchar *szCommand) {
826 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
828 if(!strcmp(szCommand, "Preferences")) {
829 dasher_preferences_dialogue_show(g_pPreferencesDialogue);
831 else if(!strcmp(szCommand, "Exit")) {
832 ask_save_before_exit(NULL, NULL);
834 else if(!strcmp(szCommand, "Help")) {
835 show_help(NULL, NULL);
837 else if(!strcmp(szCommand, "About")) {
838 about_dasher(NULL, NULL);
840 else {
841 bool bActionIterStarted = false;
842 EditorAction *pActionIter = pPrivate->pActionRing;
844 while((pActionIter != pPrivate->pActionRing) || !bActionIterStarted) {
845 bActionIterStarted = true;
847 if(!strcmp(dasher_action_get_name(pActionIter->pAction), szCommand)) {
848 dasher_action_execute(pActionIter->pAction, dasher_editor_get_all_text(pSelf));
849 return;
852 pActionIter = pActionIter->pNext;
857 // Callbacks
859 extern "C" void action_button_callback(GtkWidget *pWidget, gpointer pUserData) {
860 void **pPointers((void **)pUserData);
861 dasher_editor_action_button((DasherEditor *)pPointers[0], (DasherAction *)pPointers[1]);
864 // NOTE: The next two aren't real signals
865 extern "C" void pre_parameter_notification(GtkDasherControl *pDasherControl, gint iParameter, gpointer data) {
866 if(g_pEditor)
867 dasher_editor_handle_pre_parameter_change(g_pEditor, iParameter);
870 extern "C" void parameter_notification(GtkDasherControl *pDasherControl, gint iParameter, gpointer data) {
871 if(g_pEditor)
872 dasher_editor_handle_parameter_change(g_pEditor, iParameter);
875 extern "C" void handle_start_event(GtkDasherControl *pDasherControl, gpointer data) {
876 if(g_pEditor)
877 dasher_editor_handle_start(g_pEditor);
880 extern "C" void handle_stop_event(GtkDasherControl *pDasherControl, gpointer data) {
881 if(g_pEditor)
882 dasher_editor_handle_stop(g_pEditor);
885 extern "C" void handle_context_request(GtkDasherControl * pDasherControl, gint iMaxLength, gpointer data) {
886 if(g_pEditor)
887 dasher_editor_refresh_context(g_pEditor, iMaxLength);
890 extern "C" void handle_control_event(GtkDasherControl *pDasherControl, gint iEvent, gpointer data) {
891 if(g_pEditor)
892 dasher_editor_handle_control(g_pEditor, iEvent);
895 extern "C" void on_message(GtkDasherControl *pDasherControl, gpointer pMessageInfo, gpointer pUserData) {
896 if(g_pEditor)
897 dasher_editor_display_message(g_pEditor, (DasherMessageInfo *)pMessageInfo);
901 extern "C" void on_command(GtkDasherControl *pDasherControl, gchar *szCommand, gpointer pUserData) {
902 if(g_pEditor)
903 dasher_editor_command(g_pEditor, szCommand);
907 // TODO: The following two should probably be made the same
908 extern "C" void handle_request_settings(GtkDasherControl * pDasherControl, gpointer data) {
909 dasher_preferences_dialogue_show(g_pPreferencesDialogue);
912 extern "C" void preferences_display(GtkWidget *widget, gpointer user_data) {
913 dasher_preferences_dialogue_show(g_pPreferencesDialogue);
916 extern "C" void gtk2_edit_delete_callback(GtkDasherControl *pDasherControl, const gchar *szText, gpointer user_data) {
917 gint displaylength = g_utf8_strlen(szText, -1);
918 dasher_editor_delete(g_pEditor, displaylength);
921 extern "C" void gtk2_edit_output_callback(GtkDasherControl *pDasherControl, const gchar *szText, gpointer user_data) {
922 dasher_editor_output(g_pEditor, szText);
925 extern "C" void convert_cb(GtkDasherControl *pDasherControl, gpointer pUserData) {
926 dasher_editor_convert(g_pEditor);
929 extern "C" void protect_cb(GtkDasherControl *pDasherControl, gpointer pUserData) {
930 dasher_editor_protect(g_pEditor);
933 // TODO: This should call back into editor, not directly into Dasher control
934 extern "C" void context_changed_handler(GObject *pSource, gpointer pUserData) {
935 gtk_dasher_control_invalidate_context(GTK_DASHER_CONTROL(pDasherWidget), false);