1 /*
2 * Gnome Chemisty Utils
3 * spectrumview.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 "spectrumdoc.h"
25 #include "spectrumview.h"
26 #include <gsf/gsf-output-gio.h>
27 #include <glib/gi18n-lib.h>
28 #include <cmath>
29 #include <map>
30 #include <string>
31
32 using namespace std;
33
34 namespace gcugtk
35 {
36
37 class SpectrumViewPrivate
38 {
39 public:
40 static void OnSize (GtkWidget* w, GtkAllocation *allocation, SpectrumView *view);
41 };
42
OnSize(G_GNUC_UNUSED GtkWidget * w,GtkAllocation * allocation,SpectrumView * view)43 void SpectrumViewPrivate::OnSize (G_GNUC_UNUSED GtkWidget* w, GtkAllocation *allocation, SpectrumView *view)
44 {
45 view->m_Width = allocation->width;
46 view->m_Height = allocation->height;
47 }
48
on_min_changed(SpectrumView * view)49 static void on_min_changed (SpectrumView *view)
50 {
51 view->OnMinChanged ();
52 }
53
on_max_changed(SpectrumView * view)54 static void on_max_changed (SpectrumView *view)
55 {
56 view->OnMaxChanged ();
57 }
58
on_ymin_changed(SpectrumView * view)59 static void on_ymin_changed (SpectrumView *view)
60 {
61 view->OnYMinChanged ();
62 }
63
on_ymax_changed(SpectrumView * view)64 static void on_ymax_changed (SpectrumView *view)
65 {
66 view->OnYMaxChanged();
67 }
68
on_xrange_changed(SpectrumView * view)69 static void on_xrange_changed (SpectrumView *view)
70 {
71 view->OnXRangeChanged ();
72 }
73
on_yrange_changed(SpectrumView * view)74 static void on_yrange_changed (SpectrumView *view)
75 {
76 view->OnYRangeChanged ();
77 }
78
SpectrumView(SpectrumDocument * pDoc)79 SpectrumView::SpectrumView (SpectrumDocument *pDoc)
80 {
81 m_Doc = pDoc;
82 m_Widget = go_graph_widget_new (NULL);
83 g_signal_connect (G_OBJECT (m_Widget), "size_allocate", G_CALLBACK (SpectrumViewPrivate::OnSize), this);
84 GogGraph *graph = go_graph_widget_get_graph (GO_GRAPH_WIDGET (m_Widget));
85 /* Add a title */
86 GogLabel *label = (GogLabel *) g_object_new (GOG_TYPE_LABEL, NULL);
87 gog_object_add_by_name (GOG_OBJECT (graph), "Title", GOG_OBJECT (label));
88 /* Get the chart created by the widget initialization */
89 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
90 /* Create a scatter plot and add it to the chart */
91 GogPlot *plot = (GogPlot *) gog_plot_new_by_name ("GogXYPlot");
92 g_object_set (plot, "default-style-has-markers", false, NULL);
93 gog_object_add_by_name (GOG_OBJECT (chart), "Plot", GOG_OBJECT (plot));
94 /* Create a series for the plot */
95 m_Series = gog_plot_new_series (plot);
96 m_OptionBox = gtk_grid_new ();
97 g_object_set (G_OBJECT (m_OptionBox),
98 "orientation", GTK_ORIENTATION_VERTICAL,
99 "margin-left", 6,
100 "margin-top", 6,
101 "margin-right", 6,
102 NULL);
103 GtkGrid *grid = GTK_GRID (m_OptionBox);
104 if (!gtk_check_version (3, 2, 0)) {
105 gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
106 gtk_grid_set_row_spacing (grid, 6);
107 } else {
108 gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
109 gtk_grid_set_row_spacing (grid, 6);
110 }
111 GtkWidget *w = gtk_label_new (_("Minimum X value:"));
112 gtk_grid_attach (grid, w, 0, 0, 1, 1);
113 xminbtn = GTK_SPIN_BUTTON (gtk_spin_button_new_with_range (0., 1., 0.1));
114 minsgn = g_signal_connect_swapped (xminbtn, "value-changed", G_CALLBACK (on_min_changed), this);
115 gtk_grid_attach (grid, GTK_WIDGET (xminbtn), 1, 0, 1, 1);
116 w = gtk_label_new (_("Maximum X value:"));
117 gtk_grid_attach (grid, w, 2, 0, 1, 1);
118 xmaxbtn = GTK_SPIN_BUTTON (gtk_spin_button_new_with_range (0., 1., 0.1));
119 maxsgn = g_signal_connect_swapped (xmaxbtn, "value-changed", G_CALLBACK (on_max_changed), this);
120 gtk_grid_attach (grid, GTK_WIDGET (xmaxbtn), 3, 0, 1, 1);
121 xrange = GTK_RANGE (gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, NULL));
122 g_object_set (G_OBJECT (xrange), "hexpand", true, NULL);
123 gtk_widget_set_sensitive (GTK_WIDGET (xrange), false);
124 xrangesgn = g_signal_connect_swapped (xrange, "value-changed", G_CALLBACK (on_xrange_changed), this);
125 gtk_grid_attach (grid, GTK_WIDGET (xrange), 4, 0, 1, 1);
126 w = gtk_label_new (_("Minimum Y value:"));
127 gtk_grid_attach (grid, w, 0, 1, 1, 1);
128 yminbtn = GTK_SPIN_BUTTON (gtk_spin_button_new_with_range (0., 1., 0.1));
129 yminsgn = g_signal_connect_swapped (yminbtn, "value-changed", G_CALLBACK (on_ymin_changed), this);
130 gtk_grid_attach (grid, GTK_WIDGET (yminbtn), 1, 1, 1, 1);
131 w = gtk_label_new (_("Maximum Y value:"));
132 gtk_grid_attach (grid, w, 2, 1, 1, 1);
133 ymaxbtn = GTK_SPIN_BUTTON (gtk_spin_button_new_with_range (0., 1., 0.1));
134 ymaxsgn = g_signal_connect_swapped (ymaxbtn, "value-changed", G_CALLBACK (on_ymax_changed), this);
135 gtk_grid_attach (grid, GTK_WIDGET (ymaxbtn), 3, 1, 1, 1);
136 yrange = GTK_RANGE (gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, NULL));
137 gtk_widget_set_sensitive (GTK_WIDGET (yrange), false);
138 yrangesgn = g_signal_connect_swapped (yrange, "value-changed", G_CALLBACK (on_yrange_changed), this);
139 gtk_grid_attach (grid, GTK_WIDGET (yrange), 4, 1, 1, 1);
140 m_ExtraWidget = NULL;
141 }
142
~SpectrumView()143 SpectrumView::~SpectrumView ()
144 {
145 g_signal_handler_disconnect (xminbtn, minsgn);
146 g_signal_handler_disconnect (xmaxbtn, maxsgn);
147 g_signal_handler_disconnect (yminbtn, yminsgn);
148 g_signal_handler_disconnect (ymaxbtn, ymaxsgn);
149 g_signal_handler_disconnect (xrange, xrangesgn);
150 g_signal_handler_disconnect (yrange, yrangesgn);
151 }
152
SetAxisBounds(GogAxisType target,double min,double max,bool inverted)153 void SpectrumView::SetAxisBounds (GogAxisType target, double min, double max, bool inverted)
154 {
155 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
156 GSList *axes = gog_chart_get_axes (chart, target);
157 GogAxis *axis = GOG_AXIS (axes->data);
158 gog_axis_set_bounds (axis, min, max);
159 g_object_set (axis, "invert-axis", inverted, NULL);
160 if (target == GOG_AXIS_X) {
161 double l = log10 (fabs (max - min));
162 int n = (l < 3)? rint (3 - l): 0;
163 xstep = pow (10., -n);
164 g_signal_handler_block (xminbtn, minsgn);
165 g_signal_handler_block (xmaxbtn, maxsgn);
166 gtk_spin_button_set_range (xminbtn, min, max);
167 gtk_spin_button_set_range (xmaxbtn, min, max);
168 gtk_spin_button_set_increments (xminbtn, xstep, 100 * xstep);
169 gtk_spin_button_set_increments (xmaxbtn, xstep, 100 * xstep);
170 gtk_range_set_increments (xrange, xstep, 100 * xstep);
171 gtk_range_set_inverted (xrange, !inverted);
172 g_signal_handler_block (xrange, xrangesgn);
173 gtk_range_set_value (xrange, 0.);
174 gtk_widget_set_sensitive (GTK_WIDGET (xrange), false);
175 g_signal_handler_unblock (xrange, xrangesgn);
176 gtk_spin_button_set_value (xminbtn, min);
177 gtk_spin_button_set_value (xmaxbtn, max);
178 gtk_spin_button_set_digits (xminbtn, n);
179 gtk_spin_button_set_digits (xmaxbtn, n);
180 g_signal_handler_unblock (xminbtn, minsgn);
181 g_signal_handler_unblock (xmaxbtn, maxsgn);
182 xmin = min;
183 xmax = max;
184 }
185 else if (target == GOG_AXIS_Y) {
186 double l = log10 (fabs (max - min));
187 int n = (l < 3)? rint (3 - l): 0;
188 ystep = pow (10., -n);
189 g_signal_handler_block (yminbtn, yminsgn);
190 g_signal_handler_block (ymaxbtn, ymaxsgn);
191 gtk_spin_button_set_range (yminbtn, min, max);
192 gtk_spin_button_set_range (ymaxbtn, min, max);
193 gtk_spin_button_set_increments (yminbtn, ystep, 100 * ystep);
194 gtk_spin_button_set_increments (ymaxbtn, ystep, 100 * ystep);
195 gtk_range_set_increments (yrange, ystep, 100 * ystep);
196 gtk_range_set_inverted (yrange, !inverted);
197 g_signal_handler_block (yrange, yrangesgn);
198 gtk_range_set_value (yrange, 0.);
199 gtk_widget_set_sensitive (GTK_WIDGET (yrange), false);
200 g_signal_handler_unblock (yrange, yrangesgn);
201 gtk_spin_button_set_value (yminbtn, min);
202 gtk_spin_button_set_value (ymaxbtn, max);
203 gtk_spin_button_set_digits (yminbtn, n);
204 gtk_spin_button_set_digits (ymaxbtn, n);
205 g_signal_handler_unblock (yminbtn, yminsgn);
206 g_signal_handler_unblock (ymaxbtn, ymaxsgn);
207 ymin = min;
208 ymax = max;
209 }
210 }
211
SetAxisLabel(GogAxisType target,char const * unit)212 void SpectrumView::SetAxisLabel (GogAxisType target, char const *unit)
213 {
214 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
215 GSList *axes = gog_chart_get_axes (chart, target);
216 GogObject *axis = GOG_OBJECT (axes->data);
217 GOData *data = go_data_scalar_str_new (unit, false);
218 GogObject *label = gog_object_get_child_by_name (axis, "Label");
219 if (label) {
220 // remove the old label if any
221 gog_object_clear_parent (label);
222 g_object_unref (label);
223 }
224 label = GOG_OBJECT (g_object_new (GOG_TYPE_LABEL, NULL));
225 g_object_set (G_OBJECT (label), "allow-markup", TRUE, NULL);
226 gog_dataset_set_dim (GOG_DATASET (label), 0, data, NULL);
227 gog_object_add_by_name (axis, "Label", label);
228
229 }
230
ShowAxis(GogAxisType target,G_GNUC_UNUSED bool show)231 void SpectrumView::ShowAxis (GogAxisType target, G_GNUC_UNUSED bool show)
232 {
233 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
234 GSList *axes = gog_chart_get_axes (chart, target);
235 GogObject *axis = GOG_OBJECT (axes->data);
236 g_object_set (G_OBJECT (axis), "major-tick-labeled", false, NULL);
237 GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (axis));
238 style->line.dash_type = GO_LINE_NONE;
239 style->line.auto_dash = false;
240 }
241
Render(cairo_t * cr,double width,double height)242 void SpectrumView::Render (cairo_t *cr, double width, double height)
243 {
244 gog_graph_render_to_cairo (go_graph_widget_get_graph (GO_GRAPH_WIDGET (m_Widget)), cr, width, height);
245 }
246
OnMinChanged()247 void SpectrumView::OnMinChanged ()
248 {
249 double min = gtk_spin_button_get_value (xminbtn);
250 double max = gtk_spin_button_get_value (xmaxbtn);
251 if (max <= min) {
252 double step;
253 gtk_spin_button_get_increments (xminbtn, &step, NULL);
254 min = max - step;
255 g_signal_handler_block (xminbtn, minsgn);
256 gtk_spin_button_set_value (xminbtn, min);
257 g_signal_handler_unblock (xminbtn, minsgn);
258 }
259 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
260 GSList *axes = gog_chart_get_axes (chart, GOG_AXIS_X);
261 GogAxis *axis = GOG_AXIS (axes->data);
262 gog_axis_set_bounds (axis, min, max);
263 g_signal_handler_block (xrange, xrangesgn);
264 if (max - min < xmax - xmin) {
265 gtk_range_set_range (xrange, 0., xmax - xmin - max + min);
266 gtk_range_set_value (xrange, min - xmin);
267 gtk_widget_set_sensitive (GTK_WIDGET (xrange), true);
268 } else {
269 gtk_range_set_value (xrange, 0.);
270 gtk_widget_set_sensitive (GTK_WIDGET (xrange), false);
271 }
272 g_signal_handler_unblock (xrange, xrangesgn);
273 }
274
OnYMinChanged()275 void SpectrumView::OnYMinChanged ()
276 {
277 double min = gtk_spin_button_get_value (yminbtn);
278 double max = gtk_spin_button_get_value (ymaxbtn);
279 if (max <= min) {
280 double step;
281 gtk_spin_button_get_increments (yminbtn, &step, NULL);
282 min = max - step;
283 g_signal_handler_block (yminbtn, yminsgn);
284 gtk_spin_button_set_value (yminbtn, min);
285 g_signal_handler_unblock (yminbtn, yminsgn);
286 }
287 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
288 GSList *axes = gog_chart_get_axes (chart, GOG_AXIS_Y);
289 GogAxis *axis = GOG_AXIS (axes->data);
290 gog_axis_set_bounds (axis, min, max);
291 g_signal_handler_block (yrange, yrangesgn);
292 if (max - min < ymax - ymin) {
293 gtk_range_set_range (yrange, 0., ymax - ymin - max + min);
294 gtk_range_set_value (yrange, min - ymin);
295 gtk_widget_set_sensitive (GTK_WIDGET (yrange), true);
296 } else {
297 gtk_range_set_value (yrange, 0.);
298 gtk_widget_set_sensitive (GTK_WIDGET (yrange), false);
299 }
300 g_signal_handler_unblock (yrange, yrangesgn);
301 }
302
OnMaxChanged()303 void SpectrumView::OnMaxChanged ()
304 {
305 double min = gtk_spin_button_get_value (xminbtn);
306 double max = gtk_spin_button_get_value (xmaxbtn);
307 if (max <= min) {
308 double step;
309 gtk_spin_button_get_increments (xmaxbtn, &step, NULL);
310 max = min + step;
311 g_signal_handler_block (xmaxbtn, maxsgn);
312 gtk_spin_button_set_value (xmaxbtn, max);
313 g_signal_handler_unblock (xmaxbtn, maxsgn);
314 }
315 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
316 GSList *axes = gog_chart_get_axes (chart, GOG_AXIS_X);
317 GogAxis *axis = GOG_AXIS (axes->data);
318 gog_axis_set_bounds (axis, min, max);
319 g_signal_handler_block (xrange, xrangesgn);
320 if (max - min < xmax - xmin) {
321 gtk_range_set_range (xrange, 0., xmax - xmin - max + min);
322 gtk_range_set_value (xrange, min - xmin);
323 gtk_widget_set_sensitive (GTK_WIDGET (xrange), true);
324 } else {
325 gtk_range_set_value (xrange, 0.);
326 gtk_widget_set_sensitive (GTK_WIDGET (xrange), false);
327 }
328 g_signal_handler_unblock (xrange, xrangesgn);
329 }
330
OnYMaxChanged()331 void SpectrumView::OnYMaxChanged ()
332 {
333 double min = gtk_spin_button_get_value (yminbtn);
334 double max = gtk_spin_button_get_value (ymaxbtn);
335 if (max <= min) {
336 double step;
337 gtk_spin_button_get_increments (ymaxbtn, &step, NULL);
338 max = min + step;
339 g_signal_handler_block (ymaxbtn, ymaxsgn);
340 gtk_spin_button_set_value (ymaxbtn, max);
341 g_signal_handler_unblock (ymaxbtn, ymaxsgn);
342 }
343 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
344 GSList *axes = gog_chart_get_axes (chart, GOG_AXIS_Y);
345 GogAxis *axis = GOG_AXIS (axes->data);
346 gog_axis_set_bounds (axis, min, max);
347 g_signal_handler_block (yrange, yrangesgn);
348 if (max - min < ymax - ymin) {
349 gtk_range_set_range (yrange, 0., ymax - ymin - max + min);
350 gtk_range_set_value (yrange, min - xmin);
351 gtk_widget_set_sensitive (GTK_WIDGET (yrange), true);
352 } else {
353 gtk_range_set_value (yrange, 0.);
354 gtk_widget_set_sensitive (GTK_WIDGET (yrange), false);
355 }
356 g_signal_handler_unblock (yrange, yrangesgn);
357 }
358
OnXRangeChanged()359 void SpectrumView::OnXRangeChanged ()
360 {
361 double max = gtk_spin_button_get_value (xmaxbtn) - gtk_spin_button_get_value (xminbtn);
362 double min = xmin + gtk_range_get_value (xrange);
363 max += min;
364 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
365 GSList *axes = gog_chart_get_axes (chart, GOG_AXIS_X);
366 GogAxis *axis = GOG_AXIS (axes->data);
367 gog_axis_set_bounds (axis, min, max);
368 g_signal_handler_block (xminbtn, minsgn);
369 gtk_spin_button_set_value (xminbtn, min);
370 g_signal_handler_unblock (xminbtn, minsgn);
371 g_signal_handler_block (xmaxbtn, maxsgn);
372 gtk_spin_button_set_value (xmaxbtn, max);
373 g_signal_handler_unblock (xmaxbtn, maxsgn);
374 }
375
OnYRangeChanged()376 void SpectrumView::OnYRangeChanged ()
377 {
378 double max = gtk_spin_button_get_value (ymaxbtn) - gtk_spin_button_get_value (yminbtn);
379 double min = ymin + gtk_range_get_value (yrange);
380 max += min;
381 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
382 GSList *axes = gog_chart_get_axes (chart, GOG_AXIS_Y);
383 GogAxis *axis = GOG_AXIS (axes->data);
384 gog_axis_set_bounds (axis, min, max);
385 g_signal_handler_block (yminbtn, yminsgn);
386 gtk_spin_button_set_value (yminbtn, min);
387 g_signal_handler_unblock (yminbtn, yminsgn);
388 g_signal_handler_block (ymaxbtn, ymaxsgn);
389 gtk_spin_button_set_value (ymaxbtn, max);
390 g_signal_handler_unblock (ymaxbtn, ymaxsgn);
391 }
392
NewSeries(bool new_plot)393 GogSeries *SpectrumView::NewSeries (bool new_plot)
394 {
395 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
396 GogPlot *plot = NULL;
397 if (new_plot) {
398 /* Create a scatter plot and add it to the chart */
399 plot = (GogPlot *) gog_plot_new_by_name ("GogXYPlot");
400 g_object_set (plot, "default-style-has-markers", false, NULL);
401 gog_object_add_by_name (GOG_OBJECT (chart), "Plot", GOG_OBJECT (plot));
402 } else {
403 // find the first plot in the chart
404 GSList *l = gog_object_get_children (GOG_OBJECT (chart), gog_object_find_role_by_name (GOG_OBJECT (chart), "Plot"));
405 plot = (GogPlot*) l->data;
406 g_slist_free (l);
407 }
408 return gog_plot_new_series (plot);
409 }
410
SaveAsImage(string const & filename,char const * mime_type,unsigned width,unsigned height) const411 void SpectrumView::SaveAsImage (string const &filename, char const *mime_type, unsigned width, unsigned height) const
412 {
413 char *fname = go_mime_to_image_format (mime_type);
414 GOImageFormat format = go_image_get_format_from_name ((fname)? fname: filename.c_str ());
415 if (format == GO_IMAGE_FORMAT_UNKNOWN)
416 return;
417 GError *error = NULL;
418 GsfOutput *output = gsf_output_gio_new_for_uri (filename.c_str (), &error);
419 if (error) {
420 g_error_free (error);
421 return;
422 }
423 GogGraph *graph = gog_graph_dup (go_graph_widget_get_graph (GO_GRAPH_WIDGET (m_Widget)));
424 gog_graph_set_size (graph, width, height);
425 gog_graph_export_image (graph, format, output, -1., -1.);
426 g_object_unref (graph);
427 }
428
InvertAxis(GogAxisType target,bool inverted)429 void SpectrumView::InvertAxis (GogAxisType target, bool inverted)
430 {
431 GogChart *chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET (m_Widget));
432 GSList *axes = gog_chart_get_axes (chart, target);
433 GogAxis *axis = GOG_AXIS (axes->data);
434 g_object_set (axis, "invert-axis", inverted, NULL);
435 }
436
AddToOptionBox(GtkWidget * w)437 void SpectrumView::AddToOptionBox (GtkWidget *w)
438 {
439 gtk_grid_attach (GTK_GRID (m_OptionBox), w, 0, 2, 5, 1);
440 m_ExtraWidget = w;
441 }
442
DestroyExtraWidget()443 void SpectrumView::DestroyExtraWidget ()
444 {
445 if (m_ExtraWidget) {
446 gtk_widget_destroy (m_ExtraWidget);
447 m_ExtraWidget = NULL;
448 }
449 }
450
451 } // namespace gcu
452