For a project of mine (I'm very new & even newer to guis) I'm making a GUI based game it has worked so far however, when you complete level one, it reopens level two but the JFrame is completely white and unresponsive, I have tried a few things but however I am not close to finding out what the issue is. Please help me, The code for the game is:
public class gameWithGuis extends JFrame implements ActionListener, Runnable{
static int levelNum = 1;
static int find;
static int length = 3;
static int area = length * length;
static ArrayList<JButton> buttonsHolder = new ArrayList<JButton>();
Container pane = getContentPane();
static gameWithGuis threadA = new gameWithGuis(); //Starts 10 second timer
static Thread timerThread = new Thread(threadA);
static boolean levelUp = false;
public void run(){
try{
int time = 1;
while (time<=10){
Thread.sleep(1000);
time++;
}
super.dispose();
JOptionPane.showMessageDialog(null,"You have ran out of time! Game over!");
highscores(levelNum);
}catch (Exception e){}
}
public void main(){
Scanner sc = new Scanner(System.in);
JOptionPane.showMessageDialog(null,"Welcome to pseudo-Where's Wally, look for the lower case L(l) character.");
JOptionPane.showMessageDialog(null,"You get 10 seconds to find it.");
JOptionPane.showMessageDialog(null,"The answer are the coordinates multiplied together.");
JOptionPane.showMessageDialog(null,"That is what you must type in. Good luck!");
JOptionPane.showMessageDialog(null,"Ready?..");
char clear = (char)(12);
System.out.print(clear);
timerThread.start();
makingGrid();
}
public static void highscores(int levelNum){
AQAWriteTextFile2013 writer = new AQAWriteTextFile2013();
AQAReadTextFile2013 reader = new AQAReadTextFile2013();
Scanner scTwo = new Scanner(System.in);
String fileName = "highscores.txt";
String save = JOptionPane.showInputDialog(null,"Would you like to save your score? (yes or no): ");
if (save.equalsIgnoreCase("yes")){
String runningLine = "";
String name = JOptionPane.showInputDialog(null,"Name please: ");
writer.openFile(fileName, true); //opens highscore file
String writeLine = name + "\t" + levelNum;
writer.writeToTextFile(writeLine); //writes your name and the level you reached to the file
writer.closeFile();
reader.openTextFile(fileName); //shows you the list of peoples scores
JOptionPane.showMessageDialog(null,"Current Highscores: ");
String line = reader.readLine();
if (line ==null) JOptionPane.showMessageDialog(null,"- - NONE - -"); //if file is empty
do //prints the lines within the file
{
line = reader.readLine();
runningLine = runningLine+"\n"+line+" ";
}while (line !=null);
JOptionPane.showMessageDialog(null,runningLine);
reader.closeFile();
System.exit(0);
} else if (save.equalsIgnoreCase("no")){
JOptionPane.showMessageDialog(null,"Okay, thank you for playing!");
System.exit(0);
} else {
JOptionPane.showMessageDialog(null,"Please answer yes or no."); //validation
}
}
public static void main(String[] args) throws Exception{
new gameWithGuis().main();
}
#Override
public void actionPerformed(ActionEvent arg0){
JButton[] buttons = buttonsHolder.toArray(new JButton[buttonsHolder.size()]);
// for(int i = 0; i<=amountOfButtons; i++){
// buttonsHolder[].toArray();
// // buttons[i] = buttonsHolder[i];
// }
if(arg0.getSource().equals(buttons[find])){
timerThread.stop();
JOptionPane.showMessageDialog(null,"Correct!");
levelNum++;
JOptionPane.showMessageDialog(null,"Level up! You are now on level: "+levelNum+". The grid has an area of "+(length*length)+" now.");
levelUp = true; //go through to the next level
pane.removeAll();
super.dispose();
makingGrid();
}else{
timerThread.stop();
JOptionPane.showMessageDialog(null,"You guessed wrong! Game over!");
JOptionPane.showMessageDialog(null,"You reached level: "+levelNum+".");
highscores(levelNum);
}
}
public void makingGrid(){
do{
if(levelNum>1){
length = length + 2; //increase length and height of box by 2 both ways
area = length*length;
}
JButton[] buttons = new JButton[area];
Random gen = new Random();
find = gen.nextInt(area);
setTitle("Where's J?");
// Container pane = getContentPane();
pane.setLayout(new GridLayout(length, length));
for(int i = 0; i<area; i++){
buttons[i] = new JButton();
buttons[i].setText("I");
if(i == find){
buttons[i].setText("J");
}
buttonsHolder.add(buttons[i]);
pane.add(buttons[i]);
buttons[i].addActionListener(this);
}
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
// timerThread.start();
} while (levelUp);
}
}
Please no sarcastic comments if I'm being stupid and missed something obvious too you, I'm new too this language and to this website. Thanks for any help.
don't worry... No one will give you a sarcastic comment. It is just not right or fair.
Your code has a lot to improve. But congratulations for writing it!
Your problem is that levelUp is always true on the second level. This causes an infinite loop inside the makingGrid(). In this case JFrame stop repainting.
Related
I have the following code.
The remoteCommand is executing after 5 seconds the method and update the h:form.
<h:form>
<p:remoteCommand name="checkPageRc" autoRun="true"
resetValues="true" async="false"
delay="5000"
actionListener="#{previewPhotoboothProfileController.doLoadCurrentPage(previewPhotoboothProfileController.selectedPhotoboothPage)}" />
</h:form>
<h:form id="previewPageForm">
<o:graphicImage dataURI="true"
rendered="#{previewPhotoboothProfileController.photoboothTemplate.photoboothCurrentSession.singlePictureOutput != null}"
value="#{storageOutputHelperController.loadImageByByteArray(previewPhotoboothProfileController.photoboothTemplate.photoboothCurrentSession.singlePictureOutput)}"
cache="false" />
</h:form>
My backend bean:
#GraphicImageBean
public class StorageOutputHelperController implements Serializable {
public byte[] loadImageByByteArray(byte[] bytes) throws IOException {
LOGGER.info("START loadImageByByteArray");
try {
// Falls Null
if (bytes == null)
return Utils.toByteArray(Faces.getResourceAsStream("/resources/images/no-photo-icon.png"));
LOGGER.info("END loadImageByByteArray");
return bytes;
} catch (Exception e) {
LOGGER.error(ExceptionUtils.getFullStackTrace(e));
return Utils.toByteArray(Faces.getResourceAsStream("/resources/images/no-photo-icon.png"));
}
}
}
previewPhotoboothProfileController.photoboothTemplate.photoboothCurrentSession.singlePictureOutput is already a byte array.
The problem is now, that the o:graphicImage will be updated, but only after 10 seconds and it the programm / thread is frozen.
It seems that the update from the p:remoteCommand for the h:form is the issue.
If I´m using <o:graphicImage dataURI="false" I cannot see this behaviour from the frozen window / programm but than the image is not showing.
Is this a known issue or is there something wrong from my end?
Edit:
Ok, it seems like the issue is in my SWT Browser screen which I open like this:
Display.getDefault().asyncExec(new Runnable() {
public void run() {
Display display = Display.getCurrent();
final Shell shell = new Shell(display);
Rectangle clientArea = Display.getCurrent().getClientArea();
shell.addListener(SWT.Traverse, new Listener() {
#Override
public void handleEvent(Event event) {
if (event.character == SWT.ESC) {
event.doit = false;
shell.close();
}
}
});
shell.setSize(500, 500);
shell.setFullScreen(false);
shell.setLocation(0, 0);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 0;
shell.setLayout(gridLayout);
final Browser browser = new Browser(shell, SWT.NONE);
browser.setJavascriptEnabled(true);
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.FILL;
data.horizontalSpan = 3;
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
data.widthHint = clientArea.width;
data.heightHint = clientArea.height;
browser.setLayoutData(data);
shell.open();
browser.setUrl("http://localhost:" + Constants.SERVER_PORT
+ "/portal/myurl);
}
});
Any idea, why the program is frozen?
If I check this in my local browser, it seems it´s working fine
I am learning zookeeper and trying out the Curator framework for service discoveries. However, I am facing a weird issue that I have difficulties to figure out. The problem is when I tried to register an instance via serviceDiscovery, the cacheChanged event of the serviceCache gets triggered three times. When I removed an instance, it is only triggered once, which is the expected behavior. Please see the code below:
public class DiscoveryExample {
private static String PATH = "/base";
static ServiceDiscovery<InstanceDetails> serviceDiscovery = null;
public static void main(String[] args) throws Exception {
CuratorFramework client = null;
try {
// this is the ip address of my VM
client = CuratorFrameworkFactory.newClient("192.168.149.129:2181", new ExponentialBackoffRetry(1000, 3));
client.start();
JsonInstanceSerializer<InstanceDetails> serializer = new JsonInstanceSerializer<InstanceDetails>(
InstanceDetails.class);
serviceDiscovery = ServiceDiscoveryBuilder.builder(InstanceDetails.class)
.client(client)
.basePath(PATH)
.serializer(serializer)
.build();
serviceDiscovery.start();
ServiceCache<InstanceDetails> serviceCache = serviceDiscovery.serviceCacheBuilder()
.name("product")
.build();
serviceCache.addListener(new ServiceCacheListener() {
#Override
public void stateChanged(CuratorFramework curator, ConnectionState state) {
// TODO Auto-generated method stub
System.out.println("State Changed to " + state.name());
}
// THIS IS THE PART GETS TRIGGERED MULTIPLE TIMES
#Override
public void cacheChanged() {
System.out.println("Cached Changed ");
List<ServiceInstance<InstanceDetails>> list = serviceCache.getInstances();
Iterator<ServiceInstance<InstanceDetails>> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next().getAddress());
}
}
});
serviceCache.start();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.print("> ");
String line = in.readLine();
} finally {
CloseableUtils.closeQuietly(serviceDiscovery);
CloseableUtils.closeQuietly(client);
}
}
}
AND
public class RegisterApplicationServer {
final static String PATH = "/base";
static ServiceDiscovery<InstanceDetails> serviceDiscovery = null;
public static void main(String[] args) throws Exception {
CuratorFramework client = null;
try {
client = CuratorFrameworkFactory.newClient("192.168.149.129:2181", new ExponentialBackoffRetry(1000, 3));
client.start();
JsonInstanceSerializer<InstanceDetails> serializer = new JsonInstanceSerializer<InstanceDetails>(
InstanceDetails.class);
serviceDiscovery = ServiceDiscoveryBuilder.builder(InstanceDetails.class).client(client).basePath(PATH)
.serializer(serializer).build();
serviceDiscovery.start();
// SOME OTHER CODE THAT TAKES CARES OF USER INPUT...
} finally {
CloseableUtils.closeQuietly(serviceDiscovery);
CloseableUtils.closeQuietly(client);
}
}
private static void addInstance(String[] args, CuratorFramework client, String command,
ServiceDiscovery<InstanceDetails> serviceDiscovery) throws Exception {
// simulate a new instance coming up
// in a real application, this would be a separate process
if (args.length < 2) {
System.err.println("syntax error (expected add <name> <description>): " + command);
return;
}
StringBuilder description = new StringBuilder();
for (int i = 1; i < args.length; ++i) {
if (i > 1) {
description.append(' ');
}
description.append(args[i]);
}
String serviceName = args[0];
ApplicationServer server = new ApplicationServer(client, PATH, serviceName, description.toString());
server.start();
serviceDiscovery.registerService(server.getThisInstance());
System.out.println(serviceName + " added");
}
private static void deleteInstance(String[] args, String command, ServiceDiscovery<InstanceDetails> serviceDiscovery) throws Exception {
// in a real application, this would occur due to normal operation, a
// crash, maintenance, etc.
if (args.length != 2) {
System.err.println("syntax error (expected delete <name>): " + command);
return;
}
final String serviceName = args[0];
Collection<ServiceInstance<InstanceDetails>> set = serviceDiscovery.queryForInstances(serviceName);
Iterator<ServiceInstance<InstanceDetails>> it = set.iterator();
while (it.hasNext()) {
ServiceInstance<InstanceDetails> si = it.next();
if (si.getPayload().getDescription().indexOf(args[1]) != -1) {
serviceDiscovery.unregisterService(si);
}
}
System.out.println("Removed an instance of: " + serviceName);
}
}
I appriciate if anyone can please point out where I am doing wrong and maybe can share some good materials/examples so I can refer to. The official website and the examples on github does not help a lot.
I have followed this tutorial and the testbed launches, but nothing ever happens. The GUI appears but the tests never run, it just sits there. The testbed is launched from the driver class and you add the testbed test. Anyone else have this problem?
Driver Class
public class Driver {
public static final String GRAVITY_SETTING = "Gravity";
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TestbedModel model = new TestbedModel(); // create our model
// add tests
model.addCategory("My Tests");
model.addTest(new MJWTest2());
model.addTest(new VerticalStack());
// add our custom setting "My Range Setting", with a default value of 10, between 0 and 20
model.getSettings().addSetting(new TestbedSetting(GRAVITY_SETTING, SettingType.ENGINE, false));
TestbedPanel panel = new TestPanelJ2D(model); // create our testbed panel
JFrame testbed = new TestbedFrame(model, panel, null); // put both into our testbed frame
// etc
testbed.setVisible(true);
testbed.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Testbed Test class
public class MJWTest2 extends TestbedTest {
public static final String GRAVITY_SETTING = "Gravity";
#Override
public void initTest(boolean argDeserialized) {
setTitle("Couple of Things Test");
getWorld().setGravity(new Vec2());
for (int i = 0; i < 2; i++) {
PolygonShape polygonShape = new PolygonShape();
polygonShape.setAsBox(1, 1);
FixtureDef fix = new FixtureDef();
fix.shape = polygonShape;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DYNAMIC;
bodyDef.position.set(5 * i, 0);
bodyDef.angle = (float) (Math.PI / 4 * i);
bodyDef.allowSleep = false;
Body body = getWorld().createBody(bodyDef);
body.createFixture(fix);
body.applyForce(new Vec2(-10000 * (i - 1), 0), new Vec2());
}
}
#Override
public void step(TestbedSettings settings) {
super.step(settings); // make sure we update the engine!
TestbedSetting gravity = settings.getSetting(GRAVITY_SETTING); // grab our setting
if (gravity.enabled) {
getWorld().setGravity(new Vec2(0, -9));
}
else {
getWorld().setGravity(new Vec2());
}
}
#Override
public String getTestName() {
return "Couple of Things";
}
}
There was an updated to the engine w/o an update to the wiki. Whoops! sorry. You need to create a controller and start is, as shown here:
https://github.com/dmurph/jbox2d/blob/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework/j2d/TestbedMain.java
Try this
public class MJWTest2 extends TestbedTest {
#Override
public void initTest(boolean argDeserialized) {
setTitle("Couple of Things Test");
getWorld().setGravity(new Vec2());
for (int i = 0; i < 2; i++)
{
// CircleShape circleShape = new CircleShape();
// circleShape.m_radius = 1;
// Shape shape = circleShape;
PolygonShape polygonShape = new PolygonShape();
polygonShape.setAsBox(1, 1);
Shape shape = polygonShape;
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DYNAMIC;
bodyDef.position.set(5 * i, 0);
bodyDef.angle = (float) (Math.PI / 4 * i);
bodyDef.allowSleep = false;
Body body = getWorld().createBody(bodyDef);
body.createFixture(shape, 5.0f);
body.applyForce(new Vec2(-10000 * (i - 1), 0), new Vec2());
}
}
/**
* #see org.jbox2d.testbed.framework.TestbedTest#getTestName()
*/
#Override
public String getTestName() {
return "Couple of Things";
}
}
And so called 'driver class'
public class App2 {
public static final String GRAVITY_SETTING = "Gravity";
public static void main(String[] args) {
// TODO Auto-generated method stub
TestbedModel model = new TestbedModel(); // create our model
// add tests
model.addCategory("My Tests");
model.addTest(new MJWTest2());
model.addTest(new VerticalStack());
// add our custom setting "My Range Setting", with a default value of 10, between 0 and 20
model.getSettings().addSetting(new TestbedSetting(GRAVITY_SETTING, SettingType.ENGINE, false));
TestbedPanel panel = new TestPanelJ2D(model); // create our testbed panel
JFrame testbed = new TestbedFrame(model, panel); // put both into our testbed frame
// etc
testbed.setVisible(true);
testbed.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
worked fine for me.. hope that it works for you as well..
I'm using JavaFX's Drag and Drop system in my application, and it has been working well so far.
Now I want to support drag and drop to outside applications, eg. dragging files from my application to the explorer. How would I achieve that?
I've achieved what you described by using:
Vector<File> files = new Vector<File>();
private ClipboardContent filesToCopyClipboard = new ClipboardContent();
...
final ObjectWithAReturnablePathField draggableObj = new ObjectWithAReturnablePathField();
...
draggableObj.setOnDragDetected(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent me)
{
Dragboard db = draggableObj.startDragAndDrop(TransferMode.ANY);
try
{
File f = new File(new URI(draggableObj.getFilePath()));
files.add(f);
filesToCopyClipboard.putFiles(files);
}
catch (URISyntaxException e)
{
e.printStackTrace();
}
db.setContent(filesToCopyClipboard);
me.consume();
}
});
draggableObj.setOnDragDone(new EventHandler<DragEvent>()
{
#Override
public void handle(DragEvent me)
{
me.consume();
}
});
Which means:
It's possible to achieve file transference between JavaFX 2 and a native application by filling a ClipboardContent with a list using the TransferMode.ANY on the setOnDragDetected method of any Draggable Object (Any Node) which can return a Path for a file. In my case, I've created a class called Thumb extending ImageView and (among others things) I made a method called getFilePath() which returns the Path from the Image used to initialize the ImageView(). I'm sorry BTW for the poor example and the poor english, but I'm running out of time to give a more detailed answer as of now. I hope it helps. Cheers
Here is a sample source for an action listener on an ImageView image extraction to OS' explorer (With a custom process for jpg image to remove alpha-channel to display it correctly):
inputImageView.setOnDragDetected(new EventHandler <MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// for paste as file, e.g. in Windows Explorer
try {
Clipboard clipboard Clipboard.getSystemClipboard();
Dragboard db = inputImageView.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
Image sourceImage = inputImageView.getImage();
ImageInfo imageInfo = (ImageInfo) inputImageView.getUserData();
String name = FilenameUtils.getBaseName(imageInfo.getName());
String ext = FilenameUtils.getExtension(imageInfo.getName());
///Avoid get "prefix lenght too short" error when file name lenght <= 3
if (name.length() < 4){
name = name+Long.toHexString(Double.doubleToLongBits(Math.random()));;
}
File temp = File.createTempFile(name, "."+ext);
if (ext.contentEquals("jpg")|| ext.contentEquals("jpeg")){
BufferedImage image = SwingFXUtils.fromFXImage(sourceImage, null); // Get buffered image.
BufferedImage imageRGB = new BufferedImage(image.getWidth(),image.getHeight(),
BufferedImage.OPAQUE);
Graphics2D graphics = imageRGB.createGraphics();
graphics.drawImage(image, 0, 0, null);
ImageIO.write(imageRGB, ext, temp);
graphics.dispose();
ImageIO.write(imageRGB,
ext, temp);
}else{
ImageIO.write(SwingFXUtils.fromFXImage(sourceImage, null),
ext, temp);
}
content.putFiles(java.util.Collections.singletonList(temp));
db.setContent(content);
clipboard.setContent(content);
event.consume();
temp.deleteOnExit();
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
});
With the help of use of an Object that is passed to the imageView's setUserData method, it helps me to retrieve database id and pic name:
public class ImageInfo {
private String imageInfo;
private int inputId;
#Override
public String toString() {
return imageInfo;
}
public ImageInfo(String imageInfo, int inputId) {
this.imageInfo = imageInfo;
this.inputId = inputId;
}
public String getName() {
return imageInfo;
}
public void setName(String imageInfo) {
this.imageInfo = imageInfo;
}
public int getIndex() {
return inputId;
}
public void setIndex(int areaindex) {
this.inputId = inputId;
}
}
I hope it will help somenone at an expected time :-)
Regards
Background
Our Eclipse RCP 3.6-based application lets people drag files in for storage/processing. This works fine when the files are dragged from a filesystem, but not when people drag items (messages or attachments) directly from Outlook.
This appears to be because Outlook wants to feed our application the files via a FileGroupDescriptorW and FileContents, but SWT only includes a FileTransfer type. (In a FileTransfer, only the file paths are passed, with the assumption that the receiver can locate and read them. The FileGroupDescriptorW/FileContents approach can supply files directly application-to-application without writing temporary files out to disk.)
We have tried to produce a ByteArrayTransfer subclass that could accept FileGroupDescriptorW and FileContents. Based on some examples on the Web, we were able to receive and parse the FileGroupDescriptorW, which (as the name implies) describes the files available for transfer. (See code sketch below.) But we have been unable to accept the FileContents.
This seems to be because Outlook offers the FileContents data only as TYMED_ISTREAM or TYMED_ISTORAGE, but SWT only understands how to exchange data as TYMED_HGLOBAL. Of those, it appears that TYMED_ISTORAGE would be preferable, since it's not clear how TYMED_ISTREAM could provide access to multiple files' contents.
(We also have some concerns about SWT's desire to pick and convert only a single TransferData type, given that we need to process two, but we think we could probably hack around that in Java somehow: it seems that all the TransferDatas are available at other points of the process.)
Questions
Are we on the right track here? Has anyone managed to accept FileContents in SWT yet? Is there any chance that we could process the TYMED_ISTORAGE data without leaving Java (even if by creating a fragment-based patch to, or a derived version of, SWT), or would we have to build some new native support code too?
Relevant code snippets
Sketch code that extracts file names:
// THIS IS NOT PRODUCTION-QUALITY CODE - FOR ILLUSTRATION ONLY
final Transfer transfer = new ByteArrayTransfer() {
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
#Override
protected String[] getTypeNames() {
return typeNames;
}
#Override
protected int[] getTypeIds() {
return typeIds;
}
#Override
protected Object nativeToJava(TransferData transferData) {
if (!isSupportedType(transferData))
return null;
final byte[] buffer = (byte[]) super.nativeToJava(transferData);
if (buffer == null)
return null;
try {
final DataInputStream in = new DataInputStream(new ByteArrayInputStream(buffer));
long count = 0;
for (int i = 0; i < 4; i++) {
count += in.readUnsignedByte() << i;
}
for (int i = 0; i < count; i++) {
final byte[] filenameBytes = new byte[260 * 2];
in.skipBytes(72); // probable architecture assumption(s) - may be wrong outside standard 32-bit Win XP
in.read(filenameBytes);
final String fileNameIncludingTrailingNulls = new String(filenameBytes, "UTF-16LE");
int stringLength = fileNameIncludingTrailingNulls.indexOf('\0');
if (stringLength == -1)
stringLength = 260;
final String fileName = fileNameIncludingTrailingNulls.substring(0, stringLength);
System.out.println("File " + i + ": " + fileName);
}
in.close();
return buffer;
}
catch (final Exception e) {
return null;
}
}
};
In the debugger, we see that ByteArrayTransfer's isSupportedType() ultimately returns false for the FileContents because the following test is not passed (since its tymed is TYMED_ISTREAM | TYMED_ISTORAGE):
if (format.cfFormat == types[i] &&
(format.dwAspect & COM.DVASPECT_CONTENT) == COM.DVASPECT_CONTENT &&
(format.tymed & COM.TYMED_HGLOBAL) == COM.TYMED_HGLOBAL )
return true;
This excerpt from org.eclipse.swt.internal.ole.win32.COM leaves us feeling less hope for an easy solution:
public static final int TYMED_HGLOBAL = 1;
//public static final int TYMED_ISTORAGE = 8;
//public static final int TYMED_ISTREAM = 4;
Thanks.
even if
//public static final int TYMED_ISTREAM = 4;
Try below code.. it should work
package com.nagarro.jsag.poc.swtdrag;
imports ...
public class MyTransfer extends ByteArrayTransfer {
private static int BYTES_COUNT = 592;
private static int SKIP_BYTES = 72;
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
#Override
protected String[] getTypeNames() {
return typeNames;
}
#Override
protected int[] getTypeIds() {
return typeIds;
}
#Override
protected Object nativeToJava(TransferData transferData) {
String[] result = null;
if (!isSupportedType(transferData) || transferData.pIDataObject == 0)
return null;
IDataObject data = new IDataObject(transferData.pIDataObject);
data.AddRef();
// Check for descriptor format type
try {
FORMATETC formatetcFD = transferData.formatetc;
STGMEDIUM stgmediumFD = new STGMEDIUM();
stgmediumFD.tymed = COM.TYMED_HGLOBAL;
transferData.result = data.GetData(formatetcFD, stgmediumFD);
if (transferData.result == COM.S_OK) {
// Check for contents format type
long hMem = stgmediumFD.unionField;
long fileDiscriptorPtr = OS.GlobalLock(hMem);
int[] fileCount = new int[1];
try {
OS.MoveMemory(fileCount, fileDiscriptorPtr, 4);
fileDiscriptorPtr += 4;
result = new String[fileCount[0]];
for (int i = 0; i < fileCount[0]; i++) {
String fileName = handleFile(fileDiscriptorPtr, data);
System.out.println("FileName : = " + fileName);
result[i] = fileName;
fileDiscriptorPtr += BYTES_COUNT;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
OS.GlobalFree(hMem);
}
}
} finally {
data.Release();
}
return result;
}
private String handleFile(long fileDiscriptorPtr, IDataObject data) throws Exception {
// GetFileName
char[] fileNameChars = new char[OS.MAX_PATH];
byte[] fileNameBytes = new byte[OS.MAX_PATH];
COM.MoveMemory(fileNameBytes, fileDiscriptorPtr, BYTES_COUNT);
// Skip some bytes.
fileNameBytes = Arrays.copyOfRange(fileNameBytes, SKIP_BYTES, fileNameBytes.length);
String fileNameIncludingTrailingNulls = new String(fileNameBytes, "UTF-16LE");
fileNameChars = fileNameIncludingTrailingNulls.toCharArray();
StringBuilder builder = new StringBuilder(OS.MAX_PATH);
for (int i = 0; fileNameChars[i] != 0 && i < fileNameChars.length; i++) {
builder.append(fileNameChars[i]);
}
String name = builder.toString();
try {
File file = saveFileContent(name, data);
if (file != null) {
System.out.println("File Saved # " + file.getAbsolutePath());
;
}
} catch (IOException e) {
System.out.println("Count not save file content");
;
}
return name;
}
private File saveFileContent(String fileName, IDataObject data) throws IOException {
File file = null;
FORMATETC formatetc = new FORMATETC();
formatetc.cfFormat = typeIds[1];
formatetc.dwAspect = COM.DVASPECT_CONTENT;
formatetc.lindex = 0;
formatetc.tymed = 4; // content.
STGMEDIUM stgmedium = new STGMEDIUM();
stgmedium.tymed = 4;
if (data.GetData(formatetc, stgmedium) == COM.S_OK) {
file = new File(fileName);
IStream iStream = new IStream(stgmedium.unionField);
iStream.AddRef();
try (FileOutputStream outputStream = new FileOutputStream(file)) {
int increment = 1024 * 4;
long pv = COM.CoTaskMemAlloc(increment);
int[] pcbWritten = new int[1];
while (iStream.Read(pv, increment, pcbWritten) == COM.S_OK && pcbWritten[0] > 0) {
byte[] buffer = new byte[pcbWritten[0]];
OS.MoveMemory(buffer, pv, pcbWritten[0]);
outputStream.write(buffer);
}
COM.CoTaskMemFree(pv);
} finally {
iStream.Release();
}
return file;
} else {
return null;
}
}
}
Have you looked at https://bugs.eclipse.org/bugs/show_bug.cgi?id=132514 ?
Attached to this bugzilla entry is an patch (against an rather old version of SWT) that might be of interest.
I had the same problem and created a small library providing a Drag'n Drop Transfer Class for JAVA SWT. It can be found here:
https://github.com/HendrikHoetker/OutlookItemTransfer
Currently it supports dropping Mail Items from Outlook to your Java SWT application and will provide a list of OutlookItems with the Filename and a byte array of the file contents.
All is pure Java and in-memory (no temp files).
Usage in your SWT java application:
if (OutlookItemTransfer.getInstance().isSupportedType(event.currentDataType)) {
Object o = OutlookItemTransfer.getInstance().nativeToJava(event.currentDataType);
if (o != null && o instanceof OutlookMessage[]) {
OutlookMessage[] outlookMessages = (OutlookMessage[])o;
for (OutlookMessage msg: outlookMessages) {
//...
}
}
}
The OutlookItem will then provide two elements: filename as String and file contents as array of byte.
From here on, one could write it to a file or further process the byte array.
To your question above:
- What you find in the file descriptor is the filename of the outlook item and a pointer to an IDataObject
- the IDataObject can be parsed and will provide an IStorage object
- The IStorageObject will be then a root container providing further sub-IStorageObjects or IStreams similar to a filesystem (directory = IStorage, file = IStream
You find those elements in the following lines of code:
Get File Contents, see OutlookItemTransfer.java, method nativeToJava:
FORMATETC format = new FORMATETC();
format.cfFormat = getTypeIds()[1];
format.dwAspect = COM.DVASPECT_CONTENT;
format.lindex = <fileIndex>;
format.ptd = 0;
format.tymed = TYMED_ISTORAGE | TYMED_ISTREAM | COM.TYMED_HGLOBAL;
STGMEDIUM medium = new STGMEDIUM();
if (data.GetData(format, medium) == COM.S_OK) {
// medium.tymed will now contain TYMED_ISTORAGE
// in medium.unionfield you will find the root IStorage
}
Read the root IStorage, see CompoundStorage, method readOutlookStorage:
// open IStorage object
IStorage storage = new IStorage(pIStorage);
storage.AddRef();
// walk through the content of the IStorage object
long[] pEnumStorage = new long[1];
if (storage.EnumElements(0, 0, 0, pEnumStorage) == COM.S_OK) {
// get storage iterator
IEnumSTATSTG enumStorage = new IEnumSTATSTG(pEnumStorage[0]);
enumStorage.AddRef();
enumStorage.Reset();
// prepare statstg structure which tells about the object found by the iterator
long pSTATSTG = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, STATSTG.sizeof);
int[] fetched = new int[1];
while (enumStorage.Next(1, pSTATSTG, fetched) == COM.S_OK && fetched[0] == 1) {
// get the description of the the object found
STATSTG statstg = new STATSTG();
COM.MoveMemory(statstg, pSTATSTG, STATSTG.sizeof);
// get the name of the object found
String name = readPWCSName(statstg);
// depending on type of object
switch (statstg.type) {
case COM.STGTY_STREAM: { // load an IStream (=File)
long[] pIStream = new long[1];
// get the pointer to the IStream
if (storage.OpenStream(name, 0, COM.STGM_DIRECT | COM.STGM_READ | COM.STGM_SHARE_EXCLUSIVE, 0, pIStream) == COM.S_OK) {
// load the IStream
}
}
case COM.STGTY_STORAGE: { // load an IStorage (=SubDirectory) - requires recursion to traverse the sub dies
}
}
}
}
// close the iterator
enumStorage.Release();
}
// close the IStorage object
storage.Release();