Overriding Cut/Copy/Paste in SWT Text control - java

What is the correct way to override the cut(), copy(), and paste() methods of the Text control? What triggers the execution of these methods?
I have created an example application with a custom class that overrides these methods. Unfortunately, nothing seems to execute these overridden methods, including the act of using Ctrl+X / Ctrl+C / Ctrl+V or selecting cut/copy/paste from the context menu.
Custom Text Class:
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
public class TextCopyable extends Text{
public TextCopyable(Composite parent, int style) {
super(parent, style);
}
#Override
public void checkSubclass() {
}
#Override
public void cut() {
System.out.println("Cut!");
}
#Override
public void copy() {
System.out.println("Copy!");
}
#Override
public void paste() {
System.out.println("Paste!");
}
}
Test Shell:
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.layout.GridData;
public class CopyPasteTest extends Shell {
private TextCopyable text;
public static void main(String args[]) {
try {
Display display = Display.getDefault();
CopyPasteTest shell = new CopyPasteTest(display);
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public CopyPasteTest(Display display) {
super(display, SWT.SHELL_TRIM);
createContents();
}
protected void createContents() {
setText("SWT Application");
setSize(450, 300);
GridLayout gridLayout = new GridLayout();
setLayout(gridLayout);
text = new TextCopyable(this, SWT.BORDER);
text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
}
#Override
protected void checkSubclass() {
// Disable the check that prevents subclassing of SWT components
}
}
My expectation was that the copy() method would be called any time that I use the Ctrl+C command to copy text from the textbox. However, the methods do not trigger at all. Is my assumption faulty?

Related

Eclipse SWT: Can't scroll over Text when vertical scrolling is enabled

I have a Text inside a Group, which is inside a Composite and that resides inside a ScrolledComposite. All Elements are inside an EditorPart.
ScrolledComposite mySc
|- Composite myComposite
|- Group myGroup
|- Text myText
I can scroll (using the mouse wheel) over all Elements in the EditorPart, but when the cursor is over the Text area, the scrolling stops.
I want to scroll inside the Text only when it has keyboard focus.
Instantiation of the Text myText:
myText = new Text(myGroup, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
Without SWT.V_SCROLL it works, but then I don't have scroll bars and the possibility to scroll in the Text.
I thought I could maybe use forceFocus() on the parent in case the Text has no Focus Control:
myText.addListener(SWT.MouseWheel, new Listener() {
#Override
public void handleEvent(Event event) {
if (!commandText.isFocusControl()) {
System.out.println("no focus");
Control wheelControl = myText.getParent();
Point cursorPos = wheelControl.toControl(event.display.getCursorLocation());
event.x = cursorPos.x;
event.y = cursorPos.y;
event.widget = wheelControl;
wheelControl.forceFocus();
wheelControl.notifyListeners(SWT.MouseWheel, event);
} else {
System.out.println("Focus control");
}
}
});
But it doesn't work. No change at all. It only prints "Focus control" and "no focus" correctly.
EDIT:
Here is a minimal working example:
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
public class MyEditor extends EditorPart {
private Text myText;
private boolean dirty = false;
public MyEditor() {
super();
}
#Override
public void init(IEditorSite site, IEditorInput input) {
setSite(site);
setInput(input);
}
#Override
public void doSave(IProgressMonitor monitor) {
return;
}
#Override
public void doSaveAs() {
return;
}
#Override
public boolean isDirty() {
return dirty;
}
#Override
public boolean isSaveAsAllowed() {
return false;
}
#Override
public void createPartControl(Composite parent) {
parent.setLayout(new FillLayout());
ScrolledComposite mySc = new ScrolledComposite(parent, SWT.V_SCROLL);
Composite myComposite = new Composite(mySc, SWT.BORDER);
myComposite.setLayout(new GridLayout(1, false));
// Set the child as the scrolled content of the ScrolledComposite
mySc.setContent(myComposite);
// Expand both horizontally and vertically
mySc.setExpandHorizontal(true);
mySc.setExpandVertical(true);
Group myGroup = new Group(myComposite, SWT.NONE);
myGroup.setText("Hello or something");
myGroup.setLayout(new GridLayout(1, false));
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.verticalIndent = 10;
myGroup.setLayoutData(gd);
Label aLabel = new Label(myGroup, SWT.NONE);
aLabel.setText("You can write here: ");
myText = new Text(myGroup, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
myText.setText("Some Default Text");
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.heightHint = 300;
gd.horizontalIndent = 10;
myText.setLayoutData(gd);
myText.addListener(SWT.MouseWheel, new Listener() {
#Override
public void handleEvent(Event event) {
if (!myText.isFocusControl() ) {
System.out.println("no focus");
Control wheelControl = myText.getParent();
Point cursorPos = wheelControl.toControl(event.display.getCursorLocation());
event.x = cursorPos.x;
event.y = cursorPos.y;
event.widget = wheelControl;
wheelControl.forceFocus();
wheelControl.notifyListeners(SWT.MouseWheel, event);
myText.setCapture(false);
} else {
System.out.println("Focus control");
myText.setCapture(true);
}
}
});
mySc.setMinSize(myComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
// TODO Auto-generated method stub
}
#Override
public void setFocus() {
// TODO Auto-generated method stub
}
}
The solution I found was to disable the vertical scrollbar. This also disables scrolling by mouse wheel. Also use SWT's addMouseWheelListener() and mouseScrolled() methods instead of addListener(). Than just scroll the ScrolledComposite by using its getOrigin() method.
myText.addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseScrolled(MouseEvent e) {
if (!myText.isFocusControl() ) {
myText.getVerticalBar().setEnabled(false);
if (e.count == 3) {
mySc.setOrigin(sc.getOrigin().x, mySc.getOrigin().y - 30);
} else if (e.count == -3) {
mySc.setOrigin(sc.getOrigin().x, mySc.getOrigin().y + 30);
}
} else {
myText.getVerticalBar().setEnabled(true);
}
}
});
count always returns 3 or -3, depending on the scroll direction. The value of 30 for scrolling up/down is good for me, might be more or less for other purposes. I didn't check the behavior on a Windows machine yet.

Swing UI freezes when trying to update from SwingWorker

I've tried to make a simple Swing example which uses SwingWorker to update the text of a Label. Whenever the SwingWorker finishes and tries to update the text, the UI will freeze. After closing the window it'll show a AWT-EventQueue-0 "Widget is disposed" exception.
I've tried to make sure it's running on the EDT etc, but I think I'm missing something very basic here. Could someone please look at my code and tell me what might be happening?
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.SWT;
public class MySwingWindow {
private Label mainText;
protected Shell shell;
/**
* Launch the application.
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MySwingWindow window = new MySwingWindow();
window.open();
}
});
}
/**
* Open the window.
*/
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
try {
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
#Override
protected Boolean doInBackground() throws Exception {
for (int i = 0; i <= 2; i++) {
Thread.sleep(1000);
System.out.println("Running " + i);
}
return true;
}
#Override
protected void done() {
mainText.setText("Done");
}
};
worker.execute();
} catch (Exception e) {
e.printStackTrace();
}
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Create contents of the window.
*/
protected void createContents() {
shell = new Shell();
shell.setSize(450, 300);
shell.setText("SWT Application");
mainText = new Label(shell, SWT.NONE);
mainText.setBounds(78, 84, 55, 15);
mainText.setText("Working");
}
}

Opening one shell each time

In my project i have a shell, in the shell there are 3 buttons, i want that a click on each button will open a shell ,but i want that if a shell is already open due to a click on a button then that shell will be closed and a new shell will be opened.
(I dont want 2 shell from clicking buttons to be open at the same time)
But i have no idea how to do this.
In this class the opening of the shells should be.
public class ClickLabel implements MouseListener
{
Shell shell;
int p;
public ClickLabel(int p)
{
shell = new Shell();
this.p = p;
}
#Override
public void mouseDoubleClick(MouseEvent e) {}
#Override
public void mouseDown(MouseEvent e) {}
#Override
public void mouseUp(MouseEvent e) {
shell.open();
}
}
Can anyone help me?
Here is simple example with buttons and one active Shell, examine that:
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class Example{
public static void main(String[] args) {
new Example();
}
private Shell openedShell;
public Example() {
final Display display = new Display ();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
SelectionAdapter adapter = new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
if(openedShell != null){
openedShell.dispose();
}
openedShell = new Shell(display);
openedShell.setSize(200,200);
openedShell.setText(((Button)e.getSource()).getText());
openedShell.open();
}
};
for(int i =1;i<4;i++){
Button b = new Button(shell, SWT.PUSH);
b.setText("shell "+i);
b.addSelectionListener(adapter);
b.pack();
}
shell.pack();
shell.open ();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}
}

Disable close button in java JFace dialog?

How can i disable the close button (or make it disappear completely if possible) in a java JFace dialog?
Buttons in a Dialog are created with the method createButton(). To "filter out" the cancel button, you can override it as follows:
protected Button createButton(Composite parent, int id,
String label, boolean defaultButton) {
if (id == IDialogConstants.CANCEL_ID) return null;
return super.createButton(parent, id, label, defaultButton);
}
However, the Dialog's close button (provided by the OS) still works. To disable it, you can override canHandleShellCloseEvent():
protected boolean canHandleShellCloseEvent() {
return false;
}
Here is a complete, minimal example:
package stackoverflow;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class JFaceDialogNoCloseButton {
private static final Display DISPLAY = Display.getDefault();
public static void main(String[] args) {
Shell shell = new Shell(DISPLAY, SWT.CLOSE | SWT.RESIZE);
shell.setSize(200, 100);
shell.setLayout(new FillLayout());
final Dialog dialog = new InputDialog(shell, "Title", "Message",
"initial value", null) {
#Override
protected Button createButton(Composite parent, int id,
String label, boolean defaultButton) {
if (id == IDialogConstants.CANCEL_ID)
return null;
return super.createButton(parent, id, label, defaultButton);
}
#Override
protected boolean canHandleShellCloseEvent() {
return false;
}
};
Button button = new Button(shell, SWT.PUSH);
button.setText("Launch JFace Dialog");
button.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
dialog.open();
}
});
shell.open();
while (!shell.isDisposed()) {
if (!DISPLAY.readAndDispatch()) {
DISPLAY.sleep();
}
}
DISPLAY.dispose();
}
}
To make the X button not visible on a Dialog you have to turn off the SWT.CLOSE style property. Important to note that this has to be done before the dialog is opened, so in the constructor of your dialog would work.
public NoCloseDialog(...){
super(...);
setShellStyle(getShellStyle() & ~SWT.CLOSE);
}
The default shell style on a JFace Window is SWT.SHELL_TRIM which is equal to SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX | SWT.RESIZE
See here for an example showing how to hide the close button in a Dialog. You simply override following method:
protected void setShellStyle(int arg0){
//Use the following not to show the default close X button in the title bar
super.setShellStyle(SWT.TITLE);
}
Otherwise override close() and return false to prevent closing.
Update: While the above code "solves" the problem at hand, it doesn't explain a lot, and introduces a nasty bug. Please see Goog's answer, for a way better version.
For a dialog extended from org.eclipse.jface.dialogs.Dialog, overrding canHandleShellCloseEvent did not work for me,
Then closing the entire application worked as a good strategy for my situation, because I had to do the same if the user choose to cancel.
I know this is not the exact answer to the question but can be used as a workaround to handle the situation.
under open() or createContents() method,
shell.addListener(SWT.Close, new Listener() {
public void handleEvent(Event event) {
System.exit(0);
}
});

How do you hide a Swing Popup when you click somewhere else

I have a Popup that is shown when a user clicks on a button. I would like to hide the popup when any of the following events occur:
The user clicks somewhere else in the application. (The background panel for example)
The user minimizes the application.
The JPopupMenu has this behavior, but I need more than just JMenuItems. The following code block is a simplified illustration to demonstrate the current usage.
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class PopupTester extends JFrame {
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
int y = (int) (location.getY() + getHeight());
int x = (int) location.getX();
JLabel myComponent = new JLabel("Howdy");
Popup popup = PopupFactory.getSharedInstance().getPopup(popupTester, myComponent, x, y);
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Use a JPopupMenu. You can add any component to it, not just menu items.
As pajton noted in a previous comment, Popup is not a JComponent to which listeners can be readily bound. But, as its documentation states, "implementations of Popup are responsible for creating and maintaining their own Components to render [its subject] to the user."
So in using it as your presentation mechanism, your Popup is going to have to present itself as an actual Swing component anyway. Have it register itself to that component. Have it hide itself when the component loses focus.
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.Popup;
public class PopupTester extends JFrame {
private static class MessagePopup extends Popup
implements WindowFocusListener
{
private final JDialog dialog;
public MessagePopup(Frame base, String message) {
super();
dialog = new JOptionPane().createDialog( base, "Message" );
dialog.setModal( false );
dialog.setContentPane( new JLabel( message ) );
}
#Override public void show() {
dialog.addWindowFocusListener( this );
dialog.setVisible( true );
}
#Override public void hide() {
dialog.setVisible( false );
dialog.removeWindowFocusListener( this );
}
public void windowGainedFocus( WindowEvent e ) {
// NO-OP
}
public void windowLostFocus( WindowEvent e ) {
hide();
}
}
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
MessagePopup popup = new MessagePopup( popupTester, "Howdy" );
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
You can add MouseListener to your background panel and hide the popup when somebody clicks on the panel.
To react on application minimization, use WindowListener attached to a JFrame.
Etc, etc. May seem tedious, but surely will work.
Thanks pajton and Noel Ang for getting me pointed in the right direction! Here is the solution that I ended up with. I'm just including it here so that others may benefit from it.
I ended up going with a JWindow since it doesn't get the window decorations but does get focus events.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PopupTester extends JFrame {
private static class MessagePopup extends Popup implements WindowFocusListener {
private final JWindow dialog;
public MessagePopup(Frame base, JLabel component, int x, int y) {
super();
dialog = new JWindow(base);
dialog.setFocusable(true);
dialog.setLocation(x, y);
dialog.setContentPane(component);
component.setBorder(new JPopupMenu().getBorder());
dialog.setSize(component.getPreferredSize());
dialog.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
dialog.setVisible(false);
}
}
});
}
#Override
public void show() {
dialog.addWindowFocusListener(this);
dialog.setVisible(true);
}
#Override
public void hide() {
dialog.setVisible(false);
dialog.removeWindowFocusListener(this);
}
public void windowGainedFocus(WindowEvent e) {
// NO-OP
}
public void windowLostFocus(WindowEvent e) {
hide();
}
}
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
int x = (int) location.getX();
int y = (int) (location.getY() + getHeight());
JLabel myComponent = new JLabel("Howdy");
MessagePopup popup = new MessagePopup(popupTester, myComponent, x, y);
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
You could add a FocusListener to your popup-window, and dispose it when it loses focus. However, that will cause you some troubles when the focus loss is due to some other application (new windows comes to the foreground, you switch virtual desktops, etc.)
But perhaps you (a) know that that cannot happen in your case or (b) would want to close the popup in such cases anyway, a focus-based approach may still be interesting to you.
I know this is an old question but I really needed the Popup to work in my case. So I tried a few things and the following is my solution.
Add a FocusListener to the component you add to the popup and program the focusLost event on that component to hide the popup when focus is lost. Call the requestFocus method on your component just after showing the popup.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.*;
public class PopupTester extends JFrame {
JButton myButton = new JButton("Click Me");
JLabel myComponent = new JLabel("Howdy");
Popup popup = null;
public PopupTester() {
setLayout(new FlowLayout());
setSize(300, 100);
add(myButton);
add(new JButton("No Click Me"));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myComponent.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
if (popup != null) {
popup.hide();
}
}
});
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
if (popup != null) {
popup.hide();
popup = null;
}
Point location = myButton.getLocationOnScreen();
int y = (int) (location.getY() + myButton.getHeight());
int x = (int) location.getX();
popup = PopupFactory.getSharedInstance().getPopup(PopupTester.this, myComponent, x, y);
popup.show();
myComponent.requestFocus();
}
});
}
public static void main(String[] args) {
PopupTester popupTester = new PopupTester();
popupTester.setVisible(true);
}
}

Categories