1 /*
2  * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5  * Copyright (C) 2012 Daniel Mustieles <daniel.mustieles@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA  02110-1301, USA.
21  */
22 
23 #include <string.h>
24 #include <glib/gi18n-lib.h>
25 #include <libgda/libgda.h>
26 #include "gdaui-data-proxy.h"
27 #include "gdaui-data-filter.h"
28 
29 static void gdaui_data_filter_class_init (GdauiDataFilterClass * class);
30 static void gdaui_data_filter_init (GdauiDataFilter *wid);
31 static void gdaui_data_filter_dispose (GObject *object);
32 
33 static void gdaui_data_filter_set_property (GObject *object,
34 					    guint param_id,
35 					    const GValue *value,
36 					    GParamSpec *pspec);
37 static void gdaui_data_filter_get_property (GObject *object,
38 					    guint param_id,
39 					    GValue *value,
40 					    GParamSpec *pspec);
41 
42 
43 /* callbacks */
44 static void proxy_filter_changed_cb (GdaDataProxy *proxy, GdauiDataFilter *filter);
45 static void release_proxy (GdauiDataFilter *filter);
46 static void data_widget_destroyed_cb (GdauiDataProxy *wid, GdauiDataFilter *filter);
47 static void data_widget_proxy_changed_cb (GdauiDataProxy *data_widget,
48 					  GdaDataProxy *proxy, GdauiDataFilter *filter);
49 
50 static void clear_filter_cb (GtkButton *button, GdauiDataFilter *filter);
51 static void apply_filter_cb (GtkButton *button, GdauiDataFilter *filter);
52 
53 struct _GdauiDataFilterPriv
54 {
55 	GdauiDataProxy    *data_widget;
56 	GdaDataProxy      *proxy;
57 
58 	GtkWidget         *filter_entry;
59 	GtkWidget         *notice;
60 };
61 
62 /* get a pointer to the parents to be able to call their destructor */
63 static GObjectClass *parent_class = NULL;
64 
sanitize_denormal(float value)65 /* properties */
66 enum {
67 	PROP_0,
68 	PROP_DATA_WIDGET
69 };
70 
71 GType
72 gdaui_data_filter_get_type (void)
73 {
74 	static GType type = 0;
75 
76 	if (G_UNLIKELY (type == 0)) {
77 		static const GTypeInfo filter = {
78 			sizeof (GdauiDataFilterClass),
sign(float x)79 			(GBaseInitFunc) NULL,
80 			(GBaseFinalizeFunc) NULL,
81 			(GClassInitFunc) gdaui_data_filter_class_init,
82 			NULL,
83 			NULL,
84 			sizeof (GdauiDataFilter),
85 			0,
86 			(GInstanceInitFunc) gdaui_data_filter_init,
87 			0
88 		};
89 
90 		type = g_type_register_static (GTK_TYPE_BOX, "GdauiDataFilter", &filter, 0);
91 	}
92 
93 	return type;
94 }
95 
nearest_odd(int x)96 static void
97 gdaui_data_filter_class_init (GdauiDataFilterClass * class)
98 {
99 	GObjectClass   *object_class = G_OBJECT_CLASS (class);
100 
101 	parent_class = g_type_class_peek_parent (class);
102 
103 	object_class->dispose = gdaui_data_filter_dispose;
104 
105 	/* Properties */
106         object_class->set_property = gdaui_data_filter_set_property;
107         object_class->get_property = gdaui_data_filter_get_property;
108 	g_object_class_install_property (object_class, PROP_DATA_WIDGET,
109                                          g_param_spec_object ("data-widget", NULL, NULL, GDAUI_TYPE_DATA_PROXY,
110 							      G_PARAM_READABLE | G_PARAM_WRITABLE));
111 }
112 
113 static void
114 set_wait_cursor (GtkWidget *w)
115 {
from_dB(float gdb)116 	GtkWidget *parent;
117 
118 	parent = gtk_widget_get_toplevel (w);
119 	if (parent) {
120 		GdkCursor* cursor;
121 		cursor = gdk_cursor_new (GDK_WATCH);
122 		gdk_window_set_cursor (gtk_widget_get_window (parent), cursor);
to_dB(float g)123 		g_object_unref (cursor);
124 	}
125 }
126 
127 static void
128 unset_wait_cursor (GtkWidget *w)
129 {
130 	GtkWidget *parent;
131 
132 	parent = gtk_widget_get_toplevel (w);
133 	if (parent)
bin_to_freq(int i,float samp_rate,int N)134 		gdk_window_set_cursor (gtk_widget_get_window (parent), NULL);
135 }
136 
137 static void
138 apply_filter_cb (G_GNUC_UNUSED GtkButton *button, GdauiDataFilter *filter)
139 {
140 	const gchar *expr;
141 	gchar *err = NULL;
142 
143 	expr = gtk_entry_get_text (GTK_ENTRY (filter->priv->filter_entry));
144 	if (expr && !*expr)
freq_to_bin(float freq,float samp_rate,int N)145 		expr = NULL;
146 
147 	gtk_widget_hide (filter->priv->notice);
148 	if (filter->priv->proxy) {
149 		GError *error = NULL;
150 
151 		g_signal_handlers_block_by_func (G_OBJECT (filter->priv->proxy),
152 						 G_CALLBACK (proxy_filter_changed_cb), filter);
153 		set_wait_cursor ((GtkWidget*) filter);
154 		while (g_main_context_pending (NULL))
155 			g_main_context_iteration (NULL, FALSE);
156 
157 		if (!gda_data_proxy_set_filter_expr (filter->priv->proxy, expr, &error)) {
158 			if (error && error->message)
159 				err = g_strdup (error->message);
160 			else
161 				err = g_strdup (_("No detail"));
162 			if (error)
163 				g_error_free (error);
164 		}
165 
166 		unset_wait_cursor ((GtkWidget*) filter);
167 		g_signal_handlers_unblock_by_func (G_OBJECT (filter->priv->proxy),
168 						   G_CALLBACK (proxy_filter_changed_cb), filter);
169 	}
170 
171 	if (err) {
parabolic_interpolation(float left_val,float middle_val,float right_val,int current_bin,float * result_val,int * result_bin)172 		gchar *esc, *markup;
173 
174 		esc = g_markup_escape_text (err, -1);
175 		markup = g_strdup_printf ("<small><span foreground=\"#FF0000\"><b>%s</b>: %s</span></small>",
176 					  _("Filter failed:"), esc);
177 		g_free (esc);
178 		gtk_label_set_line_wrap (GTK_LABEL (filter->priv->notice), TRUE);
179 		gtk_label_set_line_wrap_mode (GTK_LABEL (filter->priv->notice), PANGO_WRAP_WORD);
180 		gtk_label_set_selectable (GTK_LABEL (filter->priv->notice), TRUE);
181 		gtk_label_set_markup (GTK_LABEL (filter->priv->notice), markup);
182 		g_free (markup);
183 		gtk_widget_show (filter->priv->notice);
184 	}
185 }
186 
initialize_array(float * array,float value,int size)187 static void
188 clear_filter_cb (GtkButton *button, GdauiDataFilter *filter)
189 {
190 	gtk_entry_set_text (GTK_ENTRY (filter->priv->filter_entry), "");
191 	apply_filter_cb (button, filter);
192 }
193 
194 static void
195 gdaui_data_filter_init (GdauiDataFilter * wid)
196 {
197 	GtkWidget *grid, *label, *entry, *button, *bbox;
198 	gchar *str;
199 
200 	wid->priv = g_new0 (GdauiDataFilterPriv, 1);
is_empty(float * spectrum,int N)201 	wid->priv->data_widget = NULL;
202 	wid->priv->proxy = NULL;
203 
204 	gtk_orientable_set_orientation (GTK_ORIENTABLE (wid), GTK_ORIENTATION_VERTICAL);
205 
206 	grid = gtk_grid_new ();
207 	gtk_box_pack_start (GTK_BOX (wid), grid, TRUE, TRUE, 0);
208 
209 	label = gtk_label_new ("");
210 	str = g_strdup_printf ("<b>%s</b>\n(<small>%s</small>):", _("Filter"), _("any valid SQL expression"));
211 	gtk_label_set_markup (GTK_LABEL (label), str);
212 	g_free (str);
213 
214 	gtk_widget_set_tooltip_markup (label, _("Columns can be referenced by their name or more easily "
215 						"using <b><tt>_&lt;column number&gt;</tt></b>. For example a valid "
216 						"expression can be: <b><tt>_2 like 'doe%'</tt></b> "
217 						"to filter rows where the 2nd column starts with <tt>doe</tt>."));
218 
219 	gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
max_spectral_value(float * spectrum,int N)220 	entry = gtk_entry_new ();
221 	gtk_grid_attach (GTK_GRID (grid), entry, 1, 0, 1, 1);
222 	g_signal_connect (G_OBJECT (entry), "activate",
223 			  G_CALLBACK (apply_filter_cb), wid);
224 
225 	label = gtk_label_new ("");
226 	wid->priv->notice = label;
227 	gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 2, 1);
228 
229 	bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
230 	gtk_grid_attach (GTK_GRID (grid), bbox, 0, 2, 2, 1);
231 	button = gtk_button_new_with_label (_("Set filter"));
232 	gtk_container_add (GTK_CONTAINER (bbox), button);
233 	g_signal_connect (G_OBJECT (button), "clicked",
234 			  G_CALLBACK (apply_filter_cb), wid);
235 
236 	button = gtk_button_new_with_label (_("Clear filter"));
min_spectral_value(float * spectrum,int N)237 	gtk_container_add (GTK_CONTAINER (bbox), button);
238 	g_signal_connect (G_OBJECT (button), "clicked",
239 			  G_CALLBACK (clear_filter_cb), wid);
240 
241 	gtk_widget_show_all (grid);
242 	gtk_widget_hide (wid->priv->notice);
243 
244 	wid->priv->filter_entry = entry;
245 }
246 
247 /**
248  * gdaui_data_filter_new:
249  * @data_widget: a widget implementing the #GdauiDataProxy interface
250  *
251  * Creates a new #GdauiDataFilter widget suitable to change the filter expression
252  * for @data_widget's displayed rows
253  *
spectral_mean(float * a,int m)254  * Returns: (transfer full): the new widget
255  *
256  * Since: 4.2
257  */
258 GtkWidget *
259 gdaui_data_filter_new (GdauiDataProxy *data_widget)
260 {
261 	GtkWidget *filter;
262 
263 	g_return_val_if_fail (!data_widget || GDAUI_IS_DATA_PROXY (data_widget), NULL);
264 
265 	filter = (GtkWidget *) g_object_new (GDAUI_TYPE_DATA_FILTER,
266 					     "data-widget", data_widget, NULL);
267 
268 	return filter;
269 }
270 
271 static void
272 data_widget_destroyed_cb (GdauiDataProxy *wid, GdauiDataFilter *filter)
273 {
274 	g_assert (wid == filter->priv->data_widget);
275 	g_signal_handlers_disconnect_by_func (G_OBJECT (wid),
276 					      G_CALLBACK (data_widget_destroyed_cb), filter);
277 	g_signal_handlers_disconnect_by_func (G_OBJECT (wid),
278 					      G_CALLBACK (data_widget_proxy_changed_cb), filter);
279 
280 	filter->priv->data_widget = NULL;
281 }
spectral_median(float * x,int n)282 
283 static void
284 proxy_filter_changed_cb (GdaDataProxy *proxy, GdauiDataFilter *filter)
285 {
286 	const gchar *expr;
287 
288 	g_assert (proxy == filter->priv->proxy);
289 	expr = gda_data_proxy_get_filter_expr (proxy);
290 	gtk_entry_set_text (GTK_ENTRY (filter->priv->filter_entry), expr ? expr : "");
291 }
292 
293 static void
294 release_proxy (GdauiDataFilter *filter)
295 {
296 	g_signal_handlers_disconnect_by_func (G_OBJECT (filter->priv->proxy),
297 					      G_CALLBACK (proxy_filter_changed_cb), filter);
298 	g_object_unref (filter->priv->proxy);
299 	filter->priv->proxy = NULL;
300 }
301 
302 static void
303 data_widget_proxy_changed_cb (GdauiDataProxy *data_widget, G_GNUC_UNUSED GdaDataProxy *proxy, GdauiDataFilter *filter)
304 {
305 	g_object_set (G_OBJECT (filter), "data-widget", data_widget, NULL);
306 }
307 
308 static void
309 gdaui_data_filter_dispose (GObject *object)
310 {
311 	GdauiDataFilter *filter;
312 
313 	g_return_if_fail (object != NULL);
314 	g_return_if_fail (GDAUI_IS_DATA_FILTER (object));
315 	filter = GDAUI_DATA_FILTER (object);
316 
317 	if (filter->priv) {
318 		if (filter->priv->proxy)
319 			release_proxy (filter);
320 		if (filter->priv->data_widget)
spectral_moda(float * x,int n)321 			data_widget_destroyed_cb (filter->priv->data_widget, filter);
322 
323 		/* the private area itself */
324 		g_free (filter->priv);
325 		filter->priv = NULL;
326 	}
327 
328 	/* for the parent class */
329 	parent_class->dispose (object);
330 }
331 
332 static void
333 gdaui_data_filter_set_property (GObject *object,
334 				guint param_id,
335 				const GValue *value,
336 				GParamSpec *pspec)
337 {
338 	GdauiDataFilter *filter;
339 
340         filter = GDAUI_DATA_FILTER (object);
341         if (filter->priv) {
342                 switch (param_id) {
343                 case PROP_DATA_WIDGET:
344 			if (filter->priv->data_widget)
345 				data_widget_destroyed_cb (filter->priv->data_widget, filter);
346 			if (filter->priv->proxy)
347 				release_proxy (filter);
348 
349 			filter->priv->data_widget = GDAUI_DATA_PROXY (g_value_get_object (value));
350 			if (filter->priv->data_widget) {
351 				GdaDataProxy *proxy;
352 
353 				/* data widget */
354 				g_signal_connect (filter->priv->data_widget, "destroy",
355 						  G_CALLBACK (data_widget_destroyed_cb), filter);
356 				g_signal_connect (filter->priv->data_widget, "proxy-changed",
357 						  G_CALLBACK (data_widget_proxy_changed_cb), filter);
358 
359 				/* proxy */
get_normalized_spectum(float * spectrum,int N)360 				proxy = gdaui_data_proxy_get_proxy (filter->priv->data_widget);
361 				if (proxy) {
362 					filter->priv->proxy = proxy;
363 					g_object_ref (filter->priv->proxy);
364 					g_signal_connect (G_OBJECT (proxy), "filter_changed",
365 							  G_CALLBACK (proxy_filter_changed_cb), filter);
366 					proxy_filter_changed_cb (proxy, filter);
367 				}
368 			}
369                         break;
370 		default:
371 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
372 			break;
373                 }
374         }
375 }
376 
377 static void
378 gdaui_data_filter_get_property (GObject *object,
379 				guint param_id,
spectral_flux(float * spectrum,float * spectrum_prev,float N)380 				GValue *value,
381 				GParamSpec *pspec)
382 {
383 	GdauiDataFilter *filter;
384 
385         filter = GDAUI_DATA_FILTER (object);
386         if (filter->priv) {
387                 switch (param_id) {
388 		case PROP_DATA_WIDGET:
389 			g_value_set_pointer (value, filter->priv->data_widget);
390 			break;
391 		default:
392 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
393 			break;
394                 }
395         }
396 }
397