1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-trunk
4 * Copyright (C) Johannes Schmid 2008 <jhs@gnome.org>
5 *
6 * anjuta-trunk is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * anjuta-trunk is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "sourceview-io.h"
21 #include "sourceview-private.h"
22 #include <libanjuta/interfaces/ianjuta-editor.h>
23 #include <libanjuta/anjuta-convert.h>
24 #include <libanjuta/anjuta-encodings.h>
25
26 #define READ_SIZE 4096
27 #define RATE_LIMIT 5000 /* Use a big rate limit to avoid duplicates */
28
29 enum
30 {
31 SAVE_STATUS,
32 SAVE_FINISHED,
33 OPEN_STATUS,
34 OPEN_FINISHED,
35 OPEN_FAILED,
36 SAVE_FAILED,
37 FILE_DELETED,
38
39 LAST_SIGNAL
40 };
41 static guint io_signals[LAST_SIGNAL] = { 0 };
42
43 #define IO_ERROR_QUARK g_quark_from_string ("SourceviewIO-Error")
44
45 #define IO_PRIORITY G_PRIORITY_DEFAULT
46
47 G_DEFINE_TYPE (SourceviewIO, sourceview_io, G_TYPE_OBJECT);
48
49 static void
on_sourceview_finalized(gpointer data,GObject * where_the_object_was)50 on_sourceview_finalized (gpointer data, GObject* where_the_object_was)
51 {
52 SourceviewIO* sio = SOURCEVIEW_IO (data);
53
54 sio->sv = NULL;
55 /* Cancel all open operations */
56 g_cancellable_cancel (sio->open_cancel);
57 }
58
59 static void
sourceview_io_init(SourceviewIO * object)60 sourceview_io_init (SourceviewIO *object)
61 {
62 object->file = NULL;
63 object->filename = NULL;
64 object->read_buffer = NULL;
65 object->write_buffer = NULL;
66 object->open_cancel = g_cancellable_new();
67 object->monitor = NULL;
68 object->last_encoding = NULL;
69 object->bytes_read = 0;
70 }
71
72 static void
sourceview_io_finalize(GObject * object)73 sourceview_io_finalize (GObject *object)
74 {
75 SourceviewIO* sio = SOURCEVIEW_IO(object);
76
77 if (sio->sv)
78 g_object_weak_unref (G_OBJECT (sio->sv), on_sourceview_finalized, sio);
79
80 if (sio->file)
81 g_object_unref (sio->file);
82 g_free (sio->etag);
83 g_free(sio->filename);
84 g_free(sio->read_buffer);
85 g_free(sio->write_buffer);
86 g_object_unref (sio->open_cancel);
87 if (sio->monitor)
88 g_object_unref (sio->monitor);
89
90 G_OBJECT_CLASS (sourceview_io_parent_class)->finalize (object);
91 }
92
93 static void
sourceview_io_class_init(SourceviewIOClass * klass)94 sourceview_io_class_init (SourceviewIOClass *klass)
95 {
96 GObjectClass* object_class = G_OBJECT_CLASS (klass);
97
98 object_class->finalize = sourceview_io_finalize;
99
100 klass->changed = NULL;
101 klass->deleted = NULL;
102 klass->save_finished = NULL;
103 klass->open_finished = NULL;
104 klass->open_failed = NULL;
105 klass->save_failed = NULL;
106
107 io_signals[SAVE_STATUS] =
108 g_signal_new ("changed",
109 G_OBJECT_CLASS_TYPE (klass),
110 0,
111 G_STRUCT_OFFSET (SourceviewIOClass, changed),
112 NULL, NULL,
113 g_cclosure_marshal_VOID__VOID,
114 G_TYPE_NONE, 0,
115 NULL);
116
117 io_signals[SAVE_FINISHED] =
118 g_signal_new ("save-finished",
119 G_OBJECT_CLASS_TYPE (klass),
120 0,
121 G_STRUCT_OFFSET (SourceviewIOClass, save_finished),
122 NULL, NULL,
123 g_cclosure_marshal_VOID__VOID,
124 G_TYPE_NONE, 0,
125 NULL);
126
127 io_signals[OPEN_FINISHED] =
128 g_signal_new ("open-finished",
129 G_OBJECT_CLASS_TYPE (klass),
130 0,
131 G_STRUCT_OFFSET (SourceviewIOClass, open_finished),
132 NULL, NULL,
133 g_cclosure_marshal_VOID__VOID,
134 G_TYPE_NONE, 0,
135 NULL);
136
137 io_signals[OPEN_FAILED] =
138 g_signal_new ("open-failed",
139 G_OBJECT_CLASS_TYPE (klass),
140 0,
141 G_STRUCT_OFFSET (SourceviewIOClass, open_failed),
142 NULL, NULL,
143 g_cclosure_marshal_VOID__POINTER,
144 G_TYPE_NONE, 1,
145 G_TYPE_POINTER);
146
147 io_signals[SAVE_FAILED] =
148 g_signal_new ("save-failed",
149 G_OBJECT_CLASS_TYPE (klass),
150 0,
151 G_STRUCT_OFFSET (SourceviewIOClass, save_failed),
152 NULL, NULL,
153 g_cclosure_marshal_VOID__POINTER,
154 G_TYPE_NONE, 1,
155 G_TYPE_POINTER);
156
157 io_signals[FILE_DELETED] =
158 g_signal_new ("deleted",
159 G_OBJECT_CLASS_TYPE (klass),
160 0,
161 G_STRUCT_OFFSET (SourceviewIOClass, deleted),
162 NULL, NULL,
163 g_cclosure_marshal_VOID__VOID,
164 G_TYPE_NONE, 0,
165 NULL);
166 }
167
on_file_changed(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,gpointer data)168 static void on_file_changed (GFileMonitor* monitor,
169 GFile* file,
170 GFile* other_file,
171 GFileMonitorEvent event_type,
172 gpointer data)
173 {
174 SourceviewIO* sio = SOURCEVIEW_IO(data);
175
176 switch (event_type)
177 {
178 case G_FILE_MONITOR_EVENT_CREATED:
179 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
180 {
181 GFileInfo* info;
182 const gchar* etag;
183
184 /* Ignore changes that happen during a save. */
185 if (sio->write_buffer)
186 break;
187
188 info = g_file_query_info (file, G_FILE_ATTRIBUTE_ETAG_VALUE,
189 G_FILE_QUERY_INFO_NONE, NULL, NULL);
190 if (!info)
191 break;
192
193 etag = g_file_info_get_etag (info);
194
195 /* Only emit "changed" if the etag has changed. */
196 if (g_strcmp0 (sio->etag, etag))
197 g_signal_emit_by_name (sio, "changed");
198
199 g_object_unref (info);
200 break;
201 }
202 case G_FILE_MONITOR_EVENT_DELETED:
203 {
204 gchar* filename = NULL;
205
206 if (G_IS_FILE (file))
207 filename = g_file_get_basename (file);
208 /* Only emit "deleted" if monitored file isn't temporary */
209 if (filename != NULL && !g_str_has_prefix (filename, ".goutputstream-"))
210 g_signal_emit_by_name (sio, "deleted");
211
212 g_free (filename);
213 break;
214 }
215 default:
216 break;
217 }
218 }
219
220 static void
setup_monitor(SourceviewIO * sio)221 setup_monitor(SourceviewIO* sio)
222 {
223 if (sio->monitor != NULL)
224 g_object_unref (sio->monitor);
225 sio->monitor = g_file_monitor_file (sio->file,
226 G_FILE_MONITOR_NONE,
227 NULL,
228 NULL);
229 if (sio->monitor)
230 {
231 g_signal_connect (sio->monitor, "changed",
232 G_CALLBACK(on_file_changed), sio);
233 g_file_monitor_set_rate_limit (sio->monitor, RATE_LIMIT);
234 }
235 }
236
237 static void
sourceview_io_unset_current_file(SourceviewIO * sio)238 sourceview_io_unset_current_file (SourceviewIO* sio)
239 {
240 g_clear_object (&sio->file);
241 g_clear_object (&sio->monitor);
242
243 g_free (sio->etag);
244 sio->etag = NULL;
245
246 g_free (sio->filename);
247 sio->filename = NULL;
248 }
249
250 static void
set_display_name(SourceviewIO * sio)251 set_display_name (SourceviewIO* sio)
252 {
253 GFileInfo* file_info = g_file_query_info (sio->file,
254 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
255 G_FILE_QUERY_INFO_NONE,
256 NULL,
257 NULL);
258 if (file_info)
259 {
260 g_free (sio->filename);
261 sio->filename = g_strdup(g_file_info_get_display_name (file_info));
262 }
263 else
264 {
265 g_free (sio->filename);
266 sio->filename = NULL;
267 }
268 g_object_unref (file_info);
269 }
270
271 /*
272 * This function may be called after the corresponding Sourceview (sio->sv)
273 * has been destroyed.
274 */
275 static void
on_save_finished(GObject * file,GAsyncResult * result,gpointer data)276 on_save_finished (GObject* file, GAsyncResult* result, gpointer data)
277 {
278 SourceviewIO* sio = SOURCEVIEW_IO(data);
279
280 GError* err = NULL;
281 gchar* etag;
282
283 g_file_replace_contents_finish (G_FILE (file),
284 result,
285 &etag,
286 &err);
287 g_free (sio->write_buffer);
288 sio->write_buffer = NULL;
289 if (err)
290 {
291 g_signal_emit_by_name (sio, "save-failed", err);
292 g_error_free (err);
293 }
294 else
295 {
296 set_display_name (sio);
297 if (!sio->monitor)
298 setup_monitor (sio);
299
300 g_free (sio->etag);
301 sio->etag = etag;
302
303 g_signal_emit_by_name (sio, "save-finished");
304 }
305
306 if (sio->shell)
307 anjuta_shell_saving_pop (sio->shell);
308
309 g_object_unref (sio);
310 }
311
312 void
sourceview_io_save(SourceviewIO * sio)313 sourceview_io_save (SourceviewIO* sio)
314 {
315 g_return_if_fail (SOURCEVIEW_IS_IO (sio));
316 g_return_if_fail (sio->sv != NULL);
317
318 if (!sio->file)
319 {
320 GError* error = NULL;
321 g_set_error (&error, IO_ERROR_QUARK, 0,
322 _("Could not save file because filename not yet specified"));
323 g_signal_emit_by_name (sio, "save-failed", error);
324 g_error_free(error);
325 }
326 else
327 sourceview_io_save_as (sio, sio->file);
328 }
329
330 void
sourceview_io_save_as(SourceviewIO * sio,GFile * file)331 sourceview_io_save_as (SourceviewIO* sio, GFile* file)
332 {
333 gboolean backup = TRUE;
334 gsize len;
335
336 g_return_if_fail (SOURCEVIEW_IS_IO (sio));
337 g_return_if_fail (sio->sv != NULL);
338 g_return_if_fail (G_IS_FILE (file));
339
340 if (sio->file != file)
341 {
342 sourceview_io_unset_current_file (sio);
343
344 sio->file = g_object_ref (file);
345 }
346
347 backup = g_settings_get_boolean (sio->sv->priv->settings,
348 "backup");
349
350 if (sio->last_encoding == NULL)
351 {
352 sio->write_buffer = ianjuta_editor_get_text_all (IANJUTA_EDITOR(sio->sv),
353 NULL);
354 len = strlen (sio->write_buffer);
355 }
356 else
357 {
358 GError* err = NULL;
359 gchar* buffer_text = ianjuta_editor_get_text_all (IANJUTA_EDITOR(sio->sv),
360 NULL);
361 sio->write_buffer = anjuta_convert_from_utf8 (buffer_text,
362 -1,
363 sio->last_encoding,
364 &len,
365 &err);
366 g_free (buffer_text);
367 if (err != NULL)
368 {
369 g_signal_emit_by_name (sio, "save-failed", err);
370 g_error_free(err);
371 return;
372 }
373 }
374 g_file_replace_contents_async (file,
375 sio->write_buffer,
376 len,
377 NULL,
378 backup,
379 G_FILE_CREATE_NONE,
380 NULL,
381 on_save_finished,
382 sio);
383 anjuta_shell_saving_push (sio->shell);
384
385 g_object_ref (sio);
386 }
387
insert_text_in_document(SourceviewIO * sio,const gchar * text,gsize len)388 static void insert_text_in_document(SourceviewIO* sio, const gchar* text, gsize len)
389 {
390 GtkSourceBuffer* document = GTK_SOURCE_BUFFER (sio->sv->priv->document);
391 gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (sio->sv->priv->document));
392
393 /* Insert text in the buffer */
394 gtk_text_buffer_set_text (GTK_TEXT_BUFFER (document),
395 text,
396 len);
397
398 gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (document),
399 FALSE);
400
401 gtk_source_buffer_end_not_undoable_action (document);
402 }
403
404 static gboolean
append_buffer(SourceviewIO * sio,gsize size)405 append_buffer (SourceviewIO* sio, gsize size)
406 {
407 /* Text is utf-8 - good */
408 if (g_utf8_validate (sio->read_buffer, size, NULL))
409 {
410 insert_text_in_document (sio, sio->read_buffer, size);
411 }
412 else
413 {
414 /* Text is not utf-8 */
415 GError *conv_error = NULL;
416 gchar *converted_text = NULL;
417 gsize new_len = size;
418 const AnjutaEncoding* enc = NULL;
419
420 converted_text = anjuta_convert_to_utf8 (sio->read_buffer,
421 size,
422 &enc,
423 &new_len,
424 &conv_error);
425 if (converted_text == NULL)
426 {
427 /* Last chance, let's try 8859-15 */
428 enc = anjuta_encoding_get_from_charset( "ISO-8859-15");
429 conv_error = NULL;
430 converted_text = anjuta_convert_to_utf8 (sio->read_buffer,
431 size,
432 &enc,
433 &new_len,
434 &conv_error);
435 }
436 if (converted_text == NULL)
437 {
438 g_return_val_if_fail (conv_error != NULL, FALSE);
439
440 g_signal_emit_by_name (sio, "open-failed", conv_error);
441 g_error_free (conv_error);
442 return FALSE;
443 }
444 sio->last_encoding = enc;
445 insert_text_in_document (sio, converted_text, new_len);
446 g_free (converted_text);
447 }
448 return TRUE;
449 }
450
451 static void
on_read_finished(GObject * input,GAsyncResult * result,gpointer data)452 on_read_finished (GObject* input, GAsyncResult* result, gpointer data)
453 {
454 SourceviewIO* sio = SOURCEVIEW_IO(data);
455 GInputStream* input_stream = G_INPUT_STREAM(input);
456 gsize current_bytes = 0;
457 GError* err = NULL;
458
459 if (!g_cancellable_set_error_if_cancelled (sio->open_cancel, &err))
460 current_bytes = g_input_stream_read_finish (input_stream, result, &err);
461 if (err)
462 {
463 g_signal_emit_by_name (sio, "open-failed", err);
464 g_error_free (err);
465 }
466 else
467 {
468 /* Check that the parent Sourceview is still alive. */
469 if (sio->sv == NULL)
470 {
471 g_warning ("Sourceview was destroyed without canceling SourceviewIO open operation");
472 goto out;
473 }
474
475 sio->bytes_read += current_bytes;
476 if (current_bytes != 0)
477 {
478 sio->read_buffer = g_realloc (sio->read_buffer, sio->bytes_read + READ_SIZE);
479 g_input_stream_read_async (G_INPUT_STREAM (input_stream),
480 sio->read_buffer + sio->bytes_read,
481 READ_SIZE,
482 IO_PRIORITY,
483 sio->open_cancel,
484 on_read_finished,
485 sio);
486 return;
487 }
488 else
489 {
490 GFileInfo* info;
491
492 info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (input_stream),
493 G_FILE_ATTRIBUTE_ETAG_VALUE,
494 NULL, &err);
495 if (!info)
496 {
497 g_signal_emit_by_name (sio, "open-failed", err);
498 g_error_free (err);
499 }
500 else
501 {
502 g_free (sio->etag);
503 sio->etag = g_strdup (g_file_info_get_etag (info));
504 g_object_unref (info);
505
506 if (append_buffer (sio, sio->bytes_read))
507 g_signal_emit_by_name (sio, "open-finished");
508 setup_monitor (sio);
509 }
510 }
511 }
512
513 out:
514 g_object_unref (input_stream);
515 g_free (sio->read_buffer);
516 sio->read_buffer = NULL;
517 sio->bytes_read = 0;
518 g_object_unref (sio);
519 }
520
521 void
sourceview_io_open(SourceviewIO * sio,GFile * file)522 sourceview_io_open (SourceviewIO* sio, GFile* file)
523 {
524 GFileInputStream* input_stream;
525 GError* err = NULL;
526
527 g_return_if_fail (SOURCEVIEW_IS_IO (sio));
528 g_return_if_fail (sio->sv != NULL);
529 g_return_if_fail (G_IS_FILE (file));
530
531 if (sio->file != file)
532 {
533 sourceview_io_unset_current_file (sio);
534
535 sio->file = g_object_ref (file);
536 set_display_name(sio);
537 }
538
539 input_stream = g_file_read (file, NULL, &err);
540 if (!input_stream)
541 {
542 g_signal_emit_by_name (sio, "open-failed", err);
543 g_error_free (err);
544 return;
545 }
546 sio->read_buffer = g_realloc (sio->read_buffer, READ_SIZE);
547 g_input_stream_read_async (G_INPUT_STREAM (input_stream),
548 sio->read_buffer,
549 READ_SIZE,
550 IO_PRIORITY,
551 sio->open_cancel,
552 on_read_finished,
553 g_object_ref (sio));
554 }
555
556 GFile*
sourceview_io_get_file(SourceviewIO * sio)557 sourceview_io_get_file (SourceviewIO* sio)
558 {
559 if (sio->file)
560 g_object_ref (sio->file);
561 return sio->file;
562 }
563
564 const gchar*
sourceview_io_get_filename(SourceviewIO * sio)565 sourceview_io_get_filename (SourceviewIO* sio)
566 {
567 static gint new_file_count = 1;
568 if (sio->filename == NULL) /* new file */
569 {
570 sio->filename = g_strdup_printf (_("New file %d"), new_file_count++);
571 }
572 return sio->filename;
573 }
574
575 void
sourceview_io_set_filename(SourceviewIO * sio,const gchar * filename)576 sourceview_io_set_filename (SourceviewIO* sio, const gchar* filename)
577 {
578 g_free (sio->filename);
579 sio->filename = g_strdup(filename);
580 }
581
582 gchar*
sourceview_io_get_mime_type(SourceviewIO * sio)583 sourceview_io_get_mime_type (SourceviewIO* sio)
584 {
585 GFileInfo* file_info;
586
587 if (!sio->file)
588 return NULL;
589
590 file_info = g_file_query_info (sio->file,
591 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
592 G_FILE_QUERY_INFO_NONE,
593 NULL,
594 NULL);
595 if (file_info)
596 {
597 gchar* mime_type = g_strdup (g_file_info_get_content_type (file_info));
598 g_object_unref (file_info);
599 return mime_type;
600 }
601 else
602 return NULL;
603
604 }
605
606 gboolean
sourceview_io_get_read_only(SourceviewIO * sio)607 sourceview_io_get_read_only (SourceviewIO* sio)
608 {
609 GFileInfo* file_info;
610 gboolean retval;
611
612 if (!sio->file)
613 return FALSE;
614
615 file_info = g_file_query_info (sio->file,
616 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
617 G_FILE_QUERY_INFO_NONE,
618 NULL,
619 NULL);
620 if (!file_info)
621 return FALSE;
622
623 retval = !g_file_info_get_attribute_boolean (file_info,
624 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
625 g_object_unref (file_info);
626 return retval;
627 }
628
629 SourceviewIO*
sourceview_io_new(Sourceview * sv)630 sourceview_io_new (Sourceview* sv)
631 {
632 SourceviewIO* sio;
633
634 g_return_val_if_fail (ANJUTA_IS_SOURCEVIEW(sv), NULL);
635
636 sio = SOURCEVIEW_IO(g_object_new (SOURCEVIEW_TYPE_IO, NULL));
637
638 sio->sv = sv;
639 g_object_weak_ref (G_OBJECT (sv), on_sourceview_finalized, sio);
640
641 /* Store a separate pointer to the shell since we want to have access
642 * to it even though the parent Sourceview has been destroyed .*/
643 sio->shell = ANJUTA_PLUGIN (sv->priv->plugin)->shell;
644 g_object_add_weak_pointer (G_OBJECT (sio->shell), (gpointer*)&sio->shell);
645
646 return sio;
647 }
648