Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 75988

Summary: [Preferences] IPreferencePage#performOk() returning false should prevent the dialog from closing
Product: [Eclipse Project] Platform Reporter: Genady Beryozkin <eclipse>
Component: UIAssignee: Tod Creasey <Tod_Creasey>
Status: VERIFIED FIXED QA Contact:
Severity: normal    
Priority: P2 CC: cebarne2, christof_marti, eckart.langhuth, jamie.beznoski, JWootton, martinae
Version: 3.0   
Target Milestone: 3.1 M5   
Hardware: PC   
OS: Windows XP   
Whiteboard:

Description Genady Beryozkin CLA 2004-10-09 12:13:35 EDT
I think that if this method returns false, it should prevent the dialog from
closing.

Today, in order to perform validation I have to register key listeners 
to all fields an use #setValid(false) to disable the "OK" button.
Comment 1 Tod Creasey CLA 2004-10-12 08:16:20 EDT
The purpose of this method is to an abort a save - so it is called after OK is 
pressed which means disabling OK would have no effect.

You should implement isValid to disable the OK button.
Comment 2 Genady Beryozkin CLA 2004-10-12 14:47:50 EDT
There is a problem with it. Suppose some pages were closed ok, 
then you have a page that returns "false" in its "performOK" method,
and after that you have pages with valid values.

If the save is aborted, some values (of the last valid pages) are discarded 
and the user does not have a chance to commit them.

If you suggest relying on "isValid()" then I believe performOK should 
return "void". If it is not possible without breaking an API, maybe it is 
possible to use "canPerformOk" method that will ask all pages regarding their 
state.

I have a serious problem with the "isValid" method, because I it requires me 
to have a listener for every field in the preference page that will call 
setValid(). isValid() values are cached in some way, and when I tried to 
override it, the method was not called after the page contents has changed.
Comment 3 Tod Creasey CLA 2004-10-12 15:49:51 EDT
performOK usually only returns false in cases that cannot be determined during 
field entry (i.e. errors).

We can't do an isValid check for you as we are not sure how you are going to 
structure your page - this is one of the problems with a framework - we have 
to leave some functionality to the developer as we can't make potentially bad 
decisions for you.

Could you use a FieldEditorsPreferencePage?
Comment 4 Genady Beryozkin CLA 2004-10-12 16:05:23 EDT
I mean that the availability of the "OK" button is determined by a call
to #setValid() method, which must be called explicitly by the developer when 
one of the fields changes.

Is it possible for eclipse preferences code to call #isValid() each time 
something changes on the page?

Consider for example how IWizard#performFinish() is documented.
If I get it right, if the method returns false, the wizard is not closed.
Comment 5 Tod Creasey CLA 2004-10-12 16:22:01 EDT
Sadly it isn't because there is no way for me to know what has changed unless 
I crawl the entire widget hierarchy and add selection and key listeners to 
everything that it is able to take them - the issue is that the preference 
page is very open ended and so there is little knowledge we have about them.
Comment 6 Chad Barnes CLA 2004-10-29 15:20:33 EDT
Why has this changed since 2.1.x?  I had a set of properties pages working 
great in 2.1.x, and now they always close when PerformOk() returns false.

I do not want to set isValid on every field modification.  I would rather 
check for errors once when performOk() is called.  Doing it any other way 
seriously degrades performance of the UI.

I don;t think this bug should be resolved yet.
Comment 7 Tod Creasey CLA 2004-10-29 16:47:57 EDT
We haven't made any changes in this behaviour since 2.1 to the best of my 
knowledge. If you think there is a regression please log a report with some 
steps.
Comment 8 Chad Barnes CLA 2004-11-01 11:54:39 EST
Here's is a way to duplicate this behavior.

1) Create a new Plug-in project w/ Property Page wizard
2) Replace the appropriate property page code with following methods:

	protected Control createContents(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout();
		composite.setLayout(layout);
		GridData data = new GridData(GridData.FILL);
		data.grabExcessHorizontalSpace = true;
		composite.setLayoutData(data);

		text = new Text(composite, SWT.BORDER);
		text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		return composite;
	}

		
	public boolean performOk() {
		// store the value in the owner text field
		return (text.getCharCount() > 0);
	}

Now, in Eclipse 2.1.3 if I leave the text field blank and press OK, the 
property page stays open.  If I put text in the field, the property page will 
close when I press OK.

In Eclipse 3.0.1, if I press OK with the field empty, the property page closes 
(even though I am returning false).

This shows a definite change from the behavior of 2.x.

One person I have been discussing with in the community had mentioned he 
tracked the problem to PreferenceDialog, and that the close() reference was 
inside a finally block.  Perhaps this is why it ALWAYS closes in 3.0.1.
Comment 9 Chad Barnes CLA 2004-11-01 14:05:13 EST
Here is the change I made to jface to get this to work like it did in 2.1.3.  
It seems 3.0.1 introduced a try-catch-finally block into this method and the 
close() reference got put into the finally block (where it is always called 
regardless of a previous return).  In 2.1.3 close() was not called if the 
previous return was reached.

Index: PreferenceDialog.java
===================================================================
RCS file: h:/cvsrepo/dev/org.eclipse.jface/src-
jface/org/eclipse/jface/preference/PreferenceDialog.java,v
retrieving revision 1.1
diff -u -r1.1 PreferenceDialog.java
--- PreferenceDialog.java	1 Nov 2004 18:38:56 -0000	1.1
+++ PreferenceDialog.java	1 Nov 2004 18:39:57 -0000
@@ -755,8 +755,9 @@
 					if (!errorOccurred)
 						handleSave();
 					// Need to restore state
-					close();
+					
 				}
+				close();
 			}
 			/*
 			 * (non-Javadoc)
Comment 10 Tod Creasey CLA 2004-11-01 14:11:25 EST
Reopening to investigate the regression
Comment 11 Chad Barnes CLA 2004-11-01 14:56:52 EST
I think this is a better patch.  In the previous, I wasn't re-enabling the OK 
button if "return;" was being reached.

Index: PreferenceDialog.java
===================================================================
RCS file: h:/cvsrepo/dev/org.eclipse.jface/src-
jface/org/eclipse/jface/preference/PreferenceDialog.java,v
retrieving revision 1.1
diff -u -r1.1 PreferenceDialog.java
--- PreferenceDialog.java	1 Nov 2004 18:38:56 -0000	1.1
+++ PreferenceDialog.java	1 Nov 2004 19:41:58 -0000
@@ -743,8 +743,9 @@
 						IPreferenceNode node = 
(IPreferenceNode) nodes.next();
 						IPreferencePage page = 
node.getPage();
 						if (page != null) {
-							if (!page.performOk())
-								return;
+							if (!page.performOk()){
+								getButton
(IDialogConstants.OK_ID).setEnabled(true);
+								return;}
 						}
 					}
 				} catch (Exception e) {
@@ -755,8 +756,9 @@
 					if (!errorOccurred)
 						handleSave();
 					// Need to restore state
-					close();
 				}
+				close();
+				
 			}
 			/*
 			 * (non-Javadoc)
Comment 12 Tod Creasey CLA 2004-11-08 08:34:30 EST
*** Bug 77652 has been marked as a duplicate of this bug. ***
Comment 13 Christof Marti CLA 2004-12-07 09:00:16 EST
I came across the same problem while writing an interface that allows
implementers to contribute part of a preference page.

A problem the patch from comment 11 does not solve is that after one preference
page returned false, there are some preference pages that were allowed to save
their state, one that failed to save its state and some preference pages that
were never asked to save their state.

As stated earlier, a possible solution might be to add a 'boolean
canPerformOk()' to determine whether all preference pages will successfully (to
their current knowledge) performOk(). If at least one canPerformOk() returns
false no additional action would be taken.

The return value of performOk() would only be false in exceptional cases and
PreferenceDialog would continue to call performOk() on each page such that as
much as possible of the preferences are stored, the dialog would be kept open
and a message box could inform about which pages failed to performOk(). This or
something similar would make sense even in the absence of canPerformOk() in
order to avoid inconsistently saved preferences to some degree and to inform the
user about what went wrong. I agree this has a compatibility issue, but
currently clients are well adviced to always return true on performOk() anyway.

Bug 65134 is probably a duplicate.
Comment 14 Christof Marti CLA 2004-12-15 05:27:18 EST
Changing the Java compiler settings shows a dialog on OK. When pressing Cancel
on that dialog, the preference changes are lost because the preference dialog
closes.
Comment 15 Tod Creasey CLA 2005-01-05 13:18:48 EST
*** Bug 65134 has been marked as a duplicate of this bug. ***
Comment 16 Tod Creasey CLA 2005-01-05 13:49:36 EST
Fixed in build >20040105. We now close if all of the performOKs are true and 
if there are no errors.

We stop calling performOK if one of them returns false as we are not closing 
the dialog now.
Comment 17 Tod Creasey CLA 2005-01-05 15:09:24 EST
*** Bug 81644 has been marked as a duplicate of this bug. ***
Comment 18 Tod Creasey CLA 2005-02-16 14:01:24 EST
Verified in 20050215