diff --git a/src/main/java/org/humanistika/oxygen/tei/completer/GUI/newSuggestionForm.form b/src/main/java/org/humanistika/oxygen/tei/completer/GUI/newSuggestionForm.form new file mode 100644 index 0000000..6eb0d5d --- /dev/null +++ b/src/main/java/org/humanistika/oxygen/tei/completer/GUI/newSuggestionForm.form @@ -0,0 +1,179 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + <Editor/> + <Renderer/> + </Column> + <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> + <Title/> + <Editor/> + <Renderer/> + </Column> + <Column maxWidth="-1" minWidth="-1" prefWidth="-1" resizable="true"> + <Title/> + <Editor/> + <Renderer/> + </Column> + </TableColumnModel> + </Property> + <Property name="columnSelectionAllowed" type="boolean" value="true"/> + <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> + <TableHeader reorderingAllowed="true" resizingAllowed="true"/> + </Property> + </Properties> + <Events> + <EventHandler event="mousePressed" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="restultsJTableMousePressed"/> + </Events> + </Component> + </SubComponents> + </Container> + </SubComponents> +</Form> diff --git a/src/main/java/org/humanistika/oxygen/tei/completer/GUI/newSuggestionForm.java b/src/main/java/org/humanistika/oxygen/tei/completer/GUI/newSuggestionForm.java new file mode 100644 index 0000000..fef672b --- /dev/null +++ b/src/main/java/org/humanistika/oxygen/tei/completer/GUI/newSuggestionForm.java @@ -0,0 +1,396 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/GUIForms/JDialog.java to edit this template + */ +package org.humanistika.oxygen.tei.completer.GUI; + +import org.humanistika.oxygen.tei.completer.SuggestedAutocomplete; +import org.humanistika.oxygen.tei.completer.TeiCompleter; +import org.humanistika.oxygen.tei.completer.configuration.beans.AutoComplete; +import org.jetbrains.annotations.Nullable; +import ro.sync.contentcompletion.xml.CIValue; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * + * @author younes + */ +public class newSuggestionForm extends javax.swing.JDialog { + private TeiCompleter teiCompleter; + private SuggestedAutocomplete suggestedAutocomplete = null; + + private ArrayList<SuggestedAutocomplete> results = new ArrayList<>(); + + /** + * Creates new form JDialogForm + */ + public newSuggestionForm(java.awt.Frame parent, final TeiCompleter teiCompleter) { + super(parent, ModalityType.DOCUMENT_MODAL); + this.teiCompleter = teiCompleter; + initComponents(); + customLabels(); + } + + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents + private void initComponents() { + + cancleJButton = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); + dependentJLabel = new javax.swing.JLabel(); + dependentJTextField = new javax.swing.JTextField(); + selectionJLabel = new javax.swing.JLabel(); + selectionJTextField = new javax.swing.JTextField(); + fetchjButton = new javax.swing.JButton(); + jScrollPane2 = new javax.swing.JScrollPane(); + restultsJTable = new javax.swing.JTable(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("Custom lookup"); + cancleJButton.setText("Cancel"); + cancleJButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancleJButtonActionPerformed(evt); + } + }); + + jLabel1.setFont(new java.awt.Font("Segoe UI", 0, 18)); // NOI18N + jLabel1.setText("Custom lookup"); + + dependentJLabel.setText("Dependent:"); + dependentJLabel.setToolTipText(""); + + dependentJTextField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dependentJTextFieldActionPerformed(evt); + } + }); + + selectionJLabel.setText("Selection:"); + + fetchjButton.setText("Search..."); + fetchjButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + fetchjButtonActionPerformed(evt); + } + }); + + restultsJTable.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + }, + new String [] { + "Value", "Description" + } + ) { + Class[] types = new Class [] { + java.lang.String.class, java.lang.String.class + }; + boolean[] canEdit = new boolean [] { + false, false + }; + + public Class getColumnClass(int columnIndex) { + return types [columnIndex]; + } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit [columnIndex]; + } + }); + restultsJTable.setColumnSelectionAllowed(true); + restultsJTable.addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + restultsJTableMousePressed(evt); + } + }); + jScrollPane2.setViewportView(restultsJTable); + restultsJTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + + restultsJTable.setCellSelectionEnabled(false); + restultsJTable.setRowSelectionAllowed(true); + + restultsJTable.getColumnModel().getColumn(0).setPreferredWidth(120); + restultsJTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(cancleJButton)) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 460, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(jLabel1) + .addGroup(layout.createSequentialGroup() + .addGap(6, 6, 6) + .addComponent(dependentJLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(dependentJTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addGap(16, 16, 16) + .addComponent(selectionJLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(selectionJTextField)) + .addComponent(fetchjButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(dependentJLabel) + .addComponent(dependentJTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(selectionJLabel) + .addComponent(selectionJTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(8, 8, 8) + .addComponent(fetchjButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 307, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancleJButton) + .addContainerGap()) + ); + + selectionJTextField.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { + textChanged(); + } + + public void removeUpdate(DocumentEvent e) { + textChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + + } + }); + + dependentJTextField.getDocument().addDocumentListener(new DocumentListener() { + public void insertUpdate(DocumentEvent e) { + textChanged(); + } + + public void removeUpdate(DocumentEvent e) { + textChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + + } + }); + + pack(); + }// </editor-fold>//GEN-END:initComponents + + private void cancleJButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancleJButtonActionPerformed + // TODO add your handling code here: + this.suggestedAutocomplete = null; + dispose(); + }//GEN-LAST:event_cancleJButtonActionPerformed + + private void dependentJTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dependentJTextFieldActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_dependentJTextFieldActionPerformed + + private void fetchjButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fetchjButtonActionPerformed + // TODO add your handling code here: + + String selection = selectionJTextField.getText(); + String dependent = dependentJTextField.getText(); + + // get the auto complete suggestions based on the user input + final List<CIValue> suggestions = new ArrayList<>(); + + for (final AutoComplete autoComplete : teiCompleter.getConfiguration().getAutoCompletes()) { + suggestions.addAll(teiCompleter.requestAutoComplete(autoComplete, selection, dependent)); + } + + //get the model to populate the table + DefaultTableModel model = (DefaultTableModel) restultsJTable.getModel(); + + for(int i= 0;i< suggestions.size();i++) { + results.add(new SuggestedAutocomplete(suggestions.get(i).getValue(), suggestions.get(i).getAnnotation(), new ArrayList<>())); + model.addRow(new Object[]{suggestions.get(i).getValue(), suggestions.get(i).getAnnotation()}); + } + + }//GEN-LAST:event_fetchjButtonActionPerformed + + private void restultsJTableMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_restultsJTableMousePressed + // TODO add your handling code here: + JTable table =(JTable) evt.getSource(); + Point point = evt.getPoint(); + int row = table.rowAtPoint(point); + if (evt.getClickCount() == 2 && table.getSelectedRow() != -1) { + // your valueChanged overridden method + suggestedAutocomplete = results.get(row); + dispose(); + } + + }//GEN-LAST:event_restultsJTableMousePressed + +// /** +// * @param args the command line arguments +// */ +// public static void main(String args[]) { +// /* Set the Nimbus look and feel */ +// //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> +// /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. +// * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html +// */ +// try { +// for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { +// if ("Nimbus".equals(info.getName())) { +// javax.swing.UIManager.setLookAndFeel(info.getClassName()); +// break; +// } +// } +// } catch (ClassNotFoundException ex) { +// java.util.logging.Logger.getLogger(JDialogForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); +// } catch (InstantiationException ex) { +// java.util.logging.Logger.getLogger(JDialogForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); +// } catch (IllegalAccessException ex) { +// java.util.logging.Logger.getLogger(JDialogForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); +// } catch (javax.swing.UnsupportedLookAndFeelException ex) { +// java.util.logging.Logger.getLogger(JDialogForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); +// } +// //</editor-fold> +// +// /* Create and display the dialog */ +// java.awt.EventQueue.invokeLater(new Runnable() { +// public void run() { +// newSuggestionForm dialog = new newSuggestionForm(new javax.swing.JFrame(), true); +// dialog.addWindowListener(new java.awt.event.WindowAdapter() { +// @Override +// public void windowClosing(java.awt.event.WindowEvent e) { +// System.exit(0); +// } +// }); +// dialog.setVisible(true); +// } +// }); +// } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton cancleJButton; + private javax.swing.JTextField dependentJTextField; + private javax.swing.JButton fetchjButton; + private javax.swing.JLabel jLabel1; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JTable restultsJTable; + private javax.swing.JLabel selectionJLabel; + private javax.swing.JLabel dependentJLabel; + private javax.swing.JTextField selectionJTextField; + // End of variables declaration//GEN-END:variables + + private AtomicBoolean runningState = new AtomicBoolean(false); + + private void textChanged() { + + // GATE Keeper + // reject if either fields have less than 4 chars + // this correspond to requestAutoComplete function as well + //TODO USE a constant for this value + if(selectionJTextField.getText().length() < 4) return; + if(dependentJTextField.getText().length() < 4) return; + + // if we are already fetching results no need to do it again + if(runningState.compareAndSet(false, true)) { + //No SwingWorker is running. + //Create and start swing worker. + LiveAutoComplete live = new LiveAutoComplete(); + live.execute(); + } + } + + @Nullable + public SuggestedAutocomplete getSuggestedAutocomplete() { + return suggestedAutocomplete; + } + + private void customLabels() { + dependentJLabel.setText(this.teiCompleter.getConfiguration().getAutoCompletes().get(0).getDependent().getLabel() + ":"); + selectionJLabel.setText(this.teiCompleter.getConfiguration().getAutoCompletes().get(0).getSelection().getLabel() + ":"); + } + + public class LiveAutoComplete extends SwingWorker { + List<CIValue> suggestions = new ArrayList<>(); + String selection; + String dependent; + @Override + protected Object doInBackground() throws Exception { + + DefaultTableModel model = (DefaultTableModel) restultsJTable.getModel(); + // clear the old results + for(int i =0; i < model.getRowCount(); i++) { + model.removeRow(i); + } + + // Introduce a delay of 300 milliseconds + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // Handle interruption if necessary + } + + // get the text field values after the cooldown + selection = selectionJTextField.getText(); + dependent = dependentJTextField.getText(); + + // get the auto complete suggestions based on the user input + final List<CIValue> suggestions = new ArrayList<>(); + + for (final AutoComplete autoComplete : teiCompleter.getConfiguration().getAutoCompletes()) { + suggestions.addAll(teiCompleter.requestAutoComplete(autoComplete, selection, dependent)); + } + + this.suggestions.addAll(suggestions); + + return null; + } + + @Override + protected void done() { + // Update UI on EDT when the task is complete + DefaultTableModel model = (DefaultTableModel) restultsJTable.getModel(); + + //populate with the new results + for(int i= 0;i< suggestions.size();i++) { + results.add(new SuggestedAutocomplete(suggestions.get(i).getValue(), suggestions.get(i).getAnnotation(), new ArrayList<>())); + model.addRow(new Object[]{suggestions.get(i).getValue(), suggestions.get(i).getAnnotation()}); + System.out.println(suggestions.get(i).getAnnotation()); + } + + if(suggestions.size() == 0) { + model.addRow(new Object[]{"No results Matching " + dependent + "+" + selection , ""}); + } + + runningState.set(false); + } + } +} diff --git a/src/main/java/org/humanistika/oxygen/tei/completer/SuggestedAutocomplete.java b/src/main/java/org/humanistika/oxygen/tei/completer/SuggestedAutocomplete.java new file mode 100644 index 0000000..9c34b0d --- /dev/null +++ b/src/main/java/org/humanistika/oxygen/tei/completer/SuggestedAutocomplete.java @@ -0,0 +1,52 @@ +package org.humanistika.oxygen.tei.completer; + + +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class SuggestedAutocomplete { + + public static class UserValue { + private final String name; + private final String value; + + public UserValue(final String name, final String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + } + + private final String suggestion; + @Nullable + private final String description; + @Nullable private final List<UserValue> userValues; + + public SuggestedAutocomplete(final String suggestion, final String description, final List<UserValue> userValues) { + this.suggestion = suggestion; + this.description = description; + this.userValues = userValues; + } + + public String getSuggestion() { + return suggestion; + } + + @Nullable + public String getDescription() { + return description; + } + + @Nullable + public List<UserValue> getUserValues() { + return userValues; + } +} diff --git a/src/main/java/org/humanistika/oxygen/tei/completer/TeiCompleter.java b/src/main/java/org/humanistika/oxygen/tei/completer/TeiCompleter.java index 7bd5ba7..3b616e5 100755 --- a/src/main/java/org/humanistika/oxygen/tei/completer/TeiCompleter.java +++ b/src/main/java/org/humanistika/oxygen/tei/completer/TeiCompleter.java @@ -25,6 +25,7 @@ import org.humanistika.ns.tei_completer.Suggestion; import org.humanistika.ns.tei_completer.Suggestions; +import org.humanistika.oxygen.tei.completer.GUI.newSuggestionForm; import org.humanistika.oxygen.tei.completer.configuration.beans.Authentication; import org.humanistika.oxygen.tei.completer.configuration.beans.AutoComplete; import org.humanistika.oxygen.tei.completer.configuration.Configuration; @@ -35,6 +36,7 @@ import org.humanistika.oxygen.tei.completer.remote.ClientFactory.AuthenticationType; import org.humanistika.oxygen.tei.completer.remote.impl.JerseyClientFactory; import javax.annotation.Nullable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; @@ -50,6 +52,10 @@ import static org.humanistika.oxygen.tei.completer.XPathUtil.isSubset; import static org.humanistika.oxygen.tei.completer.XPathUtil.parseXPath; + +import java.awt.*; +import java.util.List; + /** * TEI-Completer * @@ -87,6 +93,11 @@ public List<CIValue> filterAttributeValues(final List<CIValue> list, final WhatP if(autoCompleteSuggestions != null) { list.addAll(autoCompleteSuggestions.getSuggestions()); } + if(autoCompleteSuggestions != null && autoCompleteSuggestions.getSuggestions().size() == 0) { + // the value needs to be prefixed with a space character to bump it to the top of the list + list.add(new CustomCIValue(" Custom Entry...", this)); + } + } return list; } @@ -102,7 +113,13 @@ public List<CIValue> filterAttributeValues(final List<CIValue> list, final WhatP protected final AutoCompleteSuggestions<AutoComplete> getAutoCompleteSuggestions(final WhatPossibleValuesHasAttributeContext context) { final String elemXPath = context.computeContextXPathExpression(); final String attrXPath = elemXPath + "/@" + context.getAttributeName(); - final Expr attributeExpr = parseXPath(attrXPath); + Expr attributeExpr; + try { + attributeExpr = parseXPath(attrXPath); + } catch (Exception e) { + return null; + } + for (final AutoComplete autoComplete : getConfiguration().getAutoCompletes()) { final AutoCompleteXPaths autoCompleteXPaths = getXPaths(autoComplete); @@ -130,19 +147,24 @@ protected final AutoCompleteSuggestions<AutoComplete> getAutoCompleteSuggestions return null; } - protected List<CIValue> requestAutoComplete(final AutoComplete autoComplete, final String selection, @Nullable final String dependent) { + public List<CIValue> requestAutoComplete(final AutoComplete autoComplete, final String selection, @Nullable final String dependent) { final Authentication.AuthenticationType authenticationType = autoComplete.getRequestInfo().getAuthentication() == null ? null : autoComplete.getRequestInfo().getAuthentication().getAuthenticationType(); - final Suggestions suggestions = getClient(authenticationType).getSuggestions(autoComplete.getRequestInfo(), selection, dependent, autoComplete.getResponseAction()); - final List<CIValue> results = new ArrayList<>(); - for(final Suggestion suggestion : suggestions.getSuggestion()) { - results.add(new CIValue(suggestion.getValue(), suggestion.getDescription())); + //TODO USE a constant for this value + if(selection.length() > 3 && dependent.length() > 3) { + final Suggestions suggestions = getClient(authenticationType).getSuggestions(autoComplete.getRequestInfo(), selection, dependent, autoComplete.getResponseAction()); + final List<CIValue> results = new ArrayList<>(); + for(final Suggestion suggestion : suggestions.getSuggestion()) { + results.add(new CIValue(suggestion.getValue(), suggestion.getDescription())); + } + return results; } - return results; + return Collections.emptyList(); //TODO(AR) consider some visual warnings/errors in Oxygen such as JOptionPane.showMessageDialog(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(), "some error message here"); } - protected Configuration<? extends AutoComplete> getConfiguration() { + + public Configuration<? extends AutoComplete> getConfiguration() { if(configuration == null) { synchronized(this) { if(configuration == null) { @@ -304,7 +326,7 @@ protected String getDependent(final Context context, final String elemXPath, fin } private String getAutoCompleteSelectionXPath(final String elemXPath, final AutoComplete autoComplete) { - return elemXPath + "/" + autoComplete.getSelection(); + return elemXPath + "/" + autoComplete.getSelection().getDefault(); } private String getAutoCompleteDependentXPath(final String elemXPath, final Dependent dependent) { @@ -374,4 +396,63 @@ public List<CIAttribute> filterAttributes(final List<CIAttribute> list, final Wh public List<CIValue> filterElementValues(final List<CIValue> list, final Context context) { return list; } + + + /** + * A CIValue labelled "Add New..." which + * prompts the user to enter a new suggestion + * via a dialog box + */ + public class CustomCIValue extends CIValue { + private TeiCompleter teiCompleter; + private String suggestion; + public CustomCIValue(String s, final TeiCompleter teiCompleter) { + super(s); + this.teiCompleter = teiCompleter; + } + + @Override + public String getInsertString() { + if(suggestion == null) { + //Ask the user for an autocomplete dependent and selection + final SuggestedAutocomplete suggestedAutocomplete = promptUserForNewSuggestion(); + suggestion = suggestedAutocomplete.getSuggestion(); + } + + return suggestion; + } + + private SuggestedAutocomplete promptUserForNewSuggestion() { + final KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + final Component comp = keyboardFocusManager.getFocusOwner(); + final Frame parentFrame = getParentFrame(comp); + final newSuggestionForm newSuggestionForm = new newSuggestionForm(parentFrame, teiCompleter); + + + + //display the dialog + newSuggestionForm.setLocationRelativeTo(parentFrame); + newSuggestionForm.setVisible(true); + final SuggestedAutocomplete suggestedAutocomplete = newSuggestionForm.getSuggestedAutocomplete(); + newSuggestionForm.dispose(); + return suggestedAutocomplete; + } + + private Frame getParentFrame(final Component component) { + if(component == null) { + return null; + } + + final Component parent = component.getParent(); + if(parent == null) { + return null; + } + + if(parent instanceof Frame) { + return (Frame)parent; + } + + return getParentFrame(parent); + } + } } diff --git a/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/AutoComplete.java b/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/AutoComplete.java index 3f71b5c..b8011a1 100644 --- a/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/AutoComplete.java +++ b/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/AutoComplete.java @@ -36,11 +36,11 @@ public class AutoComplete { private final String context; private final String attribute; @Nullable private final Dependent dependent; - private final String selection; + private final Selection selection; private final RequestInfo requestInfo; @Nullable private final ResponseAction responseAction; - public AutoComplete(final Map<String, String> namespaceBindings, final String context, final String attribute, final Dependent dependent, final String selection, final RequestInfo requestInfo, final ResponseAction responseAction) { + public AutoComplete(final Map<String, String> namespaceBindings, final String context, final String attribute, final Dependent dependent, final Selection selection, final RequestInfo requestInfo, final ResponseAction responseAction) { this.namespaceBindings = namespaceBindings; this.context = context; this.attribute = attribute; @@ -67,7 +67,7 @@ public Dependent getDependent() { return dependent; } - public String getSelection() { + public Selection getSelection() { return selection; } @@ -79,4 +79,6 @@ public RequestInfo getRequestInfo() { public ResponseAction getResponseAction() { return responseAction; } + + } diff --git a/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/Dependent.java b/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/Dependent.java index bfaf35d..05dc84f 100644 --- a/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/Dependent.java +++ b/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/Dependent.java @@ -28,9 +28,12 @@ public class Dependent { @Nullable private final String def; //default private final String attribute; - public Dependent(@Nullable final String def, final String attribute) { + private final String label; + + public Dependent(@Nullable final String def, final String attribute, final String label) { this.def = def; this.attribute = attribute; + this.label = label; } @Nullable @@ -41,4 +44,8 @@ public String getDefault() { public String getAttribute() { return attribute; } + + public String getLabel() { + return label; + } } diff --git a/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/Selection.java b/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/Selection.java new file mode 100644 index 0000000..21b861e --- /dev/null +++ b/src/main/java/org/humanistika/oxygen/tei/completer/configuration/beans/Selection.java @@ -0,0 +1,45 @@ +/** + * TEI Completer + * An Oxygen XML Editor plugin for customizable attribute and value completion for TEI P5 documents + * Copyright (C) 2016 Belgrade Center for Digital Humanities + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +package org.humanistika.oxygen.tei.completer.configuration.beans; + +import javax.annotation.Nullable; + +/** + * Configuration Details of an optional selection of the {@link AutoComplete#context} + */ +public class Selection { + @Nullable private final String def; //default + + private final String label; + + public Selection(@Nullable final String def, final String label) { + this.def = def; + this.label = label; + } + + @Nullable + public String getDefault() { + return def; + } + + public String getLabel() { + return label; + } +} diff --git a/src/main/java/org/humanistika/oxygen/tei/completer/configuration/impl/XmlConfiguration.java b/src/main/java/org/humanistika/oxygen/tei/completer/configuration/impl/XmlConfiguration.java index ddada05..a98de37 100755 --- a/src/main/java/org/humanistika/oxygen/tei/completer/configuration/impl/XmlConfiguration.java +++ b/src/main/java/org/humanistika/oxygen/tei/completer/configuration/impl/XmlConfiguration.java @@ -102,7 +102,18 @@ private List<T> expandConfig(final Config config) { } else { dependent = new Dependent( autoComplete.getDependent().getDefault(), - autoComplete.getDependent().getValue() + autoComplete.getDependent().getValue(), + autoComplete.getDependent().getLabel() + ); + } + + final Selection selection; + if(autoComplete.getSelection() == null) { + selection = null; + } else { + selection = new Selection( + autoComplete.getSelection().getValue(), + autoComplete.getSelection().getLabel() ); } @@ -124,7 +135,7 @@ private List<T> expandConfig(final Config config) { autoComplete.getContext(), autoComplete.getAttribute(), dependent, - autoComplete.getSelection(), + selection, requestInfo, responseAction )); diff --git a/src/main/resources/config.xsd b/src/main/resources/config.xsd index 9e7085f..f97a95b 100644 --- a/src/main/resources/config.xsd +++ b/src/main/resources/config.xsd @@ -6,10 +6,10 @@ 26 January 2016 --> <xs:schema targetNamespace="http://humanistika.org/ns/tei-completer" version="1.0" - elementFormDefault="qualified" - xmlns:xs="http://www.w3.org/2001/XMLSchema" - xmlns:tc="http://humanistika.org/ns/tei-completer" - xmlns:h="http://www.w3.org/1999/xhtml"> + elementFormDefault="qualified" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:tc="http://humanistika.org/ns/tei-completer" + xmlns:h="http://www.w3.org/1999/xhtml"> <xs:annotation> <xs:documentation>XML Schema for configuration of the TEI-Completer.</xs:documentation> </xs:annotation> @@ -73,7 +73,7 @@ </xs:element> <xs:element name="dependent"> <xs:annotation> - <xs:documentation>Optional. An attribute which we have a dependency on being non-empty</xs:documentation> + <xs:documentation>Optional. An attribute which we have a dependency on being non-empty</xs:documentation> </xs:annotation> <xs:complexType> <xs:simpleContent> @@ -83,14 +83,30 @@ <xs:documentation>An optional default value to be used for the dependency if the attribute value is empty</xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute name="label" type="xs:string" default="Dependent"> + <xs:annotation> + <xs:documentation>A human-readable label for the dependent, this is used in the GUI.</xs:documentation> + </xs:annotation> + </xs:attribute> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> - <xs:element name="selection" type="tc:xpathRelativePath"> + <xs:element name="selection"> <xs:annotation> <xs:documentation>The XPath (relative to the context) to use for the selection. e.g. text()</xs:documentation> </xs:annotation> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="tc:xpathRelativePath"> + <xs:attribute name="label" type="xs:string" default="Selection"> + <xs:annotation> + <xs:documentation>A human-readable label for the selection, this is used in the GUI.</xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:extension> + </xs:simpleContent> + </xs:complexType> </xs:element> <xs:element name="request"> <xs:annotation>