How enable or disable correctly an Action - java

i have a little problem when i try to disable an Action of my Netbeans platform project. When the app starts, some Actions must be disabled, and i do that with this method:
CallableSystemAction.get(BuildProjectAction.class).setEnabled(FLAG);
It works, because the BuildProjectAction is disabled, but the corresponding items of the MenuBar and the Toolbar remains enabled until i click on one of it.
Only later that i have clicked on it, the comportament start to work correctly.
First question: Why?
If i want disable an Action, it's obvious that i want disable also the relative Icon in the Menu and in the Toolbar, so it must be automatic when i call Action.setEnabled(false).
It doesn't have sense that the Icons are not refreshed if i don't click on they.
Same problem if i try to use .getToolbarPresenter().setEnabled(false); and .getMenuPresenter().setEnabled(false);
For start the application with the icons disabled, I have tried to set the lazy attribute to FALSE and declare the image programmatically with the method setIcon(new ImageIcon(image)); that sets the same image for Menu and Toolbar.
And it works; there is only another problem: Menu and Toolbar have icons of different size (16x16 and 24x24).
It doesn't have sense that the if i set the icon with the #ActionRegistration(iconBase = "image.png") the correct icon is automatically selected, but if i use the method .setIcon(), it doesn't.
I have read some articles about Action, CookieAction, Lookup, but the only thing that i want is disable the graphic elements in the same moment when i disable the Action.
Second question: How i can do that?
This is an example of my Action.
#ActionID(
category = "Run",
id = "BuildProjectAction")
#ActionRegistration(
lazy = true,
iconBase = "images/icons/compile.png",
displayName = "#CTL_BuildProjectAction")
#ActionReferences({
#ActionReference(
path = "Menu/Run",
position = 3),
#ActionReference(path = "Toolbars/Run",
position = 3),
#ActionReference(
path = "Shortcuts",
name = "D-B")
})
#Messages("CTL_BuildProjectAction=Build Project")
public final class BuildProjectAction extends CallableSystemAction {
#Override
public void actionPerformed(ActionEvent e) {...}
#Override
public void performAction() {}
#Override
public String getName() {
return Bundle.CTL_BuildProjectAction();
}
#Override
public HelpCtx getHelpCtx() {
return HelpCtx.DEFAULT_HELP;
}
Thanks

The easiest way to create an action that is disabled at startup is to use the platform’s New Action Wizard to create your action, and to create one that depends on a "context" -- this is, on finding a specific object in the global lookup. If no object is available in the lookup, as at startup, then the action will be disabled.
The menu and toolbar graphic elements are bundled together with your action via the annotations. This means that enabled/disabled state of your context-aware action will automatically affect the icons in the menu and toolbar as well.
This article by Geertjan Wielenga has a walkthrough on creating a context-aware action:
http://netbeans.dzone.com/how-to-make-context-sensitive-actions
When you want to enable your action, you will add the object on which the action depends into the global lookup, which will cause the action (and its graphic elements) to be enabled.
This entry in the platform’s Developer FAQ has some examples of how to add an object to the global context:
http://wiki.netbeans.org/DevFaqAddGlobalContext
If you need to create an action that depends on a more complex set of conditions there is some discussion, as well as a code sample illustrating how to do this, in this platform developer list thread:
http://forums.netbeans.org/ptopic55295.html
The grayed-out versions of the icons that are shown when your action is disabled are created automatically by the platform. You only have to provide the "normal" non-grayed-out images.
As for the icons of different sizes, it’s a matter of filename convention. If your annotation declares the icon with #ActionRegistration(iconBase = "image.png”), then you will provide a 16x16 image called “image.png” and a 24x24 version called “image24.png”. The platform will find and use the appropriate size in the menu and toolbar.

Related

Eclipse 4 RCP Editor using SourceViewer Undo and Redo operations are not working

I'm creating an editor using 'SourceViewer'. Given below is the code snippet from my '#PostConstruct' method.
// viewer is my SourceViewer instance
viewer = new SourceViewer(parent,verticalRuler, styles);
IUndoManager undoManager = new TextViewerUndoManager(25);
undoManager.connect(viewer);
viewer.setUndoManager(undoManager);
Even though a default 'TextViewerUndoManager' associated with 'SourceViewer'. Ctrl+Z and Ctrl+Y is not working.
Another alternative that I tried is to override the 'IUndoManager getUndoManager(ISourceViewer sourceViewer)' of 'SourceViewerConfiguration' subclass and return a 'TextViewerUndoManager'. This approach also doesn't give the desired result.
Kindly let me know what I'm missing in the above approaches.
It is normally the SourceViewerConfiguration that provides the Undo manager, the SourceViewer expects this and will set up the manager from that. The defaults already set up TextViewerUndoManager.
In an e4 application you do not get any default key bindings, commands or handlers so you will have to set up all these to make use of the undo manager.
In your application model declare Commands for undo and redo.
Declare key bindings for Ctrl+Z and Ctrl+Y specifying your commands. You might want to put the key bindings in a Binding Table that is specific to text editors.
Declare Handlers for the undo and redo commands, the code for undo might look like:
public class UndoHandler
{
#Inject
private Adapter _adapter;
#Execute
public void execute(#Named(IServiceConstants.ACTIVE_PART) final MPart part)
{
final ITextOperationTarget opTarget = _adapter.adapt(part.getObject(), ITextOperationTarget.class);
opTarget.doOperation(ITextOperationTarget.UNDO);
}
#CanExecute
public boolean canExecute(#Named(IServiceConstants.ACTIVE_PART) final MPart part)
{
final ITextOperationTarget opTarget = _adapter.adapt(part.getObject(), ITextOperationTarget.class);
if (opTarget == null)
return false;
return opTarget.canDoOperation(ITextOperationTarget.UNDO);
}
}
Redo would be similar but using ITextOperationTarget.REDO.
The order of doing/registering things is important. Be sure to connect the undo manager AFTER setting the document to the SourceViewer instance because on connect() the document will be retrieved from the viewer by the undo manager and if it doesn't find a document it will not register anything and undoable() will always return false.

How to communicate between a RCP workbench toolbar and RCP views

I have a RCP application with some views and an ActionBarAdvisor that creates a toolbar for the application. The toolbar is initialized like this:
public class ApplicationActionBarAdvisor extends ActionBarAdvisor
...
protected void fillCoolBar(ICoolBarManager coolBar)
{
// Create new Toolbar
IToolBarManager toolbar = new ToolBarManager(coolBar.getStyle());
// Adds toolbar to perspective
coolBar.add(toolbar);
...
ActionContributionItem compareFilesCI = new ActionContributionItem(compareFiles);
compareFilesCI.setMode(ActionContributionItem.MODE_FORCE_TEXT);
toolbar.add(compareFilesCI);
...
}
The purpose of this toolbar item is to switch the color of the values of a JFace table. The user can switch the coloring in the table on or off by pressing the button.
But what is the best way to tell the table that coloring should be enabled/disabled? At the moment I've done it this way:
public class ActionCompareFiles extends Action implements ISelectionListener, ActionFactory.IWorkbenchAction
{
...
public void run()
{
// Check if the button is enabled. When it is enabled, comparison should be performed
try
{
// Get the label values view
final ViewFileValues labelViewer = (ViewFileValues) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getViewReferences()[3].getView(true);
// Refresh the view
labelViewer.setColoringValues(this.isChecked());
}
catch (Exception e)
{
System.out.println("Exception when searching the values view");
}
}
...
When pressing the button, I get the view that contains the table. Within this view class is a method 'SetColoringValues' that informs the label providers for the table columns that the button was pressed and updates the table.
This works but I'm not sure if it is the best solution...
My first idea was to simple add listeners to the button. After pressing the button, the listeners will be informed. But this seems not to work because I don't get the ActionBarAdvisor object, were the
toolbar was created. There seems to be no getter method for the toolbar that is part of the workbench window.
What is the best solution for this problem?
1) you should better use the Eclipse RCP commands framework (extension points *.commands, *.handlers, *.menu) instead of implementing this the way you do. It may look cumbersome at the first glance but in fact it is much more structured and straightforward.
2) the way you get an instance of the view is totally wrong: you rely upon the fact that the view has index of 3 in the page which is never warranted.

How to wire up GWT hyperlink click handler?

I am brand new to GWT and am trying to achieve the following:
Here's the code that I've cooked up:
public class MyWebApp implements EntryPoint {
// The main container for everything the user sees (the "view")
private LayoutPanel mainPanel;
// Simple HTML for the header ("MyWebApp") and subsequent <hr/>
private SafeHtml header;
// The three links "Dashboard", "Monitors" and "Help Desk"
private HorizontalPanel navMenu;
// The empty content that gets populated when user clicks one of
// the 3 links.
private Panel menuContent;
#Override
public void onModuleLoad() {
// The initial fragment contains the header, nav menu and empty "content" div.
// Each menu/screen then fills out content div.
initMainPanel();
RootPanel.get().add(mainPanel);
}
private void initMainPanel() {
SafeHtmlBuilder headerBuilder = new SafeHtmlBuilder();
navMenu = new HorizontalPanel();
// Leaving null until user clicks on one of the 3 menus.
// Then the menu will decide what panel gets injected for
// this panel.
menuContent = null;
// Create the simple HTML for the header.
headerBuilder.append("<h1>MyWebApp</h1><hr/>");
// Create the navMenu items.
Hyperlink dashboardLink, monitorsLink, helpDeskLink;
// Homepage is http://www.mywebapp.com
// I want the dashboardLink to inject menuContent and "redirect" user to
// http://www.mywebapp.com/dashboard
dashboardLink = new Hyperlink("???", "???");
// http://www.mywebapp.com/monitors
monitorsLink = new Hyperlink("???", "???");
// http://www.mywebapp.com/help-desk
helpDeskLink = new Hyperlink("???", "???");
navMenu.add(dashboardLink);
navMenu.add(monitorsLink);
navMenu.add(helpDeskLink);
// Add all widgets to the mainPanel.
mainPanel.add(new HTML(headerBuilder.toSafeHtml().toString()));
mainPanel.add(navMenu);
mainPanel.add(menuContent);
// Position and size the widgets (omitted for brevity).
// mainPanel.setWidgetHorizontalPosition(...);
}
private HTML getDashboardMenuContent() {
return new HTML("This is the dashboard.");
}
private HTML getMonitorsMenuContent() {
return new HTML("These are the monitors.");
}
private HTML getHelpDeskMenuContent() {
return new HTML("This is the help desk.");
}
}
Most importantly:
How do I "wire up" the Hyperlinks so that when the user clicks them, I can call the appropriate getXXXMenuContent() method, and then add that to menuContent?
But also:
I feel like I'm doing something wrong here: mainPanel.add(new HTML(headerBuilder.toSafeHtml().toString())); - if so what is it?!? How should I be adding a simple <h1> and <hr/> in a way that's secure (hence the use of the Safe* objects), efficient, and conforming to recommended practices?
Should I be implementing UiBinder here? If so, would I make UiBinders for each menu's content or for the entire mainPanel, or both?
Thanks in advance!
Hyperlink widgets trigger navigation. You don't want to handle clicks on them, you want to handle navigation (that could be triggered by clicking a Hyperlink or using the browser's back/forward buttons, a bookmark or link from elsewhere –including Ctrl+clicking a Hyperlink to open it in a new window/tab–, etc.)
To react to those navigation events, use History.addValueChangeHandler; and to handle the initial navigation on application start, call History.fireCurrentHistoryState() (after you add your handler of course).
More details in: https://developers.google.com/web-toolkit/doc/latest/DevGuideCodingBasicsHistory
Would be better to split other questions to... other questions, but here are the answers anyway:
I feel like I'm doing something wrong here: mainPanel.add(new HTML(headerBuilder.toSafeHtml().toString())); - if so what is it?!? How should I be adding a simple <h1> and <hr/> in a way that's secure (hence the use of the Safe* objects), efficient, and conforming to recommended practices?
The HTML widget has a constructor taking a SafeHtml so you don't need to call toString().
If you're only using a constant, you don't need a SafeHtmlBuilder; use SafeHtmlUtils instead. But constants are no more or less secure with or without SafeHtml, SafeHtml just makes it easier to find all occurrences of HTML in your code, to help in doing a security review of your app (BTW, we're doing HTML, so <hr>, not <hr/>; if you really want it to look like XML/XHTML, then use <hr /> but you're only cheating yourself here)
Should I be implementing UiBinder here? If so, would I make UiBinders for each menu's content or for the entire mainPanel, or both?
If you don't feel the need for UiBinder, you don't have to use it. But in this case it won't change anything: you're not handling widget events, but history events.
Something like
dashboardLink.addClickHandler(
new ClickHandler()
{
public void onClick( ClickEvent event )
{
mainPanel.setWidget( getDashboardMenuContent() );
}
} );
You should note that Hyperlink.addClickHandler(...) is deprecated and it is recommended to use Anchor.addClickHandler(...) instead.
As for the other questions: It is a lot more elegant and easier to build UI's with UIBinder, so definitely look into that, but do try to make "it" work first to avoid the added complexity of the .ui.xml setup :-)
Cheers,
I have one simple piece of advice to give you. Use what the framework has to offer.
The HTML widget should be your last escape. There are so many widgets that there is no need for you to write html almost anywhere in your code.
So instead of headerBuilder, you can user the following piece of code
Label header = new Label("MyWebApp");
header.setStyleName("headerStyle",true);
You can set the style properties in an external Css file and add the reference inside the base html file or the gwt.xml file. So that answers your question about mainPanel.add(new HTML(headerBuilder.toSafeHtml().toString()));
In respect to the Hyperlink. If you choose to use hyperlinks, remember that the most effective usage is with the MVP pattern better known as Places and Activities (Lots of information on the web)
If you want something simpler instead the MenuBar and MenuItem classes should do the trick.
Look here for an example on how to use the MenuBar to control your application. There are many other ways but why not use the tools provided?
Also the UIBinder Vs the Designer/Classes methods is extensively discussed on stackoverflow resulting to a matter of choice and programming familiarity/preference.

SWT, Maintain default tab ordering when adding Key Listner

I've been creating a custom TabFolder extension that adds a key listener to allow quick tab switching using an ALT + # hotkey.
By adding the KeyAdapter to my TabFolder, the event handler works properly only when you have a tab header selected (in which case the ALT + ARROW_LEFT/ARROW_RIGHT also work.). I need this hot key to be active when any Widget with-in the TabFolder is active; however, it shouldn't be active if the selection is in a different tab folder or widget outside of a tab folder.
In an attempt to solve this, I wrote a simple recursive function to apply the key listener to all of the children of the tab folder:
public void applyQuickSwitchKeyBindings() {
removeKeyListener(ka);
addKeyListener(ka);
for(Control c: getChildren())
applyQuickSwitchKeyBindingsToChildren(c);
}
private void applyQuickSwitchKeyBindingsToChildren(Control c) {
if(c==null) return;
if(c instanceof Composite) {
Control[] controls = ((Composite)c).getChildren();
for(Control c2: controls)
applyQuickSwitchKeyBindingsToChildren(c2);
if(controls.length < 1) {
c.removeKeyListener(ka);
c.addKeyListener(ka);
}
}
}
Then i call the applyQuickSwitchKeyBindings() after I add the controls to each TabItem in the tab group.
The good news was that the quick switch hot key (ALT + #) worked great!
The bad news was that the original TAB ordering based on z-index is now gone. When you hit the SWT.TAB key you lose focus on your current text box and don't gain focus on anything else...
Questions:
1.) Can each control only have one KeyListener?
2.) Why is the original TAB traversal not working anymore?
Thanks in advance!
to 1) I'm pretty sure that more than one KeyListener is allowed.
to 2) I'm not sure, that depends on what you're doing in your KeyAdapter. Maybe you can post that too?
I just the tab order is broken somehow, you can reset ( or change ) it with a call to setTabList( Control[] ).
setTablList( new Control[] {
control1,
control2,
control3,
....
} );
So after more time learning and developing with SWT i've discovered my problem. When you add a listener it is applied to the widget/control you call the addXXXListener function on. So if that control is not active the listeners will not be fired.
The solution seems to be SWT's global Filter mechanism which allows you to add global application(Display) scope listeners.
Display.getCurrent().addFilter(SWT.keyPress, new KeyPressListener());
Pardon the incorrectness of this line, but if you google it you'll see what i mean.
I have also read to use this sparingly.

Menu item accel key works only after menu item has been shown

I'm developing a SWT/JFace application using the libraries from Eclipse 3.4.1.
I encounter the following problem on Windows (Vista 32bit) and Ubuntu 8.10 32bit:
I create a menu bar in the createMenuManager method of the JFace ApplicationWindow. I add MenuManagers for file, edit and help.
I then add an ExitAction to the file MenuManager like so:
filemenu.add(new ExitAction(this));
The ExitAction is defined this way:
public class ExitAction extends Action {
final ApplicationWindow window;
public ExitAction(ApplicationWindow w) {
this.window = w;
setText("E&xit");
setToolTipText("Exit the application");
setAccelerator(SWT.MOD1 + 'Q');
}
}
Now when my application starts I want be able to press "CTRL+Q" to quit the application. This does however not work. Only AFTER I click on "File" in the menu bar and THEN clicking "CTRL+Q" the application will quit.
I've tried this with different accelerators- same behavior.
It does work however if I create a "MenuItem" instead of an "Action" to contribute to the menu bar.
Is this a SWT bug or do I miss something?
Torsten.
Update: There is a duplicate bug of mine which also contains a workaround.
The bug url is: https://bugs.eclipse.org/bugs/show_bug.cgi?id=243758
Basically the workaround is to call create() on the ApplicationWindow and then getMenuBarManager().updateAll(true); which will force all menu items to get initialized.
Of course you have to call the above methods after you created the menu items.
AFAIK setAccelerator(.) does nothing else than adding the appropriate text to your MenuItem. You are responsible to register for an KeyUp event and react on it.
You can use Display.addFilter(SWT.KeyUp, myListener) to register your Listener independently of your widgets.
Turns out that this is a bug in Eclipse 3.4.
I have submitted a bug report: https://bugs.eclipse.org/bugs/show_bug.cgi?id=253078

Categories