I have a view that I want to react to what happens in the editor. Right now I have a button that I want it so that when clicked it updates the data in the view to some new set of information. Where do I start, I have my selection event but no idea on how to communicate between the two. I'm looking for a loose coupling solution.
I'm sure there are many ways of doing this, but I've used the JFace IPropertyChangeListener interface in the past for simple event propagation.
Make your view implement IPropertyChangeListener. Create a Singleton class that you can register your IPropertyChangeListener with, and send a PropertyChangeEvent to. Then in the constructor of your view, register it with your Singleton.
Now you can get hold of your Singleton in your editor and fire off an event that will get picked up in your view.
Example code for the Singleton:
public class PropertyChangeEventBus {
private static PropertyChangeEventBus s_instance = new PropertyChangeEventBus();
public static PropertyChangeEventBus instance()
{
return s_instance;
}
private Set<IPropertyChangeListener> m_listeners;
private PropertyChangeEventBus()
{
// use CopyOnWriteArraySet to prevent ConcurrentModificationExceptions
m_listeners = new CopyOnWriteArraySet<IPropertyChangeListener>();
}
public void addListener(IPropertyChangeListener listener)
{
m_listeners.add(listener);
}
public void removeListener(IPropertyChangeListener listener)
{
m_listeners.remove(listener);
}
public void fire(final PropertyChangeEvent event)
{
// run property change events in UI thread to prevent having to have lots of syncExecs in the listener methods
ViewUtils.syncExec(new Runnable()
{
#Override
public void run()
{
for (IPropertyChangeListener listener : m_listeners)
{
try
{
listener.propertyChange(event);
}
catch(Exception e)
{
//log it, present error message
}
}
}
});
}
}
Example Code for the View:
//The constructor
public MyView()
{
PropertyChangeEventBus.instance().addListener(this);
}
#Override
public void propertyChange(PropertyChangeEvent event)
{
if(event.getProperty().equals(SOME_CONSTANT))
{
// Refresh View
}
}
Related
I want to implement something like gate mechanism.
I need one PublishSubject and a couple of subscribers. When PublishSubject send data via onNext only one subscriber will receive it.
For example:
I have 3 equals fragments inside tabs. They have subscription to global published called onLoginPublisher.
When onResume or onPause called gate becomes open or closed.
When onLogin called and no gates are opened because of no one of these fragments on screen, onNext will wait for fragment's onResume
Look at the pic:
You can use filter with the gate's state. For example, you can wrap all the logic into a class:
public final class GatedSubject<T> {
final PublishSubject<T> subject = PublishSubject.create();
final AtomicReferenceArray<Boolean> gates;
public GatedSubject(int numGates) {
gates = new AtomicReferenceArray<>(numGates);
}
public boolean getGateStatus(int gateIndex) {
return gates.get(gateIndex) != null;
}
public void setGateStatus(int gateIndex, boolean status) {
gates.set(gateIndex, status ? Boolean.TRUE : null);
}
public void Observable<T> getGate(int gateIndex) {
return subject.filter(v -> getGateStatus(gateIndex));
}
public void onNext(T item) {
subject.onNext(item);
}
public void onError(Throwable error) {
subject.onError(error);
}
public void onComplete() {
subject.onComplete();
}
}
Hello im doing a tcp server in java, and i got my listener class working but i want to declare an event to the listener class when it is intialized so in the listener class i can call the event newConnection(socket sck) and then it will go over to the main class again and run the method that is binded to that event like you are able to do in C# with the myclass.myevent +=
Can it be done in java or are there other ways arround, i have looked at eventlisteners or what its called but im really confused about how to handle this.
Regards Martin.
You can do something like this:
public interface ConnectionCallback {
void onConnected(Socket socket);
}
In your listener class:
public class Listener {
public void connect(ConnectionCallback callback){
//connect to your server here
Socket socket ....
callback.onConnected(socket);
}
}
In your main form, or wherever you're creating this listener:
public class MainForm{
public void createListener(){
Listener listener = new Listener();
listener.connect(new ConnectionCallback() {
public void onConnected(Socket socket) {
//perform custom logic here on the callback socket
}
});
}
}
Edit: if you were thinking of using Java 8, this might make it slightly easier for you to understand if you're coming from a C# perspective by using lambdas:
public class Listener {
public void connect(Consumer<Socket> socketConsumer){
Socket socket ....
socketConsumer.accept(socket);
}
}
Then, your MainForm is simplified:
public class MainForm {
public void createListener(){
Listener listener = new Listener();
listener.connect(socket -> onConnected(socket));
}
private void onConnected(Socket socket) {
//perform custom logic here...
}
}
Maybe something like this would help:
Declare an interface for your event listener:
public interface InitializationListener {
public void onInitialize();
}
Then, in the class which should trigger events:
//Using a List allows more than one listener to be registered
private List<InitializationListener> listeners = new ArrayList<InitializationListener>();
public void addInitializationListener(InitializationListener listener) {
this.listeners.add(listener);
}
When you want to trigger the listeners, maybe in newConnection():
public void newConnection(Socket sock) {
//do stuff..
//trigger listeners:
for (InitializationListener listener : listeners) {
listener.onInitialize();
}
}
Finally, in your client code:
server.addInitializationListener(new InitializationListener() {
public void onInitialize() {
//do stuff
}
});
The below method onReceivedTitlegets called 2-3 times with in a second when webview url changes. I want to call a method in it, when onReceivedTitle is being called last time. I am doing this because I just want to monitor url changes with in webview. shouldOverrideUrlLoading is not getting called when url changes through ajax.
class MyWebChromeClient extends WebChromeClient {
#Override
public void onReceivedTitle(WebView view, String title) {
Log.v("onReceivedTitle", "=>" + title);
// callAMehod();
super.onReceivedTitle(view, title);
}
}
If you want to throttle how often a method call causes another method call you can do so for example via a Handler. The simplest version enqueues a delayed message on the first call and any subsequent call while there is an enqueued message will not enqueue a new one. That results in 1 call every X time to go though - but it take at least that amount of time until the first action happens.
Example implementation (you can put that class unmodified somewhere in your code)
public abstract class ThrottleExecutor {
private final long mMinDelay;
public ThrottleExecutor(long minDelay) {
mMinDelay = minDelay;
}
/** Implement to do something */
public abstract void doThrottled();
public final void scheduleExecution() {
if (mHandler.hasMessages(0)) {
// message already enqueued, do nothing
} else {
// otherwise enqueue a message for later
mHandler.sendEmptyMessageDelayed(0, mMinDelay);
}
}
public final void cancelExecution() {
mHandler.removeMessages(0);
}
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
doThrottled();
}
};
}
And then use it for example like so
class Usage {
private ThrottleExecutor mThrottle = new ThrottleExecutor(2000) {
#Override
public void doThrottled() {
// happens at most every 2000ms
methodToBeThrottled();
}
};
void methodThatHappensTooOften() {
mThrottle.scheduleExecution();
}
void methodToBeThrottled() {
Log.d("TAG", "triggered at 2000ms before");
}
}
You might want to use Handler and do something like this:
class MyWebChromeClient extends WebChromeClient {
private boolean mOnReceivedTitleInvoked;
#Override
public synchronized void onReceivedTitle(final WebView view, final String title) {
if (!mOnReceivedTitleInvoked) {
mOnReceivedTitleInvoked = true;
Log.v("onReceivedTitle", "=>" + title);
handler.postDelayed(new Runnable() {
#Override
public void run() {
super.onReceivedTitle(view, title);
mOnReceivedTitleInvoked = false;
}
}, 1000);
}
}
}
Although you might want to reconsider the onReceivedTitle behaviour.
I have a View, Model, Controller classes. The View and Model are passed to the Controller constructor and all three are initialized with EventQueue.invokeLater() one after another.
The view has a JList component. My application requires to run a stream of text lines and to put each line in the JList component as they arrive. The stream architecture is done with Listeners, so as soon as an element arrives from the stream a StreamListener is notified. I need to start or stop the stream.
Now my problem is that I'm not sure how to model such a scenario. Of course setting the Controller as the StreamListener and then using a SwingWorker inside the Controller will solve the problem. However I thought it is the jobs of the Model to handle the streaming and business logic because the role of the Controller is just to facilitate the communications between the View and the Model.
One way I'm thinking to solve this is by defining a SwingPropertyChangeSupport in the Model and let the Controller implement PropertyChangeListener. Then in the Model I create a SwingWorker and call publish() every time an element from the stream arrives. Then from process() I call SwingPropertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue) which then will cause the Controller to invoke its implemented method propertyChange(PropertyChangeEvent evt) which will finally cause the JList to add the element.
However I'm not sure if my approach is correct nor if the code will run on the Event Dispatching Thread. I hope if you can help.
public class Model {
SwingPropertyChangeSupport changeFirer;
Task task;
class Task extends SwingWorker<Void, Element> implements StreamListener {
Stream stream;
protected Void doInBackground() {
stream = new Stream();
stream.start();
}
public void onRecieveing(Element element) {
public(element);
}
protected void process(List<Element> elements) {
Element element = elements.get(elements.size()-1);
changeFirer.firePropertyChange("updateList", null, element);
}
}
}
The Controller:
public class Controller implements PropertyChangeListener {
Model model;
View view;
public Controller(View view, Model model) {
this.view = view;
this.model = model;
model.setChangeFirer(this);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
Element element = evt.getNewValue();
view.updateList(element);
}
}
Main method:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
View view = new View();
Model model = new Model();
Controller controller =
new Controller(view, model);
view.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
My solution is correct, propertyChange() runs on EDT and fires correctly.
I am working on a plugin for IntelliJ Idea 13. I do some changes in beforeDocumentSaving and I use document.setText:
public class AppendAction implements ApplicationComponent
{
#Override public void initComponent()
{
MessageBus bus = ApplicationManager.getApplication().getMessageBus();
MessageBusConnection connection = bus.connect();
connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter()
{
#Override public void beforeDocumentSaving(Document document)
{
document.setText(appendSomething(document.getText()));
}
});
}
}
This works great, my only problem is that when this plugin is run, and I want to undo the changes, I get to following error message:
Cannot Undo
Following files have changes that cannot be undone:
Any Idea? :-)
The answer is wrapping the document.setText into ApplicationManager.getApplication().runWriteAction and CommandProcessor.getInstance().runUndoTransparentAction.
I found an example TrailingSpacesStripper among intellij-community sources on githib:
https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java
public class AppendAction implements ApplicationComponent
{
#Override public void initComponent()
{
MessageBus bus = ApplicationManager.getApplication().getMessageBus();
MessageBusConnection connection = bus.connect();
connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter()
{
#Override public void beforeDocumentSaving(final Document document)
{
ApplicationManager.getApplication().runWriteAction(new DocumentRunnable(document, null)
{
#Override public void run()
{
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable()
{
#Override public void run()
{
document.setText(appendSomething(document.getText()));
}
});
}
});
}
});
}
}
You should wrap the change through the CommandProcessor API.
From IntelliJ IDEA Architectural Overview:
Any operations which modify the contents of the document must be wrapped in a command (CommandProcessor.getInstance().executeCommand()). executeCommand() calls can be nested, and the outermost executeCommand call is added to the undo stack. If multiple documents are modified within a command, undoing this command will by default show a confirmation dialog to the user.