/** *######################################################################### * Displays.java - part of the demo-client for Greenstone 3, of the * Greenstone digital library suite from the New Zealand Digital Library * Project at the * University of Waikato, New Zealand. *

* Copyright (C) 2008 New Zealand Digital Library Project *

* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. *

* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *######################################################################## */ package org.greenstone.gs3client; import org.greenstone.gs3client.data.Pair; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.Map; import java.util.Vector; import java.util.Map.Entry; import javax.swing.JEditorPane; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import org.greenstone.gs3client.data.DocumentNodeData; import org.greenstone.gs3client.data.ClassifierNodeData; import org.greenstone.gs3client.data.NodeData; /** * Class containing static methods, static variables and inner classes that * are used by both SearchResultsDisplay and BrowseDisplay. * @author ak19 */ public class Displays { /** Static Comparator object for Pair objects of (name, value) Metadata */ protected static final MetadataComparator comparator = new MetadataComparator(); /* Static methods. */ /** Recursion to add all descendent docnodes into the tree. * @param docNode - the documentNodeData object for whose children treenodes * need to be created. * @param treeNode - the treeNode associated with the docNode parameter. */ public static void createNodesForChildren(DocumentNodeData docNode, DefaultMutableTreeNode treeNode) { if(docNode == null) return; DocumentNodeData[] children = docNode.getDescendents(); if(children == null) return; // base case, the child was a leaf for(int i = 0; i < children.length; i++) { DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(children[i]); treeNode.add(childTreeNode); // recursion step to add this child node's children to // the treeNode representing it createNodesForChildren(children[i], childTreeNode); } } /** JEditorPane cannot deal with <img src="" />. It can only handle * it without ending slash: <img src="" >. * This method constructs complete html (with html, head and body tags) where * the parameter imgURL is enclosed inside an img src tag. * @param imgURL - src URL for a (web) image * @return the imgURL parameter enclosed in complete html */ public static String getImgUrlEnclosedInHtml(String baseURL, String imgURL) { //return ""; // old Greenstone3 String htmlStr = ""; int index = imgURL.indexOf(""; } htmlStr += ""; return htmlStr; } /** Sorts the metadata of the docNode into the required order and displays * metadata names in the JList metanames and the associated metadata values * in the JList metavalues. * The order is based on metanames: first come all the official metadata * sets which are recognised by the existence of a period in the metaname * (eg. dc.creator, dls.title). These are arranged alphabetically. * Next come all the metadata names that start with a capital letter - * arranged alphabetically; * the remainder are organised alphabetically as well, with the last ones * being those metadata whose names do not start with a letter * (e.g. "/srclink"). * @param nodeData is the NodeData object (classifierNodeData or * documentNodeData) * whose metadata is to be displayed in the metanames and metavalues components. * @param metanames - a JList to display all the metadata names of nodeData * @param metavalues - a JList to display all the metadata values of nodeData */ public static void showMeta(NodeData nodeData, JList metanames, JList metavalues) { // get the metadata names and values, put them // into separate arrays and set the contents of // JLists metanames and metavalues to these arrays. Map metadata = nodeData.getMetadataList(); if(metadata != null && metadata.size() > 0) { Iterator i = metadata.entrySet().iterator(); Vector nameValuePairs = new Vector(metadata.size()); while(i.hasNext()) { Entry entry = (Entry)i.next(); String name = (String)entry.getKey(); Vector values = (Vector)entry.getValue(); for(int j = 0; j < values.size(); j++) { nameValuePairs.add(new Pair(name, (String)values.get(j))); } } // first sort the Vectors with our comparator: // see http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html // method Collections.sort(List l, Comparator c) // and see http://java.sun.com/j2se/1.4.2/docs/api/java/util/Comparator.html // Need to sort the nameValuePairs based on the ordering of the // *names* portion of each Pair. // This ordering is defined by the MetadataComparator object // comparator // OLD: This ordering is defined by the Comparable interface implemented // by the vector contents. No comparator was passed into // Collections.sort(List l) because the vector's contents were Pairs // which implemented Comparable and therefore the elements were sorted // based on their "natural ordering". // see http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Comparable.html // Collections.sort(nameValuePairs); Collections.sort(nameValuePairs, comparator); metanames.setListData(Pair.getFirst(nameValuePairs)); metavalues.setListData(Pair.getSecond(nameValuePairs)); nameValuePairs.clear(); nameValuePairs = null; } } /** Handles rightclicks on a treeview of documentNodeData objects by showing * the popupMenu with associated files that, when selected, can be displayed * in the htmlPane. Listens to rightclick events, but also handles clicks on * popup menu items. * @see Java tutorial on menus */ static class PopupListener extends MouseAdapter implements ActionListener { /** Handle to the running GS3JavaClient object to have access to its * methods */ final GS3JavaClient client; /** A popup component */ final JPopupMenu popupMenu; /** The treeView component on which the rightclicks occurred */ final JTree tree; /** The htmlPane wherein associated files are to be displayed */ final JEditorPane htmlArea; /** Package access inner class. Not static, so it depends on outer class' * PopupListener instance. * Represents a popup MenuItem that appears on rightclick and contains the * name(s) of associated files which will be displayed - if any are selected * - in the main htmlpane. */ class AssocFilePopupItem extends JMenuItem { /** The documentNodeData that was rightclicked upon */ final DocumentNodeData docNode; /** Constructor that creates an AssocFilePopupItem (popup menu item) * instance for each associated file of a documentNodeData that's been * rightclicked on. This popupitem displays the name of the associated * file as a menu item of the popup. * The outer PopupListener object is set to listen to mouseEvents on * this PopupMenuItem. It will listen to clicks on this menuItem to * then display the image. */ public AssocFilePopupItem(DocumentNodeData docNode, String name) { super(name); this.docNode = docNode; // PopupListener will also process clicks on this shortcut menuitem this.addActionListener(PopupListener.this); } } /** Constructor for the PopupListener. * @param popupMenu - the JPopupMenu object this PopupListener * will handle mouseEvents for * @param tree - the JTree object whose documentNodeData was clicked * @param client - handle to the running GS3JavaClient instance * @param htmlArea - the html editor pane in which to display the * associated file chosen from the popupMenu. */ public PopupListener(JPopupMenu popupMenu, JTree tree, GS3JavaClient client, JEditorPane htmlArea) { this.client = client; this.popupMenu = popupMenu; this.tree = tree; this.htmlArea = htmlArea; } /** Called when one of the popup's menuItems was clicked. * The associated file represented by the menuItem is displayed in the * html editor pane. */ public void actionPerformed(ActionEvent e) { // TODO: later, when all gsdlassocfiles are available for each docNode, // need to change the setText below to make use of imageName and // the docNode's root's assocFilePath AssocFilePopupItem menuItem = (AssocFilePopupItem)e.getSource(); DocumentNodeData docNode = menuItem.docNode; if(docNode.canBeImage()) htmlArea.setText( getImgUrlEnclosedInHtml(client.getBaseURL(), docNode.getImgURL())); // JEditorPane does not understand ! So use only else { String filename = menuItem.getText(); String filepath = client.getFilePath(docNode); // for the latest exported greenstone colllections // into fedora, Greenstone prefixes FG in front of image name... htmlArea.setText(getImgUrlEnclosedInHtml(client.getBaseURL(), filepath+filename)); } htmlArea.setCaretPosition(0); popupMenu.removeAll(); } /** Possibly a popupEvent */ public void mouseReleased(MouseEvent e) { // for some reason the popup is visible in miniature, make it // invisible, then decide whether it needs to be shown again popupMenu.setVisible(false); maybeShowPopup(e); } /** Possibly a popupEvent */ public void mousePressed(MouseEvent e) { maybeShowPopup(e); } /** If the mouseEvent was a trigger (rightclick), displays the popup * context menu. */ protected void maybeShowPopup(MouseEvent e) { // For some reason e.isPopupTrigger() does not work. (Probably // because the rightclick sometimes happens on the tiny popup // which is visible but too small to see.) // The rightclick appears to only be registered on the popup: // and popup.isPopupTrigger(e) responds even when the popup // is already visible if(popupMenu.isPopupTrigger(e)) { // remove old rightclick menu items popupMenu.removeAll(); // get the closest node in the docStructureTree to that which // was rightclicked TreePath path = tree.getClosestPathForLocation( e.getX(), e.getY()); // the following line will select this nearest node and trigger // TreeSelectionListener by calling valueChanged(), which will // retrieve the structure and display the metadata: (tree.getSelectionModel()).setSelectionPath(path); DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); if(node.getUserObject() instanceof ClassifierNodeData) // return; // DocumentNodeData docNode = (DocumentNodeData)node.getUserObject(); // We need the root and the image url part of the metadata, so // ensure we have that: if(docNode.getRoot() == null) // client.retrieveTitledStructureFor(docNode); // Vector v = docNode.getAssociatedFileNames(); if(v.size() > 0) { // there are associated files for(int i = 0; i < v.size(); i++) { Pair p = (Pair)v.get(i); JMenuItem menuItem = new AssocFilePopupItem(docNode, p.first); popupMenu.add(menuItem); } popupMenu.show(e.getComponent(), e.getX(), e.getY()); } else if(docNode.canBeImage()) { // add rightclick popup menu that will // display image upon click JMenuItem menuItem = new AssocFilePopupItem(docNode, docNode.getImgName()); popupMenu.add(menuItem); popupMenu.show(e.getComponent(), e.getX(), e.getY()); } } } } /** Static inner class MetadataComparator is a Comparator for the metadata * fields stored as a list of Pair objects (name, values). * We want to display them alphabetised, grouped by prefix (dls.title, dls.x; * dc.title; dc.creator; greenstone's ex metadata does not have a prefix) * and in order of alphabet BUT all those starting with capital letters come * first, then come those starting with lowercase letters. * Those with prefixes come first of all. * @see http://java.sun.com/j2se/1.4.2/docs/api/java/util/Comparator.html * Comparators can be passed to a sort method (such as Collections.sort) to * allow precise control over the sort order. Comparators can also be used to * control the order of certain data structures (such as TreeSet or TreeMap). */ public static class MetadataComparator implements Comparator { public int compare(Object nodeID1, Object nodeID2) { if(nodeID1 == null && nodeID2 == null) return 0; if(nodeID1 == null) return 1; if(nodeID2 == null) return -1; String metaName1 = ((Pair)nodeID1).first; String metaName2 = ((Pair)nodeID2).first; if(metaName1.equals(metaName2)) return 0; // same if(metaName1.indexOf('.') != -1 && metaName2.indexOf('.') == -1) return -1; // only c1 has a period, so c1 comes before c2 if(metaName2.indexOf('.') != -1 && metaName1.indexOf('.') == -1) return 1; // only c2 has a period, so c2 comes before c1 // we're going to inspect the first letters, so store these char c1 = metaName1.charAt(0); char c2 = metaName2.charAt(0); if(Character.isUpperCase(c1) && Character.isLowerCase(c2)) return -1; // only c1 starts with uppercase, so c1 comes before c2 if(Character.isUpperCase(c2) && Character.isLowerCase(c1)) return 1; // only c2 starts with uppercase, so c2 comes before c1 // we want metadatanames like "/srclink" to come at the end, because // they don't start with a letter if(!Character.isLetter(c2) && Character.isLetter(c1)) return -1; // if c2 starts with some strange character, // c1 comes before c2 in the ordering if(!Character.isLetter(c1) && Character.isLetter(c2)) return 1; // if c1 starts with some strange character, // c2 comes before c1 in the ordering // otherwise, all else being equal (both start with // uppercase/lowercase or both contain/don't contain periods or // both start with/don't start with letters): so sort them in // alphabetic order. return metaName1.compareTo(metaName2); } //public boolean equals(Object obj) { // return obj.equals(this); //} } }