Java tutorial
/* Copyright (C) 2015, University of Kansas Center for Research * * Specify Software Project, specify@ku.edu, Biodiversity Institute, * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA * * 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 edu.ku.brc.af.ui.db; import static edu.ku.brc.ui.UIHelper.createLabel; import static edu.ku.brc.ui.UIHelper.setControlSize; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.HeadlessException; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Hashtable; import java.util.List; import java.util.Vector; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import com.jgoodies.forms.builder.PanelBuilder; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import edu.ku.brc.af.core.db.DBFieldInfo; import edu.ku.brc.af.core.db.DBTableInfo; import edu.ku.brc.af.core.expresssearch.QueryAdjusterForDomain; import edu.ku.brc.af.prefs.AppPreferences; import edu.ku.brc.af.prefs.AppPrefsCache; import edu.ku.brc.af.ui.ESTermParser; import edu.ku.brc.af.ui.forms.FormDataObjIFace; import edu.ku.brc.af.ui.forms.ViewFactory; import edu.ku.brc.af.ui.forms.formatters.UIFieldFormatterIFace; import edu.ku.brc.af.ui.forms.formatters.UIFieldFormatterMgr; import edu.ku.brc.dbsupport.CustomQueryIFace; import edu.ku.brc.dbsupport.CustomQueryListener; import edu.ku.brc.dbsupport.JPAQuery; import edu.ku.brc.dbsupport.QueryResultsContainerIFace; import edu.ku.brc.dbsupport.QueryResultsDataObj; import edu.ku.brc.ui.CustomDialog; import edu.ku.brc.ui.DateWrapper; import edu.ku.brc.ui.DocumentAdaptor; import edu.ku.brc.ui.IconManager; import edu.ku.brc.ui.UIHelper; import edu.ku.brc.ui.UIRegistry; import sun.awt.CausedFocusEvent; /** * @author rod * * @code_status Alpha * * Dec 8, 2007 * */ @SuppressWarnings("serial") public class TextFieldWithQuery extends JPanel { protected static final Logger log = Logger.getLogger(TextFieldWithQuery.class); protected static DateWrapper scrDateFormat = AppPrefsCache.getDateWrapper("ui", "formatting", "scrdateformat"); protected int popupDlgThreshold = 15; protected JTextField textField; protected Object dataObj = null; protected Vector<Integer> idList = new Vector<Integer>(); protected Vector<String> list = new Vector<String>(); protected JPopupMenu popupMenu = null; protected JButton dbBtn; protected boolean isPopupShowing = false; protected AtomicBoolean isDoingQuery = new AtomicBoolean(false); protected boolean ignoreTab = false; protected boolean tabOutSearch = false; protected boolean doAdjustQuery = true; protected boolean doAddAddItem = false; protected DBTableInfo tableInfo; protected DBFieldInfo fieldInfo; protected String sql; protected String displayColumns; protected String format; protected String fieldFormatterName; protected UIFieldFormatterIFace uiFieldFormatter = null; protected String sqlTemplate = null; protected ViewBasedSearchQueryBuilderIFace builder = null; protected String[] keyColumns; protected int numColumns = -1; protected Object[] values; protected Hashtable<Integer, Object[]> duplicatehash = new Hashtable<Integer, Object[]>(); protected List<ListSelectionListener> listSelectionListeners = new ArrayList<ListSelectionListener>(); protected PopupMenuListener popupMenuListener = null; protected Integer selectedId = null; protected String currentText = ""; //$NON-NLS-1$ protected boolean hasNewText = false; protected boolean wasCleared = false; protected boolean ignoreDocChange = false; protected boolean isReadOnlyMode = false; protected AtomicBoolean isDoingCount = new AtomicBoolean(false); protected Integer returnCount = null; protected String prevEnteredText = null; protected String cachedPrevText = null; protected String searchedForText = null; protected FontMetrics fontMetrics = null; protected ExternalQueryProviderIFace externalQueryProvider = null; /** * Constructor. */ public TextFieldWithQuery(final DBTableInfo tableInfo, final String keyColumn, final String displayColumns, final String format, final String fieldFormatterName, final String sqlTemplate) { super(); this.tableInfo = tableInfo; this.fieldInfo = tableInfo.getFieldByName(keyColumn); this.displayColumns = displayColumns != null ? displayColumns : keyColumn; this.format = format; this.fieldFormatterName = fieldFormatterName; this.sqlTemplate = sqlTemplate; if (fieldInfo != null && fieldInfo.getFormatter() != null) { uiFieldFormatter = fieldInfo.getFormatter(); } else if (StringUtils.isNotEmpty(fieldFormatterName)) { uiFieldFormatter = UIFieldFormatterMgr.getInstance().getFormatter(fieldFormatterName); } if (StringUtils.contains(keyColumn, ",")) //$NON-NLS-1$ { keyColumns = StringUtils.split(keyColumn, ","); //$NON-NLS-1$ } else { keyColumns = new String[] { keyColumn }; } popupDlgThreshold = AppPreferences.getRemote().getInt("TFQ.POPUPDLD.THRESHOLD", 15); //$NON-NLS-1$ createUI(); } /** * */ public void createUI() { //setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); setLayout(new BorderLayout()); setOpaque(false); textField = new JTextField(10); // we are going to handle focus traversal manually // so that we can do searches when 'tab' is pressed textField.setFocusTraversalKeysEnabled(false); setControlSize(textField); ImageIcon img = IconManager.getIcon("DropDownArrow", IconManager.IconSize.NonStd); //$NON-NLS-1$ dbBtn = UIHelper.isMacOS() ? new MacGradiantBtn(img) : new JButton(img); dbBtn.setFocusable(false); PanelBuilder pb = new PanelBuilder(new FormLayout("f:d:g,p", "f:p:g"), this); //$NON-NLS-1$ //$NON-NLS-2$ CellConstraints cc = new CellConstraints(); pb.add(textField, cc.xy(1, 1)); pb.add(dbBtn, cc.xy(2, 1)); textField.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (ignoreTab && e.getKeyCode() == KeyEvent.VK_TAB) { ignoreTab = false; } else { cbxKeyReleased(e); } super.keyReleased(e); } @Override public void keyPressed(KeyEvent e) { if (popupMenu != null && popupMenu.isVisible()) { popupMenu.setVisible(false); } super.keyReleased(e); } }); textField.getDocument().addDocumentListener(new DocumentAdaptor() { @Override protected void changed(DocumentEvent e) { prevEnteredText = textField.getText(); boolean oldWasCleared = wasCleared; if (!ignoreDocChange) { wasCleared = wasCleared || selectedId != null; idList.clear(); list.clear(); selectedId = null; if (oldWasCleared != wasCleared && wasCleared) { notifyListenersOfChange( StringUtils.isEmpty(prevEnteredText) ? null : TextFieldWithQuery.this); } } } }); StringBuilder sb = new StringBuilder(); for (String k : keyColumns) { String title = k; DBFieldInfo fi = tableInfo.getFieldByName(k); if (fi != null) { title = fi.getTitle(); } if (sb.length() > 0) sb.append(", "); sb.append(title); } textField.setToolTipText(UIRegistry.getFormattedResStr("TFWQ_SEARCHES_FLDS", sb.toString())); textField.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { if (e instanceof CausedFocusEvent) { switch (((CausedFocusEvent) e).getCause()) { case TRAVERSAL: case TRAVERSAL_BACKWARD: case TRAVERSAL_FORWARD: case TRAVERSAL_UP: case TRAVERSAL_DOWN: ignoreTab = true; } } } @Override public void focusLost(FocusEvent e) { int len = textField.getText().length(); if (len < 1) { setText(""); //$NON-NLS-1$ /////////////////////////////////////////////////////////////////////////////////// // We only want to generate a change event if it once had a value and then it is // cleared and the user tabs to a new control. - rods 02/28/08 /////////////////////////////////////////////////////////////////////////////////// if (wasCleared || selectedId != null) { notifyListenersOfChange(TextFieldWithQuery.this); } } textField.setCaretPosition(0); } }); dbBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (popupMenu != null && popupMenu.isVisible()) { popupMenu.setVisible(false); } log.debug("currentText: " + currentText); doQuery(currentText, currentText, 0); } }); } public void setReadOnlyMode() { this.isReadOnlyMode = true; ViewFactory.changeTextFieldUIForDisplay(textField, false); dbBtn.setVisible(false); } public boolean isPopupShowing() { return isPopupShowing; } /** * @return the prevEnteredText */ public String getPrevEnteredText() { if (StringUtils.isEmpty(prevEnteredText) && StringUtils.isNotEmpty(cachedPrevText)) { return cachedPrevText; } return prevEnteredText; } /** * @param prevEnteredText the prevEnteredText to set */ public void setPrevEnteredText(String prevEnteredText) { this.prevEnteredText = prevEnteredText; this.cachedPrevText = prevEnteredText; } /** * @return the format */ public String getFormat() { return format; } /** * @return the uiFieldFormatter */ public UIFieldFormatterIFace getUiFieldFormatter() { return uiFieldFormatter; } /** * @param externalQueryProvider */ public void setExternalQueryProvider(ExternalQueryProviderIFace externalQueryProvider) { this.externalQueryProvider = externalQueryProvider; } /** * @param sqlTemplate the sqlTemplate to set */ public void setSqlTemplate(String sqlTemplate) { this.sqlTemplate = sqlTemplate; } /* (non-Javadoc) * @see javax.swing.JComponent#setEnabled(boolean) */ @Override public void setEnabled(final boolean enabled) { super.setEnabled(enabled); textField.setEnabled(enabled); dbBtn.setEnabled(enabled); } /** * @param popupMenuListener the popupMenuListener to set */ public void setPopupMenuListener(PopupMenuListener popupMenuListener) { this.popupMenuListener = popupMenuListener; } /** * @param l the listChangeListener to set */ public void addListSelectionListener(ListSelectionListener l) { this.listSelectionListeners.add(l); } /** * @param l the listChangeListener to set */ public void removeListSelectionListener(ListSelectionListener l) { this.listSelectionListeners.remove(l); } /** * @param addAddItem the addAddItem to set */ public void setAddAddItem(boolean addAddItem) { this.doAddAddItem = addAddItem; } /** * Processes the KeyEvent. * @param ev event */ protected void cbxKeyReleased(KeyEvent ev) { if (isReadOnlyMode) { return; } if (ev.getKeyCode() == KeyEvent.VK_SHIFT || ev.getKeyCode() == KeyEvent.VK_LEFT || ev.getKeyCode() == KeyEvent.VK_RIGHT || ev.getKeyCode() == KeyEvent.VK_CONTROL || ev.getKeyCode() == KeyEvent.VK_META) { return; } currentText = textField.getText(); if (uiFieldFormatter != null) { currentText = uiFieldFormatter.formatFromUI(currentText).toString(); } if (currentText.length() == 0 || !hasNewText) { if (ev.getKeyCode() == KeyEvent.VK_TAB) { if (ev.isShiftDown()) { textField.transferFocusBackward(); } else { textField.transferFocus(); } return; } if (ev.getKeyCode() == JAutoCompComboBox.SEARCH_KEY || ev.getKeyCode() == KeyEvent.VK_DOWN) { showPopup(0); // add only return; } if (ev.getKeyCode() != KeyEvent.VK_ENTER) { // Add variable to track whether it once had a value and now it does not rods - 02/28/08 idList.clear(); list.clear(); selectedId = null; // 02/09/08 - This should not be done here - rods // The reason is, that we may have added something only to remove // before leaving the control. So we should never send the notification // just because we delete the contents. (see wasCleared above) /*if (listSelectionListeners != null) { notifyListenersOfChange(TextFieldWithQuery.this); }*/ //log.debug("setting hasNewText to true"); //$NON-NLS-1$ hasNewText = true; } } else { hasNewText = true; //log.debug("setting hasNewText to true"); } if (ev.getKeyCode() == JAutoCompComboBox.SEARCH_KEY || ev.getKeyCode() == KeyEvent.VK_TAB || ev.getKeyCode() == KeyEvent.VK_DOWN) { String origText = textField.getText(); String text = origText; if (uiFieldFormatter != null && !uiFieldFormatter.isNumeric()) { text = uiFieldFormatter.formatFromUI(text).toString(); } text = StringUtils.replace(text, "'", "\'"); text = StringUtils.replace(text, "\"", "\\\""); // direction of focus change 1: forward 0: none -1: backwards int focusChange = (ev.isShiftDown() ? -1 : 1) * (ev.getKeyCode() == KeyEvent.VK_TAB ? 1 : 0); doQuery(text, origText, focusChange); } } /** * @param text */ public void setText(final String text) { ignoreDocChange = true; if (uiFieldFormatter != null && StringUtils.isNotEmpty(text)) { textField.setText(uiFieldFormatter.formatToUI(text).toString()); } else { // 10/2/08 - rods - Not sure why the the if was put here. //if (!textField.getText().isEmpty()) { textField.setText(text); } } ignoreDocChange = false; } /** * @param mi * @param advanceFocus * @param idListClosure */ protected void itemSelected(final JMenuItem mi, final int advanceFocus, Vector<Integer> idListClosure) { hasNewText = false; //log.debug("setting hasNewText to true"); if (advanceFocus != 0) SwingUtilities.invokeLater(new Runnable() { public void run() { transferFocus(advanceFocus); } }); String selectedStr = mi.getText(); int inx = popupMenu.getComponentIndex(mi); if (inx > -1) { if (idListClosure.size() > 0 && (!doAddAddItem || inx > 0)) { selectedId = idListClosure.get(doAddAddItem ? inx - 1 : inx); setText(selectedStr); } if (listSelectionListeners != null) { String value = StringUtils.isEmpty(prevEnteredText) ? cachedPrevText : prevEnteredText; notifyListenersOfChange(mi.getText().equals(UIRegistry.getResourceString("TFWQ_ADD_LABEL")) ? new AddItemEvent(value) : mi); } } } public static class AddItemEvent { public final String value; public AddItemEvent(String value) { this.value = value; } } /** * * @param advanceFocus */ protected void showPopup(final int advanceFocus) { final Vector<Integer> idListClosure = (Vector<Integer>) idList.clone(); if (!isEnabled()) { return; } if (hasNewText || currentText.length() == 0) { ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { itemSelected((JMenuItem) e.getSource(), advanceFocus, idListClosure); } }; popupMenu = new JPopupMenu(); if (popupMenuListener != null) { popupMenu.addPopupMenuListener(popupMenuListener); } popupMenu.addPopupMenuListener(new PopupMenuListener() { public void popupMenuCanceled(PopupMenuEvent e) { isPopupShowing = false; cachedPrevText = null; if (selectedId == null) { setText(""); //$NON-NLS-1$ } } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { isPopupShowing = false; cachedPrevText = prevEnteredText; if (selectedId == null) { setText(""); //$NON-NLS-1$ } //textField.requestFocus(); } public void popupMenuWillBecomeVisible(PopupMenuEvent e) { cachedPrevText = null; isPopupShowing = true; } }); if (doAddAddItem) { JMenuItem mi = new JMenuItem(UIRegistry.getResourceString("TFWQ_ADD_LABEL")); //$NON-NLS-1$ setControlSize(mi); popupMenu.add(mi); mi.addActionListener(al); } for (String str : list) { String label = str; if (uiFieldFormatter != null) { label = uiFieldFormatter.formatToUI(label).toString(); } JMenuItem mi = new JMenuItem(label); setControlSize(mi); popupMenu.add(mi); mi.addActionListener(al); } } if (popupMenu != null) { if (list.size() > 0 || doAddAddItem) { UIHelper.addSpecialKeyListenerForPopup(popupMenu); final Point location = getLocation(); final Dimension size = getSize(); SwingUtilities.invokeLater(new Runnable() { public void run() { popupMenu.setInvoker(TextFieldWithQuery.this); popupMenu.show(TextFieldWithQuery.this, location.x, location.y + size.height); Dimension popupSize = popupMenu.getPreferredSize(); popupMenu.setPopupSize(Math.max(size.width, popupSize.width), popupSize.height); popupMenu.requestFocus(); } }); } else { popupMenu = null; } } } /** * Adds the field name with the table abbrev if it doesn't already have one. * @param abbrev the table abbrev * @param fld the field name * @param selectSB the StringBuilder to add it to */ protected void addTblAbbrev(final String abbrev, final String fld, final StringBuilder selectSB) { if (!StringUtils.contains(fld, '.')) { selectSB.append(tableInfo.getAbbrev()); selectSB.append("."); //$NON-NLS-1$ } selectSB.append(fld.trim()); } /** * @param doAdjustQuery */ public void setDoAdjustQuery(boolean doAdjustQuery) { this.doAdjustQuery = doAdjustQuery; } /** * Builds the SQL to be used to do the search. * @param newEntryStr the string value to be searched * @param isForCount do query for count of returns * @return the full sql string. */ protected String buildSQL(final String newEntryStr, final boolean isForCount) { if (externalQueryProvider != null) { String fullSQLStr = externalQueryProvider.getFullSQL(newEntryStr, isForCount); if (StringUtils.isNotEmpty(fullSQLStr)) { return fullSQLStr; } } StringBuilder whereSB = new StringBuilder(); if (keyColumns.length > 1) { whereSB.append("("); //$NON-NLS-1$ } int cnt = 0; for (String keyCol : keyColumns) { String[] colParts = keyCol.split("\\."); String abbrev = colParts.length > 1 ? colParts[0] : tableInfo.getAbbrev(); String fld = colParts.length > 1 ? colParts[1] : keyCol; if (cnt > 0) whereSB.append(" OR "); //$NON-NLS-1$ whereSB.append(" LOWER("); //$NON-NLS-1$ whereSB.append(abbrev + "." + fld); whereSB.append(") LIKE '"); //$NON-NLS-1$ //The following condition is added specifically for catalog number lookups. Probably. if (uiFieldFormatter != null && uiFieldFormatter.isNumeric()) { whereSB.append("%"); //$NON-NLS-1$ } whereSB.append(newEntryStr.toLowerCase()); whereSB.append("%' "); //$NON-NLS-1$ cnt++; } if (keyColumns.length > 1) { whereSB.append(")"); //$NON-NLS-1$ } if (externalQueryProvider != null) { String extraWhereClause = externalQueryProvider.getExtraWhereClause(); if (StringUtils.isNotEmpty(extraWhereClause)) { whereSB.append(' '); whereSB.append(extraWhereClause); } } if (StringUtils.isNotEmpty(sqlTemplate)) { StringBuilder selectSB = new StringBuilder(); if (isForCount) { selectSB.append("count("); //$NON-NLS-1$ addTblAbbrev(tableInfo.getAbbrev(), tableInfo.getIdFieldName(), selectSB); selectSB.append(")"); //$NON-NLS-1$ } else { if (StringUtils.contains(displayColumns, ',')) { int fCnt = 0; for (String fld : StringUtils.split(displayColumns, ',')) { if (fCnt > 0) selectSB.append(", "); //$NON-NLS-1$ addTblAbbrev(tableInfo.getAbbrev(), fld, selectSB); fCnt++; } } else { addTblAbbrev(tableInfo.getAbbrev(), displayColumns, selectSB); } selectSB.append(", "); //$NON-NLS-1$ addTblAbbrev(tableInfo.getAbbrev(), tableInfo.getIdFieldName(), selectSB); } //System.err.println( selectSB.toString()); sql = StringUtils.replace(sqlTemplate, "%s1", selectSB.toString()); //$NON-NLS-1$ sql = StringUtils.replace(sql, "%s2", whereSB.toString()); //$NON-NLS-1$ if (doAdjustQuery) { sql = QueryAdjusterForDomain.getInstance().adjustSQL(sql); } //log.debug(sql); System.err.println(sql); return sql; } if (sql == null) { StringBuilder sb = new StringBuilder(); if (QueryAdjusterForDomain.getInstance().isUserInputNotInjectable(newEntryStr)) { sb.append("SELECT "); //$NON-NLS-1$ if (isForCount) { sb.append("count("); //$NON-NLS-1$ sb.append(tableInfo.getAbbrev() + "." + tableInfo.getIdFieldName()); sb.append(")"); //$NON-NLS-1$ } else { sb.append(tableInfo.getAbbrev() + "." + displayColumns); sb.append(","); //$NON-NLS-1$ sb.append(tableInfo.getAbbrev() + "." + tableInfo.getIdFieldName()); } sb.append(" FROM "); //$NON-NLS-1$ sb.append(tableInfo.getClassName()); sb.append(" as "); //$NON-NLS-1$ sb.append(tableInfo.getAbbrev()); String joinSnipet = doAdjustQuery ? QueryAdjusterForDomain.getInstance().getJoinClause(tableInfo, true, null, false) : null; //arg 2: false means SQL if (joinSnipet != null) { sb.append(' '); sb.append(joinSnipet); sb.append(' '); } sb.append(" WHERE "); //$NON-NLS-1$ //System.err.println(sb.toString()); String specialCols = doAdjustQuery ? QueryAdjusterForDomain.getInstance().getSpecialColumns(tableInfo, true) : null;//, false, isForCount ? null : tableInfo.getAbbrev()); if (StringUtils.isNotEmpty(specialCols)) { if (whereSB.length() > 0) whereSB.append(" AND "); //$NON-NLS-1$ whereSB.append(specialCols); //System.err.println(whereSB.toString()); } if (externalQueryProvider != null) { String extraWhereClause = externalQueryProvider.getExtraWhereClause(); if (StringUtils.isNotEmpty(extraWhereClause)) { whereSB.append(' '); whereSB.append(extraWhereClause); } } whereSB.append(" ORDER BY "); //$NON-NLS-1$ whereSB.append(displayColumns); //$NON-NLS-1$ whereSB.append(" ASC"); //$NON-NLS-1$ // cnt = 0; // for (String keyCol : keyColumns) // { // if (cnt > 0) whereSB.append(", "); //$NON-NLS-1$ // whereSB.append(keyCol); // whereSB.append(" ASC"); //$NON-NLS-1$ // cnt++; // } sb.append(whereSB.toString()); } //log.debug("* "+sql); return sb.toString(); } return sql; } /** * Process the results from the search * @param customQuery the query * @param advanceFocus */ private void processResults(final CustomQueryIFace customQuery, int advanceFocus) { searchedForText = prevEnteredText; List<?> dataObjList = customQuery.getDataObjects(); if (dataObjList == null || dataObjList.size() == 0) { if (doAddAddItem) { showPopup(advanceFocus); } } else { Dimension dim = getSize(); if (fontMetrics == null) { Font font = getFont(); BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); fontMetrics = bi.getGraphics().getFontMetrics(font); } boolean isFirst = true; duplicatehash.clear(); for (Object obj : dataObjList) { Object[] array = (Object[]) obj; if (isFirst) { numColumns = array.length - 1; values = new Object[numColumns]; isFirst = false; } Integer id = (Integer) array[numColumns]; idList.addElement(id); if (duplicatehash.get(id) == null) { duplicatehash.put(id, array); if (numColumns == 1) { Object value = array[0].toString(); if (uiFieldFormatter != null) { value = uiFieldFormatter.formatToUI(value); } else if (StringUtils.isNotEmpty(format)) { Object oldVal = builder == null ? null : value; value = UIHelper.getFormattedValue(format, value); if (builder != null && value == null && oldVal != null) { //customized qbx format interfering with builder's selected field(s). value = oldVal; } } list.addElement(value != null ? value.toString() : "xxx"); } else { try { for (int i = 0; i < numColumns; i++) { Object val = array[i]; if (val instanceof Calendar) { val = scrDateFormat.format((Calendar) val); } else if (val instanceof Date) { val = scrDateFormat.format((Date) val); } if (val instanceof FormDataObjIFace) { val = ((FormDataObjIFace) val).getIdentityTitle(); } values[i] = val != null ? val : null; //$NON-NLS-1$ } String valStr = (String) UIHelper.getFormattedValue(format, values); if (returnCount <= popupDlgThreshold && fontMetrics.stringWidth(valStr) > dim.width) { int len = valStr.length() - 5; while (len > 25) { valStr = valStr.substring(0, len); if (fontMetrics.stringWidth(valStr) < dim.width) { valStr = valStr + "..."; break; } len -= 5; } } // Minor hack for Bug 5824 for names with no first name // In the future we may want to do a strip of spaces form the end first if (valStr.endsWith(", ")) { valStr = valStr.substring(0, valStr.length() - 2); } list.addElement(valStr); } catch (java.util.IllegalFormatConversionException ex) { edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount(); edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(TextFieldWithQuery.class, ex); ex.printStackTrace(); list.addElement(values[0] != null ? values[0].toString() : "(No Value)"); //$NON-NLS-1$ } } } } if (idList.size() > 0 && returnCount != null) { if (tabOutSearch && idList.size() == 1) { selectedId = idList.elementAt(0); setText(list.get(0)); notifyListenersOfChange(textField); } else if (returnCount > popupDlgThreshold) { showDialog(advanceFocus); } else { showPopup(advanceFocus); } } else { setText(""); //$NON-NLS-1$ } duplicatehash.clear(); } } /** * @return where it has at least one Id */ public boolean hasId() { return selectedId != null; } /** * @param source */ private void notifyListenersOfChange(final Object source) { if (listSelectionListeners != null) { ListSelectionEvent lse = source == null ? null : new ListSelectionEvent(source, 0, 0, false); for (ListSelectionListener l : listSelectionListeners) { l.valueChanged(lse); } } } /** * * @param advanceFocus */ protected void showDialog(final int advanceFocus) { final Vector<Integer> idListLocal = (Vector<Integer>) idList.clone(); final Vector<String> listLocal = (Vector<String>) list.clone(); final String enteredText = StringUtils.isEmpty(prevEnteredText) ? cachedPrevText : prevEnteredText; DefaultListModel<String> model = new DefaultListModel<String>(); if (doAddAddItem) { model.addElement(UIRegistry.getResourceString("TFWQ_ADD_LABEL")); //$NON-NLS-1$ } for (String val : list) { model.addElement(val); } final JList<String> listBox = new JList<String>(model); JPanel panel = new JPanel(new BorderLayout()); panel.add(createLabel(UIRegistry.getResourceString("TFWQ_CHOOSE_LABEL"), SwingConstants.CENTER), //$NON-NLS-1$ BorderLayout.NORTH); panel.add(UIHelper.createScrollPane(listBox, true), BorderLayout.CENTER); panel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); // Had to do inner class in order to get it to select an item // before being shown class PopUpDialog extends CustomDialog { protected JList<String> pListBox; public PopUpDialog(final Frame frame, final boolean isModal, final Component contentPanel, JList<String> pListBoxArg) throws HeadlessException { super(frame, UIRegistry.getResourceString("TFWQ_CHOOSE_TITLE"), isModal, contentPanel); //$NON-NLS-1$ this.pListBox = pListBoxArg; initialize(); } public PopUpDialog(final Dialog dialog, final boolean isModal, final Component contentPanel, JList<String> pListBoxArg) throws HeadlessException { super(dialog, UIRegistry.getResourceString("TFWQ_CHOOSE_TITLE"), isModal, OK_BTN | CANCEL_BTN, //$NON-NLS-1$ contentPanel); this.pListBox = pListBoxArg; initialize(); } /** * */ protected void initialize() { pListBox.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { if (okBtn != null && pListBox != null) { okBtn.setEnabled(listBox.getSelectedIndex() != -1); } } } }); pListBox.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { super.mouseClicked(e); if (e.getClickCount() == 2) { okBtn.setEnabled(true); okBtn.doClick(); } } }); } @Override public void setVisible(final boolean visible) { if (visible) { listBox.setSelectedIndex(doAddAddItem ? 1 : 0); } super.setVisible(visible); } } hasNewText = false; Window mostRecent = UIRegistry.getMostRecentWindow(); CustomDialog dlg; if (mostRecent instanceof Dialog) { dlg = new PopUpDialog((Dialog) UIRegistry.getMostRecentWindow(), true, panel, listBox); } else { dlg = new PopUpDialog((Frame) UIRegistry.getMostRecentWindow(), true, panel, listBox); } dlg.setVisible(true); if (!dlg.isCancelled()) { int inx = listBox.getSelectedIndex(); boolean isDoingAdd = inx == 0 && doAddAddItem; inx = doAddAddItem ? inx - 1 : inx; if (!isDoingAdd && inx < idListLocal.size()) { selectedId = idListLocal.get(inx); setText(listLocal.get(inx)); } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { transferFocus(advanceFocus); notifyListenersOfChange(isDoingAdd ? new AddItemEvent(enteredText) : listBox); } }); } else { setText(""); //$NON-NLS-1$ } } protected void transferFocus(int direction) { if (direction > 0) textField.transferFocus(); if (direction < 0) textField.transferFocusBackward(); } protected void queryDone(final CustomQueryIFace customQuery, final int advanceFocus) { if (isDoingCount.get()) { List<?> dataObjList = customQuery.getDataObjects(); if (dataObjList != null && dataObjList.size() > 0) { returnCount = (Integer) dataObjList.get(0); } if (returnCount != null && returnCount == 0) { processResults(new EmptyCustomQuery(customQuery), advanceFocus); isDoingQuery.set(false); return; } list.clear(); idList.clear(); String sqlStr = null; if (builder != null) { sqlStr = builder.buildSQL(((JPAQuery) customQuery).getData().toString(), false); } if (sqlStr == null) { sqlStr = buildSQL(((JPAQuery) customQuery).getData().toString(), false); } //log.debug(sqlStr); JPAQuery jpaQuery = new JPAQuery(sqlStr, new CustomQueryListener() { @Override public void exectionDone(CustomQueryIFace customQuery) { queryDone(customQuery, advanceFocus); } @Override public void executionError(CustomQueryIFace customQuery) { isDoingQuery.set(false); } }); isDoingCount.set(false); jpaQuery.start(); } else { processResults(customQuery, advanceFocus); isDoingQuery.set(false); } } /** * Fill the the drop down with the list from the query */ protected void doQuery(final String newEntryStrArg, final String origTextStr, final int focusChange) { UIRegistry.getStatusBar().setText(""); if (StringUtils.isEmpty(newEntryStrArg)) { return; } ESTermParser parser = ESTermParser.getInstance(); if (parser.parse(newEntryStrArg, true)) { String newEntryStr = parser.getFields().get(0).getTermLowerCase(); prevEnteredText = origTextStr; if (!isDoingQuery.get()) { if (hasNewText) { isDoingQuery.set(true); isDoingCount.set(true); list.clear(); idList.clear(); returnCount = null; String newSql = null; if (builder != null) { newSql = builder.buildSQL(newEntryStr, true); } if (newSql == null) { newSql = buildSQL(newEntryStr, true); } if (StringUtils.isBlank(newSql)) { Toolkit.getDefaultToolkit().beep(); UIRegistry.displayLocalizedStatusBarError("TFWQ_InvalidEntry", newEntryStr); isDoingQuery.set(false); isDoingCount.set(false); } else { JPAQuery jpaQuery = new JPAQuery(newSql, new CustomQueryListener() { @Override public void exectionDone(CustomQueryIFace customQuery) { queryDone(customQuery, focusChange); } @Override public void executionError(CustomQueryIFace customQuery) { isDoingQuery.set(false); } }); jpaQuery.setUnique(true); jpaQuery.setData(newEntryStr); jpaQuery.start(); } } else if (returnCount != null && returnCount > popupDlgThreshold) { showDialog(focusChange); } else { showPopup(focusChange); } } } else { showPopup(focusChange); // add only } } /** * @return the list */ public List<String> getList() { return list; } /** * @return the id of the selected item */ public Integer getSelectedId() { return selectedId; } /** * @param selectedId the selectedId to set */ public void setSelectedId(final Integer selectedId) { this.selectedId = selectedId; this.wasCleared = true; } /* (non-Javadoc) * @see javax.swing.JComponent#requestFocus() */ @Override public void requestFocus() { textField.requestFocus(); } public void clearSelection() { list.clear(); idList.clear(); selectedId = null; hasNewText = false; wasCleared = true; currentText = ""; } public boolean hasItem() { return selectedId != null; } public void clearSearch() { setText(""); //$NON-NLS-1$ } public JTextField getTextField() { return textField; } //-------------------------------------------------------------------------- // ExternalQueryProviderIFace //-------------------------------------------------------------------------- public interface ExternalQueryProviderIFace { /** * @return just the additional where clause must start with "AND/OR". */ public String getExtraWhereClause(); /** * @return the full SQL to be used. */ public String getFullSQL(final String newEntryStr, final boolean isForCount); } //-------------------------------------------------------------------------- // Special class for the right visual appearance on the Mac. //-------------------------------------------------------------------------- class MacGradiantBtn extends JButton { protected ImageIcon imgIcon; protected boolean isPressed = false; protected Color top1 = new Color(184, 217, 250); protected Color top2 = new Color(120, 180, 241); protected Color bot1 = new Color(74, 155, 236); protected Color bot2 = new Color(179, 248, 255); protected Color topDarker1; protected Color topDarker2; protected Color botDarker1; protected Color botDarker2; /** * @param imgIcon the icon image for the dropdown */ public MacGradiantBtn(ImageIcon imgIcon) { super(imgIcon); this.imgIcon = imgIcon; topDarker1 = UIHelper.changeColorBrightness(top1, 0.95); topDarker2 = UIHelper.changeColorBrightness(top2, 0.95); botDarker1 = UIHelper.changeColorBrightness(bot1, 0.95); botDarker2 = UIHelper.changeColorBrightness(bot2, 0.95); addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { super.mousePressed(e); isPressed = true; } @Override public void mouseReleased(MouseEvent e) { super.mouseReleased(e); isPressed = false; } }); } /* (non-Javadoc) * @see javax.swing.JComponent#paint(java.awt.Graphics) */ @Override public void paint(Graphics g) { super.paint(g); if (isEnabled()) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); int w = this.getWidth() - 6; int h = this.getHeight() - 6; int x = 3; int y = 3; drawButtonBody(g2, x, y, w, (h / 2) + 4, isPressed ? topDarker1 : top1, isPressed ? topDarker2 : top2); drawButtonBody(g2, x, y + (h / 2), w, h / 2, isPressed ? botDarker1 : bot1, isPressed ? botDarker2 : bot2); x = (this.getWidth() - imgIcon.getIconWidth()) / 2; y = (this.getHeight() - imgIcon.getIconHeight()) / 2; g.drawImage(imgIcon.getImage(), x, y, imgIcon.getIconWidth(), imgIcon.getIconHeight(), null); } } /** * Draws the button body. * @param g2 the graphics to be painted into * @param w the width of the control * @param h the height of the control * @param color the of the background */ protected void drawButtonBody(Graphics2D g2, int x, int y, int w, int h, Color color, Color color2) { // draw the button body GradientPaint bg = new GradientPaint(new Point(x, y), color, new Point(x, y + h), color2); g2.setPaint(bg); g2.fillRoundRect(x, y, w, h, 6, 6); } } /** * @return the builder */ public ViewBasedSearchQueryBuilderIFace getBuilder() { return builder; } /** * @param builder the builder to set */ public void setBuilder(ViewBasedSearchQueryBuilderIFace builder) { this.builder = builder; } //-------------------------------------------------------------------------- // Class for short cutting search if count returns zero. //-------------------------------------------------------------------------- class EmptyCustomQuery implements CustomQueryIFace { private CustomQueryIFace cqi; /** * */ public EmptyCustomQuery(CustomQueryIFace cqi) { super(); this.cqi = cqi; } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#cancel() */ @Override public void cancel() { } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#execute() */ @Override public boolean execute() { return false; } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#execute(edu.ku.brc.dbsupport.CustomQueryListener) */ @Override public void execute(CustomQueryListener cql) { } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#getDataObjects() */ @Override public List<?> getDataObjects() { return new ArrayList<Object>(); } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#getName() */ @Override public String getName() { return cqi.getName(); } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#getQueryDefinition() */ @Override public List<QueryResultsContainerIFace> getQueryDefinition() { return cqi.getQueryDefinition(); } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#getResults() */ @Override public List<QueryResultsDataObj> getResults() { return new ArrayList<QueryResultsDataObj>(); } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#getTableIds() */ @Override public List<Integer> getTableIds() { return cqi.getTableIds(); } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#isCancelled() */ @Override public boolean isCancelled() { return cqi.isCancelled(); } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#isInError() */ @Override public boolean isInError() { return cqi.isInError(); } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#getMaxResults() */ @Override public int getMaxResults() { return 0; } /* (non-Javadoc) * @see edu.ku.brc.dbsupport.CustomQueryIFace#setMaxResults(int) */ @Override public void setMaxResults(int maxResults) { // ignore } } }