preparation for release
[dasher.git] / Src / Gtk2 / dasher_editor.cpp
blob313943990d08ad53c703e644920f8d2414724de9
1 #include "config.h"
3 #include <glib/gi18n.h>
4 #include <gtk/gtk.h>
5 /* TODO: r4epair gnome libs flags (or get rid of entirely) */
6 //#ifdef GNOME_LIBS
7 #include <libgnomevfs/gnome-vfs.h>
8 //#endif
10 #include "dasher_action_keyboard.h"
11 #ifdef WITH_MAEMO
12 #include "dasher_action_keyboard_maemo.h"
13 #else
14 #include "dasher_action_script.h"
15 #endif
16 #ifdef GNOME_SPEECH
17 #include "dasher_action_speech.h"
18 #endif
19 #include "dasher_editor.h"
20 #include "dasher_external_buffer.h"
21 #include "dasher_internal_buffer.h"
22 #include "dasher_lock_dialogue.h"
23 #include "dasher_main.h"
24 #include "game_mode_helper.h"
26 // TODO: Maybe reimplement something along the lines of the following, which used to be in edit.cc
28 // void set_mark() {
29 // GtkTextIter oBufferEnd;
30 // GtkTextIter oBufferStart;
31 // gtk_text_buffer_get_bounds( the_text_buffer, &oBufferStart, &oBufferEnd);
32 // gtk_text_buffer_create_mark(the_text_buffer, "new_start", &oBufferEnd, true);
33 // }
35 // const gchar *get_new_text() {
36 // GtkTextIter oNewStart;
37 // GtkTextIter oNewEnd;
38 // GtkTextIter oDummy;
40 // gtk_text_buffer_get_bounds( the_text_buffer, &oDummy, &oNewEnd);
41 // gtk_text_buffer_get_iter_at_mark( the_text_buffer, &oNewStart, gtk_text_buffer_get_mark(the_text_buffer, "new_start"));
43 // return gtk_text_buffer_get_text( the_text_buffer, &oNewStart, &oNewEnd, false );
45 // }
47 // ---
49 #define ACTION_STATE_SHOW 1
50 #define ACTION_STATE_CONTROL 2
51 #define ACTION_STATE_AUTO 4
53 typedef struct _EditorAction EditorAction;
55 struct _EditorAction {
56 DasherAction *pAction;
57 EditorAction *pNext;
58 EditorAction *pPrevious;
59 gint iControlID;
60 gint iID; // TODO: does this need to be separate from iControlID?
61 gboolean bShow;
62 gboolean bControl;
63 gboolean bAuto;
64 gint iNSub;
67 typedef struct _DasherEditorPrivate DasherEditorPrivate;
69 struct _DasherEditorPrivate {
70 DasherMain *pDasherMain;
71 GtkTextView *pTextView;
72 GtkTextBuffer *pBuffer;
73 GtkVBox *pActionPane;
74 GtkClipboard *pTextClipboard;
75 GtkClipboard *pPrimarySelection;
76 EditorAction *pActionRing;
77 EditorAction *pActionIter;
78 gboolean bActionIterStarted;
79 gint iNextActionID;
80 IDasherBufferSet *pBufferSet;
81 IDasherBufferSet *pExternalBuffer;
82 IDasherBufferSet *pInternalBuffer;
83 GameModeHelper *pGameModeHelper;
84 GtkTextMark *pNewMark;
85 DasherAppSettings *pAppSettings;
86 gchar *szFilename;
87 gboolean bFileModified; // TODO: Make this work properly, export to main for quit etc
90 /* Signals */
91 enum {
92 FILENAME_CHANGED,
93 BUFFER_CHANGED,
94 CONTEXT_CHANGED,
95 SIGNAL_NUM
98 static guint dasher_editor_signals[SIGNAL_NUM];
100 static DasherEditor *g_pEditor;
102 /* TODO: Use appropriate macros here */
103 /* G-object boilerplate code */
104 static void dasher_editor_class_init(DasherEditorClass *pClass);
105 static void dasher_editor_init(DasherEditor *pEditor);
106 static void dasher_editor_destroy(GObject *pObject);
108 /* Private methods */
109 static void dasher_editor_select_all(DasherEditor *pSelf);
110 static void dasher_editor_setup_actions(DasherEditor *pSelf);
111 static void dasher_editor_add_action(DasherEditor *pSelf, DasherAction *pNewAction);
112 static EditorAction *dasher_editor_get_action_by_id(DasherEditor *pSelf, int iID);
113 static void dasher_editor_rebuild_action_pane(DasherEditor *pSelf);
114 static void dasher_editor_display_message(DasherEditor *pSelf, DasherMessageInfo *pMessageInfo);
115 static void dasher_editor_check_activity(DasherEditor *pSelf, EditorAction *pAction);
116 static void dasher_editor_action_save_state(DasherEditor *pSelf, EditorAction *pAction);
118 static void dasher_editor_command_new(DasherEditor *pSelf);
119 static void dasher_editor_command_open(DasherEditor *pSelf);
120 static void dasher_editor_command_save(DasherEditor *pSelf, gboolean bPrompt, gboolean bAppend);
122 static gboolean dasher_editor_unix_vfs_open_file(DasherEditor *pSelf, const char *filename, gchar ** buffer, unsigned long long *size);
123 static gboolean dasher_editor_unix_vfs_save_file(DasherEditor *pSelf, const char *filename, gchar * buffer, unsigned long long length, bool append);
125 static void dasher_editor_vfs_print_error(DasherEditor *pSelf, GnomeVFSResult * result, const char *myfilename);
126 static gboolean dasher_editor_gnome_vfs_open_file(DasherEditor *pSelf, const char *filename, gchar ** buffer, unsigned long long *size);
127 static gboolean dasher_editor_gnome_vfs_save_file(DasherEditor *pSelf, const char *filename, gchar * buffer, unsigned long long length, bool append);
128 static void dasher_editor_set_filename(DasherEditor *pSelf, const gchar *szFilename);
130 // TODO: Should these be public?
131 static void dasher_editor_convert(DasherEditor *pSelf);
132 static void dasher_editor_protect(DasherEditor *pSelf);
134 static void dasher_editor_new_buffer(DasherEditor *pSelf, const gchar *szFilename);
136 static void dasher_editor_generate_filename(DasherEditor *pSelf);
137 static void dasher_editor_open(DasherEditor *pSelf, const gchar *szFilename);
138 static bool dasher_editor_save_as(DasherEditor *pSelf, const gchar *szFilename, bool bAppend);
139 static void dasher_editor_create_buffer(DasherEditor *pSelf);
140 static void dasher_editor_clear(DasherEditor *pSelf, gboolean bStore);
141 static void dasher_editor_clipboard(DasherEditor *pSelf, clipboard_action act);
144 // Private methods not in class
145 extern "C" void delete_children_callback(GtkWidget *pWidget, gpointer pUserData);
146 extern "C" void main_window_realized(DasherMain *pMain, gpointer pUserData);
147 extern "C" void action_button_callback(GtkWidget *pWidget, gpointer pUserData);
148 extern "C" void context_changed_handler(GObject *pSource, gpointer pUserData);
149 extern "C" void handle_start_event(GtkDasherControl *pDasherControl, gpointer data);
150 extern "C" void handle_stop_event(GtkDasherControl *pDasherControl, gpointer data);
151 extern "C" void on_message(GtkDasherControl *pDasherControl, gpointer pMessageInfo, gpointer pUserData);
152 extern "C" void on_command(GtkDasherControl *pDasherControl, gchar *szCommand, gpointer pUserData);
153 extern "C" void handle_request_settings(GtkDasherControl * pDasherControl, gpointer data);
154 extern "C" void gtk2_edit_delete_callback(GtkDasherControl *pDasherControl, const gchar *szText, gpointer user_data);
155 extern "C" void gtk2_edit_output_callback(GtkDasherControl *pDasherControl, const gchar *szText, gpointer user_data);
156 extern "C" void convert_cb(GtkDasherControl *pDasherControl, gpointer pUserData);
157 extern "C" void protect_cb(GtkDasherControl *pDasherControl, gpointer pUserData);
159 static void
160 dasher_editor_class_init(DasherEditorClass *pClass) {
161 g_debug("Initialising DasherEditor");
163 GObjectClass *pObjectClass = (GObjectClass *) pClass;
164 pObjectClass->finalize = dasher_editor_destroy;
166 /* Setup signals */
167 dasher_editor_signals[FILENAME_CHANGED] = g_signal_new("filename-changed", G_TYPE_FROM_CLASS(pClass),
168 static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
169 G_STRUCT_OFFSET(DasherEditorClass, filename_changed),
170 NULL, NULL, g_cclosure_marshal_VOID__VOID,
171 G_TYPE_NONE, 0);
173 dasher_editor_signals[BUFFER_CHANGED] = g_signal_new("buffer-changed", G_TYPE_FROM_CLASS(pClass),
174 static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
175 G_STRUCT_OFFSET(DasherEditorClass, buffer_changed),
176 NULL, NULL, g_cclosure_marshal_VOID__VOID,
177 G_TYPE_NONE, 0);
179 dasher_editor_signals[CONTEXT_CHANGED] = g_signal_new("context-changed", G_TYPE_FROM_CLASS(pClass),
180 static_cast < GSignalFlags > (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
181 G_STRUCT_OFFSET(DasherEditorClass, context_changed),
182 NULL, NULL, g_cclosure_marshal_VOID__VOID,
183 G_TYPE_NONE, 0);
186 static void
187 dasher_editor_init(DasherEditor *pDasherControl) {
188 DasherEditorPrivate *pPrivate = new DasherEditorPrivate;
189 pDasherControl->private_data = pPrivate;
191 pPrivate->pBufferSet = NULL;
192 pPrivate->pInternalBuffer = NULL;
193 pPrivate->pExternalBuffer = NULL;
194 pPrivate->bFileModified = FALSE;
195 pPrivate->szFilename = NULL;
198 static void
199 dasher_editor_destroy(GObject *pObject) {
200 g_debug("Finalising DasherEditor");
202 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(((DasherEditor *)pObject)->private_data);
204 EditorAction *pCurrentAction = pPrivate->pActionRing;
206 if(pCurrentAction) {
207 bool bStarted = false;
209 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
210 bStarted = true;
211 dasher_action_deactivate(pCurrentAction->pAction);
212 g_object_unref(G_OBJECT(pCurrentAction->pAction));
213 pCurrentAction = pCurrentAction->pNext;
217 if(pPrivate->pBufferSet)
218 g_object_unref(G_OBJECT(pPrivate->pBufferSet));
220 delete (DasherEditorPrivate *)(((DasherEditor *)pObject)->private_data);
223 /* Public methods */
224 DasherEditor *
225 dasher_editor_new(DasherAppSettings *pAppSettings, DasherMain *pDasherMain, GladeXML *pGladeXML, const gchar *szFullPath) {
226 DasherEditor *pDasherEditor;
227 pDasherEditor = (DasherEditor *)(g_object_new(dasher_editor_get_type(), NULL));
229 g_pEditor = pDasherEditor;
231 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pDasherEditor->private_data);
232 pPrivate->pAppSettings = pAppSettings;
233 pPrivate->pDasherMain = pDasherMain;
235 GtkTextView *pTextView = GTK_TEXT_VIEW(glade_xml_get_widget(pGladeXML, "the_text_view"));
236 GtkVBox *pActionPane = GTK_VBOX(glade_xml_get_widget(pGladeXML, "vbox39"));
238 pPrivate->pTextView = pTextView;
239 pPrivate->pBuffer = gtk_text_view_get_buffer(pTextView);
241 GtkTextIter oStartIter;
242 gtk_text_buffer_get_start_iter(pPrivate->pBuffer, &oStartIter);
243 pPrivate->pNewMark = gtk_text_buffer_create_mark(pPrivate->pBuffer, NULL, &oStartIter, TRUE);
245 pPrivate->pActionPane = pActionPane;
246 pPrivate->pTextClipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
247 pPrivate->pPrimarySelection = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
248 pPrivate->pActionRing = NULL;
249 pPrivate->iNextActionID = 0;
250 pPrivate->pGameModeHelper = 0;
252 // TODO: is this still needed?
253 dasher_editor_create_buffer(pDasherEditor);
255 dasher_editor_setup_actions(pDasherEditor);
257 // TODO: see note in command_new method
258 if(szFullPath)
259 dasher_editor_open(pDasherEditor, szFullPath);
260 else {
261 dasher_editor_generate_filename(pDasherEditor);
262 dasher_editor_clear(pDasherEditor, false);
265 return pDasherEditor;
268 GType
269 dasher_editor_get_type() {
270 static GType dasher_editor_type = 0;
272 if(!dasher_editor_type) {
273 static const GTypeInfo dasher_editor_info = {
274 sizeof(DasherEditorClass),
275 NULL,
276 NULL,
277 (GClassInitFunc) dasher_editor_class_init,
278 NULL,
279 NULL,
280 sizeof(DasherEditor),
282 (GInstanceInitFunc) dasher_editor_init,
283 NULL
286 dasher_editor_type = g_type_register_static(G_TYPE_OBJECT, "DasherEditor", &dasher_editor_info, static_cast < GTypeFlags > (0));
289 return dasher_editor_type;
292 // IDasherBufferSet *
293 // dasher_editor_get_buffer_set(DasherEditor *pSelf) {
294 // DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
295 // return IDASHER_BUFFER_SET(dasher_internal_buffer_new(pPrivate->pTextView));
296 // }
298 static void
299 dasher_editor_clipboard(DasherEditor *pSelf, clipboard_action act) {
300 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
302 GtkTextIter *start = new GtkTextIter;
303 GtkTextIter *end = new GtkTextIter;
305 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), start, 0);
306 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), end, -1);
308 gchar *the_text = gtk_text_buffer_get_text(pPrivate->pBuffer, start, end, TRUE);
310 switch (act) {
311 case CLIPBOARD_CUT:
312 gtk_text_buffer_cut_clipboard(pPrivate->pBuffer, pPrivate->pTextClipboard, TRUE);
313 break;
314 case CLIPBOARD_COPY:
315 gtk_text_buffer_copy_clipboard(pPrivate->pBuffer, pPrivate->pTextClipboard);
316 break;
317 case CLIPBOARD_PASTE:
318 gtk_text_buffer_paste_clipboard(pPrivate->pBuffer, pPrivate->pTextClipboard, NULL, TRUE);
319 break;
320 case CLIPBOARD_COPYALL:
321 gtk_clipboard_set_text(pPrivate->pTextClipboard, the_text, strlen(the_text));
322 gtk_clipboard_set_text(pPrivate->pPrimarySelection, the_text, strlen(the_text));
324 break;
325 case CLIPBOARD_SELECTALL:
326 dasher_editor_select_all(pSelf);
327 break;
328 case CLIPBOARD_CLEAR:
329 gtk_text_buffer_set_text(pPrivate->pBuffer, "", 0);
330 break;
332 g_free(the_text);
334 delete start;
335 delete end;
338 void
339 dasher_editor_handle_stop(DasherEditor *pSelf) {
340 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
342 // See if anything is set to auto:
343 EditorAction *pCurrentAction = pPrivate->pActionRing;
345 if(pCurrentAction) {
346 bool bStarted = false;
348 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
349 bStarted = true;
350 if(pCurrentAction->bAuto)
351 dasher_action_execute(pCurrentAction->pAction, pSelf, -1);
352 pCurrentAction = pCurrentAction->pNext;
357 void
358 dasher_editor_handle_start(DasherEditor *pSelf) {
359 // The edit box keeps track of where we started
361 // TODO: This should be filtered through the buffer, rather than directly to the edit box
362 // set_mark();
365 /* TODO: This is obsolete - sort this out when commands are reconsidered */
366 void
367 dasher_editor_handle_control(DasherEditor *pSelf, int iNodeID) {
368 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
370 if(iNodeID == Dasher::CControlManager::CTL_USER + 1)
371 dasher_editor_clear(pSelf, false); // Clear node is a special case (it shouldn't be)
372 else {
373 EditorAction *pCurrentAction = pPrivate->pActionRing;
374 bool bStarted = false;
376 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
377 bStarted = true;
378 if((iNodeID >= pCurrentAction->iControlID) && (iNodeID <= pCurrentAction->iControlID + pCurrentAction->iNSub)) {
379 dasher_action_execute(pCurrentAction->pAction, pSelf, iNodeID - pCurrentAction->iControlID - 1);
380 // dasher_editor_clear(pSelf, true);
382 pCurrentAction = pCurrentAction->pNext;
387 // TODO: Think about changing signals so we don't need to do this translation
389 struct SControlMap {
390 int iEvent;
391 int iDir;
392 int iDist;
393 bool bDelete;
396 static struct SControlMap sMap[] = {
397 {Dasher::CControlManager::CTL_MOVE_FORWARD_CHAR, EDIT_FORWARDS, EDIT_CHAR, false},
398 {Dasher::CControlManager::CTL_MOVE_FORWARD_WORD, EDIT_FORWARDS, EDIT_WORD, false},
399 {Dasher::CControlManager::CTL_MOVE_FORWARD_LINE, EDIT_FORWARDS, EDIT_LINE, false},
400 {Dasher::CControlManager::CTL_MOVE_FORWARD_FILE, EDIT_FORWARDS, EDIT_FILE, false},
401 {Dasher::CControlManager::CTL_MOVE_BACKWARD_CHAR, EDIT_BACKWARDS, EDIT_CHAR, false},
402 {Dasher::CControlManager::CTL_MOVE_BACKWARD_WORD, EDIT_BACKWARDS, EDIT_WORD, false},
403 {Dasher::CControlManager::CTL_MOVE_BACKWARD_LINE, EDIT_BACKWARDS, EDIT_LINE, false},
404 {Dasher::CControlManager::CTL_MOVE_BACKWARD_FILE, EDIT_BACKWARDS, EDIT_FILE, false},
405 {Dasher::CControlManager::CTL_DELETE_FORWARD_CHAR, EDIT_FORWARDS, EDIT_CHAR, true},
406 {Dasher::CControlManager::CTL_DELETE_FORWARD_WORD, EDIT_FORWARDS, EDIT_WORD, true},
407 {Dasher::CControlManager::CTL_DELETE_FORWARD_LINE, EDIT_FORWARDS, EDIT_LINE, true},
408 {Dasher::CControlManager::CTL_DELETE_FORWARD_FILE, EDIT_FORWARDS, EDIT_FILE, true},
409 {Dasher::CControlManager::CTL_DELETE_BACKWARD_CHAR, EDIT_BACKWARDS, EDIT_CHAR, true},
410 {Dasher::CControlManager::CTL_DELETE_BACKWARD_WORD, EDIT_BACKWARDS, EDIT_WORD, true},
411 {Dasher::CControlManager::CTL_DELETE_BACKWARD_LINE, EDIT_BACKWARDS, EDIT_LINE, true},
412 {Dasher::CControlManager::CTL_DELETE_BACKWARD_FILE, EDIT_BACKWARDS, EDIT_FILE, true}
415 if(pPrivate->pBufferSet) {
416 for(unsigned int i(0); i < sizeof(sMap)/sizeof(struct SControlMap); ++i) {
417 if(sMap[i].iEvent == iNodeID) {
418 if(sMap[i].bDelete)
419 idasher_buffer_set_edit_delete(pPrivate->pBufferSet, sMap[i].iDir, sMap[i].iDist);
420 else
421 idasher_buffer_set_edit_move(pPrivate->pBufferSet, sMap[i].iDir, sMap[i].iDist);
428 void
429 dasher_editor_action_button(DasherEditor *pSelf, DasherAction *pAction) {
430 if(pAction) {
431 dasher_action_execute(pAction, pSelf, -1);
432 dasher_editor_clear(pSelf, true);
434 else { // Clear button
435 dasher_editor_clear(pSelf, false);
439 static void
440 dasher_editor_clear(DasherEditor *pSelf, gboolean bStore) {
441 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
443 if(IS_DASHER_INTERNAL_BUFFER(pPrivate->pBufferSet))
444 dasher_internal_buffer_clear(DASHER_INTERNAL_BUFFER(pPrivate->pBufferSet));
448 void
449 dasher_editor_actions_start(DasherEditor *pSelf) {
450 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
452 pPrivate->bActionIterStarted = false;
453 pPrivate->pActionIter = pPrivate->pActionRing;
456 bool
457 dasher_editor_actions_more(DasherEditor *pSelf) {
458 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
460 return(!pPrivate->bActionIterStarted || (pPrivate->pActionIter != pPrivate->pActionRing));
463 void
464 dasher_editor_actions_get_next(DasherEditor *pSelf, const gchar **szName, gint *iID, gboolean *bShow, gboolean *bControl, gboolean *bAuto) {
465 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
467 *szName = dasher_action_get_name(pPrivate->pActionIter->pAction);
468 *iID = pPrivate->pActionIter->iID;
469 *bShow = pPrivate->pActionIter->bShow;
470 *bControl = pPrivate->pActionIter->bControl;
471 *bAuto = pPrivate->pActionIter->bAuto;
473 pPrivate->pActionIter = pPrivate->pActionIter->pNext;
474 pPrivate->bActionIterStarted = true;
477 void
478 dasher_editor_action_set_show(DasherEditor *pSelf, int iActionID, bool bValue) {
479 EditorAction *pAction;
480 pAction = dasher_editor_get_action_by_id(pSelf, iActionID);
482 if(pAction) {
483 pAction->bShow = bValue;
484 dasher_editor_check_activity(pSelf, pAction);
485 dasher_editor_rebuild_action_pane(pSelf);
487 dasher_editor_action_save_state(pSelf, pAction);
491 void
492 dasher_editor_action_set_control(DasherEditor *pSelf, int iActionID, bool bValue) {
493 // TODO: Need to actually change behaviour in resonse to these calls
495 // TODO: Reimplement
497 // EditorAction *pAction;
498 // pAction = dasher_editor_get_action_by_id(pSelf, iActionID);
500 // if(pAction) {
501 // pAction->bControl = bValue;
502 // dasher_editor_check_activity(pSelf, pAction);
503 // if(bValue)
504 // gtk_dasher_control_connect_node(GTK_DASHER_CONTROL(pDasherWidget), pAction->iControlID, Dasher::CControlManager::CTL_USER, -2);
505 // else
506 // gtk_dasher_control_disconnect_node(GTK_DASHER_CONTROL(pDasherWidget), pAction->iControlID, Dasher::CControlManager::CTL_USER);
508 // dasher_editor_action_save_state(pSelf, pAction);
509 // }
512 void
513 dasher_editor_action_set_auto(DasherEditor *pSelf, int iActionID, bool bValue) {
514 EditorAction *pAction;
515 pAction = dasher_editor_get_action_by_id(pSelf, iActionID);
517 if(pAction) {
518 pAction->bAuto = bValue;
519 dasher_editor_check_activity(pSelf, pAction);
521 dasher_editor_action_save_state(pSelf, pAction);
525 static void
526 dasher_editor_create_buffer(DasherEditor *pSelf) {
527 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
529 /* Make an external buffer anyway, for keyboard command */
530 /* TODO: Review this */
531 if(!(pPrivate->pExternalBuffer))
532 pPrivate->pExternalBuffer = IDASHER_BUFFER_SET(dasher_external_buffer_new());
534 if(dasher_app_settings_get_long(pPrivate->pAppSettings, APP_LP_STYLE) == 2) {
535 pPrivate->pBufferSet = pPrivate->pExternalBuffer;
537 else {
538 if(!(pPrivate->pInternalBuffer))
539 pPrivate->pInternalBuffer = IDASHER_BUFFER_SET(dasher_internal_buffer_new(pPrivate->pTextView));
541 pPrivate->pBufferSet = pPrivate->pInternalBuffer;
544 // TODO: Fix this
545 g_signal_connect(G_OBJECT(pPrivate->pBufferSet), "context_changed", G_CALLBACK(context_changed_handler), pSelf);
548 void
549 dasher_editor_output(DasherEditor *pSelf, const gchar *szText) {
550 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
552 if(pPrivate->pBufferSet)
553 idasher_buffer_set_insert(pPrivate->pBufferSet, szText);
555 if(pPrivate->pGameModeHelper)
556 game_mode_helper_output(pPrivate->pGameModeHelper, szText);
558 pPrivate->bFileModified = TRUE;
561 void
562 dasher_editor_delete(DasherEditor *pSelf, int iLength) {
563 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
565 if(pPrivate->pBufferSet)
566 idasher_buffer_set_delete(pPrivate->pBufferSet, iLength);
568 if(pPrivate->pGameModeHelper)
569 game_mode_helper_delete(pPrivate->pGameModeHelper, iLength);
571 pPrivate->bFileModified = TRUE;
574 const gchar *
575 dasher_editor_get_context(DasherEditor *pSelf, int iOffset, int iLength) {
576 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
578 gchar *szContext;
580 if(pPrivate->pBufferSet)
581 szContext = idasher_buffer_set_get_context(pPrivate->pBufferSet, iOffset, iLength);
582 else
583 szContext = "";
585 // TODO: reimplement
586 // if(szContext && (strlen(szContext) > 0))
587 // gtk_dasher_control_set_context( GTK_DASHER_CONTROL(pDasherWidget), szContext );
590 gint
591 dasher_editor_get_offset(DasherEditor *pSelf) {
592 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
593 return idasher_buffer_set_get_offset(pPrivate->pBufferSet);
596 static void
597 dasher_editor_generate_filename(DasherEditor *pSelf) {
598 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
600 gchar *szNewFilename = NULL;
602 if( dasher_app_settings_get_bool(pPrivate->pAppSettings, APP_BP_TIME_STAMP )) {
603 // Build a filename based on the current time and date
604 tm *t_struct;
605 time_t ctime;
606 char cwd[1000];
607 char tbuffer[200];
609 ctime = time(NULL);
611 t_struct = localtime(&ctime);
613 getcwd(cwd, 1000);
614 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);
616 szNewFilename = g_build_path("/", cwd, tbuffer, NULL);
619 dasher_editor_set_filename(pSelf, szNewFilename);
621 g_free(szNewFilename);
624 static void
625 dasher_editor_open(DasherEditor *pSelf, const gchar *szFilename) {
626 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
628 unsigned long long size;
629 gchar *buffer;
631 #ifdef GNOME_LIBS
632 if(!dasher_editor_gnome_vfs_open_file(pSelf, szFilename, &buffer, &size)) {
633 return;
635 #else
636 if(!dasher_editor_unix_vfs_open_file(pSelf, szFilename, &buffer, &size)) {
637 return;
639 #endif
641 // FIXME - REIMPLEMENT (shouldn't happen through core)
642 // dasher_clear();
644 if(size != 0) {
645 // Don't attempt to insert new text if the file is empty as it makes
646 // GTK cry
647 if(!g_utf8_validate(buffer, size, NULL)) {
648 // It's not UTF8, so we do the best we can...
650 // If there are zero bytes in the file then we have a problem -
651 // for now, just assert that we can't load these files.
652 for(unsigned int i(0); i < size; ++i)
653 if(buffer[i] == 0) {
654 // GtkWidget *pErrorBox = gtk_message_dialog_new(GTK_WINDOW(window),
655 // GTK_DIALOG_MODAL,
656 // GTK_MESSAGE_ERROR,
657 // GTK_BUTTONS_OK,
658 // "Could not open the file \"%s\". Please note that Dasher cannot load files containing binary data, which may be the cause of this error.\n",
659 // myfilename);
660 GtkWidget *pErrorBox = gtk_message_dialog_new(NULL,
661 GTK_DIALOG_MODAL,
662 GTK_MESSAGE_ERROR,
663 GTK_BUTTONS_OK,
664 "Could not open the file \"%s\". Please note that Dasher cannot load files containing binary data, which may be the cause of this error.\n",
665 szFilename);
666 gtk_dialog_run(GTK_DIALOG(pErrorBox));
667 gtk_widget_destroy(pErrorBox);
668 return;
671 pPrivate->bFileModified = TRUE;
673 gsize iNewSize;
674 gchar *buffer2 = g_strdup(g_locale_to_utf8(buffer, size, NULL, &iNewSize, NULL));
676 // TODO: This function probably needs more thought
678 // const gchar *pEnd;
679 //gboolean bValid = g_utf8_validate(buffer2, -1, &pEnd);
681 g_free(buffer);
682 buffer = buffer2;
683 size = iNewSize;
685 gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(pPrivate->pBuffer), buffer, size);
686 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(pPrivate->pTextView), gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(pPrivate->pBuffer)));
689 dasher_editor_set_filename(pSelf, szFilename);
692 static bool
693 dasher_editor_save_as(DasherEditor *pSelf, const gchar *szFilename, bool bAppend) {
694 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
696 unsigned long long length;
697 gchar *inbuffer, *outbuffer = NULL;
698 // gsize bytes_read, bytes_written;
699 gsize bytes_written;
700 // GError *error = NULL;
701 GtkTextIter *start, *end;
702 // GIConv cd;
704 start = new GtkTextIter;
705 end = new GtkTextIter;
707 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), start, 0);
708 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), end, -1);
710 inbuffer = gtk_text_iter_get_slice(start, end);
712 // g_message("String %s", inbuffer);
714 //length = gtk_text_iter_get_offset(end) - gtk_text_iter_get_offset(start);
715 //length = gtk_text_buffer_get_byte_count(GTK_TEXT_BUFFER(the_text_buffer));
717 // I'm pretty certain that this is null terminated, but not 100%
718 length = strlen(inbuffer);
720 // g_message("Length is %d", length);
722 outbuffer = (char *)malloc((length + 1) * sizeof(gchar));
723 memcpy((void *)outbuffer, (void *)inbuffer, length * sizeof(gchar));
724 outbuffer[length] = 0;
725 g_free(inbuffer);
726 inbuffer = outbuffer;
727 outbuffer = NULL;
729 // switch (fileencoding) {
730 // case Dasher::Opts::UserDefault:
731 // case Dasher::Opts::AlphabetDefault:
732 // //FIXME - need to call GetAlphabetType and do appropriate stuff regarding
733 // //the character set. Arguably we should always be saving in either UTF-8 or
734 // //the user's locale (which may, of course, be UTF-8) because otherwise
735 // //we're going to read in rubbish, and we shouldn't be encouraging weird
736 // //codepage madness any further
738 // //FIXME - error handling
739 // outbuffer = g_locale_from_utf8(inbuffer, -1, &bytes_read, &bytes_written, &error);
740 // if(outbuffer == NULL) {
741 // // We can't represent the text in the current locale, so fall back to
742 // // UTF-8
743 // outbuffer = inbuffer;
744 // bytes_written = length;
745 // }
746 // case Dasher::Opts::UTF8:
747 // outbuffer = inbuffer;
748 // bytes_written = length;
749 // break;
750 // // Does /anyone/ want to save text files in UTF16?
751 // // (in any case, my opinions regarding encouragement of data formats with
752 // // endianness damage are almost certainly unprintable)
754 // case Dasher::Opts::UTF16LE:
755 // cd = g_iconv_open("UTF16LE", "UTF8");
756 // outbuffer = g_convert_with_iconv(inbuffer, -1, cd, &bytes_read, &bytes_written, &error);
757 // break;
758 // case Dasher::Opts::UTF16BE:
759 // cd = g_iconv_open("UTF16BE", "UTF8");
760 // outbuffer = g_convert_with_iconv(inbuffer, -1, cd, &bytes_read, &bytes_written, &error);
761 // break;
762 // default:
763 outbuffer = inbuffer;
764 bytes_written = length;
765 // }
767 #ifdef GNOME_LIBS
768 if(!dasher_editor_gnome_vfs_save_file(pSelf, szFilename, outbuffer, bytes_written, bAppend)) {
769 return false;
771 #else
772 if(!dasher_editor_unix_vfs_save_file(pSelf, szFilename, outbuffer, bytes_written, bAppend)) {
773 return false;
775 #endif
777 pPrivate->bFileModified = FALSE;
778 // TODO: reimplement
779 // gtk_window_set_title(GTK_WINDOW(window), myfilename);
781 dasher_editor_set_filename(pSelf, szFilename);
783 // if(filename != myfilename) {
784 // g_free((void *)filename);
785 // filename = g_strdup(myfilename);
786 // }
788 return true;
791 // void
792 // dasher_editor_start_tutorial(DasherEditor *pSelf) {
793 // DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
795 // // TODO: reimplement
796 // // pPrivate->pGameModeHelper = GAME_MODE_HELPER(game_mode_helper_new(GTK_DASHER_CONTROL(pDasherWidget)));
797 // }
799 gboolean
800 dasher_editor_command(DasherEditor *pSelf, const gchar *szCommand) {
801 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
803 if(!strcmp(szCommand, "new")) { //select_new_file
804 dasher_editor_command_new(pSelf);
805 return TRUE;
808 if(!strcmp(szCommand, "open")) { //select open file
809 dasher_editor_command_open(pSelf);
810 return TRUE;
813 if(!strcmp(szCommand, "save")) { //save_file
814 dasher_editor_command_save(pSelf, FALSE, FALSE);
815 return TRUE;
818 if(!strcmp(szCommand, "saveas")) { // select_save_file_as
819 dasher_editor_command_save(pSelf, TRUE, FALSE);
820 return TRUE;
823 if(!strcmp(szCommand, "append")) { // select_append_file
824 dasher_editor_command_save(pSelf, TRUE, TRUE);
825 return TRUE;
828 if(!strcmp(szCommand, "cut")) { // clipboard_cut
829 dasher_editor_clipboard(pSelf, CLIPBOARD_CUT);
830 return TRUE;
833 if(!strcmp(szCommand, "copy")) { // clipboard_copy
834 dasher_editor_clipboard(pSelf, CLIPBOARD_COPY);
835 return TRUE;
838 if(!strcmp(szCommand, "copyall")) { // clipboard_copyall
839 dasher_editor_clipboard(pSelf, CLIPBOARD_COPYALL);
840 return TRUE;
843 if(!strcmp(szCommand, "paste")) { // clipboard_paste
844 dasher_editor_clipboard(pSelf, CLIPBOARD_PASTE);
845 return TRUE;
848 // TODO: This isn't actually accessible from anywhere
849 if(!strcmp(szCommand, "selectall")) { // clipboard_paste
850 dasher_editor_clipboard(pSelf, CLIPBOARD_SELECTALL);
851 return TRUE;
855 /* TODO: We need a rethink here */
856 const gchar *szForwardCommand = NULL;
857 gint iSubCommand = 0;
859 if(!strcmp(szCommand, "speakall")) {
860 szForwardCommand = "Speak";
861 iSubCommand = 0;
863 else if(!strcmp(szCommand, "speaklast")) {
864 szForwardCommand = "Speak";
865 iSubCommand = 1;
867 else if(!strcmp(szCommand, "speakrepeat")) {
868 szForwardCommand = "Speak";
869 iSubCommand = 2;
872 if(szForwardCommand) {
873 gboolean bActionIterStarted = false;
874 EditorAction *pActionIter = pPrivate->pActionRing;
876 while((pActionIter != pPrivate->pActionRing) || !bActionIterStarted) {
877 bActionIterStarted = true;
879 if(!strcmp(dasher_action_get_name(pActionIter->pAction), szForwardCommand)) {
880 dasher_action_execute(pActionIter->pAction, pSelf, iSubCommand);
881 return TRUE;
884 pActionIter = pActionIter->pNext;
886 return TRUE;
889 return FALSE;
892 gboolean
893 dasher_editor_file_changed(DasherEditor *pSelf) {
894 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
896 return pPrivate->bFileModified;
899 const gchar *
900 dasher_editor_get_filename(DasherEditor *pSelf) {
901 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
903 return pPrivate->szFilename;
906 // TODO: We shouldn't need to know about the buffer here - make this a method of the buffer set
907 const gchar *
908 dasher_editor_get_all_text(DasherEditor *pSelf) {
909 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
911 GtkTextIter oStart;
912 GtkTextIter oEnd;
914 gtk_text_buffer_get_start_iter(pPrivate->pBuffer, &oStart);
915 gtk_text_buffer_get_end_iter(pPrivate->pBuffer, &oEnd);
917 pPrivate->pNewMark = gtk_text_buffer_create_mark(pPrivate->pBuffer, NULL, &oEnd, TRUE);
919 return gtk_text_buffer_get_text(pPrivate->pBuffer, &oStart, &oEnd, false );
922 const gchar *
923 dasher_editor_get_new_text(DasherEditor *pSelf) {
924 // TODO: Implement this properly
925 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
927 GtkTextIter oStart;
928 GtkTextIter oEnd;
930 gtk_text_buffer_get_end_iter(pPrivate->pBuffer, &oEnd);
931 gtk_text_buffer_get_iter_at_mark(pPrivate->pBuffer, &oStart, pPrivate->pNewMark);
933 const gchar *szRetVal = gtk_text_buffer_get_text(pPrivate->pBuffer, &oStart, &oEnd, false );
935 pPrivate->pNewMark = gtk_text_buffer_create_mark(pPrivate->pBuffer, NULL, &oEnd, TRUE);
937 return szRetVal;
941 /* Private methods */
942 static void
943 dasher_editor_select_all(DasherEditor *pSelf) {
944 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
945 GtkTextIter *start, *end;
947 start = new GtkTextIter;
948 end = new GtkTextIter;
950 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), start, 0);
951 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate->pBuffer), end, -1);
953 GtkTextMark *selection = gtk_text_buffer_get_mark(pPrivate->pBuffer, "selection_bound");
954 GtkTextMark *cursor = gtk_text_buffer_get_mark(pPrivate->pBuffer, "insert");
956 gtk_text_buffer_move_mark(pPrivate->pBuffer, selection, start);
957 gtk_text_buffer_move_mark(pPrivate->pBuffer, cursor, end);
959 delete start;
960 delete end;
963 static void
964 dasher_editor_setup_actions(DasherEditor *pSelf) {
965 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
967 // TODO: Activate and deactivate methods for actions
968 // TODO: Clear shouldn't be a special case (include support for false in clear method)
970 #ifdef GNOME_SPEECH
971 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_speech_new()));
972 #endif
974 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_keyboard_new(pPrivate->pExternalBuffer)));
976 #ifdef WITH_MAEMO
977 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_keyboard_maemo_new()));
978 #else
979 // dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_copy_new(pSelf)));
981 GDir *pDirectory;
982 G_CONST_RETURN gchar *szFilename;
984 gchar *szUserScriptDir = new gchar[strlen(dasher_app_settings_get_string(pPrivate->pAppSettings, SP_USER_LOC))+9];
985 strcpy(szUserScriptDir, dasher_app_settings_get_string(pPrivate->pAppSettings, SP_USER_LOC));
986 strcat(szUserScriptDir, "scripts/");
988 pDirectory = g_dir_open(szUserScriptDir, 0, NULL);
990 if(pDirectory) {
991 while((szFilename = g_dir_read_name(pDirectory))) {
992 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_script_new(szUserScriptDir, szFilename)));
995 g_dir_close(pDirectory);
998 delete[] szUserScriptDir;
1000 gchar *szSystemScriptDir = new gchar[strlen(dasher_app_settings_get_string(pPrivate->pAppSettings, SP_SYSTEM_LOC))+9];
1001 strcpy(szSystemScriptDir, dasher_app_settings_get_string(pPrivate->pAppSettings, SP_SYSTEM_LOC));
1002 strcat(szSystemScriptDir, "scripts/");
1004 pDirectory = g_dir_open(szSystemScriptDir, 0, NULL);
1006 if(pDirectory) {
1007 while((szFilename = g_dir_read_name(pDirectory))) {
1008 dasher_editor_add_action(pSelf, DASHER_ACTION(dasher_action_script_new(szSystemScriptDir, szFilename)));
1011 g_dir_close(pDirectory);
1014 delete[] szSystemScriptDir;
1015 #endif
1017 // TODO: Reimplement
1019 // // TODO: This doesn't get re-called if the preferences change
1021 // gtk_dasher_control_register_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER, "Actions", -1 );
1022 // gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER, Dasher::CControlManager::CTL_ROOT, -2);
1023 // int iControlOffset(1);
1025 // gtk_dasher_control_register_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, "Clear", -1 );
1026 // gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, Dasher::CControlManager::CTL_USER, -2);
1027 // gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), -1, Dasher::CControlManager::CTL_USER + iControlOffset, -2);
1028 // ++iControlOffset;
1030 // EditorAction *pCurrentAction = pPrivate->pActionRing;
1031 // bool bStarted = false;
1033 // while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
1034 // bStarted = true;
1036 // if(pCurrentAction->bControl) {
1037 // gtk_dasher_control_register_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, dasher_action_get_name(pCurrentAction->pAction), -1 );
1038 // gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset, Dasher::CControlManager::CTL_USER, -2);
1040 // int iNSub(dasher_action_get_sub_count(pCurrentAction->pAction));
1042 // if(iNSub == 0) {
1043 // gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), -1, Dasher::CControlManager::CTL_USER + iControlOffset, -2);
1044 // }
1045 // else {
1046 // for(int i(0); i < iNSub; ++i) {
1047 // gtk_dasher_control_register_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset + i + 1, dasher_action_get_sub_name(pCurrentAction->pAction, i), -1 );
1048 // gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), Dasher::CControlManager::CTL_USER + iControlOffset + i + 1, Dasher::CControlManager::CTL_USER + iControlOffset, -2);
1049 // gtk_dasher_control_connect_node( GTK_DASHER_CONTROL(pDasherWidget), -1, Dasher::CControlManager::CTL_USER + iControlOffset + i + 1, -2);
1050 // }
1051 // }
1053 // pCurrentAction->iControlID = Dasher::CControlManager::CTL_USER + iControlOffset;
1054 // pCurrentAction->iNSub = iNSub;
1055 // iControlOffset += iNSub + 1;
1056 // }
1058 // pCurrentAction = pCurrentAction->pNext;
1059 // }
1061 #ifndef WITH_MAEMOFULLSCREEN
1062 // dasher_editor_rebuild_action_pane(pSelf);
1063 #endif
1066 static void
1067 dasher_editor_add_action(DasherEditor *pSelf, DasherAction *pNewAction) {
1068 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1070 EditorAction *pNewEditorAction = new EditorAction;
1071 pNewEditorAction->pAction = pNewAction;
1072 pNewEditorAction->iID = pPrivate->iNextActionID;
1073 ++pPrivate->iNextActionID;
1075 gchar szRegistryName[256];
1076 strncpy(szRegistryName, "Action_", 256);
1077 strncat(szRegistryName, dasher_action_get_name(pNewEditorAction->pAction), 255 - strlen(szRegistryName));
1079 for(unsigned int i(0); i < strlen(szRegistryName); ++i)
1080 if(szRegistryName[i] == ' ')
1081 szRegistryName[i] = '_';
1083 gint iState;
1085 if(!dasher_app_settings_get_free_long(pPrivate->pAppSettings, szRegistryName, iState)) {
1086 if(!strcmp(dasher_action_get_name(pNewEditorAction->pAction), "Speak"))
1087 iState = 0;
1088 else
1089 iState = ACTION_STATE_SHOW | ACTION_STATE_CONTROL;
1091 dasher_app_settings_set_free_long(pPrivate->pAppSettings, szRegistryName, iState);
1094 pNewEditorAction->bShow = iState & ACTION_STATE_SHOW;
1095 pNewEditorAction->bControl = iState & ACTION_STATE_CONTROL;
1096 pNewEditorAction->bAuto = iState & ACTION_STATE_AUTO;
1098 dasher_editor_check_activity(pSelf, pNewEditorAction);
1100 if(pPrivate->pActionRing) {
1101 pNewEditorAction->pNext = pPrivate->pActionRing;
1102 pNewEditorAction->pPrevious = pPrivate->pActionRing->pPrevious;
1103 pPrivate->pActionRing->pPrevious->pNext = pNewEditorAction;
1104 pPrivate->pActionRing->pPrevious = pNewEditorAction;
1106 else {
1107 pNewEditorAction->pNext = pNewEditorAction;
1108 pNewEditorAction->pPrevious = pNewEditorAction;
1111 pPrivate->pActionRing = pNewEditorAction;
1113 // TODO: Reimplement
1114 // if(iState & ACTION_STATE_SHOW)
1115 // gtk_dasher_control_add_action_button(GTK_DASHER_CONTROL(pDasherWidget), dasher_action_get_name(pNewEditorAction->pAction));
1118 static EditorAction *
1119 dasher_editor_get_action_by_id(DasherEditor *pSelf, int iID){
1120 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1122 EditorAction *pCurrentAction = pPrivate->pActionRing;
1123 bool bStarted = false;
1125 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
1126 bStarted = true;
1127 if(pCurrentAction->iID == iID)
1128 return pCurrentAction;
1129 pCurrentAction = pCurrentAction->pNext;
1132 return 0;
1135 static void
1136 dasher_editor_rebuild_action_pane(DasherEditor *pSelf) {
1137 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1139 // Delete any existing widgets
1140 gtk_container_foreach(GTK_CONTAINER(pPrivate->pActionPane), delete_children_callback, 0);
1142 // Add the cancel button
1143 GtkButton *pNewButton = GTK_BUTTON(gtk_button_new_with_label("Clear"));
1144 gtk_widget_show(GTK_WIDGET(pNewButton));
1146 void **pUserData = new void *[2];
1147 pUserData[0] = (void *)pSelf;
1148 pUserData[1] = 0;
1150 g_signal_connect(G_OBJECT(pNewButton), "clicked", G_CALLBACK(action_button_callback), pUserData);
1151 #ifdef WITH_MAEMO
1152 // For Maemo we want the packing to expand
1153 gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), true, true, 0);
1154 #else
1155 gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), false, false, 0);
1156 #endif
1159 EditorAction *pCurrentAction = pPrivate->pActionRing;
1160 bool bStarted = false;
1162 while(!bStarted || (pCurrentAction != pPrivate->pActionRing)) {
1163 bStarted = true;
1164 if(pCurrentAction->bShow) {
1165 GtkButton *pNewButton = GTK_BUTTON(gtk_button_new_with_label(dasher_action_get_name(pCurrentAction->pAction)));
1166 gtk_widget_show(GTK_WIDGET(pNewButton));
1168 pUserData = new void *[2];
1169 pUserData[0] = (void *)pSelf;
1170 pUserData[1] = (void *)(pCurrentAction->pAction);
1172 g_signal_connect(G_OBJECT(pNewButton), "clicked", G_CALLBACK(action_button_callback), pUserData);
1173 #ifdef WITH_MAEMO
1174 // For Maemo we want the packing to expand
1175 gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), true, true, 0);
1176 #else
1177 gtk_box_pack_start(GTK_BOX(pPrivate->pActionPane), GTK_WIDGET(pNewButton), false, false, 0);
1178 #endif
1180 pCurrentAction = pCurrentAction->pNext;
1184 // TODO: This shouldn't be a part of the editor
1185 static void
1186 dasher_editor_display_message(DasherEditor *pSelf, DasherMessageInfo *pMessageInfo) {
1187 GtkMessageDialog *pDialog = GTK_MESSAGE_DIALOG(gtk_message_dialog_new(0, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, pMessageInfo->szMessage));
1188 gtk_dialog_run(GTK_DIALOG(pDialog));
1189 gtk_widget_destroy(GTK_WIDGET(pDialog));
1192 static void
1193 dasher_editor_check_activity(DasherEditor *pSelf, EditorAction *pAction) {
1194 gboolean bNeedActive(pAction->bShow || pAction->bControl || pAction->bAuto);
1195 gboolean bActive(dasher_action_get_active(pAction->pAction));
1197 if(bNeedActive && !bActive)
1198 dasher_action_activate(pAction->pAction);
1199 else if(!bNeedActive && bActive)
1200 dasher_action_deactivate(pAction->pAction);
1203 static void
1204 dasher_editor_action_save_state(DasherEditor *pSelf, EditorAction *pAction) {
1205 gchar szRegistryName[256];
1206 strncpy(szRegistryName, "Action_", 256);
1207 strncat(szRegistryName, dasher_action_get_name(pAction->pAction), 255 - strlen(szRegistryName));
1209 for(unsigned int i(0); i < strlen(szRegistryName); ++i)
1210 if(szRegistryName[i] == ' ')
1211 szRegistryName[i] = '_';
1213 gint iState = 0;
1215 if(pAction->bShow)
1216 iState += ACTION_STATE_SHOW;
1218 if(pAction->bControl)
1219 iState += ACTION_STATE_CONTROL;
1221 if(pAction->bAuto)
1222 iState += ACTION_STATE_AUTO;
1224 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1225 dasher_app_settings_set_free_long(pPrivate->pAppSettings, szRegistryName, iState);
1228 static void
1229 dasher_editor_command_new(DasherEditor *pSelf) {
1230 dasher_editor_new_buffer(pSelf, NULL);
1233 static void
1234 dasher_editor_command_open(DasherEditor *pSelf) {
1235 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1237 // GtkWidget *filesel = gtk_file_chooser_dialog_new(_("Select File"), GTK_WINDOW(pPrivate->pMainWindow), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
1238 GtkWidget *filesel = gtk_file_chooser_dialog_new(_("Select File"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
1240 #ifdef GNOME_LIBS
1241 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(filesel), FALSE);
1242 #endif
1244 if(gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) {
1245 #ifdef GNOME_LIBS
1246 char *filename = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(filesel));
1247 #else
1248 char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
1249 #endif
1250 dasher_editor_new_buffer(pSelf, filename);
1251 g_free(filename);
1253 gtk_widget_destroy(filesel);
1256 static void
1257 dasher_editor_command_save(DasherEditor *pSelf, gboolean bPrompt, gboolean bAppend) {
1258 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1259 gchar *szFilename = NULL;
1261 if(bPrompt || !szFilename) {
1262 // GtkWidget *filesel = gtk_file_chooser_dialog_new(_("Select File"), GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
1263 GtkWidget *filesel = gtk_file_chooser_dialog_new(_("Select File"), NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
1265 #ifdef GNOME_LIBS
1266 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(filesel), FALSE);
1267 #endif
1269 if(gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) {
1270 #ifdef GNOME_LIBS
1271 szFilename = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(filesel));
1272 #else
1273 szFilename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel));
1274 #endif
1276 else {
1277 return;
1280 gtk_widget_destroy(filesel);
1283 dasher_editor_save_as(pSelf, szFilename, bAppend);
1284 g_free(szFilename);
1287 static gboolean
1288 dasher_editor_unix_vfs_open_file(DasherEditor *pSelf, const char *myfilename, gchar **buffer, unsigned long long *size) {
1289 GtkWidget *error_dialog;
1291 struct stat file_stat;
1292 FILE *fp;
1294 stat(myfilename, &file_stat);
1295 fp = fopen(myfilename, "r");
1297 if(fp == NULL || S_ISDIR(file_stat.st_mode)) {
1298 // error_dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not open the file \"%s\".\n", myfilename);
1299 error_dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not open the file \"%s\".\n", myfilename);
1300 gtk_dialog_set_default_response(GTK_DIALOG(error_dialog), GTK_RESPONSE_OK);
1301 gtk_window_set_resizable(GTK_WINDOW(error_dialog), FALSE);
1302 gtk_dialog_run(GTK_DIALOG(error_dialog));
1303 gtk_widget_destroy(error_dialog);
1304 return FALSE;
1307 *size = file_stat.st_size;
1308 *buffer = (gchar *) g_malloc(*size);
1309 fread(*buffer, *size, 1, fp);
1310 fclose(fp);
1311 return TRUE;
1314 static gboolean
1315 dasher_editor_unix_vfs_save_file(DasherEditor *pSelf, const char *myfilename, gchar *buffer, unsigned long long length, bool append) {
1316 int opened = 1;
1317 GtkWidget *error_dialog;
1319 FILE *fp;
1321 if(append == true) {
1322 fp = fopen(myfilename, "a");
1324 if(fp == NULL) {
1325 opened = 0;
1328 else {
1329 fp = fopen(myfilename, "w");
1330 if(fp == NULL) {
1331 opened = 0;
1335 if(!opened) {
1336 // error_dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not save the file \"%s\".\n", myfilename);
1337 error_dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not save the file \"%s\".\n", myfilename);
1338 gtk_dialog_set_default_response(GTK_DIALOG(error_dialog), GTK_RESPONSE_OK);
1339 gtk_window_set_resizable(GTK_WINDOW(error_dialog), FALSE);
1340 gtk_dialog_run(GTK_DIALOG(error_dialog));
1341 gtk_widget_destroy(error_dialog);
1342 return false;
1345 fwrite(buffer, 1, length, fp);
1346 fclose(fp);
1347 return true;
1351 #ifdef GNOME_LIBS
1352 static void
1353 dasher_editor_vfs_print_error(DasherEditor *pSelf, GnomeVFSResult *result, const char *myfilename) {
1354 // Turns a Gnome VFS error into English
1355 GtkWidget *error_dialog;
1356 // error_dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not open the file \"%s\"\n%s\n", myfilename, gnome_vfs_result_to_string(*result));
1357 error_dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not open the file \"%s\"\n%s\n", myfilename, gnome_vfs_result_to_string(*result));
1358 gtk_dialog_set_default_response(GTK_DIALOG(error_dialog), GTK_RESPONSE_OK);
1359 gtk_window_set_resizable(GTK_WINDOW(error_dialog), FALSE);
1360 gtk_dialog_run(GTK_DIALOG(error_dialog));
1361 gtk_widget_destroy(error_dialog);
1362 return;
1365 static gboolean
1366 dasher_editor_gnome_vfs_open_file(DasherEditor *pSelf, const char *myfilename, gchar **buffer, unsigned long long *size) {
1367 GnomeVFSHandle *read_handle;
1368 GnomeVFSResult result;
1369 GnomeVFSFileInfo info;
1370 GnomeVFSFileSize bytes_read;
1371 GnomeVFSURI *uri;
1373 uri = gnome_vfs_uri_new(myfilename);
1375 if(uri == NULL) { // It's not a URI we can cope with - assume it's a filename
1376 char *tmpfilename = gnome_vfs_get_uri_from_local_path(myfilename);
1377 // TODO: figure out how this is supposed to work, and reimplement
1378 // if(myfilename != filename) {
1379 // g_free((void *)myfilename);
1380 // }
1381 myfilename = tmpfilename;
1382 uri = gnome_vfs_uri_new(myfilename);
1383 if(uri == NULL) {
1384 return FALSE;
1388 result = gnome_vfs_open_uri(&read_handle, uri, GNOME_VFS_OPEN_READ);
1389 if(result != GNOME_VFS_OK) {
1390 dasher_editor_vfs_print_error(pSelf, &result, myfilename);
1391 g_free(uri);
1392 return FALSE;
1395 result = gnome_vfs_get_file_info_uri(uri, &info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS);
1396 if(result != GNOME_VFS_OK) {
1397 dasher_editor_vfs_print_error(pSelf, &result, myfilename);
1398 g_free(uri);
1399 return FALSE;
1402 *size = (gint) info.size;
1403 *buffer = (gchar *) g_malloc(*size);
1404 result = gnome_vfs_read(read_handle, *buffer, *size, &bytes_read);
1406 if(result != GNOME_VFS_OK) {
1407 dasher_editor_vfs_print_error(pSelf, &result, myfilename);
1408 g_free(uri);
1409 return FALSE;
1411 gnome_vfs_close(read_handle);
1412 g_free(uri);
1413 return TRUE;
1416 static gboolean
1417 dasher_editor_gnome_vfs_save_file(DasherEditor *pSelf, const char *myfilename, gchar *buffer, unsigned long long length, bool append) {
1418 GnomeVFSHandle *write_handle;
1419 GnomeVFSResult result;
1420 GnomeVFSFileSize bytes_written;
1421 GnomeVFSURI *uri;
1423 uri = gnome_vfs_uri_new(myfilename);
1425 if(uri == NULL) { // It's not a URI we can cope with - assume it's a filename
1426 char *tmpfilename = gnome_vfs_get_uri_from_local_path(myfilename);
1427 // TODO: figure out what this is supposed to do and reimplement
1428 // if(myfilename != filename) {
1429 // g_free((void *)myfilename);
1430 // }
1431 myfilename = tmpfilename;
1432 uri = gnome_vfs_uri_new(myfilename);
1433 if(uri == NULL) {
1434 return FALSE;
1438 result = gnome_vfs_create_uri(&write_handle, uri, GnomeVFSOpenMode(GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_RANDOM), TRUE, 0666);
1440 if(result == GNOME_VFS_ERROR_FILE_EXISTS) {
1441 if(append) {
1442 result = gnome_vfs_open_uri(&write_handle, uri, GnomeVFSOpenMode(GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_RANDOM));
1444 else {
1445 result = gnome_vfs_create_uri(&write_handle, uri, GnomeVFSOpenMode(GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_RANDOM), FALSE, 0666);
1449 if(result != GNOME_VFS_OK) {
1450 dasher_editor_vfs_print_error(pSelf, &result, myfilename);
1451 g_free(uri);
1452 return FALSE;
1455 if(append) {
1456 result = gnome_vfs_seek(write_handle, GNOME_VFS_SEEK_END, 0);
1457 if(result != GNOME_VFS_OK) {
1458 dasher_editor_vfs_print_error(pSelf, &result, myfilename);
1459 g_free(uri);
1460 return FALSE;
1464 result = gnome_vfs_write(write_handle, buffer, length, &bytes_written);
1465 if(result != GNOME_VFS_OK) {
1466 dasher_editor_vfs_print_error(pSelf, &result, myfilename);
1467 g_free(uri);
1468 return FALSE;
1471 gnome_vfs_close(write_handle);
1472 g_free(uri);
1473 return TRUE;
1475 #endif
1477 static void
1478 dasher_editor_set_filename(DasherEditor *pSelf, const gchar *szFilename) {
1479 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1481 if(pPrivate->szFilename)
1482 g_free((void *)pPrivate->szFilename);
1484 if(szFilename)
1485 pPrivate->szFilename = g_strdup(szFilename);
1486 else
1487 pPrivate->szFilename = NULL;
1489 g_signal_emit_by_name(G_OBJECT(pSelf), "filename_changed", G_OBJECT(pSelf), NULL, NULL);
1492 static void
1493 dasher_editor_convert(DasherEditor *pSelf) {
1494 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1496 if(pPrivate->pBufferSet)
1497 idasher_buffer_set_edit_convert(pPrivate->pBufferSet);
1500 static void
1501 dasher_editor_protect(DasherEditor *pSelf) {
1502 DasherEditorPrivate *pPrivate = (DasherEditorPrivate *)(pSelf->private_data);
1504 if(pPrivate->pBufferSet)
1505 idasher_buffer_set_edit_protect(pPrivate->pBufferSet);
1508 static void
1509 dasher_editor_new_buffer(DasherEditor *pSelf, const gchar *szFilename) {
1510 /* TODO: eventually rewrite this without references to external functions */
1512 if(szFilename) {
1513 dasher_editor_open(pSelf, szFilename);
1515 else {
1516 dasher_editor_generate_filename(pSelf);
1517 dasher_editor_clear(pSelf, false);
1520 g_signal_emit_by_name(G_OBJECT(pSelf), "buffer_changed", G_OBJECT(pSelf), NULL, NULL);
1523 /* Callback Functions */
1525 extern "C" void
1526 delete_children_callback(GtkWidget *pWidget, gpointer pUserData) {
1527 gtk_widget_destroy(pWidget);
1530 extern "C" void
1531 main_window_realized(DasherMain *pMain, gpointer pUserData) {
1534 extern "C" void
1535 action_button_callback(GtkWidget *pWidget, gpointer pUserData) {
1536 void **pPointers((void **)pUserData);
1537 dasher_editor_action_button((DasherEditor *)pPointers[0], (DasherAction *)pPointers[1]);
1540 extern "C" void
1541 context_changed_handler(GObject *pSource, gpointer pUserData) {
1542 DasherEditor *pSelf = DASHER_EDITOR(pUserData);
1544 // TODO: plumb signal back into control
1545 g_signal_emit_by_name(G_OBJECT(pSelf), "context_changed", G_OBJECT(pSelf), NULL, NULL);
1548 extern "C" void
1549 handle_start_event(GtkDasherControl *pDasherControl, gpointer data) {
1550 if(g_pEditor)
1551 dasher_editor_handle_start(g_pEditor);
1554 extern "C" void
1555 handle_stop_event(GtkDasherControl *pDasherControl, gpointer data) {
1556 if(g_pEditor)
1557 dasher_editor_handle_stop(g_pEditor);
1560 extern "C" void
1561 on_message(GtkDasherControl *pDasherControl, gpointer pMessageInfo, gpointer pUserData) {
1562 if(g_pEditor)
1563 dasher_editor_display_message(g_pEditor, (DasherMessageInfo *)pMessageInfo);
1566 extern "C" void
1567 on_command(GtkDasherControl *pDasherControl, gchar *szCommand, gpointer pUserData) {
1568 if(g_pEditor)
1569 dasher_editor_command(g_pEditor, szCommand);
1572 // TODO: The following two should probably be made the same
1573 extern "C" void
1574 handle_request_settings(GtkDasherControl * pDasherControl, gpointer data) {
1575 // TODO: reimplement
1576 // dasher_preferences_dialogue_show(g_pPreferencesDialogue);
1579 extern "C" void
1580 gtk2_edit_delete_callback(GtkDasherControl *pDasherControl, const gchar *szText, gpointer user_data) {
1581 gint displaylength = g_utf8_strlen(szText, -1);
1582 dasher_editor_delete(g_pEditor, displaylength);
1585 extern "C" void
1586 gtk2_edit_output_callback(GtkDasherControl *pDasherControl, const gchar *szText, gpointer user_data) {
1587 dasher_editor_output(g_pEditor, szText);
1590 extern "C" void
1591 convert_cb(GtkDasherControl *pDasherControl, gpointer pUserData) {
1592 dasher_editor_convert(g_pEditor);
1595 extern "C" void
1596 protect_cb(GtkDasherControl *pDasherControl, gpointer pUserData) {
1597 dasher_editor_protect(g_pEditor);