Related to question "Custom message when closing a part in Eclipse RCP 4" I also have a Eclipse RCP 4 application with multiple editor parts (implementing MDirtyable and @Persist).
The parts are closable. When the user is closing a part there should be a custom pop-up which is asking the user if he really wants to save the part or not.
Also when the user close the appliaction a pop-up should prompt the user to close/save the dirty parts. Basically it is intended to remove the default close eclipse e4 dialogs.
I have implemented custom ISaveHandler and IWindowCloseHandler subscribed to the application startup complete event UIEvents.UILifeCycle.APP_STARTUP_COMPLETE in the life cycle class.
Custom IWindowCloseHandler works fine (in terms of dialogs) but custom ISaveHandler is not.
ISaveHandler.save returns stackoverflow error when defined as follows:
@Override
public boolean save(MPart dirtyPart, boolean confirm) {
EPartService partService = dirtyPart.getContext().get(EPartService.class);
//Try to close the part and save the document to disc by
//calling the @Persist method
return partService.savePart(dirtyPart, confirm);
}
I have attached the complete LifeCycleManager class:
public class LifeCycleManager {
@Inject IEventBroker eventBroker;
@ProcessAdditions
public void processAdditions(MApplication application, EModelService modelService){
MWindow window =(MWindow)modelService.find("application-trimmedwindow", application);
eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE,
new AppStartupCompleteEventHandler(window, modelService, application));
}
public class AppStartupCompleteEventHandler implements EventHandler {
private MWindow theWindow;
private MApplication app;
private ISaveHandler saveHandler;
AppStartupCompleteEventHandler(MWindow window, EModelService modelService, MApplication application){
theWindow = window;
app = application;
}
@Override
public void handleEvent(Event event) {
theWindow.getContext().set(ISaveHandler.class, new ISaveHandler() {
@Override
public boolean save(MPart dirtyPart, boolean confirm) {
System.out.println("PARTE PARA SALVAR..." + dirtyPart.getLabel());
EPartService partService = dirtyPart.getContext().get(EPartService.class);
//partService.hidePart(dirtyPart,true);
return partService.savePart(dirtyPart, confirm);
//return true;
}
@Override
public boolean saveParts(Collection<MPart> dirtyParts, boolean confirm) {
return false;
}
@Override
public Save promptToSave(MPart dirtyPart) {
return promptToSaveDialog(dirtyPart);
}
@Override
public Save[] promptToSave(Collection<MPart> dirtyParts) {
return null;
}
});
saveHandler = (ISaveHandler)theWindow.getContext().get(ISaveHandler.class);
theWindow.getContext().set(IWindowCloseHandler.class, new IWindowCloseHandler() {
@Override
public boolean close(MWindow window) {
List<MHandler> listHandlers = window.getHandlers();
System.out.println(listHandlers.size());
Shell shell = (Shell) window.getWidget();
if (MessageDialog.openConfirm(shell, "Close Nastran Editor", "Do you really want to close the entire application?")) {
Collection<EPartService> allPartServices = getAllPartServices(app);
if (containsDirtyParts(allPartServices)) {
return iterateOverDirtyParts( allPartServices);
}
else {
return true;
}
}
return false;
}});
}
private Collection<EPartService> getAllPartServices(MApplication application) {
List<EPartService> partServices = new ArrayList<EPartService>();
EModelService modelService = application.getContext().get(EModelService.class);
List<MWindow> elements = modelService.findElements(application, MWindow.class, EModelService.IN_ACTIVE_PERSPECTIVE,
new ElementMatcher(null, MWindow.class, (List<String>) null));
for (MWindow w : elements) {
if (w.isVisible() && w.isToBeRendered()) {
EPartService partService = w.getContext().get(EPartService.class);
if (partService != null) {
partServices.add(partService);
}
}
}
return partServices;
}
private boolean containsDirtyParts(Collection<EPartService> partServices) {
for (EPartService partService : partServices) {
if (!partService.getDirtyParts().isEmpty()) return true;
}
return false;
}
private boolean iterateOverDirtyParts(Collection<EPartService> allPartServices) {
for (EPartService partService : allPartServices) {
Collection<MPart> dirtyParts = partService.getDirtyParts();
for(MPart dirtyPart : dirtyParts) {
switch(saveHandler.promptToSave(dirtyPart)) {
case NO: break;
case YES:
saveHandler.save(dirtyPart, false);
break;
case CANCEL:return false;
}
}
}
return true;
}
private Save promptToSaveDialog(MPart dirtyPart) {
MessageDialog dialog = new MessageDialog( (Shell)theWindow.getWidget(), "Save file", null,
"'"+dirtyPart.getLabel()+"' has been modified. Save changes?", MessageDialog.QUESTION, new String[] { "YES", "NO", "CANCEL" }, 0);
switch (dialog.open()){
case 0: return Save.YES;
case 1: return Save.NO;
case 2: return Save.CANCEL;
default:return Save.CANCEL;
}
}
}
}///END of LifeCycleManager
The save
method of ISaveHandler
is called from within the EPartService
savePart
method so you cannot call savePart
again.
Instead you should just call the @Persist
method of the part. So something like:
@Override
public boolean save(final MPart dirtyPart, final boolean confirm)
{
if (confirm)
{
switch (promptToSave(dirtyPart))
{
default:
case NO:
return true;
case CANCEL:
return false;
case YES:
break;
}
}
try
{
ContextInjectionFactory.invoke(dirtyPart.getObject(), Persist.class, dirtyPart.getContext());
}
catch (final InjectionException ex)
{
// TODO ignore or log error
}
return true;
}