summaryrefslogtreecommitdiff
path: root/wizards/com/sun/star/wizards/agenda/TopicsControl.java
diff options
context:
space:
mode:
Diffstat (limited to 'wizards/com/sun/star/wizards/agenda/TopicsControl.java')
-rw-r--r--wizards/com/sun/star/wizards/agenda/TopicsControl.java1320
1 files changed, 1320 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..eb13e2f8b65c
--- /dev/null
+++ b/wizards/com/sun/star/wizards/agenda/TopicsControl.java
@@ -0,0 +1,1320 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org 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 version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+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.getGuiEventListener().add(TIME + (nblockincrement - 1), EventNames.EVENT_KEY_PRESSED, mi);
+
+ addKeyListener(lastTime, (XKeyListener) dialog.getGuiEventListener());
+
+ //prepare scroll up on tab press...
+ firstTopic = ((ControlRow) ControlGroupVector.get(0)).textbox;
+
+ mi = new MethodInvocation("firstControlKeyPressed", this, KeyEvent.class);
+ dialog.getGuiEventListener().add(TOPIC + 0, EventNames.EVENT_KEY_PRESSED, mi);
+
+ addKeyListener(firstTopic, (XKeyListener) dialog.getGuiEventListener());
+
+ }
+ 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.
+ * @param guiRow
+ */
+ 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.
+ * @param fe
+ */
+ 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.
+ * @param fe
+ */
+ public void focusLost(FocusEvent fe)
+ {
+ }
+
+ /**
+ * compolsary implementation of FocusListener.
+ * @param o
+ */
+ 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).
+ * @param _index
+ * @param npos
+ * @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.
+ * @param _index
+ * @param _bIsVisible
+ * @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);
+ focus(getControl((ControlRow) ControlGroupVector.get(4), 1));
+
+ }
+ }
+ }
+
+ /**
+ * 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)
+ {
+ }
+ }
+}