summaryrefslogtreecommitdiff
path: root/wizards
diff options
context:
space:
mode:
authorOliver Bolte <obo@openoffice.org>2004-09-08 13:00:25 +0000
committerOliver Bolte <obo@openoffice.org>2004-09-08 13:00:25 +0000
commitf8809d59acf4b1b28aa1042f8bd4839198a93335 (patch)
treedaa9d3244bfb23c34e73b5aa8728e3783f9e4c2f /wizards
parent96660c2e9945d12dba18969eca0012ab73c8a141 (diff)
INTEGRATION: CWS qwizards2 (1.1.2); FILE ADDED
2004/08/20 14:55:46 tv 1.1.2.7: checked in for ron 2004/08/13 10:13:48 rpiterman 1.1.2.6: documentation 2004/07/19 13:56:43 rpiterman 1.1.2.5: removed debug ouput to stdout 2004/06/30 16:21:15 rpiterman 1.1.2.4: fixed a bug which crashed office (focusGained is now called programatically when I set the focus) optimized cell redraw - now only the actuall cell redraws when the topic text changes. (topic list only) 2004/06/25 13:02:09 rpiterman 1.1.2.3: further developement 2004/06/11 16:05:34 rpiterman 1.1.2.2: Further developement 2004/06/02 15:30:17 rpiterman 1.1.2.1: New Agenda Wizard
Diffstat (limited to 'wizards')
-rw-r--r--wizards/com/sun/star/wizards/agenda/TopicsControl.java1187
1 files changed, 1187 insertions, 0 deletions
diff --git a/wizards/com/sun/star/wizards/agenda/TopicsControl.java b/wizards/com/sun/star/wizards/agenda/TopicsControl.java
new file mode 100644
index 000000000000..3e5c946f3a0d
--- /dev/null
+++ b/wizards/com/sun/star/wizards/agenda/TopicsControl.java
@@ -0,0 +1,1187 @@
+/*************************************************************************
+ *
+ * $RCSfile: TopicsControl.java,v $
+ *
+ * $Revision: 1.2 $
+ *
+ * last change: $Author: obo $ $Date: 2004-09-08 14:00:25 $
+ *
+ * The Contents of this file are made available subject to the terms of
+ * either of the following licenses
+ *
+ * - GNU Lesser General Public License Version 2.1
+ * - Sun Industry Standards Source License Version 1.1
+ *
+ * Sun Microsystems Inc., October, 2000
+ *
+ * GNU Lesser General Public License Version 2.1
+ * =============================================
+ * Copyright 2000 by Sun Microsystems, Inc.
+ * 901 San Antonio Road, Palo Alto, CA 94303, USA
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ *
+ * Sun Industry Standards Source License Version 1.1
+ * =================================================
+ * The contents of this file are subject to the Sun Industry Standards
+ * Source License Version 1.1 (the "License"); You may not use this file
+ * except in compliance with the License. You may obtain a copy of the
+ * License at http://www.openoffice.org/license.html.
+ *
+ * Software provided under this License is provided on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+ * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+ * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+ * See the License for the specific provisions governing your rights and
+ * obligations concerning the Software.
+ *
+ * The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+ *
+ * Copyright: 2000 by Sun Microsystems, Inc.
+ *
+ * All Rights Reserved.
+ *
+ * Contributor(s): _______________________________________
+ *
+ */
+
+package com.sun.star.wizards.agenda;
+
+import java.util.List;
+
+import com.sun.star.awt.FocusEvent;
+import com.sun.star.awt.Key;
+import com.sun.star.awt.KeyEvent;
+import com.sun.star.awt.KeyModifier;
+import com.sun.star.awt.Selection;
+import com.sun.star.awt.XControl;
+import com.sun.star.awt.XFocusListener;
+import com.sun.star.awt.XKeyListener;
+import com.sun.star.awt.XTextComponent;
+import com.sun.star.awt.XWindow;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.wizards.common.Helper;
+import com.sun.star.wizards.common.Properties;
+import com.sun.star.wizards.ui.ControlScroller;
+import com.sun.star.wizards.ui.UnoDialog2;
+import com.sun.star.wizards.ui.event.EventNames;
+import com.sun.star.wizards.ui.event.MethodInvocation;
+
+
+/**
+ * @author rpiterman
+ * This class implements the UI functionality of the topics scroller control.
+ * <br/>
+ * During developement, there has been a few changes which were not *fully* done -
+ * mainly in converting the topics and time boxes from combobox and time box to normal textboxes,
+ * so in the code they might be referenced as combobox or timebox. This should be
+ * rather understood as topicstextbox and timetextbox.
+ * <br/>
+ * <br/>
+ * Important behaiviour of this control is that there is always a
+ * blank row at the end, in which the user can enter data.<br/>
+ * Once the row is not blank (thus, the user entered data...),
+ * a new blank row is added.<br/>
+ * Once the user removes the last *unempty* row, by deleteing its data, it becomes
+ * the *last empty row* and the one after is being automatically removed.<br/>
+ * <br/>
+ * The contorl shows 5 rows at a time.<br/>
+ * If, for example, only 2 rows exist (from which the 2ed one is empty...)
+ * then the other three rows, which do not exist in the data model, are disabled.
+ * <br/>
+ * The following other functionality is implemented:
+ * <br/>
+ * 0. synchroniting data between controls, data model and live preview.
+ * 1. Tab scrolling.<br/>
+ * 2. Keyboard scrolling.<br/>
+ * 3. Removing rows and adding new rows.<br/>
+ * 4. Moving rows up and down. <br/>
+ * <br/>
+ * This control relays on the ControlScroller control which uses the following
+ * Data model:<br/>
+ * 1. It uses a vector, whos members are arrays of PropertyValue.<br/>
+ * 2. Each array represents a row.<br/>
+ * (Note: the Name and Value memebrs of the PropertyValue object are being used...)
+ * 3. Each property Value represents a value for a single control with the following rules:<br/>
+ * 3. a. the Value of the property is used for as value of the controls (usually text).<br/>
+ * 3. b. the Name of the property is used to map values to UI controls in the following manner:<br/>
+ * 3. b. 1. only the Name of the first X Rows is regarded, where X is the number of visible rows
+ * (in the agenda wizard this would be 5, since 5 topic rows are visible on the dialog).<br/>
+ * 3. b. 2. The Names of the first X (or 5...) rows are the names of the UI Controls to
+ * hold values. When the control scroller scrolls, it looks at the first 5 rows and uses
+ * the names specified there to map the current values to the specified controls.
+ * <br/>
+ * This data model makes the following limitations on the implementation:
+ * When moving rows, only the values should be moved. The Rows objects, which contain
+ * also the Names of the controls should not be switched. <br/>
+ * also when deleting or inserting rows, attention should be paid that no rows should be removed
+ * or inserted. Instead, only the Values should rotate.
+ * <br/>
+ * <br/>
+ * To save the topics in the registry a ConfigSet of objects of type CGTopic is
+ * being used.
+ * This one is not synchronized "live", since it is unnecessary... instead, it is
+ * synchronized on call, before the settings should be saved.
+ */
+public class TopicsControl extends ControlScroller implements XFocusListener
+{
+ /**
+ * The name prefix of the number (label) controls
+ */
+ public static final String LABEL = "lblTopicCnt_";
+ /**
+ * The name prefix of the topic (text) controls
+ */
+ public static final String TOPIC = "txtTopicTopic_";
+ /**
+ * The name prefix of the responsible (text) controls
+ */
+ public static final String RESP = "cbTopicResp_";
+ /**
+ * The name prefix of the time (text) controls
+ */
+ public static final String TIME = "txtTopicTime_";
+
+
+ Object lastFocusControl;
+ int lastFocusRow;
+
+ /**
+ * the last
+ * topic text box.
+ * When pressing tab on this one a scroll down *may* be performed.
+ */
+ private Object firstTopic;
+ /**
+ * the first time box.
+ * When pressing shift-tab on this control, a scroll up *may* be performed.
+ */
+ private Object lastTime;
+
+ /**
+ * is used when constructing to track the tab index
+ * of the created control rows.
+ */
+ private int tabIndex = 520;
+
+ /**
+ * create a new TopicControl. Since this is used specifically for the
+ * agenda dialog, I use step 5, and constant location - and need no paramter...
+ * @param dialog the parent dialog
+ * @param xmsf service factory
+ * @param agenda the Agenda configuration data (contains the current topics data).
+ */
+ public TopicsControl(AgendaWizardDialog dialog, XMultiServiceFactory xmsf, CGAgenda agenda) {
+ super(dialog,xmsf,5, 92 , 38, 212 ,5,18, AgendaWizardDialogConst.LAST_HID );
+ initializeScrollFields(agenda);
+ initialize( agenda.cp_Topics.getSize() + 1 );
+
+ // set some focus listeners for TAB scroll down and up...
+ try {
+
+ // prepare scroll down on tab press...
+ Object lastTime = ((ControlRow)ControlGroupVector.get(nblockincrement -1 )).timebox;
+
+ MethodInvocation mi = new MethodInvocation("lastControlKeyPressed", this, KeyEvent.class);
+ dialog.guiEventListener.add( TIME + ( nblockincrement - 1), EventNames.EVENT_KEY_PRESSED, mi);
+
+ addKeyListener(lastTime, (XKeyListener) dialog.guiEventListener);
+
+ //prepare scroll up on tab press...
+ firstTopic = ((ControlRow)ControlGroupVector.get(0)).textbox;
+
+ mi = new MethodInvocation("firstControlKeyPressed", this, KeyEvent.class);
+ dialog.guiEventListener.add( TOPIC + 0 , EventNames.EVENT_KEY_PRESSED, mi);
+
+ addKeyListener(firstTopic, (XKeyListener) dialog.guiEventListener);
+
+ }
+ catch (NoSuchMethodException ex) {
+ ex.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Is used to add a keylistener to different controls...
+ */
+ static void addKeyListener(Object control,XKeyListener listener) {
+ XWindow xlastControl = (XWindow)UnoRuntime.queryInterface(XWindow.class,
+ control );
+ xlastControl.addKeyListener(listener);
+ }
+
+
+ /**
+ * Is used to add a focuslistener to different controls...
+ */
+ static void addFocusListener(Object control,XFocusListener listener) {
+ XWindow xlastControl = (XWindow)UnoRuntime.queryInterface(XWindow.class,
+ control );
+ xlastControl.addFocusListener(listener);
+ }
+
+ /**
+ * Implementation of the parent class...
+ */
+ protected void initializeScrollFields() {}
+
+
+ /**
+ * initializes the data of the control.
+ * @param agenda
+ */
+ protected void initializeScrollFields(CGAgenda agenda)
+ {
+ // create a row for each topic with the given values....
+ for (int i = 0; i < agenda.cp_Topics.getSize(); i++) {
+ PropertyValue[] row = newRow(i);
+ ((CGTopic)agenda.cp_Topics.getElementAt(i)).setDataToRow(row);
+ // a parent class method
+ registerControlGroup( row , i );
+ this.updateDocumentRow(i);
+ }
+ // inserts a blank row at the end...
+ insertRowAtEnd();
+ }
+
+ /**
+ * Insert a blank (empty) row
+ * as last row of the control.
+ * The control has always a blank row at the
+ * end, which enables the user to enter data...
+ */
+ protected void insertRowAtEnd() {
+ int l = scrollfields.size();
+ registerControlGroup( newRow(l), l );
+ setTotalFieldCount(l+1);
+
+ // if the new row is visible, it must have been disabled
+ // so it should be now enabled...
+ if ( l- nscrollvalue < nblockincrement )
+ ((ControlRow) ControlGroupVector.get( l - nscrollvalue )).setEnabled(true);
+
+ }
+
+ /**
+ * The Topics Set in the CGAgenda object is synchronized to
+ * the current content of the topics.
+ * @param agenda
+ */
+ void saveTopics(CGAgenda agenda) {
+ agenda.cp_Topics.clear();
+ for (int i = 0; i < scrollfields.size() - 1; i++ )
+ agenda.cp_Topics.add(i,
+ new CGTopic( scrollfields.get(i)) );
+ }
+
+ /**
+ * overrides the parent class method to also enable the
+ * row whenever data is written to it.
+ */
+ protected void fillupControls(int guiRow) {
+ super.fillupControls(guiRow);
+ ((ControlRow) ControlGroupVector.get( guiRow )).setEnabled(true);
+ }
+
+
+ /**
+ * remove the last row
+ */
+ protected void removeLastRow() {
+ int l = scrollfields.size();
+
+ // if we should scroll up...
+ if ( ( l - nscrollvalue >= 1) && ( l - nscrollvalue <= nblockincrement ) && nscrollvalue > 0 ) {
+ while ( ( l - nscrollvalue >= 1) && ( l - nscrollvalue <= nblockincrement ) && nscrollvalue > 0 )
+ setScrollValue(nscrollvalue - 1);
+ }
+ // if we should disable a row...
+ else if ( nscrollvalue == 0 && l - 1 < nblockincrement ) {
+ ControlRow cr = (ControlRow)ControlGroupVector.get( l - 1 );
+ cr.setEnabled(false);
+ }
+
+ unregisterControlGroup( l - 1 );
+ setTotalFieldCount(l - 1);
+ }
+
+ /**
+ * in order to use the "move up", "down" "insert" and "remove" buttons,
+ * we track the last control the gained focus, in order to know which
+ * row should be handled.
+ */
+ public void focusGained(FocusEvent fe) {
+ XControl xc = (XControl)UnoRuntime.queryInterface(XControl.class,fe.Source);
+ focusGained(xc);
+ }
+
+ /**
+ * Sometimes I set the focus programatically to a control
+ * (for example when moving a row up or down, the focus should move
+ * with it).
+ * In such cases, no VCL event is being triggered so it must
+ * be called programtically.
+ * This is done by this method.
+ * @param control
+ */
+ private void focusGained(XControl control) {
+ try {
+ //calculate in which row we are...
+ String name = (String)Helper.getUnoPropertyValue(UnoDialog2.getModel(control),"Name");
+ int i = name.indexOf("_");
+ String num = name.substring(i+1);
+ lastFocusRow = Integer.valueOf(num).intValue() + nscrollvalue;
+ lastFocusControl = control;
+ // enable/disable the buttons...
+ enableButtons();
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * enable or disable the buttons according to the
+ * current row we are in.
+ */
+ private void enableButtons() {
+ UnoDialog2.setEnabled( getAD().btnInsert , (lastFocusRow < scrollfields.size() - 1 ? Boolean.TRUE : Boolean.FALSE ) );
+ UnoDialog2.setEnabled( getAD().btnRemove , (lastFocusRow < scrollfields.size() - 1 ? Boolean.TRUE : Boolean.FALSE ) );
+ UnoDialog2.setEnabled( getAD().btnUp , (lastFocusRow > 0 ? Boolean.TRUE : Boolean.FALSE ) );
+ UnoDialog2.setEnabled( getAD().btnDown , (lastFocusRow < scrollfields.size() - 1 ? Boolean.TRUE : Boolean.FALSE ) );
+ }
+
+ /**
+ * compolsary implementation of FocusListener.
+ */
+ public void focusLost(FocusEvent fe) {}
+ /**
+ * compolsary implementation of FocusListener.
+ */
+ public void disposing(EventObject o) {}
+
+
+ /**
+ * Convenience method. Is used to get a reference of
+ * the template controller (live preview in background).
+ * @return the parent dialog, casted to AgendaWizardDialog.
+ */
+ private AgendaWizardDialog getAD() {
+ return (AgendaWizardDialog)this.CurUnoDialog;
+ }
+
+ /**
+ * move the current row up
+ */
+ public void rowUp() {
+ rowUp(lastFocusRow - nscrollvalue, lastFocusControl);
+ }
+
+ /**
+ * move the current row down.
+ */
+ public void rowDown() {
+ rowDown(lastFocusRow - nscrollvalue, lastFocusControl);
+ }
+
+ private void lockDoc() {
+ //((AgendaWizardDialogImpl)CurUnoDialog).agendaTemplate.xTextDocument.lockControllers();
+ }
+
+ private void unlockDoc() {
+ //((AgendaWizardDialogImpl)CurUnoDialog).agendaTemplate.xTextDocument.unlockControllers();
+ }
+
+ /**
+ * Removes the current row.
+ * See general class documentation explanation about the
+ * data model used and the limitations which explain the implementation here.
+ */
+ public void removeRow() {
+ lockDoc();
+ for (int i = lastFocusRow; i < scrollfields.size() - 1; i++) {
+ PropertyValue[] pv1 = (PropertyValue[])scrollfields.get(i);
+ PropertyValue[] pv2 = (PropertyValue[])scrollfields.get(i + 1);
+ pv1[1].Value = pv2[1].Value;
+ pv1[2].Value = pv2[2].Value;
+ pv1[3].Value = pv2[3].Value;
+ updateDocumentRow(i);
+ if (i - nscrollvalue < nblockincrement)
+ fillupControls(i-nscrollvalue);
+ }
+ removeLastRow();
+ // update the live preview background document
+ reduceDocumentToTopics();
+
+ // the focus should return to the edit control
+ focus(lastFocusControl);
+ unlockDoc();
+ }
+
+
+ /**
+ * Inserts a row before the current row.
+ * See general class documentation explanation about the
+ * data model used and the limitations which explain the implementation here.
+ */
+ public void insertRow() {
+ lockDoc();
+ insertRowAtEnd();
+ for (int i = scrollfields.size() - 2; i > lastFocusRow; i--) {
+ PropertyValue[] pv1 = (PropertyValue[])scrollfields.get(i);
+ PropertyValue[] pv2 = (PropertyValue[])scrollfields.get(i - 1);
+ pv1[1].Value = pv2[1].Value;
+ pv1[2].Value = pv2[2].Value;
+ pv1[3].Value = pv2[3].Value;
+ updateDocumentRow(i);
+ if (i - nscrollvalue < nblockincrement) {
+ fillupControls(i-nscrollvalue);
+ }
+ }
+
+ // after rotating all the properties from this row on,
+ // we clear the row, so it is practically a new one...
+ PropertyValue[] pv1 = (PropertyValue[])scrollfields.get(lastFocusRow);
+ pv1[1].Value = "";
+ pv1[2].Value = "";
+ pv1[3].Value = "";
+
+ // update the preview document.
+ updateDocumentRow(lastFocusRow);
+
+ fillupControls(lastFocusRow-nscrollvalue);
+
+ focus(lastFocusControl);
+ unlockDoc();
+ }
+
+ /**
+ * create a new row with the given index.
+ * The index is important because it is used in the
+ * Name member of the PropertyValue objects.
+ * To know why see general class documentation above (data model explanation).
+ * @param i the index of the new row
+ * @return
+ */
+ private PropertyValue[] newRow(int i) {
+ PropertyValue[] pv = new PropertyValue[4];
+ pv[0] = Properties.createProperty(LABEL + i, "" + (i + 1) + ".");
+ pv[1] = Properties.createProperty(TOPIC + i, "" );
+ pv[2] = Properties.createProperty(RESP + i, "");
+ pv[3] = Properties.createProperty(TIME + i, "");
+ return pv;
+ }
+
+ /**
+ * Implementation of ControlScroller
+ * This is a UI method which inserts a new row to the control.
+ * It uses the child-class ControlRow. (see below).
+ * @see ControlRow
+ */
+ protected void insertControlGroup(int _index, int npos)
+ {
+ ControlRow oControlRow = new ControlRow((AgendaWizardDialog)CurUnoDialog, iCompPosX, npos, _index, tabIndex);
+ ControlGroupVector.addElement(oControlRow);
+ tabIndex += 4;
+ }
+
+ /**
+ * Implementation of ControlScroller
+ * This is a UI method which makes a row visibele.
+ * As far as I know it is never called.
+ * @see ControlRow
+ */
+ protected void setControlGroupVisible(int _index, boolean _bIsVisible)
+ {
+ ((ControlRow)ControlGroupVector.get(_index)).setVisible(_bIsVisible);
+
+ }
+
+ /**
+ * Checks if a row is empty.
+ * This is used when the last row is changed.
+ * If it is empty, the next row (which is always blank) is removed.
+ * If it is not empty, a next row must exist.
+ * @param row the index number of the row to check.
+ * @return true if empty. false if not.
+ */
+ protected boolean isRowEmpty(int row) {
+ PropertyValue[] data = getTopicData(row );
+
+ // now - is this row empty?
+ return
+ data[1].Value.equals("") &&
+ data[2].Value.equals("") &&
+ data[3].Value.equals("");
+
+ }
+
+ /**
+ * is used for data tracking.
+ */
+ private Object[] oldData;
+
+ /**
+ * update the preview document and
+ * remove/insert rows if needed.
+ * @param guiRow
+ * @param column
+ */
+ synchronized void fieldChanged(int guiRow, int column) {
+ synchronized (this) {
+
+ try {
+ // First, I update the document
+ PropertyValue[] data = getTopicData(guiRow + nscrollvalue);
+
+ if (data == null)
+ return;
+
+ boolean equal = true;
+
+ if (oldData != null) {
+ for (int i=0; i<data.length && equal; i++)
+ equal = ( equal & data[i].Value.equals(oldData[i]) );
+
+ if (equal)
+ return;
+ }
+ else
+ oldData = new Object[4];
+
+ for (int i=0; i<data.length; i++)
+ oldData[i] = data[i].Value;
+
+ updateDocumentCell(guiRow + nscrollvalue, column, data);
+
+ if (isRowEmpty(guiRow + nscrollvalue)) {
+ /* if this is the row before the last one
+ * (the last row is always empty)
+ * delete the last row...
+ */
+ if ( guiRow + nscrollvalue == scrollfields.size() - 2) {
+ removeLastRow();
+
+ /*
+ * now consequentially check the last two rows,
+ * and remove the last one if they are both empty.
+ * (actually I check always the "before last" row,
+ * because the last one is always empty...
+ */
+ while (scrollfields.size() > 1 && isRowEmpty(scrollfields.size() -2))
+ removeLastRow();
+
+ ControlRow cr = (ControlRow)ControlGroupVector.get( scrollfields.size() - nscrollvalue - 1);
+
+ // if a remove was performed, set focus to the last row with some data in it...
+ focus(getControl( cr , column ));
+
+ // update the preview document.
+ reduceDocumentToTopics();
+ }
+
+ }
+
+ else { // row contains data
+ // is this the last row?
+ if ( ( guiRow + nscrollvalue + 1 ) == scrollfields.size() )
+ insertRowAtEnd();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+ /**
+ * return the corresponding row data for the given index.
+ * @param topic index of the topic to get.
+ * @return a PropertyValue array with the data for the given topic.
+ */
+ public PropertyValue[] getTopicData(int topic) {
+ if (topic < scrollfields.size())
+ return ( PropertyValue[]) scrollfields.get ( topic ) ;
+ else return null;
+ }
+
+ /**
+ * If the user presses tab on the last control, and
+ * there *are* more rows in the model, scroll down.
+ * @param event
+ */
+ public void lastControlKeyPressed(KeyEvent event) {
+ // if tab without shift was pressed...
+ if ( ( event.KeyCode == Key.TAB ) && ( event.Modifiers==0 ) )
+ // if there is another row...
+ if ( (nblockincrement + nscrollvalue) < scrollfields.size() ) {
+ setScrollValue(nscrollvalue + 1);
+ focus(firstTopic);
+ }
+ }
+
+ /**
+ * If the user presses shift-tab on the first control, and
+ * there *are* more rows in the model, scroll up.
+ * @param event
+ */
+ public void firstControlKeyPressed(KeyEvent event) {
+ // if tab with shift was pressed...
+ if ( ( event.KeyCode == Key.TAB ) && ( event.Modifiers== KeyModifier.SHIFT ) )
+
+ if ( nscrollvalue > 0 ) {
+ setScrollValue(nscrollvalue - 1);
+ focus(lastTime);
+ }
+ }
+
+ /**
+ * sets focus to the given control.
+ * @param textControl
+ */
+ private void focus(Object textControl) {
+ ((XWindow)UnoRuntime.queryInterface(XWindow.class,textControl)).setFocus();
+ XTextComponent xTextComponent = (XTextComponent)UnoRuntime.queryInterface(XTextComponent.class,textControl);
+ String text = xTextComponent.getText();
+ xTextComponent.setSelection( new Selection(0, text.length()) );
+ XControl xc = (XControl)UnoRuntime.queryInterface(XControl.class,textControl);
+ focusGained(xc);
+ }
+
+ /**
+ * moves the given row one row down.
+ * @param guiRow the gui index of the row to move.
+ * @param control the control to gain focus after moving.
+ */
+ synchronized void rowDown(int guiRow, Object control ) {
+ // only perform if this is not the last row.
+ int actuallRow = guiRow + nscrollvalue;
+ if ( actuallRow + 1 < scrollfields.size() ) {
+ // get the current selection
+ Selection selection = getSelection(control );
+
+ // the last row should scroll...
+ boolean scroll = guiRow == ( nblockincrement - 1 );
+ if (scroll)
+ setScrollValue( nscrollvalue + 1 );
+ int scroll1 = nscrollvalue;
+
+ switchRows( guiRow , guiRow + (scroll ? -1 : 1));
+
+ if (nscrollvalue != scroll1)
+ guiRow += (nscrollvalue - scroll1);
+
+ setSelection(guiRow + (scroll ? 0 : 1 ) , control , selection);
+ }
+ }
+
+ synchronized void rowUp(int guiRow, Object control ) {
+ // only perform if this is not the first row
+ int actuallRow = guiRow + nscrollvalue;
+ if ( actuallRow > 0 ) {
+ // get the current selection
+ Selection selection = getSelection( control );
+
+ // the last row should scroll...
+ boolean scroll = (guiRow == 0);
+ if (scroll)
+ setScrollValue( nscrollvalue - 1 );
+
+ switchRows( guiRow , guiRow + (scroll ? 1 : -1));
+
+ setSelection(guiRow - (scroll ? 0 : 1 ) ,control , selection);
+ }
+ }
+
+ /**
+ * moves the cursor up.
+ * @param guiRow
+ * @param control
+ */
+ synchronized void cursorUp(int guiRow, Object control ) {
+ // is this the last full row ?
+ int actuallRow = guiRow + nscrollvalue;
+ //if this is the first row
+ if ( actuallRow == 0 )
+ return;
+ // the first row should scroll...
+ boolean scroll = (guiRow == 0);
+ ControlRow upperRow;
+ if (scroll) {
+ setScrollValue( nscrollvalue - 1 );
+ upperRow = (ControlRow) ControlGroupVector.get( guiRow );
+ }
+ else {
+ upperRow = (ControlRow) ControlGroupVector.get( guiRow - 1 );
+ }
+ focus(getControl(upperRow, control));
+
+ }
+
+ /**
+ * moves the cursor down
+ * @param guiRow
+ * @param control
+ */
+ synchronized void cursorDown(int guiRow, Object control ) {
+ // is this the last full row ?
+ int actuallRow = guiRow + nscrollvalue;
+ //if this is the last row, exit
+ if (actuallRow == scrollfields.size() - 1)
+ return;
+ // the first row should scroll...
+ boolean scroll = (guiRow == nblockincrement - 1);
+ ControlRow lowerRow;
+ if (scroll) {
+ setScrollValue( nscrollvalue + 1 );
+ lowerRow = (ControlRow) ControlGroupVector.get( guiRow );
+ }
+ // if we scrolled we are done...
+ //otherwise...
+ else {
+ lowerRow = (ControlRow) ControlGroupVector.get( guiRow + 1 );
+ }
+ focus(getControl(lowerRow, control));
+ }
+
+
+
+ /**
+ * changes the values of the given rows with eachother
+ * @param row1 one can figure out what this parameter is...
+ * @param row2 one can figure out what this parameter is...
+ */
+ private void switchRows( int row1, int row2) {
+ PropertyValue[] o1 = (PropertyValue[]) scrollfields.get(row1 + nscrollvalue);
+ PropertyValue[] o2 = (PropertyValue[]) scrollfields.get(row2 + nscrollvalue);
+
+ Object temp = null;
+ for (int i = 1; i < o1.length; i++) {
+ temp = o1[i].Value;
+ o1[i].Value = o2[i].Value;
+ o2[i].Value = temp;
+ }
+
+ fillupControls(row1);
+ fillupControls(row2);
+
+ updateDocumentRow( row1 + nscrollvalue, o1 );
+ updateDocumentRow( row2 + nscrollvalue, o2 );
+
+ /*
+ * if we changed the last row, add another one...
+ */
+ if ( ( row1 + nscrollvalue + 1 == scrollfields.size() ) ||
+ ( row2 + nscrollvalue + 1 == scrollfields.size() )
+ )
+ insertRowAtEnd();
+ /*
+ * if we did not change the last row but
+ * we did change the one before - check if we
+ * have two empty rows at the end.
+ * If so, delete the last one...
+ */
+ else if ( (row1 + nscrollvalue) + (row2 + nscrollvalue) == ( scrollfields.size() * 2 - 5 ) )
+ if ( isRowEmpty( scrollfields.size() -2) && isRowEmpty( scrollfields.size() -1) ) {
+ removeLastRow();
+ reduceDocumentToTopics();
+ }
+
+ }
+
+ /**
+ * returns the current Selection of a text field
+ * @param control a text field from which the Selection object
+ * should be gotten.
+ * @return the selection object.
+ */
+ private Selection getSelection(Object control) {
+ return ((XTextComponent)UnoRuntime.queryInterface(XTextComponent.class,control)).getSelection();
+ }
+
+ /**
+ * sets a text selection to a given control.
+ * This is used when one moves a row up or down.
+ * After moving row X to X+/-1, the selection (or cursor position) of the
+ * last focused control should be restored.
+ * The control's row is the given guiRow.
+ * The control's column is detecte4d according to the given event.
+ * This method is called as subsequent to different events,
+ * thus it is comfortable to use the event here to detect the column,
+ * rather than in the different event methods.
+ * @param guiRow the row of the control to set the selection to.
+ * @param eventSource helps to detect the control's column to set the selection to.
+ * @param s the selection object to set.
+ */
+ private void setSelection(int guiRow, Object eventSource, Selection s) {
+ ControlRow cr = (ControlRow)ControlGroupVector.get(guiRow);
+ Object control = getControl(cr, eventSource);
+ ((XWindow)UnoRuntime.queryInterface(XWindow.class,control)).setFocus();
+ ((XTextComponent)UnoRuntime.queryInterface(XTextComponent.class,control)).setSelection(s);
+ }
+
+
+
+ /**
+ * returns a control out of the given row, according to a column number.
+ * @param cr control row object.
+ * @param column the column number.
+ * @return the control...
+ */
+ private Object getControl( ControlRow cr, int column) {
+ switch ( column ) {
+ case 0 : return cr.label;
+ case 1 : return cr.textbox;
+ case 2 : return cr.combobox;
+ case 3 : return cr.timebox;
+ default: throw new IllegalArgumentException("No such column");
+ }
+ }
+
+
+ /**
+ * returns a control out of the given row, which is
+ * in the same column as the given control.
+ * @param cr control row object
+ * @param control a control indicating a column.
+ * @return
+ */
+ private Object getControl(ControlRow cr, Object control) {
+ int column = getColumn(control);
+ return getControl(cr,column);
+ }
+
+ /**
+ * returns the column number of the given control.
+ * @param control
+ * @return
+ */
+ private int getColumn(Object control) {
+ String name = (String)Helper.getUnoPropertyValue(UnoDialog2.getModel(control),"Name");
+ if (name.startsWith( TOPIC ))
+ return 1;
+ if (name.startsWith( RESP ))
+ return 2;
+ if (name.startsWith( TIME ))
+ return 3;
+ if (name.startsWith( LABEL ))
+ return 0;
+ return -1;
+ }
+
+
+ /**
+ * updates the given row in the preview document.
+ * @param row
+ */
+ private void updateDocumentRow( int row ) {
+ updateDocumentRow( row , (PropertyValue[])scrollfields.get(row));
+ }
+
+ /**
+ * update the given row in the preview document with the given data.
+ * @param row
+ * @param data
+ */
+ private void updateDocumentRow( int row , PropertyValue[] data) {
+ try {
+ ((AgendaWizardDialogImpl)CurUnoDialog).agendaTemplate.topics.write(row, data);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * updates a single cell in the preview document.
+ * Is called when a single value is changed, since we really
+ * don't have to update the whole row for one small changhe...
+ * @param row the data row to update (topic number).
+ * @param column the column to update (a gui column, not a document column).
+ * @param data the data of the entire row.
+ */
+ private void updateDocumentCell( int row , int column, PropertyValue[] data) {
+ try {
+ ((AgendaWizardDialogImpl)CurUnoDialog).agendaTemplate.topics.writeCell(row, column, data);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * when removeing rows, this method updates
+ * the preview document to show the number of rows
+ * according to the data model.
+ */
+ private void reduceDocumentToTopics() {
+ try {
+ ((AgendaWizardDialogImpl)CurUnoDialog).agendaTemplate.topics.reduceDocumentTo(scrollfields.size()-1);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * needed to make this data poblic.
+ * @return the List containing the topics data.
+ */
+ public List getTopicsData() {
+ return scrollfields;
+ }
+
+ /**
+ * A static member used for the child-class ControlRow (GUI Constant)
+ */
+ private static Integer I_12 = new Integer(12);
+ /**
+ * A static member used for the child-class ControlRow (GUI Constant)
+ */
+ private static Integer I_8 = new Integer(8);
+ /**
+ * A static member used for the child-class ControlRow (GUI Constant)
+ */
+ private static final String[] LABEL_PROPS = new String[] {"Height", "Label", "PositionX", "PositionY", "Step", "TabIndex", "Width"};
+ /**
+ * A static member used for the child-class ControlRow (GUI Constant)
+ */
+ private static final String[] TEXT_PROPS = new String[] {"Height", "HelpURL", "PositionX", "PositionY", "Step", "TabIndex", "Width"};
+
+ /**
+ *
+ * @author rp143992
+ * A class represting a single GUI row.
+ * Note that the instance methods of this class
+ * are being called and handle controls of
+ * a single row.
+ */
+ public class ControlRow implements XKeyListener {
+ /**
+ * the number (label) control
+ */
+ Object label;
+ /**
+ * the topic (text) control
+ */
+ Object textbox;
+ /**
+ * the responsible (text, yes, text) control
+ */
+ Object combobox;
+ /**
+ * the time (text, yes, text) control
+ */
+ Object timebox;
+
+ /**
+ * the row offset of this instance (0 = first gui row)
+ */
+ int offset;
+
+ /**
+ * called through an event listener when the
+ * topic text is changed by the user.
+ * updates the data model and the preview document.
+ */
+ public void topicTextChanged() {
+ try {
+ // update the data model
+ fieldInfo(offset,1);
+ // update the preview document
+ fieldChanged(offset,1);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+
+ /**
+ * called through an event listener when the
+ * responsible text is changed by the user.
+ * updates the data model and the preview document.
+ */
+ public void responsibleTextChanged() {
+ try {
+ // update the data model
+ fieldInfo(offset,2);
+ // update the preview document
+ fieldChanged(offset,2);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+
+ /**
+ * called through an event listener when the
+ * time text is changed by the user.
+ * updates the data model and the preview document.
+ */
+ public void timeTextChanged() {
+ try {
+ // update the data model
+ fieldInfo(offset,3);
+ // update the preview document
+ fieldChanged(offset,3);
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * constructor. Create the row in the given dialog given cordinates,
+ * with the given offset (row number) and tabindex.
+ * Note that since I use this specifically for the agenda wizard,
+ * the step and all control coordinates inside the
+ * row are constant (5).
+ * @param dialog the agenda dialog
+ * @param x x coordinates
+ * @param y y coordinates
+ * @param i the gui row index
+ * @param tabindex first tab index for this row.
+ */
+ public ControlRow(AgendaWizardDialog dialog, int x, int y, int i, int tabindex) {
+
+ offset = i;
+
+ Integer y_ = new Integer(y);
+
+ label = dialog.insertLabel( LABEL + i,
+ LABEL_PROPS,
+ new Object[] {I_8, "" + (i + 1) + "." , new Integer(x+4), new Integer(y+2), IStep, new Short((short)tabindex), new Integer(10)}
+ );
+
+ textbox = dialog.insertTextField( TOPIC + i, "topicTextChanged" , this,
+ TEXT_PROPS,
+ new Object[] { I_12, "HID:" + (curHelpIndex + i * 3 + 1) , new Integer(x+15),y_,IStep ,new Short((short)(tabindex + 1)), new Integer(84)}
+ );
+
+ combobox = dialog.insertTextField( RESP + i, "responsibleTextChanged",this,
+ TEXT_PROPS,
+ new Object[] { I_12, "HID:" + (curHelpIndex + i * 3 + 2) , new Integer(x + 103),y_,IStep, new Short((short)(tabindex+ 2)), new Integer(68)}
+ );
+
+ timebox = dialog.insertTextField( TIME + i , "timeTextChanged", this,
+ TEXT_PROPS,
+ new Object[] { I_12, "HID:" + (curHelpIndex + i * 3 + 3), new Integer(x + 175),y_, IStep , new Short((short)(tabindex + 3)), new Integer(20)}
+ );
+
+ setEnabled(false);
+ addKeyListener(textbox, this);
+ addKeyListener(combobox, this);
+ addKeyListener(timebox, this);
+
+ addFocusListener(textbox, TopicsControl.this);
+ addFocusListener(combobox, TopicsControl.this);
+ addFocusListener(timebox, TopicsControl.this);
+
+ }
+
+ /**
+ * not implemented.
+ * @param visible
+ */
+ public void setVisible(boolean visible) {
+ // Helper.setUnoPropertyValue(UnoDialog2.getModel(button),"Visible", visible ? Boolean.TRUE : Boolean.FASLE);
+ }
+
+ /**
+ * enables/disables the row.
+ * @param enabled true for enable, false for disable.
+ */
+ public void setEnabled(boolean enabled) {
+ Boolean b = enabled ? Boolean.TRUE : Boolean.FALSE;
+ UnoDialog2.setEnabled(label,b);
+ UnoDialog2.setEnabled(textbox,b);
+ UnoDialog2.setEnabled(combobox,b);
+ UnoDialog2.setEnabled(timebox,b);
+ }
+
+
+ /**
+ * Impelementation of XKeyListener.
+ * Optionally performs the one of the following:
+ * cursor up, or down, row up or down
+ */
+ public void keyPressed(KeyEvent event) {
+ if (isMoveDown(event)) {
+ rowDown(offset , event.Source);
+ }
+ else if (isMoveUp(event)) {
+ rowUp(offset, event.Source);
+ }
+ else if (isDown(event))
+ cursorDown(offset, event.Source);
+ else if (isUp(event))
+ cursorUp(offset, event.Source);
+
+ enableButtons();
+ }
+
+ /**
+ * returns the column number of the given control.
+ * The given control must belong to this row or
+ * an IllegalArgumentException will accure.
+ * @param control
+ * @return an int columnd number of the given control (0 to 3).
+ */
+ private int getColumn(Object control) {
+ if ( control == textbox )
+ return 1;
+ else if ( control == combobox )
+ return 2;
+ else if ( control == timebox )
+ return 3;
+ else if ( control == label )
+ return 0;
+ else
+ throw new IllegalArgumentException("Control is not part of this ControlRow");
+ }
+
+ private boolean isMoveDown(KeyEvent e) {
+ return ( e.KeyCode == Key.DOWN ) && ( e.Modifiers == KeyModifier.MOD1 );
+ }
+ private boolean isMoveUp(KeyEvent e) {
+ return ( e.KeyCode == Key.UP ) && ( e.Modifiers == KeyModifier.MOD1 );
+ }
+ private boolean isDown(KeyEvent e) {
+ return ( e.KeyCode == Key.DOWN ) && ( e.Modifiers == 0 );
+ }
+ private boolean isUp(KeyEvent e) {
+ return ( e.KeyCode == Key.UP ) && ( e.Modifiers == 0 );
+ }
+
+ public void keyReleased(KeyEvent arg0) {
+ }
+
+
+ /* (non-Javadoc)
+ * @see com.sun.star.lang.XEventListener#disposing(com.sun.star.lang.EventObject)
+ */
+ public void disposing(EventObject arg0) {
+ }
+
+ }
+
+}