1 /*
2  * Gnome Chemisty Utils
3  * spectrumdoc.cc
4  *
5  * Copyright (C) 2007-2011 Jean Bréfort <jean.brefort@normalesup.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22 
23 #include "config.h"
24 #include "application.h"
25 #include "spectrumdoc.h"
26 #include "spectrumview.h"
27 #include <gcu/objprops.h>
28 #include <glib/gi18n-lib.h>
29 #include <cstring>
30 #include <sstream>
31 #include <iostream>
32 
33 #ifndef HAVE_EXP10
34 #	define exp10(x) pow(10.,(x))
35 #endif
36 
37 using namespace std;
38 
39 namespace gcugtk
40 {
41 
SpectrumDocument()42 SpectrumDocument::SpectrumDocument ():
43 	gcu::Document (NULL),
44 	Printable (),
45 	m_XAxisInvertBtn (NULL),
46 	m_Empty (true)
47 {
48 	m_View = new SpectrumView (this);
49 	x = y = NULL;
50 	X = Y = R = I = integral = Rt = It = Rp = -1;
51 	npoints = 0;
52 	maxx = maxy = minx = miny = go_nan;
53 	firstx = lastx = deltax = firsty = go_nan;
54 	freq = offset = refpoint = go_nan;
55 	gtk_page_setup_set_orientation (GetPageSetup (), GTK_PAGE_ORIENTATION_LANDSCAPE);
56 	SetScaleType (GCU_PRINT_SCALE_AUTO);
57 	SetHorizFit (true);
58 	SetVertFit (true);
59 	m_IntegralVisible = false;
60 }
61 
SpectrumDocument(Application * App,SpectrumView * View)62 SpectrumDocument::SpectrumDocument (Application *App, SpectrumView *View):
63 	Document (App),
64 	Printable (),
65 	m_XAxisInvertBtn (NULL),
66 	m_Empty (true)
67 {
68 	m_View = (View)? View: new SpectrumView (this);
69 	x = y = NULL;
70 	X = Xt = Y = R = I = integral = Rt = It = Rp = -1;
71 	npoints = 0;
72 	maxx = maxy = minx = miny = go_nan;
73 	firstx = lastx = deltax = firsty = go_nan;
74 	freq = offset = refpoint = go_nan;
75 	gtk_page_setup_set_orientation (GetPageSetup (), GTK_PAGE_ORIENTATION_LANDSCAPE);
76 	SetScaleType (GCU_PRINT_SCALE_AUTO);
77 	SetHorizFit (true);
78 	SetVertFit (true);
79 	m_IntegralVisible = false;
80 }
81 
~SpectrumDocument()82 SpectrumDocument::~SpectrumDocument ()
83 {
84 	if (x && X < 0)
85 		delete[] x;
86 	if (y && Y < 0)
87 		delete[] y;
88 	for (unsigned i = 0; i < variables.size (); i++)
89 		if (variables[i].Values)
90 			delete [] variables[i].Values;
91 	if (m_View)
92 		delete m_View;
93 }
94 
Load(char const * uri,char const * mime_type)95 void SpectrumDocument::Load (char const *uri, char const *mime_type)
96 {
97 	// Only supporting JCAMP-DX at the moment
98 	if (!mime_type || strcmp (mime_type, "chemical/x-jcamp-dx")) {
99 		return;
100 	}
101 	GVfs *vfs = g_vfs_get_default ();
102 	GFile *file = g_vfs_get_file_for_uri (vfs, uri);
103 	GError *error = NULL;
104 	GFileInfo *info = g_file_query_info (file,
105 										 G_FILE_ATTRIBUTE_STANDARD_SIZE,
106 										 G_FILE_QUERY_INFO_NONE,
107 										 NULL, &error);
108 	if (error) {
109 		g_message ("GIO querry failed: %s", error->message);
110 		g_error_free (error);
111 		g_object_unref (file);
112 		return;
113 	}
114 	gsize size = g_file_info_get_size (info);
115 	g_object_unref (info);
116 	GInputStream *input = G_INPUT_STREAM (g_file_read (file, NULL, &error));
117 	if (error) {
118 		g_message ("GIO could not create the stream: %s", error->message);
119 		g_error_free (error);
120 		g_object_unref (file);
121 		return;
122 	}
123 	gchar *buf = new gchar[size + 1];
124 	gsize n = size;
125 	while (n) {
126 		n -= g_input_stream_read (input, buf, size, NULL, &error);
127 		if (error) {
128 			g_message ("GIO could not read the file: %s", error->message);
129 			g_error_free (error);
130 			delete [] buf;
131 			g_object_unref (input);
132 			g_object_unref (file);
133 			return;
134 		}
135 	}
136 	buf[size] = 0;
137 	LoadJcampDx (buf);
138 	if (m_App) {
139 		char *dirname = g_path_get_dirname (uri);
140 		m_App->SetCurDir (dirname);
141 		g_free (dirname);
142 	}
143 	delete [] buf;
144 	g_object_unref (file);
145 
146 }
147 
148 struct data_type_struct {
149 	int n; // number of variables
150 	bool comma_sep; // whether the separator is a comma
151 	bool one_per_line; // whether each line has only one data set
152 	bool explicit_indep; // whether the independent variable is explicit
153 	char *variables; // each letter is a variable symbol, must be freed
154 };
155 
parse_data_type(char const * type,struct data_type_struct & s)156 static void parse_data_type (char const *type, struct data_type_struct &s)
157 {
158 	// initialize s
159 	s.variables = NULL; // if still NULL when returning, an error occurred
160 	s.n = 0;
161 	s.one_per_line = false;
162 	s.comma_sep = false;
163 	s.explicit_indep = true;
164 	string variables;
165 	if (*type != '(')
166 		return;
167 	type++;
168 	if (*type < 'A' || *type > 'Z')
169 		return;
170 	variables += *type; //first variable symbol
171 	type++;
172 	switch (*type) {
173 	case ',':
174 		s.comma_sep = true;
175 		type++;
176 		break;
177 	case '+':
178 		if (type[1] != '+')
179 			return;
180 		s.explicit_indep = false;
181 		type += 2;
182 		while (*type == '(' || *type == ')')
183 			type++;
184 		break;
185 	default:
186 		break;
187 	}
188 	// at this point *type should be the second variable symbol
189 	if (*type < 'A' || *type > 'Z')
190 		return;
191 	variables += *type; //second variable symbol
192 	type++;
193 	// add additional variables if any
194 	while (*type == ',' || (*type >= 'A' && *type <= 'Z')) {
195 		if (*type != ',')
196 			variables += *type;
197 		type++;
198 	}
199 	while (*type == ' ')
200 		type++;
201 	s.one_per_line = !(*type == '.');
202 	s.n = variables.length ();
203 	if (s.n > 0)
204 		s.variables = g_strdup (variables.c_str ());
205 }
206 
207 static struct {char const *name; SpectrumType type;} Types[] = {
208 	{"INFRARED SPECTRUM", GCU_SPECTRUM_INFRARED},
209 	{"RAMAN SPECTRUM", GCU_SPECTRUM_RAMAN},
210 	{"INFRARED PEAK TABLE", GCU_SPECTRUM_INFRARED_PEAK_TABLE},
211 	{"INFRARED INTERFEROGRAM", GCU_SPECTRUM_INFRARED_INTERFEROGRAM},
212 	{"INFRARED TRANSFORMED SPECTRUM", GCU_SPECTRUM_INFRARED_TRANSFORMED},
213 	{"UV-VISIBLE SPECTRUM", GCU_SPECTRUM_UV_VISIBLE},
214 	{"NMR SPECTRUM", GCU_SPECTRUM_NMR},
215 	{"NMR FID", GCU_SPECTRUM_NMR_FID},
216 	{"NMR PEAK TABLE", GCU_SPECTRUM_NMR_PEAK_TABLE},
217 	{"NMR PEAK ASSIGNMENTS", GCU_SPECTRUM_NMR_PEAK_ASSIGNMENTS},
218 	{"MASS SPECTRUM", GCU_SPECTRUM_MASS},
219 	{"UV-VIS SPECTRUM", GCU_SPECTRUM_UV_VISIBLE},
220 	{"UV/VISIBLE SPECTRUM", GCU_SPECTRUM_UV_VISIBLE},
221 	{"UV/VIS SPECTRUM", GCU_SPECTRUM_UV_VISIBLE}
222 };
223 
224 char const *Units[] = {
225 	"1/CM",
226 	"TRANSMITTANCE",
227 	"ABSORBANCE",
228 	"PPM",
229 	"NANOMETERS",
230 	"MICROMETERS",
231 	"SECONDS",
232 	"HZ",
233 	"M/Z",
234 	"RELATIVE ABUNDANCE",
235 	"RF"
236 };
237 
238 static struct {char const *name; SpectrumUnitType unit;} OtherUnits[] = {
239 	{"NM", GCU_SPECTRUM_UNIT_NANOMETERS}
240 };
241 
242 char const *UnitNames[] = {
243 	N_("Wavenumber (cm<sup>−1</sup>)"),
244 	N_("Transmittance"),
245 	N_("Absorbance"),
246 	N_("Chemical shift (ppm)"),
247 	N_("Wavelength (nm)"),
248 	N_("Wavelength (µm)"),
249 	N_("Time (s)"),
250 	N_("Frequency (Hz)"),
251 	N_("Mass/charge ratio"),
252 	N_("Relative abundance"),
253 	N_("Response factor")
254 };
255 
256 char const *VarTypes[] = {
257 	"INDEPENDENT",
258 	"DEPENDENT",
259 	"PAGE"
260 };
261 
262 char const *Formats[] = {
263 	"ASDF",
264 	"AFFN",
265 	"PAC",
266 	"SQZ",
267 	"DIF"
268 };
269 
get_spectrum_data_from_string(char const * type,char const * names[],int max)270 int get_spectrum_data_from_string (char const *type, char const *names[], int max)
271 {
272 	int res = 0;
273 	char *up = g_ascii_strup (type, -1);
274 	while (res < max) {
275 		if (!strncmp (up, names[res], strlen (names[res]))) {
276 			g_free (up);
277 			return res;
278 		}
279 		res++;
280 	}
281 	g_free (up);
282 	return res;
283 }
284 
285 char const *Keys[] = {
286 	"TITLE",
287 	"JCAMPDX",
288 	"DATATYPE",
289 	"DATACLASS",
290 	"APPLICATION",
291 	"DICTIONARY",
292 	"BLOCKS",
293 	"BLOCKID",
294 	"END",
295 	"XYDATA",
296 	"XYPOINTS",
297 	"XYPAIRS",
298 	"PEAKTABLE",
299 	"PEAKASSIGNMENTS",
300 	"RADATA",
301 	"XUNITS",
302 	"YUNITS",
303 	"XLABEL",
304 	"YLABEL",
305 	"XFACTOR",
306 	"YFACTOR",
307 	"FIRSTX",
308 	"LASTX",
309 	"NPOINTS",
310 	"FIRSTY",
311 	"MAXX",
312 	"MINX",
313 	"MAXY",
314 	"MINY",
315 	"RUNITS",
316 	"AUNITS",
317 	"FIRSTR",
318 	"LASTR",
319 	"MAXA",
320 	"MINA",
321 	"RFACTOR",
322 	"AFACTOR",
323 	"FIRSTA",
324 	"ALIAS",
325 	"ZPD",
326 	"NTUPLES",
327 	"VARNAME",
328 	"SYMBOL",
329 	"VARTYPE",
330 	"VARFORM",
331 	"VARDIM",
332 	"UNITS",
333 	"FIRST",
334 	"LAST",
335 	"MIN",
336 	"MAX",
337 	"FACTOR",
338 	"PAGE",
339 	"DATATABLE",
340 	"DELTAX",
341 	"CLASS",
342 	"ORIGIN",
343 	"OWNER",
344 	"DATE",
345 	"TIME",
346 	"SOURCEREFERENCE",
347 	"CROSSREFERENCE",
348 	"SPECTROMETER",
349 	"DATASYSTEM",
350 	"INSTRUMENTPARAMETERS",
351 	"SAMPLEDESCRIPTION",
352 	"CASNAME",
353 	"NAMES",
354 	"MOLEFORM",
355 	"CASREGISTRYNO",
356 	"WISWESSER",
357 	"BEILSTEINLAWSONNO",
358 	"MP",
359 	"BP",
360 	"REFRACTIVEINDEX",
361 	"DENSITY",
362 	"MW",
363 	"CONCENTRATIONS",
364 	"SAMPLINGPROCEDURE",
365 	"STATE",
366 	"PATHLENGTH",
367 	"PRESSURE",
368 	"TEMPERATURE",
369 	"DATAPROCESSING",
370 	"SPECTROMETERTYPE",
371 	"INLET",
372 	"IONIZATIONMODE",
373 	"INLETTEMPERATURE",
374 	"SOURCETEMPERATURE",
375 	"IONIZATIONENERGY",
376 	"ACCELERATINGVOLTAGE",
377 	"TOTALIONCURRENT",
378 	"ACQUISITIONRANGE",
379 	"DETECTOR",
380 	"SCANNUMBER",
381 	"RETENTIONTIME",
382 	"BASEPEAK",
383 	"BASEPEAKINTENSITY",
384 	"RIC",
385 	"NOMINALMASS",
386 	"MONOISOTOPICMASS",
387 	"OBSERVEFREQUENCY",
388 	"OBSERVENUCLEUS",
389 	"SOLVENTREFERENCE",
390 	"DELAY",
391 	"ACQUISITIONMODE",
392 	"FIELD",
393 	"DECOUPLER",
394 	"FILTERWIDTH",
395 	"ACQUISITIONTIME",
396 	"ZEROFILL",
397 	"AVERAGES",
398 	"DIGITIZERRES",
399 	"SPINNINGRATE",
400 	"PHASE0",
401 	"PHASE1",
402 	"MININTENSITY",
403 	"MAXINTENSITY",
404 	"OBSERVE90",
405 	"COUPLINGCONSTANTS",
406 	"RELAXATIONTIMES",
407 /* Brücker specific data */
408 	"$OFFSET",
409 	"$REFERENCEPOINT",
410 	NULL
411 };
412 
413 enum {
414 	JCAMP_INVALID = -10,
415 	JCAMP_UNKNOWN,
416 	JCAMP_CONTINUE,
417 	JCAMP_TITLE = 0,
418 	JCAMP_JCAMP_DX,
419 	JCAMP_DATA_TYPE,
420 	JCAMP_DATACLASS,
421 	JCAMP_APPLICATION,
422 	JCAMP_DICTIONARY,
423 	JCAMP_BLOCKS,
424 	JCAMP_BLOCK_ID,
425 	JCAMP_END,
426 	JCAMP_XYDATA,
427 	JCAMP_XYPOINTS,
428 	JCAMP_XYPAIRS,
429 	JCAMP_PEAK_TABLE,
430 	JCAMP_PEAK_ASSIGNMENTS,
431 	JCAMP_RADATA,
432 	JCAMP_XUNITS,
433 	JCAMP_YUNITS,
434 	JCAMP_XLABEL,
435 	JCAMP_YLABEL,
436 	JCAMP_XFACTOR,
437 	JCAMP_YFACTOR,
438 	JCAMP_FIRSTX,
439 	JCAMP_LASTX,
440 	JCAMP_NPOINTS,
441 	JCAMP_FIRSTY,
442 	JCAMP_MAXX,
443 	JCAMP_MINX,
444 	JCAMP_MAXY,
445 	JCAMP_MINY,
446 	JCAMP_RUNITS,
447 	JCAMP_AUNITS,
448 	JCAMP_FIRSTR,
449 	JCAMP_LASTR,
450 	JCAMP_MAXA,
451 	JCAMP_MINA,
452 	JCAMP_RFACTOR,
453 	JCAMP_AFACTOR,
454 	JCAMP_FIRSTA,
455 	JCAMP_ALIAS,
456 	JCAMP_ZPD,
457 	JCAMP_NTUPLES,
458 	JCAMP_VAR_NAME,
459 	JCAMP_SYMBOL,
460 	JCAMP_VAR_TYPE,
461 	JCAMP_VAR_FORM,
462 	JCAMP_VAR_DIM,
463 	JCAMP_UNITS,
464 	JCAMP_FIRST,
465 	JCAMP_LAST,
466 	JCAMP_MIN,
467 	JCAMP_MAX,
468 	JCAMP_FACTOR,
469 	JCAMP_PAGE,
470 	JCAMP_DATA_TABLE,
471 	JCAMP_DELTAX,
472 	JCAMP_CLASS,
473 	JCAMP_ORIGIN,
474 	JCAMP_OWNER,
475 	JCAMP_DATE,
476 	JCAMP_TIME,
477 	JCAMP_SOURCE_REFERENCE,
478 	JCAMP_CROSS_REFERENCE,
479 	JCAMP_SPECTROMETER,
480 	JCAMP_DATASYSTEM,
481 	JCAMP_INSTRUMENT_PARAMETERS,
482 	JCAMP_SAMPLE_DESCRIPTION,
483 	JCAMP_CAS_NAME,
484 	JCAMP_NAMES,
485 	JCAMP_MOLEFORM,
486 	JCAMP_CAS_REGISTRY_NO,
487 	JCAMP_WISWESSER,
488 	JCAMP_BEILSTEIN_LAWSON_NO,
489 	JCAMP_MP,
490 	JCAMP_BP,
491 	JCAMP_REFRACTIVE_INDEX,
492 	JCAMP_DENSITY,
493 	JCAMP_MW,
494 	JCAMP_CONCENTRATIONS,
495 	JCAMP_SAMPLING_PROCEDURE,
496 	JCAMP_STATE,
497 	JCAMP_PATH_LENGTH,
498 	JCAMP_PRESSURE,
499 	JCAMP_TEMPERATURE,
500 	JCAMP_DATA_PROCESSING,
501 	JCAMP_SPECTROMETER_TYPE,
502 	JCAMP_INLET,
503 	JCAMP_IONIZATION_MODE,
504 	JCAMP_INLET_TEMPERATURE,
505 	JCAMP_SOURCE_TEMPERATURE,
506 	JCAMP_IONIZATION_ENERGY,
507 	JCAMP_ACCELERATING_VOLTAGE,
508 	JCAMP_TOTAL_ION_CURRENT,
509 	JCAMP_ACQUISITION_RANGE,
510 	JCAMP_DETECTOR,
511 	JCAMP_SCAN_NUMBER,
512 	JCAMP_RETENTION_TIME,
513 	JCAMP_BASE_PEAK,
514 	JCAMP_BASE_PEAK_INTENSITY,
515 	JCAMP_RIC,
516 	JCAMP_NOMINAL_MASS,
517 	JCAMP_MONOISOTOPIC_MASS,
518 	JCAMP_OBSERVE_FREQUENCY,
519 	JCAMP_OBSERVE_NUCLEUS,
520 	JCAMP_SOLVENT_REFERENCE,
521 	JCAMP_DELAY,
522 	JCAMP_ACQUISITION_MODE,
523 	JCAMP_FIELD,
524 	JCAMP_DECOUPLER,
525 	JCAMP_FILTER_WIDTH,
526 	JCAMP_ACQUISITION_TIME,
527 	JCAMP_ZERO_FILL,
528 	JCAMP_AVERAGES,
529 	JCAMP_DIGITIZER_RES,
530 	JCAMP_SPINNING_RATE,
531 	JCAMP_PHASE_0,
532 	JCAMP_PHASE_1,
533 	JCAMP_MIN_INTENSITY,
534 	JCAMP_MAX_INTENSITY,
535 	JCAMP_OBSERVE_90,
536 	JCAMP_COUPLING_CONSTANTS,
537 	JCAMP_RELAXATION_TIMES,
538 	BRUCKER_OFFSET,
539 	VARIAN_OFFSET,
540 	JCAMP_MAX_VALID
541 };
542 
543 #define KEY_LENGTH 80
544 #define VALUE_LENGTH 128
ReadField(char const * s,char * key,char * buf)545 static int ReadField (char const *s, char *key, char *buf)
546 {
547 	char const *data = s, *eq;
548 	int i = 0;
549 	while (*data != 0 && *data < '#')
550 		data++;
551 	if (*data == 0)
552 		return JCAMP_INVALID;
553 	if (!strncmp (data, "$$", 2)) {
554 		// This is a comment, just skip
555 		return JCAMP_INVALID;
556 	}
557 /*	if (!strncmp (data, "##$", 3)) {
558 		// This is a vendor specific tag, just skip for now
559 		return JCAMP_INVALID;
560 	}*/
561 	if (!strncmp (data, "##", 2)) {
562 		// found a field
563 		data += 2;
564 		eq = strchr (data, '=');
565 		i = 0;
566 		if (*data == '$' && strncmp (data, "$$", 2)) {
567 			key[i++] = '$';
568 			data++;
569 		}
570 		while (data < eq) {
571 			if (*data >= 'A' && *data <= 'Z')
572 				key[i++] = *data;
573 			else if (*data >= 'a' && *data <= 'z')
574 				key[i++] = *data - 0x20;
575 			data++;
576 		}
577 		key[i] = 0;
578 		if (i == 0)
579 			// This is a comment, just skip
580 			return JCAMP_INVALID;
581 		data = eq + 1;
582 		while (*data != 0 && *data < '$')
583 			data++;
584 		eq = strstr (data, "$$");
585 		if (eq && *eq) {
586 			strncpy (buf, data, MIN (eq - data, VALUE_LENGTH));
587 			buf[MIN (eq - data, VALUE_LENGTH - 1)] = 0;
588 		}
589 		else {
590 			strncpy (buf, data, VALUE_LENGTH);
591 			buf[VALUE_LENGTH - 1] = 0;
592 		}
593 		// strip trailing white spaces:
594 		i = strlen (buf) - 1;
595 		if (i < 0)
596 			return JCAMP_UNKNOWN;
597 		while (buf[i] <= ' ' || buf[i] == '"')
598 			buf[i--] = 0;
599 
600 		// Now, get the key value
601 		i = JCAMP_TITLE;
602 		while (i < JCAMP_MAX_VALID && strcmp (key, Keys[i]))
603 			i++;
604 		return i;
605 	}
606 	return JCAMP_UNKNOWN;
607 }
608 
on_xunit_changed(GtkComboBox * box,SpectrumDocument * doc)609 static void on_xunit_changed (GtkComboBox *box, SpectrumDocument *doc)
610 {
611 	doc->OnXUnitChanged (gtk_combo_box_get_active (box));
612 }
613 
on_yunit_changed(GtkComboBox * box,SpectrumDocument * doc)614 static void on_yunit_changed (GtkComboBox *box, SpectrumDocument *doc)
615 {
616 	doc->OnYUnitChanged (gtk_combo_box_get_active (box));
617 }
618 
on_xaxis_invert(GtkToggleButton * btn,SpectrumDocument * doc)619 static void on_xaxis_invert (GtkToggleButton *btn, SpectrumDocument *doc)
620 {
621 	doc->OnXAxisInvert (gtk_toggle_button_get_active (btn));
622 }
623 
on_show_integral(GtkButton * btn,SpectrumDocument * doc)624 static void on_show_integral (GtkButton *btn, SpectrumDocument *doc)
625 {
626 	gtk_button_set_label (btn, (doc->GetIntegralVisible ()?
627 								_("Show integral"): _("Hide integral")));
628 	doc->OnShowIntegral ();
629 }
630 
631 
on_transform_fid(GtkButton * btn,SpectrumDocument * doc)632 static void on_transform_fid (GtkButton *btn, SpectrumDocument *doc)
633 {
634 	doc->OnTransformFID (btn);
635 }
636 
get_spectrum_type_from_string(char const * buf)637 static SpectrumType get_spectrum_type_from_string (char const *buf)
638 {
639 	unsigned i;
640 	char *up = g_ascii_strup (buf, -1);
641 	for (i = 0; i < G_N_ELEMENTS (Types); i++)
642 		if (!strcmp (Types[i].name, up)) {
643 			g_free (up);
644 			return Types[i].type;
645 		}
646 	g_free (up);
647 	return GCU_SPECTRUM_MAX;
648 }
649 
650 #define JCAMP_PREC 1e-2 // fully arbitrary
651 
LoadJcampDx(char const * data)652 void SpectrumDocument::LoadJcampDx (char const *data)
653 {
654 	char key[KEY_LENGTH];
655 	char buf[VALUE_LENGTH];
656 	char line[300]; // should be enough
657 	int n;
658 	deltax = 0.;
659 	istringstream s(data);
660 	JdxVar var;
661 	var.NbValues = 0;
662 	var.Symbol = 0;
663 	var.Type = GCU_SPECTRUM_TYPE_MAX;
664 	var.Unit = GCU_SPECTRUM_UNIT_MAX;
665 	var.Format = GCU_SPECTRUM_FORMAT_MAX;
666 	var.First = var.Last = var.Min = var.Max = var.Factor = 0.;
667 	var.Values = NULL;
668 	GString *utf8_str = NULL;
669 	while (!s.eof ()) {
670 		s.getline (line, 300);
671 parse_line:
672 		n = ReadField (line, key, buf);
673 		switch (n) {
674 		case JCAMP_TITLE:
675 			go_guess_encoding (buf, strlen (buf), "ASCII", &utf8_str, NULL);
676 			if (utf8_str) {
677 				SetTitle (utf8_str->str);
678 				g_string_free (utf8_str, TRUE);
679 				utf8_str = NULL;
680 			} else
681 				SetTitle (buf);
682 			break;
683 		case JCAMP_JCAMP_DX:
684 			break;
685 		case JCAMP_DATA_TYPE:
686 			m_SpectrumType = get_spectrum_type_from_string (buf);
687 			break;
688 		case JCAMP_DATACLASS:
689 		case JCAMP_APPLICATION:
690 		case JCAMP_DICTIONARY:
691 		case JCAMP_BLOCKS:
692 		case JCAMP_BLOCK_ID:
693 			break;
694 		case JCAMP_END:
695 			goto out;
696 		case JCAMP_PEAK_TABLE: {
697 			// in that case, add drop lines ans remove the normal line
698 			GogSeries *series = m_View->GetSeries ();
699 			gog_object_add_by_name (GOG_OBJECT (series), "Vertical drop lines", GOG_OBJECT (g_object_new (GOG_TYPE_SERIES_LINES, NULL)));
700 			GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (series));
701 			style->line.dash_type = GO_LINE_NONE;
702 			style->line.auto_dash = false;
703 		}
704 		case JCAMP_XYPAIRS:
705 		case JCAMP_XYDATA:
706 		case JCAMP_XYPOINTS: {
707 			unsigned read = 0;
708 			list<double> l;
709 			if (deltax == 0.)
710 				deltax = (lastx - firstx) / (npoints - 1);
711 			else {
712 				if (firstx > lastx && deltax > 0)
713 					deltax = -deltax;
714 				unsigned n = (unsigned) ((lastx - firstx) / deltax + .1) + 1;
715 				if (n && n != npoints) {
716 					if (x)
717 						delete[] x;
718 					x = new double[n];
719 					if (y)
720 						delete[] y;
721 					y = new double[n];
722 				}
723 				npoints = n;
724 			}
725 			// FIXME: we should implement a real parser for this value
726 			if (!strncmp (buf, "(X++(Y..Y))",strlen ("(X++(Y..Y))")))
727 				ReadDataTable (s, x, y);
728 			else if (!strncmp (buf, "(XY..XY)",strlen ("(XY..XY)"))) {
729 				char *cur;
730 				while (1) {
731 					if (s.eof ())
732 						break;	// this should not occur, but a corrupted or bad file is always possible
733 					s.getline (line, 300);
734 					if (strstr (line, "##")) {
735 						if (read > npoints) {
736 							g_warning (_("Found too many data!"));
737 							// FIXME: throw an exception
738 						} else
739 							npoints = read;
740 						if (!go_finite (minx))
741 							go_range_min (x, read, &minx);
742 						if (!go_finite (maxx))
743 							go_range_max (x, read, &maxx);
744 						if (!go_finite (miny))
745 							go_range_min (y, read, &miny);
746 						if (!go_finite (maxy))
747 							go_range_max (y, read, &maxy);
748 						goto parse_line;
749 					}
750 					cur = line;
751 					while (*cur) {
752 						while (*cur && (*cur < '0' || *cur > '9') && *cur != '-' && *cur !='+')
753 							cur++;
754 						if (*cur == 0)
755 							break;
756 						x[read] = g_ascii_strtod (cur, &cur);
757 						while (*cur && (*cur < '0' || *cur > '9') && *cur != '-' && *cur !='+')
758 							cur++;
759 						y[read] = g_ascii_strtod (cur, &cur);
760 						read++;
761 					}
762 				}
763 			}
764 			break;
765 		}
766 		case JCAMP_PEAK_ASSIGNMENTS:
767 		case JCAMP_RADATA:
768 			break;
769 		case JCAMP_XUNITS:
770 			m_XUnit = (SpectrumUnitType) get_spectrum_data_from_string (buf, Units, GCU_SPECTRUM_UNIT_MAX);
771 			if (m_XUnit == GCU_SPECTRUM_UNIT_MAX) {
772 				for (unsigned i = 0; i < G_N_ELEMENTS (OtherUnits); i++)
773 					if (!strcmp (buf, OtherUnits[i].name)) {
774 						m_XUnit = OtherUnits[i].unit;
775 						break;
776 					}
777 			}
778 			break;
779 		case JCAMP_YUNITS:
780 			m_YUnit = (SpectrumUnitType) get_spectrum_data_from_string (buf, Units, GCU_SPECTRUM_UNIT_MAX);
781 			if (m_YUnit == GCU_SPECTRUM_UNIT_MAX) {
782 				for (unsigned i = 0; i < G_N_ELEMENTS (OtherUnits); i++)
783 					if (!strcmp (buf, OtherUnits[i].name)) {
784 						m_YUnit = OtherUnits[i].unit;
785 						break;
786 					}
787 			}
788 			if (m_YUnit == GCU_SPECTRUM_UNIT_TRANSMITTANCE)
789 				m_View->SetAxisBounds (GOG_AXIS_Y, 0., 1., false);
790 			break;
791 		case JCAMP_XLABEL:
792 		case JCAMP_YLABEL:
793 			break;
794 		case JCAMP_XFACTOR:
795 			xfactor = g_ascii_strtod (buf, NULL);
796 			break;
797 		case JCAMP_YFACTOR:
798 			yfactor = g_ascii_strtod (buf, NULL);
799 			break;
800 		case JCAMP_FIRSTX:
801 			firstx = g_ascii_strtod (buf, NULL);
802 			break;
803 		case JCAMP_LASTX:
804 			lastx = g_ascii_strtod (buf, NULL);
805 			break;
806 		case JCAMP_NPOINTS: {
807 			unsigned n = (unsigned) atoi (buf);
808 			if (n && n != npoints) {
809 				if (x)
810 					delete[] x;
811 				x = new double[n];
812 				if (y)
813 					delete[] y;
814 				y = new double[n];
815 			}
816 			npoints = n;
817 			break;
818 		}
819 		case JCAMP_FIRSTY:
820 			firsty = g_ascii_strtod (buf, NULL);
821 			break;
822 		case JCAMP_MAXX:
823 			maxx = g_ascii_strtod (buf, NULL);
824 			break;
825 		case JCAMP_MINX:
826 			minx = g_ascii_strtod (buf, NULL);
827 			break;
828 		case JCAMP_MAXY:
829 			maxy = g_ascii_strtod (buf, NULL);
830 			break;
831 		case JCAMP_MINY:
832 			miny = g_ascii_strtod (buf, NULL);
833 			break;
834 		case JCAMP_RUNITS:
835 		case JCAMP_AUNITS:
836 		case JCAMP_FIRSTR:
837 		case JCAMP_LASTR:
838 		case JCAMP_MAXA:
839 		case JCAMP_MINA:
840 		case JCAMP_RFACTOR:
841 		case JCAMP_AFACTOR:
842 		case JCAMP_FIRSTA:
843 		case JCAMP_ALIAS:
844 		case JCAMP_ZPD:
845 		case JCAMP_NTUPLES:
846 			break;
847 		case JCAMP_VAR_NAME: {
848 			size_t i = 0;
849 			char *cur = buf, *end;
850 			while (*cur) {
851 				while (*cur && *cur == ' ')
852 					cur++;
853 				if (!*cur)
854 					break;
855 				end = strchr (cur, ',');
856 				if (end)
857 					*end = 0;
858 				if (i < variables.size ())
859 					variables[i].Name = cur;
860 				else {
861 					var.Name = cur;
862 					variables.push_back (var);
863 					var.Name.clear ();
864 				}
865 				cur = (end)? end + 1: cur + strlen (cur);
866 				i++;
867 			}
868 			break;
869 		}
870 		case JCAMP_SYMBOL: {
871 			size_t i = 0;
872 			char *cur = buf, *end;
873 			while (*cur) {
874 				while (*cur && *cur == ' ')
875 					cur++;
876 				if (!*cur)
877 					break;
878 				end = strchr (cur, ',');
879 				if (end)
880 					*end = 0;
881 				if (i < variables.size ())
882 					variables[i].Symbol = *cur;
883 				else {
884 					var.Symbol = *cur;
885 					variables.push_back (var);
886 					var.Symbol = 0;
887 				}
888 				switch (*cur) {
889 				case 'I':
890 					I = i;
891 					break;
892 				case 'R':
893 					R = i;
894 					break;
895 				case 'X':
896 					X = i;
897 					break;
898 				case 'Y':
899 					Y = i;
900 					break;
901 				default:
902 					break;
903 				}
904 				cur = (end)? end + 1: cur + strlen (cur);
905 				i++;
906 			}
907 			break;
908 		}
909 		case JCAMP_VAR_TYPE: {
910 			size_t i = 0;
911 			char *cur = buf, *end;
912 			SpectrumVarType Type;
913 			while (*cur) {
914 				while (*cur && *cur == ' ')
915 					cur++;
916 				if (!*cur)
917 					break;
918 				end = strchr (cur, ',');
919 				if (end)
920 					*end = 0;
921 				Type = (SpectrumVarType) get_spectrum_data_from_string (cur, VarTypes, GCU_SPECTRUM_TYPE_MAX);
922 				if (i < variables.size ())
923 					variables[i].Type = Type;
924 				else {
925 					var.Type = Type;
926 					variables.push_back (var);
927 					var.Type = GCU_SPECTRUM_TYPE_MAX;
928 				}
929 				cur = (end)? end + 1: cur + strlen (cur);
930 				i++;
931 			}
932 			break;
933 			break;
934 		}
935 		case JCAMP_VAR_FORM: {
936 			size_t i = 0;
937 			char *cur = buf, *end;
938 			SpectrumFormatType Format;
939 			while (*cur) {
940 				while (*cur && *cur == ' ')
941 					cur++;
942 				if (!*cur)
943 					break;
944 				end = strchr (cur, ',');
945 				if (end)
946 					*end = 0;
947 				Format = (SpectrumFormatType) get_spectrum_data_from_string (cur, Formats, GCU_SPECTRUM_FORMAT_MAX);
948 				if (i < variables.size ())
949 					variables[i].Format = Format;
950 				else {
951 					var.Format = Format;
952 					variables.push_back (var);
953 					var.Format = GCU_SPECTRUM_FORMAT_MAX;
954 				}
955 				cur = (end)? end + 1: cur + strlen (cur);
956 				i++;
957 			}
958 			break;
959 		}
960 		case JCAMP_VAR_DIM: {
961 			size_t i = 0;
962 			char *cur = buf;
963 			unsigned dim;
964 			while (*cur) {
965 				dim = strtoul (cur, &cur, 10);
966 				if (*cur)
967 					cur++;
968 				if (i < variables.size ())
969 					variables[i].NbValues = dim;
970 				else {
971 					var.NbValues = dim;
972 					variables.push_back (var);
973 					var.NbValues = 0;
974 				}
975 				i++;
976 			}
977 			break;
978 		}
979 		case JCAMP_UNITS: {
980 			size_t i = 0;
981 			char *cur = buf, *end;
982 			SpectrumUnitType Unit;
983 			while (*cur) {
984 				while (*cur && *cur == ' ')
985 					cur++;
986 				if (!*cur)
987 					break;
988 				end = strchr (cur, ',');
989 				if (end)
990 					*end = 0;
991 				Unit = (SpectrumUnitType) get_spectrum_data_from_string (cur, Units, GCU_SPECTRUM_UNIT_MAX);
992 				if (i < variables.size ())
993 					variables[i].Unit = Unit;
994 				else {
995 					var.Unit = Unit;
996 					variables.push_back (var);
997 					var.Unit = GCU_SPECTRUM_UNIT_MAX;
998 				}
999 				cur = (end)? end + 1: cur + strlen (cur);
1000 				i++;
1001 			}
1002 			break;
1003 		}
1004 		case JCAMP_FIRST: {
1005 			size_t i = 0;
1006 			char *cur = buf;
1007 			double x;
1008 			while (*cur) {
1009 				x = g_ascii_strtod (cur, &cur);
1010 				if (*cur)
1011 					cur++;
1012 				if (i < variables.size ())
1013 					variables[i].First = x;
1014 				else {
1015 					var.First = x;
1016 					variables.push_back (var);
1017 					var.First = 0.;
1018 				}
1019 				i++;
1020 			}
1021 			break;
1022 		}
1023 		case JCAMP_LAST: {
1024 			size_t i = 0;
1025 			char *cur = buf;
1026 			double x;
1027 			while (*cur) {
1028 				x = g_ascii_strtod (cur, &cur);
1029 				if (*cur)
1030 					cur++;
1031 				if (i < variables.size ())
1032 					variables[i].Last = x;
1033 				else {
1034 					var.Last = x;
1035 					variables.push_back (var);
1036 					var.Last = 0.;
1037 				}
1038 				i++;
1039 			}
1040 			break;
1041 		}
1042 		case JCAMP_MIN: {
1043 			size_t i = 0;
1044 			char *cur = buf;
1045 			double x;
1046 			while (*cur) {
1047 				x = g_ascii_strtod (cur, &cur);
1048 				if (*cur)
1049 					cur++;
1050 				if (i < variables.size ())
1051 					variables[i].Min = x;
1052 				else {
1053 					var.Min = x;
1054 					variables.push_back (var);
1055 					var.Min = 0.;
1056 				}
1057 				i++;
1058 			}
1059 			break;
1060 		}
1061 		case JCAMP_MAX: {
1062 			size_t i = 0;
1063 			char *cur = buf;
1064 			double x;
1065 			while (*cur) {
1066 				x = g_ascii_strtod (cur, &cur);
1067 				if (*cur)
1068 					cur++;
1069 				if (i < variables.size ())
1070 					variables[i].Max = x;
1071 				else {
1072 					var.Max = x;
1073 					variables.push_back (var);
1074 					var.Max = 0.;
1075 				}
1076 				i++;
1077 			}
1078 			break;
1079 		}
1080 		case JCAMP_FACTOR: {
1081 			size_t i = 0;
1082 			char *cur = buf;
1083 			double x;
1084 			while (*cur) {
1085 				x = g_ascii_strtod (cur, &cur);
1086 				if (*cur)
1087 					cur++;
1088 				if (i < variables.size ())
1089 					variables[i].Factor = x;
1090 				else {
1091 					var.Factor = x;
1092 					variables.push_back (var);
1093 					var.Factor = 0.;
1094 				}
1095 				i++;
1096 			}
1097 			break;
1098 		}
1099 		case JCAMP_PAGE: {
1100 			unsigned num = atoi (strchr (buf, '=') + 1);
1101 			if (num == 1) {
1102 				unsigned max = 0;
1103 				for (unsigned i = 0; i < variables.size (); i++) {
1104 					JdxVar &v = variables[i];
1105 					if (v.Name == "PAGE NUMBER")
1106 						continue;
1107 					if (v.NbValues > max)
1108 						max = v.NbValues;
1109 					switch (v.Type) {
1110 					case GCU_SPECTRUM_TYPE_INDEPENDENT:
1111 						if (v.Symbol == 'X') {
1112 							firstx = v.First;
1113 							lastx = v.Last;
1114 							minx = v.Min;
1115 							maxx = v.Max;
1116 							xfactor = v.Factor;
1117 							deltax = (lastx - firstx) / (v.NbValues - 1);
1118 						}
1119 						break;
1120 					case GCU_SPECTRUM_TYPE_DEPENDENT:
1121 						break;
1122 					default:
1123 						break;
1124 					}
1125 				}
1126 				npoints = max;
1127 			}
1128 			break;
1129 		}
1130 		case JCAMP_DATA_TABLE: {
1131 			// first split fields, we might have two, but the second is optional
1132 			char *vlist = buf, *desc;
1133 			while (*vlist && *vlist == ' ')
1134 				vlist++;
1135 			if (!*vlist)
1136 				break;
1137 			desc = strchr (vlist, ',');
1138 			if (desc)
1139 				*desc = 0;
1140 			desc++;
1141 			while (*desc && *desc == ' ')
1142 				desc++;
1143 			struct data_type_struct dts;
1144 			parse_data_type (vlist, dts);
1145 			if (!strncmp (desc, "XYDATA", 6)) {
1146 				if (dts.n == 2 && !dts.one_per_line && !dts.explicit_indep) {
1147 					int first = -1, second = -1;
1148 					switch (*dts.variables) {
1149 					case 'I':
1150 						first = I;
1151 						break;
1152 					case 'R':
1153 						first = R;
1154 						break;
1155 					case 'X':
1156 						first = X;
1157 						break;
1158 					case 'Y':
1159 						first = Y;
1160 						break;
1161 					}
1162 					switch (dts.variables[1]) {
1163 					case 'I':
1164 						second = I;
1165 						break;
1166 					case 'R':
1167 						second = R;
1168 						break;
1169 					case 'X':
1170 						second = X;
1171 						break;
1172 					case 'Y':
1173 						second = Y;
1174 						break;
1175 					}
1176 					if (first >=0 && second >=0 && first != second) {
1177 						if (variables[first].Values)
1178 							delete [] variables[first].Values;
1179 						variables[first].Values = new double[variables[first].NbValues];
1180 						if (variables[second].Values)
1181 							delete [] variables[second].Values;
1182 						variables[second].Values = new double[variables[second].NbValues];
1183 						yfactor = variables[second].Factor;
1184 						firsty = variables[second].First;
1185 						ReadDataTable (s, variables[first].Values, variables[second].Values);
1186 					}
1187 				}
1188 			} //what should be done for PROFILE, PEAKS and COUTOUR?
1189 			g_free (dts.variables);
1190 			break;
1191 		}
1192 		case JCAMP_DELTAX:
1193 			deltax = g_ascii_strtod (buf, NULL);
1194 			break;
1195 		case JCAMP_CLASS:
1196 		case JCAMP_ORIGIN:
1197 		case JCAMP_OWNER:
1198 		case JCAMP_DATE:
1199 		case JCAMP_TIME:
1200 		case JCAMP_SOURCE_REFERENCE:
1201 		case JCAMP_CROSS_REFERENCE:
1202 		case JCAMP_SPECTROMETER:
1203 		case JCAMP_DATASYSTEM:
1204 		case JCAMP_INSTRUMENT_PARAMETERS:
1205 		case JCAMP_SAMPLE_DESCRIPTION:
1206 		case JCAMP_CAS_NAME:
1207 		case JCAMP_NAMES:
1208 		case JCAMP_MOLEFORM:
1209 		case JCAMP_CAS_REGISTRY_NO:
1210 		case JCAMP_WISWESSER:
1211 		case JCAMP_BEILSTEIN_LAWSON_NO:
1212 		case JCAMP_MP:
1213 		case JCAMP_BP:
1214 		case JCAMP_REFRACTIVE_INDEX:
1215 		case JCAMP_DENSITY:
1216 		case JCAMP_MW:
1217 		case JCAMP_CONCENTRATIONS:
1218 		case JCAMP_SAMPLING_PROCEDURE:
1219 		case JCAMP_STATE:
1220 		case JCAMP_PATH_LENGTH:
1221 		case JCAMP_PRESSURE:
1222 		case JCAMP_TEMPERATURE:
1223 		case JCAMP_DATA_PROCESSING:
1224 		case JCAMP_SPECTROMETER_TYPE:
1225 		case JCAMP_INLET:
1226 		case JCAMP_IONIZATION_MODE:
1227 		case JCAMP_INLET_TEMPERATURE:
1228 		case JCAMP_SOURCE_TEMPERATURE:
1229 		case JCAMP_IONIZATION_ENERGY:
1230 		case JCAMP_ACCELERATING_VOLTAGE:
1231 		case JCAMP_TOTAL_ION_CURRENT:
1232 		case JCAMP_ACQUISITION_RANGE:
1233 		case JCAMP_DETECTOR:
1234 		case JCAMP_SCAN_NUMBER:
1235 		case JCAMP_RETENTION_TIME:
1236 		case JCAMP_BASE_PEAK:
1237 		case JCAMP_BASE_PEAK_INTENSITY:
1238 		case JCAMP_RIC:
1239 		case JCAMP_NOMINAL_MASS:
1240 		case JCAMP_MONOISOTOPIC_MASS:
1241 			break;
1242 		case JCAMP_OBSERVE_FREQUENCY:
1243 			freq = g_ascii_strtod (buf, NULL);
1244 			break;
1245 		case JCAMP_OBSERVE_NUCLEUS:
1246 		case JCAMP_SOLVENT_REFERENCE:
1247 		case JCAMP_DELAY:
1248 		case JCAMP_ACQUISITION_MODE:
1249 		case JCAMP_FIELD:
1250 		case JCAMP_DECOUPLER:
1251 		case JCAMP_FILTER_WIDTH:
1252 		case JCAMP_ACQUISITION_TIME:
1253 		case JCAMP_ZERO_FILL:
1254 		case JCAMP_AVERAGES:
1255 		case JCAMP_DIGITIZER_RES:
1256 		case JCAMP_SPINNING_RATE:
1257 		case JCAMP_PHASE_0:
1258 		case JCAMP_PHASE_1:
1259 		case JCAMP_MIN_INTENSITY:
1260 		case JCAMP_MAX_INTENSITY:
1261 		case JCAMP_OBSERVE_90:
1262 		case JCAMP_COUPLING_CONSTANTS:
1263 		case JCAMP_RELAXATION_TIMES:
1264 			break;
1265 		case BRUCKER_OFFSET:
1266 			offset = g_ascii_strtod (buf, NULL);
1267 			break;
1268 		case VARIAN_OFFSET:
1269 			refpoint = g_ascii_strtod (buf, NULL);
1270 			break;
1271 		default:
1272 			break;
1273 		}
1274 	}
1275 
1276 out:
1277 	Loaded ();
1278 }
1279 
ReadDataLine(char const * data,list<double> & l)1280 void SpectrumDocument::ReadDataLine (char const *data, list<double> &l)
1281 {
1282 	int i = 1, j;
1283 	char buf[32], c = data[0];
1284 	double val = 0., newval = 0.;
1285 	bool pos, diff = false;
1286 	char *eq = strstr (const_cast <char *> (data), "$$");
1287 	if (eq)
1288 		*eq = 0;
1289 	pos = true;
1290 	while (c) {
1291 		switch (c) {
1292 		case ' ':
1293 			c = data[i++];
1294 			continue;
1295 		case '-':
1296 			pos = false;
1297 		case '+':
1298 			c = data[i++];
1299 			if ((c < '0' || c > '9') && c != '.') // FIXME: throw an exception
1300 				{;}
1301 			continue;
1302 		case '.':
1303 		case '0':
1304 		case '1':
1305 		case '2':
1306 		case '3':
1307 		case '4':
1308 		case '5':
1309 		case '6':
1310 		case '7':
1311 		case '8':
1312 		case '9':
1313 			buf[0] = c;
1314 			diff = false;
1315 			break;
1316 		case '@':
1317 		case 'A':
1318 		case 'B':
1319 		case 'C':
1320 		case 'D':
1321 		case 'E':
1322 		case 'F':
1323 		case 'G':
1324 		case 'H':
1325 		case 'I':
1326 			buf[0] = c - 0x10;
1327 			diff = false;
1328 			break;
1329 		case 'a':
1330 		case 'b':
1331 		case 'c':
1332 		case 'd':
1333 		case 'e':
1334 		case 'f':
1335 		case 'g':
1336 		case 'h':
1337 		case 'i':
1338 			pos = false;
1339 			diff = false;
1340 			buf[0] = c - 0x30;
1341 			break;
1342 		case '%':
1343 			c = 'I';
1344 		case 'J':
1345 		case 'K':
1346 		case 'L':
1347 		case 'M':
1348 		case 'N':
1349 		case 'O':
1350 		case 'P':
1351 		case 'Q':
1352 		case 'R':
1353 			diff = true;
1354 			buf[0] = c - 0x19;
1355 			break;
1356 		case 'j':
1357 		case 'k':
1358 		case 'l':
1359 		case 'm':
1360 		case 'n':
1361 		case 'o':
1362 		case 'p':
1363 		case 'q':
1364 		case 'r':
1365 			pos = false;
1366 			diff = true;
1367 			buf[0] = c - 0x39;
1368 			break;
1369 		case 's':
1370 			c = '[';
1371 		case 'S':
1372 		case 'T':
1373 		case 'U':
1374 		case 'V':
1375 		case 'W':
1376 		case 'X':
1377 		case 'Y':
1378 		case 'Z': {
1379 			buf[0] = c - 0x22;
1380 			j = 1;
1381 			while (c = data[i++], (c >= '0' && c <= '9')) {
1382 				if (j == 31) {
1383 					g_warning (_("Constant too long"));
1384 					break;
1385 				}
1386 				buf[j++] = c;
1387 			}
1388 			buf[j] = 0;
1389 			int m, n = atoi (buf);
1390 			for (m = 1; m < n; m++) {
1391 				if (diff)
1392 					val += newval;
1393 				l.push_back (val);
1394 			}
1395 			continue;
1396 		}
1397 		case '?':
1398 			diff = false;
1399 			val = go_nan;
1400 			newval = 0.;
1401 			l.push_back (go_nan);
1402 			c = data[i++];
1403 			continue;
1404 		default:
1405 			if (c > ' ')
1406 				g_warning (_("Invalid character in data block"));
1407 			c = data[i++];
1408 			continue;
1409 		}
1410 		j = 1;
1411 		while (c = data[i++], (c >= '0' && c <= '9') || c == '.') {
1412 			if (j == 31) {
1413 				g_warning (_("Constant too long"));
1414 				break;
1415 			}
1416 			buf[j++] = c;
1417 		}
1418 		buf[j] = 0;
1419 		newval = g_ascii_strtod (buf, NULL);
1420 		if (!pos)
1421 			newval = - newval;
1422 		if (diff)
1423 			val += newval;
1424 		else
1425 			val = newval;
1426 		l.push_back (val);
1427 		pos = true;
1428 	}
1429 }
1430 
DoPrint(G_GNUC_UNUSED GtkPrintOperation * print,GtkPrintContext * context,G_GNUC_UNUSED int page) const1431 void SpectrumDocument::DoPrint (G_GNUC_UNUSED GtkPrintOperation *print, GtkPrintContext *context, G_GNUC_UNUSED int page) const
1432 {
1433 	cairo_t *cr;
1434 	gdouble width, height;
1435 
1436 	cr = gtk_print_context_get_cairo_context (context);
1437 	width = gtk_print_context_get_width (context);
1438 	height = gtk_print_context_get_height (context);
1439 	int w, h; // size in points
1440 	w = m_View->GetWidth ();
1441 	h = m_View->GetHeight ();
1442 	switch (GetScaleType ()) {
1443 	case GCU_PRINT_SCALE_NONE:
1444 		break;
1445 	case GCU_PRINT_SCALE_FIXED:
1446 		w *= Printable::GetScale ();
1447 		h *= Printable::GetScale ();
1448 		break;
1449 	case GCU_PRINT_SCALE_AUTO:
1450 		if (GetHorizFit ())
1451 			w = width;
1452 		if (GetVertFit ())
1453 			h = height;
1454 		break;
1455 	}
1456 	double x = 0., y = 0.;
1457 	if (GetHorizCentered ())
1458 		x = (width - w) / 2.;
1459 	if (GetVertCentered ())
1460 		y = (height - h) / 2.;
1461 	cairo_save (cr);
1462 	cairo_translate (cr, x, y);
1463 	m_View->Render (cr, w, h);
1464 	cairo_restore (cr);
1465 }
1466 
GetGtkWindow()1467 GtkWindow *SpectrumDocument::GetGtkWindow ()
1468 {
1469 	GtkWidget *w = m_View->GetWidget ();
1470 	return (GtkWindow*) ((w)? gtk_widget_get_toplevel (m_View->GetWidget ()): NULL);
1471 }
1472 
ReadDataTable(istream & s,double * x,double * y)1473 void SpectrumDocument::ReadDataTable (istream &s, double *x, double *y)
1474 {
1475 	char line[300]; // should be enough
1476 	unsigned read = 0;
1477 	list<double> l;
1478 	int previous = 0;
1479 	double previousx = firstx;
1480 	while (1) {
1481 		if (s.eof ())
1482 			break;	// this should not occur, but a corrupted or bad file is always possible
1483 		s.getline (line, 300);
1484 		if (strstr (line, "##")) {
1485 			s.seekg (-strlen (line) -1, s.cur);
1486 			if (read > npoints) {
1487 				g_warning (_("Found too many data!"));
1488 				// FIXME: throw an exception
1489 			} else
1490 				npoints = read;
1491 			break;
1492 		}
1493 		ReadDataLine (line, l);
1494 		if (l.empty ())
1495 			continue;
1496 		list<double>::iterator i = l.begin (), end = l.end ();
1497 		if (read > 0) {
1498 			double x1 = (*i) * xfactor;
1499 			int n = read - previous - (int)round((x1-previousx)/deltax);
1500 			previous = read;
1501 			previousx = x1;
1502 			if (n == 0) {
1503 				// values are the same, no y reminder, and nothing to do
1504 			} else if (n == 1) {
1505 				i++;
1506 				previous--;
1507 				double y0 = (*i) * yfactor;
1508 				if (fabs (y0 - y[read - 1]) > fmax (fabs (y0), fabs (y[read - 1])) * JCAMP_PREC)
1509 					g_warning (_("Data check failed!"));
1510 			} else if (previousx - x1 < 0.) {
1511 				unsigned missing = (unsigned) round ((x1 - previousx) / deltax), n;
1512 				for (n = 0; n < missing; n++) {
1513 					if (read > npoints) // FIXME: Throw an exception
1514 						break;
1515 					x[read] = firstx + deltax * read;
1516 					y[read++] = go_nan;
1517 				}
1518 			} else {
1519 				// FIXME: duplicate values, throw an exception
1520 			}
1521 		} else {
1522 			x[read] = (*i) * xfactor;
1523 			if (fabs (x[0] - firstx) > fabs (deltax * JCAMP_PREC)) {
1524 				xfactor = firstx / (*i);
1525 				deltax = (lastx - firstx) / (npoints - 1);
1526 				g_warning (_("Data check failed: FIRSTX!"));
1527 			}
1528 			i++;
1529 			y[read++] = (*i) * yfactor;
1530 			if (fabs (firsty - y[0]) > fmax (fabs (firsty), fabs (y[0])) * JCAMP_PREC)
1531 				g_warning (_("Data check failed: FIRSTY!"));
1532 		}
1533 		for (i++; i !=	end; i++) {
1534 			if (read >= npoints) { // FIXME: Throw an exception
1535 				g_warning (_("Found too many data"));
1536 				break;
1537 			}
1538 			x[read] = firstx + deltax * read;
1539 			y[read++] = (*i) * yfactor;
1540 		}
1541 		l.clear ();
1542 	}
1543 	if (!go_finite (minx))
1544 		go_range_min (x, read, &minx);
1545 	if (!go_finite (maxx))
1546 		go_range_max (x, read, &maxx);
1547 	if (!go_finite (miny))
1548 		go_range_min (y, read, &miny);
1549 	if (!go_finite (maxy))
1550 		go_range_max (y, read, &maxy);
1551 	while (npoints > read) {
1552 		// this should never occur, fill missing y values with nan
1553 		x[read] = minx + deltax * read;
1554 		y[read++] = go_nan;
1555 	}
1556 	if (isnan (maxx)) {
1557 		maxx = MAX (firstx, lastx);
1558 		minx = MIN (firstx, lastx);
1559 	}
1560 }
1561 
OnXUnitChanged(int i)1562 void SpectrumDocument::OnXUnitChanged (int i)
1563 {
1564 	SpectrumUnitType unit = GCU_SPECTRUM_UNIT_MAX;
1565 	bool invert_axis = false;
1566 	switch (m_SpectrumType) {
1567 	case GCU_SPECTRUM_NMR:
1568 		unit = (i == 0)? GCU_SPECTRUM_UNIT_PPM: GCU_SPECTRUM_UNIT_HZ;
1569 		invert_axis = true;
1570 		break;
1571 	case GCU_SPECTRUM_INFRARED:
1572 	case GCU_SPECTRUM_RAMAN:
1573 		if (i == 1) {
1574 			unit = GCU_SPECTRUM_UNIT_CM_1;
1575 			invert_axis = true;
1576 		} else
1577 			unit = GCU_SPECTRUM_UNIT_MICROMETERS;
1578 		break;
1579 	case GCU_SPECTRUM_UV_VISIBLE:
1580 		if (i == 1) {
1581 			unit = GCU_SPECTRUM_UNIT_CM_1;
1582 			invert_axis = true;
1583 		} else
1584 			unit = GCU_SPECTRUM_UNIT_NANOMETERS;
1585 		break;
1586 	default:
1587 		break;
1588 	}
1589 	if (unit == GCU_SPECTRUM_UNIT_MAX)
1590 		return;
1591 	GOData *godata;
1592 	GogSeries *series = m_View->GetSeries ();
1593 	if (x && m_XUnit == unit) {
1594 		X = -1;
1595 		godata = go_data_vector_val_new (x, npoints, NULL);
1596 		gog_series_set_dim (series, 0, godata, NULL);
1597 		m_View->SetAxisBounds (GOG_AXIS_X, minx, maxx, invert_axis);
1598 		m_View->SetAxisLabel (GOG_AXIS_X, _(UnitNames[m_XUnit]));
1599 		if (m_XAxisInvertBtn) {
1600 			g_signal_handler_block (m_XAxisInvertBtn, m_XAxisInvertSgn);
1601 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (m_XAxisInvertBtn), invert_axis);
1602 			g_signal_handler_unblock (m_XAxisInvertBtn, m_XAxisInvertSgn);
1603 		}
1604 	} else {
1605 		unsigned i, j;
1606 		double (*conv) (double, double, double);
1607 		double f, o;
1608 		for (i = 0; i < variables.size (); i++)
1609 			if (variables[i].Symbol == 'X' && variables[i].Unit == unit)
1610 				break;
1611 		if (i == variables.size ()) {
1612 			// Add new data vector
1613 			JdxVar v;
1614 			if (X >=0) {
1615 				conv = GetConversionFunction (variables[X].Unit, unit, f, o);
1616 				if (!conv)
1617 					return;
1618 				v.Name = _(UnitNames[variables[X].Unit]);
1619 				v.Symbol = variables[X].Symbol;
1620 				v.Type = variables[X].Type;
1621 				v.Unit = unit;
1622 				v.Format = variables[X].Format;
1623 				v.NbValues = variables[X].NbValues;
1624 					v.First = conv (variables[X].First, f, o);
1625 					v.Last = conv (variables[X].Last, f, o);
1626 					v.Min = conv (variables[X].Min, f, o);
1627 					v.Max = conv (variables[X].Max, f, o);
1628 					v.Factor = 1.;
1629 					v.Values = new double[variables[X].NbValues];
1630 					for (j = 0; j < variables[X].NbValues; j++)
1631 						v.Values[j] = conv (variables[X].Values[j], f, o);
1632 			} else {
1633 				conv = GetConversionFunction (m_XUnit, unit, f, o);
1634 				if (!conv)
1635 					return;
1636 				v.Name = _(UnitNames[unit]);
1637 				v.Symbol = 'X';
1638 				v.Type = GCU_SPECTRUM_TYPE_DEPENDENT;
1639 				v.Unit = unit;
1640 				v.Format = GCU_SPECTRUM_FORMAT_MAX;
1641 				v.NbValues = npoints;
1642 					v.First = conv (firstx, f, o);
1643 					v.Last = conv (lastx, f, o);
1644 					v.Min = conv (minx, f, o);
1645 					v.Max = conv (maxx, f, o);
1646 					v.Factor = 1.;
1647 					v.Values = new double[npoints];
1648 					for (j = 0; j < npoints; j++)
1649 						v.Values[j] = conv (x[j], f, o);
1650 			}
1651 			if (v.Min > v.Max) {
1652 				f = v.Min;
1653 				v.Min = v.Max;
1654 				v.Max = f;
1655 			}
1656 			variables.push_back (v);
1657 		}
1658 		X = i;
1659 		godata = go_data_vector_val_new (variables[i].Values, variables[i].NbValues, NULL);
1660 		gog_series_set_dim (series, 0, godata, NULL);
1661 		m_View->SetAxisBounds (GOG_AXIS_X, variables[i].Min, variables[i].Max, invert_axis);
1662 		m_View->SetAxisLabel (GOG_AXIS_X, _(UnitNames[variables[i].Unit]));
1663 		if (m_XAxisInvertBtn) {
1664 			g_signal_handler_block (m_XAxisInvertBtn, m_XAxisInvertSgn);
1665 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (m_XAxisInvertBtn), invert_axis);
1666 			g_signal_handler_unblock (m_XAxisInvertBtn, m_XAxisInvertSgn);
1667 		}
1668 		if (integral > 0) {
1669 			g_object_ref (godata);
1670 		}
1671 	}
1672 	if (integral > 0) {
1673 		g_object_ref (godata);
1674 		gog_series_set_dim (variables[integral].Series, 0, godata, NULL);
1675 	}
1676 }
1677 
OnYUnitChanged(int i)1678 void SpectrumDocument::OnYUnitChanged (int i)
1679 {
1680 	SpectrumUnitType unit = GCU_SPECTRUM_UNIT_MAX;
1681 	bool invert_axis = false;
1682 	switch (m_SpectrumType) {
1683 	case GCU_SPECTRUM_INFRARED:
1684 	case GCU_SPECTRUM_RAMAN:
1685 	case GCU_SPECTRUM_UV_VISIBLE:
1686 		unit = (i == 0)? GCU_SPECTRUM_UNIT_ABSORBANCE: GCU_SPECTRUM_UNIT_TRANSMITTANCE;
1687 		break;
1688 	default:
1689 		break;
1690 	}
1691 	if (unit == GCU_SPECTRUM_UNIT_MAX)
1692 		return;
1693 	GOData *godata;
1694 	GogSeries *series = m_View->GetSeries ();
1695 	if (m_YUnit == unit) {
1696 		Y = -1;
1697 		godata = go_data_vector_val_new (y, npoints, NULL);
1698 		gog_series_set_dim (series, 1, godata, NULL);
1699 		m_View->SetAxisBounds (GOG_AXIS_Y, miny, maxy, invert_axis);
1700 		m_View->SetAxisLabel (GOG_AXIS_Y, _(UnitNames[m_YUnit]));
1701 	} else {
1702 		unsigned i, j;
1703 		double (*conv) (double, double, double);
1704 		double f, o;
1705 		for (i = 0; i < variables.size (); i++)
1706 			if (variables[i].Symbol == 'Y' && variables[i].Unit == unit)
1707 				break;
1708 		if (i == variables.size ()) {
1709 			// Add new data vector
1710 			JdxVar v;
1711 			if (Y >=0) {
1712 				conv = GetConversionFunction (variables[Y].Unit, unit, f, o);
1713 				if (!conv)
1714 					return;
1715 				v.Name = _(UnitNames[variables[Y].Unit]);
1716 				v.Symbol = variables[Y].Symbol;
1717 				v.Type = variables[Y].Type;
1718 				v.Unit = unit;
1719 				v.Format = variables[Y].Format;
1720 				v.NbValues = variables[Y].NbValues;
1721 				v.First = conv (variables[Y].First, f, o);
1722 				v.Last = conv (variables[Y].Last, f, o);
1723 				v.Min = conv (variables[Y].Min, f, o);
1724 				v.Max = conv (variables[Y].Max, f, o);
1725 				v.Factor = 1.;
1726 				v.Values = new double[variables[Y].NbValues];
1727 				for (j = 0; j < variables[Y].NbValues; j++)
1728 					v.Values[j] = conv (variables[Y].Values[j], f, o);
1729 			} else {
1730 				conv = GetConversionFunction (m_YUnit, unit, f, o);
1731 				if (!conv)
1732 					return;
1733 				v.Name = _(UnitNames[unit]);
1734 				v.Symbol = 'Y';
1735 				v.Type = GCU_SPECTRUM_TYPE_DEPENDENT;
1736 				v.Unit = unit;
1737 				v.Format = GCU_SPECTRUM_FORMAT_MAX;
1738 				v.NbValues = npoints;
1739 				v.First = conv (firsty, f, o);
1740 				v.Last = 0.; // not important
1741 				v.Min = conv (miny, f, o);
1742 				v.Max = conv (maxy, f, o);
1743 				v.Factor = 1.;
1744 				v.Values = new double[npoints];
1745 				for (j = 0; j < npoints; j++)
1746 					v.Values[j] = conv (y[j], f, o);
1747 			}
1748 			if (v.Min > v.Max) {
1749 				f = v.Min;
1750 				v.Min = v.Max;
1751 				v.Max = f;
1752 			}
1753 			variables.push_back (v);
1754 		}
1755 		Y = i;
1756 		godata = go_data_vector_val_new (variables[i].Values, variables[i].NbValues, NULL);
1757 		gog_series_set_dim (series, 1, godata, NULL);
1758 		m_View->SetAxisBounds (GOG_AXIS_Y, variables[i].Min, variables[i].Max, invert_axis);
1759 		m_View->SetAxisLabel (GOG_AXIS_Y, _(UnitNames[variables[i].Unit]));
1760 	}
1761 }
1762 
mult(double val,double f,double offset)1763 static double mult (double val, double f, double offset)
1764 {
1765 	return val * f + offset;
1766 }
1767 
inv(double val,double f,double offset)1768 static double inv (double val, double f, double offset)
1769 {
1770 	return f / val + offset;
1771 }
1772 
logm(double val,double f,double offset)1773 static double logm (double val, double f, double offset)
1774 {
1775 	return -log10 (val * f + offset);
1776 }
1777 
expm(double val,double f,double offset)1778 static double expm (double val, double f, double offset)
1779 {
1780 	return exp10 (-val) * f + offset;
1781 }
1782 
GetConversionFunction(SpectrumUnitType oldu,SpectrumUnitType newu,double & factor,double & shift)1783 double (*SpectrumDocument::GetConversionFunction (SpectrumUnitType oldu, SpectrumUnitType newu, double &factor, double &shift)) (double, double, double)
1784 {
1785 	switch (oldu) {
1786 	case GCU_SPECTRUM_UNIT_CM_1:
1787 		if (newu == GCU_SPECTRUM_UNIT_NANOMETERS) {
1788 			factor = 1.e7;
1789 			shift = 0;
1790 			return inv;
1791 		}
1792 		if (newu == GCU_SPECTRUM_UNIT_MICROMETERS) {
1793 			factor = 1.e4;
1794 			shift = 0;
1795 			return inv;
1796 		}
1797 		break;
1798 	case GCU_SPECTRUM_UNIT_TRANSMITTANCE:
1799 		if (newu == GCU_SPECTRUM_UNIT_ABSORBANCE) {
1800 			factor = 1.;
1801 			shift = 0.;
1802 			return logm;
1803 		}
1804 		break;
1805 	case GCU_SPECTRUM_UNIT_ABSORBANCE:
1806 		if (newu == GCU_SPECTRUM_UNIT_TRANSMITTANCE) {
1807 			factor = 1.;
1808 			shift = 0.;
1809 			return expm;
1810 		}
1811 		break;
1812 	case GCU_SPECTRUM_UNIT_PPM:
1813 		if (go_finite (freq) && newu == GCU_SPECTRUM_UNIT_HZ) {
1814 			factor = freq;
1815 			shift = 0;
1816 			return mult;
1817 		}
1818 		break;
1819 	case GCU_SPECTRUM_UNIT_NANOMETERS:
1820 		if (newu == GCU_SPECTRUM_UNIT_CM_1) {
1821 			factor = 1.e7;
1822 			shift = 0;
1823 			return inv;
1824 		}
1825 		break;
1826 	case GCU_SPECTRUM_UNIT_MICROMETERS:
1827 		if (newu == GCU_SPECTRUM_UNIT_CM_1) {
1828 			factor = 1.e4;
1829 			shift = 0;
1830 			return inv;
1831 		}
1832 		break;
1833 	case GCU_SPECTRUM_UNIT_HZ:
1834 		if (go_finite (freq) && newu == GCU_SPECTRUM_UNIT_PPM)
1835 			factor = 1. / freq;
1836 		shift = 0.;
1837 		return mult;
1838 	default:
1839 		break;
1840 	}
1841 	return NULL;
1842 }
1843 
OnShowIntegral()1844 void SpectrumDocument::OnShowIntegral ()
1845 {
1846 	m_IntegralVisible = !m_IntegralVisible;
1847 	GOStyle *style;
1848 	if (m_IntegralVisible) {
1849 		if (integral < 0) {
1850 			integral = variables.size ();
1851 			JdxVar v;
1852 			double *xo, *xn[5], *yb, cur, acc;
1853 			v.Name = _("Integral");
1854 			v.Symbol = 'i';
1855 			v.Type = GCU_SPECTRUM_TYPE_DEPENDENT;
1856 			v.Unit = GCU_SPECTRUM_UNIT_MAX;
1857 			v.Format = GCU_SPECTRUM_FORMAT_MAX;
1858 			v.Factor = 1.;
1859 			v.NbValues = (X >= 0)? variables[X].NbValues: npoints;
1860 			xn[0] = new double[v.NbValues];
1861 			xn[1] = new double[v.NbValues];
1862 			xn[2] = new double[v.NbValues];
1863 			xn[3] = new double[v.NbValues];
1864 			xn[4] = new double[v.NbValues];
1865 			yb = new double[v.NbValues];
1866 			v.First = 0.;
1867 			v.Values = new double[v.NbValues];
1868 			unsigned i;
1869 			double *z;
1870 			if (Rp >= 0)
1871 				z = variables[Rp].Values;
1872 			else if (R >= 0)
1873 				z = variables[R].Values;
1874 			else if (Y >= 0)
1875 				z = variables[Y].Values;
1876 			else
1877 				z = y;
1878 			xo = (X >= 0 && variables[X].Values != NULL)? variables[X].Values: x;
1879 			double max, delta;
1880 			unsigned used = 0;
1881 			go_range_max (z, v.NbValues, &max);
1882 			max *= 0.005;
1883 			v.Values[0] = 0.;
1884 			for (i = 1; i < v.NbValues; i++) {
1885 				delta = 0.5 * (z[i - 1] + z[i]);
1886 				v.Values[i] = v.Values[i - 1] + delta;
1887 				if (delta < max) {
1888 					cur = xn[0][used] = xo[i];
1889 					acc = xn[1][used] = cur * cur;
1890 					xn[2][used] = (acc *= cur);
1891 					xn[3][used] = (acc *= cur);
1892 					xn[4][used] = acc * cur;
1893 					yb[used] = (used > 0)? yb[used - 1] + delta: delta;
1894 					used++;
1895 				}
1896 			}
1897 			go_regression_stat_t reg;
1898 			double res[6];
1899 			go_linear_regression (xn, 5, yb, used, true, res, &reg);
1900 			for (i = 0; i < v.NbValues; i++) {
1901 				cur = xo[i];
1902 				acc = cur * cur;
1903 				v.Values[i] -= res[0] + res[1] * cur + res[2] * acc;
1904 				v.Values[i] -= res[3] * (acc *= cur);
1905 				v.Values[i] -= res[4] * (acc *= cur);
1906 				v.Values[i] -= res[5] * cur * acc;
1907 			}
1908 			if (xo[1] > xo[0])
1909 				for (i = 0; i < v.NbValues; i++)
1910 					v.Values[i] = -v.Values[i];
1911 			g_free (reg.se);
1912 			g_free (reg.t);
1913 			g_free (reg.xbar);
1914 			v.Last = v.Max = v.Values[v.NbValues - 1];
1915 			v.Min = 0.;
1916 			v.Series = m_View->NewSeries (true);
1917 			GOData *godata;
1918 			godata = go_data_vector_val_new (xo, npoints, NULL);
1919 			gog_series_set_dim (v.Series, 0, godata, NULL);
1920 			godata = go_data_vector_val_new (v.Values, v.NbValues, NULL);
1921 			gog_series_set_dim (v.Series, 1, godata, NULL);
1922 			GOStyledObject *axis = GO_STYLED_OBJECT (g_object_new (GOG_TYPE_AXIS, "major-tick-labeled", false, NULL));
1923 			GogPlot	*plot = gog_series_get_plot (v.Series);
1924 			GogObject *chart = GOG_OBJECT (gog_object_get_parent (GOG_OBJECT (plot)));
1925 			gog_object_add_by_name (chart, "Y-Axis", GOG_OBJECT (axis));
1926 			gog_plot_set_axis (plot, GOG_AXIS (axis));
1927 			style = go_styled_object_get_style (axis);
1928 			style->line.auto_dash = false;
1929 			style->line.dash_type = GO_LINE_NONE;
1930 			style = go_styled_object_get_style (GO_STYLED_OBJECT (v.Series));
1931 			style->line.auto_dash = false;
1932 			style->line.auto_color = false;
1933 			style->line.color = GO_COLOR_RED;
1934 			variables.push_back (v);
1935 			delete [] xn[0];
1936 			delete [] xn[1];
1937 			delete [] xn[2];
1938 			delete [] xn[3];
1939 			delete [] xn[4];
1940 			delete [] yb;
1941 		} else
1942 			style = go_styled_object_get_style (GO_STYLED_OBJECT (variables[integral].Series));
1943 		// show the series
1944 		style->line.dash_type = GO_LINE_SOLID;
1945 		gog_object_request_update (GOG_OBJECT (variables[integral].Series));
1946 	} else {
1947 		// hide the series
1948 		style = go_styled_object_get_style (GO_STYLED_OBJECT (variables[integral].Series));
1949 		style->line.dash_type = GO_LINE_NONE;
1950 		gog_object_request_update (GOG_OBJECT (variables[integral].Series));
1951 	}
1952 }
1953 
OnTransformFID(G_GNUC_UNUSED GtkButton * btn)1954 void SpectrumDocument::OnTransformFID (G_GNUC_UNUSED GtkButton *btn)
1955 {
1956 	double *re = variables[R].Values, *im = variables[I].Values;
1957 	unsigned n = 2;
1958 	while (n < npoints)
1959 		n <<= 1;
1960 	go_complex *fid = new go_complex[n], *sp;
1961 	unsigned i;
1962 	for (i = 0; i < npoints; i++) {
1963 		// assuming we have as many real, imaginary and time values
1964 		fid[i].re = re[i];
1965 		fid[i].im = im[i];
1966 	}
1967 	for (; i < n; i++) {
1968 		// fill with zeros
1969 		fid[i].re =  fid[i].im = 0.;
1970 	}
1971 	//we make no apodization at the moment
1972 	go_fourier_fft (fid, n, 1, &sp, false);
1973 	delete [] fid;
1974 	// copy the unphased data to Rt and It (t for transformed)
1975 	JdxVar vr, vi, rp, xt;
1976 	vr.Name = _("Real transformed data");
1977 	vr.Symbol = 't';
1978 	vr.Type = GCU_SPECTRUM_TYPE_DEPENDENT;
1979 	vr.Unit = GCU_SPECTRUM_UNIT_MAX;
1980 	vr.Format = GCU_SPECTRUM_FORMAT_MAX;
1981 	vr.Factor = 1.;
1982 	vr.NbValues = n;
1983 	vr.Values = new double[n];
1984 	vi.Name = _("Imaginary transformed data");
1985 	vi.Symbol = 'u';
1986 	vi.Type = GCU_SPECTRUM_TYPE_DEPENDENT;
1987 	vi.Unit = GCU_SPECTRUM_UNIT_MAX;
1988 	vi.Format = GCU_SPECTRUM_FORMAT_MAX;
1989 	vi.Factor = 1.;
1990 	vi.NbValues = n;
1991 	vi.Values = new double[n];
1992 	unsigned n2 = n / 2 - 1, j;
1993 	for (i = 0, j = n2; i < n2; i++, j--) {
1994 		vr.Values[i] = sp[j].re;
1995 		vi.Values[i] = sp[j].im;
1996 	}
1997 	// the value at 0 must be skipped, doing a linear interpolation
1998 	vr.Values[i] = (sp[1].re + sp[n - 1].re) / 2.;
1999 	vi.Values[i++] = (sp[1].im + sp[n - 1].im) / 2.;
2000 	for (j = n - 1; i < n; i++, j--) {
2001 		vr.Values[i] = sp[j].re;
2002 		vi.Values[i] = sp[j].im;
2003 	}
2004 	vr.First = vr.Values[0];
2005 	vr.Last = vr.Values[n - 1];
2006 	go_range_min (vr.Values, n, &vr.Min);
2007 	go_range_max (vr.Values, n, &vr.Max);
2008 	vr.Series = NULL;
2009 	Rt = variables.size ();
2010 	variables.push_back (vr);
2011 	vi.First = vi.Values[0];
2012 	vi.Last = vi.Values[n - 1];
2013 	go_range_min (vi.Values, n, &vi.Min);
2014 	go_range_max (vi.Values, n, &vi.Max);
2015 	vi.Series = NULL;
2016 	It = variables.size ();
2017 	variables.push_back (vi);
2018 	// Now we need to adjust the phase (see http://www.ebyte.it/stan/Poster_EDISPA.html)
2019 	double phi = 0., tau = 0., *z, maxz = 0., phiopt = 0., tauopt = 0.;
2020 	z = new double[n];
2021 	for (i = 0; i < n; i++) {
2022 		// copy reordered data to sp
2023 		sp[i].re = vr.Values[i];
2024 		sp[i].im = vi.Values[i];
2025 		z[i] = go_complex_mod (sp + i);
2026 		if (z[i] > maxz)
2027 			maxz = z[i];
2028 	}
2029 	// normalize the z values
2030 	for (i = 0; i < n; i++)
2031 		z[i] /= maxz;
2032 	double c = 0.1;
2033 	unsigned nmax, nmin;
2034 	while (c > 0.) {
2035 		nmin = n, nmax = 0;
2036 		for (i = 0; i < n; i++)
2037 			if (z[i] > c) {
2038 				nmin = i;
2039 				break;
2040 			}
2041 		for (i = n - 1; i > 0; i--)
2042 			if (z[i] > c) {
2043 				nmax = i;
2044 				break;
2045 			}
2046 		if ((nmax - nmin) > n / 10) // 10 is arbitrary, but should be not too large
2047 			break;
2048 		c /= 2.;
2049 	}
2050 	// make a list of indices with z greater than c
2051 	std::list <unsigned> restricted;
2052 	for (i = 0; i < n; i++)
2053 		if (z[i] > c)
2054 			restricted.push_back (i);
2055 	// evaluate the predictor in c, since the value is not used anymore
2056 	double maxc = 0., maxk = 0., p, phis[41];
2057 	unsigned k;
2058 	std::list <unsigned>::iterator it, itend = restricted.end ();
2059 	for (k = 0; k < 41; k++) {
2060 		tau = -1. + k * .1;
2061 		maxk = 0.;
2062 		for (phi = 0; phi <  2 * M_PI; phi += 10. / 180. * M_PI) {
2063 			c = 0.;
2064 			for (it = restricted.begin (); it != itend; it++) {
2065 				i = *it;
2066 				p = phi - 2. * M_PI * tau * (n - 1 - i) / n;
2067 				c += z[i] * z[i] * (sp[i].re * cos (p) - sp[i].im * sin (p)) * exp (-fabs (4. * i / n -2));
2068 			}
2069 			if (c > maxk) {
2070 				if (c > maxc) {
2071 					maxc = c;
2072 					tauopt = tau;
2073 					phiopt = phi;
2074 				}
2075 				maxk = c;
2076 				phis[k] = phi;
2077 			}
2078 		}
2079 	}
2080 	// let's search more finely around the maximum
2081 	// using the fact that phis[k] is a(n almost) linear function of tau
2082 	// first, reevaluate the optimum phi for tauopt and previous value and next value with a step of 1°
2083 	double phase = phiopt;
2084 	for (phi = phase - 9. / 180. * M_PI; phi < phase + 10. / 180. * M_PI; phi += 1. / 180. * M_PI) {
2085 			c = 0.;
2086 			for (it = restricted.begin (); it != itend; it++) {
2087 				i = *it;
2088 				p = phi - 2. * M_PI * tauopt * (n - 1 - i) / n;
2089 				c += z[i] * z[i] * (sp[i].re * cos (p) - sp[i].im * sin (p)) * exp (-fabs (4. * i / n -2));
2090 			}
2091 			if (c > maxc) {
2092 				maxc = c;
2093 				phiopt = phi;
2094 			}
2095 	}
2096 	for (k = 1; k < 10; k++) {
2097 		// search with lower values of tau
2098 		tau = tauopt - 0.01 * k;
2099 		for (phi = 0; phi <  2 * M_PI; phi += 10. / 180. * M_PI) {
2100 			c = 0.;
2101 			maxk = 0;
2102 			for (it = restricted.begin (); it != itend; it++) {
2103 				i = *it;
2104 				p = phi - 2. * M_PI * tau * (n - 1 - i) / n;
2105 				c += z[i] * z[i] * (sp[i].re * cos (p) - sp[i].im * sin (p)) * exp (-fabs (4. * i / n -2));
2106 			}
2107 			if (c > maxk) {
2108 				phis[0] = phi;
2109 				maxk = c;
2110 			}
2111 		}
2112 		phase = phis[0];
2113 		for (phi = phase - 9. / 180. * M_PI; phi < phase + 10. / 180. * M_PI; phi += 1. / 180. * M_PI) {
2114 				c = 0.;
2115 				for (it = restricted.begin (); it != itend; it++) {
2116 					i = *it;
2117 					p = phi - 2. * M_PI * tau * (n - 1 - i) / n;
2118 					c += z[i] * z[i] * (sp[i].re * cos (p) - sp[i].im * sin (p)) * exp (-fabs (4. * i / n -2));
2119 				}
2120 				if (c > maxk) {
2121 					maxk = c;
2122 					phis[0] = phi;
2123 				}
2124 		}
2125 		if (maxk < maxc)
2126 			break;
2127 		maxc = maxk;
2128 		phiopt = phis[0];
2129 		tauopt = tau;
2130 	}
2131 	if (k == 1) {
2132 		for (k = 1; k < 10; k++) {
2133 			// search with higher values of tau
2134 			tau = tauopt + 0.01 * k;
2135 			for (phi = 0; phi <  2 * M_PI; phi += 10. / 180. * M_PI) {
2136 				c = 0.;
2137 				maxk = 0;
2138 				for (it = restricted.begin (); it != itend; it++) {
2139 					i = *it;
2140 					p = phi - 2. * M_PI * tau * (n - 1 - i) / n;
2141 					c += z[i] * z[i] * (sp[i].re * cos (p) - sp[i].im * sin (p)) * exp (-fabs (4. * i / n -2));
2142 				}
2143 				if (c > maxk) {
2144 					phis[0] = phi;
2145 					maxk = c;
2146 				}
2147 			}
2148 			phase = phis[0];
2149 			for (phi = phase - 9. / 180. * M_PI; phi < phase + 10. / 180. * M_PI; phi += 1. / 180. * M_PI) {
2150 					c = 0.;
2151 					for (it = restricted.begin (); it != itend; it++) {
2152 						i = *it;
2153 						p = phi - 2. * M_PI * tau * (n - 1 - i) / n;
2154 						c += z[i] * z[i] * (sp[i].re * cos (p) - sp[i].im * sin (p)) * exp (-fabs (4. * i / n -2));
2155 					}
2156 					if (c > maxk) {
2157 						maxk = c;
2158 						phis[0] = phi;
2159 					}
2160 			}
2161 			if (maxk < maxc)
2162 				break;
2163 			maxc = maxk;
2164 			phiopt = phis[0];
2165 			tauopt = tau;
2166 		}
2167 	}
2168 	g_free (sp);
2169 	// set the phase real values
2170 	// free what needs to be freed
2171 	delete [] z;
2172 	// set the corrected values
2173 	rp.Values = new double[n];
2174 	//store phi and tau as first and last, respectively
2175 	double step = M_PI * 2. * tauopt / n;
2176 	phase = phiopt - M_PI * 2. * tauopt;
2177 	rp.First = phase + step;
2178 	rp.Last = phiopt + n * step;
2179 	for (i = 0; i < n; i++) {
2180 		phase += step;
2181 		rp.Values[i] = vr.Values[i] * cos (phase) - vi.Values[i] * sin (phase);
2182 	}
2183 	go_range_min (rp.Values, n, &rp.Min);
2184 	go_range_max (rp.Values, n, &rp.Max);
2185 	Rp = variables.size ();
2186 	variables.push_back (rp);
2187 	// add Hz and ppm variables (0 for last point, user will have to choose a reference peak)
2188 	// first Hz
2189 	// if we are there, we have R and I values, we should have also X, but let's check
2190 	double freq, shift;
2191 	if (X >= 0 && variables[X].Values != NULL)
2192 		freq = 1 / (variables[X].Last - variables[X].First);
2193 	else
2194 		freq = 1 / (lastx - firstx);
2195 	if (!go_finite (offset))
2196 		offset = 0.;
2197 	shift = offset - freq * (npoints - 1) / 2.;
2198 	//now display the spectrum
2199 	variables[R].Series = NULL;
2200 	rp.Series = m_View->GetSeries ();
2201 	GOData *godata = go_data_vector_val_new (rp.Values, n, NULL);
2202 	gog_series_set_dim (rp.Series, 1, godata, NULL);
2203 	m_View->SetAxisBounds (GOG_AXIS_Y, rp.Min, rp.Max, false);
2204 	if (Xt < 0) {
2205 		xt.Name = _("Chemical shift");
2206 		xt.Symbol = 'X';
2207 		xt.Type = GCU_SPECTRUM_TYPE_INDEPENDENT;
2208 		xt.Unit = GCU_SPECTRUM_UNIT_HZ;
2209 		xt.Format = GCU_SPECTRUM_FORMAT_MAX;
2210 		xt.Factor = 1.;
2211 		xt.NbValues = n;
2212 		xt.Values = new double[n];
2213 		for (i = 0; i < n; i++)
2214 			xt.Values[i] = i * freq + shift;
2215 		xt.Min = xt.First = xt.Values[0];
2216 		xt.Max = xt.Last = xt.Values[n - 1];
2217 		xt.Series = NULL;
2218 		X = variables.size ();
2219 		variables.push_back (xt);
2220 	}
2221 	godata = go_data_vector_val_new (variables[X].Values, variables[X].NbValues, NULL);
2222 	gog_series_set_dim (rp.Series, 0, godata, NULL);
2223 	m_SpectrumType = GCU_SPECTRUM_NMR;
2224 	m_View->SetAxisBounds (GOG_AXIS_X, variables[X].Min, variables[X].Max, true);
2225 	m_View->SetAxisLabel (GOG_AXIS_X, _(UnitNames[variables[X].Unit]));
2226 	OnXUnitChanged (0);
2227 	// remove the last widget from the option box
2228 	m_View->DestroyExtraWidget ();
2229 	// now add the widgets appropriate for an NMR spectrum
2230 	GtkWidget *grid = gtk_grid_new (), *w;
2231 	if (!gtk_check_version (3, 2, 0))
2232 		gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
2233 	else
2234 		gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
2235 	if (go_finite (freq)) {
2236 		w = gtk_label_new (_("X unit:"));
2237 		gtk_container_add (GTK_CONTAINER (grid), w);
2238 		w = gtk_combo_box_text_new ();
2239 		gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Chemical shift (ppm)"));
2240 		gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Frequency (Hz)"));
2241 		SpectrumUnitType unit = (X >= 0)? variables[X].Unit: m_XUnit;
2242 		gtk_combo_box_set_active (GTK_COMBO_BOX (w), ((unit == GCU_SPECTRUM_UNIT_PPM)? 0: 1));
2243 		g_signal_connect (w, "changed", G_CALLBACK (on_xunit_changed), this);
2244 		gtk_container_add (GTK_CONTAINER (grid), w);
2245 	}
2246 	w = gtk_button_new_with_label (_("Show integral"));
2247 	g_signal_connect (w, "clicked", G_CALLBACK (on_show_integral), this);
2248 	gtk_container_add (GTK_CONTAINER (grid), w);
2249 	gtk_widget_show_all (grid);
2250 	m_View->AddToOptionBox (grid);
2251 }
2252 
OnXAxisInvert(bool inverted)2253 void SpectrumDocument::OnXAxisInvert (bool inverted)
2254 {
2255 	m_View->InvertAxis (GOG_AXIS_X, inverted);
2256 }
2257 
Loaded()2258 bool SpectrumDocument::Loaded () throw (gcu::LoaderError)
2259 {
2260 	bool hide_y_axis = false;
2261 	SpectrumUnitType unit = GCU_SPECTRUM_UNIT_MAX;
2262 	// doon't do anything for unsupported spectra
2263 	switch (m_SpectrumType) {
2264 	case GCU_SPECTRUM_NMR: {
2265 		if (x == NULL && X >= 0 && variables[X].Values == NULL)
2266 			return false;
2267 		// fix origin
2268 		if (go_finite (offset)) {
2269 			unsigned i;
2270 			if (x) {
2271 				double d = offset * freq - maxx;
2272 				maxx += d;
2273 				minx += d;
2274 				firstx += d;
2275 				lastx += d;
2276 				for (i = 0; i < npoints; i++)
2277 					x[i] += d;
2278 			} else if (X < 0) {
2279 				JdxVar xt;
2280 				xt.Name = _("Chemical shift");
2281 				xt.Symbol = 'X';
2282 				xt.Type = GCU_SPECTRUM_TYPE_INDEPENDENT;
2283 				xt.Unit = GCU_SPECTRUM_UNIT_HZ;
2284 				xt.Format = GCU_SPECTRUM_FORMAT_MAX;
2285 				xt.Factor = 1.;
2286 				xt.NbValues = npoints;
2287 				xt.Values = new double[npoints];
2288 				double freq = (maxx - minx) / npoints, shift = (maxx - minx) / 2. - offset;
2289 				maxx -= shift;
2290 				minx -= shift;
2291 				for (i = 0; i < npoints; i++)
2292 					xt.Values[i] = i * freq + minx;
2293 				xt.Min = xt.First = xt.Values[0];
2294 				xt.Max = xt.Last = xt.Values[npoints - 1];
2295 				xt.Series = NULL;
2296 				X = variables.size ();
2297 				variables.push_back (xt);
2298 				OnXUnitChanged (0);
2299 				minx = variables[X].Min;
2300 				maxx = variables[X].Max;
2301 			} else {
2302 				double d = offset * freq - variables[X].Max;
2303 				maxx = variables[X].Max += d;
2304 				minx = variables[X].Min += d;
2305 				variables[X].First += d;
2306 				variables[X].Last += d;
2307 				for (i = 0; i < npoints; i++)
2308 					variables[X].Values[i] += d;
2309 			}
2310 		} else if (go_finite (refpoint)) {
2311 			unsigned i;
2312 			double d = -refpoint;
2313 			if (x) {
2314 				maxx += d;
2315 				minx += d;
2316 				firstx += d;
2317 				lastx += d;
2318 				for (i = 0; i < npoints; i++)
2319 					x[i] += d;
2320 			} else {
2321 				maxx = variables[X].Max += d;
2322 				minx = variables[X].Min += d;
2323 				variables[X].First += d;
2324 				variables[X].Last += d;
2325 				for (i = 0; i < npoints; i++)
2326 					variables[X].Values[i] += d;
2327 			}
2328 		}
2329 		// add some widgets to the option box
2330 		GtkWidget *grid = gtk_grid_new (), *w;
2331 		if (!gtk_check_version (3, 2, 0))
2332 			gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
2333 		else
2334 			gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
2335 		if (go_finite (freq)) {
2336 			w = gtk_label_new (_("X unit:"));
2337 			gtk_container_add (GTK_CONTAINER (grid), w);
2338 			w = gtk_combo_box_text_new ();
2339 			gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Chemical shift (ppm)"));
2340 			gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Frequency (Hz)"));
2341 			SpectrumUnitType unit = (X >= 0)? variables[X].Unit: m_XUnit;
2342 			gtk_combo_box_set_active (GTK_COMBO_BOX (w), ((unit == GCU_SPECTRUM_UNIT_PPM)? 0: 1));
2343 			g_signal_connect (w, "changed", G_CALLBACK (on_xunit_changed), this);
2344 			gtk_container_add (GTK_CONTAINER (grid), w);
2345 		}
2346 		w = gtk_button_new_with_label (_("Show integral"));
2347 		g_signal_connect (w, "clicked", G_CALLBACK (on_show_integral), this);
2348 		gtk_container_add (GTK_CONTAINER (grid), w);
2349 		gtk_widget_show_all (grid);
2350 		m_View->AddToOptionBox (grid);
2351 		hide_y_axis = true;
2352 		break;
2353 	}
2354 	case GCU_SPECTRUM_NMR_FID: {
2355 		if (x == NULL && X >= 0 && variables[X].Values == NULL)
2356 			return false;
2357 		else if (x == NULL && X < 0) {
2358 			x = new double[npoints];
2359 			deltax = (maxx - minx) / (npoints - 1);
2360 			for (unsigned i = 0; i < npoints;i++)
2361 				x[i] = minx + i * deltax;
2362 			firstx = 0;
2363 			lastx = x[npoints - 1];
2364 		}
2365 		if (R >= 0 && I >= 0) {
2366 			GtkWidget *grid = gtk_grid_new ();
2367 			if (!gtk_check_version (3, 2, 0))
2368 				gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
2369 			else
2370 				gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
2371 			GtkWidget *w = gtk_button_new_with_label (_("Transform to spectrum"));
2372 			g_signal_connect (w, "clicked", G_CALLBACK (on_transform_fid), this);
2373 			if (!go_finite (offset))
2374 				gtk_widget_set_sensitive (w, false);
2375 			gtk_container_add (GTK_CONTAINER (grid), w);
2376 			gtk_widget_show_all (grid);
2377 			m_View->AddToOptionBox (grid);
2378 		}
2379 		hide_y_axis = true;
2380 		break;
2381 	}
2382 	case GCU_SPECTRUM_INFRARED:
2383 	case GCU_SPECTRUM_RAMAN:
2384 		unit = GCU_SPECTRUM_UNIT_MICROMETERS;
2385 //	case GCU_SPECTRUM_INFRARED_PEAK_TABLE:
2386 //	case GCU_SPECTRUM_INFRARED_INTERFEROGRAM:
2387 //	case GCU_SPECTRUM_INFRARED_TRANSFORMED:
2388 	case GCU_SPECTRUM_UV_VISIBLE:
2389 		if (x == NULL && X > 0 && variables[X].Values == NULL)
2390 			return false;
2391 		else {
2392 			if (unit == GCU_SPECTRUM_UNIT_MAX)
2393 				unit = GCU_SPECTRUM_UNIT_NANOMETERS;
2394 			// add some widgets to the option box
2395 			GtkWidget *grid = gtk_grid_new (), *w;
2396 			if (!gtk_check_version (3, 2, 0))
2397 				gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
2398 			else
2399 				gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
2400 			w = gtk_label_new (_("X unit:"));
2401 			gtk_container_add (GTK_CONTAINER (grid), w);
2402 			w = gtk_combo_box_text_new ();
2403 			GList *cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (w));
2404 			if (cells && cells->data) {
2405 				/* set "markup" as the target property */
2406 				gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (w), GTK_CELL_RENDERER (cells->data),
2407 								"markup", 0, NULL);
2408 			}
2409 			if  (unit == GCU_SPECTRUM_UNIT_NANOMETERS)
2410 				gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Wave length (nm)"));
2411 			else
2412 				gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Wave length (µm)"));
2413 			gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Wavenumber (cm<sup>−1</sup>)"));
2414 			SpectrumUnitType unit = (X >= 0)? variables[X].Unit: m_XUnit;
2415 			gtk_combo_box_set_active (GTK_COMBO_BOX (w), ((unit == GCU_SPECTRUM_UNIT_CM_1)? 1: 0));
2416 			g_signal_connect (w, "changed", G_CALLBACK (on_xunit_changed), this);
2417 			gtk_container_add (GTK_CONTAINER (grid), w);
2418 			m_XAxisInvertBtn = gtk_check_button_new_with_label (_("Invert X Axis"));
2419 			m_XAxisInvertSgn = g_signal_connect (m_XAxisInvertBtn, "toggled", G_CALLBACK (on_xaxis_invert), this);
2420 			gtk_container_add (GTK_CONTAINER (grid), m_XAxisInvertBtn);
2421 			unit = (Y >= 0)? variables[Y].Unit: m_YUnit;
2422 			if (unit == GCU_SPECTRUM_UNIT_ABSORBANCE || unit == GCU_SPECTRUM_UNIT_TRANSMITTANCE) {
2423 				w = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
2424 				gtk_container_add (GTK_CONTAINER (grid), w);
2425 				w = gtk_label_new (_("Y unit:"));
2426 				gtk_container_add (GTK_CONTAINER (grid), w);
2427 				w = gtk_combo_box_text_new ();
2428 				gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Absorbance"));
2429 				gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Transmittance"));
2430 				gtk_combo_box_set_active (GTK_COMBO_BOX (w), ((unit == GCU_SPECTRUM_UNIT_ABSORBANCE)? 0: 1));
2431 				g_signal_connect (w, "changed", G_CALLBACK (on_yunit_changed), this);
2432 				gtk_container_add (GTK_CONTAINER (grid), w);
2433 			}
2434 			gtk_widget_show_all (grid);
2435 			m_View->AddToOptionBox (grid);
2436 		}
2437 		break;
2438 //	case GCU_SPECTRUM_NMR_PEAK_TABLE:
2439 //	case GCU_SPECTRUM_NMR_PEAK_ASSIGNMENTS:
2440 	case GCU_SPECTRUM_MASS:
2441 		break;
2442 	default:
2443 		return false;
2444 	}
2445 	m_Empty = npoints == 0;
2446 	GOData *godata;
2447 	GogSeries *series = m_View->GetSeries ();
2448 	if (X >= 0 && variables[X].Values != NULL) {
2449 		godata = go_data_vector_val_new (variables[X].Values, npoints, NULL);
2450 		m_XUnit = variables[X].Unit;
2451 	}
2452 	else
2453 		godata = go_data_vector_val_new (x, npoints, NULL);
2454 	gog_series_set_dim (series, 0, godata, NULL);
2455 	if (Y >= 0 && variables[Y].Values != NULL) {
2456 		godata = go_data_vector_val_new (variables[Y].Values, npoints, NULL);
2457 		m_YUnit = variables[Y].Unit;
2458 	} else if (R >= 0 && variables[R].Values != NULL) {
2459 		godata = go_data_vector_val_new (variables[R].Values, npoints, NULL);
2460 		m_YUnit = variables[R].Unit;
2461 	} else
2462 		godata = go_data_vector_val_new (y, npoints, NULL);
2463 	gog_series_set_dim (series, 1, godata, NULL);
2464 	/* invert X-axis if needed */
2465 	bool invert_axis = false;
2466 	switch (m_XUnit) {
2467 	case GCU_SPECTRUM_UNIT_CM_1:
2468 	case GCU_SPECTRUM_UNIT_PPM:
2469 		invert_axis = true;
2470 		break;
2471 	case GCU_SPECTRUM_UNIT_HZ:
2472 		if (m_SpectrumType == GCU_SPECTRUM_NMR)
2473 			invert_axis = true;
2474 		break;
2475 	default:
2476 		break;
2477 	}
2478 	if (m_XAxisInvertBtn) {
2479 		g_signal_handler_block (m_XAxisInvertBtn, m_XAxisInvertSgn);
2480 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (m_XAxisInvertBtn), invert_axis);
2481 		g_signal_handler_unblock (m_XAxisInvertBtn, m_XAxisInvertSgn);
2482 	}
2483 	m_View->SetAxisBounds (GOG_AXIS_X, minx, maxx, invert_axis);
2484 	m_View->SetAxisBounds (GOG_AXIS_Y, miny, maxy, false);
2485 	if (hide_y_axis)
2486 		m_View->ShowAxis (GOG_AXIS_Y, false);
2487 	/* Add axes labels */
2488 	if (m_XUnit < GCU_SPECTRUM_UNIT_MAX)
2489 		m_View->SetAxisLabel (GOG_AXIS_X, _(UnitNames[m_XUnit]));
2490 	if (m_YUnit < GCU_SPECTRUM_UNIT_MAX)
2491 		m_View->SetAxisLabel (GOG_AXIS_Y, _(UnitNames[m_YUnit]));
2492 	return true;
2493 }
2494 
SetProperty(unsigned property,char const * value)2495 bool SpectrumDocument::SetProperty (unsigned property, char const *value)
2496 {
2497 	istringstream is (value);
2498 	switch (property) {
2499 	case GCU_PROP_DOC_TITLE:
2500 		SetTitle (value);
2501 		break;
2502 	case GCU_PROP_SPECTRUM_TYPE:
2503 		m_SpectrumType = get_spectrum_type_from_string (value);
2504 		break;
2505 	case GCU_PROP_SPECTRUM_NPOINTS:
2506 		is >> npoints;
2507 		break;
2508 	case GCU_PROP_SPECTRUM_DATA_X:
2509 		break;
2510 	case GCU_PROP_SPECTRUM_DATA_Y:
2511 		break;
2512 	case GCU_PROP_SPECTRUM_DATA_REAL: {
2513 		if (npoints == 0 || R >= 0)
2514 			return false;
2515 		JdxVar vr;
2516 		vr.Name = _("Real data");
2517 		vr.Symbol = 'r';
2518 		vr.Type = GCU_SPECTRUM_TYPE_DEPENDENT;
2519 		vr.Unit = GCU_SPECTRUM_UNIT_MAX;
2520 		vr.Format = GCU_SPECTRUM_FORMAT_MAX;
2521 		vr.Factor = 1.;
2522 		vr.NbValues = npoints;
2523 		vr.Values = new double[npoints];
2524 		for (unsigned i = 0; i < npoints; i++)
2525 			is >> vr.Values[i];
2526 		vr.First = vr.Values[0];
2527 		vr.Last = vr.Values[npoints - 1];
2528 		go_range_min (vr.Values, npoints, &vr.Min);
2529 		go_range_max (vr.Values, npoints, &vr.Max);
2530 		vr.Series = NULL;
2531 		Y = R = variables.size ();
2532 		variables.push_back (vr);
2533 		break;
2534 	}
2535 	case GCU_PROP_SPECTRUM_DATA_IMAGINARY: {
2536 		if (npoints == 0 || I >= 0)
2537 			return false;
2538 		JdxVar vi;
2539 		vi.Name = _("Imaginary data");
2540 		vi.Symbol = 'i';
2541 		vi.Type = GCU_SPECTRUM_TYPE_DEPENDENT;
2542 		vi.Unit = GCU_SPECTRUM_UNIT_MAX;
2543 		vi.Format = GCU_SPECTRUM_FORMAT_MAX;
2544 		vi.Factor = 1.;
2545 		vi.NbValues = npoints;
2546 		vi.Values = new double[npoints];
2547 		for (unsigned i = 0; i < npoints; i++)
2548 			is >> vi.Values[i];
2549 		vi.First = vi.Values[0];
2550 		vi.Last = vi.Values[npoints - 1];
2551 		go_range_min (vi.Values, npoints, &vi.Min);
2552 		go_range_max (vi.Values, npoints, &vi.Max);
2553 		vi.Series = NULL;
2554 		I = variables.size ();
2555 		variables.push_back (vi);
2556 		break;
2557 	}
2558 	case GCU_PROP_SPECTRUM_X_UNIT:
2559 			m_XUnit = (SpectrumUnitType) get_spectrum_data_from_string (value, Units, GCU_SPECTRUM_UNIT_MAX);
2560 		break;
2561 	case GCU_PROP_SPECTRUM_X_MIN:
2562 		is >> minx;
2563 		break;
2564 	case GCU_PROP_SPECTRUM_X_MAX:
2565 		is >> maxx;
2566 		break;
2567 	case GCU_PROP_SPECTRUM_X_OFFSET:
2568 		is >> offset;
2569 		break;
2570 	case GCU_PROP_SPECTRUM_NMR_FREQ:
2571 		is >> freq;
2572 		break;
2573 	default:
2574 		return false;
2575 	}
2576 	return true;
2577 }
2578 
2579 }	//	nampespace gcu
2580