Source code: com/flexstor/common/awt/field/ComboField.java
1 /*
2 * ComboField.java
3 *
4 * Copyright $Date: 2003/08/19 04:06:47 $ FLEXSTOR.net Inc.
5 *
6 * This work is licensed for use and distribution under license terms found at
7 * http://www.flexstor.org/license.html
8 *
9 */
10
11 package com.flexstor.common.awt.field;
12
13 import java.awt.Color;
14 import java.awt.Component;
15 import java.awt.Dimension;
16 import java.awt.Frame;
17 import java.awt.Graphics;
18 import java.awt.Panel;
19 import java.awt.event.ActionEvent;
20 import java.awt.event.ActionListener;
21 import java.awt.event.MouseEvent;
22 import java.awt.event.MouseListener;
23 import java.util.ArrayList;
24 import java.util.Vector;
25
26 //TODO:3PG fix or remove import jclass.bwt.JCActionEvent;
27 //TODO:3PG fix or remove import jclass.bwt.JCActionListener;
28
29 import com.flexstor.common.awt.ArrowButton;
30 import com.flexstor.common.awt.event.FlexTextEvent;
31 import com.flexstor.common.awt.event.FlexTextListener;
32 import com.flexstor.common.constants.FieldConstantsI;
33 import com.flexstor.common.disguise.LookupListItem;
34 import com.flexstor.common.parsers.ValidatorI;
35 import com.flexstor.common.settings.Settings;
36
37 /**
38 * ComboField is a self configuring TextField/Choice that provides full data
39 * validation and case conversion. All edit events are sent as FlexTextEvent events, so that three
40 * different events (ItemEvent, ActionEvent, and TextEvent/KeyEvent) do not need to be caught.
41 * @author Dan Schroeder
42 * @version 2.1
43 */
44 public class ComboField
45 extends Panel
46 implements FlexTextListener, MouseListener, FieldConstantsI,
47 ActionListener //TODO:3PG fix or remove JCActionListener
48 {
49 /** The current mode, either ENTRY_TYPE_UNDEFINED, ENTRY_TYPE_TEXT_FIELD, or ENTRY_TYPE_CHOICE. */
50 protected int nMode = ENTRY_TYPE_UNDEFINED;
51
52 /** Reference to the field object. */
53 protected ComponentI field = null;
54
55 /** The name associated with this field. */
56 protected String sName = "";
57
58 /** Flag for choice padding. */
59 protected boolean bPadChoice = false;
60
61 protected boolean bAllowRepaint = true;
62
63 private boolean bVisualRequired = false;
64
65 protected boolean bExtendedEdit = false;
66
67 protected boolean bShowExtEdit = false;
68
69 protected ArrowButton arrow;
70
71 private boolean bEnabled = true;
72
73 /** Text Listeners -- used for filtering of text events. */
74 private Vector textListeners = null;
75
76 private String sTextAreaText;
77
78 private int nCase;
79
80 private static int nPopupThreshold = -1;
81
82 private int nForcedLength = 0;
83
84
85 /**
86 * Creates a new ComboField with the specified name.
87 * @parma sName the name of this field.
88 */
89 public ComboField ( String sName, boolean bPadChoice )
90 {
91 setLayout ( null );
92 this.sName = sName;
93 this.bPadChoice = bPadChoice;
94 textListeners = new Vector ( 1, 1 );
95 }
96
97
98 /**
99 * Creates a new ComboField with the specified name.
100 * @parma sName the name of this field.
101 */
102 public ComboField ( String sName, boolean bPadChoice, boolean bVisualRequired, boolean bExtendedEdit )
103 {
104 this ( sName, bPadChoice );
105 arrow = new ArrowButton ( ArrowButton.RIGHT );
106 arrow.addActionListener ( this );
107
108 this.bVisualRequired = bVisualRequired;
109 this.bExtendedEdit = bExtendedEdit;
110
111 if ( nPopupThreshold == -1 )
112 nPopupThreshold = Settings.getInt ( Settings.FIELD_POPUP_THRESHOLD );
113 }
114
115 public void cleanup()
116 {
117 cleanUp();
118
119 field.cleanup();
120 field = null;
121
122 if ( arrow != null )
123 {
124 arrow.removeActionListener( this );
125 arrow = null;
126 }
127
128 textListeners.removeAllElements();
129 textListeners = null;
130 }
131
132 /**
133 * Returns the name of this field.
134 * @return the name of this field.
135 */
136 public String getName ( )
137 {
138 return sName;
139
140 }
141
142 /**
143 * Conditionally enables/disables this field.
144 * @param bEdit enabled if true.
145 */
146 public void setEnabled ( boolean bEdit )
147 {
148 if ( bShowExtEdit )
149 field.setEnabled ( false );
150 else
151 field.setEnabled ( bEdit );
152
153 bEnabled = bEdit;
154 repaint();
155 }
156
157
158 public boolean getEnabled ( )
159 {
160 return bEnabled;
161 }
162
163 /**
164 * Configures this field.
165 * @param nDataType the data type.
166 * @param nEntryType the entry type.
167 * @param nLength the maximum entry lenght.
168 * @param nCase the case of strings.
169 * @param bRequired the user is required to enter data in this field.
170 * @param bEditable the field is editable if true.
171 * @param sLabel the name that is used in error messages for vaildation.
172 */
173 public void configure ( int nDataType, int nEntryType, int nLength, int nCase, boolean bRequired, boolean bEditable, String sLabel )
174 {
175 configure( nDataType, nEntryType, nLength, nCase, bRequired, bEditable, sLabel, new ArrayList() );
176 }
177
178 /**
179 * Configures this field.
180 * @param nDataType the data type.
181 * @param nEntryType the entry type.
182 * @param nLength the maximum entry lenght.
183 * @param nCase the case of strings.
184 * @param bRequired the user is required to enter data in this field.
185 * @param bEditable the field is editable if true.
186 * @param sLabel the name that is used in error messages for vaildation.
187 * @param saLookup the list of lookup values for choices. Values id will be set to -1.
188 */
189 public void configure ( int nDataType, int nEntryType, int nLength, int nCase, boolean bRequired, boolean bEditable,
190 String sLabel, String[] saLookup )
191 {
192 ArrayList alLookup = new ArrayList();
193 if ( saLookup != null )
194 {
195 for ( int i = 0; i < saLookup.length; i++ )
196 alLookup.add( new LookupListItem( saLookup[i], -1 ) );
197 }
198
199 configure( nDataType, nEntryType, nLength, nCase, bRequired, bEditable, sLabel, alLookup );
200 }
201
202 /**
203 * Configures this field.
204 * @param nDataType the data type.
205 * @param nEntryType the entry type.
206 * @param nLength the maximum entry lenght.
207 * @param nCase the case of strings.
208 * @param bRequired the user is required to enter data in this field.
209 * @param bEditable the field is editable if true.
210 * @param sLabel the name that is used in error messages for vaildation.
211 * @param alLookup the list of lookup items including values and ids for choices.
212 */
213 public void configure ( int nDataType, int nEntryType, int nLength, int nCase, boolean bRequired, boolean bEditable,
214 String sLabel, ArrayList alLookup )
215 {
216 bShowExtEdit = false;
217 this.nCase = nCase;
218
219 switch ( nEntryType )
220 {
221 case ENTRY_TYPE_UNDEFINED:
222 {
223 cleanUp();
224 field = null;
225 break;
226 }
227
228 case ENTRY_TYPE_CHOICE:
229 {
230 if ( nMode != ENTRY_TYPE_CHOICE )
231 {
232 cleanUp();
233 field = new FlexChoice( bPadChoice );
234 ((FlexChoice)field).addTextListener ( this );
235 ((FlexChoice)field).addMouseListener ( this );
236 ((FlexChoice)field).configure ( bEditable, bRequired, sLabel, alLookup );
237 add ( (FlexChoice)field );
238 }
239 else
240 ((FlexChoice)field).configure ( bEditable, bRequired, sLabel, alLookup );
241
242 break;
243 }
244
245 case ENTRY_TYPE_TEXT_FIELD:
246 {
247 if ( nMode != ENTRY_TYPE_TEXT_FIELD )
248 {
249 cleanUp();
250 field = new FlexTextField ( );
251 ((FlexTextField)field).addTextListener ( this );
252 ((FlexTextField)field).addMouseListener ( this );
253 ((FlexTextField)field).configure ( nDataType, nLength, nCase, bEditable, bRequired, sLabel );
254 add ( ((FlexTextField)field) );
255 }
256 else
257 ((FlexTextField)field).configure ( nDataType, nLength, nCase, bEditable, bRequired, sLabel );
258
259 if ( bExtendedEdit && nLength >= nPopupThreshold )
260 {
261 bShowExtEdit = true;
262 add ( arrow );
263 }
264
265 break;
266 }
267 }
268
269 setEnabled ( bEditable );
270
271 nMode = nEntryType;
272 }
273
274 public void setItems( ArrayList alItems )
275 {
276 if ( field instanceof FlexChoice )
277 ( (FlexChoice)field ).setItems( alItems );
278 }
279
280 public void setPreferredWidth ( int nWidth )
281 {
282 nForcedLength = nWidth;
283 }
284
285
286 public Dimension getMinimumSize()
287 {
288 int nWidth;
289 if ( nForcedLength > 0 )
290 nWidth = nForcedLength;
291 else
292 nWidth = field.getMinimumSize().width;
293
294 return new Dimension ( nWidth, ComponentI.COMBO_FIELD_HEIGHT );
295 }
296
297 public Dimension getPreferredSize()
298 {
299 int nWidth;
300 if ( nForcedLength > 0 )
301 nWidth = nForcedLength;
302 else
303 nWidth = field.getPreferredSize().width;
304
305 return new Dimension ( nWidth, ComponentI.COMBO_FIELD_HEIGHT );
306 }
307
308 // for 1.0 compatibility
309 public Dimension minimumSize() {return getMinimumSize();}
310 public Dimension preferredSize() {return getPreferredSize();}
311
312 /**
313 *
314 */
315 public void setText( String sText )
316 {
317 if ( bShowExtEdit )
318 {
319 sTextAreaText = sText;
320 field.setText ( fixupLabel(sText) );
321 }
322 else
323 field.setText ( sText );
324 }
325
326 /**
327 *
328 */
329 public String getText()
330 {
331 if ( bShowExtEdit )
332 return sTextAreaText;
333 else
334 return field.getText();
335 }
336
337 /**
338 *
339 */
340 public void setId( long nId )
341 {
342 field.setId( nId );
343 }
344
345 /**
346 *
347 */
348 public long getId()
349 {
350 return field.getId();
351 }
352
353 /**
354 *
355 */
356 public int getSelectedIndex()
357 {
358 return (nMode == ENTRY_TYPE_CHOICE) ? ((FlexChoice)field).getSelectedIndex() : 0;
359 }
360
361 /**
362 *
363 */
364 public void setRequired ( boolean bRequired )
365 {
366 field.setRequired ( bRequired );
367 }
368
369 public boolean isRequired ( )
370 {
371 return field.isRequired();
372 }
373
374 /**
375 * This method will select the first item in the Choice box or clear out the text field.
376 * @param bNotify Sends an edit ActionEvent to all listeners if true.
377 */
378 public void clear ( boolean bNotify )
379 {
380 field.clear ( bNotify );
381 }
382
383 /**
384 * Checks if the field is required.
385 */
386 public boolean verify ( boolean bCheckRequired )
387 {
388 return field.verify ( bCheckRequired );
389 }
390
391 public boolean quietVerify()
392 {
393 return field.quietVerify();
394 }
395
396 public void formatCase()
397 {
398 field.formatCase();
399 }
400
401 public void setCustomValidator ( ValidatorI validator )
402 {
403 field.setCustomValidator ( validator );
404 }
405
406 public void requestFocus ( )
407 {
408 field.requestFocus();
409 }
410
411 public void enableRepaints ( boolean bPaint )
412 {
413 bAllowRepaint = bPaint;
414 }
415
416 public void paint ( Graphics g )
417 {
418 if ( bAllowRepaint )
419 {
420 super.paint ( g );
421
422 Dimension d = getSize();
423
424 // Setup color for required fields.
425 if ( bVisualRequired && isRequired() && bEnabled )
426 g.setColor ( Settings.getColor ( Settings.FIELD_REQUIRED_COLOR ) );
427 else
428 g.setColor ( super.getBackground() );
429
430
431 if ( bShowExtEdit )
432 {
433 g.drawRect ( 0, 0, d.width-1, d.height-1 );
434 field.setBounds ( 1, 1, d.width-14, d.height-2 );
435
436 if ( bEnabled )
437 arrow.setBackground ( Color.white );
438 else
439 arrow.setBackground ( Color.lightGray );
440
441 arrow.setBounds ( d.width-13, 2, 12, d.height-3 );
442 }
443 else
444 {
445 g.drawRect ( 0, 0, d.width-1, d.height-1 );
446 field.setBounds ( 1, 1, d.width-2, d.height-2 );
447 }
448 }
449 }
450
451 public void setOptimumSize ( int nMaxFieldWidth )
452 {
453 Dimension size = field.getMinimumSize();
454
455 size.width = (nMaxFieldWidth < size.width) ? nMaxFieldWidth : size.width;
456 size.height = ComponentI.COMBO_FIELD_HEIGHT;
457
458 setSize ( size );
459 }
460
461
462 public void textValueChangeBegin ( FlexTextEvent e )
463 {
464 synchronized ( textListeners )
465 {
466 FlexTextEvent ne = new FlexTextEvent( this, 0, 0, e.getText() );
467
468 for ( int i = 0; i < textListeners.size(); i++ )
469 ((FlexTextListener)textListeners.elementAt(i)).textValueChangeBegin ( ne );
470
471 e.setAllowChange ( ne.getAllowChange() );
472 }
473 }
474
475
476 public void textValueChangeEnd ( FlexTextEvent e )
477 {
478 synchronized ( textListeners )
479 {
480 FlexTextEvent ne = new FlexTextEvent( this, 0, 0, e.getText() );
481
482 for ( int i = 0; i < textListeners.size(); i++ )
483 ((FlexTextListener)textListeners.elementAt(i)).textValueChangeEnd ( ne );
484 }
485 }
486
487 public synchronized void addTextListener ( FlexTextListener l )
488 {
489 textListeners.addElement ( l );
490 }
491
492
493 public synchronized void removeTextListener ( FlexTextListener l )
494 {
495 if ( textListeners.contains ( l ) )
496 textListeners.removeElement ( l );
497 }
498
499
500 public void actionPerformed ( ActionEvent e )
501 {
502 if ( e.getSource() instanceof ArrowButton )
503 popupExtDialog();
504 }
505
506
507 private void popupExtDialog ( )
508 {
509 ExtendedFieldDialog extDlg;
510
511 extDlg = new ExtendedFieldDialog ( getParentFrame(), ((FlexTextField)field).getName(), getText(), bEnabled, nCase,
512 100); //TODO:3PG fix or remove ((FlexTextField)field).getMaximumLength() );
513
514 String s = extDlg.getChanges();
515 if ( s != null )
516 {
517 FlexTextEvent e = new FlexTextEvent ( this, 0, 0, s );
518
519 textValueChangeBegin ( e );
520 if ( e.getAllowChange() )
521 {
522 setText ( s );
523 textValueChangeEnd ( e );
524 }
525 }
526 }
527
528
529 public void mousePressed ( MouseEvent e )
530 {
531 if ( e.getComponent() != this )
532 this.processMouseEvent ( new MouseEvent ( this, e.getID(), e.getWhen(), e.getModifiers(), e.getX(), e.getY(),
533 e.getClickCount(), e.isPopupTrigger()) );
534 }
535
536 public void mouseClicked ( MouseEvent e ) { }
537 public void mouseEntered ( MouseEvent e ) { }
538 public void mouseExited ( MouseEvent e ) { }
539 public void mouseReleased ( MouseEvent e ) { }
540
541 private void cleanUp ( )
542 {
543 removeAll();
544
545 switch ( nMode )
546 {
547 case ENTRY_TYPE_CHOICE:
548 {
549 ((FlexChoice)field).removeTextListener ( this );
550 ((FlexChoice)field).removeMouseListener ( this );
551 break;
552 }
553
554 case ENTRY_TYPE_TEXT_FIELD:
555 {
556 ((FlexTextField)field).removeTextListener ( this );
557 ((FlexTextField)field).removeMouseListener ( this );
558 break;
559 }
560 }
561 }
562
563 public int getMode ( )
564 {
565 return nMode;
566 }
567
568 public boolean isBlank()
569 {
570 return field.isBlank();
571 }
572
573
574 private Frame getParentFrame ( )
575 {
576 Component c = this;
577
578 do
579 {
580 c = c.getParent();
581 }
582 while ( c != null && !(c instanceof Frame) );
583
584 return (Frame)c;
585 }
586
587
588 public boolean isShowing ( )
589 {
590 return super.isShowing() && getLocation().x >= 0 && getLocation().y >= 0;
591 }
592
593
594 /**
595 * Removes invalid characters from the label.
596 * @param sLabel the orginal label.
597 * @return the "fixed" label.
598 */
599 private String fixupLabel ( String sLabel )
600 {
601 int nOrginal = sLabel.length() - 1;
602 int nLast = nOrginal;
603 int nTemp;
604
605 // Detect Newlines.
606 nTemp = sLabel.indexOf ( '\n' );
607 if ( nTemp > -1 )
608 nLast = nTemp;
609
610 // Detect Tabs.
611 // nTemp = sLabel.indexOf ( '\t' );
612 // if ( (nTemp > -1) && (nTemp < nLast) )
613 // nLast = nTemp;
614
615 if ( nLast < nOrginal )
616 sLabel = sLabel.substring ( 0, nLast ) + "...";
617
618 return sLabel;
619 }
620
621
622 }