Friday, September 10, 2004
Localization problems
How do you know that one word is more appriopiate than another one? This must happen to every open-source project that is undergoing localizations. How much time isnt spent on getting the right words for some actions or menu entries?
Im looking for a common source for localizing softwares, a place where you can get the simplest menu entries translated just by looking at some documents. This common place could be a community with a forum for placing questions, language defintions, basic help on getting a localization working. Each language would have their own section of it, this would of course help open-source projects to become accepted quicker among ordinary users. There must be loads of info/data already available in different linux builds, (debian comes to mind) why cant we put this knowledge where it is easier to get?
Is there such a place, Ive done some googling but havent found any. Perhaps SF or any other open-source site would be suitable for this... (Or am I just dreaming?)
Tuesday, April 20, 2004
Workarounds for Swings DnD problems!
After digging around, googling and receiving tons of help; I've built two workarounds for some of the problems with Swings Drag and Drop (DnD) problems. The first problem with the DnD is the lack of the ability to dynamically create files dynamically when exporting files from a Java program. The second problem with the DnD is that you have to redo already implemented data handling.
I've already described the two problems in these blogs: Extending the.. and Shortcoming of...
These problems comes from design decisions in Swing, and cannot be fixed without tampering with the APIs of the DnD in Swing. Actually the first problem is the one that will have the largest impact on the handling of DnD to the native system. The second can be changed within the current API.
The problem was that all files that are to be DnD from a Java application to the native system, must exist before the action. And there are several areas where this isn't possible. Example the Columba email client, it was not wanted to extract attachments from an email until the user has selected so. FTP applications could just have one file list for the remote server, and then the user could DnD files from the file list to the desktop to start a transfer.
My solution was to create the files during the DnD action, before the actual drop of the file has been initiated. Therefore I came up with the class DynamicFileTransferHandler. This class could be used for any JComponent that wants to export files from Java dynamically. All it needs is a File factory that creates the files during the DnD process. The files can either be created at the start of the DnD or at the very end of it. Well not really the very end, it is actually when the DnD moves over a native application, that is the last known place for the DnD of files.. Check out the webstart demo of the frappucino package. Unfortunately this feature will not be a part of the Swing's DND API for a very long time, and I doubt that it will even go into the SDK 1.6 release. Please note that this solution is good enough but Sun should get their act together and make better support for this type of DnD. I solved the problem for the email application, but for FTP applications this is still unsolvable.
Frappucino demo jnlp
Discussion at Javadesktop
Frappucino - not yet released
Columba email client
The second problem was that often the developer only wants to add one DnD data flavor to a JComponent. This was not possible with the current API. Some of the methods in the API has protected access, and the DnD API "cheated" by being in the same package. I've now developed a solution that enables a JComponent to have multiple TransferHandlers for importing data. Now you can easily add DnD support of files into a Textarea, and not lose the default DnD implementation. I wonder why nobody has done anything about this problem, because it makes it even more tedious to add DnD import for Swing components. Anyhow my solution is to have a set of transfer handlers that uses the Chain of responsibility design pattern. The class MultipleTransferHandler can have one or more transfer handlers, and will go through the set until it can find one handler that accepts a certain data flavor. Due to the fact that the export methods for TransferHandler are protected and it is impossible to call those without extending the handler or being in the same package, I had to use the reflection API to access those two methods. Due to this "cheat" there are some security issues when using this class, but as long as it is used in one trusted application, this wont be any problem. Now the class will work with one or more transfer handler for importing data flavor, and it still can export flavors from ONE of the transfer handler. Restricting it to one export transfer handler, solved the problem when you have several handlers that export the same data flavor. This may change in the future.. With this bug, I have little faith in that they are going to fix this. The bug/feature request has been around for over one year, with no real answers. Check out the Frappucino demo, it has a demo for this component as well.
BTW, thanks for all the replies from the Java desktop forum and bug lists, I hope that I can help somebody with these Swing objects.
Friday, March 26, 2004
Extending the DnD in Swing = reinventing the wheel
The Swing's DnD functionality looks very extendable at a first glance, but as more as I dig deeper into this part of the SDK I understand that it is not 100% true. Adding new DnD functionality to a component, forces the developer to reinvent the wheel every time. Why do the developer have to redo already implemented functionality to extend a components DnD actions?
Many of the JComponents in the swing package supports drag and drop actions. Those components already have a nice feature rich implementation that handles the dragging and dropping of objects to/from the component. The problem arises when the developer wants to add a new flavor to the components DnD functionality, ie extend so the component can import new types of objects. Each JComponent can only have one TransferHandler, and this is the one used during a DnD action.
Once again I'm in the middle of adding DnD to the email client (see my previous blog). This time I would like to add drag and drop of files into a new email, ie. add them as attachments. Other native email clients lets the user drag files onto the text area and the attachment bar. When the user drops the files in either of the target components, they are added to the emails attachment list. Now this was no problem adding the DnD of files to a attachment component but when I wanted to add this feature to the editor panes DnD support, I stumbled upon this problem. The editor pane already has a TransferHandler that supports strings/texts/Unicode data flavors. I would only like to add the fileList data flavor so it would also accept files.
The problem starts with that the TransferHandlers that are used by the JEditorPane components is package private, and therefore I cant extend them to get the functionality I want. I don't want to re-implement all those features in my own version, since that would be a waste of time and bloat the code. Then I thought of using the chain of responsibility design pattern to merge the two transfer handlers into one. Ie use one as the primary transfer handler, and then if the primary doesn't support a certain flavor it will try the other handler(s) if they can import a certain flavor. This to me looked like a fine idea, and I developed a nice class to work as one TransferHandler, but actually handling several. Everything went fine until the user tries to drag and drop from a component that has this object.
During that drag and drop action, it wants to create a transferable object through the protected method createTransferable(). BUT the method is called by at javax.swing.TransferHandler$DragHandler.dragGestureRecognized(TransferHandler.java:710) method, (outside the TransferHandler class) and it uses the fact that they are in the same package, ie they can break the protected access modifier. Now I have no clue on how to create the real transferable object, since in my implementation (see below) tries to do this on the primary transfer handler, but its createTransferable() method is protected and therefore I cant reach it, I'm not in the same package so I can "cheat" as the DragHandler does.
MultipleTransferHandler.java @ CVS
Bug report at Sun
Frappucino - not yet released
Columba email client
Once again, there is a problem in the design of the Swings DnD, but at least for this problem I have a workaround. I get around the problem by calling the needed protected methods using the reflection API, and making them public. But this to me is not the correct way of doing it, I should be able to call those methods in TransferHandler without relying on using reflection. There is already a feature request at Suns bug report system, and all I can do is hope that they change it in coming releases, but for now I at least got a solution. If you would like to use it, it is part of the Frappucino module in the Columba project. Frappucino is a collection of widgets that eases the development of Swing UIs. Check the link section for the class.
Monday, March 22, 2004
Shortcomings of Sun's DnD model!?
I've tried to implement a drag and drop action of attachments in an email client, and found that the Sun's implementation of DnD makes it impossible to mimic native applications drag and drop actions. The DnD framework does not support creating files lazily. This is a shortcoming that makes it very hard to create feature-rich java clients that can compete with native clients. This is not only true for email clients, its for most clients that work with large files over the network, ex. webbrowsers, FTPs, and others.
The problem comes from the fact that the files must exists locally before the DnD of files can begin.
Note that this is about DnD a file outside the java application!
In the email client the attachments lies within the email(s), and needs to be extracted before they can be copied to the correct location. For performance reasons I dont want to extract all attachments to the disk when the user opens an email with attachments. Neither do I want to extract the attachment during the user's DnD operation. I would like to make the copy/extraction when the DnD has been completed. This is also true for FTP clients as well, for instance if the UI displays the files on the remote server and the user wants to download it to the desktop, preferably he/she should drag the file from the file list to the dekstop.
The only way of copying (or moving) a File through DnD is to create a Transferable object that supports the DataFlavor.javaFileListFlavor. This transferable should return a List of files that are to be copied/moved to the target location. When the DnD action is completed the actual copying/moving of the file is handled deep down within the Suns own code, and I havent found the place to see how its done. But the problem is that the Files must exists locally before the DnD starts, ie before the getTransferData() or the exportDone() methods are called. This I cannot do, since I dont want to extract the attachment until the DnD is complete. (performance issues) There is no way of finding out the target file from the current DnD action., ie it is impossible to make the copying yourself.
My first idea was to create an empty dummy file on the disk, and use this as the source File to be dragged around. Then when the DnD is completed, I would extract the attachment from the email and make the copy manually. Doing it this way, Im sure that
- theres no unecessary extraction,
- the DnD action is not hindered by sharing CPU with the extraction of the file itself
- the user doesnt have to wait until he has choosen where to put the file
- the extraction is done according to the API for the email client.
There are four methods that need to be overloaded in order to get a TransferHandler to work:
- getSourceAction() - return which actions the transfer handler supports, ie copy/move
- createTransferable() - returns a Transferable object that is sent around to the other components that supports DnD actions.
- canImport() - if the current component can import a certain type of data flavors
- exportDone() - is called when the DnD action is finished, remove those objects that has been moved
The Transferable interface has these methods.
- getTransferDataFlavors() - return which data flavors that this supports, in my case the javaFileListFlavor flavor
- isDataFlavorSupported() - return if the transferable understands one type of data flavor
- getTransferData() - return the actual data that is to be DnD'ed, in my case the list of Files
Ive found out that the actual Copying of the files is done before the exportDone() method and after the getTransferData(), which is not suprising since the actual copying is done by the DnD framework. But the exportDone() method has no way of telling what the target was, and therefore makes it impossible to make the copy yourself, or start the download to the location.
So, as far as I can see, there is no solution or workaround for this problem, and that it will be virtually impossible to create applications that can compete with native programs. If someone haves a solution or workaround for this problem, it would be greatly appreciated to get that.
But for now, I think the default implementation of Sun's DnD of Files is nice, but it lacks this important feature that hinders the usability.
SUN bug report - http://developer.java.sun.com/developer/bugParade/bugs/4808793.html - Bug # 4808793