The Gnome Chemistry Utils  0.13.7
gcuperiodic.c
00001 /*
00002  * Gnome Chemisty Utils
00003  * gcuperiodic.c
00004  *
00005  * Copyright (C) 2002-2012 Jean Bréfort <jean.brefort@normalesup.org>
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as
00009  * published by the Free Software Foundation; either version 3 of the
00010  * License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00020  * USA
00021  */
00022 
00023 #include "config.h"
00024 #include "gcuperiodic.h"
00025 #include <gcu/chemistry.h>
00026 #include <goffice/goffice.h>
00027 #include <string.h>
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <gsf/gsf-impl-utils.h>
00031 #include <glib/gi18n-lib.h>
00032 
00033 struct _GcuPeriodic
00034 {
00035         GtkBin bin;
00036 
00037         GtkGrid *grid;
00038         GtkToggleButton* buttons[119];
00039         GtkLabel* labels[119];
00040         double red[119], blue[119], green[119];
00041         GtkNotebook *book;
00042         guint Z;
00043         gboolean can_unselect;
00044         unsigned colorstyle;
00045         GArray *colorschemes;
00046         unsigned nbschemes;
00047         unsigned tips;
00048 };
00049 
00050 struct _GcuPeriodicClass
00051 {
00052         GtkBinClass parent_class;
00053 
00054         void (* element_changed_event)(GcuPeriodic *periodic);
00055 };
00056 
00057 GType
00058 gcu_periodic_color_style_get_type (void)
00059 {
00060   static GType etype = 0;
00061   if (etype == 0) {
00062     static const GEnumValue values[] = {
00063       { GCU_PERIODIC_COLOR_NONE, "GCU_PERIODIC_COLOR_NONE", "none" },
00064       { GCU_PERIODIC_COLOR_DEFAULT, "GCU_PERIODIC_COLOR_DEFAULT", "default" },
00065       { 0, NULL, NULL }
00066     };
00067     etype = g_enum_register_static ("GcuPeriodicColorStyle", values);
00068   }
00069   return etype;
00070 }
00071 #define GCU_TYPE_PERIODIC_COLOR_STYLE_TYPE (gcu_periodic_color_style_get_type())
00072 
00073 static GtkBinClass *parent_class = NULL;
00074 
00075 struct ColorScheme {
00076         GcuPeriodicColorFunc f;
00077         int page;
00078         gpointer data;
00079 };
00080 
00081 enum {
00082   ELEMENT_CHANGED,
00083   LAST_SIGNAL
00084 };
00085 
00086 enum {
00087         PROP_0,
00088         PROP_CAN_UNSELECT,
00089         PROP_COLOR_STYLE
00090 };
00091 
00092 static guint gcu_periodic_signals[LAST_SIGNAL] = { 0 };
00093 
00094 void on_clicked (GtkToggleButton *button, GcuPeriodic* periodic)
00095 {
00096         static gboolean change = FALSE;
00097         if (button != periodic->buttons[0]) {
00098                 const gchar* name;
00099                 change = TRUE;
00100                 if (periodic->buttons[0])
00101                         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (periodic->buttons[0]), FALSE);
00102                 periodic->buttons[0] = button;
00103                 name = gtk_buildable_get_name (GTK_BUILDABLE (periodic->buttons[0]));
00104                 periodic->Z = atoi (name + 3);
00105                 g_signal_emit (periodic, gcu_periodic_signals[ELEMENT_CHANGED], 0, periodic->Z);
00106                 change = FALSE;
00107         } else if (!change) {
00108                 if (periodic->can_unselect) {
00109                         periodic->buttons[0] = NULL;
00110                         periodic->Z = 0;
00111                         g_signal_emit (periodic, gcu_periodic_signals[ELEMENT_CHANGED], 0, 0);
00112                 } else if (periodic->buttons[0])
00113                         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (periodic->buttons[0]), TRUE);
00114         }
00115 }
00116 
00117 static void
00118 gcu_periodic_set_property (GObject              *object,
00119                                 guint                 param_id,
00120                                 const GValue         *value,
00121                                 GParamSpec           *pspec)
00122 {
00123         GcuPeriodic *periodic;
00124         g_return_if_fail (object != NULL);
00125         g_return_if_fail (GCU_IS_PERIODIC (object));
00126 
00127         periodic = GCU_PERIODIC (object);
00128 
00129         switch (param_id) {
00130         case PROP_CAN_UNSELECT:
00131                 periodic->can_unselect = g_value_get_boolean (value);
00132                 break;
00133 
00134         case PROP_COLOR_STYLE: {
00135                 unsigned style = g_value_get_uint (value);
00136                 if (style < GCU_PERIODIC_COLOR_MAX + periodic->nbschemes) {
00137                         periodic->colorstyle = style;
00138                         int page = (style >= GCU_PERIODIC_COLOR_MAX)?
00139                                 g_array_index (periodic->colorschemes, struct ColorScheme, style - GCU_PERIODIC_COLOR_MAX).page: 0;
00140                         gtk_notebook_set_current_page (periodic->book, page);
00141                         gcu_periodic_set_colors (periodic);
00142                 } else
00143                         g_warning (_("Out of range value %d for property \"color-style\" for GcuPeriodic instance %p\n"), style, periodic);
00144                 break;
00145         }
00146 
00147         default:
00148                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
00149                 break;
00150         }
00151 }
00152 
00153 static void
00154 gcu_periodic_get_property (GObject              *object,
00155                                 guint                 param_id,
00156                                 GValue               *value,
00157                                 GParamSpec           *pspec)
00158 {
00159         GcuPeriodic *periodic;
00160 
00161         g_return_if_fail (object != NULL);
00162         g_return_if_fail (GCU_IS_PERIODIC (object));
00163 
00164         periodic = GCU_PERIODIC (object);
00165 
00166         switch (param_id) {
00167         case PROP_CAN_UNSELECT:
00168                 g_value_set_boolean (value, periodic->can_unselect);
00169                 break;
00170 
00171         case PROP_COLOR_STYLE:
00172                 g_value_set_uint (value, periodic->colorstyle);
00173                 break;
00174 
00175         default:
00176                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
00177                 break;
00178         }
00179 }
00180 
00181 static void gcu_periodic_get_preferred_height (GtkWidget *w, gint *minimum_height, gint *natural_height)
00182 {
00183         GtkWidget *child = gtk_bin_get_child (GTK_BIN (w));
00184         gboolean visible = FALSE;
00185         if (child)
00186                 g_object_get (G_OBJECT (child), "visible", &visible, NULL);
00187         if (visible)
00188                 gtk_widget_get_preferred_height (child, minimum_height, natural_height);
00189         else
00190                 *minimum_height = *natural_height = 0;
00191 }
00192 
00193 static void gcu_periodic_get_preferred_width (GtkWidget *w, gint *minimum_width, gint *natural_width)
00194 {
00195         GtkWidget *child = gtk_bin_get_child (GTK_BIN (w));
00196         gboolean visible = FALSE;
00197         if (child)
00198                 g_object_get (G_OBJECT (child), "visible", &visible, NULL);
00199         if (visible)
00200                 gtk_widget_get_preferred_width (child, minimum_width, natural_width);
00201         else
00202                 *minimum_width = *natural_width = 0;
00203 }
00204 
00205 static void gcu_periodic_size_allocate (GtkWidget *w, GtkAllocation *alloc)
00206 {
00207         GtkWidget *child = gtk_bin_get_child (GTK_BIN (w));
00208         gboolean visible = FALSE;
00209         if (child)
00210                 g_object_get (G_OBJECT (child), "visible", &visible, NULL);
00211         if (visible)
00212                 gtk_widget_size_allocate (child, alloc);
00213         (GTK_WIDGET_CLASS (parent_class))->size_allocate (w, alloc);
00214 }
00215 
00216 static void gcu_periodic_finalize (GObject *object)
00217 {
00218         GcuPeriodic *periodic = (GcuPeriodic*) object;
00219 
00220         g_array_free (periodic->colorschemes, FALSE);
00221 
00222         if (G_OBJECT_CLASS (parent_class)->finalize)
00223                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
00224 }
00225 
00226 static void gcu_periodic_class_init (GcuPeriodicClass *klass)
00227 {
00228         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
00229         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
00230         parent_class = (GtkBinClass*) g_type_class_peek_parent (klass);
00231 
00232         gobject_class->set_property = gcu_periodic_set_property;
00233         gobject_class->get_property = gcu_periodic_get_property;
00234         klass->element_changed_event = NULL;
00235         gcu_periodic_signals[ELEMENT_CHANGED] =
00236         g_signal_new ("element_changed",
00237                                   G_TYPE_FROM_CLASS(gobject_class),
00238                                   G_SIGNAL_RUN_LAST,
00239                                   G_STRUCT_OFFSET(GcuPeriodicClass, element_changed_event),
00240                                   NULL, NULL,
00241                                   g_cclosure_marshal_VOID__UINT,
00242                                   G_TYPE_NONE, 1,
00243                                   G_TYPE_UINT
00244                                   );
00245         g_object_class_install_property
00246                         (gobject_class,
00247                          PROP_CAN_UNSELECT,
00248                          g_param_spec_boolean ("can_unselect", NULL, NULL,
00249                                    FALSE,
00250                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
00251         g_object_class_install_property
00252                         (gobject_class,
00253                          PROP_COLOR_STYLE,
00254                          g_param_spec_uint ("color-style", NULL, NULL,
00255                                                                 GCU_PERIODIC_COLOR_NONE, G_MAXUINT,
00256                                                                 GCU_PERIODIC_COLOR_NONE,
00257                                                                 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
00258         gobject_class->finalize = gcu_periodic_finalize;
00259         widget_class->get_preferred_height = gcu_periodic_get_preferred_height;
00260         widget_class->get_preferred_width = gcu_periodic_get_preferred_width;
00261         widget_class->size_allocate = gcu_periodic_size_allocate;
00262 }
00263 
00264 static void on_draw (GtkWidget *w, cairo_t *cr, GcuPeriodic *periodic)
00265 {
00266         if (periodic->colorstyle != GCU_PERIODIC_COLOR_NONE) {
00267                 GtkAllocation alloc;
00268                 unsigned i = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (w), "elt"));
00269                 gtk_widget_get_allocation (w, &alloc);
00270                 cairo_rectangle (cr, 0, 0, alloc.width, alloc.height);
00271                 cairo_set_source_rgb (cr, periodic->red[i], periodic->green[i], periodic->blue[i]);
00272                 cairo_fill (cr);
00273         }
00274         GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (w))->draw (w, cr);
00275 }
00276 
00277 static void gcu_periodic_init (GcuPeriodic *periodic)
00278 {
00279         GtkBuilder* xml;
00280         char name[8] = "elt";
00281         GtkToggleButton* button;
00282         int i;
00283         xml = go_gtk_builder_new (UIDIR"/gcuperiodic.ui", GETTEXT_PACKAGE, NULL);
00284         g_return_if_fail (xml);
00285         periodic->grid = GTK_GRID (gtk_builder_get_object (xml, "periodic-grid"));
00286         periodic->book = GTK_NOTEBOOK (gtk_builder_get_object (xml, "book"));
00287         periodic->colorstyle = GCU_PERIODIC_COLOR_NONE;
00288         memset(periodic->buttons, 0, sizeof (GtkToggleButton*) * 119);
00289         for (i = 1; i <= 118; i++) {
00290                 sprintf(name + 3, "%d", i);
00291                 button = (GtkToggleButton*) gtk_builder_get_object (xml, name);
00292                 if (GTK_IS_TOGGLE_BUTTON (button)) {
00293                         gtk_widget_set_tooltip_text (GTK_WIDGET(button), gcu_element_get_name(i));
00294                         periodic->buttons[i] = button;
00295                         periodic->labels[i] = GTK_LABEL (gtk_bin_get_child (GTK_BIN (button)));
00296                         g_object_set_data (G_OBJECT (periodic->labels[i]), "elt", GUINT_TO_POINTER (i));
00297                         g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (on_clicked), periodic);
00298                         g_signal_connect (G_OBJECT (periodic->labels[i]), "draw", G_CALLBACK (on_draw), periodic);
00299                 }
00300         }
00301         periodic->Z = 0;
00302         gtk_container_add (GTK_CONTAINER (periodic), GTK_WIDGET (periodic->grid));
00303         gtk_widget_show_all (GTK_WIDGET (periodic));
00304         periodic->colorschemes = g_array_new (FALSE, FALSE, sizeof (struct ColorScheme));
00305         g_object_unref (xml);
00306 }
00307 
00308 GSF_CLASS (GcuPeriodic, gcu_periodic,
00309            gcu_periodic_class_init, gcu_periodic_init,
00310            GTK_TYPE_BIN)
00311 
00312 GtkWidget* gcu_periodic_new ()
00313 {
00314         return GTK_WIDGET (g_object_new (GCU_TYPE_PERIODIC, NULL));
00315 }
00316 
00317 guint gcu_periodic_get_element(GcuPeriodic* periodic)
00318 {
00319         g_return_val_if_fail(GCU_IS_PERIODIC(periodic), 0);
00320         return periodic->Z;
00321 }
00322 
00323 void gcu_periodic_set_element (GcuPeriodic* periodic, guint element)
00324 {
00325         g_return_if_fail(GCU_IS_PERIODIC(periodic));
00326         if (periodic->can_unselect && periodic->buttons[0]) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(periodic->buttons[0]), FALSE);
00327         if (element)
00328         {
00329                 gtk_toggle_button_set_active(periodic->buttons[element], TRUE);
00330                 periodic->buttons[0] = periodic->buttons[element];
00331                 periodic->Z = element;
00332         }
00333         else if (periodic->can_unselect)
00334         {
00335                 periodic->buttons[0] = NULL;
00336                 periodic->Z = 0;
00337         }
00338 }
00339 
00340 void gcu_periodic_set_colors(GcuPeriodic *periodic)
00341 {
00342         const double *colors;
00343         PangoAttribute *attr;
00344         PangoAttrList *l;
00345         int i;
00346         GdkRGBA rgba;
00347         rgba.alpha = 1.;
00348         GcuPeriodicColorFunc func = NULL;
00349         gpointer data = NULL;
00350         if (periodic->colorstyle >= GCU_PERIODIC_COLOR_MAX) {
00351                 func = g_array_index (periodic->colorschemes, struct ColorScheme, periodic->colorstyle - GCU_PERIODIC_COLOR_MAX).f;
00352                 data = g_array_index (periodic->colorschemes, struct ColorScheme, periodic->colorstyle - GCU_PERIODIC_COLOR_MAX).data;
00353         }
00354         for (i = 1; i <= 118; i++)
00355         {
00356                 if (!periodic->labels[i])
00357                         continue;
00358                 switch (periodic->colorstyle)
00359                 {
00360                 case GCU_PERIODIC_COLOR_NONE:
00361                         l = pango_attr_list_new ();
00362                         gtk_label_set_attributes (periodic->labels[i], l);
00363                 break;
00364                 case GCU_PERIODIC_COLOR_DEFAULT:
00365                         colors = gcu_element_get_default_color(i);
00366                         periodic->red[i] = colors[0];
00367                         periodic->green[i] = colors[1];
00368                         periodic->blue[i] = colors[2];
00369                         if (colors[0] > 0.6 ||  colors[1] > 0.6 || colors[2] > 0.6)
00370                                 attr = pango_attr_foreground_new (0, 0, 0);
00371                         else
00372                                 attr = pango_attr_foreground_new (65535, 65535, 65535);
00373                         attr->start_index = 0;
00374                         attr->end_index = 100;
00375                         l = pango_attr_list_new ();
00376                         pango_attr_list_insert (l, attr);
00377                         gtk_label_set_attributes (periodic->labels[i], l);
00378                         break;
00379                 default: {
00380                         func (i, &rgba, data);
00381                         periodic->red[i] = rgba.red;
00382                         periodic->green[i] = rgba.green;
00383                         periodic->blue[i] = rgba.blue;
00384                         if (rgba.red > 0.6 ||  rgba.green > 0.6 || rgba.blue > 0.6)
00385                                 attr = pango_attr_foreground_new (0, 0, 0);
00386                         else
00387                                 attr = pango_attr_foreground_new (65535, 65535, 65535);
00388                         attr->start_index = 0;
00389                         attr->end_index = 100;
00390                         l = pango_attr_list_new ();
00391                         pango_attr_list_insert (l, attr);
00392                         gtk_label_set_attributes (periodic->labels[i], l);
00393                         break;
00394                 }
00395                 }
00396         }
00397 }
00398 
00399 int     gcu_periodic_add_color_scheme (GcuPeriodic *periodic,
00400                 GcuPeriodicColorFunc func, GtkWidget *extra_widget, gpointer user_data)
00401 {
00402         struct ColorScheme s;
00403         s.f = func;
00404         if (extra_widget)
00405                 s.page = gtk_notebook_append_page (periodic->book, extra_widget, NULL);
00406         else
00407                 s.page = 0;
00408         s.data = user_data;
00409         g_array_append_val (periodic->colorschemes, s);
00410         return GCU_PERIODIC_COLOR_MAX + periodic->nbschemes++;
00411 }
00412 
00413 void gcu_periodic_set_tips (GcuPeriodic *periodic, unsigned scheme)
00414 {
00415         if (scheme != periodic->tips) {
00416                 int i;
00417                 periodic->tips = scheme;
00418                 switch (scheme) {
00419                 default:
00420                 case GCU_PERIODIC_TIP_NAME:
00421                         for (i = 1; i <= 118; i++) {
00422                                 if (periodic->buttons[i])
00423                                         gtk_widget_set_tooltip_text (GTK_WIDGET (periodic->buttons[i]), gcu_element_get_name (i));
00424                         }
00425                         break;
00426                 case GCU_PERIODIC_TIP_STANDARD:
00427                         for (i = 1; i <= 118; i++) {
00428                                 GtkWidget *win, *grid, *w;
00429                                 char *markup, *str;
00430                                 char const *conf;
00431                                 if (!periodic->buttons[i])
00432                                         continue;
00433                                 win = gtk_window_new (GTK_WINDOW_POPUP);
00434                                 gtk_widget_set_name (win, "gtk-tooltip");
00435                                 grid = gtk_grid_new ();
00436                                 gtk_container_add (GTK_CONTAINER (win), grid);
00437                                 w = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL, "xalign", 0., NULL));
00438                                 markup = g_strdup_printf ("%u", i);
00439                                 gtk_label_set_text (GTK_LABEL (w), markup);
00440                                 g_free (markup);
00441                                 gtk_grid_attach (GTK_GRID (grid), w, 0, 0, 1, 1);
00442                                 str = gcu_element_get_weight_as_string (i);
00443                                 conf = gcu_element_get_electronic_configuration (i);
00444                                 w = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL, "justify", GTK_JUSTIFY_CENTER, NULL));
00445                                 markup = g_strdup_printf ("<span face=\"Sans\" size=\"xx-large\">%s</span>\n%s\n%s\n%s",
00446                                                           gcu_element_get_symbol (i), gcu_element_get_name (i), (conf)? conf: "", (str)? str: "");
00447                                 g_free (str);
00448                                 gtk_label_set_markup (GTK_LABEL (w), markup);
00449                                 g_free (markup);
00450                                 gtk_grid_attach (GTK_GRID (grid), w, 0, 1, 1, 1);
00451                                 gtk_widget_show_all (grid);
00452                                 gtk_widget_set_tooltip_window (GTK_WIDGET (periodic->buttons[i]), GTK_WINDOW (win));
00453                         }
00454                         break;
00455                 }
00456         }
00457 }