1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 package com.sun.star.wizards.agenda;
24 
25 import java.util.*;
26 
27 
28 import com.sun.star.awt.TextEvent;
29 import com.sun.star.beans.PropertyValue;
30 import com.sun.star.container.NoSuchElementException;
31 import com.sun.star.container.XIndexAccess;
32 import com.sun.star.container.XNamed;
33 import com.sun.star.document.XDocumentProperties;
34 import com.sun.star.frame.XComponentLoader;
35 import com.sun.star.frame.XTerminateListener;
36 import com.sun.star.i18n.NumberFormatIndex;
37 import com.sun.star.lang.Locale;
38 import com.sun.star.lang.WrappedTargetException;
39 import com.sun.star.lang.XMultiServiceFactory;
40 import com.sun.star.table.XCell;
41 import com.sun.star.table.XTableRows;
42 import com.sun.star.text.*;
43 import com.sun.star.uno.Any;
44 import com.sun.star.uno.UnoRuntime;
45 import com.sun.star.util.XNumberFormatsSupplier;
46 import com.sun.star.util.XNumberFormatter;
47 import com.sun.star.util.XSearchDescriptor;
48 import com.sun.star.util.XSearchable;
49 import com.sun.star.wizards.common.FileAccess;
50 import com.sun.star.wizards.common.Helper;
51 import com.sun.star.wizards.common.JavaTools;
52 import com.sun.star.wizards.common.NumberFormatter;
53 import com.sun.star.wizards.common.PropertyNames;
54 import com.sun.star.wizards.document.OfficeDocument;
55 import com.sun.star.wizards.text.TextDocument;
56 import com.sun.star.wizards.text.TextSectionHandler;
57 import com.sun.star.wizards.ui.UnoDialog2;
58 import com.sun.star.wizards.ui.event.DataAware;
59 
60 /**
61  *
62  * The classes here implement the whole document-functionality of the agenda wizard:
63  * the live-preview and the final "creation" of the document, when the user clicks "finish". <br/>
64  * <br/>
65  * <h2>Some terminology:<h2/>
66  * items are names or headings. we don't make any distinction.
67  *
68  * <br/>
69  * The Agenda Template is used as general "controller" of the whole document, whereas the
70  * two child-classes ItemsTable and TopicsTable control the item tables (note plural!) and the
71  * topics table (note singular).
72  * <br/>   <br/>
73  * Other small classes are used to abstract the handling of cells and text and we
74  * try to use them as components.
75  * <br/><br/>
76  * We tried to keep the Agenda Template as flexible as possible, though there
77  * must be many limitations, because it is generated dynamically.<br/><br/>
78  * To keep the template flexible the following decisions were made:<br/>
79  * 1. Item tables.<br/>
80  * 1.a. there might be arbitrary number of Item tables.<br/>
81  * 1.b. Item tables design (bordewr, background) is arbitrary.<br/>
82  * 1.c. Items text styles are individual, and use stylelist styles with predefined names.<br/>
83  * As result the following limitations:<br/>
84  * Pairs of Name->value for each item.<br/>
85  * Tables contain *only* those pairs.<br/>
86  * 2. Topics table.<br/>
87  * 2.a. arbitrary structure.<br/>
88  * 2.b. design is arbitrary.<br/>
89  * As result the following limitations:<br/>
90  * No column merge is allowed.<br/>
91  * One compolsary Heading row.<br/>
92  * <br/><br/>
93  * To let the template be flexible, we use a kind of "detection": we look where
94  * the items are read the design of each table, reaplying it after writing the
95  * table.
96  * <br/><br/>
97  * A note about threads:<br/>
98  * Many methods here are synchronized, in order to avoid colission made by
99  * events fired too often.
100  * @author rpiterman
101  *
102  */
103 public class AgendaTemplate extends TextDocument implements TemplateConsts, DataAware.Listener
104 {
105 
106     /**
107      * resources.
108      */
109     AgendaWizardDialogResources resources;
110     /**
111      * data model. This keeps the status of the agenda document, and
112      * every redraw is done according to this data.
113      * Exception: topic data is written programatically, event-oriented.
114      */
115     CGAgenda agenda;
116     /**
117      * the UNO Text Document serrvice
118      */
119     Object document;
120     /**
121      * Service Factory
122      */
123     XMultiServiceFactory docMSF;
124     /**
125      * The template-filename of the current template.
126      * Since we often re-link section and the break the link,
127      * inorder to restore them, we need a template to link to.
128      * This is practically an identicall copy of the current template.
129      */
130     String template;
131     /**
132      * used for common operations on sections.
133      */
134     TextSectionHandler textSectionHandler;
135     /**
136      * a component loader.
137      */
138     XComponentLoader xComponentLoader;
139     /**
140      * an array containing all ItemTable object (which control each an Items
141      * Table in the document.
142      */
143     ItemsTable[] itemsTables;
144     /**
145      * the controller of the topics table.
146      */
147     Topics topics;
148     /**
149      *  Stores reusable OOo Placeholder TextFields to insert to the document.
150      */
151     Map itemsCache;
152     /**
153      * This map is used to find which tables contains a certain Item, so
154      * the keys are the different Items, the Objects are the ItemTable controllers.
155      * When an Item must be redrawn (because the user checked or uncheced it),
156      * the controller is retrieved from this Map, and a redraw is issued on this controller.
157      */
158     Map itemsMap = new Hashtable(11);
159     /**
160      * A temporary variable used to list all items and map them.
161      */
162     List _allItems = new Vector();
163     /**
164      * keep a reference on some static items in the document,
165      * so when their content is changed (through the user), we
166      * can just reference them and set their text.
167      */
168     TextElement teTitle, teDate, teTime, teLocation;
169     XTextRange trTitle, trDate, trTime, trLocation;
170     /**
171      * used to format the date / time.
172      */
173     int dateFormat, timeFormat;
174     XNumberFormatter dateFormatter, timeFormatter;
175     /**
176      * used to transfare time from VCL to UNO.
177      */
178     long docNullTime;
179     Calendar calendar;
180     /**
181      * used to set the document title property (step 6).
182      */
183     private XDocumentProperties m_xDocProps;
184 
185     /**
186      * loads the given template, and analyze its structure.
187      * @param templateURL
188      * @param topics
189      * @see AgendaTemplate.initialize()
190      * @see AgendaTemplate.initializeData()
191      */
load(String templateURL, List topics)192     public synchronized void load(String templateURL, List topics)
193     {
194         template = calcTemplateName(templateURL);
195         document = loadAsPreview(templateURL, false);
196         docMSF = UnoRuntime.queryInterface(XMultiServiceFactory.class, document);
197         xFrame.getComponentWindow().setEnable(false);
198         xTextDocument.lockControllers();
199         initialize();
200         initializeData(topics);
201         xTextDocument.unlockControllers();
202     }
203 
204     /**
205      * The agenda templates are in format of aw-XXX.ott
206      * the templates name is then XXX.ott.
207      * This method calculates it.
208      * @param url
209      * @return the template name without the "aw-" at the beginning.
210      */
calcTemplateName(String url)211     private String calcTemplateName(String url)
212     {
213         return FileAccess.connectURLs(FileAccess.getParentDir(url), FileAccess.getFilename(url).substring(3));
214     }
215 
216     /**
217      * synchronize the document to the model.<br/>
218      * this method rewrites all titles, item tables , and the topics table-
219      * thus synchronizing the document to the data model (CGAgenda).
220      * @param topicsData since the model does not contain Topics
221      * information (it is only actualized on save) the given list
222      * supplies this information.
223      */
initializeData(List topicsData)224     private void initializeData(List topicsData)
225     {
226         for (int i = 0; i < itemsTables.length; i++)
227         {
228             try
229             {
230                 itemsTables[i].write(PropertyNames.EMPTY_STRING);
231             }
232             catch (Exception ex)
233             {
234                 ex.printStackTrace();
235             }
236         }
237         redrawTitle("txtTitle");
238         redrawTitle("txtDate");
239         redrawTitle("txtTime");
240         redrawTitle("cbLocation");
241 
242         topics.writeAll(topicsData);
243 
244         setTemplateTitle(agenda.cp_TemplateName);
245 
246     }
247 
248     /**
249      * redraws/rewrites the table which contains the given item
250      * This method is called when the user checks/unchecks an item.
251      * The table is being found, in which the item is, and redrawn.
252      * @param itemName
253      */
redraw(String itemName)254     public synchronized void redraw(String itemName)
255     {
256         try
257         {
258             // get the table in which the item is...
259             Object itemsTable =
260                     itemsMap.get(itemName);
261             // rewrite the table.
262             ((ItemsTable) itemsTable).write(null);
263         }
264         catch (Exception e)
265         {
266             e.printStackTrace();
267         }
268     }
269 
270     /**
271      * update the documents title property to the given title
272      * @param newTitle new title.
273      */
setTemplateTitle(String newTitle)274     synchronized void setTemplateTitle(String newTitle)
275     {
276         m_xDocProps.setTitle(newTitle);
277     }
278 
279     /**
280      * constructor. The document is *not* loaded here.
281      * only some formal members are set.
282      * @param xmsf_ service factory.
283      * @param agenda_ the data model (CGAgenda)
284      * @param resources_ resources.
285      */
AgendaTemplate(XMultiServiceFactory xmsf_, CGAgenda agenda_, AgendaWizardDialogResources resources_, XTerminateListener listener)286     AgendaTemplate(XMultiServiceFactory xmsf_, CGAgenda agenda_, AgendaWizardDialogResources resources_, XTerminateListener listener)
287     {
288         super(xmsf_, listener, "WIZARD_LIVE_PREVIEW");
289 
290         agenda = agenda_;
291         resources = resources_;
292 
293         if (itemsCache == null)
294         {
295             initItemsCache();
296         }
297         _allItems = null;
298 
299     }
300 
301     /**
302      * checks the data model if the
303      * item corresponding to the given string should be shown
304      * @param itemName a string representing an Item (name or heading).
305      * @return true if the model specifies that the item should be displayed.
306      */
isShowItem(String itemName)307     boolean isShowItem(String itemName)
308     {
309         if (itemName.equals(FILLIN_MEETING_TYPE))
310         {
311             return agenda.cp_ShowMeetingType;
312         }
313         else if (itemName.equals(FILLIN_READ))
314         {
315             return agenda.cp_ShowRead;
316         }
317         else if (itemName.equals(FILLIN_BRING))
318         {
319             return agenda.cp_ShowBring;
320         }
321         else if (itemName.equals(FILLIN_NOTES))
322         {
323             return agenda.cp_ShowNotes;
324         }
325         else if (itemName.equals(FILLIN_FACILITATOR))
326         {
327             return agenda.cp_ShowFacilitator;
328         }
329         else if (itemName.equals(FILLIN_TIMEKEEPER))
330         {
331             return agenda.cp_ShowTimekeeper;
332         }
333         else if (itemName.equals(FILLIN_NOTETAKER))
334         {
335             return agenda.cp_ShowNotetaker;
336         }
337         else if (itemName.equals(FILLIN_PARTICIPANTS))
338         {
339             return agenda.cp_ShowAttendees;
340         }
341         else if (itemName.equals(FILLIN_CALLED_BY))
342         {
343             return agenda.cp_ShowCalledBy;
344         }
345         else if (itemName.equals(FILLIN_OBSERVERS))
346         {
347             return agenda.cp_ShowObservers;
348         }
349         else if (itemName.equals(FILLIN_RESOURCE_PERSONS))
350         {
351             return agenda.cp_ShowResourcePersons;
352         }
353         else
354         {
355             throw new IllegalArgumentException("No such item");
356         }
357     }
358 
359     /**
360      * itemsCache is a Map containing all agenda item. These are object which
361      * "write themselfs" to the table, given a table cursor.
362      * A cache is used in order to reuse the objects, instead of recreate them.
363      * This method fills the cache will all items objects (names and headings).
364      */
initItemsCache()365     private void initItemsCache()
366     {
367         itemsCache = new Hashtable(11);
368 
369         XMultiServiceFactory xmsf = UnoRuntime.queryInterface(XMultiServiceFactory.class, document);
370         // Headings
371 
372         itemsCache.put(FILLIN_MEETING_TYPE,
373                 new AgendaItem(FILLIN_MEETING_TYPE, new TextElement(resources.itemMeetingType, STYLE_MEETING_TYPE),
374                 new PlaceholderElement(STYLE_MEETING_TYPE_TEXT, resources.reschkMeetingTitle_value, resources.resPlaceHolderHint, xmsf)));
375 
376         itemsCache.put(FILLIN_BRING,
377                 new AgendaItem(FILLIN_BRING, new TextElement(resources.itemBring, STYLE_BRING),
378                 new PlaceholderElement(STYLE_BRING_TEXT, resources.reschkBring_value, resources.resPlaceHolderHint, xmsf)));
379 
380         itemsCache.put(FILLIN_READ,
381                 new AgendaItem(FILLIN_READ, new TextElement(resources.itemRead, STYLE_READ),
382                 new PlaceholderElement(STYLE_READ_TEXT, resources.reschkRead_value, resources.resPlaceHolderHint, xmsf)));
383 
384         itemsCache.put(FILLIN_NOTES,
385                 new AgendaItem(FILLIN_NOTES, new TextElement(resources.itemNote, STYLE_NOTES),
386                 new PlaceholderElement(STYLE_NOTES_TEXT, resources.reschkNotes_value, resources.resPlaceHolderHint, xmsf)));
387 
388         // Names
389 
390         itemsCache.put(FILLIN_CALLED_BY,
391                 new AgendaItem(FILLIN_CALLED_BY, new TextElement(resources.itemCalledBy, STYLE_CALLED_BY),
392                 new PlaceholderElement(STYLE_CALLED_BY_TEXT, resources.reschkConvenedBy_value, resources.resPlaceHolderHint, xmsf)));
393 
394         itemsCache.put(FILLIN_FACILITATOR,
395                 new AgendaItem(FILLIN_FACILITATOR, new TextElement(resources.itemFacilitator, STYLE_FACILITATOR),
396                 new PlaceholderElement(STYLE_FACILITATOR_TEXT, resources.reschkPresiding_value, resources.resPlaceHolderHint, xmsf)));
397 
398         itemsCache.put(FILLIN_PARTICIPANTS,
399                 new AgendaItem(FILLIN_PARTICIPANTS, new TextElement(resources.itemAttendees, STYLE_PARTICIPANTS),
400                 new PlaceholderElement(STYLE_PARTICIPANTS_TEXT, resources.reschkAttendees_value, resources.resPlaceHolderHint, xmsf)));
401 
402         itemsCache.put(FILLIN_NOTETAKER,
403                 new AgendaItem(FILLIN_NOTETAKER, new TextElement(resources.itemNotetaker, STYLE_NOTETAKER),
404                 new PlaceholderElement(STYLE_NOTETAKER_TEXT, resources.reschkNoteTaker_value, resources.resPlaceHolderHint, xmsf)));
405 
406         itemsCache.put(FILLIN_TIMEKEEPER,
407                 new AgendaItem(FILLIN_TIMEKEEPER, new TextElement(resources.itemTimekeeper, STYLE_TIMEKEEPER),
408                 new PlaceholderElement(STYLE_TIMEKEEPER_TEXT, resources.reschkTimekeeper_value, resources.resPlaceHolderHint, xmsf)));
409 
410         itemsCache.put(FILLIN_OBSERVERS,
411                 new AgendaItem(FILLIN_OBSERVERS, new TextElement(resources.itemObservers, STYLE_OBSERVERS),
412                 new PlaceholderElement(STYLE_OBSERVERS_TEXT, resources.reschkObservers_value, resources.resPlaceHolderHint, xmsf)));
413 
414         itemsCache.put(FILLIN_RESOURCE_PERSONS,
415                 new AgendaItem(FILLIN_RESOURCE_PERSONS, new TextElement(resources.itemResource, STYLE_RESOURCE_PERSONS),
416                 new PlaceholderElement(STYLE_RESOURCE_PERSONS_TEXT, resources.reschkResourcePersons_value, resources.resPlaceHolderHint, xmsf)));
417 
418     }
419 
420     /**
421      * Initializes a template.<br/>
422      * This method does the following tasks:<br/>
423      * Get a Time and Date format for the document, and retrieve the null date of the document (which is
424      * document-specific).<br/>
425      * Initializes the Items Cache map.
426      * Analyses the document:<br/>
427      * -find all "fille-ins" (apear as &gt;xxx&lt; in the document).
428      * -analyze all items sections (and the tables in them).
429      * -locate the titles and actualize them
430      * -analyze the topics table
431      */
initialize()432     private void initialize()
433     {
434         /*
435          * Get the default locale of the document, and create the date and time formatters.
436          */
437         XMultiServiceFactory docMSF = UnoRuntime.queryInterface(XMultiServiceFactory.class, document);
438         try
439         {
440             Object defaults = docMSF.createInstance("com.sun.star.text.Defaults");
441             Locale l = (Locale) Helper.getUnoStructValue(defaults, "CharLocale");
442 
443             java.util.Locale jl = new java.util.Locale(
444                     l.Language, l.Country, l.Variant);
445 
446             calendar = Calendar.getInstance(jl);
447 
448             XNumberFormatsSupplier nfs = UnoRuntime.queryInterface(XNumberFormatsSupplier.class, document);
449             Object formatSettings = nfs.getNumberFormatSettings();
450             com.sun.star.util.Date date = (com.sun.star.util.Date) Helper.getUnoPropertyValue(formatSettings, "NullDate");
451 
452             calendar.set(date.Year, date.Month - 1, date.Day);
453 
454             docNullTime = JavaTools.getTimeInMillis(calendar);
455 
456             dateFormat = NumberFormatter.getNumberFormatterKey(nfs, NumberFormatIndex.DATE_SYSTEM_LONG);
457             timeFormat = NumberFormatter.getNumberFormatterKey(nfs, NumberFormatIndex.TIME_HHMM);
458 
459 
460             dateFormatter = NumberFormatter.createNumberFormatter(xMSF, nfs);
461             timeFormatter = NumberFormatter.createNumberFormatter(xMSF, nfs);
462         }
463         catch (Exception ex)
464         {
465             ex.printStackTrace();
466             throw new NullPointerException("Fatal Error: could not initialize locale or date/time formats.");
467         }
468 
469         /*
470          * get the document properties object.
471          */
472         m_xDocProps = OfficeDocument.getDocumentProperties(document);
473 
474         initItemsCache();
475         initializeItems();
476         initializeTitles();
477         initializeItemsSections();
478         XMultiServiceFactory xMultiServiceFactory = UnoRuntime.queryInterface(XMultiServiceFactory.class, document);
479         textSectionHandler = new TextSectionHandler(xMultiServiceFactory, UnoRuntime.queryInterface(XTextDocument.class, document));
480         initializeTopics();
481         _allItems.clear();
482         _allItems = null;
483     }
484 
485     /**
486      * locates the titles (name, location, date, time) and saves a reference to thier Text ranges.
487      *
488      */
initializeTitles()489     private void initializeTitles()
490     {
491         XTextRange item = null;
492 
493         XMultiServiceFactory xmsf = UnoRuntime.queryInterface(XMultiServiceFactory.class, document);
494 
495         for (int i = 0; i < _allItems.size(); i++)
496         {
497             item = (XTextRange) _allItems.get(i);
498             String text = item.getString().trim().toLowerCase();
499             if (text.equals(FILLIN_TITLE))
500             {
501 
502                 teTitle = new PlaceholderTextElement(item, resources.resPlaceHolderTitle, resources.resPlaceHolderHint, xmsf);
503                 trTitle = item;
504                 _allItems.remove(i--);
505             }
506             else if (text.equals(FILLIN_DATE))
507             {
508                 teDate = new PlaceholderTextElement(item, resources.resPlaceHolderDate, resources.resPlaceHolderHint, xmsf);
509                 trDate = item;
510                 _allItems.remove(i--);
511             }
512             else if (text.equals(FILLIN_TIME))
513             {
514                 teTime = new PlaceholderTextElement(item, resources.resPlaceHolderTime, resources.resPlaceHolderHint, xmsf);
515                 trTime = item;
516                 _allItems.remove(i--);
517             }
518             else if (text.equals(FILLIN_LOCATION))
519             {
520                 teLocation = new PlaceholderTextElement(item, resources.resPlaceHolderLocation, resources.resPlaceHolderHint, xmsf);
521                 trLocation = item;
522                 _allItems.remove(i--);
523             }
524         }
525     }
526 
initializeTopics()527     private void initializeTopics()
528     {
529         topics = new Topics();
530     }
531 
initializeItems()532     private void initializeItems()
533     {
534         _allItems = searchFillInItems();
535     }
536 
537     /**
538      * searches the document for items in the format "&gt;*&lt;"
539      * @return a vector containing the XTextRanges of the found items
540      */
searchFillInItems()541     private List searchFillInItems()
542     {
543         try
544         {
545             XSearchable xSearchable = UnoRuntime.queryInterface(XSearchable.class, document);
546             XSearchDescriptor sd = xSearchable.createSearchDescriptor();
547             sd.setSearchString("<[^>]+>");
548             sd.setPropertyValue("SearchRegularExpression", Boolean.TRUE);
549             sd.setPropertyValue("SearchWords", Boolean.TRUE);
550 
551             XIndexAccess ia = xSearchable.findAll(sd);
552 
553             List l = new ArrayList<XTextRange>(ia.getCount());
554             for (int i = 0; i < ia.getCount(); i++)
555             {
556                 try
557                 {
558                     l.add(UnoRuntime.queryInterface(XTextRange.class, ia.getByIndex(i)));
559                 }
560                 catch (Exception ex)
561                 {
562                     System.err.println("Nonfatal Error in finding fillins.");
563                 }
564             }
565             return l;
566         }
567         catch (Exception ex)
568         {
569             ex.printStackTrace();
570             throw new IllegalArgumentException("Fatal Error: Loading template failed: searching fillins failed");
571         }
572     }
573 
574     /**
575      * analyze the item sections in the template. delegates the analyze of each table to the
576      * ItemsTable class.
577      */
initializeItemsSections()578     private void initializeItemsSections()
579     {
580         String[] sections = getSections(document, TemplateConsts.SECTION_ITEMS);
581 
582         // for each section - there is a table...
583         itemsTables = new ItemsTable[sections.length];
584 
585         for (int i = 0; i < itemsTables.length; i++)
586         {
587             try
588             {
589                 itemsTables[i] = new ItemsTable(getSection(sections[i]), getTable(sections[i]));
590             }
591             catch (Exception ex)
592             {
593                 ex.printStackTrace();
594                 throw new IllegalArgumentException("Fatal Error while initialilzing Template: items table in section " + sections[i]);
595             }
596         }
597 
598     }
599 
getSections(Object document, String s)600     private String[] getSections(Object document, String s)
601     {
602         XTextSectionsSupplier xTextSectionsSupplier = UnoRuntime.queryInterface(XTextSectionsSupplier.class, document);
603         String[] allSections = xTextSectionsSupplier.getTextSections().getElementNames();
604         return getNamesWhichStartWith(allSections, s);
605     }
606 
getSection(String name)607     Object getSection(String name) throws NoSuchElementException, WrappedTargetException
608     {
609         XTextSectionsSupplier xTextSectionsSupplier = UnoRuntime.queryInterface(XTextSectionsSupplier.class, document);
610         return ((Any) (xTextSectionsSupplier.getTextSections().getByName(name))).getObject();
611     }
612 
getTable(String name)613     Object getTable(String name) throws NoSuchElementException, WrappedTargetException
614     {
615         XTextTablesSupplier xTextTablesSupplier = UnoRuntime.queryInterface(XTextTablesSupplier.class, document);
616         return ((Any) xTextTablesSupplier.getTextTables().getByName(name)).getObject();
617     }
618 
619     /**
620      * implementation of DataAware.Listener, is
621      * called when title/date/time or location are
622      * changed.
623      */
eventPerformed(Object param)624     public synchronized void eventPerformed(Object param)
625     {
626         TextEvent te = (TextEvent) param;
627         String controlName = (String) Helper.getUnoPropertyValue(
628                 UnoDialog2.getModel(te.Source),
629                 PropertyNames.PROPERTY_NAME);
630         redrawTitle(controlName);
631 
632     }
633 
redrawTitle(String controlName)634     private synchronized void redrawTitle(String controlName)
635     {
636         if (controlName.equals("txtTitle"))
637         {
638             writeTitle(teTitle, trTitle, agenda.cp_Title);
639         }
640         else if (controlName.equals("txtDate"))
641         {
642             writeTitle(teDate, trDate, getDateString(agenda.cp_Date));
643         }
644         else if (controlName.equals("txtTime"))
645         {
646             writeTitle(teTime, trTime, getTimeString(agenda.cp_Time));
647         }
648         else if (controlName.equals("cbLocation"))
649         {
650             writeTitle(teLocation, trLocation, agenda.cp_Location);
651         }
652         else
653         {
654             throw new IllegalArgumentException("No such title control...");
655         }
656     }
657 
writeTitle(TextElement te, XTextRange tr, String text)658     private void writeTitle(TextElement te, XTextRange tr, String text)
659     {
660         te.text = (text == null ? PropertyNames.EMPTY_STRING : text);
661         te.write(tr);
662     }
663     private static long DAY_IN_MILLIS = (24 * 60 * 60 * 1000);
664 
getDateString(String d)665     private String getDateString(String d)
666     {
667         if (d == null || d.equals(PropertyNames.EMPTY_STRING))
668         {
669             return PropertyNames.EMPTY_STRING;
670         }
671         int date = Integer.parseInt(d);
672         calendar.clear();
673         calendar.set(date / 10000,
674                 (date % 10000) / 100 - 1,
675                 date % 100);
676 
677         long date1 = JavaTools.getTimeInMillis(calendar);
678         /*
679          * docNullTime and date1 are in millis, but
680          * I need a day...
681          */
682         double daysDiff = (date1 - docNullTime) / DAY_IN_MILLIS + 1;
683 
684         return dateFormatter.convertNumberToString(dateFormat, daysDiff);
685     }
686 
getTimeString(String s)687     private String getTimeString(String s)
688     {
689         if (s == null || s.equals(PropertyNames.EMPTY_STRING))
690         {
691             return PropertyNames.EMPTY_STRING;
692         }
693         int time = Integer.parseInt(s);
694 
695         double t = ((double) (time / 1000000) / 24) + ((double) ((time % 1000000) / 1000) / (24 * 60));
696         return timeFormatter.convertNumberToString(timeFormat, t);
697     }
698 
699     /* *******************************************
700      *  F I N I S H
701      *********************************************/
702     /** the user clicked finish **/
finish(List topics)703     public synchronized void finish(List topics)
704     {
705         createMinutes(topics);
706         deleteHiddenSections();
707         textSectionHandler.removeAllTextSections();
708     }
709 
710     /**
711      * hidden sections exist when an item's section is hidden because the
712      * user specified not to display any items which it contains.
713      * When finishing the wizard removes this sections entireley from the document.
714      */
deleteHiddenSections()715     private void deleteHiddenSections()
716     {
717         XTextSectionsSupplier xTextSectionsSupplier = UnoRuntime.queryInterface(XTextSectionsSupplier.class, document);
718         String[] allSections = xTextSectionsSupplier.getTextSections().getElementNames();
719         try
720         {
721             for (int i = 0; i < allSections.length; i++)
722             {
723                 Object section = getSection(allSections[i]);
724                 //Try3.showProps(section);
725                 boolean visible = ((Boolean) Helper.getUnoPropertyValue(section, "IsVisible")).booleanValue();
726                 if (!visible)
727                 {
728                     UnoRuntime.queryInterface(XTextContent.class, section).getAnchor().setString(PropertyNames.EMPTY_STRING);
729                 }
730             }
731         }
732         catch (Exception ex)
733         {
734             ex.printStackTrace();
735         }
736     }
737 
738     /**
739      * create the minutes for the given topics or remove the minutes section from the document.
740      * If no topics are supplied, or the user
741      * specified not to create minuts, the minutes section will be removed,
742      * @param topicsData supplies PropertyValue arrays containing the values for the topics.
743      */
createMinutes(List topicsData)744     public synchronized void createMinutes(List topicsData)
745     {
746 
747         // if the minutes section should be removed (the
748         // user did not check "create minutes")
749         if (!agenda.cp_IncludeMinutes || (topicsData.size() <= 1))
750         {
751             try
752             {
753                 Object minutesAllSection = getSection(SECTION_MINUTES_ALL);
754                 XTextSection xTextSection = UnoRuntime.queryInterface(XTextSection.class, minutesAllSection);
755                 xTextSection.getAnchor().setString(PropertyNames.EMPTY_STRING);
756             }
757             catch (Exception ex)
758             {
759                 ex.printStackTrace();
760             }
761         }
762         // the user checked "create minutes"
763         else
764         {
765             try
766             {
767                 String itemText;
768                 XTextRange item;
769                 int topicStartTime = 0;
770                 try
771                 {
772                     topicStartTime = Integer.parseInt(agenda.cp_Time);
773                 }
774                 catch (Exception ex)
775                 {
776                 }
777 
778                 String time;
779 
780                 // first I replace the minutes titles...
781                 List items = searchFillInItems();
782                 for (int itemIndex = 0; itemIndex < items.size(); itemIndex++)
783                 {
784                     item = (XTextRange) items.get(itemIndex);
785                     itemText = item.getString().trim().toLowerCase();
786 
787                     if (itemText.equals(FILLIN_MINUTES_TITLE))
788                     {
789                         fillMinutesItem(item, agenda.cp_Title, resources.resPlaceHolderTitle);
790                     }
791                     else if (itemText.equals(FILLIN_MINUTES_LOCATION))
792                     {
793                         fillMinutesItem(item, agenda.cp_Location, resources.resPlaceHolderLocation);
794                     }
795                     else if (itemText.equals(FILLIN_MINUTES_DATE))
796                     {
797                         fillMinutesItem(item, getDateString(agenda.cp_Date), resources.resPlaceHolderDate);
798                     }
799                     else if (itemText.equals(FILLIN_MINUTES_TIME))
800                     {
801                         fillMinutesItem(item, getTimeString(agenda.cp_Time), resources.resPlaceHolderTime);
802                     }
803                 }
804 
805                 items.clear();
806 
807                 /*
808                  * now add minutes for each topic.
809                  * The template contains *one* minutes section, so
810                  * we first use the one available, and then add a new one...
811                  *
812                  * topics data has *always* an empty topic at the end...
813                  */
814                 for (int i = 0; i < topicsData.size() - 1; i++)
815                 {
816                     PropertyValue[] topic = (PropertyValue[]) topicsData.get(i);
817 
818                     items = searchFillInItems();
819                     for (int itemIndex = 0; itemIndex < items.size(); itemIndex++)
820                     {
821                         item = (XTextRange) items.get(itemIndex);
822                         itemText = item.getString().trim().toLowerCase();
823 
824                         if (itemText.equals(FILLIN_MINUTE_NUM))
825                         {
826                             fillMinutesItem(item, topic[0].Value, PropertyNames.EMPTY_STRING);
827                         }
828                         else if (itemText.equals(FILLIN_MINUTE_TOPIC))
829                         {
830                             fillMinutesItem(item, topic[1].Value, PropertyNames.EMPTY_STRING);
831                         }
832                         else if (itemText.equals(FILLIN_MINUTE_RESPONSIBLE))
833                         {
834                             fillMinutesItem(item, topic[2].Value, PropertyNames.EMPTY_STRING);
835                         }
836                         else if (itemText.equals(FILLIN_MINUTE_TIME))
837                         {
838                             int topicTime = 0;
839 
840                             try
841                             {
842                                 topicTime = (new Integer((String) topic[3].Value)).intValue();
843                             }
844                             catch (Exception ex)
845                             {
846                             }
847                             // if the topic has no time, we do not display any time here.
848                             if (topicTime == 0 || topicStartTime == 0)
849                             {
850                                 time = (String) topic[3].Value;
851                             }
852                             else
853                             {
854                                 time = getTimeString(String.valueOf(topicStartTime)) + " - ";
855                                 topicStartTime += topicTime * 1000;
856                                 time += getTimeString(String.valueOf(topicStartTime));
857                             }
858                             fillMinutesItem(item, time, PropertyNames.EMPTY_STRING);
859                         }
860                     }
861 
862                     textSectionHandler.removeTextSectionbyName(SECTION_MINUTES);
863 
864                     // after the last section we do not insert a new one.
865                     if (i < topicsData.size() - 2)
866                     {
867                         textSectionHandler.insertTextSection(SECTION_MINUTES, template, false);
868                     }
869                 }
870             }
871             catch (Exception ex)
872             {
873                 ex.printStackTrace();
874             }
875         }
876     }
877 
878     /**
879      * given a text range and a text, fills the given
880      * text range with the given text.
881      * If the given text is empty, uses a placeholder with the giveb placeholder text.
882      * @param range text range to fill
883      * @param text the text to fill to the text range object.
884      * @param placeholder the placeholder text to use, if the text argument is empty (null or PropertyNames.EMPTY_STRING)
885      */
fillMinutesItem(XTextRange range, Object text, String placeholder)886     private void fillMinutesItem(XTextRange range, Object text, String placeholder)
887     {
888         String paraStyle = (String) Helper.getUnoPropertyValue(range, "ParaStyleName");
889         range.setString((String) text);
890         Helper.setUnoPropertyValue(range, "ParaStyleName", paraStyle);
891         if (text == null || text.equals(PropertyNames.EMPTY_STRING))
892         {
893             if (placeholder != null && !placeholder.equals(PropertyNames.EMPTY_STRING))
894             {
895                 XTextContent placeHolder = createPlaceHolder(docMSF, placeholder, resources.resPlaceHolderHint);
896                 try
897                 {
898                     range.getStart().getText().insertTextContent(range.getStart(), placeHolder, true);
899                 }
900                 catch (Exception ex)
901                 {
902                     ex.printStackTrace();
903                 }
904             }
905 
906         }
907     }
908 
909     /**
910      * creates a placeholder field with the given text and given hint.
911      * @param xmsf service factory
912      * @param ph place holder text
913      * @param hint hint text
914      * @return the place holder field.
915      */
createPlaceHolder(XMultiServiceFactory xmsf, String ph, String hint)916     public static XTextContent createPlaceHolder(XMultiServiceFactory xmsf, String ph, String hint)
917     {
918         Object placeHolder;
919         try
920         {
921             placeHolder = xmsf.createInstance("com.sun.star.text.TextField.JumpEdit");
922         }
923         catch (Exception ex)
924         {
925             ex.printStackTrace();
926             return null;
927         }
928         Helper.setUnoPropertyValue(placeHolder, "PlaceHolder", ph);
929         Helper.setUnoPropertyValue(placeHolder, "Hint", hint);
930         Helper.setUnoPropertyValue(placeHolder, "PlaceHolderType", new Short(PlaceholderType.TEXT));
931         return UnoRuntime.queryInterface(XTextContent.class, placeHolder);
932 
933     }
934 
935     /*
936      * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
937      * =================================
938      *  The ItemTable class
939      * =================================
940      * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
941      */
942     public class ItemsTable
943     {
944 
945         Object table;
946         Object section;
947         /**
948          * the items in the table.
949          */
950         List items = new Vector(6);
951 
ItemsTable(Object section_, Object table_)952         public ItemsTable(Object section_, Object table_)
953         {
954 
955             table = table_;
956             section = section_;
957 
958             AgendaItem ai;
959             XTextRange item;
960             String iText;
961 
962             /* go through all <*> items in the document
963              * and each one if it is in this table.
964              * If they are, register them to belong here, notice their order
965              * and remove them from the list of all <*> items, so the next
966              * search will be faster.
967              */
968             for (int i = 0; i < _allItems.size(); i++)
969             {
970                 item = (XTextRange) _allItems.get(i);
971                 Object t = Helper.getUnoPropertyValue(item, "TextTable");
972                 if ((t instanceof Any) && ((Any) t).getObject() == table)
973                 {
974                     iText = item.getString().toLowerCase().trim();
975                     ai = (AgendaItem) itemsCache.get(item.getString().toLowerCase().trim());
976                     if (ai != null)
977                     {
978                         items.add(ai);
979                         _allItems.remove(i--);
980                         itemsMap.put(iText, this);
981                     }
982                 }
983             }
984 
985         }
986 
987         /**
988          * link the section to the template. this will restore the original table
989          * with all the items.<br/>
990          * then break the link, to make the section editable.<br/>
991          * then, starting at cell one, write all items that should be visible.
992          * then clear the rest and remove obsolete rows.
993          * If no items are visible, hide the section.
994          * @param dummy we need a param to make this an Implementation of AgendaElement.
995          * @throws Exception
996          */
write(Object dummy)997         public synchronized void write(Object dummy) throws Exception
998         {
999             synchronized(this)
1000             {
1001                 String name = getName(section);
1002 
1003                 // link and unlink the section to the template.
1004                 textSectionHandler.linkSectiontoTemplate(section, template, name);
1005                 textSectionHandler.breakLinkOfTextSection(section);
1006 
1007                 // we need to get a new instance after linking.
1008                 table = getTable(name);
1009                 section = getSection(name);
1010 
1011                 XTextTable xTextTable = UnoRuntime.queryInterface(XTextTable.class, table);
1012                 XTextTableCursor cursor = xTextTable.createCursorByCellName("A1");
1013                 AgendaItem ai;
1014                 // should this section be visible?
1015                 boolean visible = false;
1016 
1017                 // write items
1018                 // ===========
1019                 String cellName = PropertyNames.EMPTY_STRING;
1020 
1021                 /* now go through all items that belong to this
1022                  * table. Check each one agains the model. If it should
1023                  * be display, call it's write method.
1024                  * All items are of type AgendaItem which means they write
1025                  * two cells to the table: a title (text) and a placeholder.
1026                  * see AgendaItem class below.
1027                  */
1028                 for (int i = 0; i < items.size(); i++)
1029                 {
1030                     ai = (AgendaItem) items.get(i);
1031                     if (isShowItem(ai.name))
1032                     {
1033                         visible = true;
1034                         ai.table = table;
1035                         ai.write(cursor);
1036                         // I store the cell name which was last written...
1037                         cellName = cursor.getRangeName();
1038 
1039                         cursor.goRight((short) 1, false);
1040 
1041                     }
1042                 }
1043 
1044                 Helper.setUnoPropertyValue(section, "IsVisible", visible ? Boolean.TRUE : Boolean.FALSE);
1045                 if (!visible)
1046                 {
1047                     return;
1048                 /* remove obsolete rows
1049                  * ====================
1050                  * if the cell that was last written is the current cell,
1051                  * it means this is the end of the table, so we end here.
1052                  * (because after getting the cellName above, I call the goRight method.
1053                  * If it did not go right, it means its the last cell.
1054                  */
1055                 }
1056                 if (cellName.equals(cursor.getRangeName()))
1057                 {
1058                     return;
1059                 /*
1060                  * if not, we continue and clear all cells until we are at the end of the row.
1061                  */
1062                 }
1063                 Object cell;
1064                 while ((!cellName.equals(cursor.getRangeName()) && (!cursor.getRangeName().startsWith("A"))))
1065                 {
1066                     cell = xTextTable.getCellByName(cursor.getRangeName());
1067                     UnoRuntime.queryInterface(XTextRange.class, cell).setString(PropertyNames.EMPTY_STRING);
1068                     cellName = cursor.getRangeName();
1069                     cursor.goRight((short) 1, false);
1070                 }
1071 
1072                 /*
1073                  * again: if we are at the end of the table, end here.
1074                  */
1075                 if (cellName.equals(cursor.getRangeName()))
1076                 {
1077                     return;
1078                 }
1079                 int rowIndex = getRowIndex(cursor);
1080                 int rowsCount = getRowCount(UnoRuntime.queryInterface(XTextTable.class, table));
1081 
1082                 /* now before deleteing i move the cursor up so it
1083                  * does not disappear, because it will crash office.
1084                  */
1085                 cursor.gotoStart(false);
1086 
1087                 if (rowsCount >= rowIndex)
1088                 {
1089                     removeTableRows(table, rowIndex - 1, (rowsCount - rowIndex) + 1);
1090                 }
1091             }
1092         }
1093     }
1094 
1095     /*
1096      * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
1097      * =================================
1098      *  The Topics class
1099      * =================================
1100      * $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
1101      */
1102     /**
1103      * This class handles the preview of the topics table.
1104      * You can call it the controller of the topics table.
1105      * It differs from ItemsTable in that it has no data model -
1106      * the update is done programttically.<br/>
1107      * <br/>
1108      * The decision to make this class a class by its own
1109      * was done out of logic reasons and not design/functionality reasons,
1110      * since there is anyway only one instance of this class at runtime
1111      * it could have also be implemented in the AgendaTemplate class
1112      * but for clarity and separation I decided to make a sub class for it.
1113      *
1114      * @author rp143992
1115      */
1116     public class Topics
1117     {
1118 
1119         /**
1120          * the topics table
1121          */
1122         XTextTable table;
1123         /**
1124          * A List of Cell Formatters for the first row.
1125          */
1126         List firstRowFormat = new Vector();
1127         /**
1128          * A List of Cell Formatters for the last row.
1129          * (will contain them in reverse order)
1130          */
1131         List lastRowFormat = new Vector();
1132         /**
1133          * the format of the cell of each topic cell.
1134          */
1135         List topicCellFormats = new Vector();
1136         /**
1137          * for each topic cell there is
1138          * a member in this vector
1139          */
1140         List topicCells = new Vector();
1141         int rowsPerTopic;
1142         /**
1143          * fields which hold the number of the
1144          * fillins in the cells vectors.
1145          */
1146         int numCell = -1;
1147         int topicCell = -1;
1148         int responsibleCell = -1;
1149         int timeCell = -1;
1150         /**
1151          * this is a list which traces which topics were written to the document
1152          * and which not. When a cell needs to be actualized, it is checked that the
1153          * whole topic is already present in the document, using this vector.
1154          * The vector contains nulls for topics which were not written, and
1155          * empty strings for topics which were written (though any other
1156          * object would also do - i check only if it is a null or not...);
1157          */
1158         List writtenTopics = new Vector();
1159 
1160         /**
1161          * Analyze the structure of the Topics table.
1162          * The structure Must be as follows:<br>
1163          * -One Header Row. <br>
1164          * -arbitrary number of rows per topic <br>
1165          * -arbitrary content in the topics row <br>
1166          * -only soft formatting will be restored. <br>
1167          * -the topic rows must repeat three times. <br>
1168          * -in the topics rows, placeholders for number, topic, responsible, and duration
1169          * must be placed.<br>
1170          * <br>
1171          * A word about table format: to reconstruct the format of the
1172          * table we hold to the following formats: first row (header), topic, and last row.
1173          * We hold the format of the last row, because one might wish to give it
1174          * a special format, other than the one on the bottom of each topic.
1175          * The left and right borders of the whole table are, on the other side,
1176          * part of the topics rows format, and need not be preserved seperateley.
1177          */
Topics()1178         public Topics()
1179         {
1180             Object t;
1181 
1182             Map topicItems = new Hashtable(4);
1183 
1184             // This is the topics table. say hallo :-)
1185             try
1186             {
1187                 t = getTable(SECTION_TOPICS);
1188             }
1189             catch (Exception ex)
1190             {
1191                 ex.printStackTrace();
1192                 throw new IllegalArgumentException("Fatal error while loading template: table " + SECTION_TOPICS + " could not load.");
1193             }
1194 
1195             // and this is the XTable.
1196             table = UnoRuntime.queryInterface(XTextTable.class, t);
1197 
1198             /* first I store all <*> ranges
1199              * which are in the topics table.
1200              * I store each <*> range in this - the key
1201              * is the cell it is in. Later when analyzing the topic,
1202              * cell by cell, I check in this map to know
1203              * if a cell contains a <*> or not.
1204              */
1205             Hashtable items = new Hashtable();
1206 
1207             XTextRange item;
1208             Object cell;
1209             for (int i = 0; i < _allItems.size(); i++)
1210             {
1211                 item = (XTextRange) _allItems.get(i);
1212                 t = Helper.getUnoPropertyValue(item, "TextTable");
1213                 if ((t instanceof Any) && ((Any) t).getObject() == table)
1214                 {
1215                     cell = Helper.getUnoPropertyValue(item, "Cell");
1216                     items.put(((Any) cell).getObject(), item);
1217                 }
1218             }
1219 
1220             /*
1221              * in the topics table, there are always one
1222              * title row and three topics defined.
1223              * So no mutter how many rows a topic takes - we
1224              * can restore its structure and format.
1225              */
1226             int rows = getRowCount(table);
1227 
1228             rowsPerTopic = (rows - 1) / 3;
1229 
1230             String firstCell = "A" + (1 + rowsPerTopic + 1);
1231             String afterLastCell = "A" + (1 + (rowsPerTopic * 2) + 1);
1232 
1233             // go to the first row of the 2. topic
1234             XTextTableCursor cursor = table.createCursorByCellName(firstCell);
1235             XTextRange range;
1236 
1237             // analyze the structure of the topic rows.
1238             while (!cursor.getRangeName().equals(afterLastCell))
1239             {
1240                 cell = table.getCellByName(cursor.getRangeName());
1241                 XTextRange xTextRange = UnoRuntime.queryInterface(XTextRange.class, cell);
1242                 // first I store the content and para style of the cell
1243                 AgendaElement ae = new TextElement(xTextRange);
1244                 // if the cell contains a relevant <...>
1245                 // i add the text element to the hash,
1246                 // so it's text can be updated later.
1247                 range = (XTextRange) items.get(cell);
1248                 if (range != null)
1249                 {
1250                     topicItems.put(xTextRange.getString().toLowerCase().trim(), ae);
1251                 }
1252 
1253                 topicCells.add(ae);
1254 
1255                 // and store the format of the cell.
1256                 topicCellFormats.add(new TableCellFormatter(table.getCellByName(cursor.getRangeName())));
1257 
1258                 // goto next cell.
1259                 cursor.goRight((short) 1, false);
1260             }
1261 
1262             /*
1263              * now - in which cell is every fillin?
1264              */
1265             numCell = topicCells.indexOf(topicItems.get(FILLIN_TOPIC_NUMBER));
1266             topicCell = topicCells.indexOf(topicItems.get(FILLIN_TOPIC_TOPIC));
1267             responsibleCell = topicCells.indexOf(topicItems.get(FILLIN_TOPIC_RESPONSIBLE));
1268             timeCell = topicCells.indexOf(topicItems.get(FILLIN_TOPIC_TIME));
1269 
1270 
1271 
1272             /* now that we know how the topics look like,
1273              * we get the format of the first and last rows.
1274              */
1275 
1276             // format of first row
1277             cursor.gotoStart(false);
1278             do
1279             {
1280                 firstRowFormat.add(new TableCellFormatter(table.getCellByName(cursor.getRangeName())));
1281                 cursor.goRight((short) 1, false);
1282             }
1283             while (!cursor.getRangeName().startsWith("A"));
1284 
1285             // format of the last row
1286             cursor.gotoEnd(false);
1287             while (!cursor.getRangeName().startsWith("A"))
1288             {
1289                 lastRowFormat.add(new TableCellFormatter(table.getCellByName(cursor.getRangeName())));
1290                 cursor.goLeft((short) 1, false);
1291             }
1292             // we missed the A cell - so we have to add it also..
1293             lastRowFormat.add(new TableCellFormatter(table.getCellByName(cursor.getRangeName())));
1294 
1295             removeTableRows(table, 1 + rowsPerTopic, rows - rowsPerTopic - 1);
1296 
1297         }
1298 
1299         /**
1300          * @param topic the topic number to write
1301          * @param data the data of the topic.
1302          * @return the number of rows that have been added
1303          * to the table. 0 or a negative number: no rows added.
1304          */
write2(int topic, PropertyValue[] data)1305         private int write2(int topic, PropertyValue[] data) throws Exception
1306         {
1307             while (topic >= writtenTopics.size())
1308             {
1309                 writtenTopics.add(null);
1310             }
1311             writtenTopics.set(topic, PropertyNames.EMPTY_STRING);
1312 
1313             // make sure threr are enough rows for me...
1314             int rows = getRowCount(table);
1315             int reqRows = 1 + (topic + 1) * rowsPerTopic;
1316             int firstRow = reqRows - rowsPerTopic + 1;
1317             int diff = reqRows - rows;
1318             if (diff > 0)
1319             {
1320                 insertTableRows(table, rows, diff);            // set the item's text...
1321             }
1322             setItemText(numCell, data[0].Value);
1323             setItemText(topicCell, data[1].Value);
1324             setItemText(responsibleCell, data[2].Value);
1325             setItemText(timeCell, data[3].Value);
1326 
1327             // now write !
1328             XTextTableCursor cursor = table.createCursorByCellName("A" + firstRow);
1329 
1330             for (int i = 0; i < topicCells.size(); i++)
1331             {
1332                 ((AgendaElement) topicCells.get(i)).write(table.getCellByName(cursor.getRangeName()));
1333                 cursor.goRight((short) 1, false);
1334             }
1335 
1336             // now format !
1337             cursor.gotoCellByName("A" + firstRow, false);
1338 
1339             formatTable(cursor, topicCellFormats, false);
1340 
1341             return diff;
1342 
1343         }
1344 
1345         /**
1346          * check if the topic with the given index is written to the table.
1347          * @param topic the topic number (0 base)
1348          * @return true if the topic is already written to the table. False if not.
1349          * (false would mean new rows must be added to the table in order to
1350          * be able to write this topic).
1351          */
isWritten(int topic)1352         private boolean isWritten(int topic)
1353         {
1354             return (writtenTopics.size() > topic && writtenTopics.get(topic) != null);
1355         }
1356 
1357         /**
1358          * rewrites a single cell containing.
1359          * This is used in order to refresh the topic/responsible/duration data in the
1360          * preview document, in response to a change in the gui (by the user).
1361          * Since the structure of the topics table is flexible, we don't reference a cell
1362          * number. Rather, we use "what" argument to specify which cell should be redrawn.
1363          * The Topics object, which analyzed the structure of the topics table appon
1364          * initialization, refreshes the approperiate cell.
1365          * @param topic index of the topic (0 based).
1366          * @param what 0 for num, 1 for topic, 2 for responsible, 3 for duration
1367          * @param data the row's data.
1368          * @throws Exception if something goes wrong (thow nothing should)
1369          */
writeCell(int topic, int what, PropertyValue[] data)1370         public void writeCell(int topic, int what, PropertyValue[] data) throws Exception
1371         {
1372             // if the whole row should be written...
1373             if (!isWritten(topic))
1374             {
1375                 write(topic, data);
1376             // write only the "what" cell.
1377             }
1378             else
1379             {
1380                 // calculate the table row.
1381                 int firstRow = 1 + (topic * rowsPerTopic) + 1;
1382                 // go to the first cell of this topic.
1383                 XTextTableCursor cursor = table.createCursorByCellName("A" + firstRow);
1384 
1385                 TextElement te = null;
1386                 int cursorMoves = 0;
1387 
1388                 switch (what)
1389                 {
1390                     case 0:
1391                         te = setItemText(numCell, data[0].Value);
1392                         cursorMoves = numCell;
1393                         break;
1394                     case 1:
1395                         te = setItemText(topicCell, data[1].Value);
1396                         cursorMoves = topicCell;
1397                         break;
1398                     case 2:
1399                         te = setItemText(responsibleCell, data[2].Value);
1400                         cursorMoves = responsibleCell;
1401                         break;
1402                     case 3:
1403                         te = setItemText(timeCell, data[3].Value);
1404                         cursorMoves = timeCell;
1405                         break;
1406                 }
1407                 // move the cursor to the needed cell...
1408                 if ( te != null)
1409                 {
1410                     cursor.goRight((short) cursorMoves, false);
1411                     XCell xc = table.getCellByName(cursor.getRangeName());
1412                     // and write it !
1413                     te.write(xc);
1414                     ((TableCellFormatter) topicCellFormats.get(cursorMoves)).format(xc);
1415                 }
1416             }
1417         }
1418 
1419         /**
1420          * writes the given topic.
1421          * if the first topic was involved, reformat the
1422          * first row.
1423          * If any rows were added to the table, reformat
1424          * the last row.
1425          * @param topic the index of the topic to write.
1426          * @param data the topic's data. (see TopicsControl
1427          * for explanation about the topics data model)
1428          * @throws Exception if something goes wrong (though nothing should).
1429          */
write(int topic, PropertyValue[] data)1430         public void write(int topic, PropertyValue[] data) throws Exception
1431         {
1432             int diff = write2(topic, data);
1433             /* if the first topic has been written,
1434              * one needs to reformat the first row.
1435              */
1436             if (topic == 0)
1437             {
1438                 formatFirstRow();
1439             }
1440             /*
1441              * if any rows were added, one needs to format
1442              * the whole table again.
1443              */
1444             if (diff > 0)
1445             {
1446                 formatLastRow();
1447             }
1448         }
1449 
1450         /**
1451          * Writes all the topics to thetopics table.
1452          * @param topicsData a List containing all Topic's Data.
1453          */
writeAll(List topicsData)1454         public void writeAll(List topicsData)
1455         {
1456             try
1457             {
1458                 for (int i = 0; i < topicsData.size() - 1; i++)
1459                 {
1460                     write2(i, (PropertyValue[]) topicsData.get(i));
1461                 }
1462                 formatLastRow();
1463             }
1464             catch (Exception ex)
1465             {
1466                 ex.printStackTrace();
1467             }
1468         }
1469 
1470         /**
1471          * removes obsolete rows, reducing the
1472          * topics table to the given number of topics.
1473          * Note this method does only reducing - if
1474          * the number of topics given is greater than the
1475          * number of actuall topics it does *not* add
1476          * new rows !
1477          * Note also that the first topic will never be removed.
1478          * If the table contains no topics, the whole section will
1479          * be removed uppon finishing.
1480          * The reason for that is a "table-design" one: the first topic is
1481          * maintained in order to be able to add rows with a design of this topic,
1482          * and not of the header row.
1483          * @param topics the number of topics the table should contain.
1484          * @throws Exception
1485          */
reduceDocumentTo(int topics)1486         public void reduceDocumentTo(int topics) throws Exception
1487         {
1488             // we never remove the first topic...
1489             if (topics <= 0)
1490             {
1491                 topics = 1;
1492             }
1493             XTableRows tableRows = table.getRows();
1494             int targetNumOfRows = topics * rowsPerTopic + 1;
1495             if (tableRows.getCount() > targetNumOfRows)
1496             {
1497                 tableRows.removeByIndex(targetNumOfRows, tableRows.getCount() - targetNumOfRows);
1498             }
1499             formatLastRow();
1500             while (writtenTopics.size() > topics)
1501             {
1502                 writtenTopics.remove(topics);
1503             }
1504         }
1505 
1506         /**
1507          * reapply the format of the first (header) row.
1508          */
formatFirstRow()1509         private void formatFirstRow()
1510         {
1511             XTextTableCursor cursor = table.createCursorByCellName("A1");
1512             formatTable(cursor, firstRowFormat, false);
1513         }
1514 
1515         /**
1516          * reaply the format of the last row.
1517          */
formatLastRow()1518         private void formatLastRow()
1519         {
1520             XTextTableCursor cursor = table.createCursorByCellName("A1");
1521             cursor.gotoEnd(false);
1522             formatTable(cursor, lastRowFormat, true);
1523         }
1524 
1525         /**
1526          * returns a text element for the given cell,
1527          * which will write the given text.
1528          * @param cell the topics cell number.
1529          * @param value the value to write.
1530          * @return a TextElement object which will write the given value
1531          * to the given cell.
1532          */
setItemText(int cell, Object value)1533         private TextElement setItemText(int cell, Object value)
1534         {
1535             if (cell >= 0)
1536             {
1537                 TextElement te = ((TextElement) topicCells.get(cell));
1538                 if (te != null)
1539                 {
1540                     te.text = value.toString();
1541                 }
1542                 return te;
1543             }
1544             return null;
1545         }
1546 
1547         /**
1548          * formats a series of cells from the given one,
1549          * using the given List of TableCellFormatter objects,
1550          * in the given order.
1551          * This method is used to format the first (header) and the last
1552          * rows of the table.
1553          * @param cursor a table cursor, pointing to the start cell to format
1554          * @param formats a List containing TableCellFormatter objects. Each will format one cell in the direction specified.
1555          * @param reverse if true the cursor will move left, formatting in reverse order (used for the last row).
1556          */
formatTable(XTextTableCursor cursor, List formats, boolean reverse)1557         private void formatTable(XTextTableCursor cursor, List formats, boolean reverse)
1558         {
1559             for (int i = 0; i < formats.size(); i++)
1560             {
1561                 ((TableCellFormatter) formats.get(i)).format(table.getCellByName(cursor.getRangeName()));
1562                 if (reverse)
1563                 {
1564                     cursor.goLeft((short) 1, false);
1565                 }
1566                 else
1567                 {
1568                     cursor.goRight((short) 1, false);
1569                 }
1570             }
1571         }
1572     }
1573 
1574 
1575     /*
1576      * =================================
1577      * Here are some static help methods
1578      * =================================
1579      */
getNamesWhichStartWith(String[] allNames, String prefix)1580     public static String[] getNamesWhichStartWith(String[] allNames, String prefix)
1581     {
1582         ArrayList<String> v = new ArrayList<String>();
1583         for (int i = 0; i < allNames.length; i++)
1584         {
1585             if (allNames[i].startsWith(prefix))
1586             {
1587                 v.add(allNames[i]);
1588             }
1589         }
1590         String[] s = new String[v.size()];
1591         return v.toArray(s);
1592     }
1593 
1594     /**
1595      * Convenience method, costs the given object to an XNamed, and returnes its name.
1596      * @param obj an XNamed object.
1597      * @return the name of the given object.
1598      */
getName(Object obj)1599     public static String getName(Object obj)
1600     {
1601         return UnoRuntime.queryInterface(XNamed.class, obj).getName();
1602     }
1603 
1604     /**
1605      * convenience method, for removing a number of cells from a table.
1606      * @param table
1607      * @param start
1608      * @param count
1609      */
removeTableRows(Object table, int start, int count)1610     public static void removeTableRows(Object table, int start, int count)
1611     {
1612         XTableRows rows = UnoRuntime.queryInterface(XTextTable.class, table).getRows();
1613         rows.removeByIndex(start, count);
1614     }
1615 
1616     /**
1617      * Convenience method for inserting some cells into a table.
1618      * @param table
1619      * @param start
1620      * @param count
1621      */
insertTableRows(Object table, int start, int count)1622     public static void insertTableRows(Object table, int start, int count)
1623     {
1624         XTableRows rows = UnoRuntime.queryInterface(XTextTable.class, table).getRows();
1625         rows.insertByIndex(start, count);
1626     }
1627 
1628     /**
1629      * returns the row index for this cursor, assuming
1630      * the cursor points to a single cell.
1631      * @param cursor
1632      * @return the row index in which the cursor is.
1633      */
getRowIndex(XTextTableCursor cursor)1634     public static int getRowIndex(XTextTableCursor cursor)
1635     {
1636         return getRowIndex(cursor.getRangeName());
1637     }
1638 
1639     /**
1640      * returns the row index for this cell name.
1641      * @param cellName
1642      * @return the row index for this cell name.
1643      */
getRowIndex(String cellName)1644     public static int getRowIndex(String cellName)
1645     {
1646         return Integer.parseInt(cellName.substring(1));
1647     }
1648 
1649     /**
1650      * returns the rows count of this table, assuming
1651      * there is no vertical merged cells.
1652      * @param table
1653      * @return the rows count of the given table.
1654      */
getRowCount(XTextTable table)1655     public static int getRowCount(XTextTable table)
1656     {
1657         String[] cells = table.getCellNames();
1658         return getRowIndex(cells[cells.length - 1]);
1659     }
1660 }
1661 
1662 /*
1663  * ===========================================================================================
1664  *
1665  *                  End of AgendaTempalte class
1666  *
1667  * ===========================================================================================
1668  *
1669  */
1670 /*
1671  * =================================
1672  *  The AgendaElement interface
1673  * =================================
1674  */
1675 /**
1676  * Interface that is used for writing content to a Uno Text / TextRange
1677  * @author rp143992
1678  *
1679  */
1680 interface AgendaElement
1681 {
1682 
write(Object any)1683     void write(Object any) throws Exception;
1684 }
1685 
1686 
1687 /*
1688  * =================================
1689  *  The ParaStyled class
1690  * =================================
1691  */
1692 /**
1693  * Basic implementation of the AgendaElement interface -
1694  * writes nothing, but applies a ParaStyle to the given XText/XTextRange
1695  * @author rp143992
1696  *
1697  * TODO To change the template for this generated type comment go to
1698  * Window - Preferences - Java - Code Style - Code Templates
1699  */
1700 class ParaStyled implements AgendaElement
1701 {
1702 
1703     String paraStyle;
1704 
ParaStyled(String paraStyle_)1705     ParaStyled(String paraStyle_)
1706     {
1707         paraStyle = paraStyle_;
1708     }
1709 
format(Object textRange)1710     void format(Object textRange)
1711     {
1712         XText o;
1713         o = UnoRuntime.queryInterface(XText.class, textRange);
1714         if (o == null)
1715         {
1716             o = UnoRuntime.queryInterface(XTextRange.class, textRange).getText();
1717         }
1718         XTextRange xtr = UnoRuntime.queryInterface(XTextRange.class, textRange);
1719         XTextCursor cursor = o.createTextCursorByRange(xtr);
1720 
1721         Helper.setUnoPropertyValue(cursor, "ParaStyleName", paraStyle);
1722     }
1723 
write(Object textRange)1724     public void write(Object textRange)
1725     {
1726         format(textRange);
1727     }
1728 }
1729 
1730 /*
1731  * =================================
1732  *  The TextElement class
1733  * =================================
1734  */
1735 /**
1736  * A basic implementation of AgendaElement:
1737  * writes a String to the given XText/XTextRange, and applies
1738  * a ParaStyle to it (using the parent class).
1739  * @author rp143992
1740  */
1741 class TextElement extends ParaStyled
1742 {
1743 
1744     String text;
1745 
TextElement(XTextRange range)1746     TextElement(XTextRange range)
1747     {
1748         this(range.getString(), (String) Helper.getUnoPropertyValue(range.getStart(), "ParaStyleName"));
1749     }
1750 
TextElement(String text_, String paraStyle_)1751     TextElement(String text_, String paraStyle_)
1752     {
1753         super(paraStyle_);
1754         text = text_;
1755     }
1756 
write(Object textRange)1757     public void write(Object textRange)
1758     {
1759         UnoRuntime.queryInterface(XTextRange.class, textRange).setString(text);
1760         if (!text.equals(PropertyNames.EMPTY_STRING))
1761         {
1762             super.write(textRange);
1763         }
1764     }
1765 }
1766 
1767 /**
1768  * A Text element which, if the text to write is empty (null or PropertyNames.EMPTY_STRING)
1769  * inserts a placeholder instead.
1770  * @author rp143992
1771  *
1772  * TODO To change the template for this generated type comment go to
1773  * Window - Preferences - Java - Code Style - Code Templates
1774  */
1775 class PlaceholderTextElement extends TextElement
1776 {
1777 
1778     String hint;
1779     String placeHolderText;
1780     XMultiServiceFactory xmsf;
1781 
PlaceholderTextElement(XTextRange textRange, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_)1782     PlaceholderTextElement(XTextRange textRange, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_)
1783     {
1784         super(textRange);
1785         placeHolderText = placeHolderText_;
1786         hint = hint_;
1787         xmsf = xmsf_;
1788     }
1789 
PlaceholderTextElement(String text, String paraStyle, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_)1790     PlaceholderTextElement(String text, String paraStyle, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_)
1791     {
1792         super(text, paraStyle);
1793         placeHolderText = placeHolderText_;
1794         hint = hint_;
1795         xmsf = xmsf_;
1796     }
1797 
write(Object textRange)1798     public void write(Object textRange)
1799     {
1800         super.write(textRange);
1801         if (text == null || text.equals(PropertyNames.EMPTY_STRING))
1802         {
1803             XTextRange xTextRange = UnoRuntime.queryInterface(XTextRange.class, textRange);
1804             try
1805             {
1806                 XTextContent xTextContent = AgendaTemplate.createPlaceHolder(xmsf, placeHolderText, hint);
1807                 xTextRange.getText().insertTextContent(xTextRange.getStart(), xTextContent, true);
1808             }
1809             catch (Exception ex)
1810             {
1811                 ex.printStackTrace();
1812             }
1813         }
1814     }
1815 }
1816 
1817 /*
1818  * =================================
1819  *  The PlaceHolder class
1820  * =================================
1821  */
1822 /**
1823  * An Agenda element which writes no text, but inserts a placeholder, and formats
1824  * it using a ParaStyleName.
1825  * @author rp143992
1826  *
1827  */
1828 class PlaceholderElement extends ParaStyled
1829 {
1830 
1831     String hint;
1832     String placeHolderText;
1833     XMultiServiceFactory xmsf;
1834 
PlaceholderElement(String paraStyle, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_)1835     PlaceholderElement(String paraStyle, String placeHolderText_, String hint_, XMultiServiceFactory xmsf_)
1836     {
1837         super(paraStyle);
1838         placeHolderText = placeHolderText_;
1839         hint = hint_;
1840         xmsf = xmsf_;
1841     }
1842 
write(Object textRange)1843     public void write(Object textRange)
1844     {
1845         XTextRange xTextRange = UnoRuntime.queryInterface(XTextRange.class, textRange);
1846         try
1847         {
1848             XTextContent xTextContent = AgendaTemplate.createPlaceHolder(xmsf, placeHolderText, hint);
1849             xTextRange.getText().insertTextContent(xTextRange.getStart(), xTextContent, true);
1850             super.write(textRange);
1851         }
1852         catch (Exception ex)
1853         {
1854             ex.printStackTrace();
1855         }
1856     }
1857 }
1858 
1859 
1860 /*
1861  * =================================
1862  *  The AgendaItem class
1863  * =================================
1864  */
1865 /**
1866  * An implementation of AgendaElement which
1867  * gets as a parameter a table cursor, and writes
1868  * a text to the cell marked by this table cursor, and
1869  * a place holder to the next cell.
1870  * @author rp143992
1871  *
1872  * TODO To change the template for this generated type comment go to
1873  * Window - Preferences - Java - Code Style - Code Templates
1874  */
1875 class AgendaItem implements AgendaElement
1876 {
1877 
1878     TextElement textElement;
1879     AgendaElement field;
1880     public Object table;
1881     String name;
1882 
AgendaItem(String name_, TextElement te, AgendaElement f)1883     AgendaItem(String name_, TextElement te, AgendaElement f)
1884     {
1885         name = name_;
1886         field = f;
1887         textElement = te;
1888     }
1889 
write(Object tableCursor)1890     public void write(Object tableCursor) throws Exception
1891     {
1892         XTextTableCursor xTextTableCursor = UnoRuntime.queryInterface(XTextTableCursor.class, tableCursor);
1893         XTextTable xTextTable = UnoRuntime.queryInterface(XTextTable.class, table);
1894 
1895         String cellname = xTextTableCursor.getRangeName();
1896         Object cell = xTextTable.getCellByName(cellname);
1897 
1898         textElement.write(cell);
1899 
1900         xTextTableCursor.goRight((short) 1, false);
1901 
1902         //second field is actually always null...
1903         // this is a preparation for adding placeholders.
1904         if (field != null)
1905         {
1906             field.write(xTextTable.getCellByName(xTextTableCursor.getRangeName()));
1907         }
1908     }
1909 }
1910 
1911 /*
1912  * =================================
1913  *  The TableCellFormatter class
1914  * =================================
1915  */
1916 /**
1917  * reads/write a table cell format from/to a table cell or a group of cells.
1918  *
1919  */
1920 class TableCellFormatter
1921 {
1922 
1923     static String[] properties = new String[]
1924     {
1925         "BackColor",
1926         "BackTransparent",
1927         "BorderDistance",
1928         "BottomBorder",
1929         "BottomBorderDistance",
1930         "LeftBorder",
1931         "LeftBorderDistance",
1932         "RightBorder",
1933         "RightBorderDistance",
1934         "TopBorder",
1935         "TopBorderDistance"
1936     };
1937     private Object[] values = new Object[properties.length];
1938 
TableCellFormatter(Object tableCell)1939     public TableCellFormatter(Object tableCell)
1940     {
1941         for (int i = 0; i < properties.length; i++)
1942         {
1943             values[i] = Helper.getUnoPropertyValue(tableCell, properties[i]);
1944         }
1945     }
1946 
format(Object tableCell)1947     public void format(Object tableCell)
1948     {
1949         Helper.setUnoPropertyValues(tableCell, properties, values);
1950     }
1951 }
1952 
1953 
1954 
1955 
1956