gschem
|
00001 /* gEDA - GPL Electronic Design Automation 00002 * gschem - gEDA Schematic Capture 00003 * Copyright (C) 1998-2010 Ales Hvezda 00004 * Copyright (C) 1998-2011 gEDA Contributors (see ChangeLog for details) 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00019 */ 00020 #include <config.h> 00021 00022 #include <stdio.h> 00023 #ifdef HAVE_STDLIB_H 00024 #include <stdlib.h> 00025 #endif 00026 #ifdef HAVE_STRING_H 00027 #include <string.h> 00028 #endif 00029 00030 #include "gschem.h" 00031 #include <gdk/gdkkeysyms.h> 00032 00033 #ifdef HAVE_LIBDMALLOC 00034 #include <dmalloc.h> 00035 #endif 00036 00037 00049 static void callback_selection_changed (SELECTION *selection, 00050 gpointer user_data) 00051 { 00052 Multiattrib *multiattrib = MULTIATTRIB (user_data); 00053 GList *iter; 00054 OBJECT *object; 00055 gint object_count = 0; 00056 00057 for (iter = geda_list_get_glist (selection); 00058 iter != NULL; 00059 iter = g_list_next (iter)) { 00060 object = (OBJECT *)iter->data; 00061 g_assert (object != NULL); 00062 00063 if (object->type == OBJ_COMPLEX || 00064 object->type == OBJ_PLACEHOLDER || 00065 object->type == OBJ_NET || 00066 object->type == OBJ_BUS || 00067 object->type == OBJ_PIN) { 00068 object_count++; 00069 } 00070 } 00071 00072 if (object_count == 0) { 00073 /* TODO: If the user selects a single attribute which is 00074 * not floating, should we find its parent object and 00075 * display the multi-attribute editor for that? 00076 * Bonus marks for making it jump to the correct attrib. 00077 */ 00078 object = NULL; 00079 } else if (object_count == 1) { 00080 object = (OBJECT *)((geda_list_get_glist (selection))->data); 00081 } else { 00082 /* TODO: Something clever with multiple objects selected */ 00083 object = NULL; 00084 } 00085 00086 g_object_set (multiattrib, 00087 "object", object, 00088 NULL); 00089 } 00090 00091 #define DIALOG_DATA_SELECTION "current-selection" 00092 00109 static void callback_selection_finalized (gpointer data, 00110 GObject *where_the_object_was) 00111 { 00112 Multiattrib *multiattrib = MULTIATTRIB (data); 00113 g_object_set (multiattrib, 00114 "object", NULL, 00115 NULL); 00116 g_object_set_data (G_OBJECT (multiattrib), DIALOG_DATA_SELECTION, NULL); 00117 } 00118 00128 static void connect_selection (Multiattrib *multiattrib, 00129 SELECTION *selection) 00130 { 00131 g_assert (g_object_get_data (G_OBJECT (multiattrib), 00132 DIALOG_DATA_SELECTION) == NULL); 00133 g_object_set_data (G_OBJECT (multiattrib), DIALOG_DATA_SELECTION, selection); 00134 g_object_weak_ref (G_OBJECT (selection), 00135 callback_selection_finalized, 00136 multiattrib); 00137 g_signal_connect (selection, 00138 "changed", 00139 (GCallback)callback_selection_changed, 00140 multiattrib); 00141 /* Synthesise a selection changed update to refresh the view */ 00142 callback_selection_changed (selection, multiattrib); 00143 } 00144 00153 static void disconnect_selection (Multiattrib *multiattrib) { 00154 SELECTION *selection; 00155 00156 /* get selection watched from dialog data */ 00157 selection = (SELECTION*)g_object_get_data (G_OBJECT (multiattrib), 00158 DIALOG_DATA_SELECTION); 00159 if (selection == NULL) { 00160 /* no selection watched */ 00161 return; 00162 } 00163 00164 g_signal_handlers_disconnect_matched (selection, 00165 G_SIGNAL_MATCH_FUNC | 00166 G_SIGNAL_MATCH_DATA, 00167 0, 0, NULL, 00168 callback_selection_changed, 00169 multiattrib); 00170 g_object_weak_unref (G_OBJECT (selection), 00171 callback_selection_finalized, 00172 multiattrib); 00173 00174 /* reset dialog data */ 00175 g_object_set_data (G_OBJECT (multiattrib), DIALOG_DATA_SELECTION, NULL); 00176 } 00177 00187 static void 00188 multiattrib_callback_response (GtkDialog *dialog, 00189 gint arg1, 00190 gpointer user_data) 00191 { 00192 GSCHEM_TOPLEVEL *w_current = (GSCHEM_TOPLEVEL*)user_data; 00193 00194 switch (arg1) { 00195 case GTK_RESPONSE_CLOSE: 00196 case GTK_RESPONSE_DELETE_EVENT: 00197 /* cut link from dialog to selection */ 00198 disconnect_selection (MULTIATTRIB (w_current->mawindow)); 00199 gtk_widget_destroy (GTK_WIDGET (dialog)); 00200 w_current->mawindow = NULL; 00201 break; 00202 } 00203 } 00204 00211 void x_multiattrib_open (GSCHEM_TOPLEVEL *w_current) 00212 { 00213 if ( w_current->mawindow == NULL ) { 00214 w_current->mawindow = 00215 GTK_WIDGET (g_object_new (TYPE_MULTIATTRIB, 00216 "object", NULL, 00217 /* GschemDialog */ 00218 "settings-name", "multiattrib", 00219 "gschem-toplevel", w_current, 00220 NULL)); 00221 00222 gtk_window_set_transient_for (GTK_WINDOW(w_current->mawindow), 00223 GTK_WINDOW(w_current->main_window)); 00224 00225 g_signal_connect (w_current->mawindow, 00226 "response", 00227 G_CALLBACK (multiattrib_callback_response), 00228 w_current); 00229 00230 /* attach dialog to selection of current page */ 00231 x_multiattrib_update (w_current); 00232 00233 gtk_widget_show (w_current->mawindow); 00234 } else { 00235 gtk_window_present (GTK_WINDOW(w_current->mawindow)); 00236 } 00237 } 00238 00239 00248 void x_multiattrib_close (GSCHEM_TOPLEVEL *w_current) 00249 { 00250 if (w_current->mawindow != NULL) { 00251 /* cut link from dialog to selection */ 00252 disconnect_selection (MULTIATTRIB (w_current->mawindow)); 00253 gtk_widget_destroy (w_current->mawindow); 00254 w_current->mawindow = NULL; 00255 } 00256 } 00257 00267 void x_multiattrib_update( GSCHEM_TOPLEVEL *w_current ) 00268 { 00269 if (!IS_MULTIATTRIB (w_current->mawindow)) { 00270 return; 00271 } 00272 00273 /* disconnect dialog from previous selection */ 00274 disconnect_selection (MULTIATTRIB (w_current->mawindow)); 00275 /* connect the dialog to the selection of the current page */ 00276 connect_selection (MULTIATTRIB (w_current->mawindow), 00277 w_current->toplevel->page_current->selection_list); 00278 } 00279 00280 00286 static void celltextview_class_init (CellTextViewClass *klass); 00287 static void celltextview_init (CellTextView *self); 00288 static void celltextview_cell_editable_init (GtkCellEditableIface *iface); 00289 00290 enum { 00291 PROP_EDIT_CANCELED = 1 00292 }; 00293 00294 static void celltextview_set_property (GObject *object, 00295 guint property_id, 00296 const GValue *value, 00297 GParamSpec *pspec) 00298 { 00299 CellTextView *celltextview = (CellTextView*) object; 00300 00301 switch (property_id) { 00302 case PROP_EDIT_CANCELED: 00303 celltextview->editing_canceled = g_value_get_boolean (value); 00304 break; 00305 default: 00306 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 00307 } 00308 } 00309 00310 static void celltextview_get_property (GObject *object, 00311 guint property_id, 00312 GValue *value, 00313 GParamSpec *pspec) 00314 { 00315 CellTextView *celltextview = (CellTextView*) object; 00316 00317 switch (property_id) { 00318 case PROP_EDIT_CANCELED: 00319 g_value_set_boolean (value, celltextview->editing_canceled); 00320 break; 00321 default: 00322 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 00323 } 00324 } 00325 00326 00332 static gboolean celltextview_key_press_event (GtkWidget *widget, 00333 GdkEventKey *key_event, 00334 gpointer data) 00335 { 00336 CellTextView *celltextview = (CellTextView*)widget; 00337 00338 /* If the Escape key is pressed, we flag the edit as canceled */ 00339 if (key_event->keyval == GDK_Escape) 00340 celltextview->editing_canceled = TRUE; 00341 00342 /* ends editing of cell if one of these keys are pressed or editing is canceled */ 00343 if (celltextview->editing_canceled == TRUE || 00344 /* the Enter key without the Control modifier */ 00345 (!(key_event->state & GDK_CONTROL_MASK) && 00346 (key_event->keyval == GDK_Return || 00347 key_event->keyval == GDK_KP_Enter))) { 00348 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (celltextview)); 00349 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (celltextview)); 00350 return TRUE; 00351 } 00352 00353 return FALSE; 00354 } 00355 00361 static void celltextview_start_editing (GtkCellEditable *cell_editable, 00362 GdkEvent *event) 00363 { 00364 g_signal_connect (cell_editable, 00365 "key_press_event", 00366 G_CALLBACK (celltextview_key_press_event), 00367 NULL); 00368 00369 } 00370 00376 GType celltextview_get_type() 00377 { 00378 static GType celltextview_type = 0; 00379 00380 if (!celltextview_type) { 00381 static const GTypeInfo celltextview_info = { 00382 sizeof(CellTextViewClass), 00383 NULL, /* base_init */ 00384 NULL, /* base_finalize */ 00385 (GClassInitFunc) celltextview_class_init, 00386 NULL, /* class_finalize */ 00387 NULL, /* class_data */ 00388 sizeof(CellTextView), 00389 0, /* n_preallocs */ 00390 (GInstanceInitFunc) celltextview_init, 00391 }; 00392 00393 static const GInterfaceInfo cell_editable_info = { 00394 (GInterfaceInitFunc) celltextview_cell_editable_init, 00395 NULL, /* interface_finalize */ 00396 NULL /* interface_data */ 00397 }; 00398 00399 celltextview_type = g_type_register_static(GTK_TYPE_TEXT_VIEW, 00400 "CellTextView", 00401 &celltextview_info, 0); 00402 g_type_add_interface_static(celltextview_type, 00403 GTK_TYPE_CELL_EDITABLE, 00404 &cell_editable_info); 00405 } 00406 00407 return celltextview_type; 00408 } 00409 00415 static void celltextview_class_init(CellTextViewClass *klass) 00416 { 00417 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 00418 00419 gobject_class->get_property = celltextview_get_property; 00420 gobject_class->set_property = celltextview_set_property; 00421 00422 g_object_class_install_property ( 00423 gobject_class, 00424 PROP_EDIT_CANCELED, 00425 g_param_spec_boolean ("editing-canceled", 00426 "", 00427 "", 00428 FALSE, 00429 G_PARAM_READWRITE)); 00430 } 00431 00437 static void celltextview_init(CellTextView *celltextview) 00438 { 00439 celltextview->editing_canceled = FALSE; 00440 } 00441 00447 static void celltextview_cell_editable_init(GtkCellEditableIface *iface) 00448 { 00449 iface->start_editing = celltextview_start_editing; 00450 } 00451 00457 static void cellrenderermultilinetext_class_init(CellRendererMultiLineTextClass *klass); 00458 static void cellrenderermultilinetext_editing_done (GtkCellEditable *cell_editable, 00459 gpointer user_data); 00460 static gboolean cellrenderermultilinetext_focus_out_event (GtkWidget *widget, 00461 GdkEvent *event, 00462 gpointer user_data); 00463 00464 00465 #define CELL_RENDERER_MULTI_LINE_TEXT_PATH "cell-renderer-multi-line-text-path" 00466 00467 00473 static GtkCellEditable* cellrenderermultilinetext_start_editing(GtkCellRenderer *cell, 00474 GdkEvent *event, 00475 GtkWidget *widget, 00476 const gchar *path, 00477 GdkRectangle *background_area, 00478 GdkRectangle *cell_area, 00479 GtkCellRendererState flags) 00480 { 00481 GtkCellRendererText *cell_text; 00482 CellRendererMultiLineText *cell_mlt; 00483 GtkWidget *textview; 00484 GtkTextBuffer *textbuffer; 00485 00486 cell_text = GTK_CELL_RENDERER_TEXT (cell); 00487 if (cell_text->editable == FALSE) { 00488 return NULL; 00489 } 00490 00491 cell_mlt = CELL_RENDERER_MULTI_LINE_TEXT (cell); 00492 00493 textbuffer = GTK_TEXT_BUFFER (g_object_new (GTK_TYPE_TEXT_BUFFER, 00494 NULL)); 00495 gtk_text_buffer_set_text (textbuffer, 00496 cell_text->text, 00497 strlen (cell_text->text)); 00498 00499 textview = GTK_WIDGET (g_object_new (TYPE_CELL_TEXT_VIEW, 00500 /* GtkTextView */ 00501 "buffer", textbuffer, 00502 "editable", TRUE, 00503 /* GtkWidget */ 00504 "height-request", cell_area->height, 00505 NULL)); 00506 g_object_set_data_full (G_OBJECT (textview), 00507 CELL_RENDERER_MULTI_LINE_TEXT_PATH, 00508 g_strdup (path), g_free); 00509 00510 gtk_widget_show (textview); 00511 00512 g_signal_connect (GTK_CELL_EDITABLE (textview), 00513 "editing_done", 00514 G_CALLBACK (cellrenderermultilinetext_editing_done), 00515 cell_mlt); 00516 cell_mlt->focus_out_id = 00517 g_signal_connect (textview, 00518 "focus_out_event", 00519 G_CALLBACK (cellrenderermultilinetext_focus_out_event), 00520 cell_mlt); 00521 00522 return GTK_CELL_EDITABLE (textview); 00523 } 00524 00530 static void cellrenderermultilinetext_editing_done(GtkCellEditable *cell_editable, 00531 gpointer user_data) 00532 { 00533 CellRendererMultiLineText *cell = CELL_RENDERER_MULTI_LINE_TEXT (user_data); 00534 GtkTextBuffer *buffer; 00535 GtkTextIter start, end; 00536 gchar *new_text; 00537 const gchar *path; 00538 00539 if (cell->focus_out_id > 0) { 00540 g_signal_handler_disconnect (cell_editable, 00541 cell->focus_out_id); 00542 cell->focus_out_id = 0; 00543 } 00544 00545 if (CELL_TEXT_VIEW (cell_editable)->editing_canceled) { 00546 g_signal_emit_by_name (cell, "editing-canceled"); 00547 return; 00548 } 00549 00550 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (cell_editable)); 00551 gtk_text_buffer_get_start_iter (buffer, &start); 00552 gtk_text_buffer_get_end_iter (buffer, &end); 00553 new_text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); 00554 00555 path = g_object_get_data (G_OBJECT (cell_editable), 00556 CELL_RENDERER_MULTI_LINE_TEXT_PATH); 00557 g_signal_emit_by_name (cell, "edited", path, new_text); 00558 00559 g_free (new_text); 00560 00561 } 00562 00568 static gboolean cellrenderermultilinetext_focus_out_event(GtkWidget *widget, 00569 GdkEvent *event, 00570 gpointer user_data) 00571 { 00572 cellrenderermultilinetext_editing_done (GTK_CELL_EDITABLE (widget), 00573 user_data); 00574 00575 return FALSE; 00576 } 00577 00583 GType cellrenderermultilinetext_get_type() 00584 { 00585 static GType cellrenderermultilinetext_type = 0; 00586 00587 if (!cellrenderermultilinetext_type) { 00588 static const GTypeInfo cellrenderermultilinetext_info = { 00589 sizeof(CellRendererMultiLineTextClass), 00590 NULL, /* base_init */ 00591 NULL, /* base_finalize */ 00592 (GClassInitFunc) cellrenderermultilinetext_class_init, 00593 NULL, /* class_finalize */ 00594 NULL, /* class_data */ 00595 sizeof(CellRendererMultiLineText), 00596 0, /* n_preallocs */ 00597 NULL, /* instance_init */ 00598 }; 00599 00600 cellrenderermultilinetext_type = g_type_register_static ( 00601 GTK_TYPE_CELL_RENDERER_TEXT, 00602 "CellRendererMultiLineText", 00603 &cellrenderermultilinetext_info, 0); 00604 } 00605 00606 return cellrenderermultilinetext_type; 00607 } 00608 00614 static void cellrenderermultilinetext_class_init(CellRendererMultiLineTextClass *klass) 00615 { 00616 /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ 00617 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); 00618 00619 cell_class->start_editing = cellrenderermultilinetext_start_editing; 00620 00621 } 00622 00623 00624 enum { 00625 PROP_OBJECT = 1 00626 }; 00627 00628 enum { 00629 COLUMN_ATTRIBUTE, 00630 NUM_COLUMNS 00631 }; 00632 00633 static GObjectClass *multiattrib_parent_class = NULL; 00634 00635 static void multiattrib_class_init (MultiattribClass *class); 00636 static void multiattrib_init (Multiattrib *multiattrib); 00637 static void multiattrib_set_property (GObject *object, 00638 guint property_id, 00639 const GValue *value, 00640 GParamSpec *pspec); 00641 static void multiattrib_get_property (GObject *object, 00642 guint property_id, 00643 GValue *value, 00644 GParamSpec *pspec); 00645 00646 static void multiattrib_popup_menu (Multiattrib *multiattrib, 00647 GdkEventButton *event); 00648 00649 00655 static void multiattrib_action_add_attribute(GSCHEM_TOPLEVEL *w_current, 00656 OBJECT *object, 00657 Multiattrib *multiattrib, 00658 const gchar *name, 00659 const gchar *value, 00660 gint visible, 00661 gint show_name_value) 00662 { 00663 gchar *newtext; 00664 00665 newtext = g_strdup_printf ("%s=%s", name, value); 00666 00667 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) { 00668 g_free(newtext); 00669 return; 00670 } 00671 00672 /* create a new attribute and link it */ 00673 o_attrib_add_attrib (w_current, newtext, 00674 visible, show_name_value, object); 00675 00676 w_current->toplevel->page_current->CHANGED = 1; 00677 o_undo_savestate (w_current, UNDO_ALL); 00678 00679 g_free (newtext); 00680 00681 } 00682 00688 static void multiattrib_action_duplicate_attribute(GSCHEM_TOPLEVEL *w_current, 00689 OBJECT *object, 00690 OBJECT *o_attrib) 00691 { 00692 int visibility = o_is_visible (w_current->toplevel, o_attrib) 00693 ? VISIBLE : INVISIBLE; 00694 00695 o_attrib_add_attrib (w_current, 00696 o_text_get_string (w_current->toplevel, o_attrib), 00697 visibility, 00698 o_attrib->show_name_value, 00699 object); 00700 w_current->toplevel->page_current->CHANGED = 1; 00701 o_undo_savestate (w_current, UNDO_ALL); 00702 00703 } 00704 00710 static void multiattrib_action_promote_attribute (GSCHEM_TOPLEVEL *w_current, 00711 OBJECT *object, 00712 OBJECT *o_attrib) 00713 { 00714 TOPLEVEL *toplevel = w_current->toplevel; 00715 OBJECT *o_new; 00716 00717 if (o_is_visible (toplevel, o_attrib)) { 00718 /* If the attribute we're promoting is visible, don't clone its location */ 00719 (void) o_attrib_add_attrib (w_current, 00720 o_text_get_string (w_current->toplevel, o_attrib), 00721 VISIBLE, 00722 o_attrib->show_name_value, 00723 object); 00724 } else { 00725 /* make a copy of the attribute object */ 00726 o_new = o_object_copy (toplevel, o_attrib); 00727 s_page_append (toplevel, toplevel->page_current, o_new); 00728 /* add the attribute its parent */ 00729 o_attrib_attach (toplevel, o_new, object, TRUE); 00730 /* note: this object is unselected (not added to selection). */ 00731 00732 /* Call add-objects-hook */ 00733 g_run_hook_object (w_current, "%add-objects-hook", o_new); 00734 } 00735 w_current->toplevel->page_current->CHANGED = 1; 00736 o_undo_savestate (w_current, UNDO_ALL); 00737 } 00738 00744 static void multiattrib_action_delete_attribute(GSCHEM_TOPLEVEL *w_current, 00745 OBJECT *o_attrib) 00746 { 00747 /* actually deletes the attribute */ 00748 o_delete (w_current, o_attrib); 00749 o_undo_savestate (w_current, UNDO_ALL); 00750 00751 } 00752 00758 static void multiattrib_column_set_data_name(GtkTreeViewColumn *tree_column, 00759 GtkCellRenderer *cell, 00760 GtkTreeModel *tree_model, 00761 GtkTreeIter *iter, 00762 gpointer data) 00763 { 00764 OBJECT *o_attrib; 00765 gchar *name; 00766 Multiattrib *dialog = (Multiattrib *) data; 00767 int inherited; 00768 00769 gtk_tree_model_get (tree_model, iter, 00770 COLUMN_ATTRIBUTE, &o_attrib, 00771 -1); 00772 g_assert (o_attrib->type == OBJ_TEXT); 00773 00774 inherited = o_attrib_is_inherited (o_attrib); 00775 00776 o_attrib_get_name_value (o_attrib, &name, NULL); 00777 g_object_set (cell, 00778 "text", name, 00779 "foreground-gdk", inherited ? &dialog->insensitive_text_color : NULL, 00780 "editable", !inherited, 00781 NULL); 00782 g_free (name); 00783 00784 } 00785 00791 static void multiattrib_column_set_data_value(GtkTreeViewColumn *tree_column, 00792 GtkCellRenderer *cell, 00793 GtkTreeModel *tree_model, 00794 GtkTreeIter *iter, 00795 gpointer data) 00796 { 00797 OBJECT *o_attrib; 00798 gchar *value; 00799 Multiattrib *dialog = (Multiattrib *) data; 00800 int inherited; 00801 00802 gtk_tree_model_get (tree_model, iter, 00803 COLUMN_ATTRIBUTE, &o_attrib, 00804 -1); 00805 g_assert (o_attrib->type == OBJ_TEXT); 00806 00807 inherited = o_attrib_is_inherited (o_attrib); 00808 00809 o_attrib_get_name_value (o_attrib, NULL, &value); 00810 g_object_set (cell, 00811 "text", value, 00812 "foreground-gdk", inherited ? &dialog->insensitive_text_color : NULL, 00813 "editable", !inherited, 00814 NULL); 00815 g_free (value); 00816 00817 } 00818 00824 static void multiattrib_column_set_data_visible(GtkTreeViewColumn *tree_column, 00825 GtkCellRenderer *cell, 00826 GtkTreeModel *tree_model, 00827 GtkTreeIter *iter, 00828 gpointer data) 00829 { 00830 OBJECT *o_attrib; 00831 GschemDialog *dialog = GSCHEM_DIALOG (data); 00832 int inherited; 00833 00834 gtk_tree_model_get (tree_model, iter, 00835 COLUMN_ATTRIBUTE, &o_attrib, 00836 -1); 00837 g_assert (o_attrib->type == OBJ_TEXT); 00838 00839 inherited = o_attrib_is_inherited (o_attrib); 00840 00841 g_object_set (cell, 00842 "active", o_is_visible (dialog->w_current->toplevel, o_attrib), 00843 "sensitive", !inherited, 00844 "activatable", !inherited, 00845 NULL); 00846 00847 } 00848 00854 static void multiattrib_column_set_data_show_name(GtkTreeViewColumn *tree_column, 00855 GtkCellRenderer *cell, 00856 GtkTreeModel *tree_model, 00857 GtkTreeIter *iter, 00858 gpointer data) 00859 { 00860 OBJECT *o_attrib; 00861 int inherited; 00862 00863 gtk_tree_model_get (tree_model, iter, 00864 COLUMN_ATTRIBUTE, &o_attrib, 00865 -1); 00866 g_assert (o_attrib->type == OBJ_TEXT); 00867 00868 inherited = o_attrib_is_inherited (o_attrib); 00869 00870 g_object_set (cell, 00871 "active", (o_attrib->show_name_value == SHOW_NAME_VALUE || 00872 o_attrib->show_name_value == SHOW_NAME), 00873 "sensitive", !inherited, 00874 "activatable", !inherited, 00875 NULL); 00876 00877 } 00878 00884 static void multiattrib_column_set_data_show_value(GtkTreeViewColumn *tree_column, 00885 GtkCellRenderer *cell, 00886 GtkTreeModel *tree_model, 00887 GtkTreeIter *iter, 00888 gpointer data) 00889 { 00890 OBJECT *o_attrib; 00891 int inherited; 00892 00893 gtk_tree_model_get (tree_model, iter, 00894 COLUMN_ATTRIBUTE, &o_attrib, 00895 -1); 00896 g_assert (o_attrib->type == OBJ_TEXT); 00897 00898 inherited = o_attrib_is_inherited (o_attrib); 00899 00900 g_object_set (cell, 00901 "active", (o_attrib->show_name_value == SHOW_NAME_VALUE || 00902 o_attrib->show_name_value == SHOW_VALUE), 00903 "sensitive", !inherited, 00904 "activatable", !inherited, 00905 NULL); 00906 00907 } 00908 00919 static void 00920 update_row_display (GtkTreeModel *model, GtkTreeIter *iter) 00921 { 00922 GtkTreePath *path; 00923 00924 path = gtk_tree_model_get_path (model, iter); 00925 gtk_tree_model_row_changed (model, path, iter); 00926 gtk_tree_path_free (path); 00927 00928 } 00929 00935 static void multiattrib_callback_edited_name(GtkCellRendererText *cellrenderertext, 00936 gchar *arg1, 00937 gchar *arg2, 00938 gpointer user_data) 00939 { 00940 Multiattrib *multiattrib = (Multiattrib*)user_data; 00941 GtkTreeModel *model; 00942 GtkTreeIter iter; 00943 OBJECT *o_attrib; 00944 GSCHEM_TOPLEVEL *w_current; 00945 gchar *value, *newtext; 00946 int visibility; 00947 00948 model = gtk_tree_view_get_model (multiattrib->treeview); 00949 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 00950 00951 if (!gtk_tree_model_get_iter_from_string (model, &iter, arg1)) { 00952 return; 00953 } 00954 00955 if (g_ascii_strcasecmp (arg2, "") == 0) { 00956 GtkWidget *dialog = gtk_message_dialog_new ( 00957 GTK_WINDOW (multiattrib), 00958 GTK_DIALOG_MODAL, 00959 GTK_MESSAGE_ERROR, 00960 GTK_BUTTONS_OK, 00961 _("Attributes with empty name are not allowed. Please set a name.")); 00962 00963 gtk_dialog_run (GTK_DIALOG (dialog)); 00964 gtk_widget_destroy (dialog); 00965 return; 00966 } 00967 00968 gtk_tree_model_get (model, &iter, 00969 COLUMN_ATTRIBUTE, &o_attrib, 00970 -1); 00971 g_assert (o_attrib->type == OBJ_TEXT); 00972 00973 o_attrib_get_name_value (o_attrib, NULL, &value); 00974 newtext = g_strdup_printf ("%s=%s", arg2, value); 00975 00976 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) { 00977 g_free (value); 00978 g_free(newtext); 00979 return; 00980 } 00981 00982 visibility = o_is_visible (w_current->toplevel, o_attrib) 00983 ? VISIBLE : INVISIBLE; 00984 00985 /* actually modifies the attribute */ 00986 o_text_change (w_current, o_attrib, 00987 newtext, visibility, o_attrib->show_name_value); 00988 00989 g_free (value); 00990 g_free (newtext); 00991 00992 } 00993 00999 static void multiattrib_callback_edited_value(GtkCellRendererText *cell_renderer, 01000 gchar *arg1, 01001 gchar *arg2, 01002 gpointer user_data) 01003 { 01004 Multiattrib *multiattrib = (Multiattrib*)user_data; 01005 GtkTreeModel *model; 01006 GtkTreeIter iter; 01007 OBJECT *o_attrib; 01008 GSCHEM_TOPLEVEL *w_current; 01009 gchar *name, *newtext; 01010 int visibility; 01011 01012 model = gtk_tree_view_get_model (multiattrib->treeview); 01013 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 01014 01015 if (!gtk_tree_model_get_iter_from_string (model, &iter, arg1)) { 01016 return; 01017 } 01018 01019 gtk_tree_model_get (model, &iter, 01020 COLUMN_ATTRIBUTE, &o_attrib, 01021 -1); 01022 g_assert (o_attrib->type == OBJ_TEXT); 01023 01024 o_attrib_get_name_value (o_attrib, &name, NULL); 01025 newtext = g_strdup_printf ("%s=%s", name, arg2); 01026 01027 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) { 01028 g_free (name); 01029 g_free(newtext); 01030 return; 01031 } 01032 01033 visibility = o_is_visible (w_current->toplevel, o_attrib) 01034 ? VISIBLE : INVISIBLE; 01035 01036 /* actually modifies the attribute */ 01037 o_text_change (w_current, o_attrib, 01038 newtext, visibility, o_attrib->show_name_value); 01039 01040 /* request an update of display for this row */ 01041 update_row_display (model, &iter); 01042 01043 g_free (name); 01044 g_free (newtext); 01045 01046 } 01047 01053 static void multiattrib_callback_toggled_visible(GtkCellRendererToggle *cell_renderer, 01054 gchar *path, 01055 gpointer user_data) 01056 { 01057 Multiattrib *multiattrib = (Multiattrib*)user_data; 01058 GtkTreeModel *model; 01059 GtkTreeIter iter; 01060 OBJECT *o_attrib; 01061 GSCHEM_TOPLEVEL *w_current; 01062 gint visibility; 01063 01064 model = gtk_tree_view_get_model (multiattrib->treeview); 01065 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 01066 01067 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) { 01068 return; 01069 } 01070 01071 gtk_tree_model_get (model, &iter, 01072 COLUMN_ATTRIBUTE, &o_attrib, 01073 -1); 01074 g_assert (o_attrib->type == OBJ_TEXT); 01075 o_invalidate (w_current, o_attrib); 01076 01077 /* toggle visibility */ 01078 visibility = o_is_visible (w_current->toplevel, o_attrib) 01079 ? INVISIBLE : VISIBLE; 01080 01081 /* actually modifies the attribute */ 01082 o_set_visibility (w_current->toplevel, o_attrib, visibility); 01083 o_text_recreate (w_current->toplevel, o_attrib); 01084 o_undo_savestate (w_current, UNDO_ALL); 01085 01086 /* request an update of display for this row */ 01087 update_row_display (model, &iter); 01088 01089 } 01090 01096 static void multiattrib_callback_toggled_show_name(GtkCellRendererToggle *cell_renderer, 01097 gchar *path, 01098 gpointer user_data) 01099 { 01100 Multiattrib *multiattrib = (Multiattrib*)user_data; 01101 GtkTreeModel *model; 01102 GtkTreeIter iter; 01103 OBJECT *o_attrib; 01104 GSCHEM_TOPLEVEL *w_current; 01105 gint new_snv; 01106 01107 model = gtk_tree_view_get_model (multiattrib->treeview); 01108 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 01109 01110 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) { 01111 return; 01112 } 01113 01114 gtk_tree_model_get (model, &iter, 01115 COLUMN_ATTRIBUTE, &o_attrib, 01116 -1); 01117 g_assert (o_attrib->type == OBJ_TEXT); 01118 o_invalidate (w_current, o_attrib); 01119 01120 switch (o_attrib->show_name_value) { 01121 case SHOW_NAME_VALUE: new_snv = SHOW_VALUE; break; 01122 case SHOW_NAME: new_snv = SHOW_VALUE; break; 01123 case SHOW_VALUE: new_snv = SHOW_NAME_VALUE; break; 01124 default: 01125 g_assert_not_reached (); 01126 new_snv = SHOW_NAME_VALUE; 01127 } 01128 01129 /* actually modifies the attribute */ 01130 o_attrib->show_name_value = new_snv; 01131 o_text_recreate (w_current->toplevel, o_attrib); 01132 o_undo_savestate (w_current, UNDO_ALL); 01133 01134 /* request an update of display for this row */ 01135 update_row_display (model, &iter); 01136 01137 } 01138 01144 static void multiattrib_callback_toggled_show_value(GtkCellRendererToggle *cell_renderer, 01145 gchar *path, 01146 gpointer user_data) 01147 { 01148 Multiattrib *multiattrib = (Multiattrib*)user_data; 01149 GtkTreeModel *model; 01150 GtkTreeIter iter; 01151 OBJECT *o_attrib; 01152 GSCHEM_TOPLEVEL *w_current; 01153 gint new_snv; 01154 01155 model = gtk_tree_view_get_model (multiattrib->treeview); 01156 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 01157 01158 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) { 01159 return; 01160 } 01161 01162 gtk_tree_model_get (model, &iter, 01163 COLUMN_ATTRIBUTE, &o_attrib, 01164 -1); 01165 g_assert (o_attrib->type == OBJ_TEXT); 01166 o_invalidate (w_current, o_attrib); 01167 01168 switch (o_attrib->show_name_value) { 01169 case SHOW_NAME_VALUE: new_snv = SHOW_NAME; break; 01170 case SHOW_NAME: new_snv = SHOW_NAME_VALUE; break; 01171 case SHOW_VALUE: new_snv = SHOW_NAME; break; 01172 default: 01173 g_assert_not_reached (); 01174 new_snv = SHOW_NAME_VALUE; 01175 } 01176 01177 /* actually modifies the attribute */ 01178 o_attrib->show_name_value = new_snv; 01179 o_text_recreate (w_current->toplevel, o_attrib); 01180 o_undo_savestate (w_current, UNDO_ALL); 01181 01182 /* request an update of display for this row */ 01183 update_row_display (model, &iter); 01184 01185 } 01186 01192 static gboolean multiattrib_callback_key_pressed(GtkWidget *widget, 01193 GdkEventKey *event, 01194 gpointer user_data) 01195 { 01196 Multiattrib *multiattrib = (Multiattrib*)user_data; 01197 01198 if (event->state == 0 && 01199 (event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete)) { 01200 GtkTreeModel *model; 01201 GtkTreeIter iter; 01202 OBJECT *o_attrib; 01203 int inherited; 01204 /* delete the currently selected attribute */ 01205 01206 if (!gtk_tree_selection_get_selected ( 01207 gtk_tree_view_get_selection (multiattrib->treeview), 01208 &model, &iter)) { 01209 /* nothing selected, nothing to do */ 01210 return FALSE; 01211 } 01212 01213 gtk_tree_model_get (model, &iter, 01214 COLUMN_ATTRIBUTE, &o_attrib, 01215 -1); 01216 g_assert (o_attrib->type == OBJ_TEXT); 01217 01218 inherited = o_attrib_is_inherited (o_attrib); 01219 /* We can't delete inherited attribtes */ 01220 if (inherited) 01221 return FALSE; 01222 01223 multiattrib_action_delete_attribute (GSCHEM_DIALOG (multiattrib)->w_current, 01224 o_attrib); 01225 01226 /* update the treeview contents */ 01227 multiattrib_update (multiattrib); 01228 } 01229 01230 return FALSE; 01231 } 01232 01238 static gboolean multiattrib_callback_button_pressed(GtkWidget *widget, 01239 GdkEventButton *event, 01240 gpointer user_data) 01241 { 01242 Multiattrib *multiattrib = (Multiattrib*)user_data; 01243 gboolean ret = FALSE; 01244 01245 if (event->type == GDK_BUTTON_PRESS && event->button == 3) { 01246 multiattrib_popup_menu (multiattrib, event); 01247 ret = TRUE; 01248 } 01249 01250 return ret; 01251 } 01252 01258 static gboolean multiattrib_callback_popup_menu(GtkWidget *widget, 01259 gpointer user_data) 01260 { 01261 Multiattrib *multiattrib = (Multiattrib*)user_data; 01262 01263 multiattrib_popup_menu (multiattrib, NULL); 01264 01265 return TRUE; 01266 } 01267 01273 static void multiattrib_callback_popup_duplicate(GtkMenuItem *menuitem, 01274 gpointer user_data) 01275 { 01276 Multiattrib *multiattrib = (Multiattrib*)user_data; 01277 GtkTreeModel *model; 01278 GtkTreeIter iter; 01279 GSCHEM_TOPLEVEL *w_current; 01280 OBJECT *object, *o_attrib; 01281 01282 if (!gtk_tree_selection_get_selected ( 01283 gtk_tree_view_get_selection (multiattrib->treeview), 01284 &model, &iter)) { 01285 /* nothing selected, nothing to do */ 01286 return; 01287 } 01288 01289 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 01290 object = multiattrib->object; 01291 01292 gtk_tree_model_get (model, &iter, 01293 COLUMN_ATTRIBUTE, &o_attrib, 01294 -1); 01295 g_assert (o_attrib->type == OBJ_TEXT); 01296 01297 multiattrib_action_duplicate_attribute (w_current, object, o_attrib); 01298 01299 /* update the treeview contents */ 01300 multiattrib_update (multiattrib); 01301 01302 } 01303 01304 01310 static void multiattrib_callback_popup_promote (GtkMenuItem *menuitem, 01311 gpointer user_data) 01312 { 01313 Multiattrib *multiattrib = user_data; 01314 GtkTreeModel *model; 01315 GtkTreeIter iter; 01316 GSCHEM_TOPLEVEL *w_current; 01317 OBJECT *object, *o_attrib; 01318 01319 if (!gtk_tree_selection_get_selected ( 01320 gtk_tree_view_get_selection (multiattrib->treeview), 01321 &model, &iter)) { 01322 /* nothing selected, nothing to do */ 01323 return; 01324 } 01325 01326 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 01327 object = multiattrib->object; 01328 01329 gtk_tree_model_get (model, &iter, 01330 COLUMN_ATTRIBUTE, &o_attrib, 01331 -1); 01332 g_assert (o_attrib->type == OBJ_TEXT); 01333 01334 multiattrib_action_promote_attribute (w_current, object, o_attrib); 01335 01336 /* update the treeview contents */ 01337 multiattrib_update (multiattrib); 01338 } 01339 01345 static void multiattrib_callback_popup_delete(GtkMenuItem *menuitem, 01346 gpointer user_data) 01347 { 01348 Multiattrib *multiattrib = (Multiattrib*)user_data; 01349 GtkTreeModel *model; 01350 GtkTreeIter iter; 01351 GSCHEM_TOPLEVEL *w_current; 01352 OBJECT *o_attrib; 01353 01354 if (!gtk_tree_selection_get_selected ( 01355 gtk_tree_view_get_selection (multiattrib->treeview), 01356 &model, &iter)) { 01357 /* nothing selected, nothing to do */ 01358 return; 01359 } 01360 01361 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 01362 01363 gtk_tree_model_get (model, &iter, 01364 COLUMN_ATTRIBUTE, &o_attrib, 01365 -1); 01366 g_assert (o_attrib->type == OBJ_TEXT); 01367 01368 multiattrib_action_delete_attribute (w_current, o_attrib); 01369 01370 /* update the treeview contents */ 01371 multiattrib_update (multiattrib); 01372 01373 } 01374 01380 static gboolean multiattrib_callback_value_key_pressed(GtkWidget *widget, 01381 GdkEventKey *event, 01382 gpointer user_data) 01383 { 01384 Multiattrib *multiattrib = (Multiattrib*)widget; 01385 gboolean retval = FALSE; 01386 01387 /* ends editing of cell if one of these keys are pressed: */ 01388 /* - the Return key without the Control modifier */ 01389 /* - the Tab key without the Control modifier */ 01390 if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) || 01391 (event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab)) { 01392 /* Control modifier activated? */ 01393 if (event->state & GDK_CONTROL_MASK) { 01394 /* yes the modifier in event structure and let event propagate */ 01395 event->state ^= GDK_CONTROL_MASK; 01396 retval = FALSE; 01397 } else { 01398 /* change focus and stop propagation */ 01399 g_signal_emit_by_name (multiattrib, 01400 "move_focus", 01401 (event->state & GDK_SHIFT_MASK) ? 01402 GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD); 01403 retval = TRUE; 01404 } 01405 } 01406 01407 return retval; 01408 } 01409 01410 01416 static void multiattrib_callback_value_grab_focus (GtkWidget *widget, 01417 gpointer user_data) 01418 { 01419 GtkTextView *textview = GTK_TEXT_VIEW (widget); 01420 GtkTextBuffer *textbuffer; 01421 GtkTextIter startiter, enditer; 01422 01423 textbuffer = gtk_text_view_get_buffer (textview); 01424 gtk_text_buffer_get_iter_at_offset (textbuffer, &startiter, 0); 01425 gtk_text_buffer_get_iter_at_offset (textbuffer, &enditer, -1); 01426 gtk_text_buffer_select_range (textbuffer, &enditer, &startiter); 01427 } 01428 01429 01435 static void multiattrib_callback_button_add(GtkButton *button, 01436 gpointer user_data) 01437 { 01438 Multiattrib *multiattrib = (Multiattrib*)user_data; 01439 GtkTextBuffer *buffer; 01440 GtkTextIter start, end; 01441 const gchar *name; 01442 gchar *value; 01443 GSCHEM_TOPLEVEL *w_current; 01444 OBJECT *object; 01445 gboolean visible; 01446 gint shownv; 01447 01448 w_current = GSCHEM_DIALOG (multiattrib)->w_current; 01449 object = multiattrib->object; 01450 buffer = gtk_text_view_get_buffer (multiattrib->textview_value); 01451 01452 /* retrieve information from the Add/Edit frame */ 01453 /* - attribute's name */ 01454 name = gtk_entry_get_text ( 01455 GTK_ENTRY (GTK_COMBO (multiattrib->combo_name)->entry)); 01456 /* - attribute's value */ 01457 gtk_text_buffer_get_bounds (buffer, &start, &end); 01458 value = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); 01459 /* - attribute's visibility status */ 01460 visible = gtk_toggle_button_get_active ( 01461 (GtkToggleButton*)multiattrib->button_visible); 01462 /* - visibility type */ 01463 shownv = (gint)gtk_option_menu_get_history (multiattrib->optionmenu_shownv); 01464 01465 if (name[0] == '\0' || name[0] == ' ') { 01466 /* name not allowed for an attribute */ 01467 g_free (value); 01468 return; 01469 } 01470 01471 multiattrib_action_add_attribute (w_current, object, multiattrib, 01472 name, value, 01473 visible, shownv); 01474 g_free (value); 01475 01476 multiattrib_update (multiattrib); 01477 } 01478 01484 static void multiattrib_init_attrib_names(GtkCombo *combo) 01485 { 01486 GList *items = NULL; 01487 const gchar *string; 01488 gint i; 01489 01490 for (i = 0, string = s_attrib_get (i); 01491 string != NULL; 01492 i++, string = s_attrib_get (i)) { 01493 items = g_list_append (items, (gpointer)string); 01494 } 01495 01496 gtk_combo_set_popdown_strings (GTK_COMBO (combo), items); 01497 01498 g_list_free (items); 01499 01500 } 01501 01507 static void multiattrib_init_visible_types(GtkOptionMenu *optionmenu) 01508 { 01509 GtkWidget *menu, *item; 01510 01511 menu = gtk_menu_new (); 01512 item = gtk_menu_item_new_with_label (_("Show Name & Value")); 01513 gtk_menu_append (menu, item); 01514 item = gtk_menu_item_new_with_label (_("Show Value only")); 01515 gtk_menu_append (menu, item); 01516 item = gtk_menu_item_new_with_label (_("Show Name only")); 01517 gtk_menu_append (menu, item); 01518 01519 gtk_option_menu_set_menu (optionmenu, menu); 01520 01521 } 01522 01523 01533 static void multiattrib_popup_menu(Multiattrib *multiattrib, 01534 GdkEventButton *event) 01535 { 01536 GtkTreePath *path; 01537 GtkWidget *menu; 01538 struct menuitem_t { 01539 gchar *label; 01540 GCallback callback; 01541 }; 01542 01543 struct menuitem_t menuitems_inherited[] = { 01544 { N_("Promote"), G_CALLBACK (multiattrib_callback_popup_promote) }, 01545 { NULL, NULL } }; 01546 01547 struct menuitem_t menuitems_noninherited[] = { 01548 { N_("Duplicate"), G_CALLBACK (multiattrib_callback_popup_duplicate) }, 01549 { N_("Delete"), G_CALLBACK (multiattrib_callback_popup_delete) }, 01550 { NULL, NULL } }; 01551 01552 struct menuitem_t *item_list; 01553 struct menuitem_t *tmp; 01554 int inherited; 01555 GtkTreeModel *model; 01556 GtkTreeIter iter; 01557 GtkTreeSelection *selection; 01558 OBJECT *o_attrib; 01559 01560 selection = gtk_tree_view_get_selection (multiattrib->treeview); 01561 01562 if (event != NULL && 01563 gtk_tree_view_get_path_at_pos (multiattrib->treeview, 01564 (gint)event->x, 01565 (gint)event->y, 01566 &path, NULL, NULL, NULL)) { 01567 gtk_tree_selection_unselect_all (selection); 01568 gtk_tree_selection_select_path (selection, path); 01569 gtk_tree_path_free (path); 01570 } 01571 01572 /* if nothing is selected, nothing to do */ 01573 if (!gtk_tree_selection_get_selected (selection, &model, &iter)) 01574 return; 01575 01576 gtk_tree_model_get (model, &iter, 01577 COLUMN_ATTRIBUTE, &o_attrib, 01578 -1); 01579 g_assert (o_attrib->type == OBJ_TEXT); 01580 01581 inherited = o_attrib_is_inherited (o_attrib); 01582 item_list = inherited ? menuitems_inherited : menuitems_noninherited; 01583 01584 /* create the context menu */ 01585 menu = gtk_menu_new(); 01586 for (tmp = item_list; tmp->label != NULL; tmp++) { 01587 GtkWidget *menuitem; 01588 if (g_utf8_collate (tmp->label, "-") == 0) { 01589 menuitem = gtk_separator_menu_item_new (); 01590 } else { 01591 menuitem = gtk_menu_item_new_with_label (_(tmp->label)); 01592 g_signal_connect (menuitem, 01593 "activate", 01594 tmp->callback, 01595 multiattrib); 01596 } 01597 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); 01598 } 01599 gtk_widget_show_all (menu); 01600 /* make menu a popup menu */ 01601 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 01602 (event != NULL) ? event->button : 0, 01603 gdk_event_get_time ((GdkEvent*)event)); 01604 01605 } 01606 01607 01618 static void 01619 multiattrib_geometry_save (GschemDialog *dialog, GKeyFile *key_file, gchar *group_name) 01620 { 01621 gboolean show_inherited; 01622 01623 /* Call the parent's geometry_save method */ 01624 GSCHEM_DIALOG_CLASS (multiattrib_parent_class)-> 01625 geometry_save (dialog, key_file, group_name); 01626 01627 show_inherited = 01628 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (MULTIATTRIB (dialog)->show_inherited)); 01629 g_key_file_set_boolean (key_file, group_name, "show_inherited", show_inherited); 01630 } 01631 01632 01643 static void 01644 multiattrib_geometry_restore (GschemDialog *dialog, GKeyFile *key_file, gchar *group_name) 01645 { 01646 gboolean show_inherited; 01647 GError *error = NULL; 01648 01649 /* Call the parent's geometry_restore method */ 01650 GSCHEM_DIALOG_CLASS (multiattrib_parent_class)-> 01651 geometry_restore (dialog, key_file, group_name); 01652 01653 show_inherited = g_key_file_get_boolean (key_file, group_name, "show_inherited", &error); 01654 if (error != NULL) { 01655 show_inherited = TRUE; 01656 g_error_free (error); 01657 } 01658 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (MULTIATTRIB (dialog)->show_inherited), show_inherited); 01659 } 01660 01661 01672 GType multiattrib_get_type() 01673 { 01674 static GType multiattrib_type = 0; 01675 01676 if (!multiattrib_type) { 01677 static const GTypeInfo multiattrib_info = { 01678 sizeof(MultiattribClass), 01679 NULL, /* base_init */ 01680 NULL, /* base_finalize */ 01681 (GClassInitFunc) multiattrib_class_init, 01682 NULL, /* class_finalize */ 01683 NULL, /* class_data */ 01684 sizeof(Multiattrib), 01685 0, /* n_preallocs */ 01686 (GInstanceInitFunc) multiattrib_init, 01687 }; 01688 01689 multiattrib_type = g_type_register_static (GSCHEM_TYPE_DIALOG, 01690 "Multiattrib", 01691 &multiattrib_info, 0); 01692 } 01693 01694 return multiattrib_type; 01695 } 01696 01706 static void multiattrib_class_init(MultiattribClass *klass) 01707 { 01708 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 01709 GschemDialogClass *gschem_dialog_class = GSCHEM_DIALOG_CLASS (klass); 01710 01711 gschem_dialog_class->geometry_save = multiattrib_geometry_save; 01712 gschem_dialog_class->geometry_restore = multiattrib_geometry_restore; 01713 01714 gobject_class->set_property = multiattrib_set_property; 01715 gobject_class->get_property = multiattrib_get_property; 01716 01717 multiattrib_parent_class = g_type_class_peek_parent (klass); 01718 01719 g_object_class_install_property ( 01720 gobject_class, PROP_OBJECT, 01721 g_param_spec_pointer ("object", 01722 "", 01723 "", 01724 G_PARAM_READWRITE)); 01725 } 01726 01730 static void multiattrib_show_inherited_toggled (GtkToggleButton *button, 01731 gpointer user_data) 01732 { 01733 Multiattrib *multiattrib = user_data; 01734 01735 /* update the treeview contents */ 01736 multiattrib_update (multiattrib); 01737 } 01738 01739 01749 static void multiattrib_init(Multiattrib *multiattrib) 01750 { 01751 GtkWidget *frame, *label, *scrolled_win, *treeview; 01752 GtkWidget *table, *textview, *combo, *optionm, *button; 01753 GtkWidget *attrib_vbox, *show_inherited; 01754 GtkTreeModel *store; 01755 GtkCellRenderer *renderer; 01756 GtkTreeViewColumn *column; 01757 GtkTreeSelection *selection; 01758 GtkStyle *style; 01759 01760 /* dialog initialization */ 01761 g_object_set (G_OBJECT (multiattrib), 01762 /* GtkContainer */ 01763 "border-width", 0, 01764 /* GtkWindow */ 01765 "title", _("Edit Attributes"), 01766 "default-width", 320, 01767 "default-height", 350, 01768 "window-position", GTK_WIN_POS_MOUSE, 01769 "allow-grow", TRUE, 01770 "allow-shrink", FALSE, 01771 /* GtkDialog */ 01772 "has-separator", TRUE, 01773 NULL); 01774 01775 multiattrib->object = NULL; 01776 01777 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (multiattrib)->vbox), 5); 01778 01779 /* create the attribute list frame */ 01780 frame = GTK_WIDGET (g_object_new (GTK_TYPE_FRAME, 01781 /* GtkFrame */ 01782 "shadow", GTK_SHADOW_NONE, 01783 NULL)); 01784 multiattrib->frame_add = frame; 01785 /* - create the model for the treeview */ 01786 store = (GtkTreeModel*)gtk_list_store_new (NUM_COLUMNS, 01787 G_TYPE_POINTER); /* attribute */ 01788 /* - create a scrolled window for the treeview */ 01789 scrolled_win = GTK_WIDGET ( 01790 g_object_new (GTK_TYPE_SCROLLED_WINDOW, 01791 /* GtkContainer */ 01792 "border-width", 3, 01793 /* GtkScrolledWindow */ 01794 "hscrollbar-policy", 01795 GTK_POLICY_AUTOMATIC, 01796 "vscrollbar-policy", 01797 GTK_POLICY_AUTOMATIC, 01798 "shadow-type", 01799 GTK_SHADOW_ETCHED_IN, 01800 NULL)); 01801 /* - create the treeview */ 01802 treeview = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW, 01803 /* GtkTreeView */ 01804 "model", store, 01805 "rules-hint", TRUE, 01806 NULL)); 01807 g_signal_connect (treeview, 01808 "key-press-event", 01809 G_CALLBACK (multiattrib_callback_key_pressed), 01810 multiattrib); 01811 g_signal_connect (treeview, 01812 "button-press-event", 01813 G_CALLBACK (multiattrib_callback_button_pressed), 01814 multiattrib); 01815 g_signal_connect (treeview, 01816 "popup-menu", 01817 G_CALLBACK (multiattrib_callback_popup_menu), 01818 multiattrib); 01819 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); 01820 gtk_tree_selection_set_mode (selection, 01821 GTK_SELECTION_SINGLE); 01822 01823 /* - and now the columns of the treeview */ 01824 /* - column 1: attribute name */ 01825 renderer = GTK_CELL_RENDERER ( 01826 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, 01827 /* GtkCellRendererText */ 01828 /* unknown in GTK 2.4 */ 01829 /* "ellipsize", 01830 * PANGO_ELLIPSIZE_END, */ 01831 NULL)); 01832 g_signal_connect (renderer, 01833 "edited", 01834 G_CALLBACK (multiattrib_callback_edited_name), 01835 multiattrib); 01836 column = GTK_TREE_VIEW_COLUMN ( 01837 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, 01838 /* GtkTreeViewColumn */ 01839 "title", _("Name"), 01840 "min-width", 100, 01841 "resizable", TRUE, 01842 NULL)); 01843 gtk_tree_view_column_pack_start (column, renderer, TRUE); 01844 gtk_tree_view_column_set_cell_data_func (column, renderer, 01845 multiattrib_column_set_data_name, 01846 multiattrib, NULL); 01847 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 01848 /* - column 2: attribute value */ 01849 renderer = GTK_CELL_RENDERER ( 01850 g_object_new (TYPE_CELL_RENDERER_MULTI_LINE_TEXT, 01851 /* GtkCellRendererText */ 01852 /* unknown in GTK 2.4 */ 01853 /* "ellipsize", 01854 PANGO_ELLIPSIZE_END, */ 01855 NULL)); 01856 g_signal_connect (renderer, 01857 "edited", 01858 G_CALLBACK (multiattrib_callback_edited_value), 01859 multiattrib); 01860 column = GTK_TREE_VIEW_COLUMN ( 01861 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, 01862 /* GtkTreeViewColumn */ 01863 "title", _("Value"), 01864 "min-width", 140, 01865 "resizable", TRUE, 01866 NULL)); 01867 gtk_tree_view_column_pack_start (column, renderer, TRUE); 01868 gtk_tree_view_column_set_cell_data_func (column, renderer, 01869 multiattrib_column_set_data_value, 01870 multiattrib, NULL); 01871 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 01872 /* - column 3: visibility */ 01873 renderer = GTK_CELL_RENDERER ( 01874 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE, 01875 NULL)); 01876 g_signal_connect (renderer, 01877 "toggled", 01878 G_CALLBACK (multiattrib_callback_toggled_visible), 01879 multiattrib); 01880 column = GTK_TREE_VIEW_COLUMN ( 01881 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, 01882 /* GtkTreeViewColumn */ 01883 "title", _("Vis?"), 01884 NULL)); 01885 gtk_tree_view_column_pack_start (column, renderer, TRUE); 01886 gtk_tree_view_column_set_cell_data_func (column, renderer, 01887 multiattrib_column_set_data_visible, 01888 multiattrib, NULL); 01889 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 01890 /* - column 4: show name */ 01891 renderer = GTK_CELL_RENDERER ( 01892 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE, 01893 NULL)); 01894 g_signal_connect (renderer, 01895 "toggled", 01896 G_CALLBACK (multiattrib_callback_toggled_show_name), 01897 multiattrib); 01898 column = GTK_TREE_VIEW_COLUMN ( 01899 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, 01900 /* GtkTreeViewColumn */ 01901 "title", _("N"), 01902 NULL)); 01903 gtk_tree_view_column_pack_start (column, renderer, TRUE); 01904 gtk_tree_view_column_set_cell_data_func (column, renderer, 01905 multiattrib_column_set_data_show_name, 01906 NULL, NULL); 01907 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 01908 /* - column 5: show value */ 01909 renderer = GTK_CELL_RENDERER ( 01910 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE, 01911 NULL)); 01912 g_signal_connect (renderer, 01913 "toggled", 01914 G_CALLBACK (multiattrib_callback_toggled_show_value), 01915 multiattrib); 01916 column = GTK_TREE_VIEW_COLUMN ( 01917 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, 01918 /* GtkTreeViewColumn */ 01919 "title", _("V"), 01920 NULL)); 01921 gtk_tree_view_column_pack_start (column, renderer, TRUE); 01922 gtk_tree_view_column_set_cell_data_func (column, renderer, 01923 multiattrib_column_set_data_show_value, 01924 NULL, NULL); 01925 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 01926 01927 /* add the treeview to the scrolled window */ 01928 gtk_container_add (GTK_CONTAINER (scrolled_win), treeview); 01929 /* set treeview of multiattrib */ 01930 multiattrib->treeview = GTK_TREE_VIEW (treeview); 01931 01932 attrib_vbox = gtk_vbox_new (FALSE, 0); 01933 01934 /* Pack the vbox into the frame */ 01935 gtk_container_add (GTK_CONTAINER (frame), attrib_vbox); 01936 01937 /* add the scrolled window to box */ 01938 gtk_box_pack_start (GTK_BOX (attrib_vbox), scrolled_win, TRUE, TRUE, 0); 01939 01940 /* create the show inherited button */ 01941 show_inherited = gtk_check_button_new_with_label (_("Show inherited attributes")); 01942 multiattrib->show_inherited = show_inherited; 01943 gtk_box_pack_start (GTK_BOX (attrib_vbox), show_inherited, FALSE, FALSE, 0); 01944 01945 g_signal_connect (show_inherited, 01946 "toggled", 01947 G_CALLBACK (multiattrib_show_inherited_toggled), 01948 multiattrib); 01949 01950 /* pack the frame */ 01951 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (multiattrib)->vbox), frame, 01952 TRUE, TRUE, 1); 01953 gtk_widget_show_all (frame); 01954 01955 /* create the add/edit frame */ 01956 frame = GTK_WIDGET (g_object_new (GTK_TYPE_FRAME, 01957 "label", _("Add Attribute"), 01958 NULL)); 01959 multiattrib->frame_attributes = frame; 01960 table = GTK_WIDGET (g_object_new (GTK_TYPE_TABLE, 01961 /* GtkTable */ 01962 "n-rows", 4, 01963 "n-columns", 2, 01964 "homogeneous", FALSE, 01965 NULL)); 01966 01967 /* - the name entry: a GtkComboBoxEntry */ 01968 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL, 01969 /* GtkMisc */ 01970 "xalign", 0.0, 01971 "yalign", 0.5, 01972 /* GtkLabel */ 01973 "label", _("Name:"), 01974 NULL)); 01975 combo = GTK_WIDGET (g_object_new (GTK_TYPE_COMBO, 01976 /* GtkCombo */ 01977 "value-in-list", FALSE, 01978 NULL)); 01979 multiattrib_init_attrib_names (GTK_COMBO (combo)); 01980 multiattrib->combo_name = GTK_COMBO (combo); 01981 gtk_table_attach (GTK_TABLE (table), label, 01982 0, 1, 0, 1, 0, 0, 0, 0); 01983 gtk_table_attach (GTK_TABLE (table), combo, 01984 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 6, 3); 01985 01986 /* - the value entry: a GtkEntry */ 01987 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL, 01988 /* GtkMisc */ 01989 "xalign", 0.0, 01990 "yalign", 0.5, 01991 /* GtkLabel */ 01992 "label", _("Value:"), 01993 NULL)); 01994 scrolled_win = GTK_WIDGET ( 01995 g_object_new (GTK_TYPE_SCROLLED_WINDOW, 01996 /* GtkScrolledWindow */ 01997 "hscrollbar-policy", 01998 GTK_POLICY_NEVER, 01999 "vscrollbar-policy", 02000 GTK_POLICY_AUTOMATIC, 02001 "shadow-type", 02002 GTK_SHADOW_IN, 02003 NULL)); 02006 textview = GTK_WIDGET (g_object_new (GTK_TYPE_TEXT_VIEW, 02007 "height-request", 50, 02008 NULL)); 02009 g_signal_connect (textview, 02010 "key_press_event", 02011 G_CALLBACK (multiattrib_callback_value_key_pressed), 02012 multiattrib); 02013 g_signal_connect (textview, 02014 "grab-focus", 02015 G_CALLBACK (multiattrib_callback_value_grab_focus), 02016 multiattrib); 02017 /* Save the GTK_STATE_NORMAL color so we can work around GtkTextView's 02018 * stubborn refusal to draw with GTK_STATE_INSENSITIVE later on */ 02019 style = gtk_widget_get_style (textview); 02020 multiattrib->value_normal_text_color = style->text[ GTK_STATE_NORMAL ]; 02021 02022 /* Save this one so we can pick it as a sensible colour to show the 02023 * inherited attributes dimmed. 02024 */ 02025 style = gtk_widget_get_style (treeview); 02026 multiattrib->insensitive_text_color = style->text[ GTK_STATE_INSENSITIVE ]; 02027 02028 gtk_container_add (GTK_CONTAINER (scrolled_win), textview); 02029 multiattrib->textview_value = GTK_TEXT_VIEW (textview); 02030 gtk_table_attach (GTK_TABLE (table), label, 02031 0, 1, 1, 2, 0, 0, 0, 0); 02032 gtk_table_attach (GTK_TABLE (table), scrolled_win, 02033 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 6, 3); 02034 02035 /* - the visible status */ 02036 button = GTK_WIDGET (g_object_new (GTK_TYPE_CHECK_BUTTON, 02037 /* GtkButton */ 02038 "label", _("Visible"), 02039 "active", TRUE, 02040 NULL)); 02041 multiattrib->button_visible = GTK_CHECK_BUTTON (button); 02042 gtk_table_attach (GTK_TABLE (table), button, 02043 0, 1, 2, 3, GTK_FILL, 0, 3, 0); 02044 02045 /* - the visibility type */ 02046 optionm = GTK_WIDGET (g_object_new (GTK_TYPE_OPTION_MENU, 02047 NULL)); 02048 multiattrib_init_visible_types (GTK_OPTION_MENU (optionm)); 02049 multiattrib->optionmenu_shownv = GTK_OPTION_MENU (optionm); 02050 gtk_table_attach (GTK_TABLE (table), optionm, 02051 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 6, 3); 02052 gtk_widget_show_all (table); 02053 02054 /* create the add button */ 02055 button = gtk_button_new_from_stock (GTK_STOCK_ADD); 02056 g_signal_connect (button, 02057 "clicked", 02058 G_CALLBACK (multiattrib_callback_button_add), 02059 multiattrib); 02060 gtk_table_attach (GTK_TABLE (table), button, 02061 2, 3, 0, 3, 0, 0, 6, 3); 02062 02063 /* add the table to the frame */ 02064 gtk_container_add (GTK_CONTAINER (frame), table); 02065 /* pack the frame in the dialog */ 02066 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (multiattrib)->vbox), frame, 02067 FALSE, TRUE, 1); 02068 gtk_widget_show_all (frame); 02069 02070 02071 /* now add the close button to the action area */ 02072 gtk_dialog_add_button (GTK_DIALOG (multiattrib), 02073 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); 02074 02075 } 02076 02077 02090 static void multiattrib_set_property (GObject *object, 02091 guint property_id, 02092 const GValue *value, 02093 GParamSpec *pspec) 02094 { 02095 Multiattrib *multiattrib = MULTIATTRIB (object); 02096 02097 switch(property_id) { 02098 case PROP_OBJECT: 02099 multiattrib->object = (OBJECT*)g_value_get_pointer (value); 02100 multiattrib_update (multiattrib); 02101 break; 02102 default: 02103 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 02104 } 02105 } 02106 02107 02119 static void multiattrib_get_property (GObject *object, 02120 guint property_id, 02121 GValue *value, 02122 GParamSpec *pspec) 02123 { 02124 Multiattrib *multiattrib = MULTIATTRIB (object); 02125 02126 switch(property_id) { 02127 case PROP_OBJECT: 02128 g_value_set_pointer (value, (gpointer)multiattrib->object); 02129 break; 02130 default: 02131 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 02132 } 02133 02134 } 02135 02136 02146 void multiattrib_update (Multiattrib *multiattrib) 02147 { 02148 GtkListStore *liststore; 02149 GtkTreeIter iter; 02150 GList *object_attribs; 02151 GList *a_iter; 02152 OBJECT *a_current; 02153 gboolean sensitive; 02154 GtkStyle *style; 02155 gboolean show_inherited; 02156 02157 g_assert (GSCHEM_DIALOG (multiattrib)->w_current != NULL); 02158 02159 /* clear the list of attributes */ 02160 liststore = (GtkListStore*)gtk_tree_view_get_model (multiattrib->treeview); 02161 gtk_list_store_clear (liststore); 02162 02163 /* Update sensitivities */ 02164 sensitive = (multiattrib->object != NULL); 02165 gtk_widget_set_sensitive (GTK_WIDGET (multiattrib->frame_attributes), sensitive); 02166 gtk_widget_set_sensitive (GTK_WIDGET (multiattrib->frame_add), sensitive); 02167 02168 /* Work around GtkTextView's stubborn indifference 02169 * to GTK_STATE_INSENSITIVE when rendering its text. */ 02170 style = gtk_widget_get_style (GTK_WIDGET (multiattrib->textview_value)); 02171 gtk_widget_modify_text (GTK_WIDGET (multiattrib->textview_value), 02172 GTK_STATE_NORMAL, 02173 sensitive ? &multiattrib->value_normal_text_color 02174 : &style->text[GTK_STATE_INSENSITIVE]); 02175 02176 /* If we aren't sensitive, there is nothing more to do */ 02177 if (!sensitive) 02178 return; 02179 02180 show_inherited = 02181 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (multiattrib->show_inherited)); 02182 02183 /* get list of attributes */ 02184 object_attribs = o_attrib_return_attribs (multiattrib->object); 02185 /* populate the store with attributes */ 02186 for (a_iter = object_attribs; a_iter != NULL; 02187 a_iter = g_list_next (a_iter)) { 02188 a_current = a_iter->data; 02189 02190 /* Skip over inherited attributes if we don't want to show them */ 02191 if (!show_inherited && o_attrib_is_inherited (a_current)) 02192 continue; 02193 02194 gtk_list_store_append (liststore, &iter); 02195 gtk_list_store_set (liststore, &iter, 02196 COLUMN_ATTRIBUTE, a_current, 02197 -1); 02198 } 02199 /* delete the list of attribute objects */ 02200 g_list_free (object_attribs); 02201 }