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