blob: da89806d91b40b0d1b480747edb04dee656b732d [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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 = 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 = 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", "downPropertyNames.SPACEinsert" 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 = 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 = PropertyNames.EMPTY_STRING;
pv1[2].Value = PropertyNames.EMPTY_STRING;
pv1[3].Value = PropertyNames.EMPTY_STRING;
// 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, PropertyNames.EMPTY_STRING + (i + 1) + ".");
pv[1] = Properties.createProperty(TOPIC + i, PropertyNames.EMPTY_STRING);
pv[2] = Properties.createProperty(RESP + i, PropertyNames.EMPTY_STRING);
pv[3] = Properties.createProperty(TIME + i, PropertyNames.EMPTY_STRING);
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(PropertyNames.EMPTY_STRING) &&
data[2].Value.equals(PropertyNames.EMPTY_STRING) &&
data[3].Value.equals(PropertyNames.EMPTY_STRING);
}
/**
* 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)
{
UnoRuntime.queryInterface(XWindow.class, textControl).setFocus();
XTextComponent xTextComponent = UnoRuntime.queryInterface(XTextComponent.class, textControl);
String text = xTextComponent.getText();
xTextComponent.setSelection(new Selection(0, text.length()));
XControl xc = 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 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);
UnoRuntime.queryInterface(XWindow.class, control).setFocus();
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, PropertyNames.EMPTY_STRING + (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)
{
}
}
}