Changeset 5176
- Timestamp:
- 2003-08-18T15:20:16+12:00 (21 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gli/src/org/greenstone/gatherer/gui/MetaEditPane.java
r4686 r5176 47 47 import org.greenstone.gatherer.Gatherer; 48 48 import org.greenstone.gatherer.file.FileNode; 49 import org.greenstone.gatherer.file.FileOpenActionListener;50 49 import org.greenstone.gatherer.gui.Filter; 51 50 import org.greenstone.gatherer.gui.GComboBox; … … 63 62 import org.greenstone.gatherer.util.TreeSynchronizer; 64 63 import org.greenstone.gatherer.util.Utility; 65 import org.greenstone.gatherer.util.XORToggleButtonGroup;66 import org.greenstone.gatherer.valuetree.GValueTree;67 64 import org.greenstone.gatherer.valuetree.GValueModel; 68 65 import org.greenstone.gatherer.valuetree.GValueNode; 69 import org.w3c.dom.Element;70 66 /** Provides a view of controls for the editing of metadata. It makes use of several other important components such as a GTree, GTable and GValueTree. While much of the gui content is left to these components, the MetaEditPane is resposible for actioning metadata edit requests, listing for mouse clicks within its scope and other data functionality (such as providing a list of the selected files). 71 67 * @author John Thompson, Greenstone Digital Libraries, University of Waikato … … 74 70 public class MetaEditPane 75 71 extends JPanel 76 implements ActionListener, ListSelectionListener, TreeModelListener, TreeSelectionListener, MSMListener { 77 /** <i>true</i> if the selection has changed since the last time it was asked for, <i>false</i> otherwise. */ 78 public boolean has_changed = false; 72 implements ActionListener, ListSelectionListener, TreeSelectionListener, MSMListener { 79 73 /** The GValueTree graphically shows the available metadata that has been previously assigned, and provides controls for adding, updating and removing metadata, depending on the users selections in the other important components. */ 80 public GValueTree tree = null; 81 /** If only files are selected, then show folder is not available. */ 82 private boolean show_folder_enabled = false; 74 private GValueTree tree = null; 83 75 /** The layout manager used to display either the GTable (when records are selected) or a placeholder panel with the immortal words 'No Record Selected'. */ 84 76 private CardLayout card_layout = null; … … 91 83 /** The currently reported selection. Note that this may actually be incorrect, but if so the changed flag will be set, and the selection will be updated the next time it is needed. */ 92 84 private FileNode records[] = null; 93 /** Records which of the three GTable views is currently being shown, so we can determine what view to change to next (as they cycle). */94 //private int current_table_view;95 /** The mode is determined by the metadata edit action choosen. */96 private int mode;97 85 /** The button, which when clicked, adds metadata to the selected records. */ 98 86 private JButton add; 99 87 /** The button, which when clicked, removes the selected metadata from the selected records. */ 100 88 private JButton remove; 101 /** The button, which when clicked, changes the currently shown GTable view. */102 //private JButton table_view;103 89 /** The button, which when clicked, updates the selected metadata from the selected records. */ 104 90 private JButton update; … … 111 97 /** The panel in which the metadata table card layout resides. */ 112 98 private JPanel table_card_pane; 113 /** The panel the table is added to. */114 private JPanel table_pane;115 /** An icon showing the current state of the table build. */116 //private JProgressBar activity_bar;117 99 /** The splitpane dividing the collection tree and the metadata based controls. */ 118 100 private JSplitPane external_split; 119 /** Divides the metadata table and the value tree controls. */120 private JSplitPane main_split_pane;121 101 /** A reference to the metadata table, via its superclass. */ 122 102 private JTable table; 123 103 /** The label shown at the top of the metadata table detailing the current selection statistics. */ 124 104 private JTextField table_label; 125 /** The button to control whether assigned metadata is shown. */126 //private JToggleButton assigned_metadata_view;127 /** The button to control whether unassigned metadata is shown. */128 //private JToggleButton unassigned_metadata_view;129 105 /** A reference to the collection tree. */ 130 p ublicDragTree collection_tree;106 private DragTree collection_tree; 131 107 /** The currently selected metadata determined by listening to every second list selection event from the metadata table. */ 132 108 private Metadata selected_metadata; 133 /** Listens for right clicks over the collection tree. */134 private RightButtonListener right_button_listener = null;135 109 /** A temporary storage array from Strings passed to the dictionary to be inserted in the phrase returned. */ 136 110 private String args[]; 137 111 /** Provide synchronization between the collection trees in this view and the collection pane view. */ 138 112 private TreeSynchronizer tree_sync = null; 139 static p ublicDimension BUTTON_SIZE = new Dimension(190, 25);113 static private Dimension BUTTON_SIZE = new Dimension(190, 25); 140 114 static private Dimension CONTROL_SIZE = new Dimension(560, 240); 141 static private Dimension LABEL_SIZE = new Dimension(75, 25);142 115 static private Dimension MINIMUM_SIZE = new Dimension(100, 100); 143 116 static private Dimension TABLE_SIZE = new Dimension(560, 25); 144 117 static private Dimension TREE_SIZE = new Dimension(250, 500); 145 /** An element of the Mode type enumeration, indicating we are adding metadata (ie no metadata rows are selected in the metadata table). */146 static private int ADD = 0;147 /** An element of the Mode type enumeration, indicating we are editing existing metadata (ie some row is selected in the metadata table). */148 static private int EDIT = 1;149 118 /** The name of the panel containing the metadata table. */ 150 119 static final private String CARD_ONE = "Card One"; … … 157 126 /** The name of the panel containing the value tree. */ 158 127 static final private String TOOLS_ON = "Tools On"; 128 159 129 /** Constructor. 160 130 * @param tree_sync The <strong>TreeSynchronizer</strong> to be used on the collection tree. … … 164 134 */ 165 135 public MetaEditPane(TreeSynchronizer tree_sync) { 166 //this.current_table_view = GTableModel.SHOW_FILE;167 136 this.tree = null; 168 137 this.tree_sync = tree_sync; … … 192 161 expand.setPreferredSize(new Dimension(25, 25)); 193 162 194 tree = new GValueTree(this, CONTROL_SIZE.width, CONTROL_SIZE.height, add, update, remove, expand); 195 } 163 tree = new GValueTree(this, CONTROL_SIZE.width, CONTROL_SIZE.height); 164 } 165 196 166 /** Called whenever an action occurs on one of our registered buttons. 197 198 199 200 201 202 203 204 167 * @param event An <strong>ActionEvent</strong> containing information about the event. 168 * @see org.greenstone.gatherer.collection.CollectionManager 169 * @see org.greenstone.gatherer.gui.table.GTableModel 170 * @see org.greenstone.gatherer.msm.ElementWrapper 171 * @see org.greenstone.gatherer.msm.MetadataSetManager 172 * @see org.greenstone.gatherer.msm.MSMEvent 173 * @see org.greenstone.gatherer.valuetree.GValueTree 174 */ 205 175 public void actionPerformed(ActionEvent event) { 206 176 Object esrc = event.getSource(); … … 221 191 } 222 192 } 223 //else if(esrc == assigned_metadata_view || esrc == unassigned_metadata_view) {224 // model.changeView();225 //}226 193 validateControls(); 227 194 } … … 230 197 extends Thread { 231 198 232 p ublicAppendMetadataTask() { }199 private AppendMetadataTask() { } 233 200 234 201 public void run() { … … 259 226 extends Thread { 260 227 261 p ublicUpdateMetadataTask() { }228 private UpdateMetadataTask() { } 262 229 263 230 public void run() { … … 276 243 extends Thread { 277 244 278 p ublicRemoveMetadataTask() { }245 private RemoveMetadataTask() { } 279 246 280 247 public void run() { … … 289 256 290 257 /** Some actions can only occur after this panel has been displayed on-screen, so this method is provided to do exactly that. Such actions include the proportioning of the split panes and the setting of table column widths. 291 292 258 * @see org.greenstone.gatherer.gui.table.GTableModel 259 */ 293 260 public void afterDisplay() { 294 261 external_split.setDividerLocation(0.3); … … 313 280 } 314 281 /** Called whenever a significant change has occured in the state of the currently loaded collection. 315 316 317 318 319 320 282 * @param ready <i>true</i> if there is a collection currently ready to be editing, <i>false</i> otherwise. 283 * @see org.greenstone.gatherer.collection.CollectionManager 284 * @see org.greenstone.gatherer.collection.CollectionModel 285 * @see org.greenstone.gatherer.tree.GTree 286 * @see org.greenstone.gatherer.util.TreeSynchronizer 287 */ 321 288 public void collectionChanged(boolean ready) { 322 289 if(ready) { … … 325 292 args[0] = Gatherer.c_man.getCollection().getName(); 326 293 collection_label.setText(get("Collection.Collection", args)); 327 294 // Update label coloring. 328 295 collection_label.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false)); 329 296 collection_label.setForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false)); 330 297 collection_tree.setModel(collection_model); 331 298 // Update tree coloring. 332 299 collection_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 333 300 collection_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false)); … … 354 321 } 355 322 /** Used to create, connect and layout the components to be shown on this control panel. 356 357 358 359 360 361 362 363 364 365 366 323 * @see org.greenstone.gatherer.Gatherer 324 * @see org.greenstone.gatherer.collection.CollectionManager 325 * @see org.greenstone.gatherer.collection.CollectionModel 326 * @see org.greenstone.gatherer.file.FileOpenActionListener 327 * @see org.greenstone.gatherer.gui.Filter 328 * @see org.greenstone.gatherer.gui.GComboBox 329 * @see org.greenstone.gatherer.gui.table.GTableModel 330 * @see org.greenstone.gatherer.tree.GTree 331 * @see org.greenstone.gatherer.tree.GTreeModel 332 * @see org.greenstone.gatherer.valuetree.GValueTree 333 */ 367 334 public void display() { 368 right_button_listener = new RightButtonListener();335 RightButtonListener right_button_listener = new RightButtonListener(); 369 336 // Creation 370 337 JPanel collection_pane = new JPanel(new BorderLayout()); … … 385 352 if(collection_model != null) { 386 353 collection_tree = new DragTree("MetaEdit", collection_model, null, false); 387 collection_model.addTreeModelListener(this);388 354 } 389 355 else { … … 425 391 collection_pane.add(filter, BorderLayout.SOUTH); 426 392 427 // Main pane 428 //main_pane = new JPanel(new BorderLayout()); 429 //main_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 430 431 main_split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); 393 JSplitPane main_split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); 432 394 main_split_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 433 395 main_split_pane.setDividerSize(8); … … 458 420 // be swapped to the front. 459 421 460 table_pane = new JPanel();422 JPanel table_pane = new JPanel(); 461 423 table_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); 462 424 … … 467 429 table_label.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); 468 430 table_label.setEditable(false); 469 470 //activity_bar = new JProgressBar();471 //activity_bar.setPreferredSize(LABEL_SIZE);472 //activity_bar.setString(get("Ready"));473 //activity_bar.setStringPainted(true);474 //activity_bar.setValue(activity_bar.getMaximum());475 431 476 432 card_layout = new CardLayout(); … … 495 451 496 452 JPanel table_pane_one = new JPanel(); 497 498 //JPanel view_pane = new JPanel();499 500 //JLabel view_label = new JLabel(get("View"));501 //view_label.setPreferredSize(LABEL_SIZE);502 503 //JPanel view_button_pane = new JPanel();504 505 //assigned_metadata_view = new JToggleButton(get("View_Assigned"), Utility.OFF_ICON);506 //assigned_metadata_view.setSelectedIcon(Utility.ON_ICON);507 //assigned_metadata_view.setSelected(true);508 //assigned_metadata_view.addActionListener(this);509 //unassigned_metadata_view = new JToggleButton(get("View_Unassigned"), Utility.OFF_ICON);510 //unassigned_metadata_view.setSelectedIcon(Utility.ON_ICON);511 //unassigned_metadata_view.setSelected(true);512 //unassigned_metadata_view.addActionListener(this);513 //XORToggleButtonGroup view_group = new XORToggleButtonGroup();514 //view_group.add(assigned_metadata_view);515 //view_group.add(unassigned_metadata_view);516 453 517 454 ///atherer.println("\tCreating metadata_table"); … … 584 521 //table_title_pane.setLayout(new GridLayout(1,2,5,0)); 585 522 table_title_pane.add(table_label, BorderLayout.CENTER); 586 //table_title_pane.add(activity_bar);587 523 588 524 table_pane_zero.setLayout(new BorderLayout()); … … 599 535 table_card_pane.add(table_pane_one, CARD_ONE); 600 536 table_card_pane.add(table_pane_two, CARD_TWO); 601 //view_button_pane.setLayout(new GridLayout(1,2,5,0));602 //view_button_pane.add(assigned_metadata_view);603 //view_button_pane.add(unassigned_metadata_view);604 605 //view_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));606 //view_pane.setLayout(new BorderLayout());607 //view_pane.add(view_label, BorderLayout.WEST);608 //view_pane.add(view_button_pane, BorderLayout.CENTER);609 537 610 538 table_pane.setLayout(new BorderLayout()); 611 539 table_pane.add(table_title_pane, BorderLayout.NORTH); 612 540 table_pane.add(table_card_pane, BorderLayout.CENTER); 613 //table_pane.add(view_pane, BorderLayout.SOUTH); 614 615 //main_split_pane.add(control_pane, JSplitPane.TOP); 616 //main_split_pane.add(table_pane, JSplitPane.BOTTOM); 541 617 542 main_split_pane.add(table_pane, JSplitPane.TOP); 618 543 main_split_pane.add(control_pane, JSplitPane.BOTTOM); 619 544 main_split_pane.setDividerLocation(250); 620 545 621 external_split.add(collection_pane, JSplitPane.LEFT);//BorderLayout.WEST); 622 //this.add(main_pane, BorderLayout.CENTER); 623 external_split.add(main_split_pane, JSplitPane.RIGHT);//BorderLayout.CENTER); 546 external_split.add(collection_pane, JSplitPane.LEFT); 547 external_split.add(main_split_pane, JSplitPane.RIGHT); 624 548 625 549 this.setLayout(new BorderLayout()); … … 633 557 634 558 /** Ensures a certain tree path is expanded, visible and selected within the collection tree. 635 636 637 638 p ublicvoid expandPath(TreePath path) {559 * @param path The <strong>TreePath</strong> to make visible. 560 * @see org.greenstone.gatherer.tree.GTree 561 */ 562 private void expandPath(TreePath path) { 639 563 collection_tree.expandPath(path); 640 564 collection_tree.setSelectionPath(path); 641 565 } 642 566 /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab. 643 644 567 * @see org.greenstone.gatherer.tree.GTree 568 */ 645 569 public void gainFocus() { 646 570 // Use this opportunity to update the table model etc. … … 659 583 Gatherer.g_man.meta_audit.setRecords(records); 660 584 } 661 /** Used to determine if this class contains a reference to an existing file record that matches the presumtively new record, and if one is found, returns it. Note that there is no need to search the metadata table model, as any file record that exists there must also exist in the records selection.662 * @param new_record The <strong>FileNode</strong> which may or may not be the first occurance of a certain record.663 * @return The matching <strong>FileNode</strong> if there is one, or <i>null</i>.664 */665 public FileNode getAnyExistingRecord(FileNode new_record) {666 FileNode match = null;667 for(int i = 0; records != null && i < records.length && match == null; i++) {668 if(records[i].equals(new_record)) {669 match = records[i];670 }671 }672 return match;673 }674 585 675 586 /** Retrieve the currently selected records. 676 677 587 * @return A <strong>FileNode[]</strong> containing the current, and up to date, selection. 588 */ 678 589 public FileNode[] getSelectedNode() { 679 590 return records; 680 591 } 681 592 682 /** Retrieve the currently selected metadata.683 * @return The <strong>Metadata</strong> in question.684 */685 public Metadata getSelectedMetadata() {686 return selected_metadata;687 }688 689 593 /** Called whenever the metadata value changes in some way, such as the addition of a new value. - needed for MSMListener 690 594 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired. 691 595 */ 692 596 public void metadataChanged(MSMEvent event) {} 597 598 public void refreshTrees() { 599 collection_tree.refresh(null); // Refresh entire tree. 600 } 693 601 694 602 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. - needed for MSMListener … … 701 609 } 702 610 703 public void setSelection(File file) {704 collection_tree.setSelection(file);705 }706 707 /** Determine the bounds of the selected metadata within the table, used during search and replace actions.708 * @param metadata The <strong>Metadata</strong> to search for.709 */710 public Rectangle setSelectedMetadata(Metadata metadata) {711 // return model.setSelectedMetadata(metadata);712 return null; // For now713 }714 715 public void refreshTrees() {716 collection_tree.refresh(null); // Refresh entire tree.717 }718 719 /* Called when nodes have been modified, but the model hasn't changed.720 * @param event Everything you ever wanted to know about model changes in one tidy <strong>TreeModelEvent</strong> package.721 */722 public void treeNodesChanged(TreeModelEvent event) {723 has_changed = true;724 }725 726 /* Called when nodes have been added to the model.727 * @param event Everything you ever wanted to know about model changes in one tidy <strong>TreeModelEvent</strong> package.728 */729 public void treeNodesInserted(TreeModelEvent event) {730 has_changed = true;731 }732 733 /* Called when nodes have been removed from the model.734 * @param event Everything you ever wanted to know about model changes in one tidy <strong>TreeModelEvent</strong> package.735 */736 public void treeNodesRemoved(TreeModelEvent event) {737 has_changed = true;738 }739 740 /** Called when nodes have been rearranged within the model.741 * @param event Everything you ever wanted to know about model changes in one tidy <strong>TreeModelEvent</strong> package.742 */743 public void treeStructureChanged(TreeModelEvent event) {744 has_changed = true;745 }746 /** Called to ensure that if the tree model has changed in some way, the view has been updated.747 * @see org.greenstone.gatherer.tree.GTree748 */749 public void updateTree() {750 if(has_changed) {751 tree.updateUI();752 has_changed = false;753 }754 }755 756 611 /** This method enables or diables the various controls based upon the state of the system. This method should be called after every change which could affect the GUI. 757 */ 758 public void validateControls() { 759 validateControls(true); 760 } 761 public void validateControls(boolean and_tree) { 612 */ 613 private void validateControls() { 762 614 validateMetadataTable(); 763 615 // Validate card_layout_2 … … 768 620 card_layout2.show(control_pane, TOOLS_OFF); 769 621 } 770 if(and_tree) { 771 // Validate the buttons in the lower pane 772 if (selected_metadata == null) { 773 // Nothing selected 774 return; 775 } 776 777 // Does the metadata element have no current value? 778 GValueNode value_node = selected_metadata.getValueNode(); 779 if (value_node == null) { 780 // Can only append if something has been entered 781 add.setEnabled((tree.getSelectedValue().length() > 0)); 782 // Nothing to replace or remove 622 623 // Validate the buttons in the lower pane 624 if (selected_metadata == null) { 625 // Nothing selected 626 return; 627 } 628 629 // Does the metadata element have no current value? 630 GValueNode value_node = selected_metadata.getValueNode(); 631 if (value_node == null) { 632 // Can only append if something has been entered 633 add.setEnabled((tree.getSelectedValue().length() > 0)); 634 // Nothing to replace or remove 635 update.setEnabled(false); 636 remove.setEnabled(false); 637 } 638 639 else { 640 // Check if the text in the value field is the same as the metadata value 641 if (tree.getSelectedValue().equals(value_node.getFullPath())) { 642 // Can't append or replace 643 add.setEnabled(false); 783 644 update.setEnabled(false); 784 remove.setEnabled(false); 785 } 786 645 } 787 646 else { 788 // Check if the text in the value field is the same as the metadata value 789 if (tree.getSelectedValue().equals(value_node.getFullPath())) { 790 // Can't append or replace 791 add.setEnabled(false); 792 update.setEnabled(false); 793 } 794 else { 795 // Can append or replace 796 add.setEnabled(true); 797 update.setEnabled(true); 798 } 799 800 // Can only remove if the metadata is file level 801 if (selected_metadata != null) { // Shouldn't be necessary, but is 802 remove.setEnabled(selected_metadata.isFileLevel()); 803 } 647 // Can append or replace 648 add.setEnabled(true); 649 update.setEnabled(true); 650 } 651 652 // Can only remove if the metadata is file level 653 if (selected_metadata != null) { // Shouldn't be necessary, but is 654 remove.setEnabled(selected_metadata.isFileLevel()); 804 655 } 805 656 } … … 816 667 card_layout.show(table_card_pane, CARD_ONE); 817 668 } 818 /*819 if(records != null && model.getRowCount() > 0) {820 card_layout.show(table_card_pane, CARD_ONE);821 }822 else {823 card_layout.show(table_card_pane, CARD_ZERO);824 }*/825 669 } 826 670 … … 851 695 } 852 696 } 853 validateControls( true);697 validateControls(); 854 698 } 855 699 } … … 937 781 938 782 /** Retrieve a phrase from the Dictionary based on the given key. 939 940 941 783 * @param key The <strong>String</strong> which maps to the phrase to retrieve. 784 * @return The desired phrase as a <strong>String</strong>, or possibly an error message if no such phrase exists. 785 */ 942 786 private String get(String key) { 943 787 return get(key, null); 944 788 } 945 789 /** Retrieve a phrase from the Dictionary based on the given key and filled in with the given arguments. 946 947 948 949 950 951 790 * @param key The <strong>String</strong> which maps to the phrase to retrieve. 791 * @param args A <strong>String[]</strong> of arguments to be inserted in the phrase. 792 * @return The desired phrase as a <strong>String</strong>, or possibly an error message if no such phrase exists. 793 * @see org.greenstone.gatherer.Dictionary 794 * @see org.greenstone.gatherer.Gatherer 795 */ 952 796 private String get(String key, String args[]) { 953 797 if(key.indexOf('.') == -1) { … … 1100 944 } 1101 945 } 946 947 1102 948 /** Provides a popup menu to display when a right mouse button click is detected over the collection tree. */ 1103 949 private class RightButtonMenu … … 1106 952 /** Constructor. 1107 953 * @param event The <strong>MouseEvent</strong> that triggered the creation of this menu. Used to determine where the menu will be located. 1108 1109 p ublicRightButtonMenu(MouseEvent event) {954 */ 955 private RightButtonMenu(MouseEvent event) { 1110 956 super(); 1111 957 // Creation 1112 958 JMenuItem show_metaaudit = new JMenuItem(get("Menu.Metadata_View") + " " + collection_tree.getSelectionDetails(), KeyEvent.VK_V); 1113 959 show_metaaudit.addActionListener(this); 1114 960 add(show_metaaudit); 1115 961 // Display 1116 962 show(collection_tree, event.getX(), event.getY()); 1117 963 show_metaaudit = null; 1118 args = null; 1119 1120 } 964 } 965 1121 966 /** Called whenever a user clicks on the single menu item, view all assigned metadata. 1122 1123 1124 1125 967 * @param event An <strong>ActionEvent</strong> containing further information about the action. 968 * @see org.greenstone.gatherer.Gatherer 969 * @see org.greenstone.gatherer.gui.GUIManager 970 */ 1126 971 public void actionPerformed(ActionEvent event) { 1127 972 Gatherer.g_man.showMetaAuditBox(); … … 1129 974 } 1130 975 976 977 /** 978 * @author John Thompson, Greenstone Digital Library, University of Waikato 979 * @version 2.2 980 */ 981 private class GValueTree 982 extends JPanel 983 implements TreeSelectionListener { 984 private boolean ignore_tree_selection_event = false; 985 private boolean manual_text_edit_event; 986 private CardLayout card_layout; 987 private String card_showing = null; 988 /** The metadata element that is currently selected. */ 989 private ElementWrapper selected_metadata_element = null; 990 private GValueModel vm; 991 private JTextArea extracted_message; 992 private JTextField value; 993 private JTree tree; 994 private MetaEditPane metaedit; 995 private String args[] = new String[1]; 996 /** Stock standard size for labels. */ 997 final private Dimension VALUE_LABEL_SIZE = new Dimension(66, 26); 998 999 static final private String NONE = "None"; 1000 static final private String TREE = "Tree"; 1001 1002 1003 /** 1004 * This class is a little bit complex, a little bit subtle, and a little bit odd. 1005 * The strange interaction model is due to the fact that it is very tightly 1006 * coupled to the MetaEditPane. 1007 * 1008 * The interaction is complex because there are three separate controls that the 1009 * user may interact with, each of which can affect the other two. The three 1010 * controls are: 1011 * - The "assigned metadata" table, at the top right of the meta edit pane. 1012 * - The "metadata value" text field, where users can type in new values. 1013 * - The "metadata value tree", which shows other values that have been 1014 * assigned to the selected metadata element. 1015 * 1016 * The interactions are: 1017 * - The "assigned metadata" table 1018 * Users may select one (and only one) row in this table. Selecting a row 1019 * chooses one metadata element. The text field will be set to the current 1020 * value of the metadata element. This value will also be selected in the 1021 * metadata value tree. 1022 * 1023 * - The "metadata value" text field 1024 * If the value the user has typed in this is a prefix of an entry in the 1025 * value tree, this value will be selected in the value tree. In this case, 1026 * pressing "Enter" will complete the value (ie. make the value in the text 1027 * field the same as the value in the tree). This is to allow users to 1028 * quickly and easily assign existing metadata values to new documents. 1029 * 1030 * - The "metadata value tree" 1031 * Selecting a value in the tree will set the text field to this value. 1032 */ 1033 public GValueTree(MetaEditPane metaedit, int width, int height) { 1034 super(); 1035 1036 this.metaedit = metaedit; 1037 1038 setFont(Gatherer.config.getFont("general.font", false)); 1039 setPreferredSize(new Dimension(width, height)); 1040 1041 JPanel metadata_pane = new JPanel(); 1042 1043 JPanel value_pane = new JPanel(); 1044 JLabel value_label = new JLabel(get("Value")); 1045 value_label.setPreferredSize(VALUE_LABEL_SIZE); 1046 1047 JPanel value_field_pane = new JPanel(); 1048 value = new JTextField(); 1049 value.setBackground(Gatherer.config.getColor("coloring.editable_background", false)); 1050 value.setForeground(Gatherer.config.getColor("coloring.editable_foreground", false)); 1051 value.setPreferredSize(new Dimension(413, 24)); 1052 value.getDocument().addDocumentListener(new DocumentListenerImpl()); 1053 value.addActionListener(new MetadataFieldListener()); 1054 1055 JPanel button_pane = new JPanel(); 1056 1057 JPanel tree_pane = new JPanel(); 1058 JLabel tree_label = new JLabel(get("Tree")); 1059 1060 tree = new JTree(new GValueModel()); 1061 tree.addTreeSelectionListener(this); 1062 tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 1063 tree.setRootVisible(false); 1064 tree.putClientProperty("JTree.lineStyle", "Angled"); 1065 1066 JPanel extracted_pane = new JPanel(); 1067 extracted_message = new JTextArea(""); 1068 extracted_message.setEditable(false); 1069 extracted_message.setLineWrap(true); 1070 extracted_message.setOpaque(false); 1071 extracted_message.setWrapStyleWord(true); 1072 1073 card_layout = new CardLayout(); 1074 1075 // Layout 1076 1077 value_field_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); 1078 value_field_pane.setLayout(new BorderLayout(0, 0)); 1079 value_field_pane.add(value, BorderLayout.CENTER); 1080 1081 button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); 1082 button_pane.setLayout(new GridLayout()); 1083 button_pane.add(add); 1084 button_pane.add(update); 1085 button_pane.add(remove); 1086 1087 value_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); 1088 value_pane.setLayout(new BorderLayout()); 1089 value_pane.add(value_label, BorderLayout.WEST); 1090 value_pane.add(value_field_pane, BorderLayout.CENTER); 1091 value_pane.add(expand, BorderLayout.EAST); 1092 value_pane.add(button_pane, BorderLayout.SOUTH); 1093 1094 tree_pane.setLayout(new BorderLayout()); 1095 tree_pane.add(tree_label, BorderLayout.NORTH); 1096 tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER); 1097 1098 metadata_pane.setLayout(new BorderLayout()); 1099 metadata_pane.add(value_pane, BorderLayout.NORTH); 1100 metadata_pane.add(tree_pane, BorderLayout.CENTER); 1101 1102 extracted_pane.setBorder(BorderFactory.createEmptyBorder(25,10,25,10)); 1103 extracted_pane.setLayout(new BorderLayout()); 1104 extracted_pane.add(extracted_message, BorderLayout.CENTER); 1105 1106 this.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 1107 this.setLayout(card_layout); 1108 this.add(metadata_pane, TREE); 1109 this.add(extracted_pane, NONE); 1110 card_showing = TREE; 1111 } 1112 1113 1114 public GValueModel getModel() { 1115 return (GValueModel) tree.getModel(); 1116 } 1117 1118 1119 public ElementWrapper getSelectedMetadataElement() { 1120 return selected_metadata_element; 1121 } 1122 1123 1124 public String getSelectedValue() { 1125 return value.getText(); 1126 } 1127 1128 1129 public void setSelectedMetadataElement(ElementWrapper element) 1130 { 1131 ///ystem.err.println("Setting selected metadata element to: " + element); 1132 if (element == null) { 1133 selected_metadata_element = null; 1134 return; 1135 } 1136 1137 // Don't bother if the element selected is the same as the current one 1138 if (selected_metadata_element != null && element.equals(selected_metadata_element)) { 1139 return; 1140 } 1141 1142 if (vm != null) { 1143 // Close any expanded paths in the tree 1144 GValueNode root_node = (GValueNode) vm.getRoot(); 1145 for (int i = 0; i < root_node.getChildCount(); i++) { 1146 GValueNode child_node = (GValueNode) root_node.getChildAt(i); 1147 TreePath child_path = new TreePath(child_node.getPath()); 1148 if (tree.isExpanded(child_path)) { 1149 tree.collapsePath(child_path); 1150 } 1151 } 1152 } 1153 1154 // Load the model for the newly selected metadata element 1155 selected_metadata_element = element; 1156 vm = Gatherer.c_man.getCollection().msm.getValueTree(element); 1157 tree.setModel(vm); 1158 1159 // Special case the extracted metadata set 1160 if (selected_metadata_element.getNamespace().equals("")) { 1161 // Display the panel showing the "you cannot edit this metadata" message 1162 args[0] = selected_metadata_element.toString(); 1163 extracted_message.setText(get("AutoMessage", args)); 1164 card_layout.show(this, NONE); 1165 card_showing = NONE; 1166 } 1167 else { 1168 // Display the panel for viewing and assigning metadata 1169 card_layout.show(this, TREE); 1170 card_showing = TREE; 1171 } 1172 } 1173 1174 1175 /** Handles TreeSelectionEvents. */ 1176 public void valueChanged(TreeSelectionEvent event) 1177 { 1178 if (tree.getSelectionCount() != 0 && !ignore_tree_selection_event) { 1179 TreePath path = tree.getSelectionPath(); 1180 GValueNode node = (GValueNode) path.getLastPathComponent(); 1181 setSelectedValue(node.getFullPath()); 1182 } 1183 } 1184 1185 1186 /** Sets the value in the text field. */ 1187 public void setSelectedValue(String val) 1188 { 1189 // Setting the text of the field causes the DocumentListener to be notified, and 1190 // updating the tree is handled there (DocumentListenerImpl::validate()) 1191 ///ystem.err.println("Setting selected value to: " + val); 1192 if (!card_showing.equals(NONE)) { 1193 manual_text_edit_event = val.equals(""); // Set to false unless val == "" 1194 value.setText(Utility.stripNL(val)); 1195 value.setCaretPosition(0); 1196 manual_text_edit_event = true; 1197 } 1198 } 1199 1200 1201 private String get(String key) { 1202 return get(key, null); 1203 } 1204 1205 private String get(String key, String args[]) { 1206 if (key.indexOf('.') == -1) { 1207 key = "MetaEdit." + key; 1208 } 1209 return Gatherer.dictionary.get(key,args); 1210 } 1211 1212 1213 /** Returns a TreePath for the node most closely matching the metadata value. */ 1214 private TreePath getClosestPath(String val) 1215 { 1216 // Start at the root of the tree 1217 GValueNode tree_node = (GValueNode) tree.getModel().getRoot(); 1218 1219 // Hierarchical values are separated using '\' 1220 StringTokenizer tokenizer = new StringTokenizer(val, "\\"); 1221 while (tokenizer.hasMoreTokens()) { 1222 String token = tokenizer.nextToken(); 1223 1224 // All components except the last must match exactly 1225 if (tokenizer.hasMoreTokens()) { 1226 for (int i = 0; i < tree_node.getChildCount(); i++) { 1227 GValueNode child_node = (GValueNode) tree_node.getChildAt(i); 1228 if (child_node.toString().equals(token)) { 1229 // The matching node has been found, so move onto the next token 1230 tree_node = child_node; 1231 break; 1232 } 1233 } 1234 } 1235 1236 // The last component may match partially 1237 else { 1238 for (int i = 0; i < tree_node.getChildCount(); i++) { 1239 GValueNode child_node = (GValueNode) tree_node.getChildAt(i); 1240 if (child_node.toString().startsWith(token)) { 1241 // The closest node has been found, so return its path 1242 return new TreePath(child_node.getPath()); 1243 } 1244 } 1245 1246 // Not even a partial match 1247 return null; 1248 } 1249 } 1250 1251 // If nothing else, return the path of the root node 1252 return new TreePath(tree_node.getPath()); 1253 } 1254 1255 1256 /** This function is copied from JTree::setPathToVisible(), and modified so tree rows 1257 are always shown flushed left. Note: doesn't do accessibleContext stuff. */ 1258 private void scrollTreePathToVisible(TreePath path) 1259 { 1260 if (path != null) { 1261 tree.makeVisible(path); 1262 1263 Rectangle bounds = tree.getPathBounds(path); 1264 if (bounds != null) { 1265 bounds.width += bounds.x; 1266 bounds.x = 0; 1267 tree.scrollRectToVisible(bounds); 1268 } 1269 } 1270 } 1271 1272 1273 private class MetadataFieldListener 1274 implements ActionListener { 1275 /** Gives notification of the Enter key being pressed on the text field */ 1276 public void actionPerformed(ActionEvent e) { 1277 if (tree.getSelectionCount() != 0 && !value.getText().equals("")) { 1278 TreePath path = tree.getSelectionPath(); 1279 GValueNode node = (GValueNode) path.getLastPathComponent(); 1280 value.setText(Utility.stripNL(node.getFullPath())); 1281 } 1282 } 1283 } 1284 1285 1286 private class DocumentListenerImpl 1287 implements DocumentListener { 1288 /** Gives notification that an attribute or set of attributes changed. */ 1289 public void changedUpdate(DocumentEvent e) { 1290 validate(); 1291 } 1292 1293 /** Gives notification that there was an insert into the document. */ 1294 public void insertUpdate(DocumentEvent e) { 1295 validate(); 1296 } 1297 1298 /** Gives notification that a portion of the document has been removed. */ 1299 public void removeUpdate(DocumentEvent e) { 1300 validate(); 1301 } 1302 1303 1304 /** Ensures that the value text field and value tree are synchronized. */ 1305 private void validate() 1306 { 1307 String value_text = value.getText(); 1308 ///ystem.err.println("\n(Validate) Value text: " + value_text); 1309 1310 // Ignore the validate() with empty text that occurs when value.setText() is used 1311 if (!value_text.equals("") || manual_text_edit_event == true) { 1312 TreePath closest_path = getClosestPath(value_text); 1313 1314 // Select the new path in the tree 1315 // The tree selection event this causes must be ignored, since it alters value 1316 ignore_tree_selection_event = true; 1317 tree.setSelectionPath(closest_path); 1318 ignore_tree_selection_event = false; 1319 1320 // If a folder has been specified, make sure it is expanded 1321 if (value_text.endsWith("\\") && !tree.isExpanded(closest_path)) { 1322 tree.expandPath(closest_path); 1323 } 1324 1325 // Make sure the path is visible on the screen 1326 scrollTreePathToVisible(closest_path); 1327 // One call should be enough, but it isn't in some cases (for some reason) 1328 scrollTreePathToVisible(closest_path); 1329 1330 // Update the status of the buttons 1331 metaedit.validateControls(); 1332 } 1333 } 1334 } 1335 } 1131 1336 }
Note:
See TracChangeset
for help on using the changeset viewer.