1 /* GSequencer - Advanced GTK Sequencer
2 * Copyright (C) 2005-2020 Joël Krähemann
3 *
4 * This file is part of GSequencer.
5 *
6 * GSequencer is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GSequencer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GSequencer. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <ags/file/ags_file.h>
21
22 #include <ags/util/ags_id_generator.h>
23
24 #include <ags/object/ags_application_context.h>
25 #include <ags/object/ags_connectable.h>
26 #include <ags/object/ags_marshal.h>
27
28 #include <ags/file/ags_file_lookup.h>
29 #include <ags/file/ags_file_id_ref.h>
30 #include <ags/file/ags_file_launch.h>
31
32 #include <ags/thread/ags_thread_pool.h>
33 #include <ags/thread/ags_thread.h>
34
35 #include <libxml/parser.h>
36 #include <libxml/xlink.h>
37 #include <libxml/xpath.h>
38 #include <libxml/valid.h>
39 #include <libxml/xmlIO.h>
40 #include <libxml/xmlmemory.h>
41 #include <libxml/xmlsave.h>
42
43 #include <ags/i18n.h>
44
45 void ags_file_class_init(AgsFileClass *file);
46 void ags_file_init(AgsFile *file);
47 void ags_file_set_property(GObject *gobject,
48 guint prop_id,
49 const GValue *value,
50 GParamSpec *param_spec);
51 void ags_file_get_property(GObject *gobject,
52 guint prop_id,
53 GValue *value,
54 GParamSpec *param_spec);
55 void ags_file_finalize(GObject *gobject);
56
57 void ags_file_real_open(AgsFile *file,
58 GError **error);
59 void ags_file_real_open_from_data(AgsFile *file,
60 gchar *data, guint length,
61 GError **error);
62 void ags_file_real_rw_open(AgsFile *file,
63 gboolean create,
64 GError **error);
65
66 void ags_file_real_write(AgsFile *file);
67 void ags_file_real_write_concurrent(AgsFile *file);
68 void ags_file_real_write_resolve(AgsFile *file);
69
70 void ags_file_real_read(AgsFile *file);
71 void ags_file_real_read_resolve(AgsFile *file);
72 void ags_file_real_read_start(AgsFile *file);
73
74 /**
75 * SECTION:ags_file
76 * @short_description: read/write XML file
77 * @title: AgsFile
78 * @section_id:
79 * @include: ags/file/ags_file.h
80 *
81 * The #AgsFile is an object to read or write files using XML. It
82 * is the persisting layer of Advanced Gtk+ Sequencer.
83 */
84
85 enum{
86 PROP_0,
87 PROP_FILENAME,
88 PROP_ENCODING,
89 PROP_AUDIO_FORMAT,
90 PROP_AUDIO_ENCODING,
91 PROP_XML_DOC,
92 };
93
94 enum{
95 OPEN,
96 OPEN_FROM_DATA,
97 RW_OPEN,
98 WRITE,
99 WRITE_CONCURRENT,
100 WRITE_RESOLVE,
101 READ,
102 READ_RESOLVE,
103 READ_START,
104 LAST_SIGNAL,
105 };
106
107 static gpointer ags_file_parent_class = NULL;
108 static guint file_signals[LAST_SIGNAL] = { 0 };
109
110 GType
ags_file_get_type(void)111 ags_file_get_type (void)
112 {
113 static volatile gsize g_define_type_id__volatile = 0;
114
115 if(g_once_init_enter (&g_define_type_id__volatile)){
116 GType ags_type_file = 0;
117
118 static const GTypeInfo ags_file_info = {
119 sizeof (AgsFileClass),
120 NULL, /* base_init */
121 NULL, /* base_finalize */
122 (GClassInitFunc) ags_file_class_init,
123 NULL, /* class_finalize */
124 NULL, /* class_data */
125 sizeof (AgsFile),
126 0, /* n_preallocs */
127 (GInstanceInitFunc) ags_file_init,
128 };
129
130 ags_type_file = g_type_register_static(G_TYPE_OBJECT,
131 "AgsFile",
132 &ags_file_info,
133 0);
134
135 g_once_init_leave(&g_define_type_id__volatile, ags_type_file);
136 }
137
138 return g_define_type_id__volatile;
139 }
140
141 GType
ags_file_flags_get_type()142 ags_file_flags_get_type()
143 {
144 static volatile gsize g_flags_type_id__volatile;
145
146 if(g_once_init_enter (&g_flags_type_id__volatile)){
147 static const GFlagsValue values[] = {
148 { AGS_FILE_READ, "AGS_FILE_READ", "file-read" },
149 { AGS_FILE_READ_AUDIO_SIGNAL, "AGS_FILE_READ_AUDIO_SIGNAL", "file-read-audio-signal" },
150 { AGS_FILE_READ_EMBEDDED_AUDIO, "AGS_FILE_READ_EMBEDDED_AUDIO", "file-read-embedded-audio" },
151 { AGS_FILE_WRITE, "AGS_FILE_WRITE", "file-write" },
152 { AGS_FILE_WRITE_AUDIO_SIGNAL, "AGS_FILE_WRITE_AUDIO_SIGNAL", "file-write-audio-signal" },
153 { AGS_FILE_WRITE_EMBEDDED_AUDIO, "AGS_FILE_WRITE_EMBEDDED_AUDIO", "file-write-embedded-audio" },
154 { 0, NULL, NULL }
155 };
156
157 GType g_flags_type_id = g_flags_register_static(g_intern_static_string("AgsFileFlags"), values);
158
159 g_once_init_leave (&g_flags_type_id__volatile, g_flags_type_id);
160 }
161
162 return g_flags_type_id__volatile;
163 }
164
165 void
ags_file_class_init(AgsFileClass * file)166 ags_file_class_init(AgsFileClass *file)
167 {
168 GObjectClass *gobject;
169 GParamSpec *param_spec;
170
171 ags_file_parent_class = g_type_class_peek_parent(file);
172
173 /* GObjectClass */
174 gobject = (GObjectClass *) file;
175
176 gobject->get_property = ags_file_get_property;
177 gobject->set_property = ags_file_set_property;
178
179 gobject->finalize = ags_file_finalize;
180
181 /* properties */
182 /**
183 * AgsFile:filename:
184 *
185 * The assigned filename to open and read from.
186 *
187 * Since: 3.0.0
188 */
189 param_spec = g_param_spec_string("filename",
190 i18n_pspec("filename to read or write"),
191 i18n_pspec("The filename to read or write to"),
192 NULL,
193 G_PARAM_READABLE | G_PARAM_WRITABLE);
194 g_object_class_install_property(gobject,
195 PROP_FILENAME,
196 param_spec);
197
198 /**
199 * AgsFile:encoding:
200 *
201 * The charset encoding to use.
202 *
203 * Since: 3.0.0
204 */
205 param_spec = g_param_spec_string("encoding",
206 i18n_pspec("encoding to use"),
207 i18n_pspec("The encoding of the XML document"),
208 NULL,
209 G_PARAM_READABLE | G_PARAM_WRITABLE);
210 g_object_class_install_property(gobject,
211 PROP_ENCODING,
212 param_spec);
213
214 /**
215 * AgsFile:audio-format:
216 *
217 * The format of embedded audio data.
218 *
219 * Since: 3.0.0
220 */
221 param_spec = g_param_spec_string("audio-format",
222 i18n_pspec("audio format to use"),
223 i18n_pspec("The audio format used by embedded audio"),
224 AGS_FILE_DEFAULT_AUDIO_FORMAT,
225 G_PARAM_READABLE | G_PARAM_WRITABLE);
226 g_object_class_install_property(gobject,
227 PROP_AUDIO_FORMAT,
228 param_spec);
229
230 /**
231 * AgsFile:audio-encoding:
232 *
233 * The encoding to use for embedding audio data.
234 *
235 * Since: 3.0.0
236 */
237 param_spec = g_param_spec_string("audio-encoding",
238 i18n_pspec("audio encoding to use"),
239 i18n_pspec("The audio encoding used by embedded audio"),
240 AGS_FILE_DEFAULT_AUDIO_ENCODING,
241 G_PARAM_READABLE | G_PARAM_WRITABLE);
242 g_object_class_install_property(gobject,
243 PROP_AUDIO_ENCODING,
244 param_spec);
245
246 /**
247 * AgsFile:xml-doc:
248 *
249 * The assigned xml-doc.
250 *
251 * Since: 3.0.0
252 */
253 param_spec = g_param_spec_pointer("xml-doc",
254 i18n_pspec("xml document of file"),
255 i18n_pspec("The xml document assigned with file"),
256 G_PARAM_READABLE | G_PARAM_WRITABLE);
257 g_object_class_install_property(gobject,
258 PROP_XML_DOC,
259 param_spec);
260
261 /* AgsFileClass */
262 file->open = ags_file_real_open;
263 file->rw_open = ags_file_real_rw_open;
264 file->open_from_data = ags_file_real_open_from_data;
265
266 file->write = ags_file_real_write;
267 file->write_concurrent = ags_file_real_write_concurrent;
268 file->write_resolve = ags_file_real_write_resolve;
269 file->read = ags_file_real_read;
270 file->read_resolve = ags_file_real_read_resolve;
271 file->read_start = ags_file_real_read_start;
272
273 /* signals */
274 /**
275 * AgsFile::open:
276 * @file: the #AgsFile
277 * @error: a #GError-struct pointer to return error
278 *
279 * Open @file with appropriate filename.
280 *
281 * Since: 3.0.0
282 */
283 file_signals[OPEN] =
284 g_signal_new("open",
285 G_TYPE_FROM_CLASS(file),
286 G_SIGNAL_RUN_LAST,
287 G_STRUCT_OFFSET(AgsFileClass, open),
288 NULL, NULL,
289 g_cclosure_marshal_VOID__POINTER,
290 G_TYPE_NONE, 1,
291 G_TYPE_POINTER);
292
293 /**
294 * AgsFile::open-from-data:
295 * @file: the #AgsFile
296 * @data: the buffer containing the file
297 * @length: the buffer length
298 * @error: a #GError-struct pointer to return error
299 *
300 * Open @file from a buffer containing the file.
301 *
302 * Since: 3.0.0
303 */
304 file_signals[OPEN_FROM_DATA] =
305 g_signal_new("open-from-data",
306 G_TYPE_FROM_CLASS(file),
307 G_SIGNAL_RUN_LAST,
308 G_STRUCT_OFFSET(AgsFileClass, open_from_data),
309 NULL, NULL,
310 ags_cclosure_marshal_VOID__STRING_UINT_POINTER,
311 G_TYPE_NONE, 3,
312 G_TYPE_STRING,
313 G_TYPE_UINT,
314 G_TYPE_POINTER);
315
316 /**
317 * AgsFile::rw-data:
318 * @file: the #AgsFile
319 * @create: if %TRUE the file will be created if not exists
320 * @error: a #GError-struct pointer to return error
321 *
322 * Open @file in read-write mode.
323 *
324 * Since: 3.0.0
325 */
326 file_signals[RW_OPEN] =
327 g_signal_new("rw-open",
328 G_TYPE_FROM_CLASS(file),
329 G_SIGNAL_RUN_LAST,
330 G_STRUCT_OFFSET(AgsFileClass, rw_open),
331 NULL, NULL,
332 ags_cclosure_marshal_VOID__BOOLEAN_POINTER,
333 G_TYPE_NONE, 2,
334 G_TYPE_BOOLEAN,
335 G_TYPE_POINTER);
336
337 /**
338 * AgsFile::write:
339 * @file: the #AgsFile
340 *
341 * Write XML Document to disk.
342 *
343 * Since: 3.0.0
344 */
345 file_signals[WRITE] =
346 g_signal_new("write",
347 G_TYPE_FROM_CLASS(file),
348 G_SIGNAL_RUN_LAST,
349 G_STRUCT_OFFSET(AgsFileClass, write),
350 NULL, NULL,
351 g_cclosure_marshal_VOID__VOID,
352 G_TYPE_NONE, 0);
353
354 file_signals[WRITE_CONCURRENT] =
355 g_signal_new("write-concurrent",
356 G_TYPE_FROM_CLASS(file),
357 G_SIGNAL_RUN_LAST,
358 G_STRUCT_OFFSET(AgsFileClass, write_concurrent),
359 NULL, NULL,
360 g_cclosure_marshal_VOID__VOID,
361 G_TYPE_NONE, 0);
362
363 /**
364 * AgsFile::write-resolve:
365 * @file: the #AgsFile
366 *
367 * Resolve references and generate thus XPath expressions just
368 * before writing to disk.
369 *
370 * Since: 3.0.0
371 */
372 file_signals[WRITE_RESOLVE] =
373 g_signal_new("write-resolve",
374 G_TYPE_FROM_CLASS(file),
375 G_SIGNAL_RUN_LAST,
376 G_STRUCT_OFFSET(AgsFileClass, write_resolve),
377 NULL, NULL,
378 g_cclosure_marshal_VOID__VOID,
379 G_TYPE_NONE, 0);
380
381 /**
382 * AgsFile::read:
383 * @file: the #AgsFile
384 *
385 * Read a XML document from disk with specified filename.
386 *
387 * Since: 3.0.0
388 */
389 file_signals[READ] =
390 g_signal_new("read",
391 G_TYPE_FROM_CLASS(file),
392 G_SIGNAL_RUN_LAST,
393 G_STRUCT_OFFSET(AgsFileClass, read),
394 NULL, NULL,
395 g_cclosure_marshal_VOID__VOID,
396 G_TYPE_NONE, 0);
397
398 /**
399 * AgsFile::read-resolve:
400 * @file: the #AgsFile
401 *
402 * Resolve XPath expressions to their counterpart the newly created
403 * instances refering to.
404 *
405 * Since: 3.0.0
406 */
407 file_signals[READ_RESOLVE] =
408 g_signal_new("read-resolve",
409 G_TYPE_FROM_CLASS(file),
410 G_SIGNAL_RUN_LAST,
411 G_STRUCT_OFFSET(AgsFileClass, read_resolve),
412 NULL, NULL,
413 g_cclosure_marshal_VOID__VOID,
414 G_TYPE_NONE, 0);
415
416 /**
417 * AgsFile::read-start:
418 * @file: the #AgsFile
419 *
420 * Hook after reading XML document to update or start the application.
421 *
422 * Since: 3.0.0
423 */
424 file_signals[READ_START] =
425 g_signal_new("read-start",
426 G_TYPE_FROM_CLASS(file),
427 G_SIGNAL_RUN_LAST,
428 G_STRUCT_OFFSET(AgsFileClass, read_start),
429 NULL, NULL,
430 g_cclosure_marshal_VOID__VOID,
431 G_TYPE_NONE, 0);
432 }
433
434 GQuark
ags_file_error_quark()435 ags_file_error_quark()
436 {
437 return(g_quark_from_static_string("ags-file-error-quark"));
438 }
439
440 void
ags_file_init(AgsFile * file)441 ags_file_init(AgsFile *file)
442 {
443
444 file->flags = 0;
445
446 /* add file mutex */
447 g_rec_mutex_init(&(file->obj_mutex));
448
449 file->out = NULL;
450 file->buffer = NULL;
451
452 file->filename = NULL;
453 file->encoding = AGS_FILE_DEFAULT_ENCODING;
454 file->dtd = AGS_FILE_DEFAULT_DTD;
455
456 file->audio_format = AGS_FILE_DEFAULT_AUDIO_FORMAT;
457 file->audio_encoding = AGS_FILE_DEFAULT_AUDIO_ENCODING;
458
459 file->doc = NULL;
460
461 file->id_refs = NULL;
462 file->lookup = NULL;
463 file->launch = NULL;
464
465 file->clipboard = NULL;
466 file->property = NULL;
467 file->script = NULL;
468 file->cluster = NULL;
469 file->client = NULL;
470 file->server = NULL;
471
472 file->history = NULL;
473
474 file->embedded_audio = NULL;
475 file->file_link = NULL;
476 }
477
478 void
ags_file_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)479 ags_file_set_property(GObject *gobject,
480 guint prop_id,
481 const GValue *value,
482 GParamSpec *param_spec)
483 {
484 AgsFile *file;
485
486 GRecMutex *file_mutex;
487
488 file = AGS_FILE(gobject);
489
490 /* get file mutex */
491 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
492
493 switch(prop_id){
494 case PROP_FILENAME:
495 {
496 gchar *filename;
497
498 filename = g_value_get_string(value);
499
500 g_rec_mutex_lock(file_mutex);
501
502 if(file->filename == filename){
503 g_rec_mutex_unlock(file_mutex);
504
505 return;
506 }
507
508 if(file->filename != NULL){
509 g_free(file->filename);
510 }
511
512 file->filename = g_strdup(filename);
513
514 g_rec_mutex_unlock(file_mutex);
515 }
516 break;
517 case PROP_ENCODING:
518 {
519 gchar *encoding;
520
521 encoding = g_value_get_string(value);
522
523 g_rec_mutex_lock(file_mutex);
524
525 file->encoding = encoding;
526
527 g_rec_mutex_unlock(file_mutex);
528 }
529 break;
530 case PROP_AUDIO_FORMAT:
531 {
532 gchar *audio_format;
533
534 audio_format = g_value_get_string(value);
535
536 g_rec_mutex_lock(file_mutex);
537
538 file->audio_format = audio_format;
539
540 g_rec_mutex_unlock(file_mutex);
541 }
542 break;
543 case PROP_AUDIO_ENCODING:
544 {
545 gchar *audio_encoding;
546
547 audio_encoding = g_value_get_string(value);
548
549 g_rec_mutex_lock(file_mutex);
550
551 file->audio_encoding = audio_encoding;
552
553 g_rec_mutex_unlock(file_mutex);
554 }
555 break;
556 case PROP_XML_DOC:
557 {
558 xmlDoc *doc;
559
560 doc = (xmlDoc *) g_value_get_pointer(value);
561
562 g_rec_mutex_lock(file_mutex);
563
564 file->doc = doc;
565
566 g_rec_mutex_unlock(file_mutex);
567 }
568 break;
569 default:
570 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
571 break;
572 }
573 }
574
575 void
ags_file_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)576 ags_file_get_property(GObject *gobject,
577 guint prop_id,
578 GValue *value,
579 GParamSpec *param_spec)
580 {
581 AgsFile *file;
582
583 GRecMutex *file_mutex;
584
585 file = AGS_FILE(gobject);
586
587 /* get file mutex */
588 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
589
590 switch(prop_id){
591 case PROP_FILENAME:
592 {
593 g_rec_mutex_lock(file_mutex);
594
595 g_value_set_string(value, file->filename);
596
597 g_rec_mutex_unlock(file_mutex);
598 }
599 break;
600 case PROP_ENCODING:
601 {
602 g_rec_mutex_lock(file_mutex);
603
604 g_value_set_string(value, file->encoding);
605
606 g_rec_mutex_unlock(file_mutex);
607 }
608 break;
609 case PROP_AUDIO_FORMAT:
610 {
611 g_rec_mutex_lock(file_mutex);
612
613 g_value_set_string(value, file->audio_format);
614
615 g_rec_mutex_unlock(file_mutex);
616 }
617 break;
618 case PROP_AUDIO_ENCODING:
619 {
620 g_rec_mutex_lock(file_mutex);
621
622 g_value_set_string(value, file->audio_encoding);
623
624 g_rec_mutex_unlock(file_mutex);
625 }
626 break;
627 case PROP_XML_DOC:
628 {
629 g_rec_mutex_lock(file_mutex);
630
631 g_value_set_pointer(value, file->doc);
632
633 g_rec_mutex_unlock(file_mutex);
634 }
635 break;
636 default:
637 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
638 break;
639 }
640 }
641
642 void
ags_file_finalize(GObject * gobject)643 ags_file_finalize(GObject *gobject)
644 {
645 AgsFile *file;
646
647 file = (AgsFile *) gobject;
648
649 if((AGS_FILE_READ & (file->flags)) != 0){
650 }else if((AGS_FILE_WRITE & (file->flags)) != 0){
651 }else{
652 return;
653 }
654
655 xmlFreeDoc(file->doc);
656 // xmlCleanupParser();
657 // xmlMemoryDump();
658
659 G_OBJECT_CLASS(ags_file_parent_class)->finalize(gobject);
660 }
661
662 /**
663 * ags_file_set_filename:
664 * @file: the #AgsFile
665 * @filename: the filename
666 *
667 * Set @filename of @file.
668 *
669 * Since: 3.6.17
670 */
671 void
ags_file_set_filename(AgsFile * file,gchar * filename)672 ags_file_set_filename(AgsFile *file,
673 gchar *filename)
674 {
675 if(!AGS_IS_FILE(file)){
676 return;
677 }
678
679 g_object_set(file,
680 "filename", filename,
681 NULL);
682 }
683
684 /**
685 * ags_file_get_filename:
686 * @file: the #AgsFile
687 *
688 * Get filename of @file.
689 *
690 * Returns: (transfer full): the filename
691 *
692 * Since: 3.6.17
693 */
694 gchar*
ags_file_get_filename(AgsFile * file)695 ags_file_get_filename(AgsFile *file)
696 {
697 gchar *filename;
698
699 if(!AGS_IS_FILE(file)){
700 return(NULL);
701 }
702
703 g_object_get(file,
704 "filename", &filename,
705 NULL);
706
707 return(filename);
708 }
709
710 /**
711 * ags_file_set_encoding:
712 * @file: the #AgsFile
713 * @encoding: the encoding
714 *
715 * Set @encoding of @file.
716 *
717 * Since: 3.6.17
718 */
719 void
ags_file_set_encoding(AgsFile * file,gchar * encoding)720 ags_file_set_encoding(AgsFile *file,
721 gchar *encoding)
722 {
723 if(!AGS_IS_FILE(file)){
724 return;
725 }
726
727 g_object_set(file,
728 "encoding", encoding,
729 NULL);
730 }
731
732 /**
733 * ags_file_get_encoding:
734 * @file: the #AgsFile
735 *
736 * Get encoding of @file.
737 *
738 * Returns: (transfer full): the encoding
739 *
740 * Since: 3.6.17
741 */
742 gchar*
ags_file_get_encoding(AgsFile * file)743 ags_file_get_encoding(AgsFile *file)
744 {
745 gchar *encoding;
746
747 if(!AGS_IS_FILE(file)){
748 return(NULL);
749 }
750
751 g_object_get(file,
752 "encoding", &encoding,
753 NULL);
754
755 return(encoding);
756 }
757
758 /**
759 * ags_file_set_audio_format:
760 * @file: the #AgsFile
761 * @audio_format: the audio format
762 *
763 * Set @audio_format of @file.
764 *
765 * Since: 3.6.17
766 */
767 void
ags_file_set_audio_format(AgsFile * file,gchar * audio_format)768 ags_file_set_audio_format(AgsFile *file,
769 gchar *audio_format)
770 {
771 if(!AGS_IS_FILE(file)){
772 return;
773 }
774
775 g_object_set(file,
776 "audio-format", audio_format,
777 NULL);
778 }
779
780 /**
781 * ags_file_get_audio_format:
782 * @file: the #AgsFile
783 *
784 * Get audio format of @file.
785 *
786 * Returns: (transfer full): the audio format
787 *
788 * Since: 3.6.17
789 */
790 gchar*
ags_file_get_audio_format(AgsFile * file)791 ags_file_get_audio_format(AgsFile *file)
792 {
793 gchar *audio_format;
794
795 if(!AGS_IS_FILE(file)){
796 return(NULL);
797 }
798
799 g_object_get(file,
800 "audio-format", &audio_format,
801 NULL);
802
803 return(audio_format);
804 }
805
806 /**
807 * ags_file_set_audio_encoding:
808 * @file: the #AgsFile
809 * @audio_encoding: the audio encoding
810 *
811 * Set @audio_encoding of @file.
812 *
813 * Since: 3.6.17
814 */
815 void
ags_file_set_audio_encoding(AgsFile * file,gchar * audio_encoding)816 ags_file_set_audio_encoding(AgsFile *file,
817 gchar *audio_encoding)
818 {
819 if(!AGS_IS_FILE(file)){
820 return;
821 }
822
823 g_object_set(file,
824 "audio-encoding", audio_encoding,
825 NULL);
826 }
827
828 /**
829 * ags_file_get_audio_encoding:
830 * @file: the #AgsFile
831 *
832 * Get audio encoding of @file.
833 *
834 * Returns: (transfer full): the audio encoding
835 *
836 * Since: 3.6.17
837 */
838 gchar*
ags_file_get_audio_encoding(AgsFile * file)839 ags_file_get_audio_encoding(AgsFile *file)
840 {
841 gchar *audio_encoding;
842
843 if(!AGS_IS_FILE(file)){
844 return(NULL);
845 }
846
847 g_object_get(file,
848 "audio-encoding", &audio_encoding,
849 NULL);
850
851 return(audio_encoding);
852 }
853
854 /**
855 * ags_file_set_xml_doc:
856 * @file: the #AgsFile
857 * @xml_doc: (transfer full): the #xmlDoc-struct
858 *
859 * Set @xml_doc of @file.
860 *
861 * Since: 3.6.17
862 */
863 void
ags_file_set_xml_doc(AgsFile * file,xmlDoc * xml_doc)864 ags_file_set_xml_doc(AgsFile *file,
865 xmlDoc *xml_doc)
866 {
867 if(!AGS_IS_FILE(file)){
868 return;
869 }
870
871 g_object_set(file,
872 "xml-doc", xml_doc,
873 NULL);
874 }
875
876 /**
877 * ags_file_get_xml_doc:
878 * @file: the #AgsFile
879 *
880 * Get xml doc of @file.
881 *
882 * Returns: (transfer none): the #xmlDoc-struct
883 *
884 * Since: 3.6.17
885 */
886 xmlDoc*
ags_file_get_xml_doc(AgsFile * file)887 ags_file_get_xml_doc(AgsFile *file)
888 {
889 xmlDoc *xml_doc;
890
891 if(!AGS_IS_FILE(file)){
892 return(NULL);
893 }
894
895 g_object_get(file,
896 "xml-doc", &xml_doc,
897 NULL);
898
899 return(xml_doc);
900 }
901
902 /**
903 * ags_file_str2md5:
904 * @content: the string buffer
905 * @content_length: the length of the string
906 *
907 * Compute MD5 sums of a buffer.
908 *
909 * Returns: (transfer full): the md5 checksum
910 *
911 * Since: 3.0.0
912 */
913 gchar*
ags_file_str2md5(gchar * content,guint content_length)914 ags_file_str2md5(gchar *content, guint content_length)
915 {
916 GChecksum *checksum;
917 gchar *str;
918
919 str = g_compute_checksum_for_string(G_CHECKSUM_MD5,
920 content,
921 content_length);
922
923 return(str);
924 }
925
926 /**
927 * ags_file_add_id_ref:
928 * @file: the @AgsFile
929 * @id_ref: a reference
930 *
931 * Adds @id_ref to @file.
932 *
933 * Since: 3.0.0
934 */
935 void
ags_file_add_id_ref(AgsFile * file,GObject * id_ref)936 ags_file_add_id_ref(AgsFile *file, GObject *id_ref)
937 {
938 GRecMutex *file_mutex;
939
940 if(!AGS_IS_FILE(file) ||
941 !AGS_IS_FILE_ID_REF(id_ref)){
942 return;
943 }
944
945 /* get file mutex */
946 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
947
948 /* add */
949 g_rec_mutex_lock(file_mutex);
950
951 if(g_list_find(file->id_refs,
952 id_ref) == NULL){
953 g_object_ref(id_ref);
954 file->id_refs = g_list_prepend(file->id_refs,
955 id_ref);
956 }
957
958 g_rec_mutex_unlock(file_mutex);
959 }
960
961 /**
962 * ags_file_find_id_ref_by_node:
963 * @file: the @AgsFile
964 * @node: a XML node
965 *
966 * Find a reference by its XML node.
967 *
968 * Returns: (transfer full): the matching #GObject
969 *
970 * Since: 3.0.0
971 */
972 GObject*
ags_file_find_id_ref_by_node(AgsFile * file,xmlNode * node)973 ags_file_find_id_ref_by_node(AgsFile *file, xmlNode *node)
974 {
975 AgsFileIdRef *file_id_ref;
976
977 xmlNode *current_node;
978
979 GList *start_list, *list;
980
981 GRecMutex *file_mutex;
982
983 if(!AGS_IS_FILE(file) ||
984 node == NULL){
985 return(NULL);
986 }
987
988 /* get file mutex */
989 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
990
991 /* find */
992 file_id_ref = NULL;
993
994 g_rec_mutex_lock(file_mutex);
995
996 list =
997 start_list = g_list_copy_deep(file->id_refs,
998 (GCopyFunc) g_object_ref,
999 NULL);
1000
1001 g_rec_mutex_unlock(file_mutex);
1002
1003 while(list != NULL){
1004 g_object_get(list->data,
1005 "node", ¤t_node,
1006 NULL);
1007
1008 if(current_node == node){
1009 file_id_ref = AGS_FILE_ID_REF(list->data);
1010 g_object_ref(file_id_ref);
1011
1012 break;
1013 }
1014
1015 list = list->next;
1016 }
1017
1018 g_list_free_full(start_list,
1019 (GDestroyNotify) g_object_unref);
1020
1021 return((GObject *) file_id_ref);
1022 }
1023
1024 /**
1025 * ags_file_find_id_ref_by_xpath:
1026 * @file: the #AgsFile
1027 * @xpath: a XPath expression
1028 *
1029 * Lookup a reference by @xpath.
1030 *
1031 * Returns: (transfer full): the matching #GObject
1032 *
1033 * Since: 3.0.0
1034 */
1035 GObject*
ags_file_find_id_ref_by_xpath(AgsFile * file,gchar * xpath)1036 ags_file_find_id_ref_by_xpath(AgsFile *file, gchar *xpath)
1037 {
1038 AgsFileIdRef *file_id_ref;
1039
1040 xmlXPathContext *xpath_context;
1041 xmlXPathObject *xpath_object;
1042 xmlNode **node;
1043
1044 guint i;
1045
1046 if(!AGS_IS_FILE(file) || xpath == NULL || !g_str_has_prefix(xpath, "xpath=")){
1047 g_message("invalid xpath: %s", xpath);
1048
1049 return(NULL);
1050 }
1051
1052 xpath = &(xpath[6]);
1053
1054 /* Create xpath evaluation context */
1055 xpath_context = xmlXPathNewContext(file->doc);
1056
1057 if(xpath_context == NULL) {
1058 g_warning("Error: unable to create new XPath context");
1059
1060 return(NULL);
1061 }
1062
1063 /* Evaluate xpath expression */
1064 xpath_object = xmlXPathEval(xpath, xpath_context);
1065
1066 if(xpath_object == NULL) {
1067 g_warning("Error: unable to evaluate xpath expression \"%s\"", xpath);
1068 xmlXPathFreeContext(xpath_context);
1069
1070 return(NULL);
1071 }
1072
1073 node = xpath_object->nodesetval->nodeTab;
1074
1075 for(i = 0; i < xpath_object->nodesetval->nodeNr; i++){
1076 if(node[i]->type == XML_ELEMENT_NODE){
1077 return(ags_file_find_id_ref_by_node(file,
1078 node[i]));
1079 }
1080 }
1081
1082 g_message("no xpath match: %s", xpath);
1083
1084 return(NULL);
1085 }
1086
1087 /**
1088 * ags_file_find_id_ref_by_reference:
1089 * @file: the #AgsFile
1090 * @ref: a %gpointer
1091 *
1092 * Find a reference matching @ref.
1093 *
1094 * Returns: (transfer full): the matching #GObject
1095 *
1096 * Since: 3.0.0
1097 */
1098 GObject*
ags_file_find_id_ref_by_reference(AgsFile * file,gpointer ref)1099 ags_file_find_id_ref_by_reference(AgsFile *file, gpointer ref)
1100 {
1101 AgsFileIdRef *file_id_ref;
1102
1103 GList *start_list, *list;
1104
1105 gpointer current_ref;
1106
1107 GRecMutex *file_mutex;
1108
1109 if(!AGS_IS_FILE(file)){
1110 return(NULL);
1111 }
1112
1113 /* get file mutex */
1114 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1115
1116 /* find */
1117 file_id_ref = NULL;
1118
1119 g_rec_mutex_lock(file_mutex);
1120
1121 list =
1122 start_list = g_list_copy_deep(file->id_refs,
1123 (GCopyFunc) g_object_ref,
1124 NULL);
1125
1126 g_rec_mutex_unlock(file_mutex);
1127
1128 while(list != NULL){
1129 g_object_get(list->data,
1130 "reference", ¤t_ref,
1131 NULL);
1132
1133 if(current_ref == ref){
1134 file_id_ref = AGS_FILE_ID_REF(list->data);
1135 g_object_ref(file_id_ref);
1136
1137 break;
1138 }
1139
1140 list = list->next;
1141 }
1142
1143 g_list_free_full(start_list,
1144 (GDestroyNotify) g_object_unref);
1145
1146 return((GObject *) file_id_ref);
1147 }
1148
1149 /**
1150 * ags_file_add_lookup:
1151 * @file: the #AgsFile
1152 * @file_lookup: a #AgsFileLookup
1153 *
1154 * Add @file_lookup for later invoking.
1155 *
1156 * Since: 3.0.0
1157 */
1158 void
ags_file_add_lookup(AgsFile * file,GObject * file_lookup)1159 ags_file_add_lookup(AgsFile *file, GObject *file_lookup)
1160 {
1161 GRecMutex *file_mutex;
1162
1163 if(!AGS_IS_FILE(file) || !AGS_IS_FILE_LOOKUP(file_lookup)){
1164 return;
1165 }
1166
1167 /* get file mutex */
1168 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1169
1170 /* add */
1171 g_rec_mutex_lock(file_mutex);
1172
1173 if(g_list_find(file->lookup,
1174 file_lookup) == NULL){
1175 g_object_ref(G_OBJECT(file_lookup));
1176
1177 file->lookup = g_list_prepend(file->lookup,
1178 file_lookup);
1179 }
1180
1181 g_rec_mutex_unlock(file_mutex);
1182 }
1183
1184 /**
1185 * ags_file_add_launch:
1186 * @file: the #AgsFile
1187 * @file_launch: a #AgsFileLaunch
1188 *
1189 * Add @file_launch for later invoking.
1190 *
1191 * Since: 3.0.0
1192 */
1193 void
ags_file_add_launch(AgsFile * file,GObject * file_launch)1194 ags_file_add_launch(AgsFile *file, GObject *file_launch)
1195 {
1196 GRecMutex *file_mutex;
1197
1198 if(!AGS_IS_FILE(file) || !AGS_IS_FILE_LAUNCH(file_launch)){
1199 return;
1200 }
1201
1202 /* get file mutex */
1203 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1204
1205 /* add */
1206 g_rec_mutex_lock(file_mutex);
1207
1208 if(g_list_find(file->launch,
1209 file_launch) == NULL){
1210 g_object_ref(G_OBJECT(file_launch));
1211
1212 file->launch = g_list_prepend(file->launch,
1213 file_launch);
1214 }
1215
1216 g_rec_mutex_unlock(file_mutex);
1217 }
1218
1219 void
ags_file_real_open(AgsFile * file,GError ** error)1220 ags_file_real_open(AgsFile *file,
1221 GError **error)
1222 {
1223 xmlDoc *doc;
1224
1225 GRecMutex *file_mutex;
1226
1227 if(!AGS_IS_FILE(file)){
1228 return;
1229 }
1230
1231 /* get file mutex */
1232 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1233
1234 /* parse the file and get the DOM */
1235 doc = xmlReadFile(file->filename, NULL, 0);
1236
1237 g_rec_mutex_lock(file_mutex);
1238
1239 file->doc = doc;
1240
1241 if(doc == NULL){
1242 g_warning("ags_file.c - failed to read XML document %s", file->filename);
1243
1244 if(error != NULL){
1245 g_set_error(error,
1246 AGS_FILE_ERROR,
1247 AGS_FILE_ERROR_PARSER_FAILURE,
1248 "unable to parse document: %s\n",
1249 file->filename);
1250 }
1251 }else{
1252 /* get the root node */
1253 file->root_node = xmlDocGetRootElement(file->doc);
1254 }
1255
1256 g_rec_mutex_unlock(file_mutex);
1257 }
1258
1259 /**
1260 * ags_file_open:
1261 * @file: the #AgsFile
1262 * @error: the return location for errors
1263 *
1264 * Opens the file specified by :filename property.
1265 *
1266 * Since: 3.0.0
1267 */
1268 void
ags_file_open(AgsFile * file,GError ** error)1269 ags_file_open(AgsFile *file,
1270 GError **error)
1271 {
1272 g_return_if_fail(AGS_IS_FILE(file));
1273
1274 g_object_ref(G_OBJECT(file));
1275 g_signal_emit(G_OBJECT(file),
1276 file_signals[OPEN], 0,
1277 error);
1278 g_object_unref(G_OBJECT(file));
1279 }
1280
1281 void
ags_file_real_open_from_data(AgsFile * file,gchar * data,guint length,GError ** error)1282 ags_file_real_open_from_data(AgsFile *file,
1283 gchar *data, guint length,
1284 GError **error)
1285 {
1286 xmlDoc *doc;
1287
1288 GRecMutex *file_mutex;
1289
1290 if(!AGS_IS_FILE(file)){
1291 return;
1292 }
1293
1294 /* get file mutex */
1295 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1296
1297 /* parse */
1298 doc = xmlReadMemory(data, length, file->filename, NULL, 0);
1299
1300 g_rec_mutex_lock(file_mutex);
1301
1302 file->doc = doc;
1303
1304 if(file->doc == NULL){
1305 g_warning("ags_file.c - failed to read XML document %s", file->filename);
1306
1307 if(error != NULL){
1308 g_set_error(error,
1309 AGS_FILE_ERROR,
1310 AGS_FILE_ERROR_PARSER_FAILURE,
1311 "unable to parse document from data: %s\n",
1312 file->filename);
1313 }
1314 }else{
1315 /*Get the root element node */
1316 file->root_node = xmlDocGetRootElement(file->doc);
1317 }
1318
1319 g_rec_mutex_unlock(file_mutex);
1320 }
1321
1322 /**
1323 * ags_file_open_from_data:
1324 * @file: the #AgsFile
1325 * @data: a buffer containing the XML document
1326 * @length: the buffer length
1327 * @error: the return location for errors
1328 *
1329 * Opens the file provided by @data.
1330 *
1331 * Since: 3.0.0
1332 */
1333 void
ags_file_open_from_data(AgsFile * file,gchar * data,guint length,GError ** error)1334 ags_file_open_from_data(AgsFile *file,
1335 gchar *data, guint length,
1336 GError **error)
1337 {
1338 g_return_if_fail(AGS_IS_FILE(file));
1339
1340 g_object_ref(G_OBJECT(file));
1341 g_signal_emit(G_OBJECT(file),
1342 file_signals[OPEN_FROM_DATA], 0,
1343 data, length,
1344 error);
1345 g_object_unref(G_OBJECT(file));
1346 }
1347
1348 void
ags_file_real_rw_open(AgsFile * file,gboolean create,GError ** error)1349 ags_file_real_rw_open(AgsFile *file,
1350 gboolean create,
1351 GError **error)
1352 {
1353 GRecMutex *file_mutex;
1354
1355 if(!AGS_IS_FILE(file)){
1356 return;
1357 }
1358
1359 /* get file mutex */
1360 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1361
1362 /* create */
1363 g_rec_mutex_lock(file_mutex);
1364
1365 file->out = fopen(file->filename, "w+");
1366
1367 file->doc = xmlNewDoc("1.0");
1368 file->root_node = xmlNewNode(NULL, "ags");
1369 xmlDocSetRootElement(file->doc, file->root_node);
1370
1371 g_rec_mutex_unlock(file_mutex);
1372 }
1373
1374 /**
1375 * ags_file_rw_open:
1376 * @file: the #AgsFile
1377 * @create: if %TRUE create the file as needed
1378 * @error: the return location for errors
1379 *
1380 * Opens the file specified by :filename property in read-write mode.
1381 *
1382 * Since: 3.0.0
1383 */
1384 void
ags_file_rw_open(AgsFile * file,gboolean create,GError ** error)1385 ags_file_rw_open(AgsFile *file,
1386 gboolean create,
1387 GError **error)
1388 {
1389 g_return_if_fail(AGS_IS_FILE(file));
1390
1391 g_object_ref(G_OBJECT(file));
1392 g_signal_emit(G_OBJECT(file),
1393 file_signals[RW_OPEN], 0,
1394 create,
1395 error);
1396 g_object_unref(G_OBJECT(file));
1397 }
1398
1399 /**
1400 * ags_file_open_filename:
1401 * @file: the #AgsFile
1402 * @filename: a path
1403 *
1404 * Opens the file specified by @filename property.
1405 *
1406 * Since: 3.0.0
1407 */
1408 void
ags_file_open_filename(AgsFile * file,gchar * filename)1409 ags_file_open_filename(AgsFile *file,
1410 gchar *filename)
1411 {
1412 gchar *current_filename;
1413
1414 GError *error;
1415
1416 GRecMutex *file_mutex;
1417
1418 if(!AGS_IS_FILE(file)){
1419 return;
1420 }
1421
1422 /* get file mutex */
1423 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1424
1425 /* check close */
1426 g_rec_mutex_lock(file_mutex);
1427
1428 current_filename = file->filename;
1429
1430 g_rec_mutex_unlock(file_mutex);
1431
1432 if(current_filename != NULL){
1433 ags_file_close(file);
1434 }
1435
1436 g_object_set(file,
1437 "filename", filename,
1438 NULL);
1439
1440 error = NULL;
1441 ags_file_open(file,
1442 &error);
1443
1444 if(error != NULL){
1445 g_warning("%s", error->message);
1446
1447 g_error_free(error);
1448 }
1449 }
1450
1451 /**
1452 * ags_file_close:
1453 * @file: the #AgsFile
1454 *
1455 * Closes @file.
1456 *
1457 * Since: 3.0.0
1458 */
1459 void
ags_file_close(AgsFile * file)1460 ags_file_close(AgsFile *file)
1461 {
1462 GRecMutex *file_mutex;
1463
1464 if(!AGS_IS_FILE(file)){
1465 return;
1466 }
1467
1468 /* get file mutex */
1469 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1470
1471 /* close */
1472 g_rec_mutex_lock(file_mutex);
1473
1474 if(file->out != NULL){
1475 fclose(file->out);
1476 }
1477
1478 /* free the document */
1479 xmlFreeDoc(file->doc);
1480
1481 /*
1482 *Free the global variables that may
1483 *have been allocated by the parser.
1484 */
1485 xmlCleanupParser();
1486
1487 /*
1488 * this is to debug memory for regression tests
1489 */
1490 xmlMemoryDump();
1491
1492 file->filename = NULL;
1493
1494 g_rec_mutex_unlock(file_mutex);
1495 }
1496
1497 void
ags_file_real_write(AgsFile * file)1498 ags_file_real_write(AgsFile *file)
1499 {
1500 AgsApplicationContext *application_context;
1501
1502 GList *list;
1503
1504 int size;
1505
1506 GRecMutex *file_mutex;
1507
1508 /* get file mutex */
1509 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1510
1511 // ags_file_rw_open(file,
1512 // TRUE);
1513
1514 /* write clip board */
1515 //TODO:JK: implement me
1516
1517 /* write scripts */
1518 //TODO:JK: implement me
1519
1520 /* write cluster */
1521 //TODO:JK: implement me
1522
1523 /* write client */
1524 //TODO:JK: implement me
1525
1526 /* write server */
1527 //TODO:JK: implement me
1528
1529 /* write application context */
1530 ags_file_write_application_context(file,
1531 file->root_node,
1532 ags_application_context_get_instance());
1533
1534 /* write embedded audio */
1535 //TODO:JK: implement me
1536
1537 /* write file link */
1538 //TODO:JK: implement me
1539
1540 /* write history */
1541 //TODO:JK: implement me
1542
1543 /* resolve */
1544 ags_file_write_resolve(file);
1545
1546 /*
1547 * Dumping document to file
1548 */
1549 // xmlSaveFormatFileEnc(file->filename, file->doc, "UTF-8", 1);
1550 xmlDocDumpFormatMemoryEnc(file->doc, &(file->buffer), &size, file->encoding, TRUE);
1551
1552 fwrite(file->buffer, size, sizeof(xmlChar), file->out);
1553 fflush(file->out);
1554 }
1555
1556 /**
1557 * ags_file_write:
1558 * @file: the #AgsFile
1559 *
1560 * Write the XML document to disk.
1561 *
1562 * Since: 3.0.0
1563 */
1564 void
ags_file_write(AgsFile * file)1565 ags_file_write(AgsFile *file)
1566 {
1567 g_return_if_fail(AGS_IS_FILE(file));
1568
1569 g_object_ref(G_OBJECT(file));
1570 g_signal_emit(G_OBJECT(file),
1571 file_signals[WRITE], 0);
1572 g_object_unref(G_OBJECT(file));
1573 }
1574
1575 void
ags_file_real_write_concurrent(AgsFile * file)1576 ags_file_real_write_concurrent(AgsFile *file)
1577 {
1578 AgsApplicationContext *application_context;
1579 AgsThread *main_loop, *gui_thread, *task_thread;
1580 xmlNode *root_node;
1581 FILE *file_out;
1582 GList *list;
1583 xmlChar *buffer;
1584 int size;
1585
1586 xmlNode *parent, *node, *child;
1587 gchar *id;
1588
1589 application_context = ags_application_context_get_instance();
1590
1591 main_loop = (AgsThread *) AGS_APPLICATION_CONTEXT(application_context)->main_loop;
1592 //gui_thread = AGS_AUDIO_LOOP(main_loop)->gui_thread;
1593 //task_thread = AGS_AUDIO_LOOP(main_loop)->task_thread;
1594
1595 file->doc = xmlNewDoc("1.0");
1596 root_node = xmlNewNode(NULL, "ags");
1597 xmlDocSetRootElement(file->doc, root_node);
1598
1599 parent = root_node;
1600
1601 /* write clip board */
1602 //TODO:JK: implement me
1603
1604 /* write scripts */
1605 //TODO:JK: implement me
1606
1607 /* write cluster */
1608 //TODO:JK: implement me
1609
1610 /* write client */
1611 //TODO:JK: implement me
1612
1613 /* write server */
1614 //TODO:JK: implement me
1615
1616 /* the main code - write main */
1617 //TODO:JK: implement me
1618
1619 /* write embedded audio */
1620 //TODO:JK: implement me
1621
1622 /* write file link */
1623 //TODO:JK: implement me
1624
1625 /* write history */
1626 //TODO:JK: implement me
1627
1628 /* resolve */
1629 ags_file_write_resolve(file);
1630
1631 /*
1632 * Dumping document to file
1633 */
1634 // xmlSaveFormatFileEnc(file->filename, file->doc, "UTF-8", 1);
1635 xmlDocDumpFormatMemoryEnc(file->doc, &buffer, &size, file->encoding, TRUE);
1636
1637 file_out = fopen(file->filename, "w+");
1638 fwrite(buffer, size, sizeof(xmlChar), file_out);
1639 fflush(file_out);
1640 fclose(file_out);
1641
1642 /*free the document */
1643 xmlFreeDoc(file->doc);
1644
1645 /*
1646 *Free the global variables that may
1647 *have been allocated by the parser.
1648 */
1649 xmlCleanupParser();
1650
1651 /*
1652 * this is to debug memory for regression tests
1653 */
1654 xmlMemoryDump();
1655 }
1656
1657 void
ags_file_write_concurrent(AgsFile * file)1658 ags_file_write_concurrent(AgsFile *file)
1659 {
1660 g_return_if_fail(AGS_IS_FILE(file));
1661
1662 g_object_ref(G_OBJECT(file));
1663 g_signal_emit(G_OBJECT(file),
1664 file_signals[WRITE_CONCURRENT], 0);
1665 g_object_unref(G_OBJECT(file));
1666 }
1667
1668 void
ags_file_real_write_resolve(AgsFile * file)1669 ags_file_real_write_resolve(AgsFile *file)
1670 {
1671 GList *start_list, *list;
1672
1673 GRecMutex *file_mutex;
1674
1675 /* get file mutex */
1676 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1677
1678 /* resolve */
1679 g_rec_mutex_lock(file_mutex);
1680
1681 file->lookup = g_list_prepend(file->lookup,
1682 NULL);
1683
1684 list =
1685 start_list = g_list_copy(file->lookup);
1686
1687 g_rec_mutex_unlock(file_mutex);
1688
1689 while(list != NULL){
1690 ags_file_lookup_resolve(AGS_FILE_LOOKUP(list->data));
1691
1692 list = list->next;
1693 }
1694
1695 g_list_free(start_list);
1696 }
1697
1698 /**
1699 * ags_file_write_resolve:
1700 * @file: the #AgsFile
1701 *
1702 * Resolve references to XPath expressions.
1703 *
1704 * Since: 3.0.0
1705 */
1706 void
ags_file_write_resolve(AgsFile * file)1707 ags_file_write_resolve(AgsFile *file)
1708 {
1709 g_return_if_fail(AGS_IS_FILE(file));
1710
1711 g_object_ref(G_OBJECT(file));
1712 g_signal_emit(G_OBJECT(file),
1713 file_signals[WRITE_RESOLVE], 0);
1714 g_object_unref(G_OBJECT(file));
1715 }
1716
1717 void
ags_file_real_read(AgsFile * file)1718 ags_file_real_read(AgsFile *file)
1719 {
1720 AgsApplicationContext *application_context;
1721
1722 xmlNode *root_node, *child;
1723
1724 GRecMutex *file_mutex;
1725
1726 /* get file mutex */
1727 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1728
1729
1730 /* child elements */
1731 g_rec_mutex_lock(file_mutex);
1732
1733 root_node = file->root_node;
1734
1735 child = root_node->children;
1736 application_context = ags_application_context_get_instance();
1737
1738 g_rec_mutex_unlock(file_mutex);
1739
1740 while(child != NULL){
1741 if(child->type == XML_ELEMENT_NODE){
1742 if(!xmlStrncmp("ags-main",
1743 child->name,
1744 9)){
1745 ags_file_read_application_context(file,
1746 child,
1747 (GObject **) &application_context);
1748 }else if(!xmlStrncmp("ags-embedded-audio-list",
1749 child->name,
1750 24)){
1751 //TODO:JK: implement me
1752 }else if(!xmlStrncmp("ags-file-link-list",
1753 child->name,
1754 19)){
1755 //TODO:JK: implement me
1756 }else if(!xmlStrncmp("ags-history",
1757 child->name,
1758 12)){
1759 //TODO:JK: implement me
1760 }
1761 }
1762
1763 child = child->next;
1764 }
1765
1766 /* resolve */
1767 ags_file_read_resolve(file);
1768
1769 g_message("XML file resolved");
1770
1771 ags_connectable_connect(AGS_CONNECTABLE(application_context));
1772
1773 g_message("XML file connected");
1774
1775 /* start */
1776 ags_file_read_start(file);
1777 }
1778
1779 /**
1780 * ags_file_read:
1781 * @file: the #AgsFile
1782 *
1783 * Read XML document from disk.
1784 *
1785 * Since: 3.0.0
1786 */
1787 void
ags_file_read(AgsFile * file)1788 ags_file_read(AgsFile *file)
1789 {
1790 g_return_if_fail(AGS_IS_FILE(file));
1791
1792 g_object_ref(G_OBJECT(file));
1793 g_signal_emit(G_OBJECT(file),
1794 file_signals[READ], 0);
1795 g_object_unref(G_OBJECT(file));
1796 }
1797
1798 void
ags_file_real_read_resolve(AgsFile * file)1799 ags_file_real_read_resolve(AgsFile *file)
1800 {
1801 GList *start_list, *list;
1802
1803 GRecMutex *file_mutex;
1804
1805 /* get file mutex */
1806 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1807
1808 /* resolve */
1809 g_rec_mutex_lock(file_mutex);
1810
1811 file->lookup = g_list_prepend(file->lookup,
1812 NULL);
1813 list =
1814 start_list = g_list_reverse(g_list_copy(file->lookup));
1815
1816 g_rec_mutex_unlock(file_mutex);
1817
1818 while(list != NULL){
1819 ags_file_lookup_resolve(AGS_FILE_LOOKUP(list->data));
1820
1821 list = list->next;
1822 }
1823
1824 g_list_free(start_list);
1825 }
1826
1827 /**
1828 * ags_file_resolve:
1829 * @file: the #AgsFile
1830 *
1831 * Resolve XPath expressions to references.
1832 *
1833 * Since: 3.0.0
1834 */
1835 void
ags_file_read_resolve(AgsFile * file)1836 ags_file_read_resolve(AgsFile *file)
1837 {
1838 g_return_if_fail(AGS_IS_FILE(file));
1839
1840 g_object_ref(G_OBJECT(file));
1841 g_signal_emit(G_OBJECT(file),
1842 file_signals[READ_RESOLVE], 0);
1843 g_object_unref(G_OBJECT(file));
1844 }
1845
1846 void
ags_file_real_read_start(AgsFile * file)1847 ags_file_real_read_start(AgsFile *file)
1848 {
1849 GList *start_list, *list;
1850
1851 GRecMutex *file_mutex;
1852
1853 /* get file mutex */
1854 file_mutex = AGS_FILE_GET_OBJ_MUTEX(file);
1855
1856 /* start */
1857 g_rec_mutex_lock(file_mutex);
1858
1859 list =
1860 start_list = g_list_reverse(g_list_copy(file->launch));
1861
1862 g_rec_mutex_unlock(file_mutex);
1863
1864 while(list != NULL){
1865 ags_file_launch_start(AGS_FILE_LAUNCH(list->data));
1866
1867 list = list->next;
1868 }
1869
1870 g_list_free(start_list);
1871 }
1872
1873 /**
1874 * ags_file_read_start:
1875 * @file: the #AgsFile
1876 *
1877 * Update or start the application.
1878 *
1879 * Since: 3.0.0
1880 */
1881 void
ags_file_read_start(AgsFile * file)1882 ags_file_read_start(AgsFile *file)
1883 {
1884 g_return_if_fail(AGS_IS_FILE(file));
1885
1886 g_object_ref(G_OBJECT(file));
1887 g_signal_emit(G_OBJECT(file),
1888 file_signals[READ_START], 0);
1889 g_object_unref(G_OBJECT(file));
1890 }
1891
1892 void
ags_file_read_config(AgsFile * file,xmlNode * node,GObject ** config)1893 ags_file_read_config(AgsFile *file, xmlNode *node, GObject **config)
1894 {
1895 AgsConfig *gobject;
1896
1897 gchar *id;
1898
1899 char *buffer;
1900 gsize buffer_length;
1901
1902 gobject = (AgsConfig *) *config;
1903 gobject->version = xmlGetProp(node,
1904 "version");
1905
1906 gobject->build_id = xmlGetProp(node,
1907 "id");
1908
1909 buffer = xmlNodeGetContent(node);
1910 buffer_length = xmlStrlen(buffer);
1911
1912 ags_config_load_from_data(gobject,
1913 buffer, buffer_length);
1914 }
1915
1916 void
ags_file_write_config(AgsFile * file,xmlNode * parent,GObject * config)1917 ags_file_write_config(AgsFile *file, xmlNode *parent, GObject *config)
1918 {
1919 xmlNode *node;
1920 xmlNode *cdata;
1921
1922 gchar *id;
1923 char *buffer;
1924 gsize buffer_length;
1925
1926 id = ags_id_generator_create_uuid();
1927
1928 node = xmlNewNode(NULL,
1929 "ags-config");
1930
1931 ags_file_add_id_ref(file,
1932 g_object_new(AGS_TYPE_FILE_ID_REF,
1933 "file", file,
1934 "node", node,
1935 "xpath", g_strdup_printf("xpath=//*[@id='%s']", id),
1936 "reference", config,
1937 NULL));
1938
1939 xmlNewProp(node,
1940 "id",
1941 id);
1942
1943 xmlNewProp(node,
1944 "version",
1945 AGS_CONFIG(config)->version);
1946
1947 xmlNewProp(node,
1948 "build-id",
1949 AGS_CONFIG(config)->build_id);
1950
1951 xmlAddChild(parent,
1952 node);
1953
1954 /* cdata */
1955 ags_config_to_data(AGS_CONFIG(config),
1956 &buffer,
1957 &buffer_length);
1958
1959 cdata = xmlNewCDataBlock(file->doc,
1960 buffer,
1961 buffer_length);
1962
1963 xmlAddChild(node,
1964 cdata);
1965 }
1966
1967 void
ags_file_read_application_context(AgsFile * file,xmlNode * node,GObject ** application_context)1968 ags_file_read_application_context(AgsFile *file, xmlNode *node, GObject **application_context)
1969 {
1970 AgsApplicationContext *current_application_context;
1971
1972 GList *list;
1973 gchar *context;
1974
1975 current_application_context = ags_application_context_get_instance();
1976
1977 context = xmlGetProp(node,
1978 "context");
1979
1980 AGS_APPLICATION_CONTEXT_GET_CLASS(current_application_context)->register_types(AGS_APPLICATION_CONTEXT(current_application_context));
1981 AGS_APPLICATION_CONTEXT_GET_CLASS(current_application_context)->read(file,
1982 node,
1983 application_context);
1984 }
1985
1986 void
ags_file_write_application_context(AgsFile * file,xmlNode * parent,GObject * application_context)1987 ags_file_write_application_context(AgsFile *file, xmlNode *parent, GObject *application_context)
1988 {
1989 AGS_APPLICATION_CONTEXT_GET_CLASS(application_context)->register_types(AGS_APPLICATION_CONTEXT(application_context));
1990 AGS_APPLICATION_CONTEXT_GET_CLASS(application_context)->write(file,
1991 parent,
1992 application_context);
1993 }
1994
1995 /**
1996 * ags_file_new:
1997 *
1998 * Creates an #AgsFile
1999 *
2000 * Returns: a new #AgsFile
2001 *
2002 * Since: 3.0.0
2003 */
2004 AgsFile*
ags_file_new()2005 ags_file_new()
2006 {
2007 AgsFile *file;
2008
2009 file = (AgsFile *) g_object_new(AGS_TYPE_FILE,
2010 NULL);
2011
2012 return(file);
2013 }
2014