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

Last change on this file was 37764, checked in by anupama, 11 months ago

Following Dr Bainbridge's instruction, I think I've now worked out how to display just the raw XML in GLI's Config File editor (available under GLI's Edit menu). This is to solve the problem noticed when following the DjVu/UnknownConverterPlugin tutorial: the tutorial advises using escaped quotes (backslash-quote) to embed filepaths containing spaces. That worked fine until viewing the config file in GLI's config File editor, where the embedded quote is not allowed and needs to be an entity. Changing it to an entity in the config file editor changes the value in the UnknownConverterPlugin option and breaks building the collection. From preliminary testing, this minor change suffices to solve this problem and doesn't appear to cause other problems, for example, it doesn't clash with HTML collection descriptions (containing XML-valid HTML elements) added through the Formal panel's General section.

File size: 25.3 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 //} // outer else statement
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 //editor.setCaretPosition(0);
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 // **** getModifiers() in InputEvent has been deprecated
592 // Oracle directs you to use getModifiersEx() which is an extended version that
593 // has been more carefully constructed.
594 //
595
596 // int onmask = SHIFT_DOWN_MASK | BUTTON1_DOWN_MASK;
597 // int offmask = CTRL_DOWN_MASK;
598 // if ((event.getModifiersEx() & (onmask | offmask)) == onmask) {
599 // // ...
600 // }
601
602 // The above code excerpt is from:
603 // https://docs.oracle.com/javase/10/docs/api/java/awt/event/InputEvent.html
604 //
605 // In working through changes to avoid using this deprecated call, as similar
606 // change from using getMenuShortcutKeyMask() to getMenuShortcutKeyMaskEx()
607 // will be needed.
608
609
610 // Don't hardcode check for Ctrl key "(e.getModifiers() & KeyEvent.CTRL_MASK) != 0)"
611 // because Mac uses another modifier key, Command-F instead of Ctrl-F.
612 // https://stackoverflow.com/questions/5970765/java-detect-ctrlx-key-combination-on-a-jtree
613 if((key_event.getModifiers() == Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())
614 && key_event.getKeyCode() == KeyEvent.VK_F) {
615
616 // Instead of requestFocus() or the more forceful sounding grabFocus(),
617 // use requestFocusInWindow(), see
618 // https://stackoverflow.com/questions/1425392/how-do-you-set-a-focus-on-textfield-in-swing
619 ConfigFileEditor.this.searchField.requestFocusInWindow();
620 //JOptionPane.showMessageDialog(ConfigFileEditor.this, "Got a key", "Got key " + key_event.getKeyCode(), JOptionPane.INFORMATION_MESSAGE);
621 }
622 }
623 }
624}
Note: See TracBrowser for help on using the repository browser.