I have spent almost three days trying to do a simple enable / disable of Actions in the netbeans plaform, something that I though was going to be simple, and should be a common feature is more complex than I thought.
At the begging I tried to see if there was an setEnable() method on the default actions generated and to my surprise there is not. Then I started looking into that and I found that most common method to do it was setting a conditionally enabled action (which depends on a Cookie class), So I figured out how to add a fake class to the Lookup so it gets enabled and disabled, I did it the following way. To test it out I added the following code to another action which should enable or disable the second one.
private final PlottingStarted plottingStarted = new PlottingStarted();
#Override
public void actionPerformed(ActionEvent e) {
// TODO implement action body
if (Lookup.getDefault().lookup(PlottingStarted.class) == null) {
ic.add(plottingStarted);
}else{
ic.remove(plottingStarted);
}
So PlottingStarted is a fake object I created which only purpose is being in the lookup to disable or enable the action.
For some reason it did not do anything at all an the Action was always disabled. I tried many things and finally I gave up.
Then I tried a different approach and was using AbstractActions which do have the setEnabled() ability.
To retrieve the action I based myself on one the Geertjan blogs and I created the following method
public Action findAction(String actionName) {
FileObject myActionsFolder = FileUtil.getConfigFile("Actions/RealTimeViewer");
if (myActionsFolder != null){
FileObject[] myActionsFolderKids = myActionsFolder.getChildren();
for (FileObject fileObject : myActionsFolderKids) {
//Probably want to make this more robust,
//but the point is that here we find a particular Action:
if (fileObject.getName().contains(actionName)) {
try {
DataObject dob = DataObject.find(fileObject);
InstanceCookie ic = dob.getLookup().lookup(InstanceCookie.class);
if (ic != null) {
Object instance = ic.instanceCreate();
if (instance instanceof Action) {
Action a = (Action) instance;
return a;
}
}
} catch (Exception e) {
ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
return null;
}
}
}
}
return null;
}
This method worked perfectly and I was able to retrieve the action and call its setEnabled() method. Unfortunately no matter why I did the Action was always enabled.
Reading some literature I found that I should add the following to the registration of the action "lazy = false" and finally I was able to enable and disable the Action... But off course the default registration is lost and I have no Icons and Names.
Now I decided to post again because I cannot believe that it need to be that complex, there must be a way to do it easier. The only thing I need is to have a PLAY / STOP functionality, when PLAY is enabled STOP is disabled and vice-versa.
I have not done this myself but it seems to be covered in Chapter 5.1.2.1 "Complex Enablement" of the book "Netbeans Platform for Beginners". https://leanpub.com/nbp4beginners
The book is not free but the corresponding code sample is available on
github. https://github.com/walternyland/nbp4beginners/tree/master/chapters/ch05/5.1.2.1 He extends AbstractAction overrides the resultChanged method and uses super.setEnabled().
#ActionID(id = "org.carsales.evaluator.EvaluateCarAction1", category = "Car")
#ActionRegistration(displayName = "not-used", lazy = false)
public class EvaluateCarAction extends AbstractAction
implements ContextAwareAction, LookupListener {
// ...
#Override
public void resultChanged(LookupEvent le) {
//Optionally, check if the property is set to the value you're interested in
//prior to enabling the Action.
super.setEnabled(result.allInstances().size() > 0);
}
Thanks to everybody for your responses. I finally got it to work by extending AbstractAction, it seems that even if you register "lazy = false" some of the registration is still being done by the platform and you just need some minor tweaking in the Action constructor. The final result was
#ActionID(
category = "RealTimeViewer",
id = "main.java.com.graph.actions.StopPlotting"
)
#ActionRegistration(
//iconBase = "main/java/com/graph/images/stop-plotting-24x24.png",
displayName = "#CTL_StopPlotting",
lazy = false
)
#ActionReference(path = "Toolbars/RealTimeViewer", position = 600)
#Messages("CTL_StopPlotting=Stop Plotting")
public final class StopPlotting extends AbstractAction{
private static final String ICON = "main/java/com/dacsys/cna/core/graph/images/stop-plotting-24x24.png";
public StopPlotting() {
putValue(SMALL_ICON, ImageUtilities.loadImageIcon(ICON, false));
putValue(NAME, Bundle.CTL_StopPlotting());
this.setEnabled(false);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO implement action body
Action a = new ActionsHelper().findAction("StartPlotting");
if (a != null){
if (a != null){
if (a.isEnabled()){
a.setEnabled(false);
this.setEnabled(true);
}else{
a.setEnabled(true);
this.setEnabled(false);
}
}
}
}
}
I have a Task Flow with two views: listOfClients and newClient. Shown here:
listOfClients view has a table which I want to sort before rendering it. To do it, I want to create a SortEvent with the table as source (as shown in the docs, section 29.4.4 ), but I cannot access the table before rendering the view.
I call the method queueSortIdEvent in a pageFlow-scoped managed bean but findComponent cannot find the table (returns null ). Tried also view-scoped, same result.
My code in the bean is:
public UIComponent findComponent(final String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
final UIComponent[] found = new UIComponent[1];
root.visitTree(new FullVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if(component.getId().equals(id)){
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
}
});
return found[0];
}
public void queueSortIdEvent(){
SortCriterion sc = new SortCriterion("ClientId", true);
List<SortCriterion> listSC = new ArrayList<>();
listSC.add(sc);
SortEvent new = new SortEvent(findComponent("t1"), listSC); // the table id is "t1"
new.queue();
}
Is there a way to queue the event before rendering the view?
Note: findComponent function works fine in other parts of the code, got it from here
My JDeveloper version is 12.1.3
What JDeveloper version are you using?
The findComponent won't work because nothing has been rendered yet. So your component isn't available (= doesn't exists yet) at that moment.
I think there is an easier way to do sorting.
On your page/fragment, go to 'Bindings', select your iterator (middle column), click on Edit (pencil icon) and set the sorting you want in the 'Sort criteria' tab.
I have a OSGi DS component with properties defined and which is referencing another bundles service. In the setService method I get the reference of the other's bundle service and start another thread. I'm dependent on properties defined in component.xml. Thus the first time I'm able to read the properties from the component.xml file is after the bundle gets the activated method called and getting the reference of the ComponentContext. Now it seems I have serious timing problems, as the setService is executed before the activate is executed.
How can that be? How can the bundle get the required service references before it is even activated?
How can I access the properties defined in the component.xml when starting the thread in the setService method?
The concrete example:
private String publishingUrl = "http://0.0.0.0:11023/ws"; // default address
protected synchronized void activate(ComponentContext context) {
this.ctx = context;
if (ctx != null) {
String url = String.valueOf(ctx.getProperties().get("publishingUrl"));
if (url != null) publishingUrl = url;
}
logger.info("Activated and got the publishingUrl: "+ publishingUrl);
}
public void setService(AnotherService service) {
synchronized (this) {
if (this.anotherService == service) {
logger.info("anotherService was already set.");
return;
} else {
this.anotherService = service;
logger.info("Got anotherService. Thank you DS!");
}
}
startWebserviceThread(publishingUrl);
}
In the console ouput I see the logger message from the setService, then from activate. The method startWebserviceThread is always called with the default value of publishingUrl not with the one got from the ComponentContext property file.
Also it doesn't make a difference if I set immediate="true" or immediate="false" in component.xml
Runtime: Java 1.6, eclipse equinox
The setService() method is used to inject dependencies into your DS.
Then activate() is called, and it is the place where the worker thread should start.
You have to move startWebserviceThread(publishingUrl); at the end of activate() method.
This is also suggested by your logic. You get the publishing url setting from your context, and then you can start your web service. To use another service, you need its reference before starting, so this is why setService() is called before activate().
As noted in this tutorial, a service should not be used inside set/unset methods.
I have created new Eclipse IDE (plugin project, Eclipse Kepler, rel. 1) with default template of mail client.
After the first run of the app, the Perspective is stored and remembered (somewhere?) and any changes to Perspective.java has no any effect! Even if I delete the content of createInitialLayout(IPageLayout layout) from the Perspective.java, everything is restored again.
BTW: adding this code to ApplicationWorkbenchAdvisor.java didnt help:
#Override
public void initialize(IWorkbenchConfigurer configurer) {
super.initialize(configurer);
configurer.setSaveAndRestore(false);
}
How can I force the app to not to remmeber the layout?
You could call IWorkbenchPage.resetPerspective() to reinitialize the perspective, perhaps in the WorkbenchWindowAdvisor.postWindowRestore() method.
Thank you. I have added the following code to menu. Now I can reset perspective whenever I need.
public class ApplicationActionBarAdvisor extends ActionBarAdvisor {
private IWorkbenchAction resetPerspectiveAction;
#Override
protected void makeActions(IWorkbenchWindow window) {
// ...
// create and register the actions
resetPerspectiveAction = ActionFactory.RESET_PERSPECTIVE.create(window);
register(resetPerspectiveAction);
// ...
}
#Override
protected void fillMenuBar(IMenuManager menuBar) {
// ...
// create and fill the window menu
MenuManager windowMenu = new MenuManager("&Window", WorkbenchActionConstants.M_WINDOW);
menuBar.add(windowMenu);
windowMenu.add(resetPerspectiveAction);
// ...
}
}
I have the following question:
I have to use the following function from BT printer SDK:
StarIOPort port = null;
byte[] texttoprint = new byte[]{0x1b, 0x40, 0x1b,0x74,0x0D,(byte) 0x91,(byte) 0x92,(byte) 0x93,(byte) 0x94,(byte) 0x95,(byte) 0x96,(byte) 0x97,(byte) 0x98,(byte) 0x99,0x0A,0x0A,0x0A,0x0A,0x0A};
try
{
port = StarIOPort.getPort(portName, portSettings, 10000, context);
port.writePort(textToPrint, 0, textToPrint.length);
port.writePort(new byte[] {0x0a}, 0, 1);
}
catch (StarIOPortException e)
{
Builder dialog = new AlertDialog.Builder(context);
dialog.setNegativeButton("Ok", null);
AlertDialog alert = dialog.create();
alert.setTitle("Failure");
alert.setMessage("Failed to connect to printer");
alert.show();
}
I have understand everything except of context.
The manufacturer mention that
* #param context - Activity for displaying messages to the user
How can I use the above function because in the way I use it I do not receive any error neither any alert message.
To display Alert (or any other UI component) you need Activity context, that's right. If you do not have any Activity running at the moment, you can't display Alert.
But you can display Toast, using static method of class Toast:
public static Toast makeText(Context context, CharSequence text, int duration);
passing to it Application Context as first parameter.
Application context is always available while your app is running, even if there are no UI running at the moment. You can get it by calling getApplicationContext() method from your context. If you don't have any context at all, you can always use YourAppClass (public class YourAppClass extends Application), defined in your manifest under xml tag.
Most common practice is to make YourAppClass a singleton, and it always be available at any point of code within your app.
The context is your Activity.
private Context context;
context = this;
Here is a complete sample activity.
package com.example.helloworld;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import <my_star_io_library>;
public class HelloWorld extends Activity
{
private Context context;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//Save context
context = this;
StarIOPort port = null;
byte[] texttoprint = new byte[]{0x1b, 0x40, 0x1b,0x74,0x0D,(byte) 0x91,(byte) 0x92,(byte) 0x93,(byte) 0x94,(byte) 0x95,(byte) 0x96,(byte) 0x97,(byte) 0x98,(byte) 0x99,0x0A,0x0A,0x0A,0x0A,0x0A};
try
{
port = StarIOPort.getPort(portName, portSettings, 10000, context);
port.writePort(textToPrint, 0, textToPrint.length);
port.writePort(new byte[] {0x0a}, 0, 1);
}
catch (StarIOPortException e)
{
Builder dialog = new AlertDialog.Builder(context);
dialog.setNegativeButton("Ok", null);
AlertDialog alert = dialog.create();
alert.setTitle("Failure");
alert.setMessage("Failed to connect to printer");
alert.show();
}
}
}
Context is a class related with an Activity,that you use when you have to show and AlertDialog, Toast, get system services... It is related with the arquitecture MVC,which is a bit longer to explain. About using it, there are two ways. One is explained by droidhot and another way is, for example, MainActivity.this, if you are using the AlertDialog in the Main Activity (MainActivity.java file), so the Main Activity will be the one that launches the Alert Dialog. If it is launched from another class which is not an Activity, you have to put the context as a parameter (for example, new Class(MainActivity.this)) and inside the class,it would be, for example, public class( Context context) ant the parameter context is the one you have to use.
Activity is subclass of Context so if you printing code is part of your Activity class, then simply provide this as required context to fulfill SDK requirements:
port = StarIOPort.getPort(portName, portSettings, 10000, this);
and later
Builder dialog = new AlertDialog.Builder(this);
I have posted answer here:
Android Phonegap Plugin different result on virtual and real device (Looper.prepare() ERROR)
I had the same problem which appeared on some devices. Successfully one smart boy, Toby, helped me. So, solution is the next:
- before you call any StarIOPort's methods you have to check if looper exist:
if (Looper.myLooper() == null) {
Looper.prepare();
}
in your case it's will looks like this:
try
{
if (Looper.myLooper() == null) {
Looper.prepare();
}
port = StarIOPort.getPort("BT:", "mini", 10000, null);
try
{
Thread.sleep(500);
}
catch(InterruptedException e) {}
port.writePort(texttoprint, 0, texttoprint.length);
try
{
Thread.sleep(3000);
}
catch(InterruptedException e) {}
resultType = "success";
}
catch (StarIOPortException e)
{
resultType = "error";
}
One more advise:
instead
port = StarIOPort.getPort("BT:", "mini", 10000, null);
use just
port = StarIOPort.getPort("BT:", "mini", 10000);
in plugin you will not use Context
Good luck.