1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
30 #include <libgeda/libgeda.h>
32 #include "../include/globals.h"
33 #include "../include/prototype.h"
35 #ifdef HAVE_LIBDMALLOC
39 #include "../include/gschem_dialog.h"
40 #include "../include/x_pagesel.h"
43 static void x_pagesel_callback_response (GtkDialog
*dialog
,
49 /*! \brief Open the page manager dialog.
50 * \par Function Description
51 * Opens the page manager dialog for <B>toplevel</B> if it is not already.
52 * In this last case, it raises the dialog.
54 * \param [in] toplevel The TOPLEVEL object to open page manager for.
56 void x_pagesel_open (TOPLEVEL
*toplevel
)
58 if (toplevel
->pswindow
== NULL
) {
59 toplevel
->pswindow
= GTK_WIDGET (g_object_new (TYPE_PAGESEL
,
61 "settings-name", "pagesel",
65 g_signal_connect (toplevel
->pswindow
,
67 G_CALLBACK (x_pagesel_callback_response
),
70 gtk_widget_show (toplevel
->pswindow
);
72 gdk_window_raise (toplevel
->pswindow
->window
);
77 /*! \brief Close the page manager dialog.
78 * \par Function Description
79 * Closes the page manager dialog associated with <B>toplevel</B>.
81 * \param [in] toplevel The TOPLEVEL object to close page manager for.
83 void x_pagesel_close (TOPLEVEL
*toplevel
)
85 if (toplevel
->pswindow
) {
86 g_assert (IS_PAGESEL (toplevel
->pswindow
));
87 gtk_widget_destroy (toplevel
->pswindow
);
88 toplevel
->pswindow
= NULL
;
93 /*! \brief Update the list and status of <B>toplevel</B>'s pages.
94 * \par Function Description
95 * Updates the list and status of <B>toplevel</B>\'s pages if the page
96 * manager dialog is opened.
98 * \param [in] toplevel The TOPLEVEL object to update.
100 void x_pagesel_update (TOPLEVEL
*toplevel
)
102 if (toplevel
->pswindow
) {
103 g_assert (IS_PAGESEL (toplevel
->pswindow
));
104 pagesel_update (PAGESEL (toplevel
->pswindow
));
108 /*! \brief Callback for page manager response.
109 * \par Function Description
110 * Handles response <B>arg1</B> of the page manager dialog <B>dialog</B>.
112 * \param [in] dialog GtkDialog that issues callback.
113 * \param [in] arg1 Response argument of page manager dialog.
114 * \param [in] user_data Pointer to relevant TOPLEVEL structure.
116 static void x_pagesel_callback_response (GtkDialog
*dialog
,
120 TOPLEVEL
*toplevel
= (TOPLEVEL
*)user_data
;
123 case PAGESEL_RESPONSE_UPDATE
:
124 pagesel_update (PAGESEL (dialog
));
126 case GTK_RESPONSE_DELETE_EVENT
:
127 case PAGESEL_RESPONSE_CLOSE
:
128 g_assert (GTK_WIDGET (dialog
) == toplevel
->pswindow
);
129 gtk_widget_destroy (GTK_WIDGET (dialog
));
130 toplevel
->pswindow
= NULL
;
133 g_assert_not_reached ();
146 static void pagesel_class_init (PageselClass
*class);
147 static void pagesel_init (Pagesel
*pagesel
);
149 static void pagesel_popup_menu (Pagesel
*pagesel
,
150 GdkEventButton
*event
);
152 /*! \todo Finish function documentation!!!
154 * \par Function Description
157 static void pagesel_callback_selection_changed (GtkTreeSelection
*selection
,
162 Pagesel
*pagesel
= (Pagesel
*)user_data
;
166 if (!gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
170 toplevel
= GSCHEM_DIALOG (pagesel
)->toplevel
;
171 gtk_tree_model_get (model
, &iter
,
176 s_page_goto (toplevel
, page
);
177 i_set_filename (toplevel
, toplevel
->page_current
->page_filename
);
178 x_scrollbars_update (toplevel
);
179 o_redraw_all (toplevel
);
181 /* We would like to use the following call, but since it calls
182 * x_pagesel_update() it would cause an infinite loop.
184 /* x_window_set_current_page (toplevel, page); */
188 /*! \todo Finish function documentation!!!
190 * \par Function Description
193 static gboolean
pagesel_callback_button_pressed (GtkWidget
*widget
,
194 GdkEventButton
*event
,
197 Pagesel
*pagesel
= (Pagesel
*)user_data
;
198 gboolean ret
= FALSE
;
200 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3) {
201 pagesel_popup_menu (pagesel
, event
);
208 /*! \todo Finish function documentation!!!
210 * \par Function Description
213 static gboolean
pagesel_callback_popup_menu (GtkWidget
*widget
,
216 Pagesel
*pagesel
= (Pagesel
*)user_data
;
218 pagesel_popup_menu (pagesel
, NULL
);
223 #define DEFINE_POPUP_CALLBACK(name, action) \
225 pagesel_callback_popup_ ## name (GtkMenuItem *menuitem, \
226 gpointer user_data) \
228 i_callback_ ## action (GSCHEM_DIALOG (user_data)->toplevel, 0, NULL); \
231 DEFINE_POPUP_CALLBACK (new_page
, file_new
)
232 DEFINE_POPUP_CALLBACK (open_page
, file_open
)
233 DEFINE_POPUP_CALLBACK (save_page
, file_save
)
234 DEFINE_POPUP_CALLBACK (close_page
, page_close
)
235 DEFINE_POPUP_CALLBACK (discard_page
, page_discard
)
238 /*! \brief Popup context-sensitive menu.
239 * \par Function Description
240 * Pops up a context-sensitive menu.
242 * <B>event</B> can be NULL if the popup is triggered by a key binding
243 * instead of a mouse click.
245 * \param [in] pagesel The Pagesel object.
246 * \param [in] event Mouse click event info.
248 static void pagesel_popup_menu (Pagesel
*pagesel
,
249 GdkEventButton
*event
)
257 struct menuitem_t menuitems
[] = {
258 { N_("New Page"), G_CALLBACK (pagesel_callback_popup_new_page
) },
259 { N_("Open Page..."), G_CALLBACK (pagesel_callback_popup_open_page
) },
261 { N_("Save Page"), G_CALLBACK (pagesel_callback_popup_save_page
) },
262 { N_("Close Page"), G_CALLBACK (pagesel_callback_popup_close_page
) },
263 { N_("Discard Page"), G_CALLBACK (pagesel_callback_popup_discard_page
) },
265 struct menuitem_t
*tmp
;
268 gtk_tree_view_get_path_at_pos (pagesel
->treeview
,
271 &path
, NULL
, NULL
, NULL
)) {
272 GtkTreeSelection
*selection
;
273 selection
= gtk_tree_view_get_selection (pagesel
->treeview
);
274 gtk_tree_selection_unselect_all (selection
);
275 gtk_tree_selection_select_path (selection
, path
);
276 gtk_tree_path_free (path
);
279 /* create the context menu */
280 menu
= gtk_menu_new();
281 for (tmp
= menuitems
; tmp
->label
!= NULL
; tmp
++) {
283 if (g_strcasecmp (tmp
->label
, "-") == 0) {
284 menuitem
= gtk_separator_menu_item_new ();
286 menuitem
= gtk_menu_item_new_with_label (_(tmp
->label
));
287 g_signal_connect (menuitem
,
292 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
294 gtk_widget_show_all (menu
);
295 /* make menu a popup menu */
296 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
297 (event
!= NULL
) ? event
->button
: 0,
298 gdk_event_get_time ((GdkEvent
*)event
));
303 /*! \brief Handler for the notify::toplevel signal of GschemDialog
305 * \par Function Description
307 * When the toplevel property is set on the parent GschemDialog,
308 * we should update the pagesel dialog.
310 * \param [in] pspec the GParamSpec of the property which changed
311 * \param [in] gobject the object which received the signal.
312 * \param [in] user_data user data set when the signal handler was connected.
314 static void notify_toplevel_cb (GObject
*gobject
,
318 Pagesel
*pagesel
= PAGESEL( gobject
);
320 pagesel_update( pagesel
);
324 /*! \todo Finish function documentation!!!
326 * \par Function Description
329 GType
pagesel_get_type()
331 static GType pagesel_type
= 0;
334 static const GTypeInfo pagesel_info
= {
335 sizeof(PageselClass
),
336 NULL
, /* base_init */
337 NULL
, /* base_finalize */
338 (GClassInitFunc
) pagesel_class_init
,
339 NULL
, /* class_finalize */
340 NULL
, /* class_data */
343 (GInstanceInitFunc
) pagesel_init
,
346 pagesel_type
= g_type_register_static (GSCHEM_TYPE_DIALOG
,
354 /*! \todo Finish function documentation!!!
356 * \par Function Description
359 static void pagesel_class_init (PageselClass
*klass
)
363 /*! \todo Finish function documentation!!!
365 * \par Function Description
368 static void pagesel_init (Pagesel
*pagesel
)
370 GtkWidget
*scrolled_win
, *treeview
, *label
;
372 GtkCellRenderer
*renderer
;
373 GtkTreeViewColumn
*column
;
374 GtkTreeSelection
*selection
;
376 /* dialog initialization */
377 g_object_set (G_OBJECT (pagesel
),
381 "type", GTK_WINDOW_TOPLEVEL
,
382 "title", _("Page Manager"),
383 "default-height", 180,
384 "default-width", 515,
386 "window-position", GTK_WIN_POS_NONE
,
387 "type-hint", GDK_WINDOW_TYPE_HINT_NORMAL
,
389 "has-separator", TRUE
,
392 /* create the model for the treeview */
393 store
= (GtkTreeModel
*)gtk_tree_store_new (NUM_COLUMNS
,
394 G_TYPE_POINTER
, /* page */
395 G_TYPE_STRING
, /* name */
396 G_TYPE_BOOLEAN
); /* changed */
398 /* create a scrolled window for the treeview */
399 scrolled_win
= GTK_WIDGET (
400 g_object_new (GTK_TYPE_SCROLLED_WINDOW
,
403 /* GtkScrolledWindow */
404 "hscrollbar-policy", GTK_POLICY_AUTOMATIC
,
405 "vscrollbar-policy", GTK_POLICY_ALWAYS
,
406 "shadow-type", GTK_SHADOW_ETCHED_IN
,
408 /* create the treeview */
409 treeview
= GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW
,
414 g_signal_connect (treeview
,
415 "button-press-event",
416 G_CALLBACK (pagesel_callback_button_pressed
),
418 g_signal_connect (treeview
,
420 G_CALLBACK (pagesel_callback_popup_menu
),
422 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview
));
423 gtk_tree_selection_set_mode (selection
,
424 GTK_SELECTION_SINGLE
);
425 g_signal_connect (selection
,
427 G_CALLBACK (pagesel_callback_selection_changed
),
429 /* - first column: page name */
430 renderer
= GTK_CELL_RENDERER (
431 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT
,
432 /* GtkCellRendererText */
435 column
= GTK_TREE_VIEW_COLUMN (
436 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
437 /* GtkTreeViewColumn */
438 "title", _("Filename"),
442 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
443 gtk_tree_view_column_add_attribute (column
, renderer
, "text", COLUMN_NAME
);
444 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
445 /* - second column: changed */
446 renderer
= GTK_CELL_RENDERER (
447 g_object_new (GTK_TYPE_CELL_RENDERER_TOGGLE
,
448 /* GtkCellRendererToggle */
449 "activatable", FALSE
,
451 column
= GTK_TREE_VIEW_COLUMN (
452 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
453 /* GtkTreeViewColumn */
454 "title", _("Changed"),
455 "sizing", GTK_TREE_VIEW_COLUMN_FIXED
,
457 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
458 gtk_tree_view_column_add_attribute (column
, renderer
, "active", COLUMN_CHANGED
);
459 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
461 /* add the treeview to the scrolled window */
462 gtk_container_add (GTK_CONTAINER (scrolled_win
), treeview
);
463 /* set treeview of pagesel */
464 pagesel
->treeview
= GTK_TREE_VIEW (treeview
);
466 /* add the scrolled window to the dialog vbox */
467 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel
)->vbox
), scrolled_win
,
469 gtk_widget_show_all (scrolled_win
);
471 /* add a label below the scrolled window */
472 label
= GTK_WIDGET (g_object_new (GTK_TYPE_LABEL
,
474 "label", _("Right click on the filename for more options..."),
476 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel
)->vbox
), label
,
478 gtk_widget_show (label
);
480 /* now add buttons in the action area */
481 gtk_dialog_add_buttons (GTK_DIALOG (pagesel
),
482 /* - update button */
483 GTK_STOCK_REFRESH
, PAGESEL_RESPONSE_UPDATE
,
485 GTK_STOCK_CLOSE
, PAGESEL_RESPONSE_CLOSE
,
488 #if GTK_CHECK_VERSION (2,6,0)
489 /* Set the alternative button order (ok, cancel, help) for other systems */
490 gtk_dialog_set_alternative_button_order(GTK_DIALOG(pagesel
),
491 PAGESEL_RESPONSE_UPDATE
,
492 PAGESEL_RESPONSE_CLOSE
,
496 g_signal_connect( pagesel
, "notify::toplevel",
497 G_CALLBACK( notify_toplevel_cb
), NULL
);
501 /*! \brief Update tree model of <B>pagesel</B>'s treeview.
502 * \par Function Description
503 * Updates the tree model of <B>pagesel</B>\'s treeview.
505 * Right now, each time it is called, it rebuilds all the model from the
506 * list of page in the toplevel.
507 * It is a recursive function to populate the tree store
509 * \param [in] model GtkTreeModel to update.
510 * \param [in] parent GtkTreeIter pointer to tree root.
511 * \param [in] page The PAGE object to update tree model from.
513 static void add_page (GtkTreeModel
*model
, GtkTreeIter
*parent
,
519 /* add the page to the store */
520 gtk_tree_store_append (GTK_TREE_STORE (model
),
523 gtk_tree_store_set (GTK_TREE_STORE (model
),
526 COLUMN_NAME
, page
->page_filename
,
527 COLUMN_CHANGED
, page
->CHANGED
,
530 /* search a page that has a up field == p_current->pid */
531 for (p_current
= page
->next
;
533 p_current
= p_current
->next
) {
534 if (p_current
->up
== page
->pid
) {
535 add_page (model
, &iter
, p_current
);
541 /*! \todo Finish function documentation!!!
543 * \par Function Description
544 * Recursive function to select the current page in the treeview
547 static void select_page(GtkTreeView
*treeview
,
548 GtkTreeIter
*parent
, PAGE
*page
)
550 GtkTreeModel
*treemodel
= gtk_tree_view_get_model (treeview
);
554 if (!gtk_tree_model_iter_children (treemodel
, &iter
, parent
)) {
559 gtk_tree_model_get (treemodel
, &iter
,
560 COLUMN_PAGE
, &p_current
,
562 if (p_current
== page
) {
563 gtk_tree_view_expand_all (treeview
);
564 gtk_tree_selection_select_iter (
565 gtk_tree_view_get_selection (treeview
),
570 select_page (treeview
, &iter
, page
);
572 } while (gtk_tree_model_iter_next (treemodel
, &iter
));
576 /*! \todo Finish function documentation!!!
578 * \par Function Description
581 void pagesel_update (Pagesel
*pagesel
)
587 g_assert (IS_PAGESEL (pagesel
));
589 g_return_if_fail (GSCHEM_DIALOG (pagesel
)->toplevel
);
591 toplevel
= GSCHEM_DIALOG (pagesel
)->toplevel
;
592 model
= gtk_tree_view_get_model (pagesel
->treeview
);
594 /* wipe out every thing in the store */
595 gtk_tree_store_clear (GTK_TREE_STORE (model
));
597 for (p_current
= toplevel
->page_head
->next
;
599 p_current
= p_current
->next
) {
600 /* find every page that is not a hierarchy-down of another page */
601 if (p_current
->up
< 0 ||
602 s_hierarchy_find_page (toplevel
->page_head
->next
,
603 p_current
->up
) == NULL
) {
604 add_page (model
, NULL
, p_current
);
608 /* select the current page in the treeview */
609 select_page (pagesel
->treeview
, NULL
, toplevel
->page_current
);