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