| /************************************************************** |
| * |
| * 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.*; |
| |
| |
| 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 = 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(PropertyNames.EMPTY_STRING); |
| } |
| 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 = 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 = 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 = 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 = UnoRuntime.queryInterface(XMultiServiceFactory.class, document); |
| textSectionHandler = new TextSectionHandler(xMultiServiceFactory, 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 = 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 = 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 ArrayList<XTextRange>(ia.getCount()); |
| for (int i = 0; i < ia.getCount(); i++) |
| { |
| try |
| { |
| l.add(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 = UnoRuntime.queryInterface(XTextSectionsSupplier.class, document); |
| String[] allSections = xTextSectionsSupplier.getTextSections().getElementNames(); |
| return getNamesWhichStartWith(allSections, s); |
| } |
| |
| Object getSection(String name) throws NoSuchElementException, WrappedTargetException |
| { |
| XTextSectionsSupplier xTextSectionsSupplier = UnoRuntime.queryInterface(XTextSectionsSupplier.class, document); |
| return ((Any) (xTextSectionsSupplier.getTextSections().getByName(name))).getObject(); |
| } |
| |
| Object getTable(String name) throws NoSuchElementException, WrappedTargetException |
| { |
| 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 ? PropertyNames.EMPTY_STRING : text); |
| te.write(tr); |
| } |
| private static long DAY_IN_MILLIS = (24 * 60 * 60 * 1000); |
| |
| private String getDateString(String d) |
| { |
| if (d == null || d.equals(PropertyNames.EMPTY_STRING)) |
| { |
| return PropertyNames.EMPTY_STRING; |
| } |
| int date = Integer.parseInt(d); |
| 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(PropertyNames.EMPTY_STRING)) |
| { |
| return PropertyNames.EMPTY_STRING; |
| } |
| int time = Integer.parseInt(s); |
| |
| 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 = 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) |
| { |
| UnoRuntime.queryInterface(XTextContent.class, section).getAnchor().setString(PropertyNames.EMPTY_STRING); |
| } |
| } |
| } |
| 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 = UnoRuntime.queryInterface(XTextSection.class, minutesAllSection); |
| xTextSection.getAnchor().setString(PropertyNames.EMPTY_STRING); |
| } |
| catch (Exception ex) |
| { |
| ex.printStackTrace(); |
| } |
| } |
| // the user checked "create minutes" |
| else |
| { |
| try |
| { |
| String itemText; |
| XTextRange item; |
| int topicStartTime = 0; |
| try |
| { |
| topicStartTime = Integer.parseInt(agenda.cp_Time); |
| } |
| 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, PropertyNames.EMPTY_STRING); |
| } |
| else if (itemText.equals(FILLIN_MINUTE_TOPIC)) |
| { |
| fillMinutesItem(item, topic[1].Value, PropertyNames.EMPTY_STRING); |
| } |
| else if (itemText.equals(FILLIN_MINUTE_RESPONSIBLE)) |
| { |
| fillMinutesItem(item, topic[2].Value, PropertyNames.EMPTY_STRING); |
| } |
| 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, PropertyNames.EMPTY_STRING); |
| } |
| } |
| |
| 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 PropertyNames.EMPTY_STRING) |
| */ |
| 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(PropertyNames.EMPTY_STRING)) |
| { |
| if (placeholder != null && !placeholder.equals(PropertyNames.EMPTY_STRING)) |
| { |
| 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 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 = UnoRuntime.queryInterface(XTextTable.class, table); |
| XTextTableCursor cursor = xTextTable.createCursorByCellName("A1"); |
| AgendaItem ai; |
| // should this section be visible? |
| boolean visible = false; |
| |
| // write items |
| // =========== |
| String cellName = PropertyNames.EMPTY_STRING; |
| |
| /* 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()); |
| UnoRuntime.queryInterface(XTextRange.class, cell).setString(PropertyNames.EMPTY_STRING); |
| 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(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 = 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 = 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, PropertyNames.EMPTY_STRING); |
| |
| // 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... |
| if ( te != null) |
| { |
| 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) |
| { |
| ArrayList<String> v = new ArrayList<String>(); |
| for (int i = 0; i < allNames.length; i++) |
| { |
| if (allNames[i].startsWith(prefix)) |
| { |
| v.add(allNames[i]); |
| } |
| } |
| String[] s = new String[v.size()]; |
| return v.toArray(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 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 = 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 = 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 = UnoRuntime.queryInterface(XText.class, textRange); |
| if (o == null) |
| { |
| o = UnoRuntime.queryInterface(XTextRange.class, textRange).getText(); |
| } |
| XTextRange xtr = 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) |
| { |
| UnoRuntime.queryInterface(XTextRange.class, textRange).setString(text); |
| if (!text.equals(PropertyNames.EMPTY_STRING)) |
| { |
| super.write(textRange); |
| } |
| } |
| } |
| |
| /** |
| * A Text element which, if the text to write is empty (null or PropertyNames.EMPTY_STRING) |
| * 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(PropertyNames.EMPTY_STRING)) |
| { |
| 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 = 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 = UnoRuntime.queryInterface(XTextTableCursor.class, tableCursor); |
| 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); |
| } |
| } |
| |
| |
| |
| |