Hey again! Although i promised to publish my third blog entry already the week before, I have to admit that I wasn´t able to write about my filesharing and chat components as announced, because I ran into some serious problems the last 2 weeks, regarding the further implementation of the GUI. The main aim of my application is to easily share files between users in a Local Area Network, so it´s clear, that the basic user interaction involves dragging files or folders from the OS filesystem to the GUI. Of course, some may argument, that there are GUI components like filechoosers and so on, but hey, I want to make a clean, proper and fancy GUI, so working with this kind of components was never an option – drag and drop is definitely a must.
So before choosing the GUI Framework for Distributed Clipboard and Swing/AWT was not an option, I read some articles about state-of-the art technology, and finally, in consultation with Martin, I ended up with choosing the public beta of the upcoming JavaFX 2.0 version, as I already told you earlier. Choosing a framework with is especially built for developing RIAs and GUIs, I never even thought of running into problems concerning user interaction and look and feel of the application. I developed a simple GUI and then began to code all the logic needed: UDP and TCP Listener Threads, UDP and TCP Worker Threads, classes for saving the application state (because I don´t use a database), the discovery and so on. When I finished all these tasks the week before, I first coded the chat-component of the app (remember to NEVER code nice-to-haves before you´ve finished your main targets ), and was really pleased, how easy I was able to merge existing logic, with the new chat logic and the GUI – awesome!
After that, I developed filesharing logic, at first on sender, then on receiver side. I remembered, that during my first JavaFX 2.0 API journeys, I read something about DropTarget Interfaces in the Scene class, which is simply all the content of the top level container (called Stage in FX, corresponds to our beloved JFrame in Swing). I again searched for it in the API, and was kind of surprised, that there are no get( ) or set( ) Methods for these type. So I tried to look for static fields or methods – nothing. Since there was an entry in the API, called scene.Scene.DropTarget, I tried to dig into with the help of Eclipse and Cmd+Space – and found that the field, or embedded class, or WHATEVER they did, was simply private – OUCH – why would you put something into your Documentation, when noone ever has access to it (since there were no getters and setters)?
This was the first time I experienced a real problem with FX, but I thought: “well, of course there is a way to drag and drop, I just haven´t found it yet”. The next days I spent hours in the official Beta Dev Forum, reading and reading and reading. I quickly found some snippets, that showed how to drag files from outside into the GUI, but they all used FX 1.3. I tried to look for the classes people used in version 1.3, but the whole package isn´t available in FX 2.0 so far. At this point I had already spent a lot of time with this problem, and therefor I began to simple try things, but that didn´t take me any further either (which I noticed few days later ). I then accepted that there is no way, getting drag and drop to work over the current provided public Beta. Just a little note: Of course, FX provides some classes, which deal with dragEvents and dropEvents, but they all just work, if your DragEvent STARTS inside the GUI. I tried to manually instantiate DragEvents, but this is not possible. I managed to drag and drop entry of two lists in the SAME window with 3 minutes of looking into the API and 4 lines of code. The crucial thing, is that you need to start a Drag-Gesture on a JavaFX Object with an invocation of the startDragAndDrop( ) method, otherwise, it is not registered. I guess, that this would have been the perfect time to reconsider my attitude towards filechoosers – but I didn´t, since I wanted Drag and Drop from outside – nothing else.
The next day I noticed a new package in the new buid of the public Beta: javafx.embed.swing. It just holds a single class, calles FXPanel, and all you can do with it, is to set the aforementioned Scene (main content) to be displayed inside this panel. As the packagename embed.swing already tells, FXPanel can be embedded into an ordinary Swing JFrame. I was kind of freaking out, because of course there are a lots of options to register drag and drop events on a JFrame. At first I refactored everything, to be not placed in the native JavaFX Top-Level-Container, but in the FXPanel instead. To be able to show my GUI again then, I just had to extend another own class from JFrame, and set the FXPanel there. I then implemented an awt.DropTargetListener interface and set it onto that JFrame. At this point I was able to detect things being dropped on the window, but except invoking event.getX( ) respectively getY( ), I was not able to detect, where on my FX ListView object (which displays currently online users), the Drop has actually occured, since they are not detected by FX.
I then again struggled hard for two or three days, until I found a working solution: The main problem is, that my GUI now runs in 2 threads: one for JavaFX, to which i have to switch, if I want to update a listcell or alter something else on the GUI, and one for Swing/AWT, which just helds the JFrame, and the registered DropTargetListener. If I drag a file from outside into the GUI, the applications registers it on the Swing-Thread, and saves the path. After that I switch back to the FX-Thread. My ListView is populated via a custom cellFactory, which is responsible for instantiating, drawing and changing the listCells. I added an mouseMovedListener to each of the cells, which is just activated, if the path, that I can get via the drop-gesture, is set via the Swing-Thread. In the moment I release the mousebutton, the applications switches back to the FX-Thread, recognizes the actually hovered ListCell, saves the path internally and sets it null again (to deactivate the listCell Mouse Listeners) and returns the actually hovered cell – and – AFTER MORE THAN ONE WEEK OF WORK AND READING – I have the path of the files to send, and the listcell, which is bound to the name and the IP-address of the receiver at the same time, at one place in my application
Nevertheless, filetransfer and chat are working now, I was able to get rid of most of the bugs and added a SystemTrayIcon, which displays incoming messages and incoming download requests in the typical bubbles. I registered ActionListeners on these Bubbles, to provide a nice workflow, but since its not completely ready, I will tell you more towards the end of the week – as always, stay tuned, and thanks for tuning in!