1 /* schemes_ui.c */
2 /*
3 * ggobi
4 * Copyright (C) AT&T, Duncan Temple Lang, Dianne Cook 1999-2005
5 *
6 * ggobi is free software; you may use, redistribute, and/or modify it
7 * under the terms of the Eclipse Public License, which is distributed
8 * with the source code and displayed on the ggobi web site,
9 * www.ggobi.org. For more information, contact the authors:
10 *
11 * Deborah F. Swayne dfs@research.att.com
12 * Di Cook dicook@iastate.edu
13 * Duncan Temple Lang duncan@wald.ucdavis.edu
14 * Andreas Buja andreas.buja@wharton.upenn.edu
15 */
16
17 /*
18 * It is my understanding that I'm supposed to use gdk_colormap_free_colors
19 * to free anything I've allocated with gdk_colormap_alloc_color(s), but
20 * it just isn't working to free all the svis.color_map colors.
21 * dfs, 10/30/2001
22 */
23
24 #include <string.h>
25 #include <stdlib.h>
26
27 #include <gtk/gtk.h>
28 #include "vars.h"
29 #include "externs.h"
30
31 #include "colorscheme.h"
32
33 static gint xmargin = 20;
34 static gint ymargin = 20;
35
36 GtkWidget *createColorSchemeTree (int numTypes, gchar * schemeTypes[],
37 ggobid * gg);
38 static void entry_set_scheme_name (ggobid * gg);
39
40 /*-------------------------------------------------------------------*/
41 /* Using colorschemed objects */
42 /*-------------------------------------------------------------------*/
43
44 void
colorscheme_set_cb(GtkTreeSelection * sel,GtkTreeView * tree_view)45 colorscheme_set_cb (GtkTreeSelection * sel, GtkTreeView * tree_view)
46 {
47 ggobid *gg = GGobiFromWidget (GTK_WIDGET (tree_view), true);
48 gboolean rval = false;
49 GtkTreeModel *model;
50 GGobiData *d;
51 colorschemed *scheme;
52 GtkTreeIter iter;
53
54 /*
55 * gg->svis sometimes has its own scheme, and then we'll use it.
56 * If it's null, we use gg->activeColorScheme. We update
57 * gg->activeColorScheme to the value of scheme when the user asks.
58 */
59
60 if (!gtk_tree_selection_get_selected (sel, &model, &iter))
61 return;
62
63 gtk_tree_model_get (model, &iter, 1, &scheme, -1);
64
65 if (scheme) {
66 gg->svis.scheme = scheme;
67 entry_set_scheme_name (gg);
68 colorscheme_init (scheme);
69 }
70
71 /*-- delete this line once debugging is complete --*/
72 displays_plot (NULL, FULL, gg);
73
74 /*-- rebuild the drawing area in this window --*/
75 /*
76 * This is using two expose events, which is odd: it's something
77 * to do with getting the numbers of points in each bin to appear,
78 * and there's probably a way to do it better.
79 */
80 tree_view = gtk_tree_selection_get_tree_view (sel);
81 if (tree_view != NULL) {
82 d = (GGobiData *) g_object_get_data (G_OBJECT (tree_view), "datad");
83 }
84 else {
85 d = (GGobiData *) g_slist_nth_data (gg->d, 0);
86 }
87
88 g_signal_emit_by_name (G_OBJECT (gg->svis.da), "expose_event",
89 (gpointer) gg, (gpointer) & rval);
90 }
91
92
93 /*-------------------------------------------------------------------------*/
94
95 /*-- called when closed from the close button --*/
96 static void
close_btn_cb(GtkWidget * w,ggobid * gg)97 close_btn_cb (GtkWidget * w, ggobid * gg)
98 {
99 gtk_widget_hide (gg->svis.window);
100 }
101
102 /*-- called when closed from the window manager --*/
103 static void
close_wmgr_cb(GtkWidget * w,GdkEventButton * event,ggobid * gg)104 close_wmgr_cb (GtkWidget * w, GdkEventButton * event, ggobid * gg)
105 {
106 gtk_widget_hide (gg->svis.window);
107 }
108
109 static gint
da_configure_cb(GtkWidget * w,GdkEventConfigure * event,ggobid * gg)110 da_configure_cb (GtkWidget * w, GdkEventConfigure * event, ggobid * gg)
111 {
112 /*-- Create new backing pixmaps of the appropriate size --*/
113 if (gg->svis.pix != NULL)
114 gdk_pixmap_unref (gg->svis.pix);
115 gg->svis.pix = gdk_pixmap_new (w->window,
116 w->allocation.width, w->allocation.height,
117 -1);
118
119 gtk_widget_queue_draw (w);
120
121 return false;
122 }
123
124 /*
125 * Set the bin boundaries (the values of svis.pct[]) by
126 * simply dividing the range of the data into
127 * scheme->n equal-sized pieces
128 */
129 static void
bin_boundaries_set(GGobiData * d,ggobid * gg)130 bin_boundaries_set (GGobiData * d, ggobid * gg)
131 {
132 gint k;
133
134 /*
135 * These numbers are the upper boundaries of each interval.
136 * By default, they start at .1 and end at 1.0.
137 */
138 for (k = 0; k < gg->svis.npct; k++) {
139 gg->svis.pct[k] = (gfloat) (k + 1) / (gfloat) gg->svis.npct;
140 }
141 }
142
143 static void
da_expose_cb(GtkWidget * w,GdkEventExpose * event,ggobid * gg)144 da_expose_cb (GtkWidget * w, GdkEventExpose * event, ggobid * gg)
145 {
146 gint height = w->allocation.height - 2 * ymargin;
147 gint x0, x1, k, hgt;
148 colorschemed *scheme = (gg->svis.scheme != NULL) ?
149 gg->svis.scheme : gg->activeColorScheme;
150 GGobiData *d = NULL;
151 GdkPixmap *pix = gg->svis.pix;
152
153 if (gg->svis.GC == NULL)
154 gg->svis.GC = gdk_gc_new (w->window);
155
156 hgt = height / (scheme->n - 1);
157
158 if (gg->svis.npct != scheme->n) {
159 gg->svis.npct = scheme->n;
160 gg->svis.pct = (gfloat *) g_realloc (gg->svis.pct,
161 gg->svis.npct * sizeof (gfloat));
162 bin_boundaries_set (d, gg);
163 }
164
165 /*-- clear the pixmap --*/
166 gdk_gc_set_foreground (gg->svis.GC, &scheme->rgb_bg);
167 gdk_draw_rectangle (pix, gg->svis.GC, TRUE,
168 0, 0, w->allocation.width, w->allocation.height);
169
170
171 /*-- draw the color bars --*/
172 x0 = xmargin;
173 for (k = 0; k < scheme->n; k++) {
174 x1 = xmargin + gg->svis.pct[k] * (w->allocation.width - 2 * xmargin);
175 gdk_gc_set_foreground (gg->svis.GC, &scheme->rgb[k]);
176 gdk_draw_rectangle (pix, gg->svis.GC, TRUE, x0, ymargin, x1 - x0, height);
177 x0 = x1;
178 }
179
180 gdk_draw_pixmap (w->window, gg->svis.GC, pix,
181 0, 0, 0, 0, w->allocation.width, w->allocation.height);
182 }
183
184
185 /*
186 * Find out whether it's possible to use the new scheme
187 * without losing brushing information. If so, go ahead
188 * and change index values if that's required
189 *
190 * If force is true, remap even if the number of colors
191 * is too large.
192 */
193 /*-- move this to color.c --*/
194 gboolean
colors_remap(colorschemed * scheme,gboolean force,ggobid * gg)195 colors_remap (colorschemed * scheme, gboolean force, ggobid * gg)
196 {
197 gint i, k;
198 gboolean all_colors_p[MAXNCOLORS];
199 GSList *l;
200 GGobiData *d;
201 gushort colors_used[MAXNCOLORS];
202 gint maxcolorid, ncolors_used;
203 gboolean remap_ok = true;
204
205 for (k = 0; k < MAXNCOLORS; k++)
206 all_colors_p[k] = false;
207
208 /*-- find out all the colors (indices) are currently in use --*/
209 for (l = gg->d; l; l = l->next) {
210 d = (GGobiData *) l->data;
211 datad_colors_used_get (&ncolors_used, colors_used, d, gg);
212 for (k = 0; k < ncolors_used; k++)
213 all_colors_p[colors_used[k]] = true;
214 }
215
216 /*-- find out how many colors are currently in use --*/
217 ncolors_used = 0;
218 for (k = 0; k < MAXNCOLORS; k++)
219 if (all_colors_p[k])
220 ncolors_used++;
221
222 /*-- find the largest color index currently in use --*/
223 maxcolorid = -1;
224 for (k = MAXNCOLORS - 1; k > 0; k--) {
225 if (all_colors_p[k]) {
226 maxcolorid = k;
227 break;
228 }
229 }
230
231 if (maxcolorid < scheme->n)
232 /* no problem, go right ahead */
233 ;
234 else if (!force && ncolors_used > scheme->n) {
235
236 /* fatal: bail out with a warning */
237 quick_message
238 ("The number of colors now in use is greater than than\nthe number of colors in the chosen color scheme. Please choose a color scheme with more colours, or use less colors in the plot.",
239 false);
240
241 remap_ok = false;
242 }
243 else if (maxcolorid >= scheme->n) {
244 /*-- build the vector that will be used to reset the current indices --*/
245 gint *newind = (gint *) g_malloc ((maxcolorid + 1) * sizeof (gint));
246 gint n = 0;
247
248 for (k = 0; k <= maxcolorid; k++) {
249 if (all_colors_p[k]) {
250 newind[k] = n;
251
252 /*
253 * try to achieve a decent spread of the color values,
254 * which is helpful in most color maps
255 */
256 n += ((scheme->n + 1) / ncolors_used);
257 /*-- make sure we haven't gone too far --*/
258 if (n >= scheme->n - 1)
259 n = scheme->n - 1;
260
261 }
262 }
263
264 for (l = gg->d; l; l = l->next) {
265 d = (GGobiData *) l->data;
266 for (i = 0; i < d->nrows; i++) {
267 d->color.els[i] = newind[d->color.els[i]];
268 d->color_now.els[i] = newind[d->color_now.els[i]];
269 /*-- what about color_prev? --*/
270 }
271 }
272 g_free (newind);
273
274 }
275 else {
276 g_printerr ("nothing else should possibly happen, no?\n");
277 }
278
279 return remap_ok;
280 }
281
282 static void
scale_set_cb(GtkWidget * w,ggobid * gg)283 scale_set_cb (GtkWidget * w, ggobid * gg)
284 {
285 GtkWidget *tree_view = get_tree_view_from_object (G_OBJECT (w));
286 GGobiData *d = NULL;
287 gboolean rval = false;
288
289 if (tree_view)
290 d = (GGobiData *) g_object_get_data (G_OBJECT (tree_view), "datad");
291
292 /*
293 * If we've been using gg->svis.scheme, set gg->activeColorScheme
294 * to the current scheme.
295 */
296 if (gg->svis.scheme) {
297 colorschemed *scheme = gg->svis.scheme;
298
299 /*-- if no current color index is too high, continue --*/
300 if (!colors_remap (scheme, false, gg))
301 return;
302
303 gg->activeColorScheme = scheme;
304 gg->svis.scheme = NULL;
305 }
306
307 displays_plot (NULL, FULL, gg);
308 g_signal_emit_by_name (G_OBJECT (gg->svis.da), "expose_event",
309 (gpointer) gg, (gpointer) & rval);
310
311 entry_set_scheme_name (gg);
312
313 symbol_window_redraw (gg);
314 cluster_table_update (d, gg);
315 }
316
317
318 static void
entry_set_scheme_name(ggobid * gg)319 entry_set_scheme_name (ggobid * gg)
320 {
321 gtk_entry_set_text (GTK_ENTRY (gg->svis.entry_preview),
322 (gg->svis.scheme != NULL) ? gg->svis.scheme->name :
323 gg->activeColorScheme->name);
324
325 gtk_entry_set_text (GTK_ENTRY (gg->svis.entry_applied),
326 gg->activeColorScheme->name);
327 }
328
329 void
svis_window_open(ggobid * gg)330 svis_window_open (ggobid * gg)
331 {
332 GtkWidget *vbox;
333 GtkWidget *hb;
334 GtkWidget *btn, *label;
335
336 /*-- for colorscales --*/
337 GtkWidget *hpane, *tr, *sw;
338 static gchar *colorscaletype_lbl[UNKNOWN_COLOR_TYPE] = {
339 "<b>Diverging</b>",
340 "<b>Sequential</b>",
341 "<b>Spectral</b>",
342 "<b>Qualitative</b>"
343 };
344
345 if (gg->svis.window == NULL) {
346
347 gg->svis.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
348 gtk_window_set_title (GTK_WINDOW (gg->svis.window),
349 "Choose Color Scheme");
350 g_signal_connect (G_OBJECT (gg->svis.window),
351 "delete_event", G_CALLBACK (close_wmgr_cb), gg);
352
353 hpane = gtk_hpaned_new ();
354 //gtk_paned_set_position (GTK_PANED(hpane), 150);
355 gtk_container_add (GTK_CONTAINER (gg->svis.window), hpane);
356
357 /* Color scheme tree */
358 sw = gtk_scrolled_window_new (NULL, NULL);
359 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
360 GTK_SHADOW_IN);
361 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
362 GTK_POLICY_AUTOMATIC,
363 GTK_POLICY_AUTOMATIC);
364 gtk_container_add (GTK_CONTAINER (hpane), sw);
365
366 tr = createColorSchemeTree (UNKNOWN_COLOR_TYPE, colorscaletype_lbl, gg);
367 gtk_widget_set_size_request (sw, 150, 20);
368 gtk_container_add (GTK_CONTAINER (sw), tr);
369
370
371 /*
372 * Right half of window
373 */
374 vbox = gtk_vbox_new (false, 0);
375 gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
376 gtk_box_set_spacing (GTK_BOX (vbox), 5);
377 gtk_container_add (GTK_CONTAINER (hpane), vbox);
378
379 /* Name currently in use */
380 hb = gtk_hbox_new (true, 0);
381 gtk_box_pack_start (GTK_BOX (vbox), hb, true, true, 5);
382 label = gtk_label_new ("Color scheme in use");
383 gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
384 gtk_box_pack_start (GTK_BOX (hb), label, true, true, 0);
385 gg->svis.entry_applied = gtk_entry_new ();
386 gtk_editable_set_editable (GTK_EDITABLE (gg->svis.entry_applied), false);
387 gtk_tooltips_set_tip (GTK_TOOLTIPS (gg->tips), gg->svis.entry_applied,
388 "The name of the currently active color scheme.",
389 NULL);
390 gtk_box_pack_start (GTK_BOX (hb), gg->svis.entry_applied, true, true, 0);
391 /**/
392 /* preview scheme */
393 hb = gtk_hbox_new (true, 0);
394 gtk_box_pack_start (GTK_BOX (vbox), hb, true, true, 5);
395 label = gtk_label_new ("Color scheme in preview");
396 gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
397 gtk_box_pack_start (GTK_BOX (hb), label, true, true, 0);
398
399 gg->svis.entry_preview = gtk_entry_new ();
400 gtk_editable_set_editable (GTK_EDITABLE (gg->svis.entry_preview), false);
401 gtk_tooltips_set_tip (GTK_TOOLTIPS (gg->tips), gg->svis.entry_preview,
402 "The name of the color scheme whose colors are displayed below.",
403 NULL);
404 gtk_box_pack_start (GTK_BOX (hb), gg->svis.entry_preview, true, true, 0);
405
406
407 /* Drawing area */
408 gg->svis.da = gtk_drawing_area_new ();
409 gtk_widget_set_double_buffered (gg->svis.da, false);
410 gtk_widget_set_size_request (GTK_WIDGET (gg->svis.da), 300, 150);
411 gtk_box_pack_start (GTK_BOX (vbox), gg->svis.da, false, false, 0);
412
413 g_signal_connect (G_OBJECT (gg->svis.da),
414 "configure_event",
415 G_CALLBACK (da_configure_cb), (gpointer) gg);
416 g_signal_connect (G_OBJECT (gg->svis.da),
417 "expose_event",
418 G_CALLBACK (da_expose_cb), (gpointer) gg);
419
420 gtk_widget_set_events (gg->svis.da, GDK_EXPOSURE_MASK);
421
422 /* Initializes both entries */
423 entry_set_scheme_name (gg);
424
425 /*-- add a close button --*/
426 gtk_box_pack_start (GTK_BOX (vbox), gtk_hseparator_new (),
427 false, true, 2);
428 hb = gtk_hbox_new (false, 2);
429 gtk_box_pack_start (GTK_BOX (vbox), hb, false, false, 1);
430
431 /* Apply button */
432 btn = gtk_button_new_from_stock (GTK_STOCK_APPLY);
433 gtk_tooltips_set_tip (GTK_TOOLTIPS (gg->tips), btn,
434 "Make this the current color scheme for brushing in ggobi, preserving current color groups. If the number of colors in the new scheme is less than the number of colors currently in use, this won't work.",
435 NULL);
436 gtk_box_pack_start (GTK_BOX (hb), btn, true, true, 2);
437 g_signal_connect (G_OBJECT (btn), "clicked",
438 G_CALLBACK (scale_set_cb), gg);
439
440 btn = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
441 gtk_tooltips_set_tip (GTK_TOOLTIPS (gg->tips), btn,
442 "Close the window", NULL);
443 gtk_box_pack_start (GTK_BOX (hb), btn, true, true, 2);
444 g_signal_connect (G_OBJECT (btn), "clicked",
445 G_CALLBACK (close_btn_cb), gg);
446 }
447
448 gtk_widget_show_all (gg->svis.window);
449 gdk_window_raise (gg->svis.window->window);
450 }
451
452 GtkWidget *createSchemeColorsTree (colorschemed * scheme);
453
454 /**
455 Create the tree displaying the colorscheme information.
456 This displays the different levels:
457 types of schemes,
458 different schemes within each type,
459 colors within each scheme.
460
461 */
462 GtkWidget *
createColorSchemeTree(gint numTypes,gchar * schemeTypes[],ggobid * gg)463 createColorSchemeTree (gint numTypes, gchar * schemeTypes[], ggobid * gg)
464 {
465 //GtkWidget *item;
466 //GtkWidget **trees, *top;
467 /*GtkWidget *tree; */
468 GtkWidget *tree_view;
469 GtkTreeStore *model;
470 GtkTreeIter *iters;
471 gint n;
472 GList *l;
473 colorschemed *scheme;
474
475 model = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
476
477 iters = g_new (GtkTreeIter, numTypes);
478 for (n = 0; n < numTypes; n++) {
479 gtk_tree_store_append (GTK_TREE_STORE (model), &iters[n], NULL);
480 gtk_tree_store_set (GTK_TREE_STORE (model), &iters[n], 0, schemeTypes[n],
481 1, NULL, -1);
482 }
483
484 for (l = gg->colorSchemes; l; l = l->next) {
485 GtkTreeIter iter;
486 scheme = (colorschemed *) l->data;
487 gtk_tree_store_append (GTK_TREE_STORE (model), &iter,
488 &iters[scheme->type]);
489 gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 0, scheme->name, 1,
490 scheme, -1);
491 }
492
493 tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
494 GGobi_widget_set (tree_view, gg, true);
495
496 populate_tree_view (tree_view, NULL, 1, false, GTK_SELECTION_SINGLE,
497 G_CALLBACK (colorscheme_set_cb), tree_view);
498
499 return (tree_view);
500 }
501