diff options
author | Oliver Bolte <obo@openoffice.org> | 2004-09-08 13:00:25 +0000 |
---|---|---|
committer | Oliver Bolte <obo@openoffice.org> | 2004-09-08 13:00:25 +0000 |
commit | f8809d59acf4b1b28aa1042f8bd4839198a93335 (patch) | |
tree | daa9d3244bfb23c34e73b5aa8728e3783f9e4c2f /wizards | |
parent | 96660c2e9945d12dba18969eca0012ab73c8a141 (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.java | 1187 |
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) { + } + + } + +} |