gschem
|
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