1 /*   EXTRAITS DE LA LICENCE
2 	Copyright CEA, contributeurs : Damien
3 	CALISTE, laboratoire L_Sim, (2016)
4 
5 	Adresse mèl :
6 	CALISTE, damien P caliste AT cea P fr.
7 
8 	Ce logiciel est un programme informatique servant à visualiser des
9 	structures atomiques dans un rendu pseudo-3D.
10 
11 	Ce logiciel est régi par la licence CeCILL soumise au droit français et
12 	respectant les principes de diffusion des logiciels libres. Vous pouvez
13 	utiliser, modifier et/ou redistribuer ce programme sous les conditions
14 	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
15 	sur le site "http://www.cecill.info".
16 
17 	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
18 	pris connaissance de la licence CeCILL, et que vous en avez accepté les
19 	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
20 */
21 
22 /*   LICENCE SUM UP
23 	Copyright CEA, contributors : Damien
24 	CALISTE, laboratoire L_Sim, (2016)
25 
26 	E-mail address:
27 	CALISTE, damien P caliste AT cea P fr.
28 
29 	This software is a computer program whose purpose is to visualize atomic
30 	configurations in 3D.
31 
32 	This software is governed by the CeCILL  license under French law and
33 	abiding by the rules of distribution of free software.  You can  use,
34 	modify and/ or redistribute the software under the terms of the CeCILL
35 	license as circulated by CEA, CNRS and INRIA at the following URL
36 	"http://www.cecill.info".
37 
38 	The fact that you are presently reading this means that you have had
39 	knowledge of the CeCILL license and that you accept its terms. You can
40 	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
41 */
42 
43 #include "visu_dataloadable.h"
44 
45 #include <glib/gstdio.h>
46 #include <sys/stat.h>
47 
48 #include "visu_commandLine.h"
49 #include "visu_dataatomic.h"
50 #include "visu_dataspin.h"
51 
52 /**
53  * SECTION: visu_dataloadable
54  * @short_description: a base class for all loadable representation of
55  * #VisuData objects.
56  *
57  * <para>#VisuData is a memory representation of node data. This class
58  * is defining common methods for all #VisuDataLoadable object that
59  * provide disk method to load #VisuData objects.</para>
60  */
61 
62 enum {
63   PROP_0,
64   PROP_N_SOURCES,
65   PROP_LABEL,
66   PROP_LOADING,
67   PROP_STATUS,
68   PROP_AUTO_REFRESH,
69   PROP_REFRESH_PERIOD,
70   N_PROPS
71 };
72 static GParamSpec *_properties[N_PROPS];
73 
74 /**
75  * VisuDataLoadable:
76  *
77  * An opaque structure storing #VisuDataLoadable objects.
78  *
79  * Since: 3.8
80  */
81 struct _VisuDataLoadablePrivate
82 {
83   guint nFiles;
84   guint nSets;
85   guint iSet;
86   gchar **labels;
87 
88   gboolean loading;
89   gchar *status;
90 
91   gboolean autoRefresh;
92   guint refreshPeriod, refreshId;
93   time_t *lastReadTime;
94 };
95 
96 static void visu_data_loadable_finalize(GObject* obj);
97 static void visu_data_loadable_get_property(GObject* obj, guint property_id,
98                                             GValue *value, GParamSpec *pspec);
99 static void visu_data_loadable_set_property(GObject* obj, guint property_id,
100                                             const GValue *value, GParamSpec *pspec);
101 static gboolean _reload(VisuDataLoadable *loadable);
102 
G_DEFINE_TYPE_WITH_CODE(VisuDataLoadable,visu_data_loadable,VISU_TYPE_DATA,G_ADD_PRIVATE (VisuDataLoadable))103 G_DEFINE_TYPE_WITH_CODE(VisuDataLoadable, visu_data_loadable, VISU_TYPE_DATA,
104                         G_ADD_PRIVATE(VisuDataLoadable))
105 
106 static void visu_data_loadable_class_init(VisuDataLoadableClass *klass)
107 {
108   G_OBJECT_CLASS(klass)->finalize     = visu_data_loadable_finalize;
109   G_OBJECT_CLASS(klass)->get_property = visu_data_loadable_get_property;
110   G_OBJECT_CLASS(klass)->set_property = visu_data_loadable_set_property;
111 
112   /**
113    * VisuDataLoadable::n-files:
114    *
115    * Number of input files.
116    *
117    * Since: 3.8
118    */
119   _properties[PROP_N_SOURCES] =
120     g_param_spec_uint("n-files", "N files", "number of input files",
121                       1, 10, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
122   /**
123    * VisuDataLoadable::label:
124    *
125    * A string that can be displayed representing the file names.
126    *
127    * Since: 3.8
128    */
129   _properties[PROP_LABEL] =
130     g_param_spec_string("label", "Label", "representation of the filenames",
131                         _("No input files"), G_PARAM_READABLE);
132   /**
133    * VisuDataLoadable::loading:
134    *
135    * TRUE during the loading of a file.
136    *
137    * Since: 3.8
138    */
139   _properties[PROP_LOADING] =
140     g_param_spec_boolean("loading", "Loading", "TRUE when a file is loading",
141                          FALSE, G_PARAM_READABLE);
142   /**
143    * VisuDataLoadable::status:
144    *
145    * A string describing the current status of the load process.
146    *
147    * Since: 3.8
148    */
149   _properties[PROP_STATUS] =
150     g_param_spec_string("status", "Status", "loading status", "",
151                        G_PARAM_READABLE);
152   /**
153    * VisuDataLoadable::auto-refresh:
154    *
155    * TRUE if any modification on input file should produce a refresh.
156    *
157    * Since: 3.8
158    */
159   _properties[PROP_AUTO_REFRESH] =
160     g_param_spec_boolean("auto-refresh", "Auto refresh", "automatically reload on modification",
161                          FALSE, G_PARAM_READWRITE);
162   /**
163    * VisuDataLoadable::refresh-period:
164    *
165    * Polling period for automatic reload in seconds.
166    *
167    * Since: 3.8
168    */
169   _properties[PROP_REFRESH_PERIOD] =
170     g_param_spec_uint("refresh-period", "Refresh period", "Refresh period in seconds",
171                       1, 3600, 1, G_PARAM_READWRITE);
172 
173   g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROPS, _properties);
174 }
visu_data_loadable_init(VisuDataLoadable * obj)175 static void visu_data_loadable_init(VisuDataLoadable *obj)
176 {
177   obj->priv = visu_data_loadable_get_instance_private(obj);
178   obj->priv->iSet = 0;
179   obj->priv->nSets = 0;
180   obj->priv->status = (gchar*)0;
181   obj->priv->refreshId = 0;
182   obj->priv->autoRefresh = FALSE;
183   obj->priv->refreshPeriod = 1;
184   obj->priv->lastReadTime = (time_t*)0;
185 
186   obj->priv->labels = (gchar**)0;
187   visu_data_loadable_setNSets(obj, 1);
188 }
visu_data_loadable_get_property(GObject * obj,guint property_id,GValue * value,GParamSpec * pspec)189 static void visu_data_loadable_get_property(GObject* obj, guint property_id,
190                                             GValue *value, GParamSpec *pspec)
191 {
192   VisuDataLoadable *self = VISU_DATA_LOADABLE(obj);
193 
194   switch (property_id)
195     {
196     case PROP_N_SOURCES:
197       g_value_set_uint(value, self->priv->nFiles);
198       break;
199     case PROP_LABEL:
200       g_value_set_static_string(value, _("No file"));
201       break;
202     case PROP_LOADING:
203       g_value_set_boolean(value, self->priv->loading);
204       break;
205     case PROP_STATUS:
206       if (self->priv->loading)
207         g_value_set_string(value, self->priv->status);
208       else
209         g_value_set_string(value, (const gchar*)0);
210       break;
211     case PROP_AUTO_REFRESH:
212       g_value_set_boolean(value, self->priv->autoRefresh);
213       break;
214     case PROP_REFRESH_PERIOD:
215       g_value_set_uint(value, self->priv->refreshPeriod);
216       break;
217     default:
218       /* We don't have any other property... */
219       G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
220       break;
221     }
222 }
visu_data_loadable_set_property(GObject * obj,guint property_id,const GValue * value,GParamSpec * pspec)223 static void visu_data_loadable_set_property(GObject* obj, guint property_id,
224                                             const GValue *value, GParamSpec *pspec)
225 {
226   VisuDataLoadable *self = VISU_DATA_LOADABLE(obj);
227 
228   switch (property_id)
229     {
230     case PROP_N_SOURCES:
231       self->priv->nFiles = g_value_get_uint(value);
232       self->priv->lastReadTime = g_malloc0(sizeof(time_t) * self->priv->nFiles);
233       break;
234     case PROP_AUTO_REFRESH:
235       if (g_value_get_boolean(value) == self->priv->autoRefresh)
236         return;
237       self->priv->autoRefresh = g_value_get_boolean(value);
238       if (self->priv->refreshId && !self->priv->autoRefresh)
239         {
240           g_source_remove(self->priv->refreshId);
241           self->priv->refreshId = 0;
242         }
243       if (!self->priv->refreshId && self->priv->autoRefresh)
244         self->priv->refreshId = g_timeout_add_seconds(self->priv->refreshPeriod,
245                                                       (GSourceFunc)_reload, obj);
246       break;
247     case PROP_REFRESH_PERIOD:
248       if (g_value_get_uint(value) ==  self->priv->refreshPeriod)
249         return;
250       self->priv->refreshPeriod = g_value_get_uint(value);
251       if (self->priv->refreshId)
252         {
253           g_source_remove(self->priv->refreshId);
254           self->priv->refreshId = 0;
255         }
256       if (self->priv->autoRefresh)
257         self->priv->refreshId = g_timeout_add_seconds(self->priv->refreshPeriod,
258                                                       (GSourceFunc)_reload, obj);
259       break;
260     default:
261       /* We don't have any other property... */
262       G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
263       break;
264     }
265 }
visu_data_loadable_finalize(GObject * obj)266 static void visu_data_loadable_finalize(GObject* obj)
267 {
268   VisuDataLoadable *self;
269 
270   self = VISU_DATA_LOADABLE(obj);
271 
272   g_free(self->priv->status);
273   if (self->priv->refreshId)
274     g_source_remove(self->priv->refreshId);
275   g_free(self->priv->lastReadTime);
276   g_strfreev(self->priv->labels);
277 
278   /* Chain up to the parent class */
279   G_OBJECT_CLASS(visu_data_loadable_parent_class)->finalize(obj);
280 }
281 
282 static GQuark quark = (GQuark)0;
283 /**
284  * visu_data_loadable_getErrorQuark:
285  *
286  * Internal function to handle error.
287  *
288  * Returns: a #GQuark for #VisuDataLoadable method errors.
289  */
visu_data_loadable_getErrorQuark(void)290 GQuark visu_data_loadable_getErrorQuark(void)
291 {
292   if (!quark)
293     quark = g_quark_from_static_string("visu_data_loadable");
294 
295   return quark;
296 }
297 
298 /**
299  * visu_data_loadable_getFilename:
300  * @self: a #VisuDataLoadable object.
301  * @type: when the loadable is multi-files, the type of file.
302  *
303  * Returns the source filename for @type.
304  *
305  * Since: 3.8
306  *
307  * Returns: a filename.
308  **/
visu_data_loadable_getFilename(const VisuDataLoadable * self,guint type)309 const gchar* visu_data_loadable_getFilename(const VisuDataLoadable *self, guint type)
310 {
311   g_return_val_if_fail(VISU_IS_DATA_LOADABLE(self), (const gchar*)0);
312 
313   if (VISU_DATA_LOADABLE_GET_CLASS(self)->getFilename)
314     return VISU_DATA_LOADABLE_GET_CLASS(self)->getFilename(self, type);
315   else
316     return (const gchar*)0;
317 }
318 
_reload(VisuDataLoadable * loadable)319 static gboolean _reload(VisuDataLoadable *loadable)
320 {
321   struct stat statBuf;
322   gboolean res;
323   const gchar *file;
324   guint i;
325   GError *error;
326 
327   for (i = 0; i < loadable->priv->nFiles; i++)
328     {
329       file = visu_data_loadable_getFilename(loadable, i);
330       g_return_val_if_fail(file, FALSE);
331 
332       if (!stat(file, &statBuf) && statBuf.st_ctime > loadable->priv->lastReadTime[i])
333         {
334           error = (GError*)0;
335           res = visu_data_loadable_reload(loadable, (GCancellable*)0, &error);
336           return loadable->priv->autoRefresh && res;
337         }
338     }
339   return loadable->priv->autoRefresh;
340 }
341 
342 /**
343  * visu_data_loadable_checkFile:
344  * @self: a #VisuDataLoadable object.
345  * @fileType: a file type.
346  * @error: an error location.
347  *
348  * Tests if the provided file for a given @fileType is a valid file.
349  *
350  * Since: 3.8
351  *
352  * Returns: TRUE if the file set for @fileType is a valid file.
353  **/
visu_data_loadable_checkFile(VisuDataLoadable * self,guint fileType,GError ** error)354 gboolean visu_data_loadable_checkFile(VisuDataLoadable *self,
355                                       guint fileType, GError **error)
356 {
357   const gchar *file;
358 #if GLIB_MINOR_VERSION > 5
359   struct stat buf;
360 #endif
361 
362   g_return_val_if_fail(!error || !*error, FALSE);
363 
364   file = visu_data_loadable_getFilename(self, fileType);
365   if (!file)
366     {
367       *error = g_error_new(VISU_DATA_LOADABLE_ERROR, DATA_LOADABLE_ERROR_FILE,
368                            _("No filename available."));
369       return FALSE;
370     }
371 
372   if (!g_file_test(file, G_FILE_TEST_IS_REGULAR))
373     {
374       *error = g_error_new(VISU_DATA_LOADABLE_ERROR, DATA_LOADABLE_ERROR_FILE,
375                            _("File '%s' is not a regular file or may not exist."),
376                            file);
377       return FALSE;
378     }
379 #if GLIB_MINOR_VERSION > 5
380   if (!g_stat(file, &buf) && buf.st_size == 0)
381     {
382       *error = g_error_new(VISU_DATA_LOADABLE_ERROR, DATA_LOADABLE_ERROR_FILE,
383                            _("File '%s' is empty."), file);
384       return FALSE;
385     }
386 #endif
387   return TRUE;
388 }
389 
390 #define USE_TASK 0
391 #if GLIB_MINOR_VERSION > 35 && USE_TASK == 1
_load(GTask * task _U_,VisuDataLoadable * self,gpointer data,GCancellable * cancel)392 static void _load(GTask *task _U_, VisuDataLoadable *self, gpointer data, GCancellable *cancel)
393 {
394   GError *error;
395 
396   error = (GError*)0;
397   VISU_DATA_LOADABLE_GET_CLASS(self)->load(self, GPOINTER_TO_INT(data),
398                                            cancel, &error);
399   if (error)
400     g_task_return_error(task, error);
401   else
402     g_task_return_boolean(task, TRUE);
403 }
404 #endif
405 
406 /**
407  * visu_data_loadable_load:
408  * @self: a #VisuDataLoadable object.
409  * @iSet: an integer.
410  * @cancel: (allow-none): a cancellation object.
411  * @error: an error location.
412  *
413  * Call the load class method of @self. Also store the last read time
414  * for each file of @self.
415  *
416  * Since: 3.8
417  *
418  * Returns: TRUE if @delf is successfully loaded.
419  **/
visu_data_loadable_load(VisuDataLoadable * self,guint iSet,GCancellable * cancel,GError ** error)420 gboolean visu_data_loadable_load(VisuDataLoadable *self, guint iSet,
421                                  GCancellable *cancel, GError **error)
422 {
423   gboolean res;
424   guint i;
425   const gchar *file;
426   struct stat statBuf;
427 #if GLIB_MINOR_VERSION > 35 && USE_TASK == 1
428   GTask *task;
429 #endif
430 
431   g_return_val_if_fail(VISU_IS_DATA_LOADABLE(self), FALSE);
432   g_return_val_if_fail(!self->priv->loading &&
433                        VISU_DATA_LOADABLE_GET_CLASS(self)->load, FALSE);
434   g_return_val_if_fail(!visu_node_array_getNNodes(VISU_NODE_ARRAY(self)), FALSE);
435 
436   self->priv->loading = TRUE;
437   g_object_notify_by_pspec(G_OBJECT(self), _properties[PROP_LOADING]);
438 
439 #if GLIB_MINOR_VERSION > 35 && USE_TASK == 1
440   task = g_task_new(self, cancel, NULL, NULL);
441   g_task_set_task_data(task, GINT_TO_POINTER(iSet), (GDestroyNotify)0);
442   g_task_run_in_thread_sync(task, (GTaskThreadFunc)_load);
443   res = GPOINTER_TO_INT(g_task_propagate_pointer(task, error));
444   g_object_unref(task);
445 #else
446   res = VISU_DATA_LOADABLE_GET_CLASS(self)->load(self, iSet, cancel, error);
447 #endif
448 
449   self->priv->iSet = iSet;
450   self->priv->loading = FALSE;
451   g_object_notify_by_pspec(G_OBJECT(self), _properties[PROP_LOADING]);
452 
453   if (res)
454     for (i = 0; i < self->priv->nFiles; i++)
455       {
456         file = visu_data_loadable_getFilename(self, i);
457         g_return_val_if_fail(file, res);
458 
459         if (!stat(file, &statBuf))
460           self->priv->lastReadTime[i] = statBuf.st_ctime;
461       }
462 
463   return res;
464 }
465 
466 /**
467  * visu_data_loadable_reload:
468  * @self: a #VisuDataLoadable object.
469  * @cancel: (allow-none): a cancellation object.
470  * @error: an error location.
471  *
472  * A wrapper to load again the same sub set of self.
473  *
474  * Since: 3.8
475  *
476  * Returns: TRUE if reload of @self is successful.
477  **/
visu_data_loadable_reload(VisuDataLoadable * self,GCancellable * cancel,GError ** error)478 gboolean visu_data_loadable_reload(VisuDataLoadable *self,
479                                    GCancellable *cancel, GError **error)
480 {
481   g_return_val_if_fail(VISU_IS_DATA_LOADABLE(self), FALSE);
482 
483   visu_data_freePopulation(VISU_DATA(self));
484   return visu_data_loadable_load(self, self->priv->iSet, cancel, error);
485 }
486 
487 /**
488  * visu_data_loadable_getSetId:
489  * @self: a #VisuDataLoadable object.
490  *
491  * Some loadable format can store several #VisuData (like a MD
492  * trajectory), see the @iSet attribute in visu_data_loadable_load().
493  *
494  * Since: 3.8
495  *
496  * Returns: the set id that has been loaded.
497  **/
visu_data_loadable_getSetId(const VisuDataLoadable * self)498 guint visu_data_loadable_getSetId(const VisuDataLoadable *self)
499 {
500   g_return_val_if_fail(VISU_IS_DATA_LOADABLE(self), G_MAXUINT);
501 
502   return self->priv->iSet;
503 }
504 
505 /**
506  * visu_data_loadable_setNSets:
507  * @self: a #VisuDataLoadable object.
508  * @nSets: a positive number.
509  *
510  * Setup the number of #VisuData that @self is storing on disk.
511  *
512  * Since: 3.8
513  *
514  * Returns: TRUE if the actual number of sets for @self has changed.
515  **/
visu_data_loadable_setNSets(VisuDataLoadable * self,guint nSets)516 gboolean visu_data_loadable_setNSets(VisuDataLoadable *self, guint nSets)
517 {
518   guint i;
519 
520   g_return_val_if_fail(VISU_IS_DATA_LOADABLE(self), FALSE);
521 
522   if (self->priv->nSets == nSets)
523     return FALSE;
524 
525   self->priv->nSets = nSets;
526   if (self->priv->labels)
527     g_strfreev(self->priv->labels);
528   self->priv->labels = g_malloc(sizeof(gchar*) * (nSets + 1));
529   for (i = 0; i < nSets; i++)
530     self->priv->labels[i] = g_strdup("");
531   self->priv->labels[nSets] = (gchar*)0;
532 
533   return TRUE;
534 }
535 
536 /**
537  * visu_data_loadable_getNSets:
538  * @self: a #VisuDataLoadable object.
539  *
540  * Some loadable format can store several #VisuData (like a MD
541  * trajectory), see the @iSet attribute in visu_data_loadable_load().
542  *
543  * Since: 3.8
544  *
545  * Returns: the set number of sets that has been read.
546  **/
visu_data_loadable_getNSets(const VisuDataLoadable * self)547 guint visu_data_loadable_getNSets(const VisuDataLoadable *self)
548 {
549   g_return_val_if_fail(VISU_IS_DATA_LOADABLE(self), G_MAXUINT);
550 
551   return self->priv->nSets;
552 }
553 
554 /**
555  * visu_data_loadable_getSetLabel:
556  * @self: a #VisuDataLoadable object.
557  * @iSet: an id.
558  *
559  * Retrieves a possible label for the given set. If @iSet is the
560  * currently loaded set, then this method is equivalent to calling
561  * visu_data_getDescription().
562  *
563  * Since: 3.8
564  *
565  * Returns: a label.
566  **/
visu_data_loadable_getSetLabel(const VisuDataLoadable * self,guint iSet)567 const gchar* visu_data_loadable_getSetLabel(const VisuDataLoadable *self, guint iSet)
568 {
569   g_return_val_if_fail(VISU_IS_DATA_LOADABLE(self) && iSet < self->priv->nSets,
570                        (const gchar*)0);
571 
572   if (iSet == self->priv->iSet)
573     return visu_data_getDescription(VISU_DATA(self));
574   else
575     return self->priv->labels[iSet];
576 }
577 
578 /**
579  * visu_data_loadable_setSetLabel:
580  * @self: a #VisuDataLoadable object ;
581  * @label: the message to be stored (null terminated) ;
582  * @iSet: an integer.
583  *
584  * This method is used to store a description of the given @data. Before using this
585  * method, the number of possible node sets must have been defined
586  * using visu_data_loadable_setNSets(), if not, only iSet == 0 is allowed.
587  */
visu_data_loadable_setSetLabel(VisuDataLoadable * self,const gchar * label,guint iSet)588 void visu_data_loadable_setSetLabel(VisuDataLoadable *self, const gchar* label, guint iSet)
589 {
590   g_return_if_fail(VISU_IS_DATA_LOADABLE(self) && iSet < self->priv->nSets);
591 
592   g_free(self->priv->labels[iSet]);
593   self->priv->labels[iSet] = g_strdup(label);
594   if (self->priv->iSet == iSet)
595     visu_data_setDescription(VISU_DATA(self), label);
596 }
597 
598 /**
599  * visu_data_loadable_new_fromCLI:
600  *
601  * Read the command line option and set the filenames for a new
602  * #VisuData. The object is not loaded (files are not parsed), just prepared.
603  *
604  * Returns: (transfer full): a newly allocated #VisuData if required.
605  */
visu_data_loadable_new_fromCLI(void)606 VisuDataLoadable* visu_data_loadable_new_fromCLI(void)
607 {
608   char* filename, *spin_filename;
609   VisuDataLoadable *newData;
610 
611   newData = (VisuDataLoadable*)0;
612 
613   DBG_fprintf(stderr, "Visu DataLoadable: create data from command line arguments.\n");
614   filename = commandLineGet_ArgFilename();
615   spin_filename = commandLineGet_ArgSpinFileName();
616   if (filename && !spin_filename)
617     newData = VISU_DATA_LOADABLE(visu_data_atomic_new(filename, (VisuDataLoader*)0));
618   else if(filename && spin_filename)
619     newData = VISU_DATA_LOADABLE(visu_data_spin_new(filename, spin_filename,
620                                                     (VisuDataLoader*)0,
621                                                     (VisuDataLoader*)0));
622   return newData;
623 }
624