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.igor.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.igor.beans.event.ActionsListenerWithControllerListener;
048: import br.com.igor.db.ControllerCreationException;
049: import br.com.igor.db.ControllerEvent;
050: import br.com.igor.db.ControllerListener;
051: import br.com.igor.db.PersistentObject;
052: import br.com.igor.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.igor.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: /**
071: * Cria uma nova instância de DBComboBox
072: */
073: public DBComboBox() {
074: setRenderer(new DBComboBoxRenderer());
075: editorDBC.addKeyListener(buscador);
076: editorDBC.addFocusListener(new FocusObserver());
077: setEditor((ComboBoxEditor) editorDBC);
078: setModel(new DBComboBoxModel(null));
079: setEditable(true);
080: }
081:
082: /**
083: * Cria uma nova instância de DBComboBox
084: * @param dataType tipo de dados que deverão ser carregados pelo componente.
085: */
086: public DBComboBox(PersistentObject dataType) {
087: this ();
088: if (dataModel instanceof DBComboBoxModel)
089: setDataType(dataType);
090: }
091:
092: /**
093: * Retona o modelo de dados que representa o conteúdo deste componente.
094: * @return modelo de dados que representa o conteúdo deste componente, do tipo DBComboBoxModel.
095: * @see br.com.igor.beans.DBComboBoxModel
096: */
097: public DBComboBoxModel getDBModel() {
098: if (dataModel instanceof DBComboBoxModel)
099: return (DBComboBoxModel) dataModel;
100: return null;
101: }
102:
103: /**
104: * Retorna o nome deste componente
105: * @return String com o nome deste componente
106: */
107: @Override
108: public String getName() {
109: return "DBComboBox";
110: }
111:
112: /**
113: * @see javax.swing.JComboBox#setSelectedItem(java.lang.Object)
114: */
115: @Override
116: public void setSelectedItem(Object anObject) {
117: // super.setSelectedItem(anObject);
118: getDBModel().setSelectedItem(anObject);
119: if (anObject != null
120: && anObject.toString().equals(
121: DBComboBoxModel.INSERT_NEW)) {
122: if (insertActionListener != null) {
123: ControllerListener listener = new ControllerListener() {
124:
125: public void adicionadoNovo(ControllerEvent e) {
126: try {
127: getDBModel().loadDados();
128: } catch (ControllerCreationException e1) {
129: // TODO Auto-generated catch block
130: e1.printStackTrace();
131: } catch (SQLException e1) {
132: // TODO Auto-generated catch block
133: e1.printStackTrace();
134: }
135: setSelectedItem(e.getPersistentObject());
136: }
137:
138: public void atualizado(ControllerEvent e) {
139: }
140:
141: public void deletado(ControllerEvent e) {
142: }
143:
144: public void filtrado(ControllerEvent e) {
145: }
146:
147: public void selecionado(ControllerEvent e) {
148: }
149: };
150: insertActionListener.setControllerListener(listener);
151: insertActionListener.actionPerformed(new ActionEvent(
152: this , 0, getDBModel().getDataType().getClass()
153: .getName()));
154: } else
155: JOptionPane.showMessageDialog(this ,
156: PaginadorDeTabelaMessages.getMessages()
157: .getString("OperationNotSupported"));
158: }
159: }
160:
161: /**
162: * Atribui um action listener a ser chamado quando for solicitada a inclusão
163: * @param listener
164: */
165: public void setInsertNewListener(
166: ActionsListenerWithControllerListener listener) {
167: insertActionListener = listener;
168: }
169:
170: /**
171: * @see javax.swing.JComboBox#getSelectedIndex()
172: */
173: @Override
174: public int getSelectedIndex() {
175: return getDBModel() == null ? super .getSelectedIndex()
176: : getDBModel().getSelectedIndex();
177: }
178:
179: /**
180: * Retrona os dados em uma determinada posição na lista da combo
181: * @param index Posição onde estão os dados que se deseja recuperar
182: * @return Map contendo os dados
183: * @throws SQLException
184: */
185: public Map getDataAt(int index) throws SQLException {
186: return getDBModel().getDataAt(index);
187: }
188:
189: /**
190: * Retorna um PersistentObject representando os dados de uma determinada posição da coleção.
191: * @param index Posição da qual se deseja pegar os dados.
192: * @return PersistentObject representando os dados/
193: * @throws SQLException Caso ocorra um erro sql.
194: */
195: public PersistentObject getPersistentObject(int index)
196: throws SQLException {
197: return getDBModel().getPersistentObject(index);
198: }
199:
200: /**
201: * Determina o tipo de dados contidos nesta coleção assim como o filtro para o que será retido.
202: * @param dataType tipo de dados contidos nesta coleção assim como o filtro para o que será retido
203: */
204: public void setDataType(PersistentObject dataType) {
205: getDBModel().setDataType(dataType);
206: buscador.resetCache();
207: if (dataType != null)
208: buscador
209: .configureCache(dataType.getClass().getSimpleName());
210: }
211:
212: /**
213: * Carrega os dados da colação a partir do banco de dados usando um critério para filtrar os dados carregados.
214: * @throws ControllerCreationException Quando houver qualquer problema referente a conexão com o banco de dados.
215: * @throws SQLException Quando houver problema na execução da query que retem os dados do banco.
216: */
217: public void filter() throws ControllerCreationException,
218: SQLException {
219: getDBModel().filter();
220: buscador.resetCache();
221: }
222:
223: /**
224: * Carrega os dados da coleção a partir do banco de dados.
225: * @throws ControllerCreationException Quando houver qualquer problema referente a conexão com o banco de dados.
226: * @throws SQLException Quando houver problema na execução da query que retem os dados do banco.
227: */
228: public void loadDados() throws ControllerCreationException,
229: SQLException {
230: getDBModel().loadDados();
231: buscador.resetCache();
232: }
233:
234: /**
235: * Determina o item selecionado na combobox
236: * @param anObject Objeto que deve ser selecionado
237: * @param key Chave usada parafazer a comparação de citério de seleção
238: * @throws SQLException
239: */
240: public void setSelectedItem(Object anObject, String key)
241: throws SQLException {
242: getDBModel().setSelectedItem(anObject, key);
243: }
244:
245: private class FocusObserver implements FocusListener {
246: /**
247: * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
248: */
249: public void focusGained(FocusEvent e) {
250: //Do Nothing
251: }
252:
253: /**
254: * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
255: */
256: public void focusLost(FocusEvent e) {
257: setPopupVisible(false);
258: }
259: }
260:
261: private class Buscador extends KeyAdapter {
262: private PersistentObject oldSelecion = null;
263:
264: private String searchString = null;
265:
266: private Map<PersistentObject, String> asStringCache = new HashMap<PersistentObject, String>(
267: 10);
268:
269: private boolean hasFormatter = false;
270:
271: /**
272: * @see java.awt.event.KeyAdapter#keyReleased(java.awt.event.KeyEvent)
273: */
274: @Override
275: public void keyReleased(KeyEvent e) {
276: searchString = getEditor().getItem() != null ? ((JTextField) getEditor())
277: .getText().substring(
278: 0,
279: ((JTextField) getEditor())
280: .getSelectionStart())
281: : null;
282: if (searchString == null)
283: searchString = "" + e.getKeyChar();
284: if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
285: if (searchString.length() == 0) {
286: searchString = "";
287: } else {
288: searchString = searchString.substring(0,
289: searchString.length() - 1);
290: }
291: }
292: if (searchString.equals("")) {
293: setSelectedIndex(0);
294: return;
295: }
296:
297: if (oldSelecion != null
298: && getPersistentObjectAsString(oldSelecion)
299: .startsWith(searchString)) {
300: getEditor().setItem(oldSelecion);
301: } else {
302: for (int i = 0; i < dataModel.getSize(); i++) {
303: Object element = dataModel.getElementAt(i);
304: if (element != null
305: && element instanceof PersistentObject) {
306: String elementAsText = getPersistentObjectAsString((PersistentObject) element);
307: if (elementAsText.toLowerCase().startsWith(
308: searchString.toLowerCase())) {
309: setSelectedIndex(i);
310: oldSelecion = (PersistentObject) element;
311: break;
312: }
313: }
314: }
315: }
316: ((JTextField) getEditor())
317: .setSelectionStart(((JTextField) getEditor())
318: .getText().toLowerCase().indexOf(
319: searchString.toLowerCase())
320: + searchString.length());
321: ((JTextField) getEditor())
322: .setSelectionEnd(((JTextField) getEditor())
323: .getText().length());
324: }
325:
326: void configureCache(String dataType) {
327: hasFormatter = DBComboBoxEditor.generalFormatter
328: .hasFormatter(dataType);
329: }
330:
331: /*
332: * Método utilitario para o keypressed
333: */
334: private String getPersistentObjectAsString(PersistentObject o) {
335: String elementAsText = null;
336: if (hasFormatter
337: && (elementAsText = asStringCache.get(o)) == null)
338: elementAsText = DBComboBoxEditor.generalFormatter
339: .format(o, GeneralFormatter.FORMATO_CURTO, o
340: .getClass().getSimpleName());
341: if (elementAsText == null)
342: elementAsText = o.getAsString(PersistentObject.CURTO);
343: return elementAsText;
344: }
345:
346: /**
347: * Limpa o cache interno
348: *
349: */
350: void resetCache() {
351: asStringCache.clear();
352: }
353: }
354:
355: public Object getRequiredValue() {
356: try {
357: return getDataAt(getSelectedIndex());
358: } catch (Exception e) {
359: return null;
360: }
361: }
362:
363: /*
364: * @see br.com.igor.beans.RequiredField#isSameComponent(java.awt.Component)
365: */
366: public boolean isSameComponent(Component c) {
367: return c == this || c == editorDBC;
368: }
369:
370: /*
371: * @see java.awt.Component#addFocusListener(java.awt.event.FocusListener)
372: */
373: @Override
374: public synchronized void addFocusListener(FocusListener l) {
375: super .addFocusListener(l);
376: if (editorDBC != null)
377: editorDBC.addFocusListener(l);
378: }
379:
380: /*
381: * @see java.awt.Component#removeFocusListener(java.awt.event.FocusListener)
382: */
383: @Override
384: public synchronized void removeFocusListener(FocusListener l) {
385: super .removeFocusListener(l);
386: if (editorDBC != null)
387: editorDBC.removeFocusListener(l);
388: }
389:
390: @Override
391: public Color getBackground() {
392: if (editorDBC != null)
393: return editorDBC.getBackground();
394: return super .getBackground();
395: }
396:
397: @Override
398: public void setBackground(Color c) {
399: if (editorDBC != null)
400: editorDBC.setBackground(c);
401: super .setBackground(c);
402: }
403:
404: /**
405: * For some bizzare reason we need to override this method to remove
406: * the call to BasicComboPopup$Handler, because it's making our key
407: * type response get slow (we lost ~280milis there) this is enough
408: * time to bother the user.
409: */
410: @Override
411: protected void fireItemStateChanged(ItemEvent e) {
412: Object[] listeners = listenerList.getListenerList();
413: for (int i = listeners.length - 2; i >= 0; i -= 2)
414: if (listeners[i] == ItemListener.class)
415: if (!listeners[i + 1]
416: .getClass()
417: .getName()
418: .equals(
419: "javax.swing.plaf.basic.BasicComboPopup$Handler"))
420: ((ItemListener) listeners[i + 1])
421: .itemStateChanged(e);
422: }
423:
424: }
|