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", &current_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", &current_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