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