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 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the 18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 #include <gdk/gdkkeysyms.h> 23 #include "gdaui-entry-shell.h" 24 #include "gdaui-entry-none.h" 25 #include <libgda/gda-data-handler.h> 26 #include <libgda-ui/internal/utility.h> 27 #include <glib/gi18n-lib.h> 28 #include "widget-embedder.h" 29 static void gdaui_entry_shell_class_init (GdauiEntryShellClass *class); 30 static void gdaui_entry_shell_init (GdauiEntryShell *wid); 31 static void gdaui_entry_shell_dispose (GObject *object); 32 33 static void gdaui_entry_shell_set_property (GObject *object, 34 guint param_id, 35 const GValue *value, 36 GParamSpec *pspec); 37 static void gdaui_entry_shell_get_property (GObject *object, 38 guint param_id, 39 GValue *value, 40 GParamSpec *pspec); 41 42 43 static gint event_cb (GtkWidget *widget, GdkEvent *event, GdauiEntryShell *shell); 44 static void show_all (GtkWidget *widget); 45 static void contents_modified_cb (GdauiEntryShell *shell, gpointer unused); 46 static void gdaui_entry_shell_refresh_status_display (GdauiEntryShell *shell); 47 48 /* properties */ 49 enum { 50 PROP_0, 51 PROP_HANDLER, 52 PROP_ACTIONS, 53 PROP_IS_CELL_RENDERER 54 }; 55 56 struct _GdauiEntryShellPriv { 57 GtkWidget *embedder; 58 GtkWidget *hbox; 59 GtkWidget *button; 60 GdaDataHandler *data_handler; 61 gboolean show_actions; 62 63 gboolean value_is_null; 64 gboolean value_is_modified; 65 gboolean value_is_default; 66 gboolean value_is_non_valid; 67 68 gboolean is_cell_renderer; 69 }; 70 71 /* get a pointer to the parents to be able to call their destructor */ 72 static GObjectClass *parent_class = NULL; 73 74 /** 75 * gdaui_entry_shell_get_type: 76 * 77 * Register the GdauiEntryShell class on the GLib type system. 78 * 79 * Returns: the GType identifying the class. 80 */ 81 GType 82 gdaui_entry_shell_get_type (void) 83 { 84 static GType type = 0; 85 86 if (G_UNLIKELY (type == 0)) { 87 static const GTypeInfo info = { 88 sizeof (GdauiEntryShellClass), 89 (GBaseInitFunc) NULL, 90 (GBaseFinalizeFunc) NULL, 91 (GClassInitFunc) gdaui_entry_shell_class_init, 92 NULL, 93 NULL, 94 sizeof (GdauiEntryShell), 95 0, 96 (GInstanceInitFunc) gdaui_entry_shell_init, 97 0 98 }; 99 100 type = g_type_register_static (GTK_TYPE_VIEWPORT, "GdauiEntryShell", &info, 0); 101 } 102 return type; 103 } 104 105 106 static void 107 gdaui_entry_shell_class_init (GdauiEntryShellClass * class) 108 { 109 GObjectClass *object_class = G_OBJECT_CLASS (class); 110 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); 111 112 parent_class = g_type_class_peek_parent (class); 113 114 object_class->dispose = gdaui_entry_shell_dispose; 115 widget_class->show_all = show_all; 116 117 /* Properties */ 118 object_class->set_property = gdaui_entry_shell_set_property; 119 object_class->get_property = gdaui_entry_shell_get_property; 120 g_object_class_install_property (object_class, PROP_HANDLER, 121 g_param_spec_object ("handler", NULL, NULL, GDA_TYPE_DATA_HANDLER, 122 (G_PARAM_READABLE | G_PARAM_WRITABLE))); 123 g_object_class_install_property (object_class, PROP_ACTIONS, 124 g_param_spec_boolean ("actions", NULL, NULL, TRUE, 125 (G_PARAM_READABLE | G_PARAM_WRITABLE))); 126 127 g_object_class_install_property (object_class, PROP_IS_CELL_RENDERER, 128 g_param_spec_boolean ("is-cell-renderer", NULL, NULL, TRUE, 129 (G_PARAM_READABLE | G_PARAM_WRITABLE))); 130 } 131 132 static void 133 show_all (GtkWidget *widget) 134 { 135 if (((GdauiEntryShell*) widget)->priv->show_actions) 136 gtk_widget_show (((GdauiEntryShell*) widget)->priv->button); 137 } 138 139 static void 140 gdaui_entry_shell_init (GdauiEntryShell *shell) 141 { 142 GtkWidget *button, *hbox, *arrow; 143 GValue *gval; 144 145 /* Private structure */ 146 shell->priv = g_new0 (GdauiEntryShellPriv, 1); 147 shell->priv->embedder = NULL; 148 shell->priv->button = NULL; 149 shell->priv->show_actions = TRUE; 150 shell->priv->data_handler = NULL; 151 152 shell->priv->value_is_null = FALSE; 153 shell->priv->value_is_modified = FALSE; 154 shell->priv->value_is_default = FALSE; 155 shell->priv->value_is_non_valid = FALSE; 156 157 shell->priv->is_cell_renderer = FALSE; 158 159 /* Setting the initial layout */ 160 gtk_viewport_set_shadow_type (GTK_VIEWPORT (shell), GTK_SHADOW_NONE); 161 gtk_container_set_border_width (GTK_CONTAINER (shell), 0); 162 163 /* hbox */ 164 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); 165 gtk_container_add (GTK_CONTAINER (shell), hbox); 166 gtk_widget_show (hbox); 167 shell->priv->hbox = hbox; 168 169 /* vbox to insert the real widget to edit data */ 170 shell->priv->embedder = widget_embedder_new (); 171 gtk_box_pack_start (GTK_BOX (hbox), shell->priv->embedder, TRUE, TRUE, 0); 172 gtk_widget_show (shell->priv->embedder); 173 174 /* button to change the entry's state and to display that state */ 175 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); 176 button = gtk_button_new (); 177 gtk_container_add (GTK_CONTAINER (button), arrow); 178 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); 179 shell->priv->button = button; 180 gtk_widget_show_all (button); 181 182 g_signal_connect (G_OBJECT (button), "event", 183 G_CALLBACK (event_cb), shell); 184 185 /* focus */ 186 gval = g_new0 (GValue, 1); 187 g_value_init (gval, G_TYPE_BOOLEAN); 188 g_value_set_boolean (gval, TRUE); 189 g_object_set_property (G_OBJECT (button), "can-focus", gval); 190 g_free (gval); 191 } 192 193 static void 194 gdaui_entry_shell_dispose (GObject * object) 195 { 196 GdauiEntryShell *shell; 197 198 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (object)); 199 200 shell = GDAUI_ENTRY_SHELL (object); 201 202 if (shell->priv) { 203 if (shell->priv->data_handler) 204 g_object_unref (shell->priv->data_handler); 205 206 g_free (shell->priv); 207 shell->priv = NULL; 208 } 209 210 /* for the parent class */ 211 parent_class->dispose (object); 212 } 213 214 static void 215 gdaui_entry_shell_set_property (GObject *object, 216 guint param_id, 217 const GValue *value, 218 GParamSpec *pspec) 219 { 220 gpointer ptr; 221 GdauiEntryShell *shell; 222 223 shell = GDAUI_ENTRY_SHELL (object); 224 if (shell->priv) { 225 switch (param_id) { 226 case PROP_HANDLER: 227 ptr = g_value_get_object (value); 228 if (shell->priv->data_handler) { 229 g_object_unref (shell->priv->data_handler); 230 shell->priv->data_handler = NULL; 231 } 232 233 if (ptr) { 234 shell->priv->data_handler = GDA_DATA_HANDLER (ptr); 235 g_object_ref (G_OBJECT (shell->priv->data_handler)); 236 } 237 else 238 g_message (_("Widget of class '%s' does not have any associated GdaDataHandler, " 239 "(to be set using the 'handler' property) expect some mis-behaviours"), 240 G_OBJECT_TYPE_NAME (object)); 241 break; 242 case PROP_ACTIONS: 243 shell->priv->show_actions = g_value_get_boolean (value); 244 if (shell->priv->show_actions) 245 gtk_widget_show (shell->priv->button); 246 else 247 gtk_widget_hide (shell->priv->button); 248 break; 249 case PROP_IS_CELL_RENDERER: 250 if (GTK_IS_CELL_EDITABLE (shell) && 251 (g_value_get_boolean (value) != shell->priv->is_cell_renderer)) { 252 shell->priv->is_cell_renderer = g_value_get_boolean (value); 253 } 254 break; 255 default: 256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 257 break; 258 } 259 } 260 } 261 262 static void 263 gdaui_entry_shell_get_property (GObject *object, 264 guint param_id, 265 GValue *value, 266 GParamSpec *pspec) 267 { 268 GdauiEntryShell *shell; 269 270 shell = GDAUI_ENTRY_SHELL (object); 271 if (shell->priv) { 272 switch (param_id) { 273 case PROP_HANDLER: 274 g_value_set_object (value, shell->priv->data_handler); 275 break; 276 case PROP_ACTIONS: 277 g_value_set_boolean (value, shell->priv->show_actions); 278 break; 279 case PROP_IS_CELL_RENDERER: 280 g_value_set_boolean (value, shell->priv->is_cell_renderer); 281 break; 282 default: 283 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 284 break; 285 } 286 } 287 } 288 289 290 /** 291 * gdaui_entry_shell_pack_entry: 292 * @shell: a #GdauiEntryShell object 293 * @main_widget: a #GtkWidget to pack into @shell 294 * 295 * Packs a #GTkWidget widget into the GdauiEntryShell. 296 */ 297 void 298 gdaui_entry_shell_pack_entry (GdauiEntryShell *shell, GtkWidget *main_widget) 299 { 300 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell)); 301 g_return_if_fail (main_widget && GTK_IS_WIDGET (main_widget)); 302 gtk_container_add (GTK_CONTAINER (shell->priv->embedder), main_widget); 303 304 /* signals */ 305 g_signal_connect (G_OBJECT (shell), "contents-modified", 306 G_CALLBACK (contents_modified_cb), NULL); 307 308 g_signal_connect (G_OBJECT (shell), "status-changed", 309 G_CALLBACK (contents_modified_cb), NULL); 310 } 311 312 static void 313 contents_modified_cb (GdauiEntryShell *shell, G_GNUC_UNUSED gpointer unused) 314 { 315 gdaui_entry_shell_refresh (shell); 316 } 317 318 static void mitem_activated_cb (GtkWidget *mitem, GdauiEntryShell *shell); 319 static GdaValueAttribute gdaui_entry_shell_refresh_attributes (GdauiEntryShell *shell); 320 static gint 321 event_cb (G_GNUC_UNUSED GtkWidget *widget, GdkEvent *event, GdauiEntryShell *shell) 322 { 323 gboolean done = FALSE; 324 325 if (!shell->priv->show_actions) 326 return done; 327 328 if (event->type == GDK_BUTTON_PRESS) { 329 GdkEventButton *bevent = (GdkEventButton *) event; 330 if ((bevent->button == 1) || (bevent->button == 3)) { 331 GtkWidget *menu; 332 guint attributes; 333 334 attributes = gdaui_entry_shell_refresh_attributes (shell); 335 menu = _gdaui_utility_entry_build_actions_menu (G_OBJECT (shell), attributes, 336 G_CALLBACK (mitem_activated_cb)); 337 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 338 bevent->button, bevent->time); 339 done = TRUE; 340 } 341 } 342 343 if (event->type == GDK_KEY_PRESS) { 344 GtkWidget *menu; 345 GdkEventKey *kevent = (GdkEventKey *) event; 346 347 if (kevent->keyval == GDK_KEY_space) { 348 guint attributes; 349 350 attributes = gdaui_entry_shell_refresh_attributes (shell); 351 menu = _gdaui_utility_entry_build_actions_menu (G_OBJECT (shell), attributes, 352 G_CALLBACK (mitem_activated_cb)); 353 354 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 355 0, kevent->time); 356 done = TRUE; 357 } 358 else { 359 if (kevent->keyval == GDK_KEY_Tab) 360 done = FALSE; 361 else 362 done = TRUE; 363 } 364 } 365 366 return done; 367 } 368 369 static void 370 mitem_activated_cb (GtkWidget *mitem, GdauiEntryShell *shell) 371 { 372 guint action; 373 374 action = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (mitem), "action")); 375 gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (shell), action, action); 376 } 377 378 static void 379 gdaui_entry_shell_refresh_status_display (GdauiEntryShell *shell) 380 { 381 static GdkRGBA **colors = NULL; 382 GdkRGBA *normal = NULL, *prelight = NULL; 383 384 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell)); 385 386 if (!colors) 387 colors = _gdaui_utility_entry_build_info_colors_array_a (); 388 389 gtk_widget_set_tooltip_text (shell->priv->button, NULL); 390 391 if (shell->priv->value_is_null) { 392 normal = colors[0]; 393 prelight = colors[1]; 394 gtk_widget_set_tooltip_text (shell->priv->button, _("Value is NULL")); 395 } 396 397 if (shell->priv->value_is_default) { 398 normal = colors[2]; 399 prelight = colors[3]; 400 gtk_widget_set_tooltip_text (shell->priv->button, _("Value will be determined by default")); 401 } 402 403 if (shell->priv->value_is_non_valid) { 404 normal = colors[4]; 405 prelight = colors[5]; 406 gtk_widget_set_tooltip_text (shell->priv->button, _("Value is invalid")); 407 } 408 409 gtk_widget_override_background_color (shell->priv->button, GTK_STATE_FLAG_NORMAL, normal); 410 gtk_widget_override_background_color (shell->priv->button, GTK_STATE_FLAG_ACTIVE, normal); 411 gtk_widget_override_background_color (shell->priv->button, GTK_STATE_FLAG_PRELIGHT, prelight); 412 } 413 414 static GdaValueAttribute 415 gdaui_entry_shell_refresh_attributes (GdauiEntryShell *shell) 416 { 417 GdaValueAttribute attrs; 418 419 attrs = gdaui_data_entry_get_attributes (GDAUI_DATA_ENTRY (shell)); 420 shell->priv->value_is_null = attrs & GDA_VALUE_ATTR_IS_NULL; 421 shell->priv->value_is_modified = ! (attrs & GDA_VALUE_ATTR_IS_UNCHANGED); 422 shell->priv->value_is_default = attrs & GDA_VALUE_ATTR_IS_DEFAULT; 423 shell->priv->value_is_non_valid = attrs & GDA_VALUE_ATTR_DATA_NON_VALID; 424 425 return attrs; 426 } 427 428 /** 429 * gdaui_entry_shell_refresh: 430 * @shell: the GdauiEntryShell widget to refresh 431 * 432 * Forces the shell to refresh its display (mainly the color of the 433 * button). 434 */ 435 void 436 gdaui_entry_shell_refresh (GdauiEntryShell *shell) 437 { 438 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell)); 439 gdaui_entry_shell_refresh_attributes (shell); 440 gdaui_entry_shell_refresh_status_display (shell); 441 } 442 443 /** 444 * gdaui_entry_shell_set_unknown: 445 * @shell: the #GdauiEntryShell widget to refresh 446 * @unknown: set to %TRUE if @shell's contents is unavailable and should not be modified 447 * 448 * Defines if @shell's contents is in an undefined state (shows or hides @shell's contents) 449 */ 450 void 451 gdaui_entry_shell_set_unknown (GdauiEntryShell *shell, gboolean unknown) 452 { 453 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell)); 454 455 widget_embedder_set_valid ((WidgetEmbedder*) shell->priv->embedder, !unknown); 456 } 457 458 /** 459 * gdaui_entry_shell_set_ucolor: 460 * @shell: a #GdauiEntryShell 461 * @red: the red component of a color 462 * @green: the green component of a color 463 * @blue: the blue component of a color 464 * @alpha: the alpha component of a color 465 * 466 * Defines the color to be used when @de displays an invalid value. Any value not 467 * between 0. and 1. will result in the default hard coded values to be used (grayish). 468 * 469 * Since: 5.0.3 470 */ 471 void 472 gdaui_entry_shell_set_ucolor (GdauiEntryShell *shell, gdouble red, gdouble green, 473 gdouble blue, gdouble alpha) 474 { 475 g_return_if_fail (GDAUI_IS_ENTRY_SHELL (shell)); 476 widget_embedder_set_ucolor ((WidgetEmbedder*) shell->priv->embedder, red, green, blue, alpha); 477 } 478