~ubuntu-branches/ubuntu/raring/geany/raring-proposed

« back to all changes in this revision

Viewing changes to src/document.c

  • Committer: Bazaar Package Importer
  • Author(s): Damián Viano
  • Date: 2008-05-02 11:37:45 UTC
  • mfrom: (1.2.1 upstream) (9 hardy)
  • mto: (3.2.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 12.
  • Revision ID: james.westby@ubuntu.com-20080502113745-xzp4g6dmovrpoj17
Tags: 0.14-1
New upstream release (Closes: #478126)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 *      document.c - this file is part of Geany, a fast and lightweight IDE
3
3
 *
4
 
 *      Copyright 2006 Enrico Troeger <enrico.troeger@uvena.de>
 
4
 *      Copyright 2005-2008 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
 
5
 *      Copyright 2006-2008 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
5
6
 *
6
7
 *      This program is free software; you can redistribute it and/or modify
7
8
 *      it under the terms of the GNU General Public License as published by
17
18
 *      along with this program; if not, write to the Free Software
18
19
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20
 *
20
 
 * $Id: document.c 805 2006-09-09 12:03:11Z ntrel $
21
 
 */
22
 
 
23
 
#include "SciLexer.h"
 
21
 * $Id: document.c 2505 2008-04-19 08:14:00Z eht16 $
 
22
 */
 
23
 
 
24
/*
 
25
 *  Document related actions: new, save, open, etc.
 
26
 *  Also Scintilla search actions.
 
27
 */
 
28
 
24
29
#include "geany.h"
25
30
 
26
31
#ifdef TIME_WITH_SYS_TIME
43
48
#include <ctype.h>
44
49
#include <stdlib.h>
45
50
 
 
51
#include <glib/gstdio.h>
 
52
 
46
53
#include "document.h"
 
54
#include "prefs.h"
 
55
#include "filetypes.h"
47
56
#include "support.h"
48
57
#include "sciwrappers.h"
49
 
#include "sci_cb.h"
 
58
#include "editor.h"
50
59
#include "dialogs.h"
51
60
#include "msgwindow.h"
52
61
#include "templates.h"
57
66
#include "notebook.h"
58
67
#include "main.h"
59
68
#include "vte.h"
60
 
 
61
 
 
62
 
/* Returns -1 if no text found or the new range endpoint after replacing. */
63
 
static gint
64
 
document_replace_range(gint idx, const gchar *find_text, const gchar *replace_text,
65
 
        gint flags, gint start, gint end, gboolean escaped_chars);
66
 
 
67
 
 
68
 
/* returns the index of the notebook page which has the given filename
69
 
 * is_tm_filename is needed when passing TagManager filenames because they are
70
 
 * dereferenced, and would not match the link filename. */
 
69
#include "build.h"
 
70
#include "symbols.h"
 
71
#include "callbacks.h"
 
72
#include "geanyobject.h"
 
73
#include "highlighting.h"
 
74
#include "navqueue.h"
 
75
#include "win32.h"
 
76
 
 
77
 
 
78
/* dynamic array of document elements to hold all information of the notebook tabs */
 
79
GArray *doc_array;
 
80
 
 
81
/* Whether to colourise the document straight after styling settings are changed.
 
82
 * (e.g. when filetype is set or typenames are updated) */
 
83
static gboolean delay_colourise = FALSE;
 
84
 
 
85
 
 
86
static void document_undo_clear(gint idx);
 
87
static void document_redo_add(gint idx, guint type, gpointer data);
 
88
 
 
89
static gboolean update_type_keywords(ScintillaObject *sci, gint lang);
 
90
 
 
91
 
 
92
/* ignore the case of filenames and paths under WIN32, causes errors if not */
 
93
#ifdef G_OS_WIN32
 
94
#define filenamecmp(a,b)        strcasecmp((a), (b))
 
95
#else
 
96
#define filenamecmp(a,b)        strcmp((a), (b))
 
97
#endif
 
98
 
 
99
static gint find_by_tm_filename(const gchar *filename)
 
100
{
 
101
        guint i;
 
102
 
 
103
        for (i = 0; i < doc_array->len; i++)
 
104
        {
 
105
                TMWorkObject *tm_file = doc_list[i].tm_file;
 
106
 
 
107
                if (tm_file == NULL || tm_file->file_name == NULL) continue;
 
108
 
 
109
                if (filenamecmp(filename, tm_file->file_name) == 0)
 
110
                        return i;
 
111
        }
 
112
        return -1;
 
113
}
 
114
 
 
115
 
 
116
static gchar *get_real_path_from_utf8(const gchar *utf8_filename)
 
117
{
 
118
        gchar *locale_name = utils_get_locale_from_utf8(utf8_filename);
 
119
        gchar *realname = tm_get_real_path(locale_name);
 
120
 
 
121
        g_free(locale_name);
 
122
        return realname;
 
123
}
 
124
 
 
125
 
 
126
/**
 
127
 *  Find and retrieve the index of the given filename @a filename in the %document list.
 
128
 *
 
129
 *  @param filename The filename to search (in UTF-8 encoding for non-TagManager filenames,
 
130
 *         else in locale encoding).
 
131
 *  @param is_tm_filename Whether the passed @a filename is a TagManager filename and therefore
 
132
 *         locale-encoded and already a realpath().
 
133
 *
 
134
 *  @return The %document index which has the given filename @a filename or @c -1
 
135
 *   if @a filename is not open.
 
136
 **/
71
137
gint document_find_by_filename(const gchar *filename, gboolean is_tm_filename)
72
138
{
73
139
        guint i;
 
140
        gint ret = -1;
 
141
        gchar *realname;
74
142
 
75
143
        if (! filename) return -1;
76
144
 
77
 
        for(i = 0; i < GEANY_MAX_OPEN_FILES; i++)
 
145
        if (is_tm_filename)
 
146
                return find_by_tm_filename(filename);   /* more efficient */
 
147
 
 
148
        realname = get_real_path_from_utf8(filename);   /* dereference symlinks, /../ junk in path */
 
149
        if (! realname) return -1;
 
150
 
 
151
        for (i = 0; i < doc_array->len; i++)
78
152
        {
79
 
                gchar *dl_fname = (is_tm_filename && doc_list[i].tm_file) ?
80
 
                                                        doc_list[i].tm_file->file_name : doc_list[i].file_name;
81
 
#ifdef G_OS_WIN32
82
 
                // ignore the case of filenames and paths under WIN32, causes errors if not
83
 
                if (dl_fname && ! strcasecmp(dl_fname, filename)) return i;
84
 
#else
85
 
                if (dl_fname && utils_strcmp(dl_fname, filename)) return i;
86
 
#endif
 
153
                document *doc = &doc_list[i];
 
154
                gchar *docname;
 
155
 
 
156
                if (doc->file_name == NULL) continue;
 
157
 
 
158
                docname = get_real_path_from_utf8(doc->file_name);
 
159
                if (! docname) continue;
 
160
 
 
161
                if (filenamecmp(realname, docname) == 0)
 
162
                {
 
163
                        ret = i;
 
164
                        g_free(docname);
 
165
                        break;
 
166
                }
 
167
                g_free(docname);
87
168
        }
88
 
        return -1;
 
169
        g_free(realname);
 
170
        return ret;
89
171
}
90
172
 
91
173
 
92
 
/* returns the index of the notebook page which has sci */
 
174
/* returns the document index which has sci */
93
175
gint document_find_by_sci(ScintillaObject *sci)
94
176
{
95
177
        guint i;
96
178
 
97
179
        if (! sci) return -1;
98
180
 
99
 
        for(i = 0; i < GEANY_MAX_OPEN_FILES; i++)
 
181
        for(i = 0; i < doc_array->len; i++)
100
182
        {
101
183
                if (doc_list[i].is_valid && doc_list[i].sci == sci) return i;
102
184
        }
104
186
}
105
187
 
106
188
 
107
 
/* returns the index of the given notebook page in the document list */
 
189
/* returns the index of the notebook page from the document index */
 
190
gint document_get_notebook_page(gint doc_idx)
 
191
{
 
192
        if (! DOC_IDX_VALID(doc_idx)) return -1;
 
193
 
 
194
        return gtk_notebook_page_num(GTK_NOTEBOOK(app->notebook),
 
195
                GTK_WIDGET(doc_list[doc_idx].sci));
 
196
}
 
197
 
 
198
 
 
199
/**
 
200
 *  Find and retrieve the index of the given notebook page @a page_num in the %document list.
 
201
 *
 
202
 *  @param page_num The notebook page number to search.
 
203
 *
 
204
 *  @return The index of the given notebook page @a page_num in the %document list or @c -1
 
205
 *   if no documents are opened.
 
206
 **/
108
207
gint document_get_n_idx(guint page_num)
109
208
{
110
209
        ScintillaObject *sci;
111
 
        if (page_num >= GEANY_MAX_OPEN_FILES) return -1;
 
210
        if (page_num >= doc_array->len) return -1;
112
211
 
113
212
        sci = (ScintillaObject*)gtk_notebook_get_nth_page(
114
213
                                GTK_NOTEBOOK(app->notebook), page_num);
117
216
}
118
217
 
119
218
 
120
 
/* returns the index of the current notebook page in the document list */
 
219
/**
 
220
 *  Find and retrieve the index of the current %document.
 
221
 *
 
222
 *  @return The index of the current notebook page in the %document list or @c -1
 
223
 *   if no documents are opened.
 
224
 **/
121
225
gint document_get_cur_idx()
122
226
{
123
227
        gint cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(app->notebook));
124
 
        ScintillaObject *sci = (ScintillaObject*)gtk_notebook_get_nth_page(GTK_NOTEBOOK(app->notebook), cur_page);
125
228
 
126
229
        if (cur_page == -1)
127
230
                return -1;
128
231
        else
 
232
        {
 
233
                ScintillaObject *sci = (ScintillaObject*)
 
234
                        gtk_notebook_get_nth_page(GTK_NOTEBOOK(app->notebook), cur_page);
 
235
 
129
236
                return document_find_by_sci(sci);
 
237
        }
130
238
}
131
239
 
132
240
 
133
 
/* returns NULL if no documents are open */
 
241
/**
 
242
 *  Find and retrieve the current %document.
 
243
 *
 
244
 *  @return A pointer to the current %document or @c NULL if there are no opened documents.
 
245
 **/
134
246
document *document_get_current()
135
247
{
136
248
        gint idx = document_get_cur_idx();
139
251
}
140
252
 
141
253
 
142
 
/* returns the next free place(i.e. index) in the document list
143
 
 * If there is for any reason no free place, -1 is returned
144
 
 */
145
 
gint document_get_new_idx()
 
254
void document_init_doclist()
 
255
{
 
256
        doc_array = g_array_new(FALSE, FALSE, sizeof(document));
 
257
}
 
258
 
 
259
 
 
260
void document_finalize()
 
261
{
 
262
        g_array_free(doc_array, TRUE);
 
263
}
 
264
 
 
265
 
 
266
/**
 
267
 *  Update the tab labels, the status bar, the window title and some save-sensitive buttons
 
268
 *  according to the document's save state.
 
269
 *  This is called by Geany mostly when opening or saving files.
 
270
 *
 
271
 *  @param idx The %document index to operate on.
 
272
 **/
 
273
void document_set_text_changed(gint idx)
 
274
{
 
275
        if (DOC_IDX_VALID(idx) && ! main_status.quitting)
 
276
        {
 
277
                ui_update_tab_status(idx);
 
278
                ui_save_buttons_toggle(doc_list[idx].changed);
 
279
                ui_set_window_title(idx);
 
280
                ui_update_statusbar(idx, -1);
 
281
        }
 
282
}
 
283
 
 
284
 
 
285
void document_set_use_tabs(gint idx, gboolean use_tabs)
 
286
{
 
287
        document *doc = &doc_list[idx];
 
288
 
 
289
        g_return_if_fail(DOC_IDX_VALID(idx));
 
290
 
 
291
        doc->use_tabs = use_tabs;
 
292
        sci_set_use_tabs(doc->sci, use_tabs);
 
293
        /* remove indent spaces on backspace, if using spaces to indent */
 
294
        SSM(doc->sci, SCI_SETBACKSPACEUNINDENTS, ! use_tabs, 0);
 
295
}
 
296
 
 
297
 
 
298
void document_set_line_wrapping(gint idx, gboolean wrap)
 
299
{
 
300
        document *doc = &doc_list[idx];
 
301
 
 
302
        g_return_if_fail(DOC_IDX_VALID(idx));
 
303
 
 
304
        doc->line_wrapping = wrap;
 
305
        sci_set_lines_wrapped(doc->sci, wrap);
 
306
}
 
307
 
 
308
 
 
309
/* Apply just the prefs that can change in the Preferences dialog */
 
310
void document_apply_update_prefs(gint idx)
 
311
{
 
312
        ScintillaObject *sci = doc_list[idx].sci;
 
313
 
 
314
        sci_set_mark_long_lines(sci, editor_prefs.long_line_type, editor_prefs.long_line_column, editor_prefs.long_line_color);
 
315
 
 
316
        sci_set_tab_width(sci, editor_prefs.tab_width);
 
317
 
 
318
        sci_set_autoc_max_height(sci, editor_prefs.symbolcompletion_max_height);
 
319
 
 
320
        sci_set_indentation_guides(sci, editor_prefs.show_indent_guide);
 
321
        sci_set_visible_white_spaces(sci, editor_prefs.show_white_space);
 
322
        sci_set_visible_eols(sci, editor_prefs.show_line_endings);
 
323
 
 
324
        sci_set_folding_margin_visible(sci, editor_prefs.folding);
 
325
 
 
326
        doc_list[idx].auto_indent = (editor_prefs.indent_mode != INDENT_NONE);
 
327
 
 
328
        sci_assign_cmdkey(sci, SCK_HOME,
 
329
                editor_prefs.smart_home_key ? SCI_VCHOMEWRAP : SCI_HOMEWRAP);
 
330
        sci_assign_cmdkey(sci, SCK_END,  SCI_LINEENDWRAP);
 
331
}
 
332
 
 
333
 
 
334
/* Sets is_valid to FALSE and initializes some members to NULL, to mark it uninitialized.
 
335
 * The flag is_valid is set to TRUE in document_create(). */
 
336
static void init_doc_struct(document *new_doc)
 
337
{
 
338
        new_doc->is_valid = FALSE;
 
339
        new_doc->has_tags = FALSE;
 
340
        new_doc->auto_indent = (editor_prefs.indent_mode != INDENT_NONE);
 
341
        new_doc->line_wrapping = editor_prefs.line_wrapping;
 
342
        new_doc->readonly = FALSE;
 
343
        new_doc->tag_store = NULL;
 
344
        new_doc->tag_tree = NULL;
 
345
        new_doc->file_name = NULL;
 
346
        new_doc->file_type = NULL;
 
347
        new_doc->tm_file = NULL;
 
348
        new_doc->encoding = NULL;
 
349
        new_doc->has_bom = FALSE;
 
350
        new_doc->sci = NULL;
 
351
        new_doc->undo_actions = NULL;
 
352
        new_doc->redo_actions = NULL;
 
353
        new_doc->scroll_percent = -1.0F;
 
354
}
 
355
 
 
356
 
 
357
/* returns the next free place(i.e. index) in the document list,
 
358
 * or -1 if the current doc_array is full */
 
359
static gint document_get_new_idx(void)
146
360
{
147
361
        guint i;
148
362
 
149
 
        for(i = 0; i < GEANY_MAX_OPEN_FILES; i++)
 
363
        for(i = 0; i < doc_array->len; i++)
150
364
        {
151
365
                if (doc_list[i].sci == NULL)
152
366
                {
157
371
}
158
372
 
159
373
 
160
 
void document_set_text_changed(gint idx)
161
 
{
162
 
        if (idx >= 0 && doc_list[idx].is_valid && ! app->quitting)
163
 
        {
164
 
                // changes the color of the tab text according to the status
165
 
                GdkColor colorred = {0, 65535, 0, 0};
166
 
                GdkColor colorblack = {0, 0, 0, 0};
167
 
 
168
 
                gtk_widget_modify_fg(doc_list[idx].tab_label, GTK_STATE_NORMAL,
169
 
                                        (doc_list[idx].changed) ? &colorred : &colorblack);
170
 
                gtk_widget_modify_fg(doc_list[idx].tab_label, GTK_STATE_ACTIVE,
171
 
                                        (doc_list[idx].changed) ? &colorred : &colorblack);
172
 
                gtk_widget_modify_fg(doc_list[idx].tabmenu_label, GTK_STATE_PRELIGHT,
173
 
                                        (doc_list[idx].changed) ? &colorred : &colorblack);
174
 
                gtk_widget_modify_fg(doc_list[idx].tabmenu_label, GTK_STATE_NORMAL,
175
 
                                        (doc_list[idx].changed) ? &colorred : &colorblack);
176
 
 
177
 
                ui_save_buttons_toggle(doc_list[idx].changed);
178
 
                ui_set_window_title(idx);
179
 
        }
180
 
}
181
 
 
182
 
 
183
 
/* sets in all document structs the flag is_valid to FALSE and initializes some members to NULL,
184
 
 * to mark it uninitialized. The flag is_valid is set to TRUE in document_create_new_sci(). */
185
 
void document_init_doclist()
186
 
{
187
 
        gint i;
188
 
 
189
 
        for (i = 0; i < GEANY_MAX_OPEN_FILES; i++)
190
 
        {
191
 
                doc_list[i].is_valid = FALSE;
192
 
                doc_list[i].has_tags = FALSE;
193
 
                doc_list[i].use_auto_indention = app->pref_editor_use_auto_indention;
194
 
                doc_list[i].line_breaking = app->pref_editor_line_breaking;
195
 
                doc_list[i].readonly = FALSE;
196
 
                doc_list[i].tag_store = NULL;
197
 
                doc_list[i].tag_tree = NULL;
198
 
                doc_list[i].file_name = NULL;
199
 
                doc_list[i].file_type = NULL;
200
 
                doc_list[i].tm_file = NULL;
201
 
                doc_list[i].encoding = NULL;
202
 
                doc_list[i].has_bom = FALSE;
203
 
                doc_list[i].sci = NULL;
204
 
                doc_list[i].undo_actions = NULL;
205
 
                doc_list[i].redo_actions = NULL;
206
 
        }
207
 
}
208
 
 
209
 
 
210
 
// Apply just the prefs that can change in the Preferences dialog
211
 
void document_apply_update_prefs(ScintillaObject *sci)
212
 
{
213
 
        sci_set_mark_long_lines(sci, app->long_line_type, app->long_line_column, app->long_line_color);
214
 
 
215
 
        sci_set_tab_width(sci, app->pref_editor_tab_width);
216
 
        sci_set_autoc_max_height(sci, app->autocompletion_max_height);
217
 
 
218
 
        sci_set_indentionguides(sci, app->pref_editor_show_indent_guide);
219
 
        sci_set_visible_white_spaces(sci, app->pref_editor_show_white_space);
220
 
        sci_set_visible_eols(sci, app->pref_editor_show_line_endings);
221
 
 
222
 
        sci_set_folding_margin_visible(sci, app->pref_editor_folding);
223
 
}
224
 
 
225
 
 
226
 
/* creates a new tab in the notebook and does all related stuff
227
 
 * finally it returns the index of the created document */
228
 
gint document_create_new_sci(const gchar *filename)
 
374
static void setup_sci_keys(ScintillaObject *sci)
 
375
{
 
376
        /* disable some Scintilla keybindings to be able to redefine them cleanly */
 
377
        sci_clear_cmdkey(sci, 'A' | (SCMOD_CTRL << 16)); /* select all */
 
378
        sci_clear_cmdkey(sci, 'D' | (SCMOD_CTRL << 16)); /* duplicate */
 
379
        sci_clear_cmdkey(sci, 'T' | (SCMOD_CTRL << 16)); /* line transpose */
 
380
        sci_clear_cmdkey(sci, 'T' | (SCMOD_CTRL << 16) | (SCMOD_SHIFT << 16)); /* line copy */
 
381
        sci_clear_cmdkey(sci, 'L' | (SCMOD_CTRL << 16)); /* line cut */
 
382
        sci_clear_cmdkey(sci, 'L' | (SCMOD_CTRL << 16) | (SCMOD_SHIFT << 16)); /* line delete */
 
383
        sci_clear_cmdkey(sci, SCK_UP | (SCMOD_CTRL << 16)); /* scroll line up */
 
384
        sci_clear_cmdkey(sci, SCK_DOWN | (SCMOD_CTRL << 16)); /* scroll line down */
 
385
 
 
386
        if (editor_prefs.use_gtk_word_boundaries)
 
387
        {
 
388
                /* use GtkEntry-like word boundaries */
 
389
                sci_assign_cmdkey(sci, SCK_RIGHT | (SCMOD_CTRL << 16), SCI_WORDRIGHTEND);
 
390
                sci_assign_cmdkey(sci, SCK_RIGHT | (SCMOD_CTRL << 16) | (SCMOD_SHIFT << 16), SCI_WORDRIGHTENDEXTEND);
 
391
                sci_assign_cmdkey(sci, SCK_DELETE | (SCMOD_CTRL << 16), SCI_DELWORDRIGHTEND);
 
392
        }
 
393
        sci_assign_cmdkey(sci, SCK_UP | (SCMOD_ALT << 16), SCI_LINESCROLLUP);
 
394
        sci_assign_cmdkey(sci, SCK_DOWN | (SCMOD_ALT << 16), SCI_LINESCROLLDOWN);
 
395
        sci_assign_cmdkey(sci, SCK_UP | (SCMOD_CTRL << 16), SCI_PARAUP);
 
396
        sci_assign_cmdkey(sci, SCK_UP | (SCMOD_CTRL << 16) | (SCMOD_SHIFT << 16), SCI_PARAUPEXTEND);
 
397
        sci_assign_cmdkey(sci, SCK_DOWN | (SCMOD_CTRL << 16), SCI_PARADOWN);
 
398
        sci_assign_cmdkey(sci, SCK_DOWN | (SCMOD_CTRL << 16) | (SCMOD_SHIFT << 16), SCI_PARADOWNEXTEND);
 
399
 
 
400
        sci_clear_cmdkey(sci, SCK_BACK | (SCMOD_ALT << 16)); /* clear Alt-Backspace (Undo) */
 
401
}
 
402
 
 
403
 
 
404
/* Create new editor (the scintilla widget) */
 
405
static ScintillaObject *create_new_sci(gint new_idx)
229
406
{
230
407
        ScintillaObject *sci;
 
408
 
 
409
        sci = SCINTILLA(scintilla_new());
 
410
        scintilla_set_id(sci, new_idx);
 
411
 
 
412
        gtk_widget_show(GTK_WIDGET(sci));
 
413
 
 
414
        sci_set_codepage(sci, SC_CP_UTF8);
 
415
        /*SSM(sci, SCI_SETWRAPSTARTINDENT, 4, 0);*/
 
416
        /* disable scintilla provided popup menu */
 
417
        sci_use_popup(sci, FALSE);
 
418
 
 
419
        setup_sci_keys(sci);
 
420
 
 
421
        sci_set_tab_indents(sci, editor_prefs.use_tab_to_indent);
 
422
        sci_set_symbol_margin(sci, editor_prefs.show_markers_margin);
 
423
        sci_set_lines_wrapped(sci, editor_prefs.line_wrapping);
 
424
        sci_set_scrollbar_mode(sci, editor_prefs.show_scrollbars);
 
425
        sci_set_caret_policy_x(sci, CARET_JUMPS | CARET_EVEN, 0);
 
426
        /*sci_set_caret_policy_y(sci, CARET_JUMPS | CARET_EVEN, 0);*/
 
427
        SSM(sci, SCI_AUTOCSETSEPARATOR, '\n', 0);
 
428
        /* (dis)allow scrolling past end of document */
 
429
        SSM(sci, SCI_SETENDATLASTLINE, editor_prefs.scroll_stop_at_last_line, 0);
 
430
        SSM(sci, SCI_SETSCROLLWIDTHTRACKING, 1, 0);
 
431
 
 
432
        /* signal for insert-key(works without too, but to update the right status bar) */
 
433
        /*g_signal_connect((GtkWidget*) sci, "key-press-event",
 
434
                                         G_CALLBACK(keybindings_got_event), GINT_TO_POINTER(new_idx));*/
 
435
        /* signal for the popup menu */
 
436
        g_signal_connect(G_OBJECT(sci), "button-press-event",
 
437
                                        G_CALLBACK(on_editor_button_press_event), GINT_TO_POINTER(new_idx));
 
438
        g_signal_connect(G_OBJECT(sci), "motion-notify-event", G_CALLBACK(on_motion_event), NULL);
 
439
 
 
440
        return sci;
 
441
}
 
442
 
 
443
 
 
444
/* Creates a new document and editor, adding a tab in the notebook.
 
445
 * @return The index of the created document */
 
446
static gint document_create(const gchar *utf8_filename)
 
447
{
231
448
        PangoFontDescription *pfd;
232
 
        gchar *title, *fname;
233
 
        GtkTreeIter iter;
 
449
        gchar *fname;
234
450
        gint new_idx;
235
451
        document *this;
236
452
        gint tabnum;
 
453
        gint cur_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook));
237
454
 
238
 
        if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook)) == 1)
 
455
        if (cur_pages == 1)
239
456
        {
240
457
                gint idx = document_get_cur_idx();
241
 
                // remove the empty document and open a new one
 
458
                /* remove the empty document and open a new one */
242
459
                if (doc_list[idx].file_name == NULL && ! doc_list[idx].changed) document_remove(0);
243
460
        }
244
461
 
245
462
        new_idx = document_get_new_idx();
246
 
        if (new_idx == -1) return -1;
247
 
 
248
 
        this = &(doc_list[new_idx]);
249
 
 
250
 
        /* SCI - Code */
251
 
        sci = SCINTILLA(scintilla_new());
252
 
        scintilla_set_id(sci, new_idx);
253
 
        this->sci = sci;
254
 
 
255
 
        gtk_widget_show(GTK_WIDGET(sci));
256
 
 
257
 
        sci_set_codepage(sci, SC_CP_UTF8);
258
 
        //SSM(sci, SCI_SETWRAPSTARTINDENT, 4, 0);
259
 
        // disable scintilla provided popup menu
260
 
        sci_use_popup(sci, FALSE);
261
 
        sci_assign_cmdkey(sci, SCK_HOME, SCI_VCHOMEWRAP);
262
 
        sci_assign_cmdkey(sci, SCK_END,  SCI_LINEENDWRAP);
263
 
        // disable select all to be able to redefine it
264
 
        sci_clear_cmdkey(sci, 'A' | (SCMOD_CTRL << 16));
265
 
 
266
 
        document_apply_update_prefs(sci);
267
 
 
268
 
        sci_set_symbol_margin(sci, app->show_markers_margin);
269
 
        sci_set_line_numbers(sci, app->show_linenumber_margin, 0);
270
 
        sci_set_lines_wrapped(sci, app->pref_editor_line_breaking);
271
 
 
272
 
        pfd = pango_font_description_from_string(app->editor_font);
 
463
        if (new_idx == -1)      /* expand the array, no free places */
 
464
        {
 
465
                document new_doc;
 
466
                init_doc_struct(&new_doc);
 
467
                new_idx = doc_array->len;
 
468
                g_array_append_val(doc_array, new_doc);
 
469
        }
 
470
        this = &doc_list[new_idx];
 
471
 
 
472
        this->sci = create_new_sci(new_idx);
 
473
 
 
474
        document_apply_update_prefs(new_idx);
 
475
 
 
476
        pfd = pango_font_description_from_string(prefs.editor_font);
273
477
        fname = g_strdup_printf("!%s", pango_font_description_get_family(pfd));
274
478
        document_set_font(new_idx, fname, pango_font_description_get_size(pfd) / PANGO_SCALE);
275
479
        pango_font_description_free(pfd);
276
480
        g_free(fname);
277
481
 
278
 
        title = (filename) ? g_path_get_basename(filename) : g_strdup(GEANY_STRING_UNTITLED);
279
 
 
280
 
        tabnum = notebook_new_tab(new_idx, title, GTK_WIDGET(sci));
281
 
        gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook), tabnum);
282
 
 
283
 
        iter = treeviews_openfiles_add(new_idx, title);
284
 
        g_free(title);
285
 
 
286
482
        this->tag_store = NULL;
287
483
        this->tag_tree = NULL;
288
484
 
289
 
        // signal for insert-key(works without too, but to update the right status bar)
290
 
/*      g_signal_connect((GtkWidget*) sci, "key-press-event",
291
 
                                        G_CALLBACK(keybindings_got_event), GINT_TO_POINTER(new_idx));
292
 
*/      // signal for the popup menu
293
 
        g_signal_connect((GtkWidget*) sci, "button-press-event",
294
 
                                        G_CALLBACK(on_editor_button_press_event), GINT_TO_POINTER(new_idx));
295
 
 
296
 
        ui_close_buttons_toggle();
297
 
 
298
 
        // store important pointers in the tab list
299
 
        this->file_name = (filename) ? g_strdup(filename) : NULL;
 
485
        /* store important pointers in the tab list */
 
486
        this->file_name = (utf8_filename) ? g_strdup(utf8_filename) : NULL;
300
487
        this->encoding = NULL;
 
488
        this->saved_encoding.encoding = NULL;
 
489
        this->saved_encoding.has_bom = FALSE;
301
490
        this->tm_file = NULL;
302
 
        this->iter = iter;
303
491
        this->file_type = NULL;
304
492
        this->mtime = 0;
305
493
        this->changed = FALSE;
306
494
        this->last_check = time(NULL);
307
 
        this->do_overwrite = FALSE;
308
495
        this->readonly = FALSE;
309
 
        this->line_breaking = app->pref_editor_line_breaking;
310
 
        this->use_auto_indention = app->pref_editor_use_auto_indention;
 
496
        this->line_wrapping = editor_prefs.line_wrapping;
 
497
        this->auto_indent = (editor_prefs.indent_mode != INDENT_NONE);
311
498
        this->has_tags = FALSE;
312
 
        this->is_valid = TRUE;
313
 
 
 
499
 
 
500
        treeviews_openfiles_add(new_idx);       /* sets this->iter */
 
501
 
 
502
        tabnum = notebook_new_tab(new_idx);
 
503
 
 
504
        /* select document in sidebar */
 
505
        {
 
506
                GtkTreeSelection *sel;
 
507
 
 
508
                sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv.tree_openfiles));
 
509
                gtk_tree_selection_select_iter(sel, &this->iter);
 
510
        }
 
511
 
 
512
        ui_document_buttons_update();
 
513
 
 
514
        this->is_valid = TRUE;  /* do this last to prevent UI updating with NULL items. */
314
515
        return new_idx;
315
516
}
316
517
 
317
518
 
318
 
/* removes the given notebook tab and clears the related entry in the document list */
 
519
/**
 
520
 *  Remove the given notebook tab at @a page_num and clear all related information
 
521
 *  in the document list.
 
522
 *
 
523
 *  @param page_num The notebook page number to remove.
 
524
 *
 
525
 *  @return @a TRUE if the document was actually removed or @a FALSE otherwise.
 
526
 **/
319
527
gboolean document_remove(guint page_num)
320
528
{
321
529
        gint idx = document_get_n_idx(page_num);
322
530
 
323
 
        if (idx >= 0 && idx <= GEANY_MAX_OPEN_FILES)
 
531
        if (DOC_IDX_VALID(idx))
324
532
        {
325
533
                if (doc_list[idx].changed && ! dialogs_show_unsaved_file(idx))
326
534
                {
327
535
                        return FALSE;
328
536
                }
329
 
                gtk_notebook_remove_page(GTK_NOTEBOOK(app->notebook), page_num);
330
 
                treeviews_openfiles_remove(doc_list[idx].iter);
331
 
                if (GTK_IS_WIDGET(doc_list[idx].tag_tree))
332
 
                {
333
 
                        //g_object_unref(doc_list[idx].tag_tree); // no need to unref when destroying?
334
 
                        gtk_widget_destroy(doc_list[idx].tag_tree);
335
 
                }
336
 
                msgwin_status_add(_("File %s closed."),
337
 
                                (doc_list[idx].file_name) ? doc_list[idx].file_name : GEANY_STRING_UNTITLED);
 
537
                notebook_remove_page(page_num);
 
538
                treeviews_remove_document(idx);
 
539
                navqueue_remove_file(doc_list[idx].file_name);
 
540
                msgwin_status_add(_("File %s closed."), DOC_FILENAME(idx));
338
541
                g_free(doc_list[idx].encoding);
 
542
                g_free(doc_list[idx].saved_encoding.encoding);
339
543
                g_free(doc_list[idx].file_name);
340
 
                tm_workspace_remove_object(doc_list[idx].tm_file, TRUE);
 
544
                tm_workspace_remove_object(doc_list[idx].tm_file, TRUE, TRUE);
 
545
 
341
546
                doc_list[idx].is_valid = FALSE;
342
547
                doc_list[idx].sci = NULL;
343
548
                doc_list[idx].file_name = NULL;
345
550
                doc_list[idx].encoding = NULL;
346
551
                doc_list[idx].has_bom = FALSE;
347
552
                doc_list[idx].tm_file = NULL;
 
553
                doc_list[idx].scroll_percent = -1.0F;
348
554
                document_undo_clear(idx);
349
 
                document_redo_clear(idx);
350
555
                if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook)) == 0)
351
556
                {
352
 
                        ui_update_tag_list(-1, FALSE);
353
 
                        //on_notebook1_switch_page(GTK_NOTEBOOK(app->notebook), NULL, 0, NULL);
 
557
                        treeviews_update_tag_list(-1, FALSE);
 
558
                        /*on_notebook1_switch_page(GTK_NOTEBOOK(app->notebook), NULL, 0, NULL);*/
354
559
                        ui_set_window_title(-1);
355
560
                        ui_save_buttons_toggle(FALSE);
356
 
                        ui_close_buttons_toggle();
357
 
                        ui_build_show_hide(-1);
 
561
                        ui_document_buttons_update();
 
562
                        build_menu_update(-1);
358
563
                }
359
564
        }
360
 
        else geany_debug("Error: idx: %d page_num: %d", idx, page_num);
 
565
        else
 
566
        {
 
567
                geany_debug("Error: idx: %d page_num: %d", idx, page_num);
 
568
                return FALSE;
 
569
        }
361
570
 
362
571
        return TRUE;
363
572
}
364
573
 
365
574
 
366
 
/* This creates a new document, by clearing the text widget and setting the
367
 
   current filename to NULL. */
368
 
void document_new_file(filetype *ft)
369
 
{
370
 
        if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook)) < GEANY_MAX_OPEN_FILES)
371
 
        {
372
 
                gint idx = document_create_new_sci(NULL);
373
 
                gchar *template = document_prepare_template(ft);
374
 
 
375
 
                if (idx == -1)
376
 
                {
377
 
                        dialogs_show_error(
378
 
                        _("You have opened too many files. There is a limit of %d concurrent open files."),
379
 
                        GEANY_MAX_OPEN_FILES);
380
 
                        return;
381
 
                }
382
 
 
 
575
/* used to keep a record of the unchanged document state encoding */
 
576
static void store_saved_encoding(gint idx)
 
577
{
 
578
        g_free(doc_list[idx].saved_encoding.encoding);
 
579
        doc_list[idx].saved_encoding.encoding = g_strdup(doc_list[idx].encoding);
 
580
        doc_list[idx].saved_encoding.has_bom = doc_list[idx].has_bom;
 
581
}
 
582
 
 
583
 
 
584
/* Opens a new empty document only if there are no other documents open */
 
585
gint document_new_file_if_non_open()
 
586
{
 
587
        if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook)) == 0)
 
588
                return document_new_file(NULL, NULL, NULL);
 
589
 
 
590
        return -1;
 
591
}
 
592
 
 
593
 
 
594
/**
 
595
 *  Creates a new %document.
 
596
 *  After all, the "document-new" signal is emitted for plugins.
 
597
 *
 
598
 *  @param filename The file name in UTF-8 encoding, or @c NULL to open a file as "untitled".
 
599
 *  @param ft The filetype to set or @c NULL to detect it from @a filename if not @c NULL.
 
600
 *  @param text The initial content of the file (in UTF-8 encoding), or @c NULL.
 
601
 *
 
602
 *  @return The index of the new file in the @ref doc_list array.
 
603
 **/
 
604
gint document_new_file(const gchar *filename, filetype *ft, const gchar *text)
 
605
{
 
606
        gint idx = document_create(filename);
 
607
 
 
608
        g_assert(idx != -1);
 
609
 
 
610
        sci_set_undo_collection(doc_list[idx].sci, FALSE); /* avoid creation of an undo action */
 
611
        if (text)
 
612
                sci_set_text(doc_list[idx].sci, text);
 
613
        else
383
614
                sci_clear_all(doc_list[idx].sci);
384
 
                sci_set_text(doc_list[idx].sci, template);
385
 
                g_free(template);
386
615
 
387
 
                doc_list[idx].encoding = g_strdup(encodings[app->pref_editor_default_encoding].charset);
388
 
                //document_set_filetype(idx, (ft == NULL) ? filetypes[GEANY_FILETYPES_ALL] : ft);
389
 
                document_set_filetype(idx, ft);
390
 
                if (ft == NULL) filetypes[GEANY_FILETYPES_ALL]->style_func_ptr(doc_list[idx].sci);
391
 
                ui_set_window_title(idx);
392
 
                ui_build_show_hide(idx);
393
 
                ui_update_tag_list(idx, FALSE);
394
 
                doc_list[idx].mtime = time(NULL);
395
 
                doc_list[idx].changed = FALSE;
396
 
                document_set_text_changed(idx);
397
 
                ui_document_show_hide(idx); //update the document menu
398
616
#ifdef G_OS_WIN32
399
 
                sci_set_eol_mode(doc_list[idx].sci, SC_EOL_CRLF);
 
617
        sci_set_eol_mode(doc_list[idx].sci, SC_EOL_CRLF);
400
618
#else
401
 
                sci_set_eol_mode(doc_list[idx].sci, SC_EOL_LF);
 
619
        sci_set_eol_mode(doc_list[idx].sci, SC_EOL_LF);
402
620
#endif
403
 
                sci_set_line_numbers(doc_list[idx].sci, app->show_linenumber_margin, 0);
404
 
                sci_empty_undo_buffer(doc_list[idx].sci);
405
 
                sci_goto_pos(doc_list[idx].sci, 0, TRUE);
406
 
 
407
 
                // "the" SCI signal (connect after initial setup(i.e. adding text))
408
 
                g_signal_connect((GtkWidget*) doc_list[idx].sci, "sci-notify",
409
 
                                        G_CALLBACK(on_editor_notification), GINT_TO_POINTER(idx));
410
 
 
411
 
                msgwin_status_add(_("New file opened."));
412
 
        }
413
 
        else
 
621
        document_set_use_tabs(idx, editor_prefs.use_tabs);
 
622
        sci_set_undo_collection(doc_list[idx].sci, TRUE);
 
623
        sci_empty_undo_buffer(doc_list[idx].sci);
 
624
 
 
625
        doc_list[idx].mtime = time(NULL);
 
626
        doc_list[idx].changed = FALSE;
 
627
 
 
628
        doc_list[idx].encoding = g_strdup(encodings[prefs.default_new_encoding].charset);
 
629
        /* store the opened encoding for undo/redo */
 
630
        store_saved_encoding(idx);
 
631
 
 
632
        /*document_set_filetype(idx, (ft == NULL) ? filetypes[GEANY_FILETYPES_ALL] : ft);*/
 
633
        if (ft == NULL && filename != NULL) /* guess the filetype from the filename if one is given */
 
634
                ft = filetypes_detect_from_file(idx);
 
635
 
 
636
        document_set_filetype(idx, ft); /* also clears taglist */
 
637
        if (ft == NULL)
 
638
                highlighting_set_styles(doc_list[idx].sci, GEANY_FILETYPES_ALL);
 
639
        ui_set_window_title(idx);
 
640
        build_menu_update(idx);
 
641
        document_update_tag_list(idx, FALSE);
 
642
        document_set_text_changed(idx);
 
643
        ui_document_show_hide(idx); /* update the document menu */
 
644
 
 
645
        sci_set_line_numbers(doc_list[idx].sci, editor_prefs.show_linenumber_margin, 0);
 
646
        sci_goto_pos(doc_list[idx].sci, 0, TRUE);
 
647
 
 
648
        /* "the" SCI signal (connect after initial setup(i.e. adding text)) */
 
649
        g_signal_connect((GtkWidget*) doc_list[idx].sci, "sci-notify",
 
650
                                G_CALLBACK(on_editor_notification), GINT_TO_POINTER(idx));
 
651
 
 
652
        if (geany_object)
414
653
        {
415
 
                dialogs_show_error(
416
 
                _("You have opened too many files. There is a limit of %d concurrent open files."),
417
 
                                                        GEANY_MAX_OPEN_FILES);
 
654
                g_signal_emit_by_name(geany_object, "document-new", idx);
418
655
        }
419
 
}
420
 
 
421
 
 
422
 
// reload file with specified encoding
 
656
 
 
657
        msgwin_status_add(_("New file \"%s\" opened."),
 
658
                (doc_list[idx].file_name != NULL) ? doc_list[idx].file_name : GEANY_STRING_UNTITLED);
 
659
 
 
660
        return idx;
 
661
}
 
662
 
 
663
 
 
664
/**
 
665
 *  Open a %document specified by @a locale_filename.
 
666
 *  After all, the "document-open" signal is emitted for plugins.
 
667
 *
 
668
 *  When opening more than one file, either:
 
669
 *  -# Use document_open_files().
 
670
 *  -# Call document_delay_colourise() before document_open_file() and
 
671
 *     document_colourise_new() after opening all files.
 
672
 *
 
673
 *  This avoids unnecessary recolourising, saving significant processing when a lot of files
 
674
 *  are open of a %filetype that supports user typenames, e.g. C.
 
675
 *
 
676
 *  @param locale_filename The filename of the %document to load, in locale encoding.
 
677
 *  @param readonly Whether to open the %document in read-only mode.
 
678
 *  @param ft The %filetype for the %document or @c NULL to auto-detect the %filetype.
 
679
 *  @param forced_enc The file encoding to use or @c NULL to auto-detect the file encoding.
 
680
 *
 
681
 *  @return The index of the opened file or -1 if an error occurred.
 
682
 **/
 
683
gint document_open_file(const gchar *locale_filename, gboolean readonly,
 
684
                filetype *ft, const gchar *forced_enc)
 
685
{
 
686
        /* This is a wrapper for document_open_file_full().
 
687
         * Do not use this when opening multiple files (unless using document_delay_colourise()). */
 
688
        return document_open_file_full(-1, locale_filename, 0, readonly, ft, forced_enc);
 
689
}
 
690
 
 
691
 
 
692
typedef struct
 
693
{
 
694
        gchar           *data;  /* null-terminated file data */
 
695
        gsize            size;  /* actual file size on disk */
 
696
        gsize            len;   /* string length of data */
 
697
        gchar           *enc;
 
698
        gboolean         bom;
 
699
        time_t           mtime; /* modification time, read by stat::st_mtime */
 
700
        gboolean         readonly;
 
701
} FileData;
 
702
 
 
703
 
 
704
/* reload file with specified encoding */
423
705
static gboolean
424
 
handle_forced_encoding(gchar **data, gsize *size, const gchar *forced_enc, gchar **enc,
425
 
        gboolean *bom)
 
706
handle_forced_encoding(FileData *filedata, const gchar *forced_enc)
426
707
{
427
 
        if (utils_strcmp(forced_enc, "UTF-8"))
 
708
        GeanyEncodingIndex enc_idx;
 
709
 
 
710
        if (utils_str_equal(forced_enc, "UTF-8"))
428
711
        {
429
 
                if (! g_utf8_validate(*data, *size, NULL))
 
712
                if (! g_utf8_validate(filedata->data, filedata->len, NULL))
430
713
                {
431
714
                        return FALSE;
432
715
                }
433
 
                else
434
 
                {
435
 
                        *bom = utils_strcmp(utils_scan_unicode_bom(*data), "UTF-8");
436
 
                        *enc = g_strdup(forced_enc);
437
 
                }
438
716
        }
439
717
        else
440
718
        {
441
 
                gchar *converted_text = utils_convert_to_utf8_from_charset(*data, *size, forced_enc, FALSE);
 
719
                gchar *converted_text = encodings_convert_to_utf8_from_charset(
 
720
                                                                                filedata->data, filedata->len, forced_enc, FALSE);
442
721
                if (converted_text == NULL)
443
722
                {
444
723
                        return FALSE;
445
724
                }
446
725
                else
447
726
                {
448
 
                        g_free(*data);
449
 
                        *data = (void*)converted_text;
450
 
                        *size = strlen(converted_text);
451
 
                        *bom = utils_strcmp(utils_scan_unicode_bom(*data), "UTF-8");
452
 
                        *enc = g_strdup(forced_enc);
 
727
                        g_free(filedata->data);
 
728
                        filedata->data = converted_text;
 
729
                        filedata->len = strlen(converted_text);
453
730
                }
454
731
        }
 
732
        enc_idx = encodings_scan_unicode_bom(filedata->data, filedata->size, NULL);
 
733
        filedata->bom = (enc_idx == GEANY_ENCODING_UTF_8);
 
734
        filedata->enc = g_strdup(forced_enc);
455
735
        return TRUE;
456
736
}
457
737
 
458
738
 
 
739
/* detect encoding and convert to UTF-8 if necessary */
459
740
static gboolean
460
 
handle_encoding(gchar **data, gsize *size, gchar **enc, gboolean *bom)
 
741
handle_encoding(FileData *filedata)
461
742
{
462
 
        if (*size > 0)
463
 
        {       // the usual way to detect encoding and convert to UTF-8
464
 
                if (*size >= 4)
465
 
                {
466
 
                        *enc = utils_scan_unicode_bom(*data);
467
 
                }
468
 
                if (*enc != NULL)
469
 
                {
470
 
                        *bom = TRUE;
471
 
                        if ((*enc)[4] != '8') // the BOM indicated something else than UTF-8
 
743
        g_return_val_if_fail(filedata->enc == NULL, FALSE);
 
744
        g_return_val_if_fail(filedata->bom == FALSE, FALSE);
 
745
 
 
746
        if (filedata->size == 0)
 
747
        {
 
748
                /* we have no data so assume UTF-8, filedata->len can be 0 even we have an empty
 
749
                 * e.g. UTF32 file with a BOM(so size is 4, len is 0) */
 
750
                filedata->enc = g_strdup("UTF-8");
 
751
        }
 
752
        else
 
753
        {
 
754
                /* first check for a BOM */
 
755
                GeanyEncodingIndex enc_idx =
 
756
                        encodings_scan_unicode_bom(filedata->data, filedata->size, NULL);
 
757
 
 
758
                if (enc_idx != GEANY_ENCODING_NONE)
 
759
                {
 
760
                        filedata->enc = g_strdup(encodings[enc_idx].charset);
 
761
                        filedata->bom = TRUE;
 
762
 
 
763
                        if (enc_idx != GEANY_ENCODING_UTF_8) /* the BOM indicated something else than UTF-8 */
472
764
                        {
473
 
                                gchar *converted_text = utils_convert_to_utf8_from_charset(*data, *size, *enc, FALSE);
474
 
                                if (converted_text == NULL)
 
765
                                gchar *converted_text = encodings_convert_to_utf8_from_charset(
 
766
                                                                                filedata->data, filedata->size, filedata->enc, FALSE);
 
767
                                if (converted_text != NULL)
475
768
                                {
476
 
                                        g_free(*enc);
477
 
                                        *enc = NULL;
478
 
                                        *bom = FALSE;
 
769
                                        g_free(filedata->data);
 
770
                                        filedata->data = converted_text;
 
771
                                        filedata->len = strlen(converted_text);
479
772
                                }
480
773
                                else
481
774
                                {
482
 
                                        g_free(*data);
483
 
                                        *data = (void*)converted_text;
484
 
                                        *size = strlen(converted_text);
 
775
                                        /* there was a problem converting data from BOM encoding type */
 
776
                                        g_free(filedata->enc);
 
777
                                        filedata->enc = NULL;
 
778
                                        filedata->bom = FALSE;
485
779
                                }
486
780
                        }
487
781
                }
488
 
                // this if is important, else doesn't work because enc can be altered in the above block
489
 
                if (*enc == NULL)
 
782
 
 
783
                if (filedata->enc == NULL)      /* either there was no BOM or the BOM encoding failed */
490
784
                {
491
 
                        if (g_utf8_validate(*data, *size, NULL))
 
785
                        /* try UTF-8 first */
 
786
                        if (g_utf8_validate(filedata->data, filedata->len, NULL))
492
787
                        {
493
 
                                *enc = g_strdup("UTF-8");
 
788
                                filedata->enc = g_strdup("UTF-8");
494
789
                        }
495
790
                        else
496
791
                        {
497
 
                                gchar *converted_text = utils_convert_to_utf8(*data, *size, enc);
 
792
                                /* detect the encoding */
 
793
                                gchar *converted_text = encodings_convert_to_utf8(filedata->data,
 
794
                                        filedata->size, &filedata->enc);
498
795
 
499
796
                                if (converted_text == NULL)
500
797
                                {
501
798
                                        return FALSE;
502
799
                                }
503
 
                                else
504
 
                                {
505
 
                                        g_free(*data);
506
 
                                        *data = (void*)converted_text;
507
 
                                        *size = strlen(converted_text);
508
 
                                }
 
800
                                g_free(filedata->data);
 
801
                                filedata->data = converted_text;
 
802
                                filedata->len = strlen(converted_text);
509
803
                        }
510
804
                }
511
805
        }
512
 
        else
513
 
        {
514
 
                *enc = g_strdup("UTF-8");
515
 
        }
516
806
        return TRUE;
517
807
}
518
808
 
519
809
 
520
810
static void
521
 
handle_bom(gchar **data)
522
 
{
523
 
        gchar *data_without_bom;
524
 
        data_without_bom = g_strdup(*data + 3);
525
 
        g_free(*data);
526
 
        *data = data_without_bom;
527
 
}
528
 
 
529
 
 
530
 
/* If idx is set to -1, it creates a new tab, opens the file from filename and
531
 
 * set the cursor to pos.
532
 
 * If idx is greater than -1, it reloads the file in the tab corresponding to
533
 
 * idx and set the cursor to position 0. In this case, filename should be NULL
534
 
 * It returns the idx of the opened file or -1 if an error occurred.
535
 
 */
536
 
int document_open_file(gint idx, const gchar *filename, gint pos, gboolean readonly, filetype *ft,
537
 
                                           const gchar *forced_enc)
 
811
handle_bom(FileData *filedata)
 
812
{
 
813
        guint bom_len;
 
814
 
 
815
        encodings_scan_unicode_bom(filedata->data, filedata->size, &bom_len);
 
816
        g_return_if_fail(bom_len != 0);
 
817
 
 
818
        /* use filedata->len here because the contents are already converted into UTF-8 */
 
819
        filedata->len -= bom_len;
 
820
        /* overwrite the BOM with the remainder of the file contents, plus the NULL terminator. */
 
821
        g_memmove(filedata->data, filedata->data + bom_len, filedata->len + 1);
 
822
        filedata->data = g_realloc(filedata->data, filedata->len + 1);
 
823
}
 
824
 
 
825
 
 
826
/* loads textfile data, verifies and converts to forced_enc or UTF-8. Also handles BOM. */
 
827
static gboolean load_text_file(const gchar *locale_filename, const gchar *utf8_filename,
 
828
                FileData *filedata, const gchar *forced_enc)
 
829
{
 
830
        GError *err = NULL;
 
831
        struct stat st;
 
832
        GeanyEncodingIndex tmp_enc_idx;
 
833
 
 
834
        filedata->data = NULL;
 
835
        filedata->len = 0;
 
836
        filedata->enc = NULL;
 
837
        filedata->bom = FALSE;
 
838
        filedata->readonly = FALSE;
 
839
 
 
840
        if (g_stat(locale_filename, &st) != 0)
 
841
        {
 
842
                ui_set_statusbar(TRUE, _("Could not open file %s (%s)"), utf8_filename, g_strerror(errno));
 
843
                return FALSE;
 
844
        }
 
845
 
 
846
        filedata->mtime = st.st_mtime;
 
847
 
 
848
        if (! g_file_get_contents(locale_filename, &filedata->data, NULL, &err))
 
849
        {
 
850
                ui_set_statusbar(TRUE, "%s", err->message);
 
851
                g_error_free(err);
 
852
                return FALSE;
 
853
        }
 
854
 
 
855
        /* use strlen to check for null chars */
 
856
        filedata->size = (gsize) st.st_size;
 
857
        filedata->len = strlen(filedata->data);
 
858
 
 
859
        /* temporarily retrieve the encoding idx based on the BOM to suppress the following warning
 
860
         * if we have a BOM */
 
861
        tmp_enc_idx = encodings_scan_unicode_bom(filedata->data, filedata->size, NULL);
 
862
 
 
863
        /* check whether the size of the loaded data is equal to the size of the file in the filesystem */
 
864
        /* file size may be 0 to allow opening files in /proc/ which have typically a file size
 
865
         * of 0 bytes */
 
866
        if (filedata->len != filedata->size && filedata->size != 0 && (
 
867
                tmp_enc_idx == GEANY_ENCODING_UTF_8 || /* tmp_enc_idx can be UTF-7/8/16/32, UCS and None */
 
868
                tmp_enc_idx == GEANY_ENCODING_UTF_7 || /* filter out UTF-7/8 and None where no NULL bytes */
 
869
                tmp_enc_idx == GEANY_ENCODING_NONE))   /* are allowed */
 
870
        {
 
871
                gchar *warn_msg = _("The file \"%s\" could not be opened properly and has been truncated. "
 
872
                                "This can occur if the file contains a NULL byte. "
 
873
                                "Be aware that saving it can cause data loss.\nThe file was set to read-only.");
 
874
 
 
875
                if (main_status.main_window_realized)
 
876
                        dialogs_show_msgbox(GTK_MESSAGE_WARNING, warn_msg, utf8_filename);
 
877
 
 
878
                ui_set_statusbar(TRUE, warn_msg, utf8_filename);
 
879
 
 
880
                /* set the file to read-only mode because saving it is probably dangerous */
 
881
                filedata->readonly = TRUE;
 
882
        }
 
883
 
 
884
        /* Determine character encoding and convert to UTF-8 */
 
885
        if (forced_enc != NULL)
 
886
        {
 
887
                /* the encoding should be ignored(requested by user), so open the file "as it is" */
 
888
                if (utils_str_equal(forced_enc, encodings[GEANY_ENCODING_NONE].charset))
 
889
                {
 
890
                        filedata->bom = FALSE;
 
891
                        filedata->enc = g_strdup(encodings[GEANY_ENCODING_NONE].charset);
 
892
                }
 
893
                else if (! handle_forced_encoding(filedata, forced_enc))
 
894
                {
 
895
                        ui_set_statusbar(TRUE, _("The file \"%s\" is not valid %s."), utf8_filename, forced_enc);
 
896
                        utils_beep();
 
897
                        g_free(filedata->data);
 
898
                        return FALSE;
 
899
                }
 
900
        }
 
901
        else if (! handle_encoding(filedata))
 
902
        {
 
903
                ui_set_statusbar(TRUE,
 
904
                        _("The file \"%s\" does not look like a text file or the file encoding is not supported."),
 
905
                        utf8_filename);
 
906
                utils_beep();
 
907
                g_free(filedata->data);
 
908
                return FALSE;
 
909
        }
 
910
 
 
911
        if (filedata->bom)
 
912
                handle_bom(filedata);
 
913
        return TRUE;
 
914
}
 
915
 
 
916
 
 
917
/* Sets the cursor position on opening a file. First it sets the line when cl_options.goto_line
 
918
 * is set, otherwise it sets the line when pos is greater than zero and finally it sets the column
 
919
 * if cl_options.goto_column is set. */
 
920
static void set_cursor_position(gint idx, gint pos)
 
921
{
 
922
        if (cl_options.goto_line >= 0)
 
923
        {       /* goto line which was specified on command line and then undefine the line */
 
924
                sci_goto_line(doc_list[idx].sci, cl_options.goto_line - 1, TRUE);
 
925
                doc_list[idx].scroll_percent = 0.5F;
 
926
                cl_options.goto_line = -1;
 
927
        }
 
928
        else if (pos > 0)
 
929
        {
 
930
                sci_set_current_position(doc_list[idx].sci, pos, FALSE);
 
931
                doc_list[idx].scroll_percent = 0.5F;
 
932
        }
 
933
 
 
934
        if (cl_options.goto_column >= 0)
 
935
        {       /* goto column which was specified on command line and then undefine the column */
 
936
                gint cur_pos = sci_get_current_position(doc_list[idx].sci);
 
937
                sci_set_current_position(doc_list[idx].sci, cur_pos + cl_options.goto_column, FALSE);
 
938
                doc_list[idx].scroll_percent = 0.5F;
 
939
                cl_options.goto_column = -1;
 
940
        }
 
941
}
 
942
 
 
943
 
 
944
static gboolean detect_use_tabs(ScintillaObject *sci)
 
945
{
 
946
        gint line;
 
947
        gsize tabs = 0, spaces = 0;
 
948
 
 
949
        for (line = 0; line < sci_get_line_count(sci); line++)
 
950
        {
 
951
                gint pos = sci_get_position_from_line(sci, line);
 
952
                gchar c;
 
953
 
 
954
                c = sci_get_char_at(sci, pos);
 
955
                if (c == '\t')
 
956
                        tabs++;
 
957
                else
 
958
                if (c == ' ')
 
959
                {
 
960
                        /* check at least 2 spaces */
 
961
                        if (sci_get_char_at(sci, pos + 1) == ' ')
 
962
                                spaces++;
 
963
                }
 
964
        }
 
965
        if (spaces == 0 && tabs == 0)
 
966
                return editor_prefs.use_tabs;
 
967
 
 
968
        /* Skew comparison by a factor of 2 in favour of default editor pref */
 
969
        if (editor_prefs.use_tabs)
 
970
                return ! (spaces > tabs * 2);
 
971
        else
 
972
                return (tabs > spaces * 2);
 
973
}
 
974
 
 
975
 
 
976
/* To open a new file, set idx to -1; filename should be locale encoded.
 
977
 * To reload a file, set the idx for the document to be reloaded; filename should be NULL.
 
978
 * pos is the cursor position, which can be overridden by --line and --column.
 
979
 * forced_enc can be NULL to detect the file encoding.
 
980
 * Returns: idx of the opened file or -1 if an error occurred.
 
981
 *
 
982
 * When opening more than one file, either:
 
983
 * 1. Use document_open_files().
 
984
 * 2. Call document_delay_colourise() before document_open_file() and
 
985
 *    document_colourise_new() after opening all files.
 
986
 *
 
987
 * This avoids unnecessary recolourising, saving significant processing when a lot of files
 
988
 * are open of a filetype that supports user typenames, e.g. C. */
 
989
gint document_open_file_full(gint idx, const gchar *filename, gint pos, gboolean readonly,
 
990
                filetype *ft, const gchar *forced_enc)
538
991
{
539
992
        gint editor_mode;
540
 
        gsize size;
541
993
        gboolean reload = (idx == -1) ? FALSE : TRUE;
542
 
        struct stat st;
543
 
        gboolean bom = FALSE;
544
 
        gchar *enc = NULL;
545
994
        gchar *utf8_filename = NULL;
546
995
        gchar *locale_filename = NULL;
547
 
        GError *err = NULL;
548
 
        gchar *data = NULL;
 
996
        filetype *use_ft;
 
997
        FileData filedata;
549
998
 
550
 
        //struct timeval tv, tv1;
551
 
        //struct timezone tz;
552
 
        //gettimeofday(&tv, &tz);
 
999
        /*struct timeval tv, tv1;*/
 
1000
        /*struct timezone tz;*/
 
1001
        /*gettimeofday(&tv, &tz);*/
553
1002
 
554
1003
        if (reload)
555
1004
        {
558
1007
        }
559
1008
        else
560
1009
        {
561
 
                // filename must not be NULL when it is a new file
 
1010
                /* filename must not be NULL when opening a file */
562
1011
                if (filename == NULL)
563
1012
                {
564
 
                        msgwin_status_add(_("Invalid filename"));
 
1013
                        ui_set_statusbar(FALSE, _("Invalid filename"));
565
1014
                        return -1;
566
1015
                }
567
1016
 
568
 
                // try to get the UTF-8 equivalent for the filename, fallback to filename if error
 
1017
#ifdef G_OS_WIN32
 
1018
                /* if filename is a shortcut, try to resolve it */
 
1019
                locale_filename = win32_get_shortcut_target(filename);
 
1020
#else
569
1021
                locale_filename = g_strdup(filename);
 
1022
#endif
 
1023
                /* try to get the UTF-8 equivalent for the filename, fallback to filename if error */
570
1024
                utf8_filename = utils_get_utf8_from_locale(locale_filename);
571
1025
 
572
 
                // if file is already open, switch to it and go
 
1026
                /* if file is already open, switch to it and go */
573
1027
                idx = document_find_by_filename(utf8_filename, FALSE);
574
1028
                if (idx >= 0)
575
1029
                {
576
 
                        ui_add_recent_file(utf8_filename);      // either add or reorder recent item
 
1030
                        ui_add_recent_file(utf8_filename);      /* either add or reorder recent item */
577
1031
                        gtk_notebook_set_current_page(GTK_NOTEBOOK(app->notebook),
578
1032
                                        gtk_notebook_page_num(GTK_NOTEBOOK(app->notebook),
579
1033
                                        (GtkWidget*) doc_list[idx].sci));
580
 
                        sci_goto_pos(doc_list[idx].sci, pos, TRUE);
581
1034
                        g_free(utf8_filename);
582
1035
                        g_free(locale_filename);
 
1036
                        utils_check_disk_status(idx, TRUE);     /* force a file changed check */
 
1037
                        set_cursor_position(idx, pos);
583
1038
                        return idx;
584
1039
                }
585
1040
        }
586
1041
 
587
 
        if (stat(locale_filename, &st) != 0)
588
 
        {
589
 
                msgwin_status_add(_("Could not open file %s (%s)"), utf8_filename, g_strerror(errno));
590
 
                g_free(utf8_filename);
591
 
                g_free(locale_filename);
592
 
                return -1;
593
 
        }
594
 
 
595
 
#ifdef G_OS_WIN32
596
 
        if (! g_file_get_contents(utf8_filename, &data, &size, &err))
597
 
#else
598
 
        if (! g_file_get_contents(locale_filename, &data, &size, &err))
599
 
#endif
600
 
        {
601
 
                //msgwin_status_add(_("Could not open file %s (%s)"), utf8_filename, g_strerror(err->code));
602
 
                msgwin_status_add(err->message);
603
 
                g_error_free(err);
604
 
                g_free(utf8_filename);
605
 
                g_free(locale_filename);
606
 
                return -1;
607
 
        }
608
 
 
609
 
        /* Determine character encoding and convert to utf-8*/
610
 
        if (reload && forced_enc != NULL)
611
 
        {
612
 
                if (! handle_forced_encoding(&data, &size, forced_enc, &enc, &bom))
613
 
                {
614
 
                        msgwin_status_add(_("The file \"%s\" is not valid %s."), utf8_filename, forced_enc);
615
 
                        utils_beep();
616
 
                        g_free(data);
617
 
                        g_free(utf8_filename);
618
 
                        g_free(locale_filename);
619
 
                        return -1;
620
 
                }
621
 
        }
622
 
        else if (! handle_encoding(&data, &size, &enc, &bom))
623
 
        {
624
 
                msgwin_status_add(
625
 
                        _("The file \"%s\" does not look like a text file or the file encoding is not supported."),
626
 
                        utf8_filename);
627
 
                utils_beep();
628
 
                g_free(data);
629
 
                g_free(utf8_filename);
630
 
                g_free(locale_filename);
631
 
                return -1;
632
 
        }
633
 
 
634
 
        if (bom) handle_bom(&data);
635
 
 
636
 
        if (! reload) idx = document_create_new_sci(utf8_filename);
637
 
        if (idx == -1) return -1;       // really should not happen
638
 
 
639
 
        // set editor mode and add the text to the ScintillaObject
640
 
        sci_set_text(doc_list[idx].sci, data);  // NULL terminated data; avoids modifying sci
641
 
        editor_mode = utils_get_line_endings(data, size);
 
1042
        /* if default encoding for opening files is set, use it if no forced encoding is set */
 
1043
        if (prefs.default_open_encoding >= 0 && forced_enc == NULL)
 
1044
                forced_enc = encodings[prefs.default_open_encoding].charset;
 
1045
 
 
1046
        if (! load_text_file(locale_filename, utf8_filename, &filedata, forced_enc))
 
1047
        {
 
1048
                g_free(utf8_filename);
 
1049
                g_free(locale_filename);
 
1050
                return -1;
 
1051
        }
 
1052
 
 
1053
        if (! reload) idx = document_create(utf8_filename);
 
1054
        g_return_val_if_fail(idx != -1, -1);    /* really should not happen */
 
1055
 
 
1056
        sci_set_undo_collection(doc_list[idx].sci, FALSE); /* avoid creation of an undo action */
 
1057
        sci_empty_undo_buffer(doc_list[idx].sci);
 
1058
 
 
1059
        /* add the text to the ScintillaObject */
 
1060
        sci_set_readonly(doc_list[idx].sci, FALSE);     /* to allow replacing text */
 
1061
        sci_set_text(doc_list[idx].sci, filedata.data); /* NULL terminated data */
 
1062
 
 
1063
        /* detect & set line endings */
 
1064
        editor_mode = utils_get_line_endings(filedata.data, filedata.len);
642
1065
        sci_set_eol_mode(doc_list[idx].sci, editor_mode);
643
 
 
644
 
        sci_set_line_numbers(doc_list[idx].sci, app->show_linenumber_margin, 0);
645
 
        sci_set_savepoint(doc_list[idx].sci);
646
 
        sci_empty_undo_buffer(doc_list[idx].sci);
647
 
        // get the modification time from file and keep it
648
 
        doc_list[idx].mtime = st.st_mtime;
 
1066
        g_free(filedata.data);
 
1067
 
 
1068
        if (reload)
 
1069
                document_set_use_tabs(idx, doc_list[idx].use_tabs); /* resetup sci */
 
1070
        else
 
1071
        if (! editor_prefs.detect_tab_mode)
 
1072
                document_set_use_tabs(idx, editor_prefs.use_tabs);
 
1073
        else
 
1074
        {       /* detect & set tabs/spaces */
 
1075
                gboolean use_tabs = detect_use_tabs(doc_list[idx].sci);
 
1076
 
 
1077
                if (use_tabs != editor_prefs.use_tabs)
 
1078
                        ui_set_statusbar(TRUE, _("Setting %s indentation mode."),
 
1079
                                (use_tabs) ? _("Tabs") : _("Spaces"));
 
1080
                document_set_use_tabs(idx, use_tabs);
 
1081
        }
 
1082
 
 
1083
        sci_set_undo_collection(doc_list[idx].sci, TRUE);
 
1084
 
 
1085
        doc_list[idx].mtime = filedata.mtime; /* get the modification time from file and keep it */
649
1086
        doc_list[idx].changed = FALSE;
650
 
        doc_list[idx].file_name = g_strdup(utf8_filename);
651
 
        doc_list[idx].encoding = enc;
652
 
        doc_list[idx].has_bom = bom;
653
 
 
654
 
        sci_goto_pos(doc_list[idx].sci, pos, TRUE);
 
1087
        g_free(doc_list[idx].encoding); /* if reloading, free old encoding */
 
1088
        doc_list[idx].encoding = filedata.enc;
 
1089
        doc_list[idx].has_bom = filedata.bom;
 
1090
        store_saved_encoding(idx);      /* store the opened encoding for undo/redo */
 
1091
 
 
1092
        doc_list[idx].readonly = readonly || filedata.readonly;
 
1093
        sci_set_readonly(doc_list[idx].sci, doc_list[idx].readonly);
 
1094
 
 
1095
        /* update line number margin width */
 
1096
        sci_set_line_numbers(doc_list[idx].sci, editor_prefs.show_linenumber_margin, 0);
 
1097
 
 
1098
        /* set the cursor position according to pos, cl_options.goto_line and cl_options.goto_column */
 
1099
        set_cursor_position(idx, pos);
655
1100
 
656
1101
        if (! reload)
657
1102
        {
658
 
                filetype *use_ft = (ft != NULL) ? ft : filetypes_get_from_filename(utf8_filename);
659
 
 
660
 
                doc_list[idx].readonly = readonly;
661
 
                sci_set_readonly(doc_list[idx].sci, readonly);
662
 
 
663
 
                document_set_filetype(idx, use_ft);
664
 
                ui_build_show_hide(idx);
665
 
        }
666
 
        else
667
 
        {
668
 
                document_update_tag_list(idx, TRUE);
669
 
        }
670
 
 
671
 
        // "the" SCI signal (connect after initial setup(i.e. adding text))
672
 
        g_signal_connect((GtkWidget*) doc_list[idx].sci, "sci-notify",
 
1103
                /* "the" SCI signal (connect after initial setup(i.e. adding text)) */
 
1104
                g_signal_connect((GtkWidget*) doc_list[idx].sci, "sci-notify",
673
1105
                                        G_CALLBACK(on_editor_notification), GINT_TO_POINTER(idx));
674
1106
 
675
 
        document_set_text_changed(idx);
676
 
        ui_document_show_hide(idx); //update the document menu
677
 
 
678
 
        g_free(data);
679
 
 
680
 
 
681
 
        // finally add current file to recent files menu, but not the files from the last session
682
 
        if (! app->opening_session_files) ui_add_recent_file(utf8_filename);
 
1107
                use_ft = (ft != NULL) ? ft : filetypes_detect_from_file(idx);
 
1108
        }
 
1109
        else
 
1110
        {       /* reloading */
 
1111
                document_undo_clear(idx);
 
1112
                use_ft = ft;
 
1113
        }
 
1114
        /* update taglist, typedef keywords and build menu if necessary */
 
1115
        document_set_filetype(idx, use_ft);
 
1116
 
 
1117
        document_set_text_changed(idx); /* also updates tab state */
 
1118
        ui_document_show_hide(idx);     /* update the document menu */
 
1119
 
 
1120
        /* finally add current file to recent files menu, but not the files from the last session */
 
1121
        if (! main_status.opening_session_files)
 
1122
                ui_add_recent_file(utf8_filename);
 
1123
 
 
1124
        if (! reload && geany_object)
 
1125
                g_signal_emit_by_name(geany_object, "document-open", idx);
683
1126
 
684
1127
        if (reload)
685
 
                msgwin_status_add(_("File %s reloaded."), utf8_filename);
 
1128
                ui_set_statusbar(TRUE, _("File %s reloaded."), utf8_filename);
686
1129
        else
687
1130
                msgwin_status_add(_("File %s opened(%d%s)."),
688
1131
                                utf8_filename, gtk_notebook_get_n_pages(GTK_NOTEBOOK(app->notebook)),
690
1133
 
691
1134
        g_free(utf8_filename);
692
1135
        g_free(locale_filename);
693
 
        //gettimeofday(&tv1, &tz);
694
 
        //geany_debug("%s: %d", filename, (gint)(tv1.tv_usec - tv.tv_usec));
 
1136
        /*gettimeofday(&tv1, &tz);*/
 
1137
        /*geany_debug("%s: %d", filename, (gint)(tv1.tv_usec - tv.tv_usec)); */
695
1138
 
696
1139
        return idx;
697
1140
}
698
1141
 
699
1142
 
700
 
gint document_reload_file(gint idx, const gchar *forced_enc)
 
1143
/* Takes a new line separated list of filename URIs and opens each file.
 
1144
 * length is the length of the string or -1 if it should be detected */
 
1145
void document_open_file_list(const gchar *data, gssize length)
 
1146
{
 
1147
        gint i;
 
1148
        gchar *filename;
 
1149
        gchar **list;
 
1150
 
 
1151
        if (data == NULL) return;
 
1152
 
 
1153
        if (length < 0)
 
1154
                length = strlen(data);
 
1155
 
 
1156
        switch (utils_get_line_endings(data, length))
 
1157
        {
 
1158
                case SC_EOL_CR: list = g_strsplit(data, "\r", 0); break;
 
1159
                case SC_EOL_CRLF: list = g_strsplit(data, "\r\n", 0); break;
 
1160
                case SC_EOL_LF: list = g_strsplit(data, "\n", 0); break;
 
1161
                default: list = g_strsplit(data, "\n", 0);
 
1162
        }
 
1163
 
 
1164
        document_delay_colourise();
 
1165
 
 
1166
        for (i = 0; ; i++)
 
1167
        {
 
1168
                if (list[i] == NULL) break;
 
1169
                filename = g_filename_from_uri(list[i], NULL, NULL);
 
1170
                if (filename == NULL) continue;
 
1171
                document_open_file(filename, FALSE, NULL, NULL);
 
1172
                g_free(filename);
 
1173
        }
 
1174
        document_colourise_new();
 
1175
 
 
1176
        g_strfreev(list);
 
1177
}
 
1178
 
 
1179
 
 
1180
/**
 
1181
 *  Opens each file in the list @a filenames, ensuring the newly opened documents and
 
1182
 *  existing documents (if necessary) are only colourised once.
 
1183
 *  Internally, document_open_file() is called for every list item.
 
1184
 *
 
1185
 *  @param filenames A list of filenames to load, in locale encoding.
 
1186
 *  @param readonly Whether to open the %document in read-only mode.
 
1187
 *  @param ft The %filetype for the %document or @c NULL to auto-detect the %filetype.
 
1188
 *  @param forced_enc The file encoding to use or @c NULL to auto-detect the file encoding.
 
1189
 *
 
1190
 *  @return The index of the opened file or -1 if an error occurred.
 
1191
 **/
 
1192
void document_open_files(const GSList *filenames, gboolean readonly, filetype *ft,
 
1193
                const gchar *forced_enc)
 
1194
{
 
1195
        const GSList *item;
 
1196
 
 
1197
        document_delay_colourise();
 
1198
 
 
1199
        for (item = filenames; item != NULL; item = g_slist_next(item))
 
1200
        {
 
1201
                document_open_file(item->data, readonly, ft, forced_enc);
 
1202
        }
 
1203
        document_colourise_new();
 
1204
}
 
1205
 
 
1206
 
 
1207
/**
 
1208
 *  Reloads the %document with the given index @a idx with the specified file encoding
 
1209
 *  @a forced_enc or @c NULL to auto-detect the file encoding.
 
1210
 *
 
1211
 *  @param idx The %document index for the file to reload.
 
1212
 *  @param forced_enc The file encoding to use or @c NULL to auto-detect the file encoding.
 
1213
 *
 
1214
 *  @return @a TRUE if the %document was actually reloaded or @a FALSE otherwise.
 
1215
 **/
 
1216
gboolean document_reload_file(gint idx, const gchar *forced_enc)
701
1217
{
702
1218
        gint pos = 0;
703
1219
 
704
 
        if (idx < 0 || ! doc_list[idx].is_valid)
705
 
                return -1;
 
1220
        if (! DOC_IDX_VALID(idx))
 
1221
                return FALSE;
706
1222
 
707
 
        // try to set the cursor to the position before reloading
 
1223
        /* try to set the cursor to the position before reloading */
708
1224
        pos = sci_get_current_position(doc_list[idx].sci);
709
 
        return document_open_file(idx, NULL, pos, doc_list[idx].readonly,
 
1225
        idx = document_open_file_full(idx, NULL, pos, doc_list[idx].readonly,
710
1226
                                        doc_list[idx].file_type, forced_enc);
711
 
}
712
 
 
713
 
 
714
 
/* This saves the file.
715
 
 * When force is set then it is always saved, even if it is unchanged(useful when using Save As)
716
 
 * It returns whether the file could be saved or not. */
 
1227
        return (idx != -1);
 
1228
}
 
1229
 
 
1230
 
 
1231
static gboolean document_update_timestamp(gint idx)
 
1232
{
 
1233
        struct stat st;
 
1234
        gchar *locale_filename;
 
1235
 
 
1236
        g_return_val_if_fail(DOC_IDX_VALID(idx), FALSE);
 
1237
 
 
1238
        locale_filename = utils_get_locale_from_utf8(doc_list[idx].file_name);
 
1239
        if (g_stat(locale_filename, &st) != 0)
 
1240
        {
 
1241
                ui_set_statusbar(TRUE, _("Could not open file %s (%s)"), doc_list[idx].file_name,
 
1242
                        g_strerror(errno));
 
1243
                g_free(locale_filename);
 
1244
                return FALSE;
 
1245
        }
 
1246
 
 
1247
        doc_list[idx].mtime = st.st_mtime; /* get the modification time from file and keep it */
 
1248
        g_free(locale_filename);
 
1249
        return TRUE;
 
1250
}
 
1251
 
 
1252
 
 
1253
/* Sets line and column to the given position byte_pos in the document.
 
1254
 * byte_pos is the position counted in bytes, not characters */
 
1255
static void get_line_column_from_pos(gint idx, guint byte_pos, gint *line, gint *column)
 
1256
{
 
1257
        gint i;
 
1258
        gint line_start;
 
1259
 
 
1260
        /* for some reason we can use byte count instead of character count here */
 
1261
        *line = sci_get_line_from_position(doc_list[idx].sci, byte_pos);
 
1262
        line_start = sci_get_position_from_line(doc_list[idx].sci, *line);
 
1263
        /* get the column in the line */
 
1264
        *column = byte_pos - line_start;
 
1265
 
 
1266
        /* any non-ASCII characters are encoded with two bytes(UTF-8, always in Scintilla), so
 
1267
         * skip one byte(i++) and decrease the column number which is based on byte count */
 
1268
        for (i = line_start; i < (line_start + *column); i++)
 
1269
        {
 
1270
                if (sci_get_char_at(doc_list[idx].sci, i) < 0)
 
1271
                {
 
1272
                        (*column)--;
 
1273
                        i++;
 
1274
                }
 
1275
        }
 
1276
}
 
1277
 
 
1278
 
 
1279
/*
 
1280
 * Save the %document specified by @a idx, detecting the filetype.
 
1281
 *
 
1282
 * @param idx The %document index for the file to save.
 
1283
 * @return @c TRUE if the file was saved or @c FALSE if the file could not be saved.
 
1284
 * @see document_save_file().
 
1285
 */
 
1286
gboolean document_save_file_as(gint idx)
 
1287
{
 
1288
        gboolean ret;
 
1289
 
 
1290
        if (! DOC_IDX_VALID(idx)) return FALSE;
 
1291
 
 
1292
        /* detect filetype */
 
1293
        if (FILETYPE_ID(doc_list[idx].file_type) == GEANY_FILETYPES_ALL)
 
1294
        {
 
1295
                filetype *ft = filetypes_detect_from_file(idx);
 
1296
 
 
1297
                document_set_filetype(idx, ft);
 
1298
                if (document_get_cur_idx() == idx)
 
1299
                {
 
1300
                        app->ignore_callback = TRUE;
 
1301
                        filetypes_select_radio_item(doc_list[idx].file_type);
 
1302
                        app->ignore_callback = FALSE;
 
1303
                }
 
1304
        }
 
1305
        utils_replace_filename(idx);
 
1306
 
 
1307
        ret = document_save_file(idx, TRUE);
 
1308
        if (ret)
 
1309
                ui_add_recent_file(doc_list[idx].file_name);
 
1310
        return ret;
 
1311
}
 
1312
 
 
1313
 
 
1314
/**
 
1315
 *  Save the %document specified by @a idx. Saving includes replacing tabs by spaces,
 
1316
 *  stripping trailing spaces and adding a final new line at the end of the file (all only if
 
1317
 *  user enabled these features). The filetype is set again or auto-detected if it wasn't
 
1318
 *  set yet. After all, the "document-save" signal is emitted for plugins.
 
1319
 *
 
1320
 *  If the file is not modified, this functions does nothing unless force is set to @c TRUE.
 
1321
 *
 
1322
 *  @param idx The %document index for the file to save.
 
1323
 *  @param force Whether to save the file even if it is not modified (e.g. for Save As).
 
1324
 *
 
1325
 *  @return @c TRUE if the file was saved or @c FALSE if the file could not or should not be saved.
 
1326
 **/
717
1327
gboolean document_save_file(gint idx, gboolean force)
718
1328
{
719
1329
        gchar *data;
721
1331
        gint bytes_written, len;
722
1332
        gchar *locale_filename = NULL;
723
1333
 
724
 
        if (idx == -1) return FALSE;
725
 
        if (! force && ! doc_list[idx].changed) return FALSE;
 
1334
        if (! DOC_IDX_VALID(idx)) return FALSE;
 
1335
        /* the "changed" flag should exclude the "readonly" flag, but check it anyway for safety */
 
1336
        if (! force && (! doc_list[idx].changed || doc_list[idx].readonly)) return FALSE;
726
1337
 
727
1338
        if (doc_list[idx].file_name == NULL)
728
1339
        {
729
 
                msgwin_status_add(_("Error saving file."));
 
1340
                ui_set_statusbar(TRUE, _("Error saving file."));
730
1341
                utils_beep();
731
1342
                return FALSE;
732
1343
        }
733
1344
 
734
 
        // replaces tabs by spaces
735
 
        if (app->pref_editor_replace_tabs) document_replace_tabs(idx);
736
 
        // strip trailing spaces
737
 
        if (app->pref_editor_trail_space) document_strip_trailing_spaces(idx);
738
 
        // ensure the file has a newline at the end
739
 
        if (app->pref_editor_new_line) document_ensure_final_newline(idx);
740
 
        // ensure there a really the same EOL chars
741
 
        sci_convert_eols(doc_list[idx].sci, sci_get_eol_mode(doc_list[idx].sci));
 
1345
        /* replaces tabs by spaces */
 
1346
        if (prefs.replace_tabs) document_replace_tabs(idx);
 
1347
        /* strip trailing spaces */
 
1348
        if (prefs.strip_trailing_spaces) document_strip_trailing_spaces(idx);
 
1349
        /* ensure the file has a newline at the end */
 
1350
        if (prefs.final_new_line) document_ensure_final_newline(idx);
742
1351
 
743
1352
        len = sci_get_length(doc_list[idx].sci) + 1;
744
 
        if (doc_list[idx].has_bom && utils_is_unicode_charset(doc_list[idx].encoding))
745
 
        {
746
 
                data = (gchar*) g_malloc(len + 3);      // 3 chars for BOM
747
 
                data[0] = 0xef;
748
 
                data[1] = 0xbb;
749
 
                data[2] = 0xbf;
 
1353
        if (doc_list[idx].has_bom && encodings_is_unicode_charset(doc_list[idx].encoding))
 
1354
        {       /* always write a UTF-8 BOM because in this moment the text itself is still in UTF-8
 
1355
                 * encoding, it will be converted to doc_list[idx].encoding below and this conversion
 
1356
                 * also changes the BOM */
 
1357
                data = (gchar*) g_malloc(len + 3);      /* 3 chars for BOM */
 
1358
                data[0] = (gchar) 0xef;
 
1359
                data[1] = (gchar) 0xbb;
 
1360
                data[2] = (gchar) 0xbf;
750
1361
                sci_get_text(doc_list[idx].sci, len, data + 3);
751
1362
                len += 3;
752
1363
        }
756
1367
                sci_get_text(doc_list[idx].sci, len, data);
757
1368
        }
758
1369
 
759
 
        // save in original encoding, skip when it is already UTF-8
760
 
        if (doc_list[idx].encoding != NULL && ! utils_strcmp(doc_list[idx].encoding, "UTF-8"))
 
1370
        /* save in original encoding, skip when it is already UTF-8 or has the encoding "None" */
 
1371
        if (doc_list[idx].encoding != NULL && ! utils_str_equal(doc_list[idx].encoding, "UTF-8") &&
 
1372
                ! utils_str_equal(doc_list[idx].encoding, encodings[GEANY_ENCODING_NONE].charset))
761
1373
        {
762
1374
                GError *conv_error = NULL;
763
1375
                gchar* conv_file_contents = NULL;
 
1376
                gsize bytes_read;
764
1377
                gsize conv_len;
765
1378
 
766
 
                // try to convert it from UTF-8 to original encoding
 
1379
                /* try to convert it from UTF-8 to original encoding */
767
1380
                conv_file_contents = g_convert(data, len-1, doc_list[idx].encoding, "UTF-8",
768
 
                                                                                                        NULL, &conv_len, &conv_error);
 
1381
                                                                                                        &bytes_read, &conv_len, &conv_error);
769
1382
 
770
1383
                if (conv_error != NULL)
771
1384
                {
772
 
                        dialogs_show_error(
773
 
        _("An error occurred while converting the file from UTF-8 in \"%s\". The file remains unsaved."
774
 
          "\nError message: %s\n"),
775
 
                        doc_list[idx].encoding, conv_error->message);
776
 
                        geany_debug("encoding error: %s)", conv_error->message);
 
1385
                        gchar *text = g_strdup_printf(
 
1386
                                _("An error occurred while converting the file from UTF-8 in \"%s\". The file remains unsaved."),
 
1387
                                doc_list[idx].encoding);
 
1388
                        gchar *error_text;
 
1389
 
 
1390
                        if (conv_error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
 
1391
                        {
 
1392
                                gchar *context = NULL;
 
1393
                                gint line, column;
 
1394
                                gint context_len;
 
1395
                                gunichar unic;
 
1396
                                gint max_len = MIN((gint)bytes_read + 6, len - 1); /* don't read over the doc length */
 
1397
                                context = g_malloc(7); /* read 6 bytes from Sci + '\0' */
 
1398
                                sci_get_text_range(doc_list[idx].sci, bytes_read, max_len, context);
 
1399
 
 
1400
                                /* take only one valid Unicode character from the context and discard the leftover */
 
1401
                                unic = g_utf8_get_char_validated(context, -1);
 
1402
                                context_len = g_unichar_to_utf8(unic, context);
 
1403
                                context[context_len] = '\0';
 
1404
                                get_line_column_from_pos(idx, bytes_read, &line, &column);
 
1405
 
 
1406
                                error_text = g_strdup_printf(
 
1407
                                        _("Error message: %s\nThe error occurred at \"%s\" (line: %d, column: %d)."),
 
1408
                                        conv_error->message, context, line + 1, column);
 
1409
                                g_free(context);
 
1410
                        }
 
1411
                        else
 
1412
                                error_text = g_strdup_printf(_("Error message: %s."), conv_error->message);
 
1413
 
 
1414
                        geany_debug("encoding error: %s", conv_error->message);
 
1415
                        dialogs_show_msgbox_with_secondary(GTK_MESSAGE_ERROR, text, error_text);
777
1416
                        g_error_free(conv_error);
778
1417
                        g_free(data);
 
1418
                        g_free(text);
 
1419
                        g_free(error_text);
779
1420
                        return FALSE;
780
1421
                }
781
1422
                else
789
1430
        {
790
1431
                len = strlen(data);
791
1432
        }
 
1433
 
792
1434
        locale_filename = utils_get_locale_from_utf8(doc_list[idx].file_name);
793
 
        fp = fopen(locale_filename, "wb"); // this should fix the windows \n problem
 
1435
        fp = g_fopen(locale_filename, "wb");
794
1436
        g_free(locale_filename);
 
1437
 
795
1438
        if (fp == NULL)
796
1439
        {
797
 
                msgwin_status_add(_("Error saving file (%s)."), strerror(errno));
 
1440
                ui_set_statusbar(TRUE, _("Error saving file (%s)."), g_strerror(errno));
798
1441
                utils_beep();
799
1442
                g_free(data);
800
1443
                return FALSE;
806
1449
 
807
1450
        if (len != bytes_written)
808
1451
        {
809
 
                msgwin_status_add(_("Error saving file."));
 
1452
                ui_set_statusbar(TRUE, _("Error saving file."));
810
1453
                utils_beep();
811
1454
                return FALSE;
812
1455
        }
813
1456
 
814
 
        // ignore the following things if we are quitting
815
 
        if (! app->quitting)
 
1457
        /* store the opened encoding for undo/redo */
 
1458
        store_saved_encoding(idx);
 
1459
 
 
1460
        /* ignore the following things if we are quitting */
 
1461
        if (! main_status.quitting)
816
1462
        {
817
 
                gchar *basename = g_path_get_basename(doc_list[idx].file_name);
 
1463
                gchar *base_name = g_path_get_basename(doc_list[idx].file_name);
818
1464
 
819
 
                // set line numbers again, to reset the margin width, if
820
 
                // there are more lines than before
821
 
                sci_set_line_numbers(doc_list[idx].sci, app->show_linenumber_margin, 0);
 
1465
                /* set line numbers again, to reset the margin width, if
 
1466
                 * there are more lines than before */
 
1467
                sci_set_line_numbers(doc_list[idx].sci, editor_prefs.show_linenumber_margin, 0);
822
1468
                sci_set_savepoint(doc_list[idx].sci);
823
 
                doc_list[idx].mtime = time(NULL);
824
 
                if (doc_list[idx].file_type == NULL || doc_list[idx].file_type->id == GEANY_FILETYPES_ALL)
825
 
                {
826
 
                        doc_list[idx].file_type = filetypes_get_from_filename(doc_list[idx].file_name);
827
 
                        filetypes_select_radio_item(doc_list[idx].file_type);
828
 
                }
 
1469
 
 
1470
                /* stat the file to get the timestamp, otherwise on Windows the actual
 
1471
                 * timestamp can be ahead of time(NULL) */
 
1472
                document_update_timestamp(idx);
 
1473
 
 
1474
                /* update filetype-related things */
829
1475
                document_set_filetype(idx, doc_list[idx].file_type);
 
1476
 
830
1477
                tm_workspace_update(TM_WORK_OBJECT(app->tm_workspace), TRUE, TRUE, FALSE);
831
 
                gtk_label_set_text(GTK_LABEL(doc_list[idx].tab_label), basename);
832
 
                gtk_label_set_text(GTK_LABEL(doc_list[idx].tabmenu_label), basename);
833
 
                treeviews_openfiles_update(doc_list[idx].iter, doc_list[idx].file_name);
 
1478
                gtk_label_set_text(GTK_LABEL(doc_list[idx].tab_label), base_name);
 
1479
                gtk_label_set_text(GTK_LABEL(doc_list[idx].tabmenu_label), base_name);
834
1480
                msgwin_status_add(_("File %s saved."), doc_list[idx].file_name);
835
1481
                ui_update_statusbar(idx, -1);
836
 
                treeviews_openfiles_update(doc_list[idx].iter, basename);
837
 
                g_free(basename);
 
1482
                g_free(base_name);
838
1483
#ifdef HAVE_VTE
839
 
                vte_cwd(doc_list[idx].file_name);
 
1484
                vte_cwd(doc_list[idx].file_name, FALSE);
840
1485
#endif
841
 
 
 
1486
        }
 
1487
        if (geany_object)
 
1488
        {
 
1489
                g_signal_emit_by_name(geany_object, "document-save", idx);
842
1490
        }
843
1491
        return TRUE;
844
1492
}
845
1493
 
846
1494
 
847
 
#define SEARCH_NOT_FOUND_TXT _("The document has been searched completely but the match \"%s\" was not found. Wrap search around the document?")
848
 
 
849
 
/* special search function, used from the find entry in the toolbar */
850
 
void document_find_next(gint idx, const gchar *text, gint flags, gboolean find_button, gboolean inc)
 
1495
/* special search function, used from the find entry in the toolbar
 
1496
 * return TRUE if text was found otherwise FALSE
 
1497
 * return also TRUE if text is empty  */
 
1498
gboolean document_search_bar_find(gint idx, const gchar *text, gint flags, gboolean inc)
851
1499
{
852
 
        gint selection_end, search_pos;
853
 
 
854
 
        g_return_if_fail(text != NULL);
855
 
        if (idx == -1 || ! *text) return;
856
 
 
857
 
        selection_end =  sci_get_selection_end(doc_list[idx].sci);
858
 
        if (!inc && sci_can_copy(doc_list[idx].sci))
859
 
        { // there's a selection so go to the end
860
 
                sci_goto_pos(doc_list[idx].sci, selection_end, TRUE);
 
1500
        gint start_pos, search_pos;
 
1501
        struct TextToFind ttf;
 
1502
 
 
1503
        g_return_val_if_fail(text != NULL, FALSE);
 
1504
        if (! DOC_IDX_VALID(idx))
 
1505
                return FALSE;
 
1506
        if (! *text)
 
1507
                return TRUE;
 
1508
 
 
1509
        start_pos = (inc) ? sci_get_selection_start(doc_list[idx].sci) :
 
1510
                sci_get_selection_end(doc_list[idx].sci);       /* equal if no selection */
 
1511
 
 
1512
        /* search cursor to end */
 
1513
        ttf.chrg.cpMin = start_pos;
 
1514
        ttf.chrg.cpMax = sci_get_length(doc_list[idx].sci);
 
1515
        ttf.lpstrText = (gchar *)text;
 
1516
        search_pos = sci_find_text(doc_list[idx].sci, flags, &ttf);
 
1517
 
 
1518
        /* if no match, search start to cursor */
 
1519
        if (search_pos == -1)
 
1520
        {
 
1521
                ttf.chrg.cpMin = 0;
 
1522
                ttf.chrg.cpMax = start_pos + strlen(text);
 
1523
                search_pos = sci_find_text(doc_list[idx].sci, flags, &ttf);
861
1524
        }
862
1525
 
863
 
        sci_set_search_anchor(doc_list[idx].sci);
864
 
        search_pos = sci_search_next(doc_list[idx].sci, flags, text);
865
1526
        if (search_pos != -1)
866
1527
        {
867
 
                sci_scroll_caret(doc_list[idx].sci);
 
1528
                gint line = sci_get_line_from_position(doc_list[idx].sci, ttf.chrgText.cpMin);
 
1529
 
 
1530
                /* unfold maybe folded results */
 
1531
                sci_ensure_line_is_visible(doc_list[idx].sci, line);
 
1532
 
 
1533
                sci_set_selection_start(doc_list[idx].sci, ttf.chrgText.cpMin);
 
1534
                sci_set_selection_end(doc_list[idx].sci, ttf.chrgText.cpMax);
 
1535
 
 
1536
                if (! editor_line_in_view(doc_list[idx].sci, line))
 
1537
                {       /* we need to force scrolling in case the cursor is outside of the current visible area
 
1538
                         * doc_list[].scroll_percent doesn't work because sci isn't always updated
 
1539
                         * while searching */
 
1540
                        editor_scroll_to_line(doc_list[idx].sci, -1, 0.3F);
 
1541
                }
 
1542
                return TRUE;
868
1543
        }
869
1544
        else
870
1545
        {
871
 
                if (find_button)
872
 
                {
873
 
                        if (dialogs_show_question(SEARCH_NOT_FOUND_TXT, text))
874
 
                        {
875
 
                                sci_goto_pos(doc_list[idx].sci, 0, FALSE);
876
 
                                document_find_next(idx, text, flags, TRUE, inc);
877
 
                        }
878
 
                }
879
 
                else
880
 
                {
881
 
                        utils_beep();
882
 
                        sci_goto_pos(doc_list[idx].sci, 0, FALSE);
883
 
                }
 
1546
                if (! inc)
 
1547
                {
 
1548
                        ui_set_statusbar(FALSE, _("\"%s\" was not found."), text);
 
1549
                }
 
1550
                utils_beep();
 
1551
                sci_goto_pos(doc_list[idx].sci, start_pos, FALSE);      /* clear selection */
 
1552
                return FALSE;
884
1553
        }
885
1554
}
886
1555
 
888
1557
/* General search function, used from the find dialog.
889
1558
 * Returns -1 on failure or the start position of the matching text.
890
1559
 * Will skip past any selection, ignoring it. */
891
 
gint document_find_text(gint idx, const gchar *text, gint flags, gboolean search_backwards)
 
1560
gint document_find_text(gint idx, const gchar *text, gint flags, gboolean search_backwards,
 
1561
                gboolean scroll, GtkWidget *parent)
892
1562
{
893
1563
        gint selection_end, selection_start, search_pos;
894
1564
 
895
1565
        g_return_val_if_fail(text != NULL, -1);
896
1566
        if (idx == -1 || ! *text) return -1;
897
 
        // Sci doesn't support searching backwards with a regex
 
1567
        /* Sci doesn't support searching backwards with a regex */
898
1568
        if (flags & SCFIND_REGEXP) search_backwards = FALSE;
899
1569
 
900
1570
        selection_start = sci_get_selection_start(doc_list[idx].sci);
901
1571
        selection_end = sci_get_selection_end(doc_list[idx].sci);
902
1572
        if ((selection_end - selection_start) > 0)
903
 
        { // there's a selection so go to the end
 
1573
        { /* there's a selection so go to the end */
904
1574
                if (search_backwards)
905
1575
                        sci_goto_pos(doc_list[idx].sci, selection_start, TRUE);
906
1576
                else
915
1585
 
916
1586
        if (search_pos != -1)
917
1587
        {
918
 
                sci_scroll_caret(doc_list[idx].sci);
 
1588
                /* unfold maybe folded results */
 
1589
                sci_ensure_line_is_visible(doc_list[idx].sci,
 
1590
                        sci_get_line_from_position(doc_list[idx].sci, search_pos));
 
1591
                if (scroll)
 
1592
                        doc_list[idx].scroll_percent = 0.3F;
919
1593
        }
920
1594
        else
921
1595
        {
922
 
                if (dialogs_show_question(SEARCH_NOT_FOUND_TXT, text))
923
 
                {
924
 
                        sci_goto_pos(doc_list[idx].sci, (search_backwards) ? sci_get_length(doc_list[idx].sci) : 0, TRUE);
925
 
                        return document_find_text(idx, text, flags, search_backwards);
 
1596
                gint sci_len = sci_get_length(doc_list[idx].sci);
 
1597
 
 
1598
                /* if we just searched the whole text, give up searching. */
 
1599
                if ((selection_end == 0 && ! search_backwards) ||
 
1600
                        (selection_end == sci_len && search_backwards))
 
1601
                {
 
1602
                        ui_set_statusbar(FALSE, _("\"%s\" was not found."), text);
 
1603
                        utils_beep();
 
1604
                        return -1;
 
1605
                }
 
1606
 
 
1607
                /* we searched only part of the document, so ask whether to wraparound. */
 
1608
                if (prefs.suppress_search_dialogs ||
 
1609
                        dialogs_show_question_full(parent, GTK_STOCK_FIND, GTK_STOCK_CANCEL,
 
1610
                                _("Wrap search and find again?"), _("\"%s\" was not found."), text))
 
1611
                {
 
1612
                        gint ret;
 
1613
 
 
1614
                        sci_set_current_position(doc_list[idx].sci, (search_backwards) ? sci_len : 0, FALSE);
 
1615
                        ret = document_find_text(idx, text, flags, search_backwards, scroll, parent);
 
1616
                        if (ret == -1)
 
1617
                        {       /* return to original cursor position if not found */
 
1618
                                sci_set_current_position(doc_list[idx].sci, selection_start, FALSE);
 
1619
                        }
 
1620
                        return ret;
926
1621
                }
927
1622
        }
928
1623
        return search_pos;
929
1624
}
930
1625
 
931
1626
 
932
 
/* Replaces the selection if it matches, otherwise just finds the next match */
933
 
void document_replace_text(gint idx, const gchar *find_text, const gchar *replace_text,
934
 
        gint flags, gboolean search_backwards)
 
1627
/* Replaces the selection if it matches, otherwise just finds the next match.
 
1628
 * Returns: start of replaced text, or -1 if no replacement was made */
 
1629
gint document_replace_text(gint idx, const gchar *find_text, const gchar *replace_text,
 
1630
                gint flags, gboolean search_backwards)
935
1631
{
936
1632
        gint selection_end, selection_start, search_pos;
937
1633
 
938
 
        g_return_if_fail(find_text != NULL && replace_text != NULL);
939
 
        if (idx == -1 || ! *find_text) return;
940
 
        // Sci doesn't support searching backwards with a regex
 
1634
        g_return_val_if_fail(find_text != NULL && replace_text != NULL, -1);
 
1635
        if (idx == -1 || ! *find_text) return -1;
 
1636
 
 
1637
        /* Sci doesn't support searching backwards with a regex */
941
1638
        if (flags & SCFIND_REGEXP) search_backwards = FALSE;
942
1639
 
943
 
        selection_start =  sci_get_selection_start(doc_list[idx].sci);
944
 
        selection_end =  sci_get_selection_end(doc_list[idx].sci);
 
1640
        selection_start = sci_get_selection_start(doc_list[idx].sci);
 
1641
        selection_end = sci_get_selection_end(doc_list[idx].sci);
945
1642
        if (selection_end == selection_start)
946
1643
        {
947
 
                // no selection so just find the next match
948
 
                document_find_text(idx, find_text, flags, search_backwards);
949
 
                return;
 
1644
                /* no selection so just find the next match */
 
1645
                document_find_text(idx, find_text, flags, search_backwards, TRUE, NULL);
 
1646
                return -1;
950
1647
        }
951
 
        // there's a selection so go to the start before finding to search through it
952
 
        // this ensures there is a match
 
1648
        /* there's a selection so go to the start before finding to search through it
 
1649
         * this ensures there is a match */
953
1650
        if (search_backwards)
954
1651
                sci_goto_pos(doc_list[idx].sci, selection_end, TRUE);
955
1652
        else
956
1653
                sci_goto_pos(doc_list[idx].sci, selection_start, TRUE);
957
1654
 
958
 
        search_pos = document_find_text(idx, find_text, flags, search_backwards);
959
 
        // return if the original selected text did not match (at the start of the selection)
960
 
        if (search_pos != selection_start) return;
 
1655
        search_pos = document_find_text(idx, find_text, flags, search_backwards, TRUE, NULL);
 
1656
        /* return if the original selected text did not match (at the start of the selection) */
 
1657
        if (search_pos != selection_start) return -1;
961
1658
 
962
1659
        if (search_pos != -1)
963
1660
        {
964
1661
                gint replace_len;
965
 
                // search next/prev will select matching text, which we use to set the replace target
 
1662
                /* search next/prev will select matching text, which we use to set the replace target */
966
1663
                sci_target_from_selection(doc_list[idx].sci);
967
1664
                replace_len = sci_target_replace(doc_list[idx].sci, replace_text, flags & SCFIND_REGEXP);
968
 
                // select the replacement - find text will skip past the selected text
 
1665
                /* select the replacement - find text will skip past the selected text */
969
1666
                sci_set_selection_start(doc_list[idx].sci, search_pos);
970
1667
                sci_set_selection_end(doc_list[idx].sci, search_pos + replace_len);
971
 
                document_find_text(idx, find_text, flags, search_backwards);
972
1668
        }
973
1669
        else
974
1670
        {
975
 
                // no match in the selection
 
1671
                /* no match in the selection */
976
1672
                utils_beep();
977
1673
        }
978
 
}
979
 
 
980
 
 
981
 
/* Returns -1 if no text found or the new range endpoint after replacing. */
982
 
static gint
 
1674
        return search_pos;
 
1675
}
 
1676
 
 
1677
 
 
1678
static void show_replace_summary(gint idx, gint count, const gchar *find_text,
 
1679
                const gchar *replace_text, gboolean escaped_chars)
 
1680
{
 
1681
        gchar *escaped_find_text, *escaped_replace_text, *filename;
 
1682
 
 
1683
        if (count == 0)
 
1684
        {
 
1685
                ui_set_statusbar(FALSE, _("No matches found for \"%s\"."), find_text);
 
1686
                return;
 
1687
        }
 
1688
 
 
1689
        filename = g_path_get_basename(DOC_FILENAME(idx));
 
1690
 
 
1691
        if (escaped_chars)
 
1692
        {       /* escape special characters for showing */
 
1693
                escaped_find_text = g_strescape(find_text, NULL);
 
1694
                escaped_replace_text = g_strescape(replace_text, NULL);
 
1695
                ui_set_statusbar(TRUE, _("%s: replaced %d occurrence(s) of \"%s\" with \"%s\"."),
 
1696
                                                filename, count, escaped_find_text, escaped_replace_text);
 
1697
                g_free(escaped_find_text);
 
1698
                g_free(escaped_replace_text);
 
1699
        }
 
1700
        else
 
1701
        {
 
1702
                ui_set_statusbar(TRUE, _("%s: replaced %d occurrence(s) of \"%s\" with \"%s\"."),
 
1703
                                                filename, count, find_text, replace_text);
 
1704
        }
 
1705
        g_free(filename);
 
1706
}
 
1707
 
 
1708
 
 
1709
/* Replace all text matches in a certain range within document idx.
 
1710
 * If not NULL, *new_range_end is set to the new range endpoint after replacing,
 
1711
 * or -1 if no text was found.
 
1712
 * scroll_to_match is whether to scroll the last replacement in view (which also
 
1713
 * clears the selection).
 
1714
 * Returns: the number of replacements made. */
 
1715
static guint
983
1716
document_replace_range(gint idx, const gchar *find_text, const gchar *replace_text,
984
 
        gint flags, gint start, gint end, gboolean escaped_chars)
 
1717
        gint flags, gint start, gint end, gboolean scroll_to_match, gint *new_range_end)
985
1718
{
986
 
        gint search_pos;
987
1719
        gint count = 0;
988
 
        gint find_len = 0, replace_len = 0;
989
 
        gboolean match_found = FALSE;
990
 
        gchar *escaped_find_text, *escaped_replace_text;
991
1720
        struct TextToFind ttf;
992
 
 
993
 
        g_return_val_if_fail(find_text != NULL && replace_text != NULL, FALSE);
994
 
        if (idx == -1 || ! *find_text) return FALSE;
995
 
 
996
 
        sci_start_undo_action(doc_list[idx].sci);
 
1721
        ScintillaObject *sci;
 
1722
 
 
1723
        if (new_range_end != NULL)
 
1724
                *new_range_end = -1;
 
1725
        g_return_val_if_fail(find_text != NULL && replace_text != NULL, 0);
 
1726
        if (idx == -1 || ! *find_text || doc_list[idx].readonly) return 0;
 
1727
 
 
1728
        sci = doc_list[idx].sci;
 
1729
 
 
1730
        sci_start_undo_action(sci);
997
1731
        ttf.chrg.cpMin = start;
998
1732
        ttf.chrg.cpMax = end;
999
1733
        ttf.lpstrText = (gchar*)find_text;
1000
1734
 
1001
1735
        while (TRUE)
1002
1736
        {
1003
 
                search_pos = sci_find_text(doc_list[idx].sci, flags, &ttf);
1004
 
                if (search_pos == -1) break;
 
1737
                gint search_pos;
 
1738
                gint find_len = 0, replace_len = 0;
 
1739
 
 
1740
                search_pos = sci_find_text(sci, flags, &ttf);
1005
1741
                find_len = ttf.chrgText.cpMax - ttf.chrgText.cpMin;
 
1742
                if (search_pos == -1)
 
1743
                        break;  /* no more matches */
 
1744
                if (find_len == 0 && ! NZV(replace_text))
 
1745
                        break;  /* nothing to do */
1006
1746
 
1007
1747
                if (search_pos + find_len > end)
1008
 
                        break; //found text is partly out of range
 
1748
                        break;  /* found text is partly out of range */
1009
1749
                else
1010
1750
                {
1011
 
                        match_found = TRUE;
1012
 
                        sci_target_start(doc_list[idx].sci, search_pos);
1013
 
                        sci_target_end(doc_list[idx].sci, search_pos + find_len);
1014
 
                        replace_len = sci_target_replace(doc_list[idx].sci, replace_text,
 
1751
                        gint movepastEOL = 0;
 
1752
 
 
1753
                        sci_target_start(sci, search_pos);
 
1754
                        sci_target_end(sci, search_pos + find_len);
 
1755
 
 
1756
                        if (find_len <= 0)
 
1757
                        {
 
1758
                                gchar chNext = sci_get_char_at(sci, SSM(sci, SCI_GETTARGETEND, 0, 0));
 
1759
 
 
1760
                                if (chNext == '\r' || chNext == '\n')
 
1761
                                        movepastEOL = 1;
 
1762
                        }
 
1763
                        replace_len = sci_target_replace(sci, replace_text,
1015
1764
                                flags & SCFIND_REGEXP);
1016
 
                        ttf.chrg.cpMin = search_pos + replace_len; //next search starts after replacement
1017
 
                        end += replace_len - find_len; //update end of range now text has changed
 
1765
                        count++;
 
1766
                        if (search_pos == end)
 
1767
                                break;  /* Prevent hang when replacing regex $ */
 
1768
 
 
1769
                        /* make the next search start after the replaced text */
 
1770
                        start = search_pos + replace_len + movepastEOL;
 
1771
                        if (find_len == 0)
 
1772
                                start = SSM(sci, SCI_POSITIONAFTER, start, 0);  /* prevent '[ ]*' regex rematching part of replaced text */
 
1773
                        ttf.chrg.cpMin = start;
 
1774
                        end += replace_len - find_len;  /* update end of range now text has changed */
1018
1775
                        ttf.chrg.cpMax = end;
1019
 
                        count++;
1020
1776
                }
1021
1777
        }
1022
 
        sci_end_undo_action(doc_list[idx].sci);
1023
 
 
1024
 
        if (escaped_chars)
1025
 
        {       // escape special characters for showing
1026
 
                escaped_find_text = g_strescape(find_text, NULL);
1027
 
                escaped_replace_text = g_strescape(replace_text, NULL);
1028
 
                msgwin_status_add(_("Replaced %d occurrences of \"%s\" with \"%s\"."),
1029
 
                                                        count, escaped_find_text, escaped_replace_text);
1030
 
                g_free(escaped_find_text);
1031
 
                g_free(escaped_replace_text);
1032
 
        }
1033
 
        else
1034
 
        {
1035
 
                msgwin_status_add(_("Replaced %d occurrences of \"%s\" with \"%s\"."),
1036
 
                                                        count, find_text, replace_text);
1037
 
        }
1038
 
 
1039
 
 
1040
 
        if (match_found)
1041
 
        {
1042
 
                // scroll last match in view.
1043
 
                sci_goto_pos(doc_list[idx].sci, ttf.chrg.cpMin, TRUE);
1044
 
                return end;
1045
 
        }
1046
 
        else
1047
 
                return -1; //no text was found
 
1778
        sci_end_undo_action(sci);
 
1779
 
 
1780
        if (count > 0)
 
1781
        {       /* scroll last match in view, will destroy the existing selection */
 
1782
                if (scroll_to_match)
 
1783
                        sci_goto_pos(sci, ttf.chrg.cpMin, TRUE);
 
1784
 
 
1785
                if (new_range_end != NULL)
 
1786
                        *new_range_end = end;
 
1787
        }
 
1788
        return count;
1048
1789
}
1049
1790
 
1050
1791
 
1051
1792
void document_replace_sel(gint idx, const gchar *find_text, const gchar *replace_text, gint flags,
1052
1793
                                                  gboolean escaped_chars)
1053
1794
{
1054
 
        gint selection_end, selection_start;
 
1795
        gint selection_end, selection_start, selection_mode, selected_lines, last_line = 0;
 
1796
        gint max_column = 0, count = 0;
 
1797
        gboolean replaced = FALSE;
1055
1798
 
1056
1799
        g_return_if_fail(find_text != NULL && replace_text != NULL);
1057
1800
        if (idx == -1 || ! *find_text) return;
1058
1801
 
1059
1802
        selection_start = sci_get_selection_start(doc_list[idx].sci);
1060
1803
        selection_end = sci_get_selection_end(doc_list[idx].sci);
 
1804
        /* do we have a selection? */
1061
1805
        if ((selection_end - selection_start) == 0)
1062
1806
        {
1063
1807
                utils_beep();
1064
1808
                return;
1065
1809
        }
1066
1810
 
1067
 
        selection_end = document_replace_range(idx, find_text, replace_text, flags,
1068
 
                selection_start, selection_end, escaped_chars);
1069
 
        if (selection_end == -1)
 
1811
        selection_mode = sci_get_selection_mode(doc_list[idx].sci);
 
1812
        selected_lines = sci_get_lines_selected(doc_list[idx].sci);
 
1813
        /* handle rectangle, multi line selections (it doesn't matter on a single line) */
 
1814
        if (selection_mode == SC_SEL_RECTANGLE && selected_lines > 1)
 
1815
        {
 
1816
                gint first_line, line;
 
1817
 
 
1818
                sci_start_undo_action(doc_list[idx].sci);
 
1819
 
 
1820
                first_line = sci_get_line_from_position(doc_list[idx].sci, selection_start);
 
1821
                /* Find the last line with chars selected (not EOL char) */
 
1822
                last_line = sci_get_line_from_position(doc_list[idx].sci,
 
1823
                        selection_end - utils_get_eol_char_len(idx));
 
1824
                last_line = MAX(first_line, last_line);
 
1825
                for (line = first_line; line < (first_line + selected_lines); line++)
 
1826
                {
 
1827
                        gint line_start = sci_get_pos_at_line_sel_start(doc_list[idx].sci, line);
 
1828
                        gint line_end = sci_get_pos_at_line_sel_end(doc_list[idx].sci, line);
 
1829
 
 
1830
                        /* skip line if there is no selection */
 
1831
                        if (line_start != INVALID_POSITION)
 
1832
                        {
 
1833
                                /* don't let document_replace_range() scroll to match to keep our selection */
 
1834
                                gint new_sel_end;
 
1835
 
 
1836
                                count += document_replace_range(idx, find_text, replace_text, flags,
 
1837
                                                                line_start, line_end, FALSE, &new_sel_end);
 
1838
                                if (new_sel_end != -1)
 
1839
                                {
 
1840
                                        replaced = TRUE;
 
1841
                                        /* this gets the greatest column within the selection after replacing */
 
1842
                                        max_column = MAX(max_column,
 
1843
                                                new_sel_end - sci_get_position_from_line(doc_list[idx].sci, line));
 
1844
                                }
 
1845
                        }
 
1846
                }
 
1847
                sci_end_undo_action(doc_list[idx].sci);
 
1848
        }
 
1849
        else    /* handle normal line selection */
 
1850
        {
 
1851
                count += document_replace_range(idx, find_text, replace_text, flags,
 
1852
                                                selection_start, selection_end, TRUE, &selection_end);
 
1853
                if (selection_end != -1)
 
1854
                        replaced = TRUE;
 
1855
        }
 
1856
 
 
1857
        if (replaced)
 
1858
        {       /* update the selection for the new endpoint */
 
1859
 
 
1860
                if (selection_mode == SC_SEL_RECTANGLE && selected_lines > 1)
 
1861
                {
 
1862
                        /* now we can scroll to the selection and destroy it because we rebuild it later */
 
1863
                        /*sci_goto_pos(doc_list[idx].sci, selection_start, FALSE);*/
 
1864
 
 
1865
                        /* Note: the selection will be wrapped to last_line + 1 if max_column is greater than
 
1866
                         * the highest column on the last line. The wrapped selection is completely different
 
1867
                         * from the original one, so skip the selection at all */
 
1868
                        /* TODO is there a better way to handle the wrapped selection? */
 
1869
                        if ((sci_get_line_length(doc_list[idx].sci, last_line) - 1) >= max_column)
 
1870
                        {       /* for keeping and adjusting the selection in multi line rectangle selection we
 
1871
                                 * need the last line of the original selection and the greatest column number after
 
1872
                                 * replacing and set the selection end to the last line at the greatest column */
 
1873
                                sci_set_selection_start(doc_list[idx].sci, selection_start);
 
1874
                                sci_set_selection_end(doc_list[idx].sci,
 
1875
                                        sci_get_position_from_line(doc_list[idx].sci, last_line) + max_column);
 
1876
                                sci_set_selection_mode(doc_list[idx].sci, selection_mode);
 
1877
                        }
 
1878
                }
 
1879
                else
 
1880
                {
 
1881
                        sci_set_selection_start(doc_list[idx].sci, selection_start);
 
1882
                        sci_set_selection_end(doc_list[idx].sci, selection_end);
 
1883
                }
 
1884
        }
 
1885
        else /* no replacements */
1070
1886
                utils_beep();
1071
 
        else
1072
 
        {
1073
 
                //update the selection for the new endpoint
1074
 
                sci_set_selection_start(doc_list[idx].sci, selection_start);
1075
 
                sci_set_selection_end(doc_list[idx].sci, selection_end);
1076
 
        }
 
1887
 
 
1888
        show_replace_summary(idx, count, find_text, replace_text, escaped_chars);
1077
1889
}
1078
1890
 
1079
1891
 
1080
 
void document_replace_all(gint idx, const gchar *find_text, const gchar *replace_text,
1081
 
                                                  gint flags, gboolean escaped_chars)
 
1892
/* returns TRUE if at least one replacement was made. */
 
1893
gboolean document_replace_all(gint idx, const gchar *find_text, const gchar *replace_text,
 
1894
                gint flags, gboolean escaped_chars)
1082
1895
{
1083
 
        gint len;
1084
 
        g_return_if_fail(find_text != NULL && replace_text != NULL);
1085
 
        if (idx == -1 || ! *find_text) return;
 
1896
        gint len, count;
 
1897
        g_return_val_if_fail(find_text != NULL && replace_text != NULL, FALSE);
 
1898
        if (idx == -1 || ! *find_text) return FALSE;
1086
1899
 
1087
1900
        len = sci_get_length(doc_list[idx].sci);
1088
 
        if (document_replace_range(idx, find_text, replace_text, flags, 0, len, escaped_chars) == -1)
1089
 
                utils_beep();
 
1901
        count = document_replace_range(
 
1902
                        idx, find_text, replace_text, flags, 0, len, TRUE, NULL);
 
1903
 
 
1904
        show_replace_summary(idx, count, find_text, replace_text, escaped_chars);
 
1905
        return (count > 0);
1090
1906
}
1091
1907
 
1092
1908
 
1096
1912
 
1097
1913
        for (style = 0; style <= 127; style++)
1098
1914
                sci_set_font(doc_list[idx].sci, style, font_name, size);
1099
 
        // line number and braces
 
1915
        /* line number and braces */
1100
1916
        sci_set_font(doc_list[idx].sci, STYLE_LINENUMBER, font_name, size);
1101
1917
        sci_set_font(doc_list[idx].sci, STYLE_BRACELIGHT, font_name, size);
1102
1918
        sci_set_font(doc_list[idx].sci, STYLE_BRACEBAD, font_name, size);
1103
 
        // zoom to 100% to prevent confusion
 
1919
        /* zoom to 100% to prevent confusion */
1104
1920
        sci_zoom_off(doc_list[idx].sci);
1105
1921
}
1106
1922
 
1107
1923
 
1108
1924
void document_update_tag_list(gint idx, gboolean update)
1109
1925
{
1110
 
        // if the filetype doesn't has a tag parser or it is a new file, leave
 
1926
        /* We must call treeviews_update_tag_list() before returning,
 
1927
         * to ensure that the symbol list is always updated properly (e.g.
 
1928
         * when creating a new document with a partial filename set. */
 
1929
        gboolean success = FALSE;
 
1930
 
 
1931
        /* if the filetype doesn't have a tag parser or it is a new file */
1111
1932
        if (idx == -1 || doc_list[idx].file_type == NULL ||
1112
 
                ! doc_list[idx].file_type->has_tags || ! doc_list[idx].file_name) return;
 
1933
                app->tm_workspace == NULL ||
 
1934
                ! filetype_has_tags(doc_list[idx].file_type) || ! doc_list[idx].file_name)
 
1935
        {
 
1936
                /* set the default (empty) tag list */
 
1937
                treeviews_update_tag_list(idx, FALSE);
 
1938
                return;
 
1939
        }
1113
1940
 
1114
1941
        if (doc_list[idx].tm_file == NULL)
1115
1942
        {
1116
1943
                gchar *locale_filename = utils_get_locale_from_utf8(doc_list[idx].file_name);
1117
 
                doc_list[idx].tm_file = tm_source_file_new(locale_filename, FALSE,
1118
 
                                                                                                   doc_list[idx].file_type->name);
 
1944
 
 
1945
                doc_list[idx].tm_file = tm_source_file_new(
 
1946
                                locale_filename, FALSE, doc_list[idx].file_type->name);
1119
1947
                g_free(locale_filename);
1120
 
                if (! doc_list[idx].tm_file) return;
1121
 
                tm_workspace_add_object(doc_list[idx].tm_file);
1122
 
                if (update)
1123
 
                        tm_source_file_update(doc_list[idx].tm_file, TRUE, FALSE, TRUE);
1124
 
                ui_update_tag_list(idx, TRUE);
 
1948
 
 
1949
                if (doc_list[idx].tm_file)
 
1950
                {
 
1951
                        if (!tm_workspace_add_object(doc_list[idx].tm_file))
 
1952
                        {
 
1953
                                tm_work_object_free(doc_list[idx].tm_file);
 
1954
                                doc_list[idx].tm_file = NULL;
 
1955
                        }
 
1956
                        else
 
1957
                        {
 
1958
                                if (update)
 
1959
                                        tm_source_file_update(doc_list[idx].tm_file, TRUE, FALSE, TRUE);
 
1960
                                success = TRUE;
 
1961
                        }
 
1962
                }
1125
1963
        }
1126
1964
        else
1127
1965
        {
1128
 
                if (tm_source_file_update(doc_list[idx].tm_file, TRUE, FALSE, TRUE))
1129
 
                {
1130
 
                        ui_update_tag_list(idx, TRUE);
1131
 
                }
1132
 
                else
1133
 
                {
 
1966
                success = tm_source_file_update(doc_list[idx].tm_file, TRUE, FALSE, TRUE);
 
1967
                if (! success)
1134
1968
                        geany_debug("tag list updating failed");
1135
 
                }
1136
 
        }
1137
 
}
1138
 
 
1139
 
 
1140
 
/* sets the filetype of the the document (sets syntax highlighting and tagging) */
 
1969
        }
 
1970
        treeviews_update_tag_list(idx, success);
 
1971
}
 
1972
 
 
1973
 
 
1974
/* Caches the list of project typenames, as a space separated GString.
 
1975
 * Returns: TRUE if typenames have changed.
 
1976
 * (*types) is set to the list of typenames, or NULL if there are none. */
 
1977
static gboolean get_project_typenames(const GString **types, gint lang)
 
1978
{
 
1979
        static GString *last_typenames = NULL;
 
1980
        GString *s = NULL;
 
1981
 
 
1982
        if (app->tm_workspace)
 
1983
        {
 
1984
                GPtrArray *tags_array = app->tm_workspace->work_object.tags_array;
 
1985
 
 
1986
                if (tags_array)
 
1987
                {
 
1988
                        s = symbols_find_tags_as_string(tags_array, TM_GLOBAL_TYPE_MASK, lang);
 
1989
                }
 
1990
        }
 
1991
 
 
1992
        if (s && last_typenames && g_string_equal(s, last_typenames))
 
1993
        {
 
1994
                g_string_free(s, TRUE);
 
1995
                *types = last_typenames;
 
1996
                return FALSE;   /* project typenames haven't changed */
 
1997
        }
 
1998
        /* cache typename list for next time */
 
1999
        if (last_typenames)
 
2000
                g_string_free(last_typenames, TRUE);
 
2001
        last_typenames = s;
 
2002
 
 
2003
        *types = s;
 
2004
        if (s == NULL) return FALSE;
 
2005
        return TRUE;
 
2006
}
 
2007
 
 
2008
 
 
2009
/* If sci is NULL, update project typenames for all documents that support typenames,
 
2010
 * if typenames have changed.
 
2011
 * If sci is not NULL, then if sci supports typenames, project typenames are updated
 
2012
 * if necessary, and typename keywords are set for sci.
 
2013
 * Returns: TRUE if any scintilla type keywords were updated. */
 
2014
static gboolean update_type_keywords(ScintillaObject *sci, gint lang)
 
2015
{
 
2016
        gboolean ret = FALSE;
 
2017
        guint n;
 
2018
        const GString *s;
 
2019
 
 
2020
        if (sci != NULL && editor_lexer_get_type_keyword_idx(sci_get_lexer(sci)) == -1)
 
2021
                return FALSE;
 
2022
 
 
2023
        if (! get_project_typenames(&s, lang))
 
2024
        {       /* typenames have not changed */
 
2025
                if (s != NULL && sci != NULL)
 
2026
                {
 
2027
                        gint keyword_idx = editor_lexer_get_type_keyword_idx(sci_get_lexer(sci));
 
2028
 
 
2029
                        sci_set_keywords(sci, keyword_idx, s->str);
 
2030
                        if (! delay_colourise)
 
2031
                        {
 
2032
                                sci_colourise(sci, 0, -1);
 
2033
                        }
 
2034
                }
 
2035
                return FALSE;
 
2036
        }
 
2037
        g_return_val_if_fail(s != NULL, FALSE);
 
2038
 
 
2039
        for (n = 0; n < doc_array->len; n++)
 
2040
        {
 
2041
                ScintillaObject *wid = doc_list[n].sci;
 
2042
 
 
2043
                if (wid)
 
2044
                {
 
2045
                        gint keyword_idx = editor_lexer_get_type_keyword_idx(sci_get_lexer(wid));
 
2046
 
 
2047
                        if (keyword_idx > 0)
 
2048
                        {
 
2049
                                sci_set_keywords(wid, keyword_idx, s->str);
 
2050
                                if (! delay_colourise)
 
2051
                                {
 
2052
                                        sci_colourise(wid, 0, -1);
 
2053
                                }
 
2054
                                ret = TRUE;
 
2055
                        }
 
2056
                }
 
2057
        }
 
2058
        return ret;
 
2059
}
 
2060
 
 
2061
 
 
2062
/* sets the filetype of the document (sets syntax highlighting and tagging) */
1141
2063
void document_set_filetype(gint idx, filetype *type)
1142
2064
{
1143
 
        if (! type || idx < 0) return;
1144
 
        if (type->id > GEANY_MAX_FILE_TYPES) return;
1145
 
 
1146
 
        geany_debug("%s : %s (%s)",     doc_list[idx].file_name, type->name, doc_list[idx].encoding);
1147
 
        doc_list[idx].file_type = type;
 
2065
        gboolean colourise = FALSE;
 
2066
        gboolean ft_changed;
 
2067
 
 
2068
        if (type == NULL || ! DOC_IDX_VALID(idx))
 
2069
                return;
 
2070
 
 
2071
        geany_debug("%s : %s (%s)",
 
2072
                (doc_list[idx].file_name != NULL) ? doc_list[idx].file_name : "unknown",
 
2073
                (type->name != NULL) ? type->name : "unknown",
 
2074
                (doc_list[idx].encoding != NULL) ? doc_list[idx].encoding : "unknown");
 
2075
 
 
2076
        ft_changed = (doc_list[idx].file_type != type);
 
2077
        if (ft_changed) /* filetype has changed */
 
2078
        {
 
2079
                doc_list[idx].file_type = type;
 
2080
 
 
2081
                /* delete tm file object to force creation of a new one */
 
2082
                if (doc_list[idx].tm_file != NULL)
 
2083
                {
 
2084
                        tm_workspace_remove_object(doc_list[idx].tm_file, TRUE, TRUE);
 
2085
                        doc_list[idx].tm_file = NULL;
 
2086
                }
 
2087
                highlighting_set_styles(doc_list[idx].sci, type->id);
 
2088
                build_menu_update(idx);
 
2089
                colourise = TRUE;
 
2090
        }
 
2091
 
1148
2092
        document_update_tag_list(idx, TRUE);
1149
 
        type->style_func_ptr(doc_list[idx].sci);
1150
 
 
1151
 
        // For C/C++/Java files, get list of typedefs for colourising
1152
 
        if (sci_get_lexer(doc_list[idx].sci) == SCLEX_CPP)
1153
 
        {
1154
 
                guint j, n;
1155
 
 
1156
 
                // assign project keywords
1157
 
                if ((app->tm_workspace) && (app->tm_workspace->work_object.tags_array))
1158
 
                {
1159
 
                        GPtrArray *typedefs = tm_tags_extract(app->tm_workspace->work_object.tags_array,
1160
 
                                                                        tm_tag_typedef_t | tm_tag_struct_t | tm_tag_class_t);
1161
 
                        if ((typedefs) && (typedefs->len > 0))
1162
 
                        {
1163
 
                                GString *s = g_string_sized_new(typedefs->len * 10);
1164
 
                                for (j = 0; j < typedefs->len; ++j)
1165
 
                                {
1166
 
                                        if (!(TM_TAG(typedefs->pdata[j])->atts.entry.scope))
1167
 
                                        {
1168
 
                                                if (TM_TAG(typedefs->pdata[j])->name)
1169
 
                                                {
1170
 
                                                        g_string_append(s, TM_TAG(typedefs->pdata[j])->name);
1171
 
                                                        g_string_append_c(s, ' ');
1172
 
                                                }
1173
 
                                        }
1174
 
                                }
1175
 
                                for (n = 0; n < GEANY_MAX_OPEN_FILES; n++)
1176
 
                                {
1177
 
                                        if (doc_list[n].sci)
1178
 
                                        {
1179
 
                                                sci_set_keywords(doc_list[n].sci, 3, s->str);
1180
 
                                                sci_colourise(doc_list[n].sci, 0, -1);
1181
 
                                        }
1182
 
                                }
1183
 
                                //SSM(doc_list[idx].sci, SCI_SETKEYWORDS, 3, (sptr_t) s->str);
1184
 
                                g_string_free(s, TRUE);
1185
 
                        }
1186
 
                        g_ptr_array_free(typedefs, TRUE);
1187
 
                }
1188
 
        }
1189
 
        sci_colourise(doc_list[idx].sci, 0, -1);
1190
 
        ui_build_show_hide(idx);
 
2093
        if (! delay_colourise)
 
2094
        {
 
2095
                /* Check if project typename keywords have changed.
 
2096
                 * If they haven't, we may need to colourise the document. */
 
2097
                if (! update_type_keywords(doc_list[idx].sci, type->lang) && colourise)
 
2098
                        sci_colourise(doc_list[idx].sci, 0, -1);
 
2099
        }
 
2100
        if (ft_changed)
 
2101
        {
 
2102
                utils_get_current_function(-1, NULL);
 
2103
                ui_update_statusbar(idx, -1);
 
2104
        }
1191
2105
}
1192
2106
 
1193
2107
 
1205
2119
}
1206
2120
 
1207
2121
 
1208
 
/// TODO move me to filetypes.c
1209
 
gchar *document_prepare_template(filetype *ft)
1210
 
{
1211
 
        gchar *gpl_notice = NULL;
1212
 
        gchar *template = NULL;
1213
 
        gchar *ft_template = NULL;
1214
 
 
1215
 
        if (ft != NULL)
1216
 
        {
1217
 
                switch (ft->id)
1218
 
                {
1219
 
                        case GEANY_FILETYPES_PHP:
1220
 
                        {       // PHP: include the comment in <?php ?> - tags
1221
 
                                gchar *tmp = templates_get_template_fileheader(
1222
 
                                                GEANY_TEMPLATE_FILEHEADER, ft->extension, -1);
1223
 
                                gpl_notice = g_strconcat("<?php\n", tmp, "?>\n\n", NULL);
1224
 
                                g_free(tmp);
1225
 
                                break;
1226
 
                        }
1227
 
                        case GEANY_FILETYPES_HTML:
1228
 
                        {       // HTML: include the comment in <!-- --> - tags
1229
 
                                gchar *tmp = templates_get_template_fileheader(
1230
 
                                                GEANY_TEMPLATE_FILEHEADER, ft->extension, -1);
1231
 
                                gpl_notice = g_strconcat("<!--\n", tmp, "-->\n\n", NULL);
1232
 
                                g_free(tmp);
1233
 
                                break;
1234
 
                        }
1235
 
                        case GEANY_FILETYPES_PASCAL:
1236
 
                        {       // Pascal: comments are in { } brackets
1237
 
                                gpl_notice = templates_get_template_fileheader(
1238
 
                                                GEANY_TEMPLATE_FILEHEADER_PASCAL, ft->extension, -1);
1239
 
                                break;
1240
 
                        }
1241
 
                        case GEANY_FILETYPES_PYTHON:
1242
 
                        case GEANY_FILETYPES_RUBY:
1243
 
                        case GEANY_FILETYPES_SH:
1244
 
                        case GEANY_FILETYPES_MAKE:
1245
 
                        case GEANY_FILETYPES_PERL:
1246
 
                        {
1247
 
                                gpl_notice = templates_get_template_fileheader(
1248
 
                                                GEANY_TEMPLATE_FILEHEADER_ROUTE, ft->extension, -1);
1249
 
                                break;
1250
 
                        }
1251
 
                        default:
1252
 
                        {       // -> C, C++, Java, ...
1253
 
                                gpl_notice = templates_get_template_fileheader(
1254
 
                                                GEANY_TEMPLATE_FILEHEADER, ft->extension, -1);
1255
 
                        }
1256
 
                }
1257
 
                ft_template = filetypes_get_template(ft);
1258
 
                template = g_strconcat(gpl_notice, ft_template, NULL);
1259
 
                g_free(ft_template);
1260
 
                g_free(gpl_notice);
1261
 
                return template;
1262
 
        }
1263
 
        else
1264
 
        {       // new file w/o template
1265
 
                return templates_get_template_fileheader(GEANY_TEMPLATE_FILETYPE_NONE, NULL, -1);
1266
 
        }
1267
 
}
1268
 
 
1269
 
 
1270
 
void document_unfold_all(gint idx)
1271
 
{
1272
 
        gint lines, pos, i;
1273
 
 
1274
 
        if (idx == -1 || ! doc_list[idx].is_valid) return;
1275
 
 
1276
 
        lines = sci_get_line_count(doc_list[idx].sci);
1277
 
        pos = sci_get_current_position(doc_list[idx].sci);
1278
 
 
1279
 
        for (i = 0; i < lines; i++)
1280
 
        {
1281
 
                sci_ensure_line_is_visible(doc_list[idx].sci, i);
1282
 
        }
1283
 
}
1284
 
 
1285
 
 
1286
 
void document_fold_all(gint idx)
1287
 
{
1288
 
        gint lines, pos, i;
1289
 
 
1290
 
        if (idx == -1 || ! doc_list[idx].is_valid) return;
1291
 
 
1292
 
        lines = sci_get_line_count(doc_list[idx].sci);
1293
 
        pos = sci_get_current_position(doc_list[idx].sci);
 
2122
static void fold_all(gint idx, gboolean want_fold)
 
2123
{
 
2124
        gint lines, first, i;
 
2125
 
 
2126
        if (! DOC_IDX_VALID(idx) || ! editor_prefs.folding) return;
 
2127
 
 
2128
        lines = sci_get_line_count(doc_list[idx].sci);
 
2129
        first = sci_get_first_visible_line(doc_list[idx].sci);
1294
2130
 
1295
2131
        for (i = 0; i < lines; i++)
1296
2132
        {
1297
2133
                gint level = sci_get_fold_level(doc_list[idx].sci, i);
1298
2134
                if (level & SC_FOLDLEVELHEADERFLAG)
1299
2135
                {
1300
 
                        if (sci_get_fold_expanded(doc_list[idx].sci, i))
 
2136
                        if (sci_get_fold_expanded(doc_list[idx].sci, i) == want_fold)
1301
2137
                                        sci_toggle_fold(doc_list[idx].sci, i);
1302
2138
                }
1303
2139
        }
 
2140
        editor_scroll_to_line(doc_list[idx].sci, first, 0.0F);
 
2141
}
 
2142
 
 
2143
 
 
2144
void document_unfold_all(gint idx)
 
2145
{
 
2146
        fold_all(idx, FALSE);
 
2147
}
 
2148
 
 
2149
 
 
2150
void document_fold_all(gint idx)
 
2151
{
 
2152
        fold_all(idx, TRUE);
1304
2153
}
1305
2154
 
1306
2155
 
1307
2156
void document_clear_indicators(gint idx)
1308
2157
{
1309
 
        glong last_pos = sci_get_length(doc_list[idx].sci);
 
2158
        glong last_pos;
 
2159
 
 
2160
        g_return_if_fail(DOC_IDX_VALID(idx));
 
2161
 
 
2162
        last_pos = sci_get_length(doc_list[idx].sci);
1310
2163
        if (last_pos > 0)
1311
2164
        {
1312
2165
                sci_start_styling(doc_list[idx].sci, 0, INDIC2_MASK);
1313
2166
                sci_set_styling(doc_list[idx].sci, last_pos, 0);
1314
2167
        }
 
2168
        sci_marker_delete_all(doc_list[idx].sci, 0);    /* remove the yellow error line marker */
1315
2169
}
1316
2170
 
1317
2171
 
1326
2180
        start = sci_get_position_from_line(doc_list[idx].sci, line);
1327
2181
        end = sci_get_position_from_line(doc_list[idx].sci, line + 1);
1328
2182
 
1329
 
        // skip blank lines
 
2183
        /* skip blank lines */
1330
2184
        if ((start + 1) == end ||
1331
2185
                sci_get_line_length(doc_list[idx].sci, line) == utils_get_eol_char_len(idx))
1332
2186
                return;
1333
2187
 
 
2188
        /* don't set the indicator on whitespace */
1334
2189
        len = end - start;
1335
 
        linebuf = g_malloc(len);
1336
 
 
1337
 
        // don't set the indicator on whitespace
1338
 
        sci_get_line(doc_list[idx].sci, line, linebuf);
1339
 
        if (linebuf == NULL) return;
 
2190
        linebuf = sci_get_line(doc_list[idx].sci, line);
1340
2191
 
1341
2192
        while (isspace(linebuf[i])) i++;
1342
 
        while (isspace(linebuf[len-1])) len--;
 
2193
        while (len > 1 && len > i && isspace(linebuf[len-1])) len--;
1343
2194
        g_free(linebuf);
1344
2195
 
1345
2196
        current_mask = sci_get_style_at(doc_list[idx].sci, start);
1346
2197
        current_mask &= INDICS_MASK;
1347
2198
        current_mask |= INDIC2_MASK;
1348
2199
        sci_start_styling(doc_list[idx].sci, start + i, INDIC2_MASK);
1349
 
        //geany_debug("%p\tline: %d\tstart-end: %d - %d\t%d - %i", doc_list[idx].sci, line, start, end, len, i);
 
2200
        /*geany_debug("%p\tline: %d\tstart-end: %d - %d\t%d - %i", doc_list[idx].sci, line, start, end, len, i);*/
1350
2201
        sci_set_styling(doc_list[idx].sci, len - i, current_mask);
1351
2202
}
1352
2203
 
1353
2204
 
1354
 
/* simple file print */
1355
 
void document_print(gint idx)
1356
 
{
1357
 
        gchar *cmdline;
1358
 
 
1359
 
        if (idx == -1 || ! doc_list[idx].is_valid || doc_list[idx].file_name == NULL) return;
1360
 
 
1361
 
        cmdline = g_strdup(app->tools_print_cmd);
1362
 
        cmdline = utils_str_replace(cmdline, "%f", doc_list[idx].file_name);
1363
 
 
1364
 
        if (dialogs_show_question(_("The file \"%s\" will be printed with the following command:\n\n%s"),
1365
 
                                                                doc_list[idx].file_name, cmdline))
1366
 
        {
1367
 
                gint rc;
1368
 
                // system() is not the best way, but the only one I found to get the following working:
1369
 
                // a2ps -1 --medium=A4 -o - %f | xfprint4
1370
 
                rc = system(cmdline);
1371
 
                if (rc != 0)
1372
 
                {
1373
 
                        dialogs_show_error(_("Printing of \"%s\" failed (return code: %d)."),
1374
 
                                                                doc_list[idx].file_name, rc);
1375
 
                }
1376
 
                else
1377
 
                {
1378
 
                        msgwin_status_add(_("File %s printed."), doc_list[idx].file_name);
1379
 
                }
1380
 
        }
1381
 
        g_free(cmdline);
1382
 
}
1383
 
 
1384
 
 
1385
2205
void document_replace_tabs(gint idx)
1386
2206
{
1387
 
        gint i, len, j = 0, k, tabs_amount = 0, tab_w, pos;
1388
 
        gchar *data, *text;
1389
 
 
1390
 
        if (idx < 0 || ! doc_list[idx].is_valid) return;
1391
 
 
1392
 
        pos = sci_get_current_position(doc_list[idx].sci);
1393
 
        tab_w = sci_get_tab_width(doc_list[idx].sci);
1394
 
 
1395
 
        // get the text
1396
 
        len = sci_get_length(doc_list[idx].sci) + 1;
1397
 
        data = g_malloc(len);
1398
 
        sci_get_text(doc_list[idx].sci, len, data);
1399
 
 
1400
 
        for (i = 0; i < len; i++) if (data[i] == 9) tabs_amount++;
1401
 
 
1402
 
        // if there are no tabs, just return and leave the content untouched
1403
 
        if (tabs_amount == 0)
1404
 
        {
1405
 
                g_free(data);
1406
 
                return;
1407
 
        }
1408
 
 
1409
 
        text = g_malloc(len + (tabs_amount * (tab_w - 1)) + 1);
1410
 
 
1411
 
        for (i = 0; i < len; i++)
1412
 
        {
1413
 
                if (data[i] == 9)
1414
 
                {
1415
 
                        // increase cursor position to keep it at same position
1416
 
                        if (pos > i) pos += 3;
1417
 
 
1418
 
                        for (k = 0; k < tab_w; k++) text[j++] = 32;
1419
 
                }
1420
 
                else
1421
 
                {
1422
 
                        text[j++] = data[i];
1423
 
                }
1424
 
        }
1425
 
        text[j] = '\0';
1426
 
 
1427
 
        geany_debug("Replacing Tabs: tabs_amount: %d, text len: %d, j: %d", tabs_amount, len, j);
1428
 
        sci_set_text(doc_list[idx].sci, text);
1429
 
        sci_set_current_position(doc_list[idx].sci, pos);
1430
 
 
1431
 
        g_free(data);
1432
 
        g_free(text);
 
2207
        gint search_pos, pos_in_line, current_tab_true_length;
 
2208
        gint tab_len;
 
2209
        gchar *tab_str;
 
2210
        struct TextToFind ttf;
 
2211
 
 
2212
        if (! DOC_IDX_VALID(idx)) return;
 
2213
 
 
2214
        sci_start_undo_action(doc_list[idx].sci);
 
2215
        tab_len = sci_get_tab_width(doc_list[idx].sci);
 
2216
        ttf.chrg.cpMin = 0;
 
2217
        ttf.chrg.cpMax = sci_get_length(doc_list[idx].sci);
 
2218
        ttf.lpstrText = (gchar*) "\t";
 
2219
 
 
2220
        while (TRUE)
 
2221
        {
 
2222
                search_pos = sci_find_text(doc_list[idx].sci, SCFIND_MATCHCASE, &ttf);
 
2223
                if (search_pos == -1)
 
2224
                        break;
 
2225
 
 
2226
                pos_in_line = sci_get_col_from_position(doc_list[idx].sci,search_pos);
 
2227
                current_tab_true_length = tab_len - (pos_in_line % tab_len);
 
2228
                tab_str = g_strnfill(current_tab_true_length, ' ');
 
2229
                sci_target_start(doc_list[idx].sci, search_pos);
 
2230
                sci_target_end(doc_list[idx].sci, search_pos + 1);
 
2231
                sci_target_replace(doc_list[idx].sci, tab_str, FALSE);
 
2232
                ttf.chrg.cpMin = search_pos + current_tab_true_length - 1;      /* next search starts after replacement */
 
2233
                ttf.chrg.cpMax += current_tab_true_length - 1;  /* update end of range now text has changed */
 
2234
                g_free(tab_str);
 
2235
        }
 
2236
        sci_end_undo_action(doc_list[idx].sci);
 
2237
}
 
2238
 
 
2239
 
 
2240
void document_strip_line_trailing_spaces(gint idx, gint line)
 
2241
{
 
2242
        gint line_start = sci_get_position_from_line(doc_list[idx].sci, line);
 
2243
        gint line_end = sci_get_line_end_position(doc_list[idx].sci, line);
 
2244
        gint i = line_end - 1;
 
2245
        gchar ch = sci_get_char_at(doc_list[idx].sci, i);
 
2246
 
 
2247
        while ((i >= line_start) && ((ch == ' ') || (ch == '\t')))
 
2248
        {
 
2249
                i--;
 
2250
                ch = sci_get_char_at(doc_list[idx].sci, i);
 
2251
        }
 
2252
        if (i < (line_end-1))
 
2253
        {
 
2254
                sci_target_start(doc_list[idx].sci, i + 1);
 
2255
                sci_target_end(doc_list[idx].sci, line_end);
 
2256
                sci_target_replace(doc_list[idx].sci, "", FALSE);
 
2257
        }
1433
2258
}
1434
2259
 
1435
2260
 
1438
2263
        gint max_lines = sci_get_line_count(doc_list[idx].sci);
1439
2264
        gint line;
1440
2265
 
 
2266
        sci_start_undo_action(doc_list[idx].sci);
 
2267
 
1441
2268
        for (line = 0; line < max_lines; line++)
1442
2269
        {
1443
 
                gint line_start = sci_get_position_from_line(doc_list[idx].sci, line);
1444
 
                gint line_end = sci_get_line_end_from_position(doc_list[idx].sci, line);
1445
 
                gint i = line_end - 1;
1446
 
                gchar ch = sci_get_char_at(doc_list[idx].sci, i);
1447
 
 
1448
 
                while ((i >= line_start) && ((ch == ' ') || (ch == '\t')))
1449
 
                {
1450
 
                        i--;
1451
 
                        ch = sci_get_char_at(doc_list[idx].sci, i);
1452
 
                }
1453
 
                if (i < (line_end-1))
1454
 
                {
1455
 
                        sci_target_start(doc_list[idx].sci, i + 1);
1456
 
                        sci_target_end(doc_list[idx].sci, line_end);
1457
 
                        sci_target_replace(doc_list[idx].sci, "", FALSE);
1458
 
                }
 
2270
                document_strip_line_trailing_spaces(idx, line);
1459
2271
        }
 
2272
        sci_end_undo_action(doc_list[idx].sci);
1460
2273
}
1461
2274
 
1462
2275
 
1486
2299
        }
1487
2300
}
1488
2301
 
 
2302
 
 
2303
/**
 
2304
 *  Sets the encoding of a %document.
 
2305
 *  This function only set the encoding of the %document, it does not any conversions. The new
 
2306
 *  encoding is used when e.g. saving the file.
 
2307
 *
 
2308
 *  @param idx The index of the %document.
 
2309
 *  @param new_encoding The encoding to be set for the %document.
 
2310
 **/
 
2311
void document_set_encoding(gint idx, const gchar *new_encoding)
 
2312
{
 
2313
        if (! DOC_IDX_VALID(idx) || new_encoding == NULL ||
 
2314
                utils_str_equal(new_encoding, doc_list[idx].encoding)) return;
 
2315
 
 
2316
        g_free(doc_list[idx].encoding);
 
2317
        doc_list[idx].encoding = g_strdup(new_encoding);
 
2318
 
 
2319
        ui_update_statusbar(idx, -1);
 
2320
        gtk_widget_set_sensitive(lookup_widget(app->window, "menu_write_unicode_bom1"),
 
2321
                        encodings_is_unicode_charset(doc_list[idx].encoding));
 
2322
}
 
2323
 
 
2324
 
1489
2325
/* own Undo / Redo implementation to be able to undo / redo changes
1490
2326
 * to the encoding or the Unicode BOM (which are Scintilla independet).
1491
2327
 * All Scintilla events are stored in the undo / redo buffer and are passed through. */
1492
2328
 
1493
 
/* Clears the Undo buffer (to be called after saving a file or when closing the document) */
 
2329
/* Clears the Undo and Redo buffer (to be called when reloading or closing the document) */
1494
2330
void document_undo_clear(gint idx)
1495
2331
{
1496
2332
        undo_action *a;
1511
2347
        }
1512
2348
        doc_list[idx].undo_actions = NULL;
1513
2349
 
1514
 
        doc_list[idx].changed = FALSE;
1515
 
        if (! app->quitting) document_set_text_changed(idx);
1516
 
}
1517
 
 
1518
 
 
1519
 
/* Clears the Redo buffer (to be called after saving a file or when closing the document) */
1520
 
void document_redo_clear(gint idx)
1521
 
{
1522
 
/*
1523
 
        undo_action *a;
1524
 
 
1525
2350
        while (g_trash_stack_height(&doc_list[idx].redo_actions) > 0)
1526
2351
        {
1527
2352
                a = g_trash_stack_pop(&doc_list[idx].redo_actions);
1539
2364
        doc_list[idx].redo_actions = NULL;
1540
2365
 
1541
2366
        doc_list[idx].changed = FALSE;
1542
 
        if (! app->quitting) document_set_text_changed(idx);
1543
 
*/
 
2367
        if (! main_status.quitting) document_set_text_changed(idx);
 
2368
 
 
2369
        /*geany_debug("%s: new undo stack height: %d, new redo stack height: %d", __func__,
 
2370
                                 *g_trash_stack_height(&doc_list[idx].undo_actions), g_trash_stack_height(&doc_list[idx].redo_actions)); */
1544
2371
}
1545
2372
 
1546
2373
 
1548
2375
{
1549
2376
        undo_action *action;
1550
2377
 
1551
 
        if (idx == -1 || ! doc_list[idx].is_valid) return;
 
2378
        if (! DOC_IDX_VALID(idx)) return;
1552
2379
 
1553
2380
        action = g_new0(undo_action, 1);
1554
2381
        action->type = type;
1560
2387
        document_set_text_changed(idx);
1561
2388
        ui_update_popup_reundo_items(idx);
1562
2389
 
1563
 
        {
1564
 
                geany_debug("%s: new stack height: %d, added type: %d", __func__,
1565
 
                                g_trash_stack_height(&doc_list[idx].undo_actions), action->type);
1566
 
        }
 
2390
        /*geany_debug("%s: new stack height: %d, added type: %d", __func__,
 
2391
                                 *g_trash_stack_height(&doc_list[idx].undo_actions), action->type); */
1567
2392
}
1568
2393
 
1569
2394
 
1570
2395
gboolean document_can_undo(gint idx)
1571
2396
{
1572
 
        return sci_can_undo(doc_list[idx].sci);
1573
 
 
1574
 
        if (idx == -1 || ! doc_list[idx].is_valid) return FALSE;
 
2397
        if (! DOC_IDX_VALID(idx)) return FALSE;
1575
2398
 
1576
2399
        if (g_trash_stack_height(&doc_list[idx].undo_actions) > 0 || sci_can_undo(doc_list[idx].sci))
1577
2400
                return TRUE;
1580
2403
}
1581
2404
 
1582
2405
 
1583
 
gboolean document_can_redo(gint idx)
 
2406
static void update_changed_state(gint idx)
1584
2407
{
1585
 
        if (idx == -1 || ! doc_list[idx].is_valid) return FALSE;
1586
 
 
1587
 
        return sci_can_redo(doc_list[idx].sci);
 
2408
        doc_list[idx].changed =
 
2409
                (sci_is_modified(doc_list[idx].sci) ||
 
2410
                doc_list[idx].has_bom != doc_list[idx].saved_encoding.has_bom ||
 
2411
                ! utils_str_equal(doc_list[idx].encoding, doc_list[idx].saved_encoding.encoding));
 
2412
        document_set_text_changed(idx);
1588
2413
}
1589
2414
 
1590
2415
 
1592
2417
{
1593
2418
        undo_action *action;
1594
2419
 
1595
 
#if 1
1596
 
        sci_undo(doc_list[idx].sci);
1597
 
        return;
1598
 
#endif
1599
 
 
1600
 
        if (idx == -1 || ! doc_list[idx].is_valid) return;
 
2420
        if (! DOC_IDX_VALID(idx)) return;
1601
2421
 
1602
2422
        action = g_trash_stack_pop(&doc_list[idx].undo_actions);
1603
2423
 
1604
2424
        if (action == NULL)
1605
2425
        {
1606
 
                // fallback, should not be necessary
 
2426
                /* fallback, should not be necessary */
 
2427
                geany_debug("%s: fallback used", __func__);
1607
2428
                sci_undo(doc_list[idx].sci);
1608
2429
        }
1609
2430
        else
1612
2433
                {
1613
2434
                        case UNDO_SCINTILLA:
1614
2435
                        {
1615
 
                                geany_debug("undo: Scintilla");
 
2436
                                document_redo_add(idx, UNDO_SCINTILLA, NULL);
 
2437
 
1616
2438
                                sci_undo(doc_list[idx].sci);
1617
2439
                                break;
1618
2440
                        }
1619
2441
                        case UNDO_BOM:
1620
2442
                        {
1621
 
                                geany_debug("undo: BOM");
 
2443
                                document_redo_add(idx, UNDO_BOM, GINT_TO_POINTER(doc_list[idx].has_bom));
 
2444
 
1622
2445
                                doc_list[idx].has_bom = GPOINTER_TO_INT(action->data);
1623
2446
                                ui_update_statusbar(idx, -1);
1624
2447
                                ui_document_show_hide(idx);
1626
2449
                        }
1627
2450
                        case UNDO_ENCODING:
1628
2451
                        {
1629
 
                                geany_debug("undo: Encoding");
1630
 
                                doc_list[idx].encoding = (gchar*) action->data;
1631
 
                                ui_update_statusbar(idx, -1);
1632
 
                                encodings_select_radio_item(doc_list[idx].encoding);
1633
 
                                gtk_widget_set_sensitive(lookup_widget(app->window, "menu_write_unicode_bom1"),
1634
 
                                                                utils_is_unicode_charset(doc_list[idx].encoding));
 
2452
                                /* use the "old" encoding */
 
2453
                                document_redo_add(idx, UNDO_ENCODING, g_strdup(doc_list[idx].encoding));
 
2454
 
 
2455
                                document_set_encoding(idx, (const gchar*)action->data);
 
2456
 
 
2457
                                app->ignore_callback = TRUE;
 
2458
                                encodings_select_radio_item((const gchar*)action->data);
 
2459
                                app->ignore_callback = FALSE;
 
2460
 
 
2461
                                g_free(action->data);
1635
2462
                                break;
1636
2463
                        }
1637
2464
                        default: break;
1638
2465
                }
1639
2466
        }
 
2467
        g_free(action); /* free the action which was taken from the stack */
1640
2468
 
1641
 
        if (g_trash_stack_height(&doc_list[idx].undo_actions) == 0) doc_list[idx].changed = FALSE;
 
2469
        update_changed_state(idx);
1642
2470
        ui_update_popup_reundo_items(idx);
1643
 
        geany_debug("%s: new stack height: %d", __func__, g_trash_stack_height(&doc_list[idx].undo_actions));
 
2471
        /*geany_debug("%s: new stack height: %d", __func__, g_trash_stack_height(&doc_list[idx].undo_actions));*/
 
2472
}
 
2473
 
 
2474
 
 
2475
gboolean document_can_redo(gint idx)
 
2476
{
 
2477
        if (! DOC_IDX_VALID(idx)) return FALSE;
 
2478
 
 
2479
        if (g_trash_stack_height(&doc_list[idx].redo_actions) > 0 || sci_can_redo(doc_list[idx].sci))
 
2480
                return TRUE;
 
2481
        else
 
2482
                return FALSE;
1644
2483
}
1645
2484
 
1646
2485
 
1647
2486
void document_redo(gint idx)
1648
2487
{
1649
 
        if (idx == -1 || ! doc_list[idx].is_valid) return;
1650
 
 
1651
 
        sci_redo(doc_list[idx].sci);
1652
 
}
 
2488
        undo_action *action;
 
2489
 
 
2490
        if (! DOC_IDX_VALID(idx)) return;
 
2491
 
 
2492
        action = g_trash_stack_pop(&doc_list[idx].redo_actions);
 
2493
 
 
2494
        if (action == NULL)
 
2495
        {
 
2496
                /* fallback, should not be necessary */
 
2497
                geany_debug("%s: fallback used", __func__);
 
2498
                sci_redo(doc_list[idx].sci);
 
2499
        }
 
2500
        else
 
2501
        {
 
2502
                switch (action->type)
 
2503
                {
 
2504
                        case UNDO_SCINTILLA:
 
2505
                        {
 
2506
                                document_undo_add(idx, UNDO_SCINTILLA, NULL);
 
2507
 
 
2508
                                sci_redo(doc_list[idx].sci);
 
2509
                                break;
 
2510
                        }
 
2511
                        case UNDO_BOM:
 
2512
                        {
 
2513
                                document_undo_add(idx, UNDO_BOM, GINT_TO_POINTER(doc_list[idx].has_bom));
 
2514
 
 
2515
                                doc_list[idx].has_bom = GPOINTER_TO_INT(action->data);
 
2516
                                ui_update_statusbar(idx, -1);
 
2517
                                ui_document_show_hide(idx);
 
2518
                                break;
 
2519
                        }
 
2520
                        case UNDO_ENCODING:
 
2521
                        {
 
2522
                                document_undo_add(idx, UNDO_ENCODING, g_strdup(doc_list[idx].encoding));
 
2523
 
 
2524
                                document_set_encoding(idx, (const gchar*)action->data);
 
2525
 
 
2526
                                app->ignore_callback = TRUE;
 
2527
                                encodings_select_radio_item((const gchar*)action->data);
 
2528
                                app->ignore_callback = FALSE;
 
2529
 
 
2530
                                g_free(action->data);
 
2531
                                break;
 
2532
                        }
 
2533
                        default: break;
 
2534
                }
 
2535
        }
 
2536
        g_free(action); /* free the action which was taken from the stack */
 
2537
 
 
2538
        update_changed_state(idx);
 
2539
        ui_update_popup_reundo_items(idx);
 
2540
        /*geany_debug("%s: new stack height: %d", __func__, g_trash_stack_height(&doc_list[idx].redo_actions));*/
 
2541
}
 
2542
 
 
2543
 
 
2544
static void document_redo_add(gint idx, guint type, gpointer data)
 
2545
{
 
2546
        undo_action *action;
 
2547
 
 
2548
        if (! DOC_IDX_VALID(idx)) return;
 
2549
 
 
2550
        action = g_new0(undo_action, 1);
 
2551
        action->type = type;
 
2552
        action->data = data;
 
2553
 
 
2554
        g_trash_stack_push(&doc_list[idx].redo_actions, action);
 
2555
 
 
2556
        doc_list[idx].changed = TRUE;
 
2557
        document_set_text_changed(idx);
 
2558
        ui_update_popup_reundo_items(idx);
 
2559
 
 
2560
        /*geany_debug("%s: new stack height: %d, added type: %d", __func__,
 
2561
                                 *g_trash_stack_height(&doc_list[idx].redo_actions), action->type); */
 
2562
}
 
2563
 
 
2564
 
 
2565
/* Gets the status colour of the document, or NULL if default widget
 
2566
 * colouring should be used. */
 
2567
GdkColor *document_get_status(gint idx)
 
2568
{
 
2569
        static GdkColor red = {0, 0xFFFF, 0, 0};
 
2570
        static GdkColor green = {0, 0, 0x7FFF, 0};
 
2571
        GdkColor *color = NULL;
 
2572
 
 
2573
        if (doc_list[idx].changed)
 
2574
                color = &red;
 
2575
        else if (doc_list[idx].readonly)
 
2576
                color = &green;
 
2577
 
 
2578
        return color;   /* return pointer to static GdkColor. */
 
2579
}
 
2580
 
 
2581
 
 
2582
/* useful debugging function (usually debug macros aren't enabled) */
 
2583
#ifdef GEANY_DEBUG
 
2584
document *doc(gint idx)
 
2585
{
 
2586
        return DOC_IDX_VALID(idx) ? &doc_list[idx] : NULL;
 
2587
}
 
2588
#endif
 
2589
 
 
2590
 
 
2591
static GArray *doc_indexes = NULL;
 
2592
 
 
2593
/* Cache the current document indexes and prevent any colourising until
 
2594
 * document_colourise_new() is called. */
 
2595
void document_delay_colourise()
 
2596
{
 
2597
        gint n;
 
2598
 
 
2599
        g_return_if_fail(delay_colourise == FALSE);
 
2600
        g_return_if_fail(doc_indexes == NULL);
 
2601
 
 
2602
        /* make an array containing all the current document indexes */
 
2603
        doc_indexes = g_array_new(FALSE, FALSE, sizeof(gint));
 
2604
        for (n = 0; n < (gint) doc_array->len; n++)
 
2605
        {
 
2606
                if (DOC_IDX_VALID(n))
 
2607
                        g_array_append_val(doc_indexes, n);
 
2608
        }
 
2609
        delay_colourise = TRUE;
 
2610
}
 
2611
 
 
2612
 
 
2613
/* Colourise only newly opened documents and existing documents whose project typenames
 
2614
 * keywords have changed.
 
2615
 * document_delay_colourise() should already have been called. */
 
2616
void document_colourise_new()
 
2617
{
 
2618
        guint n, i;
 
2619
        /* A bitset representing which docs need [re]colourising.
 
2620
         * (use gint8 to save memory because gboolean = gint) */
 
2621
        gint8 *doc_set = g_newa(gint8, doc_array->len);
 
2622
        gboolean recolour = FALSE;      /* whether to recolourise existing typenames */
 
2623
 
 
2624
        g_return_if_fail(delay_colourise == TRUE);
 
2625
        g_return_if_fail(doc_indexes != NULL);
 
2626
 
 
2627
        /* first assume recolourising all docs */
 
2628
        memset(doc_set, TRUE, doc_array->len * sizeof(gint8));
 
2629
 
 
2630
        /* remove existing docs from the set if they don't use typenames or typenames haven't changed */
 
2631
        recolour = update_type_keywords(NULL, -2);
 
2632
        for (i = 0; i < doc_indexes->len; i++)
 
2633
        {
 
2634
                ScintillaObject *sci;
 
2635
 
 
2636
                n = g_array_index(doc_indexes, gint, i);
 
2637
                sci = doc_list[n].sci;
 
2638
                if (! recolour || (sci && editor_lexer_get_type_keyword_idx(sci_get_lexer(sci)) == -1))
 
2639
                {
 
2640
                        doc_set[n] = FALSE;
 
2641
                }
 
2642
        }
 
2643
        /* colourise all in the doc_set */
 
2644
        for (n = 0; n < doc_array->len; n++)
 
2645
        {
 
2646
                if (doc_set[n] && doc_list[n].is_valid)
 
2647
                        sci_colourise(doc_list[n].sci, 0, -1);
 
2648
        }
 
2649
        delay_colourise = FALSE;
 
2650
        g_array_free(doc_indexes, TRUE);
 
2651
        doc_indexes = NULL;
 
2652
 
 
2653
        /* now that the current document is colourised, fold points are now accurate,
 
2654
         * so force an update of the current function/tag. */
 
2655
        utils_get_current_function(-1, NULL);
 
2656
        ui_update_statusbar(-1, -1);
 
2657
}
 
2658
 
 
2659
 
 
2660
/* Inserts the given colour (format should be #...), if there is a selection starting with 0x...
 
2661
 * the replacement will also start with 0x... */
 
2662
void document_insert_colour(gint idx, const gchar *colour)
 
2663
{
 
2664
        g_return_if_fail(DOC_IDX_VALID(idx));
 
2665
 
 
2666
        if (sci_can_copy(doc_list[idx].sci))
 
2667
        {
 
2668
                gint start = sci_get_selection_start(doc_list[idx].sci);
 
2669
                const gchar *replacement = colour;
 
2670
 
 
2671
                if (sci_get_char_at(doc_list[idx].sci, start) == '0' &&
 
2672
                        sci_get_char_at(doc_list[idx].sci, start + 1) == 'x')
 
2673
                {
 
2674
                        sci_set_selection_start(doc_list[idx].sci, start + 2);
 
2675
                        sci_set_selection_end(doc_list[idx].sci, start + 8);
 
2676
                        replacement++; /* skip the leading "0x" */
 
2677
                }
 
2678
                else if (sci_get_char_at(doc_list[idx].sci, start - 1) == '#')
 
2679
                {       /* double clicking something like #00ffff may only select 00ffff because of wordchars */
 
2680
                        replacement++; /* so skip the '#' to only replace the colour value */
 
2681
                }
 
2682
                sci_replace_sel(doc_list[idx].sci, replacement);
 
2683
        }
 
2684
        else
 
2685
                sci_add_text(doc_list[idx].sci, colour);
 
2686
}
 
2687
 
 
2688
 
 
2689
gint document_clone(gint old_idx, const gchar *utf8_filename)
 
2690
{
 
2691
        /* create a new file and copy file content and properties */
 
2692
        gint len, idx;
 
2693
        gchar *text;
 
2694
 
 
2695
        len = sci_get_length(doc_list[old_idx].sci) + 1;
 
2696
        text = (gchar*) g_malloc(len);
 
2697
        sci_get_text(doc_list[old_idx].sci, len, text);
 
2698
        /* use old file type (or maybe NULL for auto detect would be better?) */
 
2699
        idx = document_new_file(utf8_filename, doc_list[old_idx].file_type, text);
 
2700
        g_free(text);
 
2701
 
 
2702
        /* copy file properties */
 
2703
        doc_list[idx].line_wrapping = doc_list[old_idx].line_wrapping;
 
2704
        doc_list[idx].readonly = doc_list[old_idx].readonly;
 
2705
        doc_list[idx].has_bom = doc_list[old_idx].has_bom;
 
2706
        document_set_encoding(idx, doc_list[old_idx].encoding);
 
2707
        sci_set_lines_wrapped(doc_list[idx].sci, doc_list[idx].line_wrapping);
 
2708
        sci_set_readonly(doc_list[idx].sci, doc_list[idx].readonly);
 
2709
 
 
2710
        ui_document_show_hide(idx);
 
2711
        return idx;
 
2712
}
 
2713
 
1653
2714