Ok my problem is simple: i make Paint application, and i would like to provide some kind of "undo" functionality. I read about undo managers and so on, but this approach seems to be difficult to understand for me so i decided to design my own. My idea is simple: since i draw on JPanel i will just save its current content just before any drawing operation is made. And undo button will just restore previous one.
The trouble is: I am unable to "restore" the saved image and place it on the JPanel. I am buffled since i have already implemented "saving" the current image on harddrive and also "opening" arbitrary image from harddrive. Both of those work perfectly fine.
Unfortunately trying the exactly the same approach for my "undo" system does not work.
Look at the code: (to simplify, i manually 'save' the current picture -> for testing purposes)
public class PictureEdit { //class for saving the current JPanel drawing for undo
public BufferedImage saveBI;
public Graphics2D saveG2D;
}
public void actionPerformed(ActionEvent e) { //action for "UNDO" button, not working
//drawingField - instance of JPanel
//pe - instance of PictureEdit class
drawingField.setBufferedImg(drawingField.pe.saveBI);
drawingField.updateArea(drawingField.getBufferedImg());
}
public void actionPerformed(ActionEvent e) { //action for manual "save" (for undo)
drawingField.pe.saveBI=drawingField.getBufferedImg();
drawingField.pe.saveG2D=(Graphics2D)drawingField.getBufferedImg().getGraphics();
}
Now the example of my working solution but related to woking witk files on HDD:
public void actionPerformed(ActionEvent e){ //Opening file from harddrive - works fine
JFileChooser jfc = new JFileChooser();
int selection = jfc.showOpenDialog(PaintUndo.this);
if (selection == JFileChooser.APPROVE_OPTION){
try {
drawingField.setBufferedImg(ImageIO.read(jfc.getSelectedFile()));
drawingField.updateArea(drawingField.getBufferedImg());
} catch (IOException ex) {
Logger.getLogger(PaintUndo.class.getName()).log(Level.SEVERE, null, ex);
JOptionPane.showMessageDialog(PaintUndo.this, "Could not open file");
}
}
}
public void actionPerformed(ActionEvent e) { //Saving to harddrive - works fine
int n = JOptionPane.showConfirmDialog(PaintUndo.this, "Are you sure you want to save?", "Saving image...", JOptionPane.OK_CANCEL_OPTION);
if (n == JOptionPane.YES_OPTION){
JFileChooser jfc = new JFileChooser();
int nn = jfc.showSaveDialog(PaintUndo.this);
if (nn == JFileChooser.APPROVE_OPTION){
File saveFile = new File(jfc.getSelectedFile()+".bmp");
try {
ImageIO.write(drawingField.getBufferedImg(), "bmp", saveFile);
} catch (IOException ex) {
Logger.getLogger(PaintUndo.class.getName()).log(Level.SEVERE, null, ex);
JOptionPane.showMessageDialog(PaintUndo.this, "Error while saving file");
}
}
}
}
Just in case someone is curious: updateArea function (member of extended JPanel class that represents mentioned drawingField):
public void updateArea(BufferedImage img){ //it is just resizing the current picture (if necessary) and then assigns the new Graphics.
area.height=img.getHeight();
area.width=img.getWidth();
this.setPreferredSize(area);
g2d = (Graphics2D)this.getGraphics();
}
Basically the procedure is exactly the same, and working with files is ok while i cannot save and restore image when im operating on variables... What could be wrong? any ideas? When i use Undo/UndoSave buttons, absolutely nothing changes in my JPanel (drawingField)
Any help appreciated
In the code you've shown, you are copying your reference to the image, but both references still point at the same image object - if you perform any action which doesn't reassign the reference (=) it will be reflected in both references! In order to save an old version of the image, you need to copy the actual object, not just the reference.
I do recommend using Java's established undo management approach but if you want to continue with your own, these are methods you should look at:
//BufferedImage image;
Raster raster = image.getData(); // save
image.setData(raster); // restore
Related
I am opening a JInternal Frame by getting its name from JTextField
and ** then creates its object at runtime**, the problem is that if I
write 10 times different Internal Frame name in textBox and then click the button it opens the new JInternal Frame everytime.
Now i want whenever a new JInternal Frame is open, the previous
JInternalFrame should be close automatically.
I know it's pretty easy to do that but my case is difficult because of i
create its object at run time, how can i do it.
My code behine the Button is following
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
String st = TextField.getText().toString(); // in TextField i enter the JInternal Frame Name
String clazzname = "practice."+st; // practice is the package name
try
{
JInternalFrame obj1 = (JInternalFrame) Class.forName( clazzname ).newInstance();
obj1.setVisible(true);
jPanel1.add(obj1); // now in there i want that whenever i click the button , it check either is there any Jinternal frame is open already or not if yes then close the previously open JFrame
}
catch(Exception e)
{
System.out.println("error "+e);
}
}
I know it's pretty easy to do that but my case is difficult because i creats its object at run time, how can i do it.
There's nothing magical about runtime that makes this any different from how you'd normally close it. The secret is in having a reference to the JInternalFrame readily available. A solution is to use a JInternalFrame field, a non-static instance variable, to hold the reference and not use a local variable as you're currently doing. The key here is to understand that references are what matter, much more so than variables. If you need a reference variable that persists when the method ends, then the variable cannot be declared within the method but should be on class scale.
Something like:
public class MyGui {
// instance field to hold reference to currently displayed JInternalFrame
private JInternalFrame currentInternalFrame = null;
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
if (currentInternalFrame != null) {
currentInternalFrame.dispose(); // clear current one
}
String st = TextField.getText().toString(); // in TextField i enter the JInternal Frame Name
String clazzname = "practice."+st; // practice is the package name
try {
// JInternalFrame obj1 = (JInternalFrame) Class.forName( clazzname ).newInstance();
currentInternalFrame = (JInternalFrame) Class.forName( clazzname ).newInstance();
currentInternalFrame.setVisible(true);
jPanel1.add(currentInternalFrame);
} catch(Exception e) {
System.out.println("error "+e);
}
}
}
Note that this code has not been tested and is not here for a copy-and paste solution but to give you a general idea.
Another unrelated issue is on program design: users don't usually like windows opening and closing, and perhaps a better program structure for your user is to swap JPanel views via a CardLayout (please read the CardLayout Tutorial for more on this).
The image you see below is a BufferedImage representation of a PDF. The bufferedimage is in an imageicon that is contained in a jlabel.
What I am attempting to do here is to be able to drag that image out to another application in my system. If I drag any document (including images) into that application it accepts it. I need to drag drop this into that application as a PDF. I am using PDFBOX to help manipulate pdfs. My end users HAVE TO HAVE this implemented as drag drop. The other application that will be accepting these documents is written in a way so that once the document is in the system it cannot be removed and so drag drop will be the best way for them to do so.
Because, however, the image is contained in a jlabel I cannot drag it out, or at least I cannot find a way to do so. I can drag into a jlabel, but dragging out does not appear to be an option.
This is my first attempt at doing a drag drop built into an application of mine and I can only see the ability to drag a file url onto the desktop. Is there some way for me to be able to drag this (url or otherwise) out of the window at least onto the desktop? I am thinking that if I can get it onto the desktop I can get it into the other application.
I can save the file temporarily as a PDF which I believe I am going to have to do, but I just don't know how to get this file out of the JLabel. Any help is greatly appreciated.
My design was wrong which was why DnD was not working. In the setup I had, the image above was contained in an ImageIcon which in itself was contained within a JLabel. The coding to drag the contents of a JLabel became the issue as JLabels are not setup to drag out of by default (or you cannot do it at all).
When I changed the setup so that my image was in an ImageIcon that was contained in a JList the setup became fairly strait forward. My other application would take any flavor thrown at it, but specifically I needed a PDF in it.
To do this, the JFrame that contained the Jlist had to implement the DragDropListener (obviously). The magic code was in the FileTransferHandler class:
private class FileTransferHandler extends TransferHandler {
#Override
protected Transferable createTransferable(JComponent c) {
List<File> files = new ArrayList<>();
files.add(currentPDF.getFile());
return new FileTransferable(files);
}
#Override
public int getSourceActions(JComponent c) {
return MOVE;
}
}
-
private class FileTransferable implements Transferable {
private final List<File> files;
public FileTransferable(List<File> files) {
this.files = files;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{DataFlavor.javaFileListFlavor};
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(DataFlavor.javaFileListFlavor);
}
#Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (!isDataFlavorSupported(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return files;
}
}
currentPDF.getFile() is a method written into my PDF object that would return a finalized file of what would be dragged into the other application. I left the option to transfer multiple files at once should it be needed in the future hence the ArrayLists.
I have a program which when runs displays an icon in system tray. i am using the below code to display an icon in system tray area:
public static void showTrayIcon() {
if (java.awt.SystemTray.isSupported()) {
st = java.awt.SystemTray.getSystemTray();
image = Toolkit.getDefaultToolkit().getImage(PongeeUtil.class.getClass().getResource("export.png"));
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "Hello");
}
};
PopupMenu popup = new PopupMenu();
MenuItem defaultItem = new MenuItem("sdf");
defaultItem.addActionListener(listener);
popup.add(defaultItem);
trayIcon = new TrayIcon(image, "Tray Demo", popup);
trayIcon.addActionListener(listener);
try {
st.add(trayIcon);
} catch (AWTException e) {
System.err.println(e);
}
}
}
When i call this method in my main() i got something in my system tray but icon is missing. i think image is not able to load. image is in the same package where my java files resides.
What am i doing wrong here ?
image is in the same package where my java files are
If you take a look at the JavaDocs for Toolkit#getImage you will find that it says...
Returns an image which gets pixel data from the specified file
This is important. You should also know that getImage loads the physical image in a background thread, which means if it fails to load the image, it will do so silently...
Okay. The core problem is, once the image is placed within the context of the application (with the class files), it becomes, whats commonly known as, an embedded resource.
These resources can not be loaded via any means that requires access to a file on the file system.
Instead, you need to use Class#getResource or Class#getResourceAsStream to load them, for example
image = Toolkit.getDefaultToolkit().getImage(YourClass.class.getResource("/package/path/to/classes/export.png"));
Or more preferabbly...
BufferedImage img = ImageIO.read(YourClass.class.getResource("/package/path/to/classes/export.png"));
image = new ImageIcon(img);
ImageIO will throw an IOException when it can't load the image for some reason, which gives you more diagnostic information to fix problems
nb:
YourClass is you class which contains the showTrayIcon method...
/package/path/to/classes is the package name under which the image is stored...
I have a JFileChooser that lets users choose an image for themselves. I want to limit the images they can choose to ones with square dimensions, for example -
width and height both 50
width and height both 75, etc...
So when they select an image with the JFileChooser and click 'Open' I need to validate the image size and if it doesn't have square dimensions I need to present the user with a dialog informing them "The image must have the same width and height".
I'm just learning swing so I don't know how to do this. Any ideas on how to do this? Is there a way of hooking the "Open" button's event handler?
You can hide all images that do not confirm to the rules with an implementation of a FileFilter:
JFileChooser fileChooser = new JFileChooser(new File(filename));
fileChooser.addChoosableFileFilter(new MyFilter());
// Open file dialog.
fileChooser.showOpenDialog(frame);
openFile(fileChooser.getSelectedFile());
class MyFilter extends javax.swing.filechooser.FileFilter {
public boolean accept(File file) {
// load the image
// check if it satisfies the criteria
// return boolean result
}
}
I tried overwriting
public void approveSelection ()
by deriving a own class from JFileChooser, and at first glance, it seemed to work.
The method is called, I can make a test on the selected file, and, if it fails, recall showOpenDialog (ref);.
But ...
It works fine, when I call a legitimate file, and it opens a new dialog, if not, but after that, the dialog won't close again normally, and if forced by the X of the window, I get a StackTrace printed. So I guess the state of the dialog is the critical thing here - it doesn't work if 'showOpenDialog' is called recursively.
Here is one of the variants I tested:
class ProportionalImageChooser extends JFileChooser
{
private Component ref;
public ProportionalImageChooser (File f)
{
super (f);
}
public int showOpenDialog (Component parent)
{
ref = parent;
return super.showOpenDialog (parent);
}
public void approveSelection () {
System.out.println ("approving selection!");
String fname = getSelectedFile ().getName ();
if (fname.matches (".*e.*")) {
cancelSelection ();
System.out.println ("Dialog: size doesn't match");
showOpenDialog (ref);
}
else super.approveSelection ();
}
}
To keep the test simple, I only tested the filename to include an 'e' or not.
So I suggest, use Boris' approach, and test your file after finishing the dialog. If it fails, immediately reopen a new one.
I don't think this is a new issue. But it seems that there is an error that comes up whenever an ordered/unordered list in JTextPane (EditorKit -> HTMLEditorKit, Document -> HTMLDocument) is deleted from all the way down to the top using the backspace key. The following is the exception thrown from the getText () method of GlyphView.
Exception in thread "AWT-EventQueue-0" javax.swing.text.StateInvariantError: GlyphView: Stale view: javax.swing.text.BadLocationException: Invalid location
I can provide a SSCCE for this. But it is not very difficult to simulate. Just use a JTextPane with HTMLEditorKit and HTMLDOcument model set inside it. Either use the custom "InsertOrderedList" action or have some way to insert the string
<HTML><HEAD></HEAD><BODY><UL><LI></LI></UL></BODY></HTML>
which will result in the insertion of an ordered/unordered list inside the text pane.
The weird parts of this bug are as follows:
Once you start deleting the characters (and if you happen to have lines below the bulleted list), the characters will get deleted until you hit the last character of the last bullet item. Once you reach this, the caret just refuses to move up and the error from GlyphView gets thrown.
Sometimes what happens is that after you have deleted most of the characters - you still won't be able to delete the first bullet of the list. It just hangs on until you do a ctrl+a and then backspace.
I have seen these bugs in almost all Java based HTML editors available online except for JWebEngine, where this behavior is not present. Unfortunately JWebEngine is not open sourced and hence I can't take a peek inside their code to see as to how they have solved this problem.
My guess is that the notification that is coming from the HTML document model has some problem due to which the cursor positioning code is not working correctly. I have also searched the Sun bugs database to check if this particular issue has been raised but could not find any (though I have seen quite a few bugs which are very similar to this). Also I am very sure that someone must have noticed this before and must have brought it to the Swing team's attention.
Does anyone working with Swing (particularly text) part know as to if this issue has been raised with Sun or if there is any known workaround that has been found to mitigate the problem?
Though it is possible that the user is still able to delete the lists from the pane using the mouse selection, not having the option to do the same using the backspace key just seems very weird.
SSCCE is now attached. To repro the bug pls. follow the steps as shown in the attached fig.
Add a line of text. Then add 2/3 bullet items by clicking on the button above the text pane. Now place the caret at the end of the last char of the last bullet and keep on pressing the entire backspace key until all the chars get deleted.
Observed behavior: the last bullet will hang on (won't get deleted) and the exception will be thrown (as mentioned above)
Expected: No exception and the contents of the text pane should get cleared.
public class Test {
static final JFrame frame = new JFrame ();
static final JTextPane textPane = new JTextPane ();
static EditorKit kit;
static JButton listButton;
public static void createAndShowGUI () {
//Create frame
frame.setSize(400, 600);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
//Customize text pane visual properties
textPane.setSize(300, 500);
textPane.setLocation(50, 50);
//customize text pane non visual properties
kit = new CustomEditorKit ();
textPane.setEditorKitForContentType("text/html", kit);
textPane.setContentType("text/html");
Action[] actions = ((HTMLEditorKit) kit).getActions();
Action action = null;
for (int i = 0; i < actions.length; i++) {
action = actions [i];
if (action.getValue(Action.NAME).equals("InsertUnorderedList")) {
break;
}
}
listButton = new JButton (action);
listButton.setText("List");
listButton.setSize(100, 20);
listButton.setLocation(100, 10);
listButton.setVisible(true);
/* Add button and text pane to frame */
frame.add(listButton);
frame.add(textPane);
}
public static void main(String[] args) {
try {
EventQueue.invokeAndWait(new Runnable () {
#Override
public void run() {
createAndShowGUI ();
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
static class CustomEditorKit extends HTMLEditorKit {
#Override
public Document createDefaultDocument () {
return new HTMLDocument (this.getStyleSheet());
}
}
}
I used this
action=new HTMLEditorKit.InsertHTMLTextAction("test", "<UL><LI><P>\n</P></LI></UL>", HTML.Tag.BODY, HTML.Tag.UL);
instead of default action in your example to provide correct structure.
Works fine for me. (Win 7, Java 1.6)