# Expert Java Programming - Part 2



## JGuru (Dec 15, 2014)

*Part II of Expert Java*

*JButton*

A JButton can show an Image or text or both. Here we display an Image. An array of buttons is shown displaying the images.
The method loadImages() loads the images in the 'Images' directory. The method darken and brighten , darken & brighten the Image.
We create a rollover effect using setRolloverIcon method. setPressedIcon is used to display the image when the button is pressed.
We display the corresponding image when the button is clicked.


```
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Sowndar
 */
public class RollOverButtonDemo extends JFrame implements Runnable {

    String[] name;
    String path = "Images/";
    File directory = new File(path);
    // Get supported Image formats
    String[] format = ImageIO.getReaderFormatNames();
    JButton[] button;
    Image image;
    int maxWidth, maxHeight;
    // Bigger buttons
    int iconWidth = 150;
    int iconHeight = 145;
    JLabel label = new JLabel();
    JScrollPane scroller;
    Thread thread = null;
    Color bgColor = Color.white;
    JPanel westPanel;
    JPanel eastPanel;
    int MAX;

    public RollOverButtonDemo() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) {
        }
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        westPanel = new JPanel(new FlowLayout());
        eastPanel = new JPanel(new FlowLayout());
        scroller = new JScrollPane(label);
        // Filter only the Images
        name = directory.list(new FilenameFilter() {

            @Override
            public boolean accept(File dir, String name) {
                for (int i = 0; i < format.length; i++) {
                    if (name.endsWith(format[i])) {
                        return true;
                    }
                }
                return false;
            }
        });
        MAX = name.length;
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setVerticalAlignment(SwingConstants.CENTER);

        // Create that many Buttons
        button = new JButton[MAX];
        scroller.getViewport().setBackground(bgColor);
        eastPanel.setPreferredSize(new Dimension(scrDim.width / 10, scrDim.height));
        westPanel.setPreferredSize(new Dimension(scrDim.width / 10, scrDim.height));
        // Add the Components
        add(westPanel, BorderLayout.WEST);
        add(eastPanel, BorderLayout.EAST);
        add(scroller, BorderLayout.CENTER);
        setTitle("RollOverButton");
        setSize(new Dimension(iconWidth * MAX, scrDim.height));
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void start() {
        if (thread == null) {
            thread = new Thread(this);
            thread.setPriority(Thread.MAX_PRIORITY);
            thread.start();
        }
    }

    @Override
    public void run() {
        loadImages();
    }

    public void loadImages() {
        for (int i = 0; i < button.length; i++) {
            button[i] = new JButton();
            // Get the Images
            image = getImage(path + name[i]);
            image = scaleImage(image);
            button[i].setRolloverEnabled(true);
            // Show the normal image when the Mouse moves over it
            button[i].setRolloverIcon(new ImageIcon(image));
            // When the Mouse is pressed display brighter image
            button[i].setPressedIcon(new ImageIcon(brighten(image)));
            // Default darken Image for button
            button[i].setIcon(new ImageIcon(darken(image)));
            button[i].setActionCommand(name[i]);
            button[i].addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent ae) {
                    String imgName = ae.getActionCommand();
                    image = getImage(path + imgName);
                    label.setIcon(new ImageIcon(image));
                }
            });
            if (i < MAX / 2) {
                westPanel.add(button[i]);
            } else {
                eastPanel.add(button[i]);
            }
            button[i].validate();
            validate();
        }
        // Get the 1st Image, display it
        image = getImage(path + name[0]);
        label.setIcon(new ImageIcon(image));
    }
    // Darken the Image

    public Image darken(Image image) {
        if (image != null) {
            // Darken the image by 70%
            float scaleFactor = 0.3f;
            RescaleOp op = new RescaleOp(scaleFactor, 0, null);
            return op.filter(toBufferedImage(image), null);
        }
        return null;
    }
    // Brighten the Image

    public BufferedImage brighten(Image image) {
        if (image != null) {
            // Brighten the image by 30%
            float scaleFactor = 1.3f;
            RescaleOp op = new RescaleOp(scaleFactor, 0, null);
            return op.filter(toBufferedImage(image), null);
        }
        return null;
    }

    public BufferedImage getImage(String fileName) {
        if (fileName.endsWith("jpg") || fileName.endsWith("png")) {
            return toBufferedImage(new ImageIcon(fileName).getImage());
        }
        try {
            return ImageIO.read(new File(fileName));
        } catch (IOException ioe) {
            System.err.println("Error loading Image : " + fileName);
        }
        return null;
    }
    // Convert the Image to BufferedImage

    public BufferedImage toBufferedImage(Image image) {
        if (image != null) {
            BufferedImage buffImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
            Graphics g = buffImage.getGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();
            return buffImage;
        }
        return null;
    }
    // Scale the Image

    public Image scaleImage(Image image) {
        int width = image.getWidth(null);
        int height = image.getHeight(null);
        double aspect = ((double) width) / ((double) height);
        // If aspect ratio exceeds 1.3333
        if (aspect > 1.3333) {
            // Fix the width as maxWidth, calculate the maxHeight
            maxWidth = iconWidth;
            maxHeight = (int) (((double) maxWidth) / aspect);
        } else {
            // Fix the height as iconHeight, calculate the maxWidth for this
            maxHeight = iconHeight;
            maxWidth = (int) (((double) maxHeight) * aspect);
        }
        image.setAccelerationPriority(0.9f);
        return image.getScaledInstance(maxWidth, maxHeight, Image.SCALE_SMOOTH);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Thread(new RollOverButtonDemo()).start();
            }
        });
    }
}
```

*s15.postimg.org/vpfl4li8n/Roll_Over_Button_Demo.jpg

*JTextField auto-completion*

A text field is a basic text control that enables the user to type a small amount of text. When the user indicates that text entry is complete (usually by pressing Enter), the text field fires an action event.
The following program implements a auto-completion of TextField using a custom class.

*Adding automatic selection*

To actually select an item in the TextField.Adding some kind of automatic selection inside insertString...


```
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
  // insert the string into the document
  super.insertString(offs, str, a);
  // get the resulting string
  String content = getText(0, getLength());
  // lookup a matching item
  Object item = lookupItem(content);
  // select the item (or deselect if null)
  model.setSelectedItem(item);
}

private Object lookupItem(String pattern) {
  // iterate over all items
  for (int i=0, n=model.getSize(); i < n; i++) {
    Object currentItem = model.getElementAt(i);
    // current item starts with the pattern?
    if (currentItem.toString().startsWith(pattern)) {
      return currentItem;
    }
  }
  // no item starts with the pattern => return null
  return null;
}
```

This automatic selection works, but it is rather confusing. This is due to the fact, that some magic already seems to do the automatic completion. Watch the console: if the users types 'J' the output looks like...

insert J at 0
insert Jordi at 0

However the user only inserted 'J' and the program does not contain any explicit call to insert the string 'Jordi'.

It comes out, that the call to setSelectedItem provokes another call to insertString with the newly selected item. This should normally lead to an infinite loop. On Sun's JDKs this won't happen when using DefaultComboBoxModel, as it only calls insertString if the selected item has not been selected before. However, when using a custom model or a JDK from Apple or IBM the call to setSelectedItem will not return. A flag can be set to indicate that setSelectedItem has been called from insertString. Any subsequent call will then return immediately without further processing when the flag is set.


```
// return immediately when selecting an item
  if (selecting) return;
  [...]
  selecting=true;
  model.setSelectedItem(item);
  selecting=false;
```
 
  Adding automatic completion

  Now, to do automatic completion instead of just automatic selection access to the combo box' editor is needed. Otherwise it would not be possible to highlight the completed part using selection (see above). I added the whole JComboBox to the constructor. The selection should start right after the last character that was inserted (at position offs+str.length()).

  See how the highlighting works inside insertString...


```
// remove all text and insert the completed text
  remove(0, getLength());
  super.insertString(0, item.toString(), a);
  
  // select the completed part
  JTextComponent editor = (JTextComponent) comboBox.getEditor().getEditorComponent();
  editor.setSelectionStart(offs+str.length());
  editor.setSelectionEnd(getLength());
```
 
  Although this works, it is not a robust implementation. However, it is a good starting point. You can strengthen the implementation on your own or follow the article...

  Case insensitive matching

  The typical usecases for an autocompleting combo box will hardly need case sensitive matches. It is easy to modify the lookup mechanism to ignore case, so that the user does not have to care about typing 'E' or 'e'. This short method does the trick...


```
private boolean startsWithIgnoreCase(String str1, String str2) {
    return str1.toUpperCase().startsWith(str2.toUpperCase());
  }
```
 
  Ignore input that does not match

  If the user entered a key that does not match any item you get a NullPointerException. Of course this is not acceptable. A small enhancement in the insertString method will ignore 'invalid' input.


```
// lookup and select a matching item
  Object item = lookupItem(getText(0, getLength()));
  if (item != null) {
    comboBox.setSelectedItem(item);
  } else {
    // keep old item selected if there is no match
    item = comboBox.getSelectedItem();
    // imitate no insert
    // (later on offs will be incremented by str.length(): selection won't move forward)
    offs = offs-str.length();
    // provide feedback to the user that his input has been received but can not be accepted
    // normally a "beep" that is
    UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
  }
```
 
  The solution is straight forward, although you might wonder why it is needed to decrement the offset. Try it yourself without the decrement (the cursor will move on as if you had typed the right letter).

  In the first place there was no error feedback (normally a "beep") - I added it after a usability test. Users would sometimes wonder why the combo box was not accepting their input. The beep indicates to the user that his input has been received and processed but can not be accepted. Again, these little things make a big difference to the user experience!

  I found that experienced users (aka power users) don't like their computer to beep and are tempted to turn this off. Don't do it - future users of your application (likely less experienced) will appreciate it.

  Highlight complete text when the user selects an item via mouse

  When the user selects an item via mouse or using the cursor keys, the text is not highlighted. When the user select an item directly, the insertString method is called once with the complete string. Inside this method only completed text is highlighted which in this case is none, as the call already contained the complete string.

  A solution is to highlight the complete text whenever an item gets selected and this selection was not initiated by the autocompletion mechanism. This can be achieved using an ActionListener...


```
comboBox.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
      if (!selecting) highlightCompletedText(0);
    }
 });
```
 
 Here is the complete code!!


```
/**
 * Created with IntelliJ IDEA.
 * User: Sowndar
 * Date: 12/14/14
 * Time: 6:23 PM
 * To change this template use File | Settings | File Templates.
 */

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Vector;


class AutoCompletion extends PlainDocument {

    //For customizing the ComboBox
    private JTextField textField;
    //static ComboBoxModel model;
    private JTextComponent editor;
    private Vector<String> allItems;
    private static boolean process = true;
    // flag to indicate if setSelectedItem has been called
    // subsequent calls to remove/insertString should be ignored
    private boolean selecting = false;
    private boolean hidePopupOnFocusLoss;
    private boolean hitBackspace = false;
    private boolean hitBackspaceOnSelection;
    private static String separator = "";
    private KeyListener editorKeyListener;
    private FocusListener editorFocusListener;
    private int selectIndex = -1;

    public AutoCompletion(final JTextField textField, Vector<String> vector) {
        this.textField = textField;
        allItems = vector;
        //model = textField.getModel();
        editor = textField;
        textField.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (!selecting) {
                    highlightCompletedText(0);
                }
            }
        });
        enable(textField);
        editorKeyListener = new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                if (textField.isDisplayable()) {
                    hitBackspace = false;
                }
                process = true;
                switch (e.getKeyCode()) {
                    // determine if the pressed key is backspace (needed by the remove method)
                    case KeyEvent.VK_BACK_SPACE:
                        hitBackspace = true;
                        hitBackspaceOnSelection = editor.getSelectionStart() != editor.getSelectionEnd();
                        break;
                    // ignore delete key
                    case KeyEvent.VK_DELETE:
                        e.consume();
                        textField.getToolkit().beep();
                        break;
                }
            }
        };
        // Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when tabbing out
        hidePopupOnFocusLoss = System.getProperty("java.version").startsWith("1.5");
        // Highlight whole text when gaining focus
        editorFocusListener = new FocusAdapter() {

            @Override
            public void focusGained(FocusEvent e) {
                highlightCompletedText(0);
            }

            @Override
            public void focusLost(FocusEvent e) {
                //  Do nothing!
            }
        };
        configureEditor(editor);
        // Handle initially selected object
        Object selected = textField.getSelectedText(); //.getSelectedItem();

        if (selected != null) {
            setText(selected.toString());
        }
        highlightCompletedText(0);
    }

    public static void enable(JTextField textField) {
        // has to be editable
        textField.setEditable(true);
        // change the editor's document
        //new AutoCompletion(textField);
        separator = System.getProperty("file.separator");
    }

    void configureEditor(JTextComponent newEditor) {
        if (editor != null) {
            editor.removeKeyListener(editorKeyListener);
            editor.removeFocusListener(editorFocusListener);
        }

        if (newEditor != null) {
            editor = newEditor;
            editor.addKeyListener(editorKeyListener);
            editor.addFocusListener(editorFocusListener);
            editor.setDocument(this);
        }
    }

    @Override
    public void remove(int offs, int len) throws BadLocationException {
        // return immediately when selecting an item
        if (selecting) {
            return;
        }
        if (hitBackspace) {
            // user hit backspace => move the selection backwards
            // old item keeps being selected
            if (offs > 0) {
                if (hitBackspaceOnSelection) {
                    offs--;
                }
            } else {
                // User hit backspace with the cursor positioned on the start => beep
                textField.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(textField);

            }
            highlightCompletedText(offs);
        } else {
            super.remove(offs, len);
        }
    }

    @Override
    public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
        try {
            // return immediately when selecting an item
            if (selecting) {
                return;
                // insert the string into the document
            }
            super.insertString(offs, str, a);
            // lookup and select a matching item
            if (!process) {
                return;
            }
            Object item = lookupItem(getText(0, getLength()));
            if (item != null) {
                setText((String) item);
            } else {
                // keep old item selected if there is no match
                item = textField.getSelectedText();
                // imitate no insert (later on offs will be incremented by str.length(): selection won't move forward)
                offs = offs - str.length();
                // provide feedback to the user that his input has been received but can not be accepted
                textField.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(textField);

            }
            setText(item.toString());
            // select the completed part
            highlightCompletedText(offs + str.length());
        } catch (BadLocationException e) {
        }

    }

    private void setText(String text) {
        try {
            // remove all text and insert the completed string
            super.remove(0, getLength());
            super.insertString(0, text, null);
        } catch (BadLocationException e) {
            throw new RuntimeException(e.toString());
        }
    }

    private void highlightCompletedText(int start) {
        editor.setCaretPosition(getLength());
        editor.moveCaretPosition(start);
    }

    private Object lookupItem(String pattern) {
        Object selectedItem = textField.getSelectedText(); //model.getSelectedItem();
        // only search for a different item if the currently selected does not match

        if (selectedItem != null && startsWithIgnoreCase(selectedItem.toString(), pattern)) {
            return selectedItem;
        } else {
            //Pass the Vector here
            // iterate over all items

            for (int i = 0, n = allItems.size(); i < n; i++) {
                Object currentItem = allItems.elementAt(i);
                // current item starts with the pattern?
                if (currentItem != null && startsWithIgnoreCase(currentItem.toString(), pattern)) {
                    selectIndex = i;
                    //A small bug here!!
                    //What if there are four classes of the same name , but different package!!
                    //The List always selects the first matching item!!
                    //Item must be selected based on the package!
                    return currentItem;
                }
            }

        }
        // no item starts with the pattern => return null
        return null;
    }

    // checks if str1 starts with str2 - ignores case
    private static boolean startsWithIgnoreCase(String str1, String str2) {
        return str1.toUpperCase().startsWith(str2.toUpperCase());
    }
}

/**
 * JTextField Demo
 *
 * @author JGuru
 * @version 1.13 11/17/05
 */

// Shows the use of AutoCompletion JTextField
public class TextfieldCompletion extends JPanel {

    private String path = "Images/";
    private File directory;
    private JTextField textfield;
    private String[] name;
    private String[] imgName;
    private JLabel label = new JLabel();
    private String fullPath;

    public TextfieldCompletion() {
        try {
            for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) {
            // Do nothing
        }
        setLayout(new BorderLayout());

        // Convert the URL to String path
        try {
            fullPath = path;//getClass().getResource(path).toURI().toString();
        } catch (Exception e) {
        }

        fullPath = path;
        directory = new File(fullPath);
        // Filter out Images
        name = directory.list(new FilenameFilter() {
            String[] readFormat = ImageIO.getReaderFormatNames();

            @Override
            public boolean accept(File dir, String name) {
                for (int i = 0; i < readFormat.length; i++) {
                    if (name.endsWith(readFormat[i])) {
                        return true;
                    }
                }
                return false;
            }
        });
        // Only filename without the extension
        imgName = new String[name.length];
        for (int i = 0; i < name.length; i++) {
            imgName[i] = name[i];
            //imgName[i] = imgName[i].substring(0, imgName[i].lastIndexOf("."));
        }
        if (!directory.exists()) {
            System.err.println("The specified directory doesn't exist!!");
            System.exit(1);
        }
        Vector <String> vector = new Vector<>();
        for (int i = 0; i < imgName.length; i++) {
            vector.addElement(imgName[i]);
        }
        textfield = new JTextField(25);
        new AutoCompletion(textfield, vector);
        // Bold Font
        Font font = textfield.getFont();
        int size = font.getSize();
        textfield.setFont(new Font(font.getName(), Font.BOLD, size));
        // Enable auto completion for this JTextField
        //AutoCompletion.enable(textfield);
        textfield.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                new Thread(new Runnable() {
                    // Concurrent modification exception
                    @Override
                    public void run() {
                        String selected = textfield.getText();
                        label.setIcon(createImageIcon(selected, selected));
                    }
                }).start();
            }
        });

        JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        panel.add(new JLabel("JTextField Auto-completion "));
        panel.add(new JLabel(" Search File :"));
        panel.add(textfield);
        add(panel, BorderLayout.NORTH);
        label.setHorizontalAlignment(SwingConstants.CENTER);
        label.setVerticalAlignment(SwingConstants.CENTER);
        // Show the first Image
        String fileName = "";
        fileName = name[0];
        textfield.setText(name[0]);
        label.setIcon(createImageIcon(fileName, fileName));
        add(new JScrollPane(label), BorderLayout.CENTER);
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        setPreferredSize(new Dimension(scrDim.width / 2 + 150, scrDim.height));
    }

    public ImageIcon createImageIcon(String filename, String description) {
        Image image = null;
        try
        {
            image = ImageIO.read(new File(path + filename));
            return new ImageIcon(image);
        } catch (IOException ioe) {

        }
        return null;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("TextfieldCompletion");
                f.add(new TextfieldCompletion());
                f.pack();
                f.setVisible(true);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}
```

*s18.postimg.org/senf17oat/Textfield_Completion.jpg

*JTextArea word-completion*

The TextAreaDemo example introduces an editable text area with a special feature — a word completion function. As the user types in words, the program suggests hints to complete the word whenever the program's vocabulary contains a word that starts with what has been typed. Here is a picture of the TextAreaDemo application.

Now explore how the word completion function is implemented. Type in a word like "Swing" or "special". As soon as you have typed "sw" the program shows a possible completion "ing" highlighted in light-blue. Press Enter to accept the completion or continue typing.

The following code adds a document listener to the text area's document:

  textArea.getDocument().addDocumentListener(this);

When you started typing a word, the insertUpdate method checks whether the program's vocabulary contains the typed prefix. Once a completion for the prefix is found, a call to the invokeLater method submits a task for changing the document later. It is important to remember that you cannot modify the document from within the document event notification, otherwise you will get an exception. Examine the following code below.


```
String prefix = content.substring(w + 1).toLowerCase();
int n = Collections.binarySearch(words, prefix);
if (n < 0 && -n <= words.size()) {
    String match = words.get(-n - 1);
    if (match.startsWith(prefix)) {
        // A completion is found
        String completion = match.substring(pos - w);
        // We cannot modify Document from within notification,
        // so we submit a task that does the change later
        SwingUtilities.invokeLater(
            new CompletionTask(completion, pos + 1));
    }
} else {
    // Nothing found
    mode = Mode.INSERT;
}
```

The code shown in bold illustrates how the selection is created. The caret is first set to the end of the complete word, then moved back to a position after the last character typed. The moveCaretPosition method not only moves the caret to a new position but also selects the text between the two positions. The completion task is implemented with the following code:


```
private class CompletionTask implements Runnable {
        String completion;
        int position;
        
        CompletionTask(String completion, int position) {
            this.completion = completion;
            this.position = position;
        }
        
        public void run() {
            textArea.insert(completion, position);
            textArea.setCaretPosition(position + completion.length());
            textArea.moveCaretPosition(position);
            mode = Mode.COMPLETION;
        }
    }
```

Here is the entire code for this demo.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */

/*
 * TextAreaDemo.java requires no other files.
 */
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.GroupLayout.ParallelGroup;
import javax.swing.GroupLayout.SequentialGroup;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;

public class TextAreaDemo extends JFrame implements DocumentListener {

    private JLabel jLabel1;
    private JScrollPane jScrollPane1;
    private JTextArea textArea;
    private static final String COMMIT_ACTION = "commit";

    private static enum Mode {

        INSERT, COMPLETION
    };
    private final java.util.List<String> words;
    private Mode mode = Mode.INSERT;

    public TextAreaDemo() {
        super("TextAreaDemo");
        initComponents();

        textArea.getDocument().addDocumentListener(this);

        InputMap im = textArea.getInputMap();
        ActionMap am = textArea.getActionMap();
        im.put(KeyStroke.getKeyStroke("ENTER"), COMMIT_ACTION);
        am.put(COMMIT_ACTION, new CommitAction());
        // Our words vocabulary
        words = new ArrayList<>(5);
        words.add("spark");
        words.add("special");
        words.add("spectacles");
        words.add("spectacular");
        words.add("swing");
        setVisible(true);
    }

    private void initComponents() {
        jLabel1 = new JLabel("Try typing 'spectacular' or 'Swing'...");

        textArea = new JTextArea();
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        textArea.setColumns(20);
        textArea.setLineWrap(true);
        textArea.setRows(5);
        textArea.setWrapStyleWord(true);

        jScrollPane1 = new JScrollPane(textArea);

        GroupLayout layout = new GroupLayout(getContentPane());
        getContentPane().setLayout(layout);

        //Create a parallel group for the horizontal axis
        ParallelGroup hGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
        //Create a sequential and a parallel groups
        SequentialGroup h1 = layout.createSequentialGroup();
        ParallelGroup h2 = layout.createParallelGroup(GroupLayout.Alignment.TRAILING);
        //Add a scroll panel and a label to the parallel group h2
        h2.addComponent(jScrollPane1, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 212, Short.MAX_VALUE);
        h2.addComponent(jLabel1, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 212, Short.MAX_VALUE);

        //Add a container gap to the sequential group h1
        h1.addContainerGap();
        // Add the group h2 to the group h1
        h1.addGroup(h2);
        h1.addContainerGap();
        //Add the group h1 to hGroup
        hGroup.addGroup(Alignment.TRAILING, h1);
        //Create the horizontal group
        layout.setHorizontalGroup(hGroup);

        //Create a parallel group for the vertical axis
        ParallelGroup vGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING);
        //Create a sequential group
        SequentialGroup v1 = layout.createSequentialGroup();
        //Add a container gap to the sequential group v1
        v1.addContainerGap();
        //Add a label to the sequential group v1
        v1.addComponent(jLabel1);
        v1.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED);
        //Add scroll panel to the sequential group v1
        v1.addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE);
        v1.addContainerGap();
        //Add the group v1 to vGroup
        vGroup.addGroup(v1);
        //Create the vertical group
        layout.setVerticalGroup(vGroup);
        setSize(800, 600);
    }
    // Listener methods

    @Override
    public void changedUpdate(DocumentEvent ev) {
    }

    @Override
    public void removeUpdate(DocumentEvent ev) {
    }

    @Override
    public void insertUpdate(DocumentEvent ev) {
        if (ev.getLength() != 1) {
            return;
        }

        int pos = ev.getOffset();
        String content = null;
        try {
            content = textArea.getText(0, pos + 1);
        } catch (BadLocationException e) {
        }

        // Find where the word starts
        int w;
        for (w = pos; w >= 0; w--) {
            if (!Character.isLetter(content.charAt(w))) {
                break;
            }
        }
        if (pos - w < 2) {
            // Too few chars
            return;
        }

        String prefix = content.substring(w + 1).toLowerCase();
        int n = Collections.binarySearch(words, prefix);
        if (n < 0 && -n <= words.size()) {
            String match = words.get(-n - 1);
            if (match.startsWith(prefix)) {
                // A completion is found
                String completion = match.substring(pos - w);
                // We cannot modify Document from within notification,
                // so we submit a task that does the change later
                SwingUtilities.invokeLater(
                        new CompletionTask(completion, pos + 1));
            }
        } else {
            // Nothing found
            mode = Mode.INSERT;
        }
    }

    private class CompletionTask implements Runnable {

        String completion;
        int position;

        CompletionTask(String completion, int position) {
            this.completion = completion;
            this.position = position;
        }

        @Override
        public void run() {
            textArea.insert(completion, position);
            textArea.setCaretPosition(position + completion.length());
            textArea.moveCaretPosition(position);
            mode = Mode.COMPLETION;
        }
    }

    private class CommitAction extends AbstractAction {

        @Override
        public void actionPerformed(ActionEvent ev) {
            if (mode == Mode.COMPLETION) {
                int pos = textArea.getSelectionEnd();
                textArea.insert(" ", pos);
                textArea.setCaretPosition(pos + 1);
                mode = Mode.INSERT;
            } else {
                textArea.replaceSelection("\n");
            }
        }
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TextAreaDemo();
            }
        });
    }
}
```

*JSlider - Image shadow*

This demo presents how to use DropShadowPanel to which you give a picture. You can control the appearance of the shadow itself by changing the angle, the distance from the subject, the color,
the opacity and the size (or fuzziness). You can also load any picture you want. Beware though, the sliders Size and Opacity regenerate the shadow using a blur filter. To prevent these operations
from blocking the UI, they are performed in a separate thread and I didn't do anything to synchronize the threads. This means you might see some strange problems while playing with these sliders.

To create this effect, we first prepare the image. The idea is to change the canvas size to make room for the shadow, particularily when the blur operation occurs:


```
private BufferedImage prepareImage(BufferedImage original) {
  BufferedImage subject = new BufferedImage(original.getWidth() + shadowSize * 2,
                                            original.getHeight() + shadowSize * 2,
                                            BufferedImage.TYPE_INT_ARGB);
  Graphics2D g2 = subject.createGraphics();
  g2.drawImage(original, null, shadowSize, shadowSize);
  g2.dispose();
  return subject;
}
```

The following step is the generation of the shadow itself. We first create the shadow mask and then blur it:


```
private BufferedImage createDropShadow(BufferedImage image) {
  if (image != null) {
    BufferedImage shadow = new BufferedImage(image.getWidth(),
                                             image.getHeight(),
                                             BufferedImage.TYPE_INT_ARGB);
    getBlurOp(shadowSize).filter(createShadowMask(image), shadow);
    return shadow;
  }
  return null;
}
```


We have a huge performances bottleneck here. We first copy the original picture to a larger one, then create the shadow mask and we filter the whole. It is actually possible to do all these operations in one loop and thus improve the performances. On large pictures, this could make a huge difference but I'll try that later 

The magic happens in createShadowMask() where Icopy every pixel from the original picture and transform it according to the selected shadow color and opacity:


```
private BufferedImage createShadowMask(BufferedImage image) {
  BufferedImage mask = new BufferedImage(image.getWidth(),
                                         image.getHeight(),
                                         BufferedImage.TYPE_INT_ARGB);
  
  for (int x = 0; x < image.getWidth(); x++) {
    for (int y = 0; y < image.getHeight(); y++) {
      int argb = image.getRGB(x, y);
      argb = (int) ((argb >> 24 & 0xFF) * shadowOpacity) << 24 |
             shadowColor.getRGB() & 0x00FFFFFF;
      mask.setRGB(x, y, argb);
    }
  }
  return mask;
}
[code]

Note I preserve the opacity of the original picture to take into account invisible pixels and anti-aliased edges.
Run the app. Click on the 'internal' button. Select an Image using the FileChooser. Now select the color using the
JColorChooser dialog. You can change the opacity, angle, distance, size using the corresponding JSlider.
You can see the shadow drawn as you drag the Slider.

DropShadowDemo.java

[code]
/**
 * Created with IntelliJ IDEA.
 * User: Sowndar
 * Date: 12/14/14
 * Time: 5:02 PM
 * To change this template use File | Settings | File Templates.
 */

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.DataBufferInt;
import java.awt.image.Kernel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

class CheckboardPanel extends JPanel {

    protected int tileSize = 7;
    protected Color darkColor = new Color(204, 204, 204);
    private BufferedImage buffer;

    public CheckboardPanel() {
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension superSize = super.getPreferredSize();
        superSize.setSize(superSize.width + 50,
                superSize.height + 50);
        return superSize;
    }

    @Override
    public void paintComponent(Graphics g) {
        if (buffer == null
                || getWidth() != buffer.getWidth()
                || getHeight() != buffer.getHeight()) {
            createCheckboard();
        }

        Rectangle bounds = g.getClipBounds();
        g.drawImage(buffer.getSubimage(bounds.x, bounds.y,
                bounds.width, bounds.height),
                bounds.x, bounds.y,
                null);
    }

    private void createCheckboard() {
        int width = getWidth();
        int height = getHeight();
        buffer = new BufferedImage(width,
                height,
                BufferedImage.TYPE_INT_RGB);
        Graphics2D g = buffer.createGraphics();

        int boundsStart = 0;
        int boundsEnd = boundsStart + width;
        int startX = boundsStart - (boundsStart % tileSize);
        int endX = boundsEnd + (boundsEnd % tileSize);

        boundsStart = 0;
        boundsEnd = boundsStart + height;
        int startY = boundsStart - (boundsStart % tileSize);
        int endY = boundsEnd + (boundsEnd % tileSize);

        for (int x = startX; x < endX; x += tileSize) {
            int xdark = x % (tileSize << 1) == 0 ? tileSize : 0;
            for (int y = startY; y < endY; y += tileSize) {
                boolean dark = ((y + xdark) % (tileSize << 1)) == 0;
                g.setColor(dark ? darkColor : Color.WHITE);
                g.fillRect(x, y, tileSize, tileSize);
            }
        }
        g.dispose();
    }
}



// TODO merge shadow mask + blur in a single loop
class DropShadowPanel extends JComponent {

    public static String KEY_BLUR_QUALITY = "blur_quality";
    public static String VALUE_BLUR_QUALITY_FAST = "fast";
    public static String VALUE_BLUR_QUALITY_HIGH = "high";

    protected BufferedImage shadow = null;
    protected BufferedImage original = null;

    protected float angle = 30;
    protected int distance = 5;

    protected int shadowSize = 5;
    protected float shadowOpacity = 0.5f;
    protected Color shadowColor = new Color(0x000000);

    // cached values for fast painting
    protected int distance_x = 0;
    protected int distance_y = 0;

    protected HashMap<Object, Object> hints;

    protected DropShadowPanel() {
        computeShadowPosition();
        hints = new HashMap<Object, Object>();
        hints.put(KEY_BLUR_QUALITY, VALUE_BLUR_QUALITY_FAST);
    }

    public DropShadowPanel(String imageName) {
        this();
        setSubject(imageName);
    }

    public DropShadowPanel(URL imageUrl) {
        this();
        setSubject(imageUrl);
    }

    public DropShadowPanel(File imageFile) {
        this();
        setSubject(imageFile);
    }

    public DropShadowPanel(BufferedImage image) {
        this();
        setSubject(image);
    }

    // TODO use an enum?
    public void setRenderingHint(Object hint, Object value) {
        hints.put(hint, value);
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension size;
        if (original == null) {
            size = new Dimension(50, 50);
        } else {
            size = new Dimension(original.getWidth() + (distance + shadowSize) * 2,
                    original.getHeight() + (distance + shadowSize) * 2);
        }
        return size;
    }

    public void setSubject(String imageName) {
        URL imageUrl = DropShadowPanel.class.getResource(imageName);
        setSubject(imageUrl);
    }

    public void setSubject(URL imageUrl) {
        if (imageUrl != null) {
            try {
                BufferedImage subject = ImageIO.read(imageUrl);
                setSubject(subject);
            } catch (IOException e) {
                this.original = null;
                this.original = null;
            }
        } else {
            this.original = null;
            this.original = null;
        }
    }

    public void setSubject(File imageFile) {
        if (imageFile != null) {
            try {
                BufferedImage subject = ImageIO.read(imageFile);
                setSubject(subject);
            } catch (IOException e) {
                this.original = null;
                this.original = null;
            }
        } else {
            this.original = null;
            this.original = null;
        }
    }

    public void setSubject(BufferedImage subject) {
        if (subject != null) {
            this.original = subject;
            refreshShadow();
        } else {
            this.original = null;
            this.original = null;
        }
    }

    public float getAngle() {
        return angle;
    }

    public void setAngle(float angle) {
        this.angle = angle;
        computeShadowPosition();
    }

    public int getDistance() {
        return distance;
    }

    public void setDistance(int distance) {
        this.distance = distance;
        computeShadowPosition();
    }

    public Color getShadowColor() {
        return shadowColor;
    }

    public void setShadowColor(Color shadowColor) {
        if (shadowColor != null) {
            this.shadowColor = shadowColor;
            refreshShadow();
        }
    }

    public float getShadowOpacity() {
        return shadowOpacity;
    }

    public void setShadowOpacity(float shadowOpacity) {
        this.shadowOpacity = shadowOpacity;
        refreshShadow();
    }

    public int getShadowSize() {
        return shadowSize;
    }

    public void setShadowSize(int shadowSize) {
        this.shadowSize = shadowSize;
        refreshShadow();
    }

    public void refreshShadow() {
        if (original != null) {
            shadow = createDropShadow(original);
        }
    }

    private void computeShadowPosition() {
        double angleRadians = Math.toRadians(angle);
        distance_x = (int) (Math.cos(angleRadians) * distance);
        distance_y = (int) (Math.sin(angleRadians) * distance);
    }

    private BufferedImage prepareImage(BufferedImage image) {
        BufferedImage subject = new BufferedImage(image.getWidth() + shadowSize * 2,
                image.getHeight() + shadowSize * 2,
                BufferedImage.TYPE_INT_ARGB);

        Graphics2D g2 = subject.createGraphics();
        g2.drawImage(image, null, shadowSize, shadowSize);
        g2.dispose();

        return subject;
    }

    private BufferedImage createDropShadow(BufferedImage image) {
        BufferedImage subject = prepareImage(image);

        if (hints.get(KEY_BLUR_QUALITY) == VALUE_BLUR_QUALITY_HIGH) {
            BufferedImage shadow = new BufferedImage(subject.getWidth(),
                    subject.getHeight(),
                    BufferedImage.TYPE_INT_ARGB);
            BufferedImage shadowMask = createShadowMask(subject);
            getLinearBlurOp(shadowSize).filter(shadowMask, shadow);
            return shadow;
        }
        try {
            applyShadow(subject);
        } catch (Exception e) {
        }
        return subject;
    }

    private void applyShadow(BufferedImage image) {
        try {
            int dstWidth = image.getWidth();
            int dstHeight = image.getHeight();

            int left = (shadowSize - 1) >> 1;
            int right = shadowSize - left;
            int xStart = left;
            int xStop = dstWidth - right;
            int yStart = left;
            int yStop = dstHeight - right;

            int shadowRgb = shadowColor.getRGB() & 0x00FFFFFF;

            int[] aHistory = new int[shadowSize];
            int historyIdx = 0;

            int aSum;

            int[] dataBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
            int lastPixelOffset = right * dstWidth;
            float sumDivider = shadowOpacity / shadowSize;

            // horizontal pass
            for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) {
                aSum = 0;
                historyIdx = 0;
                for (int x = 0; x < shadowSize; x++, bufferOffset++) {
                    int a = dataBuffer[bufferOffset] >>> 24;
                    aHistory[x] = a;
                    aSum += a;
                }

                bufferOffset -= right;

                for (int x = xStart; x < xStop; x++, bufferOffset++) {
                    int a = (int) (aSum * sumDivider);
                    dataBuffer[bufferOffset] = a << 24 | shadowRgb;

                    // substract the oldest pixel from the sum
                    aSum -= aHistory[historyIdx];

                    // get the lastest pixel
                    a = dataBuffer[bufferOffset + right] >>> 24;
                    aHistory[historyIdx] = a;
                    aSum += a;

                    if (++historyIdx >= shadowSize) {
                        historyIdx -= shadowSize;
                    }
                }
            }

            // vertical pass
            for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) {
                aSum = 0;
                historyIdx = 0;
                for (int y = 0; y < shadowSize; y++, bufferOffset += dstWidth) {
                    int a = dataBuffer[bufferOffset] >>> 24;
                    aHistory[y] = a;
                    aSum += a;
                }

                bufferOffset -= lastPixelOffset;

                for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) {
                    int a = (int) (aSum * sumDivider);
                    dataBuffer[bufferOffset] = a << 24 | shadowRgb;

                    // substract the oldest pixel from the sum
                    aSum -= aHistory[historyIdx];

                    // get the lastest pixel
                    a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24;
                    aHistory[historyIdx] = a;
                    aSum += a;

                    if (++historyIdx >= shadowSize) {
                        historyIdx -= shadowSize;
                    }
                }
            }
        } catch (Exception e) {
        }
    }

    private BufferedImage createShadowMask(BufferedImage image) {
        BufferedImage mask = new BufferedImage(image.getWidth(),
                image.getHeight(),
                BufferedImage.TYPE_INT_ARGB);

        Graphics2D g2d = mask.createGraphics();
        g2d.drawImage(image, 0, 0, null);
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN,
                shadowOpacity));
        g2d.setColor(shadowColor);
        g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
        g2d.dispose();

        return mask;
    }

    private ConvolveOp getLinearBlurOp(int size) {
        float[] data = new float[size * size];
        float value = 1.0f / (float) (size * size);
        for (int i = 0; i < data.length; i++) {
            data[i] = value;
        }
        return new ConvolveOp(new Kernel(size, size, data));
    }

    @Override
    protected void paintComponent(Graphics g) {
        if (shadow != null) {
            int x = (getWidth() - shadow.getWidth()) / 2;
            int y = (getHeight() - shadow.getHeight()) / 2;
            g.drawImage(shadow, x + distance_x, y + distance_y, null);
        }

        if (original != null) {
            int x = (getWidth() - original.getWidth()) / 2;
            int y = (getHeight() - original.getHeight()) / 2;
            g.drawImage(original, x, y, null);
        }
    }
}

class ImagePreview extends JComponent implements PropertyChangeListener {

    ImageIcon thumbnail = null;
    int width = 120;

    public ImagePreview(JFileChooser fc) {
        setPreferredSize(new Dimension(width, 50));
        fc.addPropertyChangeListener(this);
        setBorder(new BevelBorder(BevelBorder.LOWERED));
    }

    public void loadImage(File f) {
        if (f == null) {
            thumbnail = null;
        } else {
            ImageIcon tmpIcon = new ImageIcon(getImage(f.getPath()));
            if (tmpIcon.getIconWidth() > width) {
                thumbnail = new ImageIcon(
                        tmpIcon.getImage().getScaledInstance(width, -1, Image.SCALE_SMOOTH));
            } else {
                thumbnail = tmpIcon;
            }
        }
    }

    public BufferedImage getImage(String fileName) {
        try {
            return ImageIO.read(new File(fileName));
        } catch (IOException ioe) {
            System.err.println("Error loading Image!!");
        }
        return null;
    }

    @Override
    public void propertyChange(PropertyChangeEvent e) {
        String prop = e.getPropertyName();
        if (prop == JFileChooser.SELECTED_FILE_CHANGED_PROPERTY) {
            if (isShowing()) {
                loadImage((File) e.getNewValue());
                repaint();
            }
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (thumbnail != null) {
            int x = getWidth() / 2 - thumbnail.getIconWidth() / 2;
            int y = getHeight() / 2 - thumbnail.getIconHeight() / 2;
            if (y < 0) {
                y = 0;
            }

            if (x < 5) {
                x = 5;
            }
            thumbnail.paintIcon(this, g, x, y);
        }
    }
}

class StackLayout implements LayoutManager2 {

    public static final String BOTTOM = "bottom";
    public static final String TOP = "top";

    private final List<Component> components = new LinkedList<>();

    @Override
    public void addLayoutComponent(Component comp, Object constraints) {
        synchronized (comp.getTreeLock()) {
            if (BOTTOM.equals(constraints)) {
                components.add(0, comp);
            } else if (TOP.equals(constraints)) {
                components.add(comp);
            } else {
                components.add(comp);
            }
        }
    }

    @Override
    public void addLayoutComponent(String name, Component comp) {
        addLayoutComponent(comp, TOP);
    }

    @Override
    public void removeLayoutComponent(Component comp) {
        synchronized (comp.getTreeLock()) {
            components.remove(comp);
        }
    }

    @Override
    public float getLayoutAlignmentX(Container target) {
        return 0.5f;
    }

    @Override
    public float getLayoutAlignmentY(Container target) {
        return 0.5f;
    }

    @Override
    public void invalidateLayout(Container target) {
    }

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        synchronized (parent.getTreeLock()) {
            int width = 0;
            int height = 0;

            for (Component comp : components) {
                Dimension size = comp.getPreferredSize();
                width = Math.max(size.width, width);
                height = Math.max(size.height, height);
            }

            Insets insets = parent.getInsets();
            width += insets.left + insets.right;
            height += insets.top + insets.bottom;

            return new Dimension(width, height);
        }
    }

    @Override
    public Dimension minimumLayoutSize(Container parent) {
        synchronized (parent.getTreeLock()) {
            int width = 0;
            int height = 0;

            for (Component comp : components) {
                Dimension size = comp.getMinimumSize();
                width = Math.max(size.width, width);
                height = Math.max(size.height, height);
            }

            Insets insets = parent.getInsets();
            width += insets.left + insets.right;
            height += insets.top + insets.bottom;

            return new Dimension(width, height);
        }
    }

    @Override
    public Dimension maximumLayoutSize(Container target) {
        return new Dimension(Integer.MAX_VALUE,
                Integer.MAX_VALUE);
    }

    @Override
    public void layoutContainer(Container parent) {
        synchronized (parent.getTreeLock()) {
            int width = parent.getWidth();
            int height = parent.getHeight();

            Rectangle bounds = new Rectangle(0, 0, width, height);

            int componentsCount = components.size();

            for (int i = 0; i < componentsCount; i++) {
                Component comp = components.get(i);
                comp.setBounds(bounds);
                parent.setComponentZOrder(comp, componentsCount - i - 1);
            }
        }
    }
}


public class DropShadowDemo extends JFrame {

    private DropShadowPanel dropShadowPanel;

    public DropShadowDemo() throws HeadlessException {
        super("Drop Shadow");
        try {
            for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) {
            // Do nothing
        }
        buildContentPane();
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        setSize(new Dimension(scrDim.width / 2 + 150, scrDim.height));
        setLocationRelativeTo(null);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private void buildContentPane() {
        JPanel viewPane = buildViewPane();
        JPanel debugPane = buildDebugPane();

        add(new JScrollPane(viewPane), BorderLayout.CENTER);
        add(debugPane, BorderLayout.EAST);
    }

    private JPanel buildDebugPane() {
        JPanel panel = new JPanel(new GridBagLayout());
        JSlider slider;

        panel.add(new JLabel("Angle:"),
                new GridBagConstraints(0, 0,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 0),
                        0, 0));
        panel.add(slider = new JSlider(0, 360, 30),
                new GridBagConstraints(0, 1,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 6),
                        0, 0));
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                final int value = ((JSlider) e.getSource()).getValue();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        dropShadowPanel.setAngle(value);
                        dropShadowPanel.repaint();
                    }
                }).start();
            }
        });
        panel.add(new JLabel("Distance:"),
                new GridBagConstraints(0, 2,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 0),
                        0, 0));
        panel.add(slider = new JSlider(1, 80, 5),
                new GridBagConstraints(0, 3,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 6),
                        0, 0));
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                final int value = ((JSlider) e.getSource()).getValue();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        dropShadowPanel.setDistance(value);
                        dropShadowPanel.revalidate();
                        dropShadowPanel.repaint();
                    }
                }).start();
            }
        });
        panel.add(new JLabel("Size:"),
                new GridBagConstraints(0, 4,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 0),
                        0, 0));
        panel.add(slider = new JSlider(1, 40, 5),
                new GridBagConstraints(0, 5,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 6),
                        0, 0));
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                final int value = ((JSlider) e.getSource()).getValue();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        dropShadowPanel.setShadowSize(value);
                        dropShadowPanel.revalidate();
                        dropShadowPanel.repaint();
                    }
                }).start();
            }
        });
        panel.add(new JLabel("Opacity"),
                new GridBagConstraints(0, 6,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 0),
                        0, 0));
        panel.add(slider = new JSlider(0, 100, 50),
                new GridBagConstraints(0, 7,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 6),
                        0, 0));
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                final int value = ((JSlider) e.getSource()).getValue();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        dropShadowPanel.setShadowOpacity(value / 100.0f);
                        dropShadowPanel.repaint();
                    }
                }).start();
            }
        });
        panel.add(new JLabel("Color:"),
                new GridBagConstraints(0, 8,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 0),
                        0, 0));
        JButton button;
        panel.add(button = new JButton(" "),
                new GridBagConstraints(0, 9,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.HORIZONTAL,
                        new Insets(0, 6, 0, 6),
                        0, 0));
        button.setBackground(dropShadowPanel.getShadowColor());
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JButton source = (JButton) e.getSource();
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        Color color = JColorChooser.showDialog(DropShadowDemo.this,
                                "Shadow Color",
                                source.getBackground());
                        if (color != null) {
                            source.setBackground(color);
                            dropShadowPanel.setShadowColor(color);
                            dropShadowPanel.repaint();
                        }
                    }
                });
            }
        });
        panel.add(new JLabel("Picture:"),
                new GridBagConstraints(0, 10,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 0),
                        0, 0));
        panel.add(button = new JButton("[Internal]"),
                new GridBagConstraints(0, 11,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.HORIZONTAL,
                        new Insets(0, 6, 0, 6),
                        0, 0));
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JButton source = (JButton) e.getSource();
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        JFileChooser chooser = new JFileChooser();
                        chooser.setPreferredSize(new Dimension(650, 450));
                        chooser.setAccessory(new ImagePreview(chooser));
                        chooser.setAcceptAllFileFilterUsed(false);
                        chooser.addChoosableFileFilter(new FileFilter() {
                            String[] readFormat = ImageIO.getReaderFormatNames();
                            String name;

                            @Override
                            public boolean accept(File f) {
                                name = f.getAbsolutePath();
                                for (int i = 0; i < readFormat.length; i++) {
                                    if (f.isDirectory() || name.endsWith(readFormat[i])) {
                                        return true;
                                    }
                                }
                                return false;
                            }

                            @Override
                            public String getDescription() {
                                return "Images Filter";
                            }
                        });
                        chooser.setCurrentDirectory(new File("Sample"));
                        int result = chooser.showOpenDialog(DropShadowDemo.this);
                        if (result == JFileChooser.APPROVE_OPTION) {
                            File file = chooser.getSelectedFile();
                            source.setText(file.getName());
                            dropShadowPanel.setSubject(file);
                            dropShadowPanel.repaint();
                        }
                    }
                });
            }
        });
        panel.add(new JLabel("Rendering:"),
                new GridBagConstraints(0, 12,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 6, 0, 0),
                        0, 0));
        JCheckBox checkbox;
        panel.add(checkbox = new JCheckBox("Fast Rendering", true),
                new GridBagConstraints(0, 13,
                        1, 1,
                        1.0, 0.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.HORIZONTAL,
                        new Insets(0, 6, 0, 6),
                        0, 0));
        checkbox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JCheckBox source = (JCheckBox) e.getSource();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        dropShadowPanel.setRenderingHint(DropShadowPanel.KEY_BLUR_QUALITY,
                                source.isSelected()
                                        ? DropShadowPanel.VALUE_BLUR_QUALITY_FAST
                                        : DropShadowPanel.VALUE_BLUR_QUALITY_HIGH);
                        dropShadowPanel.refreshShadow();
                        dropShadowPanel.repaint();
                    }
                }).start();
            }
        });
        panel.add(Box.createVerticalGlue(),
                new GridBagConstraints(0, 14,
                        1, 1,
                        1.0, 1.0,
                        GridBagConstraints.LINE_START,
                        GridBagConstraints.NONE,
                        new Insets(0, 0, 0, 0),
                        0, 0));
        return panel;
    }

    private JPanel buildViewPane() {
        JPanel panel = new JPanel(new StackLayout());
        panel.setOpaque(false);

        dropShadowPanel = new DropShadowPanel("images/subject.png");

        panel.add(new CheckboardPanel(), StackLayout.BOTTOM);
        panel.add(dropShadowPanel, StackLayout.TOP);
        return panel;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new DropShadowDemo();
            }
        });
    }
}
```

*s11.postimg.org/q9ys165rz/Drop_Shadow.jpg

*JLayeredPane*

I am a huge fan of Apple's Aperture. This application lets you organize and process your pictures in a very elegant fashion. One of the coolest features of this application is its loupe, and most notably the new centered loupe, added in Aperture 1.5.
I came up with a simple demo that I quite like. (Its role is to demonstrate the use of layouts with JLayeredPane but that's a different story but this means this demo does not use the glass pane; how's that for a change?) It's nothing impressive really,
but I thought some of you might enjoy it!!
Run the app, select the Loupe layer 1 to 4 . Select the zoom factor slider level.
As you hover the Mouse over the Image, the Image is zoomed and shown in the aperture window.


```
/**
 * Created with IntelliJ IDEA.
 * User: Sowndar
 * Date: 12/14/14
 * Time: 5:23 PM
 * To change this template use File | Settings | File Templates.
 */
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

/**
 *
 * @author JGuru
 */


class Loupe extends JComponent {
    private BufferedImage loupeImage;
    private Point loupeLocation = new Point(0, 0);
    private JLayeredPane layeredPane;
    private BufferedImage buffer;
    private int zoomLevel = 2;

    public Loupe(JLayeredPane layeredPane) {
        this.layeredPane = layeredPane;

        loadImages();

        layeredPane.addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseMoved(MouseEvent mouseEvent) {
                Point location = mouseEvent.getPoint();
                location.translate(-getWidth() / 2, -getHeight() / 2);
                setLocation(location);
            }
        });
        addComponentListener(new ComponentListener() {
            public void componentHidden(ComponentEvent componentEvent) {
                resetBuffer();
            }

            public void componentMoved(ComponentEvent componentEvent) {
            }

            public void componentResized(ComponentEvent componentEvent) {
                resetBuffer();
            }

            public void componentShown(ComponentEvent componentEvent) {
            }
        });
    }

    public int getZoomLevel() {
        return this.zoomLevel;
    }

    public void setZoomLevel(int zoom) {
        if (zoom < 1) {
            zoom = 1;
        }

        int oldZoom = this.zoomLevel;
        this.zoomLevel = zoom;
        firePropertyChange("zoomLevel", oldZoom, this.zoomLevel);

        repaint();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(loupeImage.getWidth(),
                loupeImage.getHeight());
    }

    public void resetBuffer() {
        buffer = null;
    }

    private void loadImages() {
        try {
            loupeImage = ImageIO.read(getClass().getResource("images/loupe.png"));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        if (buffer == null) {
            buffer = createBuffer();
        }

        Graphics2D g2 = buffer.createGraphics();
        g2.setComposite(AlphaComposite.Clear);
        g2.fillRect(0, 0, buffer.getWidth(), buffer.getHeight());
        g2.setComposite(AlphaComposite.Src);

        Point location = getLocation();
        location.translate(getWidth() / 2, getHeight() / 2);

        int myLayer = layeredPane.getLayer(this);
        for (int i = myLayer - 1; i >= 2; i -= 2) {
            Component[] components = layeredPane.getComponentsInLayer(i);
            for (Component c : components) {
                if (c.getBounds().contains(location)) {
                    g2.translate(c.getX(), c.getY());
                    c.paint(g2);
                    g2.translate(-c.getX(), -c.getY());
                }
            }
        }

        g2.dispose();

        if (zoomLevel > 1) {
            ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);

            Shape clip = g.getClip();
            Area newClip = new Area(clip);
            newClip.intersect(new Area(new Ellipse2D.Double(6.0, 6.0, 138.0, 138.0)));

            g.setClip(newClip);
            g.drawImage(buffer,
                    (int) (-getX() * zoomLevel - getWidth() * 0.5 * (zoomLevel - 1)),
                    (int) (-getY() * zoomLevel - getHeight() * 0.5 * (zoomLevel - 1)),
                    buffer.getWidth() * zoomLevel,
                    buffer.getHeight() * zoomLevel, null);
            g.setClip(clip);
        }

        g.drawImage(loupeImage, 0, 0, null);
    }

    private BufferedImage createBuffer() {
        GraphicsEnvironment local = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice device = local.getDefaultScreenDevice();
        GraphicsConfiguration config = device.getDefaultConfiguration();

        Container parent = getParent();
        return config.createCompatibleImage(parent.getWidth(), parent.getHeight(),
                Transparency.TRANSLUCENT);
    }
}

public class LoupeLayeredPane extends JFrame {
    private JLayeredPane layeredPane;
    private Loupe loupe;

    public LoupeLayeredPane() {
        super("LoupeLayeredPane");

        layeredPane = new JLayeredPane();

        addLayersControl();
        loadImagesInLayers();
        createLoupe();

        setSize(540, 350);
        setLocationRelativeTo(null);
        setResizable(false);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new LoupeLayeredPane().setVisible(true);
            }
        });
    }

    private void loadImagesInLayers() {
        layeredPane.setLayout(new FlowLayout());

        for (int i = 2; i <= 5; i++) {
            String name = "images/photo" + i + ".jpg";
            URL url = getClass().getResource(name);
            Icon icon = new ImageIcon(url);
            JLabel label = new JLabel(icon);

            layeredPane.add(label,
                    (Integer) (JLayeredPane.DEFAULT_LAYER + (i - 1) * 2));
        }

        add(layeredPane);
    }

    private void addLayersControl() {
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));

        JComboBox layerSelection = new JComboBox(new String[] {
                "Layer 0", "Layer 1", "Layer 2", "Layer 3", "Layer 4"
        });
        layerSelection.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                JComboBox layerSelection = (JComboBox) actionEvent.getSource();
                int layerId = layerSelection.getSelectedIndex();
                layeredPane.setLayer(loupe,
                        (Integer) (JLayeredPane.DEFAULT_LAYER + layerId * 2 + 1));
            }
        });
        panel.add(new JLabel("Loupe Layer: "));
        panel.add(layerSelection);

        JSlider zoomSelection = new JSlider(1, 16, 2);
        zoomSelection.setPaintTicks(true);
        zoomSelection.setSnapToTicks(true);
        zoomSelection.setPaintLabels(true);
        zoomSelection.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent changeEvent) {
                JSlider zoomSelection = (JSlider) changeEvent.getSource();
                loupe.setZoomLevel(zoomSelection.getValue());
            }
        });

        panel.add(Box.createHorizontalStrut(24));
        panel.add(new JLabel("Zoom: "));
        panel.add(new JLabel("1"));
        panel.add(zoomSelection);
        panel.add(new JLabel("16"));

        add(panel, BorderLayout.NORTH);
    }

    private void createLoupe() {
        loupe = new Loupe(layeredPane);
        Dimension size = loupe.getPreferredSize();
        layeredPane.add(loupe,
                (Integer) (JLayeredPane.DEFAULT_LAYER + 1));
    }
}
```

*s12.postimg.org/55sz28e4p/Loupe_Layered_Pane.jpg

Download the images used : filehosting.org | Download | Loupe.zip

*Mouse Drag - Tiled Image Fill*

The Base Class: Fill

The example uses a simple base class called Fill that can be used to create reusable, combinable objects that paint a rectangular area. The Fill class is similar to Border -- it's really just an encapsulation of paint methods:


```
public class Fill {
    public void paintFill(Component c, Graphics g, Rectangle r) {...}
    public void paintFill(Component c, Graphics g) {...}
    public void paintFill(Component c, Graphics g, int x, ...) {...}
}
```

The first paintFill method just paints within the area defined by the rectangle r using the Graphics object g. Typically the component parameter, c, is the target of the Graphics object although it's not required to be. The other methods are convenience methods. The second one computes the coordinates of the component's insets rectangle and then passes them to the first method. The third one creates a Rectangle based on the parameters and passes it to the first method.

What the paintFill method paints can reflect the values of the specified component's properties. The default implementation of the paintFill method does this -- it just fills the specified rectangle with the component's background color.

To use a Fill object to paint a Swing component, you just create a subclass of the component's class and override the paintComponent method like this:


```
public void paintComponent(Graphics g) {
    Graphics gFill = g.create();
    myFillObject.paintFill(this, gFill);
    gFill.dispose();
}
```

Note that we've passed a copy of the Graphics object to the paintFill method. This insulates any other painting code we might add to the paintComponent method from changes made to the Graphics object by the paintFill method. 

Example: TiledFill

TiledFill is a Fill subclass that wraps another Fill object. TiledFill uses the wrapped Fill object to paint each tileWidth x tileHeight cell in a grid whose bounds are defined by the paintFill rectangle. The paintFill method for TiledFill paints the first tile in the upper left corner of the incoming rectangle. Here is the painting code:


```
public void paintFill(Component c, Graphics g, Rectangle r) {
    int x = r.x, y = r.y, w = r.width, h = r.height;
    ...
    Graphics clippedG = g.create(x, y, w, h);
    for (int tx = 0; tx < w; tx += tileWidth) {
	for (int ty = 0; ty < h; ty += tileHeight) {
	    tile.paintFill(c, clippedG, tx, ty, tileWidth, tileHeight);
	}
    }
    clippedG.dispose();
}
```

The paintFill method clips its graphics object because the full size tiles painted in the rightmost column and bottom row may extend beyond the paintFill rectangle. 
The TestTiledFill application combines a TiledFill object with an ImageFill object to paint a window. The composite object, tiledFill, is created like this:


```
BufferedImage image = ImageIO.read(file);
ImageFill fill = new ImageFill(image);
tiledFill = new TiledFill(fill, image.getWidth(), image.getHeight());
```

The paintComponent method of TestTiledFill is a little more complicated than the usual implementation because we want to paint the area within the clipBounds of the incoming paintComponent graphics object with tiles that are aligned with a tileWidth by tileHeight grid whose origin is at 0,0. To do so we create a paintFill rectangle that's based on the clipBounds: we enlarge the clipBounds rectangle so that its origin is aligned with the origin of a tile and its size is a multiple of the tile size. 
The TestTiledFill application allows you to interactively change the tile size by dragging on the lower right corner of the green rectangle. 
As you drag the Mouse diagonally downwards or upwards, more tiles are created!!


```
/**
 * Created with IntelliJ IDEA.
 * User: Sowndar
 * Date: 12/14/14
 * Time: 5:39 PM
 * To change this template use File | Settings | File Templates.
 */

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

class Fill {

    public void paintFill(Component c, Graphics g, Rectangle r) {
        // assert c, g, not null
        g.setColor(c.getBackground());
        g.fillRect(r.x, r.y, r.width, r.height);
    }

    public void paintFill(Container c, Graphics g) {
        // assert c, g, not null
        Insets insets = c.getInsets();
        int x = insets.left;
        int y = insets.top;
        int w = c.getWidth() - (insets.left + insets.right);
        int h = c.getHeight() - (insets.top + insets.bottom);
        paintFill(c, g, new Rectangle(x, y, w, h));
    }

    public void paintFill(Component c, Graphics g, int x, int y, int w, int h) {
        paintFill(c, g, new Rectangle(x, y, w, h));
    }
}

/**
 * Displays a single
 * <code>BufferedImage</code>, scaled to fit the
 * <code>paintFill</code> rectangle.
 * <pre>
 *  BufferedImage image = ImageIO.read(new File("background.jpg"));
 *  final ImageFill imageFill = new ImageFill(image);
 *  JPanel p = new JPanel() {
 *      public c void paintComponent(Graphics g) {
 *	    imageFill.paintFill(this, g);
 *	}
 *  };
 * </pre> Note that animated gifs aren't supported as there's no image observer.
 */
class ImageFill extends Fill {

    private final static int IMAGE_CACHE_SIZE = 8;
    private BufferedImage image;
    private BufferedImage[] imageCache = new BufferedImage[IMAGE_CACHE_SIZE];
    private int imageCacheIndex = 0;

    /**
     * Creates an
     * <code>ImageFill</code> that draws <i>image</i>
     * scaled to fit the
     * <code>paintFill</code> rectangle parameters.
     *
     * [MENTION=288550]see[/MENTION] #getImage
     * [MENTION=288550]see[/MENTION] #paintFill
     */
    public ImageFill(BufferedImage image) {
        this.image = image;
    }

    /**
     * Creates an "empty" ImageFill. Before the ImageFill can be drawn with the
     * <code>paintFill</code> method, the
     * <code>image</code> property must be set.
     *
     * [MENTION=288550]see[/MENTION] #setImage
     * [MENTION=288550]see[/MENTION] #paintFill
     */
    public ImageFill() {
        this.image = null;
    }

    /**
     * Returns the image that the
     * <code>paintFill</code> method draws.
     *
     * @return the value of the <code>image</code> property
     * [MENTION=288550]see[/MENTION] #setImage
     * [MENTION=288550]see[/MENTION] #paintFill
     */
    public BufferedImage getImage() {
        return image;
    }

    /**
     * Set the image that the
     * <code>paintFill</code> method draws.
     *
     * [MENTION=9956]PARAM[/MENTION] image the new value of the <code>image</code> property
     * [MENTION=288550]see[/MENTION] #getImage
     * [MENTION=288550]see[/MENTION] #paintFill
     */
    public void setImage(BufferedImage image) {
        this.image = image;
        for (int i = 0; i < imageCache.length; i++) {
            imageCache[i] = null;
        }
    }

    /**
     * Returns the actual width of the
     * <code>BufferedImage</code> rendered by the
     * <code>paintFill</code> method. If the image property hasn't been set, -1
     * is returned.
     *
     * @return the value of <code>getImage().getWidth()</code> or -1 if
     * getImage() returns null
     * [MENTION=288550]see[/MENTION] #getHeight
     * [MENTION=288550]see[/MENTION] #setImage
     */
    public int getWidth() {
        BufferedImage image = getImage();
        return (image == null) ? -1 : image.getWidth();
    }

    /**
     * Returns the actual height of the
     * <code>BufferedImage</code> rendered by the
     * <code>paintFill</code> method. If the image property hasn't been set, -1
     * is returned.
     *
     * @return the value of <code>getImage().getHeight()</code> or -1 if
     * getImage() returns null
     * [MENTION=288550]see[/MENTION] #getWidth
     * [MENTION=288550]see[/MENTION] #setImage
     */
    public int getHeight() {
        BufferedImage image = getImage();
        return (image == null) ? -1 : image.getHeight();
    }

    /**
     * Create a copy of image scaled to width,height w,h and add it to the null
     * element of the imageCache array. If the imageCache array is full, then we
     * replace the "least recently used element", at imageCacheIndex.
     */
    private BufferedImage createScaledImage(Component c, int w, int h) {
        GraphicsConfiguration gc = c.getGraphicsConfiguration();
        BufferedImage newImage = gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);

        boolean cacheOverflow = true;
        for (int i = 0; i < imageCache.length; i++) {
            Image image = imageCache[i];
            if (image == null) {
                imageCache[i] = newImage;
                cacheOverflow = false;
                break;
            }
        }
        if (cacheOverflow) {
            imageCache[imageCacheIndex] = newImage;
            imageCacheIndex = (imageCacheIndex + 1) % imageCache.length;
        }

        Graphics g = newImage.getGraphics();
        int width = image.getWidth();
        int height = image.getHeight();
        g.drawImage(image, 0, 0, w, h, 0, 0, width, height, null);
        g.dispose();

        return newImage;
    }

    /**
     * Returns either the image itself or a cached scaled copy.
     */
    private BufferedImage getFillImage(Component c, int w, int h) {
        if ((w == getWidth()) && (h == getHeight())) {
            return image;
        }
        for (int i = 0; i < imageCache.length; i++) {
            BufferedImage cimage = imageCache[i];
            if (cimage == null) {
                break;
            }
            if ((cimage.getWidth(c) == w) && (cimage.getHeight(c) == h)) {
                return cimage;
            }
        }
        return createScaledImage(c, w, h);
    }

    /**
     * Draw the image at <i>r.x,r.y</i>, scaled to <i>r.width</i>
     * and <i>r.height</i>.
     *
     * [MENTION=9956]PARAM[/MENTION] c
     * [MENTION=9956]PARAM[/MENTION] g
     * [MENTION=9956]PARAM[/MENTION] r
     */
    public void paintFill(Component c, Graphics g, Rectangle r) {
        if ((r.width > 0) && (r.height > 0)) {
            BufferedImage fillImage = getFillImage(c, r.width, r.height);
            g.drawImage(fillImage, r.x, r.y, c);

        }
    }
}

class TiledFill extends Fill {

    private Fill tile;
    private int tileWidth;
    private int tileHeight;

    public TiledFill(Fill tile, int tileWidth, int tileHeight) {
        this.tile = tile;
        this.tileWidth = tileWidth;
        this.tileHeight = tileHeight;
    }

    public TiledFill() {
        this.tile = null;
        this.tileWidth = -1;
        this.tileHeight = -1;
    }

    public void setTileWidth(int tileWidth) {
        // assert tileWidth > 0
        this.tileWidth = tileWidth;
    }

    public int getTileWidth() {
        return tileWidth;
    }

    public void setTileHeight(int tileHeight) {
        // assert tileHeight > 0
        this.tileHeight = tileHeight;
    }

    public int getTileHeight() {
        return tileHeight;
    }

    public void setTile(Fill tile) {
        this.tile = tile;
    }

    public Fill getTile() {
        return tile;
    }

    public void paintFill(Component c, Graphics g, Rectangle r) {
        int x = r.x, y = r.y, w = r.width, h = r.height;
        int tileWidth = getTileWidth();
        int tileHeight = getTileHeight();
        if (tile != null) {
            Graphics clippedG = g.create(x, y, w, h);
            Rectangle tr = new Rectangle(tileWidth, tileHeight);
            for (tr.x = 0; tr.x < w; tr.x += tileWidth) {
                for (tr.y = 0; tr.y < h; tr.y += tileHeight) {
                    tile.paintFill(c, clippedG, tr);
                }
            }
            clippedG.dispose();
        }
    }
}

/**
 * Simple demonstration of the TiledFill class. Fill a window, using a TiledFill
 * object, with the texture.jpg image. Dragging on the lower right hand corner
 * of the green rectangle changes the tile size.
 */
public class TiledImageDemo extends JPanel {

    /**
     * Initialized by the (first call to the) constructor. This Fill is used to
     * paint the entire panel.
     */
    private static TiledFill tiledFill = null;

    /**
     * A simple rectangular outline, intended to represent the size of a single
     * tile, that can be resized by dragging the mouse on the bottom right
     * corner.
     */
    private class ResizePanel extends JPanel implements MouseListener, MouseMotionListener {

        private boolean doDrag = false;
        private final Color color = new Color(0x99, 0xFF, 0x99);
        private final Stroke stroke
                = new BasicStroke(4.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);

        public ResizePanel() {
            super(null);
            addMouseListener(this);
            addMouseMotionListener(this);
        }

        public void paintComponent(Graphics g) {
            int w = getWidth();
            int h = getHeight();
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(color);
            Stroke defaultStroke = g2d.getStroke();
            g2d.setStroke(stroke);
            g2d.drawRect(2, 2, w - 3, h - 3);
            g2d.setStroke(defaultStroke);
            if (doDrag) {
                g2d.setColor(Color.blue);
            }
            g2d.fillRect(w - 11, h - 11, 11, 11);

        }

        public boolean isOpaque() {
            return false;
        }

        public void mousePressed(MouseEvent e) {
            int resizeHandleX = getWidth() - 11;
            int resizeHandleY = getHeight() - 11;
            if ((e.getX() >= resizeHandleX) && (e.getY() >= resizeHandleY)) {
                doDrag = true;
            }
        }

        private void resetTile(int width, int height) {
            tiledFill.setTileWidth(width);
            tiledFill.setTileHeight(height);
            getParent().repaint();
        }

        public void mouseDragged(MouseEvent e) {
            if (doDrag) {
                int w = e.getX();
                int h = e.getY();
                if ((w > 1) && (h > 1)) {
                    setSize(w, h);
                    resetTile(w, h);
                }
            }
        }

        public void mouseReleased(MouseEvent e) {
            doDrag = false;
            tiledFill.setTileWidth(getWidth());
            tiledFill.setTileHeight(getHeight());
            getParent().repaint();
        }

        public void mouseClicked(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseMoved(MouseEvent e) {
        }
    }

    /**
     * Create a TestTiledFill instance that uses an TiledFill object to draw
     * "Adel Miller6.jpg".
     */
    public TiledImageDemo() {
        super(null);

        setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
        setForeground(Color.black);
        setBackground(Color.white);

        if (tiledFill == null) {
            URL file = getClass().getResource("Images/Adel Miller6.jpg");
            try {
                BufferedImage image = ImageIO.read(file);
                ImageFill fill = new ImageFill(image);
                tiledFill = new TiledFill(fill, image.getWidth(), image.getHeight());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        Insets insets = getInsets();
        int x = insets.left;
        int y = insets.top;
        int w = tiledFill.getTileWidth() - (insets.left + insets.right);
        int h = tiledFill.getTileHeight() - (insets.top + insets.bottom);
        JPanel resizePanel = new ResizePanel();
        resizePanel.setBounds(x, y, w, h);
        add(resizePanel);
    }

    /**
     * Paint the area within
     * <code>g.getClipBounds()</code> making sure that the new tiles are aligned
     * with a tileWidth by tileHeight grid whose origin is at 0,0.
     */
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        /* To ensure that the tiles we paint are aligned with
         * a tileWidth X tileHeight grid whose origin is 0,0 we
         * enlarge the clipBounds rectangle so that its origin
         * is aligned with the origin of a tile and its size
         * is a multiple of the tile size.
         */
        Rectangle clip = g.getClipBounds();
        int tw = tiledFill.getTileWidth();
        int th = tiledFill.getTileHeight();
        int x = (clip.x / tw) * tw;
        int y = (clip.y / th) * th;
        int w = (((clip.x + clip.width + tw - 1) / tw) * tw) - x;
        int h = (((clip.y + clip.height + th - 1) / th) * th) - y;

        Graphics2D gFill = (Graphics2D) g.create();
        // Set RenderingHints properties
        gFill.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        gFill.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        gFill.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        gFill.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        tiledFill.paintFill(this, gFill, new Rectangle(x, y, w, h));
        gFill.dispose();
    }

    /**
     * The preferred size is equal to the size of a 3x2 grid of ImageFill sized
     * tiles (created from the "background.jpg" image plus the insets space
     * allocated to the border.
     *
     * @return
     *
     * @returns the size of the background image and the border.
     */
    @Override
    public Dimension getPreferredSize() {
        Insets insets = getInsets();
        int w = (3 * tiledFill.getTileWidth()) + insets.left + insets.right;
        int h = (2 * tiledFill.getTileHeight()) + insets.top + insets.bottom;
        return new Dimension(w, h);
    }

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("TiledImageDemo");
                f.add(new TiledImageDemo(), BorderLayout.CENTER);
                f.setSize(Toolkit.getDefaultToolkit().getScreenSize());
                f.setVisible(true);
                f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
            }
        });
    }
}
```

*s12.postimg.org/qxh99f79l/Tiled_Image_Fill.jpg


*Mirror Image*

We load a random Image from the 'Images' directory. Draw the normal Image and also the mirror Image.



```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author JGuru
 */
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Sowndar
 */
public class MirrorDemo extends JPanel {

    private Image image;
    private File directory = new File("Images/");
    private final String[] imgName;
    private String[] readFormat = ImageIO.getReaderFormatNames();
    private final Dimension scrDim;
    private int imgWidth, imgHeight;

    public MirrorDemo() {

        if (!directory.exists()) {
            System.err.println("The specified directory doesn't exist!!");
            System.exit(-1);
        }
        imgName = directory.list(new FilenameFilter() {

            @Override
            public boolean accept(File dir, final String name) {
                for (int i = 0; i < readFormat.length; i++) {
                    if (name.endsWith(readFormat[i])) {
                        return true;
                    }
                }
                return false;
            }
        });
        scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        int rand = (int) (imgName.length * Math.random());

        image = getImage(directory.toString() + "/" + imgName[rand]);
        if (image != null) {
            //Get Image's dimension
            imgWidth = image.getWidth(this);
            imgHeight = image.getHeight(this);
            if ((imgWidth > scrDim.width / 2) || (imgHeight > scrDim.height)) {
                image = resizeImage(image);
                //Get Image's new dimension
                imgWidth = image.getWidth(this);
                imgHeight = image.getHeight(this);
            }

            setPreferredSize(new Dimension(imgWidth * 2, imgHeight));
        }
    }

    // Get the Image
    public BufferedImage getImage(String fileName) {
        try {
            return ImageIO.read(new File(fileName));
        } catch (IOException ioe) {
            System.err.println("Error loading Image!!");
            System.exit(-1);
        }
        return null;
    }

    // Resize the Image
    public Image resizeImage(Image image) {
        if (image != null) {
            imgWidth = image.getWidth(null);
            imgHeight = image.getHeight(null);
            int maxWidth, maxHeight;

            double aspect = ((double) imgWidth) / ((double) imgHeight);

            if (aspect > 1.3333) {
                // Fix the width as maxWidth, calculate the maxHeight
                maxWidth = scrDim.width / 2;
                maxHeight = (int) (((double) maxWidth) / aspect);
            } else {
                // Fix the height, calculate the maxWidth for this
                maxHeight = scrDim.height;
                maxWidth = (int) (((double) maxHeight) * aspect);
            }
            return image.getScaledInstance(maxWidth, maxHeight, Image.SCALE_SMOOTH);
        }
        return null;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_DEFAULT);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        g2d.setColor(Color.red);
        g2d.setFont(new Font("Serif", Font.BOLD, 18));
        int X = 0, Y = 0;
        if (image != null) {
            // Draw the original Image
            g2d.drawImage(image, X, Y, this);
            g2d.drawString("Original Image", X + 50, Y + 50);

            X = imgWidth * 2;
            Y = 0;
            // Draw the mirror image right next to the original Image
            g2d.drawImage(image, X, Y, -imgWidth, imgHeight, this);
            g2d.setColor(Color.blue);
            g2d.drawString("Mirror Image", imgWidth + 50, Y + 50);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("Mirror Demo");
                f.add(new MirrorDemo());
                f.pack();
                f.setVisible(true);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}
```

*s12.postimg.org/f5sgvvert/Mirror_Demo.jpg

*Image Flip demo*

The following program draws the Image , it also draws the flipped image at the bottom.


```
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
import java.awt.*;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;

public class ImageFlip extends JPanel {

    private Image image;
    private File directory = new File("Images/");
    private final String[] imgName;
    private String[] readFormat = ImageIO.getReaderFormatNames();
    private final Dimension scrDim;
    private int imgWidth, imgHeight;

    public ImageFlip() {

        if (!directory.exists()) {
            System.err.println("The specified directory doesn't exist!!");
            System.exit(-1);
        }
        imgName = directory.list(new FilenameFilter() {

            @Override
            public boolean accept(File dir, final String name) {
                for (int i = 0; i < readFormat.length; i++) {
                    if (name.endsWith(readFormat[i])) {
                        return true;
                    }
                }
                return false;
            }
        });
        scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        int rand = (int) (imgName.length * Math.random());
        try {
            image = ImageIO.read(new File(directory.toString() + "/" + imgName[rand]));
            imgWidth = image.getWidth(this);
            imgHeight = image.getHeight(this);
            if (imgHeight > scrDim.height / 2) {
                image = resize(image);
                imgWidth = image.getWidth(this);
                imgHeight = image.getHeight(this);
            }
        } catch (IOException ioe) {
            System.err.println("Error loading Image!!");
            System.exit(1);
        }
        setPreferredSize(new Dimension(imgWidth, imgHeight * 2));
    }

    // Resize the Image
    public Image resize(Image image) {
        if (image != null) {
            imgWidth = image.getWidth(null);
            imgHeight = image.getHeight(null);
            int maxWidth, maxHeight;

            double aspect = ((double) imgWidth) / ((double) imgHeight);

            if (imgHeight > imgWidth) {
                // Fix the height, calculate the maxWidth for this
                maxHeight = scrDim.height / 2;
                maxWidth = (int) (((double) maxHeight) * aspect);

            } else {
                // Fix the width as maxWidth, calculate the maxHeight
                maxWidth = scrDim.height / 2 + 280;
                maxHeight = (int) (((double) maxWidth) / aspect);
            }
            return image.getScaledInstance(maxWidth, maxHeight, Image.SCALE_SMOOTH);
        }
        return null;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_DEFAULT);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        g2d.setColor(Color.red);
        g2d.setFont(new Font("Serif", Font.BOLD, 18));
        int X = 0, Y = 0;
        if (image != null) {
            // Draw the original Image
            g2d.drawImage(image, X, Y, this);
            g2d.drawString("Original Image", X + 50, Y + 50);

            X = 0;
            Y = imgHeight * 2;
            // Draw the flip image at the bottom (half way)
            // Draw the invert of the BufferedImage
            g2d.drawImage(image, X, Y, imgWidth, -imgHeight, this);
            g2d.setColor(Color.blue);
            g2d.drawString("Flip Image", 50, image.getHeight(this) + 50);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("Image Flip");
                f.add(new ImageFlip());
                f.pack();
                f.setVisible(true);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }
}
```

[img=*s16.postimg.org/iqtecgab5/Image_Flip.jpg]

*JComboBox with preview*

A JComboBox, which lets the user choose one of several choices, can have two very different forms. The default form is the uneditable combo box, which features a button and a drop-down list of values. The second form, called the editable combo box, features a text field with a small button abutting it.

Providing a Custom Renderer

A combo box uses a renderer to display each item in its menu. If the combo box is uneditable, it also uses the renderer to display the currently selected item. An editable combo box, on the other hand, uses an editor to display the selected item. A renderer for a combo box must implement the ListCellRenderer interface. A combo box's editor must implement ComboBoxEditor. This section shows how to provide a custom renderer for an uneditable combo box.
The default renderer knows how to render strings and icons. If you put other objects in a combo box, the default renderer calls the toString method to provide a string to display. You can customize the way a combo box renders itself and its items by implementing your own ListCellRenderer.


```
// Inner class ComboBoxRenderer for showing Font preview
    class ComboBoxRenderer extends JLabel implements ListCellRenderer<Object> {

        public ComboBoxRenderer() {
            setOpaque(true);
            setPreferredSize(new Dimension(250, 40));
            setHorizontalAlignment(LEFT);
            setVerticalAlignment(CENTER);
        }

        /*
         * This method finds the Font and text corresponding to the selected value and returns the label,
         *  set up to display the text and Font.
         */
        @Override
        public Component getListCellRendererComponent(
                JList<?> list,
                Object value,
                int index,
                boolean isSelected,
                boolean cellHasFocus) {
            //Get the selected index. (The index param isn't always valid, so just use the value
            int selectedIndex = ((Integer) value).intValue();
            if (isSelected) {
                setBackground(list.getSelectionBackground());
                setForeground(list.getSelectionForeground());
            } else {
                setBackground(list.getBackground());
                setForeground(list.getForeground());
            }
            try {
                setIcon(null);
                String filename = fontName[selectedIndex];
                setText(filename);
                setFont(new Font(fontName[selectedIndex], Font.BOLD, 16));

            } catch (Exception e) {
                // Do nothing
            }
            return this;
        }
    }
```

As a ListCellRenderer, ComboBoxRenderer implements a method called getListCellRendererComponent, which returns a component whose paintComponent method is used to display the combo box and each of its items. The easiest way to display an image and an icon is to use a label. So ComboBoxRenderer is a subclass of label and returns itself. The implementation of getListCellRendererComponent configures the renderer to display the currently selected icon and its description.
Here is the entire code.


```
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Sowndar
 */
public class ListFonts extends JPanel {

    // Get all available font faces names
    private final String[] fontName = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
    private JComboBox<Integer> comboBox;
    private String string = "The quick brown fox jumps over the lazy dog. 1234567890";
    private static JPanel bottom = new JPanel(new FlowLayout(FlowLayout.CENTER));
    private String fontString;
    private final Integer[] intArray;

    public ListFonts() {
        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) {
            // Do nothing
        }
        setBackground(Color.white);
        intArray = new Integer[fontName.length];
        for (int i = 0; i < fontName.length; i++) {
            intArray[i] = new Integer(i);
        }
        comboBox = new JComboBox<>(intArray);
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        setPreferredSize(new Dimension(scrDim.width / 2 + 200, scrDim.height - 150));
        JLabel msg = new JLabel("Select Font : ");
        Font font = msg.getFont();
        String fName = font.getFontName();
        msg.setFont(new Font(fName, Font.BOLD, 18));
        bottom.add(msg);
        // Custom renderer for the ComboBox
        comboBox.setRenderer(new ComboBoxRenderer());
        bottom.add(comboBox);

        comboBox.addItemListener(new ItemListener() {

            @Override
            public void itemStateChanged(ItemEvent e) {
                int index = comboBox.getSelectedIndex();
                fontString = fontName[index];
                repaint();
            }
        });
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        // Set the rendering hints
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST, 100);

        g2d.setStroke(new BasicStroke(3f));
        int X = 10, Y = 30;
        int x = X, y = Y;
        Font font;
        int fontSize = 13;
        for (int i = 0; i < 15; i++) {
            font = new Font(fontString, Font.BOLD, fontSize);
            fontSize += 5;
            g2d.setFont(font);
            g2d.drawString(string, x, y);
            y += 14 + (i * 6);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame("List Fonts");
                f.add(new ListFonts());
                f.add(bottom, BorderLayout.SOUTH);
                f.pack();
                f.setVisible(true);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }

    // Inner class ComboBoxRenderer for showing Font preview
    class ComboBoxRenderer extends JLabel implements ListCellRenderer<Object> {

        public ComboBoxRenderer() {
            setOpaque(true);
            setPreferredSize(new Dimension(250, 40));
            setHorizontalAlignment(LEFT);
            setVerticalAlignment(CENTER);
        }

        /*
         * This method finds the Font and text corresponding to the selected value and returns the label,
         *  set up to display the text and Font.
         */
        @Override
        public Component getListCellRendererComponent(
                JList<?> list,
                Object value,
                int index,
                boolean isSelected,
                boolean cellHasFocus) {
            //Get the selected index. (The index param isn't always valid, so just use the value
            int selectedIndex = ((Integer) value).intValue();
            if (isSelected) {
                setBackground(list.getSelectionBackground());
                setForeground(list.getSelectionForeground());
            } else {
                setBackground(list.getBackground());
                setForeground(list.getForeground());
            }
            try {
                setIcon(null);
                String filename = fontName[selectedIndex];
                setText(filename);
                setFont(new Font(fontName[selectedIndex], Font.BOLD, 16));

            } catch (Exception e) {
                // Do nothing
            }
            return this;
        }
    }
}
```

*s21.postimg.org/vyv16t83n/Combobox_List_Fonts.jpg

The program lists all the fonts of the System OS. It also shows its preview.

The next program draws ellipses of different colors. The method initComponents() initialises the various Components.
The class DrElp renders the ellipses on the screen. You can change the various factors like X Value, Y Value, Start Degree, Scale Factor etc.,
Click on the 'Draw' button to draw the ellipses. While it's still rendering the ellipses, you can select a different color.
The ellipses will be drawn using the color that you selected. Continue changing the colors to get an excellent pattern.
And finally click on the 'Erase' button to erase all the drawing.


```
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.*;

public class ElpGenerator extends JFrame implements ActionListener {

    public ElpGenerator(String fName) {
        super(fName);
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) {
        }
        initComponents();
        setSize(Toolkit.getDefaultToolkit().getScreenSize());
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private void initComponents() {
        // default color is black
        colorInt = 0;
        drawing = false;
        timer = new Timer(30, this);
        timer.setInitialDelay(0);
        timer.setCoalesce(true);

        jPanel1 = new DrElp();
        jTextField1 = new JTextField();
        jTextField2 = new JTextField();
        jTextField3 = new JTextField();
        jTextField4 = new JTextField();
        jTextField5 = new JTextField();
        jTextField6 = new JTextField();
        jTextField7 = new JTextField();
        jTextField8 = new JTextField();
        jButton1 = new JButton();
        jButton1.setMnemonic('D');
        jComboBox1 = new JComboBox<>(colorstrg);
        // Mnemonic for ComboBox ie., cyan is selected here!!
        jComboBox1.selectWithKeyChar('C');
        jLabel1 = new JLabel();
        jLabel2 = new JLabel();
        jLabel3 = new JLabel();
        jLabel4 = new JLabel();
        jLabel5 = new JLabel();
        jLabel6 = new JLabel();
        jLabel7 = new JLabel();
        jLabel8 = new JLabel();
        jLabel9 = new JLabel();
        jButton2 = new JButton();
        jButton2.setMnemonic('E');
        setLayout(new GridBagLayout());
        GridBagConstraints gbc1;

        jPanel1.setLayout(new GridBagLayout());
        jPanel1.setBorder(new BevelBorder(BevelBorder.LOWERED));
        Dimension scrDim = Toolkit.getDefaultToolkit().getScreenSize();
        jPanel1.setPreferredSize(new Dimension(scrDim.width / 4, scrDim.height / 4));

        GridBagConstraints gbc2;

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 0;
        gbc1.gridy = 0;
        gbc1.gridheight = 19;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.ipadx = 530;
        gbc1.ipady = 320;
        gbc1.insets = new Insets(10, 20, 0, 0);
        getContentPane().add(jPanel1, gbc1);

        jTextField1.setFont(new Font("SansSerif", 0, 9));
        jTextField1.setText("265");

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 4;
        gbc1.gridwidth = 5;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.ipadx = 31;
        gbc1.ipady = 3;
        gbc1.insets = new Insets(0, 20, 0, 0);
        getContentPane().add(jTextField1, gbc1);

        jTextField2.setFont(new Font("SansSerif", 0, 9));
        jTextField2.setText("160");

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 4;
        gbc1.gridwidth = 5;
        gbc1.ipadx = 31;
        gbc1.ipady = 3;
        gbc1.insets = new Insets(0, 15, 0, 0);
        getContentPane().add(jTextField2, gbc1);

        jTextField3.setFont(new Font("SansSerif", 0, 9));
        jTextField3.setText("110");

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 8;
        gbc1.gridwidth = 5;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.ipadx = 31;
        gbc1.ipady = 3;
        gbc1.insets = new Insets(4, 20, 0, 0);
        getContentPane().add(jTextField3, gbc1);

        jTextField4.setPreferredSize(new Dimension(24, 17));
        jTextField4.setFont(new Font("SansSerif", 0, 9));
        jTextField4.setText("20");

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 8;
        gbc1.gridwidth = 5;
        gbc1.ipadx = 31;
        gbc1.ipady = 3;
        gbc1.insets = new Insets(0, 15, 0, 0);
        getContentPane().add(jTextField4, gbc1);

        jTextField5.setFont(new Font("SansSerif", 0, 9));
        jTextField5.setText("0");

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 12;
        gbc1.gridwidth = 5;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.ipadx = 40;
        gbc1.insets = new Insets(4, 20, 0, 0);
        getContentPane().add(jTextField5, gbc1);

        jTextField6.setFont(new Font("SansSerif", 0, 9));
        jTextField6.setText("450");

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 12;
        gbc1.gridwidth = 5;
        gbc1.ipadx = 31;
        gbc1.ipady = 3;
        gbc1.insets = new Insets(4, 15, 0, 0);
        getContentPane().add(jTextField6, gbc1);

        jTextField7.setPreferredSize(new Dimension(24, 17));
        jTextField7.setFont(new Font("SansSerif", 0, 9));
        jTextField7.setText("1.01");

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 16;
        gbc1.gridwidth = 5;
        gbc1.ipadx = 31;
        gbc1.ipady = 3;
        gbc1.insets = new Insets(4, 15, 0, 0);
        getContentPane().add(jTextField7, gbc1);

        jTextField8.setFont(new Font("SansSerif", 0, 9));
        jTextField8.setText("1");

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 16;
        gbc1.gridwidth = 5;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.ipadx = 41;
        gbc1.ipady = 3;
        gbc1.insets = new Insets(4, 20, 0, 0);
        getContentPane().add(jTextField8, gbc1);

        jButton1.setPreferredSize(new Dimension(65, 23));
        jButton1.setToolTipText("");
        jButton1.setMaximumSize(new Dimension(65, 23));
        jButton1.setFont(new Font("Dialog", 0, 9));
        jButton1.setText("Draw");
        jButton1.setBackground(Color.lightGray);
        jButton1.setMinimumSize(new Dimension(60, 23));
        jButton1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                //Set the focus to ComboBox
                jComboBox1.requestFocus();
                jButton1ActionPerformed(evt);
            }
        });

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 0;
        gbc1.gridwidth = 9;
        gbc1.ipadx = -1;
        gbc1.ipady = -7;
        gbc1.insets = new Insets(50, 20, 0, 0);
        getContentPane().add(jButton1, gbc1);

        jComboBox1.setFont(new Font("Dialog", 0, 9));
        jComboBox1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                jComboBox1ActionPerformed(evt);
            }
        });

        jLabel9.setText("Color");
        jLabel9.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 9;
        gbc1.gridy = 19;
        gbc1.gridwidth = 2;
        gbc1.gridheight = 2;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.insets = new Insets(10, 0, 0, 0);
        getContentPane().add(jLabel9, gbc1);

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 17;
        gbc1.gridwidth = 13;
        gbc1.gridheight = 2;
        gbc1.fill = GridBagConstraints.HORIZONTAL;
        gbc1.insets = new Insets(20, 20, 0, 0);
        getContentPane().add(jComboBox1, gbc1);

        jLabel1.setText("X Value");
        jLabel1.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 2;
        gbc1.gridwidth = 3;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.insets = new Insets(10, 20, 0, 0);
        getContentPane().add(jLabel1, gbc1);

        jLabel2.setText("Y Value");
        jLabel2.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 2;
        gbc1.gridwidth = 5;
        gbc1.gridheight = 2;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.ipadx = 11;
        gbc1.ipady = 4;
        gbc1.insets = new Insets(10, 15, 0, 0);
        getContentPane().add(jLabel2, gbc1);

        jLabel3.setText("Width");
        jLabel3.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 6;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.insets = new Insets(20, 20, 0, 0);
        getContentPane().add(jLabel3, gbc1);

        jLabel4.setText("Height");
        jLabel4.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 6;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.insets = new Insets(20, 15, 0, 0);
        getContentPane().add(jLabel4, gbc1);

        jLabel5.setText("Start Deg");
        jLabel5.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 10;
        gbc1.gridwidth = 4;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.insets = new Insets(20, 20, 0, 0);
        getContentPane().add(jLabel5, gbc1);

        jLabel6.setText("To Deg");
        jLabel6.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 10;
        gbc1.gridwidth = 2;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.insets = new Insets(20, 15, 0, 0);
        getContentPane().add(jLabel6, gbc1);

        jLabel7.setText("Deg Inc");
        jLabel7.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 1;
        gbc1.gridy = 14;
        gbc1.gridwidth = 2;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.insets = new Insets(20, 20, 0, 0);
        getContentPane().add(jLabel7, gbc1);

        jLabel8.setText("Scale Factor");
        jLabel8.setFont(new Font("Dialog", 0, 9));

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 14;
        gbc1.gridwidth = 4;
        gbc1.fill = GridBagConstraints.BOTH;
        gbc1.insets = new Insets(20, 15, 0, 0);
        getContentPane().add(jLabel8, gbc1);

        jButton2.setToolTipText("Erase Draw Panel");
        jButton2.setFont(new Font("Dialog", 0, 9));
        jButton2.setText("Erase");
        jButton2.setPreferredSize(new Dimension(65, 23));
        jButton2.setBackground(Color.lightGray);
        jButton2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        gbc1 = new GridBagConstraints();
        gbc1.gridx = 11;
        gbc1.gridy = 0;
        gbc1.gridwidth = 5;
        gbc1.ipadx = -1;
        gbc1.ipady = -7;
        gbc1.insets = new Insets(50, 5, 0, 0);
        getContentPane().add(jButton2, gbc1);

    }

    private void jComboBox1ActionPerformed(ActionEvent evt) {
        colorInt = jComboBox1.getSelectedIndex();
        if (g2d != null) {
            g2d.setColor(colorObj[ colorInt]);
        }
    }

    private void jButton2ActionPerformed(ActionEvent evt) {
        // Erase Button Code
        // if g2d is null, no data to erase
        if (g2d != null) {
            drawing = false;
            g2d.dispose();
            g2d = null;
            buffer = null;
            stopAnimation();
            repaint();
        }
    }

    private void jButton1ActionPerformed(ActionEvent evt) {
        // Draw Button Code
        // Input Field Data Validation
        inputError = false;
        try {
            xCent = Integer.parseInt(jTextField1.getText());
            if (xCent < -1000 | xCent > 1000) {
                /* x outside range */
                jTextField1.setText("Too Big");
                inputError = true;
            }
        } catch (NumberFormatException nfe) {
            inputError = true;
            jTextField1.setText("Integer");
        }
        try {
            yCent = Integer.parseInt(jTextField2.getText());
            if (yCent < -1000 | yCent > 1000) {
                /* y outside range */
                jTextField2.setText("Too Big");
                inputError = true;
            }
        } catch (NumberFormatException nfe) {
            inputError = true;
            jTextField2.setText("Integer");
        }
        try {
            w = Integer.parseInt(jTextField3.getText());
            if (w <= 0) {
                /* w must be a postive integer */
                jTextField3.setText("Pos Int");
                inputError = true;
            }
            if (w > 5000) {
                /* w must be within range */
                jTextField3.setText("Too Big");
                inputError = true;
            }
        } catch (NumberFormatException nfe) {
            inputError = true;
            jTextField3.setText("Integer");
        }
        try {
            h = Integer.parseInt(jTextField4.getText());
            if (h <= 0) {
                /* h must be a postive integer */
                jTextField4.setText("Pos Int");
                inputError = true;
            }
            if (h > 5000) {
                /* h must be within range */
                jTextField4.setText("Too Big");
                inputError = true;
            }
        } catch (NumberFormatException nfe) {
            inputError = true;
            jTextField4.setText("Integer");
        }
        try {
            degreeStart = Integer.parseInt(jTextField5.getText());
        } catch (NumberFormatException nfe) {
            inputError = true;
            jTextField5.setText("Integer");
        }
        try {
            degreeRotateTo = Integer.parseInt(jTextField6.getText());
        } catch (NumberFormatException nfe) {
            inputError = true;
            jTextField6.setText("Integer");
        }
        try {
            degreeIncrement = Integer.parseInt(jTextField8.getText());
            if (degreeIncrement <= 0) {
                /* degreeIncrement must be postive integer */
                jTextField8.setText("Pos Int");
                inputError = true;
            }
        } catch (NumberFormatException nfe) {
            inputError = true;
            jTextField8.setText("Integer");
        }
        try {
            scaleFact = Float.parseFloat(jTextField7.getText());
            if (scaleFact <= 0) {
                /* scaleFact must be postive */
                jTextField7.setText("Pos Flt");
                inputError = true;
            }
        } catch (NumberFormatException nfe) {
            inputError = true;
            jTextField7.setText("Float");
        }

        if (!inputError) {
            firstPass = true;
            drawing = true;
            startAnimation();
        }
    }

    private Timer timer;
    private Graphics2D g2d = null;
    private BufferedImage buffer = null;
    private AffineTransform afn;
    private Ellipse2D elp;
    private Shape elpTrans;
    private String setColor;
    private Color colorObj[] = {Color.black, Color.cyan, Color.blue,
        Color.green, Color.magenta, Color.yellow, Color.pink, Color.orange, Color.white};
    private String colorstrg[] = {"Black", "Cyan", "Blue",
        "Green", "Magenta", "Yellow", "Pink", "Orange", "White"};
    private boolean drawing, inputError, firstPass;
    private Insets insets = new Insets(0, 0, 0, 0);
    private int x, y, w, h, degreeStart, degreeIncrement, degreeRotateTo, colorInt;
    private int direction, theta, xCent, yCent;
    private double xFactor, yFactor, scaleFact, wTrans, hTrans, wTemp, hTemp;
    private double xTrans, yTrans, xTemp, yTemp, xTransCon, yTransCon;
    private double scaleFactacc;

    // Variables declaration
    private DrElp jPanel1;
    private JTextField jTextField1;
    private JTextField jTextField2;
    private JTextField jTextField3;
    private JTextField jTextField4;
    private JTextField jTextField5;
    private JTextField jTextField6;
    private JTextField jTextField7;
    private JTextField jTextField8;
    private JButton jButton1;
    private JComboBox<String> jComboBox1;
    private JLabel jLabel1;
    private JLabel jLabel2;
    private JLabel jLabel3;
    private JLabel jLabel4;
    private JLabel jLabel5;
    private JLabel jLabel6;
    private JLabel jLabel7;
    private JLabel jLabel8;
    private JLabel jLabel9;
    private JButton jButton2;

    private class DrElp extends JPanel {

        public void updateBuffer() {

            if (firstPass) {
                if (buffer == null) {
                    Dimension size = getSize();
                    insets = getInsets();
                    size.width = size.width - insets.left - insets.right;
                    size.height = size.height - insets.top - insets.bottom;
                    buffer = (BufferedImage) createImage(size.width, size.height);
                    g2d = buffer.createGraphics();
                    g2d.setColor(getBackground());
                    g2d.fillRect(insets.left, insets.top, size.width, size.height);
                }//End  if ( buffer == null)

                // set anti-alias ON, for smooth lines
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setColor(colorObj[colorInt]);
                afn = new AffineTransform();
                /* calculate upper left corner of rectange defining the ellipse */
                x = xCent - w / 2;
                y = yCent - h / 2;
                elp = new Ellipse2D.Double(x, y, w, h);
                /* calculate translation constants to keep ellipses centered */
                xTransCon = (xCent * scaleFact - xCent) / scaleFact;
                yTransCon = (yCent * scaleFact - yCent) / scaleFact;
                scaleFactacc = scaleFact;

                afn.rotate(-Math.toRadians(degreeStart), xCent, yCent);
                elpTrans = afn.createTransformedShape(elp);
                g2d.draw(elpTrans);
                direction = -1;    /* shape rotates in positive direction */

                if (degreeRotateTo < 0) {
                    direction = 1;  /* shape rotates in negative direction */

                }
                theta = degreeStart;
                firstPass = false;
                drawing = true;
            }//End  if (firstPass)

            /*  LOOP--ROTATE and EXPAND */
            if (drawing) {

                if (degreeRotateTo > 0) {
                    theta += degreeIncrement;
                } else if (degreeRotateTo < 0) {
                    theta -= degreeIncrement;
                } else if (degreeRotateTo == 0 && degreeStart != 0) {
                    theta += degreeIncrement;
                }
                if ((degreeRotateTo > 0) && (theta > degreeRotateTo)) {
                    drawing = false;
                    stopAnimation();
                } else if ((degreeRotateTo < 0) && (theta < degreeRotateTo)) {
                    drawing = false;
                    stopAnimation();
                } else if ((degreeRotateTo == 0) && ((theta > 360) || (theta == 0))) {
                    drawing = false;
                    stopAnimation();
                } else {
                    /* Affine Transforms */
                    afn.scale(scaleFact, scaleFact);
                    afn.translate(-xTransCon, -yTransCon);
                    afn.rotate(Math.toRadians(degreeIncrement) * direction, xCent, yCent);
                    elpTrans = afn.createTransformedShape(elp);
                    g2d.draw(elpTrans);
                    scaleFactacc *= scaleFact;
                    /* if wildly outside viewable area, turn off the loop */
                    if (scaleFactacc > 300.0 || scaleFactacc < .001) {
                        drawing = false;
                        stopAnimation();
                    }
                }
            }//End  if ( drawing )
        }//End  updateBuffer()

        @Override
        public void paintComponent(Graphics g) {
            if (buffer != null) {
                Graphics2D graphics = (Graphics2D) g;
                graphics.drawImage(buffer, insets.left, insets.top, this);
            } else {
                super.paintComponent(g);
            }
        }//End  paintComponent()
    }//End  inner class DrElp

    @Override
    public void actionPerformed(final ActionEvent p1) {
        jPanel1.updateBuffer();
        jPanel1.repaint();
    }

    public void start() {
        startAnimation();
    }

    public void stop() {
        stopAnimation();
    }

    public synchronized void startAnimation() {
        if (!drawing) {
            // do nothing, animation not enabled
        } else if (!timer.isRunning()) {
            timer.start();
        }
    }

    public synchronized void stopAnimation() {
        if (timer.isRunning()) {
            timer.stop();
        }
    }

    public static void main(String[] arg) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ElpGenerator("Ellipse Graphics");
            }
        });
    }

}// End class ElpGenerator
```

*s14.postimg.org/rvjucmv59/Elp_Generator.jpg

*JFileChooser*

File choosers provide a GUI for navigating the file system, and then either choosing a file or directory from a list, or entering the name of a file or directory. To display a file chooser, you usually use the JFileChooser API to show a modal dialog containing the file chooser. Another way to present a file chooser is to add an instance of JFileChooser to a container.

Run the app. Click on the button 'Select with preview' , select a valid Image. You can flip the Image horizontally, vertically, rotate left, rotate right etc.,
You can also apply various filters like blur, edge, sharpen, darken brighten etc., click on the apply filter , to apply the filter.
You can save the Image by clicking on the 'Save' button. You can restore the original image by clicking on the 'Cancel' button.


```
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 * JFileChooserDemo2
 *
 * @author Pavel Porvatov
 */
public class FileChooserDemo2 extends JPanel {

    private enum State {

        EMPTY,
        IMAGE_LOADED,
        IMAGE_CHANGED
    }

    private static final int MIN_FILTER_ID = 0;

    private static final int MAX_FILTER_ID = 7;

    private static final String[] FILTER_NAMES = {
        "blur",
        "edge",
        "sharpen",
        "darken",
        "brighten",
        "less contrast",
        "more contrast",
        "gray"
    };

    private static final BufferedImageOp[] FILTER_OPERATIONS = {
        new ConvolveOp(new Kernel(3, 3,
        new float[]{.1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f, .1111f}),
        ConvolveOp.EDGE_NO_OP, null),
        new ConvolveOp(new Kernel(3, 3,
        new float[]{0.0f, -1.0f, 0.0f, -1.0f, 4.f, -1.0f, 0.0f, -1.0f, 0.0f}),
        ConvolveOp.EDGE_NO_OP, null),
        new ConvolveOp(new Kernel(3, 3,
        new float[]{0.0f, -1.0f, 0.0f, -1.0f, 5.f, -1.0f, 0.0f, -1.0f, 0.0f}),
        ConvolveOp.EDGE_NO_OP, null),
        new RescaleOp(1, -5.0f, null),
        new RescaleOp(1, 5.0f, null),
        new RescaleOp(0.9f, 0, null),
        new RescaleOp(1.1f, 0, null),
        new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null)
    };

    private final JLabel lbImage = new JLabel("Click the Select image button to select an image", JLabel.CENTER);

    private final JScrollPane pnImage = new JScrollPane(lbImage);

    private final JButton btnSelect = new JButton("Select image");

    private final JButton btnSelectWithPreview = new JButton("Select with preview");

    private final JComboBox<FilterItem> cbFilters = new JComboBox<>();

    private final JButton btnApplyFilter = createButton("apply.png", "Apply filter");

    private final JButton btnRotateLeft = createButton("rotateleft.png", "Rotate left");

    private final JButton btnRotateRight = createButton("rotateright.png", "Rotate right");

    private final JButton btnFlipHorizontal = createButton("fliphor.png", "Flip horizontal");

    private final JButton btnFlipVertical = createButton("flipvert.png", "Flip vertical");

    private final JButton btnSave = new JButton("Save");

    private final JButton btnCancel = new JButton("Cancel");

    private final JFileChooser externalChooser = new JFileChooser();

    private final JFileChooser embeddedChooser = new JFileChooser();

    private final JGridPanel pnContent = new JGridPanel(1, 0, 0);

    private State state;

    private boolean fileChoosing;

    private File file;

    private BufferedImage image;

    /**
     * FileChooserDemo2 Constructor
     */
    public FileChooserDemo2() {
        setLayout(new BorderLayout());

        initUI();

        embeddedChooser.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (JFileChooser.APPROVE_SELECTION.equals(e.getActionCommand())) {
                    loadFile(embeddedChooser.getSelectedFile());
                }

                if (JFileChooser.CANCEL_SELECTION.equals(e.getActionCommand())) {
                    setState(state, false);
                }
            }
        });

        btnSelect.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (fileChoosing) {
                    loadFile(embeddedChooser.getSelectedFile());
                } else {
                    setState(state, true);
                }
            }
        });

        btnSelectWithPreview.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (externalChooser.showOpenDialog(FileChooserDemo2.this) == JFileChooser.APPROVE_OPTION) {
                    loadFile(externalChooser.getSelectedFile());
                }
            }
        });

        btnApplyFilter.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doFilter(FILTER_OPERATIONS[((FilterItem) cbFilters.getSelectedItem()).getId()]);
            }
        });

        btnRotateLeft.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doAffineTransform(image.getHeight(), image.getWidth(),
                        new AffineTransform(0, -1, 1, 0, 0, image.getWidth()));
            }
        });

        btnRotateRight.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doAffineTransform(image.getHeight(), image.getWidth(),
                        new AffineTransform(0, 1, -1, 0, image.getHeight(), 0));
            }
        });

        btnFlipHorizontal.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doAffineTransform(image.getWidth(), image.getHeight(),
                        new AffineTransform(-1, 0, 0, 1, image.getWidth(), 0));
            }
        });

        btnFlipVertical.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doAffineTransform(image.getWidth(), image.getHeight(),
                        new AffineTransform(1, 0, 0, -1, 0, image.getHeight()));
            }
        });

        btnSave.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (JOptionPane.showConfirmDialog(FileChooserDemo2.this,
                        "Do you really want to save changes?",
                        "Confirmation",
                        JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
                    return;
                }

                String fileName = file.getName();

                int i = fileName.lastIndexOf('.');

                try {
                    ImageIO.write(image, fileName.substring(i + 1), file);

                    setState(State.IMAGE_LOADED, false);
                } catch (IOException e1) {
                    JOptionPane.showMessageDialog(FileChooserDemo2.this,
                            MessageFormat.format("Cannot save file: {0}", e1),
                            "Error",
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        });

        btnCancel.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                loadFile(file);
            }
        });
    }

    private void initUI() {
        externalChooser.addChoosableFileFilter(new FileNameExtensionFilter("JPEG images", "jpg"));
        externalChooser.addChoosableFileFilter(new FileNameExtensionFilter("All supported images",
                ImageIO.getWriterFormatNames()));

        final FilePreview filePreview = new FilePreview();

        externalChooser.setAccessory(filePreview);
        externalChooser.setPreferredSize(new Dimension(660, 500));
        externalChooser.addPropertyChangeListener(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY,
                new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        filePreview.loadFileInfo(externalChooser.getSelectedFile());
                    }
                });

        embeddedChooser.setControlButtonsAreShown(false);

        embeddedChooser.addChoosableFileFilter(new FileNameExtensionFilter("JPEG images", "jpg"));

        FileNameExtensionFilter filter = new FileNameExtensionFilter("All supported images",
                ImageIO.getWriterFormatNames());

        embeddedChooser.addChoosableFileFilter(filter);
        embeddedChooser.setFileFilter(filter);

        for (int i = MIN_FILTER_ID; i <= MAX_FILTER_ID; i++) {
            cbFilters.addItem(new FilterItem(i, FILTER_NAMES[i]));
        }

        JGridPanel pnFilter = new JGridPanel(2, 0);

        pnFilter.cell(cbFilters).
                cell(btnApplyFilter);

        JGridPanel pnRotateButtons = new JGridPanel(4, 3);

        pnRotateButtons.cell(btnRotateLeft).
                cell(btnRotateRight).
                cell(btnFlipHorizontal).
                cell(btnFlipVertical);

        JGridPanel pnBottom = new JGridPanel(4, 1);

        pnBottom.setHGap(JGridPanel.DEFAULT_GAP * 4);

        pnBottom.cell(btnSelect, JGridPanel.Layout.FILL).
                cell().
                cell(pnFilter).
                cell(btnSave, JGridPanel.Layout.FILL).
                cell(btnSelectWithPreview, JGridPanel.Layout.FILL).
                cell().
                cell(pnRotateButtons).
                cell(btnCancel, JGridPanel.Layout.FILL);

        pnContent.cell(pnImage);
        pnContent.cell(pnBottom, new Insets(10, 10, 10, 10));

        add(pnContent);

        setState(State.EMPTY, false);
    }

    private JButton createButton(String image, String toolTip) {
        JButton res = new JButton(createImageIcon(image, null));

        res.setPreferredSize(new Dimension(26, 26));
        res.setMinimumSize(new Dimension(26, 26));
        res.setToolTipText(toolTip);

        return res;
    }

    public ImageIcon createImageIcon(String filename, String description) {
        String path = "resources/images/filechooser/" + filename;

        URL imageURL = getClass().getResource(path);

        if (imageURL == null) {
            System.err.println("unable to access image file: " + path);
            return null;
        } else {
            return new ImageIcon(imageURL, description);
        }
    }

    private void doAffineTransform(int width, int height, AffineTransform transform) {
        BufferedImage newImage = new BufferedImage(image.getColorModel(),
                image.getRaster().createCompatibleWritableRaster(width, height),
                image.isAlphaPremultiplied(), new Hashtable<>());

        ((Graphics2D) newImage.getGraphics()).drawRenderedImage(image, transform);

        image = newImage;

        lbImage.setIcon(new ImageIcon(image));

        setState(State.IMAGE_CHANGED, false);
    }

    private void doFilter(BufferedImageOp imageOp) {
        BufferedImage newImage = new BufferedImage(image.getColorModel(),
                image.getRaster().createCompatibleWritableRaster(image.getWidth(), image.getHeight()),
                image.isAlphaPremultiplied(), new Hashtable<>());

        imageOp.filter(image, newImage);

        image = newImage;

        lbImage.setIcon(new ImageIcon(image));

        setState(State.IMAGE_CHANGED, false);
    }

    private void loadFile(File file) {
        if (file == null) {
            JOptionPane.showMessageDialog(this,
                    "Select a valid image file please",
                    "Information",
                    JOptionPane.INFORMATION_MESSAGE);

            return;
        }

        try {
            image = ImageIO.read(file);

            if (image != null) {
                lbImage.setText(null);
                lbImage.setIcon(new ImageIcon(image));

                this.file = file;

                setState(State.IMAGE_LOADED, false);

                return;
            }
        } catch (IOException e1) {
            // Do nothing
        }

        JOptionPane.showMessageDialog(this,
                "Cannot load file",
                "Error",
                JOptionPane.ERROR_MESSAGE);
    }

    private void setState(State state, boolean fileChoosing) {
        if (this.fileChoosing != fileChoosing) {
            pnContent.setComponent(fileChoosing ? embeddedChooser : pnImage, 0, 0);
        }

        this.state = state;
        this.fileChoosing = fileChoosing;

        btnSelectWithPreview.setEnabled(!fileChoosing);

        boolean isImageLoaded = !fileChoosing && state != State.EMPTY;

        cbFilters.setEnabled(isImageLoaded);
        btnApplyFilter.setEnabled(isImageLoaded);
        btnRotateRight.setEnabled(isImageLoaded);
        btnRotateLeft.setEnabled(isImageLoaded);
        btnFlipHorizontal.setEnabled(isImageLoaded);
        btnFlipVertical.setEnabled(isImageLoaded);

        boolean isImageChanged = !fileChoosing && state == State.IMAGE_CHANGED;

        btnSave.setEnabled(isImageChanged);
        btnCancel.setEnabled(isImageChanged);
    }

    private static class FilterItem {

        /**
         * 0 - blur 1 - edge 2 - sharpen 3 - darken 4 - brighten 5 - less
         * contrast 6 - more contrast 7 - gray
         */
        private final int id;

        private final String name;

        private FilterItem(int id, String name) {
            assert id >= MIN_FILTER_ID && id <= MAX_FILTER_ID;

            this.id = id;
            this.name = name;
        }

        public int getId() {
            return id;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    private enum FileType {

        IMAGE
    }

    private class FilePreview extends JGridPanel {

        private static final int SIZE = 200;

        private final JLabel lbType = new JLabel();

        private final JLabel lbSize = new JLabel();

        private final JLabel lbPreview = new JLabel("Preview area", JLabel.CENTER);

        private final Map<String, FileType> knownTypes = new HashMap<>();

        public FilePreview() {
            super(1, 0, 1);

            for (String s : ImageIO.getWriterFormatNames()) {
                knownTypes.put(s.toLowerCase(), FileType.IMAGE);
            }

            initUI();
        }

        public void loadFileInfo(File file) {
            boolean emptyPreview = true;

            if (file == null) {
                lbType.setText(null);
                lbSize.setText(null);
            } else {
                lbType.setText(externalChooser.getFileSystemView().getSystemTypeDescription(file));
                lbSize.setText(Long.toString(file.length()));

                String fileName = file.getName();

                int i = fileName.lastIndexOf(".");

                String ext = i < 0 ? null : fileName.substring(i + 1);

                FileType fileType = knownTypes.get(ext.toLowerCase());

                if (fileType != null) {
                    switch (fileType) {
                        case IMAGE:
                            try {
                            BufferedImage image = ImageIO.read(file);

                            double coeff = Math.min(((double) SIZE) / image.getWidth(),
                                    ((double) SIZE) / image.getHeight());

                            BufferedImage scaledImage = new BufferedImage(
                                    (int) Math.round(image.getWidth() * coeff),
                                    (int) Math.round(image.getHeight() * coeff),
                                    BufferedImage.TYPE_INT_RGB);

                            Graphics2D g = (Graphics2D) scaledImage.getGraphics();

                            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                            g.drawImage(image, 0, 0, scaledImage.getWidth(), scaledImage.getHeight(), null);

                            lbPreview.setText(null);
                            lbPreview.setIcon(new ImageIcon(scaledImage));

                            setComponent(lbPreview, 0, 1);

                            emptyPreview = false;
                        } catch (IOException e) {
                            // Empty preview
                        }
                            break;
                    }
                }
            }

            if (emptyPreview) {
                lbPreview.setIcon(null);
                lbPreview.setText("Preview area");

                setComponent(lbPreview, 0, 1);
            }
        }

        private void initUI() {
            setPreferredSize(new Dimension(SIZE, -1));

            setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));

            JGridPanel pnInfo = new JGridPanel(2, 1);

            pnInfo.cell(new JLabel("Type")).
                    cell(lbType).
                    cell(new JLabel("Size")).
                    cell(lbSize);

            cell(pnInfo);
            cell(lbPreview, Layout.FILL, Layout.FILL);
        }
    }

    /**
     * main method allows us to run as a standalone demo.
     *
     * [MENTION=9956]PARAM[/MENTION] args
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame("FileChooserDemo2");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.getContentPane().add(new FileChooserDemo2());
                frame.setPreferredSize(new Dimension(900, 800));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });

    }
}

/**
 * JGridPanel
 *
 * @author Pavel Porvatov
 */
class JGridPanel extends JPanel {

    public static final int DEFAULT_GAP = 5;

    public enum Layout {

        FIRST,
        CENTER,
        LAST,
        FILL
    }

    private enum ComponentType {

        NO_RESIZABLE,
        HORIZONTAL_FILL,
        FULL
    }

    private final int cols;

    private int bigCol = -1;

    private int bigRow = -1;

    private int vGap = -1;

    private int hGap = -1;

    public JGridPanel(int cols) {
        this(cols, -1, -1);
    }

    public JGridPanel(int cols, int bigCol) {
        this(cols, bigCol, -1);
    }

    public JGridPanel(int cols, int bigCol, int bigRow) {
        super(new GridBagLayout());

        assert cols > 0;
        assert bigCol >= -1 && bigCol < cols;
        assert bigRow >= -1;

        this.cols = cols;
        this.bigCol = bigCol;
        this.bigRow = bigRow;
    }

    public void setVGap(int vGap) {
        this.vGap = vGap;
    }

    public void setHGap(int hGap) {
        this.hGap = hGap;
    }

    public JGridPanel cell() {
        return cell(null, getHLayout(null), getVLayout(null), null);
    }

    public JGridPanel cell(Component component) {
        return cell(component, getHLayout(component), getVLayout(component), null);
    }

    public JGridPanel cell(Component component, Layout hLayout) {
        return cell(component, hLayout, getVLayout(component), null);
    }

    public JGridPanel cell(Component component, Layout hLayout, Layout vLayout) {
        return cell(component, hLayout, vLayout, null);
    }

    public JGridPanel cell(Component component, Insets insets) {
        assert insets != null;

        return cell(component, getHLayout(component), getVLayout(component), insets);
    }

    private JGridPanel cell(Component component, Layout hLayout, Layout vLayout, Insets insets) {
        int componentCount = getComponentCount();
        int x = componentCount % cols;
        int y = componentCount / cols;

        int weightx = x == bigCol || (bigCol < 0 && hLayout == Layout.FILL) ? 1 : 0;
        int weighty = y == bigRow || (bigRow < 0 && vLayout == Layout.FILL) ? 1 : 0;

        if (insets == null) {
            int topGap = y == 0 ? 0 : vGap;
            int leftGap = x == 0 ? 0 : hGap;

            insets = new Insets(topGap < 0 ? DEFAULT_GAP : topGap,
                    leftGap < 0 ? DEFAULT_GAP : leftGap, 0, 0);
        }

        add(component == null ? createSeparator() : component,
                new GridBagConstraints(x, y,
                1, 1,
                weightx, weighty,
                getAnchor(hLayout, vLayout), getFill(hLayout, vLayout),
                insets, 0, 0));

        return this;
    }

    public void setComponent(Component component, int col, int row) {
        assert col >= 0 && col < cols;
        assert row >= 0;

        GridBagLayout layout = (GridBagLayout) getLayout();

        for (int i = 0; i < getComponentCount(); i++) {
            Component oldComponent = getComponent(i);

            GridBagConstraints constraints = layout.getConstraints(oldComponent);

            if (constraints.gridx == col && constraints.gridy == row) {
                remove(i);

                add(component == null ? createSeparator() : component, constraints);

                validate();
                repaint();

                return;
            }
        }

        // Cell not found
        assert false;
    }

    private static JComponent createSeparator() {
        return new JLabel();
    }

    private static int getFill(Layout hLayout, Layout vLayout) {
        if (hLayout == Layout.FILL) {
            return vLayout == Layout.FILL ? GridBagConstraints.BOTH : GridBagConstraints.HORIZONTAL;
        }

        return vLayout == Layout.FILL ? GridBagConstraints.VERTICAL : GridBagConstraints.NONE;
    }

    private static ComponentType getComponentType(Component component) {
        if (component == null
                || component instanceof JLabel
                || component instanceof JRadioButton
                || component instanceof JCheckBox
                || component instanceof JButton) {
            return ComponentType.NO_RESIZABLE;
        }

        if (component instanceof JComboBox
                || component instanceof JTextField) {
            return ComponentType.HORIZONTAL_FILL;
        }

        return ComponentType.FULL;
    }

    private static Layout getHLayout(Component component) {
        if (getComponentType(component) == ComponentType.NO_RESIZABLE) {
            return Layout.FIRST;
        } else {
            return Layout.FILL;
        }
    }

    private static Layout getVLayout(Component component) {
        if (getComponentType(component) == ComponentType.FULL) {
            return Layout.FILL;
        } else {
            return Layout.CENTER;
        }
    }

    private static final int[][] ANCHORS = new int[][]{
        {GridBagConstraints.NORTHWEST, GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST},
        {GridBagConstraints.WEST, GridBagConstraints.CENTER, GridBagConstraints.EAST},
        {GridBagConstraints.SOUTHWEST, GridBagConstraints.SOUTH, GridBagConstraints.SOUTHEAST}
    };

    private static int getAnchorIndex(Layout layout) {
        if (layout == Layout.CENTER) {
            return 1;
        } else if (layout == Layout.LAST) {
            return 2;
        } else {
            return 0;
        }
    }

    private static int getAnchor(Layout hLayout, Layout vLayout) {
        return ANCHORS[getAnchorIndex(vLayout)][getAnchorIndex(hLayout)];
    }

    public void setBorderEqual(int border) {
        setBorder(BorderFactory.createEmptyBorder(border, border, border, border));
    }
}
```

*s4.postimg.org/s827k7og9/File_Chooser_Demo2.jpg

*JTable*

With the JTable class you can display tables of data, optionally allowing the user to edit the data. JTable does not contain or cache data; it is simply a view of your data.
The following program shows the list of all the Oscar award movies, categories (Best Song, Best Sound, Best Special Effects, Best Actor, Best Supporting Actor, Best Actress, Best Director etc.,)
You can also search the Titles and Recipients. You can also show only the winners. To do that select the checkbox 'Show Only Winners'
This demo requires a file called 'oscars.xml' that contains list of all the Oscar entries - winners, categories etc.,
You need to copy this file to the location where your class files are stored.


```
/**
 * Created with IntelliJ IDEA.
 * User: Sowndar
 * Date: 10/4/14
 * Time: 9:49 PM
 * To change this template use File | Settings | File Templates.
 */

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;


/**
 *
 * @author Sowndar
 */
/**
 * Table renderer which renders cell value as hyperlink with optional rollover
 * underline.
 *
 * @author Sowndar
 */
class HyperlinkCellRenderer extends JHyperlink implements TableCellRenderer {

    private JTable table;
    private final ArrayList<Integer> columnModelIndeces = new ArrayList<>();

    private Color rowColors[];
    private Color foreground;
    private Color visitedForeground;
    private Border focusBorder;
    private Border noFocusBorder;

    private boolean underlineOnRollover = true;

    private transient int hitColumnIndex = -1;
    private transient int hitRowIndex = -1;

    private HashMap<Object, int[]> visitedCache;

    HyperlinkCellRenderer(Action action, boolean underlineOnRollover) {
        setAction(action);
        setHorizontalAlignment(JHyperlink.LEFT);
        rowColors = new Color[1];
        rowColors[0] = UIManager.getColor("Table.background");
        this.underlineOnRollover = underlineOnRollover;
        applyDefaults();
    }

    public void setRowColors(Color[] colors) {
        this.rowColors = colors;
    }

    public void updateUI() {
        super.updateUI();
        applyDefaults();
    }

    protected void applyDefaults() {
        setOpaque(true);
        setBorderPainted(false);
        foreground = UIManager.getColor("Hyperlink.foreground");
        visitedForeground = UIManager.getColor("Hyperlink.visitedForeground");

        // Make sure border used on non-focussed cells is same size as focussed border
        focusBorder = UIManager.getBorder("Table.focusCellHighlightBorder");
        if (focusBorder != null) {
            Insets insets = focusBorder.getBorderInsets(this);
            noFocusBorder = new EmptyBorder(insets.top, insets.left, insets.bottom, insets.right);
        } else {
            focusBorder = noFocusBorder = new EmptyBorder(1, 1, 1, 1);
        }
    }

    public Component getTableCellRendererComponent(JTable table, Object value,
                                                   boolean isSelected, boolean hasFocus, int row, int column) {
        if (this.table == null) {
            this.table = table;
            HyperlinkCellRenderer.HyperlinkMouseListener hyperlinkListener = new HyperlinkCellRenderer.HyperlinkMouseListener();
            table.addMouseMotionListener(hyperlinkListener);
            table.addMouseListener(hyperlinkListener);
        }
        int columnModelIndex = table.getColumnModel().getColumn(column).getModelIndex();
        if (!columnModelIndeces.contains(columnModelIndex)) {
            columnModelIndeces.add(columnModelIndex);
        }

        if (value instanceof Link) {
            Link link = (Link) value;
            setText(link.getDisplayText());
            setToolTipText(link.getDescription());
        } else {
            setText(value != null ? value.toString() : "");
        }
        setVisited(isCellLinkVisited(value, row, column));
        setDrawUnderline(!underlineOnRollover
                || (row == hitRowIndex && column == hitColumnIndex));

        if (!isSelected) {
            setBackground(rowColors[row % rowColors.length]);
            //setForeground(isCellLinkVisited(value, row, column)?
            //  visitedForeground : foreground);
            setForeground(foreground);
            setVisitedForeground(visitedForeground);
        } else {
            setBackground(table.getSelectionBackground());
            setForeground(table.getSelectionForeground());
            setVisitedForeground(table.getSelectionForeground());
        }
        //setBorder(hasFocus? focusBorder : noFocusBorder);
        //System.out.println("border insets="+getBorder().getBorderInsets(this));

        return this;
    }

    protected void setCellLinkVisited(Object value, int row, int column) {
        if (!isCellLinkVisited(value, row, column)) {
            if (value instanceof Link) {
                ((Link) value).setVisited(true);
            } else {
                if (visitedCache == null) {
                    visitedCache = new HashMap<Object, int[]>();
                }
                int position[] = new int[2];
                position[0] = table.convertRowIndexToModel(row);
                position[1] = table.convertColumnIndexToModel(column);
                visitedCache.put(value, position);
            }
        }
    }

    protected boolean isCellLinkVisited(Object value, int row, int column) {
        if (value instanceof Link) {
            return ((Link) value).isVisited();
        }
        if (visitedCache != null) {
            int position[] = visitedCache.get(value);
            if (position != null) {
                return position[0] == table.convertRowIndexToModel(row)
                        && position[1] == table.convertColumnIndexToModel(column);
            }
        }
        return false;
    }

    public int getActiveHyperlinkRow() {
        return hitRowIndex;
    }

    public int getActiveHyperlinkColumn() {
        return hitColumnIndex;
    }

    // overridden because the AbstractButton's version forces the source of the event
    // to be the AbstractButton and we want a little more freedom to configure the
    // event
    @Override
    protected void fireActionPerformed(ActionEvent event) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();

        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ActionListener.class) {
                ((ActionListener) listeners[i + 1]).actionPerformed(event);
            }
        }
    }

    public void invalidate() {
    }

    public void validate() {
    }

    public void revalidate() {
    }

    public void repaint(long tm, int x, int y, int width, int height) {
    }

    public void repaint(Rectangle r) {
    }

    public void repaint() {
    }

    private class HyperlinkMouseListener extends MouseAdapter {

        private transient Rectangle cellRect;
        private final transient Rectangle iconRect = new Rectangle();
        private final transient Rectangle textRect = new Rectangle();
        private transient Cursor tableCursor;

        @Override
        public void mouseMoved(MouseEvent event) {
            // This should only be called if underlineOnRollover is true
            JTable table = (JTable) event.getSource();

            // Locate the table cell under the event location
            int oldHitColumnIndex = hitColumnIndex;
            int oldHitRowIndex = hitRowIndex;

            checkIfPointInsideHyperlink(event.getPoint());

            if (hitRowIndex != oldHitRowIndex
                    || hitColumnIndex != oldHitColumnIndex) {
                if (hitRowIndex != -1) {
                    if (tableCursor == null) {
                        tableCursor = table.getCursor();
                    }
                    table.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                } else {
                    table.setCursor(tableCursor);
                }

                // repaint the cells affected by rollover
                Rectangle repaintRect;
                if (hitRowIndex != -1 && hitColumnIndex != -1) {
                    // we need to repaint new cell with rollover underline
                    // cellRect already contains rect of hit cell
                    if (oldHitRowIndex != -1 && oldHitColumnIndex != -1) {
                        // we also need to repaint previously underlined hyperlink cell
                        // to remove the underline
                        repaintRect = cellRect.union(
                                table.getCellRect(oldHitRowIndex, oldHitColumnIndex, false));
                    } else {
                        // we don't have a previously underlined hyperlink, so just repaint new one'
                        repaintRect = table.getCellRect(hitRowIndex, hitColumnIndex, false);
                    }
                } else {
                    // we just need to repaint previously underlined hyperlink cell
                    //to remove the underline
                    repaintRect = table.getCellRect(oldHitRowIndex, oldHitColumnIndex, false);
                }
                table.repaint(repaintRect);
            }

        }

        @Override
        public void mouseClicked(MouseEvent event) {
            if (checkIfPointInsideHyperlink(event.getPoint())) {

                ActionEvent actionEvent = new ActionEvent(new Integer(hitRowIndex),
                        ActionEvent.ACTION_PERFORMED,
                        "hyperlink");

                HyperlinkCellRenderer.this.fireActionPerformed(actionEvent);

                setCellLinkVisited(table.getValueAt(hitRowIndex, hitColumnIndex),
                        hitRowIndex, hitColumnIndex);

            }
        }

        protected boolean checkIfPointInsideHyperlink(Point p) {
            hitColumnIndex = table.columnAtPoint(p);
            hitRowIndex = table.rowAtPoint(p);

            if (hitColumnIndex != -1 && hitRowIndex != -1
                    && columnModelIndeces.contains(table.getColumnModel().
                    getColumn(hitColumnIndex).getModelIndex())) {
                // We know point is within a hyperlink column, however we do further hit testing
                // to see if point is within the text bounds on the hyperlink
                TableCellRenderer renderer = table.getCellRenderer(hitRowIndex, hitColumnIndex);
                JHyperlink hyperlink = (JHyperlink) table.prepareRenderer(renderer, hitRowIndex, hitColumnIndex);

                // Convert the event to the renderer's coordinate system
                cellRect = table.getCellRect(hitRowIndex, hitColumnIndex, false);
                hyperlink.setSize(cellRect.width, cellRect.height);
                p.translate(-cellRect.x, -cellRect.y);
                cellRect.x = cellRect.y = 0;
                iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
                textRect.x = textRect.y = textRect.width = textRect.height = 0;
                SwingUtilities.layoutCompoundLabel(
                        hyperlink.getFontMetrics(hyperlink.getFont()),
                        hyperlink.getText(), hyperlink.getIcon(),
                        hyperlink.getVerticalAlignment(),
                        hyperlink.getHorizontalAlignment(),
                        hyperlink.getVerticalTextPosition(),
                        hyperlink.getHorizontalTextPosition(),
                        cellRect, iconRect, textRect, hyperlink.getIconTextGap());

                if (textRect.contains(p)) {
                    // point is within hyperlink text bounds
                    return true;
                }
            }
            // point is not within a hyperlink's text bounds
            hitRowIndex = -1;
            hitColumnIndex = -1;
            return false;
        }
    }
}

/**
 * Class used to support converting a movie title string into an IMDB URI
 * corresponding to that movie's IMDB entry. Since IMDB encodes entries with an
 * alpha-numeric key (rather than title), we have to use Yahoo search on the
 * title and then screenscrape the search results to find the IMDB key.
 *
 * @author aim
 */
class IMDBLink {

    private IMDBLink() {
    }

    /**
     * [MENTION=9956]PARAM[/MENTION] movieTitle the title of the movie
     * [MENTION=9956]PARAM[/MENTION] year the year the movie was nominated for the oscar
     * @return String containing URI for movie's IMDB entry or null if URI could
     * not be found
     */
    public static String getMovieURIString(String movieTitle, int year) throws IOException {
        ArrayList<String> matches = new ArrayList<String>();
        URL url;
        BufferedReader reader;

        // btw, google rejects the request with a 403 return code!
        // URL url = new URL("*www.google.com/search?q=Dazed+and+confused");
        // Thank you, yahoo, for granting our search request :-)
        try {
            String urlKey = URLEncoder.encode(movieTitle, "UTF-8");
            url = new URL("*search.yahoo.com/search?ei=utf-8&fr=sfp&p=imdb+"
                    + urlKey + "&iscqry=");
        } catch (UnsupportedEncodingException | MalformedURLException ex) {
            System.err.println(ex);

            return null;
        }

        URLConnection conn = url.openConnection();
        conn.connect();

        // Get the response from Yahoo search query
        reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

        // Parse response a find each imdb/titleString result
        String line;
        String imdbString = ".imdb.com";
        String titleStrings[] = {"/title", "/Title"};

        while ((line = reader.readLine()) != null) {
            for (String titleString : titleStrings) {
                String scrapeKey = imdbString + titleString;
                int index = line.indexOf(scrapeKey);
                if (index != -1) {
                    // The IMDB key looks something like "tt0032138"
                    // so we look for the 9 characters after the scrape key
                    // to construct the full IMDB URI.
                    // e.g. [url]*www.imdb.com/title/tt0032138[/url]
                    int len = scrapeKey.length();
                    String imdbURL = "*www"
                            + line.substring(index, index + len)
                            + line.substring(index + len, index + len + 10);

                    if (!matches.contains(imdbURL)) {
                        matches.add(imdbURL);
                    }
                }
            }
        }
        reader.close();

        // Since imdb contains entries for multiple movies of the same titleString,
        // use the year to find the right entry
        if (matches.size() > 1) {
            for (String matchURL : matches) {
                if (verifyYear(matchURL, year)) {
                    return matchURL;
                }
            }
        }
        return matches.isEmpty() ? null : matches.get(0);
    }

    private static boolean verifyYear(String imdbURL, int movieYear) throws IOException {
        boolean yearMatches = false;

        URLConnection conn = new URL(imdbURL).openConnection();
        conn.connect();

        // Get the response
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

        String line;
        while ((line = reader.readLine()) != null) {
            int index = line.indexOf("</title>");
            if (index != -1) {
                // looking for "<title>movie title (YEAR)</title>"
                try {
                    int year = Integer.parseInt(line.substring(index - 5, index - 1));
                    // Movie may have been made the year prior to oscar award
                    yearMatches = year == movieYear || year == movieYear - 1;

                } catch (NumberFormatException ex) {
                    // ignore title lines that have other formatting
                }
                break; // only interested in analyzing the one line
            }
        }
        reader.close();

        return yearMatches;
    }
}

/**
 * A simple JPanel extension that adds translucency support. This component and
 * all of its content will be displayed with the specified &quot;alpha&quot;
 * transluscency property value. It also supports the Painters using the
 * backgroundPainter property. For example, to change the background of the
 * panel to a checkeboard do something like this:
 *
 *
 * <PRE>
 * <CODE>JXPanel panel = new JXPanel();
 * panel.setBackgroundPainter(new CheckerboardPainter());</CODE></PRE>
 *
 * @author rbair
 *
 * Note: This has been imported directly into the SwingSet3 source in order to
 * make a critical bugfix which was needed for SwingSet3.
 */
class JXPanel extends JPanel implements Scrollable {

    private boolean scrollableTracksViewportHeight;
    private boolean scrollableTracksViewportWidth;

    /**
     * The alpha level for this component.
     */
    private float alpha = 1.0f;
    /**
     * If the old alpha value was 1.0, I keep track of the opaque setting
     * because a translucent component is not opaque, but I want to be able to
     * restore opacity to its default setting if the alpha is 1.0. Honestly, I
     * don't know if this is necessary or not, but it sounded good on paper :)
     * <p>
     * TODO: Check whether this variable is necessary or not</p>
     */
    private boolean oldOpaque;

    /**
     * Creates a new instance of JXPanel
     */
    public JXPanel() {
    }

    /**
     * [MENTION=9956]PARAM[/MENTION] isDoubleBuffered
     */
    public JXPanel(boolean isDoubleBuffered) {
        super(isDoubleBuffered);
    }

    /**
     * [MENTION=9956]PARAM[/MENTION] layout
     */
    public JXPanel(LayoutManager layout) {
        super(layout);
    }

    /**
     * [MENTION=9956]PARAM[/MENTION] layout
     * [MENTION=9956]PARAM[/MENTION] isDoubleBuffered
     */
    public JXPanel(LayoutManager layout, boolean isDoubleBuffered) {
        super(layout, isDoubleBuffered);
    }

    /**
     * Set the alpha transparency level for this component. This automatically
     * causes a repaint of the component.
     *
     * [MENTION=9956]PARAM[/MENTION] alpha must be a value between 0 and 1 inclusive.
     */
    public void setAlpha(float alpha) {
        if (alpha < 0 || alpha > 1) {
            throw new IllegalArgumentException("Alpha must be between 0 and 1 inclusive");
        }

        if (this.alpha != alpha) {
            float oldAlpha = this.alpha;
            this.alpha = alpha;
            if (alpha > 0f && alpha < 1f) {
                if (oldAlpha == 1) {
                    //it used to be 1, but now is not. Save the oldOpaque
                    oldOpaque = isOpaque();
                    setOpaque(false);
                }

                //TODO this was quite the controversial choice, in automatically
                //replacing the repaint manager. In retrospect, I'd probably
                //opt for making this a manual choice. There really isn't a clear
                //win, no matter the approach.
                RepaintManager manager = RepaintManager.currentManager(this);
                /*
                 if (!manager.getClass().isAnnotationPresent(TranslucentRepaintManager.class)) {
                 RepaintManager.setCurrentManager(new RepaintManagerX());
                 }*/
            } else if (alpha == 1) {
                //restore the oldOpaque if it was true (since opaque is false now)
                if (oldOpaque) {
                    setOpaque(true);
                }
            }
            firePropertyChange("alpha", oldAlpha, alpha);
            repaint();
        }
    }

    /**
     * @return the alpha translucency level for this component. This will be a
     * value between 0 and 1, inclusive.
     */
    public float getAlpha() {
        return alpha;
    }

    /* (non-Javadoc)
     * [MENTION=288550]see[/MENTION] javax.swing.Scrollable#getScrollableTracksViewportHeight()
     */
    @Override
    public boolean getScrollableTracksViewportHeight() {
        return scrollableTracksViewportHeight;
    }

    /* (non-Javadoc)
     * [MENTION=288550]see[/MENTION] javax.swing.Scrollable#getScrollableTracksViewportWidth()
     */
    @Override
    public boolean getScrollableTracksViewportWidth() {
        return scrollableTracksViewportWidth;
    }

    /* (non-Javadoc)
     * [MENTION=288550]see[/MENTION] javax.swing.Scrollable#getPreferredScrollableViewportSize()
     */
    @Override
    public Dimension getPreferredScrollableViewportSize() {
        return getPreferredSize();
    }

    /* (non-Javadoc)
     * [MENTION=288550]see[/MENTION] javax.swing.Scrollable#getScrollableBlockIncrement(java.awt.Rectangle, int, int)
     */
    @Override
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        return 10;
    }

    /* (non-Javadoc)
     * [MENTION=288550]see[/MENTION] javax.swing.Scrollable#getScrollableUnitIncrement(java.awt.Rectangle, int, int)
     */
    @Override
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        return 10;
    }

    /**
     * [MENTION=9956]PARAM[/MENTION] scrollableTracksViewportHeight The scrollableTracksViewportHeight
     * to set.
     */
    public void setScrollableTracksViewportHeight(boolean scrollableTracksViewportHeight) {
        this.scrollableTracksViewportHeight = scrollableTracksViewportHeight;
    }

    /**
     * [MENTION=9956]PARAM[/MENTION] scrollableTracksViewportWidth The scrollableTracksViewportWidth to
     * set.
     */
    public void setScrollableTracksViewportWidth(boolean scrollableTracksViewportWidth) {
        this.scrollableTracksViewportWidth = scrollableTracksViewportWidth;
    }

    /**
     * Overridden paint method to take into account the alpha setting
     *
     * [MENTION=9956]PARAM[/MENTION] g
     */
    @Override
    public void paint(Graphics g) {
        float a = getAlpha();
        if (a == 1) {
            super.paint(g);
        } else {
            //the component is translucent, so we need to render to
            Graphics2D g2d = (Graphics2D) g;
            Composite oldComp = g2d.getComposite();
            Composite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a);
            g2d.setComposite(alphaComp);
            //g2d.drawImage(img, null, 0, 0);
            g2d.setComposite(oldComp);
        }
    }
}

/**
 * Class representing the state of a hyperlink This class may be used in
 * conjunction with HyperlinkCellRenderer, but it is not required.
 *
 * @author aim
 */
class Link {

    protected String displayText;
    private URI uri;
    private String description;
    private boolean visited;

    /**
     * Creates a new instance of Link
     */
    Link(String text) {
        setDisplayText(text);
    }

    public Link(String text, URI uri) {
        this(text);
        setUri(uri);
    }

    public String getDisplayText() {
        return displayText;
    }

    public void setDisplayText(String text) {
        this.displayText = text;
    }

    public URI getUri() {
        return uri;
    }

    public void setUri(URI uri) {
        this.uri = uri;
    }

    public String getDescription() {
        return description != null ? description
                : uri != null ? uri.getPath() : null;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean isVisited() {
        return visited;
    }

    public void setVisited(boolean visited) {
        this.visited = visited;
    }

}


class OscarCandidate {

    private String category;
    private Integer year;
    private boolean winner = false;
    private String movie;
    private URI imdbURI;
    private final ArrayList<String> persons = new ArrayList<>();

    /**
     * Creates a new instance of OscarCandidate
     */
    OscarCandidate(String category) {
        this.category = category;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public Integer getYear() {
        return year;
    }

    public void setYear(Integer year) {
        this.year = year;
    }

    public boolean isWinner() {
        return winner;
    }

    public void setWinner(boolean winner) {
        this.winner = winner;
    }

    public String getMovieTitle() {
        return movie;
    }

    public void setMovieTitle(String movie) {
        this.movie = movie;
    }

    public URI getIMDBMovieURI() {
        return imdbURI;
    }

    public void setIMDBMovieURI(URI uri) {
        this.imdbURI = uri;
    }

    public List<String> getPersons() {
        return persons;
    }
}

abstract class OscarDataParser extends DefaultHandler {

    private static final String[] CATEGORIES_IN = {
            "actor", "actress", "bestPicture",
            "actorSupporting", "actressSupporting", "artDirection",
            "assistantDirector", "director", "cinematography",
            "costumeDesign", "danceDirection", "docFeature",
            "docShort", "filmEditing", "foreignFilm",
            "makeup", "musicScore", "musicSong",
            "screenplayAdapted", "screenplayOriginal", "shortAnimation",
            "shortLiveAction", "sound", "soundEditing",
            "specialEffects", "visualEffects", "writing",
            "engEffects", "uniqueArtisticPicture"
    };

    private static final String[] CATEGORIES_OUT = {
            "Best Actor", "Best Actress", "Best Picture",
            "Best Supporting Actor", "Best Supporting Actress", "Best Art Direction",
            "Best Assistant Director", "Best Director", "Best Cinematography",
            "Best Costume Design", "Best Dance Direction", "Best Feature Documentary",
            "Best Short Documentary", "Best Film Editing", "Best Foreign Film",
            "Best Makeup", "Best Musical Score", "Best Song",
            "Best Adapted Screenplay", "Best Original Screenplay", "Best Animation Short",
            "Best Live Action Short", "Best Sound", "Best Sound Editing",
            "Best Special Effects", "Best Visual Effects", "Best Engineering Effects",
            "Best Writing", "Most Unique Artistic Picture"
    };

    private String tempVal;

    //to maintain context
    private OscarCandidate tempOscarCandidate;

    private int count = 0;

    public int getCount() {
        return count;
    }

    public void parseDocument(URL oscarURL) {
        //get a factory
        SAXParserFactory spf = SAXParserFactory.newInstance();

        try {
            //get a new instance of parser
            SAXParser sp = spf.newSAXParser();

            //parse the file and also register this class for call backs
            InputStream is = new BufferedInputStream(oscarURL.openStream());
            sp.parse(is, this);
            System.out.println("done parsing count=" + count);
            is.close();

        } catch (SAXException | ParserConfigurationException | IOException se) {
        }
    }

    //Event Handlers
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        //reset
        tempVal = "";
        for (int i = 0; i < CATEGORIES_IN.length; i++) {
            if (qName.equalsIgnoreCase(CATEGORIES_IN[i])) {
                tempOscarCandidate = new OscarCandidate(CATEGORIES_OUT[i]);
                tempOscarCandidate.setYear(Integer.parseInt(attributes.getValue("year")));
                if (CATEGORIES_IN[i].equals("screenplayOriginal")
                        && tempOscarCandidate.getYear() == 2007) {
                }
                return;
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        tempVal = new String(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if (qName.equalsIgnoreCase("won")) {
            tempOscarCandidate.setWinner(true);
        } else if (qName.equalsIgnoreCase("lost")) {
            tempOscarCandidate.setWinner(false);
        } else if (qName.equalsIgnoreCase("movie")) {
            tempOscarCandidate.setMovieTitle(tempVal);
        } else if (qName.equalsIgnoreCase("person")) {
            tempOscarCandidate.getPersons().add(tempVal);
        } else {
            // find category
            for (String category : CATEGORIES_IN) {
                if (qName.equalsIgnoreCase(category)) {
                    //add it to the list
                    count++;
                    addCandidate(tempOscarCandidate);
                    break;
                }
            }
        }
    }

    @Override
    public void error(SAXParseException ex) throws SAXException {
        OscarTableDemo.logger.log(Level.SEVERE, "error parsing oscar data ", ex);
    }

    @Override
    public void fatalError(SAXParseException ex) throws SAXException {
        OscarTableDemo.logger.log(Level.SEVERE, "fatal error parsing oscar data ", ex);
    }

    @Override
    public void warning(SAXParseException ex) {
        OscarTableDemo.logger.log(Level.WARNING, "warning occurred while parsing oscar data ", ex);
    }

    @Override
    public void endDocument() throws SAXException {
        OscarTableDemo.logger.log(Level.FINER, "parsed to end of oscar data.");
    }

    protected abstract void addCandidate(OscarCandidate candidate);
}

/**
 * Container class which extends JLayeredPane to ensure its sizing tracks its
 * "master" child component and supported fading a message component in/out of
 * the top layer
 *
 * @author Amy Fowler
 */
class Stacker extends JLayeredPane {

    private Component master; // dictates sizing, scrolling
    private JPanel messageLayer;
    private JXPanel messageAlpha;

    Stacker(Component master) {
        this.master = master;
        setLayout(null);
        add(master, JLayeredPane.DEFAULT_LAYER);
    }

    @Override
    public Dimension getPreferredSize() {
        return master.getPreferredSize();
    }

    @Override
    public void doLayout() {
        // ensure all layers are sized the same
        Dimension size = getSize();
        Component layers[] = getComponents();
        for (Component layer : layers) {
            layer.setBounds(0, 0, size.width, size.height);
        }
    }

    /**
     * Fades in the specified message component in the top layer of this layered
     * pane.
     *
     * [MENTION=9956]PARAM[/MENTION] message the component to be displayed in the message layer
     * [MENTION=9956]PARAM[/MENTION] finalAlpha the alpha value of the component when fade in is
     * complete
     */
    public void showMessageLayer(JComponent message, final float finalAlpha) {
        messageLayer = new JPanel();
        messageLayer.setOpaque(false);
        GridBagLayout gridbag = new GridBagLayout();
        messageLayer.setLayout(gridbag);
        GridBagConstraints c = new GridBagConstraints();
        c.anchor = GridBagConstraints.CENTER;

        messageAlpha = new JXPanel();
        messageAlpha.setOpaque(false);
        messageAlpha.setAlpha(0.0f);
        gridbag.addLayoutComponent(messageAlpha, c);
        messageLayer.add(messageAlpha);
        messageAlpha.add(message);

        add(messageLayer, JLayeredPane.POPUP_LAYER);
        revalidate();

    }

    /**
     * Fades out and removes the current message component
     */
    public void hideMessageLayer() {
    }
}

/**
 * Data model for oscar candidate data: a list of OscarCandidate beans.
 *
 * @author aim
 */

class OscarTableModel extends AbstractTableModel {

    public static final int CATEGORY_COLUMN = 0;
    public static final int YEAR_COLUMN = 1;
    public static final int WINNER_COLUMN = 2;
    public static final int MOVIE_COLUMN = 3;
    public static final int PERSONS_COLUMN = 4;
    public static final int COLUMN_COUNT = 5;

    private final List<OscarCandidate> candidates = new ArrayList<>();

    public void add(List<OscarCandidate> newCandidates) {
        int first = candidates.size();
        int last = first + newCandidates.size() - 1;
        candidates.addAll(newCandidates);
        fireTableRowsInserted(first, last);
    }

    public void add(OscarCandidate candidate) {
        int index = candidates.size();
        candidates.add(candidate);
        fireTableRowsInserted(index, index);
    }

    @Override
    public int getRowCount() {
        return candidates.size();
    }

    @Override
    public int getColumnCount() {
        return COLUMN_COUNT;
    }

    @Override
    public Class getColumnClass(int column) {
        return getValueAt(0, column).getClass();
    }

    public OscarCandidate getCandidate(int row) {
        return candidates.get(row);
    }

    @Override
    public Object getValueAt(int row, int column) {
        OscarCandidate oscarCandidate = candidates.get(row);
        switch (column) {
            case CATEGORY_COLUMN:
                return oscarCandidate.getCategory();
            case YEAR_COLUMN:
                return oscarCandidate.getYear();
            case MOVIE_COLUMN:
                return oscarCandidate.getMovieTitle();
            case WINNER_COLUMN:
                return oscarCandidate.isWinner() ? Boolean.TRUE : Boolean.FALSE;
            case PERSONS_COLUMN:
                return oscarCandidate.getPersons();
        }
        return null;
    }
}

public class OscarTableDemo extends JPanel {

    static final Logger logger = Logger.getLogger(OscarTableDemo.class.getName());
    private OscarTableModel oscarModel;
    private JPanel controlPanel;
    private Stacker dataPanel;
    private JTable oscarTable;
    private JCheckBox winnersCheckbox;
    private JTextField filterField;
    private Box statusBarLeft;
    private JLabel actionStatus;
    private JLabel tableStatus;
    private Color[] rowColors;
    private String statusLabelString;
    private String searchLabelString;
    private boolean showOnlyWinners = false;
    private String filterString = null;
    private TableRowSorter<OscarTableModel> sorter;
    private RowFilter<OscarTableModel, Integer> winnerFilter;
    private RowFilter<OscarTableModel, Integer> searchFilter;

    public OscarTableDemo() {
        try {
            for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException e) {
            // Do nothing
        }
        initModel();
        initComponents();
        initSortingFiltering();
    }

    protected void initModel() {
        oscarModel = new OscarTableModel();
    }

    protected void initComponents() {
        setLayout(new BorderLayout());

        controlPanel = createControlPanel();
        add(controlPanel, BorderLayout.NORTH);

        //<snip>Create JTable
        oscarTable = new JTable(oscarModel);
        //</snip>

        //</snip>Set JTable display properties
        oscarTable.setColumnModel(createColumnModel());
        oscarTable.setAutoCreateRowSorter(true);
        oscarTable.setRowHeight(26);
        oscarTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
        oscarTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        oscarTable.setIntercellSpacing(new Dimension(0, 0));
        //</snip>

        //<snip>Initialize preferred size for table's viewable area
        Dimension viewSize = new Dimension();
        viewSize.width = oscarTable.getColumnModel().getTotalColumnWidth();
        viewSize.height = 10 * oscarTable.getRowHeight();
        oscarTable.setPreferredScrollableViewportSize(viewSize);
        //</snip>

        //<snip>Customize height and alignment of table header
        JTableHeader header = oscarTable.getTableHeader();
        header.setPreferredSize(new Dimension(30, 26));
        TableCellRenderer headerRenderer = header.getDefaultRenderer();
        if (headerRenderer instanceof JLabel) {
            ((JLabel) headerRenderer).setHorizontalAlignment(JLabel.CENTER);
        }
        //</snip>

        JScrollPane scrollpane = new JScrollPane(oscarTable);
        dataPanel = new Stacker(scrollpane);
        add(dataPanel, BorderLayout.CENTER);

        add(createStatusBar(), BorderLayout.SOUTH);

    }

    protected JPanel createControlPanel() {
        JPanel controlPanel = new JPanel();
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        controlPanel.setLayout(gridbag);

        c.gridx = 0;
        c.gridy = 1;
        c.gridheight = 1;
        c.insets = new Insets(20, 10, 0, 10);
        c.anchor = GridBagConstraints.SOUTHWEST;

        JLabel searchLabel = new JLabel("Search Titles and Recipients");
        controlPanel.add(searchLabel, c);

        c.gridx = 0;
        c.gridy = 2;
        c.weightx = 1.0;
        c.insets.top = 0;
        c.insets.bottom = 12;
        c.anchor = GridBagConstraints.SOUTHWEST;

        filterField = new JTextField(24);
        filterField.getDocument().addDocumentListener(new SearchFilterListener());
        controlPanel.add(filterField, c);

        c.gridx = 1;
        c.gridy = 2;
        c.gridwidth = GridBagConstraints.REMAINDER;
        //c.insets.right = 24;
        //c.insets.left = 12;
        c.weightx = 0.0;
        c.anchor = GridBagConstraints.EAST;
        c.fill = GridBagConstraints.NONE;
        winnersCheckbox = new JCheckBox("Show Only Winners");
        winnersCheckbox.addChangeListener(new ShowWinnersListener());
        controlPanel.add(winnersCheckbox, c);

        return controlPanel;
    }

    protected Container createStatusBar() {
        statusLabelString = "Showing ";
        searchLabelString = "Search found ";

        Box statusBar = Box.createHorizontalBox();

        // Left status area
        statusBar.add(Box.createRigidArea(new Dimension(10, 22)));
        statusBarLeft = Box.createHorizontalBox();
        statusBar.add(statusBarLeft);
        actionStatus = new JLabel("No data loaded");
        actionStatus.setHorizontalAlignment(JLabel.LEADING);
        statusBarLeft.add(actionStatus);

        // Middle (should stretch)
        statusBar.add(Box.createHorizontalGlue());
        statusBar.add(Box.createHorizontalGlue());
        statusBar.add(Box.createVerticalGlue());

        // Right status area
        tableStatus = new JLabel(statusLabelString + "0");
        statusBar.add(tableStatus);
        statusBar.add(Box.createHorizontalStrut(12));

        //<snip>Track number of rows currently displayed
        oscarModel.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                // Get rowCount from *table*, not model, as the view row count
                // may be different from the model row count due to filtering
                tableStatus.setText((hasFilterString() ? searchLabelString : statusLabelString)
                        + oscarTable.getRowCount());
            }
        });
        //</snip>

        return statusBar;
    }

    private Color[] getTableRowColors() {
        if (rowColors == null) {
            rowColors = new Color[2];
            rowColors[0] = UIManager.getColor("Table.background");
            rowColors[1] = new Color((int) (rowColors[0].getRed() * .9),
                    (int) (rowColors[0].getGreen() * .9),
                    (int) (rowColors[0].getBlue() * .9));
        }
        return rowColors;
    }

    public void start() {
        if (oscarModel.getRowCount() == 0) {
            loadData("resources/oscars.xml");
        }
    }

    //<snip>Initialize table columns
    protected TableColumnModel createColumnModel() {
        DefaultTableColumnModel columnModel = new DefaultTableColumnModel();

        TableCellRenderer cellRenderer = new OscarCellRenderers.RowRenderer(getTableRowColors());

        TableColumn column = new TableColumn();
        column.setModelIndex(OscarTableModel.YEAR_COLUMN);
        column.setHeaderValue("Year");
        column.setPreferredWidth(26);
        column.setCellRenderer(new OscarCellRenderers.YearRenderer(getTableRowColors()));
        columnModel.addColumn(column);

        column = new TableColumn();
        column.setModelIndex(OscarTableModel.CATEGORY_COLUMN);
        column.setHeaderValue("Award Category");
        column.setPreferredWidth(100);
        column.setCellRenderer(cellRenderer);
        columnModel.addColumn(column);

        column = new TableColumn();
        column.setModelIndex(OscarTableModel.MOVIE_COLUMN);
        column.setHeaderValue("Movie Title");
        column.setPreferredWidth(180);
        HyperlinkCellRenderer hyperlinkRenderer
                = new OscarCellRenderers.MovieRenderer(new IMDBLinkAction(),
                true, getTableRowColors());
        hyperlinkRenderer.setRowColors(getTableRowColors());
        column.setCellRenderer(hyperlinkRenderer);
        columnModel.addColumn(column);

        column = new TableColumn();
        column.setModelIndex(OscarTableModel.PERSONS_COLUMN);
        column.setHeaderValue("Nominees");
        column.setPreferredWidth(120);
        column.setCellRenderer(new OscarCellRenderers.NomineeRenderer(getTableRowColors()));
        columnModel.addColumn(column);

        return columnModel;
    }
    //</snip>

    protected void initSortingFiltering() {
        //<snip>Setup filtering for winners
        sorter = new TableRowSorter<>(oscarModel);
        oscarTable.setRowSorter(sorter);
        winnerFilter = new RowFilter<OscarTableModel, Integer>() {
            @Override
            public boolean include(Entry<? extends OscarTableModel, ? extends Integer> entry) {
                OscarTableModel oscarModel = entry.getModel();
                OscarCandidate candidate = oscarModel.getCandidate(entry.getIdentifier().intValue());
                if (candidate.isWinner()) {
                    // Returning true indicates this row should be shown.
                    return true;
                }
                // loser
                return false;
            }
        };
        //</snip>

        //<snip>Setup search filter
        searchFilter = new RowFilter<OscarTableModel, Integer>() {
            @Override
            public boolean include(Entry<? extends OscarTableModel, ? extends Integer> entry) {
                OscarTableModel oscarModel = entry.getModel();
                OscarCandidate candidate = oscarModel.getCandidate(entry.getIdentifier().intValue());
                boolean matches = false;
                Pattern p = Pattern.compile(filterString + ".*", Pattern.CASE_INSENSITIVE);

                String movie = candidate.getMovieTitle();
                if (movie != null) {
                    if (movie.startsWith("The ")) {
                        movie = movie.replace("The ", "");
                    } else if (movie.startsWith("A ")) {
                        movie = movie.replace("A ", "");
                    }
                    // Returning true indicates this row should be shown.
                    matches = p.matcher(movie).matches();
                }
                List<String> persons = candidate.getPersons();
                for (String person : persons) {
                    if (p.matcher(person).matches()) {
                        matches = true;
                    }
                }
                return matches;
            }
        };
        //</snip>
    }

    public void setShowOnlyWinners(boolean showOnlyWinners) {
        boolean oldShowOnlyWinners = this.showOnlyWinners;
        this.showOnlyWinners = showOnlyWinners;
        configureFilters();
        tableStatus.setText(statusLabelString + oscarTable.getRowCount());
        firePropertyChange("showOnlyWinners", oldShowOnlyWinners, showOnlyWinners);
    }

    public boolean getShowOnlyWinners() {
        return showOnlyWinners;
    }

    public void setFilterString(String filterString) {
        String oldFilterString = this.filterString;
        this.filterString = filterString;
        configureFilters();
        firePropertyChange("filterString", oldFilterString, filterString);
    }

    protected boolean hasFilterString() {
        return filterString != null && !filterString.equals("");
    }

    protected void configureFilters() {
        if (showOnlyWinners && hasFilterString()) {
            List<RowFilter<OscarTableModel, Integer>> filters
                    = new ArrayList<>(2);
            filters.add(winnerFilter);
            filters.add(searchFilter);
            RowFilter<OscarTableModel, Integer> comboFilter = RowFilter.andFilter(filters);
            sorter.setRowFilter(comboFilter);
        } else if (showOnlyWinners) {
            sorter.setRowFilter(winnerFilter);
        } else if (hasFilterString()) {
            sorter.setRowFilter(searchFilter);
        } else {
            sorter.setRowFilter(null);
        }
        tableStatus.setText((hasFilterString() ? searchLabelString : statusLabelString)
                + oscarTable.getRowCount());

    }

    private class ShowWinnersListener implements ChangeListener {

        @Override
        public void stateChanged(ChangeEvent event) {
            setShowOnlyWinners(winnersCheckbox.isSelected());
        }
    }

    //<snip>Setup search filter
    protected class SearchFilterListener implements DocumentListener {

        protected void changeFilter(DocumentEvent event) {
            Document document = event.getDocument();
            try {
                setFilterString(document.getText(0, document.getLength()));

            } catch (BadLocationException ex) {
                System.err.println(ex);
            }
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
            changeFilter(e);
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            changeFilter(e);
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            changeFilter(e);
        }
    }
    //</snip>

    //<snip>Use SwingWorker to asynchronously load the data
    public void loadData(String dataPath) {
        // create SwingWorker which will load the data on a separate thread
        OscarDataLoader loader = new OscarDataLoader(
                OscarTableDemo.class.getResource(dataPath), oscarModel);

        actionStatus.setText("Loading data: ");

        // display progress bar while data loads
        final JProgressBar progressBar = new JProgressBar();
        statusBarLeft.add(progressBar);
        loader.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent event) {
                if (event.getPropertyName().equals("progress")) {
                    int progress = ((Integer) event.getNewValue()).intValue();
                    progressBar.setValue(progress);

                    if (progress == 100) {
                        statusBarLeft.remove(progressBar);
                        actionStatus.setText("");
                        revalidate();
                    }
                }
            }
        });
        loader.execute();

    }
    //</snip>

    protected void showMessage(String title, String message) {
        JOptionPane.showMessageDialog(this, message, title, JOptionPane.INFORMATION_MESSAGE);
    }

    //<snip>Use SwingWorker to asynchronously load the data
    private class OscarDataLoader extends SwingWorker<List<OscarCandidate>, OscarCandidate> {

        private final URL oscarData;
        private final OscarTableModel oscarModel;
        private final List<OscarCandidate> candidates = new ArrayList<>();
        private JLabel credits;

        private OscarDataLoader(URL oscarURL, OscarTableModel oscarTableModel) {
            this.oscarData = oscarURL;
            this.oscarModel = oscarTableModel;
        }

        @Override
        public List<OscarCandidate> doInBackground() {
            OscarDataParser parser = new OscarDataParser() {
                @Override
                protected void addCandidate(OscarCandidate candidate) {
                    candidates.add(candidate);
                    if (candidates.size() % 3 == 0) {
                        try { // slow it down so we can see progress :-)
                            Thread.sleep(1);
                        } catch (InterruptedException ex) {
                        }
                    }
                    publish(candidate);
                    setProgress(100 * candidates.size() / 8545);
                }
            };
            parser.parseDocument(oscarData);
            return candidates;
        }

        @Override
        protected void process(List<OscarCandidate> moreCandidates) {
            if (credits == null) {
                showCredits();
            }
            oscarModel.add(moreCandidates);
        }

        // For older Java 6 on OS X
        protected void process(OscarCandidate... moreCandidates) {
            for (OscarCandidate candidate : moreCandidates) {
                oscarModel.add(candidate);
            }
        }

        private void showCredits() {
            credits = new JLabel("<html><p align=\"center\">Academy Award data<br>courtesy of Howard Katz</p></html>");
            credits.setFont(UIManager.getFont("Table.font").deriveFont(24f));
            credits.setHorizontalAlignment(JLabel.CENTER);
            credits.setBorder(new CompoundBorder(new TitledBorder(""),
                    new EmptyBorder(20, 20, 20, 20)));
            dataPanel.showMessageLayer(credits, .75f);
        }

        @Override
        protected void done() {
            setProgress(100);
            dataPanel.hideMessageLayer();
        }

    }
    //</snip>

    private class IMDBLinkAction extends AbstractAction {

        @Override
        public void actionPerformed(ActionEvent event) {
            int row = ((Integer) event.getSource()).intValue();
            OscarCandidate candidate = oscarModel.getCandidate(oscarTable.convertRowIndexToModel(row));

            try {
                URI imdbURI = candidate.getIMDBMovieURI();
                if (imdbURI == null) {
                    String imdbString = IMDBLink.getMovieURIString(candidate.getMovieTitle(),
                            candidate.getYear());
                    if (imdbString != null) {
                        imdbURI = new URI(imdbString);
                        candidate.setIMDBMovieURI(imdbURI);
                    }
                }
                if (imdbURI != null) {
                    //DemoUtilities.browse(imdbURI);
                } else {
                    showMessage("IMDB Link",
                            "Unable to locate IMDB URL for" + "\n"
                                    + candidate.getMovieTitle());
                }

            } catch (IOException | URISyntaxException ex) {
            }
        }
    }

    public static void main(String args[]) {

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("OscarTable");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                OscarTableDemo demo = new OscarTableDemo();
                frame.add(demo);
                frame.setSize(900, 700);
                frame.setVisible(true);
                demo.start();
            }
        });
    }
}

/**
 *
 * @author Sowndar
 */
//<snip>Create HTML hyperlink
//<snip>Create HTML image hyperlink
class JHyperlink extends JButton {

    private static final BrowseAction defaultBrowseAction = new BrowseAction();

    private URI targetURI;
    private boolean visited;

    private final transient Rectangle viewRect = new Rectangle();
    private final transient Rectangle iconRect = new Rectangle();
    private final transient Rectangle textRect = new Rectangle();

    //remind(aim): lookup colors instead of hardcoding them
    private Color normalForeground;
    private Color activeForeground;
    private Color visitedForeground;
    private boolean drawUnderline = true;

    static {
        UIManager.put("Hyperlink.foreground", Color.blue);
        UIManager.put("Hyperlink.activeForeground", Color.red);
        UIManager.put("Hyperlink.visitedForeground", new Color(85, 145, 90));
    }

    /**
     * Creates a new instance of JHyperlink
     */
    public JHyperlink() {
        super();
        normalForeground = UIManager.getColor("Hyperlink.foreground");
        activeForeground = UIManager.getColor("Hyperlink.activeForeground");
        visitedForeground = UIManager.getColor("Hyperlink.visitedForeground");
        setBorderPainted(false);
        setContentAreaFilled(false);
        setForeground(normalForeground);
        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        setMargin(new Insets(0, 0, 0, 0));
        setAction(defaultBrowseAction);
    }

    /**
     * Creates a new instance of JHyperlink
     */
    public JHyperlink(String text) {
        this();
        setText(text); // override the inheritence of the action's name
    }

    public JHyperlink(String text, String targetURI) throws URISyntaxException {
        this(text, new URI(targetURI));
    }

    public JHyperlink(String text, URI target) {
        this(text);
        setTarget(target);
    }

    public JHyperlink(String text, Action action) {
        this(text);
        setAction(action); // replaces default browse action
        setText(text); // override the inheritence of the action's name
    }

    public JHyperlink(String text, Icon icon) {
        this(text);
        setIcon(icon);
    }

    public JHyperlink(Icon icon, String targetURI) throws URISyntaxException {
        this(null, icon, targetURI);
    }

    public JHyperlink(String text, Icon icon, String targetURI) throws URISyntaxException {
        this(text, new URI(targetURI));
        setIcon(icon);
    }

    public JHyperlink(String text, Icon icon, URI target) {
        this(text);
        setIcon(icon);
        setTarget(target);
    }

    public void setTarget(URI target) {
        this.targetURI = target;
        setToolTipText(target.toASCIIString());
    }

    public URI getTarget() {
        return targetURI;
    }

    public void setVisited(boolean visited) {
        this.visited = visited;
    }

    public boolean isVisited() {
        return visited;
    }

    @Override
    public void setForeground(Color foreground) {
        normalForeground = foreground;
        super.setForeground(foreground);
    }

    public void setVisitedForeground(Color visited) {
        visitedForeground = visited;
    }

    public void setDrawUnderline(boolean drawUnderline) {
        this.drawUnderline = drawUnderline;
    }

    public boolean getDrawUnderline() {
        return drawUnderline;
    }

    @Override
    protected void paintComponent(Graphics g) {
        // Set the foreground on the fly to ensure the text is painted
        // with the proper color in super.paintComponent
        ButtonModel model = getModel();
        if (model.isArmed()) {
            super.setForeground(activeForeground);
        } else if (visited) {
            super.setForeground(visitedForeground);
        } else {
            super.setForeground(normalForeground);
        }
        super.paintComponent(g);

        if (drawUnderline) {
            Insets insets = getInsets();
            viewRect.x = insets.left;
            viewRect.y = insets.top;
            viewRect.width = getWidth() - insets.left - insets.right;
            viewRect.height = getHeight() - insets.top - insets.bottom;
            int baseline = getBaseline(viewRect.width, viewRect.height);

            iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
            textRect.x = textRect.y = textRect.width = textRect.height = 0;
            SwingUtilities.layoutCompoundLabel(g.getFontMetrics(), getText(),
                    getIcon(), getVerticalAlignment(), getHorizontalAlignment(),
                    getVerticalTextPosition(), getHorizontalTextPosition(),
                    viewRect, iconRect, textRect, getIconTextGap());

            // getBaseline not returning correct results, so workaround for now
            if (UIManager.getLookAndFeel().getName().equals("Nimbus")) {
                baseline += 7;
            } else {
                baseline += 3;
            }

            g.setColor(getForeground());
            g.drawLine(textRect.x,
                    baseline,
                    textRect.x + textRect.width,
                    baseline);
        }

    }

    // This action is stateless and hence can be shared across hyperlinks
    private static class BrowseAction extends AbstractAction {

        public BrowseAction() {
            super();
        }

        public static boolean browse(URI uri) throws IOException {
            // Try using the Desktop api first
            try {
                Desktop desktop = Desktop.getDesktop();
                desktop.browse(uri);

                return true;
            } catch (SecurityException e) {
            }
            return false;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JHyperlink hyperlink = (JHyperlink) e.getSource();

            URI targetURI = hyperlink.getTarget();
            if (targetURI != null) {
                try {
                    browse(targetURI);
                    hyperlink.setVisited(true);
                } catch (IOException ex) {
                    System.err.println(ex);
                }
            }
        }
    }
//</snip>
//</snip>

}

/**
 *
 * @author aim
 */
class OscarCellRenderers {

    //<snip>Render table rows with alternating colors
    static class RowRenderer extends DefaultTableCellRenderer {

        private Color rowColors[];

        public RowRenderer() {
            // initialize default colors from look-and-feel
            rowColors = new Color[1];
            rowColors[0] = UIManager.getColor("Table.background");
        }

        public RowRenderer(Color colors[]) {
            super();
            setRowColors(colors);
        }

        public void setRowColors(Color colors[]) {
            rowColors = colors;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                                                       boolean isSelected, boolean hasFocus, int row, int column) {
            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            setText(value != null ? value.toString() : "unknown");
            if (!isSelected) {
                setBackground(rowColors[row % rowColors.length]);
            }
            return this;
        }

        @Override
        public boolean isOpaque() {
            return true;
        }
    }
    //<snip>

    //<snip>Render "year" table column with font representing style of decade
    // currently only used on OS X because fonts are Mac centric.
    static class YearRenderer extends RowRenderer {

        private HashMap<String, Font> eraFonts;

        public YearRenderer() {
            setHorizontalAlignment(JLabel.CENTER);

            if (System.getProperty("os.name").equals("Mac OS X")) {
                eraFonts = new HashMap<>();
                eraFonts.put("192"/*1920's*/, new Font("Jazz LET", Font.PLAIN, 12));
                eraFonts.put("193"/*1930's*/, new Font("Mona Lisa Solid ITC TT", Font.BOLD, 18));
                eraFonts.put("194"/*1940's*/, new Font("American Typewriter", Font.BOLD, 12));
                eraFonts.put("195"/*1950's*/, new Font("Britannic Bold", Font.PLAIN, 12));
                eraFonts.put("196"/*1960's*/, new Font("Cooper Black", Font.PLAIN, 14));
                eraFonts.put("197"/*1970's*/, new Font("Syncro LET", Font.PLAIN, 14));
                eraFonts.put("198"/*1980's*/, new Font("Mistral", Font.PLAIN, 18));
                eraFonts.put("199"/*1990's*/, new Font("Papyrus", Font.BOLD, 14));
                eraFonts.put("200"/*2000's*/, new Font("Calisto MT", Font.PLAIN, 14));
            }
        }

        public YearRenderer(Color colors[]) {
            this();
            setRowColors(colors);
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                                                       boolean isSelected, boolean hasFocus, int row, int column) {

            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

            String year = table.getValueAt(row,
                    table.convertColumnIndexToView(OscarTableModel.YEAR_COLUMN)).toString();
            if (eraFonts != null && year != null && year.length() == 4) {
                String era = year.substring(0, 3);
                Font eraFont = eraFonts.get(era);
                setFont(eraFont);
            }
            return this;
        }
    }
    //</snip>

    //<snip>Render "nominee" table column with special icon for winners
    static class NomineeRenderer extends RowRenderer {

        private final ImageIcon winnerIcon;
        private final ImageIcon nomineeIcon; // nice way of saying "loser" :)

        public NomineeRenderer() {
            winnerIcon = new ImageIcon(createGoldStar());
                    //getClass().getResource("resources/goldstar.png"));
            nomineeIcon = new ImageIcon("");
                    //getClass().getResource("resources/nominee.png"));
            setHorizontalTextPosition(JLabel.TRAILING);
        }

        NomineeRenderer(Color colors[]) {
            this();
            setRowColors(colors);
        }

        BufferedImage createGoldStar() {
            BufferedImage buff = new BufferedImage(32, 32, BufferedImage.TRANSLUCENT);
            Graphics2D g2d = buff.createGraphics();
            // Create a Star using a general path object
            GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
            p.moveTo(-100.0f/8, -25.0f/8);
            p.lineTo(+100.0f/8, -25.0f/8);
            p.lineTo(-50.0f/8, +100.0f/8);
            p.lineTo(+0.0f/8, -100.0f/8);
            p.lineTo(+50.0f/8, +100.0f/8);
            p.closePath();

            // Translate origin towards center of canvas
            g2d.translate(getWidth() / 2, getHeight() / 2);

            // Render the star's path
            g2d.setColor(Color.yellow.darker().darker());
            g2d.fill(p);
            g2d.dispose();
            return buff;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                                                       boolean isSelected, boolean hasFocus, int row, int column) {

            super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

            TableModel model = table.getModel();
            boolean winner = ((Boolean) model.getValueAt(table.convertRowIndexToModel(row),
                    OscarTableModel.WINNER_COLUMN)).booleanValue();

            List<String> persons = (List<String>) value;
            String text = persons != null && !persons.isEmpty() ? persons.get(0) : "name unknown";
            int personCount = persons.size();
            if (personCount > 1) {
                setText(text + " + more...");
                StringBuffer winners = new StringBuffer("");
                for (int i = 0; i < personCount; i++) {
                    String person = persons.get(i);
                    winners.append(" ").append(person).append(i < personCount - 1 ? ", " : "");
                }
                setToolTipText((winner ? "Winners:" : "Nominees:") + winners);
            } else {
                setText(text);
                setToolTipText(winner ? "Winner!" : "Nominee");
            }

            setIcon(winner ? winnerIcon : nomineeIcon);

            return this;
        }
    }
    //</snip>

    static class MovieRenderer extends HyperlinkCellRenderer {

        MovieRenderer(Action action, boolean underlineOnRollover, Color rowColors[]) {
            super(action, underlineOnRollover);
            setRowColors(rowColors);
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                                                       boolean isSelected, boolean hasFocus, int row, int column) {
            super.getTableCellRendererComponent(table, value, isSelected,
                    hasFocus, row, column);
            if (value != null) {
                setToolTipText("*www.imdb.com/" + "\"" + value + "\"");
            }
            return this;
        }
    }
}
```

Download the file oscars.xml *www.filehosting.org/file/details/466709/oscars.xml

*s8.postimg.org/dmf5xj2kh/Oscar_Table.jpg

*Audio Meter*

The Java Sound API provides the lowest level of sound support on the Java platform. It provides application programs with a great amount of control over sound operations, and it is extensible.
For example, the Java Sound API supplies mechanisms for installing, accessing, and manipulating system resources such as audio mixers, MIDI synthesizers, other audio or MIDI devices, file readers
and writers, and sound format converters. The Java Sound API does not include sophisticated sound editors or graphical tools, but it provides capabilities upon which such programs can be built.

The following program loads a WAV audio file selected by the user and plays it. While it's playing the program displays the PlayerLeveler graphically.
It correspondingly draws a rectangular graph. 


```
/**
 * Created with IntelliJ IDEA.
 * User: Sowndar
 * Date: 12/15/14
 * Time: 3:38 PM
 * To change this template use File | Settings | File Templates.
 */

import javax.sound.sampled.*;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class DataLineInfoGUI extends JPanel {

    private static final long serialVersionUID = 1L;

    PCMFilePlayerLeveler player;
    JButton startButton;

    public DataLineInfoGUI(File f) {
        super();
        try {
            player = new PCMFilePlayerLeveler(f);
        } catch (IOException | UnsupportedAudioFileException | LineUnavailableException ioe) {
            add(new JLabel("Error: "
                    + ioe.getMessage()));
            return;
        }
        DataLine line = player.getLine();
        // layout
        // line 1: name
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        add(new JLabel("File:  "
                + player.getFile().getName()));
        // line 2: levels
        add(new DataLineLevelMeter(line));
        // line 3: format info as textarea
        AudioFormat format = line.getFormat();
        JTextArea ta = new JTextArea();
        ta.setBorder(new TitledBorder("Format"));
        ta.append("Encoding: "
                + format.getEncoding().toString() + "\n");
        ta.append("Bits/sample: "
                + format.getSampleSizeInBits() + "\n");
        ta.append("Channels: "
                + format.getChannels() + "\n");
        ta.append("Endianness: "
                + (format.isBigEndian() ? " big " : "little") + "\n");
        ta.append("Frame size: "
                + format.getFrameSize() + "\n");
        ta.append("Frame rate: "
                + format.getFrameRate() + "\n");
        add(ta);

        // now start playing
        player.start();

    }

    public static void main(String[] args) {
        JFileChooser chooser = new JFileChooser();
        chooser.setAcceptAllFileFilterUsed(false);
        final String[] format = {"wav", "au", "aiff"};
        chooser.addChoosableFileFilter(new FileFilter() {

            @Override
            public boolean accept(File file) {
                String fileName = file.getName();
                for (int i = 0; i < format.length; i++) {
                    // Show directory & images files only
                    if (fileName.endsWith(format[i]) || file.isDirectory()) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public String getDescription() {
                return "Sound File";
            }
        });
        chooser.showOpenDialog(null);
        File file = chooser.getSelectedFile();
        if (file != null) {
            DataLineInfoGUI demo
                    = new DataLineInfoGUI(file);

            JFrame f = new JFrame("JavaSound info");
            f.add(demo);
            f.setSize(800, 450);
            f.setVisible(true);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
    }

    class DataLineLevelMeter extends JPanel {

        DataLine line;
        float level = 0.0f;

        public DataLineLevelMeter(DataLine l) {
            line = l;
            Timer timer
                    = new Timer(50,
                    new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            // level = line.getLevel();
                            level = player.getLevel();
                            repaint();
                        }
                    });
            timer.start();
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            int width = getWidth();
            int height = getHeight();
            // paint
            g.setColor(Color.green);
            int meterWidth = (int) (level * (float) width);
            g.fillRect(0, 0, meterWidth, height);
        }

    }
}


class PCMFilePlayerLeveler implements Runnable {

    File file;
    AudioInputStream in;
    SourceDataLine line;
    int frameSize;
    byte[] buffer;
    Thread playThread;
    boolean playing;
    boolean notYetEOF;
    AudioFormat format;
    float level;

    final static float MAX_8_BITS_SIGNED = Byte.MAX_VALUE;
    final static float MAX_8_BITS_UNSIGNED = 0xff;
    final static float MAX_16_BITS_SIGNED = Short.MAX_VALUE;
    final static float MAX_16_BITS_UNSIGNED = 0xffff;

    public PCMFilePlayerLeveler(File f)
            throws IOException,
            UnsupportedAudioFileException,
            LineUnavailableException {
        file = f;
        // in = AudioSystem.getAudioInputStream (f);
        in = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream(f)));
        format = in.getFormat();
        AudioFormat.Encoding formatEncoding = format.getEncoding();
        if (!(formatEncoding.equals(AudioFormat.Encoding.PCM_SIGNED)
                || formatEncoding.equals(AudioFormat.Encoding.PCM_UNSIGNED))) {
            throw new UnsupportedAudioFileException(
                    file.getName() + " is not PCM audio");
        }
        System.out.println("got PCM format:  "
                + format.getChannels() + " channels, "
                + format.getSampleSizeInBits() + " bit samples");
        frameSize = format.getFrameSize();
        System.out.println("got frame size: ");
        DataLine.Info info
                = new DataLine.Info(SourceDataLine.class, format);
        System.out.println("got info");
        line = (SourceDataLine) AudioSystem.getLine(info);

        // figure out a small buffer size
        int bytesPerSec = format.getSampleSizeInBits()
                * (int) format.getSampleRate();
        System.out.println("bytesPerSec = " + bytesPerSec);
        int bufferSize = bytesPerSec / 20;
        buffer = new byte[bufferSize];

        System.out.println("got line");
        line.open();
        System.out.println("opened line");
        playThread = new Thread(this);
        playing = false;
        notYetEOF = true;
        playThread.start();
    }

    @Override
    public void run() {
        int readPoint = 0;
        int bytesRead = 0;

        try {
            while (notYetEOF) {
                if (playing) {
                    // only write if the line will take at
                    // least a buffer-ful of data
                    if (line.available() < buffer.length) {
                        Thread.yield();
                        continue;
                    }
                    bytesRead = in.read(buffer,
                            readPoint,
                            buffer.length - readPoint);
                    if (bytesRead == -1) {
                        notYetEOF = false;
                        break;
                    }
                    // how many frames did we get,
                    // and how many are left over?
                    int frames = bytesRead / frameSize;
                    int leftover = bytesRead % frameSize;
                    // calculate level
                    calculateLevel(buffer, readPoint, leftover);
                    // if (level > 1)
                    //     System.out.println ("WTF? level = " + level);
                    // System.out.println ("level: " + level);
                    // send to line
                    line.write(buffer, readPoint, bytesRead - leftover);
                    // save the leftover bytes
                    System.arraycopy(buffer, bytesRead,
                            buffer, 0,
                            leftover);
                    readPoint = leftover;

                } else {
                    // if not playing
                    // Thread.yield();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException ie) {
                    }
                }
            } // while notYetEOF
            System.out.println("reached eof");
            line.drain();
            line.stop();
        } catch (IOException ioe) {
        } finally {
            // line.close();
        }
    } // run

    /**
     * resets level by finding max value in buffer, taking into account whether
     * these are 8 or 16 bit values (doesn't care about mono vs stereo - if one
     * channel is disproportionately louder than the other, it wins)
     */
    private void calculateLevel(byte[] buffer,
                                int readPoint,
                                int leftOver) {
        int max = 0;
        boolean use16Bit = (format.getSampleSizeInBits() == 16);
        boolean signed = (format.getEncoding()
                == AudioFormat.Encoding.PCM_SIGNED);
        boolean bigEndian = (format.isBigEndian());
        if (use16Bit) {
            for (int i = readPoint; i < buffer.length - leftOver; i += 2) {
                int value = 0;
                // deal with endianness
                int hiByte = (bigEndian ? buffer[i] : buffer[i + 1]);
                int loByte = (bigEndian ? buffer[i + 1] : buffer[i]);
                if (signed) {
                    short shortVal = (short) hiByte;
                    shortVal = (short) ((shortVal << 8) | (byte) loByte);
                    value = shortVal;
                } else {
                    value = (hiByte << 8) | loByte;
                }
                max = Math.max(max, value);
            } // for
        } else {
            // 8 bit - no endianness issues, just sign
            for (int i = readPoint; i < buffer.length - leftOver; i++) {
                int value = 0;
                if (signed) {
                    value = buffer[i];
                } else {
                    short shortVal = 0;
                    shortVal = (short) (shortVal | buffer[i]);
                    value = shortVal;
                }
                max = Math.max(max, value);
            } // for
        } // 8 bit
        // express max as float of 0.0 to 1.0 of max value
        // of 8 or 16 bits (signed or unsigned)
        if (signed) {
            if (use16Bit) {
                level = (float) max / MAX_16_BITS_SIGNED;
            } else {
                level = (float) max / MAX_8_BITS_SIGNED;
            }
        } else {
            if (use16Bit) {
                level = (float) max / MAX_16_BITS_UNSIGNED;
            } else {
                level = (float) max / MAX_8_BITS_UNSIGNED;
            }
        }
    } // calculateLevel

    public void start() {
        playing = true;
        if (!playThread.isAlive()) {
            playThread.start();
        }
        line.start();
    }

    public void stop() {
        playing = false;
        line.stop();
    }

    public SourceDataLine getLine() {
        return line;
    }

    public File getFile() {
        return file;
    }

    public float getLevel() {
        return level;
    }
}
```

*s22.postimg.org/y9hsgtv4d/Data_Line_Info_GUI.jpg

This wraps up the Expert Java programming Part 2.
You can easily take Java to the next level if you program at the higher level!!


----------

