I would like to modify the right click context menu for some some SWT Text boxes.
I would like to still have some of the default options like Copy, Cut, Paste, but would also like to have a custom action 'Generate Random' to fill the text box with a UUID.
How can I add such a menu to the control?
here's what I came up with to add some of the standard functions (cut, copy, paste, select all) as well as a custom action (generate UUID)
public static void addContextMenuWithUUID(final Text control)
{
Menu menu = new Menu(control);
MenuItem item = new MenuItem(menu, SWT.PUSH);
item.setText("Cut");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.cut();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Copy");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.copy();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Paste");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.paste();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Select All");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.selectAll();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Generate UUID");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.setText(UUID.randomUUID().toString());
}
});
control.setMenu(menu);
}
When I had to do something similar a while ago, this is what I did,
I adopted the TextActionHandler class provided by eclipse and modified the code to suit my needs.
public final class TextActionHandler {
enum TextAction {
CUT (WorkbenchMessages.Workbench_cut, IWorkbenchCommandConstants.EDIT_CUT),
COPY (WorkbenchMessages.Workbench_copy, IWorkbenchCommandConstants.EDIT_COPY),
PASTE (WorkbenchMessages.Workbench_paste, IWorkbenchCommandConstants.EDIT_PASTE),
DELETE (WorkbenchMessages.Workbench_delete, null),
SELECT_ALL(WorkbenchMessages.Workbench_selectAll, WorkbenchCommandConstants.EDIT_SELECT_ALL);
private String text;
private String commandId;
private TextAction(String text, String commandId ) {
this.text = text;
this.commandId = commandId;
}
public String getCommandId() {
return commandId;
}
public String getText() {
return text;
}
}
public TextActionHandler(Text text) {
addText(text);
}
public TextActionHandler() {
super();
}
public void addText(Text textControl) {
if (textControl == null) {
return;
}
textControl.addDisposeListener(new DisposeListener() {
#Override
public void widgetDisposed(DisposeEvent e) {
removeText(activeTextControl);
}
});
textControl.addListener(SWT.Activate, textControlListener);
textControl.addListener(SWT.Deactivate, textControlListener);
textControl.addKeyListener(keyAdapter);
textControl.addMouseListener(mouseAdapter);
activeTextControl = textControl;
updateActionsEnableState();
}
public void hookContextMenu() {
final MenuManager menuMgr = new MenuManager("#PMPopupMenu");
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
addContextMenuOptions(menuMgr);
}
});
Menu menu = menuMgr.createContextMenu(activeTextControl);
activeTextControl.setMenu(menu);
}
private void addContextMenuOptions(MenuManager manager) {
manager.removeAll();
manager.add(textCutAction);
manager.add(textCopyAction);
manager.add(textPasteAction);
manager.add(textDeleteAction);
manager.add(new Separator());
manager.add(textSelectAllAction);
// add your own action handlers here
}
...
// example.
private final class CutActionHandler extends Action {
private CutActionHandler() {
setProperties(this, TextAction.CUT);
setEnabled(false);
}
#Override
public void runWithEvent(Event event) {
if (activeTextControl != null && !activeTextControl.isDisposed()) {
activeTextControl.cut();
updateActionsEnableState();
}
}
#Override
public boolean isEnabled() {
return activeTextControl != null && !activeTextControl.isDisposed()
&& activeTextControl.getEditable()
&& activeTextControl.getSelectionCount() > 0;
}
public void updateEnabledState() {
setEnabled(isEnabled());
}
}
private void setProperties(Action action, TextAction actionEnum){
action.setText(actionEnum.getText());
action.setActionDefinitionId(actionEnum.getCommandId());
action.setImageDescriptor(getImageDescriptor(actionEnum));
action.setDisabledImageDescriptor(getDisabledImageDescriptor(actionEnum));
}
}
Likewise, you can have your own ActionHandlers added. e.g, RandomGeneratorHandler.
To hook this to your textboxes, do
Text text = new Text(parent, SWT.NONE);
...
TextActionHandler handler = new TextActionHandler();
handler.addText(text);
handler.hookContextMenu();
Note - I have not provided the complete class here, for other actions like copy, paste, delete and select all etc, you will have to do something similar as Cut. I have used the same code defined in the TextActionHandler class.
Related
I want to have a context menu on a TreeView item.
I am expecting that the ActionEvent of the event handler gives me information about which TreeView item is clicked, but I just find that getSource and getTarget return a Menu Item. How can I find out which TreeView Item has been clicked? I can have multiple treevIews in separate Tabs.
FileContextMenu cm = new FileContextMenu(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
System.out.println("Open File");
//MenuItem mi = (MenuItem)e.getSource();
EventTarget et = e.getTarget();
//File editorFile = new File(mi.getId());
System.out.println(et);
//mainWindowController.openEditor(editorFile);
}
}, new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
System.out.println("Create Project From Template");
}
});
which calls this:
public class FileContextMenu extends ContextMenu
{
public FileContextMenu(EventHandler<ActionEvent> ehOpenFile,
EventHandler<ActionEvent> ehProjectFromTemplate)
{
MenuItem item1 = new MenuItem("Open File");
item1.setOnAction(ehOpenFile);
MenuItem item2 = new MenuItem("Create Project From Template");
item2.setOnAction(ehProjectFromTemplate);
this.getItems().addAll(item1, item2);
}
}
I am attaching the menu here:
private void addFilesTab(FXMLLoader loader, String sRoot, ContextMenu cm) throws IOException
{
AnchorPane fileView = loader.load();
FileViewController fileViewController = loader.getController();
FileShort fsRoot = new FileShort(sRoot);
if(fsRoot.exists()) {
fileViewController.setRootFolder(fsRoot);
fileViewController.setContextMenu(cm);
ObservableList<Tab> tabs = navigationTabPane.getTabs();
tabs.add(new Tab(sRoot));
// Connect the FileView with last tab of the Navigation TabPane.
tabs.get(tabs.size()-1).setContent(fileView);
}
}
which calls this:
public void setContextMenu(ContextMenu cm)
{
fileTreeView.setContextMenu(cm);
}
I now try to use a cellfactory, but I don't understand how to use the p parameter to find a cells value . My code for this is:
this.fileTreeView.setCellFactory(new Callback<TreeView<FileShort>,TreeCell<FileShort>>(){
#Override
public TreeCell<FileShort> call(TreeView<FileShort> p) {
TreeCell<FileShort> cell = new TreeCell<FileShort>();
cell.setContextMenu(cm);
return cell;
}
});
You have to create a different context menu for each cell:
this.fileTreeView.setCellFactory(new Callback<TreeView<FileShort>,TreeCell<FileShort>>(){
#Override
public TreeCell<FileShort> call(TreeView<FileShort> p) {
TreeCell<FileShort> cell = new TreeCell<FileShort>() {
#Override
protected void updateItem(FileShort file, boolean empty) {
super.updateItem(file, empty);
if (empty) {
setText(null);
} else {
// maybe use a more appropriate string for display here
// e.g. if you were using a regular java.io.File you would
// likely want file.getName()
setText(file.toString());
}
}
};
ContextMenu cm = createContextMenu(cell);
cell.setContextMenu(cm);
return cell;
}
});
private ContextMenu createContextMenu(TreeCell<FileShort> cell) {
ContextMenu cm = new ContextMenu();
MenuItem openItem = new MenuItem("Open File");
openItem.setOnAction(event -> {
FileShort file = cell.getItem();
if (file != null) {
// open the file...
}
});
cm.getItems().add(openItem);
// other menu items...
return cm ;
}
I've created a vaadin table. When I do right-click it shows context menu with +New... text and when I click on it - it shows modal window with two tables. Every table has the same fuctionality.
The problem is that every time I open and close modal window it adds duplicates for context menu items on modal tables(on the main page it works correct). Moreover - it adds several modal windows when I click on modal table context menu (for example if I open window 5 times - it add 5 context menu items and 5 modal windows for clicked modal context menus)
The only way to return to one item - restart whole application.
What is the problem?
Every my table looks like this
#Component("taskTable")
#Scope("prototype")
public class TaskTable extends AbstractObjectTable {
#Autowired
private TaskService taskService;
#Autowired
private NewTaskWindow taskWindow;
#Autowired
private ShowTaskDetailsWindow detailsWindow;
private Action[] action = new Action[] { new Action("+New...") };
#Override
public Table createTable() {
caption = "Tasks";
headers = new String[] { "Description", "Project", "Status", "Weight", "Developer", "ID" };
this.addActionHandler(new Handler() {
#Override
public Action[] getActions(Object target, Object sender) {
return action;
}
#Override
public void handleAction(Action action, Object sender, Object target) {
switch(action.getCaption()) {
case "+New...": {
PmcUi.getCurrent().addWindow(taskWindow.createWindow());
break;
}
}
//what to do for action
}
});
this.addItemClickListener(new ItemClickListener(){
#Override
public void itemClick(ItemClickEvent event) {
if (event.isDoubleClick()) {
PmcUi.getCurrent().addWindow(detailsWindow.createWindow());
}
return;
}
});
return super.createTable();
}
#Override
protected IndexedContainer projectDatasource() {
IndexedContainer indexedContainer = new IndexedContainer();
for(String header: headers) {
indexedContainer.addContainerProperty(header, String.class, "");
}
List<Task> tasks = taskService.findAllTasks();
for(int i = 0; i < tasks.size(); i++) {
Object id = indexedContainer.addItem();
Task item = tasks.get(i);
indexedContainer.getContainerProperty(id, headers[0]).setValue(item.getDescription());
indexedContainer.getContainerProperty(id, headers[1]).setValue(item.getTaskProject());
indexedContainer.getContainerProperty(id, headers[2]).setValue(item.getStatus());
indexedContainer.getContainerProperty(id, headers[3]).setValue(item.getWeight());
indexedContainer.getContainerProperty(id, headers[4]).setValue(item.getTaskDeveloper());
indexedContainer.getContainerProperty(id, headers[5]).setValue(item.getTaskId());
}
return indexedContainer;
}
}
Where AbstractObjectTable
public abstract class AbstractObjectTable extends Table {
protected String caption;
protected String[] headers = null;
protected Table createTable() {
this.setContainerDataSource(projectDatasource());
this.setVisibleColumns(headers);
this.setSelectable(true);
this.setImmediate(true);
return this;
}
protected abstract IndexedContainer projectDatasource();
}
My +New... modal windows looks similar to that
#Component("newTaskWindow")
public class NewTaskWindow {
private Window createTaskWindow;
#Autowired
private TaskService taskService;
public Window createWindow() {
createTaskWindow = new Window("New Task");
initWindow();
fillWindow();
return createTaskWindow;
}
private void initWindow() {
createTaskWindow.setSizeUndefined();
createTaskWindow.setResizable(false);
createTaskWindow.setModal(true);
createTaskWindow.addCloseListener(new CloseListener() {
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField taskDescription = new TextField("Description");
final ComboBox taskProject = new ComboBox("Select project");
final ComboBox taskDeveloper = new ComboBox("Select developer");
final TextField taskWeight = new TextField("Task weight");
final TextField taskStatus = new TextField("Task status");
Button create = new Button("Create");
create.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Task task = new Task();
task.setTaskId(UUID.randomUUID().toString());
task.setStatus(taskStatus.getValue());
task.setTaskDeveloper(taskDeveloper.getValue().toString());
task.setTaskProject(taskProject.getValue().toString());
task.setWeight(taskWeight.getValue());
task.setDescription(taskDescription.getValue());
taskService.insertTask(task);
createTaskWindow.close();
}
});
Button close = new Button("Cancel");
close.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
createTaskWindow.close();
}
});
HorizontalLayout layout = new HorizontalLayout(create, close);
FormLayout formLayout = new FormLayout(taskProject, taskDeveloper, taskWeight, taskStatus,
taskDescription, layout);
formLayout.setMargin(true);
createTaskWindow.setContent(formLayout);
}
}
And my details windows also have similar architecture.
#Component("showTaskDetailsWindow")
public class ShowTaskDetailsWindow {
private Window showDetailsWindow;
#Autowired
private TaskService taskService;
public Window createWindow() {
showDetailsWindow = new Window("Show details");
initWindow();
fillWindow();
return showDetailsWindow;
}
private void initWindow() {
showDetailsWindow.setSizeUndefined();
showDetailsWindow.setResizable(false);
showDetailsWindow.setModal(true);
showDetailsWindow.addCloseListener(new CloseListener() {
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField taskDescription = new TextField("Description");
final TextField taskProject = new TextField("Task project");
final TextField taskDeveloper = new TextField("Task developer");
final TextField taskWeight = new TextField("Task weight");
final TextField taskStatus = new TextField("Task status");
FormLayout formLayout = new FormLayout(taskProject, taskDeveloper, taskWeight, taskStatus, taskDescription);
formLayout.setMargin(true);
showDetailsWindow.setContent(formLayout);
}
}
What is the problem? Why it is continuously multiplying?
The problem is your getActions implementation
#Override
public Action[] getActions(Object target, Object sender) {
return new Action[] { new Action("+New...")};
}
You should create one instance of the "new Action("+New...")" item and store it for example in the TaskTable object.
The getActions(..) should alsways return the same instance.
If you always create a new action, it just adds them to the already existing actions.
Looks like the createTable() method of the TaskTable class is called too many times but the provided code doesn't show where that method is called. That causes that multiple action handlers and item click listeners are added to a table.
I have a code which adds four check box button to SWT shell , i have added selection Listener to the checkBox buttons , which i want to remove on click of OK,CANCEL and CLOSE button of SWT Shell , How can i dispose such event , this is my code
grpVersionTreeComponents.setLayoutData(componentsRenderer.createGridData(490, 220, 4));
for(int versionCount = 0; versionCount < versionSplitters.length ; versionCount++ ){
String splitter= versionSplitters[versionCount];
Button cbVersionSplitter = new CheckBoxWrapper().getButton(grpVersionTreeComponents,splitter.toString() , "");
cbVersionSplitter.setEnabled(true);
versionSplitterCheckBoxList.add(cbVersionSplitter);
versionSplitterCheckBoxList.get(versionCount).addSelectionListener(addSplitterCheckBoxListner(cbVersionSplitter));
}
this my selection Adapter method
public SelectionAdapter addSplitterCheckBoxListner(final Button button){
return new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
String[] defaultVersionSplitters = PropertyClass.getPropertyLabel(QTLConstants.VERSIONING_ASSISTENT_PAGE_SPLLITER).split(QTLConstants.MULTIPLE_EXTENSIONS_SPLITER);
//check if button.getText() is equal to "Custom Splitter" String
if(button.getText().equalsIgnoreCase(defaultVersionSplitters[3])){
if(button.getSelection()){
customVersionSplitterText.setEnabled(true);
}else{
customVersionSplitterText.setEnabled(false);
}
}
}
};
}
and OK cancel Buttons are added in this fashion
private void addOkCancelButtonOnVersionTreePopup(final Shell versionTreeComponentsShell){
Button ok = componentsRenderer.createButtonWidget(versionTreeComponentsShell, SWT.PUSH,
PropertyClass.getPropertyLabel(QTLConstants.OK_BUTTON));
Button cancel = componentsRenderer.createButtonWidget(versionTreeComponentsShell, SWT.PUSH,
PropertyClass.getPropertyLabel(QTLConstants.CANCEL_BUTTON));
ok.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
}
});
}
cancel.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
/*disposeSelectionListener(cbVersionSplitter);*/
versionTreeComponentsShell.close();
}
});
}
}
so how can i remove this selection listener
Regards
You can do the following:
1). Create class which will extend the SelectionAdapter class.
2). Override widgetSelected() method as you have done in your code.
3). Override equals(Object other) method with the following:
#Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (other instanceof MyOwnSelectionAdapter) {
return true;
}
}
where MyOwnSelectionAdapter is your own implementation of SelectionAdapter.
4). Now you can do the following: cancel.removeSelectionListener(new MyOwnSelectionAdapter()); and it will delete that selection listener.
I want to use a context menu item on the lines of a listView. In the event handler of the listView's MOUSE_CLICKED event, the getSelectionModel().getSelectedItem() returns the selected item, thats ok. But, when I handle the contextMenuItem's onAction event, it returns null. However, graphically the item is selected.
Is there a way to "keep" the selection after the first event handling?
Here is the relevant part of the code:
ListView<Text> nameList = new ListView<>();
final ContextMenu cCm = new ContextMenu();
MenuItem cItem = new MenuItem("someText");
cCm.getItems().add(cItem);
...
nameList.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
if (e.getButton() == MouseButton.SECONDARY) {
//its OK here:
System.out.println(nameList.getSelectionModel().getSelectedItem().getText());
cCm.show(nameList, e.getScreenX(), e.getScreenY());
}
}
});
cItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
final Stage dialog = new Stage();
dialog.initModality(Modality.WINDOW_MODAL);
//nullPointerException on the following:
Text t = new Text(nameList.getSelectionModel().getSelectedItem().getText());
//showing dialog, etc.
I pretty much created an exact replica of what you did, and my implementation worked:
private void initRandomCardListView() {
populateRandomList();
final ContextMenu randomListContextMenu = new ContextMenu();
MenuItem replaceCardMenuItem = new MenuItem("Replace");
replaceCardMenuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
replaceRandomCard();
}
});
randomListContextMenu.getItems().add(replaceCardMenuItem);
randomCardList.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton().equals(MouseButton.SECONDARY)) {
randomListContextMenu.show(randomCardList, event.getScreenX(), event.getScreenY());
}
}
});
}
private void replaceRandomCard() {
System.out.println("jobs done");
System.out.println("card selected: " + randomCardList.selectionModelProperty().get().getSelectedItem().toString());
System.out.println("card index: " + randomCardList.getSelectionModel().getSelectedIndex());
System.out.println("card index: " + randomCardList.getSelectionModel().getSelectedItem().toString());
}
I don't have any null pointer exceptions. Overall your implementation looks good. There is most likely something that is wrong with the items in your listview.
I have subclassed org.eclipse.swt.widgets.Composite to create a new composite control. I want to capture MouseEnter and MouseExit events in this control but the problem I have is that when the mouse is hovered over a component in the control (say, a Label) the MouseExit event is fired, even though the label is part of the whole Composite.
Is there any way to stop this event being fired? I only want to see the event if the mouse leaves the total boundary of the control. Here is some example code to show you what I mean.
public class MyControl extends Composite{
Label label;
public MyControl(Composite parent, String label) {
super(parent, SWT.NONE);
label = new Label(this,0);
label.setText(label);
this.addListener(SWT.MouseEnter, new Listener() {
#Override
public void handleEvent(Event event) {
// handle this event
}
});
this.addListener(SWT.MouseExit, new Listener() {
#Override
public void handleEvent(Event event) {
// handle this event
}
});
}
}
You can simply put an logic in your event handler to see if the control is a child of your new control and ignore it. Something like the following: (I haven't tested the code, but I think this should work for you)
this.addListener(SWT.MouseExit, new Listener() {
#Override
public void handleEvent(Event event) {
for (Control control : ParentClass.this.getChildren()) {
if (control == event.item)
return;
}
// handler logic goes here
}
});
I solved the same problem (MouseExit is sent to a Composite when the mouse enters one of its children) with an event filter. Here's the code.
public class MouseTracker implements Listener {
static MouseTracker instance;
private static class Item {
Composite composite;
boolean inside;
MouseTrackListener listener;
}
private List<Item> listeners = new ArrayList<>();
private MouseTracker() {
}
public static MouseTracker getInstance() {
if (instance == null)
instance = new MouseTracker();
return instance;
}
private void install() {
Display.getCurrent().addFilter(SWT.MouseEnter, this);
Display.getCurrent().addFilter(SWT.MouseExit, this);
Display.getCurrent().addFilter(SWT.Resize, this);
}
private void uninstall() {
Display.getCurrent().removeFilter(SWT.MouseEnter, this);
Display.getCurrent().removeFilter(SWT.MouseExit, this);
Display.getCurrent().removeFilter(SWT.Resize, this);
}
public void addMouseTrackListener(Composite c, MouseTrackListener listener) {
if (listeners.isEmpty())
install();
Item i = new Item();
i.composite = c;
i.inside = false;
i.listener = listener;
listeners.add(i);
}
public void removeMouseTrackListener(Composite c, MouseTrackListener listener) {
listeners.removeIf((i) -> i.composite == c && i.listener == listener);
if (listeners.isEmpty())
uninstall();
}
public void handleEvent(Event e) {
boolean hasDisposed = false;
for (Item i : listeners) {
Composite c = i.composite;
if (c.isDisposed())
hasDisposed = true;
else {
Point p = Display.getCurrent().getCursorLocation();
boolean containsMouse = c.getBounds().contains(c.getParent().toControl(p));
if (i.inside != containsMouse) {
i.inside = containsMouse;
if (containsMouse)
i.listener.mouseEnter(new MouseEvent(e));
else
i.listener.mouseExit(new MouseEvent(e));
}
}
}
if (hasDisposed) {
listeners.removeIf((i) -> i.composite.isDisposed());
if (listeners.isEmpty())
uninstall();
}
}
}