gschem

x_pagesel.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-2010 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 
00032 #ifdef HAVE_LIBDMALLOC
00033 #include <dmalloc.h>
00034 #endif
00035 
00036 
00037 static void x_pagesel_callback_response (GtkDialog *dialog,
00038                                          gint arg1,
00039                                          gpointer user_data);
00040 
00041 
00042 
00050 void x_pagesel_open (GSCHEM_TOPLEVEL *w_current)
00051 {
00052   if (w_current->pswindow == NULL) {
00053     w_current->pswindow = GTK_WIDGET (g_object_new (TYPE_PAGESEL,
00054                                                     /* GschemDialog */
00055                                                     "settings-name", "pagesel",
00056                                                     "gschem-toplevel", w_current,
00057                                                     NULL));
00058 
00059     g_signal_connect (w_current->pswindow,
00060                       "response",
00061                       G_CALLBACK (x_pagesel_callback_response),
00062                       w_current);
00063 
00064     gtk_widget_show (w_current->pswindow);
00065   } else {
00066     gdk_window_raise (w_current->pswindow->window);
00067   }
00068 
00069 }
00070 
00077 void x_pagesel_close (GSCHEM_TOPLEVEL *w_current)
00078 {
00079   if (w_current->pswindow) {
00080     g_assert (IS_PAGESEL (w_current->pswindow));
00081     gtk_widget_destroy (w_current->pswindow);
00082     w_current->pswindow = NULL;
00083   }
00084   
00085 }
00086 
00094 void x_pagesel_update (GSCHEM_TOPLEVEL *w_current)
00095 {
00096   if (w_current->pswindow) {
00097     g_assert (IS_PAGESEL (w_current->pswindow));
00098     pagesel_update (PAGESEL (w_current->pswindow));
00099   }
00100 }
00101 
00110 static void x_pagesel_callback_response (GtkDialog *dialog,
00111                      gint arg1,
00112                      gpointer user_data)
00113 {
00114   GSCHEM_TOPLEVEL *w_current = (GSCHEM_TOPLEVEL*)user_data;
00115 
00116   switch (arg1) {
00117       case PAGESEL_RESPONSE_UPDATE:
00118         pagesel_update (PAGESEL (dialog));
00119         break;
00120       case GTK_RESPONSE_DELETE_EVENT:
00121       case PAGESEL_RESPONSE_CLOSE:
00122         g_assert (GTK_WIDGET (dialog) == w_current->pswindow);
00123         gtk_widget_destroy (GTK_WIDGET (dialog));
00124         w_current->pswindow = NULL;
00125         break;
00126       default:
00127         g_assert_not_reached ();
00128   }
00129   
00130 }
00131 
00132 enum {
00133   COLUMN_PAGE,
00134   COLUMN_NAME,
00135   COLUMN_CHANGED,
00136   NUM_COLUMNS
00137 };
00138 
00139 
00140 static void pagesel_class_init (PageselClass *class);
00141 static void pagesel_init       (Pagesel *pagesel);
00142 
00143 static void pagesel_popup_menu (Pagesel *pagesel,
00144                                 GdkEventButton *event);
00145 
00151 static void pagesel_callback_selection_changed (GtkTreeSelection *selection,
00152                         gpointer user_data)
00153 {
00154   GtkTreeModel *model;
00155   GtkTreeIter iter;
00156   Pagesel *pagesel = (Pagesel*)user_data;
00157   GSCHEM_TOPLEVEL *w_current;
00158   PAGE *page;
00159 
00160   if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
00161     return;
00162   }
00163 
00164   w_current = GSCHEM_DIALOG (pagesel)->w_current;
00165   gtk_tree_model_get (model, &iter,
00166                       COLUMN_PAGE, &page,
00167                       -1);
00168 
00169   /* Since setting the current page may call x_pagesel_update(), which
00170    * might change the current page selection, make sure we do nothing
00171    * if the newly-selected page is already the current page. */
00172   if (page == w_current->toplevel->page_current) return;
00173   x_window_set_current_page (w_current, page);
00174 }
00175 
00181 static gboolean pagesel_callback_button_pressed (GtkWidget *widget,
00182                          GdkEventButton *event,
00183                          gpointer user_data)
00184 {
00185   Pagesel *pagesel = (Pagesel*)user_data;
00186   gboolean ret = FALSE;
00187 
00188   if (event->type == GDK_BUTTON_PRESS  &&  event->button == 3) {
00189     pagesel_popup_menu (pagesel, event);
00190     ret = TRUE;
00191   }
00192 
00193   return ret;
00194 }
00195 
00201 static gboolean pagesel_callback_popup_menu (GtkWidget *widget,
00202                          gpointer user_data)
00203 {
00204   Pagesel *pagesel = (Pagesel*)user_data;
00205 
00206   pagesel_popup_menu (pagesel, NULL);
00207   
00208   return TRUE;
00209 }
00210 
00211 #define DEFINE_POPUP_CALLBACK(name, action)                       \
00212 static void                                                       \
00213 pagesel_callback_popup_ ## name (GtkMenuItem *menuitem,           \
00214                                  gpointer user_data)              \
00215 {                                                                 \
00216   i_callback_ ## action (GSCHEM_DIALOG (user_data)->w_current, 0, NULL); \
00217 }
00218 
00219 DEFINE_POPUP_CALLBACK (new_page,     file_new)
00220 DEFINE_POPUP_CALLBACK (open_page,    file_open)
00221 DEFINE_POPUP_CALLBACK (save_page,    file_save)
00222 DEFINE_POPUP_CALLBACK (close_page,   page_close)
00223 DEFINE_POPUP_CALLBACK (discard_page, page_discard)
00224 
00225 
00236 static void pagesel_popup_menu (Pagesel *pagesel,
00237                 GdkEventButton *event)
00238 {
00239   GtkTreePath *path;
00240   GtkWidget *menu;
00241   struct menuitem_t {
00242     gchar *label;
00243     GCallback callback;
00244   };
00245   struct menuitem_t menuitems[] = {
00246     { N_("New Page"),     G_CALLBACK (pagesel_callback_popup_new_page)     },
00247     { N_("Open Page..."), G_CALLBACK (pagesel_callback_popup_open_page)    },
00248     { "-",                NULL                                             },
00249     { N_("Save Page"),    G_CALLBACK (pagesel_callback_popup_save_page)    },
00250     { N_("Close Page"),   G_CALLBACK (pagesel_callback_popup_close_page)   },
00251     { N_("Discard Page"), G_CALLBACK (pagesel_callback_popup_discard_page) },
00252     { NULL,               NULL                                             } };
00253   struct menuitem_t *tmp;
00254   
00255   if (event != NULL &&
00256       gtk_tree_view_get_path_at_pos (pagesel->treeview,
00257                                      (gint)event->x, 
00258                                      (gint)event->y,
00259                                      &path, NULL, NULL, NULL)) {
00260     GtkTreeSelection *selection;
00261     selection = gtk_tree_view_get_selection (pagesel->treeview);
00262     gtk_tree_selection_unselect_all (selection);
00263     gtk_tree_selection_select_path (selection, path);
00264     gtk_tree_path_free (path);
00265   }
00266 
00267   /* create the context menu */
00268   menu = gtk_menu_new();
00269   for (tmp = menuitems; tmp->label != NULL; tmp++) {
00270     GtkWidget *menuitem;
00271     if (g_utf8_collate (tmp->label, "-") == 0) {
00272       menuitem = gtk_separator_menu_item_new ();
00273     } else {
00274       menuitem = gtk_menu_item_new_with_label (_(tmp->label));
00275       g_signal_connect (menuitem,
00276                         "activate",
00277                         tmp->callback,
00278                         pagesel);
00279     }
00280     gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
00281   }
00282   gtk_widget_show_all (menu);
00283   /* make menu a popup menu */
00284   gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
00285                   (event != NULL) ? event->button : 0,
00286                   gdk_event_get_time ((GdkEvent*)event));
00287   
00288 }
00289 
00290 
00302 static void notify_gschem_toplevel_cb (GObject    *gobject,
00303                                        GParamSpec *arg1,
00304                                        gpointer    user_data)
00305 {
00306   Pagesel *pagesel = PAGESEL( gobject );
00307 
00308   pagesel_update( pagesel );
00309 }
00310 
00311 
00317 GType pagesel_get_type()
00318 {
00319   static GType pagesel_type = 0;
00320   
00321   if (!pagesel_type) {
00322     static const GTypeInfo pagesel_info = {
00323       sizeof(PageselClass),
00324       NULL, /* base_init */
00325       NULL, /* base_finalize */
00326       (GClassInitFunc) pagesel_class_init,
00327       NULL, /* class_finalize */
00328       NULL, /* class_data */
00329       sizeof(Pagesel),
00330       0,    /* n_preallocs */
00331       (GInstanceInitFunc) pagesel_init,
00332     };
00333         
00334     pagesel_type = g_type_register_static (GSCHEM_TYPE_DIALOG,
00335                                            "Pagesel",
00336                                            &pagesel_info, 0);
00337   }
00338   
00339   return pagesel_type;
00340 }
00341 
00347 static void pagesel_class_init (PageselClass *klass)
00348 {
00349 }
00350 
00356 static void pagesel_init (Pagesel *pagesel)
00357 {
00358   GtkWidget *scrolled_win, *treeview, *label;
00359   GtkTreeModel *store;
00360   GtkCellRenderer *renderer;
00361   GtkTreeViewColumn *column;
00362   GtkTreeSelection *selection;
00363 
00364   /* dialog initialization */
00365   g_object_set (G_OBJECT (pagesel),
00366                 /* GtkContainer */
00367                 "border-width",    0,
00368                 /* GtkWindow */
00369                 "title",           _("Page Manager"),
00370                 "default-height",  180,
00371                 "default-width",   515,
00372                 "modal",           FALSE,
00373                 "window-position", GTK_WIN_POS_NONE,
00374                 "type-hint",       GDK_WINDOW_TYPE_HINT_NORMAL,
00375                 /* GtkDialog */
00376                 "has-separator",   TRUE,
00377                 NULL);
00378 
00379   /* create the model for the treeview */
00380   store = (GtkTreeModel*)gtk_tree_store_new (NUM_COLUMNS,
00381                                              G_TYPE_POINTER,  /* page */
00382                                              G_TYPE_STRING,   /* name */
00383                                              G_TYPE_BOOLEAN); /* changed */
00384 
00385   /* create a scrolled window for the treeview */
00386   scrolled_win = GTK_WIDGET (
00387     g_object_new (GTK_TYPE_SCROLLED_WINDOW,
00388                   /* GtkContainer */
00389                   "border-width",      5,
00390                   /* GtkScrolledWindow */
00391                   "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
00392                   "vscrollbar-policy", GTK_POLICY_ALWAYS,
00393                   "shadow-type",       GTK_SHADOW_ETCHED_IN,
00394                   NULL));
00395   /* create the treeview */
00396   treeview = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
00397                                        /* GtkTreeView */
00398                                        "model",      store,
00399                                        "rules-hint", TRUE,
00400                                        NULL));
00401   g_signal_connect (treeview,
00402                     "button-press-event",
00403                     G_CALLBACK (pagesel_callback_button_pressed),
00404                     pagesel);
00405   g_signal_connect (treeview,
00406                     "popup-menu",
00407                     G_CALLBACK (pagesel_callback_popup_menu),
00408                     pagesel);
00409   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
00410   gtk_tree_selection_set_mode (selection,
00411                                GTK_SELECTION_SINGLE);
00412   g_signal_connect (selection,
00413                     "changed",
00414                     G_CALLBACK (pagesel_callback_selection_changed),
00415                     pagesel); 
00416   /*   - first column: page name */
00417   renderer = GTK_CELL_RENDERER (
00418     g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
00419                   /* GtkCellRendererText */
00420                   "editable", FALSE,
00421                   NULL));
00422   column = GTK_TREE_VIEW_COLUMN (
00423     g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
00424                   /* GtkTreeViewColumn */
00425                   "title", _("Filename"),
00426                   "min-width", 400,
00427                   "resizable", TRUE,
00428                   NULL));
00429   gtk_tree_view_column_pack_start (column, renderer, TRUE);
00430   gtk_tree_view_column_add_attribute (column, renderer, "text", COLUMN_NAME);
00431   gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
00432   /*   - second column: changed */
00433   renderer = GTK_CELL_RENDERER (
00434     g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
00435                   /* GtkCellRendererToggle */
00436                   "activatable", FALSE,
00437                   NULL));
00438   column = GTK_TREE_VIEW_COLUMN (
00439     g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
00440                   /* GtkTreeViewColumn */
00441                   "title", _("Changed"),
00442                   "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
00443                   NULL));
00444   gtk_tree_view_column_pack_start (column, renderer, TRUE);
00445   gtk_tree_view_column_add_attribute (column, renderer, "active", COLUMN_CHANGED);
00446   gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
00447       
00448   /* add the treeview to the scrolled window */
00449   gtk_container_add (GTK_CONTAINER (scrolled_win), treeview);
00450   /* set treeview of pagesel */
00451   pagesel->treeview = GTK_TREE_VIEW (treeview);
00452 
00453   /* add the scrolled window to the dialog vbox */
00454   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel)->vbox), scrolled_win,
00455                       TRUE, TRUE, 0);
00456   gtk_widget_show_all (scrolled_win);
00457 
00458   /* add a label below the scrolled window */
00459   label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL,
00460                                     /* GtkLabel */
00461                                     "label", _("Right click on the filename for more options..."),
00462                                     NULL));
00463   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel)->vbox), label,
00464                       FALSE, TRUE, 5);
00465   gtk_widget_show (label);
00466 
00467   /* now add buttons in the action area */
00468   gtk_dialog_add_buttons (GTK_DIALOG (pagesel),
00469                           /*  - update button */
00470                           GTK_STOCK_REFRESH, PAGESEL_RESPONSE_UPDATE,
00471                           /*  - close button */
00472                           GTK_STOCK_CLOSE,   PAGESEL_RESPONSE_CLOSE,
00473                           NULL);
00474 
00475   /* Set the alternative button order (ok, cancel, help) for other systems */
00476   gtk_dialog_set_alternative_button_order(GTK_DIALOG(pagesel),
00477                       PAGESEL_RESPONSE_UPDATE,
00478                       PAGESEL_RESPONSE_CLOSE,
00479                       -1);
00480 
00481   g_signal_connect( pagesel, "notify::gschem-toplevel",
00482                     G_CALLBACK( notify_gschem_toplevel_cb ), NULL );
00483 }
00484 
00485 
00499 static void add_page (GtkTreeModel *model, GtkTreeIter *parent,
00500                       GedaPageList *pages, PAGE *page)
00501 {
00502   GtkTreeIter iter;
00503   PAGE *p_current;
00504   GList *p_iter;
00505 
00506   /* add the page to the store */
00507   gtk_tree_store_append (GTK_TREE_STORE (model),
00508                          &iter,
00509                          parent);
00510   gtk_tree_store_set (GTK_TREE_STORE (model),
00511                       &iter,
00512                       COLUMN_PAGE, page,
00513                       COLUMN_NAME, page->page_filename,
00514                       COLUMN_CHANGED, page->CHANGED,
00515                       -1);
00516 
00517   /* search a page that has a up field == p_current->pid */
00518   for ( p_iter = geda_list_get_glist( pages );
00519         p_iter != NULL;
00520         p_iter = g_list_next( p_iter ) ) {
00521 
00522     p_current = (PAGE *)p_iter->data;
00523     if (p_current->up == page->pid) {
00524       add_page (model, &iter, pages, p_current);
00525     }
00526   }
00527 }
00528 
00535 static void select_page(GtkTreeView *treeview,
00536             GtkTreeIter *parent, PAGE *page)
00537 {
00538   GtkTreeModel *treemodel = gtk_tree_view_get_model (treeview);
00539   GtkTreeIter iter;
00540   PAGE *p_current;
00541 
00542   if (!gtk_tree_model_iter_children (treemodel, &iter, parent)) {
00543     return;
00544   }
00545 
00546   do {
00547     gtk_tree_model_get (treemodel, &iter,
00548                         COLUMN_PAGE, &p_current,
00549                         -1);
00550     if (p_current == page) {
00551       gtk_tree_view_expand_all (treeview);
00552       gtk_tree_selection_select_iter (
00553         gtk_tree_view_get_selection (treeview),
00554         &iter);
00555       return;
00556     }
00557 
00558     select_page (treeview, &iter, page);
00559     
00560   } while (gtk_tree_model_iter_next (treemodel, &iter));
00561   
00562 }
00563 
00569 void pagesel_update (Pagesel *pagesel)
00570 {
00571   GtkTreeModel *model;
00572   TOPLEVEL *toplevel;
00573   PAGE *p_current;
00574   GList *iter;
00575 
00576   g_assert (IS_PAGESEL (pagesel));
00577 
00578   g_return_if_fail (GSCHEM_DIALOG (pagesel)->w_current);
00579 
00580   toplevel = GSCHEM_DIALOG (pagesel)->w_current->toplevel;
00581   model    = gtk_tree_view_get_model (pagesel->treeview);
00582 
00583   /* wipe out every thing in the store */
00584   gtk_tree_store_clear (GTK_TREE_STORE (model));
00585   /* now rebuild */
00586   for ( iter = geda_list_get_glist( toplevel->pages );
00587         iter != NULL;
00588         iter = g_list_next( iter ) ) {
00589 
00590     p_current = (PAGE *)iter->data;
00591     /* find every page that is not a hierarchy-down of another page */
00592     if (p_current->up < 0 ||
00593         s_page_search_by_page_id (toplevel->pages,
00594                                   p_current->up) == NULL) {
00595       add_page (model, NULL, toplevel->pages, p_current);
00596     }
00597   }
00598 
00599   /* select the current page in the treeview */
00600   select_page (pagesel->treeview, NULL, toplevel->page_current);  
00601 }
00602 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines