1 /*
2 * pluma-document.c
3 * This file is part of pluma
4 *
5 * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence
6 * Copyright (C) 2000, 2001 Chema Celorio, Paolo Maggi
7 * Copyright (C) 2002-2005 Paolo Maggi
8 * Copyright (C) 2012-2021 MATE Developers
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26 /*
27 * Modified by the pluma Team, 1998-2005. See the AUTHORS file for a
28 * list of people on the pluma Team.
29 * See the ChangeLog files for a list of changes.
30 *
31 * $Id$
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #include <string.h>
39 #include <stdlib.h>
40
41 #include <glib/gi18n.h>
42 #include <gtk/gtk.h>
43
44 #include "pluma-settings.h"
45 #include "pluma-document.h"
46 #include "pluma-debug.h"
47 #include "pluma-utils.h"
48 #include "pluma-language-manager.h"
49 #include "pluma-style-scheme-manager.h"
50 #include "pluma-document-loader.h"
51 #include "pluma-document-saver.h"
52 #include "pluma-enum-types.h"
53 #include "plumatextregion.h"
54
55 #ifndef ENABLE_GVFS_METADATA
56 #include "pluma-metadata-manager.h"
57 #else
58 #define METADATA_QUERY "metadata::*"
59 #endif
60
61 #undef ENABLE_PROFILE
62
63 #ifdef ENABLE_PROFILE
64 #define PROFILE(x) x
65 #else
66 #define PROFILE(x)
67 #endif
68
69 PROFILE (static GTimer *timer = NULL)
70
71 #ifdef MAXPATHLEN
72 #define PLUMA_MAX_PATH_LEN MAXPATHLEN
73 #elif defined (PATH_MAX)
74 #define PLUMA_MAX_PATH_LEN PATH_MAX
75 #else
76 #define PLUMA_MAX_PATH_LEN 2048
77 #endif
78
79 /* undo https://gitlab.gnome.org/GNOME/gtksourceview/-/commit/b3dffc39 */
80 #undef GTK_SOURCE_CHECK_VERSION
81 #define GTK_SOURCE_CHECK_VERSION(major, minor, micro) \
82 (GTK_SOURCE_MAJOR_VERSION > (major) || \
83 (GTK_SOURCE_MAJOR_VERSION == (major) && GTK_SOURCE_MINOR_VERSION > (minor)) || \
84 (GTK_SOURCE_MAJOR_VERSION == (major) && GTK_SOURCE_MINOR_VERSION == (minor) && \
85 GTK_SOURCE_MICRO_VERSION >= (micro)))
86
87 static void pluma_document_load_real (PlumaDocument *doc,
88 const gchar *uri,
89 const PlumaEncoding *encoding,
90 gint line_pos,
91 gboolean create);
92 static void pluma_document_save_real (PlumaDocument *doc,
93 const gchar *uri,
94 const PlumaEncoding *encoding,
95 PlumaDocumentSaveFlags flags);
96 static void to_search_region_range (PlumaDocument *doc,
97 GtkTextIter *start,
98 GtkTextIter *end);
99 static void insert_text_cb (PlumaDocument *doc,
100 GtkTextIter *pos,
101 const gchar *text,
102 gint length);
103
104 static void delete_range_cb (PlumaDocument *doc,
105 GtkTextIter *start,
106 GtkTextIter *end);
107
108 struct _PlumaDocumentPrivate
109 {
110 GSettings *editor_settings;
111
112 gchar *uri;
113 gint untitled_number;
114 gchar *short_name;
115
116 GFileInfo *metadata_info;
117
118 const PlumaEncoding *encoding;
119
120 gchar *content_type;
121
122 gint64 mtime;
123 gint64 time_of_last_save_or_load;
124
125 guint search_flags;
126 gchar *search_text;
127 gchar *last_replace_text;
128 gint num_of_lines_search_text;
129
130 PlumaDocumentNewlineType newline_type;
131
132 /* Temp data while loading */
133 PlumaDocumentLoader *loader;
134 gboolean create; /* Create file if uri points
135 * to a non existing file */
136 const PlumaEncoding *requested_encoding;
137 gint requested_line_pos;
138
139 /* Saving stuff */
140 PlumaDocumentSaver *saver;
141
142 /* Search highlighting support variables */
143 PlumaTextRegion *to_search_region;
144 GtkTextTag *found_tag;
145
146 /* Mount operation factory */
147 PlumaMountOperationFactory mount_operation_factory;
148 gpointer mount_operation_userdata;
149
150 gint readonly : 1;
151 gint last_save_was_manually : 1;
152 gint language_set_by_user : 1;
153 gint stop_cursor_moved_emission : 1;
154 gint dispose_has_run : 1;
155 };
156
157 enum {
158 PROP_0,
159
160 PROP_URI,
161 PROP_SHORTNAME,
162 PROP_CONTENT_TYPE,
163 PROP_MIME_TYPE,
164 PROP_READ_ONLY,
165 PROP_ENCODING,
166 PROP_CAN_SEARCH_AGAIN,
167 PROP_ENABLE_SEARCH_HIGHLIGHTING,
168 PROP_NEWLINE_TYPE
169 };
170
171 enum {
172 CURSOR_MOVED,
173 LOAD,
174 LOADING,
175 LOADED,
176 SAVE,
177 SAVING,
178 SAVED,
179 SEARCH_HIGHLIGHT_UPDATED,
180 LAST_SIGNAL
181 };
182
183 static guint document_signals[LAST_SIGNAL] = { 0 };
184
G_DEFINE_TYPE_WITH_PRIVATE(PlumaDocument,pluma_document,GTK_SOURCE_TYPE_BUFFER)185 G_DEFINE_TYPE_WITH_PRIVATE (PlumaDocument, pluma_document, GTK_SOURCE_TYPE_BUFFER)
186
187 GQuark
188 pluma_document_error_quark (void)
189 {
190 static GQuark quark = 0;
191
192 if (G_UNLIKELY (quark == 0))
193 quark = g_quark_from_static_string ("pluma_io_load_error");
194
195 return quark;
196 }
197
198 static GHashTable *allocated_untitled_numbers = NULL;
199
200 static gint
get_untitled_number(void)201 get_untitled_number (void)
202 {
203 gint i = 1;
204
205 if (allocated_untitled_numbers == NULL)
206 allocated_untitled_numbers = g_hash_table_new (NULL, NULL);
207
208 g_return_val_if_fail (allocated_untitled_numbers != NULL, -1);
209
210 while (TRUE)
211 {
212 if (g_hash_table_lookup (allocated_untitled_numbers, GINT_TO_POINTER (i)) == NULL)
213 {
214 g_hash_table_insert (allocated_untitled_numbers,
215 GINT_TO_POINTER (i),
216 GINT_TO_POINTER (i));
217
218 return i;
219 }
220
221 ++i;
222 }
223 }
224
225 static void
release_untitled_number(gint n)226 release_untitled_number (gint n)
227 {
228 g_return_if_fail (allocated_untitled_numbers != NULL);
229
230 g_hash_table_remove (allocated_untitled_numbers, GINT_TO_POINTER (n));
231 }
232
233 static void
pluma_document_dispose(GObject * object)234 pluma_document_dispose (GObject *object)
235 {
236 PlumaDocument *doc = PLUMA_DOCUMENT (object);
237
238 pluma_debug (DEBUG_DOCUMENT);
239
240 /* Metadata must be saved here and not in finalize
241 * because the language is gone by the time finalize runs.
242 * beside if some plugin prevents proper finalization by
243 * holding a ref to the doc, we still save the metadata */
244 if ((!doc->priv->dispose_has_run) && (doc->priv->uri != NULL))
245 {
246 GtkTextIter iter;
247 gchar *position;
248 const gchar *language = NULL;
249
250 if (doc->priv->language_set_by_user)
251 {
252 GtkSourceLanguage *lang;
253
254 lang = pluma_document_get_language (doc);
255
256 if (lang == NULL)
257 language = "_NORMAL_";
258 else
259 language = gtk_source_language_get_id (lang);
260 }
261
262 gtk_text_buffer_get_iter_at_mark (
263 GTK_TEXT_BUFFER (doc),
264 &iter,
265 gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (doc)));
266
267 position = g_strdup_printf ("%d",
268 gtk_text_iter_get_offset (&iter));
269
270 if (language == NULL)
271 pluma_document_set_metadata (doc, PLUMA_METADATA_ATTRIBUTE_POSITION,
272 position, NULL);
273 else
274 pluma_document_set_metadata (doc, PLUMA_METADATA_ATTRIBUTE_POSITION,
275 position, PLUMA_METADATA_ATTRIBUTE_LANGUAGE,
276 language, NULL);
277 g_free (position);
278 }
279
280 if (doc->priv->loader)
281 {
282 g_object_unref (doc->priv->loader);
283 doc->priv->loader = NULL;
284 }
285
286 if (doc->priv->metadata_info != NULL)
287 {
288 g_object_unref (doc->priv->metadata_info);
289 doc->priv->metadata_info = NULL;
290 }
291
292 g_clear_object (&doc->priv->editor_settings);
293
294 doc->priv->dispose_has_run = TRUE;
295
296 G_OBJECT_CLASS (pluma_document_parent_class)->dispose (object);
297 }
298
299 static void
pluma_document_finalize(GObject * object)300 pluma_document_finalize (GObject *object)
301 {
302 PlumaDocument *doc = PLUMA_DOCUMENT (object);
303
304 pluma_debug (DEBUG_DOCUMENT);
305
306 if (doc->priv->untitled_number > 0)
307 {
308 g_return_if_fail (doc->priv->uri == NULL);
309 release_untitled_number (doc->priv->untitled_number);
310 }
311
312 g_free (doc->priv->uri);
313 g_free (doc->priv->content_type);
314 g_free (doc->priv->search_text);
315 g_free (doc->priv->last_replace_text);
316
317 if (doc->priv->to_search_region != NULL)
318 {
319 /* we can't delete marks if we're finalizing the buffer */
320 pluma_text_region_destroy (doc->priv->to_search_region, FALSE);
321 }
322
323 G_OBJECT_CLASS (pluma_document_parent_class)->finalize (object);
324 }
325
326 static void
pluma_document_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)327 pluma_document_get_property (GObject *object,
328 guint prop_id,
329 GValue *value,
330 GParamSpec *pspec)
331 {
332 PlumaDocument *doc = PLUMA_DOCUMENT (object);
333
334 switch (prop_id)
335 {
336 case PROP_URI:
337 g_value_set_string (value, doc->priv->uri);
338 break;
339 case PROP_SHORTNAME:
340 g_value_take_string (value, pluma_document_get_short_name_for_display (doc));
341 break;
342 case PROP_CONTENT_TYPE:
343 g_value_take_string (value, pluma_document_get_content_type (doc));
344 break;
345 case PROP_MIME_TYPE:
346 g_value_take_string (value, pluma_document_get_mime_type (doc));
347 break;
348 case PROP_READ_ONLY:
349 g_value_set_boolean (value, doc->priv->readonly);
350 break;
351 case PROP_ENCODING:
352 g_value_set_boxed (value, doc->priv->encoding);
353 break;
354 case PROP_CAN_SEARCH_AGAIN:
355 g_value_set_boolean (value, pluma_document_get_can_search_again (doc));
356 break;
357 case PROP_ENABLE_SEARCH_HIGHLIGHTING:
358 g_value_set_boolean (value, pluma_document_get_enable_search_highlighting (doc));
359 break;
360 case PROP_NEWLINE_TYPE:
361 g_value_set_enum (value, doc->priv->newline_type);
362 break;
363 default:
364 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
365 break;
366 }
367 }
368
369 static void
pluma_document_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)370 pluma_document_set_property (GObject *object,
371 guint prop_id,
372 const GValue *value,
373 GParamSpec *pspec)
374 {
375 PlumaDocument *doc = PLUMA_DOCUMENT (object);
376
377 switch (prop_id)
378 {
379 case PROP_ENABLE_SEARCH_HIGHLIGHTING:
380 pluma_document_set_enable_search_highlighting (doc,
381 g_value_get_boolean (value));
382 break;
383 case PROP_NEWLINE_TYPE:
384 pluma_document_set_newline_type (doc,
385 g_value_get_enum (value));
386 break;
387 case PROP_SHORTNAME:
388 pluma_document_set_short_name_for_display (doc,
389 g_value_get_string (value));
390 break;
391 case PROP_CONTENT_TYPE:
392 pluma_document_set_content_type (doc,
393 g_value_get_string (value));
394 break;
395 default:
396 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
397 break;
398 }
399 }
400
401 static void
emit_cursor_moved(PlumaDocument * doc)402 emit_cursor_moved (PlumaDocument *doc)
403 {
404 if (!doc->priv->stop_cursor_moved_emission)
405 {
406 g_signal_emit (doc,
407 document_signals[CURSOR_MOVED],
408 0);
409 }
410 }
411
412 static void
pluma_document_mark_set(GtkTextBuffer * buffer,const GtkTextIter * iter,GtkTextMark * mark)413 pluma_document_mark_set (GtkTextBuffer *buffer,
414 const GtkTextIter *iter,
415 GtkTextMark *mark)
416 {
417 PlumaDocument *doc = PLUMA_DOCUMENT (buffer);
418
419 if (GTK_TEXT_BUFFER_CLASS (pluma_document_parent_class)->mark_set)
420 GTK_TEXT_BUFFER_CLASS (pluma_document_parent_class)->mark_set (buffer,
421 iter,
422 mark);
423
424 if (mark == gtk_text_buffer_get_insert (buffer))
425 {
426 emit_cursor_moved (doc);
427 }
428 }
429
430 static void
pluma_document_changed(GtkTextBuffer * buffer)431 pluma_document_changed (GtkTextBuffer *buffer)
432 {
433 emit_cursor_moved (PLUMA_DOCUMENT (buffer));
434
435 GTK_TEXT_BUFFER_CLASS (pluma_document_parent_class)->changed (buffer);
436 }
437
438 static void
pluma_document_class_init(PlumaDocumentClass * klass)439 pluma_document_class_init (PlumaDocumentClass *klass)
440 {
441 GObjectClass *object_class = G_OBJECT_CLASS (klass);
442 GtkTextBufferClass *buf_class = GTK_TEXT_BUFFER_CLASS (klass);
443
444 object_class->dispose = pluma_document_dispose;
445 object_class->finalize = pluma_document_finalize;
446 object_class->get_property = pluma_document_get_property;
447 object_class->set_property = pluma_document_set_property;
448
449 buf_class->mark_set = pluma_document_mark_set;
450 buf_class->changed = pluma_document_changed;
451
452 klass->load = pluma_document_load_real;
453 klass->save = pluma_document_save_real;
454
455 g_object_class_install_property (object_class, PROP_URI,
456 g_param_spec_string ("uri",
457 "URI",
458 "The document's URI",
459 NULL,
460 G_PARAM_READABLE |
461 G_PARAM_STATIC_STRINGS));
462
463 g_object_class_install_property (object_class, PROP_SHORTNAME,
464 g_param_spec_string ("shortname",
465 "Short Name",
466 "The document's short name",
467 NULL,
468 G_PARAM_READWRITE |
469 G_PARAM_STATIC_STRINGS));
470
471 g_object_class_install_property (object_class, PROP_CONTENT_TYPE,
472 g_param_spec_string ("content-type",
473 "Content Type",
474 "The document's Content Type",
475 NULL,
476 G_PARAM_READWRITE |
477 G_PARAM_STATIC_STRINGS));
478
479 g_object_class_install_property (object_class, PROP_MIME_TYPE,
480 g_param_spec_string ("mime-type",
481 "MIME Type",
482 "The document's MIME Type",
483 "text/plain",
484 G_PARAM_READABLE |
485 G_PARAM_STATIC_STRINGS));
486
487 g_object_class_install_property (object_class, PROP_READ_ONLY,
488 g_param_spec_boolean ("read-only",
489 "Read Only",
490 "Whether the document is read only or not",
491 FALSE,
492 G_PARAM_READABLE |
493 G_PARAM_STATIC_STRINGS));
494
495 g_object_class_install_property (object_class, PROP_ENCODING,
496 g_param_spec_boxed ("encoding",
497 "Encoding",
498 "The PlumaEncoding used for the document",
499 PLUMA_TYPE_ENCODING,
500 G_PARAM_READABLE |
501 G_PARAM_STATIC_STRINGS));
502
503 g_object_class_install_property (object_class, PROP_CAN_SEARCH_AGAIN,
504 g_param_spec_boolean ("can-search-again",
505 "Can search again",
506 "Whether it's possible to search again in the document",
507 FALSE,
508 G_PARAM_READABLE |
509 G_PARAM_STATIC_STRINGS));
510
511 g_object_class_install_property (object_class, PROP_ENABLE_SEARCH_HIGHLIGHTING,
512 g_param_spec_boolean ("enable-search-highlighting",
513 "Enable Search Highlighting",
514 "Whether all the occurrences of the searched string must be highlighted",
515 FALSE,
516 G_PARAM_READWRITE |
517 G_PARAM_STATIC_STRINGS));
518
519 /**
520 * PlumaDocument:newline-type:
521 *
522 * The :newline-type property determines what is considered
523 * as a line ending when saving the document
524 */
525 g_object_class_install_property (object_class, PROP_NEWLINE_TYPE,
526 g_param_spec_enum ("newline-type",
527 "Newline type",
528 "The accepted types of line ending",
529 PLUMA_TYPE_DOCUMENT_NEWLINE_TYPE,
530 PLUMA_DOCUMENT_NEWLINE_TYPE_LF,
531 G_PARAM_READWRITE |
532 G_PARAM_STATIC_NAME |
533 G_PARAM_STATIC_BLURB));
534
535 /* This signal is used to update the cursor position is the statusbar,
536 * it's emitted either when the insert mark is moved explicitely or
537 * when the buffer changes (insert/delete).
538 * We prevent the emission of the signal during replace_all to
539 * improve performance.
540 */
541 document_signals[CURSOR_MOVED] =
542 g_signal_new ("cursor-moved",
543 G_OBJECT_CLASS_TYPE (object_class),
544 G_SIGNAL_RUN_LAST,
545 G_STRUCT_OFFSET (PlumaDocumentClass, cursor_moved),
546 NULL, NULL, NULL,
547 G_TYPE_NONE,
548 0);
549
550 /**
551 * PlumaDocument::load:
552 * @document: the #PlumaDocument.
553 * @uri: the uri where to load the document from.
554 * @encoding: the #PlumaEncoding to encode the document.
555 * @line_pos: the line to show.
556 * @create: whether the document should be created if it doesn't exist.
557 *
558 * The "load" signal is emitted when a document is loaded.
559 */
560 document_signals[LOAD] =
561 g_signal_new ("load",
562 G_OBJECT_CLASS_TYPE (object_class),
563 G_SIGNAL_RUN_LAST,
564 G_STRUCT_OFFSET (PlumaDocumentClass, load),
565 NULL, NULL, NULL,
566 G_TYPE_NONE,
567 4,
568 G_TYPE_STRING,
569 /* we rely on the fact that the PlumaEncoding pointer stays
570 * the same forever */
571 PLUMA_TYPE_ENCODING | G_SIGNAL_TYPE_STATIC_SCOPE,
572 G_TYPE_INT,
573 G_TYPE_BOOLEAN);
574
575
576 document_signals[LOADING] =
577 g_signal_new ("loading",
578 G_OBJECT_CLASS_TYPE (object_class),
579 G_SIGNAL_RUN_LAST,
580 G_STRUCT_OFFSET (PlumaDocumentClass, loading),
581 NULL, NULL, NULL,
582 G_TYPE_NONE,
583 2,
584 G_TYPE_UINT64,
585 G_TYPE_UINT64);
586
587 document_signals[LOADED] =
588 g_signal_new ("loaded",
589 G_OBJECT_CLASS_TYPE (object_class),
590 G_SIGNAL_RUN_LAST,
591 G_STRUCT_OFFSET (PlumaDocumentClass, loaded),
592 NULL, NULL, NULL,
593 G_TYPE_NONE,
594 1,
595 G_TYPE_POINTER);
596
597 /**
598 * PlumaDocument::save:
599 * @document: the #PlumaDocument.
600 * @uri: the uri where the document is about to be saved.
601 * @encoding: the #PlumaEncoding used to save the document.
602 * @flags: the #PlumaDocumentSaveFlags for the save operation.
603 *
604 * The "save" signal is emitted when the document is saved.
605 */
606 document_signals[SAVE] =
607 g_signal_new ("save",
608 G_OBJECT_CLASS_TYPE (object_class),
609 G_SIGNAL_RUN_LAST,
610 G_STRUCT_OFFSET (PlumaDocumentClass, save),
611 NULL, NULL, NULL,
612 G_TYPE_NONE,
613 3,
614 G_TYPE_STRING,
615 /* we rely on the fact that the PlumaEncoding pointer stays
616 * the same forever */
617 PLUMA_TYPE_ENCODING | G_SIGNAL_TYPE_STATIC_SCOPE,
618 PLUMA_TYPE_DOCUMENT_SAVE_FLAGS);
619
620 document_signals[SAVING] =
621 g_signal_new ("saving",
622 G_OBJECT_CLASS_TYPE (object_class),
623 G_SIGNAL_RUN_LAST,
624 G_STRUCT_OFFSET (PlumaDocumentClass, saving),
625 NULL, NULL, NULL,
626 G_TYPE_NONE,
627 2,
628 G_TYPE_UINT64,
629 G_TYPE_UINT64);
630
631 document_signals[SAVED] =
632 g_signal_new ("saved",
633 G_OBJECT_CLASS_TYPE (object_class),
634 G_SIGNAL_RUN_LAST,
635 G_STRUCT_OFFSET (PlumaDocumentClass, saved),
636 NULL, NULL, NULL,
637 G_TYPE_NONE,
638 1,
639 G_TYPE_POINTER);
640
641 document_signals[SEARCH_HIGHLIGHT_UPDATED] =
642 g_signal_new ("search_highlight_updated",
643 G_OBJECT_CLASS_TYPE (object_class),
644 G_SIGNAL_RUN_LAST,
645 G_STRUCT_OFFSET (PlumaDocumentClass, search_highlight_updated),
646 NULL, NULL, NULL,
647 G_TYPE_NONE,
648 2,
649 GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
650 GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
651 }
652
653 #if !GTK_SOURCE_CHECK_VERSION(4, 3, 1)
654 static gboolean
file_with_bom(GFile * file)655 file_with_bom (GFile *file)
656 {
657 FILE *testfile;
658 gchar c;
659 int i;
660 gchar bom[3];
661 gchar *file_path;
662
663 bom[0] = bom[1] = bom[2] = 0;
664
665 file_path = g_file_get_path (file);
666
667 testfile = fopen (file_path, "r");
668
669 g_free (file_path);
670
671 if (testfile == NULL)
672 {
673 perror ("fopen");
674 return FALSE;
675 }
676
677 for (i = 0; i < 3; i++)
678 {
679 c = fgetc (testfile);
680
681 if (c == EOF)
682 break;
683 else
684 bom[i] = c;
685 }
686
687 fclose (testfile);
688
689 if ((bom[0] == '\357') &&
690 (bom[1] == '\273') &&
691 (bom[2] == '\277'))
692 return TRUE;
693 else
694 return FALSE;
695 }
696 #endif
697
698 static void
set_language(PlumaDocument * doc,GtkSourceLanguage * lang,gboolean set_by_user)699 set_language (PlumaDocument *doc,
700 GtkSourceLanguage *lang,
701 gboolean set_by_user)
702 {
703 GtkSourceLanguage *old_lang;
704
705 #if !GTK_SOURCE_CHECK_VERSION(4, 3, 1)
706 const gchar *new_lang_id;
707 const gchar *bom_langs[] = {
708 "asp", "dtl", "docbook", "html", "mxml", "mallard", "markdown",
709 "mediawiki", "php", "tera", "xml", "xslt", NULL
710 };
711 gboolean is_bom_lang = FALSE;
712 #endif
713
714 pluma_debug (DEBUG_DOCUMENT);
715
716 old_lang = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (doc));
717
718 if (old_lang == lang)
719 return;
720
721 #if !GTK_SOURCE_CHECK_VERSION(4, 3, 1)
722 new_lang_id = gtk_source_language_get_id (lang);
723 if (new_lang_id)
724 is_bom_lang = g_strv_contains (bom_langs, new_lang_id);
725
726 if (is_bom_lang)
727 {
728 GFile *file;
729
730 file = pluma_document_get_location (doc);
731 if (file)
732 {
733 if (!file_with_bom (file))
734 gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (doc), lang);
735
736 g_object_unref (file);
737 }
738 else
739 gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (doc), lang);
740 }
741 else
742 #endif
743 gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (doc), lang);
744
745 if (lang != NULL)
746 {
747 gboolean syntax_hl;
748
749 syntax_hl = g_settings_get_boolean (doc->priv->editor_settings,
750 PLUMA_SETTINGS_SYNTAX_HIGHLIGHTING);
751
752 gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (doc),
753 syntax_hl);
754 }
755 else
756 gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (doc),
757 FALSE);
758
759 if (set_by_user && (doc->priv->uri != NULL))
760 {
761 pluma_document_set_metadata (doc, PLUMA_METADATA_ATTRIBUTE_LANGUAGE,
762 (lang == NULL) ? "_NORMAL_" : gtk_source_language_get_id (lang),
763 NULL);
764 }
765
766 doc->priv->language_set_by_user = set_by_user;
767 }
768
769 static void
set_encoding(PlumaDocument * doc,const PlumaEncoding * encoding,gboolean set_by_user)770 set_encoding (PlumaDocument *doc,
771 const PlumaEncoding *encoding,
772 gboolean set_by_user)
773 {
774 g_return_if_fail (encoding != NULL);
775
776 pluma_debug (DEBUG_DOCUMENT);
777
778 if (doc->priv->encoding == encoding)
779 return;
780
781 doc->priv->encoding = encoding;
782
783 if (set_by_user)
784 {
785 const gchar *charset;
786
787 charset = pluma_encoding_get_charset (encoding);
788
789 pluma_document_set_metadata (doc, PLUMA_METADATA_ATTRIBUTE_ENCODING,
790 charset, NULL);
791 }
792
793 g_object_notify (G_OBJECT (doc), "encoding");
794 }
795
796 static GtkSourceStyleScheme *
get_default_style_scheme(GSettings * editor_settings)797 get_default_style_scheme (GSettings *editor_settings)
798 {
799 gchar *scheme_id;
800 GtkSourceStyleScheme *def_style;
801 GtkSourceStyleSchemeManager *manager;
802
803 manager = pluma_get_style_scheme_manager ();
804 scheme_id = g_settings_get_string (editor_settings, PLUMA_SETTINGS_COLOR_SCHEME);
805 def_style = gtk_source_style_scheme_manager_get_scheme (manager,
806 scheme_id);
807
808 if (def_style == NULL)
809 {
810 g_warning ("Default style scheme '%s' cannot be found, falling back to 'classic' style scheme ", scheme_id);
811
812 def_style = gtk_source_style_scheme_manager_get_scheme (manager, "classic");
813 if (def_style == NULL)
814 {
815 g_warning ("Style scheme 'classic' cannot be found, check your GtkSourceView installation.");
816 }
817 }
818
819 g_free (scheme_id);
820
821 return def_style;
822 }
823
824 static void
on_uri_changed(PlumaDocument * doc,GParamSpec * pspec,gpointer useless)825 on_uri_changed (PlumaDocument *doc,
826 GParamSpec *pspec,
827 gpointer useless)
828 {
829 #ifdef ENABLE_GVFS_METADATA
830 GFile *location;
831
832 location = pluma_document_get_location (doc);
833
834 /* load metadata for this uri: we load sync since metadata is
835 * always local so it should be fast and we need the information
836 * right after the uri was set.
837 */
838 if (location != NULL)
839 {
840 GError *error = NULL;
841
842 if (doc->priv->metadata_info != NULL)
843 g_object_unref (doc->priv->metadata_info);
844
845 doc->priv->metadata_info = g_file_query_info (location,
846 METADATA_QUERY,
847 G_FILE_QUERY_INFO_NONE,
848 NULL,
849 &error);
850
851 if (error != NULL)
852 {
853 if (error->code != G_FILE_ERROR_ISDIR &&
854 error->code != G_FILE_ERROR_NOTDIR &&
855 error->code != G_FILE_ERROR_NOENT)
856 {
857 g_warning ("%s", error->message);
858 }
859
860 g_error_free (error);
861 }
862
863 g_object_unref (location);
864 }
865 #endif
866 }
867
868 static GtkSourceLanguage *
guess_language(PlumaDocument * doc,const gchar * content_type)869 guess_language (PlumaDocument *doc,
870 const gchar *content_type)
871 {
872 gchar *data;
873 GtkSourceLanguage *language = NULL;
874
875 data = pluma_document_get_metadata (doc, PLUMA_METADATA_ATTRIBUTE_LANGUAGE);
876
877 if (data != NULL)
878 {
879 pluma_debug_message (DEBUG_DOCUMENT, "Language from metadata: %s", data);
880
881 if (strcmp (data, "_NORMAL_") != 0)
882 {
883 language = gtk_source_language_manager_get_language (
884 pluma_get_language_manager (),
885 data);
886 }
887
888 g_free (data);
889 }
890 else
891 {
892 GFile *file;
893 gchar *basename = NULL;
894
895 file = pluma_document_get_location (doc);
896 pluma_debug_message (DEBUG_DOCUMENT, "Sniffing Language");
897
898 if (file)
899 {
900 basename = g_file_get_basename (file);
901 }
902 else if (doc->priv->short_name != NULL)
903 {
904 basename = g_strdup (doc->priv->short_name);
905 }
906
907 language = gtk_source_language_manager_guess_language (
908 pluma_get_language_manager (),
909 basename,
910 content_type);
911
912 g_free (basename);
913
914 if (file != NULL)
915 {
916 g_object_unref (file);
917 }
918 }
919
920 return language;
921 }
922
923 static void
on_content_type_changed(PlumaDocument * doc,GParamSpec * pspec,gpointer useless)924 on_content_type_changed (PlumaDocument *doc,
925 GParamSpec *pspec,
926 gpointer useless)
927 {
928 if (!doc->priv->language_set_by_user)
929 {
930 GtkSourceLanguage *language;
931
932 language = guess_language (doc, doc->priv->content_type);
933
934 pluma_debug_message (DEBUG_DOCUMENT, "Language: %s",
935 language != NULL ? gtk_source_language_get_name (language) : "None");
936
937 set_language (doc, language, FALSE);
938 }
939 }
940
941 static gchar *
get_default_content_type(void)942 get_default_content_type (void)
943 {
944 return g_content_type_from_mime_type ("text/plain");
945 }
946
947 static void
pluma_document_init(PlumaDocument * doc)948 pluma_document_init (PlumaDocument *doc)
949 {
950 GtkSourceStyleScheme *style_scheme;
951 gint undo_actions;
952 gboolean bracket_matching;
953 gboolean search_hl;
954
955 pluma_debug (DEBUG_DOCUMENT);
956
957 doc->priv = pluma_document_get_instance_private (doc);
958
959 doc->priv->editor_settings = g_settings_new (PLUMA_SCHEMA_ID);
960
961 doc->priv->uri = NULL;
962 doc->priv->untitled_number = get_untitled_number ();
963
964 doc->priv->metadata_info = NULL;
965
966 doc->priv->content_type = get_default_content_type ();
967
968 doc->priv->readonly = FALSE;
969
970 doc->priv->stop_cursor_moved_emission = FALSE;
971
972 doc->priv->last_save_was_manually = TRUE;
973 doc->priv->language_set_by_user = FALSE;
974
975 doc->priv->dispose_has_run = FALSE;
976
977 doc->priv->mtime = 0;
978
979 doc->priv->time_of_last_save_or_load = g_get_real_time ();
980
981 doc->priv->encoding = pluma_encoding_get_utf8 ();
982
983 doc->priv->newline_type = PLUMA_DOCUMENT_NEWLINE_TYPE_DEFAULT;
984
985 undo_actions = g_settings_get_uint (doc->priv->editor_settings, PLUMA_SETTINGS_MAX_UNDO_ACTIONS);
986
987 bracket_matching = g_settings_get_boolean (doc->priv->editor_settings,
988 PLUMA_SETTINGS_BRACKET_MATCHING);
989 search_hl = g_settings_get_boolean (doc->priv->editor_settings,
990 PLUMA_SETTINGS_SEARCH_HIGHLIGHTING);
991
992 gtk_source_buffer_set_max_undo_levels (GTK_SOURCE_BUFFER (doc),
993 undo_actions);
994
995 gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (doc),
996 bracket_matching);
997
998 pluma_document_set_enable_search_highlighting (doc, search_hl);
999
1000
1001 style_scheme = get_default_style_scheme (doc->priv->editor_settings);
1002 if (style_scheme != NULL)
1003 gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (doc),
1004 style_scheme);
1005
1006 g_signal_connect_after (doc,
1007 "insert-text",
1008 G_CALLBACK (insert_text_cb),
1009 NULL);
1010
1011 g_signal_connect_after (doc,
1012 "delete-range",
1013 G_CALLBACK (delete_range_cb),
1014 NULL);
1015
1016 g_signal_connect (doc,
1017 "notify::content-type",
1018 G_CALLBACK (on_content_type_changed),
1019 NULL);
1020
1021 g_signal_connect (doc,
1022 "notify::uri",
1023 G_CALLBACK (on_uri_changed),
1024 NULL);
1025 }
1026
1027 PlumaDocument *
pluma_document_new(void)1028 pluma_document_new (void)
1029 {
1030 pluma_debug (DEBUG_DOCUMENT);
1031
1032 return PLUMA_DOCUMENT (g_object_new (PLUMA_TYPE_DOCUMENT, NULL));
1033 }
1034
1035 static void
set_content_type_no_guess(PlumaDocument * doc,const gchar * content_type)1036 set_content_type_no_guess (PlumaDocument *doc,
1037 const gchar *content_type)
1038 {
1039 pluma_debug (DEBUG_DOCUMENT);
1040
1041 if (doc->priv->content_type != NULL && content_type != NULL &&
1042 (0 == strcmp (doc->priv->content_type, content_type)))
1043 return;
1044
1045 g_free (doc->priv->content_type);
1046
1047 if (content_type == NULL || g_content_type_is_unknown (content_type))
1048 doc->priv->content_type = get_default_content_type ();
1049 else
1050 doc->priv->content_type = g_strdup (content_type);
1051
1052 g_object_notify (G_OBJECT (doc), "content-type");
1053 }
1054
1055 static void
set_content_type(PlumaDocument * doc,const gchar * content_type)1056 set_content_type (PlumaDocument *doc,
1057 const gchar *content_type)
1058 {
1059 pluma_debug (DEBUG_DOCUMENT);
1060
1061 if (content_type == NULL)
1062 {
1063 GFile *file;
1064 gchar *guessed_type = NULL;
1065
1066 /* If content type is null, we guess from the filename */
1067 file = pluma_document_get_location (doc);
1068 if (file != NULL)
1069 {
1070 gchar *basename;
1071
1072 basename = g_file_get_basename (file);
1073 guessed_type = g_content_type_guess (basename, NULL, 0, NULL);
1074
1075 g_free (basename);
1076 g_object_unref (file);
1077 }
1078
1079 set_content_type_no_guess (doc, guessed_type);
1080
1081 g_free (guessed_type);
1082 }
1083 else
1084 {
1085 set_content_type_no_guess (doc, content_type);
1086 }
1087 }
1088
1089 /**
1090 * pluma_document_set_content_type:
1091 * @doc:
1092 * @content_type: (allow-none):
1093 */
1094 void
pluma_document_set_content_type(PlumaDocument * doc,const gchar * content_type)1095 pluma_document_set_content_type (PlumaDocument *doc,
1096 const gchar *content_type)
1097 {
1098 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
1099
1100 set_content_type (doc, content_type);
1101 }
1102
1103 static void
set_uri(PlumaDocument * doc,const gchar * uri)1104 set_uri (PlumaDocument *doc,
1105 const gchar *uri)
1106 {
1107 pluma_debug (DEBUG_DOCUMENT);
1108
1109 g_return_if_fail ((uri == NULL) || pluma_utils_is_valid_uri (uri));
1110
1111 if (uri != NULL)
1112 {
1113 if (doc->priv->uri == uri)
1114 return;
1115
1116 g_free (doc->priv->uri);
1117 doc->priv->uri = g_strdup (uri);
1118
1119 if (doc->priv->untitled_number > 0)
1120 {
1121 release_untitled_number (doc->priv->untitled_number);
1122 doc->priv->untitled_number = 0;
1123 }
1124 }
1125
1126 g_object_notify (G_OBJECT (doc), "uri");
1127
1128 if (doc->priv->short_name == NULL)
1129 {
1130 g_object_notify (G_OBJECT (doc), "shortname");
1131 }
1132 }
1133
1134
1135 /**
1136 * pluma_document_get_location:
1137 * @doc: a #PlumaDocument
1138 *
1139 * Returns: (allow-none) (transfer full): a new #GFile
1140 */
1141 GFile *
pluma_document_get_location(PlumaDocument * doc)1142 pluma_document_get_location (PlumaDocument *doc)
1143 {
1144 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
1145
1146 return doc->priv->uri == NULL ? NULL : g_file_new_for_uri (doc->priv->uri);
1147 }
1148
1149 gchar *
pluma_document_get_uri(PlumaDocument * doc)1150 pluma_document_get_uri (PlumaDocument *doc)
1151 {
1152 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
1153
1154 return g_strdup (doc->priv->uri);
1155 }
1156
1157 void
pluma_document_set_uri(PlumaDocument * doc,const gchar * uri)1158 pluma_document_set_uri (PlumaDocument *doc,
1159 const gchar *uri)
1160 {
1161 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
1162 g_return_if_fail (uri != NULL);
1163
1164 set_uri (doc, uri);
1165 set_content_type (doc, NULL);
1166 }
1167
1168 /**
1169 * pluma_document_get_uri_for_display:
1170 * @doc:
1171 *
1172 * Note: this never returns %NULL.
1173 **/
1174 gchar *
pluma_document_get_uri_for_display(PlumaDocument * doc)1175 pluma_document_get_uri_for_display (PlumaDocument *doc)
1176 {
1177 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), g_strdup (""));
1178
1179 if (doc->priv->uri == NULL)
1180 return g_strdup_printf (_("Unsaved Document %d"),
1181 doc->priv->untitled_number);
1182 else
1183 return pluma_utils_uri_for_display (doc->priv->uri);
1184 }
1185
1186 /**
1187 * pluma_document_get_short_name_for_display:
1188 * @doc:
1189 *
1190 * Note: this never returns %NULL.
1191 **/
1192 gchar *
pluma_document_get_short_name_for_display(PlumaDocument * doc)1193 pluma_document_get_short_name_for_display (PlumaDocument *doc)
1194 {
1195 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), g_strdup (""));
1196
1197 if (doc->priv->short_name != NULL)
1198 return g_strdup (doc->priv->short_name);
1199 else if (doc->priv->uri == NULL)
1200 return g_strdup_printf (_("Unsaved Document %d"),
1201 doc->priv->untitled_number);
1202 else
1203 return pluma_utils_basename_for_display (doc->priv->uri);
1204 }
1205
1206 /**
1207 * pluma_document_set_short_name_for_display:
1208 * @doc:
1209 * @name: (allow-none):
1210 */
1211 void
pluma_document_set_short_name_for_display(PlumaDocument * doc,const gchar * short_name)1212 pluma_document_set_short_name_for_display (PlumaDocument *doc,
1213 const gchar *short_name)
1214 {
1215 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
1216
1217 g_free (doc->priv->short_name);
1218 doc->priv->short_name = g_strdup (short_name);
1219
1220 g_object_notify (G_OBJECT (doc), "shortname");
1221 }
1222
1223 gchar *
pluma_document_get_content_type(PlumaDocument * doc)1224 pluma_document_get_content_type (PlumaDocument *doc)
1225 {
1226 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
1227
1228 return g_strdup (doc->priv->content_type);
1229 }
1230
1231 /**
1232 * pluma_document_get_mime_type:
1233 * @doc:
1234 *
1235 * Note: this never returns %NULL.
1236 **/
1237 gchar *
pluma_document_get_mime_type(PlumaDocument * doc)1238 pluma_document_get_mime_type (PlumaDocument *doc)
1239 {
1240 gchar *mime_type = NULL;
1241
1242 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), g_strdup ("text/plain"));
1243
1244 if ((doc->priv->content_type != NULL) &&
1245 (!g_content_type_is_unknown (doc->priv->content_type)))
1246 {
1247 mime_type = g_content_type_get_mime_type (doc->priv->content_type);
1248 }
1249
1250 return mime_type != NULL ? mime_type : g_strdup ("text/plain");
1251 }
1252
1253 /* Note: do not emit the notify::read-only signal */
1254 static gboolean
set_readonly(PlumaDocument * doc,gboolean readonly)1255 set_readonly (PlumaDocument *doc,
1256 gboolean readonly)
1257 {
1258 pluma_debug (DEBUG_DOCUMENT);
1259
1260 readonly = (readonly != FALSE);
1261
1262 if (doc->priv->readonly == readonly)
1263 return FALSE;
1264
1265 doc->priv->readonly = readonly;
1266
1267 return TRUE;
1268 }
1269
1270 /**
1271 * pluma_document_set_readonly:
1272 * @doc: a #PlumaDocument
1273 * @readonly: %TRUE to se the document as read-only
1274 *
1275 * If @readonly is %TRUE sets @doc as read-only.
1276 */
1277 void
_pluma_document_set_readonly(PlumaDocument * doc,gboolean readonly)1278 _pluma_document_set_readonly (PlumaDocument *doc,
1279 gboolean readonly)
1280 {
1281 pluma_debug (DEBUG_DOCUMENT);
1282
1283 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
1284
1285 if (set_readonly (doc, readonly))
1286 {
1287 g_object_notify (G_OBJECT (doc), "read-only");
1288 }
1289 }
1290
1291 gboolean
pluma_document_get_readonly(PlumaDocument * doc)1292 pluma_document_get_readonly (PlumaDocument *doc)
1293 {
1294 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), TRUE);
1295
1296 return doc->priv->readonly;
1297 }
1298
1299 gboolean
_pluma_document_check_externally_modified(PlumaDocument * doc)1300 _pluma_document_check_externally_modified (PlumaDocument *doc)
1301 {
1302 GFile *gfile;
1303 GFileInfo *info;
1304
1305 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
1306
1307 if (doc->priv->uri == NULL)
1308 {
1309 return FALSE;
1310 }
1311
1312 gfile = g_file_new_for_uri (doc->priv->uri);
1313 info = g_file_query_info (gfile,
1314 G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
1315 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "," \
1316 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
1317 G_FILE_QUERY_INFO_NONE,
1318 NULL, NULL);
1319 g_object_unref (gfile);
1320
1321 if (info != NULL)
1322 {
1323 /* While at it also check if permissions changed */
1324 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
1325 {
1326 gboolean read_only;
1327
1328 read_only = !g_file_info_get_attribute_boolean (info,
1329 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
1330
1331 _pluma_document_set_readonly (doc, read_only);
1332 }
1333
1334 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
1335 {
1336 guint64 timeval;
1337
1338 timeval = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED) * G_USEC_PER_SEC;
1339 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC))
1340 {
1341 guint32 usec;
1342
1343 usec = g_file_info_get_attribute_uint32 (info,
1344 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
1345 timeval += (guint64) usec;
1346 }
1347 g_object_unref (info);
1348
1349 return (((gint64) timeval) > doc->priv->mtime);
1350 }
1351 }
1352
1353 return FALSE;
1354 }
1355
1356 static void
reset_temp_loading_data(PlumaDocument * doc)1357 reset_temp_loading_data (PlumaDocument *doc)
1358 {
1359 /* the loader has been used, throw it away */
1360 g_object_unref (doc->priv->loader);
1361 doc->priv->loader = NULL;
1362
1363 doc->priv->requested_encoding = NULL;
1364 doc->priv->requested_line_pos = 0;
1365 }
1366
1367 static void
document_loader_loaded(PlumaDocumentLoader * loader,const GError * error,PlumaDocument * doc)1368 document_loader_loaded (PlumaDocumentLoader *loader,
1369 const GError *error,
1370 PlumaDocument *doc)
1371 {
1372 /* load was successful */
1373 if (error == NULL ||
1374 (error->domain == PLUMA_DOCUMENT_ERROR &&
1375 error->code == PLUMA_DOCUMENT_ERROR_CONVERSION_FALLBACK))
1376 {
1377 GtkTextIter iter;
1378 GFileInfo *info;
1379 gboolean restore_cursor;
1380 const gchar *content_type = NULL;
1381 gboolean read_only = FALSE;
1382 guint64 mtime = 0;
1383
1384 info = pluma_document_loader_get_info (loader);
1385
1386 if (info)
1387 {
1388 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE))
1389 content_type = g_file_info_get_attribute_string (info,
1390 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1391
1392 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
1393 mtime = g_file_info_get_attribute_uint64 (info,
1394 G_FILE_ATTRIBUTE_TIME_MODIFIED) * G_USEC_PER_SEC;
1395
1396 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC))
1397 {
1398 guint32 usec;
1399
1400 usec = g_file_info_get_attribute_uint32 (info,
1401 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
1402 mtime += (guint64) usec;
1403 }
1404
1405 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
1406 read_only = !g_file_info_get_attribute_boolean (info,
1407 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
1408 }
1409
1410 doc->priv->mtime = (gint64) mtime;
1411
1412 set_readonly (doc, read_only);
1413
1414 doc->priv->time_of_last_save_or_load = g_get_real_time ();
1415
1416 set_encoding (doc,
1417 pluma_document_loader_get_encoding (loader),
1418 (doc->priv->requested_encoding != NULL));
1419
1420 set_content_type (doc, content_type);
1421
1422 pluma_document_set_newline_type (doc,
1423 pluma_document_loader_get_newline_type (loader));
1424
1425 restore_cursor = g_settings_get_boolean (doc->priv->editor_settings,
1426 PLUMA_SETTINGS_RESTORE_CURSOR_POSITION);
1427
1428 /* move the cursor at the requested line if any */
1429 if (doc->priv->requested_line_pos > 0)
1430 {
1431 /* line_pos - 1 because get_iter_at_line counts from 0 */
1432 gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (doc),
1433 &iter,
1434 doc->priv->requested_line_pos - 1);
1435 }
1436 /* else, if enabled, to the position stored in the metadata */
1437 else if (restore_cursor)
1438 {
1439 gchar *pos;
1440 gint offset;
1441
1442 pos = pluma_document_get_metadata (doc, PLUMA_METADATA_ATTRIBUTE_POSITION);
1443
1444 offset = pos ? atoi (pos) : 0;
1445 g_free (pos);
1446
1447 gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (doc),
1448 &iter,
1449 MAX (offset, 0));
1450
1451 /* make sure it's a valid position, if the file
1452 * changed we may have ended up in the middle of
1453 * a utf8 character cluster */
1454 if (!gtk_text_iter_is_cursor_position (&iter))
1455 {
1456 gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (doc),
1457 &iter);
1458 }
1459 }
1460 /* otherwise to the top */
1461 else
1462 {
1463 gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (doc),
1464 &iter);
1465 }
1466
1467 gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter);
1468 }
1469
1470 /* special case creating a named new doc */
1471 else if (doc->priv->create &&
1472 (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND) &&
1473 (pluma_utils_uri_has_file_scheme (doc->priv->uri)))
1474 {
1475 reset_temp_loading_data (doc);
1476
1477 g_signal_emit (doc,
1478 document_signals[LOADED],
1479 0,
1480 NULL);
1481
1482 return;
1483 }
1484
1485 g_signal_emit (doc,
1486 document_signals[LOADED],
1487 0,
1488 error);
1489
1490 reset_temp_loading_data (doc);
1491 }
1492
1493 static void
document_loader_loading(PlumaDocumentLoader * loader,gboolean completed,const GError * error,PlumaDocument * doc)1494 document_loader_loading (PlumaDocumentLoader *loader,
1495 gboolean completed,
1496 const GError *error,
1497 PlumaDocument *doc)
1498 {
1499 if (completed)
1500 {
1501 document_loader_loaded (loader, error, doc);
1502 }
1503 else
1504 {
1505 goffset size = 0;
1506 goffset read;
1507 GFileInfo *info;
1508
1509 info = pluma_document_loader_get_info (loader);
1510
1511 if (info && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
1512 size = g_file_info_get_attribute_uint64 (info,
1513 G_FILE_ATTRIBUTE_STANDARD_SIZE);
1514
1515 read = pluma_document_loader_get_bytes_read (loader);
1516
1517 g_signal_emit (doc,
1518 document_signals[LOADING],
1519 0,
1520 read,
1521 size);
1522 }
1523 }
1524
1525 static void
pluma_document_load_real(PlumaDocument * doc,const gchar * uri,const PlumaEncoding * encoding,gint line_pos,gboolean create)1526 pluma_document_load_real (PlumaDocument *doc,
1527 const gchar *uri,
1528 const PlumaEncoding *encoding,
1529 gint line_pos,
1530 gboolean create)
1531 {
1532 g_return_if_fail (doc->priv->loader == NULL);
1533
1534 pluma_debug_message (DEBUG_DOCUMENT, "load_real: uri = %s", uri);
1535
1536 /* create a loader. It will be destroyed when loading is completed */
1537 doc->priv->loader = pluma_document_loader_new (doc, uri, encoding);
1538
1539 g_signal_connect (doc->priv->loader,
1540 "loading",
1541 G_CALLBACK (document_loader_loading),
1542 doc);
1543
1544 doc->priv->create = create;
1545 doc->priv->requested_encoding = encoding;
1546 doc->priv->requested_line_pos = line_pos;
1547
1548 set_uri (doc, uri);
1549 set_content_type (doc, NULL);
1550
1551 pluma_document_loader_load (doc->priv->loader);
1552 }
1553
1554 /**
1555 * pluma_document_load:
1556 * @doc: the #PlumaDocument.
1557 * @uri: the uri where to load the document from.
1558 * @encoding: the #PlumaEncoding to encode the document.
1559 * @line_pos: the line to show.
1560 * @create: whether the document should be created if it doesn't exist.
1561 *
1562 * Load a document. This results in the "load" signal to be emitted.
1563 */
1564 void
pluma_document_load(PlumaDocument * doc,const gchar * uri,const PlumaEncoding * encoding,gint line_pos,gboolean create)1565 pluma_document_load (PlumaDocument *doc,
1566 const gchar *uri,
1567 const PlumaEncoding *encoding,
1568 gint line_pos,
1569 gboolean create)
1570 {
1571 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
1572 g_return_if_fail (uri != NULL);
1573 g_return_if_fail (pluma_utils_is_valid_uri (uri));
1574
1575 g_signal_emit (doc, document_signals[LOAD], 0, uri, encoding, line_pos, create);
1576 }
1577
1578 /**
1579 * pluma_document_load_cancel:
1580 * @doc: the #PlumaDocument.
1581 *
1582 * Cancel load of a document.
1583 */
1584 gboolean
pluma_document_load_cancel(PlumaDocument * doc)1585 pluma_document_load_cancel (PlumaDocument *doc)
1586 {
1587 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
1588
1589 if (doc->priv->loader == NULL)
1590 return FALSE;
1591
1592 return pluma_document_loader_cancel (doc->priv->loader);
1593 }
1594
1595 static void
document_saver_saving(PlumaDocumentSaver * saver,gboolean completed,const GError * error,PlumaDocument * doc)1596 document_saver_saving (PlumaDocumentSaver *saver,
1597 gboolean completed,
1598 const GError *error,
1599 PlumaDocument *doc)
1600 {
1601 pluma_debug (DEBUG_DOCUMENT);
1602
1603 if (completed)
1604 {
1605 /* save was successful */
1606 if (error == NULL)
1607 {
1608 const gchar *uri;
1609 const gchar *content_type = NULL;
1610 guint64 mtime = 0;
1611 GFileInfo *info;
1612
1613 uri = pluma_document_saver_get_uri (saver);
1614 set_uri (doc, uri);
1615
1616 info = pluma_document_saver_get_info (saver);
1617
1618 if (info != NULL)
1619 {
1620 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE))
1621 content_type = g_file_info_get_attribute_string (info,
1622 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
1623
1624 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
1625 mtime = g_file_info_get_attribute_uint64 (info,
1626 G_FILE_ATTRIBUTE_TIME_MODIFIED) * G_USEC_PER_SEC;
1627
1628 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC))
1629 {
1630 guint32 usec;
1631
1632 usec = g_file_info_get_attribute_uint32 (info,
1633 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
1634 mtime += (guint64) usec;
1635 }
1636 }
1637
1638 set_content_type (doc, content_type);
1639 doc->priv->mtime = (gint64) mtime;
1640
1641 doc->priv->time_of_last_save_or_load = g_get_real_time ();
1642
1643 _pluma_document_set_readonly (doc, FALSE);
1644
1645 gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (doc),
1646 FALSE);
1647
1648 set_encoding (doc,
1649 doc->priv->requested_encoding,
1650 TRUE);
1651 }
1652
1653 g_signal_emit (doc,
1654 document_signals[SAVED],
1655 0,
1656 error);
1657
1658 /* the saver has been used, throw it away */
1659 g_object_unref (doc->priv->saver);
1660 doc->priv->saver = NULL;
1661 }
1662 else
1663 {
1664 goffset size = 0;
1665 goffset written = 0;
1666
1667 size = pluma_document_saver_get_file_size (saver);
1668 written = pluma_document_saver_get_bytes_written (saver);
1669
1670 pluma_debug_message (DEBUG_DOCUMENT, "save progress: %" G_GINT64_FORMAT " of %" G_GINT64_FORMAT, written, size);
1671
1672 g_signal_emit (doc,
1673 document_signals[SAVING],
1674 0,
1675 written,
1676 size);
1677 }
1678 }
1679
1680 static void
pluma_document_save_real(PlumaDocument * doc,const gchar * uri,const PlumaEncoding * encoding,PlumaDocumentSaveFlags flags)1681 pluma_document_save_real (PlumaDocument *doc,
1682 const gchar *uri,
1683 const PlumaEncoding *encoding,
1684 PlumaDocumentSaveFlags flags)
1685 {
1686 g_return_if_fail (doc->priv->saver == NULL);
1687
1688 /* create a saver, it will be destroyed once saving is complete */
1689 doc->priv->saver = pluma_document_saver_new (doc, uri, encoding,
1690 doc->priv->newline_type,
1691 flags);
1692
1693 g_signal_connect (doc->priv->saver,
1694 "saving",
1695 G_CALLBACK (document_saver_saving),
1696 doc);
1697
1698 doc->priv->requested_encoding = encoding;
1699
1700 pluma_document_saver_save (doc->priv->saver,
1701 &doc->priv->mtime);
1702 }
1703
1704 /**
1705 * pluma_document_save:
1706 * @doc: the #PlumaDocument.
1707 * @flags: optionnal #PlumaDocumentSaveFlags.
1708 *
1709 * Save the document to its previous location. This results in the "save"
1710 * signal to be emitted.
1711 */
1712 void
pluma_document_save(PlumaDocument * doc,PlumaDocumentSaveFlags flags)1713 pluma_document_save (PlumaDocument *doc,
1714 PlumaDocumentSaveFlags flags)
1715 {
1716 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
1717 g_return_if_fail (doc->priv->uri != NULL);
1718
1719 g_signal_emit (doc,
1720 document_signals[SAVE],
1721 0,
1722 doc->priv->uri,
1723 doc->priv->encoding,
1724 flags);
1725 }
1726
1727 /**
1728 * pluma_document_save_as:
1729 * @doc: the #PlumaDocument.
1730 * @uri: the uri where to save the document.
1731 * @encoding: the #PlumaEncoding to encode the document.
1732 * @flags: optionnal #PlumaDocumentSaveFlags.
1733 *
1734 * Save the document to a new location. This results in the "save" signal
1735 * to be emitted.
1736 */
1737 void
pluma_document_save_as(PlumaDocument * doc,const gchar * uri,const PlumaEncoding * encoding,PlumaDocumentSaveFlags flags)1738 pluma_document_save_as (PlumaDocument *doc,
1739 const gchar *uri,
1740 const PlumaEncoding *encoding,
1741 PlumaDocumentSaveFlags flags)
1742 {
1743 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
1744 g_return_if_fail (uri != NULL);
1745 g_return_if_fail (encoding != NULL);
1746
1747 /* priv->mtime refers to the the old uri (if any). Thus, it should be
1748 * ignored when saving as. */
1749 g_signal_emit (doc,
1750 document_signals[SAVE],
1751 0,
1752 uri,
1753 encoding,
1754 flags | PLUMA_DOCUMENT_SAVE_IGNORE_MTIME);
1755 }
1756
1757 gboolean
pluma_document_insert_file(PlumaDocument * doc,GtkTextIter * iter,const gchar * uri,const PlumaEncoding * encoding)1758 pluma_document_insert_file (PlumaDocument *doc,
1759 GtkTextIter *iter,
1760 const gchar *uri,
1761 const PlumaEncoding *encoding)
1762 {
1763 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
1764 g_return_val_if_fail (iter != NULL, FALSE);
1765 g_return_val_if_fail (gtk_text_iter_get_buffer (iter) ==
1766 GTK_TEXT_BUFFER (doc), FALSE);
1767
1768 /* TODO */
1769
1770 return FALSE;
1771 }
1772
1773 gboolean
pluma_document_is_untouched(PlumaDocument * doc)1774 pluma_document_is_untouched (PlumaDocument *doc)
1775 {
1776 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), TRUE);
1777
1778 return (doc->priv->uri == NULL) &&
1779 (!gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)));
1780 }
1781
1782 gboolean
pluma_document_is_untitled(PlumaDocument * doc)1783 pluma_document_is_untitled (PlumaDocument *doc)
1784 {
1785 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), TRUE);
1786
1787 return (doc->priv->uri == NULL);
1788 }
1789
1790 gboolean
pluma_document_is_local(PlumaDocument * doc)1791 pluma_document_is_local (PlumaDocument *doc)
1792 {
1793 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
1794
1795 if (doc->priv->uri == NULL)
1796 {
1797 return FALSE;
1798 }
1799
1800 return pluma_utils_uri_has_file_scheme (doc->priv->uri);
1801 }
1802
1803 gboolean
pluma_document_get_deleted(PlumaDocument * doc)1804 pluma_document_get_deleted (PlumaDocument *doc)
1805 {
1806 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
1807
1808 return doc->priv->uri && !pluma_utils_uri_exists (doc->priv->uri);
1809 }
1810
1811 /*
1812 * If @line is bigger than the lines of the document, the cursor is moved
1813 * to the last line and FALSE is returned.
1814 */
1815 gboolean
pluma_document_goto_line(PlumaDocument * doc,gint line)1816 pluma_document_goto_line (PlumaDocument *doc,
1817 gint line)
1818 {
1819 gboolean ret = TRUE;
1820 guint line_count;
1821 GtkTextIter iter;
1822
1823 pluma_debug (DEBUG_DOCUMENT);
1824
1825 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
1826 g_return_val_if_fail (line >= -1, FALSE);
1827
1828 line_count = gtk_text_buffer_get_line_count (GTK_TEXT_BUFFER (doc));
1829
1830 if (line >= line_count)
1831 {
1832 ret = FALSE;
1833 gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc),
1834 &iter);
1835 }
1836 else
1837 {
1838 gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (doc),
1839 &iter,
1840 line);
1841 }
1842
1843 gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter);
1844
1845 return ret;
1846 }
1847
1848 gboolean
pluma_document_goto_line_offset(PlumaDocument * doc,gint line,gint line_offset)1849 pluma_document_goto_line_offset (PlumaDocument *doc,
1850 gint line,
1851 gint line_offset)
1852 {
1853 gboolean ret = TRUE;
1854 guint offset_count;
1855 GtkTextIter iter;
1856
1857 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
1858 g_return_val_if_fail (line >= -1, FALSE);
1859 g_return_val_if_fail (line_offset >= -1, FALSE);
1860
1861 gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (doc),
1862 &iter,
1863 line);
1864
1865 offset_count = gtk_text_iter_get_chars_in_line (&iter);
1866 if (line_offset > offset_count)
1867 {
1868 ret = FALSE;
1869 }
1870 else
1871 {
1872 gtk_text_iter_set_line_offset (&iter, line_offset);
1873 }
1874
1875 gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (doc), &iter);
1876
1877 return ret;
1878 }
1879
1880 static gint
compute_num_of_lines(const gchar * text)1881 compute_num_of_lines (const gchar *text)
1882 {
1883 const gchar *p;
1884 gint len;
1885 gint n = 1;
1886
1887 g_return_val_if_fail (text != NULL, 0);
1888
1889 len = strlen (text);
1890 p = text;
1891
1892 while (len > 0)
1893 {
1894 gint del, par;
1895
1896 pango_find_paragraph_boundary (p, len, &del, &par);
1897
1898 if (del == par) /* not found */
1899 break;
1900
1901 p += par;
1902 len -= par;
1903 ++n;
1904 }
1905
1906 return n;
1907 }
1908
1909 /**
1910 * pluma_document_set_search_text:
1911 * @doc:
1912 * @text: (allow-none):
1913 * @flags:
1914 **/
1915 void
pluma_document_set_search_text(PlumaDocument * doc,const gchar * text,guint flags)1916 pluma_document_set_search_text (PlumaDocument *doc,
1917 const gchar *text,
1918 guint flags)
1919 {
1920 gchar *converted_text;
1921 gboolean notify = FALSE;
1922 gboolean update_to_search_region = FALSE;
1923
1924 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
1925 g_return_if_fail ((text == NULL) || (doc->priv->search_text != text));
1926 g_return_if_fail ((text == NULL) || g_utf8_validate (text, -1, NULL));
1927
1928 pluma_debug_message (DEBUG_DOCUMENT, "text = %s", text);
1929
1930 if (text != NULL)
1931 {
1932 if (*text != '\0')
1933 {
1934 converted_text = pluma_utils_unescape_search_text (text);
1935 notify = !pluma_document_get_can_search_again (doc);
1936 }
1937 else
1938 {
1939 converted_text = g_strdup("");
1940 notify = pluma_document_get_can_search_again (doc);
1941 }
1942
1943 g_free (doc->priv->search_text);
1944
1945 doc->priv->search_text = converted_text;
1946 doc->priv->num_of_lines_search_text = compute_num_of_lines (doc->priv->search_text);
1947 update_to_search_region = TRUE;
1948 }
1949
1950 if (!PLUMA_SEARCH_IS_DONT_SET_FLAGS (flags))
1951 {
1952 if (doc->priv->search_flags != flags)
1953 update_to_search_region = TRUE;
1954
1955 doc->priv->search_flags = flags;
1956
1957 }
1958
1959 if (update_to_search_region)
1960 {
1961 GtkTextIter begin;
1962 GtkTextIter end;
1963
1964 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc),
1965 &begin,
1966 &end);
1967
1968 to_search_region_range (doc,
1969 &begin,
1970 &end);
1971 }
1972
1973 if (notify)
1974 g_object_notify (G_OBJECT (doc), "can-search-again");
1975 }
1976
1977 /**
1978 * pluma_document_get_search_text:
1979 * @doc:
1980 * @flags: (allow-none):
1981 */
1982 gchar *
pluma_document_get_search_text(PlumaDocument * doc,guint * flags)1983 pluma_document_get_search_text (PlumaDocument *doc,
1984 guint *flags)
1985 {
1986 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
1987
1988 if (flags != NULL)
1989 *flags = doc->priv->search_flags;
1990
1991 return pluma_utils_escape_search_text (doc->priv->search_text);
1992 }
1993
1994 /**
1995 * pluma_document_set_last_replace_text:
1996 * @doc:
1997 * @text: (allow-none):
1998 **/
1999 void
pluma_document_set_last_replace_text(PlumaDocument * doc,const gchar * text)2000 pluma_document_set_last_replace_text (PlumaDocument *doc,
2001 const gchar *text)
2002 {
2003 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
2004
2005 g_free(doc->priv->last_replace_text);
2006
2007 pluma_debug_message (DEBUG_SEARCH, "last_replace_text = %s", text == NULL ? "NULL" : text);
2008 doc->priv->last_replace_text = g_strdup(text);
2009 }
2010
2011 /**
2012 * pluma_document_get_last_replace_text:
2013 * @doc:
2014 */
2015 gchar *
pluma_document_get_last_replace_text(PlumaDocument * doc)2016 pluma_document_get_last_replace_text (PlumaDocument *doc)
2017 {
2018 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
2019
2020 return doc->priv->last_replace_text;
2021 }
2022
2023 gboolean
pluma_document_get_can_search_again(PlumaDocument * doc)2024 pluma_document_get_can_search_again (PlumaDocument *doc)
2025 {
2026 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
2027
2028 return ((doc->priv->search_text != NULL) &&
2029 (*doc->priv->search_text != '\0'));
2030 }
2031
2032 /**
2033 * pluma_document_search_forward:
2034 * @doc:
2035 * @start: (allow-none):
2036 * @end: (allow-none):
2037 * @match_start: (allow-none):
2038 * @match_end: (allow-none):
2039 **/
2040 gboolean
pluma_document_search_forward(PlumaDocument * doc,const GtkTextIter * start,const GtkTextIter * end,GtkTextIter * match_start,GtkTextIter * match_end)2041 pluma_document_search_forward (PlumaDocument *doc,
2042 const GtkTextIter *start,
2043 const GtkTextIter *end,
2044 GtkTextIter *match_start,
2045 GtkTextIter *match_end)
2046 {
2047 GtkTextIter iter;
2048 GtkTextSearchFlags search_flags;
2049 gboolean found = FALSE;
2050 GtkTextIter m_start;
2051 GtkTextIter m_end;
2052
2053 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
2054 g_return_val_if_fail ((start == NULL) ||
2055 (gtk_text_iter_get_buffer (start) == GTK_TEXT_BUFFER (doc)), FALSE);
2056 g_return_val_if_fail ((end == NULL) ||
2057 (gtk_text_iter_get_buffer (end) == GTK_TEXT_BUFFER (doc)), FALSE);
2058
2059 if (doc->priv->search_text == NULL)
2060 {
2061 pluma_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == NULL\n");
2062 return FALSE;
2063 }
2064 else
2065 pluma_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == \"%s\"\n", doc->priv->search_text);
2066
2067 if (start == NULL)
2068 gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (doc), &iter);
2069 else
2070 iter = *start;
2071
2072 search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
2073
2074 if (!PLUMA_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags))
2075 {
2076 search_flags = search_flags | GTK_TEXT_SEARCH_CASE_INSENSITIVE;
2077 }
2078
2079 while (!found)
2080 {
2081 if(!PLUMA_SEARCH_IS_MATCH_REGEX(doc->priv->search_flags))
2082 {
2083 found = gtk_text_iter_forward_search (&iter,
2084 doc->priv->search_text,
2085 search_flags,
2086 &m_start,
2087 &m_end,
2088 end);
2089 } else {
2090 found = pluma_gtk_text_iter_regex_search (&iter,
2091 doc->priv->search_text,
2092 search_flags,
2093 &m_start,
2094 &m_end,
2095 end,
2096 TRUE,
2097 &doc->priv->last_replace_text);
2098 }
2099
2100 if (found && PLUMA_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags))
2101 {
2102 found = gtk_text_iter_starts_word (&m_start) &&
2103 gtk_text_iter_ends_word (&m_end);
2104
2105 if (!found)
2106 iter = m_end;
2107 }
2108 else
2109 break;
2110 }
2111
2112 if (found && (match_start != NULL))
2113 *match_start = m_start;
2114
2115 if (found && (match_end != NULL))
2116 *match_end = m_end;
2117
2118 return found;
2119 }
2120
2121 /**
2122 * pluma_document_search_backward:
2123 * @doc:
2124 * @start: (allow-none):
2125 * @end: (allow-none):
2126 * @match_start: (allow-none):
2127 * @match_end: (allow-none):
2128 **/
2129 gboolean
pluma_document_search_backward(PlumaDocument * doc,const GtkTextIter * start,const GtkTextIter * end,GtkTextIter * match_start,GtkTextIter * match_end)2130 pluma_document_search_backward (PlumaDocument *doc,
2131 const GtkTextIter *start,
2132 const GtkTextIter *end,
2133 GtkTextIter *match_start,
2134 GtkTextIter *match_end)
2135 {
2136 GtkTextIter iter;
2137 GtkTextSearchFlags search_flags;
2138 gboolean found = FALSE;
2139 GtkTextIter m_start;
2140 GtkTextIter m_end;
2141
2142 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
2143 g_return_val_if_fail ((start == NULL) ||
2144 ( gtk_text_iter_get_buffer (start) == GTK_TEXT_BUFFER (doc)), FALSE);
2145 g_return_val_if_fail ((end == NULL) ||
2146 (gtk_text_iter_get_buffer (end) == GTK_TEXT_BUFFER (doc)), FALSE);
2147
2148 if (doc->priv->search_text == NULL)
2149 {
2150 pluma_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == NULL\n");
2151 return FALSE;
2152 }
2153 else
2154 pluma_debug_message (DEBUG_DOCUMENT, "doc->priv->search_text == \"%s\"\n", doc->priv->search_text);
2155
2156 if (end == NULL)
2157 gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &iter);
2158 else
2159 iter = *end;
2160
2161 search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
2162
2163 if (!PLUMA_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags))
2164 {
2165 search_flags = search_flags | GTK_TEXT_SEARCH_CASE_INSENSITIVE;
2166 }
2167
2168 while (!found)
2169 {
2170 if(!PLUMA_SEARCH_IS_MATCH_REGEX(doc->priv->search_flags))
2171 {
2172 found = gtk_text_iter_backward_search (&iter,
2173 doc->priv->search_text,
2174 search_flags,
2175 &m_start,
2176 &m_end,
2177 start);
2178 }
2179 else
2180 {
2181 found = pluma_gtk_text_iter_regex_search (&iter,
2182 doc->priv->search_text,
2183 search_flags,
2184 &m_start,
2185 &m_end,
2186 start,
2187 FALSE,
2188 &doc->priv->last_replace_text);
2189 }
2190
2191 if (found && PLUMA_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags))
2192 {
2193 found = gtk_text_iter_starts_word (&m_start) &&
2194 gtk_text_iter_ends_word (&m_end);
2195
2196 if (!found)
2197 iter = m_start;
2198 }
2199 else
2200 break;
2201 }
2202
2203 if (found && (match_start != NULL))
2204 *match_start = m_start;
2205
2206 if (found && (match_end != NULL))
2207 *match_end = m_end;
2208
2209 return found;
2210 }
2211
2212 /* FIXME this is an issue for introspection regardning @find */
2213 gint
pluma_document_replace_all(PlumaDocument * doc,const gchar * find,const gchar * replace,guint flags)2214 pluma_document_replace_all (PlumaDocument *doc,
2215 const gchar *find,
2216 const gchar *replace,
2217 guint flags)
2218 {
2219 GtkTextIter iter;
2220 GtkTextIter m_start;
2221 GtkTextIter m_end;
2222 GtkTextSearchFlags search_flags = 0;
2223 gboolean found = TRUE;
2224 gint cont = 0;
2225 gchar *search_text;
2226 gchar *replace_text = NULL;
2227 gint replace_text_len = 0;
2228 GtkTextBuffer *buffer;
2229 gboolean brackets_highlighting;
2230 gboolean search_highliting;
2231
2232 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), 0);
2233 g_return_val_if_fail (replace != NULL, 0);
2234 g_return_val_if_fail ((find != NULL) || (doc->priv->search_text != NULL), 0);
2235
2236 buffer = GTK_TEXT_BUFFER (doc);
2237
2238 if (find == NULL)
2239 search_text = g_strdup (doc->priv->search_text);
2240 else
2241 search_text = pluma_utils_unescape_search_text (find);
2242
2243 if(!PLUMA_SEARCH_IS_MATCH_REGEX(flags))
2244 {
2245 replace_text = pluma_utils_unescape_search_text (replace);
2246 replace_text_len = strlen (replace_text);
2247 }
2248
2249 gtk_text_buffer_get_start_iter (buffer, &iter);
2250
2251 search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
2252
2253 if (!PLUMA_SEARCH_IS_CASE_SENSITIVE (flags))
2254 {
2255 search_flags = search_flags | GTK_TEXT_SEARCH_CASE_INSENSITIVE;
2256 }
2257
2258
2259 /* disable cursor_moved emission until the end of the
2260 * replace_all so that we don't spend all the time
2261 * updating the position in the statusbar
2262 */
2263 doc->priv->stop_cursor_moved_emission = TRUE;
2264
2265 /* also avoid spending time matching brackets */
2266 brackets_highlighting = gtk_source_buffer_get_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer));
2267 gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer), FALSE);
2268
2269 /* and do search highliting later */
2270 search_highliting = pluma_document_get_enable_search_highlighting (doc);
2271 pluma_document_set_enable_search_highlighting (doc, FALSE);
2272
2273 gtk_text_buffer_begin_user_action (buffer);
2274
2275 do
2276 {
2277 if(!PLUMA_SEARCH_IS_MATCH_REGEX(flags))
2278 {
2279 found = gtk_text_iter_forward_search (&iter,
2280 search_text,
2281 search_flags,
2282 &m_start,
2283 &m_end,
2284 NULL);
2285 } else {
2286 if(replace_text != NULL)
2287 g_free (replace_text);
2288 replace_text = g_strdup (replace);
2289 found = pluma_gtk_text_iter_regex_search (&iter,
2290 search_text,
2291 search_flags,
2292 &m_start,
2293 &m_end,
2294 NULL,
2295 TRUE,
2296 &replace_text);
2297 replace_text_len = strlen (replace_text);
2298 }
2299
2300 if (found && PLUMA_SEARCH_IS_ENTIRE_WORD (flags))
2301 {
2302 gboolean word;
2303
2304 word = gtk_text_iter_starts_word (&m_start) &&
2305 gtk_text_iter_ends_word (&m_end);
2306
2307 if (!word)
2308 {
2309 iter = m_end;
2310 continue;
2311 }
2312 }
2313
2314 if (found)
2315 {
2316 ++cont;
2317
2318 gtk_text_buffer_delete (buffer,
2319 &m_start,
2320 &m_end);
2321 gtk_text_buffer_insert (buffer,
2322 &m_start,
2323 replace_text,
2324 replace_text_len);
2325
2326 iter = m_start;
2327 }
2328
2329 } while (found);
2330
2331 gtk_text_buffer_end_user_action (buffer);
2332
2333 /* re-enable cursor_moved emission and notify
2334 * the current position
2335 */
2336 doc->priv->stop_cursor_moved_emission = FALSE;
2337 emit_cursor_moved (doc);
2338
2339 gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (buffer),
2340 brackets_highlighting);
2341 pluma_document_set_enable_search_highlighting (doc, search_highliting);
2342
2343 g_free (search_text);
2344 if(replace_text != NULL)
2345 g_free (replace_text);
2346
2347 return cont;
2348 }
2349
2350 /**
2351 * pluma_document_set_language:
2352 * @doc:
2353 * @lang: (allow-none):
2354 **/
2355 void
pluma_document_set_language(PlumaDocument * doc,GtkSourceLanguage * lang)2356 pluma_document_set_language (PlumaDocument *doc,
2357 GtkSourceLanguage *lang)
2358 {
2359 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
2360
2361 set_language (doc, lang, TRUE);
2362 }
2363
2364 /**
2365 * pluma_document_get_language:
2366 * @doc:
2367 *
2368 * Return value: (transfer none):
2369 */
2370 GtkSourceLanguage *
pluma_document_get_language(PlumaDocument * doc)2371 pluma_document_get_language (PlumaDocument *doc)
2372 {
2373 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
2374
2375 return gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (doc));
2376 }
2377
2378 const PlumaEncoding *
pluma_document_get_encoding(PlumaDocument * doc)2379 pluma_document_get_encoding (PlumaDocument *doc)
2380 {
2381 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
2382
2383 return doc->priv->encoding;
2384 }
2385
2386 glong
_pluma_document_get_seconds_since_last_save_or_load(PlumaDocument * doc)2387 _pluma_document_get_seconds_since_last_save_or_load (PlumaDocument *doc)
2388 {
2389 pluma_debug (DEBUG_DOCUMENT);
2390
2391 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), -1);
2392
2393 return ((g_get_real_time () - doc->priv->time_of_last_save_or_load) / G_USEC_PER_SEC);
2394 }
2395
2396 static void
get_search_match_colors(PlumaDocument * doc,gboolean * foreground_set,GdkRGBA * foreground,gboolean * background_set,GdkRGBA * background)2397 get_search_match_colors (PlumaDocument *doc,
2398 gboolean *foreground_set,
2399 GdkRGBA *foreground,
2400 gboolean *background_set,
2401 GdkRGBA *background)
2402 {
2403 GtkSourceStyleScheme *style_scheme;
2404 GtkSourceStyle *style;
2405 gchar *bg;
2406 gchar *fg;
2407
2408 style_scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (doc));
2409 if (style_scheme == NULL)
2410 goto fallback;
2411
2412 style = gtk_source_style_scheme_get_style (style_scheme,
2413 "search-match");
2414 if (style == NULL)
2415 goto fallback;
2416
2417 g_object_get (style,
2418 "foreground-set", foreground_set,
2419 "foreground", &fg,
2420 "background-set", background_set,
2421 "background", &bg,
2422 NULL);
2423
2424 if (*foreground_set)
2425 {
2426 if (fg == NULL ||
2427 !gdk_rgba_parse (foreground, fg))
2428 {
2429 *foreground_set = FALSE;
2430 }
2431 }
2432
2433 if (*background_set)
2434 {
2435 if (bg == NULL ||
2436 !gdk_rgba_parse (background, bg))
2437 {
2438 *background_set = FALSE;
2439 }
2440 }
2441
2442 g_free (fg);
2443 g_free (bg);
2444
2445 return;
2446
2447 fallback:
2448 pluma_debug_message (DEBUG_DOCUMENT,
2449 "Falling back to hard-coded colors "
2450 "for the \"found\" text tag.");
2451
2452 gdk_rgba_parse (background, "#FFFF78");
2453 *background_set = TRUE;
2454 *foreground_set = FALSE;
2455
2456 return;
2457 }
2458
2459 static void
sync_found_tag(PlumaDocument * doc,GParamSpec * pspec,gpointer data)2460 sync_found_tag (PlumaDocument *doc,
2461 GParamSpec *pspec,
2462 gpointer data)
2463 {
2464 GdkRGBA fg;
2465 GdkRGBA bg;
2466 gboolean fg_set;
2467 gboolean bg_set;
2468
2469 pluma_debug (DEBUG_DOCUMENT);
2470
2471 g_return_if_fail (GTK_TEXT_TAG (doc->priv->found_tag));
2472
2473 get_search_match_colors (doc,
2474 &fg_set, &fg,
2475 &bg_set, &bg);
2476
2477 g_object_set (doc->priv->found_tag,
2478 "foreground-rgba", fg_set ? &fg : NULL,
2479 NULL);
2480 g_object_set (doc->priv->found_tag,
2481 "background-rgba", bg_set ? &bg : NULL,
2482 NULL);
2483 }
2484
2485 static void
text_tag_set_highest_priority(GtkTextTag * tag,GtkTextBuffer * buffer)2486 text_tag_set_highest_priority (GtkTextTag *tag,
2487 GtkTextBuffer *buffer)
2488 {
2489 GtkTextTagTable *table;
2490 gint n;
2491
2492 table = gtk_text_buffer_get_tag_table (buffer);
2493 n = gtk_text_tag_table_get_size (table);
2494 gtk_text_tag_set_priority (tag, n - 1);
2495 }
2496
2497 static void
search_region(PlumaDocument * doc,GtkTextIter * start,GtkTextIter * end)2498 search_region (PlumaDocument *doc,
2499 GtkTextIter *start,
2500 GtkTextIter *end)
2501 {
2502 GtkTextIter iter;
2503 GtkTextIter m_start;
2504 GtkTextIter m_end;
2505 GtkTextSearchFlags search_flags = 0;
2506 gboolean found = TRUE;
2507
2508 GtkTextBuffer *buffer;
2509
2510 pluma_debug (DEBUG_DOCUMENT);
2511
2512 buffer = GTK_TEXT_BUFFER (doc);
2513
2514 if (doc->priv->found_tag == NULL)
2515 {
2516 doc->priv->found_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (doc),
2517 "found",
2518 NULL);
2519
2520 sync_found_tag (doc, NULL, NULL);
2521
2522 g_signal_connect (doc,
2523 "notify::style-scheme",
2524 G_CALLBACK (sync_found_tag),
2525 NULL);
2526 }
2527
2528 /* make sure the 'found' tag has the priority over
2529 * syntax highlighting tags */
2530 text_tag_set_highest_priority (doc->priv->found_tag,
2531 GTK_TEXT_BUFFER (doc));
2532
2533
2534 if (doc->priv->search_text == NULL)
2535 return;
2536
2537 g_return_if_fail (doc->priv->num_of_lines_search_text > 0);
2538
2539 gtk_text_iter_backward_lines (start, doc->priv->num_of_lines_search_text);
2540 gtk_text_iter_forward_lines (end, doc->priv->num_of_lines_search_text);
2541
2542 if (gtk_text_iter_has_tag (start, doc->priv->found_tag) &&
2543 !gtk_text_iter_starts_tag (start, doc->priv->found_tag))
2544 gtk_text_iter_backward_to_tag_toggle (start, doc->priv->found_tag);
2545
2546 if (gtk_text_iter_has_tag (end, doc->priv->found_tag) &&
2547 !gtk_text_iter_ends_tag (end, doc->priv->found_tag))
2548 gtk_text_iter_forward_to_tag_toggle (end, doc->priv->found_tag);
2549
2550 /*
2551 g_print ("[%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start),
2552 gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end));
2553 */
2554
2555 gtk_text_buffer_remove_tag (buffer,
2556 doc->priv->found_tag,
2557 start,
2558 end);
2559
2560 if (*doc->priv->search_text == '\0')
2561 return;
2562
2563 iter = *start;
2564
2565 search_flags = GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY;
2566
2567 if (!PLUMA_SEARCH_IS_CASE_SENSITIVE (doc->priv->search_flags))
2568 {
2569 search_flags = search_flags | GTK_TEXT_SEARCH_CASE_INSENSITIVE;
2570 }
2571
2572 do
2573 {
2574 if ((end != NULL) && gtk_text_iter_is_end (end))
2575 end = NULL;
2576
2577 found = gtk_text_iter_forward_search (&iter,
2578 doc->priv->search_text,
2579 search_flags,
2580 &m_start,
2581 &m_end,
2582 end);
2583
2584 iter = m_end;
2585
2586 if (found && PLUMA_SEARCH_IS_ENTIRE_WORD (doc->priv->search_flags))
2587 {
2588 gboolean word;
2589
2590 word = gtk_text_iter_starts_word (&m_start) &&
2591 gtk_text_iter_ends_word (&m_end);
2592
2593 if (!word)
2594 continue;
2595 }
2596
2597 if (found)
2598 {
2599 gtk_text_buffer_apply_tag (buffer,
2600 doc->priv->found_tag,
2601 &m_start,
2602 &m_end);
2603 }
2604
2605 } while (found);
2606 }
2607
2608 static void
to_search_region_range(PlumaDocument * doc,GtkTextIter * start,GtkTextIter * end)2609 to_search_region_range (PlumaDocument *doc,
2610 GtkTextIter *start,
2611 GtkTextIter *end)
2612 {
2613 pluma_debug (DEBUG_DOCUMENT);
2614
2615 if (doc->priv->to_search_region == NULL)
2616 return;
2617
2618 gtk_text_iter_set_line_offset (start, 0);
2619 gtk_text_iter_forward_to_line_end (end);
2620
2621 /*
2622 g_print ("+ [%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start),
2623 gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end));
2624 */
2625
2626 /* Add the region to the refresh region */
2627 pluma_text_region_add (doc->priv->to_search_region, start, end);
2628
2629 /* Notify views of the updated highlight region */
2630 gtk_text_iter_backward_lines (start, doc->priv->num_of_lines_search_text);
2631 gtk_text_iter_forward_lines (end, doc->priv->num_of_lines_search_text);
2632
2633 g_signal_emit (doc, document_signals [SEARCH_HIGHLIGHT_UPDATED], 0, start, end);
2634 }
2635
2636 void
_pluma_document_search_region(PlumaDocument * doc,const GtkTextIter * start,const GtkTextIter * end)2637 _pluma_document_search_region (PlumaDocument *doc,
2638 const GtkTextIter *start,
2639 const GtkTextIter *end)
2640 {
2641 PlumaTextRegion *region;
2642
2643 pluma_debug (DEBUG_DOCUMENT);
2644
2645 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
2646 g_return_if_fail (start != NULL);
2647 g_return_if_fail (end != NULL);
2648
2649 if (doc->priv->to_search_region == NULL)
2650 return;
2651
2652 /*
2653 g_print ("U [%u (%u), %u (%u)]\n", gtk_text_iter_get_line (start), gtk_text_iter_get_offset (start),
2654 gtk_text_iter_get_line (end), gtk_text_iter_get_offset (end));
2655 */
2656
2657 /* get the subregions not yet highlighted */
2658 region = pluma_text_region_intersect (doc->priv->to_search_region,
2659 start,
2660 end);
2661 if (region)
2662 {
2663 gint i;
2664 GtkTextIter start_search;
2665 GtkTextIter end_search;
2666
2667 i = pluma_text_region_subregions (region);
2668 pluma_text_region_nth_subregion (region,
2669 0,
2670 &start_search,
2671 NULL);
2672
2673 pluma_text_region_nth_subregion (region,
2674 i - 1,
2675 NULL,
2676 &end_search);
2677
2678 pluma_text_region_destroy (region, TRUE);
2679
2680 gtk_text_iter_order (&start_search, &end_search);
2681
2682 search_region (doc, &start_search, &end_search);
2683
2684 /* remove the just highlighted region */
2685 pluma_text_region_subtract (doc->priv->to_search_region,
2686 start,
2687 end);
2688 }
2689 }
2690
2691 static void
insert_text_cb(PlumaDocument * doc,GtkTextIter * pos,const gchar * text,gint length)2692 insert_text_cb (PlumaDocument *doc,
2693 GtkTextIter *pos,
2694 const gchar *text,
2695 gint length)
2696 {
2697 GtkTextIter start;
2698 GtkTextIter end;
2699
2700 pluma_debug (DEBUG_DOCUMENT);
2701
2702 start = end = *pos;
2703
2704 /*
2705 * pos is invalidated when
2706 * insertion occurs (because the buffer contents change), but the
2707 * default signal handler revalidates it to point to the end of the
2708 * inserted text
2709 */
2710 gtk_text_iter_backward_chars (&start,
2711 g_utf8_strlen (text, length));
2712
2713 to_search_region_range (doc, &start, &end);
2714 }
2715
2716 static void
delete_range_cb(PlumaDocument * doc,GtkTextIter * start,GtkTextIter * end)2717 delete_range_cb (PlumaDocument *doc,
2718 GtkTextIter *start,
2719 GtkTextIter *end)
2720 {
2721 GtkTextIter d_start;
2722 GtkTextIter d_end;
2723
2724 pluma_debug (DEBUG_DOCUMENT);
2725
2726 d_start = *start;
2727 d_end = *end;
2728
2729 to_search_region_range (doc, &d_start, &d_end);
2730 }
2731
2732 void
pluma_document_set_enable_search_highlighting(PlumaDocument * doc,gboolean enable)2733 pluma_document_set_enable_search_highlighting (PlumaDocument *doc,
2734 gboolean enable)
2735 {
2736 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
2737
2738 enable = enable != FALSE;
2739
2740 if ((doc->priv->to_search_region != NULL) == enable)
2741 return;
2742
2743 if (doc->priv->to_search_region != NULL)
2744 {
2745 /* Disable search highlighting */
2746 if (doc->priv->found_tag != NULL)
2747 {
2748 /* If needed remove the found_tag */
2749 GtkTextIter begin;
2750 GtkTextIter end;
2751
2752 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc),
2753 &begin,
2754 &end);
2755
2756 gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (doc),
2757 doc->priv->found_tag,
2758 &begin,
2759 &end);
2760 }
2761
2762 pluma_text_region_destroy (doc->priv->to_search_region,
2763 TRUE);
2764 doc->priv->to_search_region = NULL;
2765 }
2766 else
2767 {
2768 doc->priv->to_search_region = pluma_text_region_new (GTK_TEXT_BUFFER (doc));
2769 if (pluma_document_get_can_search_again (doc))
2770 {
2771 /* If search_text is not empty, highligth all its occurrences */
2772 GtkTextIter begin;
2773 GtkTextIter end;
2774
2775 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc),
2776 &begin,
2777 &end);
2778
2779 to_search_region_range (doc,
2780 &begin,
2781 &end);
2782 }
2783 }
2784 }
2785
2786 gboolean
pluma_document_get_enable_search_highlighting(PlumaDocument * doc)2787 pluma_document_get_enable_search_highlighting (PlumaDocument *doc)
2788 {
2789 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), FALSE);
2790
2791 return (doc->priv->to_search_region != NULL);
2792 }
2793
2794 void
pluma_document_set_newline_type(PlumaDocument * doc,PlumaDocumentNewlineType newline_type)2795 pluma_document_set_newline_type (PlumaDocument *doc,
2796 PlumaDocumentNewlineType newline_type)
2797 {
2798 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
2799
2800 if (doc->priv->newline_type != newline_type)
2801 {
2802 doc->priv->newline_type = newline_type;
2803
2804 g_object_notify (G_OBJECT (doc), "newline-type");
2805 }
2806 }
2807
2808 PlumaDocumentNewlineType
pluma_document_get_newline_type(PlumaDocument * doc)2809 pluma_document_get_newline_type (PlumaDocument *doc)
2810 {
2811 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), 0);
2812
2813 return doc->priv->newline_type;
2814 }
2815
2816 void
_pluma_document_set_mount_operation_factory(PlumaDocument * doc,PlumaMountOperationFactory callback,gpointer userdata)2817 _pluma_document_set_mount_operation_factory (PlumaDocument *doc,
2818 PlumaMountOperationFactory callback,
2819 gpointer userdata)
2820 {
2821 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
2822
2823 doc->priv->mount_operation_factory = callback;
2824 doc->priv->mount_operation_userdata = userdata;
2825 }
2826
2827 GMountOperation *
_pluma_document_create_mount_operation(PlumaDocument * doc)2828 _pluma_document_create_mount_operation (PlumaDocument *doc)
2829 {
2830 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
2831
2832 if (doc->priv->mount_operation_factory == NULL)
2833 return g_mount_operation_new ();
2834 else
2835 return doc->priv->mount_operation_factory (doc,
2836 doc->priv->mount_operation_userdata);
2837 }
2838
2839 #ifndef ENABLE_GVFS_METADATA
2840 gchar *
pluma_document_get_metadata(PlumaDocument * doc,const gchar * key)2841 pluma_document_get_metadata (PlumaDocument *doc,
2842 const gchar *key)
2843 {
2844 gchar *value = NULL;
2845
2846 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
2847 g_return_val_if_fail (key != NULL, NULL);
2848
2849 if (!pluma_document_is_untitled (doc))
2850 {
2851 value = pluma_metadata_manager_get (doc->priv->uri, key);
2852 }
2853
2854 return value;
2855 }
2856
2857 void
pluma_document_set_metadata(PlumaDocument * doc,const gchar * first_key,...)2858 pluma_document_set_metadata (PlumaDocument *doc,
2859 const gchar *first_key,
2860 ...)
2861 {
2862 const gchar *key;
2863 const gchar *value;
2864 va_list var_args;
2865
2866 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
2867 g_return_if_fail (first_key != NULL);
2868
2869 if (pluma_document_is_untitled (doc))
2870 {
2871 /* Can't set metadata for untitled documents */
2872 return;
2873 }
2874
2875 va_start (var_args, first_key);
2876
2877 for (key = first_key; key; key = va_arg (var_args, const gchar *))
2878 {
2879 value = va_arg (var_args, const gchar *);
2880
2881 pluma_metadata_manager_set (doc->priv->uri,
2882 key,
2883 value);
2884 }
2885
2886 va_end (var_args);
2887 }
2888
2889 #else
2890
2891 /**
2892 * pluma_document_get_metadata:
2893 * @doc: a #PlumaDocument
2894 * @key: name of the key
2895 *
2896 * Gets the metadata assigned to @key.
2897 *
2898 * Returns: the value assigned to @key.
2899 */
2900 gchar *
pluma_document_get_metadata(PlumaDocument * doc,const gchar * key)2901 pluma_document_get_metadata (PlumaDocument *doc,
2902 const gchar *key)
2903 {
2904 gchar *value = NULL;
2905
2906 g_return_val_if_fail (PLUMA_IS_DOCUMENT (doc), NULL);
2907 g_return_val_if_fail (key != NULL, NULL);
2908
2909 if (doc->priv->metadata_info && g_file_info_has_attribute (doc->priv->metadata_info,
2910 key))
2911 {
2912 value = g_strdup (g_file_info_get_attribute_string (doc->priv->metadata_info,
2913 key));
2914 }
2915
2916 return value;
2917 }
2918
2919 static void
set_attributes_cb(GObject * source,GAsyncResult * res,gpointer useless)2920 set_attributes_cb (GObject *source,
2921 GAsyncResult *res,
2922 gpointer useless)
2923 {
2924 g_file_set_attributes_finish (G_FILE (source),
2925 res,
2926 NULL,
2927 NULL);
2928 }
2929
2930 /**
2931 * pluma_document_set_metadata:
2932 * @doc: a #PlumaDocument
2933 * @first_key: name of the first key to set
2934 * @...: value for the first key, followed optionally by more key/value pairs,
2935 * followed by %NULL.
2936 *
2937 * Sets metadata on a document.
2938 */
2939 void
pluma_document_set_metadata(PlumaDocument * doc,const gchar * first_key,...)2940 pluma_document_set_metadata (PlumaDocument *doc,
2941 const gchar *first_key,
2942 ...)
2943 {
2944 const gchar *key;
2945 const gchar *value;
2946 va_list var_args;
2947 GFileInfo *info;
2948 GFile *location;
2949
2950 g_return_if_fail (PLUMA_IS_DOCUMENT (doc));
2951 g_return_if_fail (first_key != NULL);
2952
2953 info = g_file_info_new ();
2954
2955 va_start (var_args, first_key);
2956
2957 for (key = first_key; key; key = va_arg (var_args, const gchar *))
2958 {
2959 value = va_arg (var_args, const gchar *);
2960
2961 if (value != NULL)
2962 {
2963 g_file_info_set_attribute_string (info,
2964 key, value);
2965 }
2966 else
2967 {
2968 /* Unset the key */
2969 g_file_info_set_attribute (info, key,
2970 G_FILE_ATTRIBUTE_TYPE_INVALID,
2971 NULL);
2972 }
2973 }
2974
2975 va_end (var_args);
2976
2977 if (doc->priv->metadata_info != NULL)
2978 g_file_info_copy_into (info, doc->priv->metadata_info);
2979
2980 location = pluma_document_get_location (doc);
2981
2982 if (location != NULL)
2983 {
2984 g_file_set_attributes_async (location,
2985 info,
2986 G_FILE_QUERY_INFO_NONE,
2987 G_PRIORITY_DEFAULT,
2988 NULL,
2989 set_attributes_cb,
2990 NULL);
2991
2992 g_object_unref (location);
2993 }
2994
2995 g_object_unref (info);
2996 }
2997 #endif
2998