source: main/trunk/gli/src/org/greenstone/gatherer/gui/MetadataValueTreePane.java@ 32706

Last change on this file since 32706 was 18370, checked in by kjdon, 15 years ago

committed code submitted by Amin Hedjazi for making the GLI right to left. I worked on this code on the rtl-gli branch, then merged the branch back to the trunk at revision 18368. The branch code was slightly different in a couple of places where it shouldn't have been. So don't use the branch code next time. Start a new branch.

  • Property svn:keywords set to Author Date Id Revision
File size: 15.0 KB
Line 
1/**
2 *############################################################################
3 * A component of the Greenstone Librarian Interface, part of the Greenstone
4 * digital library suite from the New Zealand Digital Library Project at the
5 * University of Waikato, New Zealand.
6 *
7 * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
8 *
9 * Copyright (C) 2005 New Zealand Digital Library Project
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *############################################################################
25 */
26
27package org.greenstone.gatherer.gui;
28
29
30import java.awt.*;
31import javax.swing.*;
32import javax.swing.event.*;
33import javax.swing.tree.*;
34import org.greenstone.gatherer.Configuration;
35import org.greenstone.gatherer.Dictionary;
36import org.greenstone.gatherer.metadata.MetadataElement;
37import org.greenstone.gatherer.metadata.MetadataValue;
38import org.greenstone.gatherer.metadata.MetadataValueTreeNode;
39import org.greenstone.gatherer.util.PatternTokenizer;
40
41
42/**
43 * This class is a little bit complex, a little bit subtle, and a little bit odd.
44 * The strange interaction model is due to the fact that it is very tightly
45 * coupled to the EnrichPane.
46 *
47 * The interaction is complex because there are three separate controls that the
48 * user may interact with, each of which can affect the other two. The three
49 * controls are:
50 * - The "assigned metadata" table, at the top right of the meta edit pane.
51 * - The "metadata value" text field, where users can type in new values.
52 * - The "metadata value tree", which shows other values that have been
53 * assigned to the selected metadata element.
54 *
55 * The interactions are:
56 * - The "assigned metadata" table
57 * Users may select one (and only one) row in this table. Selecting a row
58 * chooses one metadata element. The text field will be set to the current
59 * value of the metadata element. This value will also be selected in the
60 * metadata value tree.
61 *
62 * - The "metadata value" text field
63 * If the value the user has typed in this is a prefix of an entry in the
64 * value tree, this value will be selected in the value tree. In this case,
65 * pressing "Tab" will complete the value (ie. make the value in the text
66 * field the same as the value in the tree). This is to allow users to
67 * quickly and easily assign existing metadata values to new documents.
68 *
69 * - The "metadata value tree"
70 * Selecting a value in the tree will set the text field to this value.
71 */
72public class MetadataValueTreePane
73 extends JPanel
74{
75 private boolean ignore_tree_selection_event = false;
76 /** The metadata value that the tree pane is built on */
77 private MetadataValue metadata_value = null;
78
79 /** Used to display either the MetadataValueTree (when metadata is selected) or a placeholder panel */
80 private CardLayout card_layout = null;
81 /** The name of the panel containing the metadata value tree */
82 private String METADATA_VALUE_TREE_CARD = "";
83 /** The name of the panel containing the "extracted metadata element selected" placeholder */
84 private String EXTRACTED_METADATA_ELEMENT_SELECTED_CARD = "Extracted metadata element selected";
85 /** The name of the panel containing the "inherited metadata selected" placeholder */
86 private String INHERITED_METADATA_SELECTED_CARD = "Inherited metadata selected";
87 /** The name of the panel containing the "not only file only metadata selected" placeholder */
88 private String NOT_ONE_FILE_ONLY_METADATA_SELECTED_CARD = "Not one file only metadata selected";
89 /** The name of the panel containing the "no metadata element selected" placeholder */
90 private String NO_METADATA_ELEMENT_SELECTED_CARD = "No metadata element selected";
91
92 private JLabel metadata_value_tree_label = new JLabel();
93 private JTextArea extracted_metadata_element_selected_message;
94 private JTree metadata_value_tree;
95
96
97 public MetadataValueTreePane()
98 {
99 super();
100 JScrollPane scrol_tmp;
101 this.setComponentOrientation(Dictionary.getOrientation());
102 metadata_value_tree_label.setComponentOrientation(Dictionary.getOrientation());
103 // Card containing the metadata value tree
104 metadata_value_tree = new JTree();
105 metadata_value_tree.setComponentOrientation(Dictionary.getOrientation());
106 metadata_value_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
107 metadata_value_tree.setModel(null);
108 metadata_value_tree.setRootVisible(false);
109 metadata_value_tree.putClientProperty("JTree.lineStyle", "Angled");
110
111 JPanel metadata_value_tree_pane = new JPanel();
112 metadata_value_tree_pane.setComponentOrientation(Dictionary.getOrientation());
113 metadata_value_tree_pane.setLayout(new BorderLayout());
114 metadata_value_tree_pane.add(metadata_value_tree_label, BorderLayout.NORTH);
115 scrol_tmp = new JScrollPane(metadata_value_tree);
116 scrol_tmp.setComponentOrientation(Dictionary.getOrientation());
117 metadata_value_tree_pane.add(scrol_tmp, BorderLayout.CENTER);
118
119 // Card containing the "extracted metadata element selected" message
120 extracted_metadata_element_selected_message = new JTextArea("");
121 extracted_metadata_element_selected_message.setComponentOrientation(Dictionary.getOrientation());
122 extracted_metadata_element_selected_message.setEditable(false);
123 extracted_metadata_element_selected_message.setLineWrap(true);
124 extracted_metadata_element_selected_message.setOpaque(false);
125 extracted_metadata_element_selected_message.setWrapStyleWord(true);
126
127 JPanel extracted_metadata_element_selected_pane = new JPanel();
128 extracted_metadata_element_selected_pane.setComponentOrientation(Dictionary.getOrientation());
129 extracted_metadata_element_selected_pane.setBorder(BorderFactory.createEmptyBorder(25,0,0,0));
130 extracted_metadata_element_selected_pane.setLayout(new BorderLayout());
131 extracted_metadata_element_selected_pane.add(extracted_metadata_element_selected_message, BorderLayout.CENTER);
132
133 // Card containing the "inherited metadata selected" message
134 JTextArea inherited_metadata_selected_message = new JTextArea(Dictionary.get("EnrichPane.InheritedMetadataSelected"));
135 inherited_metadata_selected_message.setEditable(false);
136 inherited_metadata_selected_message.setLineWrap(true);
137 inherited_metadata_selected_message.setOpaque(false);
138 inherited_metadata_selected_message.setWrapStyleWord(true);
139 inherited_metadata_selected_message.setComponentOrientation(Dictionary.getOrientation());
140
141 JPanel inherited_metadata_selected_pane = new JPanel();
142 inherited_metadata_selected_pane.setBorder(BorderFactory.createEmptyBorder(25,0,0,0));
143 inherited_metadata_selected_pane.setLayout(new BorderLayout());
144 inherited_metadata_selected_pane.add(inherited_metadata_selected_message, BorderLayout.CENTER);
145 inherited_metadata_selected_pane.setComponentOrientation(Dictionary.getOrientation());
146 // Card containing the "not one file only metadata selected" message
147 JTextArea not_one_file_only_metadata_selected_message = new JTextArea(Dictionary.get("EnrichPane.NotOneFileOnlyMetadataSelected"));
148 not_one_file_only_metadata_selected_message.setEditable(false);
149 not_one_file_only_metadata_selected_message.setLineWrap(true);
150 not_one_file_only_metadata_selected_message.setOpaque(false);
151 not_one_file_only_metadata_selected_message.setWrapStyleWord(true);
152 not_one_file_only_metadata_selected_message.setComponentOrientation(Dictionary.getOrientation());
153
154 JPanel not_one_file_only_metadata_selected_pane = new JPanel();
155 not_one_file_only_metadata_selected_pane.setBorder(BorderFactory.createEmptyBorder(25,0,0,0));
156 not_one_file_only_metadata_selected_pane.setLayout(new BorderLayout());
157 not_one_file_only_metadata_selected_pane.add(not_one_file_only_metadata_selected_message, BorderLayout.CENTER);
158 not_one_file_only_metadata_selected_pane.setComponentOrientation(Dictionary.getOrientation());
159
160 // Card containing the "no metadata element selected" message
161 JLabel no_metadata_element_selected_label = new JLabel(Dictionary.get("EnrichPane.No_Metadata_Element"));
162 no_metadata_element_selected_label.setHorizontalAlignment(JLabel.CENTER);
163 no_metadata_element_selected_label.setOpaque(false);
164 no_metadata_element_selected_label.setVerticalAlignment(JLabel.CENTER);
165 no_metadata_element_selected_label.setComponentOrientation(Dictionary.getOrientation());
166
167 JPanel no_metadata_element_selected_pane = new JPanel();
168 no_metadata_element_selected_pane.setLayout(new BorderLayout());
169 no_metadata_element_selected_pane.add(no_metadata_element_selected_label, BorderLayout.CENTER);
170 no_metadata_element_selected_pane.setComponentOrientation(Dictionary.getOrientation());
171
172 card_layout = new CardLayout();
173
174 this.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
175 this.setFont(Configuration.getFont("general.font", false));
176 this.setLayout(card_layout);
177 this.add(no_metadata_element_selected_pane, NO_METADATA_ELEMENT_SELECTED_CARD);
178 this.add(extracted_metadata_element_selected_pane, EXTRACTED_METADATA_ELEMENT_SELECTED_CARD);
179 this.add(inherited_metadata_selected_pane, INHERITED_METADATA_SELECTED_CARD);
180 this.add(not_one_file_only_metadata_selected_pane, NOT_ONE_FILE_ONLY_METADATA_SELECTED_CARD);
181 this.add(metadata_value_tree_pane, METADATA_VALUE_TREE_CARD);
182 }
183
184
185 public void addMetadataValueTreeSelectionListener(TreeSelectionListener tree_selection_listener)
186 {
187 metadata_value_tree.addTreeSelectionListener(tree_selection_listener);
188 }
189
190
191 /** Returns a TreePath for the node most closely matching the metadata value. */
192 private TreePath getClosestPath(String metadata_value_string)
193 {
194 if (metadata_value_tree.getModel() == null) {
195 return null;
196 }
197
198 // Start at the root of the tree
199 MetadataValueTreeNode tree_node = (MetadataValueTreeNode) metadata_value_tree.getModel().getRoot();
200
201 // Separate hierarchical values
202 PatternTokenizer tokenizer = new PatternTokenizer(metadata_value_string, MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
203 while (tokenizer.hasMoreTokens()) {
204 String token = tokenizer.nextToken();
205
206 // All components except the last must match exactly
207 if (tokenizer.hasMoreTokens()) {
208 for (int i = 0; i < tree_node.getChildCount(); i++) {
209 MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
210 if (child_node.getValue().equals(token)) {
211 // The matching node has been found, so move onto the next token
212 tree_node = child_node;
213 break;
214 }
215 }
216 }
217
218 // The last component may match partially
219 else {
220 for (int i = 0; i < tree_node.getChildCount(); i++) {
221 MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
222 if (child_node.getFullValue().startsWith(metadata_value_string)) {
223 // The closest node has been found, so return its path
224 return new TreePath(child_node.getPath());
225 }
226 }
227
228 // Not even a partial match
229 return null;
230 }
231 }
232
233 // If nothing else, return the path of the root node
234 return new TreePath(tree_node.getPath());
235 }
236
237
238 public MetadataValueTreeNode getSelectedMetadataValueTreeNode()
239 {
240 if (metadata_value_tree.getSelectionCount() == 0 || ignore_tree_selection_event) {
241 return null;
242 }
243
244 return (MetadataValueTreeNode) metadata_value_tree.getSelectionPath().getLastPathComponent();
245 }
246
247
248 public void rebuild(MetadataValue new_metadata_value)
249 {
250 // If the metadata value hasn't changed there is nothing to do
251 if (new_metadata_value == metadata_value) {
252 return;
253 }
254
255 MetadataElement metadata_element = ((metadata_value != null) ? metadata_value.getMetadataElement() : null);
256 metadata_value = new_metadata_value;
257
258 // Selection cleared, so display "no metadata element selected" card and we're done
259 if (new_metadata_value == null) {
260 metadata_value_tree.setModel(null);
261 card_layout.show(this, NO_METADATA_ELEMENT_SELECTED_CARD);
262 return;
263 }
264
265 // If a piece of inherited metadata is selected, display "inherited metadata selected" card and we're done
266 if (new_metadata_value.isInheritedMetadata()) {
267 card_layout.show(this, INHERITED_METADATA_SELECTED_CARD);
268 return;
269 }
270
271 // If the metadata applies to multiple files, display "not one file only metadata selected" card and we're done
272 if (new_metadata_value.isOneFileOnlyMetadata() == false) {
273 card_layout.show(this, NOT_ONE_FILE_ONLY_METADATA_SELECTED_CARD);
274 return;
275 }
276
277 // If an extracted metadata element is selected, display "extracted metadata element selected" card
278 MetadataElement new_metadata_element = new_metadata_value.getMetadataElement();
279 if (new_metadata_element.isExtractedMetadataElement()) {
280 String[] args = new String[1];
281 args[0] = new_metadata_element.getDisplayName();
282 extracted_metadata_element_selected_message.setText(Dictionary.get("EnrichPane.AutoMessage", args));
283 card_layout.show(this, EXTRACTED_METADATA_ELEMENT_SELECTED_CARD);
284 return;
285 }
286
287 // Display the value tree for the selected metadata element
288 String[] args = new String[1];
289 args[0] = new_metadata_element.getDisplayName();
290 metadata_value_tree_label.setText(Dictionary.get("EnrichPane.ExistingValues", args));
291
292 metadata_value_tree.setModel(new_metadata_element.getMetadataValueTreeModel());
293 card_layout.show(this, METADATA_VALUE_TREE_CARD);
294 selectBestPathForMetadataValue(new_metadata_value.getFullValue());
295 }
296
297
298 public void selectBestPathForMetadataValue(String metadata_value_string)
299 {
300 TreePath best_path = getClosestPath(metadata_value_string);
301
302 // Select the new path in the tree
303 // The tree selection event this causes must be ignored, since it alters the metadata text field value
304 ignore_tree_selection_event = true;
305 metadata_value_tree.setSelectionPath(best_path);
306 ignore_tree_selection_event = false;
307
308 // If a folder has been specified, make sure it is expanded
309 if (metadata_value_string.endsWith(MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN) && !metadata_value_tree.isExpanded(best_path)) {
310 metadata_value_tree.expandPath(best_path);
311 }
312
313 // Make sure the tree path is expanded
314 metadata_value_tree.makeVisible(best_path);
315
316 // Make sure the tree path is visible on screen
317 final Rectangle bounds = metadata_value_tree.getPathBounds(best_path);
318 if (bounds != null) {
319 bounds.x = 0; // Want the tree to be always flushed left
320
321 // Scrolling the path to be visible must be done on the GUI thread
322 Runnable task = new Runnable() {
323 public void run() {
324 metadata_value_tree.scrollRectToVisible(bounds);
325 }
326 };
327 SwingUtilities.invokeLater(task);
328 }
329 }
330}
Note: See TracBrowser for help on using the repository browser.