update
This commit is contained in:
@@ -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))
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -0,0 +1,161 @@
|
||||
#
|
||||
# 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 sys, os
|
||||
|
||||
if sys.version_info < (3,4):
|
||||
import imp
|
||||
imp.load_source('strings', os.path.join(os.path.dirname(__file__), '../common/strings.hrc'))
|
||||
import strings
|
||||
elif sys.version_info < (3,7):
|
||||
# imp is deprecated since Python v.3.4
|
||||
from importlib.machinery import SourceFileLoader
|
||||
SourceFileLoader('strings', os.path.join(os.path.dirname(__file__), '../common/strings.hrc')).load_module()
|
||||
import strings
|
||||
else:
|
||||
# have to jump through hoops since 3.7, partly because python does not like loading modules that do have a .py extension
|
||||
import importlib
|
||||
import importlib.util
|
||||
import importlib.machinery
|
||||
module_name = 'strings'
|
||||
path = os.path.join(os.path.dirname(__file__), '../common/strings.hrc')
|
||||
spec = importlib.util.spec_from_loader(
|
||||
module_name,
|
||||
importlib.machinery.SourceFileLoader(module_name, path)
|
||||
)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
sys.modules[module_name] = module
|
||||
strings = module
|
||||
|
||||
|
||||
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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -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>"
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user