diff --git a/src/callbacks.c b/src/callbacks.c
index db5154e..b23de37 100644
--- a/src/callbacks.c
+++ b/src/callbacks.c
@@ -90,21 +90,6 @@ static gboolean insert_callback_from_menu = FALSE;
 /*static gboolean switch_tv_notebook_page = FALSE; */
 
 
-static gboolean check_no_unsaved(void)
-{
-	guint i;
-
-	for (i = 0; i < documents_array->len; i++)
-	{
-		if (documents[i]->is_valid && documents[i]->changed)
-		{
-			return FALSE;
-		}
-	}
-	return TRUE;	/* no unsaved edits */
-}
-
-
 /* set editor_info.click_pos to the current cursor position if insert_callback_from_menu is TRUE
  * to prevent invalid cursor positions which can cause segfaults */
 static void verify_click_pos(GeanyDocument *doc)
@@ -117,42 +102,26 @@ static void verify_click_pos(GeanyDocument *doc)
 }
 
 
-/* should only be called from on_exit_clicked */
-static void quit_app(void)
-{
-	configuration_save();
-
-	if (app->project != NULL)
-		project_close(FALSE);	/* save project session files */
-
-	document_close_all();
-
-	main_status.quitting = TRUE;
-
-	main_quit();
-}
-
-
 /* wrapper function to abort exit process if cancel button is pressed */
 gboolean
 on_exit_clicked                        (GtkWidget *widget, gpointer gdata)
 {
 	main_status.quitting = TRUE;
 
-	if (! check_no_unsaved())
+	gboolean quit = TRUE;
+	if (!document_any_unsaved() && prefs.confirm_exit)
 	{
-		if (document_account_for_unsaved())
-		{
-			quit_app();
-			return FALSE;
-		}
+		/*
+		 * Ask for confirmation only if there are no unsaved documents.
+		 * If any documents are unsaved, user will be asked in main_save().
+		 */
+		if (!dialogs_show_question_full(NULL, GTK_STOCK_QUIT, GTK_STOCK_CANCEL, NULL, _("Do you really want to quit?")))
+			quit = FALSE;
 	}
-	else
-	if (! prefs.confirm_exit ||
-		dialogs_show_question_full(NULL, GTK_STOCK_QUIT, GTK_STOCK_CANCEL, NULL,
-			_("Do you really want to quit?")))
+
+	if (quit && main_save(TRUE))
 	{
-		quit_app();
+		main_finalize();
 		return FALSE;
 	}
 
@@ -239,7 +208,7 @@ void
 on_close_all1_activate                 (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 {
-	document_close_all();
+	document_close_all(FALSE);
 }
 
 
@@ -1824,7 +1793,7 @@ void
 on_project_close1_activate             (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 {
-	project_close(TRUE);
+	project_close(TRUE, TRUE);
 }
 
 
diff --git a/src/document.c b/src/document.c
index b3471ea..04facea 100644
--- a/src/document.c
+++ b/src/document.c
@@ -2850,6 +2850,17 @@ GeanyDocument *document_clone(GeanyDocument *old_doc, const gchar *utf8_filename
 }
 
 
+/* @return TRUE if there are some unsaved documents */
+gboolean document_any_unsaved(void)
+{
+	guint i;
+	foreach_document(i)
+		if (documents[i]->changed)
+			return TRUE;
+	return FALSE;
+}
+
+
 /* @note If successful, this should always be followed up with a call to
  * document_close_all().
  * @return TRUE if all files were saved or had their changes discarded. */
@@ -2904,13 +2915,12 @@ static void force_close_all(void)
 }
 
 
-gboolean document_close_all(void)
+gboolean document_close_all(gboolean force)
 {
-	if (! document_account_for_unsaved())
+	if (!force && !document_account_for_unsaved())
 		return FALSE;
 
 	force_close_all();
-
 	return TRUE;
 }
 
diff --git a/src/document.h b/src/document.h
index 541b49a..31a86b4 100644
--- a/src/document.h
+++ b/src/document.h
@@ -196,9 +196,11 @@ void document_try_focus(GeanyDocument *doc);
 
 gboolean document_close(GeanyDocument *doc);
 
+gboolean document_any_unsaved(void);
+
 gboolean document_account_for_unsaved(void);
 
-gboolean document_close_all(void);
+gboolean document_close_all(gboolean force);
 
 GeanyDocument *document_clone(GeanyDocument *old_doc, const gchar *utf8_filename);
 
diff --git a/src/main.c b/src/main.c
index b78acb9..8055dc8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1066,10 +1066,28 @@ static void queue_free(GQueue *queue)
 }
 
 
-void main_quit()
+gboolean main_save(gboolean interactive)
+{
+	if (interactive && !document_account_for_unsaved())
+		return FALSE;
+
+	configuration_save();
+
+	if (app->project)
+		project_save(FALSE);
+
+	return TRUE;
+}
+
+
+void main_finalize()
 {
 	geany_debug("Quitting...");
 
+	if (app->project)
+		project_close(FALSE, FALSE);
+	document_close_all(TRUE);
+
 #ifdef HAVE_SOCKET
 	socket_finalize();
 #endif
diff --git a/src/main.h b/src/main.h
index 3c4f41a..b5a6716 100644
--- a/src/main.h
+++ b/src/main.h
@@ -56,7 +56,9 @@ const gchar *main_get_version_string(void);
 
 gchar *main_get_argv_filename(const gchar *filename);
 
-void main_quit(void);
+gboolean main_save(gboolean interactive);
+
+void main_finalize(void);
 
 gboolean main_handle_filename(const gchar *locale_filename);
 
diff --git a/src/project.c b/src/project.c
index 6a6cf7e..07e311b 100644
--- a/src/project.c
+++ b/src/project.c
@@ -335,15 +335,22 @@ static void remove_foreach_project_filetype(gpointer data, gpointer user_data)
 }
 
 
+void project_save(gboolean emit_signal)
+{
+	write_config(emit_signal);
+}
+
+
 /* open_default will make function reload default session files on close */
-void project_close(gboolean open_default)
+void project_close(gboolean save_config, gboolean open_default)
 {
 	g_return_if_fail(app->project != NULL);
 
 	ui_set_statusbar(TRUE, _("Project \"%s\" closed."), app->project->name);
 
 	/* use write_config() to save project session files */
-	write_config(FALSE);
+	if (save_config)
+		write_config(FALSE);
 
 	/* remove project filetypes build entries */
 	if (app->project->build_filetypes_list != NULL)
@@ -370,7 +377,7 @@ void project_close(gboolean open_default)
 	if (project_prefs.project_session)
 	{
 		/* close all existing tabs first */
-		document_close_all();
+		document_close_all(FALSE);
 
 		/* after closing all tabs let's open the tabs found in the default config */
 		if (open_default && cl_options.load_session)
@@ -601,7 +608,7 @@ gboolean project_ask_close(void)
 			_("Do you want to close it before proceeding?"),
 			_("The '%s' project is already open."), app->project->name))
 		{
-			project_close(FALSE);
+			project_close(TRUE, FALSE);
 			return TRUE;
 		}
 		else
@@ -976,7 +983,7 @@ static gboolean load_config(const gchar *filename)
 		/* save current (non-project) session (it could has been changed since program startup) */
 		configuration_save_default_session();
 		/* now close all open files */
-		document_close_all();
+		document_close_all(FALSE);
 		/* read session files so they can be opened with configuration_open_files() */
 		configuration_load_session_files(config, FALSE);
 	}
diff --git a/src/project.h b/src/project.h
index beccb2f..f72cbf6 100644
--- a/src/project.h
+++ b/src/project.h
@@ -68,7 +68,9 @@ void project_new(void);
 
 void project_open(void);
 
-void project_close(gboolean open_default);
+void project_save(gboolean emit_signal);
+
+void project_close(gboolean save_config, gboolean open_default);
 
 void project_properties(void);
 
