/*
 * @(#)HTMLView.java  1.1  2002-02-10
 *
 * Copyright (c) 1999 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.view;

import ch.randelshofer.util.*;
import ch.randelshofer.util.regex.*;

import java.awt.*;
import java.awt.dnd.*;
import java.awt.event.*;

import java.net.URL;


import java.io.IOException;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.activation.FileDataSource;

import javax.swing.*;

import javax.swing.event.HyperlinkListener;
import javax.swing.event.HyperlinkEvent;

import javax.swing.text.EditorKit;

import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTMLFrameHyperlinkEvent;

/**
 * View for HTML documents.
 *
 * @author  Werner Randelshofer, Hausmatt 10, CH-6405 Immensee, Switzerland
 * @version 1.1 2002-02-10 Support for DnD added. Find panel added.
 * <br>1.0.1 2000-09-25
 * <br>1.0 1999-10-19
 */
public class HTMLView extends AbstractView implements HyperlinkListener, ActionListener {
    protected JTextPane textPane;
    private JScrollPane scrollPane;
    
    /**
     * The menu item used to invoke the find action.
     */
    private JMenuItem findItem;
    /**
     * The menu item used to invoke the find again action.
     */
    private JMenuItem findAgainItem;
    /**
     * The panel used for the find feature.
     */
    private FindPanel findPanel;
    /**
     * The Matcher used to perform find operation.
     */
    private Matcher matcher;
    
    /**
     * Resource bundle for internationalisation.
     */
    private ResourceBundleUtil labels;
    
    public HTMLView() {
        labels = ResourceBundleUtil.getLAFBundle("ch.randelshofer.view.TextView");
        setLayout(new BorderLayout());
        textPane = new JTextPane();
        EditorKit kit = getEditorKit();
        textPane.setEditorKitForContentType("content/unknown",kit);
        textPane.setEditorKit(kit);
        textPane.setEditable(false);
        textPane.addHyperlinkListener(this);
        scrollPane = new JScrollPane(textPane);
        add(scrollPane);
        setPreferredSize(new Dimension(400,400));
        
        // Menu items for the find action
        findItem = new JMenuItem(labels.getString("findItem"));
        findItem.setMnemonic(labels.getMnem("findItem"));
        findItem.setAccelerator(labels.getAcc("findItem"));
        findItem.addActionListener(this);
        findItem.setActionCommand("find");
        
        // Menu items for the find again action
        findAgainItem = new JMenuItem(labels.getString("findAgainItem"));
        findAgainItem.setMnemonic(labels.getMnem("findAgainItem"));
        findAgainItem.setAccelerator(labels.getAcc("findAgainItem"));
        findAgainItem.addActionListener(this);
        findAgainItem.setActionCommand("findAgain");
    }
    
    protected EditorKit getEditorKit() {
        return new HTMLEditorKit();
    }
    
    /**
     * Sets the command context for the bean. The receiver loads
     * the image data provided by the data handler and displays it.
     */
    public void setCommandContext(String verb, DataHandler dh)
    throws IOException {
        // Try to get an URL from the data handler.
        // This is very awkward code, sigh.
        DataSource source = dh.getDataSource();
        if (source instanceof URLDataSource) {
            textPane.setPage( ((URLDataSource)source).getURL() );
        }
        else if (source instanceof FileDataSource) {
            // Code for JDK 1.1 to retrieve an URL from a File (this won't work on all platforms.)
            textPane.setPage( new URL( "file:"+((FileDataSource)source).getFile() ) );
            
            // Code for JDK 1.2 (platform independent).
            //textPane.setPage(  ((FileDataSource)source).getFile().getURL() );
        }
        else { // no URL available? Read the stream (links and images will not work.)
            textPane.read(dh.getInputStream(),dh);
        }
    }
    
    public void hyperlinkUpdate(HyperlinkEvent e) {
        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
            JTextPane pane = (JTextPane) e.getSource();
            if (e instanceof HTMLFrameHyperlinkEvent) {
                HTMLFrameHyperlinkEvent  evt = (HTMLFrameHyperlinkEvent)e;
                HTMLDocument doc = (HTMLDocument)pane.getDocument();
                doc.processHTMLFrameHyperlinkEvent(evt);
            }
            else {
                try {
                    pane.setPage(e.getURL());
                }
                catch (Throwable t)
                { t.printStackTrace(); }
            }
        }
    }
    
    /**
     * Adds menu items to the "View" menu.
     *
     * When the viewer is added to a container with a
     * JMenuBar. Then the container asks the viewer to
     * add his menu items to the "View" menu of the container.
     */
    public void mergeViewMenuItems(JMenu m) {
        m.add(findItem);
        m.add(findAgainItem);
    }
    
    /**
     * Adds menu items to the "Settings" menu.
     *
     * When the viewer is added to a container with a
     * JMenuBar. Then the container asks the viewer to
     * add his menu items to the "Settings" menu of the container.
     */
    public void mergeSettingsMenuItems(JMenu m) {
    }
    
    protected void updateViewProperties() {
        getViewProperties().put("title", " as HTML");
    }
    
    protected void updateApplicationProperties() {
    }
    
    
    /**
     * Returns true if this viewer has a scrollpane of its own.
     * The container that contains this viewers uses this method to
     * determine if it should wrap a scrollpane around the viewer
     * or not.
     *
     * @return  true if this viewer has a scrollpane of its own.
     */
    public boolean hasScrollPane() {
        return true;
    }
    
    /**
     * Disposes all resources allocated by this viewer.
     */
    public void dispose() {
    }
    
    /**
     * This method adds the given drop target listener to
     * all components of the viewer.
     */
    public void addDropTargetListener(DropTargetListener listener) {
        new DropTarget(this, listener);
        new DropTarget(textPane, listener);
        new DropTarget(scrollPane, listener);
    }
    
    public String toString() {
        return "Text HTML";
    }
    
    /**
     * Invoked when an action occurs.
     */
    public void actionPerformed(ActionEvent event) {
        Object source = event.getSource();
        if (source == findItem) showFindDialog();
        else if (source == findAgainItem) findAgain();
    }
    
    public void showFindDialog() {
        if (findPanel == null) {
            findPanel = new FindPanel();
            findPanel.setFindString(getViewProperties().getProperty("findString"));
            findPanel.setMatchCase(getViewProperties().getProperty("findMatchCase","false").equals("true"));
            findPanel.setMatchWord(getViewProperties().getProperty("findMatchWord","false").equals("true"));
        }
        if (findPanel.showDialog(this, labels.getString("findDialogTitle"))
        == FindPanel.APPROVE_OPTION) {
            getViewProperties().put("findString",findPanel.getFindString());
            getViewProperties().put("findMatchCase",new Boolean(findPanel.isMatchCase()).toString());
            getViewProperties().put("findMatchWord",new Boolean(findPanel.isMatchWord()).toString());
            
            matcher = new Matcher(
            textPane.getDocument(),
            findPanel.getFindString(),
            findPanel.isMatchCase(),
            findPanel.isMatchWord()
            );
            textPane.setCaretPosition(0);
            findAgain();
        }
    }
    
    public void findAgain() {
        if (matcher == null) {
            if (getViewProperties().getProperty("findString") == null) {
                showFindDialog();
                return;
            }
            matcher = new Matcher(
            textPane.getDocument(),
            getViewProperties().getProperty("findString", ""),
            getViewProperties().getProperty("findMatchCase","false").equals("true"),
            getViewProperties().getProperty("findMatchWord","false").equals("true")
            );
        }
        int index = matcher.find(
        textPane.getCaretPosition() < textPane.getDocument().getLength() - 1
        ? textPane.getCaretPosition()
        : 0
        );
        if (index == -1) {
            getToolkit().beep();
            textPane.setCaretPosition(textPane.getDocument().getLength());
            matcher.reset();
        } else {
            textPane.select(index, index+matcher.getFindString().length());
            textPane.requestFocus();
        }
    }
}
