Updated copyright text/header in most source files.
[geda-gaf/peter-b.git] / gschem / src / x_multiattrib.c
blob7bf214063b640d3d540a203d2cb66aaf8fc12211
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 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., 59 Temple Place, Suite 330, Boston, MA 02111 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 <gdk/gdkkeysyms.h>
33 #ifdef HAVE_LIBDMALLOC
34 #include <dmalloc.h>
35 #endif
38 /*! \brief Update the multiattrib editor dialog when the page's
39 * selection changes.
40 * \par Function Description
41 * When the page's selection changes this function identifies how
42 * many objects which can have attributes are currently selected. If
43 * this number is 1, the dialog is set to edit the attributes of the
44 * first selected object..
46 * \param [in] selection The SELECTION object of page being edited.
47 * \param [in] user_data The multi-attribute editor dialog.
49 static void callback_selection_changed (SELECTION *selection,
50 gpointer user_data)
52 Multiattrib *multiattrib = MULTIATTRIB (user_data);
53 GList *iter;
54 OBJECT *object;
55 gint object_count = 0;
57 for (iter = geda_list_get_glist (selection);
58 iter != NULL;
59 iter = g_list_next (iter)) {
60 object = (OBJECT *)iter->data;
61 g_assert (object != NULL);
63 if (object->type == OBJ_COMPLEX ||
64 object->type == OBJ_PLACEHOLDER ||
65 object->type == OBJ_NET ||
66 object->type == OBJ_BUS ||
67 object->type == OBJ_PIN) {
68 object_count++;
72 if (object_count == 0) {
73 /* TODO: If the user selects a single attribute which is
74 * not floating, should we find its parent object and
75 * display the multi-attribute editor for that?
76 * Bonus marks for making it jump to the correct attrib.
78 object = NULL;
79 } else if (object_count == 1) {
80 object = (OBJECT *)((geda_list_get_glist (selection))->data);
81 } else {
82 /* TODO: Something clever with multiple objects selected */
83 object = NULL;
86 g_object_set (multiattrib,
87 "object", object,
88 NULL);
91 #define DIALOG_DATA_SELECTION "current-selection"
93 /*! \brief Update the dialog when the current page's SELECTION object
94 * is destroyed
95 * \par Function Description
96 * This handler is called when the g_object_weak_ref() on the
97 * SELECTION object we're watching expires. We reset our
98 * multiattrib->selection pointer to NULL to avoid attempting to
99 * access the destroyed object.
101 * \note
102 * Our signal handlers were automatically disconnected during the
103 * destruction process.
105 * \param [in] data Pointer to the multi-attrib dialog
106 * \param [in] where_the_object_was Pointer to where the object was
107 * just destroyed
109 static void callback_selection_finalized (gpointer data,
110 GObject *where_the_object_was)
112 Multiattrib *multiattrib = MULTIATTRIB (data);
113 g_object_set (multiattrib,
114 "object", NULL,
115 NULL);
116 g_object_set_data (G_OBJECT (multiattrib), DIALOG_DATA_SELECTION, NULL);
119 /*! \brief Add link between multiattrib dialog and current selection.
120 * \par Function Description
121 * This function connects a handler to the "changed" signal of
122 * current selection to let the dialog watch it. It also adds a weak
123 * reference on the selection.
125 * \param [in] multiattrib The Multiattrib dialog.
126 * \param [in] selection The selection to watch.
128 static void connect_selection (Multiattrib *multiattrib,
129 SELECTION *selection)
131 g_assert (g_object_get_data (G_OBJECT (multiattrib),
132 DIALOG_DATA_SELECTION) == NULL);
133 g_object_set_data (G_OBJECT (multiattrib), DIALOG_DATA_SELECTION, selection);
134 g_object_weak_ref (G_OBJECT (selection),
135 callback_selection_finalized,
136 multiattrib);
137 g_signal_connect (selection,
138 "changed",
139 (GCallback)callback_selection_changed,
140 multiattrib);
141 /* Synthesise a selection changed update to refresh the view */
142 callback_selection_changed (selection, multiattrib);
145 /*! \brief Remove the link between multiattrib dialog and selection.
146 * \par Function Description
147 * If the dialog is watching a selection, this function disconnects
148 * the "changed" signal and removes the weak reference it previously
149 * added on it.
151 * \param [in] multiattrib The Multiattrib dialog.
153 static void disconnect_selection (Multiattrib *multiattrib) {
154 SELECTION *selection;
156 /* get selection watched from dialog data */
157 selection = (SELECTION*)g_object_get_data (G_OBJECT (multiattrib),
158 DIALOG_DATA_SELECTION);
159 if (selection == NULL) {
160 /* no selection watched */
161 return;
164 g_signal_handlers_disconnect_matched (selection,
165 G_SIGNAL_MATCH_FUNC |
166 G_SIGNAL_MATCH_DATA,
167 0, 0, NULL,
168 callback_selection_changed,
169 multiattrib);
170 g_object_weak_unref (G_OBJECT (selection),
171 callback_selection_finalized,
172 multiattrib);
174 /* reset dialog data */
175 g_object_set_data (G_OBJECT (multiattrib), DIALOG_DATA_SELECTION, NULL);
178 /*! \brief Process the response returned by the multi-attribte dialog.
179 * \par Function Description
180 * This function handles the response <B>arg1</B> of the multi-attribute
181 * editor dialog <B>dialog</B>.
183 * \param [in] dialog The multi-attribute editor dialog.
184 * \param [in] arg1 The response ID.
185 * \param [in] user_data A pointer on the GSCHEM_TOPLEVEL environment.
187 static void
188 multiattrib_callback_response (GtkDialog *dialog,
189 gint arg1,
190 gpointer user_data)
192 GSCHEM_TOPLEVEL *w_current = (GSCHEM_TOPLEVEL*)user_data;
194 switch (arg1) {
195 case GTK_RESPONSE_CLOSE:
196 case GTK_RESPONSE_DELETE_EVENT:
197 /* cut link from dialog to selection */
198 disconnect_selection (MULTIATTRIB (w_current->mawindow));
199 gtk_widget_destroy (GTK_WIDGET (dialog));
200 w_current->mawindow = NULL;
201 break;
205 /*! \brief Open multiple attribute editor dialog.
206 * \par Function Description
207 * Opens the multiple attribute editor dialog for objects in this <B>toplevel</B>.
209 * \param [in] w_current The GSCHEM_TOPLEVEL object.
211 void x_multiattrib_open (GSCHEM_TOPLEVEL *w_current)
213 if ( w_current->mawindow == NULL ) {
214 w_current->mawindow =
215 GTK_WIDGET (g_object_new (TYPE_MULTIATTRIB,
216 "object", NULL,
217 /* GschemDialog */
218 "settings-name", "multiattrib",
219 "gschem-toplevel", w_current,
220 NULL));
222 gtk_window_set_transient_for (GTK_WINDOW(w_current->mawindow),
223 GTK_WINDOW(w_current->main_window));
225 g_signal_connect (w_current->mawindow,
226 "response",
227 G_CALLBACK (multiattrib_callback_response),
228 w_current);
230 /* attach dialog to selection of current page */
231 x_multiattrib_update (w_current);
233 gtk_widget_show (w_current->mawindow);
234 } else {
235 gtk_window_present (GTK_WINDOW(w_current->mawindow));
240 /*! \brief Close the multiattrib dialog.
242 * \par Function Description
244 * Closes the multiattrib dialog associated with <B>w_current</B>.
246 * \param [in] w_current The GSCHEM_TOPLEVEL object.
248 void x_multiattrib_close (GSCHEM_TOPLEVEL *w_current)
250 if (w_current->mawindow != NULL) {
251 /* cut link from dialog to selection */
252 disconnect_selection (MULTIATTRIB (w_current->mawindow));
253 gtk_widget_destroy (w_current->mawindow);
254 w_current->mawindow = NULL;
258 /*! \brief Update the multiattrib editor dialog for a GSCHEM_TOPLEVEL.
260 * \par Function Description
262 * If the GSCHEM_TOPLEVEL has an open multiattrib dialog, switch to
263 * watching the current page's SELECTION object for changes.
265 * \param [in] w_current The GSCHEM_TOPLEVEL object.
267 void x_multiattrib_update( GSCHEM_TOPLEVEL *w_current )
269 if (!IS_MULTIATTRIB (w_current->mawindow)) {
270 return;
273 /* disconnect dialog from previous selection */
274 disconnect_selection (MULTIATTRIB (w_current->mawindow));
275 /* connect the dialog to the selection of the current page */
276 connect_selection (MULTIATTRIB (w_current->mawindow),
277 w_current->toplevel->page_current->selection_list);
281 /*! \section celltextview-widget Cell TextView Widget Code.
282 * This widget makes a 'GtkTextView' widget implements the 'GtkCellEditable'
283 * interface. It can then be used to renderer multi-line texts inside
284 * tree views ('GtkTreeView').
286 static void celltextview_class_init (CellTextViewClass *klass);
287 static void celltextview_init (CellTextView *self);
288 static void celltextview_cell_editable_init (GtkCellEditableIface *iface);
290 /*! \todo Finish function documentation
291 * \brief
292 * \par Function Description
295 static gboolean celltextview_key_press_event (GtkWidget *widget,
296 GdkEventKey *key_event,
297 gpointer data)
299 CellTextView *celltextview = (CellTextView*)widget;
301 /* If the Escape key is pressed, we flag the edit as canceled */
302 if (key_event->keyval == GDK_Escape)
303 celltextview->editing_canceled = TRUE;
305 /* ends editing of cell if one of these keys are pressed or editing is canceled */
306 if (celltextview->editing_canceled == TRUE ||
307 /* the Enter key without the Control modifier */
308 (!(key_event->state & GDK_CONTROL_MASK) &&
309 (key_event->keyval == GDK_Return ||
310 key_event->keyval == GDK_KP_Enter))) {
311 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (celltextview));
312 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (celltextview));
313 return TRUE;
316 return FALSE;
319 /*! \todo Finish function documentation
320 * \brief
321 * \par Function Description
324 static void celltextview_start_editing (GtkCellEditable *cell_editable,
325 GdkEvent *event)
327 g_signal_connect (cell_editable,
328 "key_press_event",
329 G_CALLBACK (celltextview_key_press_event),
330 NULL);
334 /*! \todo Finish function documentation
335 * \brief
336 * \par Function Description
339 GType celltextview_get_type()
341 static GType celltextview_type = 0;
343 if (!celltextview_type) {
344 static const GTypeInfo celltextview_info = {
345 sizeof(CellTextViewClass),
346 NULL, /* base_init */
347 NULL, /* base_finalize */
348 (GClassInitFunc) celltextview_class_init,
349 NULL, /* class_finalize */
350 NULL, /* class_data */
351 sizeof(CellTextView),
352 0, /* n_preallocs */
353 (GInstanceInitFunc) celltextview_init,
356 static const GInterfaceInfo cell_editable_info = {
357 (GInterfaceInitFunc) celltextview_cell_editable_init,
358 NULL, /* interface_finalize */
359 NULL /* interface_data */
362 celltextview_type = g_type_register_static(GTK_TYPE_TEXT_VIEW,
363 "CellTextView",
364 &celltextview_info, 0);
365 g_type_add_interface_static(celltextview_type,
366 GTK_TYPE_CELL_EDITABLE,
367 &cell_editable_info);
370 return celltextview_type;
373 /*! \todo Finish function documentation
374 * \brief
375 * \par Function Description
378 static void celltextview_class_init(CellTextViewClass *klass)
380 /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */
383 /*! \todo Finish function documentation
384 * \brief
385 * \par Function Description
388 static void celltextview_init(CellTextView *celltextview)
390 celltextview->editing_canceled = FALSE;
393 /*! \todo Finish function documentation
394 * \brief
395 * \par Function Description
398 static void celltextview_cell_editable_init(GtkCellEditableIface *iface)
400 iface->start_editing = celltextview_start_editing;
403 /*! \section multi-line-text-cell-renderer Multi-line Text Cell Renderer
404 * GTK has no multi-line text cell renderer. This code adds one to be used
405 * in gschem code. It is inspired by the 'GtkCellRendererCombo' renderer
406 * of GTK 2.4 (LGPL).
408 static void cellrenderermultilinetext_class_init(CellRendererMultiLineTextClass *klass);
409 static void cellrenderermultilinetext_editing_done (GtkCellEditable *cell_editable,
410 gpointer user_data);
411 static gboolean cellrenderermultilinetext_focus_out_event (GtkWidget *widget,
412 GdkEvent *event,
413 gpointer user_data);
416 #define CELL_RENDERER_MULTI_LINE_TEXT_PATH "cell-renderer-multi-line-text-path"
419 /*! \todo Finish function documentation
420 * \brief
421 * \par Function Description
424 static GtkCellEditable* cellrenderermultilinetext_start_editing(GtkCellRenderer *cell,
425 GdkEvent *event,
426 GtkWidget *widget,
427 const gchar *path,
428 GdkRectangle *background_area,
429 GdkRectangle *cell_area,
430 GtkCellRendererState flags)
432 GtkCellRendererText *cell_text;
433 CellRendererMultiLineText *cell_mlt;
434 GtkWidget *textview;
435 GtkTextBuffer *textbuffer;
437 cell_text = GTK_CELL_RENDERER_TEXT (cell);
438 if (cell_text->editable == FALSE) {
439 return NULL;
442 cell_mlt = CELL_RENDERER_MULTI_LINE_TEXT (cell);
444 textbuffer = GTK_TEXT_BUFFER (g_object_new (GTK_TYPE_TEXT_BUFFER,
445 NULL));
446 gtk_text_buffer_set_text (textbuffer,
447 cell_text->text,
448 strlen (cell_text->text));
450 textview = GTK_WIDGET (g_object_new (TYPE_CELL_TEXT_VIEW,
451 /* GtkTextView */
452 "buffer", textbuffer,
453 "editable", TRUE,
454 /* GtkWidget */
455 "height-request", cell_area->height,
456 NULL));
457 g_object_set_data_full (G_OBJECT (textview),
458 CELL_RENDERER_MULTI_LINE_TEXT_PATH,
459 g_strdup (path), g_free);
461 gtk_widget_show (textview);
463 g_signal_connect (GTK_CELL_EDITABLE (textview),
464 "editing_done",
465 G_CALLBACK (cellrenderermultilinetext_editing_done),
466 cell_mlt);
467 cell_mlt->focus_out_id =
468 g_signal_connect (textview,
469 "focus_out_event",
470 G_CALLBACK (cellrenderermultilinetext_focus_out_event),
471 cell_mlt);
473 return GTK_CELL_EDITABLE (textview);
476 /*! \todo Finish function documentation
477 * \brief
478 * \par Function Description
481 static void cellrenderermultilinetext_editing_done(GtkCellEditable *cell_editable,
482 gpointer user_data)
484 CellRendererMultiLineText *cell = CELL_RENDERER_MULTI_LINE_TEXT (user_data);
485 GtkTextBuffer *buffer;
486 GtkTextIter start, end;
487 gchar *new_text;
488 const gchar *path;
490 if (cell->focus_out_id > 0) {
491 g_signal_handler_disconnect (cell_editable,
492 cell->focus_out_id);
493 cell->focus_out_id = 0;
496 if (CELL_TEXT_VIEW (cell_editable)->editing_canceled) {
497 g_signal_emit_by_name (cell, "editing-canceled");
498 return;
501 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (cell_editable));
502 gtk_text_buffer_get_start_iter (buffer, &start);
503 gtk_text_buffer_get_end_iter (buffer, &end);
504 new_text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
506 path = g_object_get_data (G_OBJECT (cell_editable),
507 CELL_RENDERER_MULTI_LINE_TEXT_PATH);
508 g_signal_emit_by_name (cell, "edited", path, new_text);
510 g_free (new_text);
514 /*! \todo Finish function documentation
515 * \brief
516 * \par Function Description
519 static gboolean cellrenderermultilinetext_focus_out_event(GtkWidget *widget,
520 GdkEvent *event,
521 gpointer user_data)
523 cellrenderermultilinetext_editing_done (GTK_CELL_EDITABLE (widget),
524 user_data);
526 return FALSE;
529 /*! \todo Finish function documentation
530 * \brief
531 * \par Function Description
534 GType cellrenderermultilinetext_get_type()
536 static GType cellrenderermultilinetext_type = 0;
538 if (!cellrenderermultilinetext_type) {
539 static const GTypeInfo cellrenderermultilinetext_info = {
540 sizeof(CellRendererMultiLineTextClass),
541 NULL, /* base_init */
542 NULL, /* base_finalize */
543 (GClassInitFunc) cellrenderermultilinetext_class_init,
544 NULL, /* class_finalize */
545 NULL, /* class_data */
546 sizeof(CellRendererMultiLineText),
547 0, /* n_preallocs */
548 NULL, /* instance_init */
551 cellrenderermultilinetext_type = g_type_register_static (
552 GTK_TYPE_CELL_RENDERER_TEXT,
553 "CellRendererMultiLineText",
554 &cellrenderermultilinetext_info, 0);
557 return cellrenderermultilinetext_type;
560 /*! \todo Finish function documentation
561 * \brief
562 * \par Function Description
565 static void cellrenderermultilinetext_class_init(CellRendererMultiLineTextClass *klass)
567 /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */
568 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
570 cell_class->start_editing = cellrenderermultilinetext_start_editing;
575 enum {
576 PROP_OBJECT = 1
579 enum {
580 COLUMN_ATTRIBUTE,
581 NUM_COLUMNS
584 static GObjectClass *multiattrib_parent_class = NULL;
586 static void multiattrib_class_init (MultiattribClass *class);
587 static void multiattrib_init (Multiattrib *multiattrib);
588 static void multiattrib_set_property (GObject *object,
589 guint property_id,
590 const GValue *value,
591 GParamSpec *pspec);
592 static void multiattrib_get_property (GObject *object,
593 guint property_id,
594 GValue *value,
595 GParamSpec *pspec);
597 static void multiattrib_popup_menu (Multiattrib *multiattrib,
598 GdkEventButton *event);
601 /*! \todo Finish function documentation
602 * \brief
603 * \par Function Description
606 static void multiattrib_action_add_attribute(GSCHEM_TOPLEVEL *w_current,
607 OBJECT *object,
608 Multiattrib *multiattrib,
609 const gchar *name,
610 const gchar *value,
611 gint visible,
612 gint show_name_value)
614 OBJECT *o_attrib;
615 gchar *newtext;
617 newtext = g_strdup_printf ("%s=%s", name, value);
619 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) {
620 g_free(newtext);
621 return;
624 /* create a new attribute and link it */
625 o_attrib = o_attrib_add_attrib (w_current, newtext,
626 visible, show_name_value, object);
628 w_current->toplevel->page_current->CHANGED = 1;
629 o_undo_savestate (w_current, UNDO_ALL);
631 g_free (newtext);
635 /*! \todo Finish function documentation
636 * \brief
637 * \par Function Description
640 static void multiattrib_action_duplicate_attribute(GSCHEM_TOPLEVEL *w_current,
641 OBJECT *object,
642 OBJECT *o_attrib)
644 OBJECT *o_new;
646 o_new = o_attrib_add_attrib (w_current,
647 o_text_get_string (w_current->toplevel, o_attrib),
648 o_attrib->visibility,
649 o_attrib->show_name_value,
650 object);
651 w_current->toplevel->page_current->CHANGED = 1;
652 o_undo_savestate (w_current, UNDO_ALL);
656 /*! \todo Finish function documentation
657 * \brief
658 * \par Function Description
661 static void multiattrib_action_promote_attribute (GSCHEM_TOPLEVEL *w_current,
662 OBJECT *object,
663 OBJECT *o_attrib)
665 TOPLEVEL *toplevel = w_current->toplevel;
666 OBJECT *o_new;
668 if (o_attrib->visibility) {
669 /* If the attribute we're promoting is visible, don't clone its location */
670 o_new = o_attrib_add_attrib (w_current,
671 o_text_get_string (w_current->toplevel, o_attrib),
672 o_attrib->visibility,
673 o_attrib->show_name_value,
674 object);
675 } else {
676 /* make a copy of the attribute object */
677 o_new = o_object_copy (toplevel, o_attrib, NORMAL_FLAG);
678 s_page_append (toplevel, toplevel->page_current, o_new);
679 /* add the attribute its parent */
680 o_attrib_attach (toplevel, o_new, object, TRUE);
681 /* redraw the attribute object */
682 o_invalidate (w_current, o_new);
683 /* note: this object is unselected (not added to selection). */
685 w_current->toplevel->page_current->CHANGED = 1;
686 o_undo_savestate (w_current, UNDO_ALL);
689 /*! \todo Finish function documentation
690 * \brief
691 * \par Function Description
694 static void multiattrib_action_delete_attribute(GSCHEM_TOPLEVEL *w_current,
695 OBJECT *o_attrib)
697 /* actually deletes the attribute */
698 o_selection_remove (w_current->toplevel,
699 w_current->toplevel->page_current->selection_list,
700 o_attrib);
701 o_delete (w_current, o_attrib);
702 w_current->toplevel->page_current->CHANGED=1;
703 o_undo_savestate (w_current, UNDO_ALL);
707 /*! \todo Finish function documentation
708 * \brief
709 * \par Function Description
712 static void multiattrib_column_set_data_name(GtkTreeViewColumn *tree_column,
713 GtkCellRenderer *cell,
714 GtkTreeModel *tree_model,
715 GtkTreeIter *iter,
716 gpointer data)
718 OBJECT *o_attrib;
719 gchar *name;
720 Multiattrib *dialog = (Multiattrib *) data;
721 int inherited;
723 gtk_tree_model_get (tree_model, iter,
724 COLUMN_ATTRIBUTE, &o_attrib,
725 -1);
726 g_assert (o_attrib->type == OBJ_TEXT);
728 inherited = o_attrib_is_inherited (o_attrib);
730 o_attrib_get_name_value (o_attrib, &name, NULL);
731 g_object_set (cell,
732 "text", name,
733 "foreground-gdk", inherited ? &dialog->insensitive_text_color : NULL,
734 "editable", !inherited,
735 NULL);
736 g_free (name);
740 /*! \todo Finish function documentation
741 * \brief
742 * \par Function Description
745 static void multiattrib_column_set_data_value(GtkTreeViewColumn *tree_column,
746 GtkCellRenderer *cell,
747 GtkTreeModel *tree_model,
748 GtkTreeIter *iter,
749 gpointer data)
751 OBJECT *o_attrib;
752 gchar *value;
753 Multiattrib *dialog = (Multiattrib *) data;
754 int inherited;
756 gtk_tree_model_get (tree_model, iter,
757 COLUMN_ATTRIBUTE, &o_attrib,
758 -1);
759 g_assert (o_attrib->type == OBJ_TEXT);
761 inherited = o_attrib_is_inherited (o_attrib);
763 o_attrib_get_name_value (o_attrib, NULL, &value);
764 g_object_set (cell,
765 "text", value,
766 "foreground-gdk", inherited ? &dialog->insensitive_text_color : NULL,
767 "editable", !inherited,
768 NULL);
769 g_free (value);
773 /*! \todo Finish function documentation
774 * \brief
775 * \par Function Description
778 static void multiattrib_column_set_data_visible(GtkTreeViewColumn *tree_column,
779 GtkCellRenderer *cell,
780 GtkTreeModel *tree_model,
781 GtkTreeIter *iter,
782 gpointer data)
784 OBJECT *o_attrib;
785 int inherited;
787 gtk_tree_model_get (tree_model, iter,
788 COLUMN_ATTRIBUTE, &o_attrib,
789 -1);
790 g_assert (o_attrib->type == OBJ_TEXT);
792 inherited = o_attrib_is_inherited (o_attrib);
794 g_object_set (cell,
795 "active", (o_attrib->visibility == VISIBLE),
796 "sensitive", !inherited,
797 "activatable", !inherited,
798 NULL);
802 /*! \todo Finish function documentation
803 * \brief
804 * \par Function Description
807 static void multiattrib_column_set_data_show_name(GtkTreeViewColumn *tree_column,
808 GtkCellRenderer *cell,
809 GtkTreeModel *tree_model,
810 GtkTreeIter *iter,
811 gpointer data)
813 OBJECT *o_attrib;
814 int inherited;
816 gtk_tree_model_get (tree_model, iter,
817 COLUMN_ATTRIBUTE, &o_attrib,
818 -1);
819 g_assert (o_attrib->type == OBJ_TEXT);
821 inherited = o_attrib_is_inherited (o_attrib);
823 g_object_set (cell,
824 "active", (o_attrib->show_name_value == SHOW_NAME_VALUE ||
825 o_attrib->show_name_value == SHOW_NAME),
826 "sensitive", !inherited,
827 "activatable", !inherited,
828 NULL);
832 /*! \todo Finish function documentation
833 * \brief
834 * \par Function Description
837 static void multiattrib_column_set_data_show_value(GtkTreeViewColumn *tree_column,
838 GtkCellRenderer *cell,
839 GtkTreeModel *tree_model,
840 GtkTreeIter *iter,
841 gpointer data)
843 OBJECT *o_attrib;
844 int inherited;
846 gtk_tree_model_get (tree_model, iter,
847 COLUMN_ATTRIBUTE, &o_attrib,
848 -1);
849 g_assert (o_attrib->type == OBJ_TEXT);
851 inherited = o_attrib_is_inherited (o_attrib);
853 g_object_set (cell,
854 "active", (o_attrib->show_name_value == SHOW_NAME_VALUE ||
855 o_attrib->show_name_value == SHOW_VALUE),
856 "sensitive", !inherited,
857 "activatable", !inherited,
858 NULL);
862 /*! \brief Requests an update of the display of a row.
863 * \par Function Description
864 * This is an helper function to update the display of a row when
865 * data for this row have been modified in the model.
867 * It emits the 'row_changed' signal on the pointed row.
869 * \param [in] model A GtkTreeModel.
870 * \param [in] iter A valid GtkTreeIter pointing to the changed row.
872 static void
873 update_row_display (GtkTreeModel *model, GtkTreeIter *iter)
875 GtkTreePath *path;
877 path = gtk_tree_model_get_path (model, iter);
878 gtk_tree_model_row_changed (model, path, iter);
879 gtk_tree_path_free (path);
883 /*! \todo Finish function documentation
884 * \brief
885 * \par Function Description
888 static void multiattrib_callback_edited_name(GtkCellRendererText *cellrenderertext,
889 gchar *arg1,
890 gchar *arg2,
891 gpointer user_data)
893 Multiattrib *multiattrib = (Multiattrib*)user_data;
894 GtkTreeModel *model;
895 GtkTreeIter iter;
896 OBJECT *o_attrib;
897 GSCHEM_TOPLEVEL *w_current;
898 gchar *value, *newtext;
900 model = gtk_tree_view_get_model (multiattrib->treeview);
901 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
903 if (!gtk_tree_model_get_iter_from_string (model, &iter, arg1)) {
904 return;
907 if (g_ascii_strcasecmp (arg2, "") == 0) {
908 GtkWidget *dialog = gtk_message_dialog_new (
909 GTK_WINDOW (multiattrib),
910 GTK_DIALOG_MODAL,
911 GTK_MESSAGE_ERROR,
912 GTK_BUTTONS_OK,
913 _("Attributes with empty name are not allowed. Please set a name."));
915 gtk_dialog_run (GTK_DIALOG (dialog));
916 gtk_widget_destroy (dialog);
917 return;
920 gtk_tree_model_get (model, &iter,
921 COLUMN_ATTRIBUTE, &o_attrib,
922 -1);
923 g_assert (o_attrib->type == OBJ_TEXT);
925 o_attrib_get_name_value (o_attrib, NULL, &value);
926 newtext = g_strdup_printf ("%s=%s", arg2, value);
928 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) {
929 g_free (value);
930 g_free(newtext);
931 return;
935 /* actually modifies the attribute */
936 o_text_change (w_current, o_attrib,
937 newtext, o_attrib->visibility, o_attrib->show_name_value);
939 g_free (value);
940 g_free (newtext);
944 /*! \todo Finish function documentation
945 * \brief
946 * \par Function Description
949 static void multiattrib_callback_edited_value(GtkCellRendererText *cell_renderer,
950 gchar *arg1,
951 gchar *arg2,
952 gpointer user_data)
954 Multiattrib *multiattrib = (Multiattrib*)user_data;
955 GtkTreeModel *model;
956 GtkTreeIter iter;
957 OBJECT *o_attrib;
958 GSCHEM_TOPLEVEL *w_current;
959 gchar *name, *newtext;
961 model = gtk_tree_view_get_model (multiattrib->treeview);
962 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
964 if (!gtk_tree_model_get_iter_from_string (model, &iter, arg1)) {
965 return;
968 gtk_tree_model_get (model, &iter,
969 COLUMN_ATTRIBUTE, &o_attrib,
970 -1);
971 g_assert (o_attrib->type == OBJ_TEXT);
973 o_attrib_get_name_value (o_attrib, &name, NULL);
974 newtext = g_strdup_printf ("%s=%s", name, arg2);
976 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) {
977 g_free (name);
978 g_free(newtext);
979 return;
982 /* actually modifies the attribute */
983 o_text_change (w_current, o_attrib,
984 newtext, o_attrib->visibility, o_attrib->show_name_value);
986 /* request an update of display for this row */
987 update_row_display (model, &iter);
989 g_free (name);
990 g_free (newtext);
994 /*! \todo Finish function documentation
995 * \brief
996 * \par Function Description
999 static void multiattrib_callback_toggled_visible(GtkCellRendererToggle *cell_renderer,
1000 gchar *path,
1001 gpointer user_data)
1003 Multiattrib *multiattrib = (Multiattrib*)user_data;
1004 GtkTreeModel *model;
1005 GtkTreeIter iter;
1006 OBJECT *o_attrib;
1007 GSCHEM_TOPLEVEL *w_current;
1008 gint visibility;
1010 model = gtk_tree_view_get_model (multiattrib->treeview);
1011 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
1013 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) {
1014 return;
1017 gtk_tree_model_get (model, &iter,
1018 COLUMN_ATTRIBUTE, &o_attrib,
1019 -1);
1020 g_assert (o_attrib->type == OBJ_TEXT);
1021 o_invalidate (w_current, o_attrib);
1023 visibility = o_attrib->visibility == VISIBLE ? INVISIBLE : VISIBLE;
1025 /* actually modifies the attribute */
1026 o_attrib->visibility = visibility;
1027 o_text_recreate (w_current->toplevel, o_attrib);
1028 o_invalidate (w_current, o_attrib);
1029 o_undo_savestate (w_current, UNDO_ALL);
1031 /* request an update of display for this row */
1032 update_row_display (model, &iter);
1036 /*! \todo Finish function documentation
1037 * \brief
1038 * \par Function Description
1041 static void multiattrib_callback_toggled_show_name(GtkCellRendererToggle *cell_renderer,
1042 gchar *path,
1043 gpointer user_data)
1045 Multiattrib *multiattrib = (Multiattrib*)user_data;
1046 GtkTreeModel *model;
1047 GtkTreeIter iter;
1048 OBJECT *o_attrib;
1049 GSCHEM_TOPLEVEL *w_current;
1050 gint new_snv;
1052 model = gtk_tree_view_get_model (multiattrib->treeview);
1053 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
1055 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) {
1056 return;
1059 gtk_tree_model_get (model, &iter,
1060 COLUMN_ATTRIBUTE, &o_attrib,
1061 -1);
1062 g_assert (o_attrib->type == OBJ_TEXT);
1063 o_invalidate (w_current, o_attrib);
1065 switch (o_attrib->show_name_value) {
1066 case SHOW_NAME_VALUE: new_snv = SHOW_VALUE; break;
1067 case SHOW_NAME: new_snv = SHOW_VALUE; break;
1068 case SHOW_VALUE: new_snv = SHOW_NAME_VALUE; break;
1069 default:
1070 g_assert_not_reached ();
1071 new_snv = SHOW_NAME_VALUE;
1074 /* actually modifies the attribute */
1075 o_attrib->show_name_value = new_snv;
1076 o_text_recreate (w_current->toplevel, o_attrib);
1077 o_invalidate (w_current, o_attrib);
1078 o_undo_savestate (w_current, UNDO_ALL);
1080 /* request an update of display for this row */
1081 update_row_display (model, &iter);
1085 /*! \todo Finish function documentation
1086 * \brief
1087 * \par Function Description
1090 static void multiattrib_callback_toggled_show_value(GtkCellRendererToggle *cell_renderer,
1091 gchar *path,
1092 gpointer user_data)
1094 Multiattrib *multiattrib = (Multiattrib*)user_data;
1095 GtkTreeModel *model;
1096 GtkTreeIter iter;
1097 OBJECT *o_attrib;
1098 GSCHEM_TOPLEVEL *w_current;
1099 gint new_snv;
1101 model = gtk_tree_view_get_model (multiattrib->treeview);
1102 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
1104 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) {
1105 return;
1108 gtk_tree_model_get (model, &iter,
1109 COLUMN_ATTRIBUTE, &o_attrib,
1110 -1);
1111 g_assert (o_attrib->type == OBJ_TEXT);
1112 o_invalidate (w_current, o_attrib);
1114 switch (o_attrib->show_name_value) {
1115 case SHOW_NAME_VALUE: new_snv = SHOW_NAME; break;
1116 case SHOW_NAME: new_snv = SHOW_NAME_VALUE; break;
1117 case SHOW_VALUE: new_snv = SHOW_NAME; break;
1118 default:
1119 g_assert_not_reached ();
1120 new_snv = SHOW_NAME_VALUE;
1123 /* actually modifies the attribute */
1124 o_attrib->show_name_value = new_snv;
1125 o_text_recreate (w_current->toplevel, o_attrib);
1126 o_invalidate (w_current, o_attrib);
1127 o_undo_savestate (w_current, UNDO_ALL);
1129 /* request an update of display for this row */
1130 update_row_display (model, &iter);
1134 /*! \todo Finish function documentation
1135 * \brief
1136 * \par Function Description
1139 static gboolean multiattrib_callback_key_pressed(GtkWidget *widget,
1140 GdkEventKey *event,
1141 gpointer user_data)
1143 Multiattrib *multiattrib = (Multiattrib*)user_data;
1145 if (event->state == 0 &&
1146 (event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete)) {
1147 GtkTreeModel *model;
1148 GtkTreeIter iter;
1149 OBJECT *o_attrib;
1150 int inherited;
1151 /* delete the currently selected attribute */
1153 if (!gtk_tree_selection_get_selected (
1154 gtk_tree_view_get_selection (multiattrib->treeview),
1155 &model, &iter)) {
1156 /* nothing selected, nothing to do */
1157 return FALSE;
1160 gtk_tree_model_get (model, &iter,
1161 COLUMN_ATTRIBUTE, &o_attrib,
1162 -1);
1163 g_assert (o_attrib->type == OBJ_TEXT);
1165 inherited = o_attrib_is_inherited (o_attrib);
1166 /* We can't delete inherited attribtes */
1167 if (inherited)
1168 return FALSE;
1170 multiattrib_action_delete_attribute (GSCHEM_DIALOG (multiattrib)->w_current,
1171 o_attrib);
1173 /* update the treeview contents */
1174 multiattrib_update (multiattrib);
1177 return FALSE;
1180 /*! \todo Finish function documentation
1181 * \brief
1182 * \par Function Description
1185 static gboolean multiattrib_callback_button_pressed(GtkWidget *widget,
1186 GdkEventButton *event,
1187 gpointer user_data)
1189 Multiattrib *multiattrib = (Multiattrib*)user_data;
1190 gboolean ret = FALSE;
1192 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
1193 multiattrib_popup_menu (multiattrib, event);
1194 ret = TRUE;
1197 return ret;
1200 /*! \todo Finish function documentation
1201 * \brief
1202 * \par Function Description
1205 static gboolean multiattrib_callback_popup_menu(GtkWidget *widget,
1206 gpointer user_data)
1208 Multiattrib *multiattrib = (Multiattrib*)user_data;
1210 multiattrib_popup_menu (multiattrib, NULL);
1212 return TRUE;
1215 /*! \todo Finish function documentation
1216 * \brief
1217 * \par Function Description
1220 static void multiattrib_callback_popup_duplicate(GtkMenuItem *menuitem,
1221 gpointer user_data)
1223 Multiattrib *multiattrib = (Multiattrib*)user_data;
1224 GtkTreeModel *model;
1225 GtkTreeIter iter;
1226 GSCHEM_TOPLEVEL *w_current;
1227 OBJECT *object, *o_attrib;
1229 if (!gtk_tree_selection_get_selected (
1230 gtk_tree_view_get_selection (multiattrib->treeview),
1231 &model, &iter)) {
1232 /* nothing selected, nothing to do */
1233 return;
1236 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
1237 object = multiattrib->object;
1239 gtk_tree_model_get (model, &iter,
1240 COLUMN_ATTRIBUTE, &o_attrib,
1241 -1);
1242 g_assert (o_attrib->type == OBJ_TEXT);
1244 multiattrib_action_duplicate_attribute (w_current, object, o_attrib);
1246 /* update the treeview contents */
1247 multiattrib_update (multiattrib);
1252 /*! \todo Finish function documentation
1253 * \brief
1254 * \par Function Description
1257 static void multiattrib_callback_popup_promote (GtkMenuItem *menuitem,
1258 gpointer user_data)
1260 Multiattrib *multiattrib = user_data;
1261 GtkTreeModel *model;
1262 GtkTreeIter iter;
1263 GSCHEM_TOPLEVEL *w_current;
1264 OBJECT *object, *o_attrib;
1266 if (!gtk_tree_selection_get_selected (
1267 gtk_tree_view_get_selection (multiattrib->treeview),
1268 &model, &iter)) {
1269 /* nothing selected, nothing to do */
1270 return;
1273 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
1274 object = multiattrib->object;
1276 gtk_tree_model_get (model, &iter,
1277 COLUMN_ATTRIBUTE, &o_attrib,
1278 -1);
1279 g_assert (o_attrib->type == OBJ_TEXT);
1281 multiattrib_action_promote_attribute (w_current, object, o_attrib);
1283 /* update the treeview contents */
1284 multiattrib_update (multiattrib);
1287 /*! \todo Finish function documentation
1288 * \brief
1289 * \par Function Description
1292 static void multiattrib_callback_popup_delete(GtkMenuItem *menuitem,
1293 gpointer user_data)
1295 Multiattrib *multiattrib = (Multiattrib*)user_data;
1296 GtkTreeModel *model;
1297 GtkTreeIter iter;
1298 GSCHEM_TOPLEVEL *w_current;
1299 OBJECT *o_attrib;
1301 if (!gtk_tree_selection_get_selected (
1302 gtk_tree_view_get_selection (multiattrib->treeview),
1303 &model, &iter)) {
1304 /* nothing selected, nothing to do */
1305 return;
1308 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
1310 gtk_tree_model_get (model, &iter,
1311 COLUMN_ATTRIBUTE, &o_attrib,
1312 -1);
1313 g_assert (o_attrib->type == OBJ_TEXT);
1315 multiattrib_action_delete_attribute (w_current, o_attrib);
1317 /* update the treeview contents */
1318 multiattrib_update (multiattrib);
1322 /*! \todo Finish function documentation
1323 * \brief
1324 * \par Function Description
1327 static gboolean multiattrib_callback_value_key_pressed(GtkWidget *widget,
1328 GdkEventKey *event,
1329 gpointer user_data)
1331 Multiattrib *multiattrib = (Multiattrib*)widget;
1332 gboolean retval = FALSE;
1334 /* ends editing of cell if one of these keys are pressed: */
1335 /* - the Return key without the Control modifier */
1336 /* - the Tab key without the Control modifier */
1337 if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) ||
1338 (event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab)) {
1339 /* Control modifier activated? */
1340 if (event->state & GDK_CONTROL_MASK) {
1341 /* yes the modifier in event structure and let event propagate */
1342 event->state ^= GDK_CONTROL_MASK;
1343 retval = FALSE;
1344 } else {
1345 /* change focus and stop propagation */
1346 g_signal_emit_by_name (multiattrib,
1347 "move_focus",
1348 (event->state & GDK_SHIFT_MASK) ?
1349 GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
1350 retval = TRUE;
1354 return retval;
1358 /*! \brief GtkWidget "grab-focus" signal handler
1360 * \par Function Description
1361 * Select the text in the GtkTextView so it may be over-typed quickly
1363 static void multiattrib_callback_value_grab_focus (GtkWidget *widget,
1364 gpointer user_data)
1366 GtkTextView *textview = GTK_TEXT_VIEW (widget);
1367 GtkTextBuffer *textbuffer;
1368 GtkTextIter startiter, enditer;
1370 textbuffer = gtk_text_view_get_buffer (textview);
1371 gtk_text_buffer_get_iter_at_offset (textbuffer, &startiter, 0);
1372 gtk_text_buffer_get_iter_at_offset (textbuffer, &enditer, -1);
1373 gtk_text_buffer_select_range (textbuffer, &enditer, &startiter);
1377 /*! \todo Finish function documentation
1378 * \brief
1379 * \par Function Description
1382 static void multiattrib_callback_button_add(GtkButton *button,
1383 gpointer user_data)
1385 Multiattrib *multiattrib = (Multiattrib*)user_data;
1386 GtkTextBuffer *buffer;
1387 GtkTextIter start, end;
1388 const gchar *name;
1389 gchar *value;
1390 GSCHEM_TOPLEVEL *w_current;
1391 OBJECT *object;
1392 gboolean visible;
1393 gint shownv;
1395 w_current = GSCHEM_DIALOG (multiattrib)->w_current;
1396 object = multiattrib->object;
1397 buffer = gtk_text_view_get_buffer (multiattrib->textview_value);
1399 /* retrieve information from the Add/Edit frame */
1400 /* - attribute's name */
1401 name = gtk_entry_get_text (
1402 GTK_ENTRY (GTK_COMBO (multiattrib->combo_name)->entry));
1403 /* - attribute's value */
1404 gtk_text_buffer_get_bounds (buffer, &start, &end);
1405 value = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1406 /* - attribute's visibility status */
1407 visible = gtk_toggle_button_get_active (
1408 (GtkToggleButton*)multiattrib->button_visible);
1409 /* - visibility type */
1410 shownv = (gint)gtk_option_menu_get_history (multiattrib->optionmenu_shownv);
1412 if (name[0] == '\0' || name[0] == ' ') {
1413 /* name not allowed for an attribute */
1414 g_free (value);
1415 return;
1418 multiattrib_action_add_attribute (w_current, object, multiattrib,
1419 name, value,
1420 visible, shownv);
1421 g_free (value);
1423 multiattrib_update (multiattrib);
1426 /*! \todo Finish function documentation
1427 * \brief
1428 * \par Function Description
1431 static void multiattrib_init_attrib_names(GtkCombo *combo)
1433 GList *items = NULL;
1434 const gchar *string;
1435 gint i;
1437 for (i = 0, string = s_attrib_get (i);
1438 string != NULL;
1439 i++, string = s_attrib_get (i)) {
1440 items = g_list_append (items, (gpointer)string);
1443 gtk_combo_set_popdown_strings (GTK_COMBO (combo), items);
1445 g_list_free (items);
1449 /*! \todo Finish function documentation
1450 * \brief
1451 * \par Function Description
1454 static void multiattrib_init_visible_types(GtkOptionMenu *optionmenu)
1456 GtkWidget *menu, *item;
1458 menu = gtk_menu_new ();
1459 item = gtk_menu_item_new_with_label (_("Show Name & Value"));
1460 gtk_menu_append (menu, item);
1461 item = gtk_menu_item_new_with_label (_("Show Value only"));
1462 gtk_menu_append (menu, item);
1463 item = gtk_menu_item_new_with_label (_("Show Name only"));
1464 gtk_menu_append (menu, item);
1466 gtk_option_menu_set_menu (optionmenu, menu);
1471 /*! \brief Popup a context-sensitive menu.
1472 * \par Function Description
1473 * Pops up a context-sensitive menu.
1474 * <B>event</B> can be NULL if the popup is triggered by a key binding
1475 * instead of a mouse click.
1477 * \param [in] multiattrib The Multiattrib object.
1478 * \param [in] event Mouse event.
1480 static void multiattrib_popup_menu(Multiattrib *multiattrib,
1481 GdkEventButton *event)
1483 GtkTreePath *path;
1484 GtkWidget *menu;
1485 struct menuitem_t {
1486 gchar *label;
1487 GCallback callback;
1490 struct menuitem_t menuitems_inherited[] = {
1491 { N_("Promote"), G_CALLBACK (multiattrib_callback_popup_promote) },
1492 { NULL, NULL } };
1494 struct menuitem_t menuitems_noninherited[] = {
1495 { N_("Duplicate"), G_CALLBACK (multiattrib_callback_popup_duplicate) },
1496 { N_("Delete"), G_CALLBACK (multiattrib_callback_popup_delete) },
1497 { NULL, NULL } };
1499 struct menuitem_t *item_list;
1500 struct menuitem_t *tmp;
1501 int inherited;
1502 GtkTreeModel *model;
1503 GtkTreeIter iter;
1504 GtkTreeSelection *selection;
1505 OBJECT *o_attrib;
1507 selection = gtk_tree_view_get_selection (multiattrib->treeview);
1509 if (event != NULL &&
1510 gtk_tree_view_get_path_at_pos (multiattrib->treeview,
1511 (gint)event->x,
1512 (gint)event->y,
1513 &path, NULL, NULL, NULL)) {
1514 gtk_tree_selection_unselect_all (selection);
1515 gtk_tree_selection_select_path (selection, path);
1516 gtk_tree_path_free (path);
1519 /* if nothing is selected, nothing to do */
1520 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1521 return;
1523 gtk_tree_model_get (model, &iter,
1524 COLUMN_ATTRIBUTE, &o_attrib,
1525 -1);
1526 g_assert (o_attrib->type == OBJ_TEXT);
1528 inherited = o_attrib_is_inherited (o_attrib);
1529 item_list = inherited ? menuitems_inherited : menuitems_noninherited;
1531 /* create the context menu */
1532 menu = gtk_menu_new();
1533 for (tmp = item_list; tmp->label != NULL; tmp++) {
1534 GtkWidget *menuitem;
1535 if (g_strcasecmp (tmp->label, "-") == 0) {
1536 menuitem = gtk_separator_menu_item_new ();
1537 } else {
1538 menuitem = gtk_menu_item_new_with_label (_(tmp->label));
1539 g_signal_connect (menuitem,
1540 "activate",
1541 tmp->callback,
1542 multiattrib);
1544 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1546 gtk_widget_show_all (menu);
1547 /* make menu a popup menu */
1548 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
1549 (event != NULL) ? event->button : 0,
1550 gdk_event_get_time ((GdkEvent*)event));
1555 /*! \brief GschemDialog "geometry_save" class method handler
1557 * \par Function Description
1558 * Chain up to our parent's method to save the dialog's size and
1559 * position, then save the dialog's current internal geometry.
1561 * \param [in] dialog The GschemDialog to save the geometry of.
1562 * \param [in] key_file The GKeyFile to save the geometry data to.
1563 * \param [in] group_name The group name in the key file to store the data under.
1565 static void
1566 multiattrib_geometry_save (GschemDialog *dialog, GKeyFile *key_file, gchar *group_name)
1568 gboolean show_inherited;
1570 /* Call the parent's geometry_save method */
1571 GSCHEM_DIALOG_CLASS (multiattrib_parent_class)->
1572 geometry_save (dialog, key_file, group_name);
1574 show_inherited =
1575 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (MULTIATTRIB (dialog)->show_inherited));
1576 g_key_file_set_boolean (key_file, group_name, "show_inherited", show_inherited);
1580 /*! \brief GschemDialog "geometry_restore" class method handler
1582 * \par Function Description
1583 * Chain up to our parent's method to restore the dialog's size and
1584 * position, then restore the dialog's current internal geometry.
1586 * \param [in] dialog The GschemDialog to restore the geometry of.
1587 * \param [in] key_file The GKeyFile to save the geometry data to.
1588 * \param [in] group_name The group name in the key file to store the data under.
1590 static void
1591 multiattrib_geometry_restore (GschemDialog *dialog, GKeyFile *key_file, gchar *group_name)
1593 gboolean show_inherited;
1594 GError *error = NULL;
1596 /* Call the parent's geometry_restore method */
1597 GSCHEM_DIALOG_CLASS (multiattrib_parent_class)->
1598 geometry_restore (dialog, key_file, group_name);
1600 show_inherited = g_key_file_get_boolean (key_file, group_name, "show_inherited", &error);
1601 if (error != NULL) {
1602 show_inherited = TRUE;
1603 g_error_free (error);
1605 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (MULTIATTRIB (dialog)->show_inherited), show_inherited);
1609 /*! \brief Function to retrieve Multiattrib's GType identifier.
1611 * \par Function Description
1613 * Function to retrieve Multiattrib's GType identifier.
1614 * Upon first call, this registers Multiattrib in the GType system.
1615 * Subsequently it returns the saved value from its first execution.
1617 * \return the GType identifier associated with Multiattrib.
1619 GType multiattrib_get_type()
1621 static GType multiattrib_type = 0;
1623 if (!multiattrib_type) {
1624 static const GTypeInfo multiattrib_info = {
1625 sizeof(MultiattribClass),
1626 NULL, /* base_init */
1627 NULL, /* base_finalize */
1628 (GClassInitFunc) multiattrib_class_init,
1629 NULL, /* class_finalize */
1630 NULL, /* class_data */
1631 sizeof(Multiattrib),
1632 0, /* n_preallocs */
1633 (GInstanceInitFunc) multiattrib_init,
1636 multiattrib_type = g_type_register_static (GSCHEM_TYPE_DIALOG,
1637 "Multiattrib",
1638 &multiattrib_info, 0);
1641 return multiattrib_type;
1644 /*! \brief GType class initialiser for Multiattrib
1646 * \par Function Description
1648 * GType class initialiser for Multiattrib. We override our parent
1649 * virtual class methods as needed and register our GObject properties.
1651 * \param [in] klass The MultiattribClass we are initialising
1653 static void multiattrib_class_init(MultiattribClass *klass)
1655 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1656 GschemDialogClass *gschem_dialog_class = GSCHEM_DIALOG_CLASS (klass);
1658 gschem_dialog_class->geometry_save = multiattrib_geometry_save;
1659 gschem_dialog_class->geometry_restore = multiattrib_geometry_restore;
1661 gobject_class->set_property = multiattrib_set_property;
1662 gobject_class->get_property = multiattrib_get_property;
1664 multiattrib_parent_class = g_type_class_peek_parent (klass);
1666 g_object_class_install_property (
1667 gobject_class, PROP_OBJECT,
1668 g_param_spec_pointer ("object",
1671 G_PARAM_READWRITE));
1674 /*! \brief Regenerate the attribute list when the visibility
1675 * setting for inherited attributes changes
1677 static void multiattrib_show_inherited_toggled (GtkToggleButton *button,
1678 gpointer user_data)
1680 Multiattrib *multiattrib = user_data;
1682 /* update the treeview contents */
1683 multiattrib_update (multiattrib);
1687 /*! \brief GType instance initialiser for Multiattrib
1689 * \par Function Description
1691 * GType instance initialiser for Multiattrib. Create
1692 * and setup the widgets which make up the dialog.
1694 * \param [in] multiattrib The Multiattrib we are initialising
1696 static void multiattrib_init(Multiattrib *multiattrib)
1698 GtkWidget *frame, *label, *scrolled_win, *treeview;
1699 GtkWidget *table, *textview, *combo, *optionm, *button;
1700 GtkWidget *attrib_vbox, *show_inherited;
1701 GtkTreeModel *store;
1702 GtkCellRenderer *renderer;
1703 GtkTreeViewColumn *column;
1704 GtkTreeSelection *selection;
1705 GtkStyle *style;
1707 /* dialog initialization */
1708 g_object_set (G_OBJECT (multiattrib),
1709 /* GtkContainer */
1710 "border-width", 0,
1711 /* GtkWindow */
1712 "type", GTK_WINDOW_TOPLEVEL,
1713 "title", _("Edit Attributes"),
1714 "default-width", 320,
1715 "default-height", 350,
1716 "window-position", GTK_WIN_POS_MOUSE,
1717 "allow-grow", TRUE,
1718 "allow-shrink", FALSE,
1719 /* GtkDialog */
1720 "has-separator", TRUE,
1721 NULL);
1723 multiattrib->object = NULL;
1725 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (multiattrib)->vbox), 5);
1727 /* create the attribute list frame */
1728 frame = GTK_WIDGET (g_object_new (GTK_TYPE_FRAME,
1729 /* GtkFrame */
1730 "shadow", GTK_SHADOW_NONE,
1731 NULL));
1732 multiattrib->frame_add = frame;
1733 /* - create the model for the treeview */
1734 store = (GtkTreeModel*)gtk_list_store_new (NUM_COLUMNS,
1735 G_TYPE_POINTER); /* attribute */
1736 /* - create a scrolled window for the treeview */
1737 scrolled_win = GTK_WIDGET (
1738 g_object_new (GTK_TYPE_SCROLLED_WINDOW,
1739 /* GtkContainer */
1740 "border-width", 3,
1741 /* GtkScrolledWindow */
1742 "hscrollbar-policy",
1743 GTK_POLICY_AUTOMATIC,
1744 "vscrollbar-policy",
1745 GTK_POLICY_AUTOMATIC,
1746 "shadow-type",
1747 GTK_SHADOW_ETCHED_IN,
1748 NULL));
1749 /* - create the treeview */
1750 treeview = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
1751 /* GtkTreeView */
1752 "model", store,
1753 "rules-hint", TRUE,
1754 NULL));
1755 g_signal_connect (treeview,
1756 "key-press-event",
1757 G_CALLBACK (multiattrib_callback_key_pressed),
1758 multiattrib);
1759 g_signal_connect (treeview,
1760 "button-press-event",
1761 G_CALLBACK (multiattrib_callback_button_pressed),
1762 multiattrib);
1763 g_signal_connect (treeview,
1764 "popup-menu",
1765 G_CALLBACK (multiattrib_callback_popup_menu),
1766 multiattrib);
1767 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
1768 gtk_tree_selection_set_mode (selection,
1769 GTK_SELECTION_SINGLE);
1771 /* - and now the columns of the treeview */
1772 /* - column 1: attribute name */
1773 renderer = GTK_CELL_RENDERER (
1774 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
1775 /* GtkCellRendererText */
1776 /* unknown in GTK 2.4 */
1777 /* "ellipsize",
1778 * PANGO_ELLIPSIZE_END, */
1779 NULL));
1780 g_signal_connect (renderer,
1781 "edited",
1782 G_CALLBACK (multiattrib_callback_edited_name),
1783 multiattrib);
1784 column = GTK_TREE_VIEW_COLUMN (
1785 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1786 /* GtkTreeViewColumn */
1787 "title", _("Name"),
1788 "min-width", 100,
1789 "resizable", TRUE,
1790 NULL));
1791 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1792 gtk_tree_view_column_set_cell_data_func (column, renderer,
1793 multiattrib_column_set_data_name,
1794 multiattrib, NULL);
1795 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1796 /* - column 2: attribute value */
1797 renderer = GTK_CELL_RENDERER (
1798 g_object_new (TYPE_CELL_RENDERER_MULTI_LINE_TEXT,
1799 /* GtkCellRendererText */
1800 /* unknown in GTK 2.4 */
1801 /* "ellipsize",
1802 PANGO_ELLIPSIZE_END, */
1803 NULL));
1804 g_signal_connect (renderer,
1805 "edited",
1806 G_CALLBACK (multiattrib_callback_edited_value),
1807 multiattrib);
1808 column = GTK_TREE_VIEW_COLUMN (
1809 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1810 /* GtkTreeViewColumn */
1811 "title", _("Value"),
1812 "min-width", 140,
1813 "resizable", TRUE,
1814 NULL));
1815 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1816 gtk_tree_view_column_set_cell_data_func (column, renderer,
1817 multiattrib_column_set_data_value,
1818 multiattrib, NULL);
1819 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1820 /* - column 3: visibility */
1821 renderer = GTK_CELL_RENDERER (
1822 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
1823 NULL));
1824 g_signal_connect (renderer,
1825 "toggled",
1826 G_CALLBACK (multiattrib_callback_toggled_visible),
1827 multiattrib);
1828 column = GTK_TREE_VIEW_COLUMN (
1829 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1830 /* GtkTreeViewColumn */
1831 "title", _("Vis?"),
1832 NULL));
1833 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1834 gtk_tree_view_column_set_cell_data_func (column, renderer,
1835 multiattrib_column_set_data_visible,
1836 NULL, NULL);
1837 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1838 /* - column 4: show name */
1839 renderer = GTK_CELL_RENDERER (
1840 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
1841 NULL));
1842 g_signal_connect (renderer,
1843 "toggled",
1844 G_CALLBACK (multiattrib_callback_toggled_show_name),
1845 multiattrib);
1846 column = GTK_TREE_VIEW_COLUMN (
1847 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1848 /* GtkTreeViewColumn */
1849 "title", _("N"),
1850 NULL));
1851 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1852 gtk_tree_view_column_set_cell_data_func (column, renderer,
1853 multiattrib_column_set_data_show_name,
1854 NULL, NULL);
1855 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1856 /* - column 5: show value */
1857 renderer = GTK_CELL_RENDERER (
1858 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
1859 NULL));
1860 g_signal_connect (renderer,
1861 "toggled",
1862 G_CALLBACK (multiattrib_callback_toggled_show_value),
1863 multiattrib);
1864 column = GTK_TREE_VIEW_COLUMN (
1865 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1866 /* GtkTreeViewColumn */
1867 "title", _("V"),
1868 NULL));
1869 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1870 gtk_tree_view_column_set_cell_data_func (column, renderer,
1871 multiattrib_column_set_data_show_value,
1872 NULL, NULL);
1873 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1875 /* add the treeview to the scrolled window */
1876 gtk_container_add (GTK_CONTAINER (scrolled_win), treeview);
1877 /* set treeview of multiattrib */
1878 multiattrib->treeview = GTK_TREE_VIEW (treeview);
1880 attrib_vbox = gtk_vbox_new (FALSE, 0);
1882 /* Pack the vbox into the frame */
1883 gtk_container_add (GTK_CONTAINER (frame), attrib_vbox);
1885 /* add the scrolled window to box */
1886 gtk_box_pack_start (GTK_BOX (attrib_vbox), scrolled_win, TRUE, TRUE, 0);
1888 /* create the show inherited button */
1889 show_inherited = gtk_check_button_new_with_label (_("Show inherited attributes"));
1890 multiattrib->show_inherited = show_inherited;
1891 gtk_box_pack_start (GTK_BOX (attrib_vbox), show_inherited, FALSE, FALSE, 0);
1893 g_signal_connect (show_inherited,
1894 "toggled",
1895 G_CALLBACK (multiattrib_show_inherited_toggled),
1896 multiattrib);
1898 /* pack the frame */
1899 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (multiattrib)->vbox), frame,
1900 TRUE, TRUE, 1);
1901 gtk_widget_show_all (frame);
1903 /* create the add/edit frame */
1904 frame = GTK_WIDGET (g_object_new (GTK_TYPE_FRAME,
1905 "label", _("Add Attribute"),
1906 NULL));
1907 multiattrib->frame_attributes = frame;
1908 table = GTK_WIDGET (g_object_new (GTK_TYPE_TABLE,
1909 /* GtkTable */
1910 "n-rows", 4,
1911 "n-columns", 2,
1912 "homogeneous", FALSE,
1913 NULL));
1915 /* - the name entry: a GtkComboBoxEntry */
1916 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL,
1917 /* GtkMisc */
1918 "xalign", 0.0,
1919 "yalign", 0.5,
1920 /* GtkLabel */
1921 "label", _("Name:"),
1922 NULL));
1923 combo = GTK_WIDGET (g_object_new (GTK_TYPE_COMBO,
1924 /* GtkCombo */
1925 "value-in-list", FALSE,
1926 NULL));
1927 multiattrib_init_attrib_names (GTK_COMBO (combo));
1928 multiattrib->combo_name = GTK_COMBO (combo);
1929 gtk_table_attach (GTK_TABLE (table), label,
1930 0, 1, 0, 1, 0, 0, 0, 0);
1931 gtk_table_attach (GTK_TABLE (table), combo,
1932 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 6, 3);
1934 /* - the value entry: a GtkEntry */
1935 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL,
1936 /* GtkMisc */
1937 "xalign", 0.0,
1938 "yalign", 0.5,
1939 /* GtkLabel */
1940 "label", _("Value:"),
1941 NULL));
1942 scrolled_win = GTK_WIDGET (
1943 g_object_new (GTK_TYPE_SCROLLED_WINDOW,
1944 /* GtkScrolledWindow */
1945 "hscrollbar-policy",
1946 GTK_POLICY_NEVER,
1947 "vscrollbar-policy",
1948 GTK_POLICY_AUTOMATIC,
1949 "shadow-type",
1950 GTK_SHADOW_IN,
1951 NULL));
1952 textview = GTK_WIDGET (g_object_new (GTK_TYPE_TEXT_VIEW,
1953 NULL));
1954 g_signal_connect (textview,
1955 "key_press_event",
1956 G_CALLBACK (multiattrib_callback_value_key_pressed),
1957 multiattrib);
1958 g_signal_connect (textview,
1959 "grab-focus",
1960 G_CALLBACK (multiattrib_callback_value_grab_focus),
1961 multiattrib);
1962 /* Save the GTK_STATE_NORMAL color so we can work around GtkTextView's
1963 * stubborn refusal to draw with GTK_STATE_INSENSITIVE later on */
1964 style = gtk_widget_get_style (textview);
1965 multiattrib->value_normal_text_color = style->text[ GTK_STATE_NORMAL ];
1967 /* Save this one so we can pick it as a sensible colour to show the
1968 * inherited attributes dimmed.
1970 style = gtk_widget_get_style (treeview);
1971 multiattrib->insensitive_text_color = style->text[ GTK_STATE_INSENSITIVE ];
1973 gtk_container_add (GTK_CONTAINER (scrolled_win), textview);
1974 multiattrib->textview_value = GTK_TEXT_VIEW (textview);
1975 gtk_table_attach (GTK_TABLE (table), label,
1976 0, 1, 1, 2, 0, 0, 0, 0);
1977 gtk_table_attach (GTK_TABLE (table), scrolled_win,
1978 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 6, 3);
1980 /* - the visible status */
1981 button = GTK_WIDGET (g_object_new (GTK_TYPE_CHECK_BUTTON,
1982 /* GtkButton */
1983 "label", _("Visible"),
1984 "active", TRUE,
1985 NULL));
1986 multiattrib->button_visible = GTK_CHECK_BUTTON (button);
1987 gtk_table_attach (GTK_TABLE (table), button,
1988 0, 1, 2, 3, GTK_FILL, 0, 3, 0);
1990 /* - the visibility type */
1991 optionm = GTK_WIDGET (g_object_new (GTK_TYPE_OPTION_MENU,
1992 NULL));
1993 multiattrib_init_visible_types (GTK_OPTION_MENU (optionm));
1994 multiattrib->optionmenu_shownv = GTK_OPTION_MENU (optionm);
1995 gtk_table_attach (GTK_TABLE (table), optionm,
1996 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 6, 3);
1997 gtk_widget_show_all (table);
1999 /* create the add button */
2000 button = gtk_button_new_from_stock (GTK_STOCK_ADD);
2001 g_signal_connect (button,
2002 "clicked",
2003 G_CALLBACK (multiattrib_callback_button_add),
2004 multiattrib);
2005 gtk_table_attach (GTK_TABLE (table), button,
2006 2, 3, 0, 3, 0, 0, 6, 3);
2008 /* add the table to the frame */
2009 gtk_container_add (GTK_CONTAINER (frame), table);
2010 /* pack the frame in the dialog */
2011 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (multiattrib)->vbox), frame,
2012 FALSE, TRUE, 1);
2013 gtk_widget_show_all (frame);
2016 /* now add the close button to the action area */
2017 gtk_dialog_add_button (GTK_DIALOG (multiattrib),
2018 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
2023 /*! \brief GObject property setter function
2025 * \par Function Description
2026 * Setter function for Multiattrib's GObject property, "object".
2028 * \param [in] object The GObject whose properties we are setting
2029 * \param [in] property_id The numeric id. under which the property was
2030 * registered with g_object_class_install_property()
2031 * \param [in] value The GValue the property is being set from
2032 * \param [in] pspec A GParamSpec describing the property being set
2035 static void multiattrib_set_property (GObject *object,
2036 guint property_id,
2037 const GValue *value,
2038 GParamSpec *pspec)
2040 Multiattrib *multiattrib = MULTIATTRIB (object);
2042 switch(property_id) {
2043 case PROP_OBJECT:
2044 multiattrib->object = (OBJECT*)g_value_get_pointer (value);
2045 multiattrib_update (multiattrib);
2046 break;
2047 default:
2048 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2053 /*! \brief GObject property getter function
2055 * \par Function Description
2056 * Getter function for Multiattrib's GObject property, "object".
2058 * \param [in] object The GObject whose properties we are getting
2059 * \param [in] property_id The numeric id. under which the property was
2060 * registered with g_object_class_install_property()
2061 * \param [out] value The GValue in which to return the value of the property
2062 * \param [in] pspec A GParamSpec describing the property being got
2064 static void multiattrib_get_property (GObject *object,
2065 guint property_id,
2066 GValue *value,
2067 GParamSpec *pspec)
2069 Multiattrib *multiattrib = MULTIATTRIB (object);
2071 switch(property_id) {
2072 case PROP_OBJECT:
2073 g_value_set_pointer (value, (gpointer)multiattrib->object);
2074 break;
2075 default:
2076 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2082 /*! \brief Update the multiattrib editor dialog's interface
2084 * \par Function Description
2086 * Update the dialog to reflect the attributes of the object. If
2087 * there is no object set, the dialog's controls are set insensitive.
2089 * \param [in] multiattrib The multi-attribute editor dialog.
2091 void multiattrib_update (Multiattrib *multiattrib)
2093 TOPLEVEL *toplevel;
2094 GtkListStore *liststore;
2095 GtkTreeIter iter;
2096 GList *object_attribs;
2097 GList *a_iter;
2098 OBJECT *a_current;
2099 gboolean sensitive;
2100 GtkStyle *style;
2101 gboolean show_inherited;
2103 g_assert (GSCHEM_DIALOG (multiattrib)->w_current != NULL);
2104 toplevel = GSCHEM_DIALOG (multiattrib)->w_current->toplevel;
2106 /* clear the list of attributes */
2107 liststore = (GtkListStore*)gtk_tree_view_get_model (multiattrib->treeview);
2108 gtk_list_store_clear (liststore);
2110 /* Update sensitivities */
2111 sensitive = (multiattrib->object != NULL);
2112 gtk_widget_set_sensitive (GTK_WIDGET (multiattrib->frame_attributes), sensitive);
2113 gtk_widget_set_sensitive (GTK_WIDGET (multiattrib->frame_add), sensitive);
2115 /* Work around GtkTextView's stubborn indifference
2116 * to GTK_STATE_INSENSITIVE when rendering its text. */
2117 style = gtk_widget_get_style (GTK_WIDGET (multiattrib->textview_value));
2118 gtk_widget_modify_text (GTK_WIDGET (multiattrib->textview_value),
2119 GTK_STATE_NORMAL,
2120 sensitive ? &multiattrib->value_normal_text_color
2121 : &style->text[GTK_STATE_INSENSITIVE]);
2123 /* If we aren't sensitive, there is nothing more to do */
2124 if (!sensitive)
2125 return;
2127 show_inherited =
2128 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (multiattrib->show_inherited));
2130 /* get list of attributes */
2131 object_attribs = o_attrib_return_attribs (multiattrib->object);
2132 /* populate the store with attributes */
2133 for (a_iter = object_attribs; a_iter != NULL;
2134 a_iter = g_list_next (a_iter)) {
2135 a_current = a_iter->data;
2137 /* Skip over inherited attributes if we don't want to show them */
2138 if (!show_inherited && o_attrib_is_inherited (a_current))
2139 continue;
2141 gtk_list_store_append (liststore, &iter);
2142 gtk_list_store_set (liststore, &iter,
2143 COLUMN_ATTRIBUTE, a_current,
2144 -1);
2146 /* delete the list of attribute objects */
2147 g_list_free (object_attribs);