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