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
20 /*! \todo STILL NEED to clean up line lengths in aa and tr */
33 #define GLADE_HOOKUP_OBJECT(component,widget,name) \
34 g_object_set_data_full (G_OBJECT (component), name, \
35 gtk_widget_ref (widget), (GDestroyNotify) g_object_unref)
39 /***************** Start of Close Confirmation dialog box ************/
41 #define TYPE_CLOSE_CONFIRMATION_DIALOG (close_confirmation_dialog_get_type ())
42 #define CLOSE_CONFIRMATION_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_CLOSE_CONFIRMATION_DIALOG, CloseConfirmationDialog))
43 #define CLOSE_CONFIRMATION_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_CLOSE_CONFIRMATION_DIALOG, CloseConfirmationDialogClass))
44 #define IS_CLOSE_CONFIRMATION_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_CLOSE_CONFIRMATION_DIALOG))
45 #define IS_CLOSE_CONFIRMATION_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_CLOSE_CONFIRMATION_DIALOG))
46 #define CLOSE_CONFIRMATION_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),TYPE_CLOSE_CONFIRMATION_DIALOG, CloseConfirmationDialogClass))
49 typedef struct _CloseConfirmationDialog CloseConfirmationDialog
;
50 typedef struct _CloseConfirmationDialogClass CloseConfirmationDialogClass
;
52 struct _CloseConfirmationDialog
56 GschemToplevel
*w_current
;
58 GtkListStore
*store_unsaved_pages
;
61 struct _CloseConfirmationDialogClass
63 GtkDialogClass parent_class
;
68 PROP_GSCHEM_TOPLEVEL
= 1,
81 static gpointer close_confirmation_dialog_parent_class
= NULL
;
84 static void close_confirmation_dialog_class_init (CloseConfirmationDialogClass
*klass
);
85 static void close_confirmation_dialog_init (CloseConfirmationDialog
*self
);
86 static void close_confirmation_dialog_set_property (GObject
*object
,
90 static void close_confirmation_dialog_get_property (GObject
*object
,
94 static GObject
* close_confirmation_dialog_constructor (GType type
,
95 guint n_construct_properties
,
96 GObjectConstructParam
*construct_params
);
98 GList
*close_confirmation_dialog_get_selected_pages (CloseConfirmationDialog
*dialog
);
103 close_confirmation_dialog_get_type ()
105 static GType close_confirmation_dialog_type
= 0;
107 if (!close_confirmation_dialog_type
) {
108 static const GTypeInfo close_confirmation_dialog_info
= {
109 sizeof(CloseConfirmationDialogClass
),
110 NULL
, /* base_init */
111 NULL
, /* base_finalize */
112 (GClassInitFunc
) close_confirmation_dialog_class_init
,
113 NULL
, /* class_finalize */
114 NULL
, /* class_data */
115 sizeof(CloseConfirmationDialog
),
117 (GInstanceInitFunc
) close_confirmation_dialog_init
,
120 close_confirmation_dialog_type
=
121 g_type_register_static (GTK_TYPE_DIALOG
,
122 "CloseConfirmationDialog",
123 &close_confirmation_dialog_info
, 0);
126 return close_confirmation_dialog_type
;
130 close_confirmation_dialog_class_init (CloseConfirmationDialogClass
*klass
)
132 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
134 close_confirmation_dialog_parent_class
= g_type_class_peek_parent (klass
);
136 gobject_class
->constructor
= close_confirmation_dialog_constructor
;
137 gobject_class
->set_property
= close_confirmation_dialog_set_property
;
138 gobject_class
->get_property
= close_confirmation_dialog_get_property
;
140 g_object_class_install_property (
141 gobject_class
, PROP_GSCHEM_TOPLEVEL
,
142 g_param_spec_pointer ("gschem-toplevel",
145 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
146 g_object_class_install_property (
147 gobject_class
, PROP_UNSAVED_PAGE
,
148 g_param_spec_pointer ("unsaved-page",
151 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_WRITABLE
));
152 g_object_class_install_property (
153 gobject_class
, PROP_UNSAVED_PAGES
,
154 g_param_spec_pointer ("unsaved-pages",
157 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_WRITABLE
));
158 g_object_class_install_property (
159 gobject_class
, PROP_SELECTED_PAGES
,
160 g_param_spec_pointer ("selected-pages",
168 close_confirmation_dialog_init (CloseConfirmationDialog
*self
)
170 /* create model for treeview and populate */
171 self
->store_unsaved_pages
= gtk_list_store_new (NUM_COLUMNS
,
172 G_TYPE_BOOLEAN
, /* save? */
173 G_TYPE_POINTER
); /* page */
177 /*! \brief Returns the number of pages in the model.
178 * \par Function Description
179 * This function determines the number of pages with unsaved changes
182 * \param [in] model The tree model.
183 * \returns The number of pages with unsaved changes.
186 count_pages (GtkTreeModel
*model
)
191 gtk_tree_model_get_iter_first (model
, &iter
);
193 gtk_tree_model_iter_next (model
, &iter
);
199 /*! \brief Returns the name to use for the given page in the model.
200 * \par Function Description
201 * This function determines the text to be used to identify a
202 * specific page from the model of pages with unsaved changes.
204 * If <B>piter</B> is NULL, the name for the first page of the model
205 * is returned. Otherwise, it returns the name for the page defined
206 * by the pointed iterator.
208 * The returned value must be freed by caller.
210 * \param [in] model The tree model.
211 * \param [in] piter A pointer on a GtkTreeIter of model or NULL.
212 * \returns The name for the page.
215 get_page_name (GtkTreeModel
*model
, GtkTreeIter
*piter
)
220 g_return_val_if_fail (GTK_IS_TREE_MODEL (model
), NULL
);
223 gtk_tree_model_get_iter_first (model
, &iter
);
228 gtk_tree_model_get (model
, &iter
,
231 g_assert (page
!= NULL
&& page
->page_filename
!= NULL
);
232 if (page
->is_untitled
)
233 return g_strdup (_("Untitled page"));
234 return g_path_get_basename (page
->page_filename
);
237 /*! \brief Sets the contents of the name cell in the treeview of dialog.
238 * \par Function Description
239 * This functions sets the cell of the treeview with the short name
240 * of the page obtained with <B>get_page_name()</B>.
242 * \param [in] tree_column A GtkTreeColumn.
243 * \param [in] cell The GtkCellRenderer that is being rendered by
245 * \param [in] tree_model The GtkTreeModel being rendered.
246 * \param [in] iter A GtkTreeIter of the current row rendered.
250 close_confirmation_dialog_set_page_name (GtkTreeViewColumn
*tree_column
,
251 GtkCellRenderer
*cell
,
252 GtkTreeModel
*tree_model
,
258 page_name
= get_page_name (tree_model
, iter
);
266 /*! \brief Callback function for the toggled signal of check box in treeview.
267 * \par Function Description
268 * This functions changes the value of the save column in the model
269 * for the affected row when user toggles the check box in the
272 * \param [in] cell_renderer The GtkCellRendererToggle.
273 * \param [in] path The GtkTreePath to the concerned row in model.
274 * \param [in] user_data The dialog as user data.
277 close_confirmation_dialog_callback_renderer_toggled (GtkCellRendererToggle
*cell_renderer
,
281 CloseConfirmationDialog
*dialog
= CLOSE_CONFIRMATION_DIALOG (user_data
);
286 model
= GTK_TREE_MODEL (dialog
->store_unsaved_pages
);
288 if (!gtk_tree_model_get_iter_from_string (model
, &iter
, path
)) {
291 gtk_tree_model_get (model
, &iter
,
294 gtk_list_store_set (GTK_LIST_STORE (model
), &iter
,
295 COLUMN_SAVE
, (save
!= TRUE
),
300 /*! \brief Adds a treeview to confirmation dialog for selecting of pages.
301 * \par Function Description
302 * This function adds a treeview and caption to display the content
303 * of the dialog model of pages with unsaved changes.
305 * The treeview displays the page names with check boxes.
307 * \param [in] dialog The dialog.
308 * \returns A pointer on the GtkVBox to add to dialog.
311 close_confirmation_dialog_build_page_list (CloseConfirmationDialog
*dialog
)
313 GtkWidget
*vbox
, *scrolled_window
, *treeview
, *label
;
314 GtkCellRenderer
*renderer
;
315 GtkTreeViewColumn
*column
;
318 /* place the treeview and its caption into their own box */
319 vbox
= GTK_WIDGET (g_object_new (GTK_TYPE_VBOX
,
321 "homogeneous", FALSE
,
325 /* the list of pages with changes */
326 /* - scrolled window as container for the treeview first */
327 scrolled_window
= GTK_WIDGET (g_object_new (GTK_TYPE_SCROLLED_WINDOW
,
328 /* GtkScrolledWindow */
329 "hscrollbar-policy", GTK_POLICY_AUTOMATIC
,
330 "vscrollbar-policy", GTK_POLICY_AUTOMATIC
,
331 "shadow-type", GTK_SHADOW_IN
,
333 /* - then the treeview */
334 /* create model for treeview and populate */
335 treeview
= GTK_WIDGET (g_object_new (GTK_TYPE_TREE_VIEW
,
337 "enable-search", FALSE
,
338 "headers-visible", FALSE
,
339 "model", dialog
->store_unsaved_pages
,
341 renderer
= gtk_cell_renderer_toggle_new ();
342 g_signal_connect (renderer
, "toggled",
344 close_confirmation_dialog_callback_renderer_toggled
),
346 column
= gtk_tree_view_column_new_with_attributes ("Save?",
348 "active", COLUMN_SAVE
,
350 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
352 renderer
= gtk_cell_renderer_text_new ();
353 column
= GTK_TREE_VIEW_COLUMN (
354 g_object_new (GTK_TYPE_TREE_VIEW_COLUMN
,
355 /* GtkTreeViewColumn */
358 gtk_tree_view_column_pack_start (column
, renderer
, TRUE
);
359 gtk_tree_view_column_set_cell_data_func (column
, renderer
,
360 close_confirmation_dialog_set_page_name
,
362 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview
), column
);
364 gtk_container_add (GTK_CONTAINER (scrolled_window
), treeview
);
366 gtk_box_pack_end (GTK_BOX (vbox
), scrolled_window
,
369 /* the caption label above the list of pages */
370 label
= GTK_WIDGET (g_object_new (GTK_TYPE_LABEL
,
376 "mnemonic-widget", treeview
,
378 text
= _("S_elect the files you want to save:");
379 gtk_label_set_text_with_mnemonic (GTK_LABEL (label
), text
);
380 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), treeview
);
381 gtk_box_pack_start (GTK_BOX (vbox
), label
,
388 close_confirmation_dialog_constructor (GType type
,
389 guint n_construct_properties
,
390 GObjectConstructParam
*construct_params
)
393 CloseConfirmationDialog
*dialog
;
394 GtkWidget
*hbox
, *image
, *vbox
, *label
;
396 gboolean ret
, single_page
;
400 /* chain up to constructor of parent class */
402 G_OBJECT_CLASS (close_confirmation_dialog_parent_class
)->constructor (
404 n_construct_properties
,
406 dialog
= CLOSE_CONFIRMATION_DIALOG (object
);
408 g_object_set (dialog
,
410 "has-separator", FALSE
,
413 "skip-taskbar-hint", TRUE
,
417 g_object_set (GTK_DIALOG (dialog
)->vbox
,
421 g_object_set (GTK_DIALOG (dialog
)->action_area
,
428 /* check if there is one or more than one page with changes */
429 ret
= gtk_tree_model_get_iter_first (GTK_TREE_MODEL (
430 dialog
->store_unsaved_pages
),
433 single_page
= !gtk_tree_model_iter_next (GTK_TREE_MODEL (
434 dialog
->store_unsaved_pages
),
437 /* here starts the layout of the dialog */
438 hbox
= GTK_WIDGET (g_object_new (GTK_TYPE_HBOX
,
442 "homogeneous", FALSE
,
447 image
= g_object_new (GTK_TYPE_IMAGE
,
452 "stock", GTK_STOCK_DIALOG_WARNING
,
453 "icon-size", GTK_ICON_SIZE_DIALOG
,
455 gtk_box_pack_start (GTK_BOX (hbox
), image
,
458 /* vertical box on the right hand side of the dialog */
459 vbox
= GTK_WIDGET (g_object_new (GTK_TYPE_VBOX
,
461 "homogeneous", FALSE
,
468 GtkTreeModel
*model
= GTK_TREE_MODEL (dialog
->store_unsaved_pages
);
472 gtk_tree_model_get_iter_first (model
, &iter
);
473 gtk_tree_model_get (model
, &iter
,
476 g_assert (page
!= NULL
);
478 x_window_set_current_page (dialog
->w_current
, page
);
480 if (page
->is_untitled
)
481 tmp
= g_strdup (_("Save changes before closing?"));
483 gchar
*page_name
= g_path_get_basename (page
->page_filename
);
484 tmp
= g_strdup_printf (
485 _("Save the changes to \"%s\" before closing?"),
491 tmp
= g_strdup_printf (
492 _("There are %d files with unsaved changes.\n"
493 "Save changes before closing?"),
494 count_pages (GTK_TREE_MODEL (dialog
->store_unsaved_pages
)));
496 str
= g_strconcat ("<big><b>", tmp
, "</b></big>", NULL
);
498 label
= GTK_WIDGET (g_object_new (GTK_TYPE_LABEL
,
509 gtk_box_pack_start (GTK_BOX (vbox
), label
,
513 /* more than one page with changes, display each page and offer */
514 /* the opportunity to save them before exiting */
515 gtk_box_pack_start (GTK_BOX (vbox
),
516 close_confirmation_dialog_build_page_list (dialog
),
520 /* secondary label */
521 cstr
= _("If you don't save, all your changes will be permanently lost.");
522 label
= GTK_WIDGET (g_object_new (GTK_TYPE_LABEL
,
531 gtk_box_pack_start (GTK_BOX (vbox
), label
,
535 gtk_box_pack_start (GTK_BOX (hbox
), vbox
,
539 /* add buttons to dialog action area */
540 gtk_dialog_add_buttons (GTK_DIALOG (dialog
),
541 _("Close _without saving"), GTK_RESPONSE_NO
,
542 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
543 GTK_STOCK_SAVE
, GTK_RESPONSE_YES
,
546 /* Set the alternative button order (ok, cancel, help) for other systems */
547 gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog
),
553 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_YES
);
555 /* all done, let's show the contents of the dialog */
556 gtk_widget_show_all (hbox
);
558 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), hbox
,
565 close_confirmation_dialog_set_property (GObject
*object
,
570 CloseConfirmationDialog
*dialog
= CLOSE_CONFIRMATION_DIALOG (object
);
575 switch(property_id
) {
576 case PROP_GSCHEM_TOPLEVEL
:
577 dialog
->w_current
= GSCHEM_TOPLEVEL (g_value_get_pointer (value
));
580 case PROP_UNSAVED_PAGE
:
581 data
= g_value_get_pointer (value
);
583 /* add single page to model */
584 gtk_list_store_append (dialog
->store_unsaved_pages
,
586 gtk_list_store_set (dialog
->store_unsaved_pages
,
594 case PROP_UNSAVED_PAGES
:
595 data
= g_value_get_pointer (value
);
596 /* add set of pages to model */
597 for (p_current
= (GList
*)data
;
599 p_current
= g_list_next (p_current
)) {
600 gtk_list_store_append (dialog
->store_unsaved_pages
,
602 gtk_list_store_set (dialog
->store_unsaved_pages
,
605 COLUMN_PAGE
, p_current
->data
,
611 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
617 close_confirmation_dialog_get_property (GObject
*object
,
622 CloseConfirmationDialog
*dialog
= CLOSE_CONFIRMATION_DIALOG (object
);
624 switch(property_id
) {
625 case PROP_GSCHEM_TOPLEVEL
:
626 g_value_set_pointer (value
, dialog
->w_current
);
629 case PROP_SELECTED_PAGES
:
630 g_value_set_pointer (
632 close_confirmation_dialog_get_selected_pages (dialog
));
636 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
641 /*! \brief Helps building a list of selected page to save.
642 * \par Function Description
643 * This is the <B>GtkTreeModelForeachFunc</B> for function
644 * <B>close_confirmation_dialog_get_selected_pages()</B>.
646 * It builds from the tree model a list of PAGEs for which a save
647 * action has been requested. Each selected page is appended to the
648 * GList pointed by <B>data</B>
650 * \param [in] model The tree model.
653 * \param [in] data A pointer on a GList* to fill.
654 * \returns FALSE to continue walking the tree.
657 get_selected_pages (GtkTreeModel
*model
,
665 gtk_tree_model_get (model
, iter
,
670 g_assert (page
!= NULL
);
671 *(GList
**)data
= g_list_append (*(GList
**)data
, page
);
677 /*! \brief Returns a list of the selected pages with changes to save.
678 * \par Function Description
679 * This function returns the pages that the user has selected in the
680 * confirmation dialog.
682 * The returned list must be freed.
684 * \param [in] dialog The dialog.
685 * \returns A GList of selected PAGE* in dialog.
688 close_confirmation_dialog_get_selected_pages (CloseConfirmationDialog
*dialog
)
690 GList
*selected
= NULL
;
692 gtk_tree_model_foreach (GTK_TREE_MODEL (dialog
->store_unsaved_pages
),
693 (GtkTreeModelForeachFunc
)get_selected_pages
,
700 /*! \brief Asks for confirmation before closing a changed page.
701 * \par Function Description
702 * This function asks the user to confirm its closing order for
703 * page <B>page</B> while it still has unsaved changes.
705 * It displays a message dialog inviting the user to cancel the
706 * closing, or to discard the changes or to save the changes to a
709 * \param [in] w_current The toplevel environment.
710 * \param [in] page The page to close.
712 * \return TRUE if okay to continue with closing page, FALSE
716 x_dialog_close_changed_page (GschemToplevel
*w_current
, PAGE
*page
)
720 gboolean result
= FALSE
;
722 g_return_val_if_fail (page
!= NULL
&& page
->CHANGED
, TRUE
);
724 dialog
= GTK_WIDGET (g_object_new (TYPE_CLOSE_CONFIRMATION_DIALOG
,
725 "gschem-toplevel", w_current
,
726 "unsaved-page", page
,
728 /* set default response signal. This is usually triggered by the
730 gtk_dialog_set_default_response(GTK_DIALOG(dialog
),
733 response_id
= gtk_dialog_run (GTK_DIALOG (dialog
));
734 gtk_widget_destroy (dialog
);
736 switch (response_id
) {
737 case GTK_RESPONSE_NO
:
738 /* action selected: close without saving */
739 /* close the page, discard changes */
744 case GTK_RESPONSE_YES
:
745 /* action selected: save */
746 if (x_highlevel_save_page (w_current
, page
))
748 /* no, user has cancelled the save and page has changes */
749 /* do not close page */
752 case GTK_RESPONSE_CANCEL
:
753 /* action selected: cancel */
756 /* Hit when the user breaks out of the dialog with the escape key
757 * or otherwise destroys the dialog window without a proper response */
765 /*! \brief Asks for confirmation before closing a window.
766 * \par Function Description
767 * This function asks the user to confirm its closing order for
770 * The user is given the possibility to save the pages that currently
771 * have unsaved changes, if any.
773 * It returns TRUE if the user really accepts the close of the
774 * window. Otherwise the user has somehow cancelled and the window
775 * must not be closed.
777 * \param [in] w_current The toplevel environment.
778 * \returns TRUE if the window can be closed, FALSE otherwise.
781 x_dialog_close_window (GschemToplevel
*w_current
)
783 TOPLEVEL
*toplevel
= gschem_toplevel_get_toplevel (w_current
);
787 GList
*unsaved_pages
, *p_unsaved
;
788 gboolean ret
= FALSE
;
790 for ( iter
= geda_list_get_glist( toplevel
->pages
), unsaved_pages
= NULL
;
792 iter
= g_list_next( iter
) ) {
794 p_current
= (PAGE
*)iter
->data
;
796 if (p_current
->CHANGED
) {
797 unsaved_pages
= g_list_append (unsaved_pages
, (gpointer
)p_current
);
801 if (unsaved_pages
== NULL
) {
802 /* no page with unsaved changes, close window */
806 dialog
= GTK_WIDGET (g_object_new (TYPE_CLOSE_CONFIRMATION_DIALOG
,
807 "gschem-toplevel", w_current
,
808 "unsaved-pages", unsaved_pages
,
811 gtk_window_set_transient_for (GTK_WINDOW (dialog
),
812 GTK_WINDOW (w_current
->main_window
));
814 g_list_free (unsaved_pages
);
815 unsaved_pages
= NULL
;
817 switch (gtk_dialog_run (GTK_DIALOG (dialog
))) {
818 case GTK_RESPONSE_NO
:
819 /* action selected: close without saving */
820 /* discard changes, ok to close window */
824 case GTK_RESPONSE_YES
:
825 /* action selected: save */
826 g_object_get (dialog
,
827 "selected-pages", &unsaved_pages
,
832 case GTK_RESPONSE_CANCEL
:
833 /* action selected: cancel */
836 /* Hit when the user breaks out of the dialog with the escape key
837 * or otherwise destroys the dialog window without a proper response */
841 gtk_widget_destroy (dialog
);
843 /* if response was "save", save all selected pages */
844 for (p_unsaved
= unsaved_pages
;
846 p_unsaved
= g_list_next (p_unsaved
)) {
847 p_current
= (PAGE
*) p_unsaved
->data
;
849 if (!x_highlevel_save_page (w_current
, p_current
))
850 /* if user cancelled save, do not close window */
853 g_list_free (unsaved_pages
);
858 /***************** End of Close Confirmation dialog box **************/