Java tutorial
/** * Copyright (c) 2014 Matthias Jaenicke <matthias.jaenicke@student.kit.edu>, * Matthias Plappert <undkc@student.kit.edu>, * Julien Duman <uncyc@student.kit.edu>, * Christian Dreher <uaeef@student.kit.edu>, * Wasilij Beskorovajnov <uajkm@student.kit.edu> and * Aydin Tekin <aydin.tekin@student.kit.edu> * * Released under the MIT license (refer to LICENSE.md) * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package edu.kit.iks.cryptographicslib.common.view; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import javax.imageio.ImageIO; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.Timer; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; import edu.kit.iks.cryptographicslib.common.view.partial.ImageView; import edu.kit.iks.cryptographicslib.util.Logger; /** * View of the information page. * * @author Matthias Plappert */ public class InformationView extends JPanel implements MouseListener { /** * Possible scroll directions. */ private enum ScrollDirection { NONE, UP, DOWN }; /** * Resources. */ private Element resources; /** * The delay between scroll events when a scroll button is pressed. */ private static final int scrollDelay = 50; /** * Serial Version UID. */ private static final long serialVersionUID = -8593552609491347799L; /** * QR code for further information. */ private Image qrCode; /** * The HTML used to display the additional information in a web view. */ private String html; /** * The content of the QR code. */ private String qrCodeContent; /** * The QR code view. */ private ImageView qrCodeView; /** * Displays the contents of the QR code. */ private JLabel qrCodeLabel; /** * The scrollable container of the web view. */ private JScrollPane webViewContainer; /** * The web view. */ private JEditorPane webView; /** * Scrolls the web view up. */ private JButton scrollUpButton; /** * Scrolls the web view down. */ private JButton scrollDownButton; /** * The current scroll direction. */ private ScrollDirection scrollDirection = ScrollDirection.NONE; /** * This timer is used to periodically scroll up or down depending on the * current value of scroll direction. This is used when pressing the up or * down button. */ private Timer scrollTimer; /** * Constructor initializing a new instance of {InformationView} * with given {HTML} and {qrCode}. * * @param html The HTML used to display the additional informations * @param qrCode QR code for further information * @param url The content of the QR code */ public InformationView(String html, Image qrCode, String qrCodeContent) { this.qrCode = qrCode; this.html = html; this.qrCodeContent = qrCodeContent; this.initResources(); // Initialize view. this.setLayout(new BorderLayout()); this.loadWebViewComponents(); this.loadQRCodeViewComponents(); this.validate(); // The JScrollPane returns invalid scroll information, resulting in an invalid // button state. Adding this slight delay fixes the problem. SwingUtilities.invokeLater(new Runnable() { @Override public void run() { updateButtonStates(); } }); } /** * Helper to init the resources. */ private void initResources() { SAXBuilder saxBuilder = new SAXBuilder(); // obtain file object InputStream is = this.getClass().getResourceAsStream("/icons/IconResources.xml"); try { // converted file to document object Document document = saxBuilder.build(is); // get root node from xml this.resources = document.getRootElement().getChild("InformationView"); } catch (JDOMException | IOException e) { Logger.error(e); } } /** * Loads the web view and related components. */ private void loadWebViewComponents() { JPanel layoutContainer = new JPanel(new BorderLayout()); layoutContainer.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); this.loadWebView(); layoutContainer.add(this.webViewContainer, BorderLayout.CENTER); this.loadScrollButtons(); JPanel buttonLayoutContainer = new JPanel(new BorderLayout()); buttonLayoutContainer.add(this.scrollUpButton, BorderLayout.NORTH); buttonLayoutContainer.add(this.scrollDownButton, BorderLayout.SOUTH); buttonLayoutContainer.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); layoutContainer.add(buttonLayoutContainer, BorderLayout.EAST); // Valdiate and add. layoutContainer.validate(); this.add(layoutContainer, BorderLayout.CENTER); } private void loadWebView() { // Editor pane used as a web view. this.webView = new JEditorPane(); this.webView.setEditable(false); this.webView.setContentType("text/html"); this.webView.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); try { this.webView.setText(this.getHtml()); } catch (Exception e) { Logger.error(e); } this.webView.setCaretPosition(0); // scrolls to the top // Scroll pane that contains the editor pane. this.webViewContainer = new JScrollPane(this.webView); this.webViewContainer.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); this.webViewContainer.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); this.webViewContainer.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY)); this.webViewContainer.validate(); } /** * Loads the scroll up and scroll down buttons for scrolling the web view. * This is necessary because the scroll bar is very clumsy to control on * a touch screen device. */ private void loadScrollButtons() { // Scroll up button. this.scrollUpButton = new JButton(); this.scrollUpButton.setIcon(this.loadIcon("ArrowUp")); this.scrollUpButton.addMouseListener(this); // Scroll down button. this.scrollDownButton = new JButton(); this.scrollDownButton.setIcon(this.loadIcon("ArrowDown")); this.scrollDownButton.addMouseListener(this); } /** * Loads an icon with the given name. * @param name The name * @return The icon */ private Icon loadIcon(String name) { String path = this.resources.getChild(name).getAttributeValue("path"); ImageIcon icon = null; try { InputStream is = this.getClass().getResourceAsStream(path); icon = new ImageIcon(ImageIO.read(is)); } catch (IOException e) { Logger.error(e); } return icon; } /** * Loads the QR code view and related components. */ private void loadQRCodeViewComponents() { // We use a container to horizontally center the QR code. JPanel container = new JPanel(new GridBagLayout()); GridBagConstraints imageConstraints = new GridBagConstraints(); imageConstraints.insets = new Insets(10, 10, 10, 10); imageConstraints.anchor = GridBagConstraints.ABOVE_BASELINE; this.qrCodeView = new ImageView(this.qrCode); container.add(this.qrCodeView, imageConstraints); GridBagConstraints labelConstraints = new GridBagConstraints(); labelConstraints.anchor = GridBagConstraints.ABOVE_BASELINE; try { this.qrCodeLabel = new JLabel(URLDecoder.decode(this.qrCodeContent, "UTF-8")); } catch (UnsupportedEncodingException e) { Logger.error(e); } container.add(this.qrCodeLabel, labelConstraints); container.setName("visualizationContainerFooter"); this.add(container, BorderLayout.SOUTH); } /** * Returns the HTML contents of the additional informations. * * @return the HTML contents */ public String getHtml() { return this.html; } /** * Gets the QR code for further information. * * @return QR code for further information */ public Image getQrCode() { return this.qrCode; } /** * The QR code content. * * @return QR code content */ public String getQrCodeContent() { return this.qrCodeContent; } @Override public void mousePressed(MouseEvent e) { this.scrollDirection = this.getScrollDirectionFromMouseEvent(e); this.scroll(this.scrollDirection); if (this.scrollTimer != null) { this.stopScrollTimer(); } this.startScrollTimer(); } @Override public void mouseReleased(MouseEvent e) { if (this.scrollTimer != null) { this.stopScrollTimer(); } this.scrollDirection = ScrollDirection.NONE; } @Override public void mouseEntered(MouseEvent e) { // Unused } @Override public void mouseExited(MouseEvent e) { // Unused } @Override public void mouseClicked(MouseEvent e) { // Unused } /** * Converts the given mouse event into the correct scroll direction. * * @param e The MouseEvent that causes the scroll. * @return The scroll direction for that event or {ScrollDirection.NONE} if * the event cannot be properly mapped. */ private ScrollDirection getScrollDirectionFromMouseEvent(MouseEvent e) { Component source = (Component) e.getSource(); if (this.scrollUpButton.equals(source)) { return ScrollDirection.UP; } else if (this.scrollDownButton.equals(source)) { return ScrollDirection.DOWN; } else { return ScrollDirection.NONE; } } /** * Starts the scroll timer. We use this timer to scroll multiple times */ private void startScrollTimer() { this.scrollTimer = new Timer(InformationView.scrollDelay, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { scroll(scrollDirection); } }); this.scrollTimer.start(); } /** * Stops the scroll timer. */ private void stopScrollTimer() { this.scrollTimer.stop(); this.scrollTimer = null; } /** * Scrolls the web view in the given direction. * @param direction the scroll direction */ private void scroll(ScrollDirection direction) { JScrollBar vertical = this.webViewContainer.getVerticalScrollBar(); switch (direction) { case UP: vertical.setValue(Math.max(vertical.getMinimum(), vertical.getValue() - vertical.getBlockIncrement())); break; case DOWN: vertical.setValue(Math.min(vertical.getMaximum(), vertical.getValue() + vertical.getBlockIncrement())); break; default: // Do nothing. break; } this.updateButtonStates(); } /** * Updates the scroll button states. */ private void updateButtonStates() { JScrollBar vertical = this.webViewContainer.getVerticalScrollBar(); if (vertical.getValue() <= vertical.getMinimum()) { this.scrollUpButton.setEnabled(false); } else { this.scrollUpButton.setEnabled(true); } if (vertical.getValue() >= vertical.getMaximum() - vertical.getModel().getExtent() - 1) { this.scrollDownButton.setEnabled(false); } else { this.scrollDownButton.setEnabled(true); } } }