package org.basex.gui; import static org.basex.gui.layout.BaseXKeys.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.plaf.basic.*; import org.basex.core.*; import org.basex.core.parse.*; import org.basex.data.*; import org.basex.gui.layout.*; import org.basex.query.*; import org.basex.util.list.*; import org.basex.util.options.*; /** * This class offers a text field for keyword and XQuery input. * * @author BaseX Team 2005-17, BSD License * @author Christian Gruen */ public final class GUIInput extends BaseXTextField { /** Reference to main window. */ private final GUI gui; /** JComboBox. */ private final BaseXCombo box; /** BasicComboPopup Menu. */ private GUIInputPopup pop; /** String for temporary input. */ private String pre = ""; /** * Default constructor. * @param main main window reference */ GUIInput(final GUI main) { super(main); gui = main; final Font f = getFont(); final int fs = f.getSize(), ns = (int) (fs * 1.2d); setFont(f.deriveFont(ns)); final Dimension ps = getPreferredSize(); setPreferredSize(new Dimension(ps.width, ps.height + ns - fs)); box = new BaseXCombo(main); box.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { if(e.getModifiers() == InputEvent.BUTTON1_MASK) completeInput(); } }); pop = new GUIInputPopup(box); addKeyListener(new KeyAdapter() { @Override public void keyPressed(final KeyEvent e) { final int count = box.getItemCount(); if(ENTER.is(e)) { if(pop.isVisible()) { completeInput(); } else { store(); } // evaluate the input if(e.getModifiers() == 0) gui.execute(); } if(count == 0) return; int bi = box.getSelectedIndex(); if(NEXTLINE.is(e)) { if(pop.isVisible()) { if(++bi == count) bi = 0; } else { showPopup(); } } else if(PREVLINE.is(e)) { if(pop.isVisible()) { if(--bi < 0) bi = count - 1; } else { showPopup(); } } if(bi != box.getSelectedIndex()) box.setSelectedIndex(bi); } @Override public void keyReleased(final KeyEvent e) { if(ESCAPE.is(e)) { pop.setVisible(false); } else if(ENTER.is(e)) { pop.hide(); } else if(!NEXTLINE.is(e) && !PREVLINE.is(e)) { if(modifier(e) || control(e)) return; showPopup(); // skip commands if(gui.gopts.get(GUIOptions.EXECRT) && !cmdMode()) main.execute(); } } }); } @Override public void setText(final String txt) { super.setText(txt); box.removeAllItems(); pop.setVisible(false); } @Override public void store() { // store current input in history final Data data = gui.context.data(); final int i = data == null ? 2 : gui.gopts.get(GUIOptions.SEARCHMODE); final StringsOption options = i == 0 ? GUIOptions.SEARCH : i == 1 ? GUIOptions.XQUERY : GUIOptions.COMMANDS; new BaseXHistory(gui, options).store(getText()); } /** * Sets the input mode. * @param mode mode */ void mode(final String mode) { hint(mode + Text.DOTS).setText(""); } /** * Checks if the query is a command. * @return result of check */ private boolean cmdMode() { return gui.gopts.get(GUIOptions.SEARCHMODE) == 2 || gui.context.data() == null || getText().startsWith("!"); } /** * Completes the input with the current combobox choice. */ private void completeInput() { final String suf = box.getSelectedItem(); if(suf == null) return; final int pl = pre.length(); final int ll = pl > 0 ? pre.charAt(pl - 1) : ' '; if(Character.isLetter(ll) && Character.isLetter(suf.charAt(0))) pre += " "; setText(pre + suf); showPopup(); if(gui.gopts.get(GUIOptions.EXECRT) && !cmdMode()) gui.execute(); } /** * Shows the command popup menu. */ private void showPopup() { final String query = getText(); final int mode = gui.gopts.get(GUIOptions.SEARCHMODE); if(cmdMode()) { cmdPopup(query); } else if(mode == 1 || mode == 0 && query.startsWith("/")) { queryPopup(query); } else { pop.setVisible(false); } } /** * Shows the command popup menu. * @param query query input */ private void cmdPopup(final String query) { StringList sl = null; final boolean excl = query.startsWith("!"); try { pre = excl ? "!" : ""; final String suf = getText().substring(pre.length()); CommandParser.get(suf, gui.context).suggest().parse(); } catch(final QueryException ex) { sl = ex.suggest(); final int marked = ex.markedColumn() + (excl ? 2 : 1); if(ex.markedColumn() > -1 && marked <= query.length()) { pre = query.substring(0, marked); } } createCombo(sl); } /** * Shows the xpath popup menu. * @param query query input */ private void queryPopup(final String query) { final Data data = gui.context.data(); if(data == null) return; StringList sl; try(QueryContext qc = new QueryContext(gui.context)) { final QuerySuggest qs = new QuerySuggest(query, qc, data); qs.parseMain(); sl = qs.complete(); pre = query.substring(0, qs.mark); } catch(final QueryException ex) { sl = ex.suggest(); pre = query.substring(0, ex.column() - 1); } if(getCaretPosition() < pre.length()) sl = null; createCombo(sl); } /** * Creates and shows the combo box. * @param sl strings to be added */ private void createCombo(final StringList sl) { if(sl == null || sl.isEmpty()) { pop.setVisible(false); return; } if(comboChanged(sl)) { box.setModel(new DefaultComboBoxModel<>(sl.toArray())); box.setSelectedIndex(-1); pop = new GUIInputPopup(box); } final int w = getFontMetrics(getFont()).stringWidth(pre); pop.show(this, Math.min(getWidth(), w), getHeight()); } /** * Tests if the combo box entries have changed. * @param sl strings to be compared * @return result of check */ private boolean comboChanged(final StringList sl) { if(sl.size() != box.getItemCount()) return true; final int is = sl.size(); for(int i = 0; i < is; ++i) { if(!sl.get(i).equals(box.getItemAt(i))) return true; } return false; } /** Combo popup menu class, overriding the default constructor. */ private static final class GUIInputPopup extends BasicComboPopup { /** * Constructor. * @param combo combobox reference */ GUIInputPopup(final JComboBox<String> combo) { super(combo); final int h = combo.getMaximumRowCount(); setPreferredSize(new Dimension(getPreferredSize().width, getPopupHeightForRowCount(h) + 2)); } } }