diff options
Diffstat (limited to 'wizards/com/sun/star/wizards/agenda/TopicsControl.java')
-rw-r--r-- | wizards/com/sun/star/wizards/agenda/TopicsControl.java | 1322 |
1 files changed, 1322 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..25f0cf9103a0 --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/TopicsControl.java @@ -0,0 +1,1322 @@ +/************************************************************************* + * + * 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.wizards.common.HelpIds; + +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.common.PropertyNames; +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), PropertyNames.PROPERTY_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), PropertyNames.PROPERTY_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 = 12; + /** + * A static member used for the child-class ControlRow (GUI Constant) + */ + private static Integer I_8 = 8; + /** + * A static member used for the child-class ControlRow (GUI Constant) + */ + private static final String[] LABEL_PROPS = new String[] + { + PropertyNames.PROPERTY_HEIGHT, PropertyNames.PROPERTY_LABEL, PropertyNames.PROPERTY_POSITION_X, PropertyNames.PROPERTY_POSITION_Y, PropertyNames.PROPERTY_STEP, PropertyNames.PROPERTY_TABINDEX, PropertyNames.PROPERTY_WIDTH + }; + /** + * A static member used for the child-class ControlRow (GUI Constant) + */ + private static final String[] TEXT_PROPS = new String[] + { + PropertyNames.PROPERTY_HEIGHT, PropertyNames.PROPERTY_HELPURL, PropertyNames.PROPERTY_POSITION_X, PropertyNames.PROPERTY_POSITION_Y, PropertyNames.PROPERTY_STEP, PropertyNames.PROPERTY_TABINDEX, PropertyNames.PROPERTY_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), 10 + }); + + textbox = dialog.insertTextField(TOPIC + i, "topicTextChanged", this, + TEXT_PROPS, + new Object[] + { + I_12, HelpIds.getHelpIdString(curHelpIndex + i * 3 + 1), new Integer(x + 15), y_, IStep, new Short((short) (tabindex + 1)), 84 + }); + + combobox = dialog.insertTextField(RESP + i, "responsibleTextChanged", this, + TEXT_PROPS, + new Object[] + { + I_12, HelpIds.getHelpIdString(curHelpIndex + i * 3 + 2), new Integer(x + 103), y_, IStep, new Short((short) (tabindex + 2)), 68 + }); + + timebox = dialog.insertTextField(TIME + i, "timeTextChanged", this, + TEXT_PROPS, + new Object[] + { + I_12, HelpIds.getHelpIdString(curHelpIndex + i * 3 + 3), new Integer(x + 175), y_, IStep, new Short((short) (tabindex + 3)), 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) + { + } + } +} |