diff options
Diffstat (limited to 'wizards/com/sun/star/wizards/agenda')
12 files changed, 2987 insertions, 0 deletions
diff --git a/wizards/com/sun/star/wizards/agenda/AgendaDocument.py b/wizards/com/sun/star/wizards/agenda/AgendaDocument.py new file mode 100644 index 000000000..b96fe44cf --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/AgendaDocument.py @@ -0,0 +1,924 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +import uno +import traceback +from ..text.TextElement import TextElement +from ..text.TextDocument import TextDocument +from ..text.TextSectionHandler import TextSectionHandler +from ..common.FileAccess import FileAccess + +from datetime import datetime + +from com.sun.star.text.PlaceholderType import TEXT +from com.sun.star.i18n.NumberFormatIndex import TIME_HHMM, DATE_SYSTEM_LONG + +''' +The classes here implement the whole document-functionality of the agenda wizard: +the live-preview and the final "creation" of the document, +when the user clicks "finish". <br/> +<br/> +<h2>Some terminology:<h2/> +items are names or headings. we don't make any distinction. + +<br/> +The Agenda Template is used as general "controller" +of the whole document, whereas the two child-classes ItemsTable +and TopicsTable control the item tables (note plural!) and the +topics table (note singular).<br/> +<br/> +Other small classes are used to abstract the handling of cells and text and we +try to use them as components. +<br/><br/> +We tried to keep the Agenda Template as flexible as possible, though there +must be many limitations, because it is generated dynamically.<br/><br/> +To keep the template flexible the following decisions were made:<br/> +1. Item tables.<br/> +1.a. there might be arbitrary number of Item tables.<br/> +1.b. Item tables design (bordewr, background) is arbitrary.<br/> +1.c. Items text styles are individual, +and use stylelist styles with predefined names.<br/> +As result the following limitations:<br/> +Pairs of Name->value for each item.<br/> +Tables contain *only* those pairs.<br/> +2. Topics table.<br/> +2.a. arbitrary structure.<br/> +2.b. design is arbitrary.<br/> +As result the following limitations:<br/> +No column merge is allowed.<br/> +One compulsory Heading row.<br/> +<br/><br/> +To let the template be flexible, we use a kind of "detection": we look where +the items are read the design of each table, re-applying it after writing the +table.self.xTextDocument +<br/><br/> +A note about threads:<br/> +Many methods here are synchronized, in order to avoid collision made by +events fired too often. +''' +class AgendaDocument(TextDocument): + + ''' + constructor. The document is *not* loaded here. + only some formal members are set. + ''' + + def __init__(self, xmsf, agenda, resources, templateConsts, listener): + super(AgendaDocument,self).__init__(xmsf,listener, None, + "WIZARD_LIVE_PREVIEW") + self.agenda = agenda + self.templateConsts = templateConsts + self.resources = resources + self.itemsMap = {} + self.allItems = [] + + def load(self, templateURL): + # Each template is duplicated. aw-XXX.ott is the template itself + # and XXX.ott is a section link. + self.template = self.calcTemplateName(templateURL) + self.loadAsPreview(templateURL, False) + self.xFrame.ComponentWindow.Enable = False + self.xTextDocument.lockControllers() + self.initialize() + self.initializeData() + self.xTextDocument.unlockControllers() + + ''' + The agenda templates are in format of aw-XXX.ott + the templates name is then XXX.ott. + This method calculates it. + ''' + + def calcTemplateName(self, url): + return FileAccess.connectURLs( + FileAccess.getParentDir(url), FileAccess.getFilename(url)[3:]) + + '''synchronize the document to the model.<br/> + this method rewrites all titles, item tables , and the topics table- + thus synchronizing the document to the data model (CGAgenda). + information (it is only actualized on save) the given list + supplies this information. + ''' + + def initializeData(self): + for i in self.itemsTables: + try: + i.write() + except Exception: + traceback.print_exc() + + self.redrawTitle("txtTitle") + self.redrawTitle("txtDate") + self.redrawTitle("txtTime") + self.redrawTitle("cbLocation") + + ''' + redraws/rewrites the table which contains the given item + This method is called when the user checks/unchecks an item. + The table is being found, in which the item is, and redrawn. + ''' + + def redraw(self, itemName): + self.xTextDocument.lockControllers() + try: + # get the table in which the item is... + itemsTable = self.itemsMap[itemName] + # rewrite the table. + itemsTable.write() + except Exception: + traceback.print_exc() + self.xTextDocument.unlockControllers() + + ''' + checks the data model if the + item corresponding to the given string should be shown + ''' + + def isShowItem(self, itemName): + if itemName == self.templateConsts.FILLIN_MEETING_TYPE: + return self.agenda.cp_ShowMeetingType + elif itemName == self.templateConsts.FILLIN_READ: + return self.agenda.cp_ShowRead + elif itemName == self.templateConsts.FILLIN_BRING: + return self.agenda.cp_ShowBring + elif itemName == self.templateConsts.FILLIN_NOTES: + return self.agenda.cp_ShowNotes + elif itemName == self.templateConsts.FILLIN_FACILITATOR: + return self.agenda.cp_ShowFacilitator + elif itemName == self.templateConsts.FILLIN_TIMEKEEPER: + return self.agenda.cp_ShowTimekeeper + elif itemName == self.templateConsts.FILLIN_NOTETAKER: + return self.agenda.cp_ShowNotetaker + elif itemName == self.templateConsts.FILLIN_PARTICIPANTS: + return self.agenda.cp_ShowAttendees + elif itemName == self.templateConsts.FILLIN_CALLED_BY: + return self.agenda.cp_ShowCalledBy + elif itemName == self.templateConsts.FILLIN_OBSERVERS: + return self.agenda.cp_ShowObservers + elif itemName == self.templateConsts.FILLIN_RESOURCE_PERSONS: + return self.agenda.cp_ShowResourcePersons + else: + raise ValueError("No such item") + + '''itemsCache is a Map containing all agenda item. These are object which + "write themselves" to the table, given a table cursor. + A cache is used in order to reuse the objects, instead of recreate them. + This method fills the cache will all items objects (names and headings). + ''' + + def initItemsCache(self): + self.itemsCache = {} + # Headings + self.itemsCache[ + self.templateConsts.FILLIN_MEETING_TYPE] = \ + AgendaItem(self.templateConsts.FILLIN_MEETING_TYPE, + self.resources.itemMeetingType, + PlaceholderElement( + self.resources.reschkMeetingTitle_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_BRING] = \ + AgendaItem(self.templateConsts.FILLIN_BRING, + self.resources.itemBring, + PlaceholderElement ( + self.resources.reschkBring_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_READ] = \ + AgendaItem (self.templateConsts.FILLIN_READ, + self.resources.itemRead, + PlaceholderElement ( + self.resources.reschkRead_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_NOTES] = \ + AgendaItem (self.templateConsts.FILLIN_NOTES, + self.resources.itemNote, + PlaceholderElement ( + self.resources.reschkNotes_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + + # Names + self.itemsCache[ + self.templateConsts.FILLIN_CALLED_BY] = \ + AgendaItem(self.templateConsts.FILLIN_CALLED_BY, + self.resources.itemCalledBy, + PlaceholderElement ( + self.resources.reschkConvenedBy_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_FACILITATOR] = \ + AgendaItem(self.templateConsts.FILLIN_FACILITATOR, + self.resources.itemFacilitator, + PlaceholderElement ( + self.resources.reschkPresiding_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_PARTICIPANTS] = \ + AgendaItem(self.templateConsts.FILLIN_PARTICIPANTS, + self.resources.itemAttendees, + PlaceholderElement( + self.resources.reschkAttendees_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_NOTETAKER] = \ + AgendaItem(self.templateConsts.FILLIN_NOTETAKER, + self.resources.itemNotetaker, + PlaceholderElement( + self.resources.reschkNoteTaker_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_TIMEKEEPER] = \ + AgendaItem(self.templateConsts.FILLIN_TIMEKEEPER, + self.resources.itemTimekeeper, + PlaceholderElement( + self.resources.reschkTimekeeper_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_OBSERVERS] = \ + AgendaItem(self.templateConsts.FILLIN_OBSERVERS, + self.resources.itemObservers, + PlaceholderElement( + self.resources.reschkObservers_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + self.itemsCache[ + self.templateConsts.FILLIN_RESOURCE_PERSONS] = \ + AgendaItem(self.templateConsts.FILLIN_RESOURCE_PERSONS, + self.resources.itemResource, + PlaceholderElement( + self.resources.reschkResourcePersons_value, + self.resources.resPlaceHolderHint, self.xTextDocument)) + + '''Initializes a template.<br/> + This method does the following tasks:<br/> + get a Time and Date format for the document, and retrieve the null + date of the document (which is document-specific).<br/> + Initializes the Items Cache map. + Analyses the document:<br/> + -find all "filled-ins" (appear as >xxx< in the document). + -analyze all items sections (and the tables in them). + -locate the titles and actualize them + -analyze the topics table + ''' + + def initialize(self): + ''' + Get the default locale of the document, + and create the date and time formatters. + ''' + self.dateUtils = self.DateUtils(self.xMSF, self.xTextDocument) + self.formatter = self.dateUtils.formatter + self.dateFormat = self.dateUtils.getFormat(DATE_SYSTEM_LONG) + self.timeFormat = self.dateUtils.getFormat(TIME_HHMM) + + self.initItemsCache() + self.allItems = self.searchFillInItems(0) + self.initializeTitles() + self.initializeItemsSections() + self.textSectionHandler = TextSectionHandler( + self.xTextDocument, self.xTextDocument) + self.topics = Topics(self) + + ''' + locates the titles (name, location, date, time) + and saves a reference to their Text ranges. + ''' + + def initializeTitles(self): + auxList = [] + for i in self.allItems: + text = i.String.lstrip().lower() + if text == self.templateConsts.FILLIN_TITLE: + self.teTitle = PlaceholderTextElement( + i, self.resources.resPlaceHolderTitle, + self.resources.resPlaceHolderHint, self.xTextDocument) + self.trTitle = i + elif text == self.templateConsts.FILLIN_DATE: + self.teDate = PlaceholderTextElement( + i, self.resources.resPlaceHolderDate, + self.resources.resPlaceHolderHint, self.xTextDocument) + self.trDate = i + elif text == self.templateConsts.FILLIN_TIME: + self.teTime = PlaceholderTextElement( + i, self.resources.resPlaceHolderTime, + self.resources.resPlaceHolderHint, self.xTextDocument) + self.trTime = i + elif text == self.templateConsts.FILLIN_LOCATION: + self.teLocation = PlaceholderTextElement( + i, self.resources.resPlaceHolderLocation, + self.resources.resPlaceHolderHint, self.xTextDocument) + self.trLocation = i + else: + auxList.append(i) + self.allItems = auxList + + ''' + analyze the item sections in the template. + delegates the analyze of each table to the ItemsTable class. + ''' + + def initializeItemsSections(self): + sections = self.getSections( + self.xTextDocument, self.templateConsts.SECTION_ITEMS) + # for each section - there is a table... + self.itemsTables = [] + for i in sections: + try: + self.itemsTables.append( + ItemsTable(self.getSection(i), self.getTable(i), self)) + except Exception: + traceback.print_exc() + raise AttributeError ( + "Fatal Error while initializing \ + Template: items table in section " + i) + + + def getSections(self, document, s): + allSections = document.TextSections.ElementNames + return self.getNamesWhichStartWith(allSections, s) + + def getSection(self, name): + return self.xTextDocument.TextSections.getByName(name) + + def getTable(self, name): + return self.xTextDocument.TextTables.getByName(name) + + def redrawTitle(self, controlName): + try: + if controlName == "txtTitle": + self.teTitle.placeHolderText = self.agenda.cp_Title + self.teTitle.write(self.trTitle) + elif controlName == "txtDate": + self.teDate.placeHolderText = \ + self.getDateString(self.agenda.cp_Date) + self.teDate.write(self.trDate) + elif controlName == "txtTime": + self.teTime.placeHolderText = self.agenda.cp_Time + self.teTime.write(self.trTime) + elif controlName == "cbLocation": + self.teLocation.placeHolderText = self.agenda.cp_Location + self.teLocation.write(self.trLocation) + else: + raise Exception("No such title control...") + except Exception: + traceback.print_exc() + + def getDateString(self, date): + if not date: + return "" + dateObject = datetime.strptime(date, '%d/%m/%y').date() + return self.dateUtils.format(self.dateFormat, dateObject) + + def finish(self, topics): + self.createMinutes(topics) + self.deleteHiddenSections() + self.textSectionHandler.removeAllTextSections() + + ''' + hidden sections exist when an item's section is hidden because the + user specified not to display any items which it contains. + When finishing the wizard removes this sections + entirely from the document. + ''' + + def deleteHiddenSections(self): + allSections = self.xTextDocument.TextSections.ElementNames + try: + for i in allSections: + self.section = self.getSection(i) + visible = bool(self.section.IsVisible) + if not visible: + self.section.Anchor.String = "" + + except Exception: + traceback.print_exc() + + ''' + create the minutes for the given topics or remove the minutes + section from the document. + If no topics are supplied, or the user specified not to create minutes, + the minutes section will be removed, + @param topicsData supplies PropertyValue arrays containing + the values for the topics. + ''' + + def createMinutes(self, topicsData): + # if the minutes section should be removed (the + # user did not check "create minutes") + if not self.agenda.cp_IncludeMinutes \ + or len(topicsData) <= 1: + try: + minutesAllSection = self.getSection( + self.templateConsts.SECTION_MINUTES_ALL) + minutesAllSection.Anchor.String = "" + except Exception: + traceback.print_exc() + + # the user checked "create minutes" + else: + try: + topicStartTime = int(self.agenda.cp_Time) + # first I replace the minutes titles... + self.items = self.searchFillInItems() + itemIndex = 0 + for item in self.items: + itemText = item.String.lstrip().lower() + if itemText == \ + self.templateConsts.FILLIN_MINUTES_TITLE: + self.fillMinutesItem( + item, self.agenda.cp_Title, + self.resources.resPlaceHolderTitle) + elif itemText == \ + self.templateConsts.FILLIN_MINUTES_LOCATION: + self.fillMinutesItem( + item, self.agenda.cp_Location, + self.resources.resPlaceHolderLocation) + elif itemText == \ + self.templateConsts.FILLIN_MINUTES_DATE: + self.fillMinutesItem( + item, getDateString(self.agenda.cp_Date), + self.resources.resPlaceHolderDate) + elif itemText == \ + self.templateConsts.FILLIN_MINUTES_TIME: + self.fillMinutesItem( item, self.agenda.cp_Time, + self.resources.resPlaceHolderTime) + + self.items.clear() + ''' + now add minutes for each topic. + The template contains *one* minutes section, so + we first use the one available, and then add a one... + topics data has *always* an empty topic at the end... + ''' + + for i in xrange(len(topicsData) - 1): + topic = topicsData[i] + items = self.searchFillInItems() + itemIndex = 0 + for item in items: + itemText = item.String.lstrip().lower() + if itemText == \ + self.templateConsts.FILLIN_MINUTE_NUM: + self.fillMinutesItem(item, topic[0].Value, "") + elif itemText == \ + self.templateConsts.FILLIN_MINUTE_TOPIC: + self.fillMinutesItem(item, topic[1].Value, "") + elif itemText == \ + self.templateConsts.FILLIN_MINUTE_RESPONSIBLE: + self.fillMinutesItem(item, topic[2].Value, "") + elif itemText == \ + self.templateConsts.FILLIN_MINUTE_TIME: + topicTime = 0 + try: + topicTime = topic[3].Value + except Exception: + pass + + ''' + if the topic has no time, we do not + display any time here. + ''' + if topicTime == 0 or topicStartTime == 0: + time = topic[3].Value + else: + time = str(topicStartTime) + " - " + topicStartTime += topicTime * 1000 + time += str(topicStartTime) + + self.fillMinutesItem(item, time, "") + + self.textSectionHandler.removeTextSectionbyName( + self.templateConsts.SECTION_MINUTES) + # after the last section we do not insert a one. + if i < len(topicsData) - 2: + self.textSectionHandler.insertTextSection( + self.templateConsts.SECTION_MINUTES, + self.template, False) + + except Exception: + traceback.print_exc() + + '''given a text range and a text, fills the given + text range with the given text. + If the given text is empty, uses a placeholder with the given + placeholder text. + @param range text range to fill + @param text the text to fill to the text range object. + @param placeholder the placeholder text to use, if the + text argument is empty (null or "") + ''' + + def fillMinutesItem(self, Range, text, placeholder): + paraStyle = Range.ParaStyleName + Range.setString(text) + Range.ParaStyleName = paraStyle + if text is None or text == "": + if placeholder is not None and not placeholder == "": + placeHolder = self.createPlaceHolder( + self.xTextDocument, placeholder, + self.resources.resPlaceHolderHint) + try: + Range.Start.Text.insertTextContent( + Range.Start, placeHolder, True) + except Exception: + traceback.print_exc() + + ''' + creates a placeholder field with the given text and given hint. + ''' + + @classmethod + def createPlaceHolder(self, xmsf, ph, hint): + try: + placeHolder = xmsf.createInstance( + "com.sun.star.text.TextField.JumpEdit") + except Exception: + traceback.print_exc() + return None + + placeHolder.PlaceHolder = ph + placeHolder.Hint = hint + placeHolder.PlaceHolderType = uno.Any("short",TEXT) + return placeHolder + + def getNamesWhichStartWith(self, allNames, prefix): + v = [] + for i in allNames: + if i.startswith(prefix): + v.append(i) + return v + + ''' + Convenience method for inserting some cells into a table. + ''' + + @classmethod + def insertTableRows(self, table, start, count): + rows = table.Rows + rows.insertByIndex(start, count) + + ''' + returns the rows count of this table, assuming + there is no vertical merged cells. + ''' + + @classmethod + def getRowCount(self, table): + cells = table.getCellNames() + return int(cells[len(cells) - 1][1:]) + +class ItemsTable(object): + ''' + the items in the table. + ''' + items = [] + table = None + + def __init__(self, section, table, agenda): + self.agenda = agenda + ItemsTable.table = table + self.section = section + self.items = [] + ''' + go through all <*> items in the document + and each one if it is in this table. + If they are, register them to belong here, notice their order + and remove them from the list of all <*> items, so the next + search will be faster. + ''' + aux = [] + for item in self.agenda.allItems: + t = item.TextTable + if t == ItemsTable.table: + iText = item.String.lower().lstrip() + ai = self.agenda.itemsCache[iText] + if ai is not None: + self.items.append(ai) + self.agenda.itemsMap[iText] = self + else: + aux.append(item) + self.agenda.allItems = aux + + ''' + link the section to the template. this will restore the original table + with all the items.<br/> + then break the link, to make the section editable.<br/> + then, starting at cell one, write all items that should be visible. + then clear the rest and remove obsolete rows. + If no items are visible, hide the section. + ''' + + def write(self): + name = self.section.Name + # link and unlink the section to the template. + self.agenda.textSectionHandler.linkSectiontoTemplate( + self.agenda.template, name, self.section) + self.agenda.textSectionHandler.breakLinkOfTextSection( + self.section) + # we need to get an instance after linking + + ItemsTable.table = self.agenda.getTable(name) + self.section = self.agenda.getSection(name) + cursor = ItemsTable.table.createCursorByCellName("A1") + # should this section be visible? + visible = False + # write items + cellName = "" + ''' + now go through all items that belong to this + table. Check each one against the model. If it should + be displayed, call its write method. + All items are of type AgendaItem which means they write + two cells to the table: a title (text) and a placeholder. + see AgendaItem class below. + ''' + for i in self.items: + if self.agenda.isShowItem(i.name): + visible = True + i.table = ItemsTable.table + i.write(cursor) + # I store the cell name which was last written... + cellName = cursor.RangeName + cursor.goRight(1, False) + + if visible: + boolean = True + else: + boolean = False + self.section.IsVisible = boolean + if not visible: + return + ''' + if the cell that was last written is the current cell, + it means this is the end of the table, so we end here. + (because after getting the cellName above, + I call the goRight method. + If it did not go right, it means it's the last cell. + ''' + + if cellName == cursor.RangeName: + return + ''' + if not, we continue and clear all cells until + we are at the end of the row. + ''' + + while not cellName == cursor.RangeName and \ + not cursor.RangeName.startswith("A"): + cell = ItemsTable.table.getCellByName(cursor.RangeName) + cell.String = "" + cellName = cursor.RangeName + cursor.goRight(1, False) + + ''' + again: if we are at the end of the table, end here. + ''' + if cellName == cursor.RangeName: + return + + ''' + now before deleting i move the cursor up so it + does not disappear, because it will crash office. + ''' + cursor.gotoStart(False) + +''' +This class handles the preview of the topics table. +You can call it the controller of the topics table. +It differs from ItemsTable in that it has no data model - +the update is done programmatically.<br/> +<br/> +The decision to make this class a class by its own +was done out of logic reasons and not design/functionality reasons, +since there is anyway only one instance of this class at runtime +it could have also be implemented in the AgendaDocument class +but for clarity and separation I decided to make a sub class for it. +''' + +class Topics(object): + '''Analyze the structure of the Topics table. + The structure Must be as follows:<br> + -One Header Row. <br> + -arbitrary number of rows per topic <br> + -arbitrary content in the topics row <br> + -only soft formatting will be restored. <br> + -the topic rows must repeat three times. <br> + -in the topics rows, placeholders for number, topic, responsible, + and duration must be placed.<br><br> + A word about table format: to reconstruct the format of the table we hold + to the following formats: first row (header), topic, and last row. + We hold the format of the last row, because one might wish to give it + a special format, other than the one on the bottom of each topic. + The left and right borders of the whole table are, on the other side, + part of the topics rows format, and need not be preserved separately. + ''' + table = None + lastRowFormat = [] + rowsPerTopic = None + + def __init__(self, agenda): + self.firstRowFormat = [] + self.agenda = agenda + self.writtenTopics = -1 + try: + Topics.table = self.agenda.getTable( + self.agenda.templateConsts.SECTION_TOPICS) + except Exception: + traceback.print_exc() + raise AttributeError ( + "Fatal error while loading template: table " + \ + self.agenda.templateConsts.SECTION_TOPICS + " could not load.") + + ''' + first I store all <*> ranges + which are in the topics table. + I store each <*> range in this - the key + is the cell it is in. Later when analyzing the topic, + cell by cell, I check in this map to know + if a cell contains a <*> or not. + ''' + try: + items = {} + for i in self.agenda.allItems: + t = i.TextTable + if t == Topics.table: + cell = i.Cell + iText = cell.CellName + items[iText] = i + + ''' + in the topics table, there are always one + title row and three topics defined. + So no mutter how many rows a topic takes - we + can restore its structure and format. + ''' + rows = self.agenda.getRowCount(Topics.table) + Topics.rowsPerTopic = int((rows - 1) / 3) + + firstCell = "A" + str(1 + Topics.rowsPerTopic + 1) + afterLastCell = "A" + str(1 + (Topics.rowsPerTopic * 2) + 1) + # go to the first row of the 2. topic + + cursor = Topics.table.createCursorByCellName(firstCell) + # analyze the structure of the topic rows. + while not cursor.RangeName == afterLastCell: + cell = Topics.table.getCellByName(cursor.RangeName) + # first I store the content and para style of the cell + ae = TextElement(cell, cell.String) + ae.write() + + # goto next cell. + cursor.goRight(1, False) + except Exception: + traceback.print_exc() + + '''rewrites a single cell containing. + This is used in order to refresh the topic/responsible/duration data + in the preview document, in response to a change in the gui (by the user) + Since the structure of the topics table is flexible, + The Topics object, which analyzed the structure of the topics table upon + initialization, refreshes the appropriate cell. + ''' + def writeCell(self, row, column, data): + # if the whole row should be written... + if self.writtenTopics < row: + self.writtenTopics += 1 + rows = self.agenda.getRowCount(Topics.table) + reqRows = 1 + (row + 1) * Topics.rowsPerTopic + firstRow = reqRows - Topics.rowsPerTopic + 1 + diff = reqRows - rows + if diff > 0: + # set the item's text... + self.agenda.insertTableRows(Topics.table, rows, diff) + column = 0 + cursor = Topics.table.createCursorByCellName("A" + str(firstRow)) + else: + # calculate the table row. + firstRow = 1 + (row * Topics.rowsPerTopic) + 1 + cursor = Topics.table.createCursorByCellName("A" + str(firstRow)) + + # move the cursor to the needed cell... + cursor.goRight(column, False) + + xc = Topics.table.getCellByName(cursor.RangeName) + # and write it ! + te = TextElement(xc, data[column].Value) + te.write() + + '''removes obsolete rows, reducing the + topics table to the given number of topics. + Note this method does only reducing - if + the number of topics given is greater than the + number of actual topics it does *not* add + rows! + Note also that the first topic will never be removed. + If the table contains no topics, the whole section will + be removed upon finishing. + The reason for that is a "table-design" one: the first topic is + maintained in order to be able to add rows with a design of this topic, + and not of the header row. + @param topics the number of topics the table should contain. + @throws Exception + ''' + + def reduceDocumentTo(self, topics): + # we never remove the first topic... + if topics <= 0: + topics = 1 + + tableRows = Topics.table.Rows + targetNumOfRows = topics * Topics.rowsPerTopic + 1 + if tableRows.Count > targetNumOfRows: + tableRows.removeByIndex( + targetNumOfRows, tableRows.Count - targetNumOfRows) + +''' +A Text element which, if the text to write is empty (null or "") +inserts a placeholder instead. +''' + +class PlaceholderTextElement(TextElement): + + def __init__(self, textRange, placeHolderText_, hint_, xmsf_): + super(PlaceholderTextElement,self).__init__(textRange, "") + + self.text = placeHolderText_ + self.hint = hint_ + self.xmsf = xmsf_ + self.xTextContentList = [] + + def write(self, textRange): + textRange.String = self.placeHolderText + if self.placeHolderText is None or self.placeHolderText == "": + try: + xTextContent = AgendaDocument.createPlaceHolder( + self.xmsf, self.text, self.hint) + self.xTextContentList.append(xTextContent) + textRange.Text.insertTextContent( + textRange.Start, xTextContent, True) + except Exception: + traceback.print_exc() + else: + if self.xTextContentList: + for i in self.xTextContentList: + textRange.Text.removeTextContent(i) + self.xTextContentList = [] +''' +An Agenda element which writes no text, but inserts a placeholder, and formats +it using a ParaStyleName. +''' + +class PlaceholderElement(object): + + def __init__(self, placeHolderText_, hint_, textDocument): + self.placeHolderText = placeHolderText_ + self.hint = hint_ + self.textDocument = textDocument + + def write(self, textRange): + try: + xTextContent = AgendaDocument.createPlaceHolder( + self.textDocument, self.placeHolderText, self.hint) + textRange.Text.insertTextContent( + textRange.Start, xTextContent, True) + except Exception: + traceback.print_exc() + +''' +An implementation of AgendaElement which +gets as a parameter a table cursor, and writes +a text to the cell marked by this table cursor, and +a place holder to the next cell. +''' + +class AgendaItem(object): + + def __init__(self, name_, te, f): + self.name = name_ + self.field = f + self.textElement = te + + def write(self, tableCursor): + cellname = tableCursor.RangeName + cell = ItemsTable.table.getCellByName(cellname) + cell.String = self.textElement + tableCursor.goRight(1, False) + # second field is actually always null... + # this is a preparation for adding placeholders. + if self.field is not None: + self.field.write(ItemsTable.table.getCellByName( + tableCursor.RangeName)) diff --git a/wizards/com/sun/star/wizards/agenda/AgendaWizardDialog.py b/wizards/com/sun/star/wizards/agenda/AgendaWizardDialog.py new file mode 100644 index 000000000..991b05f04 --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/AgendaWizardDialog.py @@ -0,0 +1,320 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +from ..ui.WizardDialog import WizardDialog, uno, UIConsts, PropertyNames +from .AgendaWizardDialogConst import AgendaWizardDialogConst, HID +from .AgendaWizardDialogResources import AgendaWizardDialogResources + +from com.sun.star.awt.FontUnderline import SINGLE + +class AgendaWizardDialog(WizardDialog): + + def __init__(self, xmsf): + super(AgendaWizardDialog,self).__init__(xmsf, HID ) + + #Load Resources + self.resources = AgendaWizardDialogResources() + + #set dialog properties... + self.setDialogProperties(True, 210, True, 200, 52, 1, 1, + self.resources.resAgendaWizardDialog_title, 310) + + self.PROPS_LIST = ("Dropdown", + PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + self.PROPS_LABEL_B = ("FontDescriptor", + PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_LABEL, + PropertyNames.PROPERTY_MULTILINE, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + self.PROPS_CHECK = (PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_LABEL, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STATE, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + self.PROPS_BUTTON = (PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_LABEL, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + self.PROPS_X = (PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + self.PROPS_TEXTAREA = (PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_LABEL, + PropertyNames.PROPERTY_MULTILINE, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + self.PROPS_TEXT = (PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_LABEL, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + self.PROPS_IMAGE = ("Border", + PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_IMAGEURL, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + "ScaleImage", PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + + self.fontDescriptor4 = \ + uno.createUnoStruct('com.sun.star.awt.FontDescriptor') + self.fontDescriptor4.Weight = 150 + + def buildStep1(self): + self.insertLabel("lblTitle1", self.PROPS_LABEL_B, + (self.fontDescriptor4, 16, self.resources.reslblTitle1_value, + True, 91, 8, 1, 100,212)) + self.insertLabel("lblPageDesign", self.PROPS_TEXT, + (8, self.resources.reslblPageDesign_value, 97, 32, 1, 101, 66)) + self.listPageDesign = self.insertListBox("listPageDesign", + None, AgendaWizardDialogConst.LISTPAGEDESIGN_ACTION_PERFORMED, + self.PROPS_LIST, + (True, 12, AgendaWizardDialogConst.LISTPAGEDESIGN_HID, + 166, 30, 1, 102, 70), self) + self.chkMinutes = self.insertCheckBox("chkMinutes", None, + self.PROPS_CHECK, (9, AgendaWizardDialogConst.CHKMINUTES_HID, + self.resources.reschkMinutes_value, 97, 50, 0, 1, 103, 203), self) + self.insertImage("imgHelp1", self.PROPS_IMAGE, + (0, 10, "", UIConsts.INFOIMAGEURL, 92, 145, False, 1, 104, 10)) + self.insertLabel("lblHelp1", self.PROPS_TEXTAREA, + (39, self.resources.reslblHelp1_value, + True, 104, 145, 1, 105, 199)) + + def buildStep2(self): + self.insertLabel("lblTitle2", self.PROPS_LABEL_B, + (self.fontDescriptor4, 16, self.resources.reslblTitle2_value, + True, 91, 8, 2, 200, 212)) + self.insertLabel("lblDate", self.PROPS_TEXT, + (8, self.resources.reslblDate_value, 97, 32, 2, 201,66)) + self.txtDate = self.insertDateField( + "txtDate", AgendaWizardDialogConst.TXTDATE_TEXT_CHANGED, + self.PROPS_LIST, + (True, 12, AgendaWizardDialogConst.TXTDATE_HID, + 166,30, 2, 202, 70), self) + self.insertLabel("lblTime", self.PROPS_TEXT, + (8, self.resources.reslblTime_value, 97, 50, 2, 203, 66)) + self.txtTime = self.insertTimeField("txtTime", + AgendaWizardDialogConst.TXTTIME_TEXT_CHANGED, + (PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + "StrictFormat", + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH), + (12, AgendaWizardDialogConst.TXTTIME_HID, + 166, 48, 2, True, 204, 70), self) + self.insertLabel("lblTitle", self.PROPS_TEXT, + (8, self.resources.reslblTitle_value, 97, 68, 2, 205, 66)) + self.txtTitle = self.insertTextField( + "txtTitle", AgendaWizardDialogConst.TXTTITLE_TEXT_CHANGED, + (PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_MULTILINE, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH), + (26, AgendaWizardDialogConst.TXTTITLE_HID, + True, 166, 66, 2, 206, 138), self) + self.insertLabel("lblLocation", self.PROPS_TEXT, + (8, self.resources.reslblLocation_value, 97, 100, 2, 207, 66)) + self.cbLocation = self.insertTextField( + "cbLocation", AgendaWizardDialogConst.TXTLOCATION_TEXT_CHANGED, + (PropertyNames.PROPERTY_HEIGHT, + PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_MULTILINE, + PropertyNames.PROPERTY_POSITION_X, + PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, + PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH), + (34, AgendaWizardDialogConst.CBLOCATION_HID, + True, 166,98, 2, 208, 138), self) + self.insertImage("imgHelp2", self.PROPS_IMAGE, + (0, 10, "", UIConsts.INFOIMAGEURL, 92, 145, False, 2, 209, 10)) + self.insertLabel("lblHelp2", self.PROPS_TEXTAREA, + (39, self.resources.reslblHelp2_value, + True, 104, 145, 2, 210, 199)) + + def buildStep3(self): + self.insertLabel("lblTitle3", self.PROPS_LABEL_B, + (self.fontDescriptor4, 16, self.resources.reslblTitle3_value, + True, 91, 8, 3, 300,212)) + self.chkMeetingTitle = self.insertCheckBox("chkMeetingTitle", + AgendaWizardDialogConst.CHKUSEMEETINGTYPE_ITEM_CHANGED, + self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKMEETINGTITLE_HID, + self.resources.reschkMeetingTitle_value, + 97, 32, 1, 3, 301, 69), self) + self.chkRead = self.insertCheckBox("chkRead", + AgendaWizardDialogConst.CHKUSEREAD_ITEM_CHANGED, self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKREAD_HID, + self.resources.reschkRead_value, 97, 46, 0, 3, 302, 162), self) + self.chkBring = self.insertCheckBox("chkBring", + AgendaWizardDialogConst.CHKUSEBRING_ITEM_CHANGED, self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKBRING_HID, + self.resources.reschkBring_value, + 97, 60, 0, 3, 303, 162), self) + self.chkNotes = self.insertCheckBox("chkNotes", + AgendaWizardDialogConst.CHKUSENOTES_ITEM_CHANGED, self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKNOTES_HID, + self.resources.reschkNotes_value, + 97, 74, 1, 3, 304, 160), self) + self.insertImage("imgHelp3", self.PROPS_IMAGE, (0, 10, + "", UIConsts.INFOIMAGEURL, 92, 145, False, 3, 305, 10)) + self.insertLabel("lblHelp3", self.PROPS_TEXTAREA, + (39, self.resources.reslblHelp3_value, True,104, 145, 3, 306, 199)) + + def buildStep4(self): + self.insertLabel("lblTitle5", self.PROPS_LABEL_B, + (self.fontDescriptor4, 16, self.resources.reslblTitle5_value, + True, 91, 8, 4, 400, 212)) + self.chkConvenedBy = self.insertCheckBox("chkConvenedBy", + AgendaWizardDialogConst.CHKUSECALLEDBYNAME_ITEM_CHANGED, + self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKCONVENEDBY_HID, + self.resources.reschkConvenedBy_value, + 97, 32, 1, 4, 401, 150), self) + self.chkPresiding = self.insertCheckBox("chkPresiding", + AgendaWizardDialogConst.CHKUSEFACILITATOR_ITEM_CHANGED, + self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKPRESIDING_HID, + self.resources.reschkPresiding_value, + 97, 46, 0, 4, 402, 150), self) + self.chkNoteTaker = self.insertCheckBox("chkNoteTaker", + AgendaWizardDialogConst.CHKUSENOTETAKER_ITEM_CHANGED, + self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKNOTETAKER_HID, + self.resources.reschkNoteTaker_value, + 97, 60, 0, 4, 403, 150), self) + self.chkTimekeeper = self.insertCheckBox("chkTimekeeper", + AgendaWizardDialogConst.CHKUSETIMEKEEPER_ITEM_CHANGED, + self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKTIMEKEEPER_HID, + self.resources.reschkTimekeeper_value, + 97, 74, 0, 4, 404, 150), self) + self.chkAttendees = self.insertCheckBox("chkAttendees", + AgendaWizardDialogConst.CHKUSEATTENDEES_ITEM_CHANGED, + self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKATTENDEES_HID, + self.resources.reschkAttendees_value, + 97, 88, 1, 4, 405, 150), self) + self.chkObservers = self.insertCheckBox("chkObservers", + AgendaWizardDialogConst.CHKUSEOBSERVERS_ITEM_CHANGED, + self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKOBSERVERS_HID, + self.resources.reschkObservers_value, + 97, 102, 0, 4, 406, 150), self) + self.chkResourcePersons = self.insertCheckBox("chkResourcePersons", + AgendaWizardDialogConst.CHKUSERESOURCEPERSONS_ITEM_CHANGED, + self.PROPS_CHECK, + (8, AgendaWizardDialogConst.CHKRESOURCEPERSONS_HID, + self.resources.reschkResourcePersons_value, + 97, 116, 0, 4, 407, 150), self) + self.insertImage("imgHelp4", self.PROPS_IMAGE, + (0, 10, "", UIConsts.INFOIMAGEURL, + 92, 145, False, 4, 408, 10)) + self.insertLabel("lblHelp4", self.PROPS_TEXTAREA, + (39, self.resources.reslblHelp4_value, True, 104, 145, 4, 409, 199)) + + def buildStep5(self): + self.insertLabel("lblTitle4", self.PROPS_LABEL_B, + (self.fontDescriptor4, 16, self.resources.reslblTitle4_value, + True, 91, 8, 5, 500, 212)) + self.insertLabel("lblTopic", self.PROPS_TEXT, + (8, self.resources.reslblTopic_value, 107, 28, 5, 71, 501)) + self.insertLabel("lblResponsible", self.PROPS_TEXT, + (8, self.resources.reslblResponsible_value, 195, 28, 5, 72, 502)) + self.insertLabel("lblDuration", self.PROPS_TEXT, + (8, self.resources.reslblDuration_value, 267, 28, 5, 73, 503)) + self.btnInsert = self.insertButton("btnInsert", + AgendaWizardDialogConst.BTNINSERT_ACTION_PERFORMED, + self.PROPS_BUTTON, (14, AgendaWizardDialogConst.BTNINSERT_HID, + self.resources.resButtonInsert, 92, 136, 5, 580, 40), self) + self.btnRemove = self.insertButton("btnRemove", + AgendaWizardDialogConst.BTNREMOVE_ACTION_PERFORMED, + self.PROPS_BUTTON, (14, AgendaWizardDialogConst.BTNREMOVE_HID, + self.resources.resButtonRemove, 134, 136, 5, 581, 40), self) + self.btnUp = self.insertButton("btnUp", + AgendaWizardDialogConst.BTNUP_ACTION_PERFORMED, + self.PROPS_BUTTON, (14, AgendaWizardDialogConst.BTNUP_HID, + self.resources.resButtonUp, 180, 136, 5, 582, 60), self) + self.btnDown = self.insertButton("btnDown", + AgendaWizardDialogConst.BTNDOWN_ACTION_PERFORMED, + self.PROPS_BUTTON, (14, AgendaWizardDialogConst.BTNDOWN_HID, + self.resources.resButtonDown, 244, 136, 5, 583, 60), self) + + def buildStep6(self): + self.insertLabel("lblTitle6", self.PROPS_LABEL_B, + (self.fontDescriptor4, 16, self.resources.reslblTitle6_value, + True, 91, 8, 6, 600, 212)) + self.insertLabel("lblHelpPg6", self.PROPS_TEXTAREA, + (24, self.resources.reslblHelpPg6_value, True, + 97, 32, 6, 601,204)) + self.insertLabel("lblTemplateName", self.PROPS_TEXT, + (8, self.resources.reslblTemplateName_value, + 97, 62, 6, 602, 101)) + self.txtTemplateName = self.insertTextField("txtTemplateName", + None, self.PROPS_X, + (12, AgendaWizardDialogConst.TXTTEMPLATENAME_HID, + 202, 60, 6, 603, 100), self) + self.insertLabel("lblProceed", self.PROPS_TEXT, + (8, self.resources.reslblProceed_value, 97, 101, 6, 607,204)) + self.optCreateAgenda = self.insertRadioButton("optCreateAgenda", None, + self.PROPS_CHECK, (8, AgendaWizardDialogConst.OPTCREATEAGENDA_HID, + self.resources.resoptCreateAgenda_value, + 103, 113, 1, 6, 608, 198), self) + self.optMakeChanges = self.insertRadioButton("optMakeChanges", None, + self.PROPS_BUTTON, (8, AgendaWizardDialogConst.OPTMAKECHANGES_HID, + self.resources.resoptMakeChanges_value, + 103, 125, 6, 609, 198), self) + self.insertImage("imgHelp6", self.PROPS_IMAGE, (0, 10, "", + UIConsts.INFOIMAGEURL, 92, 145, False, 6, 610, 10)) + self.insertLabel("lblHelp6", self.PROPS_TEXTAREA, + (39, self.resources.reslblHelp6_value, True, 104, 145, 6, 611, 199)) diff --git a/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogConst.py b/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogConst.py new file mode 100644 index 000000000..e4370a416 --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogConst.py @@ -0,0 +1,77 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +from ..common.HelpIds import HelpIds + +HID = 41051 + +class AgendaWizardDialogConst: + + TXTTITLE_TEXT_CHANGED = "txtTitleTextChanged" + TXTDATE_TEXT_CHANGED = "txtDateTextChanged" + TXTTIME_TEXT_CHANGED = "txtTimeTextChanged" + TXTLOCATION_TEXT_CHANGED = "txtLocationTextChanged" + CHKMINUTES_ITEM_CHANGED = "chkMinutesItemChanged" + CHKUSEMEETINGTYPE_ITEM_CHANGED = "chkUseMeetingTypeItemChanged" + CHKUSEREAD_ITEM_CHANGED = "chkUseReadItemChanged" + CHKUSEBRING_ITEM_CHANGED = "chkUseBringItemChanged" + CHKUSENOTES_ITEM_CHANGED = "chkUseNotesItemChanged" + CHKUSECALLEDBYNAME_ITEM_CHANGED = "chkUseCalledByItemChanged" + CHKUSEFACILITATOR_ITEM_CHANGED = "chkUseFacilitatorItemChanged" + CHKUSENOTETAKER_ITEM_CHANGED = "chkUseNoteTakerItemChanged" + CHKUSETIMEKEEPER_ITEM_CHANGED = "chkUseTimeKeeperItemChanged" + CHKUSEATTENDEES_ITEM_CHANGED = "chkUseAttendeesItemChanged" + CHKUSEOBSERVERS_ITEM_CHANGED = "chkUseObserversItemChanged" + CHKUSERESOURCEPERSONS_ITEM_CHANGED = "chkUseResourcePersonsItemChanged" + LISTPAGEDESIGN_ACTION_PERFORMED = "pageDesignChanged" + BTNTEMPLATEPATH_ACTION_PERFORMED = "saveAs" + BTNINSERT_ACTION_PERFORMED = "insertRow" + BTNREMOVE_ACTION_PERFORMED = "removeRow" + BTNUP_ACTION_PERFORMED = "rowUp" + BTNDOWN_ACTION_PERFORMED = "rowDown" + + LISTPAGEDESIGN_HID = HelpIds.getHelpIdString(HID + 6) + CHKMINUTES_HID = HelpIds.getHelpIdString(HID + 7) + TXTTIME_HID = HelpIds.getHelpIdString(HID + 8) + TXTDATE_HID = HelpIds.getHelpIdString(HID + 9) + TXTTITLE_HID = HelpIds.getHelpIdString(HID + 10) + CBLOCATION_HID = HelpIds.getHelpIdString(HID + 11) + + CHKMEETINGTITLE_HID = HelpIds.getHelpIdString(HID + 12) + CHKREAD_HID = HelpIds.getHelpIdString(HID + 13) + CHKBRING_HID = HelpIds.getHelpIdString(HID + 14) + CHKNOTES_HID = HelpIds.getHelpIdString(HID + 15) + + CHKCONVENEDBY_HID = HelpIds.getHelpIdString(HID + 16) + CHKPRESIDING_HID = HelpIds.getHelpIdString(HID + 17) + CHKNOTETAKER_HID = HelpIds.getHelpIdString(HID + 18) + CHKTIMEKEEPER_HID = HelpIds.getHelpIdString(HID + 19) + CHKATTENDEES_HID = HelpIds.getHelpIdString(HID + 20) + CHKOBSERVERS_HID = HelpIds.getHelpIdString(HID + 21) + CHKRESOURCEPERSONS_HID = HelpIds.getHelpIdString(HID + 22) + + TXTTEMPLATENAME_HID = HelpIds.getHelpIdString(HID + 23) + TXTTEMPLATEPATH_HID = HelpIds.getHelpIdString(HID + 24) + BTNTEMPLATEPATH_HID = HelpIds.getHelpIdString(HID + 25) + + OPTCREATEAGENDA_HID = HelpIds.getHelpIdString(HID + 26) + OPTMAKECHANGES_HID = HelpIds.getHelpIdString(HID + 27) + + BTNINSERT_HID = HelpIds.getHelpIdString(HID + 28) + BTNREMOVE_HID = HelpIds.getHelpIdString(HID + 29) + BTNUP_HID = HelpIds.getHelpIdString(HID + 30) + BTNDOWN_HID = HelpIds.getHelpIdString(HID + 31) diff --git a/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogImpl.py b/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogImpl.py new file mode 100644 index 000000000..372dad055 --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogImpl.py @@ -0,0 +1,384 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +import traceback +import os.path +from .AgendaWizardDialog import AgendaWizardDialog, uno +from .AgendaWizardDialogConst import HID +from .AgendaDocument import AgendaDocument, TextElement +from .TemplateConsts import TemplateConsts +from .TopicsControl import TopicsControl +from .CGAgenda import CGAgenda +from ..ui.PathSelection import PathSelection +from ..ui.event.UnoDataAware import UnoDataAware +from ..ui.event.RadioDataAware import RadioDataAware +from ..ui.event.CommonListener import TerminateListenerProcAdapter +from ..common.NoValidPathException import NoValidPathException +from ..common.SystemDialog import SystemDialog +from ..common.Desktop import Desktop +from ..common.HelpIds import HelpIds +from ..common.Configuration import Configuration +from ..common.FileAccess import FileAccess +from ..document.OfficeDocument import OfficeDocument + +from com.sun.star.util import CloseVetoException +from com.sun.star.view.DocumentZoomType import OPTIMAL +from com.sun.star.awt.VclWindowPeerAttribute import YES_NO, DEF_NO + +class AgendaWizardDialogImpl(AgendaWizardDialog): + + def __init__(self, xmsf): + super(AgendaWizardDialogImpl, self).__init__(xmsf) + self.filenameChanged = False + self.pageDesign = -1 + + def enterStep(self, OldStep, NewStep): + pass + + def leaveStep(self, OldStep, NewStep): + pass + + def startWizard(self, xMSF): + self.running = True + try: + #Number of steps on WizardDialog + self.nMaxStep = 6 + + self.agenda = CGAgenda() + + # read configuration data before we initialize the topics + root = Configuration.getConfigurationRoot( + self.xMSF, "/org.openoffice.Office.Writer/Wizards/Agenda", + False) + self.agenda.readConfiguration(root, "cp_") + + self.templateConsts = TemplateConsts + + self.initializePaths() + # initialize the agenda template + self.terminateListener = TerminateListenerProcAdapter(self.queryTermination) + self.myAgendaDoc = AgendaDocument( + self.xMSF, self.agenda, self.resources, + self.templateConsts, self.terminateListener) + self.initializeTemplates() + + self.myAgendaDoc.load( + self.agendaTemplates[1][self.agenda.cp_AgendaType]) + self.drawConstants() + + # build the dialog. + self.drawNaviBar() + + self.buildStep1() + self.buildStep2() + self.buildStep3() + self.buildStep4() + self.buildStep5() + self.buildStep6() + + self.topicsControl = TopicsControl(self, self.xMSF, self.agenda) + + #special Control for setting the save Path: + self.insertPathSelectionControl() + + # synchronize GUI and CGAgenda object. + self.initConfiguration() + + if self.myPathSelection.xSaveTextBox.Text.lower() == "": + self.myPathSelection.initializePath() + + # create the peer + xContainerWindow = self.myAgendaDoc.xFrame.ContainerWindow + self.createWindowPeer(xContainerWindow) + + # initialize roadmap + self.insertRoadmap() + + self.executeDialogFromComponent(self.myAgendaDoc.xFrame) + self.removeTerminateListener() + self.closeDocument() + self.running = False + except Exception: + self.removeTerminateListener() + traceback.print_exc() + self.running = False + return + + def insertPathSelectionControl(self): + self.myPathSelection = PathSelection( + self.xMSF, self, PathSelection.TransferMode.SAVE, + PathSelection.DialogTypes.FILE) + self.myPathSelection.insert(6, 97, 70, 205, 45, + self.resources.reslblTemplatePath_value, True, + HelpIds.getHelpIdString(HID + 24), + HelpIds.getHelpIdString(HID + 25)) + self.myPathSelection.sDefaultDirectory = self.sUserTemplatePath + self.myPathSelection.sDefaultName = "myAgendaTemplate.ott" + self.myPathSelection.sDefaultFilter = "writer8_template" + self.myPathSelection.addSelectionListener(self) + + ''' + bind controls to the agenda member (DataAware model) + ''' + + def initConfiguration(self): + self.xDialogModel.listPageDesign.StringItemList = \ + tuple(self.agendaTemplates[0]) + UnoDataAware.attachListBox( + self.agenda, "cp_AgendaType", self.listPageDesign, True).updateUI() + self.pageDesign = self.agenda.cp_AgendaType + UnoDataAware.attachCheckBox( + self.agenda, "cp_IncludeMinutes", self.chkMinutes, True).updateUI() + UnoDataAware.attachEditControl( + self.agenda, "cp_Title", self.txtTitle, True).updateUI() + UnoDataAware.attachDateControl( + self.agenda, "cp_Date", self.txtDate, True).updateUI() + UnoDataAware.attachTimeControl( + self.agenda, "cp_Time", self.txtTime, True).updateUI() + UnoDataAware.attachEditControl( + self.agenda, "cp_Location", self.cbLocation, True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowMeetingType", self.chkMeetingTitle, + True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowRead", self.chkRead, True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowBring", self.chkBring, True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowNotes", self.chkNotes, True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowCalledBy", self.chkConvenedBy, + True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowFacilitator", self.chkPresiding, + True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowNotetaker", self.chkNoteTaker, + True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowTimekeeper", self.chkTimekeeper, + True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowAttendees", self.chkAttendees, + True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowObservers", self.chkObservers, + True).updateUI() + UnoDataAware.attachCheckBox( + self.agenda, "cp_ShowResourcePersons",self.chkResourcePersons, + True).updateUI() + UnoDataAware.attachEditControl( + self.agenda, "cp_TemplateName", self.txtTemplateName, + True).updateUI() + RadioDataAware.attachRadioButtons( + self.agenda, "cp_ProceedMethod", + (self.optCreateAgenda, self.optMakeChanges), True).updateUI() + + def insertRoadmap(self): + self.addRoadmap() + self.insertRoadMapItems( + self.resources.RoadmapLabels, [True, True, True, True, True, True]) + self.setRoadmapInteractive(True) + self.setRoadmapComplete(True) + self.setCurrentRoadmapItemID(1) + + ''' + read the available agenda wizard templates. + ''' + + def initializeTemplates(self): + try: + sAgendaPath = self.sTemplatePath + "/wizard/agenda" + self.agendaTemplates = FileAccess.getFolderTitles( + self.xMSF, "aw", sAgendaPath, self.resources.dictPageDesign) + return True + except NoValidPathException: + traceback.print_exc() + return False + + ''' + first page, page design listbox changed. + ''' + + def pageDesignChanged(self): + try: + SelectedItemPos = self.listPageDesign.SelectedItemPos + #avoid to load the same item again + if self.pageDesign is not SelectedItemPos: + self.pageDesign = SelectedItemPos + self.myAgendaDoc.load( + self.agendaTemplates[1][SelectedItemPos]) + self.drawConstants() + except Exception: + traceback.print_exc() + + #textFields listeners + def txtTitleTextChanged(self): + self.myAgendaDoc.redrawTitle("txtTitle") + + def txtDateTextChanged(self): + self.myAgendaDoc.redrawTitle("txtDate") + + def txtTimeTextChanged(self): + self.myAgendaDoc.redrawTitle("txtTime") + + def txtLocationTextChanged(self): + self.myAgendaDoc.redrawTitle("cbLocation") + + #checkbox listeners + def chkUseMeetingTypeItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_MEETING_TYPE) + + def chkUseReadItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_READ) + + def chkUseBringItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_BRING) + + def chkUseNotesItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_NOTES) + + def chkUseCalledByItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_CALLED_BY) + + def chkUseFacilitatorItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_FACILITATOR) + + def chkUseNoteTakerItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_NOTETAKER) + + def chkUseTimeKeeperItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_TIMEKEEPER) + + def chkUseAttendeesItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_PARTICIPANTS) + + def chkUseObserversItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_OBSERVERS) + + def chkUseResourcePersonsItemChanged(self): + self.myAgendaDoc.redraw(self.templateConsts.FILLIN_RESOURCE_PERSONS) + + def insertRow(self): + self.topicsControl.insertRow() + + def removeRow(self): + self.topicsControl.removeRow() + + def rowUp(self): + self.topicsControl.rowUp() + + def rowDown(self): + self.topicsControl.rowDown() + + def cancelWizard(self): + self.xUnoDialog.endExecute() + self.running = False + + def finishWizard(self): + self.switchToStep(self.getCurrentStep(), self.nMaxStep) + bSaveSuccess = False + endWizard = True + try: + self.sPath = self.myPathSelection.getSelectedPath() + if not self.sPath or not os.path.exists(self.sPath): + self.myPathSelection.triggerPathPicker() + self.sPath = self.myPathSelection.getSelectedPath() + + #first, if the filename was not changed, thus + #it is coming from a saved session, check if the + # file exists and warn the user. + if not self.filenameChanged: + answer = SystemDialog.showMessageBox( + self.xMSF, "MessBox", YES_NO + DEF_NO, + self.resources.resOverwriteWarning, + self.xUnoDialog.Peer) + if answer == 3: + # user said: no, do not overwrite + endWizard = False + return False + + xDocProps = self.myAgendaDoc.xTextDocument.DocumentProperties + xDocProps.Title = self.txtTemplateName.Text + self.myAgendaDoc.setWizardTemplateDocInfo( \ + self.resources.resAgendaWizardDialog_title, + self.resources.resTemplateDescription) + bSaveSuccess = OfficeDocument.store( + self.xMSF, self.myAgendaDoc.xTextDocument, self.sPath, + "writer8_template") + + if bSaveSuccess: + self.topicsControl.saveTopics(self.agenda) + root = Configuration.getConfigurationRoot( + self.xMSF, "/org.openoffice.Office.Writer/Wizards/Agenda", + True) + self.agenda.writeConfiguration(root, "cp_") + root.commitChanges() + + self.myAgendaDoc.finish(self.topicsControl.scrollfields) + + loadValues = list(range(2)) + loadValues[0] = uno.createUnoStruct( \ + 'com.sun.star.beans.PropertyValue') + loadValues[0].Name = "AsTemplate" + if self.agenda.cp_ProceedMethod == 1: + loadValues[0].Value = True + else: + loadValues[0].Value = False + + loadValues[1] = uno.createUnoStruct( \ + 'com.sun.star.beans.PropertyValue') + loadValues[1].Name = "InteractionHandler" + + xIH = self.xMSF.createInstance( + "com.sun.star.comp.uui.UUIInteractionHandler") + loadValues[1].Value = xIH + + oDoc = OfficeDocument.load( + Desktop.getDesktop(self.xMSF), + self.sPath, "_default", loadValues) + oDoc.CurrentController.ViewSettings.ZoomType = OPTIMAL + else: + pass + + except Exception: + traceback.print_exc() + finally: + if endWizard: + self.xUnoDialog.endExecute() + self.running = False + return True + + def closeDocument(self): + try: + self.myAgendaDoc.xFrame.close(False) + except CloseVetoException: + traceback.print_exc() + + def drawConstants(self): + '''Localise the template''' + constRangeList = self.myAgendaDoc.searchFillInItems(1) + + for i in constRangeList: + text = i.String.lower() + aux = TextElement(i, self.resources.dictConstants[text]) + aux.write() + + def validatePath(self): + if self.myPathSelection.usedPathPicker: + self.filenameChanged = True + self.myPathSelection.usedPathPicker = False diff --git a/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogResources.py b/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogResources.py new file mode 100644 index 000000000..b1d53de6b --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/AgendaWizardDialogResources.py @@ -0,0 +1,137 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +class AgendaWizardDialogResources(object): + + SECTION_ITEMS = "AGENDA_ITEMS" + SECTION_TOPICS = "AGENDA_TOPICS" + SECTION_MINUTES_ALL = "MINUTES_ALL" + SECTION_MINUTES = "MINUTES" + + def __init__(self): + import imp, os + imp.load_source('strings', os.path.join(os.path.dirname(__file__), '../common/strings.hrc')) + import strings + + self.resAgendaWizardDialog_title = strings.RID_AGENDAWIZARDDIALOG_START_1 + self.resoptMakeChanges_value = strings.RID_AGENDAWIZARDDIALOG_START_2 + self.reslblTemplateName_value = strings.RID_AGENDAWIZARDDIALOG_START_3 + self.reslblTemplatePath_value = strings.RID_AGENDAWIZARDDIALOG_START_4 + self.reslblProceed_value = strings.RID_AGENDAWIZARDDIALOG_START_5 + self.reslblTitle1_value = strings.RID_AGENDAWIZARDDIALOG_START_6 + self.reslblTitle3_value = strings.RID_AGENDAWIZARDDIALOG_START_7 + self.reslblTitle2_value = strings.RID_AGENDAWIZARDDIALOG_START_8 + self.reslblTitle4_value = strings.RID_AGENDAWIZARDDIALOG_START_9 + self.reslblTitle5_value = strings.RID_AGENDAWIZARDDIALOG_START_10 + self.reslblTitle6_value = strings.RID_AGENDAWIZARDDIALOG_START_11 + self.reschkMinutes_value = strings.RID_AGENDAWIZARDDIALOG_START_12 + self.reslblHelp1_value = strings.RID_AGENDAWIZARDDIALOG_START_13 + self.reslblTime_value = strings.RID_AGENDAWIZARDDIALOG_START_14 + self.reslblTitle_value = strings.RID_AGENDAWIZARDDIALOG_START_15 + self.reslblLocation_value = strings.RID_AGENDAWIZARDDIALOG_START_16 + self.reslblHelp2_value = strings.RID_AGENDAWIZARDDIALOG_START_17 + self.resbtnTemplatePath_value = strings.RID_AGENDAWIZARDDIALOG_START_18 + self.resoptCreateAgenda_value = strings.RID_AGENDAWIZARDDIALOG_START_19 + self.reslblHelp6_value = strings.RID_AGENDAWIZARDDIALOG_START_20 + self.reslblTopic_value = strings.RID_AGENDAWIZARDDIALOG_START_21 + self.reslblResponsible_value = strings.RID_AGENDAWIZARDDIALOG_START_22 + self.reslblDuration_value = strings.RID_AGENDAWIZARDDIALOG_START_23 + self.reschkConvenedBy_value = strings.RID_AGENDAWIZARDDIALOG_START_24 + self.reschkPresiding_value = strings.RID_AGENDAWIZARDDIALOG_START_25 + self.reschkNoteTaker_value = strings.RID_AGENDAWIZARDDIALOG_START_26 + self.reschkTimekeeper_value = strings.RID_AGENDAWIZARDDIALOG_START_27 + self.reschkAttendees_value = strings.RID_AGENDAWIZARDDIALOG_START_28 + self.reschkObservers_value = strings.RID_AGENDAWIZARDDIALOG_START_29 + self.reschkResourcePersons_value = strings.RID_AGENDAWIZARDDIALOG_START_30 + self.reslblHelp4_value = strings.RID_AGENDAWIZARDDIALOG_START_31 + self.reschkMeetingTitle_value = strings.RID_AGENDAWIZARDDIALOG_START_32 + self.reschkRead_value = strings.RID_AGENDAWIZARDDIALOG_START_33 + self.reschkBring_value = strings.RID_AGENDAWIZARDDIALOG_START_34 + self.reschkNotes_value = strings.RID_AGENDAWIZARDDIALOG_START_35 + self.reslblHelp3_value = strings.RID_AGENDAWIZARDDIALOG_START_36 + self.reslblDate_value = strings.RID_AGENDAWIZARDDIALOG_START_38 + self.reslblHelpPg6_value = strings.RID_AGENDAWIZARDDIALOG_START_39 + self.reslblPageDesign_value = strings.RID_AGENDAWIZARDDIALOG_START_40 + self.resDefaultFilename = strings.RID_AGENDAWIZARDDIALOG_START_41 + self.resDefaultFilename = self.resDefaultFilename[:-4] + ".ott" + self.resDefaultTitle = strings.RID_AGENDAWIZARDDIALOG_START_42 + self.resErrSaveTemplate = strings.RID_AGENDAWIZARDDIALOG_START_43 + self.resPlaceHolderTitle = strings.RID_AGENDAWIZARDDIALOG_START_44 + self.resPlaceHolderDate = strings.RID_AGENDAWIZARDDIALOG_START_45 + self.resPlaceHolderTime = strings.RID_AGENDAWIZARDDIALOG_START_46 + self.resPlaceHolderLocation = strings.RID_AGENDAWIZARDDIALOG_START_47 + self.resPlaceHolderHint = strings.RID_AGENDAWIZARDDIALOG_START_48 + self.resErrOpenTemplate = strings.RID_AGENDAWIZARDDIALOG_START_56 + self.itemMeetingType = strings.RID_AGENDAWIZARDDIALOG_START_57 + self.itemBring = strings.RID_AGENDAWIZARDDIALOG_START_58 + self.itemRead = strings.RID_AGENDAWIZARDDIALOG_START_59 + self.itemNote = strings.RID_AGENDAWIZARDDIALOG_START_60 + self.itemCalledBy = strings.RID_AGENDAWIZARDDIALOG_START_61 + self.itemFacilitator = strings.RID_AGENDAWIZARDDIALOG_START_62 + self.itemAttendees = strings.RID_AGENDAWIZARDDIALOG_START_63 + self.itemNotetaker = strings.RID_AGENDAWIZARDDIALOG_START_64 + self.itemTimekeeper = strings.RID_AGENDAWIZARDDIALOG_START_65 + self.itemObservers = strings.RID_AGENDAWIZARDDIALOG_START_66 + self.itemResource = strings.RID_AGENDAWIZARDDIALOG_START_67 + self.resButtonInsert = strings.RID_AGENDAWIZARDDIALOG_START_68 + self.resButtonRemove = strings.RID_AGENDAWIZARDDIALOG_START_69 + self.resButtonUp = strings.RID_AGENDAWIZARDDIALOG_START_70 + self.resButtonDown = strings.RID_AGENDAWIZARDDIALOG_START_71 + + #Create a dictionary for localised string in the template + self.dictConstants = { + "#datetitle#" : strings.RID_AGENDAWIZARDDIALOG_START_72, + "#timetitle#" : strings.RID_AGENDAWIZARDDIALOG_START_73, + "#locationtitle#" : strings.RID_AGENDAWIZARDDIALOG_START_74, + "#topics#" : strings.RID_AGENDAWIZARDDIALOG_START_75, + "#num.#" : strings.RID_AGENDAWIZARDDIALOG_START_76, + "#topicheader#" : strings.RID_AGENDAWIZARDDIALOG_START_77, + "#responsibleheader#" : strings.RID_AGENDAWIZARDDIALOG_START_78, + "#timeheader#" : strings.RID_AGENDAWIZARDDIALOG_START_79, + "#additional-information#" : strings.RID_AGENDAWIZARDDIALOG_START_80, + "#minutes-for#" : strings.RID_AGENDAWIZARDDIALOG_START_81, + "#discussion#" : strings.RID_AGENDAWIZARDDIALOG_START_82, + "#conclusion#" : strings.RID_AGENDAWIZARDDIALOG_START_83, + "#to-do#" : strings.RID_AGENDAWIZARDDIALOG_START_84, + "#responsible-party#" : strings.RID_AGENDAWIZARDDIALOG_START_85, + "#deadline#" : strings.RID_AGENDAWIZARDDIALOG_START_86} + + #Create a dictionary for localising the page design + self.dictPageDesign = { + "Blue" : strings.RID_AGENDAWIZARDDIALOG_START_87, + "Classic" : strings.RID_AGENDAWIZARDDIALOG_START_88, + "Colorful" : strings.RID_AGENDAWIZARDDIALOG_START_89, + "Elegant" : strings.RID_AGENDAWIZARDDIALOG_START_90, + "Green" : strings.RID_AGENDAWIZARDDIALOG_START_91, + "Grey" : strings.RID_AGENDAWIZARDDIALOG_START_92, + "Modern" : strings.RID_AGENDAWIZARDDIALOG_START_93, + "Orange" : strings.RID_AGENDAWIZARDDIALOG_START_94, + "Red" : strings.RID_AGENDAWIZARDDIALOG_START_95, + "Simple" : strings.RID_AGENDAWIZARDDIALOG_START_96} + + #Common Resources + self.resOverwriteWarning = strings.RID_COMMON_START_19 + self.resTemplateDescription = strings.RID_COMMON_START_20 + + self.RoadmapLabels = [] + self.RoadmapLabels.append(strings.RID_AGENDAWIZARDDIALOG_START_50) + self.RoadmapLabels.append(strings.RID_AGENDAWIZARDDIALOG_START_51) + self.RoadmapLabels.append(strings.RID_AGENDAWIZARDDIALOG_START_52) + self.RoadmapLabels.append(strings.RID_AGENDAWIZARDDIALOG_START_53) + self.RoadmapLabels.append(strings.RID_AGENDAWIZARDDIALOG_START_54) + self.RoadmapLabels.append(strings.RID_AGENDAWIZARDDIALOG_START_55) diff --git a/wizards/com/sun/star/wizards/agenda/CGAgenda.py b/wizards/com/sun/star/wizards/agenda/CGAgenda.py new file mode 100644 index 000000000..ee8435ba1 --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/CGAgenda.py @@ -0,0 +1,46 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +from ..common.ConfigGroup import ConfigGroup +from ..common.ConfigSet import ConfigSet +from .CGTopic import CGTopic + +class CGAgenda(ConfigGroup): + + def __init__(self): + self.cp_AgendaType = int() + self.cp_IncludeMinutes = bool() + self.cp_Title = "" + self.cp_Date = str() + self.cp_Time = str() + self.cp_Location = "" + self.cp_ShowMeetingType = bool() + self.cp_ShowRead = bool() + self.cp_ShowBring = bool() + self.cp_ShowNotes = bool() + self.cp_ShowCalledBy = bool() + self.cp_ShowFacilitator = bool() + self.cp_ShowNotetaker = bool() + self.cp_ShowTimekeeper = bool() + self.cp_ShowAttendees = bool() + self.cp_ShowObservers = bool() + self.cp_ShowResourcePersons = bool() + self.cp_TemplateName = str() + self.cp_TemplatePath = str() + self.cp_ProceedMethod = int() + + self.cp_Topics = ConfigSet(CGTopic) diff --git a/wizards/com/sun/star/wizards/agenda/CGTopic.py b/wizards/com/sun/star/wizards/agenda/CGTopic.py new file mode 100644 index 000000000..e99f770b2 --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/CGTopic.py @@ -0,0 +1,60 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +from ..common.ConfigGroup import ConfigGroup + +''' +CGTopic means: Configuration Group Topic. +This object encapsulates a configuration group with topic information. +Since the topic's gui control uses its own data model, there is +also code here to convert from the data model to CGTopic object (the constructor) +and vice versa (setDataToRow method - used when loading the last session...) +''' + +class CGTopic(ConfigGroup): + + ''' + create a new CGTopic object with data from the given row. + the row object is a PropertyValue array, as used + by the TopicsControl's data model. + @param row PropertyValue array as used by the TopicsControl's data model. + ''' + + def __init__(self, row=None): + if row is None: + self.cp_Index = int() + self.cp_Topic = str() + self.cp_Responsible = str() + self.cp_Time = str() + else: + self.cp_Index = int(row[0].Value[:-1]) + self.cp_Topic = row[1].Value + self.cp_Responsible = row[2].Value + self.cp_Time = row[3].Value + + ''' + copies the data in this CGTopic object + to the given row. + @param row the row object (PropertyValue array) to + copy the data to. + ''' + + def setDataToRow(self, row): + row[0].Value = "" + str(self.cp_Index) + "." + row[1].Value = self.cp_Topic + row[2].Value = self.cp_Responsible + row[3].Value = self.cp_Time diff --git a/wizards/com/sun/star/wizards/agenda/CallWizard.py b/wizards/com/sun/star/wizards/agenda/CallWizard.py new file mode 100644 index 000000000..2b784e2fe --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/CallWizard.py @@ -0,0 +1,75 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +import unohelper +import traceback + +from .AgendaWizardDialogImpl import AgendaWizardDialogImpl, Desktop + +from com.sun.star.lang import XServiceInfo +from com.sun.star.task import XJobExecutor + +# pythonloader looks for a static g_ImplementationHelper variable +g_ImplementationHelper = unohelper.ImplementationHelper() +g_implName = "com.sun.star.wizards.agenda.CallWizard" + +# implement a UNO component by deriving from the standard unohelper.Base class +# and from the interface(s) you want to implement. +class CallWizard(unohelper.Base, XJobExecutor, XServiceInfo): + def __init__(self, ctx): + # store the component context for later use + self.ctx = ctx + + def trigger(self, args): + try: + fw = AgendaWizardDialogImpl(self.ctx.ServiceManager) + fw.startWizard(self.ctx.ServiceManager) + except Exception as e: + print ("Wizard failure exception " + str(type(e)) + + " message " + str(e) + " args " + str(e.args) + + traceback.format_exc()) + + @classmethod + def callRemote(self): + #Call the wizard remotely(see README) + try: + ConnectStr = \ + "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" + xLocMSF = Desktop.connect(ConnectStr) + lw = AgendaWizardDialogImpl(xLocMSF) + lw.startWizard(xLocMSF) + except Exception as e: + print ("Wizard failure exception " + str(type(e)) + + " message " + str(e) + " args " + str(e.args) + + traceback.format_exc()) + + def getImplementationName(self): + return g_implName + + def supportsService(self, ServiceName): + return g_ImplementationHelper.supportsService(g_implName, ServiceName) + + def getSupportedServiceNames(self): + return g_ImplementationHelper.getSupportedServiceNames(g_implName) + +g_ImplementationHelper.addImplementation( \ + CallWizard, # UNO object class + g_implName, # implementation name + ("com.sun.star.task.Job",),) # list of implemented services + # (the only service) + +# vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/wizards/com/sun/star/wizards/agenda/TemplateConsts.py b/wizards/com/sun/star/wizards/agenda/TemplateConsts.py new file mode 100644 index 000000000..2593c2ce3 --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/TemplateConsts.py @@ -0,0 +1,83 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +class TemplateConsts: + FILLIN_TITLE = "<title>" + FILLIN_TITLE = "<title>" + FILLIN_DATE = "<date>" + FILLIN_TIME = "<time>" + FILLIN_LOCATION = "<location>" + ''' + section name <b>prefix</b> for sections that contain items. + this is also used as table name prefix, since each items section + must contain a table whose name is identical name to the section's name. + ''' + SECTION_ITEMS = "AGENDA_ITEMS" + ''' + the name of the section which contains the topics. + ''' + SECTION_TOPICS = "AGENDA_TOPICS" + ''' + the name of the parent minutes section. + ''' + SECTION_MINUTES_ALL = "MINUTES_ALL" + ''' + the name of the child minutes section. + This section will be duplicated for each topic. + ''' + SECTION_MINUTES = "MINUTES" + ''' + tagged headings and names. + These will be searched in item tables (in the template) and will be + replaced with resource strings. + + headings... + ''' + FILLIN_MEETING_TYPE = "<meeting-type>" + FILLIN_BRING = "<bring>" + FILLIN_READ = "<read>" + FILLIN_NOTES = "<notes>" + ''' + names... + ''' + FILLIN_CALLED_BY = "<called-by>" + FILLIN_FACILITATOR = "<facilitator>" + FILLIN_PARTICIPANTS = "<attendees>" + FILLIN_NOTETAKER = "<notetaker>" + FILLIN_TIMEKEEPER = "<timekeeper>" + FILLIN_OBSERVERS = "<observers>" + FILLIN_RESOURCE_PERSONS = "<resource-persons>" + + ''' + fillins for minutes. + These will be searched in the minutes section and will be replaced + with the appropriate data. + ''' + FILLIN_MINUTES_TITLE = "<minutes-title>" + FILLIN_MINUTES_LOCATION = "<minutes-location>" + FILLIN_MINUTES_DATE = "<minutes-date>" + FILLIN_MINUTES_TIME = "<minutes-time>" + ''' + Minutes-topic fillins + These will be searched in the minutes-child-section, and + will be replaced with topic data. + ''' + FILLIN_MINUTE_NUM = "<mnum>" + FILLIN_MINUTE_TOPIC = "<mtopic>" + FILLIN_MINUTE_RESPONSIBLE = "<mresponsible>" + FILLIN_MINUTE_TIME = "<mtime>" diff --git a/wizards/com/sun/star/wizards/agenda/TopicsControl.py b/wizards/com/sun/star/wizards/agenda/TopicsControl.py new file mode 100644 index 000000000..6e269f6bf --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/TopicsControl.py @@ -0,0 +1,857 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# +import uno +from ..ui.ControlScroller import ControlScroller, PropertyNames, traceback, \ + HelpIds +from .AgendaWizardDialogConst import HID +from ..common.Properties import Properties +from ..ui.event.CommonListener import FocusListenerProcAdapter, \ + KeyListenerProcAdapter + +from com.sun.star.awt.Key import DOWN, UP, TAB +from com.sun.star.awt.KeyModifier import SHIFT, MOD1 + +''' +This class implements the UI functionality of the topics scroller control. +<br/> +During development, there has been a few changes which were not *fully* done +mainly in converting the topics and time boxes +from combobox and time box to normal textboxes, +so in the code they might be referenced as combobox or timebox. This should be +rather understood as topicstextbox and timetextbox.<br/><br/> +Important behaviour of this control is that there is always a +blank row at the end, in which the user can enter data.<br/> +Once the row is not blank (thus, the user entered data...), +a new blank row is added.<br/> +Once the user removes the last *unempty* row, binsertRowy deleting its data, it becomes +the *last empty row* and the one after is being automatically removed.<br/><br/> +The control shows 5 rows at a time.<br/> +If, for example, only 2 rows exist (from which the 2ed one is empty...) +then the other three rows, which do not exist in the data model, are disabled. +<br/> +The following other functionality is implemented: +<br/> +0. synchronizing data between controls, data model and live preview. +1. Tab scrolling.<br/> +2. Keyboard scrolling.<br/> +3. Removing rows and adding new rows.<br/> +4. Moving rows up and down. <br/> +<br/> +This control relays on the ControlScroller control which uses the following +Data model:<br/> +1. It uses a vector, whose members are arrays of PropertyValue.<br/> +2. Each array represents a row.<br/> +(Note: the Name and Value members of the PropertyValue object are being used) +3. Each property Value represents a value +for a single control with the following rules:<br/> +3. a. the Value of the property is used for as value +of the controls (usually text).<br/> +3. b. the Name of the property is used to map values +to UI controls in the following manner:<br/> +3. b. 1. only the Name of the first X Rows is regarded, +where X is the number of visible rows (in the ainsertRowgenda wizard this would be 5, +since 5 topic rows are visible on the dialog).<br/> +3. b. 2. The Names of the first X (or 5...) rows are the names +of the UI Controls to hold values. When the control scroller scrolls, +it looks at the first 5 rows and uses the names specified there to map the +current values to the specified controls. <br/> +This data model makes the following limitations on the implementation: +When moving rows, only the values should be moved. The Rows objects, +which contain also the Names of the controls should not be switched. <br/> +also when deleting or inserting rows, attention should be paid that no rows +should be removed or inserted. Instead, only the Values should rotate. <br/><br/> +To save the topics in the registry a ConfigSet of objects of type CGTopic is +being used. +This one is not synchronized "live", since it is unnecessary... instead, it is +synchronized on call, before the settings should be saved. +''' + +class TopicsControl(ControlScroller): + + LABEL = "lblTopicCnt_" + TOPIC = "txtTopicTopic_" + RESP = "cbTopicResp_" + TIME = "txtTopicTime_" + LABEL_PROPS = (PropertyNames.PROPERTY_HEIGHT, PropertyNames.PROPERTY_LABEL, + PropertyNames.PROPERTY_POSITION_X, PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + TEXT_PROPS = (PropertyNames.PROPERTY_HEIGHT, PropertyNames.PROPERTY_HELPURL, + PropertyNames.PROPERTY_POSITION_X, PropertyNames.PROPERTY_POSITION_Y, + PropertyNames.PROPERTY_STEP, PropertyNames.PROPERTY_TABINDEX, + PropertyNames.PROPERTY_WIDTH) + + def __init__(self, dialog, xmsf, agenda): + try: + super(TopicsControl, self).__init__( + dialog, xmsf, 5, 92, 38, 212, 5, 18, HID + 32) + self.dialog = dialog + #fill preview's table + self.initializeScrollFields(agenda) + #fill gui's table + self.fillupControls(True) + self.nscrollvalue = 0 + self.lastFocusRow = 0 + self.lastFocusControl = None + # set some focus listeners for TAB scroll down and up... + # prepare scroll down on tab press... + self.lastTime = \ + self.ControlGroupVector[self.nblockincrement - 1].timebox + + self.lastTime.addKeyListener(KeyListenerProcAdapter( + self.lastControlKeyPressed)) + #prepare scroll up on tab press... + self.firstTopic = self.ControlGroupVector[0].textbox + self.firstTopic.addKeyListener(KeyListenerProcAdapter( + self.firstControlKeyPressed)) + self.enableButtons() + except Exception: + traceback.print_exc() + + ''' + initializes the data of the control. + ''' + + def initializeScrollFields(self, agenda): + # create a row for each topic with the given values... + for index,item in enumerate(agenda.cp_Topics.childrenList): + row = self.newRow(index) + item.setDataToRow(row) + # a parent class method + self.registerControlGroup(row, index) + self.updateDocumentRow(index) + # inserts a blank row at the end... + self.insertRowAtEnd() + + ''' + Insert a blank (empty) row + as last row of the control. + The control has always a blank row at the + end, which enables the user to enter data... + ''' + + def insertRowAtEnd(self): + l = len(self.scrollfields) + self.registerControlGroup(self.newRow(l), l) + self.setTotalFieldCount(l + 1) + # if the new row is visible, it must have been disabled + # so it should be now enabled... + if l - self.nscrollvalue < self.nblockincrement: + self.ControlGroupVector[l - self.nscrollvalue].\ + setEnabled(True) + + def saveTopics(self, agenda): + # last row is always empty + agenda.cp_Topics.childrenList = self.scrollfields[:-1] + + ''' + remove the last row + ''' + + def removeLastRow(self): + l = len(self.scrollfields) + # if we should scroll up... + if (l - self.nscrollvalue) >= 1 \ + and (l - self.nscrollvalue) <= self.nblockincrement \ + and self.nscrollvalue > 0: + while (l - self.nscrollvalue >= 1) \ + and l - self.nscrollvalue <= self.nblockincrement \ + and self.nscrollvalue > 0: + self.setScrollValue(self.nscrollvalue - 1) + # if we should disable a row... + elif self.nscrollvalue == 0 and l - 1 < self.nblockincrement: + self.ControlGroupVector[l - 1].setEnabled(False) + + self.unregisterControlGroup(l - 1) + self.setTotalFieldCount(l - 1) + + ''' + in order to use the "move up", "down" "insert" and "remove" buttons, + we track the last control the gained focus, in order to know which + row should be handled. + @param fe + ''' + + def focusGained(self, fe): + xc = fe.Source + self.focusGained2(xc) + + ''' + Sometimes I set the focus programmatically to a control + (for example when moving a row up or down, the focus should move + with it). + In such cases, no VCL event is being triggered so it must + be called programmatically. + This is done by this method. + @param control + ''' + + def focusGained2(self, control): + try: + #calculate in which row we are... + name = control.Model.Name + num = name[name.index("_") + 1:] + self.lastFocusRow = int(num) + self.nscrollvalue + self.lastFocusControl = control + # enable/disable the buttons... + self.enableButtons() + except Exception: + traceback.print_exc() + + ''' + enable or disable the buttons according to the + current row we are in. + ''' + + def enableButtons(self): + self.CurUnoDialog.btnInsert.Model.Enabled = \ + self.lastFocusRow < len(self.scrollfields) + self.CurUnoDialog.btnRemove.Model.Enabled = \ + self.lastFocusRow < len(self.scrollfields) - 1 + if self.lastFocusControl is not None: + self.CurUnoDialog.btnUp.Model.Enabled = self.lastFocusRow > 0 + self.CurUnoDialog.btnDown.Model.Enabled = \ + self.lastFocusRow < len(self.scrollfields) - 1 + else: + self.CurUnoDialog.btnUp.Model.Enabled = False + self.CurUnoDialog.btnDown.Model.Enabled = False + + ''' + Removes the current row. + See general class documentation explanation about the + data model used and the limitations which explain the implementation here. + ''' + + def removeRow(self): + try: + for i in range(self.lastFocusRow, + len(self.scrollfields) - 1): + pv1 = self.scrollfields[i] + pv2 = self.scrollfields[i + 1] + pv1[1].Value = pv2[1].Value + pv1[2].Value = pv2[2].Value + pv1[3].Value = pv2[3].Value + self.updateDocumentRow(i) + if i - self.nscrollvalue < self.nblockincrement: + self.fillupControl(i - self.nscrollvalue) + + self.removeLastRow() + # update the live preview background document + self.reduceDocumentToTopics() + self.enableButtons() + if self.lastFocusControl is not None: + # the focus should return to the edit control + self.focus(self.lastFocusControl) + except Exception: + traceback.print_exc() + + ''' + Inserts a row before the current row. + See general class documentation explanation about the + data model used and the limitations which explain the implementation here. + ''' + + def insertRow(self): + try: + self.insertRowAtEnd() + for i in range(len(self.scrollfields) - 2, + self.lastFocusRow, -1): + pv1 = self.scrollfields[i] + pv2 = self.scrollfields[i - 1] + pv1[1].Value = pv2[1].Value + pv1[2].Value = pv2[2].Value + pv1[3].Value = pv2[3].Value + self.updateDocumentRow(i) + if i - self.nscrollvalue < self.nblockincrement: + self.fillupControl(i - self.nscrollvalue) + + # after rotating all the properties from this row on, + # we clear the row, so it is practically a new one... + pv1 = self.scrollfields[self.lastFocusRow] + pv1[1].Value = "" + pv1[2].Value = "" + pv1[3].Value = "" + # update the preview document. + self.updateDocumentRow(self.lastFocusRow) + self.fillupControl( + self.lastFocusRow - self.nscrollvalue) + self.enableButtons() + + if self.lastFocusControl is not None: + self.focus(self.lastFocusControl) + except Exception: + traceback.print_exc() + + ''' + create a new row with the given index. + The index is important because it is used in the + Name member of the PropertyValue objects. + To know why see general class documentation above (data model explanation) + @param i the index of the new row + @return + ''' + + def newRow(self, i): + pv = list(range(4)) + pv[0] = Properties.createProperty( + TopicsControl.LABEL + str(i), "" + str(i + 1) + ".") + pv[1] = Properties.createProperty(TopicsControl.TOPIC + str(i), "") + pv[2] = Properties.createProperty(TopicsControl.RESP + str(i), "") + pv[3] = Properties.createProperty(TopicsControl.TIME + str(i), "") + return pv + + ''' + Implementation of ControlScroller + This is a UI method which inserts a new row to the control. + It uses the child-class ControlRow. (see below). + ''' + + def insertControlGroup(self, _index, npos): + oControlRow = ControlRow( + self.CurUnoDialog, self.iCompPosX, npos, _index, + ControlRow.tabIndex, self) + self.ControlGroupVector.append(oControlRow) + ControlRow.tabIndex += 4 + + ''' + Checks if a row is empty. + This is used when the last row is changed. + If it is empty, the next row (which is always blank) is removed. + If it is not empty, a next row must exist. + @param row the index number of the row to check. + @return true if empty. false if not. + ''' + + def isRowEmpty(self, row): + data = self.getTopicData(row) + # now - is this row empty? + return not data[1].Value and not data[2].Value and not data[3].Value + + ''' + update the preview document and + remove/insert rows if needed. + @param guiRow + @param column + ''' + + def fieldChanged(self, guiRow, column): + try: + # First, I update the document + data = self.getTopicData(guiRow + self.nscrollvalue) + if data is None: + return + self.updateDocumentCell( + guiRow + self.nscrollvalue, column, data) + if self.isRowEmpty(guiRow + self.nscrollvalue): + ''' + if this is the row before the last one + (the last row is always empty) + delete the last row... + ''' + if (guiRow + self.nscrollvalue) \ + == len(self.scrollfields) - 2: + self.removeLastRow() + '''now consequently check the last two rows, + and remove the last one if they are both empty. + (actually I check always the "before last" row, + because the last one is always empty... + ''' + while len(self.scrollfields) > 1 \ + and self.isRowEmpty( + len(self.scrollfields) - 2): + self.removeLastRow() + cr = self.ControlGroupVector[ + len(self.scrollfields) - \ + self.nscrollvalue - 1] + # if a remove was performed, set focus + #to the last row with some data in it... + self.focus(self.getControlByIndex(cr, column)) + # update the preview document. + self.reduceDocumentToTopics() + else: + # row contains data + # is this the last row? + if (guiRow + self.nscrollvalue + 1) \ + == len(self.scrollfields): + self.insertRowAtEnd() + + except Exception: + traceback.print_exc() + + ''' + return the corresponding row data for the given index. + @param topic index of the topic to get. + @return a PropertyValue array with the data for the given topic. + ''' + + def getTopicData(self, topic): + if topic < len(self.scrollfields): + return self.scrollfields[topic] + else: + return None + + ''' + If the user presses tab on the last control, and + there *are* more rows in the model, scroll down. + @param event + ''' + + def lastControlKeyPressed(self, event): + # if tab without shift was pressed... + try: + if event.KeyCode == TAB and event.Modifiers == 0: + # if there is another row... + if (self.nblockincrement + self.nscrollvalue) \ + < len(self.scrollfields): + self.setScrollValue(self.nscrollvalue + 1) + self.focus(self.getControlByIndex(self.ControlGroupVector[4], 1)) + except Exception: + traceback.print_exc() + + ''' + If the user presses shift-tab on the first control, and + there *are* more rows in the model, scroll up. + @param event + ''' + + def firstControlKeyPressed(self, event): + # if tab with shift was pressed... + if (event.KeyCode == TAB) and \ + (event.Modifiers == SHIFT): + if self.nscrollvalue > 0: + self.setScrollValue(self.nscrollvalue - 1) + self.focus(self.lastTime) + + ''' + sets focus to the given control. + @param textControl + ''' + + def focus(self, textControl): + textControl.setFocus() + text = textControl.Text + textControl.Selection = uno.createUnoStruct( \ + 'com.sun.star.awt.Selection', 0, len(text)) + self.focusGained2(textControl) + + ''' + moves the given row one row down. + @param guiRow the gui index of the row to move. + @param control the control to gain focus after moving. + ''' + + def rowDown(self, guiRow=None, control=None): + try: + if guiRow is None: + guiRow = self.lastFocusRow - self.nscrollvalue + if control is None: + control = self.lastFocusControl + # only perform if this is not the last row. + actuallRow = guiRow + self.nscrollvalue + if actuallRow + 1 < len(self.scrollfields): + # get the current selection + selection = control.Selection + # the last row should scroll... + scroll = (guiRow == self.nblockincrement - 1) + if scroll: + self.setScrollValue(self.nscrollvalue + 1) + + scroll1 = self.nscrollvalue + if scroll: + aux = -1 + else: + aux = 1 + self.switchRows(guiRow, guiRow + aux) + if self.nscrollvalue != scroll1: + guiRow += (self.nscrollvalue - scroll1) + + self.setSelection(guiRow + (not scroll), control, selection) + except Exception: + traceback.print_exc() + + ''' + move the current row up + ''' + + def rowUp(self, guiRow=None, control=None): + try: + if guiRow is None: + guiRow = self.lastFocusRow - self.nscrollvalue + if control is None: + control = self.lastFocusControl + # only perform if this is not the first row + actuallRow = guiRow + self.nscrollvalue + if actuallRow > 0: + # get the current selection + selection = control.Selection + # the last row should scroll... + scroll = (guiRow == 0) + if scroll: + self.setScrollValue(self.nscrollvalue - 1) + if scroll: + aux = 1 + else: + aux = -1 + self.switchRows(guiRow, guiRow + aux) + self.setSelection(guiRow - (not scroll), control, selection) + except Exception: + traceback.print_exc() + + ''' + moves the cursor up. + @param guiRow + @param control + ''' + + def cursorUp(self, guiRow, control): + # is this the last full row ? + actuallRow = guiRow + self.nscrollvalue + #if this is the first row + if actuallRow == 0: + return + # the first row should scroll... + + scroll = (guiRow == 0) + if scroll: + self.setScrollValue(self.nscrollvalue - 1) + upperRow = self.ControlGroupVector[guiRow] + else: + upperRow = self.ControlGroupVector[guiRow - 1] + + self.focus(self.getControl(upperRow, control)) + + ''' + moves the cursor down + @param guiRow + @param control + ''' + + def cursorDown(self, guiRow, control): + # is this the last full row ? + actuallRow = guiRow + self.nscrollvalue + #if this is the last row, exit + if actuallRow == len(self.scrollfields) - 1: + return + # the first row should scroll... + + scroll = (guiRow == self.nblockincrement - 1) + if scroll: + self.setScrollValue(self.nscrollvalue + 1) + lowerRow = self.ControlGroupVector[guiRow] + else: + # if we scrolled we are done... + #otherwise... + lowerRow = self.ControlGroupVector[guiRow + 1] + + self.focus(self.getControl(lowerRow, control)) + + ''' + changes the values of the given rows with each other + @param row1 one can figure out what this parameter is... + @param row2 one can figure out what this parameter is... + ''' + + def switchRows(self, row1, row2): + o1 = self.scrollfields[row1 + self.nscrollvalue] + o2 = self.scrollfields[row2 + self.nscrollvalue] + temp = None + for i in range(1, len(o1)): + temp = o1[i].Value + o1[i].Value = o2[i].Value + o2[i].Value = temp + self.fillupControl(row1) + self.fillupControl(row2) + self.updateDocumentRow(row1 + self.nscrollvalue, o1) + self.updateDocumentRow(row2 + self.nscrollvalue, o2) + + ''' + if we changed the last row, add another one... + ''' + if (row1 + self.nscrollvalue + 1 == \ + len(self.scrollfields)) \ + or (row2 + self.nscrollvalue + 1 == \ + len(self.scrollfields)): + + self.insertRowAtEnd() + ''' + if we did not change the last row but + we did change the one before - check if we + have two empty rows at the end. + If so, delete the last one... + ''' + elif (row1 + self.nscrollvalue) + \ + (row2 + self.nscrollvalue) \ + == (len(self.scrollfields) * 2 - 5): + if self.isRowEmpty(len(self.scrollfields) - 2) \ + and self.isRowEmpty( + len(self.scrollfields) - 1): + self.removeLastRow() + self.reduceDocumentToTopics() + + ''' + sets a text selection to a given control. + This is used when one moves a row up or down. + After moving row X to X+/-1, the selection (or cursor position) of the + last focused control should be restored. + The control's row is the given guiRow. + The control's column is detected according to the given event. + This method is called as subsequent to different events, + thus it is comfortable to use the event here to detect the column, + rather than in the different event methods. + @param guiRow the row of the control to set the selection to. + @param eventSource helps to detect + the control's column to set the selection to. + @param s the selection object to set. + ''' + + def setSelection(self, guiRow, eventSource, s): + cr = self.ControlGroupVector[guiRow] + control = self.getControl(cr, eventSource) + control.setFocus() + control.setSelection(s) + + ''' + returns a control out of the given row, according to a column number. + @param cr control row object. + @param column the column number. + @return the control... + ''' + + def getControlByIndex(self, cr, column): + tmp_switch_var1 = column + if tmp_switch_var1 == 0: + return cr.label + elif tmp_switch_var1 == 1: + return cr.textbox + elif tmp_switch_var1 == 2: + return cr.combobox + elif tmp_switch_var1 == 3: + return cr.timebox + else: + raise Exception("No such column"); + + '''getControl + returns a control out of the given row, which is + in the same column as the given control. + @param cr control row object + @param control a control indicating a column. + @return + ''' + + def getControl(self, cr, control): + column = self.getColumn(control) + return self.getControlByIndex(cr, column) + + ''' + returns the column number of the given control. + @param control + @return + ''' + + def getColumn(self, control): + name = control.Model.Name + if name.startswith(TopicsControl.TOPIC): + return 1 + if name.startswith(TopicsControl.RESP): + return 2 + if name.startswith(TopicsControl.TIME): + return 3 + if name.startswith(TopicsControl.LABEL): + return 0 + return -1 + + ''' + update the given row in the preview document with the given data. + @param row + @param data + ''' + + def updateDocumentRow(self, row, data=None): + if data is None: + data = self.scrollfields[row] + try: + for i in range(len(data)): + self.CurUnoDialog.myAgendaDoc.topics.writeCell( + row, i, data) + except Exception: + traceback.print_exc() + + ''' + updates a single cell in the preview document. + Is called when a single value is changed, since we really + don't have to update the whole row for one small change... + @param row the data row to update (topic number). + @param column the column to update (a gui column, not a document column). + @param data the data of the entire row. + ''' + + def updateDocumentCell(self, row, column, data): + try: + self.CurUnoDialog.myAgendaDoc.topics.writeCell( + row, column, data) + except Exception: + traceback.print_exc() + + ''' + when removing rows, this method updates + the preview document to show the number of rows + according to the data model. + ''' + + def reduceDocumentToTopics(self): + try: + self.CurUnoDialog.myAgendaDoc.topics.reduceDocumentTo( + len(self.scrollfields) - 1) + except Exception: + traceback.print_exc() + +''' +A class represting a single GUI row. +Note that the instance methods of this class +are being called and handle controls of +a single row. +''' + +class ControlRow(object): + + tabIndex = 520 + ''' + constructor. Create the row in the given dialog given coordinates, + with the given offset (row number) and tabindex. + Note that since I use this specifically for the agenda wizard, + the step and all control coordinates inside the + row are constant (5). + ''' + + def __init__(self, dialog, x, y, i, tabindex, topicsControl): + self.offset = i + self.dialog = dialog + self.topicsControl = topicsControl + self.label = self.dialog.insertLabel( + self.topicsControl.LABEL + str(i), + self.topicsControl.LABEL_PROPS, + (8, "" + str(i + 1) + ".", + x + 4, y + 2, self.topicsControl.iStep, tabindex, 10)) + self.textbox = self.dialog.insertTextField( + self.topicsControl.TOPIC + str(i), "topicTextChanged", + self.topicsControl.TEXT_PROPS, + (12, HelpIds.getHelpIdString(self.topicsControl.curHelpIndex + i * 3 + 1), + x + 15, y, self.topicsControl.iStep, tabindex + 1, 84), self) + self.combobox = self.dialog.insertTextField( + self.topicsControl.RESP + str(i), "responsibleTextChanged", + self.topicsControl.TEXT_PROPS, + (12, HelpIds.getHelpIdString(self.topicsControl.curHelpIndex + i * 3 + 2), + x + 103, y, self.topicsControl.iStep, tabindex + 2, 68), self) + self.timebox = self.dialog.insertTextField( + self.topicsControl.TIME + str(i), "timeTextChanged", + self.topicsControl.TEXT_PROPS, + (12, HelpIds.getHelpIdString(self.topicsControl.curHelpIndex + i * 3 + 3), + x + 175, y, self.topicsControl.iStep, tabindex + 3, 20), self) + self.setEnabled(False) + self.textbox.addKeyListener(KeyListenerProcAdapter(self.keyPressed)) + self.combobox.addKeyListener(KeyListenerProcAdapter(self.keyPressed)) + self.timebox.addKeyListener(KeyListenerProcAdapter(self.keyPressed)) + self.textbox.addFocusListener(FocusListenerProcAdapter( + self.topicsControl.focusGained)) + self.combobox.addFocusListener(FocusListenerProcAdapter( + self.topicsControl.focusGained)) + self.timebox.addFocusListener(FocusListenerProcAdapter( + self.topicsControl.focusGained)) + + def topicTextChanged(self): + try: + # update the data model + self.topicsControl.fieldInfo(self.offset, 1) + # update the preview document + self.topicsControl.fieldChanged(self.offset, 1) + except Exception: + traceback.print_exc() + + ''' + called through an event listener when the + responsible text is changed by the user. + updates the data model and the preview document. + ''' + + def responsibleTextChanged(self): + try: + # update the data model + self.topicsControl.fieldInfo(self.offset, 2) + # update the preview document + self.topicsControl.fieldChanged(self.offset, 2) + except Exception: + traceback.print_exc() + + ''' + called through an event listener when the + time text is changed by the user. + updates the data model and the preview document. + ''' + + def timeTextChanged(self): + try: + # update the data model + self.topicsControl.fieldInfo(self.offset, 3) + # update the preview document + self.topicsControl.fieldChanged(self.offset, 3) + except Exception: + traceback.print_exc() + + ''' + enables/disables the row. + @param enabled true for enable, false for disable. + ''' + + def setEnabled(self, enabled): + self.label.Model.Enabled = enabled + self.textbox.Model.Enabled = enabled + self.combobox.Model.Enabled = enabled + self.timebox.Model.Enabled = enabled + + ''' + Implementation of XKeyListener. + Optionally performs the one of the following: + cursor up, or down, row up or down + ''' + + def keyPressed(self, event): + try: + if self.isMoveDown(event): + self.topicsControl.rowDown(self.offset, event.Source) + elif self.isMoveUp(event): + self.topicsControl.rowUp(self.offset, event.Source) + elif self.isDown(event): + self.topicsControl.cursorDown(self.offset, event.Source) + elif self.isUp(event): + self.topicsControl.cursorUp(self.offset, event.Source) + + self.topicsControl.enableButtons() + except Exception: + traceback.print_exc() + + def isMoveDown(self, e): + return (e.KeyCode == DOWN) and (e.Modifiers == MOD1) + + def isMoveUp(self, e): + return (e.KeyCode == UP) and (e.Modifiers == MOD1) + + def isDown(self, e): + return (e.KeyCode == DOWN) and (e.Modifiers == 0) + + def isUp(self, e): + return (e.KeyCode == UP) and (e.Modifiers == 0) diff --git a/wizards/com/sun/star/wizards/agenda/__init__.py b/wizards/com/sun/star/wizards/agenda/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/__init__.py diff --git a/wizards/com/sun/star/wizards/agenda/agenda.component b/wizards/com/sun/star/wizards/agenda/agenda.component new file mode 100644 index 000000000..5778ae32f --- /dev/null +++ b/wizards/com/sun/star/wizards/agenda/agenda.component @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> +<component loader="com.sun.star.loader.Python" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.wizards.agenda.CallWizard"> + <service name="com.sun.star.task.Job"/> + </implementation> +</component> |