aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog23
-rw-r--r--embed/ephy-embed.c7
-rw-r--r--embed/ephy-embed.h3
-rw-r--r--embed/mozilla/EphyBrowser.cpp128
-rw-r--r--embed/mozilla/EphyBrowser.h3
-rw-r--r--embed/mozilla/mozilla-embed.cpp13
-rw-r--r--src/ephy-notebook.c25
-rw-r--r--src/ephy-notebook.h2
-rw-r--r--src/ephy-window.c146
9 files changed, 342 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index 8ae658d48..b94a45c62 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2004-01-04 Christian Persch <chpe@cvs.gnome.org>
+
+ * embed/ephy-embed.c: (ephy_embed_has_modified_forms):
+ * embed/ephy-embed.h:
+ * embed/mozilla/EphyBrowser.cpp:
+ * embed/mozilla/EphyBrowser.h:
+ * embed/mozilla/mozilla-embed.cpp:
+
+ Add API to check if an EphyEmbed has forms with user input in them.
+ Currently it required one modified textarea, or two modified text
+ fields.
+
+ * src/ephy-notebook.c: (ephy_notebook_class_init),
+ (close_button_clicked_cb):
+ * src/ephy-notebook.h:
+ * src/ephy-window.c: (confirm_close_with_modified_forms),
+ (ephy_window_delete_event_cb), (tab_delete_cb), (setup_notebook),
+ (ephy_window_init), (ephy_window_remove_tab):
+
+ When closing a window or tab, check if there is unsubmitted user
+ input in form fields, and if so, warn the user before closing.
+ Fixes bug #119857.
+
2004-01-03 Michael Terry <mterry@fastmail.fm>
* data/ui/epiphany-ui.xml:
diff --git a/embed/ephy-embed.c b/embed/ephy-embed.c
index 3c51638ae..186f3ef5b 100644
--- a/embed/ephy-embed.c
+++ b/embed/ephy-embed.c
@@ -386,3 +386,10 @@ ephy_embed_print_preview_navigate (EphyEmbed *embed,
EphyEmbedIFace *iface = EPHY_EMBED_GET_IFACE (embed);
return iface->print_preview_navigate (embed, type, page);
}
+
+gboolean
+ephy_embed_has_modified_forms (EphyEmbed *embed)
+{
+ EphyEmbedIFace *iface = EPHY_EMBED_GET_IFACE (embed);
+ return iface->has_modified_forms (embed);
+}
diff --git a/embed/ephy-embed.h b/embed/ephy-embed.h
index f047b4e4d..71ca925d8 100644
--- a/embed/ephy-embed.h
+++ b/embed/ephy-embed.h
@@ -228,6 +228,7 @@ struct EphyEmbedIFace
EmbedPrintPreviewNavType type,
int page);
void (* activate) (EphyEmbed *embed);
+ gboolean (* has_modified_forms) (EphyEmbed *embed);
};
GType ephy_embed_get_type (void);
@@ -319,6 +320,8 @@ void ephy_embed_print_preview_navigate (EphyEmbed *embed,
/* Misc. utility */
void ephy_embed_activate (EphyEmbed *embed);
+gboolean ephy_embed_has_modified_forms (EphyEmbed *embed);
+
G_END_DECLS
#endif
diff --git a/embed/mozilla/EphyBrowser.cpp b/embed/mozilla/EphyBrowser.cpp
index c8c82c62c..afed11d8a 100644
--- a/embed/mozilla/EphyBrowser.cpp
+++ b/embed/mozilla/EphyBrowser.cpp
@@ -74,6 +74,9 @@
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLCollection.h"
#include "nsIDOMHTMLElement.h"
+#include "nsIDOMHTMLFormElement.h"
+#include "nsIDOMHTMLInputElement.h"
+#include "nsIDOMHTMLTextAreaElement.h"
#include "nsIDeviceContext.h"
#include "nsIPresContext.h"
#include "nsIAtom.h"
@@ -498,7 +501,6 @@ nsresult EphyBrowser::GetZoom (float *aZoom)
nsresult EphyBrowser::GetDocument (nsIDOMDocument **aDOMDocument)
{
- nsCOMPtr<nsIDOMDocument> domDocument;
return mDOMWindow->GetDocument (aDOMDocument);
}
@@ -867,3 +869,127 @@ nsresult EphyBrowser::GetCommandState (const char *command, PRBool *enabled)
return cmdManager->IsCommandEnabled (command, nsnull, enabled);
}
+
+#define NUM_MODIFIED_TEXTFIELDS_REQUIRED 2
+
+nsresult EphyBrowser::GetDocumentHasModifiedForms (nsIDOMDocument *aDomDoc, PRUint32 *aNumTextFields, PRBool *aHasTextArea)
+{
+ nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDomDoc);
+ NS_ENSURE_TRUE (htmlDoc, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIDOMHTMLCollection> forms;
+ htmlDoc->GetForms (getter_AddRefs (forms));
+ if (!forms) return NS_OK; /* it's ok not to have any forms */
+
+ PRUint32 formNum;
+ forms->GetLength (&formNum);
+
+ /* check all forms */
+ for (PRUint32 formIndex = 0; formIndex < formNum; formIndex++)
+ {
+ nsCOMPtr<nsIDOMNode> formNode;
+ forms->Item (formIndex, getter_AddRefs (formNode));
+ if (!formNode) continue;
+
+ nsCOMPtr<nsIDOMHTMLFormElement> formElement = do_QueryInterface (formNode);
+ if (!formElement) continue;
+
+ nsCOMPtr<nsIDOMHTMLCollection> formElements;
+ formElement->GetElements (getter_AddRefs (formElements));
+ if (!formElements) continue;
+
+ PRUint32 elementNum;
+ formElements->GetLength (&elementNum);
+
+ /* check all input elements in the form for user input */
+ for (PRUint32 elementIndex = 0; elementIndex < elementNum; elementIndex++)
+ {
+ nsCOMPtr<nsIDOMNode> domNode;
+ formElements->Item (elementIndex, getter_AddRefs (domNode));
+ if (!domNode) continue;
+
+ nsCOMPtr<nsIDOMHTMLTextAreaElement> areaElement = do_QueryInterface (domNode);
+ if (areaElement)
+ {
+ nsAutoString default_text, user_text;
+ areaElement->GetDefaultValue (default_text);
+ areaElement->GetValue (user_text);
+ if (Compare (user_text, default_text) != 0)
+ {
+ *aHasTextArea = PR_TRUE;
+ return NS_OK;
+ }
+
+ continue;
+ }
+
+ nsCOMPtr<nsIDOMHTMLInputElement> inputElement = do_QueryInterface(domNode);
+ if (!inputElement) continue;
+
+ nsAutoString type;
+ inputElement->GetType(type);
+
+ if (type.EqualsIgnoreCase("text"))
+ {
+ nsAutoString default_text, user_text;
+ inputElement->GetDefaultValue (default_text);
+ inputElement->GetValue (user_text);
+ if (Compare (user_text, default_text) != 0)
+ {
+ (*aNumTextFields)++;
+ if (*aNumTextFields >= NUM_MODIFIED_TEXTFIELDS_REQUIRED)
+ {
+ return NS_OK;
+ }
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult EphyBrowser::GetHasModifiedForms (PRBool *modified)
+{
+ *modified = PR_FALSE;
+
+ nsCOMPtr<nsIDocShell> rootDocShell = do_GetInterface (mWebBrowser);
+ NS_ENSURE_TRUE (rootDocShell, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
+ nsIDocShell::ENUMERATE_FORWARDS,
+ getter_AddRefs(enumerator));
+ NS_ENSURE_TRUE (enumerator, NS_ERROR_FAILURE);
+
+ PRBool hasMore;
+ PRBool hasTextArea = PR_FALSE;
+ PRUint32 numTextFields = 0;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> element;
+ enumerator->GetNext (getter_AddRefs(element));
+ if (!element) continue;
+
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface (element);
+ if (!docShell) continue;
+
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ docShell->GetContentViewer (getter_AddRefs(contentViewer));
+ if (!contentViewer) continue;
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ contentViewer->GetDOMDocument (getter_AddRefs (domDoc));
+
+ nsresult result;
+ result = GetDocumentHasModifiedForms (domDoc, &numTextFields, &hasTextArea);
+ if (NS_SUCCEEDED (result) &&
+ (numTextFields >= NUM_MODIFIED_TEXTFIELDS_REQUIRED || hasTextArea))
+ {
+ *modified = PR_TRUE;
+ break;
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/embed/mozilla/EphyBrowser.h b/embed/mozilla/EphyBrowser.h
index e5ed2e5f5..40101ce44 100644
--- a/embed/mozilla/EphyBrowser.h
+++ b/embed/mozilla/EphyBrowser.h
@@ -119,6 +119,8 @@ public:
nsresult GetDocumentUrl (nsCString &url);
nsresult GetTargetDocumentUrl (nsCString &url);
+ nsresult GetHasModifiedForms (PRBool *modified);
+
nsCOMPtr<nsIWebBrowser> mWebBrowser;
private:
@@ -135,6 +137,7 @@ private:
nsresult SetZoomOnDocshell (float aZoom, nsIDocShell *DocShell);
nsresult GetSHistory (nsISHistory **aSHistory);
nsresult GetContentViewer (nsIContentViewer **aViewer);
+ nsresult GetDocumentHasModifiedForms (nsIDOMDocument *aDomDoc, PRUint32 *aNumTextFields, PRBool *aHasTextArea);
};
#endif
diff --git a/embed/mozilla/mozilla-embed.cpp b/embed/mozilla/mozilla-embed.cpp
index f333c8768..15b88938b 100644
--- a/embed/mozilla/mozilla-embed.cpp
+++ b/embed/mozilla/mozilla-embed.cpp
@@ -786,6 +786,18 @@ impl_get_encoding_info (EphyEmbed *embed)
return info;
}
+static gboolean
+impl_has_modified_forms (EphyEmbed *embed)
+{
+ MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
+ nsresult result;
+
+ PRBool modified;
+ result = mpriv->browser->GetHasModifiedForms (&modified);
+
+ return modified == PR_TRUE ? TRUE : FALSE;
+}
+
static void
mozilla_embed_location_changed_cb (GtkMozEmbed *embed,
MozillaEmbed *membed)
@@ -1121,6 +1133,7 @@ ephy_embed_iface_init (EphyEmbedIFace *iface)
iface->print_preview_close = impl_print_preview_close;
iface->print_preview_n_pages = impl_print_preview_n_pages;
iface->print_preview_navigate = impl_print_preview_navigate;
+ iface->has_modified_forms = impl_has_modified_forms;
}
void
diff --git a/src/ephy-notebook.c b/src/ephy-notebook.c
index 87bb719a6..799e27b74 100644
--- a/src/ephy-notebook.c
+++ b/src/ephy-notebook.c
@@ -20,7 +20,7 @@
*/
#ifdef HAVE_CONFIG_H
-#include <config.h>
+#include "config.h"
#endif
#include "ephy-notebook.h"
@@ -92,13 +92,14 @@ enum
TAB_REMOVED,
TABS_REORDERED,
TAB_DETACHED,
+ TAB_DELETE,
LAST_SIGNAL
};
-static GObjectClass *parent_class = NULL;
-
static guint signals[LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
GType
ephy_notebook_get_type (void)
{
@@ -175,6 +176,16 @@ ephy_notebook_class_init (EphyNotebookClass *klass)
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
+ signals[TAB_DELETE] =
+ g_signal_new ("tab_delete",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EphyNotebookClass, tab_delete),
+ g_signal_accumulator_true_handled, NULL,
+ ephy_marshal_BOOLEAN__OBJECT,
+ G_TYPE_BOOLEAN,
+ 1,
+ GTK_TYPE_WIDGET);
g_type_class_add_private (object_class, sizeof(EphyNotebookPrivate));
}
@@ -852,9 +863,15 @@ static void
close_button_clicked_cb (GtkWidget *widget, GtkWidget *child)
{
EphyNotebook *notebook;
+ gboolean inhibited = FALSE;
notebook = EPHY_NOTEBOOK (gtk_widget_get_parent (child));
- ephy_notebook_remove_page (notebook, child);
+ g_signal_emit (G_OBJECT (notebook), signals[TAB_DELETE], 0, child, &inhibited);
+
+ if (inhibited == FALSE)
+ {
+ ephy_notebook_remove_page (notebook, child);
+ }
}
static GtkWidget *
diff --git a/src/ephy-notebook.h b/src/ephy-notebook.h
index ca93cdf55..314ad098c 100644
--- a/src/ephy-notebook.h
+++ b/src/ephy-notebook.h
@@ -70,6 +70,8 @@ struct EphyNotebookClass
void (* tab_detached) (EphyNotebook *notebook,
GtkWidget *child);
void (* tabs_reordered) (EphyNotebook *notebook);
+ void (* tab_delete) (EphyNotebook *notebook,
+ GtkWidget *child);
};
GType ephy_notebook_get_type (void);
diff --git a/src/ephy-window.c b/src/ephy-window.c
index f5013d9c8..a4f748aa5 100644
--- a/src/ephy-window.c
+++ b/src/ephy-window.c
@@ -57,6 +57,8 @@
#include <gtk/gtktoggleaction.h>
#include <gtk/gtkuimanager.h>
#include <gtk/gtktoggleaction.h>
+#include <gtk/gtkdialog.h>
+#include <gtk/gtkmessagedialog.h>
static void ephy_window_class_init (EphyWindowClass *klass);
static void ephy_window_init (EphyWindow *gs);
@@ -636,6 +638,116 @@ ephy_window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event, EphyW
return FALSE;
}
+static gboolean
+confirm_close_with_modified_forms (EphyWindow *window)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox, *vbox, *label, *image;
+ GtkWindowGroup *group;
+ char *text;
+ int response;
+
+ dialog = gtk_dialog_new_with_buttons ("",
+ GTK_WINDOW (window),
+ GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_MODAL,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("_Close document"), GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (hbox);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
+ TRUE, TRUE, 0);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+ gtk_widget_show (image);
+ gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+
+ text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
+ _("There are unsubmitted changes to form elements."),
+ _("If you close the document anyway, you will lose that information."));
+ gtk_label_set_markup (GTK_LABEL (label), text);
+ g_free (text);
+
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ group = GTK_WINDOW (window)->group;
+ if (group == NULL)
+ {
+ group = gtk_window_group_new ();
+ gtk_window_group_add_window (group, GTK_WINDOW (window));
+ g_object_unref (group);
+ }
+
+ gtk_window_group_add_window (group, GTK_WINDOW (dialog));
+
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ return response == GTK_RESPONSE_OK;
+}
+
+static gboolean
+ephy_window_delete_event_cb (GtkWidget *widget, GdkEvent *event, EphyWindow *window)
+{
+ EphyTab *modified_tab = NULL;
+ GList *tabs, *l;
+ gboolean modified = FALSE;
+
+ tabs = ephy_window_get_tabs (window);
+ for (l = tabs; l != NULL; l = l->next)
+ {
+ EphyTab *tab = (EphyTab *) l->data;
+ EphyEmbed *embed;
+
+ g_return_val_if_fail (EPHY_IS_TAB (tab), FALSE);
+
+ embed = ephy_tab_get_embed (tab);
+ g_return_val_if_fail (EPHY_IS_EMBED (embed), FALSE);
+
+ if (ephy_embed_has_modified_forms (embed))
+ {
+ modified = TRUE;
+ modified_tab = tab;
+ break;
+ }
+ }
+ g_list_free (tabs);
+
+ if (modified)
+ {
+ /* jump to the first tab with modified forms */
+ ephy_window_jump_to_tab (window, modified_tab);
+
+ if (confirm_close_with_modified_forms (window) == FALSE)
+ {
+ /* stop window close */
+ return TRUE;
+ }
+ }
+
+ /* See bug #114689 */
+ gtk_widget_hide (widget);
+
+ /* proceed with window close */
+ return FALSE;
+}
static void
update_edit_actions_sensitivity (EphyWindow *window, gboolean hide)
@@ -1445,6 +1557,19 @@ tabs_reordered_cb (EphyNotebook *notebook, EphyWindow *window)
ephy_tabs_menu_update (window->priv->tabs_menu);
}
+static gboolean
+tab_delete_cb (EphyNotebook *notebook, GtkWidget *child, EphyWindow *window)
+{
+ g_return_val_if_fail (EPHY_IS_EMBED (child), FALSE);
+
+ if (ephy_embed_has_modified_forms (EPHY_EMBED (child)))
+ {
+ return !confirm_close_with_modified_forms (window);
+ }
+
+ return FALSE;
+}
+
static GtkNotebook *
setup_notebook (EphyWindow *window)
{
@@ -1468,6 +1593,8 @@ setup_notebook (EphyWindow *window)
G_CALLBACK (tab_detached_cb), NULL);
g_signal_connect (G_OBJECT (notebook), "tabs_reordered",
G_CALLBACK (tabs_reordered_cb), window);
+ g_signal_connect (G_OBJECT (notebook), "tab_delete",
+ G_CALLBACK (tab_delete_cb), window);
return notebook;
}
@@ -1662,6 +1789,9 @@ ephy_window_init (EphyWindow *window)
g_signal_connect (window, "window-state-event",
G_CALLBACK (ephy_window_state_event_cb),
window);
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (ephy_window_delete_event_cb),
+ window);
/* lockdown pref notifiers */
window->priv->disable_js_chrome_notifier_id = eel_gconf_notification_add
@@ -1834,15 +1964,25 @@ void
ephy_window_remove_tab (EphyWindow *window,
EphyTab *tab)
{
- GtkWidget *embed;
+ EphyEmbed *embed;
+ gboolean modified;
g_return_if_fail (EPHY_IS_WINDOW (window));
g_return_if_fail (EPHY_IS_TAB (tab));
- embed = GTK_WIDGET (ephy_tab_get_embed (tab));
+ embed = ephy_tab_get_embed (tab);
+ g_return_if_fail (EPHY_IS_EMBED (embed));
+
+ modified = ephy_embed_has_modified_forms (embed);
+ if (ephy_embed_has_modified_forms (embed)
+ && confirm_close_with_modified_forms (window) == FALSE)
+ {
+ /* don't close the tab */
+ return;
+ }
ephy_notebook_remove_page (EPHY_NOTEBOOK (window->priv->notebook),
- embed);
+ GTK_WIDGET (embed));
}
/**