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 compulsory 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, reapplying 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 collision 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 programmatically, event-oriented. 114 */ 115 CGAgenda agenda; 116 /** 117 * the UNO Text Document service 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 * in order to restore them, we need a template to link to. 128 * This is practically an identical 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 AOO 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 unchecked 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 transfer 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 themselves" 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" (appear as >xxx< 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 their 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 ">*<" 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 entirely 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 minutes, 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 given 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 against 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 deleting 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 programmatically.<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 subclass 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 separately. 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 upon 1364 * initialization, refreshes the appropriate 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 (though 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 actual 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 upon 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 * reapply 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 returns 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 AgendaTemplate 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