Remember the State
Join the DZone community and get the full member experience.
Join For FreeAs a rule of thumb, your application should try to remember the state
across sessions. So when a user hits the close button and then opens it
after few mins/days, it should present exactly in the same way where he
left. In this tip, I'm going to explain few things of how this could be
achieved.
The first and foremost thing is the number of windows opened and
current perspective and its state in each window. If you are writing a
plugin for the IDE, you need not worry about this. Eclipse does that
for you. If you are an RCP app, you have to do it yourself. You have to
do it in the WorkbenchAdvisor.initialize() method:
public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
@Override
public void initialize(IWorkbenchConfigurer configurer) {
super.initialize(configurer);
configurer.setSaveAndRestore(true);
}
// other methods ...
}
Now this will take care of lot of things - the number of windows that
are opened; location & size of those windows; perspective of each
window; view & editors in each perspective; their locations,... All
by setting that one boolean variable!
Now that our views and editors are restored by Eclipse, we need to
ensure the state of them. Most of the views will be tree/table based.
In these cases, the current selection is an important one to restore.
To store these items and anyother stuff you want to, Eclipse provides
IMemento. An instance of this IMemento is passed to the view when its
initialized and when workbench is closed. The information can be stored
hierarchically with string keys and it will be persisted as an XML. If
you are wondering why can't it be as simple as passing a Serializable
Java object and the workbench persisting it, the answer is simple. The
same class may not be there when Eclipse starts next time or even if
the same class is available, it might have changed. IMemento avoids
this problem by persisting the stae as an XML string.
So how do we use it? In the view, you have to override the init() and saveState() methods:
@Override
public void init(IViewSite site, IMemento memento) throws PartInitException {
this.memento = memento;
super.init(site, memento);
}
public void createPartControl(Composite parent) {
// create the viewer
restoreSelection();
}
private void restoreSelection() {
if (memento != null) {
IMemento storedSelection = memento.getChild("StoredSelection");
if (storedSelection != null) {
// create a structured selection from it
viewer.setSelection(selection);
}
}
}
@Override
public void saveState(IMemento memento) {
IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
if (!selection.isEmpty()) {
IMemento storedSelection = memento.createChild("StoredSelection");
// store the selection under storedSelection
}
}
Not just the selection, we can store any other information (which of
the tree nodes are expanded, sorter & filter settings etc) which
will be useful to restore the state.
Great. Now moving on from the views, the next item would be dialogs.
Similar to the workbench windows, you can store the size, location and
other elements of a dialog as well. The functionality for size and
location is available by default, but you need to enable it by
overriding the Dialog.getDialogBoundsSettings() method. The Dialog
class stores & retrieves the size and location from IDialogSettings
returned from that method. The original implementation returns null, so
nothing is saved. We need to create an instanceof IDialogSettings and
return it. Your plugin's Activator simplifies that. When required, it
creates a dialog_settings.xml under your plugin's data area and store
the dialog settings of all the dialogs. You have to create a separate
section for each dialog.
private static final String MY_DIALOG_SETTINGS = "MY_DIALOG_SETTINGS";
@Override
protected IDialogSettings getDialogBoundsSettings() {
IDialogSettings settings = Activator.getDefault().getDialogSettings();
IDialogSettings section = settings.getSection(MY_DIALOG_SETTINGS);
if (section == null) {
section = settings.addNewSection(MY_DIALOG_SETTINGS);
}
return section;
}
In case you want to store only the location or size, you can specify it by overriding the getDialogBoundsStrategy() method.
Much like the IMemento, the IDialogSettings basically organizes the
key-value strings in an hierarchical way. So along with the size &
location, you can store any other information in this IDialogSettings.
Its a good practice to store the values of the widgets (which radion
button is selecte, checked state of a check box, etc) in the dialog, so
its faster for an user who frequently repeats an operation.
Talking about the widgets, the trickier one is Combo. When the list of
options are predefined and the user can't enter a new value, then its
easy. But in places where the user can enter the values, (like
File/Directory selection, search boxes), remembering them is not
straight forward.
We shouldn't be storing every single value the user enters, but only
the recently used ones. Probably with a limit of 5 or 10 items only.
This can be done with the help of LinkedHashSet. It guarantees the
order of the element, so whenever the user enters a new values, put the
current value first in the set then add the rest of the elements (even
if the first element is repeated, it won't change the position). Then
take the first N elements and store it.
private static final String COMBO_STATE="COMBO_STATE";
private static final int HISTORY_SIZE = 5;
private String []comboState;
private void restoreComboState(IDialogSettings settings) {
comboState = settings.getArray(COMBO_STATE);
if(comboState ==null)
comboState = new String[0];
for (String value : comboState) {
myCombo.add(value);
}
}
private void saveComboState(IDialogSettings settings) {
// use LinkedHashSet to have the recently entered ones
LinkedHashSet<String> newState = new LinkedHashSet<String>();
newState.add(myCombo.getText());
newState.addAll(Arrays.asList(comboState));
// now convert it to the array of required size
int size = Math.min(HISTORY_SIZE, newState.size());
String[] newStateArray = new String[size];
newState.toArray(newStateArray);
// store
settings.put(COMBO_STATE, newStateArray);
}
One last piece. Look at this dialog:

Sometimes only for the first time you want to ask the question to the
user. Thereafter if the user prefers, you can use the same answer. To
simplify this, you can use the MessageDialogWithToggle. You need to
pass the preference store and the key for the preference value. When
the user selects the check box, the value will be stored. From the next
time onwards, you can check the value from the preference and use it.
MessageDialogWithToggle.openYesNoQuestion(shell, "Remember me", "Is this tip useful?",
"Don't bug me with this question again", true,
Activator.getDefault().getPreferenceStore(), "BUG_USER_KEY");
There you go:

Opinions expressed by DZone contributors are their own.
Trending
-
Front-End: Cache Strategies You Should Know
-
DevOps vs. DevSecOps: The Debate
-
How AI Will Change Agile Project Management
-
Java Concurrency: Condition
Comments