source: main/trunk/gli/src/org/greenstone/gatherer/gui/ConfigFileEditor.java@ 34266

Last change on this file since 34266 was 34266, checked in by ak19, 4 years ago
  1. Added rudimentary support for inserting the basic skeletons of security-related elements into collectionConfiguration.xml when using GLI's ConfigFileEditor. Hopefully this is useful. 2. Removed the self-evident tooltip from the ConfigFileEditor as it was always annoying floating over the editor, obscuring stuff. Had to addd a no-arg constructor in NumberedJTextArea for this to be possible.
File size: 24.5 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * Copyright (C) 1999 New Zealand Digital Library Project
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *########################################################################
24 */
25
26package org.greenstone.gatherer.gui;
27
28// for RSyntaxTextArea editor's search functionality:
29import org.fife.ui.rtextarea.SearchEngine;
30import org.fife.ui.rtextarea.SearchContext;
31
32import org.greenstone.gatherer.Configuration;
33import org.greenstone.gatherer.Dictionary;
34import org.greenstone.gatherer.Gatherer;
35import org.greenstone.gatherer.util.StaticStrings;
36import org.greenstone.gatherer.util.Utility;
37import org.greenstone.gatherer.util.XMLTools;
38
39import org.w3c.dom.*;
40
41import java.io.File;
42import java.io.IOException;
43
44import java.awt.*;
45import java.awt.event.*;
46import java.util.*;
47import javax.swing.*;
48import javax.swing.border.*;
49import javax.swing.event.*;
50
51
52public class ConfigFileEditor extends ModalDialog
53 implements ActionListener, DocumentListener
54{
55 public final File config_file;
56
57 /** User can add some complex elements by clcking a button */
58 private Document elements_available_for_insertion;
59
60 private GLIButton cancel_button = null;
61 private GLIButton save_button = null;
62 private NumberedJTextArea editor = null;
63 private JTextArea editor_msgarea;
64
65 private JComboBox chooseElementComboBox;
66 private GLIButton addElementButton;
67
68 // https://github.com/bobbylight/RSyntaxTextArea/wiki/Example:-Using-Find-and-Replace
69 final private JTextField searchField;
70 private JCheckBox regexCB;
71 private JCheckBox matchCaseCB;
72 private JButton nextButton;
73 private JButton prevButton;
74
75 //private final String DEFAULT_PROCESSING_INSTRUCTION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
76
77 private static final Dimension SIZE = new Dimension(850,550);
78
79
80 // Enum to define elements the user can add
81 // enum available from Java 1.5
82 // https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
83 // https://docs.oracle.com/javase/7/docs/api/java/lang/Enum.html
84 private enum ElementOption {
85 HIDE_COLLECTION (Dictionary.get("ConfigFileEditor.Hide_Collection")),
86 SECURE_COLLECTION (Dictionary.get("ConfigFileEditor.Secure_Collection")),
87 SECURE_ALL_DOCUMENTS(Dictionary.get("ConfigFileEditor.Secure_All_Documents")),
88 SECURE_CERTAIN_DOCUMENTS(Dictionary.get("ConfigFileEditor.Secure_Certain_Documents")),
89 ADD_ANOTHER_DOCUMENTSET(Dictionary.get("ConfigFileEditor.Add_Another_DocumentSet")),
90 ADD_ANOTHER_EXCEPTION(Dictionary.get("ConfigFileEditor.Add_Another_Exception"));
91
92 private final String displayString;
93 // Constructor may not be declared as public
94 ElementOption(String displayStr) {
95 // interesting that Enum implementation classes don't
96 // have to call protected superclass constructor
97 displayString = displayStr;
98 }
99
100 // override default behaviour of returning the ENUM string itself
101 // to display the displayString instead
102 public String toString() {
103 return displayString;
104 }
105 }
106
107
108 public ConfigFileEditor() {
109
110 super(Gatherer.g_man, true);
111 setModal(true);
112 setSize(SIZE);
113
114 String collection_folder_path = Gatherer.getCollectDirectoryPath();
115 String collectionName = Gatherer.c_man.getCollection().getName(); // we won't be in this constructor if collection is null.
116
117 // Gatherer.c_man.saveCollection(); // on Edit > collectionConfig.xml menu click,
118 // collection save has already happened as GLI pane.lostFocus() got called on current GLI Pane
119 // if/where this affects collectionConfig.xml. For an explanation, see GUIManager.java,
120 // look for "if(esrc == menu_bar.edit_config)"
121
122 this.config_file = new File(collection_folder_path + File.separator + collectionName, Utility.CONFIG_GS3_FILE); // col config editing is a GS3 feature
123
124
125 // Most of the validation_msg_panel code is from Format4gs3Manager.java
126 JPanel validation_msg_panel = new JPanel();
127 JLabel validation_label = new JLabel(Dictionary.get("CDM.FormatManager.MessageBox"));
128 validation_label.setBorder(new EmptyBorder(10,10,10,10)); // add some margin
129 editor_msgarea = new JTextArea();
130
131 editor_msgarea.setCaretPosition(0);
132 editor_msgarea.setLineWrap(true);
133 editor_msgarea.setRows(3);
134 editor_msgarea.setWrapStyleWord(false);
135 editor_msgarea.setEditable(false);
136 editor_msgarea.setToolTipText(Dictionary.get("CDM.FormatManager.MessageBox_Tooltip"));
137 validation_msg_panel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
138 validation_msg_panel.setLayout(new BorderLayout(5, 0));
139 validation_msg_panel.add(validation_label, BorderLayout.WEST);
140 validation_msg_panel.add(new JScrollPane(editor_msgarea), BorderLayout.CENTER);
141
142 // Find functionality - can extend for Find and Replace
143 // taken from https://github.com/bobbylight/RSyntaxTextArea/wiki/Example:-Using-Find-and-Replace
144 // Create a toolbar with searching options.
145 JToolBar toolBar = new JToolBar();
146 searchField = new JTextField(30);
147 toolBar.add(searchField);
148 nextButton = new JButton("Find Next");
149 nextButton.setActionCommand("FindNext");
150 nextButton.addActionListener(this);
151 toolBar.add(nextButton);
152 searchField.addActionListener(this);
153 prevButton = new JButton("Find Previous");
154 prevButton.setActionCommand("FindPrev");
155 prevButton.addActionListener(this);
156 toolBar.add(prevButton);
157 regexCB = new JCheckBox("Regex");
158 toolBar.add(regexCB);
159 matchCaseCB = new JCheckBox("Match Case");
160 toolBar.add(matchCaseCB);
161
162 // the all important XML editor
163 editor = new NumberedJTextArea(); // Don't pass in tooltip string for editor:
164 // The editor's purpose should be obvious, and
165 // the tooltip annoyingly constantly floats over the editor all the time
166 editor.getDocument().addDocumentListener(this);
167 // if Ctrl (or Mac equiv) + F is pressed in the config file editing area,
168 // then move the focus to the searchField, so the user can start typing the searchTerm
169 editor.addKeyListener(new ConfigFileEditorKeyListener());
170
171 save_button = new GLIButton(Dictionary.get("General.Save"), Dictionary.get("ConfigFileEditor.Save_Tooltip"));
172 cancel_button = new GLIButton(Dictionary.get("General.Cancel"), Dictionary.get("ConfigFileEditor.Cancel_Tooltip")); // tooltip is the same
173 cancel_button.addActionListener(this);
174 save_button.addActionListener(this);
175
176 // widgets in section for adding security-related XML elements into the collectionConfigXML file
177 JLabel addElementLabel = new JLabel(Dictionary.get("ConfigFileEditor.Add_Element_Label"));
178
179 chooseElementComboBox = new JComboBox(ElementOption.values()); // calls the implicit static values() method on Enum class to get an array of all Enum values
180 addElementButton = new GLIButton(Dictionary.get("ConfigFileEditor.Add_Label"),
181 Dictionary.get("ConfigFileEditor.Add_Element_Tooltip"));
182 elements_available_for_insertion = XMLTools.parseXMLFile("xml/elementsForInsertion.xml", true);
183 addElementButton.addActionListener(this);
184
185 // Elements can't be added if gli/classes/xml/elementsForInsertion.xml file
186 // does not exist or can't be parsed
187 if(elements_available_for_insertion == null) {
188 addElementButton.setEnabled(false);
189 }
190
191 // LAYOUT
192 JPanel add_elements_panel = new JPanel(new FlowLayout());
193 add_elements_panel.setComponentOrientation(Dictionary.getOrientation());
194 add_elements_panel.add(addElementLabel);
195 add_elements_panel.add(chooseElementComboBox);
196 add_elements_panel.add(addElementButton);
197
198 JPanel button_panel = new JPanel(new FlowLayout());
199 button_panel.setComponentOrientation(Dictionary.getOrientation());
200 button_panel.add(editor.undoButton);
201 button_panel.add(editor.redoButton);
202 button_panel.add(cancel_button);
203 button_panel.add(save_button);
204
205 // toolbars_panel to contain find-and-replace toolbar and undo/redo/cancel/save button panel
206 JPanel toolbars_panel = new JPanel(new BorderLayout());
207 toolbars_panel.add(toolBar, BorderLayout.NORTH);
208 toolbars_panel.add(add_elements_panel, BorderLayout.CENTER);
209 toolbars_panel.add(button_panel, BorderLayout.SOUTH);
210
211 JPanel content_pane = (JPanel) getContentPane();
212 content_pane.setComponentOrientation(Dictionary.getOrientation());
213 content_pane.setLayout(new BorderLayout());
214 content_pane.add(validation_msg_panel, BorderLayout.NORTH);
215 content_pane.add(new JScrollPane(editor), BorderLayout.CENTER); // make editor scrollable
216 content_pane.add(toolbars_panel, BorderLayout.SOUTH);
217
218 // NOW WE CAN PREPARE THE ACTUAL CONTENT TO GO INTO THE XML EDITOR TEXTAREA
219 Document xmlDoc = null;
220 xmlDoc = XMLTools.parseXMLFile(config_file);
221
222 if(xmlDoc != null) {
223
224 // Save the collectionConfig.xml before loading it.
225 String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
226 XMLTools.writeXMLFile(this.config_file, xmlDoc, nonEscapingTagNames); //prepends proc instruction
227
228 // now load the contents
229 editor_msgarea.setBackground(Color.white);
230 StringBuffer sb = new StringBuffer();
231 XMLTools.xmlNodeToString(sb, xmlDoc.getDocumentElement(), true, " ", 0);
232 editor.setText(sb.toString());
233 //editor.setText(elementToString(xmlDoc.getDocumentElement(), true)); // reading in file this way adds processing instruction, which the parser doesn't like.
234
235 } else { // parsing failed
236 // so read in file as text and display as text and show the validation box in red
237
238 // Won't re-save file that is already invalid before it's even been edited through this Editor
239 System.err.println("*** Warning: Parsing error in file " + config_file + ".");
240 System.err.println("*** Not saving ahead of editing.");
241
242 String xml_str = Utility.readFile(config_file);
243
244 // re-parse to get error msg to display in validation field
245
246 // but first get rid of the preprocessing instruction, as this will fail parsing
247 String processingInstruction = getProcessingInstruction(xml_str);
248 xml_str = xml_str.replace(processingInstruction, "").trim(); // also trim newline at start
249
250 if(!xml_str.equals("")) {
251
252 String msg = XMLTools.parse(xml_str); // re-parse
253 editor_msgarea.setBackground(Color.red);
254 editor_msgarea.setText(msg); // display the parsing error
255 save_button.setEnabled(false);
256 addElementButton.setEnabled(false);
257
258 editor.setText(xml_str); // display the xml contents with error and all
259
260 } else {
261 editor.setText("");
262 editor_msgarea.setText("Empty collection config file");
263 }
264 }
265
266
267 // Final dialog setup & positioning.
268 this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); // get rid of this dialog when it's closed (on dispose())
269 this.addWindowListener(new WindowAdapter() { // called on dispose. Return focus to any curr GLI Pane
270 @Override
271 public void windowClosed(WindowEvent we) {
272 super.windowClosed(we);
273 Gatherer.g_man.doRegainFocus();
274 //Gatherer.g_man.refresh(Gatherer.COLLECTION_OPENED, true);
275 Gatherer.g_man.updateUI();
276 }
277 });
278
279
280
281 getRootPane().setDefaultButton(save_button);
282 Dimension screen_size = Configuration.screen_size;
283 setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
284 editor.setCaretPosition(0); // start at top of config XML
285
286 // Following has no effect: not sure why on dialog show, with or without requesting focus,
287 // it takes some time for the editor to get focus and only if user's mouse curser is
288 // hovering over ConfigFileEditor and moves about a bit there.
289 // Give editor focus on dialog show, so that Ctrl-F, to shift focus to searchField, will be
290 // responsive too. Not working until mouse moves about over editor, despite the suggestion
291 // at https://stackoverflow.com/questions/17828264/java-swing-jdialog-default-focus
292 editor.requestFocusInWindow();
293 }
294
295
296 public void actionPerformed(ActionEvent e) {
297
298 if(e.getSource() == cancel_button) {
299 this.dispose(); // get rid of this dialog, we're done
300 }
301 else if(e.getSource() == save_button) {
302 // write out the XML to the collectionConfig.xml file and we're done.
303
304 // already know the xml in the textarea is valid, else the save_button would have been inactive
305 Document xml_file_doc = XMLTools.getDOM(editor.getText());
306 // This code from FormatConversionDialog.dispose()
307 // saves the XML, prepending the processing instruction (<?xml ... ?>)
308 String[] nonEscapingTagNames = { StaticStrings.FORMAT_STR, StaticStrings.DISPLAYITEM_STR };
309 XMLTools.writeXMLFile(this.config_file, xml_file_doc, nonEscapingTagNames);
310
311 this.dispose(); // get rid of this dialog, we're done
312
313 // close and reopen the collection in GLI
314 String current_collection_filepath = Gatherer.c_man.getCollection().getCollectionPath();
315 ////Gatherer.c_man.closeCollection(); // explicit save was necessary after all
316 // closeCollection() doesn't auto-save
317 ////Gatherer.c_man.loadCollection(current_collection_filepath);
318
319
320 // Fixing bug: Edit > colcfg wasn't being saved on Save when client-GLI.
321 // And in fact, it wasn't properly saving in GLI for some GLI panes.
322 // That's because save needs to be explicitly called. (Didn't understand how GLI's
323 // "autosave" worked until now. GLI > File > Close a collection saves it, but underneath
324 // it calles SAVEandCloseCollection.
325
326 // Works - but arduous: have to load in the entire collection just because cfg changed?
327 // Especial pain with client-GLI where reloading takes forever.
328 //Gatherer.g_man.saveThenCloseCurrentCollection();
329 //Gatherer.c_man.loadCollection(current_collection_filepath);
330
331 // Better way: don't have to close and reopen the entire collection after editing colCfg.xml
332 // This just updates GLI's interface with the current collCfg.xml.
333 // Fingers crossed this works for client-GLI, because then the coll won't need to be closed
334 // and reopened each time after Edit > collectionCfg.xml gets saved.
335 Gatherer.c_man.saveCollection(); // should save colcfg whether GLI or client-GLI
336 Gatherer.c_man.reloadAfterConfigFileEdited();
337 //Gatherer.g_man.updateUI(); // didn't do what I hoped it would, so is this relevant here?
338 // It's done when a new collection is loaded, so may be useful?
339 // But all my tests concerning GLI > Edit > collConfig.xml worked out without it
340
341 }
342
343 // Find functionality, can extend for Find and Replace
344 // taken from https://github.com/bobbylight/RSyntaxTextArea/wiki/Example:-Using-Find-and-Replace
345 else if(e.getSource() == searchField) {
346 nextButton.doClick(0);
347 }
348 else if(e.getSource() == nextButton || e.getSource() == prevButton) {
349 // "FindNext" => search forward, "FindPrev" => search backward
350 String command = e.getActionCommand();
351 boolean forward = "FindNext".equals(command);
352
353 // Create an object defining our search parameters.
354 SearchContext context = new SearchContext();
355 String text = searchField.getText();
356 if (text.length() == 0) {
357 return;
358 }
359 context.setSearchFor(text);
360 context.setMatchCase(matchCaseCB.isSelected());
361 context.setRegularExpression(regexCB.isSelected());
362 context.setSearchForward(forward);
363 context.setWholeWord(false);
364
365 //boolean found = SearchEngine.find(this.editor, context).wasFound();
366 boolean found = false;
367 try {
368 found = SearchEngine.find(this.editor, context);
369 // if the current search string is a correction to a previous invalid regex,
370 // then return background colour back to normal
371 searchField.setBackground(Color.white);
372 if (!found) {
373 JOptionPane.showMessageDialog(this, "Text not found");
374 }
375 } catch(java.util.regex.PatternSyntaxException regex_exception) {
376 searchField.setBackground(Color.red);
377 JOptionPane.showMessageDialog(this, "Invalid regex");
378 }
379
380 }
381 else if(e.getSource() == addElementButton) {
382
383 ElementOption elementToAdd = (ElementOption)chooseElementComboBox.getSelectedItem();
384 addElementToConfigFile(elementToAdd);
385
386 }
387 }
388
389 /** Method to add the selected security-related element to the config file
390 * so that the user can then edit it to behave as they wish.
391 */
392 private void addElementToConfigFile(ElementOption elementOption) {
393
394 // if the user just undid a previous add operation, then the editor would be empty
395 // and can't be parsed. In such a case, don't try to add any elements into the editor
396 String config_xml_str = editor.getText();
397 if(config_xml_str.equals("")) {
398 editor_msgarea.setText(Dictionary.get("ConfigFileEditor.Press_Undo_Once_More_First"));
399 editor_msgarea.setBackground(Color.orange);
400 return;
401 }
402 Document config_xml_doc = XMLTools.getDOM(config_xml_str);
403 // The addElementButton would not have been enabled/available for pressing
404 // and we wouldn't be here unless the collConfig xml parses fine.
405
406 Element targetParent = config_xml_doc.getDocumentElement(); // tentative target element
407
408
409 final String SECURITY = "security"; // just looking for security elements to insert
410 final String EXCEPTION = "exception"; // just looking for security elements to insert
411 final String DOCUMENTSET = "documentSet"; // just looking for security elements to insert
412
413 NodeList children = elements_available_for_insertion.getElementsByTagName(SECURITY);
414
415 Element elementToAdd = null;
416
417 // To find out if the collectionConfig.xml has any security nodes in it already
418 NodeList configXMLSecurityNodes = config_xml_doc.getElementsByTagName(SECURITY);
419
420
421 // Switch on an Enum type is interesting:
422 // don't have to do "case ElementOption.HIDE_COLLECTION:"
423 // and just "case HIDE_COLLECTION:" is fine
424 // see https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
425
426 switch(elementOption) {
427 case HIDE_COLLECTION:
428 Element publicElement = XMLTools.getNamedElement(
429 config_xml_doc.getDocumentElement(), // parent
430 "metadata", // element name
431 "name", // attribute name
432 "public" // attribute value
433 );
434
435 if(publicElement != null) {
436 XMLTools.setNodeText(publicElement, "false");
437 // done, with elementToAdd = null, no more processing
438 } else { // collConfig.xml missing <metadata name="public">true</metadata>
439 // need to add in the necessary element from elements_available_for_insertion XML,
440 // which is set to hide
441 elementToAdd = XMLTools.getNamedElement(
442 elements_available_for_insertion.getDocumentElement(), // parent
443 "metadata", // element name
444 "name", // attribute name
445 "public" // attribute value
446 );
447 }
448 break;
449
450 case SECURE_COLLECTION:
451 case SECURE_ALL_DOCUMENTS:
452 case SECURE_CERTAIN_DOCUMENTS:
453 if(configXMLSecurityNodes.getLength() == 0) {
454 elementToAdd = (Element)children.item(elementOption.ordinal()-1);
455 } else {
456 editor_msgarea.setText(Dictionary.get("ConfigFileEditor.Config_Already_Has_Security_Element"));
457 editor_msgarea.setBackground(Color.orange);
458 }
459 break;
460
461
462 case ADD_ANOTHER_DOCUMENTSET:
463 case ADD_ANOTHER_EXCEPTION:
464
465 if(configXMLSecurityNodes.getLength() > 0) {
466
467 // get first security element child of collectionConfigXML file.
468 // There should at most be one
469 targetParent = (Element)configXMLSecurityNodes.item(0);
470 Element lastSecurityElement = (Element)children.item(children.getLength()-1);
471
472 if(elementOption == ElementOption.ADD_ANOTHER_DOCUMENTSET) {
473 // Get the immediate child called documentSet, not any other descendants by that name
474 // i.e. not the child <exception> element's child called <documentSet>, but the last
475 // <security> element's <documentSet> element
476 elementToAdd = (Element)XMLTools.getChildByTagName(lastSecurityElement, DOCUMENTSET);
477 }
478 else { // ElementOption.ADD_ANOTHER_EXCEPTION:
479
480 // Get the last <security> element's sole <exception> child
481 elementToAdd = (Element)XMLTools.getChildByTagName(lastSecurityElement, EXCEPTION);
482 }
483 }
484 else {
485 // Can't Add Another DocSet/Exception element if the collectionConfigXML has no
486 // Security element in place to add them into.
487 // Display an error/warning if no security element and don't do anything.
488
489 targetParent = null;
490 editor_msgarea.setText(Dictionary.get("ConfigFileEditor.Need_Security_Element_To_Add"));
491 editor_msgarea.setBackground(Color.orange);
492 }
493 break;
494 }
495
496 if(elementToAdd != null) {
497 Node copiedNode = config_xml_doc.importNode(elementToAdd, true);
498 targetParent.appendChild(copiedNode);
499 StringBuffer xml_str_buf = new StringBuffer();
500 // now set the text in the XML editor to reflect the changes to the doc
501 XMLTools.xmlNodeToString(xml_str_buf, config_xml_doc.getDocumentElement(), true, " ", 0);
502 editor.setText(xml_str_buf.toString()); // display the xml contents with error and all
503 } // TODO: sadly, the act of doing a editor.setText() adds 2 actions to undo history instead of 1
504 }
505
506 // This method returns a proper processing instruction (starts with <?xml and ends with ?>)
507 // else the empty string is returned
508 public String getProcessingInstruction(String xmlStr) {
509 String processingInstruction = "";
510
511 xmlStr = xmlStr.trim();
512 if(xmlStr.startsWith("<?xml")) {
513 int endIndex = xmlStr.indexOf("?>"); // end of processing instruction
514
515 if(endIndex != -1) {
516 endIndex += 2;
517 processingInstruction = xmlStr.substring(0, endIndex);
518 }
519 }
520 return processingInstruction;
521 }
522
523
524 // THE FOLLOWING FUNCTIONS ARE LARGELY FROM Format4gs3Manager.java.EditorListener
525 public void changedUpdate(DocumentEvent e)
526 {
527 update();
528 }
529
530 public void insertUpdate(DocumentEvent e)
531 {
532 update();
533 updateUndo("insert");
534
535 }
536
537 public void removeUpdate(DocumentEvent e)
538 {
539 update();
540 updateUndo("remove");
541
542 }
543
544 private void updateUndo(String from)
545 {
546
547 editor.undoButton.setEnabled(true);
548 }
549
550 public void update()
551 {
552
553 String xml_str = editor.getText();
554
555 // Processing instructions "<?xml...?>" are no longer displayed in the editor (but
556 // they are written out to the file on save) so we don't need to deal with them here.
557
558 // can't parse with processing instruction <?xml...?>, so remove that
559 ///String processingInstruction = getProcessingInstruction(xml_str);
560 ///xml_str = xml_str.replace(processingInstruction, "");
561 // now can parse the XML portion without the processing instruction,
562 // while the original XML remains unchanged in the editor area
563 // If the processing instruction was incomplete, then xml_str would still
564 // include the incomplete processing instruction, and parsing below will catch the error.
565
566 String msg = XMLTools.parse(xml_str);
567 editor_msgarea.setText(msg);
568
569 if (msg.startsWith(XMLTools.WELLFORMED))
570 {
571 editor_msgarea.setBackground(Color.white);
572 save_button.setEnabled(true);
573 }
574 else
575 {
576 editor_msgarea.setBackground(Color.red);
577 save_button.setEnabled(false);
578 addElementButton.setEnabled(false);
579 }
580 }
581
582
583 // if Ctrl (or Mac equiv) + F is pressed in the config file editing area,
584 // then move the focus to the searchField, so the user can start typing the searchTerm
585 private class ConfigFileEditorKeyListener
586 extends KeyAdapter
587 {
588 /** Gives notification of key events on the text field */
589 public void keyPressed(KeyEvent key_event)
590 {
591 // Don't hardcode check for Ctrl key "(e.getModifiers() & KeyEvent.CTRL_MASK) != 0)"
592 // because Mac uses another modifier key, Command-F instead of Ctrl-F.
593 // https://stackoverflow.com/questions/5970765/java-detect-ctrlx-key-combination-on-a-jtree
594 if((key_event.getModifiers() == Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())
595 && key_event.getKeyCode() == KeyEvent.VK_F) {
596
597 // Instead of requestFocus() or the more forceful sounding grabFocus(),
598 // use requestFocusInWindow(), see
599 // https://stackoverflow.com/questions/1425392/how-do-you-set-a-focus-on-textfield-in-swing
600 ConfigFileEditor.this.searchField.requestFocusInWindow();
601 //JOptionPane.showMessageDialog(ConfigFileEditor.this, "Got a key", "Got key " + key_event.getKeyCode(), JOptionPane.INFORMATION_MESSAGE);
602 }
603 }
604 }
605}
Note: See TracBrowser for help on using the repository browser.