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
32 #ifdef HAVE_LIBDMALLOC
37 static void x_pagesel_callback_response (GtkDialog
*dialog
,
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
,
55 "settings-name", "pagesel",
56 "gschem-toplevel", w_current
,
59 g_signal_connect (w_current
->pswindow
,
61 G_CALLBACK (x_pagesel_callback_response
),
64 gtk_widget_show (w_current
->pswindow
);
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
,
114 GSCHEM_TOPLEVEL
*w_current
= (GSCHEM_TOPLEVEL
*)user_data
;
117 case PAGESEL_RESPONSE_UPDATE
:
118 pagesel_update (PAGESEL (dialog
));
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
;
127 g_assert_not_reached ();
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!!!
148 * \par Function Description
151 static void pagesel_callback_selection_changed (GtkTreeSelection
*selection
,
156 Pagesel
*pagesel
= (Pagesel
*)user_data
;
157 GSCHEM_TOPLEVEL
*w_current
;
160 if (!gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
164 w_current
= GSCHEM_DIALOG (pagesel
)->w_current
;
165 gtk_tree_model_get (model
, &iter
,
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!!!
178 * \par Function Description
181 static gboolean
pagesel_callback_button_pressed (GtkWidget
*widget
,
182 GdkEventButton
*event
,
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
);
196 /*! \todo Finish function documentation!!!
198 * \par Function Description
201 static gboolean
pagesel_callback_popup_menu (GtkWidget
*widget
,
204 Pagesel
*pagesel
= (Pagesel
*)user_data
;
206 pagesel_popup_menu (pagesel
, NULL
);
211 #define DEFINE_POPUP_CALLBACK(name, action) \
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
)
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
) },
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
) },
253 struct menuitem_t
*tmp
;
256 gtk_tree_view_get_path_at_pos (pagesel
->treeview
,
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
++) {
271 if (g_strcasecmp (tmp
->label
, "-") == 0) {
272 menuitem
= gtk_separator_menu_item_new ();
274 menuitem
= gtk_menu_item_new_with_label (_(tmp
->label
));
275 g_signal_connect (menuitem
,
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
,
306 Pagesel
*pagesel
= PAGESEL( gobject
);
308 pagesel_update( pagesel
);
312 /*! \todo Finish function documentation!!!
314 * \par Function Description
317 GType
pagesel_get_type()
319 static GType pagesel_type
= 0;
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 */
331 (GInstanceInitFunc
) pagesel_init
,
334 pagesel_type
= g_type_register_static (GSCHEM_TYPE_DIALOG
,
342 /*! \todo Finish function documentation!!!
344 * \par Function Description
347 static void pagesel_class_init (PageselClass
*klass
)
351 /*! \todo Finish function documentation!!!
353 * \par Function Description
356 static void pagesel_init (Pagesel
*pagesel
)
358 GtkWidget
*scrolled_win
, *treeview
, *label
;
360 GtkCellRenderer
*renderer
;
361 GtkTreeViewColumn
*column
;
362 GtkTreeSelection
*selection
;
364 /* dialog initialization */
365 g_object_set (G_OBJECT (pagesel
),
369 "title", _("Page Manager"),
370 "default-height", 180,
371 "default-width", 515,
373 "window-position", GTK_WIN_POS_NONE
,
374 "type-hint", GDK_WINDOW_TYPE_HINT_NORMAL
,
376 "has-separator", TRUE
,
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
,
390 /* GtkScrolledWindow */
391 "hscrollbar-policy", GTK_POLICY_AUTOMATIC
,
392 "vscrollbar-policy", GTK_POLICY_ALWAYS
,
393 "shadow-type", GTK_SHADOW_ETCHED_IN
,
395 /* create the treeview */
396 treeview
= GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW
,
401 g_signal_connect (treeview
,
402 "button-press-event",
403 G_CALLBACK (pagesel_callback_button_pressed
),
405 g_signal_connect (treeview
,
407 G_CALLBACK (pagesel_callback_popup_menu
),
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
,
414 G_CALLBACK (pagesel_callback_selection_changed
),
416 /* - first column: page name */
417 renderer
= GTK_CELL_RENDERER (
418 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT
,
419 /* GtkCellRendererText */
422 column
= GTK_TREE_VIEW_COLUMN (
423 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
424 /* GtkTreeViewColumn */
425 "title", _("Filename"),
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
,
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
,
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
,
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
,
461 "label", _("Right click on the filename for more options..."),
463 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel
)->vbox
), label
,
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
,
472 GTK_STOCK_CLOSE
, PAGESEL_RESPONSE_CLOSE
,
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
,
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
)
506 /* add the page to the store */
507 gtk_tree_store_append (GTK_TREE_STORE (model
),
510 gtk_tree_store_set (GTK_TREE_STORE (model
),
513 COLUMN_NAME
, page
->page_filename
,
514 COLUMN_CHANGED
, page
->CHANGED
,
517 /* search a page that has a up field == p_current->pid */
518 for ( p_iter
= geda_list_get_glist( pages
);
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!!!
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
);
542 if (!gtk_tree_model_iter_children (treemodel
, &iter
, parent
)) {
547 gtk_tree_model_get (treemodel
, &iter
,
548 COLUMN_PAGE
, &p_current
,
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
),
558 select_page (treeview
, &iter
, page
);
560 } while (gtk_tree_model_iter_next (treemodel
, &iter
));
564 /*! \todo Finish function documentation!!!
566 * \par Function Description
569 void pagesel_update (Pagesel
*pagesel
)
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
));
586 for ( iter
= geda_list_get_glist( toplevel
->pages
);
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
);