The Gnome Chemistry Utils
0.13.6
|
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 2 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 }