I have an editor in Eclipse. When the user chooses to close it, a dialog shall appear with several options. One of them is "Cancel" and should cancel the close event. How can I do that?
The partial code I have so far:
...
IEditorPart openEditor = page.openEditor(input, CS_STRINGEDITOR_ID);
openEditor.getEditorSite().getPage()
.addPartListener(new IPartListener() {
public void partOpened(IWorkbenchPart part) {}
public void partDeactivated(IWorkbenchPart part) {}
public void partClosed(IWorkbenchPart part) {
Shell sh = new Shell(cv.getViewSite().getShell());
// My MessageDialog with the options, one being "cancel"
CloseDialog closeDialog = new CloseDialog(sh);
closeDialog.open();
int returnCode = closeDialog.getReturnCode();
switch (returnCode) {
case CloseDialog.CANCEL_ID:
// Abort the close event and keep the editor alive
break;
}
}
This thread points out there is no closing event per se.
Eclipse provides mostly post-activite events, i.e. on IPartListener2, partClosed(*) is a
post-closing event.
You can provide your own dialog on close, but it will only be shown if the
editor is dirty at the time it is closed. See ISaveablePart2.promptToSaveOnClose()
(as presented in the Prevent that a RCP Editor is closed)
However, note that implementing ISaveablePart2 may lead to many dialogs being opened when you close multiple editors.
Related
I'm implementing a document editor with JavaFX8 and e(fx)clipse and want to user to be informed when the export (write to disc) is ongoing. I'm using the main (GUI) Thread for this as I want to block the gui during this operation (which takes 2-3 seconds). During this operation I want to show a small popup to inform the user that the export is ongoing, nothing fancy.
#FXML
public void export() {
Dialog dialog = new Dialog();
dialog.setContentText("exporting ...");
dialog.show();
// some lenghty methods come here, ~equivalent to Thread.sleep(3000);
dialog.hide();
}
When I press the corresponding Button which invokes the export method, I get somehow two dialogs, one of them NOT closing and remaining open after the method has finished.
Does somebody has an idea what's happening here? I'm really interested in a simple solution, I don't need to have a progress bar etc..
Another possibility would be to show a wait-cursor before the operation starts and switching back to the default cursor after that. Unfortunately, this does also not seem to work. I understand that the UI is blocked during the "lengthty" operation, but I don't udnerstand why I cant change the UI before and after that operation....
Your example isn't very complete - however I would recommend using one of two approaches. However, you aren't putting the long process on a background thread which will FREEZE your app. You want to offload that process.
1) Use the ControlsFX Dialog which has a Progess Alert. Tie your work to either a Task or a Service and provide that to the alert. This will pop the alert up while the thread is active, and will automatically close it when done. If you have intermediary progress values, it can be used to update the progress bar.
Or if you don't want to use this dialog, you could do something like this:
Alert progressAlert = displayProgressDialog(message, stage);
Executors.newSingleThreadExecutor().execute(() -> {
try {
//Do you work here....
Platform.runLater(() ->forcefullyHideDialog(progressAlert));
} catch (Exception e) {
//Do what ever handling you need here....
Platform.runLater(() ->forcefullyHideDialog(progressAlert));
}
});
private Alert displayProgressDialog(String message, Stage stage) {
Alert progressAlert = new Alert(AlertType.NONE);
final ProgressBar progressBar = new ProgressBar();
progressBar.setMaxWidth(Double.MAX_VALUE);
progressBar.setPrefHeight(30);
final Label progressLabel = new Label(message);
progressAlert.setTitle("Please wait....");
progressAlert.setGraphic(progressBar);
progressAlert.setHeaderText("This will take a moment...");
VBox vbox = new VBox(20, progressLabel, progressBar);
vbox.setMaxWidth(Double.MAX_VALUE);
vbox.setPrefSize(300, 100);
progressAlert.getDialogPane().setContent(vbox);
progressAlert.initModality(Modality.WINDOW_MODAL);
progressAlert.initOwner(stage);
progressAlert.show();
return progressAlert;
}
private void forcefullyHideDialog(javafx.scene.control.Dialog<?> dialog) {
// for the dialog to be able to hide, we need a cancel button,
// so lets put one in now and then immediately call hide, and then
// remove the button again (if necessary).
DialogPane dialogPane = dialog.getDialogPane();
dialogPane.getButtonTypes().add(ButtonType.CANCEL);
dialog.hide();
dialogPane.getButtonTypes().remove(ButtonType.CANCEL);
}
On Eclipse Luna, I select a server and click the Start button on Servers view, then the server (for example Tomcat8) will get started. If something is wrong during the start-up process, a dialog will be populated to display the error messages (for example time-out). The dialog is modeless in this test case.
Now I need to start the server programmatically from a plugin. In case that errors occur, how could I programmatically detect that a dialog has been opened and how to close it?
You could use the Display.addFilter method to listen for all SWT.Activate events which will tell you about all Shells (and other things) being activated. You can then detect the shells you want to close.
Something like:
Display.getDefault().addFilter(SWT.Activate, new Listener()
{
#Override
public void handleEvent(final Event event)
{
// Is this a Shell being activated?
if (event.widget instanceof Shell)
{
final Shell shell = (Shell)event.widget;
// Look at the shell title to see if it is the one we want
if ("About".equals(shell.getText()))
{
// Close the shell after it has finished initializing
Display.getDefault().asyncExec(new Runnable()
{
#Override
public void run()
{
shell.close();
}
});
}
}
}
});
which closes a dialog called 'About'.
In more recent versions of Java the above can be simplified to:
Display.getDefault().addFilter(SWT.Activate, event ->
{
// Is this a Shell being activated?
if (event.widget instanceof Shell shell)
{
// Look at the shell title to see if it is the one we want
if ("About".equals(shell.getText()))
{
// Close the shell after it has finished initializing
Display.getDefault().asyncExec(shell::close);
}
}
});
This uses Java 8 lambdas and method references and Java 16 instanceof type patterns.
I am working on Netbeans building a JavaFX application.
I started using ControlsFX (http://fxexperience.com/controlsfx/)
I have implemented a simple Dialog that uses custom AbstractDialogAction s as I want specific number of buttons to appear.
I do this like this:
Action a = new AbstractDialogAction(" button a ", Dialog.ActionTrait.CLOSING) {
#Override
public void execute(ActionEvent ae) {
}
};
ArrayList<Action> actions = new ArrayList<>();
actions.add(a);
actions.add(b); // other button
actions.add(c); // another button
dialog.actions(actions);
Action response = dialog.showConfirm();
Dialog is shown correctly with the given buttons.
My question is how to force the Dialog to close when a button is pressed ?
I thought setting a Dialog.ActionTrait.CLOSING would do the trick, but the Dialog stays open.
From eugener in ControlsFX mailing list
public void execute(ActionEvent ae) {
if (ae.getSource() instanceof Dialog ) {
((Dialog) ae.getSource()).setResult(this);
}
}
The above sets the result of the Dialog to be the current Action and closes the Dialog
But maybe that is a little redundant as I can simply call:
((Dialog) ae.getSource()).hide();
.hide() hides the Dialog and also sets the current action as the result.
I can't suggest which is a better solution (hide() was suggested by jewelsea)
In addition I would suggest to always override the toString() method of class AbstractDialogAction, in order to get readable result from:
Action response = dialog.showConfirm();
System.out.println("RESPONSE = "+ response.toString());
Hide the dialog to close it => dialog.hide()
I successfully extended the PyDev editor in Eclipse with a side-by-side display, but I can't copy the contents of the extra SourceViewer that I added. I can select some text in the display, but when I press Ctrl+C, it always copies the main PyDev editor's selected text.
I found an article on key bindings in Eclipse editors, but the code there seems incomplete and a bit out-of-date. How can I configure the copy command to copy from whichever SourceViewer has focus?
The reason I want to do this is that I've written a tool for live coding in Python, and it would be much easier for users to submit bug reports if they could just copy the display and paste it into the bug description.
David Green's article was a good start, but it took a bit of digging to make it all work. I published a full example project on GitHub, and I'll post a couple of snippets here.
The TextViewerSupport class wires up a new action handler for each command you want to delegate to the extra text viewer. If you have multiple text viewers, just instantiate a TextViewerSupport object for each of them. It wires up everything in its constructor.
public TextViewerSupport(TextViewer textViewer) {
this.textViewer = textViewer;
StyledText textWidget = textViewer.getTextWidget();
textWidget.addFocusListener(this);
textWidget.addDisposeListener(this);
IWorkbenchWindow window = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow();
handlerService = (IHandlerService) window
.getService(IHandlerService.class);
if (textViewer.getTextWidget().isFocusControl()) {
activateContext();
}
}
The activateContext() method has a list of all the commands you want to delegate, and registers a new handler for each one. This was one of the changes from David's article; his ITextEditorActionDefinitionIds has been deprecated and replaced with IWorkbenchCommandConstants.
protected void activateContext() {
if (handlerActivations.isEmpty()) {
activateHandler(ITextOperationTarget.COPY,
IWorkbenchCommandConstants.EDIT_COPY);
}
}
// Add a single handler.
protected void activateHandler(int operation, String actionDefinitionId) {
StyledText textWidget = textViewer.getTextWidget();
IHandler actionHandler = createActionHandler(operation,
actionDefinitionId);
IHandlerActivation handlerActivation = handlerService.activateHandler(
actionDefinitionId, actionHandler,
new ActiveFocusControlExpression(textWidget));
handlerActivations.add(handlerActivation);
}
// Create a handler that delegates to the text viewer.
private IHandler createActionHandler(final int operation,
String actionDefinitionId) {
Action action = new Action() {
#Override
public void run() {
if (textViewer.canDoOperation(operation)) {
textViewer.doOperation(operation);
}
}
};
action.setActionDefinitionId(actionDefinitionId);
return new ActionHandler(action);
}
The ActiveFocusControlExpression gives the new handler a high enough priority that it will replace the standard handler, and it's almost identical to David's version. However, to get it to compile, I had to add extra dependencies to my plug-in manifest: I imported packages org.eclipse.core.expressions and org.eclipse.ui.texteditor.
.Net Compact Framework
Scenario: User is on a screen. Device can't finds a printer and asks the user if they want to try again. If they click "No", the current screen is closed and they are returned to the parent menu screen. If they click the "No" button multiple times, the first click will be used by the No button and the next click will take effect once the screen has completed redrawing. (In effect clicking a menu item which then takes the user to another screen.)
I don't see a good place to put a wait cursor...there isn't much happening when the user clicks "No" except a form closing. But the CF framework is slow to redraw the screen.
Any ideas?
you can skip pending clicks by clearing the windows message queue with
Application.DoEvents();
We use the following custom Event class to solve your problem (preventing multiple clicks and showing a wait cursor if necessary):
using System;
using System.Windows.Forms;
public sealed class Event {
bool forwarding;
public event EventHandler Action;
void Forward (object o, EventArgs a) {
if ((Action != null) && (!forwarding)) {
forwarding = true;
Cursor cursor = Cursor.Current;
try {
Cursor.Current = Cursors.WaitCursor;
Action(o, a);
} finally {
Cursor.Current = cursor;
Application.DoEvents();
forwarding = false;
}
}
}
public EventHandler Handler {
get {
return new EventHandler(Forward);
}
}
}
You can verify that it works with the following example (Console outputs click only if HandleClick has terminated):
using System;
using System.Threading;
using System.Windows.Forms;
class Program {
static void HandleClick (object o, EventArgs a) {
Console.WriteLine("Click");
Thread.Sleep(1000);
}
static void Main () {
Form f = new Form();
Button b = new Button();
//b.Click += new EventHandler(HandleClick);
Event e = new Event();
e.Action += new EventHandler(HandleClick);
b.Click += e.Handler;
f.Controls.Add(b);
Application.Run(f);
}
}
To reproduce your problem change the above code as follows (Console outputs all clicks, with a delay):
b.Click += new EventHandler(HandleClick);
//Event e = new Event();
//e.Action += new EventHandler(HandleClick);
//b.Click += e.Handler;
The Event class can be used for every control exposing EventHandler events (Button, MenuItem, ListView, ...).
Regards,
tamberg
Random thoughts:
Disable the some of the controls on the parent dialog while a modal dialog is up. I do not believe that you can disable the entire form since it is the parent of the modal dialog.
Alternatively I would suggest using a Transparent control to catch the clicks but transparency is not supported on CF.
How many controls are on the parent dialog? I have not found CF.Net that slow in updating. Is there any chance that the dialog is overloaded and could be custom drawn faster that with sub controls?
override the DialogResult property and the Dispose method of the class to handle adding/remvoing a wait cursor.