1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2019 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 #include "../include/gschem_multiattrib_dockable.h"
32 #include <gdk/gdkkeysyms.h>
35 static void multiattrib_update (GschemMultiattribDockable
*multiattrib
);
38 snv_shows_name (int snv
)
40 return snv
== SHOW_NAME_VALUE
|| snv
== SHOW_NAME
;
44 snv_shows_value (int snv
)
46 return snv
== SHOW_NAME_VALUE
|| snv
== SHOW_VALUE
;
49 /*! \brief Update the multiattrib editor dialog for a GschemToplevel.
51 * \par Function Description
53 * If the GschemToplevel has an open multiattrib dialog, switch to
54 * watching the current page's SELECTION object for changes.
56 * \param [in] w_current The GschemToplevel object.
59 x_multiattrib_update (GschemToplevel
*w_current
)
61 g_object_set (G_OBJECT (w_current
->multiattrib_dockable
), "object_list",
62 w_current
->toplevel
->page_current
->selection_list
, NULL
);
66 /*! \section celltextview-widget Cell TextView Widget Code.
67 * This widget makes a 'GtkTextView' widget implements the 'GtkCellEditable'
68 * interface. It can then be used to renderer multi-line texts inside
69 * tree views ('GtkTreeView').
71 static void celltextview_class_init (CellTextViewClass
*klass
);
72 static void celltextview_init (CellTextView
*self
);
73 static void celltextview_cell_editable_init (GtkCellEditableIface
*iface
);
76 PROP_EDIT_CANCELED
= 1
80 celltextview_set_property (GObject
*object
,
85 CellTextView
*celltextview
= (CellTextView
*) object
;
87 switch (property_id
) {
88 case PROP_EDIT_CANCELED
:
89 celltextview
->editing_canceled
= g_value_get_boolean (value
);
92 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
97 celltextview_get_property (GObject
*object
,
102 CellTextView
*celltextview
= (CellTextView
*) object
;
104 switch (property_id
) {
105 case PROP_EDIT_CANCELED
:
106 g_value_set_boolean (value
, celltextview
->editing_canceled
);
109 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
114 /*! \todo Finish function documentation
116 * \par Function Description
120 celltextview_key_press_event (GtkWidget
*widget
,
121 GdkEventKey
*key_event
,
124 CellTextView
*celltextview
= (CellTextView
*)widget
;
126 /* If the Escape key is pressed, we flag the edit as canceled */
127 if (key_event
->keyval
== GDK_Escape
)
128 celltextview
->editing_canceled
= TRUE
;
130 /* ends editing of cell if one of these keys are pressed or editing is canceled */
131 if (celltextview
->editing_canceled
== TRUE
||
132 /* the Enter key without the Control modifier */
133 (!(key_event
->state
& GDK_CONTROL_MASK
) &&
134 (key_event
->keyval
== GDK_Return
||
135 key_event
->keyval
== GDK_KP_Enter
))) {
136 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (celltextview
));
137 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (celltextview
));
144 /*! \todo Finish function documentation
146 * \par Function Description
150 celltextview_start_editing (GtkCellEditable
*cell_editable
,
153 g_signal_connect (cell_editable
,
155 G_CALLBACK (celltextview_key_press_event
),
159 /*! \todo Finish function documentation
161 * \par Function Description
165 celltextview_get_type ()
167 static GType celltextview_type
= 0;
169 if (!celltextview_type
) {
170 static const GTypeInfo celltextview_info
= {
171 sizeof(CellTextViewClass
),
172 NULL
, /* base_init */
173 NULL
, /* base_finalize */
174 (GClassInitFunc
) celltextview_class_init
,
175 NULL
, /* class_finalize */
176 NULL
, /* class_data */
177 sizeof(CellTextView
),
179 (GInstanceInitFunc
) celltextview_init
,
182 static const GInterfaceInfo cell_editable_info
= {
183 (GInterfaceInitFunc
) celltextview_cell_editable_init
,
184 NULL
, /* interface_finalize */
185 NULL
/* interface_data */
188 celltextview_type
= g_type_register_static (GTK_TYPE_TEXT_VIEW
,
190 &celltextview_info
, 0);
191 g_type_add_interface_static (celltextview_type
,
192 GTK_TYPE_CELL_EDITABLE
,
193 &cell_editable_info
);
196 return celltextview_type
;
199 /*! \todo Finish function documentation
201 * \par Function Description
205 celltextview_class_init (CellTextViewClass
*klass
)
207 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
209 gobject_class
->get_property
= celltextview_get_property
;
210 gobject_class
->set_property
= celltextview_set_property
;
212 g_object_class_install_property (
215 g_param_spec_boolean ("editing-canceled",
222 /*! \todo Finish function documentation
224 * \par Function Description
228 celltextview_init (CellTextView
*celltextview
)
230 celltextview
->editing_canceled
= FALSE
;
233 /*! \todo Finish function documentation
235 * \par Function Description
239 celltextview_cell_editable_init (GtkCellEditableIface
*iface
)
241 iface
->start_editing
= celltextview_start_editing
;
244 /*! \section multi-line-text-cell-renderer Multi-line Text Cell Renderer
245 * GTK has no multi-line text cell renderer. This code adds one to be used
246 * in gschem code. It is inspired by the 'GtkCellRendererCombo' renderer
249 static void cellrenderermultilinetext_class_init (CellRendererMultiLineTextClass
*klass
);
250 static void cellrenderermultilinetext_editing_done (GtkCellEditable
*cell_editable
,
252 static gboolean
cellrenderermultilinetext_focus_out_event (GtkWidget
*widget
,
257 #define CELL_RENDERER_MULTI_LINE_TEXT_PATH "cell-renderer-multi-line-text-path"
260 /*! \todo Finish function documentation
262 * \par Function Description
265 static GtkCellEditable
*
266 cellrenderermultilinetext_start_editing (GtkCellRenderer
*cell
,
270 GdkRectangle
*background_area
,
271 GdkRectangle
*cell_area
,
272 GtkCellRendererState flags
)
274 GtkCellRendererText
*cell_text
;
275 CellRendererMultiLineText
*cell_mlt
;
277 GtkTextBuffer
*textbuffer
;
279 cell_text
= GTK_CELL_RENDERER_TEXT (cell
);
280 if (cell_text
->editable
== FALSE
) {
284 cell_mlt
= CELL_RENDERER_MULTI_LINE_TEXT (cell
);
286 textbuffer
= GTK_TEXT_BUFFER (g_object_new (GTK_TYPE_TEXT_BUFFER
,
288 gtk_text_buffer_set_text (textbuffer
,
290 strlen (cell_text
->text
));
292 textview
= GTK_WIDGET (g_object_new (TYPE_CELL_TEXT_VIEW
,
294 "buffer", textbuffer
,
297 "height-request", cell_area
->height
,
299 g_object_set_data_full (G_OBJECT (textview
),
300 CELL_RENDERER_MULTI_LINE_TEXT_PATH
,
301 g_strdup (path
), g_free
);
303 gtk_widget_show (textview
);
305 g_signal_connect (GTK_CELL_EDITABLE (textview
),
307 G_CALLBACK (cellrenderermultilinetext_editing_done
),
309 cell_mlt
->focus_out_id
=
310 g_signal_connect (textview
,
312 G_CALLBACK (cellrenderermultilinetext_focus_out_event
),
315 return GTK_CELL_EDITABLE (textview
);
318 /*! \todo Finish function documentation
320 * \par Function Description
324 cellrenderermultilinetext_editing_done (GtkCellEditable
*cell_editable
,
327 CellRendererMultiLineText
*cell
= CELL_RENDERER_MULTI_LINE_TEXT (user_data
);
328 GtkTextBuffer
*buffer
;
329 GtkTextIter start
, end
;
333 if (cell
->focus_out_id
> 0) {
334 g_signal_handler_disconnect (cell_editable
,
336 cell
->focus_out_id
= 0;
339 if (CELL_TEXT_VIEW (cell_editable
)->editing_canceled
) {
340 g_signal_emit_by_name (cell
, "editing-canceled");
344 buffer
= gtk_text_view_get_buffer (GTK_TEXT_VIEW (cell_editable
));
345 gtk_text_buffer_get_start_iter (buffer
, &start
);
346 gtk_text_buffer_get_end_iter (buffer
, &end
);
347 new_text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, TRUE
);
349 path
= g_object_get_data (G_OBJECT (cell_editable
),
350 CELL_RENDERER_MULTI_LINE_TEXT_PATH
);
351 g_signal_emit_by_name (cell
, "edited", path
, new_text
);
356 /*! \todo Finish function documentation
358 * \par Function Description
362 cellrenderermultilinetext_focus_out_event (GtkWidget
*widget
,
366 // cellrenderermultilinetext_editing_done (GTK_CELL_EDITABLE (widget),
372 /*! \todo Finish function documentation
374 * \par Function Description
378 cellrenderermultilinetext_get_type ()
380 static GType cellrenderermultilinetext_type
= 0;
382 if (!cellrenderermultilinetext_type
) {
383 static const GTypeInfo cellrenderermultilinetext_info
= {
384 sizeof(CellRendererMultiLineTextClass
),
385 NULL
, /* base_init */
386 NULL
, /* base_finalize */
387 (GClassInitFunc
) cellrenderermultilinetext_class_init
,
388 NULL
, /* class_finalize */
389 NULL
, /* class_data */
390 sizeof(CellRendererMultiLineText
),
392 NULL
, /* instance_init */
395 cellrenderermultilinetext_type
= g_type_register_static (
396 GTK_TYPE_CELL_RENDERER_TEXT
,
397 "CellRendererMultiLineText",
398 &cellrenderermultilinetext_info
, 0);
401 return cellrenderermultilinetext_type
;
404 /*! \todo Finish function documentation
406 * \par Function Description
410 cellrenderermultilinetext_class_init (CellRendererMultiLineTextClass
*klass
)
412 /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */
413 GtkCellRendererClass
*cell_class
= GTK_CELL_RENDERER_CLASS (klass
);
415 cell_class
->start_editing
= cellrenderermultilinetext_start_editing
;
428 COLUMN_SHOW_NAME_VALUE
,
429 COLUMN_PRESENT_IN_ALL
,
430 COLUMN_IDENTICAL_VALUE
,
431 COLUMN_IDENTICAL_VISIBILITY
,
432 COLUMN_IDENTICAL_SHOW_NAME
,
433 COLUMN_IDENTICAL_SHOW_VALUE
,
434 COLUMN_ATTRIBUTE_GEDALIST
,
438 static GObjectClass
*multiattrib_parent_class
= NULL
;
440 static void multiattrib_class_init (GschemMultiattribDockableClass
*class);
441 static GtkWidget
*multiattrib_create_widget (GschemDockable
*dockable
);
442 static void multiattrib_set_property (GObject
*object
,
446 static void multiattrib_get_property (GObject
*object
,
451 static void multiattrib_popup_menu (GschemMultiattribDockable
*multiattrib
,
452 GdkEventButton
*event
);
455 /*!\brief Invoke the multi-attribute editor to edit a single attribute.
458 x_multiattrib_edit_attribute (GschemToplevel
*w_current
, OBJECT
*object
)
460 GschemMultiattribDockable
*multiattrib
=
461 GSCHEM_MULTIATTRIB_DOCKABLE (w_current
->multiattrib_dockable
);
466 /* present editor first to make sure the widget hierarchy exists */
467 gschem_dockable_present (w_current
->multiattrib_dockable
);
469 /* find tree iterator corresponding to the attribute */
470 for (valid
= gtk_tree_model_get_iter_first (multiattrib
->store
, &iter
);
472 valid
= gtk_tree_model_iter_next (multiattrib
->store
, &iter
)) {
475 gtk_tree_model_get (multiattrib
->store
, &iter
,
476 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
478 for (a_iter
= geda_list_get_glist (attr_list
);
479 a_iter
!= NULL
; a_iter
= a_iter
->next
)
480 if ((OBJECT
*) a_iter
->data
== object
)
481 /* found attribute in list */
484 g_object_unref (attr_list
);
490 /* can't find attribute--fall back to single-attribute editor */
491 attrib_edit_dialog (w_current
, object
, FROM_MENU
);
495 /* invoke the editor */
496 path
= gtk_tree_model_get_path (multiattrib
->store
, &iter
);
497 gtk_widget_grab_focus (GTK_WIDGET (multiattrib
->treeview
));
498 gtk_tree_view_set_cursor (multiattrib
->treeview
, path
,
499 multiattrib
->column_value
, TRUE
);
500 gtk_tree_path_free (path
);
504 /*! \brief Returns TRUE/FALSE if the given object may have attributes attached.
506 * \par Function Description
508 * Returns TRUE/FALSE if the given object may have attributes attached.
510 * \param [in] object The OBJECT to test.
511 * \returns TRUE/FALSE if the given object may have attributes attached.
513 static gboolean
is_multiattrib_object (OBJECT
*object
)
515 if (object
->type
== OBJ_COMPLEX
||
516 object
->type
== OBJ_PLACEHOLDER
||
517 object
->type
== OBJ_NET
||
518 object
->type
== OBJ_BUS
||
519 object
->type
== OBJ_PIN
) {
526 /*! \todo Finish function documentation
528 * \par Function Description
532 multiattrib_action_add_attribute (GschemMultiattribDockable
*multiattrib
,
536 gint show_name_value
)
539 GtkWidget
*parent_window
;
542 GschemToplevel
*w_current
= multiattrib
->parent
.w_current
;
544 switch (gschem_dockable_get_state (GSCHEM_DOCKABLE (multiattrib
))) {
545 case GSCHEM_DOCKABLE_STATE_DIALOG
:
546 case GSCHEM_DOCKABLE_STATE_WINDOW
:
547 parent_window
= multiattrib
->parent
.window
;
550 parent_window
= w_current
->main_window
;
553 newtext
= g_strdup_printf ("%s=%s", name
, value
);
555 if (!x_dialog_validate_attribute (GTK_WINDOW (parent_window
), newtext
)) {
560 for (iter
= geda_list_get_glist (multiattrib
->object_list
);
562 iter
= g_list_next (iter
)) {
563 object
= (OBJECT
*)iter
->data
;
565 if (is_multiattrib_object (object
)) {
567 /* create a new attribute and link it */
568 o_attrib_add_attrib (w_current
, newtext
,
569 visible
, show_name_value
, object
);
573 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
574 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Add Attribute"));
579 /*! \todo Finish function documentation
581 * \par Function Description
585 multiattrib_action_duplicate_attributes (GschemMultiattribDockable
*multiattrib
,
588 GschemToplevel
*w_current
= multiattrib
->parent
.w_current
;
591 for (iter
= attr_list
;
593 iter
= g_list_next (iter
)) {
594 OBJECT
*o_attrib
= (OBJECT
*)iter
->data
;
596 /* create a new attribute and link it */
597 o_attrib_add_attrib (w_current
,
598 o_text_get_string (w_current
->toplevel
, o_attrib
),
599 o_is_visible (o_attrib
),
600 o_attrib
->show_name_value
,
601 o_attrib
->attached_to
);
604 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
605 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Duplicate Attribute"));
608 /*! \todo Finish function documentation
610 * \par Function Description
614 multiattrib_action_promote_attributes (GschemMultiattribDockable
*multiattrib
,
617 GschemToplevel
*w_current
= multiattrib
->parent
.w_current
;
618 TOPLEVEL
*toplevel
= w_current
->toplevel
;
622 for (iter
= attr_list
;
624 iter
= g_list_next (iter
)) {
625 OBJECT
*o_attrib
= (OBJECT
*)iter
->data
;
627 if (o_is_visible (o_attrib
)) {
628 /* If the attribute we're promoting is visible, don't clone its location */
629 o_attrib_add_attrib (w_current
,
630 o_text_get_string (w_current
->toplevel
, o_attrib
),
632 o_attrib
->show_name_value
,
635 /* make a copy of the attribute object */
636 o_new
= o_object_copy (toplevel
, o_attrib
);
637 s_page_append (toplevel
, toplevel
->page_current
, o_new
);
638 /* add the attribute its parent */
639 o_attrib_attach (toplevel
, o_new
, o_attrib
->parent
, TRUE
);
640 /* note: this object is unselected (not added to selection). */
642 /* Call add-objects-hook */
643 g_run_hook_object (w_current
, "%add-objects-hook", o_new
);
647 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
648 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Promote Attribute"));
651 /*! \todo Finish function documentation
653 * \par Function Description
657 multiattrib_action_delete_attributes (GschemMultiattribDockable
*multiattrib
,
660 GschemToplevel
*w_current
= multiattrib
->parent
.w_current
;
664 for (a_iter
= attr_list
; a_iter
!= NULL
; a_iter
= g_list_next (a_iter
)) {
665 o_attrib
= a_iter
->data
;
666 /* actually deletes the attribute */
667 o_delete (w_current
, o_attrib
);
670 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
671 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Delete Attribute"));
674 /*! \todo Finish function documentation
676 * \par Function Description
680 multiattrib_action_copy_attribute_to_all (GschemMultiattribDockable
*multiattrib
,
683 GschemToplevel
*w_current
= multiattrib
->parent
.w_current
;
685 GList
*objects_needing_add
;
687 objects_needing_add
= g_list_copy (geda_list_get_glist (multiattrib
->object_list
));
689 /* Remove objects which already have this attribute from the list */
690 for (iter
= attr_list
;
692 iter
= g_list_next (iter
)) {
693 OBJECT
*o_attrib
= (OBJECT
*)iter
->data
;
695 objects_needing_add
= g_list_remove (objects_needing_add
, o_attrib
->attached_to
);
698 for (iter
= objects_needing_add
; iter
!= NULL
; iter
= g_list_next (iter
)) {
699 OBJECT
*object
= iter
->data
;
701 if (is_multiattrib_object (object
)) {
703 /* Pick the first instance to copy from */
704 OBJECT
*attrib_to_copy
= attr_list
->data
;
706 int visibility
= o_is_visible (attrib_to_copy
) ? VISIBLE
: INVISIBLE
;
708 /* create a new attribute and link it */
709 o_attrib_add_attrib (w_current
,
710 o_text_get_string (w_current
->toplevel
, attrib_to_copy
),
712 attrib_to_copy
->show_name_value
,
717 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
718 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Copy Attribute to All"));
721 /*! \todo Finish function documentation
723 * \par Function Description
727 multiattrib_column_set_data_name (GtkTreeViewColumn
*tree_column
,
728 GtkCellRenderer
*cell
,
729 GtkTreeModel
*tree_model
,
733 GschemMultiattribDockable
*dialog
= GSCHEM_MULTIATTRIB_DOCKABLE (data
);
735 gboolean present_in_all
;
738 gtk_tree_model_get (tree_model
, iter
,
739 COLUMN_INHERITED
, &inherited
,
741 COLUMN_PRESENT_IN_ALL
, &present_in_all
,
746 "foreground-gdk", inherited
? &dialog
->insensitive_text_color
:
747 (!present_in_all
? &dialog
->not_present_in_all_text_color
: NULL
),
748 "editable", !inherited
,
753 /*! \todo Finish function documentation
755 * \par Function Description
759 multiattrib_column_set_data_value (GtkTreeViewColumn
*tree_column
,
760 GtkCellRenderer
*cell
,
761 GtkTreeModel
*tree_model
,
765 GschemMultiattribDockable
*dialog
= GSCHEM_MULTIATTRIB_DOCKABLE (data
);
767 gboolean identical_value
;
770 gtk_tree_model_get (tree_model
, iter
,
771 COLUMN_INHERITED
, &inherited
,
772 COLUMN_VALUE
, &value
,
773 COLUMN_IDENTICAL_VALUE
, &identical_value
,
777 "text", identical_value
? value
: _("<various>"),
778 "foreground-gdk", inherited
? &dialog
->insensitive_text_color
:
779 (!identical_value
? &dialog
->not_identical_value_text_color
: NULL
),
780 "editable", !inherited
,
785 /*! \todo Finish function documentation
787 * \par Function Description
791 multiattrib_column_set_data_visible (GtkTreeViewColumn
*tree_column
,
792 GtkCellRenderer
*cell
,
793 GtkTreeModel
*tree_model
,
798 gboolean identical_visibility
;
801 gtk_tree_model_get (tree_model
, iter
,
802 COLUMN_INHERITED
, &inherited
,
803 COLUMN_VISIBILITY
, &visibility
,
804 COLUMN_IDENTICAL_VISIBILITY
, &identical_visibility
,
808 "active", visibility
,
809 "sensitive", !inherited
,
810 "activatable", !inherited
,
811 "inconsistent", !identical_visibility
,
815 /*! \todo Finish function documentation
817 * \par Function Description
821 multiattrib_column_set_data_show_name (GtkTreeViewColumn
*tree_column
,
822 GtkCellRenderer
*cell
,
823 GtkTreeModel
*tree_model
,
828 gboolean identical_show_name
;
831 gtk_tree_model_get (tree_model
, iter
,
832 COLUMN_INHERITED
, &inherited
,
833 COLUMN_SHOW_NAME_VALUE
, &show_name_value
,
834 COLUMN_IDENTICAL_SHOW_NAME
, &identical_show_name
,
838 "active", snv_shows_name (show_name_value
),
839 "sensitive", !inherited
,
840 "activatable", !inherited
,
841 "inconsistent", !identical_show_name
,
845 /*! \todo Finish function documentation
847 * \par Function Description
851 multiattrib_column_set_data_show_value (GtkTreeViewColumn
*tree_column
,
852 GtkCellRenderer
*cell
,
853 GtkTreeModel
*tree_model
,
858 gboolean identical_show_value
;
861 gtk_tree_model_get (tree_model
, iter
,
862 COLUMN_INHERITED
, &inherited
,
863 COLUMN_SHOW_NAME_VALUE
, &show_name_value
,
864 COLUMN_IDENTICAL_SHOW_VALUE
, &identical_show_value
,
868 "active", snv_shows_value (show_name_value
),
869 "sensitive", !inherited
,
870 "activatable", !inherited
,
871 "inconsistent", !identical_show_value
,
875 /*! \todo Finish function documentation
877 * \par Function Description
881 multiattrib_callback_edited_name (GtkCellRendererText
*cellrenderertext
,
886 GschemMultiattribDockable
*multiattrib
=
887 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
892 GschemToplevel
*w_current
;
893 GtkWidget
*parent_window
;
894 gchar
*value
, *newtext
;
897 w_current
= multiattrib
->parent
.w_current
;
899 if (!gtk_tree_model_get_iter_from_string (multiattrib
->store
, &iter
, arg1
)) {
903 switch (gschem_dockable_get_state (GSCHEM_DOCKABLE (multiattrib
))) {
904 case GSCHEM_DOCKABLE_STATE_DIALOG
:
905 case GSCHEM_DOCKABLE_STATE_WINDOW
:
906 parent_window
= multiattrib
->parent
.window
;
909 parent_window
= w_current
->main_window
;
912 if (g_ascii_strcasecmp (new_name
, "") == 0) {
913 GtkWidget
*dialog
= gtk_message_dialog_new (
914 GTK_WINDOW (parent_window
),
918 _("Attributes with empty name are not allowed. Please set a name."));
920 gtk_dialog_run (GTK_DIALOG (dialog
));
921 gtk_widget_destroy (dialog
);
925 gtk_tree_model_get (multiattrib
->store
, &iter
,
926 COLUMN_VALUE
, &value
,
927 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
930 newtext
= g_strdup_printf ("%s=%s", new_name
, value
);
932 if (!x_dialog_validate_attribute (GTK_WINDOW (parent_window
), newtext
)) {
938 for (a_iter
= geda_list_get_glist (attr_list
);
940 a_iter
= g_list_next (a_iter
)) {
941 o_attrib
= a_iter
->data
;
943 visibility
= o_is_visible (o_attrib
) ? VISIBLE
: INVISIBLE
;
945 /* actually modifies the attribute */
946 o_text_change (w_current
, o_attrib
,
947 newtext
, visibility
, o_attrib
->show_name_value
);
950 g_object_unref (attr_list
);
954 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
955 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Edit Attribute Name"));
957 /* NB: We don't fix up the model to reflect the edit, we're about to nuke it below... */
959 /* Refresh the whole model.. some attribute names may consolidate into one row */
960 multiattrib_update (multiattrib
);
963 /*! \todo Finish function documentation
965 * \par Function Description
969 multiattrib_callback_edited_value (GtkCellRendererText
*cell_renderer
,
974 GschemMultiattribDockable
*multiattrib
=
975 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
980 GschemToplevel
*w_current
;
983 GtkWidget
*parent_window
;
987 w_current
= multiattrib
->parent
.w_current
;
989 if (!gtk_tree_model_get_iter_from_string (multiattrib
->store
, &iter
, arg1
)) {
993 gtk_tree_model_get (multiattrib
->store
, &iter
,
995 COLUMN_VALUE
, &old_value
,
996 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
999 /* If the edit didn't change anything, don't adjust any attributes */
1000 if (strcmp (old_value
, new_value
) == 0)
1003 switch (gschem_dockable_get_state (GSCHEM_DOCKABLE (multiattrib
))) {
1004 case GSCHEM_DOCKABLE_STATE_DIALOG
:
1005 case GSCHEM_DOCKABLE_STATE_WINDOW
:
1006 parent_window
= multiattrib
->parent
.window
;
1009 parent_window
= w_current
->main_window
;
1012 newtext
= g_strdup_printf ("%s=%s", name
, new_value
);
1014 if (!x_dialog_validate_attribute (GTK_WINDOW (parent_window
), newtext
)) {
1020 for (a_iter
= geda_list_get_glist (attr_list
);
1022 a_iter
= g_list_next (a_iter
)) {
1023 o_attrib
= (OBJECT
*)a_iter
->data
;
1025 visibility
= o_is_visible (o_attrib
) ? VISIBLE
: INVISIBLE
;
1027 /* actually modifies the attribute */
1028 o_text_change (w_current
, o_attrib
,
1029 newtext
, visibility
, o_attrib
->show_name_value
);
1032 g_object_unref (attr_list
);
1037 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
1038 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Edit Attribute"));
1040 /* Fixup the model to reflect the edit */
1041 gtk_list_store_set (GTK_LIST_STORE (multiattrib
->store
), &iter
,
1042 COLUMN_VALUE
, new_value
,
1043 COLUMN_IDENTICAL_VALUE
, TRUE
,
1047 /*! \todo Finish function documentation
1049 * \par Function Description
1053 multiattrib_callback_toggled_visible (GtkCellRendererToggle
*cell_renderer
,
1057 GschemMultiattribDockable
*multiattrib
=
1058 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1061 GschemToplevel
*w_current
;
1062 gboolean new_visibility
;
1063 GedaList
*attr_list
;
1066 w_current
= multiattrib
->parent
.w_current
;
1068 if (!gtk_tree_model_get_iter_from_string (multiattrib
->store
, &iter
, path
)) {
1072 gtk_tree_model_get (multiattrib
->store
, &iter
,
1073 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1076 new_visibility
= !gtk_cell_renderer_toggle_get_active (cell_renderer
);
1078 for (a_iter
= geda_list_get_glist (attr_list
);
1080 a_iter
= g_list_next (a_iter
)) {
1081 o_attrib
= (OBJECT
*)a_iter
->data
;
1083 /* actually modifies the attribute */
1084 o_invalidate (w_current
, o_attrib
);
1085 o_set_visibility (w_current
->toplevel
, o_attrib
, new_visibility
? VISIBLE
: INVISIBLE
);
1086 o_text_recreate (w_current
->toplevel
, o_attrib
);
1089 g_object_unref (attr_list
);
1091 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
1092 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Toggle Attribute Visibility"));
1094 /* Fixup the model to reflect the edit */
1095 gtk_list_store_set (GTK_LIST_STORE (multiattrib
->store
), &iter
,
1096 COLUMN_VISIBILITY
, new_visibility
,
1097 COLUMN_IDENTICAL_VISIBILITY
, TRUE
,
1101 /*! \todo Finish function documentation
1103 * \par Function Description
1107 multiattrib_callback_toggled_show_name (GtkCellRendererToggle
*cell_renderer
,
1111 GschemMultiattribDockable
*multiattrib
=
1112 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1114 GschemToplevel
*w_current
;
1115 gboolean new_name_visible
;
1116 GedaList
*attr_list
;
1120 w_current
= multiattrib
->parent
.w_current
;
1122 if (!gtk_tree_model_get_iter_from_string (multiattrib
->store
, &iter
, path
)) {
1126 gtk_tree_model_get (multiattrib
->store
, &iter
,
1127 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1130 new_name_visible
= !gtk_cell_renderer_toggle_get_active (cell_renderer
);
1132 for (a_iter
= geda_list_get_glist (attr_list
);
1134 a_iter
= g_list_next (a_iter
)) {
1135 OBJECT
*o_attrib
= (OBJECT
*)a_iter
->data
;
1137 gboolean value_visible
= snv_shows_value (o_attrib
->show_name_value
);
1139 /* If we switch off the name visibility, but the value was not previously visible, make it so now */
1140 if (new_name_visible
)
1141 new_snv
= value_visible
? SHOW_NAME_VALUE
: SHOW_NAME
;
1143 new_snv
= SHOW_VALUE
;
1145 o_invalidate (w_current
, o_attrib
);
1147 /* actually modifies the attribute */
1148 o_attrib
->show_name_value
= new_snv
;
1149 o_text_recreate (w_current
->toplevel
, o_attrib
);
1152 g_object_unref (attr_list
);
1154 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
1155 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Toggle Show Attribute Name"));
1157 /* NB: We don't fix up the model to reflect the edit, we're about to nuke it below... */
1159 /* request an update of display for this row */
1160 /* Recompute the whole model as the consistency for the show value column may be affected above */
1161 multiattrib_update (multiattrib
);
1164 /*! \todo Finish function documentation
1166 * \par Function Description
1170 multiattrib_callback_toggled_show_value (GtkCellRendererToggle
*cell_renderer
,
1174 GschemMultiattribDockable
*multiattrib
=
1175 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1177 GschemToplevel
*w_current
;
1178 gboolean new_value_visible
;
1179 GedaList
*attr_list
;
1183 w_current
= multiattrib
->parent
.w_current
;
1185 if (!gtk_tree_model_get_iter_from_string (multiattrib
->store
, &iter
, path
)) {
1189 gtk_tree_model_get (multiattrib
->store
, &iter
,
1190 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1193 new_value_visible
= !gtk_cell_renderer_toggle_get_active (cell_renderer
);
1195 for (a_iter
= geda_list_get_glist (attr_list
);
1197 a_iter
= g_list_next (a_iter
)) {
1198 OBJECT
*o_attrib
= (OBJECT
*)a_iter
->data
;
1200 gboolean name_visible
= snv_shows_name (o_attrib
->show_name_value
);
1202 /* If we switch off the name visibility, but the value was not previously visible, make it so now */
1203 if (new_value_visible
)
1204 new_snv
= name_visible
? SHOW_NAME_VALUE
: SHOW_VALUE
;
1206 new_snv
= SHOW_NAME
;
1208 o_invalidate (w_current
, o_attrib
);
1210 /* actually modifies the attribute */
1211 o_attrib
->show_name_value
= new_snv
;
1212 o_text_recreate (w_current
->toplevel
, o_attrib
);
1215 g_object_unref (attr_list
);
1217 gschem_toplevel_page_content_changed (w_current
, w_current
->toplevel
->page_current
);
1218 o_undo_savestate_old (w_current
, UNDO_ALL
, _("Toggle Show Attribute Value"));
1220 /* NB: We don't fix up the model to reflect the edit, we're about to nuke it below... */
1222 /* request an update of display for this row */
1223 /* Recompute the whole model as the consistency for the show name column may be affected above */
1224 multiattrib_update (multiattrib
);
1227 /*! \todo Finish function documentation
1229 * \par Function Description
1233 multiattrib_callback_key_pressed (GtkWidget
*widget
,
1237 GschemMultiattribDockable
*multiattrib
=
1238 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1240 if ((event
->state
& gtk_accelerator_get_default_mod_mask ()) == 0 &&
1241 (event
->keyval
== GDK_Delete
|| event
->keyval
== GDK_KP_Delete
)) {
1242 GtkTreeModel
*model
;
1244 GedaList
*attr_list
;
1246 /* delete the currently selected attribute */
1248 if (!gtk_tree_selection_get_selected (
1249 gtk_tree_view_get_selection (multiattrib
->treeview
),
1251 /* nothing selected, nothing to do */
1255 gtk_tree_model_get (model
, &iter
,
1256 COLUMN_INHERITED
, &inherited
,
1257 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1260 /* We can't delete inherited attribtes */
1264 multiattrib_action_delete_attributes (multiattrib
,
1265 geda_list_get_glist (attr_list
));
1267 g_object_unref (attr_list
);
1269 /* update the treeview contents */
1270 multiattrib_update (multiattrib
);
1277 /*! \brief Find model row with given name and inheritance flag.
1279 * Looks for the (first, but there should be only one) row in
1280 * \a tree_model that matches \a name and \a inherited. If found,
1281 * sets \a iter_return to an iterator pointing to that row.
1283 * \returns whether a matching row has been found
1286 find_row (GtkTreeModel
*tree_model
, GtkTreeIter
*iter_return
,
1287 gchar
*name
, gboolean inherited
)
1292 for (valid
= gtk_tree_model_get_iter_first (tree_model
, &iter
);
1294 valid
= gtk_tree_model_iter_next (tree_model
, &iter
)) {
1296 gboolean iter_inherited
;
1298 gtk_tree_model_get (tree_model
, &iter
,
1299 COLUMN_NAME
, &iter_name
,
1300 COLUMN_INHERITED
, &iter_inherited
,
1302 matches
= iter_inherited
== inherited
&& strcmp (iter_name
, name
) == 0;
1306 *iter_return
= iter
;
1315 /*! \brief Move edit focus to the cell pointed to by a mouse event.
1316 * \par Function Description
1317 * Uses the X and Y coordinates of a mouse event, to move edit focus
1318 * to the cell at those coords.
1320 * If the cell represents the value of an inherited attribute, edits
1321 * the value of the attached attribute with that name. If there's no
1322 * attached attribute with that name, promotes the attribute first.
1324 * NB: The coordinates must be relative to the tree view's bin window, IE.. have
1325 * come from en event where event->window == gtk_tree_view_get_bin_window ().
1327 * \param [in] multiattrib The GschemMultiattribDockable object.
1328 * \param [in] x The x coordinate of the mouse event.
1329 * \param [in] y The y coordinate of the mouse event.
1332 multiattrib_edit_cell_at_pos (GschemMultiattribDockable
*multiattrib
,
1336 GtkTreeViewColumn
*column
;
1340 GedaList
*attr_list
;
1342 if (!gtk_tree_view_get_path_at_pos (multiattrib
->treeview
,
1343 x
, y
, &path
, &column
, NULL
, NULL
))
1345 if (!gtk_tree_model_get_iter (multiattrib
->store
, &iter
, path
)) {
1346 gtk_tree_path_free (path
);
1350 gtk_tree_model_get (multiattrib
->store
, &iter
,
1351 COLUMN_INHERITED
, &inherited
,
1354 /* row is editable--just edit it */
1355 gtk_tree_view_set_cursor_on_cell (multiattrib
->treeview
,
1356 path
, column
, NULL
, TRUE
);
1357 gtk_tree_path_free (path
);
1360 gtk_tree_path_free (path
);
1362 /* don't promote attributes when trying to edit columns other than "value" */
1363 if (column
!= multiattrib
->column_value
)
1366 /* see if there's already a matching attached attribute */
1367 gtk_tree_model_get (multiattrib
->store
, &iter
,
1370 if (!find_row (multiattrib
->store
, &iter
, name
, FALSE
)) {
1371 /* promote attribute */
1372 gtk_tree_model_get (multiattrib
->store
, &iter
,
1373 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1375 multiattrib_action_promote_attributes (multiattrib
,
1376 geda_list_get_glist (attr_list
));
1377 g_object_unref (attr_list
);
1378 multiattrib_update (multiattrib
);
1380 /* find tree iterator corresponding to the promoted attribute */
1381 if (!find_row (multiattrib
->store
, &iter
, name
, FALSE
)) {
1388 /* get new path and invoke editor */
1389 path
= gtk_tree_model_get_path (multiattrib
->store
, &iter
);
1390 gtk_tree_view_set_cursor_on_cell (multiattrib
->treeview
,
1391 path
, column
, NULL
, TRUE
);
1392 gtk_tree_path_free (path
);
1396 /*! \todo Finish function documentation
1398 * \par Function Description
1402 multiattrib_callback_button_pressed (GtkWidget
*widget
,
1403 GdkEventButton
*event
,
1406 GschemMultiattribDockable
*multiattrib
=
1407 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1408 gboolean ret
= FALSE
;
1410 /* popup menu on right click */
1411 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3) {
1412 multiattrib_popup_menu (multiattrib
, event
);
1416 /* edit cell on double (left) click */
1417 /* (Normally, edit focus by click is handled for us, but this function is useful
1418 * for overriding the default behavior of treating a double-click the same as a
1419 * single-click, with edit focus needing two consecutive double or single clicks
1420 * with a pause in between. This can be unintuitive and time-wasting) */
1422 if (event
->type
== GDK_2BUTTON_PRESS
&& event
->button
== 1) {
1423 multiattrib_edit_cell_at_pos (multiattrib
, event
->x
, event
->y
);
1431 /*! \todo Finish function documentation
1433 * \par Function Description
1437 multiattrib_callback_query_tooltip (GtkWidget
*widget
,
1438 gint x
, gint y
, gboolean keyboard_mode
,
1439 GtkTooltip
*tooltip
, gpointer user_data
)
1441 GschemMultiattribDockable
*multiattrib
=
1442 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1444 GtkTreeView
*tree_view
= GTK_TREE_VIEW (widget
);
1445 GtkTreePath
*path
= NULL
;
1446 GtkTreeViewColumn
*column
= NULL
;
1449 gtk_tree_view_get_cursor (tree_view
, &path
, &column
);
1451 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view
, x
, y
,
1453 if (y
< 0 ? !gtk_tree_view_get_path_at_pos (tree_view
, x
, 0,
1454 NULL
, &column
, NULL
, NULL
)
1455 : !gtk_tree_view_get_path_at_pos (tree_view
, x
, y
,
1456 &path
, &column
, NULL
, NULL
))
1461 /* show tooltip for column header */
1463 if (column
== multiattrib
->column_visible
)
1464 gtk_tooltip_set_markup (tooltip
, _("Is the attribute visible?"));
1465 else if (column
== multiattrib
->column_show_name
)
1466 gtk_tooltip_set_markup (tooltip
, _("Show attribute name?"));
1467 else if (column
== multiattrib
->column_show_value
)
1468 gtk_tooltip_set_markup (tooltip
, _("Show attribute value?"));
1473 /* show tooltip for cell */
1475 GtkTreeModel
*model
;
1479 if (column
!= multiattrib
->column_value
)
1482 model
= gtk_tree_view_get_model (tree_view
);
1483 gtk_tree_model_get_iter (model
, &iter
, path
);
1484 gtk_tree_model_get (model
, &iter
, COLUMN_VALUE
, &value
, -1);
1486 gtk_tooltip_set_markup (tooltip
, value
);
1487 gtk_tree_view_set_tooltip_row (tree_view
, tooltip
, path
);
1490 gtk_tree_path_free (path
);
1498 /*! \todo Finish function documentation
1500 * \par Function Description
1504 multiattrib_callback_popup_menu (GtkWidget
*widget
,
1507 GschemMultiattribDockable
*multiattrib
=
1508 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1510 multiattrib_popup_menu (multiattrib
, NULL
);
1515 /*! \todo Finish function documentation
1517 * \par Function Description
1521 multiattrib_callback_popup_duplicate (GtkMenuItem
*menuitem
,
1524 GschemMultiattribDockable
*multiattrib
=
1525 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1526 GtkTreeModel
*model
;
1528 GedaList
*attr_list
;
1530 if (!gtk_tree_selection_get_selected (
1531 gtk_tree_view_get_selection (multiattrib
->treeview
),
1533 /* nothing selected, nothing to do */
1537 gtk_tree_model_get (model
, &iter
,
1538 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1540 multiattrib_action_duplicate_attributes (multiattrib
, geda_list_get_glist (attr_list
));
1541 g_object_unref (attr_list
);
1543 /* update the treeview contents */
1544 multiattrib_update (multiattrib
);
1547 /*! \todo Finish function documentation
1549 * \par Function Description
1553 multiattrib_callback_popup_promote (GtkMenuItem
*menuitem
,
1556 GschemMultiattribDockable
*multiattrib
=
1557 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1558 GtkTreeModel
*model
;
1560 GedaList
*attr_list
;
1562 if (!gtk_tree_selection_get_selected (
1563 gtk_tree_view_get_selection (multiattrib
->treeview
),
1565 /* nothing selected, nothing to do */
1569 gtk_tree_model_get (model
, &iter
,
1570 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1572 multiattrib_action_promote_attributes (multiattrib
, geda_list_get_glist (attr_list
));
1573 g_object_unref (attr_list
);
1575 /* update the treeview contents */
1576 multiattrib_update (multiattrib
);
1579 /*! \todo Finish function documentation
1581 * \par Function Description
1585 multiattrib_callback_popup_delete (GtkMenuItem
*menuitem
,
1588 GschemMultiattribDockable
*multiattrib
=
1589 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1590 GtkTreeModel
*model
;
1592 GedaList
*attr_list
;
1594 if (!gtk_tree_selection_get_selected (
1595 gtk_tree_view_get_selection (multiattrib
->treeview
),
1597 /* nothing selected, nothing to do */
1601 gtk_tree_model_get (model
, &iter
,
1602 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1604 multiattrib_action_delete_attributes (multiattrib
, geda_list_get_glist (attr_list
));
1605 g_object_unref (attr_list
);
1607 /* update the treeview contents */
1608 multiattrib_update (multiattrib
);
1611 /*! \todo Finish function documentation
1613 * \par Function Description
1617 multiattrib_callback_popup_copy_to_all (GtkMenuItem
*menuitem
,
1620 GschemMultiattribDockable
*multiattrib
=
1621 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1622 GtkTreeModel
*model
;
1624 GedaList
*attr_list
;
1626 if (!gtk_tree_selection_get_selected (
1627 gtk_tree_view_get_selection (multiattrib
->treeview
),
1629 /* nothing selected, nothing to do */
1633 gtk_tree_model_get (model
, &iter
,
1634 COLUMN_ATTRIBUTE_GEDALIST
, &attr_list
,
1636 multiattrib_action_copy_attribute_to_all (multiattrib
, geda_list_get_glist (attr_list
));
1637 g_object_unref (attr_list
);
1639 /* update the treeview contents */
1640 multiattrib_update (multiattrib
);
1643 /*! \todo Finish function documentation
1645 * \par Function Description
1649 multiattrib_callback_value_key_pressed (GtkWidget
*widget
,
1653 GschemMultiattribDockable
*multiattrib
=
1654 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1655 gboolean retval
= FALSE
;
1657 /* ends editing of cell if one of these keys are pressed: */
1658 /* - the Return key without the Control modifier */
1659 /* - the Tab key without the Control modifier */
1660 if ((event
->keyval
== GDK_Return
|| event
->keyval
== GDK_KP_Enter
) ||
1661 (event
->keyval
== GDK_Tab
|| event
->keyval
== GDK_KP_Tab
||
1662 event
->keyval
== GDK_ISO_Left_Tab
)) {
1663 /* Control modifier activated? */
1664 if (event
->state
& GDK_CONTROL_MASK
) {
1665 /* yes the modifier in event structure and let event propagate */
1666 event
->state
^= GDK_CONTROL_MASK
;
1669 /* change focus and stop propagation */
1670 g_signal_emit_by_name (GSCHEM_DOCKABLE (multiattrib
)->widget
,
1672 (event
->state
& GDK_SHIFT_MASK
) ?
1673 GTK_DIR_TAB_BACKWARD
: GTK_DIR_TAB_FORWARD
);
1682 /*! \brief GtkWidget "grab-focus" signal handler
1684 * \par Function Description
1685 * Select the text in the GtkTextView so it may be over-typed quickly
1688 multiattrib_callback_value_grab_focus (GtkWidget
*widget
, gpointer user_data
)
1690 GtkTextView
*textview
= GTK_TEXT_VIEW (widget
);
1691 GtkTextBuffer
*textbuffer
;
1692 GtkTextIter startiter
, enditer
;
1694 textbuffer
= gtk_text_view_get_buffer (textview
);
1695 gtk_text_buffer_get_iter_at_offset (textbuffer
, &startiter
, 0);
1696 gtk_text_buffer_get_iter_at_offset (textbuffer
, &enditer
, -1);
1697 gtk_text_buffer_select_range (textbuffer
, &enditer
, &startiter
);
1701 /*! \todo Finish function documentation
1703 * \par Function Description
1707 multiattrib_callback_button_add (GtkButton
*button
, gpointer user_data
)
1709 GschemMultiattribDockable
*multiattrib
=
1710 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
1711 GtkTextBuffer
*buffer
;
1712 GtkTextIter start
, end
;
1718 buffer
= gtk_text_view_get_buffer (multiattrib
->textview_value
);
1720 /* retrieve information from the Add/Edit frame */
1721 /* - attribute's name */
1722 name
= gtk_entry_get_text (
1723 GTK_ENTRY (GTK_COMBO (multiattrib
->combo_name
)->entry
));
1724 /* - attribute's value */
1725 gtk_text_buffer_get_bounds (buffer
, &start
, &end
);
1726 value
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
1727 /* - attribute's visibility status */
1728 visible
= gtk_toggle_button_get_active (
1729 (GtkToggleButton
*)multiattrib
->button_visible
);
1730 /* - visibility type */
1731 shownv
= (gint
)gtk_option_menu_get_history (multiattrib
->optionmenu_shownv
);
1733 if (name
[0] == '\0' || name
[0] == ' ') {
1734 /* name not allowed for an attribute */
1739 multiattrib_action_add_attribute (multiattrib
,
1744 multiattrib_update (multiattrib
);
1747 /*! \todo Finish function documentation
1749 * \par Function Description
1753 multiattrib_init_attrib_names (GtkCombo
*combo
)
1755 GList
*items
= NULL
;
1756 const gchar
*string
;
1759 for (i
= 0, string
= s_attrib_get (i
);
1761 i
++, string
= s_attrib_get (i
)) {
1762 items
= g_list_append (items
, (gpointer
)string
);
1765 gtk_combo_set_popdown_strings (GTK_COMBO (combo
), items
);
1767 g_list_free (items
);
1770 /*! \todo Finish function documentation
1772 * \par Function Description
1776 multiattrib_init_visible_types (GtkOptionMenu
*optionmenu
)
1778 GtkWidget
*menu
, *item
;
1780 menu
= gtk_menu_new ();
1781 item
= gtk_menu_item_new_with_label (_("Show Name & Value"));
1782 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1783 item
= gtk_menu_item_new_with_label (_("Show Value only"));
1784 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1785 item
= gtk_menu_item_new_with_label (_("Show Name only"));
1786 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1788 gtk_option_menu_set_menu (optionmenu
, menu
);
1792 /*! \brief Popup a context-sensitive menu.
1793 * \par Function Description
1794 * Pops up a context-sensitive menu.
1795 * <B>event</B> can be NULL if the popup is triggered by a key binding
1796 * instead of a mouse click.
1798 * \param [in] multiattrib The GschemMultiattribDockable object.
1799 * \param [in] event Mouse event.
1802 multiattrib_popup_menu (GschemMultiattribDockable
*multiattrib
,
1803 GdkEventButton
*event
)
1812 struct menuitem_t menuitems_inherited
[] = {
1813 { N_("Promote"), G_CALLBACK (multiattrib_callback_popup_promote
) },
1816 struct menuitem_t menuitems_noninherited
[] = {
1817 { N_("Duplicate"), G_CALLBACK (multiattrib_callback_popup_duplicate
) },
1818 { N_("Delete"), G_CALLBACK (multiattrib_callback_popup_delete
) },
1819 { N_("Copy to all"), G_CALLBACK (multiattrib_callback_popup_copy_to_all
)},
1822 struct menuitem_t
*item_list
;
1823 struct menuitem_t
*tmp
;
1825 GtkTreeModel
*model
;
1827 GtkTreeSelection
*selection
;
1829 selection
= gtk_tree_view_get_selection (multiattrib
->treeview
);
1831 if (event
!= NULL
&&
1832 gtk_tree_view_get_path_at_pos (multiattrib
->treeview
,
1835 &path
, NULL
, NULL
, NULL
)) {
1836 gtk_tree_selection_unselect_all (selection
);
1837 gtk_tree_selection_select_path (selection
, path
);
1838 gtk_tree_path_free (path
);
1841 /* if nothing is selected, nothing to do */
1842 if (!gtk_tree_selection_get_selected (selection
, &model
, &iter
))
1845 gtk_tree_model_get (model
, &iter
,
1846 COLUMN_INHERITED
, &inherited
,
1849 item_list
= inherited
? menuitems_inherited
: menuitems_noninherited
;
1851 /* create the context menu */
1852 menu
= gtk_menu_new();
1853 for (tmp
= item_list
; tmp
->label
!= NULL
; tmp
++) {
1854 GtkWidget
*menuitem
;
1855 if (strcmp (tmp
->label
, "-") == 0) {
1856 menuitem
= gtk_separator_menu_item_new ();
1858 menuitem
= gtk_menu_item_new_with_label (_(tmp
->label
));
1859 g_signal_connect (menuitem
,
1864 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
1866 gtk_widget_show_all (menu
);
1867 /* make menu a popup menu */
1868 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
1869 (event
!= NULL
) ? event
->button
: 0,
1870 gdk_event_get_time ((GdkEvent
*)event
));
1874 /*! \brief GschemDockable "save_internal_geometry" class method handler
1876 * \par Function Description
1877 * Save the dockable's current internal geometry.
1879 * \param [in] dockable The GschemDockable to save the geometry of.
1880 * \param [in] key_file The GKeyFile to save the geometry data to.
1881 * \param [in] group_name The group name in the key file to store the data under.
1884 multiattrib_save_internal_geometry (GschemDockable
*dockable
,
1888 gboolean show_inherited
;
1891 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GSCHEM_MULTIATTRIB_DOCKABLE (dockable
)->show_inherited
));
1892 eda_config_set_boolean (cfg
, group_name
, "show_inherited", show_inherited
);
1896 /*! \brief GschemDockable "restore_internal_geometry" class method handler
1898 * \par Function Description
1899 * Restore the dockable's current internal geometry.
1901 * \param [in] dockable The GschemDockable to restore the geometry of.
1902 * \param [in] key_file The GKeyFile to save the geometry data to.
1903 * \param [in] group_name The group name in the key file to store the data under.
1906 multiattrib_restore_internal_geometry (GschemDockable
*dockable
,
1910 gboolean show_inherited
;
1911 GError
*error
= NULL
;
1913 show_inherited
= eda_config_get_boolean (cfg
, group_name
, "show_inherited", &error
);
1914 if (error
!= NULL
) {
1915 show_inherited
= TRUE
;
1916 g_error_free (error
);
1918 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GSCHEM_MULTIATTRIB_DOCKABLE (dockable
)->show_inherited
), show_inherited
);
1922 /*! \brief Function to retrieve GschemMultiattribDockable's GType identifier.
1924 * \par Function Description
1926 * Function to retrieve GschemMultiattribDockable's GType identifier.
1927 * Upon first call, this registers GschemMultiattribDockable in the GType system.
1928 * Subsequently it returns the saved value from its first execution.
1930 * \return the GType identifier associated with GschemMultiattribDockable.
1933 gschem_multiattrib_dockable_get_type ()
1935 static GType multiattrib_type
= 0;
1937 if (!multiattrib_type
) {
1938 static const GTypeInfo multiattrib_info
= {
1939 sizeof(GschemMultiattribDockableClass
),
1940 NULL
, /* base_init */
1941 NULL
, /* base_finalize */
1942 (GClassInitFunc
) multiattrib_class_init
,
1943 NULL
, /* class_finalize */
1944 NULL
, /* class_data */
1945 sizeof(GschemMultiattribDockable
),
1946 0, /* n_preallocs */
1947 NULL
, /* multiattrib_init */
1950 multiattrib_type
= g_type_register_static (GSCHEM_TYPE_DOCKABLE
,
1951 "GschemMultiattribDockable",
1952 &multiattrib_info
, 0);
1955 return multiattrib_type
;
1959 /*! \brief Update the multiattrib editor dialog when its object list changes.
1961 * \par Function Description
1963 * \param [in] selection The GedaList object of we are watching/
1964 * \param [in] multiattrib The multi-attribute editor dialog.
1967 object_list_changed_cb (GedaList
*object_list
,
1968 GschemMultiattribDockable
*multiattrib
)
1970 multiattrib_update (multiattrib
);
1974 /*! \brief Update the dialog when the current object GedaList object is destroyed
1976 * \par Function Description
1978 * This handler is called when the g_object_weak_ref() on the GedaList object
1979 * we're watching expires. We reset our multiattrib->object_list pointer to NULL
1980 * to avoid attempting to access the destroyed object. NB: Our signal handlers
1981 * were automatically disconnected during the destruction process.
1983 * \param [in] data Pointer to the multi-attrib dialog
1984 * \param [in] where_the_object_was Pointer to where the object was just destroyed
1987 object_list_weak_ref_cb (gpointer data
, GObject
*where_the_object_was
)
1989 GschemMultiattribDockable
*multiattrib
= GSCHEM_MULTIATTRIB_DOCKABLE (data
);
1991 multiattrib
->object_list
= NULL
;
1992 multiattrib_update (multiattrib
);
1996 /*! \brief Connect signal handler and weak_ref on the GedaList object
1998 * \par Function Description
2000 * Connect the "changed" signal and add a weak reference
2001 * on the GedaList object we are going to watch.
2003 * \param [in] multiattrib The GschemMultiattribDockable dialog.
2004 * \param [in] object_list The GedaList object to watch.
2007 connect_object_list (GschemMultiattribDockable
*multiattrib
,
2008 GedaList
*object_list
)
2010 multiattrib
->object_list
= object_list
;
2011 if (multiattrib
->object_list
!= NULL
) {
2012 g_object_weak_ref (G_OBJECT (multiattrib
->object_list
),
2013 object_list_weak_ref_cb
,
2015 multiattrib
->object_list_changed_id
=
2016 g_signal_connect (G_OBJECT (multiattrib
->object_list
),
2018 G_CALLBACK (object_list_changed_cb
),
2020 /* Synthesise a object_list changed update to refresh the view */
2021 object_list_changed_cb (multiattrib
->object_list
, multiattrib
);
2023 /* Call an update to set the sensitivities */
2024 multiattrib_update (multiattrib
);
2029 /*! \brief Disconnect signal handler and weak_ref on the GedaList object
2031 * \par Function Description
2033 * If the dialog is watching a GedaList object, disconnect the
2034 * "changed" signal and remove our weak reference on the object.
2036 * \param [in] multiattrib The GschemMultiattribDockable dialog.
2039 disconnect_object_list (GschemMultiattribDockable
*multiattrib
)
2041 if (multiattrib
->object_list
!= NULL
) {
2042 g_signal_handler_disconnect (multiattrib
->object_list
,
2043 multiattrib
->object_list_changed_id
);
2044 g_object_weak_unref (G_OBJECT (multiattrib
->object_list
),
2045 object_list_weak_ref_cb
,
2051 /*! \brief GObject finalise handler
2053 * \par Function Description
2055 * Just before the GschemMultiattribDockable GObject is finalized, disconnect from
2056 * the GedaList object being watched and then chain up to the parent
2057 * class's finalize handler.
2059 * \param [in] object The GObject being finalized.
2062 multiattrib_finalize (GObject
*object
)
2064 GschemMultiattribDockable
*multiattrib
=
2065 GSCHEM_MULTIATTRIB_DOCKABLE (object
);
2067 disconnect_object_list (multiattrib
);
2068 G_OBJECT_CLASS (multiattrib_parent_class
)->finalize (object
);
2072 /*! \brief GType class initialiser for GschemMultiattribDockable
2074 * \par Function Description
2076 * GType class initialiser for GschemMultiattribDockable. We override our parent
2077 * virtual class methods as needed and register our GObject properties.
2079 * \param [in] klass The GschemMultiattribDockableClass we are initialising
2082 multiattrib_class_init (GschemMultiattribDockableClass
*klass
)
2084 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
2085 GschemDockableClass
*gschem_dockable_class
= GSCHEM_DOCKABLE_CLASS (klass
);
2087 gschem_dockable_class
->create_widget
= multiattrib_create_widget
;
2089 gschem_dockable_class
->save_internal_geometry
=
2090 multiattrib_save_internal_geometry
;
2091 gschem_dockable_class
->restore_internal_geometry
=
2092 multiattrib_restore_internal_geometry
;
2094 gobject_class
->set_property
= multiattrib_set_property
;
2095 gobject_class
->get_property
= multiattrib_get_property
;
2096 gobject_class
->finalize
= multiattrib_finalize
;
2098 multiattrib_parent_class
= g_type_class_peek_parent (klass
);
2100 g_object_class_install_property (
2101 gobject_class
, PROP_OBJECT_LIST
,
2102 g_param_spec_pointer ("object_list",
2105 G_PARAM_READWRITE
));
2108 /*! \brief Regenerate the attribute list when the visibility
2109 * setting for inherited attributes changes
2112 multiattrib_show_inherited_toggled (GtkToggleButton
*button
,
2115 GschemMultiattribDockable
*multiattrib
=
2116 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
2118 /* update the treeview contents */
2119 multiattrib_update (multiattrib
);
2123 /*! \brief Determine the index of \a name in the list of attributes.
2125 * \returns the index, or \c -1 if the attribute name wasn't found
2128 lookup_attrib_index (const gchar
*name
)
2133 for (int i
= 0; (ref
= s_attrib_get (i
)) != NULL
; i
++)
2134 if (strcmp (name
, ref
) == 0)
2139 /*! \brief Determine the order in which two rows should be sorted.
2141 * Takes the order of attributes set with \c attribute-name into
2142 * account and sorts all attached attributes before all inherited
2145 * \returns \c -1 , \c 0 or \c 1 depending on whether \a iter0 sorts
2146 * before, with or after \a iter1
2149 attribute_sort_func (GtkTreeModel
*model
,
2150 GtkTreeIter
*iter0
, GtkTreeIter
*iter1
,
2153 GschemMultiattribDockable
*multiattrib
=
2154 GSCHEM_MULTIATTRIB_DOCKABLE (user_data
);
2155 gboolean inherited0
, inherited1
;
2156 gchar
*name0
, *name1
;
2159 gtk_tree_model_get (multiattrib
->store
, iter0
,
2160 COLUMN_INHERITED
, &inherited0
,
2161 COLUMN_NAME
, &name0
,
2163 gtk_tree_model_get (multiattrib
->store
, iter1
,
2164 COLUMN_INHERITED
, &inherited1
,
2165 COLUMN_NAME
, &name1
,
2168 if (inherited1
&& !inherited0
)
2170 else if (inherited0
&& !inherited1
)
2172 else if (name0
== NULL
) {
2177 } else if (name1
== NULL
)
2180 gint index0
= lookup_attrib_index (name0
),
2181 index1
= lookup_attrib_index (name1
);
2183 if (index0
!= -1 && (index1
== -1 || index1
> index0
))
2185 else if (index1
!= -1 && (index0
== -1 || index0
> index1
))
2188 result
= strcmp (name0
, name1
);
2197 /*! \brief Create widgets for GschemMultiattribDockable
2199 * \par Function Description
2201 * Create and setup the widgets which make up the dockable.
2203 * \param [in] dockable The GschemMultiattribDockable we are initialising
2206 multiattrib_create_widget (GschemDockable
*dockable
)
2208 GschemMultiattribDockable
*multiattrib
=
2209 GSCHEM_MULTIATTRIB_DOCKABLE (dockable
);
2211 GtkWidget
*label
, *scrolled_win
, *treeview
;
2212 GtkWidget
*table
, *textview
, *combo
, *optionm
, *button
;
2213 GtkWidget
*attrib_vbox
, *show_inherited
;
2214 GtkCellRenderer
*renderer
;
2215 GtkTreeViewColumn
*column
;
2216 GtkTreeSelection
*selection
;
2219 vbox
= gtk_vbox_new (FALSE
, DIALOG_V_SPACING
);
2220 gtk_container_set_border_width (GTK_CONTAINER (vbox
), DIALOG_BORDER_SPACING
);
2222 /* create the attribute list frame */
2223 multiattrib
->list_frame
= GTK_WIDGET (g_object_new (GTK_TYPE_FRAME
,
2225 "shadow", GTK_SHADOW_NONE
,
2227 /* - create the model for the treeview */
2228 multiattrib
->store
= GTK_TREE_MODEL (
2229 gtk_list_store_new (NUM_COLUMNS
,
2230 G_TYPE_BOOLEAN
, /* COLUMN_INHERITED */
2231 G_TYPE_STRING
, /* COLUMN_NAME */
2232 G_TYPE_STRING
, /* COLUMN_VALUE */
2233 G_TYPE_BOOLEAN
, /* COLUMN_VISIBILITY */
2234 G_TYPE_INT
, /* COLUMN_SHOW_NAME_VALUE */
2235 G_TYPE_BOOLEAN
, /* COLUMN_PRESENT_IN_ALL */
2236 G_TYPE_BOOLEAN
, /* COLUMN_IDENTICAL_VALUE */
2237 G_TYPE_BOOLEAN
, /* COLUMN_IDENTICAL_VISIBILITY */
2238 G_TYPE_BOOLEAN
, /* COLUMN_IDENTICAL_SHOW_NAME */
2239 G_TYPE_BOOLEAN
, /* COLUMN_IDENTICAL_SHOW_VALUE */
2240 G_TYPE_OBJECT
)); /* COLUMN_ATTRIBUTE_GEDALIST */
2241 gtk_tree_sortable_set_default_sort_func (
2242 GTK_TREE_SORTABLE (multiattrib
->store
),
2243 attribute_sort_func
, multiattrib
, NULL
);
2244 gtk_tree_sortable_set_sort_column_id (
2245 GTK_TREE_SORTABLE (multiattrib
->store
),
2246 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID
, GTK_SORT_ASCENDING
);
2248 /* - create a scrolled window for the treeview */
2249 scrolled_win
= GTK_WIDGET (
2250 g_object_new (GTK_TYPE_SCROLLED_WINDOW
,
2253 /* GtkScrolledWindow */
2254 "hscrollbar-policy",
2255 GTK_POLICY_AUTOMATIC
,
2256 "vscrollbar-policy",
2257 GTK_POLICY_AUTOMATIC
,
2259 GTK_SHADOW_ETCHED_IN
,
2261 /* - create the treeview */
2262 treeview
= GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW
,
2264 "model", multiattrib
->store
,
2267 g_signal_connect (treeview
,
2269 G_CALLBACK (multiattrib_callback_key_pressed
),
2271 g_signal_connect (treeview
,
2272 "button-press-event",
2273 G_CALLBACK (multiattrib_callback_button_pressed
),
2275 g_signal_connect (treeview
,
2277 G_CALLBACK (multiattrib_callback_popup_menu
),
2279 g_signal_connect (treeview
,
2281 G_CALLBACK (multiattrib_callback_query_tooltip
),
2283 gtk_widget_set_has_tooltip (GTK_WIDGET (treeview
), TRUE
);
2284 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview
));
2285 gtk_tree_selection_set_mode (selection
,
2286 GTK_SELECTION_SINGLE
);
2288 /* - and now the columns of the treeview */
2289 /* - column 1: visibility */
2290 renderer
= GTK_CELL_RENDERER (
2291 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE
,
2293 g_signal_connect (renderer
,
2295 G_CALLBACK (multiattrib_callback_toggled_visible
),
2297 multiattrib
->column_visible
=
2298 column
= GTK_TREE_VIEW_COLUMN (
2299 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
2300 /* GtkTreeViewColumn */
2303 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
2304 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
2305 multiattrib_column_set_data_visible
,
2307 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
2308 /* - column 2: show name */
2309 renderer
= GTK_CELL_RENDERER (
2310 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE
,
2312 g_signal_connect (renderer
,
2314 G_CALLBACK (multiattrib_callback_toggled_show_name
),
2316 multiattrib
->column_show_name
=
2317 column
= GTK_TREE_VIEW_COLUMN (
2318 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
2319 /* GtkTreeViewColumn */
2322 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
2323 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
2324 multiattrib_column_set_data_show_name
,
2326 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
2327 /* - column 3: show value */
2328 renderer
= GTK_CELL_RENDERER (
2329 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE
,
2331 g_signal_connect (renderer
,
2333 G_CALLBACK (multiattrib_callback_toggled_show_value
),
2335 multiattrib
->column_show_value
=
2336 column
= GTK_TREE_VIEW_COLUMN (
2337 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
2338 /* GtkTreeViewColumn */
2341 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
2342 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
2343 multiattrib_column_set_data_show_value
,
2345 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
2346 /* - column 4: attribute name */
2347 renderer
= GTK_CELL_RENDERER (
2348 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT
,
2349 /* GtkCellRendererText */
2350 /* unknown in GTK 2.4 */
2352 * PANGO_ELLIPSIZE_END, */
2354 g_signal_connect (renderer
,
2356 G_CALLBACK (multiattrib_callback_edited_name
),
2358 multiattrib
->column_name
=
2359 column
= GTK_TREE_VIEW_COLUMN (
2360 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
2361 /* GtkTreeViewColumn */
2366 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
2367 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
2368 multiattrib_column_set_data_name
,
2370 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
2371 /* - column 5: attribute value */
2372 renderer
= GTK_CELL_RENDERER (
2373 g_object_new (TYPE_CELL_RENDERER_MULTI_LINE_TEXT
,
2374 /* GtkCellRendererText */
2375 /* unknown in GTK 2.4 */
2377 PANGO_ELLIPSIZE_END, */
2379 g_signal_connect (renderer
,
2381 G_CALLBACK (multiattrib_callback_edited_value
),
2383 multiattrib
->column_value
=
2384 column
= GTK_TREE_VIEW_COLUMN (
2385 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
2386 /* GtkTreeViewColumn */
2387 "title", _("Value"),
2391 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
2392 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
2393 multiattrib_column_set_data_value
,
2395 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
2397 /* add the treeview to the scrolled window */
2398 gtk_container_add (GTK_CONTAINER (scrolled_win
), treeview
);
2399 /* set treeview of multiattrib */
2400 multiattrib
->treeview
= GTK_TREE_VIEW (treeview
);
2402 attrib_vbox
= gtk_vbox_new (FALSE
, 4);
2404 /* Pack the vbox into the frame */
2405 gtk_container_add (GTK_CONTAINER (multiattrib
->list_frame
), attrib_vbox
);
2407 multiattrib
->status_label
= gtk_label_new (NULL
);
2408 gtk_misc_set_alignment (GTK_MISC (multiattrib
->status_label
), 0, 1);
2409 gtk_misc_set_padding (GTK_MISC (multiattrib
->status_label
), 3, 1);
2410 gtk_label_set_ellipsize (GTK_LABEL (multiattrib
->status_label
),
2411 PANGO_ELLIPSIZE_END
);
2412 gtk_label_set_single_line_mode (GTK_LABEL (multiattrib
->status_label
), TRUE
);
2414 gtk_box_pack_start (GTK_BOX (attrib_vbox),
2415 multiattrib->status_label, FALSE, FALSE, 0);
2416 gtk_widget_show (multiattrib->status_label);
2419 /* add the scrolled window to box */
2420 gtk_box_pack_start (GTK_BOX (attrib_vbox
), scrolled_win
, TRUE
, TRUE
, 0);
2422 /* create the show inherited button */
2423 show_inherited
= gtk_check_button_new_with_label (_("Show inherited attributes"));
2424 multiattrib
->show_inherited
= show_inherited
;
2425 gtk_box_pack_start (GTK_BOX (attrib_vbox
), show_inherited
, FALSE
, FALSE
, 0);
2427 g_signal_connect (show_inherited
,
2429 G_CALLBACK (multiattrib_show_inherited_toggled
),
2432 /* pack the frame */
2433 gtk_box_pack_start (GTK_BOX (vbox
),
2434 multiattrib
->list_frame
,
2436 gtk_widget_show_all (multiattrib
->list_frame
);
2438 /* create the add/edit frame */
2439 multiattrib
->add_frame
= GTK_WIDGET (g_object_new (GTK_TYPE_FRAME
,
2440 "label", _("Add Attribute"),
2442 table
= GTK_WIDGET (g_object_new (GTK_TYPE_TABLE
,
2446 "homogeneous", FALSE
,
2449 /* - the name entry: a GtkComboBoxEntry */
2450 label
= GTK_WIDGET (g_object_new (GTK_TYPE_LABEL
,
2455 "label", _("Name:"),
2457 combo
= GTK_WIDGET (g_object_new (GTK_TYPE_COMBO
,
2459 "value-in-list", FALSE
,
2461 multiattrib_init_attrib_names (GTK_COMBO (combo
));
2462 multiattrib
->combo_name
= GTK_COMBO (combo
);
2463 gtk_table_attach (GTK_TABLE (table
), label
,
2464 0, 1, 0, 1, 0, 0, 0, 0);
2465 /* prevent combo from forcing the add button out of add_frame */
2466 gtk_widget_set_size_request (combo
, 0, -1);
2467 gtk_table_attach (GTK_TABLE (table
), combo
,
2468 1, 2, 0, 1, GTK_EXPAND
| GTK_FILL
, 0, 6, 3);
2470 /* - the value entry: a GtkEntry */
2471 label
= GTK_WIDGET (g_object_new (GTK_TYPE_LABEL
,
2476 "label", _("Value:"),
2478 scrolled_win
= GTK_WIDGET (
2479 g_object_new (GTK_TYPE_SCROLLED_WINDOW
,
2480 /* GtkScrolledWindow */
2481 "hscrollbar-policy",
2482 GTK_POLICY_AUTOMATIC
,
2483 "vscrollbar-policy",
2484 GTK_POLICY_AUTOMATIC
,
2488 /*! \todo Forcing the size request is a horrible band-aid and
2489 * should be replaced by a better heuristic. */
2490 textview
= GTK_WIDGET (g_object_new (GTK_TYPE_TEXT_VIEW
, NULL
));
2491 gtk_widget_set_tooltip_text (GTK_WIDGET (textview
),
2492 _("Ctrl+Enter inserts new line; Ctrl+Tab inserts Tab"));
2493 g_signal_connect (textview
,
2495 G_CALLBACK (multiattrib_callback_value_key_pressed
),
2497 g_signal_connect (textview
,
2499 G_CALLBACK (multiattrib_callback_value_grab_focus
),
2501 /* Save the GTK_STATE_NORMAL color so we can work around GtkTextView's
2502 * stubborn refusal to draw with GTK_STATE_INSENSITIVE later on */
2503 style
= gtk_widget_get_style (textview
);
2504 multiattrib
->value_normal_text_color
= style
->text
[ GTK_STATE_NORMAL
];
2506 /* Save this one so we can pick it as a sensible colour to show the
2507 * inherited attributes dimmed.
2509 style
= gtk_widget_get_style (treeview
);
2510 multiattrib
->insensitive_text_color
= style
->text
[ GTK_STATE_INSENSITIVE
];
2512 gdk_color_parse ("grey", &multiattrib
->not_identical_value_text_color
);
2513 gdk_color_parse ("red", &multiattrib
->not_present_in_all_text_color
);
2515 gtk_container_add (GTK_CONTAINER (scrolled_win
), textview
);
2516 multiattrib
->textview_value
= GTK_TEXT_VIEW (textview
);
2517 gtk_table_attach (GTK_TABLE (table
), label
,
2518 0, 1, 1, 2, 0, 0, 0, 0);
2519 gtk_table_attach (GTK_TABLE (table
), scrolled_win
,
2520 1, 2, 1, 2, GTK_EXPAND
| GTK_FILL
, 0, 6, 3);
2522 /* - the visible status */
2523 button
= GTK_WIDGET (g_object_new (GTK_TYPE_CHECK_BUTTON
,
2525 "label", _("Visible"),
2528 multiattrib
->button_visible
= GTK_CHECK_BUTTON (button
);
2529 gtk_table_attach (GTK_TABLE (table
), button
,
2530 0, 1, 2, 3, GTK_FILL
, 0, 3, 0);
2532 /* - the visibility type */
2533 optionm
= GTK_WIDGET (g_object_new (GTK_TYPE_OPTION_MENU
,
2535 multiattrib_init_visible_types (GTK_OPTION_MENU (optionm
));
2536 multiattrib
->optionmenu_shownv
= GTK_OPTION_MENU (optionm
);
2537 /* prevent optionm from forcing the add button out of add_frame */
2538 gtk_widget_set_size_request (optionm
, 0, -1);
2539 gtk_table_attach (GTK_TABLE (table
), optionm
,
2540 1, 2, 2, 3, GTK_EXPAND
| GTK_FILL
, 0, 6, 3);
2541 gtk_widget_show_all (table
);
2543 /* create the add button */
2544 button
= gtk_button_new_from_stock (GTK_STOCK_ADD
);
2545 g_signal_connect (button
,
2547 G_CALLBACK (multiattrib_callback_button_add
),
2549 gtk_table_attach (GTK_TABLE (table
), button
,
2550 2, 3, 0, 3, 0, 0, 6, 3);
2552 /* add the table to the frame */
2553 gtk_container_add (GTK_CONTAINER (multiattrib
->add_frame
), table
);
2554 /* pack the frame in the dialog */
2555 gtk_box_pack_start (GTK_BOX (vbox
),
2556 multiattrib
->add_frame
,
2558 gtk_widget_show_all (multiattrib
->add_frame
);
2560 gtk_widget_show (vbox
);
2562 multiattrib_update (multiattrib
);
2568 /*! \brief GObject property setter function
2570 * \par Function Description
2571 * Setter function for GschemMultiattribDockable's GObject property, "object_list".
2573 * \param [in] object The GObject whose properties we are setting
2574 * \param [in] property_id The numeric id. under which the property was
2575 * registered with g_object_class_install_property()
2576 * \param [in] value The GValue the property is being set from
2577 * \param [in] pspec A GParamSpec describing the property being set
2581 multiattrib_set_property (GObject
*object
,
2583 const GValue
*value
,
2586 GschemMultiattribDockable
*multiattrib
=
2587 GSCHEM_MULTIATTRIB_DOCKABLE (object
);
2589 switch(property_id
) {
2590 case PROP_OBJECT_LIST
:
2591 disconnect_object_list (multiattrib
);
2592 connect_object_list (multiattrib
, g_value_get_pointer (value
));
2595 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2600 /*! \brief GObject property getter function
2602 * \par Function Description
2603 * Getter function for GschemMultiattribDockable's GObject property, "object_list".
2605 * \param [in] object The GObject whose properties we are getting
2606 * \param [in] property_id The numeric id. under which the property was
2607 * registered with g_object_class_install_property()
2608 * \param [out] value The GValue in which to return the value of the property
2609 * \param [in] pspec A GParamSpec describing the property being got
2612 multiattrib_get_property (GObject
*object
,
2617 GschemMultiattribDockable
*multiattrib
=
2618 GSCHEM_MULTIATTRIB_DOCKABLE (object
);
2620 switch(property_id
) {
2621 case PROP_OBJECT_LIST
:
2622 g_value_set_pointer (value
, (gpointer
)multiattrib
->object_list
);
2625 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
2634 gboolean visibility
;
2635 int show_name_value
;
2637 gboolean present_in_all
;
2638 gboolean identical_value
;
2639 gboolean identical_visibility
;
2640 gboolean identical_show_name
;
2641 gboolean identical_show_value
;
2643 GedaList
*attribute_gedalist
;
2646 /*! \brief For a given OBJECT, produce a GList of MODEL_ROW records
2648 * \par Function Description
2650 * The main purpose of this function is to provide the "nth_with_name"
2651 * count which we need to merge the attribute lists of various objects
2654 * \param [in] multiattrib The multi-attribute editor dialog (For libgeda API which needs a TOPLEVEL)
2655 * \param [in] object The OBJECT * whos attributes we are processing
2656 * \returns A GList of MODEL_ROW records detailing object's attributes.
2659 object_attributes_to_model_rows (GschemMultiattribDockable
*multiattrib
,
2662 GList
*model_rows
= NULL
;
2664 GList
*object_attribs
= o_attrib_return_attribs (object
);
2666 for (a_iter
= object_attribs
; a_iter
!= NULL
;
2667 a_iter
= g_list_next (a_iter
)) {
2669 OBJECT
*a_current
= a_iter
->data
;
2670 MODEL_ROW
*m_row
= g_new0 (MODEL_ROW
, 1);
2673 m_row
->inherited
= o_attrib_is_inherited (a_current
);
2674 o_attrib_get_name_value (a_current
, &m_row
->name
, &m_row
->value
);
2675 m_row
->visibility
= o_is_visible (a_current
);
2676 m_row
->show_name_value
= a_current
->show_name_value
;
2677 m_row
->nth_with_name
= 0; /* Provisional value until we check below */
2679 /* The following fields are always true for a single OBJECT */
2680 m_row
->present_in_all
= TRUE
;
2681 m_row
->identical_value
= TRUE
;
2682 m_row
->identical_visibility
= TRUE
;
2683 m_row
->identical_show_name
= TRUE
;
2684 m_row
->identical_show_value
= TRUE
;
2686 m_row
->attribute_gedalist
= geda_list_new ();
2687 geda_list_add (m_row
->attribute_gedalist
, a_current
);
2689 /* Search already processed attributes to see if we need to bump m_row->nth_with_name */
2690 for (m_iter
= model_rows
;
2692 m_iter
= g_list_next (m_iter
)) {
2693 MODEL_ROW
*m_compare
= m_iter
->data
;
2694 if (strcmp (m_compare
->name
, m_row
->name
) == 0 &&
2695 m_compare
->inherited
== m_row
->inherited
) {
2696 m_row
->nth_with_name
= m_row
->nth_with_name
+ 1;
2700 model_rows
= g_list_append (model_rows
, m_row
);
2703 g_list_free (object_attribs
);
2708 /*! \brief Produce a GList of MODEL_ROW records for all attribute objects in our GedaList
2710 * \par Function Description
2712 * This function produces a GList of MODEL_ROWs to the user can edit unattached
2713 * attributes, or attributes which are selected separately from their owning
2716 * It is not expected this will be called when the GedaList the dialog is watching
2717 * contains any higher level objects on which we could edit attributes.
2719 * \param [in] multiattrib The multi-attribute editor dialog
2720 * \returns A GList of MODEL_ROW records detailing all lone selected attributes.
2723 lone_attributes_to_model_rows (GschemMultiattribDockable
*multiattrib
)
2726 GList
*model_rows
= NULL
;
2728 /* populate the store with attributes */
2729 for (o_iter
= multiattrib
->object_list
== NULL
? NULL
: geda_list_get_glist (multiattrib
->object_list
);
2731 o_iter
= g_list_next (o_iter
)) {
2732 OBJECT
*object
= o_iter
->data
;
2735 /* Consider a selected text object might be an attribute */
2736 if (object
->type
!= OBJ_TEXT
||
2737 !o_attrib_get_name_value (object
, NULL
, NULL
))
2740 /* We have an OBJ_TEXT which libgeda can parse as an attribute */
2742 multiattrib
->num_lone_attribs_in_list
++;
2744 m_row
= g_new0 (MODEL_ROW
, 1);
2745 m_row
->inherited
= o_attrib_is_inherited (object
);
2746 o_attrib_get_name_value (object
, &m_row
->name
, &m_row
->value
);
2747 m_row
->visibility
= o_is_visible (object
);
2748 m_row
->show_name_value
= object
->show_name_value
;
2749 m_row
->nth_with_name
= 0; /* All selected attributes are treated individually */
2751 /* The following fields are always true for a single attribute */
2752 m_row
->present_in_all
= TRUE
;
2753 m_row
->identical_value
= TRUE
;
2754 m_row
->identical_visibility
= TRUE
;
2755 m_row
->identical_show_name
= TRUE
;
2756 m_row
->identical_show_value
= TRUE
;
2758 m_row
->attribute_gedalist
= geda_list_new ();
2759 geda_list_add (m_row
->attribute_gedalist
, object
);
2761 model_rows
= g_list_append (model_rows
, m_row
);
2767 /*! \brief Populate the multiattrib editor dialog's liststore
2769 * \par Function Description
2771 * Consumes the GList of MODEL_ROW data, populating the dialog's liststore.
2772 * The function frees / consumes the GList and MODEL_ROW data.
2774 * \param [in] multiattrib The multi-attribute editor dialog.
2775 * \param [in] model_rows A GList of MODEL_ROW data.
2778 multiattrib_populate_liststore (GschemMultiattribDockable
*multiattrib
,
2781 GtkListStore
*liststore
;
2782 GtkTreeIter tree_iter
;
2785 /* Clear the existing list of attributes */
2786 liststore
= GTK_LIST_STORE (multiattrib
->store
);
2787 gtk_list_store_clear (liststore
);
2789 for (m_iter
= model_rows
;
2791 m_iter
= g_list_next (m_iter
)) {
2793 MODEL_ROW
*model_row
= m_iter
->data
;
2795 model_row
->present_in_all
=
2796 (g_list_length (geda_list_get_glist (model_row
->attribute_gedalist
))
2797 == multiattrib
->total_num_in_list
);
2799 gtk_list_store_append (liststore
, &tree_iter
);
2800 gtk_list_store_set (liststore
,
2802 COLUMN_INHERITED
, model_row
->inherited
,
2803 COLUMN_NAME
, model_row
->name
,
2804 COLUMN_VALUE
, model_row
->value
,
2805 COLUMN_VISIBILITY
, model_row
->visibility
,
2806 COLUMN_SHOW_NAME_VALUE
, model_row
->show_name_value
,
2807 COLUMN_PRESENT_IN_ALL
, model_row
->present_in_all
,
2808 COLUMN_IDENTICAL_VALUE
, model_row
->identical_value
,
2809 COLUMN_IDENTICAL_VISIBILITY
, model_row
->identical_visibility
,
2810 COLUMN_IDENTICAL_SHOW_NAME
, model_row
->identical_show_name
,
2811 COLUMN_IDENTICAL_SHOW_VALUE
, model_row
->identical_show_value
,
2812 COLUMN_ATTRIBUTE_GEDALIST
, model_row
->attribute_gedalist
,
2815 /* Drop our ref on the GedaList so it is freed when the model has done with it */
2816 g_object_unref (model_row
->attribute_gedalist
);
2819 g_list_foreach (model_rows
, (GFunc
) g_free
, NULL
);
2820 g_list_free (model_rows
);
2824 append_dialog_title_extra (GString
*title_string
,
2825 int *num_title_extras
,
2831 va_start (args
, text
);
2832 if ((*num_title_extras
)++ != 0)
2833 g_string_append (title_string
, ", ");
2834 g_string_append_vprintf (title_string
, text
, args
);
2839 update_dialog_title (GschemMultiattribDockable
*multiattrib
,
2840 char *complex_title_name
)
2842 GString
*title_string
= g_string_new (NULL
);
2843 int num_title_extras
= 0;
2845 if (multiattrib
->num_complex_in_list
> 0) {
2846 append_dialog_title_extra (title_string
, &num_title_extras
,
2847 ngettext ("%i component (%s)", "%i components (%s)", multiattrib
->num_complex_in_list
),
2848 multiattrib
->num_complex_in_list
, complex_title_name
);
2851 if (multiattrib
->num_pins_in_list
> 0) {
2852 append_dialog_title_extra (title_string
, &num_title_extras
,
2853 ngettext ("%i pin", "%i pins", multiattrib
->num_pins_in_list
),
2854 multiattrib
->num_pins_in_list
);
2857 if (multiattrib
->num_nets_in_list
> 0) {
2858 append_dialog_title_extra (title_string
, &num_title_extras
,
2859 ngettext ("%i net", "%i nets", multiattrib
->num_nets_in_list
),
2860 multiattrib
->num_nets_in_list
);
2863 if (multiattrib
->num_buses_in_list
> 0) {
2864 append_dialog_title_extra (title_string
, &num_title_extras
,
2865 ngettext ("%i bus", "%i buses", multiattrib
->num_buses_in_list
),
2866 multiattrib
->num_buses_in_list
);
2869 if (multiattrib
->num_lone_attribs_in_list
> 0) {
2870 append_dialog_title_extra (title_string
, &num_title_extras
,
2871 ngettext ("%i attribute", "%i attributes", multiattrib
->num_lone_attribs_in_list
),
2872 multiattrib
->num_lone_attribs_in_list
);
2875 char *title
= g_string_free (title_string
, FALSE
);
2876 if (title
!= NULL
&& *title
!= '\0')
2877 gtk_label_set_text (GTK_LABEL (multiattrib
->status_label
), title
);
2879 gtk_label_set_text (GTK_LABEL (multiattrib
->status_label
),
2880 _("Select a component, net, or attribute"));
2884 /*! \brief Update the multiattrib editor dialog's interface
2886 * \par Function Description
2888 * Update the dialog to reflect the attributes of the currently selected
2889 * object. If no (or multiple) objects are selected, the dialog's controls
2890 * are set insensitive.
2892 * \param [in] multiattrib The multi-attribute editor dialog.
2895 multiattrib_update (GschemMultiattribDockable
*multiattrib
)
2899 gboolean show_inherited
;
2900 gboolean list_sensitive
;
2901 gboolean add_sensitive
;
2902 GList
*model_rows
= NULL
;
2903 char *complex_title_name
= NULL
;
2905 if (multiattrib
->parent
.widget
== NULL
)
2906 /* ignore if widgets haven't been created yet */
2910 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (multiattrib
->show_inherited
));
2912 multiattrib
->total_num_in_list
= 0;
2913 multiattrib
->num_complex_in_list
= 0;
2914 multiattrib
->num_pins_in_list
= 0;
2915 multiattrib
->num_nets_in_list
= 0;
2916 multiattrib
->num_buses_in_list
= 0;
2917 multiattrib
->num_lone_attribs_in_list
= 0;
2919 /* populate the store with attributes */
2920 for (o_iter
= multiattrib
->object_list
== NULL
? NULL
: geda_list_get_glist (multiattrib
->object_list
);
2922 o_iter
= g_list_next (o_iter
)) {
2923 OBJECT
*object
= o_iter
->data
;
2928 if (!is_multiattrib_object (object
))
2931 /* Count the different objects we are editing */
2932 multiattrib
->total_num_in_list
++;
2934 if (object
->type
== OBJ_COMPLEX
||
2935 object
->type
== OBJ_PLACEHOLDER
) {
2936 multiattrib
->num_complex_in_list
++;
2938 if (complex_title_name
== NULL
)
2939 complex_title_name
= object
->complex_basename
;
2940 else if (strcmp (complex_title_name
, object
->complex_basename
) != 0)
2941 complex_title_name
= _("<various>");
2944 if (object
->type
== OBJ_PIN
)
2945 multiattrib
->num_pins_in_list
++;
2947 if (object
->type
== OBJ_NET
)
2948 multiattrib
->num_nets_in_list
++;
2950 if (object
->type
== OBJ_BUS
)
2951 multiattrib
->num_buses_in_list
++;
2953 /* populate the store with any attributes from this object */
2954 object_rows
= object_attributes_to_model_rows (multiattrib
, object
);
2956 for (or_iter
= object_rows
;
2958 or_iter
= g_list_next (or_iter
)) {
2960 MODEL_ROW
*object_row
= or_iter
->data
;
2961 MODEL_ROW
*model_row
;
2963 gboolean found
= FALSE
;
2965 /* Skip over inherited attributes if we don't want to show them */
2966 if (!show_inherited
&& object_row
->inherited
)
2969 /* Search our list of attributes to see if we already encountered */
2970 for (mr_iter
= model_rows
;
2971 mr_iter
!= NULL
&& found
== FALSE
;
2972 mr_iter
= g_list_next (mr_iter
)) {
2973 model_row
= mr_iter
->data
;
2974 if (strcmp (model_row
->name
, object_row
->name
) == 0 &&
2975 model_row
->nth_with_name
== object_row
->nth_with_name
&&
2976 model_row
->inherited
== object_row
->inherited
)
2981 /* Name matches a previously found attribute */
2982 /* Check if the rest of its properties match the one we have stored... */
2984 if (strcmp (model_row
->value
, object_row
->value
) != 0)
2985 model_row
->identical_value
= FALSE
;
2987 if (model_row
->visibility
!= object_row
->visibility
)
2988 model_row
->identical_visibility
= FALSE
;
2990 if (snv_shows_name (model_row
->show_name_value
) !=
2991 snv_shows_name (object_row
->show_name_value
))
2992 model_row
->identical_show_name
= FALSE
;
2994 if (snv_shows_value (model_row
->show_name_value
) !=
2995 snv_shows_value (object_row
->show_name_value
))
2996 model_row
->identical_show_value
= FALSE
;
2998 /* Add the underlying attribute to the row's GedaList of attributes */
2999 geda_list_add_glist (model_row
->attribute_gedalist
,
3000 geda_list_get_glist (object_row
->attribute_gedalist
));
3002 g_object_unref (object_row
->attribute_gedalist
);
3003 g_free (object_row
);
3007 * The attribute name doesn't match any previously found
3008 * attribute, so add the model row entry describing it to the list.
3010 model_rows
= g_list_append (model_rows
, object_row
);
3014 /* delete the list of attribute objects */
3015 g_list_free (object_rows
);
3018 if (multiattrib
->total_num_in_list
== 0) {
3020 /* If the selection contains no high level objects we can edit,
3021 * take a look and see whether there are any lone attributes
3022 * selected we can edit directly.
3024 model_rows
= lone_attributes_to_model_rows (multiattrib
);
3025 list_sensitive
= (multiattrib
->num_lone_attribs_in_list
> 0);
3026 add_sensitive
= FALSE
;
3028 list_sensitive
= TRUE
;
3029 add_sensitive
= TRUE
;
3032 multiattrib_populate_liststore (multiattrib
, model_rows
);
3034 /* Update window title to describe the objects we are editing. */
3035 update_dialog_title (multiattrib
, complex_title_name
);
3037 /* Update sensitivities */
3038 gtk_widget_set_sensitive (GTK_WIDGET (multiattrib
->list_frame
), list_sensitive
);
3039 gtk_widget_set_sensitive (GTK_WIDGET (multiattrib
->add_frame
), add_sensitive
);
3041 /* Work around GtkTextView's stubborn indifference
3042 * to GTK_STATE_INSENSITIVE when rendering its text. */
3043 style
= gtk_widget_get_style (GTK_WIDGET (multiattrib
->textview_value
));
3044 gtk_widget_modify_text (GTK_WIDGET (multiattrib
->textview_value
),
3046 add_sensitive
? &multiattrib
->value_normal_text_color
3047 : &style
->text
[GTK_STATE_INSENSITIVE
]);