001: /*
002: * Created on 12/10/2004
003: *
004: * ============================================================================
005: * GNU Lesser General Public License
006: * ============================================================================
007: *
008: * Swing Components - visit http://sf.net/projects/gfd
009: *
010: * Copyright (C) 2004 Igor Regis da Silva Simões
011: *
012: * This library is free software; you can redistribute it and/or
013: * modify it under the terms of the GNU Lesser General Public
014: * License as published by the Free Software Foundation; either
015: * version 2.1 of the License, or (at your option) any later version.
016: *
017: * This library is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
020: * Lesser General Public License for more details.
021: *
022: * You should have received a copy of the GNU Lesser General Public
023: * License along with this library; if not, write to the Free Software
024: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
025: */
026:
027: package br.com.gfpshare.beans;
028:
029: import java.awt.Color;
030: import java.awt.Component;
031: import java.awt.event.ActionEvent;
032: import java.awt.event.FocusEvent;
033: import java.awt.event.FocusListener;
034: import java.awt.event.ItemEvent;
035: import java.awt.event.ItemListener;
036: import java.awt.event.KeyAdapter;
037: import java.awt.event.KeyEvent;
038: import java.sql.SQLException;
039: import java.util.HashMap;
040: import java.util.Map;
041:
042: import javax.swing.ComboBoxEditor;
043: import javax.swing.JComboBox;
044: import javax.swing.JOptionPane;
045: import javax.swing.JTextField;
046:
047: import br.com.gfpshare.beans.event.ActionsListenerWithControllerListener;
048: import br.com.gfpshare.db.DAOCreationException;
049: import br.com.gfpshare.db.DAOEvent;
050: import br.com.gfpshare.db.DAOListener;
051: import br.com.gfpshare.db.PersistentObject;
052: import br.com.gfpshare.db.formatter.GeneralFormatter;
053:
054: /**
055: * Componente de ComboBox capaz de realizar conexão com um banco de dados para reter os dados que irão preenche-lo. <BR>
056: * Pra usá-lo basta instanciá-lo passando o tipo de dado que desevrá ser carregado do banco. O tipo passado deverá ser um derivado de PersistentObject e a
057: * conexão com o banco de dados deverá estar devidamente configurada através do DataBaseManager.
058: *
059: * @see br.com.gfpshare.db.PersistentObject br.com.igor.db.DataBaseManager
060: * @author Igor Regis da Silva Simoes
061: */
062: public class DBComboBox extends JComboBox implements RequiredField {
063:
064: private JTextField editorDBC = new DBComboBoxEditor();
065:
066: private ActionsListenerWithControllerListener insertActionListener = null;
067:
068: private Buscador buscador = new Buscador();
069:
070: private String windowActionCommand;
071:
072: /**
073: * Cria uma nova instância de DBComboBox
074: */
075: public DBComboBox() {
076: setRenderer(new DBComboBoxRenderer());
077: editorDBC.addKeyListener(buscador);
078: editorDBC.addFocusListener(new FocusObserver());
079: setEditor((ComboBoxEditor) editorDBC);
080: setModel(new DBComboBoxModel(null));
081: setEditable(true);
082: }
083:
084: /**
085: * Cria uma nova instância de DBComboBox
086: * @param dataType tipo de dados que deverão ser carregados pelo componente.
087: */
088: public DBComboBox(PersistentObject dataType) {
089: this ();
090: if (dataModel instanceof DBComboBoxModel)
091: setDataType(dataType);
092: }
093:
094: /**
095: * Retona o modelo de dados que representa o conteúdo deste componente.
096: * @return modelo de dados que representa o conteúdo deste componente, do tipo DBComboBoxModel.
097: * @see br.com.gfpshare.beans.DBComboBoxModel
098: */
099: public DBComboBoxModel getDBModel() {
100: if (dataModel instanceof DBComboBoxModel)
101: return (DBComboBoxModel) dataModel;
102: return null;
103: }
104:
105: /**
106: * Retorna o nome deste componente
107: * @return String com o nome deste componente
108: */
109: @Override
110: public String getName() {
111: return "DBComboBox";
112: }
113:
114: /**
115: * @see javax.swing.JComboBox#setSelectedItem(java.lang.Object)
116: */
117: @Override
118: public void setSelectedItem(Object anObject) {
119: // super.setSelectedItem(anObject);
120: getDBModel().setSelectedItem(anObject);
121: if (anObject != null
122: && anObject.toString().equals(
123: DBComboBoxModel.INSERT_NEW)) {
124: if (insertActionListener != null) {
125: DAOListener listener = new DAOListener() {
126:
127: public void adicionadoNovo(DAOEvent e) {
128: try {
129: getDBModel().loadDados();
130: } catch (DAOCreationException e1) {
131: // TODO Auto-generated catch block
132: e1.printStackTrace();
133: } catch (SQLException e1) {
134: // TODO Auto-generated catch block
135: e1.printStackTrace();
136: }
137: setSelectedItem(e.getPersistentObject());
138: }
139:
140: public void atualizado(DAOEvent e) {
141: }
142:
143: public void deletado(DAOEvent e) {
144: }
145:
146: public void filtrado(DAOEvent e) {
147: }
148:
149: public void selecionado(DAOEvent e) {
150: }
151: };
152: insertActionListener.setControllerListener(listener);
153: insertActionListener.actionPerformed(new ActionEvent(
154: this , 0, windowActionCommand));
155: } else
156: JOptionPane.showMessageDialog(this ,
157: PaginadorDeTabelaMessages.getMessages()
158: .getString("OperationNotSupported"));
159: }
160: }
161:
162: /**
163: * Atribui um action listener a ser chamado quando for solicitada a inclusão
164: * @param listener
165: */
166: public void setInsertNewListener(
167: ActionsListenerWithControllerListener listener,
168: String actionCommand) {
169: insertActionListener = listener;
170: setInsertNewCommand(actionCommand);
171: }
172:
173: /**
174: * Set the command that will be used to open the window to insert a new item at this combobox
175: * The command must be the bull qualified name of the class to be instantiated
176: * @param command
177: */
178: public void setInsertNewCommand(String command) {
179: this .windowActionCommand = command;
180: }
181:
182: /**
183: * @see javax.swing.JComboBox#getSelectedIndex()
184: */
185: @Override
186: public int getSelectedIndex() {
187: return getDBModel() == null ? super .getSelectedIndex()
188: : getDBModel().getSelectedIndex();
189: }
190:
191: /**
192: * Retrona os dados em uma determinada posição na lista da combo
193: * @param index Posição onde estão os dados que se deseja recuperar
194: * @return Map contendo os dados
195: * @throws SQLException
196: */
197: public Map getDataAt(int index) throws SQLException {
198: return getDBModel().getDataAt(index);
199: }
200:
201: /**
202: * Retorna um PersistentObject representando os dados de uma determinada posição da coleção.
203: * @param index Posição da qual se deseja pegar os dados.
204: * @return PersistentObject representando os dados/
205: * @throws SQLException Caso ocorra um erro sql.
206: */
207: public PersistentObject getPersistentObject(int index)
208: throws SQLException {
209: return getDBModel().getPersistentObject(index);
210: }
211:
212: /**
213: * Determina o tipo de dados contidos nesta coleção assim como o filtro para o que será retido.
214: * @param dataType tipo de dados contidos nesta coleção assim como o filtro para o que será retido
215: */
216: public void setDataType(PersistentObject dataType) {
217: getDBModel().setDataType(dataType);
218: buscador.resetCache();
219: if (dataType != null)
220: buscador
221: .configureCache(dataType.getClass().getSimpleName());
222: }
223:
224: /**
225: * Carrega os dados da colação a partir do banco de dados usando um critério para filtrar os dados carregados.
226: * @throws DAOCreationException Quando houver qualquer problema referente a conexão com o banco de dados.
227: * @throws SQLException Quando houver problema na execução da query que retem os dados do banco.
228: */
229: public void filter() throws DAOCreationException, SQLException {
230: getDBModel().filter();
231: buscador.resetCache();
232: }
233:
234: /**
235: * Carrega os dados da coleção a partir do banco de dados.
236: * @throws DAOCreationException Quando houver qualquer problema referente a conexão com o banco de dados.
237: * @throws SQLException Quando houver problema na execução da query que retem os dados do banco.
238: */
239: public void loadDados() throws DAOCreationException, SQLException {
240: getDBModel().loadDados();
241: buscador.resetCache();
242: }
243:
244: /**
245: * Determina o item selecionado na combobox
246: * @param anObject Objeto que deve ser selecionado
247: * @param key Chave usada parafazer a comparação de citério de seleção
248: * @throws SQLException
249: */
250: public void setSelectedItem(Object anObject, String key)
251: throws SQLException {
252: getDBModel().setSelectedItem(anObject, key);
253: }
254:
255: private class FocusObserver implements FocusListener {
256: /**
257: * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
258: */
259: public void focusGained(FocusEvent e) {
260: //Do Nothing
261: }
262:
263: /**
264: * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
265: */
266: public void focusLost(FocusEvent e) {
267: setPopupVisible(false);
268: }
269: }
270:
271: private class Buscador extends KeyAdapter {
272: private PersistentObject oldSelecion = null;
273:
274: private String searchString = null;
275:
276: private Map<PersistentObject, String> asStringCache = new HashMap<PersistentObject, String>(
277: 10);
278:
279: private boolean hasFormatter = false;
280:
281: /**
282: * @see java.awt.event.KeyAdapter#keyReleased(java.awt.event.KeyEvent)
283: */
284: @Override
285: public void keyReleased(KeyEvent e) {
286: searchString = getEditor().getItem() != null ? ((JTextField) getEditor())
287: .getText().substring(
288: 0,
289: ((JTextField) getEditor())
290: .getSelectionStart())
291: : null;
292: if (searchString == null)
293: searchString = "" + e.getKeyChar();
294: if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
295: if (searchString.length() == 0) {
296: searchString = "";
297: } else {
298: searchString = searchString.substring(0,
299: searchString.length() - 1);
300: }
301: }
302: if (searchString.equals("")) {
303: setSelectedIndex(0);
304: return;
305: }
306:
307: if (oldSelecion != null
308: && getPersistentObjectAsString(oldSelecion)
309: .startsWith(searchString)) {
310: getEditor().setItem(oldSelecion);
311: } else {
312: for (int i = 0; i < dataModel.getSize(); i++) {
313: Object element = dataModel.getElementAt(i);
314: if (element != null
315: && element instanceof PersistentObject) {
316: String elementAsText = getPersistentObjectAsString((PersistentObject) element);
317: if (elementAsText.toLowerCase().startsWith(
318: searchString.toLowerCase())) {
319: setSelectedIndex(i);
320: oldSelecion = (PersistentObject) element;
321: break;
322: }
323: }
324: }
325: }
326: ((JTextField) getEditor())
327: .setSelectionStart(((JTextField) getEditor())
328: .getText().toLowerCase().indexOf(
329: searchString.toLowerCase())
330: + searchString.length());
331: ((JTextField) getEditor())
332: .setSelectionEnd(((JTextField) getEditor())
333: .getText().length());
334: }
335:
336: void configureCache(String dataType) {
337: hasFormatter = DBComboBoxEditor.generalFormatter
338: .hasFormatter(dataType);
339: }
340:
341: /*
342: * Método utilitario para o keypressed
343: */
344: private String getPersistentObjectAsString(PersistentObject o) {
345: String elementAsText = null;
346: if (hasFormatter
347: && (elementAsText = asStringCache.get(o)) == null)
348: elementAsText = DBComboBoxEditor.generalFormatter
349: .format(o, GeneralFormatter.FORMATO_CURTO, o
350: .getClass().getSimpleName());
351: if (elementAsText == null)
352: elementAsText = o.getAsString(PersistentObject.CURTO);
353: return elementAsText;
354: }
355:
356: /**
357: * Limpa o cache interno
358: *
359: */
360: void resetCache() {
361: asStringCache.clear();
362: }
363: }
364:
365: public Object getRequiredValue() {
366: try {
367: return getDataAt(getSelectedIndex());
368: } catch (Exception e) {
369: return null;
370: }
371: }
372:
373: /*
374: * @see br.com.igor.beans.RequiredField#isSameComponent(java.awt.Component)
375: */
376: public boolean isSameComponent(Component c) {
377: return c == this || c == editorDBC;
378: }
379:
380: /*
381: * @see java.awt.Component#addFocusListener(java.awt.event.FocusListener)
382: */
383: @Override
384: public synchronized void addFocusListener(FocusListener l) {
385: super .addFocusListener(l);
386: if (editorDBC != null)
387: editorDBC.addFocusListener(l);
388: }
389:
390: /*
391: * @see java.awt.Component#removeFocusListener(java.awt.event.FocusListener)
392: */
393: @Override
394: public synchronized void removeFocusListener(FocusListener l) {
395: super .removeFocusListener(l);
396: if (editorDBC != null)
397: editorDBC.removeFocusListener(l);
398: }
399:
400: @Override
401: public Color getBackground() {
402: if (editorDBC != null)
403: return editorDBC.getBackground();
404: return super .getBackground();
405: }
406:
407: @Override
408: public void setBackground(Color c) {
409: if (editorDBC != null)
410: editorDBC.setBackground(c);
411: super .setBackground(c);
412: }
413:
414: /**
415: * For some bizzare reason we need to override this method to remove
416: * the call to BasicComboPopup$Handler, because it's making our key
417: * type response get slow (we lost ~280milis there) this is enough
418: * time to bother the user.
419: */
420: @Override
421: protected void fireItemStateChanged(ItemEvent e) {
422: Object[] listeners = listenerList.getListenerList();
423: for (int i = listeners.length - 2; i >= 0; i -= 2)
424: if (listeners[i] == ItemListener.class)
425: if (!listeners[i + 1]
426: .getClass()
427: .getName()
428: .equals(
429: "javax.swing.plaf.basic.BasicComboPopup$Handler"))
430: ((ItemListener) listeners[i + 1])
431: .itemStateChanged(e);
432: }
433:
434: }
|