1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 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
31 #include "../include/gschem_pagesel_dockable.h"
32 #include "actions.decl.x"
35 static void pagesel_popup_menu (GschemPageselDockable
*pagesel
,
36 GdkEventButton
*event
);
37 static void pagesel_update (GschemPageselDockable
*pagesel
);
39 static void pagesel_class_init (GschemPageselDockableClass
*class);
40 static GtkWidget
*pagesel_create_widget (GschemDockable
*dockable
);
44 /*! \brief Update the list and status of <B>toplevel</B>'s pages.
45 * \par Function Description
46 * Updates the list and status of <B>toplevel</B>\'s pages if the page
47 * manager dialog is opened.
49 * \param [in] w_current The GschemToplevel object to update.
51 void x_pagesel_update (GschemToplevel
*w_current
)
53 if (w_current
->pagesel_dockable
!= NULL
)
54 pagesel_update (GSCHEM_PAGESEL_DOCKABLE (w_current
->pagesel_dockable
));
56 i_update_filename (w_current
);
68 /*! \todo Finish function documentation!!!
70 * \par Function Description
73 static void pagesel_callback_selection_changed (GtkTreeSelection
*selection
,
78 GschemPageselDockable
*pagesel
= GSCHEM_PAGESEL_DOCKABLE (user_data
);
79 GschemToplevel
*w_current
;
82 if (!gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
86 w_current
= GSCHEM_DOCKABLE (pagesel
)->w_current
;
87 gtk_tree_model_get (model
, &iter
,
91 x_window_set_current_page (w_current
, page
);
94 static void pagesel_callback_row_activated (GtkTreeView
*tree_view
,
96 GtkTreeViewColumn
*column
,
99 GschemDockable
*dockable
= GSCHEM_DOCKABLE (user_data
);
100 GschemToplevel
*w_current
= dockable
->w_current
;
105 model
= gtk_tree_view_get_model (tree_view
);
106 gtk_tree_model_get_iter (model
, &iter
, path
);
107 gtk_tree_model_get (model
, &iter
, COLUMN_PAGE
, &page
, -1);
109 x_window_set_current_page (w_current
, page
);
111 if (gschem_dockable_get_state (dockable
) == GSCHEM_DOCKABLE_STATE_DIALOG
)
112 gschem_dockable_hide (dockable
);
114 x_window_present (w_current
);
115 gtk_widget_grab_focus (w_current
->drawing_area
);
118 /*! \todo Finish function documentation!!!
120 * \par Function Description
123 static gboolean
pagesel_callback_button_pressed (GtkWidget
*widget
,
124 GdkEventButton
*event
,
127 GschemPageselDockable
*pagesel
= GSCHEM_PAGESEL_DOCKABLE (user_data
);
128 gboolean ret
= FALSE
;
130 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3) {
131 pagesel_popup_menu (pagesel
, event
);
138 /*! \todo Finish function documentation!!!
140 * \par Function Description
143 static gboolean
pagesel_callback_popup_menu (GtkWidget
*widget
,
146 GschemPageselDockable
*pagesel
= GSCHEM_PAGESEL_DOCKABLE (user_data
);
148 pagesel_popup_menu (pagesel
, NULL
);
153 static void pagesel_callback_popup (GtkMenuItem
*menuitem
,
156 gschem_action_activate (g_object_get_data (G_OBJECT (menuitem
), "action"),
157 GSCHEM_DOCKABLE (user_data
)->w_current
);
160 /*! \brief Popup context-sensitive menu.
161 * \par Function Description
162 * Pops up a context-sensitive menu.
164 * <B>event</B> can be NULL if the popup is triggered by a key binding
165 * instead of a mouse click.
167 * \param [in] pagesel The GschemPageselDockable object.
168 * \param [in] event Mouse click event info.
170 static void pagesel_popup_menu (GschemPageselDockable
*pagesel
,
171 GdkEventButton
*event
)
174 GtkWidget
*menu
, *menuitem
;
177 GschemAction
*action
;
179 struct menuitem_t menuitems
[] = {
180 { N_("New Page"), action_file_new
},
181 { N_("Open Page..."), action_file_open
},
183 { N_("Save Page"), action_file_save
},
184 { N_("Close Page"), action_page_close
},
186 struct menuitem_t
*tmp
;
189 gtk_tree_view_get_path_at_pos (pagesel
->treeview
,
192 &path
, NULL
, NULL
, NULL
)) {
193 GtkTreeSelection
*selection
;
194 selection
= gtk_tree_view_get_selection (pagesel
->treeview
);
195 gtk_tree_selection_unselect_all (selection
);
196 gtk_tree_selection_select_path (selection
, path
);
197 gtk_tree_path_free (path
);
200 /* create the context menu */
201 menu
= gtk_menu_new();
203 menuitem
= gtk_image_menu_item_new_with_mnemonic (_("_Refresh List"));
204 gtk_image_menu_item_set_image (
205 GTK_IMAGE_MENU_ITEM (menuitem
),
206 gtk_image_new_from_stock (GTK_STOCK_REFRESH
, GTK_ICON_SIZE_MENU
));
207 g_signal_connect_swapped (menuitem
, "activate",
208 G_CALLBACK (pagesel_update
), pagesel
);
209 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
211 menuitem
= gtk_separator_menu_item_new ();
212 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
214 for (tmp
= menuitems
; tmp
->label
!= NULL
; tmp
++) {
215 if (strcmp (tmp
->label
, "-") == 0) {
216 menuitem
= gtk_separator_menu_item_new ();
218 menuitem
= gtk_menu_item_new_with_label (_(tmp
->label
));
219 g_object_set_data (G_OBJECT (menuitem
), "action", tmp
->action
);
220 g_signal_connect (menuitem
,
222 G_CALLBACK (pagesel_callback_popup
),
225 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
227 gtk_widget_show_all (menu
);
228 /* make menu a popup menu */
229 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
230 (event
!= NULL
) ? event
->button
: 0,
231 gdk_event_get_time ((GdkEvent
*)event
));
236 /*! \brief Handler for the notify::gschem-toplevel signal of GschemDialog
238 * \par Function Description
240 * When the gschem-toplevel property is set on the parent GschemDialog,
241 * we should update the pagesel dialog.
243 * \param [in] gobject the object which received the signal.
244 * \param [in] arg1 the GParamSpec of the property which changed
245 * \param [in] user_data user data set when the signal handler was connected.
247 static void notify_gschem_toplevel_cb (GObject
*gobject
,
251 GschemPageselDockable
*pagesel
= GSCHEM_PAGESEL_DOCKABLE (gobject
);
253 pagesel_update( pagesel
);
257 /*! \todo Finish function documentation!!!
259 * \par Function Description
262 GType
gschem_pagesel_dockable_get_type()
264 static GType pagesel_type
= 0;
267 static const GTypeInfo pagesel_info
= {
268 sizeof (GschemPageselDockableClass
),
269 NULL
, /* base_init */
270 NULL
, /* base_finalize */
271 (GClassInitFunc
) pagesel_class_init
,
272 NULL
, /* class_finalize */
273 NULL
, /* class_data */
274 sizeof (GschemPageselDockable
),
276 NULL
/* instance_init */
279 pagesel_type
= g_type_register_static (GSCHEM_TYPE_DOCKABLE
,
280 "GschemPageselDockable",
287 /*! \todo Finish function documentation!!!
289 * \par Function Description
292 static void pagesel_class_init (GschemPageselDockableClass
*class)
294 GSCHEM_DOCKABLE_CLASS (class)->create_widget
= pagesel_create_widget
;
297 /*! \todo Finish function documentation!!!
299 * \par Function Description
302 static GtkWidget
*pagesel_create_widget (GschemDockable
*dockable
)
304 GschemPageselDockable
*pagesel
= GSCHEM_PAGESEL_DOCKABLE (dockable
);
305 GtkWidget
*scrolled_win
, *treeview
;
307 GtkCellRenderer
*renderer
;
308 GtkTreeViewColumn
*column
;
309 GtkTreeSelection
*selection
;
311 /* create the model for the treeview */
312 store
= (GtkTreeModel
*)gtk_tree_store_new (NUM_COLUMNS
,
313 G_TYPE_POINTER
, /* page */
314 G_TYPE_STRING
, /* basename */
315 G_TYPE_STRING
, /* path */
316 G_TYPE_BOOLEAN
); /* changed */
318 /* create a scrolled window for the treeview */
319 scrolled_win
= GTK_WIDGET (
320 g_object_new (GTK_TYPE_SCROLLED_WINDOW
,
323 /* GtkScrolledWindow */
324 "hscrollbar-policy", GTK_POLICY_AUTOMATIC
,
325 "vscrollbar-policy", GTK_POLICY_ALWAYS
,
327 /* create the treeview */
328 treeview
= GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW
,
331 "tooltip-column", COLUMN_PATH
,
333 g_signal_connect (treeview
,
335 G_CALLBACK (pagesel_callback_row_activated
),
337 g_signal_connect (treeview
,
338 "button-press-event",
339 G_CALLBACK (pagesel_callback_button_pressed
),
341 g_signal_connect (treeview
,
343 G_CALLBACK (pagesel_callback_popup_menu
),
345 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview
));
346 gtk_tree_selection_set_mode (selection
,
347 GTK_SELECTION_SINGLE
);
348 g_signal_connect (selection
,
350 G_CALLBACK (pagesel_callback_selection_changed
),
352 /* - first column: changed */
353 renderer
= GTK_CELL_RENDERER (
354 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT
,
355 /* GtkCellRendererText */
358 /* GtkCellRenderer */
361 column
= GTK_TREE_VIEW_COLUMN (
362 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
363 /* GtkTreeViewColumn */
366 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
367 gtk_tree_view_column_add_attribute (column
, renderer
, "visible", COLUMN_CHANGED
);
368 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
369 /* - second column: page name */
370 renderer
= GTK_CELL_RENDERER (
371 g_object_new (GTK_TYPE_CELL_RENDERER_TEXT
,
372 /* GtkCellRendererText */
375 column
= GTK_TREE_VIEW_COLUMN (
376 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
377 /* GtkTreeViewColumn */
378 "title", _("Filename"),
380 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
381 gtk_tree_view_column_add_attribute (column
, renderer
, "text", COLUMN_BASENAME
);
382 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
383 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (treeview
), column
);
385 /* add the treeview to the scrolled window */
386 gtk_container_add (GTK_CONTAINER (scrolled_win
), treeview
);
387 /* set treeview of pagesel */
388 pagesel
->treeview
= GTK_TREE_VIEW (treeview
);
390 /* add the scrolled window to the dialog vbox */
391 gtk_widget_show_all (scrolled_win
);
393 g_signal_connect( pagesel
, "notify::gschem-toplevel",
394 G_CALLBACK( notify_gschem_toplevel_cb
), NULL
);
396 pagesel_update (pagesel
);
401 /*! \brief Update tree model of <B>pagesel</B>'s treeview.
402 * \par Function Description
403 * Updates the tree model of <B>pagesel</B>\'s treeview.
405 * Right now, each time it is called, it rebuilds all the model from the
406 * list of pages passed in.
407 * It is a recursive function to populate the tree store
409 * \param [in] model GtkTreeModel to update.
410 * \param [in] parent GtkTreeIter pointer to tree root.
411 * \param [in] pages GedaPageList of pages for this toplevel.
412 * \param [in] page The PAGE object to update tree model from.
414 static void add_page (GtkTreeModel
*model
, GtkTreeIter
*parent
,
415 GedaPageList
*pages
, PAGE
*page
)
421 /* add the page to the store */
422 gtk_tree_store_append (GTK_TREE_STORE (model
),
426 if (page
->is_untitled
)
427 gtk_tree_store_set (GTK_TREE_STORE (model
),
430 COLUMN_BASENAME
, _("(untitled page)"),
432 COLUMN_CHANGED
, page
->CHANGED
,
435 /* get basename and abbreviated path */
436 gchar
*basename
= g_path_get_basename (page
->page_filename
);
438 const gchar
*homedir
= g_get_home_dir ();
439 size_t hd_len
= strlen (homedir
);
440 if (hd_len
> 1 && strncmp (page
->page_filename
, homedir
, hd_len
) == 0 &&
441 (homedir
[hd_len
- 1] == '/' || page
->page_filename
[hd_len
] == '/'))
442 path
= g_strdup_printf (
443 "~/%s", page
->page_filename
+ hd_len
+ (homedir
[hd_len
- 1] != '/'));
445 gtk_tree_store_set (GTK_TREE_STORE (model
),
448 COLUMN_BASENAME
, basename
,
449 COLUMN_PATH
, path
!= NULL
? path
: page
->page_filename
,
450 COLUMN_CHANGED
, page
->CHANGED
,
457 /* search a page that has a up field == p_current->pid */
458 for ( p_iter
= geda_list_get_glist( pages
);
460 p_iter
= g_list_next( p_iter
) ) {
462 p_current
= (PAGE
*)p_iter
->data
;
463 if (p_current
->up
== page
->pid
) {
464 add_page (model
, &iter
, pages
, p_current
);
469 /*! \todo Finish function documentation!!!
471 * \par Function Description
472 * Recursive function to select the current page in the treeview
475 static void select_page(GtkTreeView
*treeview
,
476 GtkTreeIter
*parent
, PAGE
*page
)
478 GtkTreeModel
*treemodel
= gtk_tree_view_get_model (treeview
);
482 if (!gtk_tree_model_iter_children (treemodel
, &iter
, parent
)) {
487 gtk_tree_model_get (treemodel
, &iter
,
488 COLUMN_PAGE
, &p_current
,
490 if (p_current
== page
) {
491 gtk_tree_view_expand_all (treeview
);
492 gtk_tree_selection_select_iter (
493 gtk_tree_view_get_selection (treeview
),
498 select_page (treeview
, &iter
, page
);
500 } while (gtk_tree_model_iter_next (treemodel
, &iter
));
504 /*! \todo Finish function documentation!!!
506 * \par Function Description
509 static void pagesel_update (GschemPageselDockable
*pagesel
)
516 g_return_if_fail (GSCHEM_IS_PAGESEL_DOCKABLE (pagesel
));
518 if (GSCHEM_PAGESEL_DOCKABLE (pagesel
)->treeview
== NULL
)
521 g_return_if_fail (GSCHEM_DOCKABLE (pagesel
)->w_current
);
523 toplevel
= gschem_toplevel_get_toplevel (GSCHEM_DOCKABLE (pagesel
)->w_current
);
524 model
= gtk_tree_view_get_model (pagesel
->treeview
);
526 /* wipe out every thing in the store */
527 gtk_tree_store_clear (GTK_TREE_STORE (model
));
529 for ( iter
= geda_list_get_glist( toplevel
->pages
);
531 iter
= g_list_next( iter
) ) {
533 p_current
= (PAGE
*)iter
->data
;
534 /* find every page that is not a hierarchy-down of another page */
535 if (p_current
->up
< 0 ||
536 s_page_search_by_page_id (toplevel
->pages
,
537 p_current
->up
) == NULL
) {
538 add_page (model
, NULL
, toplevel
->pages
, p_current
);
542 /* select the current page in the treeview */
543 select_page (pagesel
->treeview
, NULL
, toplevel
->page_current
);