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