/** *######################################################################### * SearchResultsDisplay.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 java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.BorderLayout; import java.awt.FlowLayout; import javax.swing.JPopupMenu; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JEditorPane; import javax.swing.JList; import javax.swing.JLabel; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeSelectionModel; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.text.html.HTMLDocument; import org.apache.log4j.Logger; import org.greenstone.gs3client.data.DocumentNodeData; import java.net.URL; import java.net.MalformedURLException; /** * The Search panel inside the Java-client's tab pane that's labelled * "Search Results". This panel contains two tree views: * - one for displaying the list of search results (top-level documents or document * - one for displaying the structure of document nodes selected in the list of * search results. * This panel also contains an area where the selected documentNode's metadata is * displayed, and a text area where the textual or image content of a selected * documentNode is displayed. * @author ak19 */ public class SearchResultsDisplay extends JPanel implements TreeSelectionListener, ColourCombo.ColourChangeable { /** The Logger for this class */ static Logger LOG = Logger.getLogger(SearchResultsDisplay.class); /** Access to the running instance of GS3JavaClient */ protected GS3JavaClient client; /* GUI items of this SearchResults Panel */ protected JTree searchResultsTree, docStructureTree; protected JSplitPane docInfoPane, docInfoWithContent, treeviewSplitPane; protected JEditorPane docContentEditPane; protected JList metanames, metavalues; /** Context menu that pops up when users right click in the browse tree area */ protected JPopupMenu popup; /** Constructor that creates the Search Panel and its internal GUI items. * @param client is the running instance of the client application through * which its methods can be accessed. */ public SearchResultsDisplay(GS3JavaClient client) { super(new FlowLayout(FlowLayout.LEFT)); // so the contents of this panel are not centred this.client = client; // The panels: searchresults (with a tree), docStructure (also // contains a tree), metadataPanel (with 2 lists for metanames // and metavalues and docContentEditor to show a selected html // document's contents JPanel searchResultsPanel, docStructurePanel, metadataPanel; this.docContentEditPane = new JEditorPane(); this.docContentEditPane.setEditable(false); this.docContentEditPane.setContentType("text/html"); //"text/plain" to view the html markup searchResultsPanel = new JPanel(new BorderLayout()); docStructurePanel = new JPanel(new BorderLayout()); searchResultsPanel.add( new JLabel("Documents returned"), BorderLayout.NORTH); docStructurePanel.add( new JLabel("Document structure"), BorderLayout.NORTH); metadataPanel = new JPanel(new BorderLayout()); this.metanames = new JList(); this.metavalues = new JList(); metadataPanel.add(metanames, BorderLayout.WEST); metadataPanel.add(metavalues, BorderLayout.CENTER); JPanel metaSuperPanel = new JPanel(new BorderLayout()); metaSuperPanel.add(new JLabel("Metadata"), BorderLayout.NORTH); metaSuperPanel.add( new JScrollPane(metadataPanel), BorderLayout.CENTER); // Create the searchresults tree and the docStructure tree // (neither of them initialised to any meaningful nodes at present). // Both trees allow only one selection at a time. // Add a listener to both for when their treenode selections change. // Finally add them to their respective JPanels. searchResultsTree = new JTree(new DefaultMutableTreeNode(null)); searchResultsTree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION); searchResultsTree.addTreeSelectionListener(this); searchResultsPanel.add(new JScrollPane(searchResultsTree), BorderLayout.CENTER); docStructureTree = new JTree(new DefaultMutableTreeNode(null)); docStructureTree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION); docStructureTree.addTreeSelectionListener(this); docStructurePanel.add(new JScrollPane(docStructureTree), BorderLayout.CENTER); // add a (rightclick) popup menu to the document structure tree: popup = new JPopupMenu(); docStructureTree.setComponentPopupMenu(popup); docStructureTree.addMouseListener(new Displays.PopupListener( popup, docStructureTree, client, this.docContentEditPane)); // Add the docStructure and metadata panels next to each // other within a split pane docInfoPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); docInfoPane.setLeftComponent(docStructurePanel); docInfoPane.setRightComponent(metaSuperPanel); docInfoPane.setOneTouchExpandable(true); // Add that to the top of the editorpanel showing the contents of // any selected document nodes docInfoWithContent = new JSplitPane(JSplitPane.VERTICAL_SPLIT); docInfoWithContent.setTopComponent(docInfoPane); docInfoWithContent.setBottomComponent( new JScrollPane(this.docContentEditPane)); docInfoWithContent.setOneTouchExpandable(true); // Add that splitpane to the right of the search results panel // in a new splitpane (horizontal) treeviewSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); treeviewSplitPane.setLeftComponent(searchResultsPanel); treeviewSplitPane.setRightComponent(this.docInfoWithContent); treeviewSplitPane.setOneTouchExpandable(true); this.add(treeviewSplitPane); // All the scrollviews and the splitpanes will be sized // when getPreferredSize() is called on show()/setVisible() } /** Overrode this method to resize the splitpanes within, upon resize. * It calculates the size of this panel, as well as setting those of * the splitpanes it contains based on the size of the parent container. * @return the preferred dimensions of this JPanel. */ public Dimension getPreferredSize() { Dimension size = this.getParent().getSize(); //900, 500 int x = (int)size.getWidth() / 3; int y = (int)(size.getHeight() / 5 * 2); docInfoPane.setDividerLocation(x); docInfoWithContent.setDividerLocation(y); treeviewSplitPane.setDividerLocation(x); docInfoPane.setPreferredSize(size); docInfoWithContent.setPreferredSize(size); treeviewSplitPane.setPreferredSize(size); return size; } /** Changes the colour of the query form and its controls to the * current colours set in class ColourCombo. Specified by * the ColourCombo.ColourChangeable interface. */ public void changeUIColour() { Component[] comps = { this, this.docStructureTree, this.searchResultsTree, metanames, metavalues, popup }; ColourCombo.changeColor(comps); // ensures that the JLabel for "metadata" and the trees // are coloured properly: ColourCombo.changeAncestorColor(metanames); ColourCombo.changeAncestorColor(this.docStructureTree); ColourCombo.changeAncestorColor(this.searchResultsTree); } /** Clears the service-specific buttons in the browseBar and the * service-specific display-data. The rest of the GUI (split panes, panels) * remain as they are. Resets the contents of the widgets in this panel. */ public void clear() { //searchResultsTree.removeAll(); //docStructureTree.removeAll(); // doesn't seem to emtpy tree //this.metanames.removeAll(); //doesn't seem to empty the list values //this.metavalues.removeAll(); //doesn't seem to empty the list values // empty the docStructureTree's popup menu too popup.removeAll(); // empty the trees ((DefaultTreeModel)searchResultsTree.getModel()).setRoot(null); ((DefaultTreeModel)docStructureTree.getModel()).setRoot(null); // empty the metadatalists final String[] empty = {""}; this.metanames.setListData(empty); this.metavalues.setListData(empty); // empty the html view area this.docContentEditPane.setText(""); } /** Restructures the searchResultsTree when another search has been made, so * that the search results contain a new list of documentNodeData objects. * @param resultDocs - the list of (new) DocumentNodeData objects that have * been returned as the results of a query/search operation. */ public void setResults(DocumentNodeData[] resultDocs) { // Empty out whatever was in our searchResultsTree and in the // other displays clear(); // Populate the searchresults tree with each node being a document // that represents a different search result DefaultMutableTreeNode newRoot = new DefaultMutableTreeNode(); for(int i = 0; i < resultDocs.length; i++) newRoot.add(new DefaultMutableTreeNode(resultDocs[i])); DefaultTreeModel model = (DefaultTreeModel)searchResultsTree.getModel(); model.setRoot(newRoot); // We do not want a root, without a root it is perfect for linear // display of search results (a.o.t. hierarchical display) searchResultsTree.setRootVisible(false); // hierarchical display of the first search result (if its root is set) if(resultDocs.length > 0) // array may be empty if there were no resultdocs this.restructureWithNewRoot(resultDocs[0].getRoot()); } /** Restructures the docStructureTree with a new rootNode when a different * search result has been clicked. * @param rootDocNode - the new root DocumentNodeData whose structure is * to be displayed in the docStructureTree. */ protected void restructureWithNewRoot(DocumentNodeData rootDocNode) { DefaultMutableTreeNode root = null; if(rootDocNode != null) { // Create the nodes. root = new DefaultMutableTreeNode(rootDocNode); Displays.createNodesForChildren(rootDocNode, root); } DefaultTreeModel model = (DefaultTreeModel)docStructureTree.getModel(); docStructureTree.removeAll(); model.setRoot(root); // empty the contentpane this.docContentEditPane.setText(""); } /** Part of the TreeSelectionListener interface. When this is called, an item * has been clicked in either the searchResultsTree or the docStructureTree. * This method displays the metadata and textual contents accordingly * of the selected documentNodeData object. */ public void valueChanged(TreeSelectionEvent e) { // remove any menuItems in the popup from the previously // selected docNode popup.removeAll(); DefaultMutableTreeNode node = null; if(e.getSource() == searchResultsTree) node = (DefaultMutableTreeNode) searchResultsTree.getLastSelectedPathComponent(); else if(e.getSource() == docStructureTree) node = (DefaultMutableTreeNode) docStructureTree.getLastSelectedPathComponent(); if(node == null) return; Object nodeInfo = node.getUserObject(); // after construction there's nothing in the trees, so check for that: if(nodeInfo == null) return; // We need to change to a Wait cursor while we load the documentNode Container c = client.getContentPane(); c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); DocumentNodeData docNode = (DocumentNodeData)nodeInfo; // If user had clicked on a searchResult: if(e.getSource() == this.searchResultsTree && node.isLeaf()) { this.client.retrieveTitledStructureFor(docNode); // Now it has set the root and descendents for the // selected docNode DocumentNodeData root = docNode.getRoot(); // get the nodestructuredisplay from the client // nodestructure.restructureWithNewRoot(root, docNode); this.restructureWithNewRoot(root); // First ensure the metadata for the docNode is set client.retrieveAllMetadataFor(docNode); // now metadata for the document is set // Now, if metadata viewable, then display the metadata // for the root document node: Displays.showMeta(docNode, metanames, metavalues); // make the client retrieve the content for the docNode // that's clicked on, and display it in the docContentEditPane client.retrieveContentFor(docNode); // JEditorPane can't handle Justify, it centers such paras // Therefore, left align them instead. String docContent = docNode.getContent(); if(docContent != null) { docContent = docContent.replaceAll( "ALIGN=\"JUSTIFY\"", "ALIGN=\"LEFT\""); } this.docContentEditPane.setText(docContent); this.docContentEditPane.setCaretPosition(0); // set 'cursor' at top } // If the user had clicked on any docNode - leaf or otherwise // - in the docStructureTree panel: else if(e.getSource() == this.docStructureTree) { // First ensure the metadata for the docNode is set client.retrieveAllMetadataFor(docNode); // now metadata for the document is set // Display the metadata for this document node: Displays.showMeta(docNode, metanames, metavalues); // if the document has NoText set to 1 (doc has no text), then it // has an image - display this by default if(docNode.hasNoText() && node.isLeaf() /*&& docNode.canBeImage()*/) { this.docContentEditPane.setText( Displays.getImgUrlEnclosedInHtml(docNode.getImgURL())); this.docContentEditPane.setCaretPosition(0); } else { // now we know that the document must have text: // make the client retrieve the content for the docNode // that's clicked on, and display it in the docContentEditPane client.retrieveContentFor(docNode); // Java's htmlpane does not recognise justified alignment. // It displays them all centred. So replace all ALIGN="JUSTIFY" // with ALIGN="LEFT" here. (Not in the DocumentNodeData class, // because our nodeContent should not be transformed: it will // display properly in browsers and other html displays). String docContent = docNode.getContent(); if(docContent != null) { docContent = docContent.replaceAll( "ALIGN=\"JUSTIFY\"", "ALIGN=\"LEFT\""); } // set the baseURL for digital libraries that use relative paths // to images and don't take care of base paths themselves // (GS3 uses the _httpdocimg_ macro to resolve relative urls) String baseURL = client.getBaseURL(); // TODO: make the docNode itself workout its URL by passing // baseURL to the docNode? if(!baseURL.equals("") && docNode.getRoot() != null) { // "" is the case where dlAPIA = gs3 // where the _httpdocimg_ macro will deal with // resolving the relative urls into their full ones // We don't want to set the base and meddle with macro // for those cases. // For Fedora's case: HTMLDocument doc = (HTMLDocument)this.docContentEditPane.getDocument(); try{ URL url = new URL(baseURL+docNode.getRoot().nodeID+"/"); //System.err.println("url: " + url.toString()); doc.setBase(url); //docContent = docContent.replaceAll("_httpdocimg_/", ""); LOG.debug(docContent); }catch(MalformedURLException mex) { ; //nothing to be done, leave the base as it is } } this.docContentEditPane.setText(docContent); // make sure 'cursor' (actually caret) is at the top // Don't want it to autoscroll to the bottom of the contentPane this.docContentEditPane.setCaretPosition(0); } } c.setCursor(Cursor.getDefaultCursor()); } }