This commit is contained in:
Jane
2024-07-16 15:58:23 +08:00
parent 29bc31ade5
commit 0bbdcd9ef7
1621 changed files with 300787 additions and 0 deletions

View File

@@ -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 &gt;xxx&lt; 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))

View File

@@ -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))

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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>"

View File

@@ -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)