Fix for Bug# 1782032: Blank attibute name when attribute value starts with " "
[geda-gaf/whiteaudio.git] / gschem / src / x_multiattrib.c
blob81cf244a4c6cfe6632b56e978a1af345ff8188b5
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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 <libgeda/libgeda.h>
32 #include "../include/i_vars.h"
33 #include "../include/globals.h"
34 #include "../include/prototype.h"
36 #ifdef HAVE_LIBDMALLOC
37 #include <dmalloc.h>
38 #endif
40 #include <gdk/gdkkeysyms.h>
41 #include "../include/gschem_dialog.h"
42 #include "../include/x_multiattrib.h"
44 /*! \brief Process the response returned by the multi-attribte dialog.
45 * \par Function Description
46 * This function handles the response <B>arg1</B> of the multi-attribute
47 * editor dialog <B>dialog</B>.
49 * \param [in] dialog The multi-attribute editor dialog.
50 * \param [in] arg1 The response ID.
51 * \param [in] user_data A pointer on the toplevel environment.
53 static void
54 multiattrib_callback_response (GtkDialog *dialog,
55 gint arg1,
56 gpointer user_data)
58 TOPLEVEL *toplevel = (TOPLEVEL*)user_data;
60 switch (arg1) {
61 case GTK_RESPONSE_CLOSE:
62 case GTK_RESPONSE_DELETE_EVENT:
63 gtk_widget_destroy (GTK_WIDGET (dialog));
64 toplevel->mawindow = NULL;
65 break;
69 /*! \brief Open multiple attribute editor dialog.
70 * \par Function Description
71 * Opens the multiple attribute editor dialog for objects in this <B>toplevel</B>.
73 * \param [in] toplevel The TOPLEVEL object.
75 void x_multiattrib_open (TOPLEVEL *toplevel)
77 if ( toplevel->mawindow == NULL ) {
78 toplevel->mawindow = GTK_WIDGET (g_object_new (TYPE_MULTIATTRIB,
79 "selection", toplevel->page_current->selection_list,
80 /* GschemDialog */
81 "settings-name", "multiattrib",
82 "toplevel", toplevel,
83 NULL));
85 g_signal_connect (toplevel->mawindow,
86 "response",
87 G_CALLBACK (multiattrib_callback_response),
88 toplevel);
90 gtk_window_set_transient_for (GTK_WINDOW(toplevel->mawindow),
91 GTK_WINDOW(toplevel->main_window));
93 gtk_widget_show (toplevel->mawindow);
94 } else {
95 gtk_window_present (GTK_WINDOW(toplevel->mawindow));
100 /*! \brief Close the multiattrib dialog.
102 * \par Function Description
104 * Closes the multiattrib dialog associated with <B>toplevel</B>.
106 * \param [in] toplevel The TOPLEVEL object.
108 void x_multiattrib_close (TOPLEVEL *toplevel)
110 if (toplevel->mawindow != NULL) {
111 gtk_widget_destroy (toplevel->mawindow);
112 toplevel->mawindow = NULL;
117 /*! \brief Update the multiattrib editor dialog for a TOPLEVEL.
119 * \par Function Description
121 * If the TOPLEVEL has an open multiattrib dialog, switch to
122 * watching the current page's SELECTION object for changes.
124 * \param [in] toplevel The TOPLEVEL object.
126 void x_multiattrib_update( TOPLEVEL *toplevel )
128 if (toplevel->mawindow != NULL) {
129 g_object_set (G_OBJECT (toplevel->mawindow), "selection",
130 toplevel->page_current->selection_list, NULL);
135 /*! \section celltextview-widget Cell TextView Widget Code.
136 * This widget makes a 'GtkTextView' widget implements the 'GtkCellEditable'
137 * interface. It can then be used to renderer multi-line texts inside
138 * tree views ('GtkTreeView').
140 static void celltextview_class_init (CellTextViewClass *klass);
141 static void celltextview_init (CellTextView *self);
142 static void celltextview_cell_editable_init (GtkCellEditableIface *iface);
144 /*! \todo Finish function documentation
145 * \brief
146 * \par Function Description
149 static gboolean celltextview_key_press_event (GtkWidget *widget,
150 GdkEventKey *key_event,
151 gpointer data)
153 CellTextView *celltextview = (CellTextView*)widget;
155 /* If the Escape key is pressed, we flag the edit as canceled */
156 if (key_event->keyval == GDK_Escape)
157 celltextview->editing_canceled = TRUE;
159 /* ends editing of cell if one of these keys are pressed or editing is canceled */
160 if (celltextview->editing_canceled == TRUE ||
161 /* the Enter key without the Control modifier */
162 (!(key_event->state & GDK_CONTROL_MASK) &&
163 (key_event->keyval == GDK_Return ||
164 key_event->keyval == GDK_KP_Enter))) {
165 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (celltextview));
166 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (celltextview));
167 return TRUE;
170 return FALSE;
173 /*! \todo Finish function documentation
174 * \brief
175 * \par Function Description
178 static void celltextview_start_editing (GtkCellEditable *cell_editable,
179 GdkEvent *event)
181 g_signal_connect (cell_editable,
182 "key_press_event",
183 G_CALLBACK (celltextview_key_press_event),
184 NULL);
188 /*! \todo Finish function documentation
189 * \brief
190 * \par Function Description
193 GType celltextview_get_type()
195 static GType celltextview_type = 0;
197 if (!celltextview_type) {
198 static const GTypeInfo celltextview_info = {
199 sizeof(CellTextViewClass),
200 NULL, /* base_init */
201 NULL, /* base_finalize */
202 (GClassInitFunc) celltextview_class_init,
203 NULL, /* class_finalize */
204 NULL, /* class_data */
205 sizeof(CellTextView),
206 0, /* n_preallocs */
207 (GInstanceInitFunc) celltextview_init,
210 static const GInterfaceInfo cell_editable_info = {
211 (GInterfaceInitFunc) celltextview_cell_editable_init,
212 NULL, /* interface_finalize */
213 NULL /* interface_data */
216 celltextview_type = g_type_register_static(GTK_TYPE_TEXT_VIEW,
217 "CellTextView",
218 &celltextview_info, 0);
219 g_type_add_interface_static(celltextview_type,
220 GTK_TYPE_CELL_EDITABLE,
221 &cell_editable_info);
224 return celltextview_type;
227 /*! \todo Finish function documentation
228 * \brief
229 * \par Function Description
232 static void celltextview_class_init(CellTextViewClass *klass)
234 /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */
237 /*! \todo Finish function documentation
238 * \brief
239 * \par Function Description
242 static void celltextview_init(CellTextView *celltextview)
244 celltextview->editing_canceled = FALSE;
247 /*! \todo Finish function documentation
248 * \brief
249 * \par Function Description
252 static void celltextview_cell_editable_init(GtkCellEditableIface *iface)
254 iface->start_editing = celltextview_start_editing;
257 /*! \section multi-line-text-cell-renderer Multi-line Text Cell Renderer
258 * GTK has no multi-line text cell renderer. This code adds one to be used
259 * in gschem code. It is inspired by the 'GtkCellRendererCombo' renderer
260 * of GTK 2.4 (LGPL).
262 static void cellrenderermultilinetext_class_init(CellRendererMultiLineTextClass *klass);
263 static void cellrenderermultilinetext_init (CellRendererMultiLineText *self);
265 static void cellrenderermultilinetext_editing_done (GtkCellEditable *cell_editable,
266 gpointer user_data);
267 static gboolean cellrenderermultilinetext_focus_out_event (GtkWidget *widget,
268 GdkEvent *event,
269 gpointer user_data);
272 #define CELL_RENDERER_MULTI_LINE_TEXT_PATH "cell-renderer-multi-line-text-path"
275 /*! \todo Finish function documentation
276 * \brief
277 * \par Function Description
280 static GtkCellEditable* cellrenderermultilinetext_start_editing(GtkCellRenderer *cell,
281 GdkEvent *event,
282 GtkWidget *widget,
283 const gchar *path,
284 GdkRectangle *background_area,
285 GdkRectangle *cell_area,
286 GtkCellRendererState flags)
288 GtkCellRendererText *cell_text;
289 CellRendererMultiLineText *cell_mlt;
290 GtkWidget *textview;
291 GtkTextBuffer *textbuffer;
293 cell_text = GTK_CELL_RENDERER_TEXT (cell);
294 if (cell_text->editable == FALSE) {
295 return NULL;
298 cell_mlt = CELL_RENDERER_MULTI_LINE_TEXT (cell);
300 textbuffer = GTK_TEXT_BUFFER (g_object_new (GTK_TYPE_TEXT_BUFFER,
301 NULL));
302 gtk_text_buffer_set_text (textbuffer,
303 cell_text->text,
304 strlen (cell_text->text));
306 textview = GTK_WIDGET (g_object_new (TYPE_CELL_TEXT_VIEW,
307 /* GtkTextView */
308 "buffer", textbuffer,
309 "editable", TRUE,
310 /* GtkWidget */
311 "height-request", cell_area->height,
312 NULL));
313 g_object_set_data_full (G_OBJECT (textview),
314 CELL_RENDERER_MULTI_LINE_TEXT_PATH,
315 g_strdup (path), g_free);
317 gtk_widget_show (textview);
319 g_signal_connect (GTK_CELL_EDITABLE (textview),
320 "editing_done",
321 G_CALLBACK (cellrenderermultilinetext_editing_done),
322 cell_mlt);
323 cell_mlt->focus_out_id =
324 g_signal_connect (textview,
325 "focus_out_event",
326 G_CALLBACK (cellrenderermultilinetext_focus_out_event),
327 cell_mlt);
329 return GTK_CELL_EDITABLE (textview);
332 /*! \todo Finish function documentation
333 * \brief
334 * \par Function Description
337 static void cellrenderermultilinetext_editing_done(GtkCellEditable *cell_editable,
338 gpointer user_data)
340 CellRendererMultiLineText *cell = CELL_RENDERER_MULTI_LINE_TEXT (user_data);
341 GtkTextBuffer *buffer;
342 GtkTextIter start, end;
343 gchar *new_text;
344 const gchar *path;
346 if (cell->focus_out_id > 0) {
347 g_signal_handler_disconnect (cell_editable,
348 cell->focus_out_id);
349 cell->focus_out_id = 0;
352 gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (cell),
353 CELL_TEXT_VIEW (cell_editable)->editing_canceled);
354 if (CELL_TEXT_VIEW (cell_editable)->editing_canceled)
355 return;
357 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (cell_editable));
358 gtk_text_buffer_get_start_iter (buffer, &start);
359 gtk_text_buffer_get_end_iter (buffer, &end);
360 new_text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
362 path = g_object_get_data (G_OBJECT (cell_editable),
363 CELL_RENDERER_MULTI_LINE_TEXT_PATH);
364 g_signal_emit_by_name (cell, "edited", path, new_text);
366 g_free (new_text);
370 /*! \todo Finish function documentation
371 * \brief
372 * \par Function Description
375 static gboolean cellrenderermultilinetext_focus_out_event(GtkWidget *widget,
376 GdkEvent *event,
377 gpointer user_data)
379 cellrenderermultilinetext_editing_done (GTK_CELL_EDITABLE (widget),
380 user_data);
382 return FALSE;
385 /*! \todo Finish function documentation
386 * \brief
387 * \par Function Description
390 GType cellrenderermultilinetext_get_type()
392 static GType cellrenderermultilinetext_type = 0;
394 if (!cellrenderermultilinetext_type) {
395 static const GTypeInfo cellrenderermultilinetext_info = {
396 sizeof(CellRendererMultiLineTextClass),
397 NULL, /* base_init */
398 NULL, /* base_finalize */
399 (GClassInitFunc) cellrenderermultilinetext_class_init,
400 NULL, /* class_finalize */
401 NULL, /* class_data */
402 sizeof(CellRendererMultiLineText),
403 0, /* n_preallocs */
404 (GInstanceInitFunc) cellrenderermultilinetext_init,
407 cellrenderermultilinetext_type = g_type_register_static (
408 GTK_TYPE_CELL_RENDERER_TEXT,
409 "CellRendererMultiLineText",
410 &cellrenderermultilinetext_info, 0);
413 return cellrenderermultilinetext_type;
416 /*! \todo Finish function documentation
417 * \brief
418 * \par Function Description
421 static void cellrenderermultilinetext_class_init(CellRendererMultiLineTextClass *klass)
423 /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */
424 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
426 cell_class->start_editing = cellrenderermultilinetext_start_editing;
430 /*! \todo Finish function documentation
431 * \brief
432 * \par Function Description
435 static void cellrenderermultilinetext_init(CellRendererMultiLineText *self)
440 enum {
441 PROP_SELECTION = 1
444 enum {
445 COLUMN_ATTRIBUTE,
446 NUM_COLUMNS
449 static GObjectClass *multiattrib_parent_class = NULL;
451 static void multiattrib_class_init (MultiattribClass *class);
452 static void multiattrib_init (Multiattrib *multiattrib);
453 static void multiattrib_set_property (GObject *object,
454 guint property_id,
455 const GValue *value,
456 GParamSpec *pspec);
457 static void multiattrib_get_property (GObject *object,
458 guint property_id,
459 GValue *value,
460 GParamSpec *pspec);
462 static void multiattrib_popup_menu (Multiattrib *multiattrib,
463 GdkEventButton *event);
466 /*! \todo Finish function documentation
467 * \brief
468 * \par Function Description
471 static void multiattrib_action_add_attribute(TOPLEVEL *toplevel,
472 OBJECT *object,
473 Multiattrib *multiattrib,
474 const gchar *name,
475 const gchar *value,
476 gint visible,
477 gint show_name_value)
479 OBJECT *o_attrib;
480 gchar *newtext;
482 newtext = g_strdup_printf ("%s=%s", name, value);
484 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) {
485 g_free(newtext);
486 return;
489 /* create a new attribute and link it */
490 o_attrib = o_attrib_add_attrib (toplevel, newtext,
491 visible, show_name_value, object);
493 toplevel->page_current->CHANGED = 1;
494 o_undo_savestate (toplevel, UNDO_ALL);
496 g_free (newtext);
500 /*! \todo Finish function documentation
501 * \brief
502 * \par Function Description
505 static void multiattrib_action_duplicate_attribute(TOPLEVEL *toplevel,
506 OBJECT *object,
507 OBJECT *o_attrib)
509 OBJECT *o_new;
511 o_new = o_attrib_add_attrib (toplevel,
512 o_attrib->text->string,
513 o_attrib->visibility,
514 o_attrib->show_name_value,
515 object);
516 toplevel->page_current->CHANGED = 1;
517 o_undo_savestate (toplevel, UNDO_ALL);
521 /*! \todo Finish function documentation
522 * \brief
523 * \par Function Description
526 static void multiattrib_action_delete_attribute(TOPLEVEL *toplevel,
527 OBJECT *o_attrib)
529 /* actually deletes the attribute */
530 o_selection_remove ( toplevel->page_current->selection_list, o_attrib);
531 o_delete_text (toplevel, o_attrib);
532 toplevel->page_current->CHANGED=1;
533 o_undo_savestate (toplevel, UNDO_ALL);
537 /*! \todo Finish function documentation
538 * \brief
539 * \par Function Description
542 static void multiattrib_column_set_data_name(GtkTreeViewColumn *tree_column,
543 GtkCellRenderer *cell,
544 GtkTreeModel *tree_model,
545 GtkTreeIter *iter,
546 gpointer data)
548 OBJECT *o_attrib;
549 gchar *name, *value;
551 gtk_tree_model_get (tree_model, iter,
552 COLUMN_ATTRIBUTE, &o_attrib,
553 -1);
554 g_assert (o_attrib->type == OBJ_TEXT);
556 o_attrib_get_name_value (o_attrib->text->string, &name, &value);
557 g_object_set (cell,
558 "text", name,
559 NULL);
560 g_free (name);
561 g_free (value);
565 /*! \todo Finish function documentation
566 * \brief
567 * \par Function Description
570 static void multiattrib_column_set_data_value(GtkTreeViewColumn *tree_column,
571 GtkCellRenderer *cell,
572 GtkTreeModel *tree_model,
573 GtkTreeIter *iter,
574 gpointer data)
576 OBJECT *o_attrib;
577 gchar *name, *value;
579 gtk_tree_model_get (tree_model, iter,
580 COLUMN_ATTRIBUTE, &o_attrib,
581 -1);
582 g_assert (o_attrib->type == OBJ_TEXT);
584 o_attrib_get_name_value (o_attrib->text->string, &name, &value);
585 g_object_set (cell,
586 "text", value,
587 NULL);
588 g_free (name);
589 g_free (value);
593 /*! \todo Finish function documentation
594 * \brief
595 * \par Function Description
598 static void multiattrib_column_set_data_visible(GtkTreeViewColumn *tree_column,
599 GtkCellRenderer *cell,
600 GtkTreeModel *tree_model,
601 GtkTreeIter *iter,
602 gpointer data)
604 OBJECT *o_attrib;
606 gtk_tree_model_get (tree_model, iter,
607 COLUMN_ATTRIBUTE, &o_attrib,
608 -1);
609 g_assert (o_attrib->type == OBJ_TEXT);
611 g_object_set (cell,
612 "active", (o_attrib->visibility == VISIBLE),
613 NULL);
617 /*! \todo Finish function documentation
618 * \brief
619 * \par Function Description
622 static void multiattrib_column_set_data_show_name(GtkTreeViewColumn *tree_column,
623 GtkCellRenderer *cell,
624 GtkTreeModel *tree_model,
625 GtkTreeIter *iter,
626 gpointer data)
628 OBJECT *o_attrib;
630 gtk_tree_model_get (tree_model, iter,
631 COLUMN_ATTRIBUTE, &o_attrib,
632 -1);
633 g_assert (o_attrib->type == OBJ_TEXT);
635 g_object_set (cell,
636 "active", (o_attrib->show_name_value == SHOW_NAME_VALUE ||
637 o_attrib->show_name_value == SHOW_NAME),
638 NULL);
642 /*! \todo Finish function documentation
643 * \brief
644 * \par Function Description
647 static void multiattrib_column_set_data_show_value(GtkTreeViewColumn *tree_column,
648 GtkCellRenderer *cell,
649 GtkTreeModel *tree_model,
650 GtkTreeIter *iter,
651 gpointer data)
653 OBJECT *o_attrib;
655 gtk_tree_model_get (tree_model, iter,
656 COLUMN_ATTRIBUTE, &o_attrib,
657 -1);
658 g_assert (o_attrib->type == OBJ_TEXT);
660 g_object_set (cell,
661 "active", (o_attrib->show_name_value == SHOW_NAME_VALUE ||
662 o_attrib->show_name_value == SHOW_VALUE),
663 NULL);
667 /*! \brief Requests an update of the display of a row.
668 * \par Function Description
669 * This is an helper function to update the display of a row when
670 * data for this row have been modified in the model.
672 * It emits the 'row_changed' signal on the pointed row.
674 * \param [in] model A GtkTreeModel.
675 * \param [in] iter A valid GtkTreeIter pointing to the changed row.
677 static void
678 update_row_display (GtkTreeModel *model, GtkTreeIter *iter)
680 GtkTreePath *path;
682 path = gtk_tree_model_get_path (model, iter);
683 gtk_tree_model_row_changed (model, path, iter);
684 gtk_tree_path_free (path);
688 /*! \todo Finish function documentation
689 * \brief
690 * \par Function Description
693 static void multiattrib_callback_edited_name(GtkCellRendererText *cellrenderertext,
694 gchar *arg1,
695 gchar *arg2,
696 gpointer user_data)
698 Multiattrib *multiattrib = (Multiattrib*)user_data;
699 GtkTreeModel *model;
700 GtkTreeIter iter;
701 OBJECT *o_attrib;
702 TOPLEVEL *toplevel;
703 gchar *name, *value, *newtext;
705 model = gtk_tree_view_get_model (multiattrib->treeview);
706 toplevel = GSCHEM_DIALOG (multiattrib)->toplevel;
708 if (!gtk_tree_model_get_iter_from_string (model, &iter, arg1)) {
709 return;
712 if (g_ascii_strcasecmp (arg2, "") == 0) {
713 GtkWidget *dialog = gtk_message_dialog_new (
714 GTK_WINDOW (multiattrib),
715 GTK_DIALOG_MODAL,
716 GTK_MESSAGE_ERROR,
717 GTK_BUTTONS_OK,
718 _("Attributes with empty name are not allowed. Please set a name."));
720 gtk_dialog_run (GTK_DIALOG (dialog));
721 gtk_widget_destroy (dialog);
722 return;
725 gtk_tree_model_get (model, &iter,
726 COLUMN_ATTRIBUTE, &o_attrib,
727 -1);
728 g_assert (o_attrib->type == OBJ_TEXT);
730 o_attrib_get_name_value (o_attrib->text->string, &name, &value);
731 newtext = g_strdup_printf ("%s=%s", arg2, value);
733 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) {
734 if (name) g_free (name);
735 if (value) g_free (value);
736 g_free(newtext);
737 return;
741 /* actually modifies the attribute */
742 o_text_change (toplevel, o_attrib,
743 newtext, o_attrib->visibility, o_attrib->show_name_value);
745 g_free (name);
746 g_free (value);
747 g_free (newtext);
751 /*! \todo Finish function documentation
752 * \brief
753 * \par Function Description
756 static void multiattrib_callback_edited_value(GtkCellRendererText *cell_renderer,
757 gchar *arg1,
758 gchar *arg2,
759 gpointer user_data)
761 Multiattrib *multiattrib = (Multiattrib*)user_data;
762 GtkTreeModel *model;
763 GtkTreeIter iter;
764 OBJECT *o_attrib;
765 TOPLEVEL *toplevel;
766 gchar *name, *value, *newtext;
768 model = gtk_tree_view_get_model (multiattrib->treeview);
769 toplevel = GSCHEM_DIALOG (multiattrib)->toplevel;
771 if (!gtk_tree_model_get_iter_from_string (model, &iter, arg1)) {
772 return;
775 gtk_tree_model_get (model, &iter,
776 COLUMN_ATTRIBUTE, &o_attrib,
777 -1);
778 g_assert (o_attrib->type == OBJ_TEXT);
780 o_attrib_get_name_value (o_attrib->text->string, &name, &value);
781 newtext = g_strdup_printf ("%s=%s", name, arg2);
783 if (!x_dialog_validate_attribute(GTK_WINDOW(multiattrib), newtext)) {
784 if (name) g_free (name);
785 if (value) g_free (value);
786 g_free(newtext);
787 return;
790 /* actually modifies the attribute */
791 o_text_change (toplevel, o_attrib,
792 newtext, o_attrib->visibility, o_attrib->show_name_value);
794 /* request an update of display for this row */
795 update_row_display (model, &iter);
797 g_free (name);
798 g_free (value);
799 g_free (newtext);
803 /*! \todo Finish function documentation
804 * \brief
805 * \par Function Description
808 static void multiattrib_callback_toggled_visible(GtkCellRendererToggle *cell_renderer,
809 gchar *path,
810 gpointer user_data)
812 Multiattrib *multiattrib = (Multiattrib*)user_data;
813 GtkTreeModel *model;
814 GtkTreeIter iter;
815 OBJECT *o_attrib;
816 TOPLEVEL *toplevel;
817 gint visibility;
819 model = gtk_tree_view_get_model (multiattrib->treeview);
820 toplevel = GSCHEM_DIALOG (multiattrib)->toplevel;
822 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) {
823 return;
826 gtk_tree_model_get (model, &iter,
827 COLUMN_ATTRIBUTE, &o_attrib,
828 -1);
829 g_assert (o_attrib->type == OBJ_TEXT);
830 o_text_erase (toplevel, o_attrib);
832 visibility = o_attrib->visibility == VISIBLE ? INVISIBLE : VISIBLE;
834 /* actually modifies the attribute */
835 o_attrib->visibility = visibility;
836 o_text_recreate (toplevel, o_attrib);
837 o_text_draw (toplevel, o_attrib);
838 o_undo_savestate (toplevel, UNDO_ALL);
840 /* request an update of display for this row */
841 update_row_display (model, &iter);
845 /*! \todo Finish function documentation
846 * \brief
847 * \par Function Description
850 static void multiattrib_callback_toggled_show_name(GtkCellRendererToggle *cell_renderer,
851 gchar *path,
852 gpointer user_data)
854 Multiattrib *multiattrib = (Multiattrib*)user_data;
855 GtkTreeModel *model;
856 GtkTreeIter iter;
857 OBJECT *o_attrib;
858 TOPLEVEL *toplevel;
859 gint new_snv;
861 model = gtk_tree_view_get_model (multiattrib->treeview);
862 toplevel = GSCHEM_DIALOG (multiattrib)->toplevel;
864 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) {
865 return;
868 gtk_tree_model_get (model, &iter,
869 COLUMN_ATTRIBUTE, &o_attrib,
870 -1);
871 g_assert (o_attrib->type == OBJ_TEXT);
872 o_text_erase (toplevel, o_attrib);
874 switch (o_attrib->show_name_value) {
875 case SHOW_NAME_VALUE: new_snv = SHOW_VALUE; break;
876 case SHOW_NAME: new_snv = SHOW_VALUE; break;
877 case SHOW_VALUE: new_snv = SHOW_NAME_VALUE; break;
878 default:
879 g_assert_not_reached ();
880 new_snv = SHOW_NAME_VALUE;
883 /* actually modifies the attribute */
884 o_attrib->show_name_value = new_snv;
885 o_text_recreate (toplevel, o_attrib);
886 o_text_draw (toplevel, o_attrib);
887 o_undo_savestate (toplevel, UNDO_ALL);
889 /* request an update of display for this row */
890 update_row_display (model, &iter);
894 /*! \todo Finish function documentation
895 * \brief
896 * \par Function Description
899 static void multiattrib_callback_toggled_show_value(GtkCellRendererToggle *cell_renderer,
900 gchar *path,
901 gpointer user_data)
903 Multiattrib *multiattrib = (Multiattrib*)user_data;
904 GtkTreeModel *model;
905 GtkTreeIter iter;
906 OBJECT *o_attrib;
907 TOPLEVEL *toplevel;
908 gint new_snv;
910 model = gtk_tree_view_get_model (multiattrib->treeview);
911 toplevel = GSCHEM_DIALOG (multiattrib)->toplevel;
913 if (!gtk_tree_model_get_iter_from_string (model, &iter, path)) {
914 return;
917 gtk_tree_model_get (model, &iter,
918 COLUMN_ATTRIBUTE, &o_attrib,
919 -1);
920 g_assert (o_attrib->type == OBJ_TEXT);
921 o_text_erase (toplevel, o_attrib);
923 switch (o_attrib->show_name_value) {
924 case SHOW_NAME_VALUE: new_snv = SHOW_NAME; break;
925 case SHOW_NAME: new_snv = SHOW_NAME_VALUE; break;
926 case SHOW_VALUE: new_snv = SHOW_NAME; break;
927 default:
928 g_assert_not_reached ();
929 new_snv = SHOW_NAME_VALUE;
932 /* actually modifies the attribute */
933 o_attrib->show_name_value = new_snv;
934 o_text_recreate (toplevel, o_attrib);
935 o_text_draw (toplevel, o_attrib);
936 o_undo_savestate (toplevel, UNDO_ALL);
938 /* request an update of display for this row */
939 update_row_display (model, &iter);
943 /*! \todo Finish function documentation
944 * \brief
945 * \par Function Description
948 static gboolean multiattrib_callback_key_pressed(GtkWidget *widget,
949 GdkEventKey *event,
950 gpointer user_data)
952 Multiattrib *multiattrib = (Multiattrib*)user_data;
954 if (event->state == 0 &&
955 (event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete)) {
956 GtkTreeModel *model;
957 GtkTreeIter iter;
958 OBJECT *o_attrib;
959 /* delete the currently selected attribute */
961 if (!gtk_tree_selection_get_selected (
962 gtk_tree_view_get_selection (multiattrib->treeview),
963 &model, &iter)) {
964 /* nothing selected, nothing to do */
965 return FALSE;
968 gtk_tree_model_get (model, &iter,
969 COLUMN_ATTRIBUTE, &o_attrib,
970 -1);
971 g_assert (o_attrib->type == OBJ_TEXT);
973 multiattrib_action_delete_attribute (GSCHEM_DIALOG (multiattrib)->toplevel,
974 o_attrib);
976 /* update the treeview contents */
977 multiattrib_update (multiattrib);
980 return FALSE;
983 /*! \todo Finish function documentation
984 * \brief
985 * \par Function Description
988 static gboolean multiattrib_callback_button_pressed(GtkWidget *widget,
989 GdkEventButton *event,
990 gpointer user_data)
992 Multiattrib *multiattrib = (Multiattrib*)user_data;
993 gboolean ret = FALSE;
995 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
996 multiattrib_popup_menu (multiattrib, event);
997 ret = TRUE;
1000 return ret;
1003 /*! \todo Finish function documentation
1004 * \brief
1005 * \par Function Description
1008 static gboolean multiattrib_callback_popup_menu(GtkWidget *widget,
1009 gpointer user_data)
1011 Multiattrib *multiattrib = (Multiattrib*)user_data;
1013 multiattrib_popup_menu (multiattrib, NULL);
1015 return TRUE;
1018 /*! \todo Finish function documentation
1019 * \brief
1020 * \par Function Description
1023 static void multiattrib_callback_popup_duplicate(GtkMenuItem *menuitem,
1024 gpointer user_data)
1026 Multiattrib *multiattrib = (Multiattrib*)user_data;
1027 GtkTreeModel *model;
1028 GtkTreeIter iter;
1029 TOPLEVEL *toplevel;
1030 OBJECT *object, *o_attrib;
1032 if (!gtk_tree_selection_get_selected (
1033 gtk_tree_view_get_selection (multiattrib->treeview),
1034 &model, &iter)) {
1035 /* nothing selected, nothing to do */
1036 return;
1039 toplevel = GSCHEM_DIALOG (multiattrib)->toplevel;
1040 object = multiattrib->object;
1042 gtk_tree_model_get (model, &iter,
1043 COLUMN_ATTRIBUTE, &o_attrib,
1044 -1);
1045 g_assert (o_attrib->type == OBJ_TEXT);
1047 multiattrib_action_duplicate_attribute (toplevel, object, o_attrib);
1049 /* update the treeview contents */
1050 multiattrib_update (multiattrib);
1054 /*! \todo Finish function documentation
1055 * \brief
1056 * \par Function Description
1059 static void multiattrib_callback_popup_delete(GtkMenuItem *menuitem,
1060 gpointer user_data)
1062 Multiattrib *multiattrib = (Multiattrib*)user_data;
1063 GtkTreeModel *model;
1064 GtkTreeIter iter;
1065 TOPLEVEL *toplevel;
1066 OBJECT *o_attrib;
1068 if (!gtk_tree_selection_get_selected (
1069 gtk_tree_view_get_selection (multiattrib->treeview),
1070 &model, &iter)) {
1071 /* nothing selected, nothing to do */
1072 return;
1075 toplevel = GSCHEM_DIALOG (multiattrib)->toplevel;
1077 gtk_tree_model_get (model, &iter,
1078 COLUMN_ATTRIBUTE, &o_attrib,
1079 -1);
1080 g_assert (o_attrib->type == OBJ_TEXT);
1082 multiattrib_action_delete_attribute (toplevel, o_attrib);
1084 /* update the treeview contents */
1085 multiattrib_update (multiattrib);
1089 /*! \todo Finish function documentation
1090 * \brief
1091 * \par Function Description
1094 static gboolean multiattrib_callback_value_key_pressed(GtkWidget *widget,
1095 GdkEventKey *event,
1096 gpointer user_data)
1098 Multiattrib *multiattrib = (Multiattrib*)widget;
1099 gboolean retval = FALSE;
1101 /* ends editing of cell if one of these keys are pressed: */
1102 /* - the Return key without the Control modifier */
1103 /* - the Tab key without the Control modifier */
1104 if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) ||
1105 (event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab)) {
1106 /* Control modifier activated? */
1107 if (event->state & GDK_CONTROL_MASK) {
1108 /* yes the modifier in event structure and let event propagate */
1109 event->state ^= GDK_CONTROL_MASK;
1110 retval = FALSE;
1111 } else {
1112 /* change focus and stop propagation */
1113 g_signal_emit_by_name (multiattrib,
1114 "move_focus",
1115 (event->state & GDK_SHIFT_MASK) ?
1116 GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
1117 retval = TRUE;
1121 return retval;
1125 /*! \brief GtkWidget "grab-focus" signal handler
1127 * \par Function Description
1128 * Select the text in the GtkTextView so it may be over-typed quickly
1130 static void multiattrib_callback_value_grab_focus (GtkWidget *widget,
1131 gpointer user_data)
1133 GtkTextView *textview = GTK_TEXT_VIEW (widget);
1134 GtkTextBuffer *textbuffer;
1135 GtkTextIter startiter, enditer;
1137 textbuffer = gtk_text_view_get_buffer (textview);
1138 gtk_text_buffer_get_iter_at_offset (textbuffer, &startiter, 0);
1139 gtk_text_buffer_get_iter_at_offset (textbuffer, &enditer, -1);
1140 gtk_text_buffer_select_range (textbuffer, &enditer, &startiter);
1144 /*! \todo Finish function documentation
1145 * \brief
1146 * \par Function Description
1149 static void multiattrib_callback_button_add(GtkButton *button,
1150 gpointer user_data)
1152 Multiattrib *multiattrib = (Multiattrib*)user_data;
1153 GtkTextBuffer *buffer;
1154 GtkTextIter start, end;
1155 const gchar *name;
1156 gchar *value;
1157 TOPLEVEL *toplevel;
1158 OBJECT *object;
1159 gboolean visible;
1160 gint shownv;
1162 toplevel = GSCHEM_DIALOG (multiattrib)->toplevel;
1163 object = multiattrib->object;
1164 buffer = gtk_text_view_get_buffer (multiattrib->textview_value);
1166 /* retrieve information from the Add/Edit frame */
1167 /* - attribute's name */
1168 name = gtk_entry_get_text (
1169 GTK_ENTRY (GTK_COMBO (multiattrib->combo_name)->entry));
1170 /* - attribute's value */
1171 gtk_text_buffer_get_bounds (buffer, &start, &end);
1172 value = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1173 /* - attribute's visibility status */
1174 visible = gtk_toggle_button_get_active (
1175 (GtkToggleButton*)multiattrib->button_visible);
1176 /* - visibility type */
1177 shownv = (gint)gtk_option_menu_get_history (multiattrib->optionmenu_shownv);
1179 if (name[0] == '\0' || name[0] == ' ') {
1180 /* name not allowed for an attribute */
1181 g_free (value);
1182 return;
1185 multiattrib_action_add_attribute (toplevel, object, multiattrib,
1186 name, value,
1187 visible, shownv);
1188 g_free (value);
1190 multiattrib_update (multiattrib);
1193 /*! \todo Finish function documentation
1194 * \brief
1195 * \par Function Description
1198 static void multiattrib_init_attrib_names(GtkCombo *combo)
1200 GList *items = NULL;
1201 const gchar *string;
1202 gint i;
1204 for (i = 0, string = s_attrib_get (i);
1205 string != NULL;
1206 i++, string = s_attrib_get (i)) {
1207 items = g_list_append (items, (gpointer)string);
1210 gtk_combo_set_popdown_strings (GTK_COMBO (combo), items);
1212 g_list_free (items);
1216 /*! \todo Finish function documentation
1217 * \brief
1218 * \par Function Description
1221 static void multiattrib_init_visible_types(GtkOptionMenu *optionmenu)
1223 GtkWidget *menu, *item;
1225 menu = gtk_menu_new ();
1226 item = gtk_menu_item_new_with_label (_("Show Name & Value"));
1227 gtk_menu_append (menu, item);
1228 item = gtk_menu_item_new_with_label (_("Show Value only"));
1229 gtk_menu_append (menu, item);
1230 item = gtk_menu_item_new_with_label (_("Show Name only"));
1231 gtk_menu_append (menu, item);
1233 gtk_option_menu_set_menu (optionmenu, menu);
1238 /*! \brief Popup a context-sensitive menu.
1239 * \par Function Description
1240 * Pops up a context-sensitive menu.
1241 * <B>event</B> can be NULL if the popup is triggered by a key binding
1242 * instead of a mouse click.
1244 * \param [in] multiattrib The Multiattrib object.
1245 * \param [in] event Mouse event.
1247 static void multiattrib_popup_menu(Multiattrib *multiattrib,
1248 GdkEventButton *event)
1250 GtkTreePath *path;
1251 GtkWidget *menu;
1252 struct menuitem_t {
1253 gchar *label;
1254 GCallback callback;
1256 struct menuitem_t menuitems[] = {
1257 { N_("Duplicate"), G_CALLBACK (multiattrib_callback_popup_duplicate) },
1258 { N_("Delete"), G_CALLBACK (multiattrib_callback_popup_delete) },
1259 { NULL, NULL } };
1260 struct menuitem_t *tmp;
1262 if (event != NULL &&
1263 gtk_tree_view_get_path_at_pos (multiattrib->treeview,
1264 (gint)event->x,
1265 (gint)event->y,
1266 &path, NULL, NULL, NULL)) {
1267 GtkTreeSelection *selection;
1268 selection = gtk_tree_view_get_selection (multiattrib->treeview);
1269 gtk_tree_selection_unselect_all (selection);
1270 gtk_tree_selection_select_path (selection, path);
1271 gtk_tree_path_free (path);
1274 /* create the context menu */
1275 menu = gtk_menu_new();
1276 for (tmp = menuitems; tmp->label != NULL; tmp++) {
1277 GtkWidget *menuitem;
1278 if (g_strcasecmp (tmp->label, "-") == 0) {
1279 menuitem = gtk_separator_menu_item_new ();
1280 } else {
1281 menuitem = gtk_menu_item_new_with_label (_(tmp->label));
1282 g_signal_connect (menuitem,
1283 "activate",
1284 tmp->callback,
1285 multiattrib);
1287 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1289 gtk_widget_show_all (menu);
1290 /* make menu a popup menu */
1291 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
1292 (event != NULL) ? event->button : 0,
1293 gdk_event_get_time ((GdkEvent*)event));
1298 /*! \brief Function to retrieve Multiattrib's GType identifier.
1300 * \par Function Description
1302 * Function to retrieve Multiattrib's GType identifier.
1303 * Upon first call, this registers Multiattrib in the GType system.
1304 * Subsequently it returns the saved value from its first execution.
1306 * \return the GType identifier associated with Multiattrib.
1308 GType multiattrib_get_type()
1310 static GType multiattrib_type = 0;
1312 if (!multiattrib_type) {
1313 static const GTypeInfo multiattrib_info = {
1314 sizeof(MultiattribClass),
1315 NULL, /* base_init */
1316 NULL, /* base_finalize */
1317 (GClassInitFunc) multiattrib_class_init,
1318 NULL, /* class_finalize */
1319 NULL, /* class_data */
1320 sizeof(Multiattrib),
1321 0, /* n_preallocs */
1322 (GInstanceInitFunc) multiattrib_init,
1325 multiattrib_type = g_type_register_static (GSCHEM_TYPE_DIALOG,
1326 "Multiattrib",
1327 &multiattrib_info, 0);
1330 return multiattrib_type;
1334 /*! \brief Update the multiattrib editor dialog when the page's selection changes.
1336 * \par Function Description
1338 * When the page's selection changes this function identifies how many objects
1339 * which can have attributes are currently selected. If this number is 1, the
1340 * dialog is set to edit its attributes.
1342 * \todo The dialog doesn't currently support editing multiple objects at once
1344 * \param [in] selection The SELECTION object of page being edited.
1345 * \param [in] multiattrib The multi-attribute editor dialog.
1347 static void selection_changed_cb (SELECTION *selection, Multiattrib *multiattrib)
1349 int object_count = 0;
1350 GList *selection_glist;
1351 GList *iter;
1352 OBJECT *object;
1354 selection_glist = geda_list_get_glist (selection);
1356 for ( iter = selection_glist;
1357 iter != NULL;
1358 iter = g_list_next (iter) ) {
1359 object = (OBJECT *)iter->data;
1360 g_assert( object != NULL );
1362 if (object->type == OBJ_COMPLEX ||
1363 object->type == OBJ_PLACEHOLDER ||
1364 object->type == OBJ_NET ||
1365 object->type == OBJ_BUS ||
1366 object->type == OBJ_PIN) {
1367 object_count++;
1371 if (object_count == 0) {
1372 /* TODO: If the user selects a single attribute which is
1373 * not floating, should we find its parent object and
1374 * display the multi-attribute editor for that?
1375 * Bonus marks for making it jump to the correct attrib.
1377 multiattrib->object = NULL;
1378 } else if (object_count == 1) {
1379 multiattrib->object = (OBJECT *)selection_glist->data;
1380 } else {
1381 /* TODO: Something clever with multiple objects selected */
1382 multiattrib->object = NULL;
1385 multiattrib_update (multiattrib);
1389 /*! \brief Update the dialog when the current page's SELECTION object is destroyed
1391 * \par Function Description
1393 * This handler is called when the g_object_weak_ref() on the SELECTION object
1394 * we're watching expires. We reset our multiattrib->selection pointer to NULL
1395 * to avoid attempting to access the destroyed object. NB: Our signal handlers
1396 * were automatically disconnected during the destruction process.
1398 * \param [in] data Pointer to the multi-attrib dialog
1399 * \param [in] where_the_object_was Pointer to where the object was just destroyed
1401 static void selection_weak_ref_cb (gpointer data, GObject *where_the_object_was)
1403 Multiattrib *multiattrib = (Multiattrib *)data;
1405 multiattrib->selection = NULL;
1406 multiattrib_update (multiattrib);
1410 /*! \brief Connect signal handler and weak_ref on the SELECTION object
1412 * \par Function Description
1414 * Connect the "changed" signal and add a weak reference
1415 * on the SELECTION object we are going to watch.
1417 * \param [in] multiattrib The Multiattrib dialog.
1418 * \param [in] selection The SELECTION object to watch.
1420 static void connect_selection( Multiattrib *multiattrib, SELECTION *selection )
1422 multiattrib->selection = selection;
1423 if (multiattrib->selection != NULL) {
1424 g_object_weak_ref (G_OBJECT (multiattrib->selection),
1425 selection_weak_ref_cb,
1426 multiattrib);
1427 multiattrib->selection_changed_id =
1428 g_signal_connect (G_OBJECT (multiattrib->selection),
1429 "changed",
1430 G_CALLBACK (selection_changed_cb),
1431 multiattrib);
1432 /* Synthesise a selection changed update to refresh the view */
1433 selection_changed_cb (multiattrib->selection, multiattrib);
1434 } else {
1435 /* Call an update to set the sensitivities */
1436 multiattrib_update (multiattrib);
1441 /*! \brief Disconnect signal handler and weak_ref on the SELECTION object
1443 * \par Function Description
1445 * If the dialog is watching a SELECTION object, disconnect the
1446 * "changed" signal and remove our weak reference on the object.
1448 * \param [in] multiattrib The Multiattrib dialog.
1450 static void disconnect_selection( Multiattrib *multiattrib )
1452 if (multiattrib->selection != NULL) {
1453 g_signal_handler_disconnect (multiattrib->selection,
1454 multiattrib->selection_changed_id);
1455 g_object_weak_unref(G_OBJECT( multiattrib->selection ),
1456 selection_weak_ref_cb,
1457 multiattrib );
1462 /*! \brief GObject finalise handler
1464 * \par Function Description
1466 * Just before the Multiattrib GObject is finalized, disconnect from
1467 * the SELECTION object being watched and then chain up to the parent
1468 * class's finalize handler.
1470 * \param [in] object The GObject being finalized.
1472 static void multiattrib_finalize (GObject *object)
1474 Multiattrib *multiattrib = MULTIATTRIB(object);
1476 disconnect_selection( multiattrib );
1477 G_OBJECT_CLASS (multiattrib_parent_class)->finalize (object);
1481 /*! \brief GType class initialiser for Multiattrib
1483 * \par Function Description
1485 * GType class initialiser for Multiattrib. We override our parent
1486 * virtual class methods as needed and register our GObject properties.
1488 * \param [in] klass The MultiattribClass we are initialising
1490 static void multiattrib_class_init(MultiattribClass *klass)
1492 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1494 multiattrib_parent_class = g_type_class_peek_parent (klass);
1496 gobject_class->set_property = multiattrib_set_property;
1497 gobject_class->get_property = multiattrib_get_property;
1498 gobject_class->finalize = multiattrib_finalize;
1500 g_object_class_install_property (
1501 gobject_class, PROP_SELECTION,
1502 g_param_spec_pointer ("selection",
1505 G_PARAM_READWRITE));
1509 /*! \brief GType instance initialiser for Multiattrib
1511 * \par Function Description
1513 * GType instance initialiser for Multiattrib. Create
1514 * and setup the widgets which make up the dialog.
1516 * \param [in] dialog The Multiattrib we are initialising
1518 static void multiattrib_init(Multiattrib *multiattrib)
1520 GtkWidget *frame, *label, *scrolled_win, *treeview;
1521 GtkWidget *table, *textview, *combo, *optionm, *button;
1522 GtkTreeModel *store;
1523 GtkCellRenderer *renderer;
1524 GtkTreeViewColumn *column;
1525 GtkTreeSelection *selection;
1526 GtkStyle *style;
1528 /* dialog initialization */
1529 g_object_set (G_OBJECT (multiattrib),
1530 /* GtkContainer */
1531 "border-width", 0,
1532 /* GtkWindow */
1533 "type", GTK_WINDOW_TOPLEVEL,
1534 "title", _("Edit Attributes"),
1535 "default-width", 320,
1536 "default-height", 350,
1537 "window-position", GTK_WIN_POS_MOUSE,
1538 "allow-grow", TRUE,
1539 "allow-shrink", FALSE,
1540 /* GtkDialog */
1541 "has-separator", TRUE,
1542 NULL);
1544 multiattrib->object = NULL;
1546 /* create the attribute list frame */
1547 frame = GTK_WIDGET (g_object_new (GTK_TYPE_FRAME,
1548 /* GtkFrame */
1549 "label", _("Attributes"),
1550 NULL));
1551 multiattrib->frame_add = frame;
1552 /* - create the model for the treeview */
1553 store = (GtkTreeModel*)gtk_list_store_new (NUM_COLUMNS,
1554 G_TYPE_POINTER); /* attribute */
1555 /* - create a scrolled window for the treeview */
1556 scrolled_win = GTK_WIDGET (
1557 g_object_new (GTK_TYPE_SCROLLED_WINDOW,
1558 /* GtkContainer */
1559 "border-width", 3,
1560 /* GtkScrolledWindow */
1561 "hscrollbar-policy",
1562 GTK_POLICY_AUTOMATIC,
1563 "vscrollbar-policy",
1564 GTK_POLICY_AUTOMATIC,
1565 "shadow-type",
1566 GTK_SHADOW_ETCHED_IN,
1567 NULL));
1568 /* - create the treeview */
1569 treeview = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
1570 /* GtkTreeView */
1571 "model", store,
1572 "rules-hint", TRUE,
1573 NULL));
1574 g_signal_connect (treeview,
1575 "key-press-event",
1576 G_CALLBACK (multiattrib_callback_key_pressed),
1577 multiattrib);
1578 g_signal_connect (treeview,
1579 "button-press-event",
1580 G_CALLBACK (multiattrib_callback_button_pressed),
1581 multiattrib);
1582 g_signal_connect (treeview,
1583 "popup-menu",
1584 G_CALLBACK (multiattrib_callback_popup_menu),
1585 multiattrib);
1586 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
1587 gtk_tree_selection_set_mode (selection,
1588 GTK_SELECTION_SINGLE);
1590 /* - and now the columns of the treeview */
1591 /* - column 1: attribute name */
1592 renderer = GTK_CELL_RENDERER (
1593 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
1594 /* GtkCellRendererText */
1595 "editable", TRUE,
1596 /* unknown in GTK 2.4 */
1597 /* "ellipsize",
1598 * PANGO_ELLIPSIZE_END, */
1599 NULL));
1600 g_signal_connect (renderer,
1601 "edited",
1602 G_CALLBACK (multiattrib_callback_edited_name),
1603 multiattrib);
1604 column = GTK_TREE_VIEW_COLUMN (
1605 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1606 /* GtkTreeViewColumn */
1607 "title", _("Name"),
1608 "min-width", 100,
1609 "resizable", TRUE,
1610 NULL));
1611 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1612 gtk_tree_view_column_set_cell_data_func (column, renderer,
1613 multiattrib_column_set_data_name,
1614 NULL, NULL);
1615 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1616 /* - column 2: attribute value */
1617 renderer = GTK_CELL_RENDERER (
1618 g_object_new (TYPE_CELL_RENDERER_MULTI_LINE_TEXT,
1619 /* GtkCellRendererText */
1620 "editable", TRUE,
1621 /* unknown in GTK 2.4 */
1622 /* "ellipsize",
1623 PANGO_ELLIPSIZE_END, */
1624 NULL));
1625 g_signal_connect (renderer,
1626 "edited",
1627 G_CALLBACK (multiattrib_callback_edited_value),
1628 multiattrib);
1629 column = GTK_TREE_VIEW_COLUMN (
1630 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1631 /* GtkTreeViewColumn */
1632 "title", _("Value"),
1633 "min-width", 140,
1634 "resizable", TRUE,
1635 NULL));
1636 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1637 gtk_tree_view_column_set_cell_data_func (column, renderer,
1638 multiattrib_column_set_data_value,
1639 NULL, NULL);
1640 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1641 /* - column 3: visibility */
1642 renderer = GTK_CELL_RENDERER (
1643 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
1644 /* GtkCellRendererToggle */
1645 "activatable", TRUE,
1646 NULL));
1647 g_signal_connect (renderer,
1648 "toggled",
1649 G_CALLBACK (multiattrib_callback_toggled_visible),
1650 multiattrib);
1651 column = GTK_TREE_VIEW_COLUMN (
1652 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1653 /* GtkTreeViewColumn */
1654 "title", _("Vis?"),
1655 NULL));
1656 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1657 gtk_tree_view_column_set_cell_data_func (column, renderer,
1658 multiattrib_column_set_data_visible,
1659 NULL, NULL);
1660 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1661 /* - column 4: show name */
1662 renderer = GTK_CELL_RENDERER (
1663 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
1664 /* GtkCellRendererToggle */
1665 "activatable", TRUE,
1666 NULL));
1667 g_signal_connect (renderer,
1668 "toggled",
1669 G_CALLBACK (multiattrib_callback_toggled_show_name),
1670 multiattrib);
1671 column = GTK_TREE_VIEW_COLUMN (
1672 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1673 /* GtkTreeViewColumn */
1674 "title", _("N"),
1675 NULL));
1676 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1677 gtk_tree_view_column_set_cell_data_func (column, renderer,
1678 multiattrib_column_set_data_show_name,
1679 NULL, NULL);
1680 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1681 /* - column 5: show value */
1682 renderer = GTK_CELL_RENDERER (
1683 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
1684 /* GtkCellRendererToggle */
1685 "activatable", TRUE,
1686 NULL));
1687 g_signal_connect (renderer,
1688 "toggled",
1689 G_CALLBACK (multiattrib_callback_toggled_show_value),
1690 multiattrib);
1691 column = GTK_TREE_VIEW_COLUMN (
1692 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
1693 /* GtkTreeViewColumn */
1694 "title", _("V"),
1695 NULL));
1696 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1697 gtk_tree_view_column_set_cell_data_func (column, renderer,
1698 multiattrib_column_set_data_show_value,
1699 NULL, NULL);
1700 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1702 /* add the treeview to the scrolled window */
1703 gtk_container_add (GTK_CONTAINER (scrolled_win), treeview);
1704 /* set treeview of multiattrib */
1705 multiattrib->treeview = GTK_TREE_VIEW (treeview);
1706 /* add the scrolled window to frame */
1707 gtk_container_add (GTK_CONTAINER (frame), scrolled_win);
1708 /* pack the frame */
1709 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (multiattrib)->vbox), frame,
1710 TRUE, TRUE, 1);
1711 gtk_widget_show_all (frame);
1713 /* create the add/edit frame */
1714 frame = GTK_WIDGET (g_object_new (GTK_TYPE_FRAME,
1715 "label", _("Add Attribute"),
1716 NULL));
1717 multiattrib->frame_attributes = frame;
1718 table = GTK_WIDGET (g_object_new (GTK_TYPE_TABLE,
1719 /* GtkTable */
1720 "n-rows", 4,
1721 "n-columns", 2,
1722 "homogeneous", FALSE,
1723 NULL));
1725 /* - the name entry: a GtkComboBoxEntry */
1726 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL,
1727 /* GtkMisc */
1728 "xalign", 0.0,
1729 "yalign", 0.5,
1730 /* GtkLabel */
1731 "label", _("Name:"),
1732 NULL));
1733 combo = GTK_WIDGET (g_object_new (GTK_TYPE_COMBO,
1734 /* GtkCombo */
1735 "value-in-list", FALSE,
1736 NULL));
1737 multiattrib_init_attrib_names (GTK_COMBO (combo));
1738 multiattrib->combo_name = GTK_COMBO (combo);
1739 gtk_table_attach (GTK_TABLE (table), label,
1740 0, 1, 0, 1, 0, 0, 0, 0);
1741 gtk_table_attach (GTK_TABLE (table), combo,
1742 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 6, 3);
1744 /* - the value entry: a GtkEntry */
1745 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL,
1746 /* GtkMisc */
1747 "xalign", 0.0,
1748 "yalign", 0.5,
1749 /* GtkLabel */
1750 "label", _("Value:"),
1751 NULL));
1752 scrolled_win = GTK_WIDGET (
1753 g_object_new (GTK_TYPE_SCROLLED_WINDOW,
1754 /* GtkScrolledWindow */
1755 "hscrollbar-policy",
1756 GTK_POLICY_NEVER,
1757 "vscrollbar-policy",
1758 GTK_POLICY_AUTOMATIC,
1759 "shadow-type",
1760 GTK_SHADOW_IN,
1761 NULL));
1762 textview = GTK_WIDGET (g_object_new (GTK_TYPE_TEXT_VIEW,
1763 NULL));
1764 g_signal_connect (textview,
1765 "key_press_event",
1766 G_CALLBACK (multiattrib_callback_value_key_pressed),
1767 multiattrib);
1768 g_signal_connect (textview,
1769 "grab-focus",
1770 G_CALLBACK (multiattrib_callback_value_grab_focus),
1771 multiattrib);
1772 /* Save the GTK_STATE_NORMAL color so we can work around GtkTextView's
1773 * stubborn refusal to draw with GTK_STATE_INSENSITIVE later on */
1774 style = gtk_widget_get_style (textview);
1775 multiattrib->value_normal_text_color = style->text[ GTK_STATE_NORMAL ];
1777 gtk_container_add (GTK_CONTAINER (scrolled_win), textview);
1778 multiattrib->textview_value = GTK_TEXT_VIEW (textview);
1779 gtk_table_attach (GTK_TABLE (table), label,
1780 0, 1, 1, 2, 0, 0, 0, 0);
1781 gtk_table_attach (GTK_TABLE (table), scrolled_win,
1782 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 6, 3);
1784 /* - the visible status */
1785 button = GTK_WIDGET (g_object_new (GTK_TYPE_CHECK_BUTTON,
1786 /* GtkButton */
1787 "label", _("Visible"),
1788 "active", TRUE,
1789 NULL));
1790 multiattrib->button_visible = GTK_CHECK_BUTTON (button);
1791 gtk_table_attach (GTK_TABLE (table), button,
1792 0, 1, 2, 3, GTK_FILL, 0, 3, 0);
1794 /* - the visibility type */
1795 optionm = GTK_WIDGET (g_object_new (GTK_TYPE_OPTION_MENU,
1796 NULL));
1797 multiattrib_init_visible_types (GTK_OPTION_MENU (optionm));
1798 multiattrib->optionmenu_shownv = GTK_OPTION_MENU (optionm);
1799 gtk_table_attach (GTK_TABLE (table), optionm,
1800 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 6, 3);
1801 gtk_widget_show_all (table);
1803 /* create the add button */
1804 button = gtk_button_new_from_stock (GTK_STOCK_ADD);
1805 g_signal_connect (button,
1806 "clicked",
1807 G_CALLBACK (multiattrib_callback_button_add),
1808 multiattrib);
1809 gtk_table_attach (GTK_TABLE (table), button,
1810 2, 3, 0, 3, 0, 0, 6, 3);
1812 /* add the table to the frame */
1813 gtk_container_add (GTK_CONTAINER (frame), table);
1814 /* pack the frame in the dialog */
1815 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (multiattrib)->vbox), frame,
1816 FALSE, TRUE, 4);
1817 gtk_widget_show_all (frame);
1820 /* now add the close button to the action area */
1821 gtk_dialog_add_button (GTK_DIALOG (multiattrib),
1822 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
1827 /*! \brief GObject property setter function
1829 * \par Function Description
1830 * Setter function for Multiattrib's GObject property, "selection".
1832 * \param [in] object The GObject whose properties we are setting
1833 * \param [in] property_id The numeric id. under which the property was
1834 * registered with g_object_class_install_property()
1835 * \param [in] value The GValue the property is being set from
1836 * \param [in] pspec A GParamSpec describing the property being set
1839 static void multiattrib_set_property (GObject *object,
1840 guint property_id,
1841 const GValue *value,
1842 GParamSpec *pspec)
1844 Multiattrib *multiattrib = MULTIATTRIB (object);
1846 switch(property_id) {
1847 case PROP_SELECTION:
1848 disconnect_selection (multiattrib);
1849 connect_selection (multiattrib, g_value_get_pointer (value));
1850 break;
1851 default:
1852 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1857 /*! \brief GObject property getter function
1859 * \par Function Description
1860 * Getter function for Multiattrib's GObject property, "selection".
1862 * \param [in] object The GObject whose properties we are getting
1863 * \param [in] property_id The numeric id. under which the property was
1864 * registered with g_object_class_install_property()
1865 * \param [out] value The GValue in which to return the value of the property
1866 * \param [in] pspec A GParamSpec describing the property being got
1868 static void multiattrib_get_property (GObject *object,
1869 guint property_id,
1870 GValue *value,
1871 GParamSpec *pspec)
1873 Multiattrib *multiattrib = MULTIATTRIB (object);
1875 switch(property_id) {
1876 case PROP_SELECTION:
1877 g_value_set_pointer (value, (gpointer)multiattrib->selection);
1878 break;
1879 default:
1880 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1886 /*! \brief Update the multiattrib editor dialog's interface
1888 * \par Function Description
1890 * Update the dialog to reflect the attributes of the currently selected
1891 * object. If no (or multiple) objects are selected, the dialog's controls
1892 * are set insensitive.
1894 * \todo The dialog doesn't currently support editing multiple objects at once
1896 * \param [in] multiattrib The multi-attribute editor dialog.
1898 void multiattrib_update (Multiattrib *multiattrib)
1900 GtkListStore *liststore;
1901 GtkTreeIter iter;
1902 OBJECT **object_attribs, *o_current;
1903 gint i;
1904 gboolean sensitive;
1905 GtkStyle *style;
1907 g_assert (GSCHEM_DIALOG (multiattrib)->toplevel != NULL);
1909 /* clear the list of attributes */
1910 liststore = (GtkListStore*)gtk_tree_view_get_model (multiattrib->treeview);
1911 gtk_list_store_clear (liststore);
1913 /* Update sensitivities */
1914 sensitive = (multiattrib->selection != NULL && multiattrib->object != NULL);
1915 gtk_widget_set_sensitive (GTK_WIDGET (multiattrib->frame_attributes), sensitive);
1916 gtk_widget_set_sensitive (GTK_WIDGET (multiattrib->frame_add), sensitive);
1918 /* Work around GtkTextView's stubborn indifference
1919 * to GTK_STATE_INSENSITIVE when rendering its text. */
1920 style = gtk_widget_get_style (GTK_WIDGET (multiattrib->textview_value));
1921 gtk_widget_modify_text (GTK_WIDGET (multiattrib->textview_value),
1922 GTK_STATE_NORMAL,
1923 sensitive ? &multiattrib->value_normal_text_color
1924 : &style->text[GTK_STATE_INSENSITIVE]);
1926 /* If we aren't sensitive, there is nothing more to do */
1927 if (!sensitive)
1928 return;
1930 /* get list of attributes */
1931 object_attribs = o_attrib_return_attribs (
1932 GSCHEM_DIALOG (multiattrib)->toplevel->page_current->object_head,
1933 multiattrib->object);
1934 /* populate the store with attributes */
1935 if (object_attribs) {
1936 for (i = 0, o_current = object_attribs[i];
1937 o_current != NULL;
1938 i++, o_current = object_attribs[i]) {
1939 gtk_list_store_append (liststore, &iter);
1940 gtk_list_store_set (liststore, &iter,
1941 COLUMN_ATTRIBUTE, o_current,
1942 -1);
1945 /* delete the list of attribute objects */
1946 o_attrib_free_returned (object_attribs);