refdes_renum: warn of possible number clash with non-conforming values
[geda-gaf/whiteaudio.git] / gschem / src / x_pagesel.c
blob24f59fb7b5d94362451bf3d54694dd3bcf3482ea
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <config.h>
22 #include <stdio.h>
23 #ifdef HAVE_STDLIB_H
24 #include <stdlib.h>
25 #endif
26 #ifdef HAVE_STRING_H
27 #include <string.h>
28 #endif
30 #include "gschem.h"
32 #ifdef HAVE_LIBDMALLOC
33 #include <dmalloc.h>
34 #endif
37 static void x_pagesel_callback_response (GtkDialog *dialog,
38 gint arg1,
39 gpointer user_data);
43 /*! \brief Open the page manager dialog.
44 * \par Function Description
45 * Opens the page manager dialog for <B>toplevel</B> if it is not already.
46 * In this last case, it raises the dialog.
48 * \param [in] w_current The GSCHEM_TOPLEVEL object to open page manager for.
50 void x_pagesel_open (GSCHEM_TOPLEVEL *w_current)
52 if (w_current->pswindow == NULL) {
53 w_current->pswindow = GTK_WIDGET (g_object_new (TYPE_PAGESEL,
54 /* GschemDialog */
55 "settings-name", "pagesel",
56 "gschem-toplevel", w_current,
57 NULL));
59 g_signal_connect (w_current->pswindow,
60 "response",
61 G_CALLBACK (x_pagesel_callback_response),
62 w_current);
64 gtk_widget_show (w_current->pswindow);
65 } else {
66 gdk_window_raise (w_current->pswindow->window);
71 /*! \brief Close the page manager dialog.
72 * \par Function Description
73 * Closes the page manager dialog associated with <B>toplevel</B>.
75 * \param [in] w_current The GSCHEM_TOPLEVEL object to close page manager for.
77 void x_pagesel_close (GSCHEM_TOPLEVEL *w_current)
79 if (w_current->pswindow) {
80 g_assert (IS_PAGESEL (w_current->pswindow));
81 gtk_widget_destroy (w_current->pswindow);
82 w_current->pswindow = NULL;
87 /*! \brief Update the list and status of <B>toplevel</B>'s pages.
88 * \par Function Description
89 * Updates the list and status of <B>toplevel</B>\'s pages if the page
90 * manager dialog is opened.
92 * \param [in] w_current The GSCHEM_TOPLEVEL object to update.
94 void x_pagesel_update (GSCHEM_TOPLEVEL *w_current)
96 if (w_current->pswindow) {
97 g_assert (IS_PAGESEL (w_current->pswindow));
98 pagesel_update (PAGESEL (w_current->pswindow));
102 /*! \brief Callback for page manager response.
103 * \par Function Description
104 * Handles response <B>arg1</B> of the page manager dialog <B>dialog</B>.
106 * \param [in] dialog GtkDialog that issues callback.
107 * \param [in] arg1 Response argument of page manager dialog.
108 * \param [in] user_data Pointer to relevant GSCHEM_TOPLEVEL structure.
110 static void x_pagesel_callback_response (GtkDialog *dialog,
111 gint arg1,
112 gpointer user_data)
114 GSCHEM_TOPLEVEL *w_current = (GSCHEM_TOPLEVEL*)user_data;
116 switch (arg1) {
117 case PAGESEL_RESPONSE_UPDATE:
118 pagesel_update (PAGESEL (dialog));
119 break;
120 case GTK_RESPONSE_DELETE_EVENT:
121 case PAGESEL_RESPONSE_CLOSE:
122 g_assert (GTK_WIDGET (dialog) == w_current->pswindow);
123 gtk_widget_destroy (GTK_WIDGET (dialog));
124 w_current->pswindow = NULL;
125 break;
126 default:
127 g_assert_not_reached ();
132 enum {
133 COLUMN_PAGE,
134 COLUMN_NAME,
135 COLUMN_CHANGED,
136 NUM_COLUMNS
140 static void pagesel_class_init (PageselClass *class);
141 static void pagesel_init (Pagesel *pagesel);
143 static void pagesel_popup_menu (Pagesel *pagesel,
144 GdkEventButton *event);
146 /*! \todo Finish function documentation!!!
147 * \brief
148 * \par Function Description
151 static void pagesel_callback_selection_changed (GtkTreeSelection *selection,
152 gpointer user_data)
154 GtkTreeModel *model;
155 GtkTreeIter iter;
156 Pagesel *pagesel = (Pagesel*)user_data;
157 GSCHEM_TOPLEVEL *w_current;
158 PAGE *page;
160 if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
161 return;
164 w_current = GSCHEM_DIALOG (pagesel)->w_current;
165 gtk_tree_model_get (model, &iter,
166 COLUMN_PAGE, &page,
167 -1);
169 /* Since setting the current page may call x_pagesel_update(), which
170 * might change the current page selection, make sure we do nothing
171 * if the newly-selected page is already the current page. */
172 if (page == w_current->toplevel->page_current) return;
173 x_window_set_current_page (w_current, page);
176 /*! \todo Finish function documentation!!!
177 * \brief
178 * \par Function Description
181 static gboolean pagesel_callback_button_pressed (GtkWidget *widget,
182 GdkEventButton *event,
183 gpointer user_data)
185 Pagesel *pagesel = (Pagesel*)user_data;
186 gboolean ret = FALSE;
188 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
189 pagesel_popup_menu (pagesel, event);
190 ret = TRUE;
193 return ret;
196 /*! \todo Finish function documentation!!!
197 * \brief
198 * \par Function Description
201 static gboolean pagesel_callback_popup_menu (GtkWidget *widget,
202 gpointer user_data)
204 Pagesel *pagesel = (Pagesel*)user_data;
206 pagesel_popup_menu (pagesel, NULL);
208 return TRUE;
211 #define DEFINE_POPUP_CALLBACK(name, action) \
212 static void \
213 pagesel_callback_popup_ ## name (GtkMenuItem *menuitem, \
214 gpointer user_data) \
216 i_callback_ ## action (GSCHEM_DIALOG (user_data)->w_current, 0, NULL); \
219 DEFINE_POPUP_CALLBACK (new_page, file_new)
220 DEFINE_POPUP_CALLBACK (open_page, file_open)
221 DEFINE_POPUP_CALLBACK (save_page, file_save)
222 DEFINE_POPUP_CALLBACK (close_page, page_close)
223 DEFINE_POPUP_CALLBACK (discard_page, page_discard)
226 /*! \brief Popup context-sensitive menu.
227 * \par Function Description
228 * Pops up a context-sensitive menu.
230 * <B>event</B> can be NULL if the popup is triggered by a key binding
231 * instead of a mouse click.
233 * \param [in] pagesel The Pagesel object.
234 * \param [in] event Mouse click event info.
236 static void pagesel_popup_menu (Pagesel *pagesel,
237 GdkEventButton *event)
239 GtkTreePath *path;
240 GtkWidget *menu;
241 struct menuitem_t {
242 gchar *label;
243 GCallback callback;
245 struct menuitem_t menuitems[] = {
246 { N_("New Page"), G_CALLBACK (pagesel_callback_popup_new_page) },
247 { N_("Open Page..."), G_CALLBACK (pagesel_callback_popup_open_page) },
248 { "-", NULL },
249 { N_("Save Page"), G_CALLBACK (pagesel_callback_popup_save_page) },
250 { N_("Close Page"), G_CALLBACK (pagesel_callback_popup_close_page) },
251 { N_("Discard Page"), G_CALLBACK (pagesel_callback_popup_discard_page) },
252 { NULL, NULL } };
253 struct menuitem_t *tmp;
255 if (event != NULL &&
256 gtk_tree_view_get_path_at_pos (pagesel->treeview,
257 (gint)event->x,
258 (gint)event->y,
259 &path, NULL, NULL, NULL)) {
260 GtkTreeSelection *selection;
261 selection = gtk_tree_view_get_selection (pagesel->treeview);
262 gtk_tree_selection_unselect_all (selection);
263 gtk_tree_selection_select_path (selection, path);
264 gtk_tree_path_free (path);
267 /* create the context menu */
268 menu = gtk_menu_new();
269 for (tmp = menuitems; tmp->label != NULL; tmp++) {
270 GtkWidget *menuitem;
271 if (g_strcasecmp (tmp->label, "-") == 0) {
272 menuitem = gtk_separator_menu_item_new ();
273 } else {
274 menuitem = gtk_menu_item_new_with_label (_(tmp->label));
275 g_signal_connect (menuitem,
276 "activate",
277 tmp->callback,
278 pagesel);
280 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
282 gtk_widget_show_all (menu);
283 /* make menu a popup menu */
284 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
285 (event != NULL) ? event->button : 0,
286 gdk_event_get_time ((GdkEvent*)event));
291 /*! \brief Handler for the notify::gschem-toplevel signal of GschemDialog
293 * \par Function Description
295 * When the gschem-toplevel property is set on the parent GschemDialog,
296 * we should update the pagesel dialog.
298 * \param [in] gobject the object which received the signal.
299 * \param [in] arg1 the GParamSpec of the property which changed
300 * \param [in] user_data user data set when the signal handler was connected.
302 static void notify_gschem_toplevel_cb (GObject *gobject,
303 GParamSpec *arg1,
304 gpointer user_data)
306 Pagesel *pagesel = PAGESEL( gobject );
308 pagesel_update( pagesel );
312 /*! \todo Finish function documentation!!!
313 * \brief
314 * \par Function Description
317 GType pagesel_get_type()
319 static GType pagesel_type = 0;
321 if (!pagesel_type) {
322 static const GTypeInfo pagesel_info = {
323 sizeof(PageselClass),
324 NULL, /* base_init */
325 NULL, /* base_finalize */
326 (GClassInitFunc) pagesel_class_init,
327 NULL, /* class_finalize */
328 NULL, /* class_data */
329 sizeof(Pagesel),
330 0, /* n_preallocs */
331 (GInstanceInitFunc) pagesel_init,
334 pagesel_type = g_type_register_static (GSCHEM_TYPE_DIALOG,
335 "Pagesel",
336 &pagesel_info, 0);
339 return pagesel_type;
342 /*! \todo Finish function documentation!!!
343 * \brief
344 * \par Function Description
347 static void pagesel_class_init (PageselClass *klass)
351 /*! \todo Finish function documentation!!!
352 * \brief
353 * \par Function Description
356 static void pagesel_init (Pagesel *pagesel)
358 GtkWidget *scrolled_win, *treeview, *label;
359 GtkTreeModel *store;
360 GtkCellRenderer *renderer;
361 GtkTreeViewColumn *column;
362 GtkTreeSelection *selection;
364 /* dialog initialization */
365 g_object_set (G_OBJECT (pagesel),
366 /* GtkContainer */
367 "border-width", 0,
368 /* GtkWindow */
369 "title", _("Page Manager"),
370 "default-height", 180,
371 "default-width", 515,
372 "modal", FALSE,
373 "window-position", GTK_WIN_POS_NONE,
374 "type-hint", GDK_WINDOW_TYPE_HINT_NORMAL,
375 /* GtkDialog */
376 "has-separator", TRUE,
377 NULL);
379 /* create the model for the treeview */
380 store = (GtkTreeModel*)gtk_tree_store_new (NUM_COLUMNS,
381 G_TYPE_POINTER, /* page */
382 G_TYPE_STRING, /* name */
383 G_TYPE_BOOLEAN); /* changed */
385 /* create a scrolled window for the treeview */
386 scrolled_win = GTK_WIDGET (
387 g_object_new (GTK_TYPE_SCROLLED_WINDOW,
388 /* GtkContainer */
389 "border-width", 5,
390 /* GtkScrolledWindow */
391 "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
392 "vscrollbar-policy", GTK_POLICY_ALWAYS,
393 "shadow-type", GTK_SHADOW_ETCHED_IN,
394 NULL));
395 /* create the treeview */
396 treeview = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
397 /* GtkTreeView */
398 "model", store,
399 "rules-hint", TRUE,
400 NULL));
401 g_signal_connect (treeview,
402 "button-press-event",
403 G_CALLBACK (pagesel_callback_button_pressed),
404 pagesel);
405 g_signal_connect (treeview,
406 "popup-menu",
407 G_CALLBACK (pagesel_callback_popup_menu),
408 pagesel);
409 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
410 gtk_tree_selection_set_mode (selection,
411 GTK_SELECTION_SINGLE);
412 g_signal_connect (selection,
413 "changed",
414 G_CALLBACK (pagesel_callback_selection_changed),
415 pagesel);
416 /* - first column: page name */
417 renderer = GTK_CELL_RENDERER (
418 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
419 /* GtkCellRendererText */
420 "editable", FALSE,
421 NULL));
422 column = GTK_TREE_VIEW_COLUMN (
423 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
424 /* GtkTreeViewColumn */
425 "title", _("Filename"),
426 "min-width", 400,
427 "resizable", TRUE,
428 NULL));
429 gtk_tree_view_column_pack_start (column, renderer, TRUE);
430 gtk_tree_view_column_add_attribute (column, renderer, "text", COLUMN_NAME);
431 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
432 /* - second column: changed */
433 renderer = GTK_CELL_RENDERER (
434 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
435 /* GtkCellRendererToggle */
436 "activatable", FALSE,
437 NULL));
438 column = GTK_TREE_VIEW_COLUMN (
439 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
440 /* GtkTreeViewColumn */
441 "title", _("Changed"),
442 "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
443 NULL));
444 gtk_tree_view_column_pack_start (column, renderer, TRUE);
445 gtk_tree_view_column_add_attribute (column, renderer, "active", COLUMN_CHANGED);
446 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
448 /* add the treeview to the scrolled window */
449 gtk_container_add (GTK_CONTAINER (scrolled_win), treeview);
450 /* set treeview of pagesel */
451 pagesel->treeview = GTK_TREE_VIEW (treeview);
453 /* add the scrolled window to the dialog vbox */
454 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel)->vbox), scrolled_win,
455 TRUE, TRUE, 0);
456 gtk_widget_show_all (scrolled_win);
458 /* add a label below the scrolled window */
459 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL,
460 /* GtkLabel */
461 "label", _("Right click on the filename for more options..."),
462 NULL));
463 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel)->vbox), label,
464 FALSE, TRUE, 5);
465 gtk_widget_show (label);
467 /* now add buttons in the action area */
468 gtk_dialog_add_buttons (GTK_DIALOG (pagesel),
469 /* - update button */
470 GTK_STOCK_REFRESH, PAGESEL_RESPONSE_UPDATE,
471 /* - close button */
472 GTK_STOCK_CLOSE, PAGESEL_RESPONSE_CLOSE,
473 NULL);
475 /* Set the alternative button order (ok, cancel, help) for other systems */
476 gtk_dialog_set_alternative_button_order(GTK_DIALOG(pagesel),
477 PAGESEL_RESPONSE_UPDATE,
478 PAGESEL_RESPONSE_CLOSE,
479 -1);
481 g_signal_connect( pagesel, "notify::gschem-toplevel",
482 G_CALLBACK( notify_gschem_toplevel_cb ), NULL );
486 /*! \brief Update tree model of <B>pagesel</B>'s treeview.
487 * \par Function Description
488 * Updates the tree model of <B>pagesel</B>\'s treeview.
490 * Right now, each time it is called, it rebuilds all the model from the
491 * list of pages passed in.
492 * It is a recursive function to populate the tree store
494 * \param [in] model GtkTreeModel to update.
495 * \param [in] parent GtkTreeIter pointer to tree root.
496 * \param [in] pages GedaPageList of pages for this toplevel.
497 * \param [in] page The PAGE object to update tree model from.
499 static void add_page (GtkTreeModel *model, GtkTreeIter *parent,
500 GedaPageList *pages, PAGE *page)
502 GtkTreeIter iter;
503 PAGE *p_current;
504 GList *p_iter;
506 /* add the page to the store */
507 gtk_tree_store_append (GTK_TREE_STORE (model),
508 &iter,
509 parent);
510 gtk_tree_store_set (GTK_TREE_STORE (model),
511 &iter,
512 COLUMN_PAGE, page,
513 COLUMN_NAME, page->page_filename,
514 COLUMN_CHANGED, page->CHANGED,
515 -1);
517 /* search a page that has a up field == p_current->pid */
518 for ( p_iter = geda_list_get_glist( pages );
519 p_iter != NULL;
520 p_iter = g_list_next( p_iter ) ) {
522 p_current = (PAGE *)p_iter->data;
523 if (p_current->up == page->pid) {
524 add_page (model, &iter, pages, p_current);
529 /*! \todo Finish function documentation!!!
530 * \brief
531 * \par Function Description
532 * Recursive function to select the current page in the treeview
535 static void select_page(GtkTreeView *treeview,
536 GtkTreeIter *parent, PAGE *page)
538 GtkTreeModel *treemodel = gtk_tree_view_get_model (treeview);
539 GtkTreeIter iter;
540 PAGE *p_current;
542 if (!gtk_tree_model_iter_children (treemodel, &iter, parent)) {
543 return;
546 do {
547 gtk_tree_model_get (treemodel, &iter,
548 COLUMN_PAGE, &p_current,
549 -1);
550 if (p_current == page) {
551 gtk_tree_view_expand_all (treeview);
552 gtk_tree_selection_select_iter (
553 gtk_tree_view_get_selection (treeview),
554 &iter);
555 return;
558 select_page (treeview, &iter, page);
560 } while (gtk_tree_model_iter_next (treemodel, &iter));
564 /*! \todo Finish function documentation!!!
565 * \brief
566 * \par Function Description
569 void pagesel_update (Pagesel *pagesel)
571 GtkTreeModel *model;
572 TOPLEVEL *toplevel;
573 PAGE *p_current;
574 GList *iter;
576 g_assert (IS_PAGESEL (pagesel));
578 g_return_if_fail (GSCHEM_DIALOG (pagesel)->w_current);
580 toplevel = GSCHEM_DIALOG (pagesel)->w_current->toplevel;
581 model = gtk_tree_view_get_model (pagesel->treeview);
583 /* wipe out every thing in the store */
584 gtk_tree_store_clear (GTK_TREE_STORE (model));
585 /* now rebuild */
586 for ( iter = geda_list_get_glist( toplevel->pages );
587 iter != NULL;
588 iter = g_list_next( iter ) ) {
590 p_current = (PAGE *)iter->data;
591 /* find every page that is not a hierarchy-down of another page */
592 if (p_current->up < 0 ||
593 s_page_search_by_page_id (toplevel->pages,
594 p_current->up) == NULL) {
595 add_page (model, NULL, toplevel->pages, p_current);
599 /* select the current page in the treeview */
600 select_page (pagesel->treeview, NULL, toplevel->page_current);