Cleaned up and consolidated code which creates new pages.
[geda-gaf.git] / gschem / src / x_pagesel.c
bloba50caa068fee1c207fb8b48cd4f024ec49190174
1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2000 Ales V. Hvezda
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
19 #include <config.h>
21 #include <stdio.h>
22 #ifdef HAVE_STDLIB_H
23 #include <stdlib.h>
24 #endif
25 #ifdef HAVE_STRING_H
26 #include <string.h>
27 #endif
29 #include <libgeda/libgeda.h>
31 #include "../include/globals.h"
32 #include "../include/prototype.h"
34 #ifdef HAVE_LIBDMALLOC
35 #include <dmalloc.h>
36 #endif
38 #include "../include/x_pagesel.h"
41 static void x_pagesel_callback_response (GtkDialog *dialog,
42 gint arg1,
43 gpointer user_data);
47 /*! \brief Open the page manager dialog.
48 * \par Function Description
49 * Opens the page manager dialog for <B>toplevel</B> if it is not already.
50 * In this last case, it raises the dialog.
52 * \param [in] toplevel The TOPLEVEL object to open page manager for.
54 void x_pagesel_open (TOPLEVEL *toplevel)
56 if (toplevel->pswindow == NULL) {
57 toplevel->pswindow = GTK_WIDGET (g_object_new (TYPE_PAGESEL,
58 "toplevel", toplevel,
59 NULL));
61 g_signal_connect (toplevel->pswindow,
62 "response",
63 G_CALLBACK (x_pagesel_callback_response),
64 toplevel);
66 gtk_widget_show (toplevel->pswindow);
67 } else {
68 gdk_window_raise (toplevel->pswindow->window);
73 /*! \brief Close the page manager dialog.
74 * \par Function Description
75 * Closes the page manager dialog associated with <B>toplevel</B>.
77 * \param [in] toplevel The TOPLEVEL object to close page manager for.
79 void x_pagesel_close (TOPLEVEL *toplevel)
81 if (toplevel->pswindow) {
82 g_assert (IS_PAGESEL (toplevel->pswindow));
83 gtk_widget_destroy (toplevel->pswindow);
84 toplevel->pswindow = NULL;
89 /*! \brief Update the list and status of <B>toplevel</B>'s pages.
90 * \par Function Description
91 * Updates the list and status of <B>toplevel</B>\'s pages if the page
92 * manager dialog is opened.
94 * \param [in] toplevel The TOPLEVEL object to update.
96 void x_pagesel_update (TOPLEVEL *toplevel)
98 if (toplevel->pswindow) {
99 g_assert (IS_PAGESEL (toplevel->pswindow));
100 pagesel_update (PAGESEL (toplevel->pswindow));
105 /*! \brief Callback for page manager response.
106 * \par Function Description
107 * Handles response <B>arg1</B> of the page manager dialog <B>dialog</B>.
109 * \param [in] dialog GtkDialog that issues callback.
110 * \param [in] arg1 Response argument of page manager dialog.
111 * \param [in] user_data Pointer to relevant TOPLEVEL structure.
113 static void x_pagesel_callback_response (GtkDialog *dialog,
114 gint arg1,
115 gpointer user_data)
117 TOPLEVEL *toplevel = (TOPLEVEL*)user_data;
119 switch (arg1) {
120 case PAGESEL_RESPONSE_UPDATE:
121 pagesel_update (PAGESEL (dialog));
122 break;
123 case GTK_RESPONSE_DELETE_EVENT:
124 case PAGESEL_RESPONSE_CLOSE:
125 g_assert (GTK_WIDGET (dialog) == toplevel->pswindow);
126 gtk_widget_destroy (GTK_WIDGET (dialog));
127 toplevel->pswindow = NULL;
128 break;
129 default:
130 g_assert_not_reached ();
135 enum {
136 PROP_TOPLEVEL=1
139 enum {
140 COLUMN_PAGE,
141 COLUMN_NAME,
142 COLUMN_CHANGED,
143 NUM_COLUMNS
147 static void pagesel_class_init (PageselClass *class);
148 static void pagesel_init (Pagesel *pagesel);
149 static void pagesel_set_property (GObject *object,
150 guint property_id,
151 const GValue *value,
152 GParamSpec *pspec);
153 static void pagesel_get_property (GObject *object,
154 guint property_id,
155 GValue *value,
156 GParamSpec *pspec);
158 static void pagesel_popup_menu (Pagesel *pagesel,
159 GdkEventButton *event);
161 /*! \todo Finish function documentation!!!
162 * \brief
163 * \par Function Description
166 static void pagesel_callback_selection_changed (GtkTreeSelection *selection,
167 gpointer user_data)
169 GtkTreeModel *model;
170 GtkTreeIter iter;
171 Pagesel *pagesel = (Pagesel*)user_data;
172 TOPLEVEL *toplevel;
173 PAGE *page;
175 if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
176 return;
179 toplevel = pagesel->toplevel;
180 gtk_tree_model_get (model, &iter,
181 COLUMN_PAGE, &page,
182 -1);
184 /* temp */
185 s_page_goto (toplevel, page);
186 i_set_filename (toplevel, toplevel->page_current->page_filename);
187 x_scrollbars_update (toplevel);
188 o_redraw_all (toplevel);
190 /* We would like to use the following call, but since it calls
191 * x_pagesel_update() it would cause an infinite loop.
193 /* x_window_set_current_page (toplevel, page); */
197 /*! \todo Finish function documentation!!!
198 * \brief
199 * \par Function Description
202 static gboolean pagesel_callback_button_pressed (GtkWidget *widget,
203 GdkEventButton *event,
204 gpointer user_data)
206 Pagesel *pagesel = (Pagesel*)user_data;
207 gboolean ret = FALSE;
209 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
210 pagesel_popup_menu (pagesel, event);
211 ret = TRUE;
214 return ret;
217 /*! \todo Finish function documentation!!!
218 * \brief
219 * \par Function Description
222 static gboolean pagesel_callback_popup_menu (GtkWidget *widget,
223 gpointer user_data)
225 Pagesel *pagesel = (Pagesel*)user_data;
227 pagesel_popup_menu (pagesel, NULL);
229 return TRUE;
232 #define DEFINE_POPUP_CALLBACK(name, action) \
233 static void \
234 pagesel_callback_popup_ ## name (GtkMenuItem *menuitem, \
235 gpointer user_data) \
237 i_callback_ ## action (PAGESEL (user_data)->toplevel, 0, NULL); \
240 DEFINE_POPUP_CALLBACK (new_page, file_new)
241 DEFINE_POPUP_CALLBACK (open_page, file_open)
242 DEFINE_POPUP_CALLBACK (save_page, file_save)
243 DEFINE_POPUP_CALLBACK (close_page, page_close)
244 DEFINE_POPUP_CALLBACK (discard_page, page_discard)
247 /*! \brief Popup context-sensitive menu.
248 * \par Function Description
249 * Pops up a context-sensitive menu.
251 * <B>event</B> can be NULL if the popup is triggered by a key binding
252 * instead of a mouse click.
254 * \param [in] pagesel The Pagesel object.
255 * \param [in] event Mouse click event info.
257 static void pagesel_popup_menu (Pagesel *pagesel,
258 GdkEventButton *event)
260 GtkTreePath *path;
261 GtkWidget *menu;
262 struct menuitem_t {
263 gchar *label;
264 GCallback callback;
266 struct menuitem_t menuitems[] = {
267 { N_("New Page"), G_CALLBACK (pagesel_callback_popup_new_page) },
268 { N_("Open Page..."), G_CALLBACK (pagesel_callback_popup_open_page) },
269 { "-", NULL },
270 { N_("Save Page"), G_CALLBACK (pagesel_callback_popup_save_page) },
271 { N_("Close Page"), G_CALLBACK (pagesel_callback_popup_close_page) },
272 { N_("Discard Page"), G_CALLBACK (pagesel_callback_popup_discard_page) },
273 { NULL, NULL } };
274 struct menuitem_t *tmp;
276 if (event != NULL &&
277 gtk_tree_view_get_path_at_pos (pagesel->treeview,
278 (gint)event->x,
279 (gint)event->y,
280 &path, NULL, NULL, NULL)) {
281 GtkTreeSelection *selection;
282 selection = gtk_tree_view_get_selection (pagesel->treeview);
283 gtk_tree_selection_unselect_all (selection);
284 gtk_tree_selection_select_path (selection, path);
285 gtk_tree_path_free (path);
288 /* create the context menu */
289 menu = gtk_menu_new();
290 for (tmp = menuitems; tmp->label != NULL; tmp++) {
291 GtkWidget *menuitem;
292 if (g_strcasecmp (tmp->label, "-") == 0) {
293 menuitem = gtk_separator_menu_item_new ();
294 } else {
295 menuitem = gtk_menu_item_new_with_label (_(tmp->label));
296 g_signal_connect (menuitem,
297 "activate",
298 tmp->callback,
299 pagesel);
301 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
303 gtk_widget_show_all (menu);
304 /* make menu a popup menu */
305 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
306 (event != NULL) ? event->button : 0,
307 gdk_event_get_time ((GdkEvent*)event));
311 /*! \todo Finish function documentation!!!
312 * \brief
313 * \par Function Description
316 GType pagesel_get_type()
318 static GType pagesel_type = 0;
320 if (!pagesel_type) {
321 static const GTypeInfo pagesel_info = {
322 sizeof(PageselClass),
323 NULL, /* base_init */
324 NULL, /* base_finalize */
325 (GClassInitFunc) pagesel_class_init,
326 NULL, /* class_finalize */
327 NULL, /* class_data */
328 sizeof(Pagesel),
329 0, /* n_preallocs */
330 (GInstanceInitFunc) pagesel_init,
333 pagesel_type = g_type_register_static (GTK_TYPE_DIALOG,
334 "Pagesel",
335 &pagesel_info, 0);
338 return pagesel_type;
341 /*! \todo Finish function documentation!!!
342 * \brief
343 * \par Function Description
346 static void pagesel_class_init (PageselClass *klass)
348 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
350 gobject_class->set_property = pagesel_set_property;
351 gobject_class->get_property = pagesel_get_property;
353 g_object_class_install_property (
354 gobject_class, PROP_TOPLEVEL,
355 g_param_spec_pointer ("toplevel",
358 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
362 /*! \todo Finish function documentation!!!
363 * \brief
364 * \par Function Description
367 static void pagesel_init (Pagesel *pagesel)
369 GtkWidget *scrolled_win, *treeview, *label;
370 GtkTreeModel *store;
371 GtkCellRenderer *renderer;
372 GtkTreeViewColumn *column;
373 GtkTreeSelection *selection;
375 /* dialog initialization */
376 g_object_set (G_OBJECT (pagesel),
377 /* GtkContainer */
378 "border-width", 0,
379 /* GtkWindow */
380 "type", GTK_WINDOW_TOPLEVEL,
381 "title", _("Page Manager"),
382 "default-height", 180,
383 "default-width", 515,
384 "modal", FALSE,
385 "window-position", GTK_WIN_POS_NONE,
386 "type-hint", GDK_WINDOW_TYPE_HINT_NORMAL,
387 /* GtkDialog */
388 "has-separator", TRUE,
389 NULL);
391 /* create the model for the treeview */
392 store = (GtkTreeModel*)gtk_tree_store_new (NUM_COLUMNS,
393 G_TYPE_POINTER, /* page */
394 G_TYPE_STRING, /* name */
395 G_TYPE_BOOLEAN); /* changed */
397 /* create a scrolled window for the treeview */
398 scrolled_win = GTK_WIDGET (
399 g_object_new (GTK_TYPE_SCROLLED_WINDOW,
400 /* GtkContainer */
401 "border-width", 5,
402 /* GtkScrolledWindow */
403 "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
404 "vscrollbar-policy", GTK_POLICY_ALWAYS,
405 "shadow-type", GTK_SHADOW_ETCHED_IN,
406 NULL));
407 /* create the treeview */
408 treeview = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
409 /* GtkTreeView */
410 "model", store,
411 "rules-hint", TRUE,
412 NULL));
413 g_signal_connect (treeview,
414 "button-press-event",
415 G_CALLBACK (pagesel_callback_button_pressed),
416 pagesel);
417 g_signal_connect (treeview,
418 "popup-menu",
419 G_CALLBACK (pagesel_callback_popup_menu),
420 pagesel);
421 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
422 gtk_tree_selection_set_mode (selection,
423 GTK_SELECTION_SINGLE);
424 g_signal_connect (selection,
425 "changed",
426 G_CALLBACK (pagesel_callback_selection_changed),
427 pagesel);
428 /* - first column: page name */
429 renderer = GTK_CELL_RENDERER (
430 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
431 /* GtkCellRendererText */
432 "editable", FALSE,
433 NULL));
434 column = GTK_TREE_VIEW_COLUMN (
435 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
436 /* GtkTreeViewColumn */
437 "title", _("Filename"),
438 "min-width", 400,
439 "resizable", TRUE,
440 NULL));
441 gtk_tree_view_column_pack_start (column, renderer, TRUE);
442 gtk_tree_view_column_add_attribute (column, renderer, "text", COLUMN_NAME);
443 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
444 /* - second column: changed */
445 renderer = GTK_CELL_RENDERER (
446 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE,
447 /* GtkCellRendererToggle */
448 "activatable", FALSE,
449 NULL));
450 column = GTK_TREE_VIEW_COLUMN (
451 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
452 /* GtkTreeViewColumn */
453 "title", _("Changed"),
454 "sizing", GTK_TREE_VIEW_COLUMN_FIXED,
455 NULL));
456 gtk_tree_view_column_pack_start (column, renderer, TRUE);
457 gtk_tree_view_column_add_attribute (column, renderer, "active", COLUMN_CHANGED);
458 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
460 /* add the treeview to the scrolled window */
461 gtk_container_add (GTK_CONTAINER (scrolled_win), treeview);
462 /* set treeview of pagesel */
463 pagesel->treeview = GTK_TREE_VIEW (treeview);
465 /* add the scrolled window to the dialog vbox */
466 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel)->vbox), scrolled_win,
467 TRUE, TRUE, 0);
468 gtk_widget_show_all (scrolled_win);
470 /* add a label below the scrolled window */
471 label = GTK_WIDGET (g_object_new (GTK_TYPE_LABEL,
472 /* GtkLabel */
473 "label", _("Right click on the filename for more options..."),
474 NULL));
475 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel)->vbox), label,
476 FALSE, TRUE, 5);
477 gtk_widget_show (label);
479 /* now add buttons in the action area */
480 gtk_dialog_add_buttons (GTK_DIALOG (pagesel),
481 /* - update button */
482 GTK_STOCK_REFRESH, PAGESEL_RESPONSE_UPDATE,
483 /* - close button */
484 GTK_STOCK_CLOSE, PAGESEL_RESPONSE_CLOSE,
485 NULL);
487 #if GTK_CHECK_VERSION (2,6,0)
488 /* Set the alternative button order (ok, cancel, help) for other systems */
489 gtk_dialog_set_alternative_button_order(GTK_DIALOG(pagesel),
490 PAGESEL_RESPONSE_UPDATE,
491 PAGESEL_RESPONSE_CLOSE,
492 -1);
493 #endif
498 /*! \todo Finish function documentation!!!
499 * \brief
500 * \par Function Description
503 static void pagesel_set_property (GObject *object,
504 guint property_id,
505 const GValue *value,
506 GParamSpec *pspec)
508 Pagesel *pagesel = PAGESEL (object);
510 switch(property_id) {
511 case PROP_TOPLEVEL:
512 pagesel->toplevel = (TOPLEVEL*)g_value_get_pointer (value);
513 pagesel_update (pagesel);
514 break;
515 default:
516 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
521 /*! \todo Finish function documentation!!!
522 * \brief
523 * \par Function Description
526 static void pagesel_get_property (GObject *object,
527 guint property_id,
528 GValue *value,
529 GParamSpec *pspec)
531 Pagesel *pagesel = PAGESEL (object);
533 switch(property_id) {
534 case PROP_TOPLEVEL:
535 g_value_set_pointer (value, (gpointer)pagesel->toplevel);
536 break;
537 default:
538 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
543 /*! \brief Update tree model of <B>pagesel</B>'s treeview.
544 * \par Function Description
545 * Updates the tree model of <B>pagesel</B>\'s treeview.
547 * Right now, each time it is called, it rebuilds all the model from the
548 * list of page in the toplevel.
549 * It is a recursive function to populate the tree store
551 * \param [in] model GtkTreeModel to update.
552 * \param [in] parent GtkTreeIter pointer to tree root.
553 * \param [in] page The PAGE object to update tree model from.
555 static void add_page (GtkTreeModel *model, GtkTreeIter *parent,
556 PAGE *page)
558 GtkTreeIter iter;
559 PAGE *p_current;
561 /* add the page to the store */
562 gtk_tree_store_append (GTK_TREE_STORE (model),
563 &iter,
564 parent);
565 gtk_tree_store_set (GTK_TREE_STORE (model),
566 &iter,
567 COLUMN_PAGE, page,
568 COLUMN_NAME, page->page_filename,
569 COLUMN_CHANGED, page->CHANGED,
570 -1);
572 /* search a page that has a up field == p_current->pid */
573 for (p_current = page->next;
574 p_current != NULL;
575 p_current = p_current->next) {
576 if (p_current->up == page->pid) {
577 add_page (model, &iter, p_current);
583 /*! \todo Finish function documentation!!!
584 * \brief
585 * \par Function Description
586 * Recursive function to select the current page in the treeview
589 static void select_page(GtkTreeView *treeview,
590 GtkTreeIter *parent, PAGE *page)
592 GtkTreeModel *treemodel = gtk_tree_view_get_model (treeview);
593 GtkTreeIter iter;
594 PAGE *p_current;
596 if (!gtk_tree_model_iter_children (treemodel, &iter, parent)) {
597 return;
600 do {
601 gtk_tree_model_get (treemodel, &iter,
602 COLUMN_PAGE, &p_current,
603 -1);
604 if (p_current == page) {
605 gtk_tree_view_expand_all (treeview);
606 gtk_tree_selection_select_iter (
607 gtk_tree_view_get_selection (treeview),
608 &iter);
609 return;
612 select_page (treeview, &iter, page);
614 } while (gtk_tree_model_iter_next (treemodel, &iter));
618 /*! \todo Finish function documentation!!!
619 * \brief
620 * \par Function Description
623 void pagesel_update (Pagesel *pagesel)
625 GtkTreeModel *model;
626 TOPLEVEL *toplevel;
627 PAGE *p_current;
629 g_assert (IS_PAGESEL (pagesel));
631 g_return_if_fail (pagesel->toplevel);
633 toplevel = pagesel->toplevel;
634 model = gtk_tree_view_get_model (pagesel->treeview);
636 /* wipe out every thing in the store */
637 gtk_tree_store_clear (GTK_TREE_STORE (model));
638 /* now rebuild */
639 for (p_current = toplevel->page_head->next;
640 p_current != NULL;
641 p_current = p_current->next) {
642 /* find every page that is not a hierarchy-down of another page */
643 if (p_current->up < 0 ||
644 s_hierarchy_find_page (toplevel->page_head->next,
645 p_current->up) == NULL) {
646 add_page (model, NULL, p_current);
650 /* select the current page in the treeview */
651 select_page (pagesel->treeview, NULL, toplevel->page_current);
654 /*! \deprecated
655 * This function was in the noweb file, but was not referenced.
656 * Create a gtk button with a custom label <B>text</B> and a stock
657 * icon <B>stock</B>.
658 * <B>text</B>: The mnemonic text for the label.
659 * <B>stock</B>: The name of the stock item to get the icon from.
661 * Return value: The widget.
662 * Taken from evolution code:
663 * http://lists.ximian.com/archives/public/evolution-patches/2003-April/000088.html
665 /*! \todo Finish function documentation!!!
666 * \brief
667 * \par Function Description
671 GtkWidget *e_gtk_button_new_with_icon(const char *text, const char *stock)
673 GtkWidget *button, *label;
674 GtkStockItem item;
676 button = gtk_button_new();
677 label = gtk_label_new_with_mnemonic(text);
678 gtk_label_set_mnemonic_widget((GtkLabel *)label, button);
680 if (gtk_stock_lookup(stock, &item)) {
681 GtkWidget *image, *hbox, *align;
683 image = gtk_image_new_from_stock(stock, GTK_ICON_SIZE_BUTTON);
684 hbox = gtk_hbox_new(FALSE, 2);
685 align = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
686 gtk_box_pack_start((GtkBox *)hbox, image, FALSE, FALSE, 0);
687 gtk_box_pack_end((GtkBox *)hbox, label, FALSE, FALSE, 0);
688 gtk_container_add((GtkContainer *)align, hbox);
689 gtk_container_add((GtkContainer *)button, align);
690 gtk_widget_show_all(align);
691 } else {
692 gtk_misc_set_alignment((GtkMisc *)label, 0.5, 0.5);
693 gtk_container_add((GtkContainer *)button, label);
694 gtk_widget_show(label);
697 return button;