6 #include <glib/gi18n.h>
12 #include "dasher_editor.h"
13 #include "dasher_editor_private.h"
14 #include "dasher_editor_external.h"
15 #include "dasher_lock_dialogue.h"
16 #include "dasher_main.h"
17 #include "GtkDasherControl.h"
18 #include "../DasherCore/ControlManager.h"
20 #if GTK_CHECK_VERSION (3,0,0)
21 G_DEFINE_TYPE(DasherEditor
, dasher_editor
, GTK_TYPE_BOX
);
23 G_DEFINE_TYPE(DasherEditor
, dasher_editor
, GTK_TYPE_VBOX
);
34 static guint dasher_editor_signals
[SIGNAL_NUM
];
36 static void dasher_editor_finalize(GObject
*pObject
);
38 static void dasher_editor_internal_handle_font(DasherEditor
*pSelf
, const gchar
*szFont
);
40 void dasher_editor_initialise(DasherEditor
*pSelf
, DasherAppSettings
*pAppSettings
, GtkDasherControl
*pDasherCtrl
, GtkBuilder
*pXML
, const gchar
*szFullPath
);
43 static void dasher_editor_internal_select_all(DasherEditor
*pSelf
);
45 static void dasher_editor_internal_command_new(DasherEditor
*pSelf
);
46 static void dasher_editor_internal_command_open(DasherEditor
*pSelf
);
47 static void dasher_editor_internal_command_save(DasherEditor
*pSelf
, gboolean bPrompt
, gboolean bAppend
);
48 static void edit_find(bool bForwards
, Dasher::CControlManager::EditDistance iDist
, DasherEditorPrivate
*pPrivate
, GtkTextIter
*pPos
);
51 static void dasher_editor_internal_gvfs_print_error(DasherEditor
*pSelf
, GError
*error
, const char *myfilename
);
52 static GFileOutputStream
*append_or_replace_file(GFile
*file
, bool append
, GError
**error
);
53 static gboolean
dasher_editor_internal_gvfs_open_file(DasherEditor
*pSelf
, const char *filename
, gchar
** buffer
, gsize
*size
);
54 static gboolean
dasher_editor_internal_gvfs_save_file(DasherEditor
*pSelf
, const char *filename
, gchar
* buffer
, gsize length
, bool append
);
56 static gboolean
dasher_editor_internal_unix_vfs_open_file(DasherEditor
*pSelf
, const char *filename
, gchar
** buffer
, gsize
*size
);
57 static gboolean
dasher_editor_internal_unix_vfs_save_file(DasherEditor
*pSelf
, const char *filename
, gchar
* buffer
, gsize length
, bool append
);
60 static void dasher_editor_internal_set_filename(DasherEditor
*pSelf
, const gchar
*szFilename
);
62 static void dasher_editor_internal_new_buffer(DasherEditor
*pSelf
, const gchar
*szFilename
);
64 static void dasher_editor_internal_generate_filename(DasherEditor
*pSelf
);
65 static void dasher_editor_internal_open(DasherEditor
*pSelf
, const gchar
*szFilename
);
66 static bool dasher_editor_internal_save_as(DasherEditor
*pSelf
, const gchar
*szFilename
, bool bAppend
);
67 static void dasher_editor_internal_create_buffer(DasherEditor
*pSelf
);
68 static void dasher_editor_internal_clipboard(DasherEditor
*pSelf
, clipboard_action act
);
70 /* To be obsoleted by movement to GTK buffers */
71 void dasher_editor_internal_mark_changed(DasherEditor
*pSelf
, GtkTextIter
*pIter
, GtkTextMark
*pMark
);
73 /* Todo: possibly tidy up the need to have this public (quit in dasher_main possibly too connected) */
74 gboolean
dasher_editor_internal_file_changed(DasherEditor
*pSelf
);
77 // Private methods not in class
78 extern "C" void delete_children_callback(GtkWidget
*pWidget
, gpointer pUserData
);
79 extern "C" void main_window_realized(DasherMain
*pMain
, gpointer pUserData
);
80 extern "C" void action_button_callback(GtkWidget
*pWidget
, gpointer pUserData
);
81 extern "C" void mark_set_handler(GtkWidget
*widget
, GtkTextIter
*pIter
, GtkTextMark
*pMark
, gpointer pUserData
);
82 extern "C" void handle_stop_event(GtkDasherControl
*pDasherControl
, gpointer data
);
83 extern "C" void handle_request_settings(GtkDasherControl
* pDasherControl
, gpointer data
);
84 extern "C" void gtk2_edit_delete_callback(GtkDasherControl
*pDasherControl
, const gchar
*szText
, int iOffset
, gpointer user_data
);
85 extern "C" void gtk2_edit_output_callback(GtkDasherControl
*pDasherControl
, const gchar
*szText
, int iOffset
, gpointer user_data
);
88 isdirect(DasherAppSettings
*pAppSettings
) {
89 return pAppSettings
->GetLong(APP_LP_STYLE
) == APP_STYLE_DIRECT
;
93 dasher_editor_class_init(DasherEditorClass
*pClass
) {
94 g_type_class_add_private(pClass
, sizeof(DasherEditorPrivate
));
96 GObjectClass
*pObjectClass
= (GObjectClass
*) pClass
;
97 pObjectClass
->finalize
= dasher_editor_finalize
;
100 dasher_editor_signals
[FILENAME_CHANGED
] = g_signal_new("filename-changed", G_TYPE_FROM_CLASS(pClass
),
101 static_cast < GSignalFlags
> (G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
),
102 G_STRUCT_OFFSET(DasherEditorClass
, filename_changed
),
103 NULL
, NULL
, g_cclosure_marshal_VOID__VOID
,
106 dasher_editor_signals
[BUFFER_CHANGED
] = g_signal_new("buffer-changed", G_TYPE_FROM_CLASS(pClass
),
107 static_cast < GSignalFlags
> (G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
),
108 G_STRUCT_OFFSET(DasherEditorClass
, buffer_changed
),
109 NULL
, NULL
, g_cclosure_marshal_VOID__VOID
,
112 dasher_editor_signals
[CONTEXT_CHANGED
] = g_signal_new("context-changed", G_TYPE_FROM_CLASS(pClass
),
113 static_cast < GSignalFlags
> (G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
),
114 G_STRUCT_OFFSET(DasherEditorClass
, context_changed
),
115 NULL
, NULL
, g_cclosure_marshal_VOID__VOID
,
119 pClass->initialise = NULL;
120 pClass->command = NULL;
121 pClass->clear = NULL;
122 pClass->get_all_text = NULL;
123 pClass->get_new_text = NULL;
124 pClass->output = NULL;
125 pClass->delete_text = NULL;
126 pClass->get_context = NULL;
127 pClass->get_offset = NULL;
128 pClass->ctrl_move = NULL;
129 pClass->ctrl_delete = NULL;
130 pClass->edit_convert = NULL;
131 pClass->edit_protect = NULL;
132 pClass->handle_parameter_change = NULL;
133 pClass->handle_stop = NULL;
134 pClass->handle_start = NULL;
135 pClass->grab_focus = NULL;
136 pClass->file_changed = NULL;
137 pClass->get_filename = NULL;
139 pClass->filename_changed = NULL;
140 pClass->buffer_changed = NULL;
141 pClass->context_changed = NULL;
144 pParentClass->output = dasher_editor_output;
145 pParentClass->delete_text = dasher_editor_delete;
146 pParentClass->get_context = dasher_editor_get_context;
147 pParentClass->get_offset = dasher_editor_get_offset;
149 pParentClass->file_changed = dasher_editor_internal_file_changed;
154 dasher_editor_init(DasherEditor
*pSelf
) {
155 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
157 pPrivate
->pTextView
= GTK_TEXT_VIEW(gtk_text_view_new());
158 gtk_text_view_set_wrap_mode(pPrivate
->pTextView
, GTK_WRAP_WORD
);
159 pPrivate
->pBuffer
= gtk_text_view_get_buffer(pPrivate
->pTextView
);
160 pPrivate
->pTextClipboard
= gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
);
161 pPrivate
->pPrimarySelection
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
162 GtkTextIter oStartIter
;
163 gtk_text_buffer_get_start_iter(pPrivate
->pBuffer
, &oStartIter
);
165 gtk_text_buffer_create_mark(pPrivate
->pBuffer
, NULL
, &oStartIter
, TRUE
);
166 pPrivate
->szFilename
= NULL
;
167 pPrivate
->bFileModified
= FALSE
;
168 pPrivate
->bInControlAction
= FALSE
;
170 GtkWidget
*pScrolledWindow
= gtk_scrolled_window_new(NULL
, NULL
);
172 gtk_container_add(GTK_CONTAINER(pScrolledWindow
),
173 GTK_WIDGET(pPrivate
->pTextView
));
175 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pScrolledWindow
),
176 GTK_POLICY_AUTOMATIC
,
177 GTK_POLICY_AUTOMATIC
);
179 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(pScrolledWindow
),
182 gtk_box_pack_start(GTK_BOX(&(pSelf
->box
)),
183 pScrolledWindow
, true, true, 0);
185 gtk_widget_show_all(GTK_WIDGET(&(pSelf
->box
)));
189 dasher_editor_finalize(GObject
*pObject
) {
190 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pObject
);
192 dasher_editor_external_finalize(pObject
);
194 if(pPrivate
->szFilename
)
195 g_free(pPrivate
->szFilename
);
200 dasher_editor_new(void)
202 #if GTK_CHECK_VERSION (3,0,0)
204 DASHER_EDITOR(g_object_new(DASHER_TYPE_EDITOR
, "orientation", GTK_ORIENTATION_VERTICAL
, NULL
));
207 DASHER_EDITOR(g_object_new(DASHER_TYPE_EDITOR
, NULL
));
212 dasher_editor_initialise(DasherEditor
*pSelf
, DasherAppSettings
*pAppSettings
, GtkDasherControl
*pDasherCtrl
, GtkBuilder
*pXML
, const gchar
*szFullPath
) {
213 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
215 pPrivate
->pAppSettings
= pAppSettings
;
216 pPrivate
->pDasherCtrl
= pDasherCtrl
;
218 dasher_editor_internal_handle_font(pSelf
,
219 pPrivate
->pAppSettings
->GetString(APP_SP_EDIT_FONT
).c_str());
221 dasher_editor_external_create_buffer(pSelf
);
222 // TODO: is this still needed?
223 dasher_editor_internal_create_buffer(pSelf
);
225 // TODO: see note in command_new method
227 dasher_editor_internal_open(pSelf
, szFullPath
);
229 dasher_editor_internal_generate_filename(pSelf
);
230 dasher_editor_clear(pSelf
);
235 dasher_editor_internal_clipboard(DasherEditor
*pSelf
, clipboard_action act
) {
236 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
238 GtkTextIter
*start
= new GtkTextIter
;
239 GtkTextIter
*end
= new GtkTextIter
;
241 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate
->pBuffer
), start
, 0);
242 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate
->pBuffer
), end
, -1);
244 gchar
*the_text
= gtk_text_buffer_get_text(pPrivate
->pBuffer
, start
, end
, TRUE
);
248 gtk_text_buffer_cut_clipboard(pPrivate
->pBuffer
, pPrivate
->pTextClipboard
, TRUE
);
251 gtk_text_buffer_copy_clipboard(pPrivate
->pBuffer
, pPrivate
->pTextClipboard
);
253 case CLIPBOARD_PASTE
:
254 gtk_text_buffer_paste_clipboard(pPrivate
->pBuffer
, pPrivate
->pTextClipboard
, NULL
, TRUE
);
256 case CLIPBOARD_COPYALL
:
257 gtk_clipboard_set_text(pPrivate
->pTextClipboard
, the_text
, strlen(the_text
));
258 gtk_clipboard_set_text(pPrivate
->pPrimarySelection
, the_text
, strlen(the_text
));
261 case CLIPBOARD_SELECTALL
:
262 dasher_editor_internal_select_all(pSelf
);
264 case CLIPBOARD_CLEAR
:
265 gtk_text_buffer_set_text(pPrivate
->pBuffer
, "", 0);
275 dasher_editor_handle_stop(DasherEditor
*pSelf
) {
279 dasher_editor_handle_start(DasherEditor
*pSelf
) {
280 // The edit box keeps track of where we started
282 // TODO: This should be filtered through the buffer, rather than directly to the edit box
287 dasher_editor_toggle_direct_mode(DasherEditor
*pSelf
) {
288 // Should be able to play function pointer games here to avoid
289 // isdirect() calls in dasher_editor_output() and friends.
291 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
293 dasher_editor_external_toggle_direct_mode(pSelf
, isdirect(pPrivate
->pAppSettings
));
297 dasher_editor_clear(DasherEditor
*pSelf
) {
298 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
300 GtkTextIter start
, end
;
302 gtk_text_buffer_get_iter_at_offset(pPrivate
->pBuffer
, &start
, 0);
303 gtk_text_buffer_get_iter_at_offset(pPrivate
->pBuffer
, &end
, -1);
305 gtk_text_buffer_delete(pPrivate
->pBuffer
, &start
, &end
);
307 /* TODO: this probably shouldn't emit a signal */
308 //ACL but since it did...internal_buffer emitted "buffer_changed" signal,
309 // which was picked up by callback registered by editor_internal, which
310 // then emitted a "buffer_changed" signal from the editor_internal. So
311 // emit directly from the editor_internal...
312 g_signal_emit_by_name(G_OBJECT(pSelf
), "buffer_changed", G_OBJECT(pSelf
), NULL
, NULL
);
314 pPrivate
->iCurrentState
= 0;
315 pPrivate
->iLastOffset
= 0;
319 dasher_editor_grab_focus(DasherEditor
*pSelf
) {
320 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
322 if(pPrivate
->pTextView
)
323 gtk_widget_grab_focus(GTK_WIDGET(pPrivate
->pTextView
));
328 dasher_editor_internal_create_buffer(DasherEditor
*pSelf
) {
329 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
332 pPrivate
->pOutputTag
= gtk_text_buffer_create_tag(pPrivate
->pBuffer
, NULL
, NULL
);
334 pPrivate
->pHiddenTag
= gtk_text_buffer_create_tag(pPrivate
->pBuffer
, NULL
, "invisible", TRUE
, NULL
);
336 pPrivate
->pVisibleTag
= gtk_text_buffer_create_tag(pPrivate
->pBuffer
, NULL
, "foreground", "red", NULL
);
338 pPrivate
->bConversionMode
= FALSE
;
339 pPrivate
->iLastOffset
= 1;
340 pPrivate
->iCurrentState
= 0;
342 g_signal_connect(G_OBJECT(pPrivate
->pBuffer
), "mark-set", G_CALLBACK(mark_set_handler
), pSelf
);
346 // dasher_editor_output(DasherEditor *pSelf, const gchar *szText, int iOffset)
347 // if(DASHER_EDITOR_GET_CLASS(pSelf)->output)
348 // DASHER_EDITOR_GET_CLASS(pSelf)->output(pSelf, szText, iOffset);
351 dasher_editor_output(DasherEditor
*pSelf
, const gchar
*szText
, int iOffset
) {
352 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
354 if (isdirect(pPrivate
->pAppSettings
))
355 return dasher_editor_external_output(pSelf
, szText
, iOffset
);
357 gtk_text_buffer_delete_selection(pPrivate
->pBuffer
, false, true );
360 gtk_text_buffer_get_iter_at_mark(pPrivate
->pBuffer
, &sIter
, gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
362 GtkTextTag
*pCurrentTag
= NULL
;
364 if(!pPrivate
->bConversionMode
)
365 pCurrentTag
= pPrivate
->pOutputTag
;
367 switch(pPrivate
->iCurrentState
) {
369 pCurrentTag
= pPrivate
->pVisibleTag
;
372 pCurrentTag
= pPrivate
->pOutputTag
;
380 gtk_text_buffer_insert_with_tags(pPrivate
->pBuffer
, &sIter
, szText
, -1, pCurrentTag
, NULL
);
382 gtk_text_view_scroll_mark_onscreen(pPrivate
->pTextView
, gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
384 pPrivate
->bFileModified
= TRUE
;
388 // dasher_editor_delete(DasherEditor *pSelf, int iLength, int iOffset) {
389 // if(DASHER_EDITOR_GET_CLASS(pSelf)->delete_text)
390 // DASHER_EDITOR_GET_CLASS(pSelf)->delete_text(pSelf, iLength, iOffset);
393 dasher_editor_delete(DasherEditor
*pSelf
, int iLength
, int iOffset
) {
394 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
396 if (isdirect(pPrivate
->pAppSettings
))
397 return dasher_editor_external_delete(pSelf
, iLength
, iOffset
);
401 //Dasher offset 0 = "the first character"; Gtk Text Buffer offset 0
402 // = "the cursor position just before the first character" (and we want
403 // the cursor position just after)
404 gtk_text_buffer_get_iter_at_offset(pPrivate
->pBuffer
, &end
, iOffset
+1);
406 GtkTextIter start
= end
;
408 gtk_text_iter_backward_chars(&start
, iLength
);
409 // g_bIgnoreCursorMove = true;
410 gtk_text_buffer_delete(pPrivate
->pBuffer
, &start
, &end
);
411 gtk_text_view_scroll_mark_onscreen(pPrivate
->pTextView
, gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
412 // g_bIgnoreCursorMove = false;
414 pPrivate
->bFileModified
= TRUE
;
418 // dasher_editor_get_context(DasherEditor *pSelf, int iOffset, int iLength) {
419 // if(DASHER_EDITOR_GET_CLASS(pSelf)->get_context)
420 // return DASHER_EDITOR_GET_CLASS(pSelf)->get_context(pSelf, iOffset, iLeng
424 std::string
dasher_editor_get_context(DasherEditor
*pSelf
, int iOffset
, int iLength
) {
425 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
427 if (isdirect(pPrivate
->pAppSettings
))
428 return dasher_editor_external_get_context(pSelf
, iOffset
, iLength
);
431 GtkTextIter end
; // Refers to end of context, which is start of selection!
433 gtk_text_buffer_get_iter_at_offset(pPrivate
->pBuffer
, &start
, iOffset
);
434 gtk_text_buffer_get_iter_at_offset(pPrivate
->pBuffer
, &end
, iOffset
+ iLength
);
436 auto text
= gtk_text_buffer_get_text( pPrivate
->pBuffer
, &start
, &end
, false );
437 if (text
!= nullptr) {
438 std::string context
= text
;
445 std::string
dasher_editor_get_text_around_cursor(DasherEditor
*pSelf
, Dasher::CControlManager::EditDistance distance
) {
446 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
447 if (isdirect(pPrivate
->pAppSettings
))
448 return dasher_editor_external_get_text_around_cursor(pSelf
, distance
);
450 // TODO: handle the external editor.
451 GtkTextIter end_pos
, start_pos
;
452 gtk_text_buffer_get_iter_at_mark(pPrivate
->pBuffer
, &end_pos
, gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
454 edit_find(true /* forward */, distance
, pPrivate
, &end_pos
);
455 edit_find(false /* backward */, distance
, pPrivate
, &start_pos
);
456 return gtk_text_buffer_get_slice(pPrivate
->pBuffer
, &start_pos
, &end_pos
, FALSE
/* hidden chars */);
460 // dasher_editor_get_offset(DasherEditor *pSelf) {
461 // if(DASHER_EDITOR_GET_CLASS(pSelf)->get_offset)
462 // return DASHER_EDITOR_GET_CLASS(pSelf)->get_offset(pSelf);
467 dasher_editor_get_offset(DasherEditor
*pSelf
) {
468 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
470 if (isdirect(pPrivate
->pAppSettings
))
471 return dasher_editor_external_get_offset(pSelf
);
473 GtkTextIter iter1
,iter2
;
474 gtk_text_buffer_get_iter_at_mark(pPrivate
->pBuffer
, &iter1
, gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
475 gtk_text_buffer_get_iter_at_mark(pPrivate
->pBuffer
, &iter2
, gtk_text_buffer_get_selection_bound(pPrivate
->pBuffer
));
476 return std::min(gtk_text_iter_get_offset(&iter1
),gtk_text_iter_get_offset(&iter2
));
479 void dasher_editor_internal_mark_changed(DasherEditor
*pSelf
, GtkTextIter
*pIter
, GtkTextMark
*pMark
) {
480 const char *szMarkName(gtk_text_mark_get_name(pMark
));
481 if(szMarkName
&& !strcmp(szMarkName
,"insert")) {
482 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
483 //ACL: used to emit "offset_changed" signal from buffer, which was picked up
484 // by a callback registered by editor_internal, which then emitted a context_changed
485 // signal from the editor_internal. So just emit the context_changed directly...
486 if (!pPrivate
->bInControlAction
//tho not if it's the result of a control-mode edit/delete
487 && !gtk_dasher_control_get_game_mode(pPrivate
->pDasherCtrl
)) //and not in game mode
488 g_signal_emit_by_name(G_OBJECT(pSelf
), "context_changed", G_OBJECT(pSelf
), NULL
, NULL
);
494 dasher_editor_internal_generate_filename(DasherEditor
*pSelf
) {
495 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
497 gchar
*szNewFilename
= NULL
;
499 if( pPrivate
->pAppSettings
->GetBool(APP_BP_TIME_STAMP
)) {
500 // Build a filename based on the current time and date
508 t_struct
= localtime(&ctime
);
511 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
);
513 szNewFilename
= g_build_path("/", cwd
, tbuffer
, NULL
);
516 dasher_editor_internal_set_filename(pSelf
, szNewFilename
);
518 g_free(szNewFilename
);
522 dasher_editor_internal_open(DasherEditor
*pSelf
, const gchar
*szFilename
) {
523 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
529 if(!dasher_editor_internal_gvfs_open_file(pSelf
, szFilename
, &buffer
, &size
)) {
533 if(!dasher_editor_internal_unix_vfs_open_file(pSelf
, szFilename
, &buffer
, &size
)) {
538 // FIXME - REIMPLEMENT (shouldn't happen through core)
542 // Don't attempt to insert new text if the file is empty as it makes
544 if(!g_utf8_validate(buffer
, size
, NULL
)) { // PRLW: size as gssize = signed int
545 // It's not UTF8, so we do the best we can...
547 // If there are zero bytes in the file then we have a problem -
548 // for now, just assert that we can't load these files.
549 for(gsize i
= 0; i
< size
; ++i
)
551 // GtkWidget *pErrorBox = gtk_message_dialog_new(GTK_WINDOW(window),
553 // GTK_MESSAGE_ERROR,
555 // "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",
557 GtkWidget
*pErrorBox
= gtk_message_dialog_new(NULL
,
561 "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",
563 gtk_dialog_run(GTK_DIALOG(pErrorBox
));
564 gtk_widget_destroy(pErrorBox
);
568 pPrivate
->bFileModified
= TRUE
;
571 gchar
*buffer2
= g_strdup(g_locale_to_utf8(buffer
, size
, NULL
, &iNewSize
, NULL
));
573 // TODO: This function probably needs more thought
575 // const gchar *pEnd;
576 //gboolean bValid = g_utf8_validate(buffer2, -1, &pEnd);
582 gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(pPrivate
->pBuffer
), buffer
, size
);
583 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(pPrivate
->pTextView
), gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(pPrivate
->pBuffer
)));
586 dasher_editor_internal_set_filename(pSelf
, szFilename
);
590 dasher_editor_internal_save_as(DasherEditor
*pSelf
, const gchar
*szFilename
, bool bAppend
) {
591 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
593 unsigned long long length
;
594 gchar
*inbuffer
, *outbuffer
= NULL
;
595 // gsize bytes_read, bytes_written;
597 // GError *error = NULL;
598 GtkTextIter
*start
, *end
;
601 start
= new GtkTextIter
;
602 end
= new GtkTextIter
;
604 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate
->pBuffer
), start
, 0);
605 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate
->pBuffer
), end
, -1);
607 inbuffer
= gtk_text_iter_get_slice(start
, end
);
609 // g_message("String %s", inbuffer);
611 //length = gtk_text_iter_get_offset(end) - gtk_text_iter_get_offset(start);
612 //length = gtk_text_buffer_get_byte_count(GTK_TEXT_BUFFER(the_text_buffer));
614 // I'm pretty certain that this is null terminated, but not 100%
615 length
= strlen(inbuffer
);
617 // g_message("Length is %d", length);
619 outbuffer
= (char *)malloc((length
+ 1) * sizeof(gchar
));
620 memcpy((void *)outbuffer
, (void *)inbuffer
, length
* sizeof(gchar
));
621 outbuffer
[length
] = 0;
623 inbuffer
= outbuffer
;
624 bytes_written
= length
;
627 if(!dasher_editor_internal_gvfs_save_file(pSelf
, szFilename
, outbuffer
, bytes_written
, bAppend
)) {
631 if(!dasher_editor_internal_unix_vfs_save_file(pSelf
, szFilename
, outbuffer
, bytes_written
, bAppend
)) {
636 pPrivate
->bFileModified
= FALSE
;
638 dasher_editor_internal_set_filename(pSelf
, szFilename
);
643 GtkTextBuffer
*dasher_editor_game_text_buffer(DasherEditor
*pSelf
) {
644 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
645 return pPrivate
->pBuffer
;
649 dasher_editor_command(DasherEditor
*pSelf
, const gchar
*szCommand
) {
650 if(!strcmp(szCommand
, "action_new")) { //select_new_file
651 dasher_editor_internal_command_new(pSelf
);
655 if(!strcmp(szCommand
, "action_open")) { //select open file
656 dasher_editor_internal_command_open(pSelf
);
660 if(!strcmp(szCommand
, "action_save")) { //save_file
661 dasher_editor_internal_command_save(pSelf
, FALSE
, FALSE
);
665 if(!strcmp(szCommand
, "action_saveas")) { // select_save_file_as
666 dasher_editor_internal_command_save(pSelf
, TRUE
, FALSE
);
670 if(!strcmp(szCommand
, "action_append")) { // select_append_file
671 dasher_editor_internal_command_save(pSelf
, TRUE
, TRUE
);
675 if(!strcmp(szCommand
, "action_cut")) { // clipboard_cut
676 dasher_editor_internal_clipboard(pSelf
, CLIPBOARD_CUT
);
680 if(!strcmp(szCommand
, "action_copy")) { // clipboard_copy
681 dasher_editor_internal_clipboard(pSelf
, CLIPBOARD_COPY
);
685 if(!strcmp(szCommand
, "action_copyall")) { // clipboard_copyall
686 dasher_editor_internal_clipboard(pSelf
, CLIPBOARD_COPYALL
);
690 if(!strcmp(szCommand
, "action_paste")) { // clipboard_paste
691 dasher_editor_internal_clipboard(pSelf
, CLIPBOARD_PASTE
);
695 // TODO: This isn't actually accessible from anywhere
696 if(!strcmp(szCommand
, "action_selectall")) { // clipboard_paste
697 dasher_editor_internal_clipboard(pSelf
, CLIPBOARD_SELECTALL
);
706 dasher_editor_internal_handle_font(DasherEditor
*pSelf
, const gchar
*szFont
) {
707 if(strcmp(szFont
, "")) {
708 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
710 PangoFontDescription
*pFD
= pango_font_description_from_string(szFont
);
711 #if GTK_CHECK_VERSION(3, 0, 0)
712 gtk_widget_override_font(GTK_WIDGET(pPrivate
->pTextView
), pFD
);
714 gtk_widget_modify_font(GTK_WIDGET(pPrivate
->pTextView
), pFD
);
721 dasher_editor_file_changed(DasherEditor
*pSelf
) {
722 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
723 return pPrivate
->bFileModified
;
727 dasher_editor_get_filename(DasherEditor
*pSelf
) {
728 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
730 return pPrivate
->szFilename
;
733 // TODO: We shouldn't need to know about the buffer here - make this a method of the buffer set
735 dasher_editor_get_all_text(DasherEditor
*pSelf
) {
736 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
737 if (pPrivate
== NULL
) {
738 // This function can be called before pPrivate is set.
744 gtk_text_buffer_get_start_iter(pPrivate
->pBuffer
, &oStart
);
745 gtk_text_buffer_get_end_iter(pPrivate
->pBuffer
, &oEnd
);
747 pPrivate
->pNewMark
= gtk_text_buffer_create_mark(pPrivate
->pBuffer
, NULL
, &oEnd
, TRUE
);
749 std::string all_text
;
750 auto text
= gtk_text_buffer_get_text(pPrivate
->pBuffer
, &oStart
, &oEnd
, false );
751 if (text
!= nullptr) {
759 dasher_editor_get_new_text(DasherEditor
*pSelf
) {
760 // TODO: Implement this properly
761 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
766 gtk_text_buffer_get_end_iter(pPrivate
->pBuffer
, &oEnd
);
767 gtk_text_buffer_get_iter_at_mark(pPrivate
->pBuffer
, &oStart
, pPrivate
->pNewMark
);
769 const gchar
*szRetVal
= gtk_text_buffer_get_text(pPrivate
->pBuffer
, &oStart
, &oEnd
, false );
771 pPrivate
->pNewMark
= gtk_text_buffer_create_mark(pPrivate
->pBuffer
, NULL
, &oEnd
, TRUE
);
777 /* Private methods */
779 dasher_editor_internal_select_all(DasherEditor
*pSelf
) {
780 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
781 GtkTextIter
*start
, *end
;
783 start
= new GtkTextIter
;
784 end
= new GtkTextIter
;
786 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate
->pBuffer
), start
, 0);
787 gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(pPrivate
->pBuffer
), end
, -1);
789 GtkTextMark
*selection
= gtk_text_buffer_get_mark(pPrivate
->pBuffer
, "selection_bound");
790 GtkTextMark
*cursor
= gtk_text_buffer_get_mark(pPrivate
->pBuffer
, "insert");
792 gtk_text_buffer_move_mark(pPrivate
->pBuffer
, selection
, start
);
793 gtk_text_buffer_move_mark(pPrivate
->pBuffer
, cursor
, end
);
800 dasher_editor_internal_command_new(DasherEditor
*pSelf
) {
801 dasher_editor_internal_new_buffer(pSelf
, NULL
);
805 dasher_editor_internal_command_open(DasherEditor
*pSelf
) {
806 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
808 GtkWidget
*pTopLevel
= gtk_widget_get_toplevel(GTK_WIDGET(pPrivate
->pTextView
));
809 GtkWidget
*filesel
= gtk_file_chooser_dialog_new(_("Select File"),
810 GTK_WINDOW(pTopLevel
),
811 GTK_FILE_CHOOSER_ACTION_OPEN
,
815 GTK_RESPONSE_CANCEL
, NULL
);
818 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(filesel
), FALSE
);
821 if(gtk_dialog_run(GTK_DIALOG(filesel
)) == GTK_RESPONSE_ACCEPT
) {
823 char *filename
= gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(filesel
));
825 char *filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel
));
827 dasher_editor_internal_new_buffer(pSelf
, filename
);
831 gtk_widget_destroy(filesel
);
835 dasher_editor_internal_command_save(DasherEditor
*pSelf
, gboolean bPrompt
, gboolean bAppend
) {
836 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
838 gchar
*szFilename
= NULL
;
840 // Hmm... this makes no sense - surely this always evaluates to true?
841 if(bPrompt
|| !szFilename
) {
842 GtkWidget
*pTopLevel
= gtk_widget_get_toplevel(GTK_WIDGET(pPrivate
->pTextView
));
843 GtkWidget
*filesel
= gtk_file_chooser_dialog_new(_("Select File"),
844 GTK_WINDOW(pTopLevel
),
845 GTK_FILE_CHOOSER_ACTION_SAVE
,
849 GTK_RESPONSE_CANCEL
, NULL
);
852 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(filesel
), FALSE
);
855 if(gtk_dialog_run(GTK_DIALOG(filesel
)) == GTK_RESPONSE_ACCEPT
) {
857 szFilename
= gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(filesel
));
859 szFilename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel
));
863 gtk_widget_destroy(filesel
);
867 gtk_widget_destroy(filesel
);
870 szFilename
= g_strdup(pPrivate
->szFilename
);
873 dasher_editor_internal_save_as(pSelf
, szFilename
, bAppend
);
879 dasher_editor_internal_gvfs_print_error(DasherEditor
*pSelf
, GError
*error
, const char *myfilename
) {
880 // Turns a GVFS error into English
881 GtkWidget
*error_dialog
;
882 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
, error
->message
);
883 gtk_dialog_set_default_response(GTK_DIALOG(error_dialog
), GTK_RESPONSE_OK
);
884 gtk_window_set_resizable(GTK_WINDOW(error_dialog
), FALSE
);
885 gtk_dialog_run(GTK_DIALOG(error_dialog
));
886 gtk_widget_destroy(error_dialog
);
891 dasher_editor_internal_gvfs_open_file(DasherEditor
*pSelf
, const char *uri
, gchar
**buffer
, gsize
*size
)
894 GFileInputStream
*read_handle
;
899 file
= g_file_new_for_uri (uri
);
901 read_handle
= g_file_read (file
, NULL
, &error
);
903 /* If URI didn't work, try path */
904 if (read_handle
== NULL
)
905 { // PRLW: g_object_unref isn't actually stipulated by g_file_new_for_uri
906 g_object_unref (file
);
907 file
= g_file_new_for_path (uri
);
908 read_handle
= g_file_read (file
, NULL
, &error
);
911 if (read_handle
== NULL
)
913 dasher_editor_internal_gvfs_print_error (pSelf
, error
, uri
);
914 g_object_unref (file
);
918 info
= g_file_query_info (file
, G_FILE_ATTRIBUTE_STANDARD_SIZE
,
919 G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
923 dasher_editor_internal_gvfs_print_error (pSelf
, error
, uri
);
924 g_input_stream_close (G_INPUT_STREAM(read_handle
), NULL
, &error
);
925 g_object_unref (read_handle
);
926 g_object_unref (file
);
930 // XXX PRLW: cases info > max(size) as max(size) < max(uint64),
931 // and bytes_read < size aren't handled.
932 *size
= g_file_info_get_attribute_uint64(info
,G_FILE_ATTRIBUTE_STANDARD_SIZE
);
933 *buffer
= (gchar
*) g_malloc (*size
);
934 bytes_read
= g_input_stream_read (G_INPUT_STREAM(read_handle
), *buffer
,
935 *size
, NULL
, &error
);
937 if (bytes_read
== -1)
939 dasher_editor_internal_gvfs_print_error (pSelf
, error
, uri
);
940 g_input_stream_close (G_INPUT_STREAM(read_handle
), NULL
, &error
);
941 g_object_unref (read_handle
);
942 g_object_unref (info
);
943 g_object_unref (file
);
947 g_input_stream_close (G_INPUT_STREAM(read_handle
), NULL
, NULL
);
948 g_object_unref (read_handle
);
949 g_object_unref (info
);
950 g_object_unref (file
);
954 static GFileOutputStream
*append_or_replace_file(GFile
*file
, bool append
, GError
**error
)
956 GFileOutputStream
*write_handle
;
958 write_handle
= g_file_append_to (file
, G_FILE_CREATE_NONE
, NULL
, error
);
960 write_handle
= g_file_replace (file
, NULL
, FALSE
, G_FILE_CREATE_NONE
,
967 dasher_editor_internal_gvfs_save_file(DasherEditor
*pSelf
, const char *uri
, gchar
*buffer
, gsize length
, bool append
)
970 GFileOutputStream
*write_handle
;
971 GError
*error
= NULL
;
972 gssize bytes_written
;
974 file
= g_file_new_for_uri (uri
);
976 write_handle
= append_or_replace_file (file
, append
, &error
);
978 /* If URI didn't work, try path */
979 if (write_handle
== NULL
)
981 g_object_unref (file
);
982 file
= g_file_new_for_path (uri
);
983 write_handle
= append_or_replace_file (file
, append
, &error
);
986 if (write_handle
== NULL
)
988 dasher_editor_internal_gvfs_print_error (pSelf
, error
, uri
);
989 g_object_unref (file
);
995 if (!g_seekable_seek(G_SEEKABLE(write_handle
),0,G_SEEK_END
, NULL
, &error
))
997 dasher_editor_internal_gvfs_print_error (pSelf
, error
, uri
);
998 g_object_unref (write_handle
);
999 g_object_unref (file
);
1004 bytes_written
= g_output_stream_write (G_OUTPUT_STREAM(write_handle
), buffer
,
1005 length
, NULL
, &error
);
1006 // XXX PRLW: case bytes_written < length not handled.
1007 if (bytes_written
== -1)
1009 dasher_editor_internal_gvfs_print_error (pSelf
, error
, uri
);
1010 g_output_stream_close (G_OUTPUT_STREAM(write_handle
), NULL
, NULL
);
1011 g_object_unref (write_handle
);
1012 g_object_unref (file
);
1016 g_output_stream_close (G_OUTPUT_STREAM(write_handle
), NULL
, NULL
);
1017 g_object_unref (write_handle
);
1018 g_object_unref (file
);
1022 #else /* not HAVE_GIO */
1025 dasher_editor_internal_unix_vfs_open_file(DasherEditor
*pSelf
, const char *myfilename
, gchar
**buffer
, gsize
*size
) {
1026 GtkWidget
*error_dialog
;
1028 struct stat file_stat
;
1031 stat(myfilename
, &file_stat
);
1032 fp
= fopen(myfilename
, "r");
1034 if(fp
== NULL
|| S_ISDIR(file_stat
.st_mode
)) {
1035 // 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);
1036 error_dialog
= gtk_message_dialog_new(NULL
, GTK_DIALOG_MODAL
, GTK_MESSAGE_ERROR
, GTK_BUTTONS_OK
, "Could not open the file \"%s\".\n", myfilename
);
1037 gtk_dialog_set_default_response(GTK_DIALOG(error_dialog
), GTK_RESPONSE_OK
);
1038 gtk_window_set_resizable(GTK_WINDOW(error_dialog
), FALSE
);
1039 gtk_dialog_run(GTK_DIALOG(error_dialog
));
1040 gtk_widget_destroy(error_dialog
);
1044 *size
= file_stat
.st_size
; // PRLW: is off_t = uint64_t, size is size_t, MD
1045 *buffer
= (gchar
*) g_malloc(*size
);
1046 fread(*buffer
, *size
, 1, fp
);
1052 dasher_editor_internal_unix_vfs_save_file(DasherEditor
*pSelf
, const char *myfilename
, gchar
*buffer
, gsize length
, bool append
) {
1054 GtkWidget
*error_dialog
;
1058 if(append
== true) {
1059 fp
= fopen(myfilename
, "a");
1066 fp
= fopen(myfilename
, "w");
1073 // 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);
1074 error_dialog
= gtk_message_dialog_new(NULL
, GTK_DIALOG_MODAL
, GTK_MESSAGE_ERROR
, GTK_BUTTONS_OK
, "Could not save the file \"%s\".\n", myfilename
);
1075 gtk_dialog_set_default_response(GTK_DIALOG(error_dialog
), GTK_RESPONSE_OK
);
1076 gtk_window_set_resizable(GTK_WINDOW(error_dialog
), FALSE
);
1077 gtk_dialog_run(GTK_DIALOG(error_dialog
));
1078 gtk_widget_destroy(error_dialog
);
1082 fwrite(buffer
, 1, length
, fp
);
1089 dasher_editor_internal_set_filename(DasherEditor
*pSelf
, const gchar
*szFilename
) {
1090 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
1092 if(pPrivate
->szFilename
)
1093 g_free((void *)pPrivate
->szFilename
);
1096 pPrivate
->szFilename
= g_strdup(szFilename
);
1098 pPrivate
->szFilename
= NULL
;
1100 g_signal_emit_by_name(G_OBJECT(pSelf
), "filename_changed", G_OBJECT(pSelf
), NULL
, NULL
);
1104 dasher_editor_edit_convert(DasherEditor
*pSelf
) {
1105 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
1107 if(!(pPrivate
->bConversionMode
))
1110 GtkTextIter sStartIter
;
1111 GtkTextIter sEndIter
;
1112 gtk_text_buffer_get_iter_at_offset(pPrivate
->pBuffer
, &sStartIter
, pPrivate
->iLastOffset
);
1113 gtk_text_buffer_get_iter_at_offset(pPrivate
->pBuffer
, &sEndIter
, -1);
1114 gtk_text_buffer_apply_tag(pPrivate
->pBuffer
, pPrivate
->pHiddenTag
, &sStartIter
, &sEndIter
);
1116 pPrivate
->iCurrentState
= 1;
1117 pPrivate
->iLastOffset
= gtk_text_buffer_get_char_count(pPrivate
->pBuffer
);
1121 dasher_editor_edit_protect(DasherEditor
*pSelf
) {
1122 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
1124 if(!(pPrivate
->bConversionMode
))
1127 pPrivate
->iCurrentState
= 0;
1128 pPrivate
->iLastOffset
= gtk_text_buffer_get_char_count(pPrivate
->pBuffer
);
1131 // Backward version of gtk_text_iter_forward_to_line_end.
1132 static void text_iter_backward_to_line_end(GtkTextIter
*pPos
) {
1133 // If already at the end of a paragraph back up to the first non
1134 // paragraph character.
1135 while (gtk_text_iter_ends_line(pPos
)) {
1136 if (!gtk_text_iter_backward_char(pPos
)) {
1140 while (!gtk_text_iter_ends_line(pPos
)) {
1141 if (!gtk_text_iter_backward_char (pPos
)) {
1142 return; // Start of the buffer.
1147 // If 'pPos' is on whitespace, advance it until the next non-whitespace character.
1148 static void skip_whitespace_forward(GtkTextIter
*pPos
) {
1149 while (g_unichar_isspace(gtk_text_iter_get_char(pPos
))) {
1150 if (!gtk_text_iter_forward_char (pPos
)) {
1151 return; // Start of the buffer.
1156 static void edit_find(bool bForwards
, Dasher::CControlManager::EditDistance iDist
, DasherEditorPrivate
*pPrivate
, GtkTextIter
*pPos
) {
1159 case Dasher::CControlManager::EDIT_CHAR
:
1160 gtk_text_iter_forward_char(pPos
);
1162 case Dasher::CControlManager::EDIT_WORD
:
1163 gtk_text_iter_forward_word_end(pPos
);
1164 skip_whitespace_forward(pPos
);
1166 case Dasher::CControlManager::EDIT_LINE
:
1167 if(!gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(pPrivate
->pTextView
), pPos
))
1169 gtk_text_view_forward_display_line (GTK_TEXT_VIEW(pPrivate
->pTextView
), pPos
);
1170 gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(pPrivate
->pTextView
), pPos
);
1173 case Dasher::CControlManager::EDIT_SENTENCE
:
1174 // Use the Pango sentence end rules.
1175 gtk_text_iter_forward_sentence_end(pPos
);
1176 skip_whitespace_forward(pPos
);
1178 case Dasher::CControlManager::EDIT_PARAGRAPH
:
1179 // Moves to the next \n, \r, \r\n or the Unicode paragraph separator character.
1180 gtk_text_iter_forward_to_line_end(pPos
);
1181 skip_whitespace_forward(pPos
);
1183 case Dasher::CControlManager::EDIT_FILE
:
1184 gtk_text_iter_forward_to_end(pPos
);
1186 case Dasher::CControlManager::EDIT_SELECTION
:
1192 case Dasher::CControlManager::EDIT_CHAR
:
1193 gtk_text_iter_backward_char(pPos
);
1195 case Dasher::CControlManager::EDIT_WORD
:
1196 gtk_text_iter_backward_word_start(pPos
);
1198 case Dasher::CControlManager::EDIT_LINE
:
1199 if(!gtk_text_view_backward_display_line_start(GTK_TEXT_VIEW(pPrivate
->pTextView
), pPos
))
1200 gtk_text_view_backward_display_line(GTK_TEXT_VIEW(pPrivate
->pTextView
), pPos
);
1202 case Dasher::CControlManager::EDIT_SENTENCE
:
1203 gtk_text_iter_backward_sentence_start(pPos
);
1205 case Dasher::CControlManager::EDIT_PARAGRAPH
:
1206 // Moves to the previous \n, \r, \r\n or the Unicode paragraph separator character.
1207 text_iter_backward_to_line_end(pPos
);
1209 case Dasher::CControlManager::EDIT_FILE
:
1210 gtk_text_buffer_get_start_iter(pPrivate
->pBuffer
, pPos
);
1212 case Dasher::CControlManager::EDIT_SELECTION
:
1219 dasher_editor_ctrl_delete(DasherEditor
*pSelf
, bool bForwards
, Dasher::CControlManager::EditDistance iDist
) {
1220 // XXX or return dasher_editor_get_offset?
1221 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
1223 GtkTextIter sPosStart
;
1224 GtkTextIter sPosEnd
;
1226 gtk_text_buffer_get_iter_at_mark(pPrivate
->pBuffer
, &sPosStart
, gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
1228 sPosEnd
= sPosStart
;
1229 edit_find(bForwards
, iDist
, pPrivate
, &sPosStart
);
1230 gint iRet
= gtk_text_iter_get_offset(&sPosStart
);
1231 pPrivate
->bInControlAction
= true;
1232 gtk_text_buffer_delete(pPrivate
->pBuffer
, &sPosStart
, &sPosEnd
);
1233 pPrivate
->bInControlAction
= false;
1234 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(pPrivate
->pTextView
), gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
1239 dasher_editor_ctrl_move(DasherEditor
*pSelf
, bool bForwards
, Dasher::CControlManager::EditDistance iDist
) {
1240 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
1242 if (isdirect(pPrivate
->pAppSettings
)) {
1243 dasher_editor_external_move(pSelf
, bForwards
, iDist
);
1248 gtk_text_buffer_get_iter_at_mark(pPrivate
->pBuffer
, &sPos
, gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
1250 edit_find(bForwards
, iDist
, pPrivate
, &sPos
);
1251 gint iRet
= gtk_text_iter_get_offset(&sPos
);
1252 pPrivate
->bInControlAction
= true;
1253 gtk_text_buffer_place_cursor(pPrivate
->pBuffer
, &sPos
);
1254 pPrivate
->bInControlAction
= false;
1255 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(pPrivate
->pTextView
), gtk_text_buffer_get_insert(pPrivate
->pBuffer
));
1261 dasher_editor_internal_new_buffer(DasherEditor
*pSelf
, const gchar
*szFilename
) {
1262 /* TODO: eventually rewrite this without references to external functions */
1265 dasher_editor_internal_open(pSelf
, szFilename
);
1268 dasher_editor_internal_generate_filename(pSelf
);
1269 dasher_editor_clear(pSelf
);
1272 // g_signal_emit_by_name(G_OBJECT(pSelf), "buffer_changed", G_OBJECT(pSelf), NULL, NULL);
1276 dasher_editor_handle_parameter_change(DasherEditor
*pSelf
, gint iParameter
) {
1277 DasherEditorPrivate
*pPrivate
= DASHER_EDITOR_GET_PRIVATE(pSelf
);
1278 switch(iParameter
) {
1279 case APP_SP_EDIT_FONT
:
1280 dasher_editor_internal_handle_font(pSelf
,
1281 pPrivate
->pAppSettings
->GetString(APP_SP_EDIT_FONT
).c_str());
1286 /* Callback Functions */
1289 delete_children_callback(GtkWidget
*pWidget
, gpointer pUserData
) {
1290 gtk_widget_destroy(pWidget
);
1294 main_window_realized(DasherMain
*pMain
, gpointer pUserData
) {
1297 extern "C" void mark_set_handler(GtkWidget
*widget
, GtkTextIter
*pIter
, GtkTextMark
*pMark
, gpointer pUserData
) {
1298 dasher_editor_internal_mark_changed(DASHER_EDITOR(pUserData
), pIter
, pMark
);