gschem

x_multiattrib.c

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