Remove extra debug message
[geda-gaf.git] / gschem / src / gschem_multiattrib_dockable.c
blob8c9c0242919db1ace19896e352be5faf7a7aec69
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
20 #include <config.h>
22 #include <stdio.h>
23 #ifdef HAVE_STDLIB_H
24 #include <stdlib.h>
25 #endif
26 #ifdef HAVE_STRING_H
27 #include <string.h>
28 #endif
30 #include "gschem.h"
31 #include "../include/gschem_multiattrib_dockable.h"
32 #include <gdk/gdkkeysyms.h>
35 static void multiattrib_update (GschemMultiattribDockable *multiattrib);
37 static gboolean
38 snv_shows_name (int snv)
40 return snv == SHOW_NAME_VALUE || snv == SHOW_NAME;
43 static gboolean
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.
58 void
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);
75 enum {
76 PROP_EDIT_CANCELED = 1
79 static void
80 celltextview_set_property (GObject *object,
81 guint property_id,
82 const GValue *value,
83 GParamSpec *pspec)
85 CellTextView *celltextview = (CellTextView*) object;
87 switch (property_id) {
88 case PROP_EDIT_CANCELED:
89 celltextview->editing_canceled = g_value_get_boolean (value);
90 break;
91 default:
92 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
96 static void
97 celltextview_get_property (GObject *object,
98 guint property_id,
99 GValue *value,
100 GParamSpec *pspec)
102 CellTextView *celltextview = (CellTextView*) object;
104 switch (property_id) {
105 case PROP_EDIT_CANCELED:
106 g_value_set_boolean (value, celltextview->editing_canceled);
107 break;
108 default:
109 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
114 /*! \todo Finish function documentation
115 * \brief
116 * \par Function Description
119 static gboolean
120 celltextview_key_press_event (GtkWidget *widget,
121 GdkEventKey *key_event,
122 gpointer data)
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));
138 return TRUE;
141 return FALSE;
144 /*! \todo Finish function documentation
145 * \brief
146 * \par Function Description
149 static void
150 celltextview_start_editing (GtkCellEditable *cell_editable,
151 GdkEvent *event)
153 g_signal_connect (cell_editable,
154 "key_press_event",
155 G_CALLBACK (celltextview_key_press_event),
156 NULL);
159 /*! \todo Finish function documentation
160 * \brief
161 * \par Function Description
164 GType
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),
178 0, /* n_preallocs */
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,
189 "CellTextView",
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
200 * \brief
201 * \par Function Description
204 static void
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 (
213 gobject_class,
214 PROP_EDIT_CANCELED,
215 g_param_spec_boolean ("editing-canceled",
218 FALSE,
219 G_PARAM_READWRITE));
222 /*! \todo Finish function documentation
223 * \brief
224 * \par Function Description
227 static void
228 celltextview_init (CellTextView *celltextview)
230 celltextview->editing_canceled = FALSE;
233 /*! \todo Finish function documentation
234 * \brief
235 * \par Function Description
238 static void
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
247 * of GTK 2.4 (LGPL).
249 static void cellrenderermultilinetext_class_init (CellRendererMultiLineTextClass *klass);
250 static void cellrenderermultilinetext_editing_done (GtkCellEditable *cell_editable,
251 gpointer user_data);
252 static gboolean cellrenderermultilinetext_focus_out_event (GtkWidget *widget,
253 GdkEvent *event,
254 gpointer user_data);
257 #define CELL_RENDERER_MULTI_LINE_TEXT_PATH "cell-renderer-multi-line-text-path"
260 /*! \todo Finish function documentation
261 * \brief
262 * \par Function Description
265 static GtkCellEditable*
266 cellrenderermultilinetext_start_editing (GtkCellRenderer *cell,
267 GdkEvent *event,
268 GtkWidget *widget,
269 const gchar *path,
270 GdkRectangle *background_area,
271 GdkRectangle *cell_area,
272 GtkCellRendererState flags)
274 GtkCellRendererText *cell_text;
275 CellRendererMultiLineText *cell_mlt;
276 GtkWidget *textview;
277 GtkTextBuffer *textbuffer;
279 cell_text = GTK_CELL_RENDERER_TEXT (cell);
280 if (cell_text->editable == FALSE) {
281 return NULL;
284 cell_mlt = CELL_RENDERER_MULTI_LINE_TEXT (cell);
286 textbuffer = GTK_TEXT_BUFFER (g_object_new (GTK_TYPE_TEXT_BUFFER,
287 NULL));
288 gtk_text_buffer_set_text (textbuffer,
289 cell_text->text,
290 strlen (cell_text->text));
292 textview = GTK_WIDGET (g_object_new (TYPE_CELL_TEXT_VIEW,
293 /* GtkTextView */
294 "buffer", textbuffer,
295 "editable", TRUE,
296 /* GtkWidget */
297 "height-request", cell_area->height,
298 NULL));
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),
306 "editing_done",
307 G_CALLBACK (cellrenderermultilinetext_editing_done),
308 cell_mlt);
309 cell_mlt->focus_out_id =
310 g_signal_connect (textview,
311 "focus_out_event",
312 G_CALLBACK (cellrenderermultilinetext_focus_out_event),
313 cell_mlt);
315 return GTK_CELL_EDITABLE (textview);
318 /*! \todo Finish function documentation
319 * \brief
320 * \par Function Description
323 static void
324 cellrenderermultilinetext_editing_done (GtkCellEditable *cell_editable,
325 gpointer user_data)
327 CellRendererMultiLineText *cell = CELL_RENDERER_MULTI_LINE_TEXT (user_data);
328 GtkTextBuffer *buffer;
329 GtkTextIter start, end;
330 gchar *new_text;
331 const gchar *path;
333 if (cell->focus_out_id > 0) {
334 g_signal_handler_disconnect (cell_editable,
335 cell->focus_out_id);
336 cell->focus_out_id = 0;
339 if (CELL_TEXT_VIEW (cell_editable)->editing_canceled) {
340 g_signal_emit_by_name (cell, "editing-canceled");
341 return;
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);
353 g_free (new_text);
356 /*! \todo Finish function documentation
357 * \brief
358 * \par Function Description
361 static gboolean
362 cellrenderermultilinetext_focus_out_event (GtkWidget *widget,
363 GdkEvent *event,
364 gpointer user_data)
366 // cellrenderermultilinetext_editing_done (GTK_CELL_EDITABLE (widget),
367 // user_data);
369 return FALSE;
372 /*! \todo Finish function documentation
373 * \brief
374 * \par Function Description
377 GType
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),
391 0, /* n_preallocs */
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
405 * \brief
406 * \par Function Description
409 static void
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;
419 enum {
420 PROP_OBJECT_LIST = 1
423 enum {
424 COLUMN_INHERITED,
425 COLUMN_NAME,
426 COLUMN_VALUE,
427 COLUMN_VISIBILITY,
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,
435 NUM_COLUMNS
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,
443 guint property_id,
444 const GValue *value,
445 GParamSpec *pspec);
446 static void multiattrib_get_property (GObject *object,
447 guint property_id,
448 GValue *value,
449 GParamSpec *pspec);
451 static void multiattrib_popup_menu (GschemMultiattribDockable *multiattrib,
452 GdkEventButton *event);
455 /*!\brief Invoke the multi-attribute editor to edit a single attribute.
457 void
458 x_multiattrib_edit_attribute (GschemToplevel *w_current, OBJECT *object)
460 GschemMultiattribDockable *multiattrib =
461 GSCHEM_MULTIATTRIB_DOCKABLE (w_current->multiattrib_dockable);
462 GtkTreeIter iter;
463 gboolean valid;
464 GtkTreePath *path;
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);
471 valid;
472 valid = gtk_tree_model_iter_next (multiattrib->store, &iter)) {
473 GedaList *attr_list;
474 GList *a_iter;
475 gtk_tree_model_get (multiattrib->store, &iter,
476 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
477 -1);
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 */
482 break;
484 g_object_unref (attr_list);
485 if (a_iter != NULL)
486 break;
489 if (!valid) {
490 /* can't find attribute--fall back to single-attribute editor */
491 attrib_edit_dialog (w_current, object, FROM_MENU);
492 return;
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) {
520 return TRUE;
522 return FALSE;
526 /*! \todo Finish function documentation
527 * \brief
528 * \par Function Description
531 static void
532 multiattrib_action_add_attribute (GschemMultiattribDockable *multiattrib,
533 const gchar *name,
534 const gchar *value,
535 gint visible,
536 gint show_name_value)
538 OBJECT *object;
539 GtkWidget *parent_window;
540 gchar *newtext;
541 GList *iter;
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;
548 break;
549 default:
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)) {
556 g_free(newtext);
557 return;
560 for (iter = geda_list_get_glist (multiattrib->object_list);
561 iter != NULL;
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"));
576 g_free (newtext);
579 /*! \todo Finish function documentation
580 * \brief
581 * \par Function Description
584 static void
585 multiattrib_action_duplicate_attributes (GschemMultiattribDockable *multiattrib,
586 GList *attr_list)
588 GschemToplevel *w_current = multiattrib->parent.w_current;
589 GList *iter;
591 for (iter = attr_list;
592 iter != NULL;
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
609 * \brief
610 * \par Function Description
613 static void
614 multiattrib_action_promote_attributes (GschemMultiattribDockable *multiattrib,
615 GList *attr_list)
617 GschemToplevel *w_current = multiattrib->parent.w_current;
618 TOPLEVEL *toplevel = w_current->toplevel;
619 OBJECT *o_new;
620 GList *iter;
622 for (iter = attr_list;
623 iter != NULL;
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),
631 VISIBLE,
632 o_attrib->show_name_value,
633 o_attrib->parent);
634 } else {
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
652 * \brief
653 * \par Function Description
656 static void
657 multiattrib_action_delete_attributes (GschemMultiattribDockable *multiattrib,
658 GList *attr_list)
660 GschemToplevel *w_current = multiattrib->parent.w_current;
661 GList *a_iter;
662 OBJECT *o_attrib;
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
675 * \brief
676 * \par Function Description
679 static void
680 multiattrib_action_copy_attribute_to_all (GschemMultiattribDockable *multiattrib,
681 GList *attr_list)
683 GschemToplevel *w_current = multiattrib->parent.w_current;
684 GList *iter;
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;
691 iter != NULL;
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),
711 visibility,
712 attrib_to_copy->show_name_value,
713 object);
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
722 * \brief
723 * \par Function Description
726 static void
727 multiattrib_column_set_data_name (GtkTreeViewColumn *tree_column,
728 GtkCellRenderer *cell,
729 GtkTreeModel *tree_model,
730 GtkTreeIter *iter,
731 gpointer data)
733 GschemMultiattribDockable *dialog = GSCHEM_MULTIATTRIB_DOCKABLE (data);
734 gchar *name;
735 gboolean present_in_all;
736 int inherited;
738 gtk_tree_model_get (tree_model, iter,
739 COLUMN_INHERITED, &inherited,
740 COLUMN_NAME, &name,
741 COLUMN_PRESENT_IN_ALL, &present_in_all,
742 -1);
744 g_object_set (cell,
745 "text", name,
746 "foreground-gdk", inherited ? &dialog->insensitive_text_color :
747 (!present_in_all ? &dialog->not_present_in_all_text_color : NULL),
748 "editable", !inherited,
749 NULL);
750 g_free (name);
753 /*! \todo Finish function documentation
754 * \brief
755 * \par Function Description
758 static void
759 multiattrib_column_set_data_value (GtkTreeViewColumn *tree_column,
760 GtkCellRenderer *cell,
761 GtkTreeModel *tree_model,
762 GtkTreeIter *iter,
763 gpointer data)
765 GschemMultiattribDockable *dialog = GSCHEM_MULTIATTRIB_DOCKABLE (data);
766 gchar *value;
767 gboolean identical_value;
768 int inherited;
770 gtk_tree_model_get (tree_model, iter,
771 COLUMN_INHERITED, &inherited,
772 COLUMN_VALUE, &value,
773 COLUMN_IDENTICAL_VALUE, &identical_value,
774 -1);
776 g_object_set (cell,
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,
781 NULL);
782 g_free (value);
785 /*! \todo Finish function documentation
786 * \brief
787 * \par Function Description
790 static void
791 multiattrib_column_set_data_visible (GtkTreeViewColumn *tree_column,
792 GtkCellRenderer *cell,
793 GtkTreeModel *tree_model,
794 GtkTreeIter *iter,
795 gpointer data)
797 gboolean visibility;
798 gboolean identical_visibility;
799 int inherited;
801 gtk_tree_model_get (tree_model, iter,
802 COLUMN_INHERITED, &inherited,
803 COLUMN_VISIBILITY, &visibility,
804 COLUMN_IDENTICAL_VISIBILITY, &identical_visibility,
805 -1);
807 g_object_set (cell,
808 "active", visibility,
809 "sensitive", !inherited,
810 "activatable", !inherited,
811 "inconsistent", !identical_visibility,
812 NULL);
815 /*! \todo Finish function documentation
816 * \brief
817 * \par Function Description
820 static void
821 multiattrib_column_set_data_show_name (GtkTreeViewColumn *tree_column,
822 GtkCellRenderer *cell,
823 GtkTreeModel *tree_model,
824 GtkTreeIter *iter,
825 gpointer data)
827 int show_name_value;
828 gboolean identical_show_name;
829 int inherited;
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,
835 -1);
837 g_object_set (cell,
838 "active", snv_shows_name (show_name_value),
839 "sensitive", !inherited,
840 "activatable", !inherited,
841 "inconsistent", !identical_show_name,
842 NULL);
845 /*! \todo Finish function documentation
846 * \brief
847 * \par Function Description
850 static void
851 multiattrib_column_set_data_show_value (GtkTreeViewColumn *tree_column,
852 GtkCellRenderer *cell,
853 GtkTreeModel *tree_model,
854 GtkTreeIter *iter,
855 gpointer data)
857 int show_name_value;
858 gboolean identical_show_value;
859 int inherited;
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,
865 -1);
867 g_object_set (cell,
868 "active", snv_shows_value (show_name_value),
869 "sensitive", !inherited,
870 "activatable", !inherited,
871 "inconsistent", !identical_show_value,
872 NULL);
875 /*! \todo Finish function documentation
876 * \brief
877 * \par Function Description
880 static void
881 multiattrib_callback_edited_name (GtkCellRendererText *cellrenderertext,
882 gchar *arg1,
883 gchar *new_name,
884 gpointer user_data)
886 GschemMultiattribDockable *multiattrib =
887 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
888 GtkTreeIter iter;
889 GedaList *attr_list;
890 GList *a_iter;
891 OBJECT *o_attrib;
892 GschemToplevel *w_current;
893 GtkWidget *parent_window;
894 gchar *value, *newtext;
895 int visibility;
897 w_current = multiattrib->parent.w_current;
899 if (!gtk_tree_model_get_iter_from_string (multiattrib->store, &iter, arg1)) {
900 return;
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;
907 break;
908 default:
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),
915 GTK_DIALOG_MODAL,
916 GTK_MESSAGE_ERROR,
917 GTK_BUTTONS_OK,
918 _("Attributes with empty name are not allowed. Please set a name."));
920 gtk_dialog_run (GTK_DIALOG (dialog));
921 gtk_widget_destroy (dialog);
922 return;
925 gtk_tree_model_get (multiattrib->store, &iter,
926 COLUMN_VALUE, &value,
927 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
928 -1);
930 newtext = g_strdup_printf ("%s=%s", new_name, value);
932 if (!x_dialog_validate_attribute (GTK_WINDOW (parent_window), newtext)) {
933 g_free (value);
934 g_free(newtext);
935 return;
938 for (a_iter = geda_list_get_glist (attr_list);
939 a_iter != NULL;
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);
951 g_free (value);
952 g_free (newtext);
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
964 * \brief
965 * \par Function Description
968 static void
969 multiattrib_callback_edited_value (GtkCellRendererText *cell_renderer,
970 gchar *arg1,
971 gchar *new_value,
972 gpointer user_data)
974 GschemMultiattribDockable *multiattrib =
975 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
976 GtkTreeIter iter;
977 GedaList *attr_list;
978 GList *a_iter;
979 OBJECT *o_attrib;
980 GschemToplevel *w_current;
981 char *name;
982 char *old_value;
983 GtkWidget *parent_window;
984 char *newtext;
985 int visibility;
987 w_current = multiattrib->parent.w_current;
989 if (!gtk_tree_model_get_iter_from_string (multiattrib->store, &iter, arg1)) {
990 return;
993 gtk_tree_model_get (multiattrib->store, &iter,
994 COLUMN_NAME, &name,
995 COLUMN_VALUE, &old_value,
996 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
997 -1);
999 /* If the edit didn't change anything, don't adjust any attributes */
1000 if (strcmp (old_value, new_value) == 0)
1001 return;
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;
1007 break;
1008 default:
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)) {
1015 g_free (name);
1016 g_free(newtext);
1017 return;
1020 for (a_iter = geda_list_get_glist (attr_list);
1021 a_iter != NULL;
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);
1034 g_free (name);
1035 g_free (newtext);
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,
1044 -1);
1047 /*! \todo Finish function documentation
1048 * \brief
1049 * \par Function Description
1052 static void
1053 multiattrib_callback_toggled_visible (GtkCellRendererToggle *cell_renderer,
1054 gchar *path,
1055 gpointer user_data)
1057 GschemMultiattribDockable *multiattrib =
1058 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
1059 GtkTreeIter iter;
1060 OBJECT *o_attrib;
1061 GschemToplevel *w_current;
1062 gboolean new_visibility;
1063 GedaList *attr_list;
1064 GList *a_iter;
1066 w_current = multiattrib->parent.w_current;
1068 if (!gtk_tree_model_get_iter_from_string (multiattrib->store, &iter, path)) {
1069 return;
1072 gtk_tree_model_get (multiattrib->store, &iter,
1073 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
1074 -1);
1076 new_visibility = !gtk_cell_renderer_toggle_get_active (cell_renderer);
1078 for (a_iter = geda_list_get_glist (attr_list);
1079 a_iter != NULL;
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,
1098 -1);
1101 /*! \todo Finish function documentation
1102 * \brief
1103 * \par Function Description
1106 static void
1107 multiattrib_callback_toggled_show_name (GtkCellRendererToggle *cell_renderer,
1108 gchar *path,
1109 gpointer user_data)
1111 GschemMultiattribDockable *multiattrib =
1112 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
1113 GtkTreeIter iter;
1114 GschemToplevel *w_current;
1115 gboolean new_name_visible;
1116 GedaList *attr_list;
1117 GList *a_iter;
1118 gint new_snv;
1120 w_current = multiattrib->parent.w_current;
1122 if (!gtk_tree_model_get_iter_from_string (multiattrib->store, &iter, path)) {
1123 return;
1126 gtk_tree_model_get (multiattrib->store, &iter,
1127 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
1128 -1);
1130 new_name_visible = !gtk_cell_renderer_toggle_get_active (cell_renderer);
1132 for (a_iter = geda_list_get_glist (attr_list);
1133 a_iter != NULL;
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;
1142 else
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
1165 * \brief
1166 * \par Function Description
1169 static void
1170 multiattrib_callback_toggled_show_value (GtkCellRendererToggle *cell_renderer,
1171 gchar *path,
1172 gpointer user_data)
1174 GschemMultiattribDockable *multiattrib =
1175 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
1176 GtkTreeIter iter;
1177 GschemToplevel *w_current;
1178 gboolean new_value_visible;
1179 GedaList *attr_list;
1180 GList *a_iter;
1181 gint new_snv;
1183 w_current = multiattrib->parent.w_current;
1185 if (!gtk_tree_model_get_iter_from_string (multiattrib->store, &iter, path)) {
1186 return;
1189 gtk_tree_model_get (multiattrib->store, &iter,
1190 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
1191 -1);
1193 new_value_visible = !gtk_cell_renderer_toggle_get_active (cell_renderer);
1195 for (a_iter = geda_list_get_glist (attr_list);
1196 a_iter != NULL;
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;
1205 else
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
1228 * \brief
1229 * \par Function Description
1232 static gboolean
1233 multiattrib_callback_key_pressed (GtkWidget *widget,
1234 GdkEventKey *event,
1235 gpointer user_data)
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;
1243 GtkTreeIter iter;
1244 GedaList *attr_list;
1245 int inherited;
1246 /* delete the currently selected attribute */
1248 if (!gtk_tree_selection_get_selected (
1249 gtk_tree_view_get_selection (multiattrib->treeview),
1250 &model, &iter)) {
1251 /* nothing selected, nothing to do */
1252 return FALSE;
1255 gtk_tree_model_get (model, &iter,
1256 COLUMN_INHERITED, &inherited,
1257 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
1258 -1);
1260 /* We can't delete inherited attribtes */
1261 if (inherited)
1262 return FALSE;
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);
1273 return FALSE;
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
1285 static gboolean
1286 find_row (GtkTreeModel *tree_model, GtkTreeIter *iter_return,
1287 gchar *name, gboolean inherited)
1289 GtkTreeIter iter;
1290 gboolean valid;
1292 for (valid = gtk_tree_model_get_iter_first (tree_model, &iter);
1293 valid;
1294 valid = gtk_tree_model_iter_next (tree_model, &iter)) {
1295 gchar *iter_name;
1296 gboolean iter_inherited;
1297 gboolean matches;
1298 gtk_tree_model_get (tree_model, &iter,
1299 COLUMN_NAME, &iter_name,
1300 COLUMN_INHERITED, &iter_inherited,
1301 -1);
1302 matches = iter_inherited == inherited && strcmp (iter_name, name) == 0;
1303 g_free (iter_name);
1305 if (matches) {
1306 *iter_return = iter;
1307 return TRUE;
1311 return FALSE;
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.
1331 static void
1332 multiattrib_edit_cell_at_pos (GschemMultiattribDockable *multiattrib,
1333 gint x, gint y)
1335 GtkTreePath *path;
1336 GtkTreeViewColumn *column;
1337 GtkTreeIter iter;
1338 gboolean inherited;
1339 gchar *name;
1340 GedaList *attr_list;
1342 if (!gtk_tree_view_get_path_at_pos (multiattrib->treeview,
1343 x, y, &path, &column, NULL, NULL))
1344 return;
1345 if (!gtk_tree_model_get_iter (multiattrib->store, &iter, path)) {
1346 gtk_tree_path_free (path);
1347 return;
1350 gtk_tree_model_get (multiattrib->store, &iter,
1351 COLUMN_INHERITED, &inherited,
1352 -1);
1353 if (!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);
1358 return;
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)
1364 return;
1366 /* see if there's already a matching attached attribute */
1367 gtk_tree_model_get (multiattrib->store, &iter,
1368 COLUMN_NAME, &name,
1369 -1);
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,
1374 -1);
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)) {
1382 g_free (name);
1383 return;
1386 g_free (name);
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
1397 * \brief
1398 * \par Function Description
1401 static gboolean
1402 multiattrib_callback_button_pressed (GtkWidget *widget,
1403 GdkEventButton *event,
1404 gpointer user_data)
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);
1413 ret = TRUE;
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) */
1421 else
1422 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
1423 multiattrib_edit_cell_at_pos (multiattrib, event->x, event->y);
1424 ret = TRUE;
1427 return ret;
1431 /*! \todo Finish function documentation
1432 * \brief
1433 * \par Function Description
1436 static gboolean
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;
1448 if (keyboard_mode)
1449 gtk_tree_view_get_cursor (tree_view, &path, &column);
1450 else {
1451 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y,
1452 &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))
1457 return FALSE;
1460 if (path == 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?"));
1469 else
1470 return FALSE;
1472 } else {
1473 /* show tooltip for cell */
1475 GtkTreeModel *model;
1476 GtkTreeIter iter;
1477 gchar *value;
1479 if (column != multiattrib->column_value)
1480 return FALSE;
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);
1489 g_free (value);
1490 gtk_tree_path_free (path);
1494 return TRUE;
1498 /*! \todo Finish function documentation
1499 * \brief
1500 * \par Function Description
1503 static gboolean
1504 multiattrib_callback_popup_menu (GtkWidget *widget,
1505 gpointer user_data)
1507 GschemMultiattribDockable *multiattrib =
1508 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
1510 multiattrib_popup_menu (multiattrib, NULL);
1512 return TRUE;
1515 /*! \todo Finish function documentation
1516 * \brief
1517 * \par Function Description
1520 static void
1521 multiattrib_callback_popup_duplicate (GtkMenuItem *menuitem,
1522 gpointer user_data)
1524 GschemMultiattribDockable *multiattrib =
1525 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
1526 GtkTreeModel *model;
1527 GtkTreeIter iter;
1528 GedaList *attr_list;
1530 if (!gtk_tree_selection_get_selected (
1531 gtk_tree_view_get_selection (multiattrib->treeview),
1532 &model, &iter)) {
1533 /* nothing selected, nothing to do */
1534 return;
1537 gtk_tree_model_get (model, &iter,
1538 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
1539 -1);
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
1548 * \brief
1549 * \par Function Description
1552 static void
1553 multiattrib_callback_popup_promote (GtkMenuItem *menuitem,
1554 gpointer user_data)
1556 GschemMultiattribDockable *multiattrib =
1557 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
1558 GtkTreeModel *model;
1559 GtkTreeIter iter;
1560 GedaList *attr_list;
1562 if (!gtk_tree_selection_get_selected (
1563 gtk_tree_view_get_selection (multiattrib->treeview),
1564 &model, &iter)) {
1565 /* nothing selected, nothing to do */
1566 return;
1569 gtk_tree_model_get (model, &iter,
1570 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
1571 -1);
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
1580 * \brief
1581 * \par Function Description
1584 static void
1585 multiattrib_callback_popup_delete (GtkMenuItem *menuitem,
1586 gpointer user_data)
1588 GschemMultiattribDockable *multiattrib =
1589 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
1590 GtkTreeModel *model;
1591 GtkTreeIter iter;
1592 GedaList *attr_list;
1594 if (!gtk_tree_selection_get_selected (
1595 gtk_tree_view_get_selection (multiattrib->treeview),
1596 &model, &iter)) {
1597 /* nothing selected, nothing to do */
1598 return;
1601 gtk_tree_model_get (model, &iter,
1602 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
1603 -1);
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
1612 * \brief
1613 * \par Function Description
1616 static void
1617 multiattrib_callback_popup_copy_to_all (GtkMenuItem *menuitem,
1618 gpointer user_data)
1620 GschemMultiattribDockable *multiattrib =
1621 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
1622 GtkTreeModel *model;
1623 GtkTreeIter iter;
1624 GedaList *attr_list;
1626 if (!gtk_tree_selection_get_selected (
1627 gtk_tree_view_get_selection (multiattrib->treeview),
1628 &model, &iter)) {
1629 /* nothing selected, nothing to do */
1630 return;
1633 gtk_tree_model_get (model, &iter,
1634 COLUMN_ATTRIBUTE_GEDALIST, &attr_list,
1635 -1);
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
1644 * \brief
1645 * \par Function Description
1648 static gboolean
1649 multiattrib_callback_value_key_pressed (GtkWidget *widget,
1650 GdkEventKey *event,
1651 gpointer user_data)
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;
1667 retval = FALSE;
1668 } else {
1669 /* change focus and stop propagation */
1670 g_signal_emit_by_name (GSCHEM_DOCKABLE (multiattrib)->widget,
1671 "move_focus",
1672 (event->state & GDK_SHIFT_MASK) ?
1673 GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
1674 retval = TRUE;
1678 return retval;
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
1687 static void
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
1702 * \brief
1703 * \par Function Description
1706 static void
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;
1713 const gchar *name;
1714 gchar *value;
1715 gboolean visible;
1716 gint shownv;
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 */
1735 g_free (value);
1736 return;
1739 multiattrib_action_add_attribute (multiattrib,
1740 name, value,
1741 visible, shownv);
1742 g_free (value);
1744 multiattrib_update (multiattrib);
1747 /*! \todo Finish function documentation
1748 * \brief
1749 * \par Function Description
1752 static void
1753 multiattrib_init_attrib_names (GtkCombo *combo)
1755 GList *items = NULL;
1756 const gchar *string;
1757 gint i;
1759 for (i = 0, string = s_attrib_get (i);
1760 string != NULL;
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
1771 * \brief
1772 * \par Function Description
1775 static void
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.
1801 static void
1802 multiattrib_popup_menu (GschemMultiattribDockable *multiattrib,
1803 GdkEventButton *event)
1805 GtkTreePath *path;
1806 GtkWidget *menu;
1807 struct menuitem_t {
1808 gchar *label;
1809 GCallback callback;
1812 struct menuitem_t menuitems_inherited[] = {
1813 { N_("Promote"), G_CALLBACK (multiattrib_callback_popup_promote) },
1814 { NULL, NULL } };
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)},
1820 { NULL, NULL } };
1822 struct menuitem_t *item_list;
1823 struct menuitem_t *tmp;
1824 int inherited;
1825 GtkTreeModel *model;
1826 GtkTreeIter iter;
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,
1833 (gint)event->x,
1834 (gint)event->y,
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))
1843 return;
1845 gtk_tree_model_get (model, &iter,
1846 COLUMN_INHERITED, &inherited,
1847 -1);
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 ();
1857 } else {
1858 menuitem = gtk_menu_item_new_with_label (_(tmp->label));
1859 g_signal_connect (menuitem,
1860 "activate",
1861 tmp->callback,
1862 multiattrib);
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.
1883 static void
1884 multiattrib_save_internal_geometry (GschemDockable *dockable,
1885 EdaConfig *cfg,
1886 gchar *group_name)
1888 gboolean show_inherited;
1890 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.
1905 static void
1906 multiattrib_restore_internal_geometry (GschemDockable *dockable,
1907 EdaConfig *cfg,
1908 gchar *group_name)
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.
1932 GType
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.
1966 static void
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
1986 static void
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.
2006 static void
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,
2014 multiattrib);
2015 multiattrib->object_list_changed_id =
2016 g_signal_connect (G_OBJECT (multiattrib->object_list),
2017 "changed",
2018 G_CALLBACK (object_list_changed_cb),
2019 multiattrib);
2020 /* Synthesise a object_list changed update to refresh the view */
2021 object_list_changed_cb (multiattrib->object_list, multiattrib);
2022 } else {
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.
2038 static void
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,
2046 multiattrib);
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.
2061 static void
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
2081 static void
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
2111 static void
2112 multiattrib_show_inherited_toggled (GtkToggleButton *button,
2113 gpointer user_data)
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
2127 static gint
2128 lookup_attrib_index (const gchar *name)
2130 const char *ref;
2131 if (name == NULL)
2132 return -1;
2133 for (int i = 0; (ref = s_attrib_get (i)) != NULL; i++)
2134 if (strcmp (name, ref) == 0)
2135 return i;
2136 return -1;
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
2143 * attributes.
2145 * \returns \c -1 , \c 0 or \c 1 depending on whether \a iter0 sorts
2146 * before, with or after \a iter1
2148 static gint
2149 attribute_sort_func (GtkTreeModel *model,
2150 GtkTreeIter *iter0, GtkTreeIter *iter1,
2151 gpointer user_data)
2153 GschemMultiattribDockable *multiattrib =
2154 GSCHEM_MULTIATTRIB_DOCKABLE (user_data);
2155 gboolean inherited0, inherited1;
2156 gchar *name0, *name1;
2157 gint result;
2159 gtk_tree_model_get (multiattrib->store, iter0,
2160 COLUMN_INHERITED, &inherited0,
2161 COLUMN_NAME, &name0,
2162 -1);
2163 gtk_tree_model_get (multiattrib->store, iter1,
2164 COLUMN_INHERITED, &inherited1,
2165 COLUMN_NAME, &name1,
2166 -1);
2168 if (inherited1 && !inherited0)
2169 result = -1;
2170 else if (inherited0 && !inherited1)
2171 result = 1;
2172 else if (name0 == NULL) {
2173 if (name1 == NULL)
2174 result = 0;
2175 else
2176 result = -1;
2177 } else if (name1 == NULL)
2178 result = 1;
2179 else {
2180 gint index0 = lookup_attrib_index (name0),
2181 index1 = lookup_attrib_index (name1);
2183 if (index0 != -1 && (index1 == -1 || index1 > index0))
2184 result = -1;
2185 else if (index1 != -1 && (index0 == -1 || index0 > index1))
2186 result = 1;
2187 else
2188 result = strcmp (name0, name1);
2191 g_free (name0);
2192 g_free (name1);
2193 return result;
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
2205 static GtkWidget *
2206 multiattrib_create_widget (GschemDockable *dockable)
2208 GschemMultiattribDockable *multiattrib =
2209 GSCHEM_MULTIATTRIB_DOCKABLE (dockable);
2210 GtkWidget *vbox;
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;
2217 GtkStyle *style;
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,
2224 /* GtkFrame */
2225 "shadow", GTK_SHADOW_NONE,
2226 NULL));
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,
2251 /* GtkContainer */
2252 "border-width", 0,
2253 /* GtkScrolledWindow */
2254 "hscrollbar-policy",
2255 GTK_POLICY_AUTOMATIC,
2256 "vscrollbar-policy",
2257 GTK_POLICY_AUTOMATIC,
2258 "shadow-type",
2259 GTK_SHADOW_ETCHED_IN,
2260 NULL));
2261 /* - create the treeview */
2262 treeview = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
2263 /* GtkTreeView */
2264 "model", multiattrib->store,
2265 "rules-hint", TRUE,
2266 NULL));
2267 g_signal_connect (treeview,
2268 "key-press-event",
2269 G_CALLBACK (multiattrib_callback_key_pressed),
2270 multiattrib);
2271 g_signal_connect (treeview,
2272 "button-press-event",
2273 G_CALLBACK (multiattrib_callback_button_pressed),
2274 multiattrib);
2275 g_signal_connect (treeview,
2276 "popup-menu",
2277 G_CALLBACK (multiattrib_callback_popup_menu),
2278 multiattrib);
2279 g_signal_connect (treeview,
2280 "query-tooltip",
2281 G_CALLBACK (multiattrib_callback_query_tooltip),
2282 multiattrib);
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,
2292 NULL));
2293 g_signal_connect (renderer,
2294 "toggled",
2295 G_CALLBACK (multiattrib_callback_toggled_visible),
2296 multiattrib);
2297 multiattrib->column_visible =
2298 column = GTK_TREE_VIEW_COLUMN (
2299 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
2300 /* GtkTreeViewColumn */
2301 "title", _("Vis"),
2302 NULL));
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,
2306 multiattrib, NULL);
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,
2311 NULL));
2312 g_signal_connect (renderer,
2313 "toggled",
2314 G_CALLBACK (multiattrib_callback_toggled_show_name),
2315 multiattrib);
2316 multiattrib->column_show_name =
2317 column = GTK_TREE_VIEW_COLUMN (
2318 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
2319 /* GtkTreeViewColumn */
2320 "title", _("N"),
2321 NULL));
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,
2325 NULL, NULL);
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,
2330 NULL));
2331 g_signal_connect (renderer,
2332 "toggled",
2333 G_CALLBACK (multiattrib_callback_toggled_show_value),
2334 multiattrib);
2335 multiattrib->column_show_value =
2336 column = GTK_TREE_VIEW_COLUMN (
2337 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
2338 /* GtkTreeViewColumn */
2339 "title", _("V"),
2340 NULL));
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,
2344 NULL, NULL);
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 */
2351 /* "ellipsize",
2352 * PANGO_ELLIPSIZE_END, */
2353 NULL));
2354 g_signal_connect (renderer,
2355 "edited",
2356 G_CALLBACK (multiattrib_callback_edited_name),
2357 multiattrib);
2358 multiattrib->column_name =
2359 column = GTK_TREE_VIEW_COLUMN (
2360 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
2361 /* GtkTreeViewColumn */
2362 "title", _("Name"),
2363 "min-width", 100,
2364 "resizable", TRUE,
2365 NULL));
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,
2369 multiattrib, NULL);
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 */
2376 /* "ellipsize",
2377 PANGO_ELLIPSIZE_END, */
2378 NULL));
2379 g_signal_connect (renderer,
2380 "edited",
2381 G_CALLBACK (multiattrib_callback_edited_value),
2382 multiattrib);
2383 multiattrib->column_value =
2384 column = GTK_TREE_VIEW_COLUMN (
2385 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
2386 /* GtkTreeViewColumn */
2387 "title", _("Value"),
2388 "min-width", 140,
2389 "resizable", TRUE,
2390 NULL));
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,
2394 multiattrib, NULL);
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,
2428 "toggled",
2429 G_CALLBACK (multiattrib_show_inherited_toggled),
2430 multiattrib);
2432 /* pack the frame */
2433 gtk_box_pack_start (GTK_BOX (vbox),
2434 multiattrib->list_frame,
2435 TRUE, TRUE, 0);
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"),
2441 NULL));
2442 table = GTK_WIDGET (g_object_new (GTK_TYPE_TABLE,
2443 /* GtkTable */
2444 "n-rows", 4,
2445 "n-columns", 2,
2446 "homogeneous", FALSE,
2447 NULL));
2449 /* - the name entry: a GtkComboBoxEntry */
2450 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL,
2451 /* GtkMisc */
2452 "xalign", 0.0,
2453 "yalign", 0.5,
2454 /* GtkLabel */
2455 "label", _("Name:"),
2456 NULL));
2457 combo = GTK_WIDGET (g_object_new (GTK_TYPE_COMBO,
2458 /* GtkCombo */
2459 "value-in-list", FALSE,
2460 NULL));
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,
2472 /* GtkMisc */
2473 "xalign", 0.0,
2474 "yalign", 0.5,
2475 /* GtkLabel */
2476 "label", _("Value:"),
2477 NULL));
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,
2485 "shadow-type",
2486 GTK_SHADOW_IN,
2487 NULL));
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,
2494 "key_press_event",
2495 G_CALLBACK (multiattrib_callback_value_key_pressed),
2496 multiattrib);
2497 g_signal_connect (textview,
2498 "grab-focus",
2499 G_CALLBACK (multiattrib_callback_value_grab_focus),
2500 multiattrib);
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,
2524 /* GtkButton */
2525 "label", _("Visible"),
2526 "active", TRUE,
2527 NULL));
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,
2534 NULL));
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,
2546 "clicked",
2547 G_CALLBACK (multiattrib_callback_button_add),
2548 multiattrib);
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,
2557 FALSE, TRUE, 0);
2558 gtk_widget_show_all (multiattrib->add_frame);
2560 gtk_widget_show (vbox);
2562 multiattrib_update (multiattrib);
2564 return vbox;
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
2580 static void
2581 multiattrib_set_property (GObject *object,
2582 guint property_id,
2583 const GValue *value,
2584 GParamSpec *pspec)
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));
2593 break;
2594 default:
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
2611 static void
2612 multiattrib_get_property (GObject *object,
2613 guint property_id,
2614 GValue *value,
2615 GParamSpec *pspec)
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);
2623 break;
2624 default:
2625 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2629 typedef struct {
2630 gboolean inherited;
2631 char *name;
2632 int nth_with_name;
2633 char *value;
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;
2644 } MODEL_ROW;
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
2652 * together.
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.
2658 static GList *
2659 object_attributes_to_model_rows (GschemMultiattribDockable *multiattrib,
2660 OBJECT *object)
2662 GList *model_rows = NULL;
2663 GList *a_iter;
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);
2671 GList *m_iter;
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;
2691 m_iter != NULL;
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);
2705 return model_rows;
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
2714 * object.
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.
2722 static GList *
2723 lone_attributes_to_model_rows (GschemMultiattribDockable *multiattrib)
2725 GList *o_iter;
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);
2730 o_iter != NULL;
2731 o_iter = g_list_next (o_iter)) {
2732 OBJECT *object = o_iter->data;
2733 MODEL_ROW *m_row;
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))
2738 continue;
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);
2764 return model_rows;
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.
2777 static void
2778 multiattrib_populate_liststore (GschemMultiattribDockable *multiattrib,
2779 GList *model_rows)
2781 GtkListStore *liststore;
2782 GtkTreeIter tree_iter;
2783 GList *m_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;
2790 m_iter != NULL;
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,
2801 &tree_iter,
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,
2813 -1);
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);
2823 static void
2824 append_dialog_title_extra (GString *title_string,
2825 int *num_title_extras,
2826 char *text,
2827 ...)
2829 va_list args;
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);
2835 va_end (args);
2838 static void
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);
2878 else
2879 gtk_label_set_text (GTK_LABEL (multiattrib->status_label),
2880 _("Select a component, net, or attribute"));
2881 g_free (title);
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.
2894 static void
2895 multiattrib_update (GschemMultiattribDockable *multiattrib)
2897 GList *o_iter;
2898 GtkStyle *style;
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 */
2907 return;
2909 show_inherited =
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);
2921 o_iter != NULL;
2922 o_iter = g_list_next (o_iter)) {
2923 OBJECT *object = o_iter->data;
2925 GList *object_rows;
2926 GList *or_iter;
2928 if (!is_multiattrib_object (object))
2929 continue;
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;
2957 or_iter != NULL;
2958 or_iter = g_list_next (or_iter)) {
2960 MODEL_ROW *object_row = or_iter->data;
2961 MODEL_ROW *model_row;
2962 GList *mr_iter;
2963 gboolean found = FALSE;
2965 /* Skip over inherited attributes if we don't want to show them */
2966 if (!show_inherited && object_row->inherited)
2967 continue;
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)
2977 found = TRUE;
2980 if (found) {
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);
3005 } else {
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;
3027 } else {
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),
3045 GTK_STATE_NORMAL,
3046 add_sensitive ? &multiattrib->value_normal_text_color
3047 : &style->text[GTK_STATE_INSENSITIVE]);