xref: /trunk/main/odk/examples/DevelopersGuide/Forms/DataAwareness.java (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  *  The Contents of this file are made available subject to the terms of
4  *  the BSD license.
5  *
6  *  Copyright 2000, 2010 Oracle and/or its affiliates.
7  *  All rights reserved.
8  *
9  *  Redistribution and use in source and binary forms, with or without
10  *  modification, are permitted provided that the following conditions
11  *  are met:
12  *  1. Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *  2. Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *  3. Neither the name of Sun Microsystems, Inc. nor the names of its
18  *     contributors may be used to endorse or promote products derived
19  *     from this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
31  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  *************************************************************************/
34 import com.sun.star.beans.PropertyChangeEvent;
35 import com.sun.star.beans.XPropertyChangeListener;
36 import com.sun.star.beans.XPropertySet;
37 
38 
39 // __________ Imports __________
40 import com.sun.star.beans.XPropertySetInfo;
41 
42 // base classes
43 import com.sun.star.container.XIndexContainer;
44 import com.sun.star.container.XNameAccess;
45 import com.sun.star.container.XNamed;
46 import com.sun.star.form.FormComponentType;
47 import com.sun.star.form.ListSourceType;
48 import com.sun.star.form.XGridColumnFactory;
49 import com.sun.star.form.XReset;
50 import com.sun.star.form.XResetListener;
51 import com.sun.star.form.runtime.FormFeature;
52 import com.sun.star.lang.EventObject;
53 import com.sun.star.lang.XComponent;
54 import com.sun.star.sdb.CommandType;
55 import com.sun.star.sdb.XColumnUpdate;
56 import com.sun.star.sdbc.ResultSetConcurrency;
57 import com.sun.star.sdbc.XConnection;
58 import com.sun.star.sdbc.XDataSource;
59 import com.sun.star.sdbc.XStatement;
60 import com.sun.star.sdbcx.XColumnsSupplier;
61 import com.sun.star.uno.UnoRuntime;
62 import com.sun.star.uno.XInterface;
63 
64 /**************************************************************************/
65 /** a class for enumerating a form component tree
66 */
67 class PrintComponentTree extends ComponentTreeTraversal
68 {
69     private String m_sPrefix;
70 
71     public PrintComponentTree()
72     {
73         m_sPrefix = new String();
74     }
75 
76     public void handle( Object aFormComponent ) throws com.sun.star.uno.Exception
77     {
78         // the name of the child
79         XNamed xName = (XNamed)UnoRuntime.queryInterface( XNamed.class, aFormComponent );
80 
81         // if it's a form control model, check it's type
82         XPropertySet xProps = UNO.queryPropertySet( aFormComponent );
83         String sTypeName = FLTools.classifyFormComponentType( xProps );
84 
85         String sName;
86         if ( null == xName )
87             sName = "<unnamed>";
88         else
89             sName = xName.getName();
90 
91         // print the component's name
92         if ( 0 != sTypeName.length() )
93         {
94             System.out.println( m_sPrefix + sName + " (" + sTypeName + ")" );
95         }
96         else
97         {
98             System.out.println( m_sPrefix + sName );
99         }
100 
101         // let the super class step down the tree
102         m_sPrefix = m_sPrefix + " ";
103         super.handle( aFormComponent );
104         m_sPrefix = m_sPrefix.substring( 0, m_sPrefix.length() - 1 );
105     }
106 };
107 
108 /**************************************************************************/
109 /** a class revoking button models from a ButtonOperator instance
110 */
111 class RevokeButtons extends ComponentTreeTraversal
112 {
113     private ButtonOperator m_aOperator;
114 
115     public RevokeButtons( ButtonOperator aOperator )
116     {
117         m_aOperator = aOperator;
118     }
119 
120     public void handle( Object aFormComponent ) throws com.sun.star.uno.Exception
121     {
122         // check if it's a button
123         XPropertySet xProps = UNO.queryPropertySet( aFormComponent );
124         XPropertySetInfo xPI = null;
125         if ( null != xProps )
126             xPI = xProps.getPropertySetInfo();
127         if ( ( null != xPI ) && xPI.hasPropertyByName( "ClassId" ) )
128         {
129             Short nClassId = (Short)xProps.getPropertyValue( "ClassId" );
130             if ( FormComponentType.COMMANDBUTTON == nClassId.shortValue() )
131             {
132                 // yes, it is
133                 m_aOperator.revokeButton( xProps );
134             }
135         }
136 
137         // let the super class step down the tree (if possible)
138         super.handle( aFormComponent );
139     }
140 }
141 
142 /**************************************************************************/
143 public class DataAwareness extends DocumentBasedExample implements XPropertyChangeListener, XResetListener
144 {
145     /* ================================================================== */
146     private HsqlDatabase            m_database;
147 
148     private static final String     s_tableNameSalesmen = "SALESMEN";
149     private static final String     s_tableNameCustomers = "CUSTOMERS";
150     private static final String     s_tableNameSales = "SALES";
151 
152     private XPropertySet            m_xMasterForm;
153     private ButtonOperator          m_aOperator;
154     private SalesFilter             m_aSalesFilter;
155 
156     private KeyGenerator            m_aSalesmanKeyGenerator;
157     private KeyGenerator            m_aSalesKeyGenerator;
158     private ControlLock             m_aSalesmenLocker;
159     private ControlLock             m_aSalesLocker;
160     private GridFieldValidator      m_aSalesNameValidator;
161 
162     private boolean                 m_bDefaultSalesDate;
163     private boolean                 m_bProtectKeyFields;
164     private boolean                 m_bAllowEmptySales;
165 
166     /* ------------------------------------------------------------------ */
167     public DataAwareness()
168     {
169         super( DocumentType.WRITER );
170         m_bDefaultSalesDate = false;
171         m_bProtectKeyFields = false;
172         m_bAllowEmptySales = false;
173     }
174 
175     /* ==================================================================
176        = form components
177        ================================================================== */
178 
179     /* ------------------------------------------------------------------ */
180     /** enumerates and prints all the elements in the given container, together with the container itself
181     */
182     protected void enumFormComponents( XNameAccess xContainer ) throws java.lang.Exception
183     {
184         String sObjectName;
185 
186         XNamed xNameAcc = (XNamed)UnoRuntime.queryInterface( XNamed.class, xContainer );
187         if ( null == xNameAcc )
188             sObjectName = new String( "<unnamed>" );
189         else
190             sObjectName = xNameAcc.getName();
191         System.out.println( new String( "enumerating the container named \"" ) + sObjectName +
192             new String( "\"\n" ) );
193 
194         PrintComponentTree aPrinter = new PrintComponentTree();
195         aPrinter.handle( xContainer );
196     }
197 
198     /* ------------------------------------------------------------------ */
199     /** enumerates and prints all form elements in the document
200     */
201     protected void enumFormComponents( ) throws java.lang.Exception
202     {
203         enumFormComponents( m_document.getFormComponentTreeRoot() );
204     }
205 
206     /* ==================================================================
207        = UNO callbacks
208        ================================================================== */
209 
210     /* ------------------------------------------------------------------ */
211     // XResetListener overridables
212     /* ------------------------------------------------------------------ */
213     public boolean approveReset( EventObject aEvent ) throws com.sun.star.uno.RuntimeException
214     {
215         // not interested in vetoing this
216         return true;
217     }
218 
219     /* ------------------------------------------------------------------ */
220     public void resetted( EventObject aEvent ) throws com.sun.star.uno.RuntimeException
221     {
222         // check if this reset occured becase we're on a new record
223         XPropertySet xFormProps = UNO.queryPropertySet(  aEvent.Source );
224         try
225         {
226             Boolean aIsNew = (Boolean)xFormProps.getPropertyValue( "IsNew" );
227             if ( aIsNew.booleanValue() )
228             {   // yepp
229 
230                 if ( !m_bDefaultSalesDate )
231                 {   // we're interested to do all this only if the user told us to default the sales date
232                     // to "today"
233                     // As date fields do this defaulting automatically, the semantics is inverted here:
234                     // If we're told to default, we must do nothing, if we should not default, we must
235                     // reset the value which the date field set automatically.
236 
237                     Integer aConcurrency = (Integer)xFormProps.getPropertyValue( "ResultSetConcurrency" );
238                     if ( ResultSetConcurrency.READ_ONLY != aConcurrency.intValue() )
239                     {
240                         // we're going to modify the record, though after that, to the user, it should look
241                         // like it has not been modified
242                         // So we need to ensure that we do not change the IsModified property with whatever we do
243                         Object aModifiedFlag = xFormProps.getPropertyValue( "IsModified" );
244 
245 
246                         // get the columns of our master form
247                         XColumnsSupplier xSuppCols = (XColumnsSupplier)UnoRuntime.queryInterface(
248                             XColumnsSupplier.class, xFormProps );
249                         XNameAccess xCols = xSuppCols.getColumns();
250 
251                         // and update the date column with a NULL value
252                         XColumnUpdate xDateColumn = (XColumnUpdate)UnoRuntime.queryInterface(
253                             XColumnUpdate.class, xCols.getByName( "SALEDATE" ) );
254                         xDateColumn.updateNull();
255 
256 
257                         // then restore the flag
258                         xFormProps.setPropertyValue( "IsModified", aModifiedFlag );
259                     }
260                 }
261             }
262         }
263         catch( com.sun.star.uno.Exception e )
264         {
265             System.out.println(e);
266             e.printStackTrace();
267         }
268     }
269 
270     /* ------------------------------------------------------------------ */
271     // XPropertyChangeListener overridables
272     /* ------------------------------------------------------------------ */
273     public void propertyChange( PropertyChangeEvent aEvent ) throws com.sun.star.uno.RuntimeException
274     {
275         try
276         {
277             // did it come from a radio button or checkbox?
278             if ( aEvent.PropertyName.equals( "State" ) )
279             {   // yep
280                 Short aNewState = (Short)aEvent.NewValue;
281 
282                 XPropertySet xModel = UNO.queryPropertySet( aEvent.Source );
283                 String sName = (String)xModel.getPropertyValue( "Name" );
284 
285                 Short aClassId = (Short)xModel.getPropertyValue( "ClassId" );
286                 if ( FormComponentType.RADIOBUTTON == aClassId.shortValue() )
287                 {
288                     String sRefValue = (String)xModel.getPropertyValue( "RefValue" );
289 
290                     short nNewValue = ((Short)aEvent.NewValue).shortValue();
291                     if ( sName.equals( "KeyGen" ) )
292                     {
293                         // it's one of the options for key generation
294                         if ( sRefValue.equals( "none" ) )
295                         {   // no automatic generation at all
296                             m_aSalesmanKeyGenerator.stopGenerator( );
297                             m_aSalesKeyGenerator.stopGenerator( );
298                         }
299                         else
300                         {
301                             boolean bGenerateOnReset = true;
302                             if ( sRefValue.equals( "update" ) )
303                             {   // generate on update
304                                 bGenerateOnReset = ( 0 == nNewValue );
305                             }
306                             else if ( sRefValue.equals( "reset" ) )
307                             {   // generat on reset
308                                 bGenerateOnReset = ( 0 != nNewValue );
309                             }
310                             m_aSalesmanKeyGenerator.activateKeyGenerator( bGenerateOnReset );
311                             m_aSalesKeyGenerator.activateKeyGenerator( bGenerateOnReset );
312                         }
313                     }
314                 }
315                 else if ( FormComponentType.CHECKBOX == aClassId.shortValue() )
316                 {
317                     boolean bEnabled = ( 0 != aNewState.shortValue() );
318                     if ( sName.equals( "defaultdate" ) )
319                     {
320                         m_bDefaultSalesDate = bEnabled;
321                     }
322                     else if ( sName.equals( "protectkeys" ) )
323                     {
324                         m_bProtectKeyFields = bEnabled;
325                         m_aSalesmenLocker.enableLock( m_bProtectKeyFields );
326                         m_aSalesLocker.enableLock( m_bProtectKeyFields );
327                     }
328                     else if ( sName.equals( "emptysales" ) )
329                     {
330                         m_bAllowEmptySales = bEnabled;
331                         m_aSalesNameValidator.enableColumnWatch( m_bAllowEmptySales );
332                     }
333                 }
334             }
335         }
336         catch(com.sun.star.uno.Exception e)
337         {
338             System.out.println(e);
339             e.printStackTrace();
340         }
341     }
342 
343     /* ------------------------------------------------------------------ */
344     // XEventListener overridables
345     /* ------------------------------------------------------------------ */
346     public void disposing( EventObject aEvent )
347     {
348         // simply disambiguate
349         super.disposing( aEvent );
350     }
351 
352     /* ==================================================================
353        = miscellaneous
354        ================================================================== */
355 
356     /* ------------------------------------------------------------------ */
357     /** skips line feeds in the input stream
358 
359         @returns
360             the first character which does not belong to a line feed
361     */
362     protected int skipLineFeeds( java.io.InputStream aInput ) throws java.io.IOException
363     {
364         // read characters, until we encounter something which is not a line feed character
365         int nChar = aInput.read( );
366         while ( ( 13 == nChar ) || ( 10 == nChar ) )
367             nChar = aInput.read( );
368 
369         // now read everything which is behind this single character we are interested in
370         while ( 0 < aInput.available() )
371             aInput.read( );
372 
373         return nChar;
374     }
375 
376     /* ==================================================================
377        = table handling
378        ================================================================== */
379     /* ------------------------------------------------------------------ */
380     /** checks if a given table exists.
381 
382         <p>The check is made using a SELECT statement, so even if the connection
383         is a n SDB-level connection, which may filter tables in it's table
384         supplier, the result may be reliable ....</p>
385     */
386     protected boolean existsInvisibleTable( XConnection xConn, String sTableName ) throws java.lang.Exception
387     {
388         String sStatement = "SELECT * FROM ";
389         sStatement += sTableName;
390         sStatement += " WHERE 0=1";
391 
392         boolean bSuccess = false;
393         try
394         {
395             XStatement xStatement = xConn.createStatement();
396             xStatement.execute( sStatement );
397             // if we reached this point, the table probably exists
398             bSuccess = true;
399         }
400         catch(com.sun.star.sdbc.SQLException e)
401         {
402         }
403         return bSuccess;
404     }
405 
406     /* ------------------------------------------------------------------ */
407     /** add a specified table name to the table filter of the given data source.
408     */
409     protected void makeTableVisible( XDataSource xDS, XConnection xConn, String sTableName ) throws java.lang.Exception
410     {
411         // get the table filter
412         XPropertySet xDSP = UNO.queryPropertySet( xDS );
413         String[] aCurrentFilter = (String[])xDSP.getPropertyValue( "TableFilter" );
414 
415         // check if the table name is already part of it
416         String sAllTables = "*";                                                // all tables
417 
418         for ( int i=0; i<aCurrentFilter.length; ++i )
419         {
420             String sCurrentTableFilter = aCurrentFilter[i];
421 
422             if ( sCurrentTableFilter.equals( sTableName ) )
423                 return;
424             if ( sCurrentTableFilter.equals( sAllTables ) )
425                 return;
426         }
427 
428         // if we are here, we have to add our table to the filter sequence
429         String[] aNewFilter = new String[ aCurrentFilter.length + 1 ];
430         // copy the existent filter entries
431         for ( int i=0; i<aCurrentFilter.length; ++i )
432             aNewFilter[i] = aCurrentFilter[i];
433         // add our table
434         aNewFilter[ aCurrentFilter.length ] = sTableName;
435 
436         xDSP.setPropertyValue( "TableFilter", aNewFilter );
437     }
438 
439     /* ------------------------------------------------------------------ */
440     /** executes the given statement on the given connection
441     */
442     protected boolean implExecuteStatement( XConnection xConn, String sStatement ) throws java.lang.Exception
443     {
444         try
445         {
446             XStatement xStatement = xConn.createStatement( );
447             xStatement.execute( sStatement );
448         }
449         catch(com.sun.star.sdbc.SQLException e)
450         {
451             System.err.println( e );
452             return false;
453         }
454 
455         return true;
456     }
457 
458     /* ------------------------------------------------------------------ */
459     /** creates the table witht the given name, using the given statement
460     */
461     protected boolean implCreateTable( XConnection xConn, String sCreateStatement, String sTableName ) throws java.lang.Exception
462     {
463         if ( !implExecuteStatement( xConn, sCreateStatement ) )
464         {
465             System.out.println( "  could not create the table " + sTableName + "." );
466             System.out.println( );
467             return false;
468         }
469 
470         return true;
471     }
472 
473     /* ------------------------------------------------------------------ */
474     /** creates the table SALESMEN
475 
476         @return
477             <TRUE/> if and only if the creation succeeded
478     */
479     protected boolean createTableSalesman( XConnection xConn ) throws java.lang.Exception
480     {
481         String sCreateStatement = "CREATE TABLE " + s_tableNameSalesmen + " ";
482         sCreateStatement += "(SNR INTEGER NOT NULL, ";
483         sCreateStatement += "FIRSTNAME VARCHAR(50), ";
484         sCreateStatement += "LASTNAME VARCHAR(100), ";
485         sCreateStatement += "STREET VARCHAR(50), ";
486         sCreateStatement += "STATE VARCHAR(50), ";
487         sCreateStatement += "ZIP INTEGER, ";
488         sCreateStatement += "BIRTHDATE DATE, ";
489         sCreateStatement += "PRIMARY KEY(SNR))";
490 
491         if ( implCreateTable( xConn, sCreateStatement, s_tableNameSalesmen) )
492         {
493             String sInsertionPrefix = "INSERT INTO " + s_tableNameSalesmen + " VALUES ";
494 
495             implExecuteStatement( xConn, sInsertionPrefix + "(1, 'Joseph', 'Smith', 'Bond Street', 'CA', 95460, '1946-07-02')" );
496             implExecuteStatement( xConn, sInsertionPrefix + "(2, 'Frank', 'Jones', 'Lake silver', 'CA', 95460, '1963-12-24')" );
497             implExecuteStatement( xConn, sInsertionPrefix + "(3, 'Jane', 'Esperansa', '23 Hollywood driver', 'CA', 95460, '1972-04-01')" );
498 
499             return true;
500         }
501         return false;
502     }
503 
504     /* ------------------------------------------------------------------ */
505     /** creates the table CUSTOMERS
506 
507         @return
508             <TRUE/> if and only if the creation succeeded
509     */
510     protected boolean createTableCustomer( XConnection xConn ) throws java.lang.Exception
511     {
512         String sCreateStatement = "CREATE TABLE " + s_tableNameCustomers + " ";
513         sCreateStatement += "(COS_NR INTEGER NOT NULL, ";
514         sCreateStatement += "LASTNAME VARCHAR(100), ";
515         sCreateStatement += "STREET VARCHAR(50), ";
516         sCreateStatement += "CITY VARCHAR(50), ";
517         sCreateStatement += "STATE VARCHAR(50), ";
518         sCreateStatement += "ZIP INTEGER, ";
519         sCreateStatement += "PRIMARY KEY(COS_NR))";
520 
521         if ( implCreateTable( xConn, sCreateStatement, s_tableNameCustomers ) )
522         {
523             String sInsertionPrefix = "INSERT INTO " + s_tableNameCustomers + " VALUES ";
524 
525             implExecuteStatement( xConn, sInsertionPrefix + "(100, 'Acme, Inc.', '99 Market Street', 'Groundsville', 'CA', 95199)" );
526             implExecuteStatement( xConn, sInsertionPrefix + "(101, 'Superior BugSoft', '1 Party Place', 'Mendocino', 'CA', 95460)");
527             implExecuteStatement( xConn, sInsertionPrefix + "(102, 'WeKnowAll, Inc.', '100 Coffee Lane', 'Meadows', 'CA', 93699)");
528 
529             return true;
530         }
531         return false;
532     }
533 
534     /* ------------------------------------------------------------------ */
535     /** creates the table SALES
536 
537         @return
538             <TRUE/> if and only if the creation succeeded
539     */
540     protected boolean createTableSales( XConnection xConn ) throws java.lang.Exception
541     {
542         String sCreateStatement = "CREATE TABLE " + s_tableNameSales + " ";
543         sCreateStatement += "(SALENR INTEGER NOT NULL, ";
544         sCreateStatement += "COS_NR INTEGER NOT NULL, ";
545         sCreateStatement += "SNR INTEGER NOT NULL, ";
546         sCreateStatement += "NAME VARCHAR(50), ";
547         sCreateStatement += "SALEDATE DATE, ";
548         sCreateStatement += "PRICE DECIMAL(8,2), ";
549         sCreateStatement += "PRIMARY KEY(SALENR))";
550 
551         if ( implCreateTable( xConn, sCreateStatement, s_tableNameSales ) )
552         {
553             String sInsertionPrefix = "INSERT INTO " + s_tableNameSales + " VALUES ";
554 
555             implExecuteStatement( xConn, sInsertionPrefix + "(1, 100, 1, 'Fruits', '2005-02-12', 39.99)" );
556             implExecuteStatement( xConn, sInsertionPrefix + "(2, 101, 3, 'Beef', '2005-10-18', 15.78)" );
557             implExecuteStatement( xConn, sInsertionPrefix + "(3, 102, 3, 'Orange Juice', '2005-09-08', 25.63)" );
558             implExecuteStatement( xConn, sInsertionPrefix + "(4, 101, 2, 'Oil', '2005-03-01', 12.30)" );
559 
560             return true;
561         }
562 
563         return false;
564     }
565 
566     /* ------------------------------------------------------------------ */
567     /** ensures that the tables we need for our example exist
568     */
569     protected void ensureTables() throws java.lang.Exception
570     {
571         // get the data source
572         XDataSource xDS = m_database.getDataSource();
573         XPropertySet xDSProps = UNO.queryPropertySet( xDS );
574 
575         // connect to this data source
576         XConnection xConn = xDS.getConnection( "", "" );
577         XComponent xConnComp = UNO.queryComponent( xConn );
578 
579         createTableSalesman( xConn );
580         createTableCustomer( xConn );
581         createTableSales( xConn );
582 
583         // free the resources acquired by the connection
584         xConnComp.dispose();
585     }
586 
587     /* ==================================================================
588        = sample document handling
589        ================================================================== */
590 
591     /* ------------------------------------------------------------------ */
592     /** creates the button used for demonstrating (amonst others) event handling
593         @param nXPos
594             x-position of the to be inserted shape
595         @param nYPos
596             y-position of the to be inserted shape
597         @param nXSize
598             width of the to be inserted shape
599         @param sName
600             the name of the model in the form component hierarchy
601         @param sLabel
602             the label of the button control
603         @param sActionURL
604             the URL of the action which should be triggered by the button
605         @return
606             the model of the newly created button
607     */
608     protected XPropertySet createButton( int nXPos, int nYPos, int nXSize, String sName, String sLabel, short _formFeature ) throws java.lang.Exception
609     {
610         XPropertySet xButton = m_formLayer.createControlAndShape( "CommandButton", nXPos, nYPos, nXSize, 6 );
611         // the name for referring to it later:
612         xButton.setPropertyValue( "Name", sName );
613         // the label
614         xButton.setPropertyValue( "Label", sLabel );
615         // use the name as help text
616         xButton.setPropertyValue( "HelpText", sName );
617         // don't want buttons to be accessible by the "tab" key - this would be uncomfortable when traveling
618         // with records with "tab"
619         xButton.setPropertyValue( "Tabstop", new Boolean( false ) );
620         // similar, they should not steal the focus when clicked
621         xButton.setPropertyValue( "FocusOnClick", new Boolean( false ) );
622 
623         m_aOperator.addButton( xButton, _formFeature );
624 
625         return xButton;
626     }
627 
628     /* ------------------------------------------------------------------ */
629     /** creates a column in a grid
630         @param xGridModel
631             specifies the model of the grid where the new column should be inserted
632         @param sColumnService
633             specifies the service name of the column to create (e.g. "NumericField")
634         @param sDataField
635             specifies the database field to which the column should be bound
636         @param nWidth
637             specifies the column width (in mm). If 0, no width is set.
638         @return
639             the newly created column
640     */
641     XPropertySet createGridColumn( Object aGridModel, String sColumnService, String sDataField, int nWidth )
642         throws com.sun.star.uno.Exception
643     {
644         // the container to insert columns into
645         XIndexContainer xColumnContainer = UNO.queryIndexContainer( aGridModel );
646         // the factory for creating column models
647         XGridColumnFactory xColumnFactory = (XGridColumnFactory)UnoRuntime.queryInterface(
648             XGridColumnFactory.class, aGridModel );
649 
650         // (let) create the new col
651         XInterface xNewCol = (XInterface)xColumnFactory.createColumn( sColumnService );
652         XPropertySet xColProps = UNO.queryPropertySet( xNewCol );
653 
654         // some props
655         // the field the column is bound to
656         xColProps.setPropertyValue( "DataField", sDataField );
657         // the "display name" of the column
658         xColProps.setPropertyValue( "Label", sDataField );
659         // the name of the column within it's parent
660         xColProps.setPropertyValue( "Name", sDataField );
661 
662         if ( nWidth > 0 )
663             xColProps.setPropertyValue( "Width", new Integer( nWidth * 10 ) );
664 
665         // insert
666         xColumnContainer.insertByIndex( xColumnContainer.getCount(), xNewCol );
667 
668         // outta here
669         return xColProps;
670     }
671 
672     /* ------------------------------------------------------------------ */
673     /** creates a column in a grid
674     */
675     XPropertySet createGridColumn( Object aGridModel, String sColumnService, String sDataField )
676         throws com.sun.star.uno.Exception
677     {
678         return createGridColumn( aGridModel, sColumnService, sDataField );
679     }
680 
681     /* ------------------------------------------------------------------ */
682     /** creates our sample document
683     */
684     protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception
685     {
686         super.prepareDocument();
687 
688         m_database = new HsqlDatabase( m_xCtx );
689 
690         // ensure that we have the tables needed for our example
691         ensureTables();
692 
693         // --------------------------------------------------------------
694         /* create some shapes */
695         XPropertySet xSNRField =    m_formLayer.insertControlLine( "NumericField", "SNR", "", 3 );
696                                     m_formLayer.insertControlLine( "TextField", "FIRSTNAME", "", 11);
697                                     m_formLayer.insertControlLine( "TextField", "LASTNAME", "", 19 );
698                                     m_formLayer.insertControlLine( "TextField", "STREET", "", 27 );
699                                     m_formLayer.insertControlLine( "TextField", "STATE", "", 35 );
700         XPropertySet xZipField =    m_formLayer.insertControlLine( "NumericField", "ZIP", "", 43 );
701                                     m_formLayer.insertControlLine( "FormattedField", "BIRTHDATE", "", 51 );
702 
703         // for the salesman number / zip code, we don't want to have decimal places:
704         xSNRField.setPropertyValue( "DecimalAccuracy", new Short( (short)0 ) );
705         xZipField.setPropertyValue( "DecimalAccuracy", new Short( (short)0 ) );
706 
707         // --------------------------------------------------------------
708         /** need the form the control models belong to
709             for this, we simply obtain the parent for any of the control models we have
710 
711             Note that this involves knowledge about the implementation: If a control shape is
712             inserted into a document, where the control model does not belong to the form component
713             hierarchy, yet, it is automatically inserted into the first form, which is created
714             if necessary.
715         */
716         m_xMasterForm = FLTools.getParent( xZipField );
717 
718         // set the data source signature at the form
719         m_xMasterForm.setPropertyValue( "DataSourceName", m_database.getDocumentURL() );
720         m_xMasterForm.setPropertyValue( "CommandType", new Integer( CommandType.TABLE ) );
721         m_xMasterForm.setPropertyValue( "Command", "SALESMEN" );
722 
723         // --------------------------------------------------------------
724         // insert the buttons
725         // create our button operator, if necessary
726         m_aOperator = new ButtonOperator( m_xCtx, m_document, m_xMasterForm );
727 
728         createButton( 2, 63, 8, "first", "<<", FormFeature.MoveToFirst );
729         createButton( 12, 63, 8, "prev", "<", FormFeature.MoveToPrevious );
730         createButton( 22, 63, 8, "next", ">", FormFeature.MoveToNext );
731         createButton( 32, 63, 8, "last", ">>", FormFeature.MoveToLast );
732         createButton( 42, 63, 8, "new", ">*", FormFeature.MoveToInsertRow );
733         createButton( 58, 63, 13, "reload", "reload", FormFeature.ReloadForm );
734 
735         // --------------------------------------------------------------
736         // create a sub for for the sales
737 
738         // for this, first create a sub form and bind it to the SALES table
739         XIndexContainer xSalesForm = m_document.createSubForm( m_xMasterForm, "Sales" );
740         XPropertySet xSalesFormProps = UNO.queryPropertySet( xSalesForm );
741 
742         xSalesFormProps.setPropertyValue( "DataSourceName", m_database.getDocumentURL() );
743         xSalesFormProps.setPropertyValue( "CommandType", new Integer( CommandType.COMMAND ) );
744 
745         String sCommand = new String( "SELECT * FROM " );
746         sCommand += s_tableNameSales;
747         sCommand += " WHERE " + s_tableNameSales + ".SNR = :salesmen";
748         xSalesFormProps.setPropertyValue( "Command", sCommand );
749 
750         // the master-details connection
751         String[] aMasterFields = new String[] { "SNR" };        // the field in the master form
752         String[] aDetailFields = new String[] { "salesmen" };   // the name in the detail form
753         xSalesFormProps.setPropertyValue( "MasterFields", aMasterFields );
754         xSalesFormProps.setPropertyValue( "DetailFields", aDetailFields );
755 
756         // the create thr grid model
757         XPropertySet xSalesGridModel = m_formLayer.createControlAndShape( "GridControl", 2, 80, 162, 40, xSalesForm );
758         xSalesGridModel.setPropertyValue( "Name", "SalesTable" );
759         XPropertySet xKeyColumn      =  createGridColumn( xSalesGridModel, "NumericField", "SALENR", 12 );
760         XPropertySet xCustomerColumn =  createGridColumn( xSalesGridModel, "ListBox", "COS_NR", 40 );
761         XPropertySet xSalesNameColumn = createGridColumn( xSalesGridModel, "TextField", "NAME", 25 );
762                                         createGridColumn( xSalesGridModel, "DateField", "SALEDATE", 24 );
763                                         createGridColumn( xSalesGridModel, "CurrencyField", "PRICE", 16 );
764 
765             // please note that a better solution for the SALEDATE field would have been to use
766             // a FormattedField. But we want to demonstrate some effects with DateFields here ...
767 
768         m_aSalesNameValidator = new GridFieldValidator( m_xCtx, xSalesNameColumn );
769         m_aSalesNameValidator.enableColumnWatch( m_bAllowEmptySales );
770 
771         xKeyColumn.setPropertyValue( "DecimalAccuracy", new Short( (short)0 ) );
772 
773         // init the list box which is for choosing the customer a sale belongs to
774         xCustomerColumn.setPropertyValue( "BoundColumn", new Short( (short)1 ) );
775         xCustomerColumn.setPropertyValue( "Label", "Customer" );
776         xCustomerColumn.setPropertyValue( "ListSourceType", ListSourceType.SQL );
777 
778         String sListSource = "SELECT LASTNAME, COS_NR FROM ";
779         sListSource += s_tableNameCustomers;
780         String[] aListSource = new String[] { sListSource };
781         xCustomerColumn.setPropertyValue( "ListSource", aListSource );
782 
783         // We want to demonstrate how to reset fields to NULL, we do this with the SALEDATE field
784         // above. For this, we add as reset listener to the form
785         XReset xFormReset = UNO.queryReset( xSalesForm );
786         xFormReset.addResetListener( this );
787 
788 
789         // --------------------------------------------------------------
790         // the option for filtering the sales form
791         XIndexContainer xSalesFilterForm = m_document.createSiblingForm( xSalesForm, "SalesFilter" );
792         XPropertySet xSFFProps = UNO.queryPropertySet( xSalesFilterForm );
793         XPropertySet xLabel = m_formLayer.createControlAndShape( "FixedText", 2, 125, 35, 6, xSalesFilterForm );
794         xLabel.setPropertyValue( "Label", "show only sales since" );
795         xLabel.setPropertyValue( "Name", "FilterLabel" );
796 
797         XPropertySet xFilterSelection = m_formLayer.createControlAndShape( "ListBox", 40, 125, 59, 6, xSalesFilterForm );
798         xFilterSelection.setPropertyValue( "Name", "FilterList" );
799         xFilterSelection.setPropertyValue( "LabelControl", xLabel );
800         XPropertySet xManualFilter = m_formLayer.createControlAndShape( "DateField", 104, 125, 30, 6, xSalesFilterForm );
801         xManualFilter.setPropertyValue( "Name", "ManualFilter" );
802         XPropertySet xApplyFilter = m_formLayer.createControlAndShape( "CommandButton", 139, 125, 25, 6, xSalesFilterForm );
803         xApplyFilter.setPropertyValue( "Name", "ApplyFilter" );
804         xApplyFilter.setPropertyValue( "DefaultButton", new Boolean( true ) );
805         m_aSalesFilter = new SalesFilter( m_document, xSalesFormProps, xFilterSelection,
806             xManualFilter, xApplyFilter );
807 
808 
809         // --------------------------------------------------------------
810         // the options section
811         // for this, we need a form which is a sibling of our master form (don't want to interfere
812         // the controls which represent options only with the controls which are used for data access)
813 
814         XIndexContainer xOptionsForm = m_document.createSiblingForm( m_xMasterForm, "Options" );
815 
816         xLabel = m_formLayer.createControlAndShape( "GroupBox", 98, 0, 66, 62, xOptionsForm );
817         xLabel.setPropertyValue( "Name", "Options" );
818         xLabel.setPropertyValue( "Label", "Options" );
819 
820         // radio buttons which controls how we generate unique keys
821         xLabel = m_formLayer.createControlAndShape( "GroupBox", 103, 5, 56, 25, xOptionsForm );
822         xLabel.setPropertyValue( "Label", "key generation" );
823         xLabel.setPropertyValue( "Name", "KeyGeneration" );
824         XPropertySet xKeyGen = m_formLayer.createControlAndShape( "RadioButton", 106, 11, 50, 6, xOptionsForm );
825         xKeyGen.setPropertyValue( "Name", "KeyGen" );
826         xKeyGen.setPropertyValue( "Label", "no automatic generation" );
827         xKeyGen.setPropertyValue( "RefValue", "none" );
828         xKeyGen.addPropertyChangeListener( "State", this );
829 
830         xKeyGen = m_formLayer.createControlAndShape( "RadioButton", 106, 17, 50, 6, xOptionsForm );
831         xKeyGen.setPropertyValue( "Name", "KeyGen" );
832         xKeyGen.setPropertyValue( "Label", "before inserting a record" );
833         xKeyGen.setPropertyValue( "RefValue", "update" );
834         xKeyGen.addPropertyChangeListener( "State", this );
835 
836         xKeyGen = m_formLayer.createControlAndShape( "RadioButton", 106, 23, 50, 6, xOptionsForm );
837         xKeyGen.setPropertyValue( "Name", "KeyGen" );
838         xKeyGen.setPropertyValue( "Label", "when moving to a new record" );
839         xKeyGen.setPropertyValue( "RefValue", "reset" );
840         xKeyGen.addPropertyChangeListener( "State", this );
841 
842         // initialize listeners
843         // master form - key generation
844         m_aSalesmanKeyGenerator = new KeyGenerator( m_xMasterForm, "SNR", m_xCtx );
845         m_aSalesmanKeyGenerator.activateKeyGenerator( true );
846         // master form - control locking
847         m_aSalesmenLocker = new ControlLock( m_xMasterForm, "SNR" );
848         m_aSalesmenLocker.enableLock( m_bProtectKeyFields );
849 
850         // details form - key generation
851         m_aSalesKeyGenerator = new KeyGenerator( xSalesFormProps, "SALENR", m_xCtx );
852         m_aSalesKeyGenerator.activateKeyGenerator( true );
853 
854         // details form - control locking
855         m_aSalesLocker = new ControlLock( xSalesFormProps, "SALENR" );
856         m_aSalesLocker.enableLock( m_bProtectKeyFields );
857 
858         // initally, we want to generate keys when moving to a new record
859         xKeyGen.setPropertyValue( "DefaultState", new Short( (short)1 ) );
860 
861         // --------------------------------------------------------------
862         // second options block
863         xLabel = m_formLayer.createControlAndShape( "GroupBox", 103, 33, 56, 25, xOptionsForm  );
864         xLabel.setPropertyValue( "Name", "Misc" );
865         xLabel.setPropertyValue( "Label", "Miscellaneous" );
866 
867         XPropertySet xCheck = m_formLayer.createControlAndShape( "CheckBox", 106, 39, 60, 6, xOptionsForm  );
868         xCheck.setPropertyValue( "Name", "defaultdate" );
869         xCheck.setPropertyValue( "Label", "default sales date to \"today\"" );
870         xCheck.setPropertyValue( "HelpText", "When checked, newly entered sales records are pre-filled with today's date, else left empty." );
871         xCheck.addPropertyChangeListener( "State", this );
872 
873         xCheck = m_formLayer.createControlAndShape( "CheckBox", 106, 45, 60, 6, xOptionsForm  );
874         xCheck.setPropertyValue( "Name", "protectkeys" );
875         xCheck.setPropertyValue( "Label", "protect key fields from editing" );
876         xCheck.setPropertyValue( "HelpText", "When checked, you cannot modify the values in the table's key fields (SNR and SALENR)" );
877         xCheck.addPropertyChangeListener( "State", this );
878 
879         xCheck = m_formLayer.createControlAndShape( "CheckBox", 106, 51, 60, 6, xOptionsForm  );
880         xCheck.setPropertyValue( "Name", "emptysales" );
881         xCheck.setPropertyValue( "Label", "check for empty sales names" );
882         xCheck.setPropertyValue( "HelpText", "When checked, you cannot enter empty values into the NAME column of the 'Sales' table." );
883         xCheck.addPropertyChangeListener( "State", this );
884 
885         // dump the form component tree
886         enumFormComponents( );
887     }
888 
889     /* ------------------------------------------------------------------ */
890     protected void onFormsAlive()
891     {
892         m_aOperator.onFormsAlive();
893     }
894 
895     /* ------------------------------------------------------------------ */
896     /** performs any cleanup before exiting the program
897     */
898     protected void cleanUp( ) throws java.lang.Exception
899     {
900         // remove the listeners at the buttons
901         RevokeButtons aRevoke = new RevokeButtons( m_aOperator );
902         aRevoke.handle( m_document.getFormComponentTreeRoot( ) );
903 
904         // remove the key generator listeners from the form
905         m_aSalesmanKeyGenerator.stopGenerator( );
906         m_aSalesKeyGenerator.stopGenerator( );
907 
908         // and the control lockers
909         m_aSalesmenLocker.enableLock( false );
910         m_aSalesLocker.enableLock( false );
911 
912         // the validator for the grid column
913         m_aSalesNameValidator.enableColumnWatch( false );
914 
915         // remove our own reset listener from the form
916         XNameAccess xMasterAsNames = (XNameAccess)UnoRuntime.queryInterface(
917             XNameAccess.class, m_xMasterForm );
918         XReset xFormReset = UNO.queryReset( xMasterAsNames.getByName( "Sales" ) );
919         xFormReset.removeResetListener( this );
920 
921         super.cleanUp();
922     }
923 
924     /* ------------------------------------------------------------------ */
925     /** class entry point
926     */
927     public static void main(String argv[]) throws java.lang.Exception
928     {
929         DataAwareness aSample = new DataAwareness();
930         aSample.run( argv );
931     }
932 }
933