Bumped versions to 1.1.2/20070818/30:2:0 for the next development snapshot
[geda-gaf/whiteaudio.git] / gschem / src / x_pagesel.c
blob1bd43288793d65232e809008eda97e3e9775e4d8
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
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 <libgeda/libgeda.h>
32 #include "../include/globals.h"
33 #include "../include/prototype.h"
35 #ifdef HAVE_LIBDMALLOC
36 #include <dmalloc.h>
37 #endif
39 #include "../include/gschem_dialog.h"
40 #include "../include/x_pagesel.h"
43 static void x_pagesel_callback_response (GtkDialog *dialog,
44 gint arg1,
45 gpointer user_data);
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,
60 /* GschemDialog */
61 "settings-name", "pagesel",
62 "toplevel", toplevel,
63 NULL));
65 g_signal_connect (toplevel->pswindow,
66 "response",
67 G_CALLBACK (x_pagesel_callback_response),
68 toplevel);
70 gtk_widget_show (toplevel->pswindow);
71 } else {
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,
117 gint arg1,
118 gpointer user_data)
120 TOPLEVEL *toplevel = (TOPLEVEL*)user_data;
122 switch (arg1) {
123 case PAGESEL_RESPONSE_UPDATE:
124 pagesel_update (PAGESEL (dialog));
125 break;
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;
131 break;
132 default:
133 g_assert_not_reached ();
138 enum {
139 COLUMN_PAGE,
140 COLUMN_NAME,
141 COLUMN_CHANGED,
142 NUM_COLUMNS
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!!!
153 * \brief
154 * \par Function Description
157 static void pagesel_callback_selection_changed (GtkTreeSelection *selection,
158 gpointer user_data)
160 GtkTreeModel *model;
161 GtkTreeIter iter;
162 Pagesel *pagesel = (Pagesel*)user_data;
163 TOPLEVEL *toplevel;
164 PAGE *page;
166 if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
167 return;
170 toplevel = GSCHEM_DIALOG (pagesel)->toplevel;
171 gtk_tree_model_get (model, &iter,
172 COLUMN_PAGE, &page,
173 -1);
175 /* temp */
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!!!
189 * \brief
190 * \par Function Description
193 static gboolean pagesel_callback_button_pressed (GtkWidget *widget,
194 GdkEventButton *event,
195 gpointer user_data)
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);
202 ret = TRUE;
205 return ret;
208 /*! \todo Finish function documentation!!!
209 * \brief
210 * \par Function Description
213 static gboolean pagesel_callback_popup_menu (GtkWidget *widget,
214 gpointer user_data)
216 Pagesel *pagesel = (Pagesel*)user_data;
218 pagesel_popup_menu (pagesel, NULL);
220 return TRUE;
223 #define DEFINE_POPUP_CALLBACK(name, action) \
224 static void \
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)
251 GtkTreePath *path;
252 GtkWidget *menu;
253 struct menuitem_t {
254 gchar *label;
255 GCallback callback;
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) },
260 { "-", NULL },
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) },
264 { NULL, NULL } };
265 struct menuitem_t *tmp;
267 if (event != NULL &&
268 gtk_tree_view_get_path_at_pos (pagesel->treeview,
269 (gint)event->x,
270 (gint)event->y,
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++) {
282 GtkWidget *menuitem;
283 if (g_strcasecmp (tmp->label, "-") == 0) {
284 menuitem = gtk_separator_menu_item_new ();
285 } else {
286 menuitem = gtk_menu_item_new_with_label (_(tmp->label));
287 g_signal_connect (menuitem,
288 "activate",
289 tmp->callback,
290 pagesel);
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,
315 GParamSpec *arg1,
316 gpointer user_data)
318 Pagesel *pagesel = PAGESEL( gobject );
320 pagesel_update( pagesel );
324 /*! \todo Finish function documentation!!!
325 * \brief
326 * \par Function Description
329 GType pagesel_get_type()
331 static GType pagesel_type = 0;
333 if (!pagesel_type) {
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 */
341 sizeof(Pagesel),
342 0, /* n_preallocs */
343 (GInstanceInitFunc) pagesel_init,
346 pagesel_type = g_type_register_static (GSCHEM_TYPE_DIALOG,
347 "Pagesel",
348 &pagesel_info, 0);
351 return pagesel_type;
354 /*! \todo Finish function documentation!!!
355 * \brief
356 * \par Function Description
359 static void pagesel_class_init (PageselClass *klass)
363 /*! \todo Finish function documentation!!!
364 * \brief
365 * \par Function Description
368 static void pagesel_init (Pagesel *pagesel)
370 GtkWidget *scrolled_win, *treeview, *label;
371 GtkTreeModel *store;
372 GtkCellRenderer *renderer;
373 GtkTreeViewColumn *column;
374 GtkTreeSelection *selection;
376 /* dialog initialization */
377 g_object_set (G_OBJECT (pagesel),
378 /* GtkContainer */
379 "border-width", 0,
380 /* GtkWindow */
381 "type", GTK_WINDOW_TOPLEVEL,
382 "title", _("Page Manager"),
383 "default-height", 180,
384 "default-width", 515,
385 "modal", FALSE,
386 "window-position", GTK_WIN_POS_NONE,
387 "type-hint", GDK_WINDOW_TYPE_HINT_NORMAL,
388 /* GtkDialog */
389 "has-separator", TRUE,
390 NULL);
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,
401 /* GtkContainer */
402 "border-width", 5,
403 /* GtkScrolledWindow */
404 "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
405 "vscrollbar-policy", GTK_POLICY_ALWAYS,
406 "shadow-type", GTK_SHADOW_ETCHED_IN,
407 NULL));
408 /* create the treeview */
409 treeview = GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW,
410 /* GtkTreeView */
411 "model", store,
412 "rules-hint", TRUE,
413 NULL));
414 g_signal_connect (treeview,
415 "button-press-event",
416 G_CALLBACK (pagesel_callback_button_pressed),
417 pagesel);
418 g_signal_connect (treeview,
419 "popup-menu",
420 G_CALLBACK (pagesel_callback_popup_menu),
421 pagesel);
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,
426 "changed",
427 G_CALLBACK (pagesel_callback_selection_changed),
428 pagesel);
429 /* - first column: page name */
430 renderer = GTK_CELL_RENDERER (
431 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
432 /* GtkCellRendererText */
433 "editable", FALSE,
434 NULL));
435 column = GTK_TREE_VIEW_COLUMN (
436 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
437 /* GtkTreeViewColumn */
438 "title", _("Filename"),
439 "min-width", 400,
440 "resizable", TRUE,
441 NULL));
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,
450 NULL));
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,
456 NULL));
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,
468 TRUE, TRUE, 0);
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,
473 /* GtkLabel */
474 "label", _("Right click on the filename for more options..."),
475 NULL));
476 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (pagesel)->vbox), label,
477 FALSE, TRUE, 5);
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,
484 /* - close button */
485 GTK_STOCK_CLOSE, PAGESEL_RESPONSE_CLOSE,
486 NULL);
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,
493 -1);
494 #endif
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,
514 PAGE *page)
516 GtkTreeIter iter;
517 PAGE *p_current;
519 /* add the page to the store */
520 gtk_tree_store_append (GTK_TREE_STORE (model),
521 &iter,
522 parent);
523 gtk_tree_store_set (GTK_TREE_STORE (model),
524 &iter,
525 COLUMN_PAGE, page,
526 COLUMN_NAME, page->page_filename,
527 COLUMN_CHANGED, page->CHANGED,
528 -1);
530 /* search a page that has a up field == p_current->pid */
531 for (p_current = page->next;
532 p_current != NULL;
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!!!
542 * \brief
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);
551 GtkTreeIter iter;
552 PAGE *p_current;
554 if (!gtk_tree_model_iter_children (treemodel, &iter, parent)) {
555 return;
558 do {
559 gtk_tree_model_get (treemodel, &iter,
560 COLUMN_PAGE, &p_current,
561 -1);
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),
566 &iter);
567 return;
570 select_page (treeview, &iter, page);
572 } while (gtk_tree_model_iter_next (treemodel, &iter));
576 /*! \todo Finish function documentation!!!
577 * \brief
578 * \par Function Description
581 void pagesel_update (Pagesel *pagesel)
583 GtkTreeModel *model;
584 TOPLEVEL *toplevel;
585 PAGE *p_current;
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));
596 /* now rebuild */
597 for (p_current = toplevel->page_head->next;
598 p_current != NULL;
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);