diff options
Diffstat (limited to 'wizards/com/sun/star/wizards/agenda/AgendaTemplate.java')
-rw-r--r-- | wizards/com/sun/star/wizards/agenda/AgendaTemplate.java | 1962 |
1 files changed, 1962 insertions, 0 deletions
diff --git a/wizards/com/sun/star/wizards/agenda/AgendaTemplate.java b/wizards/com/sun/star/wizards/agenda/AgendaTemplate.java new file mode 100644 index 000000000000..2d7174bdb69f --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/AgendaTemplate.java @@ -0,0 +1,1962 @@ +/************************************************************************* + * + * 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.Calendar; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Vector; + + +import com.sun.star.awt.TextEvent; +import com.sun.star.beans.PropertyValue; +import com.sun.star.container.NoSuchElementException; +import com.sun.star.container.XIndexAccess; +import com.sun.star.container.XNamed; +import com.sun.star.document.XDocumentProperties; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XTerminateListener; +import com.sun.star.i18n.NumberFormatIndex; +import com.sun.star.lang.Locale; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.table.XCell; +import com.sun.star.table.XTableRows; +import com.sun.star.text.*; +import com.sun.star.uno.Any; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.XNumberFormatsSupplier; +import com.sun.star.util.XNumberFormatter; +import com.sun.star.util.XSearchDescriptor; +import com.sun.star.util.XSearchable; +import com.sun.star.wizards.common.FileAccess; +import com.sun.star.wizards.common.Helper; +import com.sun.star.wizards.common.JavaTools; +import com.sun.star.wizards.common.NumberFormatter; +import com.sun.star.wizards.common.PropertyNames; +import com.sun.star.wizards.document.OfficeDocument; +import com.sun.star.wizards.text.TextDocument; +import com.sun.star.wizards.text.TextSectionHandler; +import com.sun.star.wizards.ui.UnoDialog2; +import com.sun.star.wizards.ui.event.DataAware; + +/** + * + * The classes here implement the whole document-functionality of the agenda wizard: + * the live-preview and the final "creation" of the document, when the user clicks "finish". <br/> + * <br/> + * <h2>Some terminology:<h2/> + * items are names or headings. we don't make any distinction. + * + * <br/> + * The Agenda Template is used as general "controller" of the whole document, whereas the + * two child-classes ItemsTable and TopicsTable control the item tables (note plural!) and the + * topics table (note singular). + * <br/> <br/> + * Other small classes are used to abstract the handling of cells and text and we + * try to use them as components. + * <br/><br/> + * We tried to keep the Agenda Template as flexible as possible, though there + * must be many limitations, because it is generated dynamically.<br/><br/> + * To keep the template flexible the following decisions were made:<br/> + * 1. Item tables.<br/> + * 1.a. there might be arbitrary number of Item tables.<br/> + * 1.b. Item tables design (bordewr, background) is arbitrary.<br/> + * 1.c. Items text styles are individual, and use stylelist styles with predefined names.<br/> + * As result the following limitations:<br/> + * Pairs of Name->value for each item.<br/> + * Tables contain *only* those pairs.<br/> + * 2. Topics table.<br/> + * 2.a. arbitrary structure.<br/> + * 2.b. design is arbitrary.<br/> + * As result the following limitations:<br/> + * No column merge is allowed.<br/> + * One compolsary Heading row.<br/> + * <br/><br/> + * To let the template be flexible, we use a kind of "detection": we look where + * the items are read the design of each table, reaplying it after writing the + * table. + * <br/><br/> + * A note about threads:<br/> + * Many methods here are synchronized, in order to avoid colission made by + * events fired too often. + * @author rpiterman + * + */ +public class AgendaTemplate extends TextDocument implements TemplateConsts, DataAware.Listener +{ + + /** + * resources. + */ + AgendaWizardDialogResources resources; + /** + * data model. This keeps the status of the agenda document, and + * every redraw is done according to this data. + * Exception: topic data is written programatically, event-oriented. + */ + CGAgenda agenda; + /** + * the UNO Text Document serrvice + */ + Object document; + /** + * Service Factory + */ + XMultiServiceFactory docMSF; + /** + * The template-filename of the current template. + * Since we often re-link section and the break the link, + * inorder to restore them, we need a template to link to. + * This is practically an identicall copy of the current template. + */ + String template; + /** + * used for common operations on sections. + */ + TextSectionHandler textSectionHandler; + /** + * a component loader. + */ + XComponentLoader xComponentLoader; + /** + * an array containing all ItemTable object (which control each an Items + * Table in the document. + */ + ItemsTable[] itemsTables; + /** + * the controller of the topics table. + */ + Topics topics; + /** + * Stores reusable OOo Placeholder TextFields to insert to the document. + */ + Map itemsCache; + /** + * This map is used to find which tables contains a certain Item, so + * the keys are the different Items, the Objects are the ItemTable controllers. + * When an Item must be redrawn (because the user checked or uncheced it), + * the controller is retrieved from this Map, and a redraw is issued on this controller. + */ + Map itemsMap = new Hashtable(11); + /** + * A temporary variable used to list all items and map them. + */ + List _allItems = new Vector(); + /** + * keep a reference on some static items in the document, + * so when their content is changed (through the user), we + * can just reference them and set their text. + */ + TextElement teTitle, teDate, teTime, teLocation; + XTextRange trTitle, trDate, trTime, trLocation; + /** + * used to format the date / time. + */ + int dateFormat, timeFormat; + XNumberFormatter dateFormatter, timeFormatter; + /** + * used to transfare time from VCL to UNO. + */ + long docNullTime; + Calendar calendar; + /** + * used to set the document title property (step 6). + */ + private XDocumentProperties m_xDocProps; + + /** + * loads the given template, and analyze its structure. + * @param templateURL + * @param topics + * @see AgendaTemplate.initialize() + * @see AgendaTemplate.initializeData() + */ + public synchronized void load(String templateURL, List topics) + { + template = calcTemplateName(templateURL); + document = loadAsPreview(templateURL, false); + docMSF = ((XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, document)); + xFrame.getComponentWindow().setEnable(false); + xTextDocument.lockControllers(); + initialize(); + initializeData(topics); + xTextDocument.unlockControllers(); + } + + /** + * The agenda templates are in format of aw-XXX.ott + * the templates name is then XXX.ott. + * This method calculates it. + * @param url + * @return the template name without the "aw-" at the beginning. + */ + private String calcTemplateName(String url) + { + return FileAccess.connectURLs(FileAccess.getParentDir(url), FileAccess.getFilename(url).substring(3)); + } + + /** + * synchronize the document to the model.<br/> + * this method rewrites all titles, item tables , and the topics table- + * thus synchronizing the document to the data model (CGAgenda). + * @param topicsData since the model does not contain Topics + * information (it is only actualized on save) the given list + * supplies this information. + */ + private void initializeData(List topicsData) + { + for (int i = 0; i < itemsTables.length; i++) + { + try + { + itemsTables[i].write(""); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + redrawTitle("txtTitle"); + redrawTitle("txtDate"); + redrawTitle("txtTime"); + redrawTitle("cbLocation"); + + topics.writeAll(topicsData); + + setTemplateTitle(agenda.cp_TemplateName); + + } + + /** + * redraws/rewrites the table which contains the given item + * This method is called when the user checks/unchecks an item. + * The table is being found, in which the item is, and redrawn. + * @param itemName + */ + public synchronized void redraw(String itemName) + { + try + { + // get the table in which the item is... + Object itemsTable = + itemsMap.get(itemName); + // rewrite the table. + ((ItemsTable) itemsTable).write(null); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * update the documents title property to the given title + * @param newTitle new title. + */ + synchronized void setTemplateTitle(String newTitle) + { + m_xDocProps.setTitle(newTitle); + } + + /** + * constructor. The document is *not* loaded here. + * only some formal members are set. + * @param xmsf_ service factory. + * @param agenda_ the data model (CGAgenda) + * @param resources_ resources. + */ + AgendaTemplate(XMultiServiceFactory xmsf_, CGAgenda agenda_, AgendaWizardDialogResources resources_, XTerminateListener listener) + { + super(xmsf_, listener, "WIZARD_LIVE_PREVIEW"); + + agenda = agenda_; + resources = resources_; + + if (itemsCache == null) + { + initItemsCache(); + } + _allItems = null; + + } + + /** + * checks the data model if the + * item corresponding to the given string should be shown + * @param itemName a string representing an Item (name or heading). + * @return true if the model specifies that the item should be displayed. + */ + boolean isShowItem(String itemName) + { + if (itemName.equals(FILLIN_MEETING_TYPE)) + { + return agenda.cp_ShowMeetingType; + } + else if (itemName.equals(FILLIN_READ)) + { + return agenda.cp_ShowRead; + } + else if (itemName.equals(FILLIN_BRING)) + { + return agenda.cp_ShowBring; + } + else if (itemName.equals(FILLIN_NOTES)) + { + return agenda.cp_ShowNotes; + } + else if (itemName.equals(FILLIN_FACILITATOR)) + { + return agenda.cp_ShowFacilitator; + } + else if (itemName.equals(FILLIN_TIMEKEEPER)) + { + return agenda.cp_ShowTimekeeper; + } + else if (itemName.equals(FILLIN_NOTETAKER)) + { + return agenda.cp_ShowNotetaker; + } + else if (itemName.equals(FILLIN_PARTICIPANTS)) + { + return agenda.cp_ShowAttendees; + } + else if (itemName.equals(FILLIN_CALLED_BY)) + { + return agenda.cp_ShowCalledBy; + } + else if (itemName.equals(FILLIN_OBSERVERS)) + { + return agenda.cp_ShowObservers; + } + else if (itemName.equals(FILLIN_RESOURCE_PERSONS)) + { + return agenda.cp_ShowResourcePersons; + } + else + { + throw new IllegalArgumentException("No such item"); + } + } + + /** + * itemsCache is a Map containing all agenda item. These are object which + * "write themselfs" to the table, given a table cursor. + * A cache is used in order to reuse the objects, instead of recreate them. + * This method fills the cache will all items objects (names and headings). + */ + private void initItemsCache() + { + itemsCache = new Hashtable(11); + + XMultiServiceFactory xmsf = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, document); + // Headings + + itemsCache.put(FILLIN_MEETING_TYPE, + new AgendaItem(FILLIN_MEETING_TYPE, new TextElement(resources.itemMeetingType, STYLE_MEETING_TYPE), + new PlaceholderElement(STYLE_MEETING_TYPE_TEXT, resources.reschkMeetingTitle_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_BRING, + new AgendaItem(FILLIN_BRING, new TextElement(resources.itemBring, STYLE_BRING), + new PlaceholderElement(STYLE_BRING_TEXT, resources.reschkBring_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_READ, + new AgendaItem(FILLIN_READ, new TextElement(resources.itemRead, STYLE_READ), + new PlaceholderElement(STYLE_READ_TEXT, resources.reschkRead_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_NOTES, + new AgendaItem(FILLIN_NOTES, new TextElement(resources.itemNote, STYLE_NOTES), + new PlaceholderElement(STYLE_NOTES_TEXT, resources.reschkNotes_value, resources.resPlaceHolderHint, xmsf))); + + // Names + + itemsCache.put(FILLIN_CALLED_BY, + new AgendaItem(FILLIN_CALLED_BY, new TextElement(resources.itemCalledBy, STYLE_CALLED_BY), + new PlaceholderElement(STYLE_CALLED_BY_TEXT, resources.reschkConvenedBy_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_FACILITATOR, + new AgendaItem(FILLIN_FACILITATOR, new TextElement(resources.itemFacilitator, STYLE_FACILITATOR), + new PlaceholderElement(STYLE_FACILITATOR_TEXT, resources.reschkPresiding_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_PARTICIPANTS, + new AgendaItem(FILLIN_PARTICIPANTS, new TextElement(resources.itemAttendees, STYLE_PARTICIPANTS), + new PlaceholderElement(STYLE_PARTICIPANTS_TEXT, resources.reschkAttendees_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_NOTETAKER, + new AgendaItem(FILLIN_NOTETAKER, new TextElement(resources.itemNotetaker, STYLE_NOTETAKER), + new PlaceholderElement(STYLE_NOTETAKER_TEXT, resources.reschkNoteTaker_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_TIMEKEEPER, + new AgendaItem(FILLIN_TIMEKEEPER, new TextElement(resources.itemTimekeeper, STYLE_TIMEKEEPER), + new PlaceholderElement(STYLE_TIMEKEEPER_TEXT, resources.reschkTimekeeper_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_OBSERVERS, + new AgendaItem(FILLIN_OBSERVERS, new TextElement(resources.itemObservers, STYLE_OBSERVERS), + new PlaceholderElement(STYLE_OBSERVERS_TEXT, resources.reschkObservers_value, resources.resPlaceHolderHint, xmsf))); + + itemsCache.put(FILLIN_RESOURCE_PERSONS, + new AgendaItem(FILLIN_RESOURCE_PERSONS, new TextElement(resources.itemResource, STYLE_RESOURCE_PERSONS), + new PlaceholderElement(STYLE_RESOURCE_PERSONS_TEXT, resources.reschkResourcePersons_value, resources.resPlaceHolderHint, xmsf))); + + } + + /** + * Initializes a template.<br/> + * This method does the following tasks:<br/> + * Get a Time and Date format for the document, and retrieve the null date of the document (which is + * document-specific).<br/> + * Initializes the Items Cache map. + * Analyses the document:<br/> + * -find all "fille-ins" (apear as >xxx< in the document). + * -analyze all items sections (and the tables in them). + * -locate the titles and actualize them + * -analyze the topics table + */ + private void initialize() + { + /* + * Get the default locale of the document, and create the date and time formatters. + */ + XMultiServiceFactory docMSF = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, document); + try + { + Object defaults = docMSF.createInstance("com.sun.star.text.Defaults"); + Locale l = (Locale) Helper.getUnoStructValue(defaults, "CharLocale"); + + java.util.Locale jl = new java.util.Locale( + l.Language, l.Country, l.Variant); + + calendar = Calendar.getInstance(jl); + + XNumberFormatsSupplier nfs = (XNumberFormatsSupplier) UnoRuntime.queryInterface(XNumberFormatsSupplier.class, document); + Object formatSettings = nfs.getNumberFormatSettings(); + com.sun.star.util.Date date = (com.sun.star.util.Date) Helper.getUnoPropertyValue(formatSettings, "NullDate"); + + calendar.set(date.Year, date.Month - 1, date.Day); + + docNullTime = JavaTools.getTimeInMillis(calendar); + + dateFormat = NumberFormatter.getNumberFormatterKey(nfs, NumberFormatIndex.DATE_SYSTEM_LONG); + timeFormat = NumberFormatter.getNumberFormatterKey(nfs, NumberFormatIndex.TIME_HHMM); + + + dateFormatter = NumberFormatter.createNumberFormatter(xMSF, nfs); + timeFormatter = NumberFormatter.createNumberFormatter(xMSF, nfs); + } + catch (Exception ex) + { + ex.printStackTrace(); + throw new NullPointerException("Fatal Error: could not initialize locale or date/time formats."); + } + + /* + * get the document properties object. + */ + m_xDocProps = OfficeDocument.getDocumentProperties(document); + + initItemsCache(); + initializeItems(); + initializeTitles(); + initializeItemsSections(); + XMultiServiceFactory xMultiServiceFactory = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, document); + textSectionHandler = new TextSectionHandler(xMultiServiceFactory, (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, document)); + initializeTopics(); + _allItems.clear(); + _allItems = null; + } + + /** + * locates the titles (name, location, date, time) and saves a reference to thier Text ranges. + * + */ + private void initializeTitles() + { + XTextRange item = null; + + XMultiServiceFactory xmsf = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, document); + + for (int i = 0; i < _allItems.size(); i++) + { + item = (XTextRange) _allItems.get(i); + String text = item.getString().trim().toLowerCase(); + if (text.equals(FILLIN_TITLE)) + { + + teTitle = new PlaceholderTextElement(item, resources.resPlaceHolderTitle, resources.resPlaceHolderHint, xmsf); + trTitle = item; + _allItems.remove(i--); + } + else if (text.equals(FILLIN_DATE)) + { + teDate = new PlaceholderTextElement(item, resources.resPlaceHolderDate, resources.resPlaceHolderHint, xmsf); + trDate = item; + _allItems.remove(i--); + } + else if (text.equals(FILLIN_TIME)) + { + teTime = new PlaceholderTextElement(item, resources.resPlaceHolderTime, resources.resPlaceHolderHint, xmsf); + trTime = item; + _allItems.remove(i--); + } + else if (text.equals(FILLIN_LOCATION)) + { + teLocation = new PlaceholderTextElement(item, resources.resPlaceHolderLocation, resources.resPlaceHolderHint, xmsf); + trLocation = item; + _allItems.remove(i--); + } + } + } + + private void initializeTopics() + { + topics = new Topics(); + } + + private void initializeItems() + { + _allItems = searchFillInItems(); + } + + /** + * searches the document for items in the format ">*<" + * @return a vector containing the XTextRanges of the found items + */ + private List searchFillInItems() + { + try + { + XSearchable xSearchable = (XSearchable) UnoRuntime.queryInterface(XSearchable.class, document); + XSearchDescriptor sd = xSearchable.createSearchDescriptor(); + sd.setSearchString("<[^>]+>"); + sd.setPropertyValue("SearchRegularExpression", Boolean.TRUE); + sd.setPropertyValue("SearchWords", Boolean.TRUE); + + XIndexAccess ia = xSearchable.findAll(sd); + + List l = new Vector(ia.getCount()); + for (int i = 0; i < ia.getCount(); i++) + { + try + { + l.add((XTextRange) UnoRuntime.queryInterface(XTextRange.class, ia.getByIndex(i))); + } + catch (Exception ex) + { + System.err.println("Nonfatal Error in finding fillins."); + } + } + return l; + } + catch (Exception ex) + { + ex.printStackTrace(); + throw new IllegalArgumentException("Fatal Error: Loading template failed: searching fillins failed"); + } + } + + /** + * analyze the item sections in the template. delegates the analyze of each table to the + * ItemsTable class. + */ + private void initializeItemsSections() + { + String[] sections = getSections(document, TemplateConsts.SECTION_ITEMS); + + // for each section - there is a table... + itemsTables = new ItemsTable[sections.length]; + + for (int i = 0; i < itemsTables.length; i++) + { + try + { + itemsTables[i] = new ItemsTable(getSection(sections[i]), getTable(sections[i])); + } + catch (Exception ex) + { + ex.printStackTrace(); + throw new IllegalArgumentException("Fatal Error while initialilzing Template: items table in section " + sections[i]); + } + } + + } + + private String[] getSections(Object document, String s) + { + XTextSectionsSupplier xTextSectionsSupplier = (XTextSectionsSupplier) UnoRuntime.queryInterface(XTextSectionsSupplier.class, document); + String[] allSections = xTextSectionsSupplier.getTextSections().getElementNames(); + return getNamesWhichStartWith(allSections, s); + } + + Object getSection(String name) throws NoSuchElementException, WrappedTargetException + { + XTextSectionsSupplier xTextSectionsSupplier = (XTextSectionsSupplier) UnoRuntime.queryInterface(XTextSectionsSupplier.class, document); + return ((Any) (xTextSectionsSupplier.getTextSections().getByName(name))).getObject(); + } + + Object getTable(String name) throws NoSuchElementException, WrappedTargetException + { + XTextTablesSupplier xTextTablesSupplier = (XTextTablesSupplier) UnoRuntime.queryInterface(XTextTablesSupplier.class, document); + return ((Any) xTextTablesSupplier.getTextTables().getByName(name)).getObject(); + } + + /** + * implementation of DataAware.Listener, is + * called when title/date/time or location are + * changed. + */ + public synchronized void eventPerformed(Object param) + { + TextEvent te = (TextEvent) param; + String controlName = (String) Helper.getUnoPropertyValue( + UnoDialog2.getModel(te.Source), + PropertyNames.PROPERTY_NAME); + redrawTitle(controlName); + + } + + private synchronized void redrawTitle(String controlName) + { + if (controlName.equals("txtTitle")) + { + writeTitle(teTitle, trTitle, agenda.cp_Title); + } + else if (controlName.equals("txtDate")) + { + writeTitle(teDate, trDate, getDateString(agenda.cp_Date)); + } + else if (controlName.equals("txtTime")) + { + writeTitle(teTime, trTime, getTimeString(agenda.cp_Time)); + } + else if (controlName.equals("cbLocation")) + { + writeTitle(teLocation, trLocation, agenda.cp_Location); + } + else + { + throw new IllegalArgumentException("No such title control..."); + } + } + + private void writeTitle(TextElement te, XTextRange tr, String text) + { + te.text = (text == null ? "" : text); + te.write(tr); + } + private static long DAY_IN_MILLIS = (24 * 60 * 60 * 1000); + + private String getDateString(String d) + { + if (d == null || d.equals("")) + { + return ""; + } + int date = new Integer(d).intValue(); + calendar.clear(); + calendar.set(date / 10000, + (date % 10000) / 100 - 1, + date % 100); + + long date1 = JavaTools.getTimeInMillis(calendar); + /* + * docNullTime and date1 are in millis, but + * I need a day... + */ + double daysDiff = (date1 - docNullTime) / DAY_IN_MILLIS + 1; + + return dateFormatter.convertNumberToString(dateFormat, daysDiff); + } + + private String getTimeString(String s) + { + if (s == null || s.equals("")) + { + return ""; + } + int time = new Integer(s).intValue(); + + double t = ((double) (time / 1000000) / 24) + ((double) ((time % 1000000) / 1000) / (24 * 60)); + return timeFormatter.convertNumberToString(timeFormat, t); + } + + /* ******************************************* + * F I N I S H + *********************************************/ + /** the user clicked finish **/ + public synchronized void finish(List topics) + { + createMinutes(topics); + deleteHiddenSections(); + textSectionHandler.removeAllTextSections(); + } + + /** + * hidden sections exist when an item's section is hidden because the + * user specified not to display any items which it contains. + * When finishing the wizard removes this sections entireley from the document. + */ + private void deleteHiddenSections() + { + XTextSectionsSupplier xTextSectionsSupplier = (XTextSectionsSupplier) UnoRuntime.queryInterface(XTextSectionsSupplier.class, document); + String[] allSections = xTextSectionsSupplier.getTextSections().getElementNames(); + try + { + for (int i = 0; i < allSections.length; i++) + { + Object section = getSection(allSections[i]); + //Try3.showProps(section); + boolean visible = ((Boolean) Helper.getUnoPropertyValue(section, "IsVisible")).booleanValue(); + if (!visible) + { + ((XTextContent) UnoRuntime.queryInterface(XTextContent.class, section)).getAnchor().setString(""); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + /** + * create the minutes for the given topics or remove the minutes section from the document. + * If no topics are supplied, or the user + * specified not to create minuts, the minutes section will be removed, + * @param topicsData supplies PropertyValue arrays containing the values for the topics. + */ + public synchronized void createMinutes(List topicsData) + { + + // if the minutes section should be removed (the + // user did not check "create minutes") + if (!agenda.cp_IncludeMinutes || (topicsData.size() <= 1)) + { + try + { + Object minutesAllSection = getSection(SECTION_MINUTES_ALL); + XTextSection xTextSection = (XTextSection) UnoRuntime.queryInterface(XTextSection.class, minutesAllSection); + xTextSection.getAnchor().setString(""); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + // the user checked "create minutes" + else + { + try + { + String itemText; + XTextRange item; + int topicStartTime = 0; + try + { + topicStartTime = new Integer(agenda.cp_Time).intValue(); + } + catch (Exception ex) + { + } + + String time; + + // first I replace the minutes titles... + List items = searchFillInItems(); + for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) + { + item = (XTextRange) items.get(itemIndex); + itemText = item.getString().trim().toLowerCase(); + + if (itemText.equals(FILLIN_MINUTES_TITLE)) + { + fillMinutesItem(item, agenda.cp_Title, resources.resPlaceHolderTitle); + } + else if (itemText.equals(FILLIN_MINUTES_LOCATION)) + { + fillMinutesItem(item, agenda.cp_Location, resources.resPlaceHolderLocation); + } + else if (itemText.equals(FILLIN_MINUTES_DATE)) + { + fillMinutesItem(item, getDateString(agenda.cp_Date), resources.resPlaceHolderDate); + } + else if (itemText.equals(FILLIN_MINUTES_TIME)) + { + fillMinutesItem(item, getTimeString(agenda.cp_Time), resources.resPlaceHolderTime); + } + } + + items.clear(); + + /* + * now add minutes for each topic. + * The template contains *one* minutes section, so + * we first use the one available, and then add a new one... + * + * topics data has *always* an empty topic at the end... + */ + for (int i = 0; i < topicsData.size() - 1; i++) + { + PropertyValue[] topic = (PropertyValue[]) topicsData.get(i); + + items = searchFillInItems(); + for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) + { + item = (XTextRange) items.get(itemIndex); + itemText = item.getString().trim().toLowerCase(); + + if (itemText.equals(FILLIN_MINUTE_NUM)) + { + fillMinutesItem(item, topic[0].Value, ""); + } + else if (itemText.equals(FILLIN_MINUTE_TOPIC)) + { + fillMinutesItem(item, topic[1].Value, ""); + } + else if (itemText.equals(FILLIN_MINUTE_RESPONSIBLE)) + { + fillMinutesItem(item, topic[2].Value, ""); + } + else if (itemText.equals(FILLIN_MINUTE_TIME)) + { + int topicTime = 0; + + try + { + topicTime = (new Integer((String) topic[3].Value)).intValue(); + } + catch (Exception ex) + { + } + // if the topic has no time, we do not display any time here. + if (topicTime == 0 || topicStartTime == 0) + { + time = (String) topic[3].Value; + } + else + { + time = getTimeString(String.valueOf(topicStartTime)) + " - "; + topicStartTime += topicTime * 1000; + time += getTimeString(String.valueOf(topicStartTime)); + } + fillMinutesItem(item, time, ""); + } + } + + textSectionHandler.removeTextSectionbyName(SECTION_MINUTES); + + // after the last section we do not insert a new one. + if (i < topicsData.size() - 2) + { + textSectionHandler.insertTextSection(SECTION_MINUTES, template, false); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + } + + /** + * given a text range and a text, fills the given + * text range with the given text. + * If the given text is empty, uses a placeholder with the giveb placeholder text. + * @param range text range to fill + * @param text the text to fill to the text range object. + * @param placeholder the placeholder text to use, if the text argument is empty (null or "") + */ + private void fillMinutesItem(XTextRange range, Object text, String placeholder) + { + String paraStyle = (String) Helper.getUnoPropertyValue(range, "ParaStyleName"); + range.setString((String) text); + Helper.setUnoPropertyValue(range, "ParaStyleName", paraStyle); + if (text == null || text.equals("")) + { + if (placeholder != null && !placeholder.equals("")) + { + XTextContent placeHolder = createPlaceHolder(docMSF, placeholder, resources.resPlaceHolderHint); + try + { + range.getStart().getText().insertTextContent(range.getStart(), placeHolder, true); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + } + } + + /** + * creates a placeholder field with the given text and given hint. + * @param xmsf service factory + * @param ph place holder text + * @param hint hint text + * @return the place holder field. + */ + public static XTextContent createPlaceHolder(XMultiServiceFactory xmsf, String ph, String hint) + { + Object placeHolder; + try + { + placeHolder = xmsf.createInstance("com.sun.star.text.TextField.JumpEdit"); + } + catch (Exception ex) + { + ex.printStackTrace(); + return null; + } + Helper.setUnoPropertyValue(placeHolder, "PlaceHolder", ph); + Helper.setUnoPropertyValue(placeHolder, "Hint", hint); + Helper.setUnoPropertyValue(placeHolder, "PlaceHolderType", new Short(PlaceholderType.TEXT)); + return (XTextContent) UnoRuntime.queryInterface(XTextContent.class, placeHolder); + + } + + /* + * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + * ================================= + * The ItemTable class + * ================================= + * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + */ + public class ItemsTable + { + + Object table; + Object section; + /** + * the items in the table. + */ + List items = new Vector(6); + + public ItemsTable(Object section_, Object table_) + { + + table = table_; + section = section_; + + AgendaItem ai; + XTextRange item; + String iText; + + /* go through all <*> items in the document + * and each one if it is in this table. + * If they are, register them to belong here, notice their order + * and remove them from the list of all <*> items, so the next + * search will be faster. + */ + for (int i = 0; i < _allItems.size(); i++) + { + item = (XTextRange) _allItems.get(i); + Object t = Helper.getUnoPropertyValue(item, "TextTable"); + if ((t instanceof Any) && ((Any) t).getObject() == table) + { + iText = item.getString().toLowerCase().trim(); + ai = (AgendaItem) itemsCache.get(item.getString().toLowerCase().trim()); + if (ai != null) + { + items.add(ai); + _allItems.remove(i--); + itemsMap.put(iText, this); + } + } + } + + } + + /** + * link the section to the template. this will restore the original table + * with all the items.<br/> + * then break the link, to make the section editable.<br/> + * then, starting at cell one, write all items that should be visible. + * then clear the rest and remove obsolete rows. + * If no items are visible, hide the section. + * @param dummy we need a param to make this an Implementation of AgendaElement. + * @throws Exception + */ + public synchronized void write(Object dummy) throws Exception + { + synchronized(this) + { + String name = getName(section); + + // link and unlink the section to the template. + textSectionHandler.linkSectiontoTemplate(section, template, name); + textSectionHandler.breakLinkOfTextSection(section); + + // we need to get a new instance after linking. + table = getTable(name); + section = getSection(name); + + XTextTable xTextTable = (XTextTable) UnoRuntime.queryInterface(XTextTable.class, table); + XTextTableCursor cursor = xTextTable.createCursorByCellName("A1"); + AgendaItem ai; + // should this section be visible? + boolean visible = false; + + // write items + // =========== + String cellName = ""; + + /* now go through all items that belong to this + * table. Check each one agains the model. If it should + * be display, call it's write method. + * All items are of type AgendaItem which means they write + * two cells to the table: a title (text) and a placeholder. + * see AgendaItem class below. + */ + for (int i = 0; i < items.size(); i++) + { + ai = (AgendaItem) items.get(i); + if (isShowItem(ai.name)) + { + visible = true; + ai.table = table; + ai.write(cursor); + // I store the cell name which was last written... + cellName = cursor.getRangeName(); + + cursor.goRight((short) 1, false); + + } + } + + Helper.setUnoPropertyValue(section, "IsVisible", visible ? Boolean.TRUE : Boolean.FALSE); + if (!visible) + { + return; + /* remove obsolete rows + * ==================== + * if the cell that was last written is the current cell, + * it means this is the end of the table, so we end here. + * (because after getting the cellName above, I call the goRight method. + * If it did not go right, it means its the last cell. + */ + } + if (cellName.equals(cursor.getRangeName())) + { + return; + /* + * if not, we continue and clear all cells until we are at the end of the row. + */ + } + Object cell; + while ((!cellName.equals(cursor.getRangeName()) && (!cursor.getRangeName().startsWith("A")))) + { + cell = xTextTable.getCellByName(cursor.getRangeName()); + ((XTextRange) UnoRuntime.queryInterface(XTextRange.class, cell)).setString(""); + cellName = cursor.getRangeName(); + cursor.goRight((short) 1, false); + } + + /* + * again: if we are at the end of the table, end here. + */ + if (cellName.equals(cursor.getRangeName())) + { + return; + } + int rowIndex = getRowIndex(cursor); + int rowsCount = getRowCount((XTextTable) UnoRuntime.queryInterface(XTextTable.class, table)); + + /* now before deleteing i move the cursor up so it + * does not disappear, because it will crash office. + */ + cursor.gotoStart(false); + + if (rowsCount >= rowIndex) + { + removeTableRows(table, rowIndex - 1, (rowsCount - rowIndex) + 1); + } + } + } + } + + /* + * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + * ================================= + * The Topics class + * ================================= + * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + */ + /** + * This class handles the preview of the topics table. + * You can call it the controller of the topics table. + * It differs from ItemsTable in that it has no data model - + * the update is done programttically.<br/> + * <br/> + * The decision to make this class a class by its own + * was done out of logic reasons and not design/functionality reasons, + * since there is anyway only one instance of this class at runtime + * it could have also be implemented in the AgendaTemplate class + * but for clarity and separation I decided to make a sub class for it. + * + * @author rp143992 + */ + public class Topics + { + + /** + * the topics table + */ + XTextTable table; + /** + * A List of Cell Formatters for the first row. + */ + List firstRowFormat = new Vector(); + /** + * A List of Cell Formatters for the last row. + * (will contain them in reverse order) + */ + List lastRowFormat = new Vector(); + /** + * the format of the cell of each topic cell. + */ + List topicCellFormats = new Vector(); + /** + * for each topic cell there is + * a member in this vector + */ + List topicCells = new Vector(); + int rowsPerTopic; + /** + * fields which hold the number of the + * fillins in the cells vectors. + */ + int numCell = -1; + int topicCell = -1; + int responsibleCell = -1; + int timeCell = -1; + /** + * this is a list which traces which topics were written to the document + * and which not. When a cell needs to be actualized, it is checked that the + * whole topic is already present in the document, using this vector. + * The vector contains nulls for topics which were not written, and + * empty strings for topics which were written (though any other + * object would also do - i check only if it is a null or not...); + */ + List writtenTopics = new Vector(); + + /** + * Analyze the structure of the Topics table. + * The structure Must be as follows:<br> + * -One Header Row. <br> + * -arbitrary number of rows per topic <br> + * -arbitrary content in the topics row <br> + * -only soft formatting will be restored. <br> + * -the topic rows must repeat three times. <br> + * -in the topics rows, placeholders for number, topic, responsible, and duration + * must be placed.<br> + * <br> + * A word about table format: to reconstruct the format of the + * table we hold to the following formats: first row (header), topic, and last row. + * We hold the format of the last row, because one might wish to give it + * a special format, other than the one on the bottom of each topic. + * The left and right borders of the whole table are, on the other side, + * part of the topics rows format, and need not be preserved seperateley. + */ + public Topics() + { + Object t; + + Map topicItems = new Hashtable(4); + + // This is the topics table. say hallo :-) + try + { + t = getTable(SECTION_TOPICS); + } + catch (Exception ex) + { + ex.printStackTrace(); + throw new IllegalArgumentException("Fatal error while loading template: table " + SECTION_TOPICS + " could not load."); + } + + // and this is the XTable. + table = ((XTextTable) UnoRuntime.queryInterface(XTextTable.class, t)); + + /* first I store all <*> ranges + * which are in the topics table. + * I store each <*> range in this - the key + * is the cell it is in. Later when analyzing the topic, + * cell by cell, I check in this map to know + * if a cell contains a <*> or not. + */ + Hashtable items = new Hashtable(); + + XTextRange item; + Object cell; + for (int i = 0; i < _allItems.size(); i++) + { + item = (XTextRange) _allItems.get(i); + t = Helper.getUnoPropertyValue(item, "TextTable"); + if ((t instanceof Any) && ((Any) t).getObject() == table) + { + cell = Helper.getUnoPropertyValue(item, "Cell"); + items.put(((Any) cell).getObject(), item); + } + } + + /* + * in the topics table, there are always one + * title row and three topics defined. + * So no mutter how many rows a topic takes - we + * can restore its structure and format. + */ + int rows = getRowCount(table); + + rowsPerTopic = (rows - 1) / 3; + + String firstCell = "A" + (1 + rowsPerTopic + 1); + String afterLastCell = "A" + (1 + (rowsPerTopic * 2) + 1); + + // go to the first row of the 2. topic + XTextTableCursor cursor = table.createCursorByCellName(firstCell); + XTextRange range; + + // analyze the structure of the topic rows. + while (!cursor.getRangeName().equals(afterLastCell)) + { + cell = table.getCellByName(cursor.getRangeName()); + XTextRange xTextRange = (XTextRange) UnoRuntime.queryInterface(XTextRange.class, cell); + // first I store the content and para style of the cell + AgendaElement ae = new TextElement(xTextRange); + // if the cell contains a relevant <...> + // i add the text element to the hash, + // so it's text can be updated later. + range = (XTextRange) items.get(cell); + if (range != null) + { + topicItems.put(xTextRange.getString().toLowerCase().trim(), ae); + } + + topicCells.add(ae); + + // and store the format of the cell. + topicCellFormats.add(new TableCellFormatter(table.getCellByName(cursor.getRangeName()))); + + // goto next cell. + cursor.goRight((short) 1, false); + } + + /* + * now - in which cell is every fillin? + */ + numCell = topicCells.indexOf(topicItems.get(FILLIN_TOPIC_NUMBER)); + topicCell = topicCells.indexOf(topicItems.get(FILLIN_TOPIC_TOPIC)); + responsibleCell = topicCells.indexOf(topicItems.get(FILLIN_TOPIC_RESPONSIBLE)); + timeCell = topicCells.indexOf(topicItems.get(FILLIN_TOPIC_TIME)); + + + + /* now that we know how the topics look like, + * we get the format of the first and last rows. + */ + + // format of first row + cursor.gotoStart(false); + do + { + firstRowFormat.add(new TableCellFormatter(table.getCellByName(cursor.getRangeName()))); + cursor.goRight((short) 1, false); + } + while (!cursor.getRangeName().startsWith("A")); + + // format of the last row + cursor.gotoEnd(false); + while (!cursor.getRangeName().startsWith("A")) + { + lastRowFormat.add(new TableCellFormatter(table.getCellByName(cursor.getRangeName()))); + cursor.goLeft((short) 1, false); + } + // we missed the A cell - so we have to add it also.. + lastRowFormat.add(new TableCellFormatter(table.getCellByName(cursor.getRangeName()))); + + removeTableRows(table, 1 + rowsPerTopic, rows - rowsPerTopic - 1); + + } + + /** + * @param topic the topic number to write + * @param data the data of the topic. + * @return the number of rows that have been added + * to the table. 0 or a negative number: no rows added. + */ + private int write2(int topic, PropertyValue[] data) throws Exception + { + while (topic >= writtenTopics.size()) + { + writtenTopics.add(null); + } + writtenTopics.set(topic, ""); + + // make sure threr are enough rows for me... + int rows = getRowCount(table); + int reqRows = 1 + (topic + 1) * rowsPerTopic; + int firstRow = reqRows - rowsPerTopic + 1; + int diff = reqRows - rows; + if (diff > 0) + { + insertTableRows(table, rows, diff); // set the item's text... + } + setItemText(numCell, data[0].Value); + setItemText(topicCell, data[1].Value); + setItemText(responsibleCell, data[2].Value); + setItemText(timeCell, data[3].Value); + + // now write ! + XTextTableCursor cursor = table.createCursorByCellName("A" + firstRow); + + for (int i = 0; i < topicCells.size(); i++) + { + ((AgendaElement) topicCells.get(i)).write(table.getCellByName(cursor.getRangeName())); + cursor.goRight((short) 1, false); + } + + // now format ! + cursor.gotoCellByName("A" + firstRow, false); + + formatTable(cursor, topicCellFormats, false); + + return diff; + + } + + /** + * check if the topic with the given index is written to the table. + * @param topic the topic number (0 base) + * @return true if the topic is already written to the table. False if not. + * (false would mean new rows must be added to the table in order to + * be able to write this topic). + */ + private boolean isWritten(int topic) + { + return (writtenTopics.size() > topic && writtenTopics.get(topic) != null); + } + + /** + * rewrites a single cell containing. + * This is used in order to refresh the topic/responsible/duration data in the + * preview document, in response to a change in the gui (by the user). + * Since the structure of the topics table is flexible, we don't reference a cell + * number. Rather, we use "what" argument to specify which cell should be redrawn. + * The Topics object, which analyzed the structure of the topics table appon + * initialization, refreshes the approperiate cell. + * @param topic index of the topic (0 based). + * @param what 0 for num, 1 for topic, 2 for responsible, 3 for duration + * @param data the row's data. + * @throws Exception if something goes wrong (thow nothing should) + */ + public void writeCell(int topic, int what, PropertyValue[] data) throws Exception + { + // if the whole row should be written... + if (!isWritten(topic)) + { + write(topic, data); + // write only the "what" cell. + } + else + { + // calculate the table row. + int firstRow = 1 + (topic * rowsPerTopic) + 1; + // go to the first cell of this topic. + XTextTableCursor cursor = table.createCursorByCellName("A" + firstRow); + + TextElement te = null; + int cursorMoves = 0; + + switch (what) + { + case 0: + te = setItemText(numCell, data[0].Value); + cursorMoves = numCell; + break; + case 1: + te = setItemText(topicCell, data[1].Value); + cursorMoves = topicCell; + break; + case 2: + te = setItemText(responsibleCell, data[2].Value); + cursorMoves = responsibleCell; + break; + case 3: + te = setItemText(timeCell, data[3].Value); + cursorMoves = timeCell; + break; + } + // move the cursor to the needed cell... + cursor.goRight((short) cursorMoves, false); + XCell xc = table.getCellByName(cursor.getRangeName()); + // and write it ! + te.write(xc); + ((TableCellFormatter) topicCellFormats.get(cursorMoves)).format(xc); + + } + } + + /** + * writes the given topic. + * if the first topic was involved, reformat the + * first row. + * If any rows were added to the table, reformat + * the last row. + * @param topic the index of the topic to write. + * @param data the topic's data. (see TopicsControl + * for explanation about the topics data model) + * @throws Exception if something goes wrong (though nothing should). + */ + public void write(int topic, PropertyValue[] data) throws Exception + { + int diff = write2(topic, data); + /* if the first topic has been written, + * one needs to reformat the first row. + */ + if (topic == 0) + { + formatFirstRow(); + } + /* + * if any rows were added, one needs to format + * the whole table again. + */ + if (diff > 0) + { + formatLastRow(); + } + } + + /** + * Writes all the topics to thetopics table. + * @param topicsData a List containing all Topic's Data. + */ + public void writeAll(List topicsData) + { + try + { + for (int i = 0; i < topicsData.size() - 1; i++) + { + write2(i, (PropertyValue[]) topicsData.get(i)); + } + formatLastRow(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + /** + * removes obsolete rows, reducing the + * topics table to the given number of topics. + * Note this method does only reducing - if + * the number of topics given is greater than the + * number of actuall topics it does *not* add + * new rows ! + * Note also that the first topic will never be removed. + * If the table contains no topics, the whole section will + * be removed uppon finishing. + * The reason for that is a "table-design" one: the first topic is + * maintained in order to be able to add rows with a design of this topic, + * and not of the header row. + * @param topics the number of topics the table should contain. + * @throws Exception + */ + public void reduceDocumentTo(int topics) throws Exception + { + // we never remove the first topic... + if (topics <= 0) + { + topics = 1; + } + XTableRows tableRows = table.getRows(); + int targetNumOfRows = topics * rowsPerTopic + 1; + if (tableRows.getCount() > targetNumOfRows) + { + tableRows.removeByIndex(targetNumOfRows, tableRows.getCount() - targetNumOfRows); + } + formatLastRow(); + while (writtenTopics.size() > topics) + { + writtenTopics.remove(topics); + } + } + + /** + * reapply the format of the first (header) row. + */ + private void formatFirstRow() + { + XTextTableCursor cursor = table.createCursorByCellName("A1"); + formatTable(cursor, firstRowFormat, false); + } + + /** + * reaply the format of the last row. + */ + private void formatLastRow() + { + XTextTableCursor cursor = table.createCursorByCellName("A1"); + cursor.gotoEnd(false); + formatTable(cursor, lastRowFormat, true); + } + + /** + * returns a text element for the given cell, + * which will write the given text. + * @param cell the topics cell number. + * @param value the value to write. + * @return a TextElement object which will write the given value + * to the given cell. + */ + private TextElement setItemText(int cell, Object value) + { + if (cell >= 0) + { + TextElement te = ((TextElement) topicCells.get(cell)); + if (te != null) + { + te.text = value.toString(); + } + return te; + } + return null; + } + + /** + * formats a series of cells from the given one, + * using the given List of TableCellFormatter objects, + * in the given order. + * This method is used to format the first (header) and the last + * rows of the table. + * @param cursor a table cursor, pointing to the start cell to format + * @param formats a List containing TableCellFormatter objects. Each will format one cell in the direction specified. + * @param reverse if true the cursor will move left, formatting in reverse order (used for the last row). + */ + private void formatTable(XTextTableCursor cursor, List formats, boolean reverse) + { + for (int i = 0; i < formats.size(); i++) + { + ((TableCellFormatter) formats.get(i)).format(table.getCellByName(cursor.getRangeName())); + if (reverse) + { + cursor.goLeft((short) 1, false); + } + else + { + cursor.goRight((short) 1, false); + } + } + } + } + + + /* + * ================================= + * Here are some static help methods + * ================================= + */ + public static String[] getNamesWhichStartWith(String[] allNames, String prefix) + { + Vector v = new Vector(); + for (int i = 0; i < allNames.length; i++) + { + if (allNames[i].startsWith(prefix)) + { + v.add(allNames[i]); + } + } + String[] s = new String[v.size()]; + System.arraycopy(v.toArray(), 0, s, 0, s.length); + return s; + } + + /** + * Convenience method, costs the given object to an XNamed, and returnes its name. + * @param obj an XNamed object. + * @return the name of the given object. + */ + public static String getName(Object obj) + { + return ((XNamed) UnoRuntime.queryInterface(XNamed.class, obj)).getName(); + } + + /** + * convenience method, for removing a number of cells from a table. + * @param table + * @param start + * @param count + */ + public static void removeTableRows(Object table, int start, int count) + { + XTableRows rows = ((XTextTable) UnoRuntime.queryInterface(XTextTable.class, table)).getRows(); + rows.removeByIndex(start, count); + } + + /** + * Convenience method for inserting some cells into a table. + * @param table + * @param start + * @param count + */ + public static void insertTableRows(Object table, int start, int count) + { + XTableRows rows = ((XTextTable) UnoRuntime.queryInterface(XTextTable.class, table)).getRows(); + rows.insertByIndex(start, count); + } + + /** + * returns the row index for this cursor, assuming + * the cursor points to a single cell. + * @param cursor + * @return the row index in which the cursor is. + */ + public static int getRowIndex(XTextTableCursor cursor) + { + return getRowIndex(cursor.getRangeName()); + } + + /** + * returns the row index for this cell name. + * @param cellName + * @return the row index for this cell name. + */ + public static int getRowIndex(String cellName) + { + return Integer.parseInt(cellName.substring(1)); + } + + /** + * returns the rows count of this table, assuming + * there is no vertical merged cells. + * @param table + * @return the rows count of the given table. + */ + public static int getRowCount(XTextTable table) + { + String[] cells = table.getCellNames(); + return getRowIndex(cells[cells.length - 1]); + } +} + +/* + * =========================================================================================== + * + * End of AgendaTempalte class + * + * =========================================================================================== + * + */ +/* + * ================================= + * The AgendaElement interface + * ================================= + */ +/** + * Interface that is used for writing content to a Uno Text / TextRange + * @author rp143992 + * + */ +interface AgendaElement +{ + + void write(Object any) throws Exception; +} + + +/* + * ================================= + * The ParaStyled class + * ================================= + */ +/** + * Basic implementation of the AgendaElement interface - + * writes nothing, but applies a ParaStyle to the given XText/XTextRange + * @author rp143992 + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +class ParaStyled implements AgendaElement +{ + + String paraStyle; + + ParaStyled(String paraStyle_) + { + paraStyle = paraStyle_; + } + + void format(Object textRange) + { + XText o; + o = ((XText) UnoRuntime.queryInterface(XText.class, textRange)); + if (o == null) + { + o = ((XTextRange) UnoRuntime.queryInterface(XTextRange.class, textRange)).getText(); + } + XTextRange xtr = (XTextRange) UnoRuntime.queryInterface(XTextRange.class, textRange); + XTextCursor cursor = o.createTextCursorByRange(xtr); + + Helper.setUnoPropertyValue(cursor, "ParaStyleName", paraStyle); + } + + public void write(Object textRange) + { + format(textRange); + } +} + +/* + * ================================= + * The TextElement class + * ================================= + */ +/** + * A basic implementation of AgendaElement: + * writes a String to the given XText/XTextRange, and applies + * a ParaStyle to it (using the parent class). + * @author rp143992 + */ +class TextElement extends ParaStyled +{ + + String text; + + TextElement(XTextRange range) + { + this(range.getString(), (String) Helper.getUnoPropertyValue(range.getStart(), "ParaStyleName")); + } + + TextElement(String text_, String paraStyle_) + { + super(paraStyle_); + text = text_; + } + + public void write(Object textRange) + { + ((XTextRange) UnoRuntime.queryInterface(XTextRange.class, textRange)).setString(text); + if (!text.equals("")) + { + super.write(textRange); + } + } +} + +/** + * A Text element which, if the text to write is empty (null or "") + * inserts a placeholder instead. + * @author rp143992 + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +class PlaceholderTextElement extends TextElement +{ + + String hint; + String placeHolderText; + XMultiServiceFactory xmsf; + + PlaceholderTextElement(XTextRange textRange, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_) + { + super(textRange); + placeHolderText = placeHolderText_; + hint = hint_; + xmsf = xmsf_; + } + + PlaceholderTextElement(String text, String paraStyle, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_) + { + super(text, paraStyle); + placeHolderText = placeHolderText_; + hint = hint_; + xmsf = xmsf_; + } + + public void write(Object textRange) + { + super.write(textRange); + if (text == null || text.equals("")) + { + XTextRange xTextRange = (XTextRange) UnoRuntime.queryInterface(XTextRange.class, textRange); + try + { + XTextContent xTextContent = AgendaTemplate.createPlaceHolder(xmsf, placeHolderText, hint); + xTextRange.getText().insertTextContent(xTextRange.getStart(), xTextContent, true); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + } +} + +/* + * ================================= + * The PlaceHolder class + * ================================= + */ +/** + * An Agenda element which writes no text, but inserts a placeholder, and formats + * it using a ParaStyleName. + * @author rp143992 + * + */ +class PlaceholderElement extends ParaStyled +{ + + String hint; + String placeHolderText; + XMultiServiceFactory xmsf; + + PlaceholderElement(String paraStyle, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_) + { + super(paraStyle); + placeHolderText = placeHolderText_; + hint = hint_; + xmsf = xmsf_; + } + + public void write(Object textRange) + { + XTextRange xTextRange = (XTextRange) UnoRuntime.queryInterface(XTextRange.class, textRange); + try + { + XTextContent xTextContent = AgendaTemplate.createPlaceHolder(xmsf, placeHolderText, hint); + xTextRange.getText().insertTextContent(xTextRange.getStart(), xTextContent, true); + super.write(textRange); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} + + +/* + * ================================= + * The AgendaItem class + * ================================= + */ +/** + * An implementation of AgendaElement which + * gets as a parameter a table cursor, and writes + * a text to the cell marked by this table cursor, and + * a place holder to the next cell. + * @author rp143992 + * + * TODO To change the template for this generated type comment go to + * Window - Preferences - Java - Code Style - Code Templates + */ +class AgendaItem implements AgendaElement +{ + + TextElement textElement; + AgendaElement field; + public Object table; + String name; + + AgendaItem(String name_, TextElement te, AgendaElement f) + { + name = name_; + field = f; + textElement = te; + } + + public void write(Object tableCursor) throws Exception + { + XTextTableCursor xTextTableCursor = (XTextTableCursor) UnoRuntime.queryInterface(XTextTableCursor.class, tableCursor); + XTextTable xTextTable = (XTextTable) UnoRuntime.queryInterface(XTextTable.class, table); + + String cellname = xTextTableCursor.getRangeName(); + Object cell = xTextTable.getCellByName(cellname); + + textElement.write(cell); + + xTextTableCursor.goRight((short) 1, false); + + //second field is actually always null... + // this is a preparation for adding placeholders. + if (field != null) + { + field.write(xTextTable.getCellByName(xTextTableCursor.getRangeName())); + } + } +} + +/* + * ================================= + * The TableCellFormatter class + * ================================= + */ +/** + * reads/write a table cell format from/to a table cell or a group of cells. + * + */ +class TableCellFormatter +{ + + static String[] properties = new String[] + { + "BackColor", + "BackTransparent", + "BorderDistance", + "BottomBorder", + "BottomBorderDistance", + "LeftBorder", + "LeftBorderDistance", + "RightBorder", + "RightBorderDistance", + "TopBorder", + "TopBorderDistance" + }; + private Object[] values = new Object[properties.length]; + + public TableCellFormatter(Object tableCell) + { + for (int i = 0; i < properties.length; i++) + { + values[i] = Helper.getUnoPropertyValue(tableCell, properties[i]); + } + } + + public void format(Object tableCell) + { + Helper.setUnoPropertyValues(tableCell, properties, values); + } +} + + + + |