1 /* $Id$ */
2 static char const _copyright[] =
3 "Copyright © 2006-2016 Pierre Pronchery <khorben@defora.org>";
4 /* This file is part of DeforaOS Desktop Editor */
5 static char const _license[] =
6 "All rights reserved.\n"
7 "\n"
8 "Redistribution and use in source and binary forms, with or without\n"
9 "modification, are permitted provided that the following conditions are\n"
10 "met:\n"
11 "\n"
12 "1. Redistributions of source code must retain the above copyright\n"
13 " notice, this list of conditions and the following disclaimer.\n"
14 "\n"
15 "2. Redistributions in binary form must reproduce the above copyright\n"
16 " notice, this list of conditions and the following disclaimer in the\n"
17 " documentation and/or other materials provided with the distribution.\n"
18 "\n"
19 "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n"
20 "IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n"
21 "TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n"
22 "PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
23 "HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
24 "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n"
25 "TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n"
26 "PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n"
27 "LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n"
28 "NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n"
29 "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
30 /* TODO:
31 * - add a "Back" button to the "Find" dialog
32 * - add a "Replace" dialog
33 * - consider using GtkSourceView also/instead */
34
35
36
37 #include <sys/stat.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <limits.h>
41 #include <errno.h>
42 #include <math.h>
43 #include <libintl.h>
44 #include <gdk/gdkkeysyms.h>
45 #include <Desktop.h>
46 #include "callbacks.h"
47 #include "editor.h"
48 #include "../config.h"
49 #define _(string) gettext(string)
50 #define N_(string) (string)
51
52
53 /* Editor */
54 /* private */
55 /* constants */
56 #ifndef PROGNAME
57 # define PROGNAME "editor"
58 #endif
59 #define EDITOR_CONFIG_FILE ".editor"
60 #define EDITOR_DEFAULT_FONT "Monospace 9"
61
62
63 /* types */
64 struct _Editor
65 {
66 EditorPrefs prefs;
67 gchar * filename;
68 size_t search;
69
70 Config * config;
71
72 /* widgets */
73 GtkWidget * window;
74 PangoFontDescription * bold;
75 GtkWidget * view;
76 GtkWidget * statusbar;
77 guint statusbar_id;
78 #if GTK_CHECK_VERSION(2, 18, 0)
79 /* infobar */
80 GtkWidget * infobar;
81 GtkWidget * infobar_label;
82 #endif
83 /* preferences */
84 GtkWidget * pr_window;
85 GtkWidget * pr_font;
86 GtkWidget * pr_wrap;
87 /* find */
88 GtkWidget * fi_dialog;
89 GtkListStore * fi_store;
90 GtkWidget * fi_text;
91 GtkWidget * fi_entry;
92 GtkWidget * fi_case;
93 GtkWidget * fi_wrap;
94 /* about */
95 GtkWidget * ab_window;
96
97 /* printing */
98 PangoFontDescription * font;
99 double font_size;
100 double line_space;
101 GtkTextIter iter;
102 guint line_count;
103 };
104
105
106 /* variables */
107 static char const * _authors[] =
108 {
109 "Pierre Pronchery <khorben@defora.org>",
110 NULL
111 };
112
113 #ifdef EMBEDDED
114 static const DesktopAccel _editor_accel[] =
115 {
116 { G_CALLBACK(on_close), GDK_CONTROL_MASK, GDK_KEY_W },
117 { G_CALLBACK(on_new), GDK_CONTROL_MASK, GDK_KEY_N },
118 { G_CALLBACK(on_open), GDK_CONTROL_MASK, GDK_KEY_O },
119 { G_CALLBACK(on_save), GDK_CONTROL_MASK, GDK_KEY_S },
120 { G_CALLBACK(on_save_as), GDK_CONTROL_MASK | GDK_SHIFT_MASK,
121 GDK_KEY_S },
122 { NULL, 0, 0 }
123 };
124 #endif
125
126 #ifndef EMBEDDED
127 static const DesktopMenu _editor_menu_file[] =
128 {
129 { N_("_New"), G_CALLBACK(on_file_new), GTK_STOCK_NEW, GDK_CONTROL_MASK,
130 GDK_KEY_N },
131 { N_("_Open"), G_CALLBACK(on_file_open), GTK_STOCK_OPEN,
132 GDK_CONTROL_MASK, GDK_KEY_O },
133 { "", NULL, NULL, 0, 0 },
134 { N_("_Save"), G_CALLBACK(on_file_save), GTK_STOCK_SAVE,
135 GDK_CONTROL_MASK, GDK_KEY_S },
136 { N_("Save _As..."), G_CALLBACK(on_file_save_as), GTK_STOCK_SAVE_AS,
137 GDK_CONTROL_MASK | GDK_SHIFT_MASK, GDK_KEY_S },
138 { "", NULL, NULL, 0, 0 },
139 { N_("_Print"), G_CALLBACK(on_file_print),
140 GTK_STOCK_PRINT, GDK_CONTROL_MASK, GDK_KEY_P },
141 { "", NULL, NULL, 0, 0 },
142 { N_("Pr_operties"), G_CALLBACK(on_file_properties),
143 GTK_STOCK_PROPERTIES, GDK_MOD1_MASK, GDK_KEY_Return },
144 { "", NULL, NULL, 0, 0 },
145 { N_("_Close"), G_CALLBACK(on_file_close), GTK_STOCK_CLOSE, 0, 0 },
146 { NULL, NULL, NULL, 0, 0 }
147 };
148
149 static const DesktopMenu _editor_menu_file_filter[] =
150 {
151 { N_("_Save"), G_CALLBACK(on_file_save), GTK_STOCK_SAVE,
152 GDK_CONTROL_MASK, GDK_KEY_S },
153 { "", NULL, NULL, 0, 0 },
154 { N_("_Print"), G_CALLBACK(on_file_print),
155 GTK_STOCK_PRINT, GDK_CONTROL_MASK, GDK_KEY_P },
156 { "", NULL, NULL, 0, 0 },
157 { N_("Pr_operties"), G_CALLBACK(on_file_properties),
158 GTK_STOCK_PROPERTIES, GDK_MOD1_MASK, GDK_KEY_Return },
159 { "", NULL, NULL, 0, 0 },
160 { N_("_Close"), G_CALLBACK(on_file_close), GTK_STOCK_CLOSE, 0, 0 },
161 { NULL, NULL, NULL, 0, 0 }
162 };
163
164 static const DesktopMenu _editor_menu_edit[] =
165 {
166 { N_("Cu_t"), G_CALLBACK(on_edit_cut), GTK_STOCK_CUT, GDK_CONTROL_MASK,
167 GDK_KEY_X },
168 { N_("_Copy"), G_CALLBACK(on_edit_copy), GTK_STOCK_COPY,
169 GDK_CONTROL_MASK, GDK_KEY_C },
170 { N_("_Paste"), G_CALLBACK(on_edit_paste), GTK_STOCK_PASTE,
171 GDK_CONTROL_MASK, GDK_KEY_V },
172 { "", NULL, NULL, 0, 0 },
173 { N_("Select _All"), G_CALLBACK(on_edit_select_all),
174 #if GTK_CHECK_VERSION(2, 10, 0)
175 GTK_STOCK_SELECT_ALL,
176 #else
177 "edit-select-all",
178 #endif
179 GDK_CONTROL_MASK, GDK_KEY_A },
180 { N_("_Unselect all"), G_CALLBACK(on_edit_unselect_all), NULL, 0, 0 },
181 { "", NULL, NULL, 0, 0 },
182 { N_("_Find"), G_CALLBACK(on_edit_find), GTK_STOCK_FIND,
183 GDK_CONTROL_MASK, GDK_KEY_F },
184 { "", NULL, NULL, 0, 0 },
185 { N_("_Preferences"), G_CALLBACK(on_edit_preferences),
186 GTK_STOCK_PREFERENCES, 0, 0 },
187 { NULL, NULL, NULL, 0, 0 }
188 };
189
190 static const DesktopMenu _editor_menu_insert[] =
191 {
192 { N_("_File..."), G_CALLBACK(on_insert_file), "insert-text", 0, 0 },
193 { NULL, NULL, NULL, 0, 0 }
194 };
195
196 static const DesktopMenu _editor_menu_help[] =
197 {
198 { N_("_Contents"), G_CALLBACK(on_help_contents), "help-contents",
199 0, GDK_KEY_F1 },
200 { N_("_About"), G_CALLBACK(on_help_about),
201 #if GTK_CHECK_VERSION(2, 6, 0)
202 GTK_STOCK_ABOUT, 0, 0 },
203 #else
204 NULL, 0, 0 },
205 #endif
206 { NULL, NULL, NULL, 0, 0 }
207 };
208
209 static const DesktopMenubar _editor_menubar[] =
210 {
211 { N_("_File"), _editor_menu_file },
212 { N_("_Edit"), _editor_menu_edit },
213 { N_("_Insert"), _editor_menu_insert },
214 { N_("_Help"), _editor_menu_help },
215 { NULL, NULL }
216 };
217
218 static const DesktopMenubar _editor_menubar_filter[] =
219 {
220 { N_("_File"), _editor_menu_file_filter },
221 { N_("_Edit"), _editor_menu_edit },
222 { N_("_Insert"), _editor_menu_insert },
223 { N_("_Help"), _editor_menu_help },
224 { NULL, NULL }
225 };
226 #endif
227
228 static DesktopToolbar _editor_toolbar[] =
229 {
230 { N_("New"), G_CALLBACK(on_new), GTK_STOCK_NEW, 0, 0, NULL },
231 { N_("Open"), G_CALLBACK(on_open), GTK_STOCK_OPEN, 0, 0, NULL },
232 { "", NULL, NULL, 0, 0, NULL },
233 { N_("Save"), G_CALLBACK(on_save), GTK_STOCK_SAVE, 0, 0, NULL },
234 { N_("Save as"), G_CALLBACK(on_save_as), GTK_STOCK_SAVE_AS, 0, 0,
235 NULL },
236 { "", NULL, NULL, 0, 0, NULL },
237 { N_("Cut"), G_CALLBACK(on_cut), GTK_STOCK_CUT, 0, 0, NULL },
238 { N_("Copy"), G_CALLBACK(on_copy), GTK_STOCK_COPY, 0, 0, NULL },
239 { N_("Paste"), G_CALLBACK(on_paste), GTK_STOCK_PASTE, 0, 0, NULL },
240 #ifdef EMBEDDED
241 { "", NULL, NULL, 0, 0, NULL },
242 { N_("Print"), G_CALLBACK(on_print), GTK_STOCK_PREFERENCES,
243 GDK_CONTROL_MASK, GDK_KEY_P, NULL },
244 { "", NULL, NULL, 0, 0, NULL },
245 { N_("Find"), G_CALLBACK(on_find), GTK_STOCK_FIND, GDK_CONTROL_MASK,
246 GDK_KEY_F, NULL },
247 { "", NULL, NULL, 0, 0, NULL },
248 { N_("Preferences"), G_CALLBACK(on_preferences), GTK_STOCK_PREFERENCES,
249 0, 0, NULL },
250 { N_("Properties"), G_CALLBACK(on_properties), GTK_STOCK_PROPERTIES,
251 GDK_MOD1_MASK, GDK_KEY_Return, NULL },
252 { "", NULL, NULL, 0, 0, NULL },
253 { N_("Help"), G_CALLBACK(on_help_contents), "help-contents",
254 0, GDK_KEY_F1, NULL },
255 #endif
256 { NULL, NULL, NULL, 0, 0, NULL }
257 };
258
259 static DesktopToolbar _editor_toolbar_filter[] =
260 {
261 { N_("Save"), G_CALLBACK(on_save), GTK_STOCK_SAVE, 0, 0, NULL },
262 { "", NULL, NULL, 0, 0, NULL },
263 { N_("Cut"), G_CALLBACK(on_cut), GTK_STOCK_CUT, 0, 0, NULL },
264 { N_("Copy"), G_CALLBACK(on_copy), GTK_STOCK_COPY, 0, 0, NULL },
265 { N_("Paste"), G_CALLBACK(on_paste), GTK_STOCK_PASTE, 0, 0, NULL },
266 #ifdef EMBEDDED
267 { "", NULL, NULL, 0, 0, NULL },
268 { N_("Find"), G_CALLBACK(on_find), GTK_STOCK_FIND, GDK_CONTROL_MASK,
269 GDK_KEY_F, NULL },
270 { "", NULL, NULL, 0, 0, NULL },
271 { N_("Preferences"), G_CALLBACK(on_preferences), GTK_STOCK_PREFERENCES,
272 0, 0, NULL },
273 { N_("Properties"), G_CALLBACK(on_properties), GTK_STOCK_PROPERTIES,
274 GDK_MOD1_MASK, GDK_KEY_Return, NULL },
275 { "", NULL, NULL, 0, 0, NULL },
276 { N_("Help"), G_CALLBACK(on_help_contents), "help-contents",
277 0, GDK_KEY_F1, NULL },
278 #endif
279 { NULL, NULL, NULL, 0, 0, NULL }
280 };
281
282 static struct
283 {
284 char const * name;
285 GtkWrapMode wrap;
286 } _editor_wrap[] =
287 {
288 { N_("none"), GTK_WRAP_NONE },
289 { N_("characters"), GTK_WRAP_CHAR },
290 { N_("words"), GTK_WRAP_WORD },
291 { N_("words then characters"), GTK_WRAP_WORD_CHAR }
292 };
293
294
295 /* prototypes */
296 static int _editor_set_filename(Editor * editor, char const * filename);
297 static void _editor_set_status(Editor * editor, char const * status);
298
299 static char * _editor_config_filename(void);
300 static gboolean _editor_find(Editor * editor, char const * text,
301 gboolean sensitive, gboolean wrap);
302 static void _editor_refresh_title(Editor * editor);
303
304 /* callbacks */
305 #if GTK_CHECK_VERSION(2, 16, 0)
306 static void _editor_on_find_clear(gpointer data);
307 #endif
308 static void _editor_on_find_clicked(gpointer data);
309 static void _editor_on_find_hide(gpointer data);
310 static void _editor_on_modified(GtkTextBuffer * tbuf, gpointer data);
311
312 /* helpers */
313 static void _helper_file_dialog_filters(GtkWidget * dialog);
314
315
316 /* public */
317 /* functions */
318 /* editor_new */
editor_new(EditorPrefs * prefs)319 Editor * editor_new(EditorPrefs * prefs)
320 {
321 Editor * editor;
322 GtkAccelGroup * group;
323 GtkWidget * vbox;
324 GtkWidget * hbox;
325 GtkWidget * widget;
326 GtkTextBuffer * tbuf;
327
328 if((editor = object_new(sizeof(*editor))) == NULL)
329 return NULL;
330 if(prefs != NULL)
331 editor->prefs = *prefs;
332 else
333 memset(&editor->prefs, 0, sizeof(editor->prefs));
334 editor->config = config_new();
335 editor->window = NULL;
336 if(editor->config == NULL)
337 {
338 editor_delete(editor);
339 return NULL;
340 }
341 editor->filename = NULL;
342 editor->search = 0;
343 editor_config_load(editor);
344 /* widgets */
345 group = gtk_accel_group_new();
346 editor->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
347 gtk_window_add_accel_group(GTK_WINDOW(editor->window), group);
348 g_object_unref(group);
349 gtk_window_set_default_size(GTK_WINDOW(editor->window), 600, 400);
350 _editor_refresh_title(editor);
351 #if GTK_CHECK_VERSION(2, 6, 0)
352 gtk_window_set_icon_name(GTK_WINDOW(editor->window), "text-editor");
353 #endif
354 g_signal_connect_swapped(G_OBJECT(editor->window), "delete-event",
355 G_CALLBACK(on_closex), editor);
356 editor->bold = pango_font_description_new();
357 pango_font_description_set_weight(editor->bold, PANGO_WEIGHT_BOLD);
358 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
359 /* menubar */
360 #ifndef EMBEDDED
361 widget = desktop_menubar_create(editor->prefs.filter
362 ? _editor_menubar_filter : _editor_menubar, editor,
363 group);
364 gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
365 #else
366 desktop_accel_create(_editor_accel, editor, group);
367 #endif
368 /* toolbar */
369 widget = desktop_toolbar_create(editor->prefs.filter
370 ? _editor_toolbar_filter : _editor_toolbar, editor,
371 group);
372 gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
373 #if GTK_CHECK_VERSION(2, 18, 0)
374 /* infobar */
375 editor->infobar = gtk_info_bar_new_with_buttons(GTK_STOCK_CLOSE,
376 GTK_RESPONSE_CLOSE, NULL);
377 gtk_info_bar_set_message_type(GTK_INFO_BAR(editor->infobar),
378 GTK_MESSAGE_ERROR);
379 g_signal_connect(editor->infobar, "close", G_CALLBACK(gtk_widget_hide),
380 NULL);
381 g_signal_connect(editor->infobar, "response", G_CALLBACK(
382 gtk_widget_hide), NULL);
383 widget = gtk_info_bar_get_content_area(GTK_INFO_BAR(editor->infobar));
384 editor->infobar_label = gtk_label_new(NULL);
385 gtk_widget_show(editor->infobar_label);
386 gtk_box_pack_start(GTK_BOX(widget), editor->infobar_label, TRUE, TRUE,
387 0);
388 gtk_widget_set_no_show_all(editor->infobar, TRUE);
389 gtk_box_pack_start(GTK_BOX(vbox), editor->infobar, FALSE, TRUE, 0);
390 #endif
391 /* view */
392 widget = gtk_scrolled_window_new(NULL, NULL);
393 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
394 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
395 editor->view = gtk_text_view_new();
396 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
397 g_signal_connect(tbuf, "modified-changed", G_CALLBACK(
398 _editor_on_modified), editor);
399 editor_set_font(editor, editor_get_font(editor));
400 editor_set_wrap_mode(editor, editor_get_wrap_mode(editor));
401 gtk_container_add(GTK_CONTAINER(widget), editor->view);
402 gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0);
403 /* find */
404 editor->fi_dialog = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
405 hbox = editor->fi_dialog;
406 gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
407 widget = gtk_label_new(_("Find:"));
408 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
409 editor->fi_store = gtk_list_store_new(1, G_TYPE_STRING);
410 #if GTK_CHECK_VERSION(2, 24, 0)
411 editor->fi_text = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(
412 editor->fi_store));
413 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(editor->fi_text), 0);
414 #else
415 editor->fi_text = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(
416 editor->fi_store), 0);
417 #endif
418 editor->fi_entry = gtk_bin_get_child(GTK_BIN(editor->fi_text));
419 g_signal_connect_swapped(editor->fi_entry, "activate", G_CALLBACK(
420 _editor_on_find_clicked), editor);
421 #if GTK_CHECK_VERSION(2, 16, 0)
422 gtk_entry_set_icon_from_stock(GTK_ENTRY(editor->fi_entry),
423 GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
424 g_signal_connect_swapped(editor->fi_entry, "icon-release", G_CALLBACK(
425 _editor_on_find_clear), editor);
426 #endif
427 gtk_box_pack_start(GTK_BOX(hbox), editor->fi_text, FALSE, TRUE, 0);
428 editor->fi_case = gtk_check_button_new_with_label(_("Case-sensitive"));
429 gtk_box_pack_start(GTK_BOX(hbox), editor->fi_case, FALSE, TRUE, 0);
430 editor->fi_wrap = gtk_check_button_new_with_label(_("Wrap"));
431 gtk_box_pack_start(GTK_BOX(hbox), editor->fi_wrap, FALSE, TRUE, 0);
432 widget = gtk_button_new_from_stock(GTK_STOCK_FIND);
433 g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
434 _editor_on_find_clicked), editor);
435 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
436 widget = gtk_button_new();
437 gtk_button_set_image(GTK_BUTTON(widget), gtk_image_new_from_stock(
438 GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON));
439 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
440 g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
441 _editor_on_find_hide), editor);
442 gtk_box_pack_end(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
443 gtk_widget_show_all(hbox);
444 gtk_widget_hide(hbox);
445 gtk_widget_set_no_show_all(hbox, TRUE);
446 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
447 /* statusbar */
448 editor->statusbar = gtk_statusbar_new();
449 editor->statusbar_id = 0;
450 _editor_set_status(editor, _("Ready"));
451 gtk_box_pack_end(GTK_BOX(vbox), editor->statusbar, FALSE, TRUE, 0);
452 /* preferences */
453 editor->pr_window = NULL;
454 /* about */
455 editor->ab_window = NULL;
456 gtk_container_add(GTK_CONTAINER(editor->window), vbox);
457 gtk_window_set_focus(GTK_WINDOW(editor->window), editor->view);
458 gtk_widget_show_all(editor->window);
459 return editor;
460 }
461
462
463 /* editor_delete */
editor_delete(Editor * editor)464 void editor_delete(Editor * editor)
465 {
466 #ifdef DEBUG
467 fprintf(stderr, "DEBUG: %s()\n", __func__);
468 #endif
469 if(editor->window != NULL)
470 gtk_widget_destroy(editor->window);
471 if(editor->config != NULL)
472 config_delete(editor->config);
473 pango_font_description_free(editor->bold);
474 object_delete(editor);
475 }
476
477
478 /* accessors */
479 /* editor_get_font */
editor_get_font(Editor * editor)480 char const * editor_get_font(Editor * editor)
481 {
482 char const * p;
483 char * q;
484 GtkSettings * settings;
485 PangoFontDescription * desc;
486
487 if((p = config_get(editor->config, NULL, "font")) != NULL)
488 return p;
489 settings = gtk_settings_get_default();
490 g_object_get(G_OBJECT(settings), "gtk-font-name", &q, NULL);
491 if(q != NULL)
492 {
493 desc = pango_font_description_from_string(q);
494 g_free(q);
495 pango_font_description_set_family(desc, "monospace");
496 q = pango_font_description_to_string(desc);
497 config_set(editor->config, NULL, "font", q);
498 g_free(q);
499 pango_font_description_free(desc);
500 if((p = config_get(editor->config, NULL, "font")) != NULL)
501 return p;
502 }
503 return EDITOR_DEFAULT_FONT;
504 }
505
506
507 /* editor_get_wrap_mode */
editor_get_wrap_mode(Editor * editor)508 GtkWrapMode editor_get_wrap_mode(Editor * editor)
509 {
510 char const * p;
511 unsigned int i;
512
513 if((p = config_get(editor->config, NULL, "wrap")) != NULL)
514 {
515 i = strtoul(p, NULL, 10);
516 if(i < sizeof(_editor_wrap) / sizeof(*_editor_wrap))
517 return _editor_wrap[i].wrap;
518 }
519 return GTK_WRAP_WORD_CHAR;
520 }
521
522
523 /* editor_set_font */
editor_set_font(Editor * editor,char const * font)524 void editor_set_font(Editor * editor, char const * font)
525 {
526 PangoFontDescription * desc;
527
528 desc = pango_font_description_from_string(font);
529 gtk_widget_override_font(editor->view, desc);
530 pango_font_description_free(desc);
531 config_set(editor->config, NULL, "font", font);
532 }
533
534
535 /* editor_set_wrap_mode */
editor_set_wrap_mode(Editor * editor,GtkWrapMode wrap)536 void editor_set_wrap_mode(Editor * editor, GtkWrapMode wrap)
537 {
538 unsigned int i;
539 char buf[10];
540
541 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(editor->view),
542 wrap);
543 for(i = 0; i < sizeof(_editor_wrap) / sizeof(*_editor_wrap); i++)
544 if(_editor_wrap[i].wrap == wrap)
545 {
546 snprintf(buf, sizeof(buf), "%u", i);
547 config_set(editor->config, NULL, "wrap", buf);
548 return;
549 }
550 }
551
552
553 /* useful */
554 /* editor_about */
555 static gboolean _about_on_closex(gpointer data);
556
editor_about(Editor * editor)557 void editor_about(Editor * editor)
558 {
559 if(editor->ab_window != NULL)
560 {
561 gtk_widget_show(editor->ab_window);
562 return;
563 }
564 editor->ab_window = desktop_about_dialog_new();
565 gtk_window_set_transient_for(GTK_WINDOW(editor->ab_window),
566 GTK_WINDOW(editor->window));
567 desktop_about_dialog_set_authors(editor->ab_window, _authors);
568 desktop_about_dialog_set_comments(editor->ab_window,
569 _("Text editor for the DeforaOS desktop"));
570 desktop_about_dialog_set_copyright(editor->ab_window, _copyright);
571 desktop_about_dialog_set_logo_icon_name(editor->ab_window,
572 "text-editor");
573 desktop_about_dialog_set_license(editor->ab_window, _license);
574 desktop_about_dialog_set_name(editor->ab_window, PACKAGE);
575 desktop_about_dialog_set_translator_credits(editor->ab_window,
576 _("translator-credits"));
577 desktop_about_dialog_set_version(editor->ab_window, VERSION);
578 desktop_about_dialog_set_website(editor->ab_window,
579 "http://www.defora.org/");
580 g_signal_connect_swapped(G_OBJECT(editor->ab_window), "delete-event",
581 G_CALLBACK(_about_on_closex), editor);
582 gtk_widget_show(editor->ab_window);
583 }
584
_about_on_closex(gpointer data)585 static gboolean _about_on_closex(gpointer data)
586 {
587 Editor * editor = data;
588
589 gtk_widget_hide(editor->ab_window);
590 return TRUE;
591 }
592
593
594 /* editor_config_load */
editor_config_load(Editor * editor)595 int editor_config_load(Editor * editor)
596 {
597 int ret;
598 char * filename;
599
600 if((filename = _editor_config_filename()) == NULL)
601 return -1;
602 ret = config_load(editor->config, filename);
603 free(filename);
604 return ret;
605 }
606
607
608 /* editor_config_save */
editor_config_save(Editor * editor)609 int editor_config_save(Editor * editor)
610 {
611 int ret;
612 char * filename;
613
614 if((filename = _editor_config_filename()) == NULL)
615 return -1;
616 if((ret = config_save(editor->config, filename)) != 0)
617 editor_error(editor, _("Could not save the configuration"), 1);
618 free(filename);
619 return ret;
620 }
621
622
623 /* editor_confirm */
editor_confirm(Editor * editor,char const * message,...)624 int editor_confirm(Editor * editor, char const * message, ...)
625 {
626 GtkWidget * dialog;
627 va_list ap;
628 char const * action;
629 int res;
630
631 dialog = gtk_message_dialog_new(GTK_WINDOW(editor->window),
632 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
633 GTK_BUTTONS_NONE,
634 # if GTK_CHECK_VERSION(2, 6, 0)
635 "%s", _("Question"));
636 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
637 # endif
638 "%s", message);
639 va_start(ap, message);
640 while((action = va_arg(ap, char const *)) != NULL)
641 gtk_dialog_add_button(GTK_DIALOG(dialog),
642 action, va_arg(ap, int));
643 va_end(ap);
644 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
645 res = gtk_dialog_run(GTK_DIALOG(dialog));
646 gtk_widget_destroy(dialog);
647 return res;
648 }
649
650
651 /* editor_error */
652 static int _error_text(char const * message, int ret);
653
editor_error(Editor * editor,char const * message,int ret)654 int editor_error(Editor * editor, char const * message, int ret)
655 {
656 #if GTK_CHECK_VERSION(2, 18, 0)
657 if(editor == NULL)
658 return _error_text(message, ret);
659 gtk_label_set_text(GTK_LABEL(editor->infobar_label), message);
660 gtk_widget_show(editor->infobar);
661 #else
662 GtkWidget * dialog;
663
664 if(editor == NULL)
665 return _error_text(message, ret);
666 dialog = gtk_message_dialog_new(GTK_WINDOW(editor->window),
667 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
668 GTK_BUTTONS_CLOSE,
669 # if GTK_CHECK_VERSION(2, 6, 0)
670 "%s", _("Error"));
671 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
672 # endif
673 "%s", message);
674 gtk_window_set_title(GTK_WINDOW(dialog), _("Error"));
675 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(
676 gtk_widget_destroy), NULL);
677 gtk_widget_show(dialog);
678 #endif
679 return ret;
680 }
681
_error_text(char const * message,int ret)682 static int _error_text(char const * message, int ret)
683 {
684 fprintf(stderr, "%s: %s\n", PROGNAME, message);
685 return ret;
686 }
687
688
689 /* editor_close */
editor_close(Editor * editor)690 int editor_close(Editor * editor)
691 {
692 int res;
693
694 #ifdef DEBUG
695 fprintf(stderr, "DEBUG: %s()\n", __func__);
696 #endif
697 if(gtk_text_buffer_get_modified(gtk_text_view_get_buffer(GTK_TEXT_VIEW(
698 editor->view))) == FALSE)
699 {
700 gtk_widget_hide(editor->window);
701 gtk_main_quit();
702 return 0;
703 }
704 res = editor_confirm(editor, _("There are unsaved changes.\n"
705 "Discard or save them?"),
706 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
707 GTK_STOCK_DISCARD, GTK_RESPONSE_REJECT,
708 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
709 if(res == GTK_RESPONSE_CANCEL || res == GTK_RESPONSE_DELETE_EVENT)
710 return 1;
711 else if(res == GTK_RESPONSE_ACCEPT && editor_save(editor) != TRUE)
712 return 1;
713 gtk_widget_hide(editor->window);
714 gtk_main_quit();
715 return 0;
716 }
717
718
719 /* editor_copy */
editor_copy(Editor * editor)720 void editor_copy(Editor * editor)
721 {
722 GtkTextBuffer * buffer;
723 GtkClipboard * clipboard;
724
725 if(gtk_window_get_focus(GTK_WINDOW(editor->window))
726 == editor->fi_entry)
727 {
728 gtk_editable_copy_clipboard(GTK_EDITABLE(editor->fi_entry));
729 return;
730 }
731 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
732 clipboard = gtk_widget_get_clipboard(editor->view,
733 GDK_SELECTION_CLIPBOARD);
734 gtk_text_buffer_copy_clipboard(buffer, clipboard);
735 }
736
737
738 /* editor_cut */
editor_cut(Editor * editor)739 void editor_cut(Editor * editor)
740 {
741 GtkTextBuffer * buffer;
742 GtkClipboard * clipboard;
743
744 if(gtk_window_get_focus(GTK_WINDOW(editor->window))
745 == editor->fi_entry)
746 {
747 gtk_editable_cut_clipboard(GTK_EDITABLE(editor->fi_entry));
748 return;
749 }
750 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
751 clipboard = gtk_widget_get_clipboard(editor->view,
752 GDK_SELECTION_CLIPBOARD);
753 gtk_text_buffer_cut_clipboard(buffer, clipboard, TRUE);
754 }
755
756
757 /* editor_find */
editor_find(Editor * editor,char const * text)758 void editor_find(Editor * editor, char const * text)
759 {
760 gtk_widget_show(editor->fi_dialog);
761 if(text != NULL)
762 gtk_entry_set_text(GTK_ENTRY(editor->fi_entry), text);
763 gtk_widget_grab_focus(editor->fi_entry);
764 }
765
766
767 /* editor_insert_file */
editor_insert_file(Editor * editor,char const * filename)768 int editor_insert_file(Editor * editor, char const * filename)
769 {
770 int ret = 0;
771 FILE * fp;
772 GtkTextBuffer * tbuf;
773 char buf[BUFSIZ];
774 size_t len;
775 char * p;
776 size_t rlen;
777 size_t wlen;
778 GError * error = NULL;
779
780 /* FIXME use a GIOChannel instead (with a GtkDialog or GtkStatusBar) */
781 if((fp = fopen(filename, "r")) == NULL)
782 {
783 snprintf(buf, sizeof(buf), "%s: %s", filename, strerror(errno));
784 return -editor_error(editor, buf, 1);
785 }
786 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
787 while((len = fread(buf, sizeof(char), sizeof(buf), fp)) > 0)
788 {
789 #if 0
790 if((p = g_convert(buf, len, "UTF-8", "ISO-8859-15", &rlen, &wlen, NULL)) != NULL)
791 {
792 gtk_text_buffer_insert_at_cursor(tbuf, p, wlen);
793 g_free(p);
794 }
795 else
796 gtk_text_buffer_insert(tbuf, &iter, buf, len);
797 #else
798 if((p = g_locale_to_utf8(buf, len, &rlen, &wlen, &error))
799 != NULL)
800 /* FIXME may lose characters */
801 gtk_text_buffer_insert_at_cursor(tbuf, p, wlen);
802 else
803 {
804 editor_error(editor, error->message, 1);
805 g_error_free(error);
806 error = NULL;
807 gtk_text_buffer_insert_at_cursor(tbuf, buf, len);
808 }
809 #endif
810 }
811 if(ferror(fp))
812 {
813 snprintf(buf, sizeof(buf), "%s: %s", filename, strerror(errno));
814 ret = -editor_error(editor, buf, 1);
815 }
816 fclose(fp);
817 return ret;
818 }
819
820
821 /* editor_insert_file_dialog */
editor_insert_file_dialog(Editor * editor)822 int editor_insert_file_dialog(Editor * editor)
823 {
824 int ret;
825 GtkWidget * dialog;
826 GtkFileFilter * filter;
827 char * filename = NULL;
828
829 dialog = gtk_file_chooser_dialog_new(_("Insert file..."),
830 GTK_WINDOW(editor->window),
831 GTK_FILE_CHOOSER_ACTION_OPEN,
832 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
833 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
834 _helper_file_dialog_filters(dialog);
835 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
836 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
837 dialog));
838 gtk_widget_destroy(dialog);
839 if(filename == NULL)
840 return 0;
841 ret = editor_insert_file(editor, filename);
842 g_free(filename);
843 return ret;
844 }
845
846
847 /* editor_open */
editor_open(Editor * editor,char const * filename)848 int editor_open(Editor * editor, char const * filename)
849 {
850 GtkTextBuffer * tbuf;
851 int res;
852 GtkTextIter iter;
853
854 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
855 if(gtk_text_buffer_get_modified(tbuf) == TRUE)
856 {
857 res = editor_confirm(editor, _("There are unsaved changes.\n"
858 "Discard or save them?"),
859 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
860 #if GTK_CHECK_VERSION(2, 12, 0)
861 GTK_STOCK_DISCARD, GTK_RESPONSE_REJECT,
862 #else
863 _("Discard"), GTK_RESPONSE_REJECT,
864 #endif
865 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
866 NULL);
867 if(res == GTK_RESPONSE_ACCEPT && editor_save(editor) != TRUE)
868 return -1;
869 else if(res != GTK_RESPONSE_REJECT)
870 return 1;
871 }
872 gtk_text_buffer_set_text(tbuf, "", 0);
873 editor->search = 0;
874 if(filename == NULL)
875 {
876 g_free(editor->filename);
877 editor->filename = NULL;
878 _editor_refresh_title(editor);
879 gtk_text_buffer_set_modified(tbuf, FALSE);
880 return 0;
881 }
882 if((res = editor_insert_file(editor, filename)) != 0)
883 return res;
884 gtk_text_buffer_set_modified(tbuf, FALSE);
885 /* XXX may fail */
886 _editor_set_filename(editor, filename);
887 _editor_refresh_title(editor);
888 /* place the cursor back at the top of the file */
889 gtk_text_buffer_get_start_iter(tbuf, &iter);
890 gtk_text_buffer_place_cursor(tbuf, &iter);
891 return 0;
892 }
893
894
895 /* editor_open_dialog */
editor_open_dialog(Editor * editor)896 int editor_open_dialog(Editor * editor)
897 {
898 int ret;
899 GtkWidget * dialog;
900 GtkFileFilter * filter;
901 gchar * filename = NULL;
902
903 dialog = gtk_file_chooser_dialog_new(_("Open file..."),
904 GTK_WINDOW(editor->window),
905 GTK_FILE_CHOOSER_ACTION_OPEN,
906 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
907 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
908 _helper_file_dialog_filters(dialog);
909 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
910 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
911 dialog));
912 gtk_widget_destroy(dialog);
913 if(filename == NULL)
914 return 1;
915 ret = editor_open(editor, filename);
916 g_free(filename);
917 return ret;
918 }
919
920
921 /* editor_paste */
editor_paste(Editor * editor)922 void editor_paste(Editor * editor)
923 {
924 GtkTextBuffer * buffer;
925 GtkClipboard * clipboard;
926
927 if(gtk_window_get_focus(GTK_WINDOW(editor->window))
928 == editor->fi_entry)
929 {
930 gtk_editable_paste_clipboard(GTK_EDITABLE(editor->fi_entry));
931 return;
932 }
933 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
934 clipboard = gtk_widget_get_clipboard(editor->view,
935 GDK_SELECTION_CLIPBOARD);
936 gtk_text_buffer_paste_clipboard(buffer, clipboard, NULL, TRUE);
937 }
938
939
940 /* editor_print_dialog */
941 static void _print_dialog_on_begin_print(GtkPrintOperation * operation,
942 GtkPrintContext * context, gpointer data);
943 static void _print_dialog_on_done(GtkPrintOperation * operation,
944 GtkPrintOperationResult result, gpointer data);
945 static void _print_dialog_on_draw_page(GtkPrintOperation * operation,
946 GtkPrintContext * context, gint page, gpointer data);
947 static void _print_dialog_on_end_print(GtkPrintOperation * operation,
948 GtkPrintContext * context, gpointer data);
949 static gboolean _print_dialog_on_paginate(GtkPrintOperation * operation,
950 GtkPrintContext * context, gpointer data);
951
editor_print_dialog(Editor * editor)952 void editor_print_dialog(Editor * editor)
953 {
954 GtkPrintOperation * operation;
955 GtkPrintSettings * settings;
956 GError * error = NULL;
957
958 operation = gtk_print_operation_new();
959 gtk_print_operation_set_embed_page_setup(operation, TRUE);
960 gtk_print_operation_set_unit(operation, GTK_UNIT_POINTS);
961 gtk_print_operation_set_use_full_page(operation, FALSE);
962 g_signal_connect(operation, "begin-print", G_CALLBACK(
963 _print_dialog_on_begin_print), editor);
964 g_signal_connect(operation, "done", G_CALLBACK(_print_dialog_on_done),
965 editor);
966 g_signal_connect(operation, "draw-page", G_CALLBACK(
967 _print_dialog_on_draw_page), editor);
968 g_signal_connect(operation, "end-print", G_CALLBACK(
969 _print_dialog_on_end_print), editor);
970 g_signal_connect(operation, "paginate", G_CALLBACK(
971 _print_dialog_on_paginate), editor);
972 settings = gtk_print_settings_new();
973 gtk_print_operation_set_print_settings(operation, settings);
974 gtk_print_operation_run(operation,
975 GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
976 GTK_WINDOW(editor->window), &error);
977 g_object_unref(settings);
978 g_object_unref(operation);
979 if(error)
980 {
981 editor_error(editor, error->message, 1);
982 g_error_free(error);
983 }
984 }
985
_print_dialog_on_begin_print(GtkPrintOperation * operation,GtkPrintContext * context,gpointer data)986 static void _print_dialog_on_begin_print(GtkPrintOperation * operation,
987 GtkPrintContext * context, gpointer data)
988 {
989 const gint size = 10;
990 Editor * editor = data;
991 char const * font;
992
993 /* initialize the font */
994 font = editor_get_font(editor);
995 editor->font = pango_font_description_from_string(font);
996 pango_font_description_set_size(editor->font,
997 pango_units_from_double(size));
998 editor->font_size = size;
999 editor->line_space = 0.0;
1000 }
1001
_print_dialog_on_done(GtkPrintOperation * operation,GtkPrintOperationResult result,gpointer data)1002 static void _print_dialog_on_done(GtkPrintOperation * operation,
1003 GtkPrintOperationResult result, gpointer data)
1004 {
1005 Editor * editor = data;
1006 GError * error = NULL;
1007
1008 switch(result)
1009 {
1010 case GTK_PRINT_OPERATION_RESULT_ERROR:
1011 gtk_print_operation_get_error(operation, &error);
1012 editor_error(editor, error->message, 2);
1013 g_error_free(error);
1014 break;
1015 default:
1016 break;
1017 }
1018 }
1019
_print_dialog_on_draw_page(GtkPrintOperation * operation,GtkPrintContext * context,gint page,gpointer data)1020 static void _print_dialog_on_draw_page(GtkPrintOperation * operation,
1021 GtkPrintContext * context, gint page, gpointer data)
1022 {
1023 Editor * editor = data;
1024 GtkTextBuffer * tbuf;
1025 cairo_t * cairo;
1026 PangoLayout * layout;
1027 guint i;
1028 gboolean valid = TRUE;
1029 GtkTextIter end;
1030 gchar * p;
1031
1032 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
1033 cairo = gtk_print_context_get_cairo_context(context);
1034 layout = gtk_print_context_create_pango_layout(context);
1035 /* set the font */
1036 pango_layout_set_font_description(layout, editor->font);
1037 /* print the text */
1038 cairo_move_to(cairo, 0.0, 0.0);
1039 gtk_text_buffer_get_iter_at_line(tbuf, &editor->iter,
1040 editor->line_count * page);
1041 for(i = 0, valid = !gtk_text_iter_is_end(&editor->iter);
1042 i < editor->line_count && valid == TRUE;
1043 i++, valid = gtk_text_iter_forward_line(&editor->iter))
1044 {
1045 end = editor->iter;
1046 if(!gtk_text_iter_ends_line(&end))
1047 gtk_text_iter_forward_to_line_end(&end);
1048 p = gtk_text_buffer_get_text(tbuf, &editor->iter, &end, FALSE);
1049 /* FIXME the line may be too long */
1050 pango_layout_set_text(layout, p, -1);
1051 g_free(p);
1052 pango_cairo_show_layout(cairo, layout);
1053 cairo_rel_move_to(cairo, 0.0, editor->font_size
1054 + editor->line_space);
1055 }
1056 g_object_unref(layout);
1057 }
1058
_print_dialog_on_end_print(GtkPrintOperation * operation,GtkPrintContext * context,gpointer data)1059 static void _print_dialog_on_end_print(GtkPrintOperation * operation,
1060 GtkPrintContext * context, gpointer data)
1061 {
1062 Editor * editor = data;
1063
1064 pango_font_description_free(editor->font);
1065 gtk_text_view_set_editable(GTK_TEXT_VIEW(editor->view), TRUE);
1066 }
1067
_print_dialog_on_paginate(GtkPrintOperation * operation,GtkPrintContext * context,gpointer data)1068 static gboolean _print_dialog_on_paginate(GtkPrintOperation * operation,
1069 GtkPrintContext * context, gpointer data)
1070 {
1071 Editor * editor = data;
1072 GtkTextBuffer * tbuf;
1073 gint count;
1074 double height;
1075
1076 /* count the lines to print */
1077 gtk_text_view_set_editable(GTK_TEXT_VIEW(editor->view), FALSE);
1078 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
1079 count = gtk_text_buffer_get_line_count(tbuf);
1080 /* count the pages required */
1081 height = gtk_print_context_get_height(context);
1082 editor->line_count = floor(height / (editor->font_size
1083 + editor->line_space));
1084 gtk_print_operation_set_n_pages(operation,
1085 ((count - 1) / editor->line_count) + 1);
1086 return TRUE;
1087 }
1088
1089
1090 /* editor_save */
editor_save(Editor * editor)1091 gboolean editor_save(Editor * editor)
1092 {
1093 FILE * fp;
1094 GtkTextBuffer * tbuf;
1095 GtkTextIter start;
1096 GtkTextIter end;
1097 gchar * buf;
1098 size_t len;
1099
1100 if(editor->filename == NULL)
1101 return editor_save_as_dialog(editor);
1102 if((fp = fopen(editor->filename, "w")) == NULL)
1103 {
1104 buf = g_strdup_printf("%s: %s", editor->filename, strerror(
1105 errno));
1106 editor_error(editor, buf, 1);
1107 g_free(buf);
1108 return FALSE;
1109 }
1110 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
1111 /* XXX allocating the complete file is not optimal */
1112 gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(tbuf), &start);
1113 gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(tbuf), &end);
1114 buf = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(tbuf), &start, &end,
1115 FALSE);
1116 len = strlen(buf);
1117 if(fwrite(buf, sizeof(*buf), len, fp) != len)
1118 {
1119 g_free(buf);
1120 fclose(fp);
1121 editor_error(editor, _("Partial write"), 1);
1122 return FALSE;
1123 }
1124 g_free(buf);
1125 if(fclose(fp) != 0)
1126 {
1127 editor_error(editor, _("Partial write"), 1);
1128 return FALSE;
1129 }
1130 gtk_text_buffer_set_modified(GTK_TEXT_BUFFER(tbuf), FALSE);
1131 return TRUE;
1132 }
1133
1134
1135 /* editor_save_as */
editor_save_as(Editor * editor,char const * filename)1136 gboolean editor_save_as(Editor * editor, char const * filename)
1137 {
1138 struct stat st;
1139 GtkWidget * dialog;
1140 int res;
1141
1142 if(stat(filename, &st) == 0)
1143 {
1144 dialog = gtk_message_dialog_new(GTK_WINDOW(editor->window),
1145 GTK_DIALOG_MODAL
1146 | GTK_DIALOG_DESTROY_WITH_PARENT,
1147 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1148 #if GTK_CHECK_VERSION(2, 6, 0)
1149 "%s", _("Question"));
1150 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(
1151 dialog),
1152 #endif
1153 "%s",
1154 _("This file already exists. Overwrite?"));
1155 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
1156 res = gtk_dialog_run(GTK_DIALOG(dialog));
1157 gtk_widget_destroy(dialog);
1158 if(res == GTK_RESPONSE_NO)
1159 return FALSE;
1160 }
1161 if(_editor_set_filename(editor, filename) != 0
1162 || editor_save(editor) != TRUE)
1163 return FALSE;
1164 _editor_refresh_title(editor);
1165 return TRUE;
1166 }
1167
1168
1169 /* editor_save_as_dialog */
editor_save_as_dialog(Editor * editor)1170 gboolean editor_save_as_dialog(Editor * editor)
1171 {
1172 gboolean ret;
1173 GtkWidget * dialog;
1174 gchar * filename = NULL;
1175
1176 dialog = gtk_file_chooser_dialog_new(_("Save as..."),
1177 GTK_WINDOW(editor->window),
1178 GTK_FILE_CHOOSER_ACTION_SAVE,
1179 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1180 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
1181 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1182 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
1183 dialog));
1184 gtk_widget_destroy(dialog);
1185 if(filename == NULL)
1186 return FALSE;
1187 ret = editor_save_as(editor, filename);
1188 g_free(filename);
1189 return ret;
1190 }
1191
1192
1193 /* editor_select_all */
editor_select_all(Editor * editor)1194 void editor_select_all(Editor * editor)
1195 {
1196 GtkTextBuffer * tbuf;
1197 GtkTextIter start;
1198 GtkTextIter end;
1199
1200 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
1201 gtk_text_buffer_get_start_iter(tbuf, &start);
1202 gtk_text_buffer_get_end_iter(tbuf, &end);
1203 gtk_text_buffer_select_range(tbuf, &start, &end);
1204 }
1205
1206
1207 /* editor_show_preferences */
1208 static gboolean _preferences_on_closex(gpointer data);
1209 static void _preferences_on_response(GtkWidget * widget, gint response,
1210 gpointer data);
1211 static void _preferences_on_apply(gpointer data);
1212 static void _preferences_on_cancel(gpointer data);
1213 static void _preferences_on_ok(gpointer data);
1214
editor_show_preferences(Editor * editor,gboolean show)1215 void editor_show_preferences(Editor * editor, gboolean show)
1216 {
1217 GtkWidget * vbox;
1218 GtkWidget * hbox;
1219 GtkWidget * widget;
1220 GtkSizeGroup * group;
1221 size_t i;
1222
1223 if(editor->pr_window != NULL)
1224 {
1225 if(show)
1226 gtk_window_present(GTK_WINDOW(editor->pr_window));
1227 else
1228 gtk_widget_hide(editor->pr_window);
1229 return;
1230 }
1231 editor->pr_window = gtk_dialog_new_with_buttons(
1232 _("Text editor preferences"),
1233 GTK_WINDOW(editor->window),
1234 GTK_DIALOG_DESTROY_WITH_PARENT,
1235 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1236 GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
1237 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1238 gtk_window_set_resizable(GTK_WINDOW(editor->pr_window), FALSE);
1239 g_signal_connect_swapped(G_OBJECT(editor->pr_window), "delete-event",
1240 G_CALLBACK(_preferences_on_closex), editor);
1241 g_signal_connect(G_OBJECT(editor->pr_window), "response",
1242 G_CALLBACK(_preferences_on_response), editor);
1243 #if GTK_CHECK_VERSION(2, 14, 0)
1244 vbox = gtk_dialog_get_content_area(GTK_DIALOG(editor->pr_window));
1245 #else
1246 vbox = GTK_DIALOG(editor->pr_window)->vbox;
1247 #endif
1248 gtk_box_set_spacing(GTK_BOX(vbox), 4);
1249 group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1250 /* font */
1251 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1252 widget = gtk_label_new(_("Font:"));
1253 #if GTK_CHECK_VERSION(3, 0, 0)
1254 g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
1255 #else
1256 gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
1257 #endif
1258 gtk_size_group_add_widget(group, widget);
1259 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
1260 editor->pr_font = gtk_font_button_new();
1261 gtk_font_button_set_use_font(GTK_FONT_BUTTON(editor->pr_font), TRUE);
1262 gtk_box_pack_start(GTK_BOX(hbox), editor->pr_font, TRUE, TRUE, 0);
1263 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1264 /* wrap mode */
1265 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1266 widget = gtk_label_new(_("Wrap mode:"));
1267 #if GTK_CHECK_VERSION(3, 0, 0)
1268 g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
1269 #else
1270 gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
1271 #endif
1272 gtk_size_group_add_widget(group, widget);
1273 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
1274 #if GTK_CHECK_VERSION(2, 24, 0)
1275 editor->pr_wrap = gtk_combo_box_text_new();
1276 #else
1277 editor->pr_wrap = gtk_combo_box_new_text();
1278 #endif
1279 for(i = 0; i < sizeof(_editor_wrap) / sizeof(*_editor_wrap); i++)
1280 #if GTK_CHECK_VERSION(2, 24, 0)
1281 gtk_combo_box_text_append_text(
1282 GTK_COMBO_BOX_TEXT(editor->pr_wrap),
1283 _(_editor_wrap[i].name));
1284 #else
1285 gtk_combo_box_append_text(GTK_COMBO_BOX(editor->pr_wrap),
1286 _(_editor_wrap[i].name));
1287 #endif
1288 gtk_box_pack_start(GTK_BOX(hbox), editor->pr_wrap, TRUE, TRUE, 0);
1289 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1290 _preferences_on_cancel(editor);
1291 gtk_widget_show_all(vbox);
1292 if(show)
1293 gtk_widget_show(editor->pr_window);
1294 }
1295
_preferences_on_closex(gpointer data)1296 static gboolean _preferences_on_closex(gpointer data)
1297 {
1298 Editor * editor = data;
1299
1300 _preferences_on_cancel(editor);
1301 return TRUE;
1302 }
1303
_preferences_on_response(GtkWidget * widget,gint response,gpointer data)1304 static void _preferences_on_response(GtkWidget * widget, gint response,
1305 gpointer data)
1306 {
1307 Editor * editor = data;
1308
1309 if(response == GTK_RESPONSE_OK)
1310 _preferences_on_ok(editor);
1311 else if(response == GTK_RESPONSE_APPLY)
1312 _preferences_on_apply(editor);
1313 else if(response == GTK_RESPONSE_CANCEL)
1314 _preferences_on_cancel(editor);
1315 }
1316
_preferences_on_apply(gpointer data)1317 static void _preferences_on_apply(gpointer data)
1318 {
1319 Editor * editor = data;
1320 char const * font;
1321 size_t i;
1322
1323 font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(editor->pr_font));
1324 editor_set_font(editor, font);
1325 i = gtk_combo_box_get_active(GTK_COMBO_BOX(editor->pr_wrap));
1326 if(i < sizeof(_editor_wrap) / sizeof(*_editor_wrap))
1327 editor_set_wrap_mode(editor, _editor_wrap[i].wrap);
1328 }
1329
_preferences_on_cancel(gpointer data)1330 static void _preferences_on_cancel(gpointer data)
1331 {
1332 Editor * editor = data;
1333 char const * p;
1334 gint u = 0;
1335
1336 gtk_widget_hide(editor->pr_window);
1337 gtk_font_button_set_font_name(GTK_FONT_BUTTON(editor->pr_font),
1338 editor_get_font(editor));
1339 if((p = config_get(editor->config, NULL, "wrap")) != NULL)
1340 u = strtol(p, NULL, 10);
1341 gtk_combo_box_set_active(GTK_COMBO_BOX(editor->pr_wrap), u);
1342 }
1343
_preferences_on_ok(gpointer data)1344 static void _preferences_on_ok(gpointer data)
1345 {
1346 Editor * editor = data;
1347
1348 gtk_widget_hide(editor->pr_window);
1349 _preferences_on_apply(editor);
1350 editor_config_save(editor);
1351 }
1352
1353
1354 /* editor_show_properties */
1355 static GtkWidget * _properties_widget(Editor * editor, GtkSizeGroup * group,
1356 char const * label, GtkWidget * value);
1357
editor_show_properties(Editor * editor,gboolean show)1358 void editor_show_properties(Editor * editor, gboolean show)
1359 {
1360 GtkWidget * dialog;
1361 GtkSizeGroup * hgroup;
1362 GtkSizeGroup * vgroup;
1363 GtkWidget * vbox;
1364 GtkWidget * widget;
1365 GtkTextBuffer * tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
1366 editor->view));
1367 gchar * p;
1368 gchar * q;
1369 char buf[256];
1370 GError * error = NULL;
1371
1372 if(show == FALSE)
1373 /* XXX should really hide the window */
1374 return;
1375 p = (editor->filename != NULL)
1376 ? g_filename_display_basename(editor->filename) : NULL;
1377 if(p != NULL)
1378 snprintf(buf, sizeof(buf), _("Properties of %s"), p);
1379 else
1380 snprintf(buf, sizeof(buf), "%s", _("Properties"));
1381 g_free(p);
1382 dialog = gtk_message_dialog_new(GTK_WINDOW(editor->window),
1383 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1384 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1385 #if GTK_CHECK_VERSION(2, 6, 0)
1386 "%s", _("Properties"));
1387 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1388 #endif
1389 "");
1390 #if GTK_CHECK_VERSION(2, 10, 0)
1391 gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog),
1392 gtk_image_new_from_stock(GTK_STOCK_PROPERTIES,
1393 GTK_ICON_SIZE_DIALOG));
1394 #endif
1395 gtk_window_set_title(GTK_WINDOW(dialog), _("Properties"));
1396 hgroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1397 vgroup = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL);
1398 #if GTK_CHECK_VERSION(2, 14, 0)
1399 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1400 #else
1401 vbox = dialog->vbox;
1402 #endif
1403 /* filename */
1404 p = g_strdup((editor->filename != NULL) ? editor->filename : "");
1405 if((q = g_filename_to_utf8(p, -1, NULL, NULL, &error)) == NULL)
1406 {
1407 editor_error(NULL, error->message, 1);
1408 g_error_free(error);
1409 q = p;
1410 }
1411 widget = gtk_entry_new();
1412 gtk_editable_set_editable(GTK_EDITABLE(widget), FALSE);
1413 gtk_entry_set_text(GTK_ENTRY(widget), q);
1414 gtk_size_group_add_widget(vgroup, widget);
1415 g_free(p);
1416 widget = _properties_widget(editor, hgroup, _("Filename:"), widget);
1417 gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
1418 /* characters */
1419 snprintf(buf, sizeof(buf), "%u", gtk_text_buffer_get_char_count(tbuf));
1420 widget = gtk_label_new(buf);
1421 #if GTK_CHECK_VERSION(3, 0, 0)
1422 g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
1423 #else
1424 gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
1425 #endif
1426 gtk_size_group_add_widget(vgroup, widget);
1427 widget = _properties_widget(editor, hgroup, _("Characters:"), widget);
1428 gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
1429 /* lines */
1430 snprintf(buf, sizeof(buf), "%u", gtk_text_buffer_get_line_count(tbuf));
1431 widget = gtk_label_new(buf);
1432 #if GTK_CHECK_VERSION(3, 0, 0)
1433 g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
1434 #else
1435 gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
1436 #endif
1437 gtk_size_group_add_widget(vgroup, widget);
1438 widget = _properties_widget(editor, hgroup, _("Lines:"), widget);
1439 gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
1440 /* FIXME implement more properties */
1441 gtk_widget_show_all(vbox);
1442 gtk_dialog_run(GTK_DIALOG(dialog));
1443 gtk_widget_destroy(dialog);
1444 }
1445
_properties_widget(Editor * editor,GtkSizeGroup * group,char const * label,GtkWidget * value)1446 static GtkWidget * _properties_widget(Editor * editor, GtkSizeGroup * group,
1447 char const * label, GtkWidget * value)
1448 {
1449 GtkWidget * hbox;
1450 GtkWidget * widget;
1451
1452 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1453 widget = gtk_label_new(label);
1454 gtk_widget_override_font(widget, editor->bold);
1455 #if GTK_CHECK_VERSION(3, 0, 0)
1456 g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
1457 #else
1458 gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
1459 #endif
1460 gtk_size_group_add_widget(group, widget);
1461 gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
1462 gtk_box_pack_start(GTK_BOX(hbox), value, TRUE, TRUE, 0);
1463 return hbox;
1464 }
1465
1466
1467 /* editor_unselect_all */
editor_unselect_all(Editor * editor)1468 void editor_unselect_all(Editor * editor)
1469 {
1470 GtkTextBuffer * tbuf;
1471 GtkTextMark * mark;
1472 GtkTextIter iter;
1473
1474 tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
1475 mark = gtk_text_buffer_get_mark(tbuf, "insert");
1476 gtk_text_buffer_get_iter_at_mark(tbuf, &iter, mark);
1477 gtk_text_buffer_select_range(tbuf, &iter, &iter);
1478 }
1479
1480
1481 /* private */
1482 /* functions */
1483 /* accessors */
1484 /* editor_set_filename */
_editor_set_filename(Editor * editor,char const * filename)1485 static int _editor_set_filename(Editor * editor, char const * filename)
1486 {
1487 gchar * p = NULL;
1488 char * q;
1489
1490 if(g_path_is_absolute(filename))
1491 p = g_strdup(filename);
1492 else if((q = getcwd(NULL, 0)) != NULL)
1493 {
1494 p = g_build_filename(q, filename, NULL);
1495 free(q);
1496 }
1497 if(p == NULL)
1498 return -editor_error(editor, _("Could not update the filename"),
1499 1);
1500 g_free(editor->filename);
1501 editor->filename = p;
1502 return 0;
1503 }
1504
1505
1506 /* editor_set_status */
_editor_set_status(Editor * editor,char const * status)1507 static void _editor_set_status(Editor * editor, char const * status)
1508 {
1509 GtkStatusbar * sb = GTK_STATUSBAR(editor->statusbar);
1510
1511 if(editor->statusbar_id != 0)
1512 gtk_statusbar_remove(sb, gtk_statusbar_get_context_id(sb, ""),
1513 editor->statusbar_id);
1514 editor->statusbar_id = gtk_statusbar_push(sb,
1515 gtk_statusbar_get_context_id(sb, ""), status);
1516 }
1517
1518
1519 /* editor_config_filename */
_editor_config_filename(void)1520 static char * _editor_config_filename(void)
1521 {
1522 char const * homedir;
1523 size_t len;
1524 char * filename;
1525
1526 if((homedir = getenv("HOME")) == NULL)
1527 homedir = g_get_home_dir();
1528 len = strlen(homedir) + 1 + sizeof(EDITOR_CONFIG_FILE);
1529 if((filename = malloc(len)) == NULL)
1530 return NULL;
1531 snprintf(filename, len, "%s/%s", homedir, EDITOR_CONFIG_FILE);
1532 return filename;
1533 }
1534
1535
1536 /* editor_find */
1537 static char const * _find_string(char const * big, char const * little,
1538 gboolean sensitive);
1539 static gboolean _find_match(Editor * editor, GtkTextBuffer * buffer,
1540 char const * buf, char const * str, size_t len);
1541
_editor_find(Editor * editor,char const * text,gboolean sensitive,gboolean wrap)1542 static gboolean _editor_find(Editor * editor, char const * text,
1543 gboolean sensitive, gboolean wrap)
1544 {
1545 gboolean ret = FALSE;
1546 size_t tlen;
1547 GtkTextBuffer * buffer;
1548 GtkTextIter start;
1549 GtkTextIter end;
1550 gchar * buf;
1551 size_t blen;
1552 char const * str;
1553
1554 if(text == NULL || (tlen = strlen(text)) == 0)
1555 return ret;
1556 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(editor->view));
1557 /* XXX highly inefficient */
1558 gtk_text_buffer_get_start_iter(buffer, &start);
1559 gtk_text_buffer_get_end_iter(buffer, &end);
1560 buf = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1561 if(buf == NULL || (blen = strlen(buf)) == 0)
1562 return ret;
1563 if(editor->search >= blen)
1564 editor->search = 0;
1565 if((str = _find_string(&buf[editor->search], text, sensitive)) != NULL)
1566 ret = _find_match(editor, buffer, buf, str, tlen);
1567 else if(wrap && editor->search != 0) /* wrap around */
1568 {
1569 buf[editor->search] = '\0';
1570 if((str = _find_string(buf, text, sensitive)) != NULL)
1571 ret = _find_match(editor, buffer, buf, str, tlen);
1572 }
1573 g_free(buf);
1574 return ret;
1575 }
1576
_find_string(char const * big,char const * little,gboolean sensitive)1577 static char const * _find_string(char const * big, char const * little,
1578 gboolean sensitive)
1579 {
1580 return sensitive ? strstr(big, little) : strcasestr(big, little);
1581 }
1582
_find_match(Editor * editor,GtkTextBuffer * buffer,char const * buf,char const * str,size_t len)1583 static gboolean _find_match(Editor * editor, GtkTextBuffer * buffer,
1584 char const * buf, char const * str, size_t len)
1585 {
1586 size_t offset;
1587 GtkTextIter start;
1588 GtkTextIter end;
1589
1590 offset = str - buf;
1591 editor->search = offset + 1;
1592 gtk_text_buffer_get_iter_at_offset(buffer, &start, offset);
1593 gtk_text_buffer_get_iter_at_offset(buffer, &end, offset + len);
1594 gtk_text_buffer_select_range(buffer, &start, &end);
1595 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(editor->view), &start, 0.0,
1596 FALSE, 0.0, 0.0);
1597 return TRUE;
1598 }
1599
1600
1601 /* editor_refresh_title */
_editor_refresh_title(Editor * editor)1602 static void _editor_refresh_title(Editor * editor)
1603 {
1604 char buf[256];
1605
1606 snprintf(buf, sizeof(buf), "%s%s", _("Text editor - "),
1607 (editor->filename == NULL)
1608 ? _("(Untitled)") : editor->filename);
1609 gtk_window_set_title(GTK_WINDOW(editor->window), buf);
1610 }
1611
1612
1613 /* callbacks */
1614 #if GTK_CHECK_VERSION(2, 16, 0)
1615 /* editor_on_find_clear */
_editor_on_find_clear(gpointer data)1616 static void _editor_on_find_clear(gpointer data)
1617 {
1618 Editor * editor = data;
1619
1620 gtk_entry_set_text(GTK_ENTRY(editor->fi_entry), "");
1621 }
1622 #endif
1623
1624
1625 /* editor_on_find_clicked */
_editor_on_find_clicked(gpointer data)1626 static void _editor_on_find_clicked(gpointer data)
1627 {
1628 Editor * editor = data;
1629 char const * text;
1630 GtkTreeModel * model = GTK_TREE_MODEL(editor->fi_store);
1631 GtkTreeIter iter;
1632 gboolean valid;
1633 char * p;
1634 int res;
1635 gboolean sensitive;
1636 gboolean wrap;
1637
1638 if((text = gtk_entry_get_text(GTK_ENTRY(editor->fi_entry))) == NULL
1639 || strlen(text) == 0)
1640 return;
1641 /* only append the text currently searched if not already known */
1642 for(valid = gtk_tree_model_get_iter_first(model, &iter); valid == TRUE;
1643 valid = gtk_tree_model_iter_next(model, &iter))
1644 {
1645 gtk_tree_model_get(model, &iter, 0, &p, -1);
1646 res = strcmp(text, p);
1647 free(p);
1648 if(res == 0)
1649 break;
1650 }
1651 if(valid == FALSE)
1652 {
1653 gtk_list_store_append(editor->fi_store, &iter);
1654 gtk_list_store_set(editor->fi_store, &iter, 0, text, -1);
1655 }
1656 sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1657 editor->fi_case));
1658 wrap = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
1659 editor->fi_wrap));
1660 if(_editor_find(editor, text, sensitive, wrap) == TRUE)
1661 return;
1662 editor_error(editor, _("Text not found"), 0);
1663 }
1664
1665
1666 /* editor_on_find_hide */
_editor_on_find_hide(gpointer data)1667 static void _editor_on_find_hide(gpointer data)
1668 {
1669 Editor * editor = data;
1670
1671 gtk_widget_hide(editor->fi_dialog);
1672 }
1673
1674
1675 /* editor_on_modified */
_editor_on_modified(GtkTextBuffer * tbuf,gpointer data)1676 static void _editor_on_modified(GtkTextBuffer * tbuf, gpointer data)
1677 {
1678 Editor * editor = data;
1679 gboolean modified;
1680 char const * status;
1681
1682 if((modified = gtk_text_buffer_get_modified(tbuf)) == TRUE)
1683 status = _("Unsaved changes");
1684 else if(editor->filename != NULL)
1685 status = _("Saved");
1686 else
1687 status = _("Ready");
1688 _editor_set_status(editor, status);
1689 }
1690
1691
1692 /* helpers */
1693 /* helper_file_dialog_filters */
_helper_file_dialog_filters(GtkWidget * dialog)1694 static void _helper_file_dialog_filters(GtkWidget * dialog)
1695 {
1696 GtkFileFilter * filter;
1697 char const * types[] = {
1698 "application/x-perl",
1699 "application/x-shellscript",
1700 "application/xml",
1701 "application/xslt+xml",
1702 "text/plain" };
1703 size_t i;
1704
1705 filter = gtk_file_filter_new();
1706 gtk_file_filter_set_name(filter, _("Text files"));
1707 for(i = 0; i < sizeof(types) / sizeof(*types); i++)
1708 gtk_file_filter_add_mime_type(filter, types[i]);
1709 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
1710 filter = gtk_file_filter_new();
1711 gtk_file_filter_set_name(filter, _("All files"));
1712 gtk_file_filter_add_pattern(filter, "*");
1713 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
1714 }
1715