Adds: unit tests and starts refactoring models

This commit is contained in:
Olivier Keshavjee 2017-11-23 11:03:14 +01:00
commit 64994cde3a
103 changed files with 2541 additions and 25812 deletions

3
.gitignore vendored
View file

@ -11,4 +11,5 @@ icons/Numix
.idea
dist
build
test-projects
test-projects
.cache

View file

@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#

View file

@ -1,61 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import ANIMNS
from element import Element
# Autogenerated
def Animate(**args):
return Element(qname = (ANIMNS,'animate'), **args)
def Animatecolor(**args):
return Element(qname = (ANIMNS,'animateColor'), **args)
def Animatemotion(**args):
return Element(qname = (ANIMNS,'animateMotion'), **args)
def Animatetransform(**args):
return Element(qname = (ANIMNS,'animateTransform'), **args)
def Audio(**args):
return Element(qname = (ANIMNS,'audio'), **args)
def Command(**args):
return Element(qname = (ANIMNS,'command'), **args)
def Iterate(**args):
return Element(qname = (ANIMNS,'iterate'), **args)
def Par(**args):
return Element(qname = (ANIMNS,'par'), **args)
def Param(**args):
return Element(qname = (ANIMNS,'param'), **args)
def Seq(**args):
return Element(qname = (ANIMNS,'seq'), **args)
def Set(**args):
return Element(qname = (ANIMNS,'set'), **args)
def Transitionfilter(**args):
return Element(qname = (ANIMNS,'transitionFilter'), **args)

File diff suppressed because it is too large Load diff

View file

@ -1,96 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2013 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from __future__ import absolute_import
from .namespaces import CHARTNS
from .element import Element
# Autogenerated
def Axis(**args):
return Element(qname = (CHARTNS,'axis'), **args)
def Categories(**args):
return Element(qname = (CHARTNS,'categories'), **args)
def Chart(**args):
return Element(qname = (CHARTNS,'chart'), **args)
def DataLabel(**args):
return Element(qname = (CHARTNS,'data-label'), **args)
def DataPoint(**args):
return Element(qname = (CHARTNS,'data-point'), **args)
def Domain(**args):
return Element(qname = (CHARTNS,'domain'), **args)
def Equation(**args):
return Element(qname = (CHARTNS,'equation'), **args)
def ErrorIndicator(**args):
return Element(qname = (CHARTNS,'error-indicator'), **args)
def Floor(**args):
return Element(qname = (CHARTNS,'floor'), **args)
def Footer(**args):
return Element(qname = (CHARTNS,'footer'), **args)
def Grid(**args):
return Element(qname = (CHARTNS,'grid'), **args)
def LabelSeparator(**args):
return Element(qname = (CHARTNS,'label-separator'), **args)
def Legend(**args):
return Element(qname = (CHARTNS,'legend'), **args)
def MeanValue(**args):
return Element(qname = (CHARTNS,'mean-value'), **args)
def PlotArea(**args):
return Element(qname = (CHARTNS,'plot-area'), **args)
def RegressionCurve(**args):
return Element(qname = (CHARTNS,'regression-curve'), **args)
def Series(**args):
return Element(qname = (CHARTNS,'series'), **args)
def StockGainMarker(**args):
return Element(qname = (CHARTNS,'stock-gain-marker'), **args)
def StockLossMarker(**args):
return Element(qname = (CHARTNS,'stock-loss-marker'), **args)
def StockRangeLine(**args):
return Element(qname = (CHARTNS,'stock-range-line'), **args)
def Subtitle(**args):
return Element(qname = (CHARTNS,'subtitle'), **args)
def SymbolImage(**args):
return Element(qname = (CHARTNS,'symbol-image'), **args)
def Title(**args):
return Element(qname = (CHARTNS,'title'), **args)
def Wall(**args):
return Element(qname = (CHARTNS,'wall'), **args)

View file

@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import CONFIGNS
from element import Element
# Autogenerated
def ConfigItem(**args):
return Element(qname = (CONFIGNS, 'config-item'), **args)
def ConfigItemMapEntry(**args):
return Element(qname = (CONFIGNS,'config-item-map-entry'), **args)
def ConfigItemMapIndexed(**args):
return Element(qname = (CONFIGNS,'config-item-map-indexed'), **args)
def ConfigItemMapNamed(**args):
return Element(qname = (CONFIGNS,'config-item-map-named'), **args)
def ConfigItemSet(**args):
return Element(qname = (CONFIGNS, 'config-item-set'), **args)

View file

@ -1,72 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import DCNS
from element import Element
# Autogenerated
def Creator(**args):
return Element(qname = (DCNS,'creator'), **args)
def Date(**args):
return Element(qname = (DCNS,'date'), **args)
def Description(**args):
return Element(qname = (DCNS,'description'), **args)
def Language(**args):
return Element(qname = (DCNS,'language'), **args)
def Subject(**args):
return Element(qname = (DCNS,'subject'), **args)
def Title(**args):
return Element(qname = (DCNS,'title'), **args)
# The following complete the Dublin Core elements, but there is no
# guarantee a compliant implementation of OpenDocument will preserve
# these elements
#def Contributor(**args):
# return Element(qname = (DCNS,'contributor'), **args)
#def Coverage(**args):
# return Element(qname = (DCNS,'coverage'), **args)
#def Format(**args):
# return Element(qname = (DCNS,'format'), **args)
#def Identifier(**args):
# return Element(qname = (DCNS,'identifier'), **args)
#def Publisher(**args):
# return Element(qname = (DCNS,'publisher'), **args)
#def Relation(**args):
# return Element(qname = (DCNS,'relation'), **args)
#def Rights(**args):
# return Element(qname = (DCNS,'rights'), **args)
#def Source(**args):
# return Element(qname = (DCNS,'source'), **args)
#def Type(**args):
# return Element(qname = (DCNS,'type'), **args)

View file

@ -1,45 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
import sys, os.path
sys.path.append(os.path.dirname(__file__))
from namespaces import DR3DNS
from element import Element
from draw import StyleRefElement
# Autogenerated
def Cube(**args):
return StyleRefElement(qname = (DR3DNS,'cube'), **args)
def Extrude(**args):
return StyleRefElement(qname = (DR3DNS,'extrude'), **args)
def Light(Element):
return StyleRefElement(qname = (DR3DNS,'light'), **args)
def Rotate(**args):
return StyleRefElement(qname = (DR3DNS,'rotate'), **args)
def Scene(**args):
return StyleRefElement(qname = (DR3DNS,'scene'), **args)
def Sphere(**args):
return StyleRefElement(qname = (DR3DNS,'sphere'), **args)

View file

@ -1,188 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
import sys, os.path
sys.path.append(os.path.dirname(__file__))
from namespaces import DRAWNS, STYLENS, PRESENTATIONNS
from element import Element
def StyleRefElement(stylename=None, classnames=None, **args):
qattrs = {}
if stylename is not None:
f = stylename.getAttrNS(STYLENS, 'family')
if f == 'graphic':
qattrs[(DRAWNS,u'style-name')]= stylename
elif f == 'presentation':
qattrs[(PRESENTATIONNS,u'style-name')]= stylename
else:
raise ValueError( "Style's family must be either 'graphic' or 'presentation'")
if classnames is not None:
f = classnames[0].getAttrNS(STYLENS, 'family')
if f == 'graphic':
qattrs[(DRAWNS,u'class-names')]= classnames
elif f == 'presentation':
qattrs[(PRESENTATIONNS,u'class-names')]= classnames
else:
raise ValueError( "Style's family must be either 'graphic' or 'presentation'")
return Element(qattributes=qattrs, **args)
def DrawElement(name=None, **args):
e = Element(name=name, **args)
if 'displayname' not in args:
e.setAttrNS(DRAWNS,'display-name', name)
return e
# Autogenerated
def A(**args):
args.setdefault('type', 'simple')
return Element(qname = (DRAWNS,'a'), **args)
def Applet(**args):
return Element(qname = (DRAWNS,'applet'), **args)
def AreaCircle(**args):
return Element(qname = (DRAWNS,'area-circle'), **args)
def AreaPolygon(**args):
return Element(qname = (DRAWNS,'area-polygon'), **args)
def AreaRectangle(**args):
return Element(qname = (DRAWNS,'area-rectangle'), **args)
def Caption(**args):
return StyleRefElement(qname = (DRAWNS,'caption'), **args)
def Circle(**args):
return StyleRefElement(qname = (DRAWNS,'circle'), **args)
def Connector(**args):
return StyleRefElement(qname = (DRAWNS,'connector'), **args)
def ContourPath(**args):
return Element(qname = (DRAWNS,'contour-path'), **args)
def ContourPolygon(**args):
return Element(qname = (DRAWNS,'contour-polygon'), **args)
def Control(**args):
return StyleRefElement(qname = (DRAWNS,'control'), **args)
def CustomShape(**args):
return StyleRefElement(qname = (DRAWNS,'custom-shape'), **args)
def Ellipse(**args):
return StyleRefElement(qname = (DRAWNS,'ellipse'), **args)
def EnhancedGeometry(**args):
return Element(qname = (DRAWNS,'enhanced-geometry'), **args)
def Equation(**args):
return Element(qname = (DRAWNS,'equation'), **args)
def FillImage(**args):
args.setdefault('type', 'simple')
return DrawElement(qname = (DRAWNS,'fill-image'), **args)
def FloatingFrame(**args):
args.setdefault('type', 'simple')
return Element(qname = (DRAWNS,'floating-frame'), **args)
def Frame(**args):
return StyleRefElement(qname = (DRAWNS,'frame'), **args)
def G(**args):
return StyleRefElement(qname = (DRAWNS,'g'), **args)
def GluePoint(**args):
return Element(qname = (DRAWNS,'glue-point'), **args)
def Gradient(**args):
return DrawElement(qname = (DRAWNS,'gradient'), **args)
def Handle(**args):
return Element(qname = (DRAWNS,'handle'), **args)
def Hatch(**args):
return DrawElement(qname = (DRAWNS,'hatch'), **args)
def Image(**args):
return Element(qname = (DRAWNS,'image'), **args)
def ImageMap(**args):
return Element(qname = (DRAWNS,'image-map'), **args)
def Layer(**args):
return Element(qname = (DRAWNS,'layer'), **args)
def LayerSet(**args):
return Element(qname = (DRAWNS,'layer-set'), **args)
def Line(**args):
return StyleRefElement(qname = (DRAWNS,'line'), **args)
def Marker(**args):
return DrawElement(qname = (DRAWNS,'marker'), **args)
def Measure(**args):
return StyleRefElement(qname = (DRAWNS,'measure'), **args)
def Object(**args):
return Element(qname = (DRAWNS,'object'), **args)
def ObjectOle(**args):
return Element(qname = (DRAWNS,'object-ole'), **args)
def Opacity(**args):
return DrawElement(qname = (DRAWNS,'opacity'), **args)
def Page(**args):
return Element(qname = (DRAWNS,'page'), **args)
def PageThumbnail(**args):
return StyleRefElement(qname = (DRAWNS,'page-thumbnail'), **args)
def Param(**args):
return Element(qname = (DRAWNS,'param'), **args)
def Path(**args):
return StyleRefElement(qname = (DRAWNS,'path'), **args)
def Plugin(**args):
args.setdefault('type', 'simple')
return Element(qname = (DRAWNS,'plugin'), **args)
def Polygon(**args):
return StyleRefElement(qname = (DRAWNS,'polygon'), **args)
def Polyline(**args):
return StyleRefElement(qname = (DRAWNS,'polyline'), **args)
def Rect(**args):
return StyleRefElement(qname = (DRAWNS,'rect'), **args)
def RegularPolygon(**args):
return StyleRefElement(qname = (DRAWNS,'regular-polygon'), **args)
def StrokeDash(**args):
return DrawElement(qname = (DRAWNS,'stroke-dash'), **args)
def TextBox(**args):
return Element(qname = (DRAWNS,'text-box'), **args)

View file

@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
# Create a <text:list-style> element from a text string.
# Copyright (C) 2008 J. David Eisenberg
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Contributor(s):
#
import re, sys, os.path
sys.path.append(os.path.dirname(__file__))
from style import Style, TextProperties, ListLevelProperties
from text import ListStyle,ListLevelStyleNumber,ListLevelStyleBullet
"""
Create a <text:list-style> element from a string or array.
List styles require a lot of code to create one level at a time.
These routines take a string and delimiter, or a list of
strings, and creates a <text:list-style> element for you.
Each item in the string (or array) represents a list level
* style for levels 1-10.</p>
*
* <p>If an item contains <code>1</code>, <code>I</code>,
* <code>i</code>, <code>A</code>, or <code>a</code>, then it is presumed
* to be a numbering style; otherwise it is a bulleted style.</p>
"""
_MAX_LIST_LEVEL = 10
SHOW_ALL_LEVELS = True
SHOW_ONE_LEVEL = False
def styleFromString(name, specifiers, delim, spacing, showAllLevels):
specArray = specifiers.split(delim)
return styleFromList( name, specArray, spacing, showAllLevels )
def styleFromList( styleName, specArray, spacing, showAllLevels):
bullet = ""
numPrefix = ""
numSuffix = ""
numberFormat = ""
cssLengthNum = 0
cssLengthUnits = ""
numbered = False
displayLevels = 0
listStyle = ListStyle(name=styleName)
numFormatPattern = re.compile("([1IiAa])")
cssLengthPattern = re.compile("([^a-z]+)\\s*([a-z]+)?")
m = cssLengthPattern.search( spacing )
if (m != None):
cssLengthNum = float(m.group(1))
if (m.lastindex == 2):
cssLengthUnits = m.group(2)
i = 0
while i < len(specArray):
specification = specArray[i]
m = numFormatPattern.search(specification)
if (m != None):
numberFormat = m.group(1)
numPrefix = specification[0:m.start(1)]
numSuffix = specification[m.end(1):]
bullet = ""
numbered = True
if (showAllLevels):
displayLevels = i + 1
else:
displayLevels = 1
else: # it's a bullet style
bullet = specification
numPrefix = ""
numSuffix = ""
numberFormat = ""
displayLevels = 1
numbered = False
if (numbered):
lls = ListLevelStyleNumber(level=(i+1))
if (numPrefix != ''):
lls.setAttribute('numprefix', numPrefix)
if (numSuffix != ''):
lls.setAttribute('numsuffix', numSuffix)
lls.setAttribute('displaylevels', displayLevels)
else:
lls = ListLevelStyleBullet(level=(i+1),bulletchar=bullet[0])
llp = ListLevelProperties()
llp.setAttribute('spacebefore', str(cssLengthNum * (i+1)) + cssLengthUnits)
llp.setAttribute('minlabelwidth', str(cssLengthNum) + cssLengthUnits)
lls.addElement( llp )
listStyle.addElement(lls)
i += 1
return listStyle
# vim: set expandtab sw=4 :

View file

@ -1,541 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2007-2010 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
# Note: This script has copied a lot of text from xml.dom.minidom.
# Whatever license applies to that file also applies to this file.
#
import sys, os.path
sys.path.append(os.path.dirname(__file__))
import xml.dom
from xml.dom.minicompat import *
from namespaces import nsdict
import grammar
from attrconverters import AttrConverters
if sys.version_info[0] == 3:
unicode=str # unicode function does not exist
# The following code is pasted form xml.sax.saxutils
# Tt makes it possible to run the code without the xml sax package installed
# To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
def _escape(data, entities={}):
""" Escape &, <, and > in a string of data.
You can escape other strings of data by passing a dictionary as
the optional entities parameter. The keys and values must all be
strings; each key will be replaced with its corresponding value.
"""
data = data.replace("&", "&amp;")
data = data.replace("<", "&lt;")
data = data.replace(">", "&gt;")
for chars, entity in entities.items():
data = data.replace(chars, entity)
return data
def _quoteattr(data, entities={}):
""" Escape and quote an attribute value.
Escape &, <, and > in a string of data, then quote it for use as
an attribute value. The \" character will be escaped as well, if
necessary.
You can escape other strings of data by passing a dictionary as
the optional entities parameter. The keys and values must all be
strings; each key will be replaced with its corresponding value.
"""
entities['\n']='&#10;'
entities['\r']='&#12;'
data = _escape(data, entities)
if '"' in data:
if "'" in data:
data = '"%s"' % data.replace('"', "&quot;")
else:
data = "'%s'" % data
else:
data = '"%s"' % data
return data
def _nssplit(qualifiedName):
""" Split a qualified name into namespace part and local part. """
fields = qualifiedName.split(':', 1)
if len(fields) == 2:
return fields
else:
return (None, fields[0])
def _nsassign(namespace):
return nsdict.setdefault(namespace,"ns" + str(len(nsdict)))
# Exceptions
class IllegalChild(Exception):
""" Complains if you add an element to a parent where it is not allowed """
class IllegalText(Exception):
""" Complains if you add text or cdata to an element where it is not allowed """
class Node(xml.dom.Node):
""" super class for more specific nodes """
parentNode = None
nextSibling = None
previousSibling = None
def hasChildNodes(self):
""" Tells whether this element has any children; text nodes,
subelements, whatever.
"""
if self.childNodes:
return True
else:
return False
def _get_childNodes(self):
return self.childNodes
def _get_firstChild(self):
if self.childNodes:
return self.childNodes[0]
def _get_lastChild(self):
if self.childNodes:
return self.childNodes[-1]
def insertBefore(self, newChild, refChild):
""" Inserts the node newChild before the existing child node refChild.
If refChild is null, insert newChild at the end of the list of children.
"""
if newChild.nodeType not in self._child_node_types:
raise IllegalChild( "%s cannot be child of %s" % (newChild.tagName, self.tagName))
if newChild.parentNode is not None:
newChild.parentNode.removeChild(newChild)
if refChild is None:
self.appendChild(newChild)
else:
try:
index = self.childNodes.index(refChild)
except ValueError:
raise xml.dom.NotFoundErr()
self.childNodes.insert(index, newChild)
newChild.nextSibling = refChild
refChild.previousSibling = newChild
if index:
node = self.childNodes[index-1]
node.nextSibling = newChild
newChild.previousSibling = node
else:
newChild.previousSibling = None
newChild.parentNode = self
return newChild
def appendChild(self, newChild):
""" Adds the node newChild to the end of the list of children of this node.
If the newChild is already in the tree, it is first removed.
"""
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
for c in tuple(newChild.childNodes):
self.appendChild(c)
### The DOM does not clearly specify what to return in this case
return newChild
if newChild.nodeType not in self._child_node_types:
raise IllegalChild( "<%s> is not allowed in %s" % ( newChild.tagName, self.tagName))
if newChild.parentNode is not None:
newChild.parentNode.removeChild(newChild)
_append_child(self, newChild)
newChild.nextSibling = None
return newChild
def removeChild(self, oldChild):
""" Removes the child node indicated by oldChild from the list of children, and returns it.
"""
#FIXME: update ownerDocument.element_dict or find other solution
try:
self.childNodes.remove(oldChild)
except ValueError:
raise xml.dom.NotFoundErr()
if oldChild.nextSibling is not None:
oldChild.nextSibling.previousSibling = oldChild.previousSibling
if oldChild.previousSibling is not None:
oldChild.previousSibling.nextSibling = oldChild.nextSibling
oldChild.nextSibling = oldChild.previousSibling = None
if self.ownerDocument:
self.ownerDocument.clear_caches()
oldChild.parentNode = None
return oldChild
def __str__(self):
val = []
for c in self.childNodes:
val.append(str(c))
return ''.join(val)
def __unicode__(self):
val = []
for c in self.childNodes:
val.append(unicode(c))
return u''.join(val)
defproperty(Node, "firstChild", doc="First child node, or None.")
defproperty(Node, "lastChild", doc="Last child node, or None.")
def _append_child(self, node):
# fast path with less checks; usable by DOM builders if careful
childNodes = self.childNodes
if childNodes:
last = childNodes[-1]
node.__dict__["previousSibling"] = last
last.__dict__["nextSibling"] = node
childNodes.append(node)
node.__dict__["parentNode"] = self
class Childless:
""" Mixin that makes childless-ness easy to implement and avoids
the complexity of the Node methods that deal with children.
"""
attributes = None
childNodes = EmptyNodeList()
firstChild = None
lastChild = None
def _get_firstChild(self):
return None
def _get_lastChild(self):
return None
def appendChild(self, node):
""" Raises an error """
raise xml.dom.HierarchyRequestErr(
self.tagName + " nodes cannot have children")
def hasChildNodes(self):
return False
def insertBefore(self, newChild, refChild):
""" Raises an error """
raise xml.dom.HierarchyRequestErr(
self.tagName + " nodes do not have children")
def removeChild(self, oldChild):
""" Raises an error """
raise xml.dom.NotFoundErr(
self.tagName + " nodes do not have children")
def replaceChild(self, newChild, oldChild):
""" Raises an error """
raise xml.dom.HierarchyRequestErr(
self.tagName + " nodes do not have children")
class Text(Childless, Node):
nodeType = Node.TEXT_NODE
tagName = "Text"
def __init__(self, data):
self.data = data
def __str__(self):
return self.data
def __unicode__(self):
return self.data
def toXml(self,level,f):
""" Write XML in UTF-8 """
if self.data:
f.write(_escape(unicode(self.data)))
class CDATASection(Text, Childless):
nodeType = Node.CDATA_SECTION_NODE
def toXml(self,level,f):
""" Generate XML output of the node. If the text contains "]]>", then
escape it by going out of CDATA mode (]]>), then write the string
and then go into CDATA mode again. (<![CDATA[)
"""
if self.data:
f.write('<![CDATA[%s]]>' % self.data.replace(']]>',']]>]]><![CDATA['))
class Element(Node):
""" Creates a arbitrary element and is intended to be subclassed not used on its own.
This element is the base of every element it defines a class which resembles
a xml-element. The main advantage of this kind of implementation is that you don't
have to create a toXML method for every different object. Every element
consists of an attribute, optional subelements, optional text and optional cdata.
"""
nodeType = Node.ELEMENT_NODE
namespaces = {} # Due to shallow copy this is a static variable
_child_node_types = (Node.ELEMENT_NODE,
Node.PROCESSING_INSTRUCTION_NODE,
Node.COMMENT_NODE,
Node.TEXT_NODE,
Node.CDATA_SECTION_NODE,
Node.ENTITY_REFERENCE_NODE)
def __init__(self, attributes=None, text=None, cdata=None, qname=None, qattributes=None, check_grammar=True, **args):
if qname is not None:
self.qname = qname
assert(hasattr(self, 'qname'))
self.ownerDocument = None
self.childNodes=[]
self.allowed_children = grammar.allowed_children.get(self.qname)
prefix = self.get_nsprefix(self.qname[0])
self.tagName = prefix + ":" + self.qname[1]
if text is not None:
self.addText(text)
if cdata is not None:
self.addCDATA(cdata)
allowed_attrs = self.allowed_attributes()
if allowed_attrs is not None:
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
self.attributes={}
# Load the attributes from the 'attributes' argument
if attributes:
for attr, value in attributes.items():
self.setAttribute(attr, value)
# Load the qualified attributes
if qattributes:
for attr, value in qattributes.items():
self.setAttrNS(attr[0], attr[1], value)
if allowed_attrs is not None:
# Load the attributes from the 'args' argument
for arg in args.keys():
self.setAttribute(arg, args[arg])
else:
for arg in args.keys(): # If any attribute is allowed
self.attributes[arg]=args[arg]
if not check_grammar:
return
# Test that all mandatory attributes have been added.
required = grammar.required_attributes.get(self.qname)
if required:
for r in required:
if self.getAttrNS(r[0],r[1]) is None:
raise AttributeError( "Required attribute missing: %s in <%s>" % (r[1].lower().replace('-',''), self.tagName))
def get_knownns(self, prefix):
""" Odfpy maintains a list of known namespaces. In some cases a prefix is used, and
we need to know which namespace it resolves to.
"""
global nsdict
for ns,p in nsdict.items():
if p == prefix: return ns
return None
def get_nsprefix(self, namespace):
""" Odfpy maintains a list of known namespaces. In some cases we have a namespace URL,
and needs to look up or assign the prefix for it.
"""
if namespace is None: namespace = ""
prefix = _nsassign(namespace)
if not namespace in self.namespaces:
self.namespaces[namespace] = prefix
return prefix
def allowed_attributes(self):
return grammar.allowed_attributes.get(self.qname)
def _setOwnerDoc(self, element):
element.ownerDocument = self.ownerDocument
for child in element.childNodes:
self._setOwnerDoc(child)
def addElement(self, element, check_grammar=True):
""" adds an element to an Element
Element.addElement(Element)
"""
if check_grammar and self.allowed_children is not None:
if element.qname not in self.allowed_children:
raise IllegalChild( "<%s> is not allowed in <%s>" % ( element.tagName, self.tagName))
self.appendChild(element)
self._setOwnerDoc(element)
if self.ownerDocument:
self.ownerDocument.rebuild_caches(element)
def addText(self, text, check_grammar=True):
""" Adds text to an element
Setting check_grammar=False turns off grammar checking
"""
if check_grammar and self.qname not in grammar.allows_text:
raise IllegalText( "The <%s> element does not allow text" % self.tagName)
else:
if text != '':
self.appendChild(Text(text))
def addCDATA(self, cdata, check_grammar=True):
""" Adds CDATA to an element
Setting check_grammar=False turns off grammar checking
"""
if check_grammar and self.qname not in grammar.allows_text:
raise IllegalText( "The <%s> element does not allow text" % self.tagName)
else:
self.appendChild(CDATASection(cdata))
def removeAttribute(self, attr, check_grammar=True):
""" Removes an attribute by name. """
allowed_attrs = self.allowed_attributes()
if allowed_attrs is None:
if type(attr) == type(()):
prefix, localname = attr
self.removeAttrNS(prefix, localname)
else:
raise AttributeError( "Unable to add simple attribute - use (namespace, localpart)")
else:
# Construct a list of allowed arguments
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
if check_grammar and attr not in allowed_args:
raise AttributeError( "Attribute %s is not allowed in <%s>" % ( attr, self.tagName))
i = allowed_args.index(attr)
self.removeAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
def setAttribute(self, attr, value, check_grammar=True):
""" Add an attribute to the element
This is sort of a convenience method. All attributes in ODF have
namespaces. The library knows what attributes are legal and then allows
the user to provide the attribute as a keyword argument and the
library will add the correct namespace.
Must overwrite, If attribute already exists.
"""
allowed_attrs = self.allowed_attributes()
if allowed_attrs is None:
if type(attr) == type(()):
prefix, localname = attr
self.setAttrNS(prefix, localname, value)
else:
raise AttributeError( "Unable to add simple attribute - use (namespace, localpart)")
else:
# Construct a list of allowed arguments
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
if check_grammar and attr not in allowed_args:
raise AttributeError( "Attribute %s is not allowed in <%s>" % ( attr, self.tagName))
i = allowed_args.index(attr)
self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value)
def setAttrNS(self, namespace, localpart, value):
""" Add an attribute to the element
In case you need to add an attribute the library doesn't know about
then you must provide the full qualified name
It will not check that the attribute is legal according to the schema.
Must overwrite, If attribute already exists.
"""
allowed_attrs = self.allowed_attributes()
prefix = self.get_nsprefix(namespace)
# if allowed_attrs and (namespace, localpart) not in allowed_attrs:
# raise AttributeError( "Attribute %s:%s is not allowed in element <%s>" % ( prefix, localpart, self.tagName))
c = AttrConverters()
self.attributes[(namespace, localpart)] = c.convert((namespace, localpart), value, self)
def getAttrNS(self, namespace, localpart):
"""
gets an attribute, given a namespace and a key
@param namespace a unicode string or a bytes: the namespace
@param localpart a unicode string or a bytes:
the key to get the attribute
@return an attribute as a unicode string or a bytes: if both paramters
are byte strings, it will be a bytes; if both attributes are
unicode strings, it will be a unicode string
"""
prefix = self.get_nsprefix(namespace)
result = self.attributes.get((namespace, localpart))
assert(
(type(namespace), type(namespace), type(namespace) == \
type(b""), type(b""), type(b"")) or
(type(namespace), type(namespace), type(namespace) == \
type(u""), type(u""), type(u""))
)
return result
def removeAttrNS(self, namespace, localpart):
del self.attributes[(namespace, localpart)]
def getAttribute(self, attr):
""" Get an attribute value. The method knows which namespace the attribute is in
"""
allowed_attrs = self.allowed_attributes()
if allowed_attrs is None:
if type(attr) == type(()):
prefix, localname = attr
return self.getAttrNS(prefix, localname)
else:
raise AttributeError( "Unable to get simple attribute - use (namespace, localpart)")
else:
# Construct a list of allowed arguments
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
i = allowed_args.index(attr)
return self.getAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
def write_open_tag(self, level, f):
f.write(('<'+self.tagName))
if level == 0:
for namespace, prefix in self.namespaces.items():
f.write(u' xmlns:' + prefix + u'="'+ _escape(str(namespace))+'"')
for qname in self.attributes.keys():
prefix = self.get_nsprefix(qname[0])
f.write(u' '+_escape(str(prefix+u':'+qname[1]))+u'='+_quoteattr(unicode(self.attributes[qname])))
f.write(u'>')
def write_close_tag(self, level, f):
f.write('</'+self.tagName+'>')
def toXml(self, level, f):
"""
Generate an XML stream out of the tree structure
@param level integer: level in the XML tree; zero at root of the tree
@param f an open writable file able to accept unicode strings
"""
f.write(u'<'+self.tagName)
if level == 0:
for namespace, prefix in self.namespaces.items():
f.write(u' xmlns:' + prefix + u'="'+ _escape(str(namespace))+u'"')
for qname in self.attributes.keys():
prefix = self.get_nsprefix(qname[0])
f.write(u' '+_escape(unicode(prefix+':'+qname[1]))+u'='+_quoteattr(unicode(self.attributes[qname])))
if self.childNodes:
f.write(u'>')
for element in self.childNodes:
element.toXml(level+1,f)
f.write(u'</'+self.tagName+'>')
else:
f.write(u'/>')
def _getElementsByObj(self, obj, accumulator):
if self.qname == obj.qname:
accumulator.append(self)
for e in self.childNodes:
if e.nodeType == Node.ELEMENT_NODE:
accumulator = e._getElementsByObj(obj, accumulator)
return accumulator
def getElementsByType(self, element):
""" Gets elements based on the type, which is function from text.py, draw.py etc. """
obj = element(check_grammar=False)
return self._getElementsByObj(obj,[])
def isInstanceOf(self, element):
""" This is a check to see if the object is an instance of a type """
obj = element(check_grammar=False)
return self.qname == obj.qname

View file

@ -1,325 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2008 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import *
# Inline element don't cause a box
# They are analogous to the HTML elements SPAN, B, I etc.
inline_elements = (
(TEXTNS,u'a'),
(TEXTNS,u'author-initials'),
(TEXTNS,u'author-name'),
(TEXTNS,u'bibliography-mark'),
(TEXTNS,u'bookmark-ref'),
(TEXTNS,u'chapter'),
(TEXTNS,u'character-count'),
(TEXTNS,u'conditional-text'),
(TEXTNS,u'creation-date'),
(TEXTNS,u'creation-time'),
(TEXTNS,u'creator'),
(TEXTNS,u'database-display'),
(TEXTNS,u'database-name'),
(TEXTNS,u'database-next'),
(TEXTNS,u'database-row-number'),
(TEXTNS,u'database-row-select'),
(TEXTNS,u'date'),
(TEXTNS,u'dde-connection'),
(TEXTNS,u'description'),
(TEXTNS,u'editing-cycles'),
(TEXTNS,u'editing-duration'),
(TEXTNS,u'execute-macro'),
(TEXTNS,u'expression'),
(TEXTNS,u'file-name'),
(TEXTNS,u'hidden-paragraph'),
(TEXTNS,u'hidden-text'),
(TEXTNS,u'image-count'),
(TEXTNS,u'initial-creator'),
(TEXTNS,u'keywords'),
(TEXTNS,u'measure'),
(TEXTNS,u'modification-date'),
(TEXTNS,u'modification-time'),
(TEXTNS,u'note-ref'),
(TEXTNS,u'object-count'),
(TEXTNS,u'page-continuation'),
(TEXTNS,u'page-count'),
(TEXTNS,u'page-number'),
(TEXTNS,u'page-variable-get'),
(TEXTNS,u'page-variable-set'),
(TEXTNS,u'paragraph-count'),
(TEXTNS,u'placeholder'),
(TEXTNS,u'print-date'),
(TEXTNS,u'printed-by'),
(TEXTNS,u'print-time'),
(TEXTNS,u'reference-ref'),
(TEXTNS,u'ruby'),
(TEXTNS,u'ruby-base'),
(TEXTNS,u'ruby-text'),
(TEXTNS,u'script'),
(TEXTNS,u'sender-city'),
(TEXTNS,u'sender-company'),
(TEXTNS,u'sender-country'),
(TEXTNS,u'sender-email'),
(TEXTNS,u'sender-fax'),
(TEXTNS,u'sender-firstname'),
(TEXTNS,u'sender-initials'),
(TEXTNS,u'sender-lastname'),
(TEXTNS,u'sender-phone-private'),
(TEXTNS,u'sender-phone-work'),
(TEXTNS,u'sender-position'),
(TEXTNS,u'sender-postal-code'),
(TEXTNS,u'sender-state-or-province'),
(TEXTNS,u'sender-street'),
(TEXTNS,u'sender-title'),
(TEXTNS,u'sequence'),
(TEXTNS,u'sequence-ref'),
(TEXTNS,u'sheet-name'),
(TEXTNS,u'span'),
(TEXTNS,u'subject'),
(TEXTNS,u'table-count'),
(TEXTNS,u'table-formula'),
(TEXTNS,u'template-name'),
(TEXTNS,u'text-input'),
(TEXTNS,u'time'),
(TEXTNS,u'title'),
(TEXTNS,u'user-defined'),
(TEXTNS,u'user-field-get'),
(TEXTNS,u'user-field-input'),
(TEXTNS,u'variable-get'),
(TEXTNS,u'variable-input'),
(TEXTNS,u'variable-set'),
(TEXTNS,u'word-count'),
)
# It is almost impossible to determine what elements are block elements.
# There are so many that don't fit the form
block_elements = (
(TEXTNS,u'h'),
(TEXTNS,u'p'),
(TEXTNS,u'list'),
(TEXTNS,u'list-item'),
(TEXTNS,u'section'),
)
declarative_elements = (
(OFFICENS,u'font-face-decls'),
(PRESENTATIONNS,u'date-time-decl'),
(PRESENTATIONNS,u'footer-decl'),
(PRESENTATIONNS,u'header-decl'),
(TABLENS,u'table-template'),
(TEXTNS,u'alphabetical-index-entry-template'),
(TEXTNS,u'alphabetical-index-source'),
(TEXTNS,u'bibliography-entry-template'),
(TEXTNS,u'bibliography-source'),
(TEXTNS,u'dde-connection-decls'),
(TEXTNS,u'illustration-index-entry-template'),
(TEXTNS,u'illustration-index-source'),
(TEXTNS,u'index-source-styles'),
(TEXTNS,u'index-title-template'),
(TEXTNS,u'note-continuation-notice-backward'),
(TEXTNS,u'note-continuation-notice-forward'),
(TEXTNS,u'notes-configuration'),
(TEXTNS,u'object-index-entry-template'),
(TEXTNS,u'object-index-source'),
(TEXTNS,u'sequence-decls'),
(TEXTNS,u'table-index-entry-template'),
(TEXTNS,u'table-index-source'),
(TEXTNS,u'table-of-content-entry-template'),
(TEXTNS,u'table-of-content-source'),
(TEXTNS,u'user-field-decls'),
(TEXTNS,u'user-index-entry-template'),
(TEXTNS,u'user-index-source'),
(TEXTNS,u'variable-decls'),
)
empty_elements = (
(ANIMNS,u'animate'),
(ANIMNS,u'animateColor'),
(ANIMNS,u'animateMotion'),
(ANIMNS,u'animateTransform'),
(ANIMNS,u'audio'),
(ANIMNS,u'param'),
(ANIMNS,u'set'),
(ANIMNS,u'transitionFilter'),
(CHARTNS,u'categories'),
(CHARTNS,u'data-point'),
(CHARTNS,u'domain'),
(CHARTNS,u'error-indicator'),
(CHARTNS,u'floor'),
(CHARTNS,u'grid'),
(CHARTNS,u'legend'),
(CHARTNS,u'mean-value'),
(CHARTNS,u'regression-curve'),
(CHARTNS,u'stock-gain-marker'),
(CHARTNS,u'stock-loss-marker'),
(CHARTNS,u'stock-range-line'),
(CHARTNS,u'symbol-image'),
(CHARTNS,u'wall'),
(DR3DNS,u'cube'),
(DR3DNS,u'extrude'),
(DR3DNS,u'light'),
(DR3DNS,u'rotate'),
(DR3DNS,u'sphere'),
(DRAWNS,u'contour-path'),
(DRAWNS,u'contour-polygon'),
(DRAWNS,u'equation'),
(DRAWNS,u'fill-image'),
(DRAWNS,u'floating-frame'),
(DRAWNS,u'glue-point'),
(DRAWNS,u'gradient'),
(DRAWNS,u'handle'),
(DRAWNS,u'hatch'),
(DRAWNS,u'layer'),
(DRAWNS,u'marker'),
(DRAWNS,u'opacity'),
(DRAWNS,u'page-thumbnail'),
(DRAWNS,u'param'),
(DRAWNS,u'stroke-dash'),
(FORMNS,u'connection-resource'),
(FORMNS,u'list-value'),
(FORMNS,u'property'),
(MANIFESTNS,u'algorithm'),
(MANIFESTNS,u'key-derivation'),
(METANS,u'auto-reload'),
(METANS,u'document-statistic'),
(METANS,u'hyperlink-behaviour'),
(METANS,u'template'),
(NUMBERNS,u'am-pm'),
(NUMBERNS,u'boolean'),
(NUMBERNS,u'day'),
(NUMBERNS,u'day-of-week'),
(NUMBERNS,u'era'),
(NUMBERNS,u'fraction'),
(NUMBERNS,u'hours'),
(NUMBERNS,u'minutes'),
(NUMBERNS,u'month'),
(NUMBERNS,u'quarter'),
(NUMBERNS,u'scientific-number'),
(NUMBERNS,u'seconds'),
(NUMBERNS,u'text-content'),
(NUMBERNS,u'week-of-year'),
(NUMBERNS,u'year'),
(OFFICENS,u'dde-source'),
(PRESENTATIONNS,u'date-time'),
(PRESENTATIONNS,u'footer'),
(PRESENTATIONNS,u'header'),
(PRESENTATIONNS,u'placeholder'),
(PRESENTATIONNS,u'play'),
(PRESENTATIONNS,u'show'),
(PRESENTATIONNS,u'sound'),
(SCRIPTNS,u'event-listener'),
(STYLENS,u'column'),
(STYLENS,u'column-sep'),
(STYLENS,u'drop-cap'),
(STYLENS,u'footnote-sep'),
(STYLENS,u'list-level-properties'),
(STYLENS,u'map'),
(STYLENS,u'ruby-properties'),
(STYLENS,u'table-column-properties'),
(STYLENS,u'tab-stop'),
(STYLENS,u'text-properties'),
(SVGNS,u'definition-src'),
(SVGNS,u'font-face-format'),
(SVGNS,u'font-face-name'),
(SVGNS,u'stop'),
(TABLENS,u'body'),
(TABLENS,u'cell-address'),
(TABLENS,u'cell-range-source'),
(TABLENS,u'change-deletion'),
(TABLENS,u'consolidation'),
(TABLENS,u'database-source-query'),
(TABLENS,u'database-source-sql'),
(TABLENS,u'database-source-table'),
(TABLENS,u'data-pilot-display-info'),
(TABLENS,u'data-pilot-field-reference'),
(TABLENS,u'data-pilot-group-member'),
(TABLENS,u'data-pilot-layout-info'),
(TABLENS,u'data-pilot-member'),
(TABLENS,u'data-pilot-sort-info'),
(TABLENS,u'data-pilot-subtotal'),
(TABLENS,u'dependency'),
(TABLENS,u'error-macro'),
(TABLENS,u'even-columns'),
(TABLENS,u'even-rows'),
(TABLENS,u'filter-condition'),
(TABLENS,u'first-column'),
(TABLENS,u'first-row'),
(TABLENS,u'highlighted-range'),
(TABLENS,u'insertion-cut-off'),
(TABLENS,u'iteration'),
(TABLENS,u'label-range'),
(TABLENS,u'last-column'),
(TABLENS,u'last-row'),
(TABLENS,u'movement-cut-off'),
(TABLENS,u'named-expression'),
(TABLENS,u'named-range'),
(TABLENS,u'null-date'),
(TABLENS,u'odd-columns'),
(TABLENS,u'odd-rows'),
(TABLENS,u'operation'),
(TABLENS,u'scenario'),
(TABLENS,u'sort-by'),
(TABLENS,u'sort-groups'),
(TABLENS,u'source-range-address'),
(TABLENS,u'source-service'),
(TABLENS,u'subtotal-field'),
(TABLENS,u'table-column'),
(TABLENS,u'table-source'),
(TABLENS,u'target-range-address'),
(TEXTNS,u'alphabetical-index-auto-mark-file'),
(TEXTNS,u'alphabetical-index-mark'),
(TEXTNS,u'alphabetical-index-mark-end'),
(TEXTNS,u'alphabetical-index-mark-start'),
(TEXTNS,u'bookmark'),
(TEXTNS,u'bookmark-end'),
(TEXTNS,u'bookmark-start'),
(TEXTNS,u'change'),
(TEXTNS,u'change-end'),
(TEXTNS,u'change-start'),
(TEXTNS,u'dde-connection-decl'),
(TEXTNS,u'index-entry-bibliography'),
(TEXTNS,u'index-entry-chapter'),
(TEXTNS,u'index-entry-link-end'),
(TEXTNS,u'index-entry-link-start'),
(TEXTNS,u'index-entry-page-number'),
(TEXTNS,u'index-entry-tab-stop'),
(TEXTNS,u'index-entry-text'),
(TEXTNS,u'index-source-style'),
(TEXTNS,u'line-break'),
(TEXTNS,u'page'),
(TEXTNS,u'reference-mark'),
(TEXTNS,u'reference-mark-end'),
(TEXTNS,u'reference-mark-start'),
(TEXTNS,u's'),
(TEXTNS,u'section-source'),
(TEXTNS,u'sequence-decl'),
(TEXTNS,u'soft-page-break'),
(TEXTNS,u'sort-key'),
(TEXTNS,u'tab'),
(TEXTNS,u'toc-mark'),
(TEXTNS,u'toc-mark-end'),
(TEXTNS,u'toc-mark-start'),
(TEXTNS,u'user-field-decl'),
(TEXTNS,u'user-index-mark'),
(TEXTNS,u'user-index-mark-end'),
(TEXTNS,u'user-index-mark-start'),
(TEXTNS,u'variable-decl')
)

View file

@ -1,115 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import FORMNS
from element import Element
# Autogenerated
def Button(**args):
return Element(qname = (FORMNS,'button'), **args)
def Checkbox(**args):
return Element(qname = (FORMNS,'checkbox'), **args)
def Column(**args):
return Element(qname = (FORMNS,'column'), **args)
def Combobox(**args):
return Element(qname = (FORMNS,'combobox'), **args)
def ConnectionResource(**args):
return Element(qname = (FORMNS,'connection-resource'), **args)
def Date(**args):
return Element(qname = (FORMNS,'date'), **args)
def File(**args):
return Element(qname = (FORMNS,'file'), **args)
def FixedText(**args):
return Element(qname = (FORMNS,'fixed-text'), **args)
def Form(**args):
return Element(qname = (FORMNS,'form'), **args)
def FormattedText(**args):
return Element(qname = (FORMNS,'formatted-text'), **args)
def Frame(**args):
return Element(qname = (FORMNS,'frame'), **args)
def GenericControl(**args):
return Element(qname = (FORMNS,'generic-control'), **args)
def Grid(**args):
return Element(qname = (FORMNS,'grid'), **args)
def Hidden(**args):
return Element(qname = (FORMNS,'hidden'), **args)
def Image(**args):
return Element(qname = (FORMNS,'image'), **args)
def ImageFrame(**args):
return Element(qname = (FORMNS,'image-frame'), **args)
def Item(**args):
return Element(qname = (FORMNS,'item'), **args)
def ListProperty(**args):
return Element(qname = (FORMNS,'list-property'), **args)
def ListValue(**args):
return Element(qname = (FORMNS,'list-value'), **args)
def Listbox(**args):
return Element(qname = (FORMNS,'listbox'), **args)
def Number(**args):
return Element(qname = (FORMNS,'number'), **args)
def Option(**args):
return Element(qname = (FORMNS,'option'), **args)
def Password(**args):
return Element(qname = (FORMNS,'password'), **args)
def Properties(**args):
return Element(qname = (FORMNS,'properties'), **args)
def Property(**args):
return Element(qname = (FORMNS,'property'), **args)
def Radio(**args):
return Element(qname = (FORMNS,'radio'), **args)
def Text(**args):
return Element(qname = (FORMNS,'text'), **args)
def Textarea(**args):
return Element(qname = (FORMNS,'textarea'), **args)
def Time(**args):
return Element(qname = (FORMNS,'time'), **args)
def ValueRange(**args):
return Element(qname = (FORMNS,'value-range'), **args)

File diff suppressed because it is too large Load diff

View file

@ -1,115 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2007-2008 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
# This script is to be embedded in opendocument.py later
# The purpose is to read an ODT/ODP/ODS file and create the datastructure
# in memory. The user should then be able to make operations and then save
# the structure again.
from xml.sax import make_parser,handler
from xml.sax.xmlreader import InputSource
import xml.sax.saxutils
from element import Element
from namespaces import OFFICENS
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
#
# Parse the XML files
#
class LoadParser(handler.ContentHandler):
""" Extract headings from content.xml of an ODT file """
triggers = (
(OFFICENS, 'automatic-styles'), (OFFICENS, 'body'),
(OFFICENS, 'font-face-decls'), (OFFICENS, 'master-styles'),
(OFFICENS, 'meta'), (OFFICENS, 'scripts'),
(OFFICENS, 'settings'), (OFFICENS, 'styles') )
def __init__(self, document):
self.doc = document
self.data = []
self.level = 0
self.parse = False
def characters(self, data):
if self.parse == False:
return
self.data.append(data)
def startElementNS(self, tag, qname, attrs):
if tag in self.triggers:
self.parse = True
if self.doc._parsing != "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
self.parse = False
if self.parse == False:
return
self.level = self.level + 1
# Add any accumulated text content
content = ''.join(self.data)
if len(content.strip()) > 0:
self.parent.addText(content, check_grammar=False)
self.data = []
# Create the element
attrdict = {}
for (att,value) in attrs.items():
attrdict[att] = value
try:
e = Element(qname = tag, qattributes=attrdict, check_grammar=False)
self.curr = e
except AttributeError as v:
print ("Error: %s" % v)
if tag == (OFFICENS, 'automatic-styles'):
e = self.doc.automaticstyles
elif tag == (OFFICENS, 'body'):
e = self.doc.body
elif tag == (OFFICENS, 'master-styles'):
e = self.doc.masterstyles
elif tag == (OFFICENS, 'meta'):
e = self.doc.meta
elif tag == (OFFICENS,'scripts'):
e = self.doc.scripts
elif tag == (OFFICENS,'settings'):
e = self.doc.settings
elif tag == (OFFICENS,'styles'):
e = self.doc.styles
elif self.doc._parsing == "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
e = self.doc.fontfacedecls
elif hasattr(self,'parent'):
self.parent.addElement(e, check_grammar=False)
self.parent = e
def endElementNS(self, tag, qname):
if self.parse == False:
return
self.level = self.level - 1
str = ''.join(self.data)
if len(str.strip()) > 0:
self.curr.addText(str, check_grammar=False)
self.data = []
self.curr = self.curr.parentNode
self.parent = self.curr
if tag in self.triggers:
self.parse = False

View file

@ -1,44 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
#
import sys, os.path
sys.path.append(os.path.dirname(__file__))
from namespaces import MANIFESTNS
from element import Element
# Autogenerated
def Manifest(**args):
return Element(qname = (MANIFESTNS,'manifest'), **args)
def FileEntry(**args):
return Element(qname = (MANIFESTNS,'file-entry'), **args)
def EncryptionData(**args):
return Element(qname = (MANIFESTNS,'encryption-data'), **args)
def Algorithm(**args):
return Element(qname = (MANIFESTNS,'algorithm'), **args)
def KeyDerivation(**args):
return Element(qname = (MANIFESTNS,'key-derivation'), **args)

View file

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import MATHNS
from element import Element
# ODF 1.0 section 12.5
# Mathematical content is represented by MathML 2.0
# Autogenerated
def Math(**args):
return Element(qname = (MATHNS,'math'), **args)

View file

@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import METANS
from element import Element
# Autogenerated
def AutoReload(**args):
return Element(qname = (METANS,'auto-reload'), **args)
def CreationDate(**args):
return Element(qname = (METANS,'creation-date'), **args)
def DateString(**args):
return Element(qname = (METANS,'date-string'), **args)
def DocumentStatistic(**args):
return Element(qname = (METANS,'document-statistic'), **args)
def EditingCycles(**args):
return Element(qname = (METANS,'editing-cycles'), **args)
def EditingDuration(**args):
return Element(qname = (METANS,'editing-duration'), **args)
def Generator(**args):
return Element(qname = (METANS,'generator'), **args)
def HyperlinkBehaviour(**args):
return Element(qname = (METANS,'hyperlink-behaviour'), **args)
def InitialCreator(**args):
return Element(qname = (METANS,'initial-creator'), **args)
def Keyword(**args):
return Element(qname = (METANS,'keyword'), **args)
def PrintDate(**args):
return Element(qname = (METANS,'print-date'), **args)
def PrintedBy(**args):
return Element(qname = (METANS,'printed-by'), **args)
def Template(**args):
args.setdefault('type', 'simple')
return Element(qname = (METANS,'template'), **args)
def UserDefined(**args):
return Element(qname = (METANS,'user-defined'), **args)

View file

@ -1,107 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2013 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
TOOLSVERSION = u"ODFPY/1.3.1dev"
ANIMNS = u"urn:oasis:names:tc:opendocument:xmlns:animation:1.0"
CHARTNS = u"urn:oasis:names:tc:opendocument:xmlns:chart:1.0"
CHARTOOONS = u"http://openoffice.org/2010/chart"
CONFIGNS = u"urn:oasis:names:tc:opendocument:xmlns:config:1.0"
CSS3TNS = u"http://www.w3.org/TR/css3-text/"
#DBNS = u"http://openoffice.org/2004/database"
DBNS = u"urn:oasis:names:tc:opendocument:xmlns:database:1.0"
DCNS = u"http://purl.org/dc/elements/1.1/"
DOMNS = u"http://www.w3.org/2001/xml-events"
DR3DNS = u"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"
DRAWNS = u"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
FIELDNS = u"urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0"
FONS = u"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
FORMNS = u"urn:oasis:names:tc:opendocument:xmlns:form:1.0"
FORMXNS = u"urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0"
GRDDLNS = u"http://www.w3.org/2003/g/data-view#"
KOFFICENS = u"http://www.koffice.org/2005/"
MANIFESTNS = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
MATHNS = u"http://www.w3.org/1998/Math/MathML"
METANS = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
NUMBERNS = u"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
OFFICENS = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0"
OFNS = u"urn:oasis:names:tc:opendocument:xmlns:of:1.2"
OOOCNS = u"http://openoffice.org/2004/calc"
OOONS = u"http://openoffice.org/2004/office"
OOOWNS = u"http://openoffice.org/2004/writer"
PRESENTATIONNS = u"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"
RDFANS = u"http://docs.oasis-open.org/opendocument/meta/rdfa#"
RPTNS = u"http://openoffice.org/2005/report"
SCRIPTNS = u"urn:oasis:names:tc:opendocument:xmlns:script:1.0"
SMILNS = u"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0"
STYLENS = u"urn:oasis:names:tc:opendocument:xmlns:style:1.0"
SVGNS = u"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
TABLENS = u"urn:oasis:names:tc:opendocument:xmlns:table:1.0"
TABLEOOONS = u"http://openoffice.org/2009/table"
TEXTNS = u"urn:oasis:names:tc:opendocument:xmlns:text:1.0"
XFORMSNS = u"http://www.w3.org/2002/xforms"
XHTMLNS = u"http://www.w3.org/1999/xhtml"
XLINKNS = u"http://www.w3.org/1999/xlink"
XMLNS = u"http://www.w3.org/XML/1998/namespace"
XSDNS = u"http://www.w3.org/2001/XMLSchema"
XSINS = u"http://www.w3.org/2001/XMLSchema-instance"
nsdict = {
ANIMNS: u'anim',
CHARTNS: u'chart',
CHARTOOONS: u'chartooo',
CONFIGNS: u'config',
CSS3TNS: u'css3t',
DBNS: u'db',
DCNS: u'dc',
DOMNS: u'dom',
DR3DNS: u'dr3d',
DRAWNS: u'draw',
FIELDNS: u'field',
FONS: u'fo',
FORMNS: u'form',
FORMXNS: u'formx',
GRDDLNS: u'grddl',
KOFFICENS: u'koffice',
MANIFESTNS: u'manifest',
MATHNS: u'math',
METANS: u'meta',
NUMBERNS: u'number',
OFFICENS: u'office',
OFNS: u'of',
OOONS: u'ooo',
OOOWNS: u'ooow',
OOOCNS: u'oooc',
PRESENTATIONNS: u'presentation',
RDFANS: u'rdfa',
RPTNS: u'rpt',
SCRIPTNS: u'script',
SMILNS: u'smil',
STYLENS: u'style',
SVGNS: u'svg',
TABLENS: u'table',
TABLEOOONS: u'tableooo',
TEXTNS: u'text',
XFORMSNS: u'xforms',
XLINKNS: u'xlink',
XHTMLNS: u'xhtml',
XMLNS: u'xml',
XSDNS: u'xsd',
XSINS: u'xsi',
}

View file

@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import NUMBERNS
from element import Element
from style import StyleElement
# Autogenerated
def AmPm(**args):
return Element(qname = (NUMBERNS,'am-pm'), **args)
def Boolean(**args):
return Element(qname = (NUMBERNS,'boolean'), **args)
def BooleanStyle(**args):
return StyleElement(qname = (NUMBERNS,'boolean-style'), **args)
def CurrencyStyle(**args):
return StyleElement(qname = (NUMBERNS,'currency-style'), **args)
def CurrencySymbol(**args):
return Element(qname = (NUMBERNS,'currency-symbol'), **args)
def DateStyle(**args):
return StyleElement(qname = (NUMBERNS,'date-style'), **args)
def Day(**args):
return Element(qname = (NUMBERNS,'day'), **args)
def DayOfWeek(**args):
return Element(qname = (NUMBERNS,'day-of-week'), **args)
def EmbeddedText(**args):
return Element(qname = (NUMBERNS,'embedded-text'), **args)
def Era(**args):
return Element(qname = (NUMBERNS,'era'), **args)
def Fraction(**args):
return Element(qname = (NUMBERNS,'fraction'), **args)
def Hours(**args):
return Element(qname = (NUMBERNS,'hours'), **args)
def Minutes(**args):
return Element(qname = (NUMBERNS,'minutes'), **args)
def Month(**args):
return Element(qname = (NUMBERNS,'month'), **args)
def Number(**args):
return Element(qname = (NUMBERNS,'number'), **args)
def NumberStyle(**args):
return StyleElement(qname = (NUMBERNS,'number-style'), **args)
def PercentageStyle(**args):
return StyleElement(qname = (NUMBERNS,'percentage-style'), **args)
def Quarter(**args):
return Element(qname = (NUMBERNS,'quarter'), **args)
def ScientificNumber(**args):
return Element(qname = (NUMBERNS,'scientific-number'), **args)
def Seconds(**args):
return Element(qname = (NUMBERNS,'seconds'), **args)
def Text(**args):
return Element(qname = (NUMBERNS,'text'), **args)
def TextContent(**args):
return Element(qname = (NUMBERNS,'text-content'), **args)
def TextStyle(**args):
return StyleElement(qname = (NUMBERNS,'text-style'), **args)
def TimeStyle(**args):
return StyleElement(qname = (NUMBERNS,'time-style'), **args)
def WeekOfYear(**args):
return Element(qname = (NUMBERNS,'week-of-year'), **args)
def Year(**args):
return Element(qname = (NUMBERNS,'year'), **args)

View file

@ -1,579 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2008 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# See http://trac.edgewall.org/wiki/WikiFormatting
#
# Contributor(s):
#
import sys, zipfile, xml.dom.minidom
from namespaces import nsdict
from elementtypes import *
IGNORED_TAGS = [
'draw:a'
'draw:g',
'draw:line',
'draw:object-ole',
'office:annotation',
'presentation:notes',
'svg:desc',
] + [ nsdict[item[0]]+":"+item[1] for item in empty_elements]
INLINE_TAGS = [ nsdict[item[0]]+":"+item[1] for item in inline_elements]
class TextProps:
""" Holds properties for a text style. """
def __init__(self):
self.italic = False
self.bold = False
self.fixed = False
self.underlined = False
self.strikethrough = False
self.superscript = False
self.subscript = False
def setItalic(self, value):
if value == "italic":
self.italic = True
elif value == "normal":
self.italic = False
def setBold(self, value):
if value == "bold":
self.bold = True
elif value == "normal":
self.bold = False
def setFixed(self, value):
self.fixed = value
def setUnderlined(self, value):
if value and value != "none":
self.underlined = True
def setStrikethrough(self, value):
if value and value != "none":
self.strikethrough = True
def setPosition(self, value):
if value is None or value == '':
return
posisize = value.split(' ')
textpos = posisize[0]
if textpos.find('%') == -1:
if textpos == "sub":
self.superscript = False
self.subscript = True
elif textpos == "super":
self.superscript = True
self.subscript = False
else:
itextpos = int(textpos[:textpos.find('%')])
if itextpos > 10:
self.superscript = False
self.subscript = True
elif itextpos < -10:
self.superscript = True
self.subscript = False
def __str__(self):
return "[italic=%s, bold=i%s, fixed=%s]" % (str(self.italic),
str(self.bold),
str(self.fixed))
class ParagraphProps:
""" Holds properties of a paragraph style. """
def __init__(self):
self.blockquote = False
self.headingLevel = 0
self.code = False
self.title = False
self.indented = 0
def setIndented(self, value):
self.indented = value
def setHeading(self, level):
self.headingLevel = level
def setTitle(self, value):
self.title = value
def setCode(self, value):
self.code = value
def __str__(self):
return "[bq=%s, h=%d, code=%s]" % (str(self.blockquote),
self.headingLevel,
str(self.code))
class ListProperties:
""" Holds properties for a list style. """
def __init__(self):
self.ordered = False
def setOrdered(self, value):
self.ordered = value
class ODF2MoinMoin(object):
def __init__(self, filepath):
self.footnotes = []
self.footnoteCounter = 0
self.textStyles = {"Standard": TextProps()}
self.paragraphStyles = {"Standard": ParagraphProps()}
self.listStyles = {}
self.fixedFonts = []
self.hasTitle = 0
self.lastsegment = None
# Tags
self.elements = {
'draw:page': self.textToString,
'draw:frame': self.textToString,
'draw:image': self.draw_image,
'draw:text-box': self.textToString,
'text:a': self.text_a,
'text:note': self.text_note,
}
for tag in IGNORED_TAGS:
self.elements[tag] = self.do_nothing
for tag in INLINE_TAGS:
self.elements[tag] = self.inline_markup
self.elements['text:line-break'] = self.text_line_break
self.elements['text:s'] = self.text_s
self.elements['text:tab'] = self.text_tab
self.load(filepath)
def processFontDeclarations(self, fontDecl):
""" Extracts necessary font information from a font-declaration
element.
"""
for fontFace in fontDecl.getElementsByTagName("style:font-face"):
if fontFace.getAttribute("style:font-pitch") == "fixed":
self.fixedFonts.append(fontFace.getAttribute("style:name"))
def extractTextProperties(self, style, parent=None):
""" Extracts text properties from a style element. """
textProps = TextProps()
if parent:
parentProp = self.textStyles.get(parent, None)
if parentProp:
textProp = parentProp
textPropEl = style.getElementsByTagName("style:text-properties")
if not textPropEl: return textProps
textPropEl = textPropEl[0]
textProps.setItalic(textPropEl.getAttribute("fo:font-style"))
textProps.setBold(textPropEl.getAttribute("fo:font-weight"))
textProps.setUnderlined(textPropEl.getAttribute("style:text-underline-style"))
textProps.setStrikethrough(textPropEl.getAttribute("style:text-line-through-style"))
textProps.setPosition(textPropEl.getAttribute("style:text-position"))
if textPropEl.getAttribute("style:font-name") in self.fixedFonts:
textProps.setFixed(True)
return textProps
def extractParagraphProperties(self, style, parent=None):
""" Extracts paragraph properties from a style element. """
paraProps = ParagraphProps()
name = style.getAttribute("style:name")
if name.startswith("Heading_20_"):
level = name[11:]
try:
level = int(level)
paraProps.setHeading(level)
except:
level = 0
if name == "Title":
paraProps.setTitle(True)
paraPropEl = style.getElementsByTagName("style:paragraph-properties")
if paraPropEl:
paraPropEl = paraPropEl[0]
leftMargin = paraPropEl.getAttribute("fo:margin-left")
if leftMargin:
try:
leftMargin = float(leftMargin[:-2])
if leftMargin > 0.01:
paraProps.setIndented(True)
except:
pass
textProps = self.extractTextProperties(style)
if textProps.fixed:
paraProps.setCode(True)
return paraProps
def processStyles(self, styleElements):
""" Runs through "style" elements extracting necessary information.
"""
for style in styleElements:
name = style.getAttribute("style:name")
if name == "Standard": continue
family = style.getAttribute("style:family")
parent = style.getAttribute("style:parent-style-name")
if family == "text":
self.textStyles[name] = self.extractTextProperties(style, parent)
elif family == "paragraph":
self.paragraphStyles[name] = \
self.extractParagraphProperties(style, parent)
self.textStyles[name] = self.extractTextProperties(style, parent)
def processListStyles(self, listStyleElements):
for style in listStyleElements:
name = style.getAttribute("style:name")
prop = ListProperties()
if style.hasChildNodes():
subitems = [el for el in style.childNodes
if el.nodeType == xml.dom.Node.ELEMENT_NODE
and el.tagName == "text:list-level-style-number"]
if len(subitems) > 0:
prop.setOrdered(True)
self.listStyles[name] = prop
def load(self, filepath):
""" Loads an ODT file. """
zip = zipfile.ZipFile(filepath)
styles_doc = xml.dom.minidom.parseString(zip.read("styles.xml"))
fontfacedecls = styles_doc.getElementsByTagName("office:font-face-decls")
if fontfacedecls:
self.processFontDeclarations(fontfacedecls[0])
self.processStyles(styles_doc.getElementsByTagName("style:style"))
self.processListStyles(styles_doc.getElementsByTagName("text:list-style"))
self.content = xml.dom.minidom.parseString(zip.read("content.xml"))
fontfacedecls = self.content.getElementsByTagName("office:font-face-decls")
if fontfacedecls:
self.processFontDeclarations(fontfacedecls[0])
self.processStyles(self.content.getElementsByTagName("style:style"))
self.processListStyles(self.content.getElementsByTagName("text:list-style"))
def compressCodeBlocks(self, text):
""" Removes extra blank lines from code blocks. """
return text
lines = text.split("\n")
buffer = []
numLines = len(lines)
for i in range(numLines):
if (lines[i].strip() or i == numLines-1 or i == 0 or
not ( lines[i-1].startswith(" ")
and lines[i+1].startswith(" ") ) ):
buffer.append("\n" + lines[i])
return ''.join(buffer)
#-----------------------------------
def do_nothing(self, node):
return ''
def draw_image(self, node):
"""
"""
link = node.getAttribute("xlink:href")
if link and link[:2] == './': # Indicates a sub-object, which isn't supported
return "%s\n" % link
if link and link[:9] == 'Pictures/':
link = link[9:]
return "[[Image(%s)]]\n" % link
def text_a(self, node):
text = self.textToString(node)
link = node.getAttribute("xlink:href")
if link.strip() == text.strip():
return "[%s] " % link.strip()
else:
return "[%s %s] " % (link.strip(), text.strip())
def text_line_break(self, node):
return "[[BR]]"
def text_note(self, node):
cite = (node.getElementsByTagName("text:note-citation")[0]
.childNodes[0].nodeValue)
body = (node.getElementsByTagName("text:note-body")[0]
.childNodes[0])
self.footnotes.append((cite, self.textToString(body)))
return "^%s^" % cite
def text_s(self, node):
try:
num = int(node.getAttribute("text:c"))
return " "*num
except:
return " "
def text_tab(self, node):
return " "
def inline_markup(self, node):
text = self.textToString(node)
if not text.strip():
return '' # don't apply styles to white space
styleName = node.getAttribute("text:style-name")
style = self.textStyles.get(styleName, TextProps())
if style.fixed:
return "`" + text + "`"
mark = []
if style:
if style.italic:
mark.append("''")
if style.bold:
mark.append("'''")
if style.underlined:
mark.append("__")
if style.strikethrough:
mark.append("~~")
if style.superscript:
mark.append("^")
if style.subscript:
mark.append(",,")
revmark = mark[:]
revmark.reverse()
return "%s%s%s" % (''.join(mark), text, ''.join(revmark))
#-----------------------------------
def listToString(self, listElement, indent = 0):
self.lastsegment = listElement.tagName
buffer = []
styleName = listElement.getAttribute("text:style-name")
props = self.listStyles.get(styleName, ListProperties())
i = 0
for item in listElement.childNodes:
buffer.append(" "*indent)
i += 1
if props.ordered:
number = str(i)
number = " " + number + ". "
buffer.append(" 1. ")
else:
buffer.append(" * ")
subitems = [el for el in item.childNodes
if el.tagName in ["text:p", "text:h", "text:list"]]
for subitem in subitems:
if subitem.tagName == "text:list":
buffer.append("\n")
buffer.append(self.listToString(subitem, indent+3))
else:
buffer.append(self.paragraphToString(subitem, indent+3))
self.lastsegment = subitem.tagName
self.lastsegment = item.tagName
buffer.append("\n")
return ''.join(buffer)
def tableToString(self, tableElement):
""" MoinMoin uses || to delimit table cells
"""
self.lastsegment = tableElement.tagName
buffer = []
for item in tableElement.childNodes:
self.lastsegment = item.tagName
if item.tagName == "table:table-header-rows":
buffer.append(self.tableToString(item))
if item.tagName == "table:table-row":
buffer.append("\n||")
for cell in item.childNodes:
buffer.append(self.inline_markup(cell))
buffer.append("||")
self.lastsegment = cell.tagName
return ''.join(buffer)
def toString(self):
""" Converts the document to a string.
FIXME: Result from second call differs from first call
"""
body = self.content.getElementsByTagName("office:body")[0]
text = body.childNodes[0]
buffer = []
paragraphs = [el for el in text.childNodes
if el.tagName in ["draw:page", "text:p", "text:h","text:section",
"text:list", "table:table"]]
for paragraph in paragraphs:
if paragraph.tagName == "text:list":
text = self.listToString(paragraph)
elif paragraph.tagName == "text:section":
text = self.textToString(paragraph)
elif paragraph.tagName == "table:table":
text = self.tableToString(paragraph)
else:
text = self.paragraphToString(paragraph)
if text:
buffer.append(text)
if self.footnotes:
buffer.append("----")
for cite, body in self.footnotes:
buffer.append("%s: %s" % (cite, body))
buffer.append("")
return self.compressCodeBlocks('\n'.join(buffer))
def textToString(self, element):
buffer = []
for node in element.childNodes:
if node.nodeType == xml.dom.Node.TEXT_NODE:
buffer.append(node.nodeValue)
elif node.nodeType == xml.dom.Node.ELEMENT_NODE:
tag = node.tagName
if tag in ("draw:text-box", "draw:frame"):
buffer.append(self.textToString(node))
elif tag in ("text:p", "text:h"):
text = self.paragraphToString(node)
if text:
buffer.append(text)
elif tag == "text:list":
buffer.append(self.listToString(node))
else:
method = self.elements.get(tag)
if method:
buffer.append(method(node))
else:
buffer.append(" {" + tag + "} ")
return ''.join(buffer)
def paragraphToString(self, paragraph, indent = 0):
dummyParaProps = ParagraphProps()
style_name = paragraph.getAttribute("text:style-name")
paraProps = self.paragraphStyles.get(style_name, dummyParaProps)
text = self.inline_markup(paragraph)
if paraProps and not paraProps.code:
text = text.strip()
if paragraph.tagName == "text:p" and self.lastsegment == "text:p":
text = "\n" + text
self.lastsegment = paragraph.tagName
if paraProps.title:
self.hasTitle = 1
return "= " + text + " =\n"
outlinelevel = paragraph.getAttribute("text:outline-level")
if outlinelevel:
level = int(outlinelevel)
if self.hasTitle: level += 1
if level >= 1:
return "=" * level + " " + text + " " + "=" * level + "\n"
elif paraProps.code:
return "{{{\n" + text + "\n}}}\n"
if paraProps.indented:
return self.wrapParagraph(text, indent = indent, blockquote = True)
else:
return self.wrapParagraph(text, indent = indent)
def wrapParagraph(self, text, indent = 0, blockquote=False):
counter = 0
buffer = []
LIMIT = 50
if blockquote:
buffer.append(" ")
return ''.join(buffer) + text
# Unused from here
for token in text.split():
if counter > LIMIT - indent:
buffer.append("\n" + " "*indent)
if blockquote:
buffer.append(" ")
counter = 0
buffer.append(token + " ")
counter += len(token)
return ''.join(buffer)

File diff suppressed because it is too large Load diff

View file

@ -1,120 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from __future__ import print_function
# This script lists the content of the manifest.xml file
import zipfile
from xml.sax import make_parser,handler
from xml.sax.xmlreader import InputSource
import xml.sax.saxutils
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
MANIFESTNS="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
#-----------------------------------------------------------------------------
#
# ODFMANIFESTHANDLER
#
#-----------------------------------------------------------------------------
class ODFManifestHandler(handler.ContentHandler):
""" The ODFManifestHandler parses a manifest file and produces a list of
content """
def __init__(self):
self.manifest = {}
# Tags
# FIXME: Also handle encryption data
self.elements = {
(MANIFESTNS, 'file-entry'): (self.s_file_entry, self.donothing),
}
def handle_starttag(self, tag, method, attrs):
method(tag,attrs)
def handle_endtag(self, tag, method):
method(tag)
def startElementNS(self, tag, qname, attrs):
method = self.elements.get(tag, (None, None))[0]
if method:
self.handle_starttag(tag, method, attrs)
else:
self.unknown_starttag(tag,attrs)
def endElementNS(self, tag, qname):
method = self.elements.get(tag, (None, None))[1]
if method:
self.handle_endtag(tag, method)
else:
self.unknown_endtag(tag)
def unknown_starttag(self, tag, attrs):
pass
def unknown_endtag(self, tag):
pass
def donothing(self, tag, attrs=None):
pass
def s_file_entry(self, tag, attrs):
m = attrs.get((MANIFESTNS, 'media-type'),"application/octet-stream")
p = attrs.get((MANIFESTNS, 'full-path'))
self.manifest[p] = { 'media-type':m, 'full-path':p }
#-----------------------------------------------------------------------------
#
# Reading the file
#
#-----------------------------------------------------------------------------
def manifestlist(manifestxml):
odhandler = ODFManifestHandler()
parser = make_parser()
parser.setFeature(handler.feature_namespaces, 1)
parser.setContentHandler(odhandler)
parser.setErrorHandler(handler.ErrorHandler())
inpsrc = InputSource()
if not isinstance(manifestxml, str):
manifestxml=manifestxml.decode("utf-8")
inpsrc.setByteStream(StringIO(manifestxml))
parser.parse(inpsrc)
return odhandler.manifest
def odfmanifest(odtfile):
z = zipfile.ZipFile(odtfile)
manifest = z.read('META-INF/manifest.xml')
z.close()
return manifestlist(manifest)
if __name__ == "__main__":
import sys
result = odfmanifest(sys.argv[1])
for file in result.values():
print ("%-40s %-40s" % (file['media-type'], file['full-path']))

View file

@ -1,107 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2013 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import OFFICENS
from element import Element
from draw import StyleRefElement
# Autogenerated
def Annotation(**args):
return StyleRefElement(qname = (OFFICENS,'annotation'), **args)
def AnnotationEnd(**args):
return StyleRefElement(qname = (OFFICENS,'annotation-end'), **args)
def AutomaticStyles(**args):
return Element(qname = (OFFICENS, 'automatic-styles'), **args)
def BinaryData(**args):
return Element(qname = (OFFICENS,'binary-data'), **args)
def Body(**args):
return Element(qname = (OFFICENS, 'body'), **args)
def ChangeInfo(**args):
return Element(qname = (OFFICENS,'change-info'), **args)
def Chart(**args):
return Element(qname = (OFFICENS,'chart'), **args)
def DdeSource(**args):
return Element(qname = (OFFICENS,'dde-source'), **args)
def Document(version="1.2", **args):
return Element(qname = (OFFICENS,'document'), version=version, **args)
def DocumentContent(version="1.2", **args):
return Element(qname = (OFFICENS, 'document-content'), version=version, **args)
def DocumentMeta(version="1.2", **args):
return Element(qname = (OFFICENS, 'document-meta'), version=version, **args)
def DocumentSettings(version="1.2", **args):
return Element(qname = (OFFICENS, 'document-settings'), version=version, **args)
def DocumentStyles(version="1.2", **args):
return Element(qname = (OFFICENS, 'document-styles'), version=version, **args)
def Drawing(**args):
return Element(qname = (OFFICENS,'drawing'), **args)
def EventListeners(**args):
return Element(qname = (OFFICENS,'event-listeners'), **args)
def FontFaceDecls(**args):
return Element(qname = (OFFICENS, 'font-face-decls'), **args)
def Forms(**args):
return Element(qname = (OFFICENS,'forms'), **args)
def Image(**args):
return Element(qname = (OFFICENS,'image'), **args)
def MasterStyles(**args):
return Element(qname = (OFFICENS, 'master-styles'), **args)
def Meta(**args):
return Element(qname = (OFFICENS, 'meta'), **args)
def Presentation(**args):
return Element(qname = (OFFICENS,'presentation'), **args)
def Script(**args):
return Element(qname = (OFFICENS, 'script'), **args)
def Scripts(**args):
return Element(qname = (OFFICENS, 'scripts'), **args)
def Settings(**args):
return Element(qname = (OFFICENS, 'settings'), **args)
def Spreadsheet(**args):
return Element(qname = (OFFICENS, 'spreadsheet'), **args)
def Styles(**args):
return Element(qname = (OFFICENS, 'styles'), **args)
def Text(**args):
return Element(qname = (OFFICENS, 'text'), **args)
# Autogenerated end

File diff suppressed because it is too large Load diff

View file

@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import PRESENTATIONNS
from element import Element
# ODF 1.0 section 9.6 and 9.7
# Autogenerated
def AnimationGroup(**args):
return Element(qname = (PRESENTATIONNS,'animation-group'), **args)
def Animations(**args):
return Element(qname = (PRESENTATIONNS,'animations'), **args)
def DateTime(**args):
return Element(qname = (PRESENTATIONNS,'date-time'), **args)
def DateTimeDecl(**args):
return Element(qname = (PRESENTATIONNS,'date-time-decl'), **args)
def Dim(**args):
return Element(qname = (PRESENTATIONNS,'dim'), **args)
def EventListener(**args):
return Element(qname = (PRESENTATIONNS,'event-listener'), **args)
def Footer(**args):
return Element(qname = (PRESENTATIONNS,'footer'), **args)
def FooterDecl(**args):
return Element(qname = (PRESENTATIONNS,'footer-decl'), **args)
def Header(**args):
return Element(qname = (PRESENTATIONNS,'header'), **args)
def HeaderDecl(**args):
return Element(qname = (PRESENTATIONNS,'header-decl'), **args)
def HideShape(**args):
return Element(qname = (PRESENTATIONNS,'hide-shape'), **args)
def HideText(**args):
return Element(qname = (PRESENTATIONNS,'hide-text'), **args)
def Notes(**args):
return Element(qname = (PRESENTATIONNS,'notes'), **args)
def Placeholder(**args):
return Element(qname = (PRESENTATIONNS,'placeholder'), **args)
def Play(**args):
return Element(qname = (PRESENTATIONNS,'play'), **args)
def Settings(**args):
return Element(qname = (PRESENTATIONNS,'settings'), **args)
def Show(**args):
return Element(qname = (PRESENTATIONNS,'show'), **args)
def ShowShape(**args):
return Element(qname = (PRESENTATIONNS,'show-shape'), **args)
def ShowText(**args):
return Element(qname = (PRESENTATIONNS,'show-text'), **args)
def Sound(**args):
args.setdefault('type', 'simple')
return Element(qname = (PRESENTATIONNS,'sound'), **args)

View file

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import SCRIPTNS
from element import Element
# ODF 1.0 section 12.4.1
# The <script:event-listener> element binds an event to a macro.
# Autogenerated
def EventListener(**args):
return Element(qname = (SCRIPTNS,'event-listener'), **args)

View file

@ -1,154 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2013 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import STYLENS
from element import Element
def StyleElement(**args):
e = Element(**args)
if args.get('check_grammar', True) == True:
if 'displayname' not in args:
e.setAttrNS(STYLENS,'display-name', args.get('name'))
return e
# Autogenerated
def BackgroundImage(**args):
return Element(qname = (STYLENS,'background-image'), **args)
def ChartProperties(**args):
return Element(qname = (STYLENS,'chart-properties'), **args)
def Column(**args):
return Element(qname = (STYLENS,'column'), **args)
def ColumnSep(**args):
return Element(qname = (STYLENS,'column-sep'), **args)
def Columns(**args):
return Element(qname = (STYLENS,'columns'), **args)
def DefaultPageLayout(**args):
return Element(qname = (STYLENS,'default-page-layout'), **args)
def DefaultStyle(**args):
return Element(qname = (STYLENS,'default-style'), **args)
def DrawingPageProperties(**args):
return Element(qname = (STYLENS,'drawing-page-properties'), **args)
def DropCap(**args):
return Element(qname = (STYLENS,'drop-cap'), **args)
def FontFace(**args):
return Element(qname = (STYLENS,'font-face'), **args)
def Footer(**args):
return Element(qname = (STYLENS,'footer'), **args)
def FooterLeft(**args):
return Element(qname = (STYLENS,'footer-left'), **args)
def FooterStyle(**args):
return Element(qname = (STYLENS,'footer-style'), **args)
def FootnoteSep(**args):
return Element(qname = (STYLENS,'footnote-sep'), **args)
def GraphicProperties(**args):
return Element(qname = (STYLENS,'graphic-properties'), **args)
def HandoutMaster(**args):
return Element(qname = (STYLENS,'handout-master'), **args)
def Header(**args):
return Element(qname = (STYLENS,'header'), **args)
def HeaderFooterProperties(**args):
return Element(qname = (STYLENS,'header-footer-properties'), **args)
def HeaderLeft(**args):
return Element(qname = (STYLENS,'header-left'), **args)
def HeaderStyle(**args):
return Element(qname = (STYLENS,'header-style'), **args)
def ListLevelLabelAlignment(**args):
return Element(qname = (STYLENS,'list-level-label-alignment'), **args)
def ListLevelProperties(**args):
return Element(qname = (STYLENS,'list-level-properties'), **args)
def Map(**args):
return Element(qname = (STYLENS,'map'), **args)
def MasterPage(**args):
return StyleElement(qname = (STYLENS,'master-page'), **args)
def PageLayout(**args):
return Element(qname = (STYLENS,'page-layout'), **args)
def PageLayoutProperties(**args):
return Element(qname = (STYLENS,'page-layout-properties'), **args)
def ParagraphProperties(**args):
return Element(qname = (STYLENS,'paragraph-properties'), **args)
def PresentationPageLayout(**args):
return StyleElement(qname = (STYLENS,'presentation-page-layout'), **args)
def RegionCenter(**args):
return Element(qname = (STYLENS,'region-center'), **args)
def RegionLeft(**args):
return Element(qname = (STYLENS,'region-left'), **args)
def RegionRight(**args):
return Element(qname = (STYLENS,'region-right'), **args)
def RubyProperties(**args):
return Element(qname = (STYLENS,'ruby-properties'), **args)
def SectionProperties(**args):
return Element(qname = (STYLENS,'section-properties'), **args)
def Style(**args):
return StyleElement(qname = (STYLENS,'style'), **args)
def TabStop(**args):
return Element(qname = (STYLENS,'tab-stop'), **args)
def TabStops(**args):
return Element(qname = (STYLENS,'tab-stops'), **args)
def TableCellProperties(**args):
return Element(qname = (STYLENS,'table-cell-properties'), **args)
def TableColumnProperties(**args):
return Element(qname = (STYLENS,'table-column-properties'), **args)
def TableProperties(**args):
return Element(qname = (STYLENS,'table-properties'), **args)
def TableRowProperties(**args):
return Element(qname = (STYLENS,'table-row-properties'), **args)
def TextProperties(**args):
return Element(qname = (STYLENS,'text-properties'), **args)

View file

@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import SVGNS
from element import Element
from draw import DrawElement
# Autogenerated
def DefinitionSrc(**args):
args.setdefault('type', 'simple')
return Element(qname = (SVGNS,'definition-src'), **args)
def Desc(**args):
return Element(qname = (SVGNS,'desc'), **args)
def FontFaceFormat(**args):
return Element(qname = (SVGNS,'font-face-format'), **args)
def FontFaceName(**args):
return Element(qname = (SVGNS,'font-face-name'), **args)
def FontFaceSrc(**args):
return Element(qname = (SVGNS,'font-face-src'), **args)
def FontFaceUri(**args):
args.setdefault('type', 'simple')
return Element(qname = (SVGNS,'font-face-uri'), **args)
def Lineargradient(**args):
return DrawElement(qname = (SVGNS,'linearGradient'), **args)
def Radialgradient(**args):
return DrawElement(qname = (SVGNS,'radialGradient'), **args)
def Stop(**args):
return Element(qname = (SVGNS,'stop'), **args)
def Title(**args):
return Element(qname = (SVGNS,'title'), **args)

View file

@ -1,321 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2013 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import TABLENS
from element import Element
# Autogenerated
def Background(**args):
return Element(qname = (TABLENS,'background'), **args)
def Body(**args):
return Element(qname = (TABLENS,'body'), **args)
def CalculationSettings(**args):
return Element(qname = (TABLENS,'calculation-settings'), **args)
def CellAddress(**args):
return Element(qname = (TABLENS,'cell-address'), **args)
def CellContentChange(**args):
return Element(qname = (TABLENS,'cell-content-change'), **args)
def CellContentDeletion(**args):
return Element(qname = (TABLENS,'cell-content-deletion'), **args)
def CellRangeSource(**args):
args.setdefault('type', 'simple')
return Element(qname = (TABLENS,'cell-range-source'), **args)
def ChangeDeletion(**args):
return Element(qname = (TABLENS,'change-deletion'), **args)
def ChangeTrackTableCell(**args):
return Element(qname = (TABLENS,'change-track-table-cell'), **args)
def Consolidation(**args):
return Element(qname = (TABLENS,'consolidation'), **args)
def ContentValidation(**args):
return Element(qname = (TABLENS,'content-validation'), **args)
def ContentValidations(**args):
return Element(qname = (TABLENS,'content-validations'), **args)
def CoveredTableCell(**args):
return Element(qname = (TABLENS,'covered-table-cell'), **args)
def CutOffs(**args):
return Element(qname = (TABLENS,'cut-offs'), **args)
def DataPilotDisplayInfo(**args):
return Element(qname = (TABLENS,'data-pilot-display-info'), **args)
def DataPilotField(**args):
return Element(qname = (TABLENS,'data-pilot-field'), **args)
def DataPilotFieldReference(**args):
return Element(qname = (TABLENS,'data-pilot-field-reference'), **args)
def DataPilotGroup(**args):
return Element(qname = (TABLENS,'data-pilot-group'), **args)
def DataPilotGroupMember(**args):
return Element(qname = (TABLENS,'data-pilot-group-member'), **args)
def DataPilotGroups(**args):
return Element(qname = (TABLENS,'data-pilot-groups'), **args)
def DataPilotLayoutInfo(**args):
return Element(qname = (TABLENS,'data-pilot-layout-info'), **args)
def DataPilotLevel(**args):
return Element(qname = (TABLENS,'data-pilot-level'), **args)
def DataPilotMember(**args):
return Element(qname = (TABLENS,'data-pilot-member'), **args)
def DataPilotMembers(**args):
return Element(qname = (TABLENS,'data-pilot-members'), **args)
def DataPilotSortInfo(**args):
return Element(qname = (TABLENS,'data-pilot-sort-info'), **args)
def DataPilotSubtotal(**args):
return Element(qname = (TABLENS,'data-pilot-subtotal'), **args)
def DataPilotSubtotals(**args):
return Element(qname = (TABLENS,'data-pilot-subtotals'), **args)
def DataPilotTable(**args):
return Element(qname = (TABLENS,'data-pilot-table'), **args)
def DataPilotTables(**args):
return Element(qname = (TABLENS,'data-pilot-tables'), **args)
def DatabaseRange(**args):
return Element(qname = (TABLENS,'database-range'), **args)
def DatabaseRanges(**args):
return Element(qname = (TABLENS,'database-ranges'), **args)
def DatabaseSourceQuery(**args):
return Element(qname = (TABLENS,'database-source-query'), **args)
def DatabaseSourceSql(**args):
return Element(qname = (TABLENS,'database-source-sql'), **args)
def DatabaseSourceTable(**args):
return Element(qname = (TABLENS,'database-source-table'), **args)
def DdeLink(**args):
return Element(qname = (TABLENS,'dde-link'), **args)
def DdeLinks(**args):
return Element(qname = (TABLENS,'dde-links'), **args)
def Deletion(**args):
return Element(qname = (TABLENS,'deletion'), **args)
def Deletions(**args):
return Element(qname = (TABLENS,'deletions'), **args)
def Dependencies(**args):
return Element(qname = (TABLENS,'dependencies'), **args)
def Dependency(**args):
return Element(qname = (TABLENS,'dependency'), **args)
def Desc(**args):
return Element(qname = (TABLENS,'desc'), **args)
def Detective(**args):
return Element(qname = (TABLENS,'detective'), **args)
def ErrorMacro(**args):
return Element(qname = (TABLENS,'error-macro'), **args)
def ErrorMessage(**args):
return Element(qname = (TABLENS,'error-message'), **args)
def EvenColumns(**args):
return Element(qname = (TABLENS,'even-columns'), **args)
def EvenRows(**args):
return Element(qname = (TABLENS,'even-rows'), **args)
def Filter(**args):
return Element(qname = (TABLENS,'filter'), **args)
def FilterAnd(**args):
return Element(qname = (TABLENS,'filter-and'), **args)
def FilterCondition(**args):
return Element(qname = (TABLENS,'filter-condition'), **args)
def FilterOr(**args):
return Element(qname = (TABLENS,'filter-or'), **args)
def FilterSetItem(**args):
return Element(qname = (TABLENS,'filter-set-item'), **args)
def FirstColumn(**args):
return Element(qname = (TABLENS,'first-column'), **args)
def FirstRow(**args):
return Element(qname = (TABLENS,'first-row'), **args)
def HelpMessage(**args):
return Element(qname = (TABLENS,'help-message'), **args)
def HighlightedRange(**args):
return Element(qname = (TABLENS,'highlighted-range'), **args)
def Insertion(**args):
return Element(qname = (TABLENS,'insertion'), **args)
def InsertionCutOff(**args):
return Element(qname = (TABLENS,'insertion-cut-off'), **args)
def Iteration(**args):
return Element(qname = (TABLENS,'iteration'), **args)
def LabelRange(**args):
return Element(qname = (TABLENS,'label-range'), **args)
def LabelRanges(**args):
return Element(qname = (TABLENS,'label-ranges'), **args)
def LastColumn(**args):
return Element(qname = (TABLENS,'last-column'), **args)
def LastRow(**args):
return Element(qname = (TABLENS,'last-row'), **args)
def Movement(**args):
return Element(qname = (TABLENS,'movement'), **args)
def MovementCutOff(**args):
return Element(qname = (TABLENS,'movement-cut-off'), **args)
def NamedExpression(**args):
return Element(qname = (TABLENS,'named-expression'), **args)
def NamedExpressions(**args):
return Element(qname = (TABLENS,'named-expressions'), **args)
def NamedRange(**args):
return Element(qname = (TABLENS,'named-range'), **args)
def NullDate(**args):
return Element(qname = (TABLENS,'null-date'), **args)
def OddColumns(**args):
return Element(qname = (TABLENS,'odd-columns'), **args)
def OddRows(**args):
return Element(qname = (TABLENS,'odd-rows'), **args)
def Operation(**args):
return Element(qname = (TABLENS,'operation'), **args)
def Previous(**args):
return Element(qname = (TABLENS,'previous'), **args)
def Scenario(**args):
return Element(qname = (TABLENS,'scenario'), **args)
def Shapes(**args):
return Element(qname = (TABLENS,'shapes'), **args)
def Sort(**args):
return Element(qname = (TABLENS,'sort'), **args)
def SortBy(**args):
return Element(qname = (TABLENS,'sort-by'), **args)
def SortGroups(**args):
return Element(qname = (TABLENS,'sort-groups'), **args)
def SourceCellRange(**args):
return Element(qname = (TABLENS,'source-cell-range'), **args)
def SourceRangeAddress(**args):
return Element(qname = (TABLENS,'source-range-address'), **args)
def SourceService(**args):
return Element(qname = (TABLENS,'source-service'), **args)
def SubtotalField(**args):
return Element(qname = (TABLENS,'subtotal-field'), **args)
def SubtotalRule(**args):
return Element(qname = (TABLENS,'subtotal-rule'), **args)
def SubtotalRules(**args):
return Element(qname = (TABLENS,'subtotal-rules'), **args)
def Table(**args):
return Element(qname = (TABLENS,'table'), **args)
def TableCell(**args):
return Element(qname = (TABLENS,'table-cell'), **args)
def TableColumn(**args):
return Element(qname = (TABLENS,'table-column'), **args)
def TableColumnGroup(**args):
return Element(qname = (TABLENS,'table-column-group'), **args)
def TableColumns(**args):
return Element(qname = (TABLENS,'table-columns'), **args)
def TableHeaderColumns(**args):
return Element(qname = (TABLENS,'table-header-columns'), **args)
def TableHeaderRows(**args):
return Element(qname = (TABLENS,'table-header-rows'), **args)
def TableRow(**args):
return Element(qname = (TABLENS,'table-row'), **args)
def TableRowGroup(**args):
return Element(qname = (TABLENS,'table-row-group'), **args)
def TableRows(**args):
return Element(qname = (TABLENS,'table-rows'), **args)
def TableSource(**args):
args.setdefault('type', 'simple')
return Element(qname = (TABLENS,'table-source'), **args)
def TableTemplate(**args):
return Element(qname = (TABLENS,'table-template'), **args)
def TargetRangeAddress(**args):
return Element(qname = (TABLENS,'target-range-address'), **args)
def Title(**args):
return Element(qname = (TABLENS,'title'), **args)
def TrackedChanges(**args):
return Element(qname = (TABLENS,'tracked-changes'), **args)

View file

@ -1,137 +0,0 @@
# -*- coding: utf-8 -*-
#
# Create and extract text from ODF, handling whitespace correctly.
# Copyright (C) 2008 J. David Eisenberg
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Class for handling whitespace properly in OpenDocument.
While it is possible to use getTextContent() and setTextContent()
to extract or create ODF content, these won't extract or create
the appropriate <text:s>, <text:tab>, or <text:line-break>
elements. This module takes care of that problem.
"""
from odf.element import Node
import odf.opendocument
from odf.text import S,LineBreak,Tab
class WhitespaceText(object):
def __init__(self):
self.textBuffer = []
self.spaceCount = 0
def addTextToElement(self, odfElement, s):
""" Process an input string, inserting
<text:tab> elements for '\t',
<text:line-break> elements for '\n', and
<text:s> elements for runs of more than one blank.
These will be added to the given element.
"""
i = 0
ch = ' '
# When we encounter a tab or newline, we can immediately
# dump any accumulated text and then emit the appropriate
# ODF element.
#
# When we encounter a space, we add it to the text buffer,
# and then collect more spaces. If there are more spaces
# after the first one, we dump the text buffer and then
# then emit the appropriate <text:s> element.
while i < len(s):
ch = s[i]
if ch == '\t':
self._emitTextBuffer(odfElement)
odfElement.addElement(Tab())
i += 1
elif ch == '\n':
self._emitTextBuffer(odfElement);
odfElement.addElement(LineBreak())
i += 1
elif ch == ' ':
self.textBuffer.append(' ')
i += 1
self.spaceCount = 0
while i < len(s) and (s[i] == ' '):
self.spaceCount += 1
i += 1
if self.spaceCount > 0:
self._emitTextBuffer(odfElement)
self._emitSpaces(odfElement)
else:
self.textBuffer.append(ch)
i += 1
self._emitTextBuffer(odfElement)
def _emitTextBuffer(self, odfElement):
""" Creates a Text Node whose contents are the current textBuffer.
Side effect: clears the text buffer.
"""
if len(self.textBuffer) > 0:
odfElement.addText(''.join(self.textBuffer))
self.textBuffer = []
def _emitSpaces(self, odfElement):
""" Creates a <text:s> element for the current spaceCount.
Side effect: sets spaceCount back to zero
"""
if self.spaceCount > 0:
spaceElement = S(c=self.spaceCount)
odfElement.addElement(spaceElement)
self.spaceCount = 0
def addTextToElement(odfElement, s):
wst = WhitespaceText()
wst.addTextToElement(odfElement, s)
def extractText(odfElement):
""" Extract text content from an Element, with whitespace represented
properly. Returns the text, with tabs, spaces, and newlines
correctly evaluated. This method recursively descends through the
children of the given element, accumulating text and "unwrapping"
<text:s>, <text:tab>, and <text:line-break> elements along the way.
"""
result = [];
if len(odfElement.childNodes) != 0:
for child in odfElement.childNodes:
if child.nodeType == Node.TEXT_NODE:
result.append(child.data)
elif child.nodeType == Node.ELEMENT_NODE:
subElement = child
tagName = subElement.qname;
if tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"line-break"):
result.append("\n")
elif tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"tab"):
result.append("\t")
elif tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"s"):
c = subElement.getAttribute('c')
if c:
spaceCount = int(c)
else:
spaceCount = 1
result.append(" " * spaceCount)
else:
result.append(extractText(subElement))
return ''.join(result)

View file

@ -1,573 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2013 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
import re, sys, os.path
sys.path.append(os.path.dirname(__file__))
from namespaces import TEXTNS
from element import Element
from style import StyleElement
# Autogenerated
def A(**args):
args.setdefault('type', 'simple')
return Element(qname = (TEXTNS,'a'), **args)
def AlphabeticalIndex(**args):
return Element(qname = (TEXTNS,'alphabetical-index'), **args)
def AlphabeticalIndexAutoMarkFile(**args):
args.setdefault('type', 'simple')
return Element(qname = (TEXTNS,'alphabetical-index-auto-mark-file'), **args)
def AlphabeticalIndexEntryTemplate(**args):
return Element(qname = (TEXTNS,'alphabetical-index-entry-template'), **args)
def AlphabeticalIndexMark(**args):
return Element(qname = (TEXTNS,'alphabetical-index-mark'), **args)
def AlphabeticalIndexMarkEnd(**args):
return Element(qname = (TEXTNS,'alphabetical-index-mark-end'), **args)
def AlphabeticalIndexMarkStart(**args):
return Element(qname = (TEXTNS,'alphabetical-index-mark-start'), **args)
def AlphabeticalIndexSource(**args):
return Element(qname = (TEXTNS,'alphabetical-index-source'), **args)
def AuthorInitials(**args):
return Element(qname = (TEXTNS,'author-initials'), **args)
def AuthorName(**args):
return Element(qname = (TEXTNS,'author-name'), **args)
def Bibliography(**args):
return Element(qname = (TEXTNS,'bibliography'), **args)
def BibliographyConfiguration(**args):
return Element(qname = (TEXTNS,'bibliography-configuration'), **args)
def BibliographyEntryTemplate(**args):
return Element(qname = (TEXTNS,'bibliography-entry-template'), **args)
def BibliographyMark(**args):
return Element(qname = (TEXTNS,'bibliography-mark'), **args)
def BibliographySource(**args):
return Element(qname = (TEXTNS,'bibliography-source'), **args)
def Bookmark(**args):
return Element(qname = (TEXTNS,'bookmark'), **args)
def BookmarkEnd(**args):
return Element(qname = (TEXTNS,'bookmark-end'), **args)
def BookmarkRef(**args):
return Element(qname = (TEXTNS,'bookmark-ref'), **args)
def BookmarkStart(**args):
return Element(qname = (TEXTNS,'bookmark-start'), **args)
def Change(**args):
return Element(qname = (TEXTNS,'change'), **args)
def ChangeEnd(**args):
return Element(qname = (TEXTNS,'change-end'), **args)
def ChangeStart(**args):
return Element(qname = (TEXTNS,'change-start'), **args)
def ChangedRegion(**args):
return Element(qname = (TEXTNS,'changed-region'), **args)
def Chapter(**args):
return Element(qname = (TEXTNS,'chapter'), **args)
def CharacterCount(**args):
return Element(qname = (TEXTNS,'character-count'), **args)
def ConditionalText(**args):
return Element(qname = (TEXTNS,'conditional-text'), **args)
def CreationDate(**args):
return Element(qname = (TEXTNS,'creation-date'), **args)
def CreationTime(**args):
return Element(qname = (TEXTNS,'creation-time'), **args)
def Creator(**args):
return Element(qname = (TEXTNS,'creator'), **args)
def DatabaseDisplay(**args):
return Element(qname = (TEXTNS,'database-display'), **args)
def DatabaseName(**args):
return Element(qname = (TEXTNS,'database-name'), **args)
def DatabaseNext(**args):
return Element(qname = (TEXTNS,'database-next'), **args)
def DatabaseRowNumber(**args):
return Element(qname = (TEXTNS,'database-row-number'), **args)
def DatabaseRowSelect(**args):
return Element(qname = (TEXTNS,'database-row-select'), **args)
def Date(**args):
return Element(qname = (TEXTNS,'date'), **args)
def DdeConnection(**args):
return Element(qname = (TEXTNS,'dde-connection'), **args)
def DdeConnectionDecl(**args):
return Element(qname = (TEXTNS,'dde-connection-decl'), **args)
def DdeConnectionDecls(**args):
return Element(qname = (TEXTNS,'dde-connection-decls'), **args)
def Deletion(**args):
return Element(qname = (TEXTNS,'deletion'), **args)
def Description(**args):
return Element(qname = (TEXTNS,'description'), **args)
def EditingCycles(**args):
return Element(qname = (TEXTNS,'editing-cycles'), **args)
def EditingDuration(**args):
return Element(qname = (TEXTNS,'editing-duration'), **args)
def ExecuteMacro(**args):
return Element(qname = (TEXTNS,'execute-macro'), **args)
def Expression(**args):
return Element(qname = (TEXTNS,'expression'), **args)
def FileName(**args):
return Element(qname = (TEXTNS,'file-name'), **args)
def FormatChange(**args):
return Element(qname = (TEXTNS,'format-change'), **args)
def H(**args):
return Element(qname = (TEXTNS, 'h'), **args)
def HiddenParagraph(**args):
return Element(qname = (TEXTNS,'hidden-paragraph'), **args)
def HiddenText(**args):
return Element(qname = (TEXTNS,'hidden-text'), **args)
def IllustrationIndex(**args):
return Element(qname = (TEXTNS,'illustration-index'), **args)
def IllustrationIndexEntryTemplate(**args):
return Element(qname = (TEXTNS,'illustration-index-entry-template'), **args)
def IllustrationIndexSource(**args):
return Element(qname = (TEXTNS,'illustration-index-source'), **args)
def ImageCount(**args):
return Element(qname = (TEXTNS,'image-count'), **args)
def IndexBody(**args):
return Element(qname = (TEXTNS,'index-body'), **args)
def IndexEntryBibliography(**args):
return Element(qname = (TEXTNS,'index-entry-bibliography'), **args)
def IndexEntryChapter(**args):
return Element(qname = (TEXTNS,'index-entry-chapter'), **args)
def IndexEntryLinkEnd(**args):
return Element(qname = (TEXTNS,'index-entry-link-end'), **args)
def IndexEntryLinkStart(**args):
return Element(qname = (TEXTNS,'index-entry-link-start'), **args)
def IndexEntryPageNumber(**args):
return Element(qname = (TEXTNS,'index-entry-page-number'), **args)
def IndexEntrySpan(**args):
return Element(qname = (TEXTNS,'index-entry-span'), **args)
def IndexEntryTabStop(**args):
return Element(qname = (TEXTNS,'index-entry-tab-stop'), **args)
def IndexEntryText(**args):
return Element(qname = (TEXTNS,'index-entry-text'), **args)
def IndexSourceStyle(**args):
return Element(qname = (TEXTNS,'index-source-style'), **args)
def IndexSourceStyles(**args):
return Element(qname = (TEXTNS,'index-source-styles'), **args)
def IndexTitle(**args):
return Element(qname = (TEXTNS,'index-title'), **args)
def IndexTitleTemplate(**args):
return Element(qname = (TEXTNS,'index-title-template'), **args)
def InitialCreator(**args):
return Element(qname = (TEXTNS,'initial-creator'), **args)
def Insertion(**args):
return Element(qname = (TEXTNS,'insertion'), **args)
def Keywords(**args):
return Element(qname = (TEXTNS,'keywords'), **args)
def LineBreak(**args):
return Element(qname = (TEXTNS,'line-break'), **args)
def LinenumberingConfiguration(**args):
return Element(qname = (TEXTNS,'linenumbering-configuration'), **args)
def LinenumberingSeparator(**args):
return Element(qname = (TEXTNS,'linenumbering-separator'), **args)
def List(**args):
return Element(qname = (TEXTNS,'list'), **args)
def ListHeader(**args):
return Element(qname = (TEXTNS,'list-header'), **args)
def ListItem(**args):
return Element(qname = (TEXTNS,'list-item'), **args)
def ListLevelStyleBullet(**args):
return Element(qname = (TEXTNS,'list-level-style-bullet'), **args)
def ListLevelStyleImage(**args):
return Element(qname = (TEXTNS,'list-level-style-image'), **args)
def ListLevelStyleNumber(**args):
return Element(qname = (TEXTNS,'list-level-style-number'), **args)
def ListStyle(**args):
return StyleElement(qname = (TEXTNS,'list-style'), **args)
def Measure(**args):
return Element(qname = (TEXTNS,'measure'), **args)
def Meta(**args):
return Element(qname = (TEXTNS,'meta'), **args)
def MetaField(**args):
return Element(qname = (TEXTNS,'meta-field'), **args)
def ModificationDate(**args):
return Element(qname = (TEXTNS,'modification-date'), **args)
def ModificationTime(**args):
return Element(qname = (TEXTNS,'modification-time'), **args)
def Note(**args):
return Element(qname = (TEXTNS,'note'), **args)
def NoteBody(**args):
return Element(qname = (TEXTNS,'note-body'), **args)
def NoteCitation(**args):
return Element(qname = (TEXTNS,'note-citation'), **args)
def NoteContinuationNoticeBackward(**args):
return Element(qname = (TEXTNS,'note-continuation-notice-backward'), **args)
def NoteContinuationNoticeForward(**args):
return Element(qname = (TEXTNS,'note-continuation-notice-forward'), **args)
def NoteRef(**args):
return Element(qname = (TEXTNS,'note-ref'), **args)
def NotesConfiguration(**args):
return Element(qname = (TEXTNS,'notes-configuration'), **args)
def Number(**args):
return Element(qname = (TEXTNS,'number'), **args)
def NumberedParagraph(**args):
return Element(qname = (TEXTNS,'numbered-paragraph'), **args)
def ObjectCount(**args):
return Element(qname = (TEXTNS,'object-count'), **args)
def ObjectIndex(**args):
return Element(qname = (TEXTNS,'object-index'), **args)
def ObjectIndexEntryTemplate(**args):
return Element(qname = (TEXTNS,'object-index-entry-template'), **args)
def ObjectIndexSource(**args):
return Element(qname = (TEXTNS,'object-index-source'), **args)
def OutlineLevelStyle(**args):
return Element(qname = (TEXTNS,'outline-level-style'), **args)
def OutlineStyle(**args):
return Element(qname = (TEXTNS,'outline-style'), **args)
def P(**args):
return Element(qname = (TEXTNS, 'p'), **args)
def Page(**args):
return Element(qname = (TEXTNS,'page'), **args)
def PageContinuation(**args):
return Element(qname = (TEXTNS,'page-continuation'), **args)
def PageCount(**args):
return Element(qname = (TEXTNS,'page-count'), **args)
def PageNumber(**args):
return Element(qname = (TEXTNS,'page-number'), **args)
def PageSequence(**args):
return Element(qname = (TEXTNS,'page-sequence'), **args)
def PageVariableGet(**args):
return Element(qname = (TEXTNS,'page-variable-get'), **args)
def PageVariableSet(**args):
return Element(qname = (TEXTNS,'page-variable-set'), **args)
def ParagraphCount(**args):
return Element(qname = (TEXTNS,'paragraph-count'), **args)
def Placeholder(**args):
return Element(qname = (TEXTNS,'placeholder'), **args)
def PrintDate(**args):
return Element(qname = (TEXTNS,'print-date'), **args)
def PrintTime(**args):
return Element(qname = (TEXTNS,'print-time'), **args)
def PrintedBy(**args):
return Element(qname = (TEXTNS,'printed-by'), **args)
def ReferenceMark(**args):
return Element(qname = (TEXTNS,'reference-mark'), **args)
def ReferenceMarkEnd(**args):
return Element(qname = (TEXTNS,'reference-mark-end'), **args)
def ReferenceMarkStart(**args):
return Element(qname = (TEXTNS,'reference-mark-start'), **args)
def ReferenceRef(**args):
return Element(qname = (TEXTNS,'reference-ref'), **args)
def Ruby(**args):
return Element(qname = (TEXTNS,'ruby'), **args)
def RubyBase(**args):
return Element(qname = (TEXTNS,'ruby-base'), **args)
def RubyText(**args):
return Element(qname = (TEXTNS,'ruby-text'), **args)
def S(**args):
return Element(qname = (TEXTNS,'s'), **args)
def Script(**args):
return Element(qname = (TEXTNS,'script'), **args)
def Section(**args):
return Element(qname = (TEXTNS,'section'), **args)
def SectionSource(**args):
return Element(qname = (TEXTNS,'section-source'), **args)
def SenderCity(**args):
return Element(qname = (TEXTNS,'sender-city'), **args)
def SenderCompany(**args):
return Element(qname = (TEXTNS,'sender-company'), **args)
def SenderCountry(**args):
return Element(qname = (TEXTNS,'sender-country'), **args)
def SenderEmail(**args):
return Element(qname = (TEXTNS,'sender-email'), **args)
def SenderFax(**args):
return Element(qname = (TEXTNS,'sender-fax'), **args)
def SenderFirstname(**args):
return Element(qname = (TEXTNS,'sender-firstname'), **args)
def SenderInitials(**args):
return Element(qname = (TEXTNS,'sender-initials'), **args)
def SenderLastname(**args):
return Element(qname = (TEXTNS,'sender-lastname'), **args)
def SenderPhonePrivate(**args):
return Element(qname = (TEXTNS,'sender-phone-private'), **args)
def SenderPhoneWork(**args):
return Element(qname = (TEXTNS,'sender-phone-work'), **args)
def SenderPosition(**args):
return Element(qname = (TEXTNS,'sender-position'), **args)
def SenderPostalCode(**args):
return Element(qname = (TEXTNS,'sender-postal-code'), **args)
def SenderStateOrProvince(**args):
return Element(qname = (TEXTNS,'sender-state-or-province'), **args)
def SenderStreet(**args):
return Element(qname = (TEXTNS,'sender-street'), **args)
def SenderTitle(**args):
return Element(qname = (TEXTNS,'sender-title'), **args)
def Sequence(**args):
return Element(qname = (TEXTNS,'sequence'), **args)
def SequenceDecl(**args):
return Element(qname = (TEXTNS,'sequence-decl'), **args)
def SequenceDecls(**args):
return Element(qname = (TEXTNS,'sequence-decls'), **args)
def SequenceRef(**args):
return Element(qname = (TEXTNS,'sequence-ref'), **args)
def SheetName(**args):
return Element(qname = (TEXTNS,'sheet-name'), **args)
def SoftPageBreak(**args):
return Element(qname = (TEXTNS,'soft-page-break'), **args)
def SortKey(**args):
return Element(qname = (TEXTNS,'sort-key'), **args)
def Span(**args):
return Element(qname = (TEXTNS,'span'), **args)
def Subject(**args):
return Element(qname = (TEXTNS,'subject'), **args)
def Tab(**args):
return Element(qname = (TEXTNS,'tab'), **args)
def TableCount(**args):
return Element(qname = (TEXTNS,'table-count'), **args)
def TableFormula(**args):
return Element(qname = (TEXTNS,'table-formula'), **args)
def TableIndex(**args):
return Element(qname = (TEXTNS,'table-index'), **args)
def TableIndexEntryTemplate(**args):
return Element(qname = (TEXTNS,'table-index-entry-template'), **args)
def TableIndexSource(**args):
return Element(qname = (TEXTNS,'table-index-source'), **args)
def TableOfContent(**args):
return Element(qname = (TEXTNS,'table-of-content'), **args)
def TableOfContentEntryTemplate(**args):
return Element(qname = (TEXTNS,'table-of-content-entry-template'), **args)
def TableOfContentSource(**args):
return Element(qname = (TEXTNS,'table-of-content-source'), **args)
def TemplateName(**args):
return Element(qname = (TEXTNS,'template-name'), **args)
def TextInput(**args):
return Element(qname = (TEXTNS,'text-input'), **args)
def Time(**args):
return Element(qname = (TEXTNS,'time'), **args)
def Title(**args):
return Element(qname = (TEXTNS,'title'), **args)
def TocMark(**args):
return Element(qname = (TEXTNS,'toc-mark'), **args)
def TocMarkEnd(**args):
return Element(qname = (TEXTNS,'toc-mark-end'), **args)
def TocMarkStart(**args):
return Element(qname = (TEXTNS,'toc-mark-start'), **args)
def TrackedChanges(**args):
return Element(qname = (TEXTNS,'tracked-changes'), **args)
def UserDefined(**args):
return Element(qname = (TEXTNS,'user-defined'), **args)
def UserFieldDecl(**args):
return Element(qname = (TEXTNS,'user-field-decl'), **args)
def UserFieldDecls(**args):
return Element(qname = (TEXTNS,'user-field-decls'), **args)
def UserFieldGet(**args):
return Element(qname = (TEXTNS,'user-field-get'), **args)
def UserFieldInput(**args):
return Element(qname = (TEXTNS,'user-field-input'), **args)
def UserIndex(**args):
return Element(qname = (TEXTNS,'user-index'), **args)
def UserIndexEntryTemplate(**args):
return Element(qname = (TEXTNS,'user-index-entry-template'), **args)
def UserIndexMark(**args):
return Element(qname = (TEXTNS,'user-index-mark'), **args)
def UserIndexMarkEnd(**args):
return Element(qname = (TEXTNS,'user-index-mark-end'), **args)
def UserIndexMarkStart(**args):
return Element(qname = (TEXTNS,'user-index-mark-start'), **args)
def UserIndexSource(**args):
return Element(qname = (TEXTNS,'user-index-source'), **args)
def VariableDecl(**args):
return Element(qname = (TEXTNS,'variable-decl'), **args)
def VariableDecls(**args):
return Element(qname = (TEXTNS,'variable-decls'), **args)
def VariableGet(**args):
return Element(qname = (TEXTNS,'variable-get'), **args)
def VariableInput(**args):
return Element(qname = (TEXTNS,'variable-input'), **args)
def VariableSet(**args):
return Element(qname = (TEXTNS,'variable-set'), **args)
def WordCount(**args):
return Element(qname = (TEXTNS,'word-count'), **args)

View file

@ -1,80 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# This contains a 104x128 px thumbnail in PNG format
# Downloaded from http://da.libreoffice.org/assets/Uploads/Da-Projekt_Billeder/IconLibreOffice.png
# Copyright information: Unless otherwise specified, all text and images on the
# http://da.libreoffice.org website are licensed under the Creative Commons
# Attribution-Share Alike 3.0 License. (As of 2013-01-09)
# Alternative download: http://commons.wikimedia.org/wiki/File:LibreOffice_icon_3.3.1_48_px.svg
import base64
iconstr = """\
iVBORw0KGgoAAAANSUhEUgAAAGgAAACACAYAAADnCyxOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz
AAAG7AAABuwBHnU4NQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAwBSURB
VHic7Z1bbFTHHca/2UsxYDs4IRiv7Y0xUNRUCCmAFBXiCwaiRhFIxjdsjG18g8YvlfoQqepDH/yA
hKI+EUIIKqVxwiVAuapUlh+qqi+8VH2hfUgDUpuHVnhjs17vnsv0oZ3lnNkzs2fXu2eX3flJR7ue
nbN7znz7/ec/M+esCaUUk5OTtQDOAOgAUAdnqKBc9lqm5bl8Lwrg7wD+BODWJ5988lDymY6cPHmy
F8AIpfRHAL6X6f5ZQAE8JYQ80DTtF5999tkiGR8fXw/gLwBCHhxAITkD4MPz588bbipPTEx8H8Bf
4Y0wKRBC/qbr+m4fgGmUvjgA8DMADyYmJta4qUwI6UCBxAEASuk2v9//Sx+Adwt1EAXgAIC7bkSi
lM5TSlHg7VCAUvqG9cCmp6dBqayLeLmYm5vD7OystagdwN3x8fH3P/300yXRfpWVlb9bWFh4Qgh5
Q1THAzb7+BJKKUzTLJmtra0N+/bt40+TiSR00kcffRQzTXPCNE0tx42eET7eVgxN0xCNRrG0tISl
pSXEYjHEYjEsLy9jeXkZ8Xgc8XgciUQCiUQCmqbZNl3Xoes6DMOAYRjJBuM/J99QStHe3o729nY+
fLRTSu+OjY0JRbp48eJD0zR7NU3TChXmyOjoqK21pqenYZomotEompqa8t6A+cAwDEQiEZimmSwj
hGB2dhZzc3N89TkA71+4cEEY7oaGhjoBfBkMBoN5OWAJthDHf7MJIS/lFggEUFNTA5/PZzu3jo4O
tLW18W3QDkDqpEuXLt0wTbM/Ho977qSUEOcU7l5G/H4/ampqQAhJno9pmujo6EBLS4tjuBsdHRWK
dPny5euGYRyLx+Oe9kkpDioVgYAXIvFOOnDgAFpbW/nq7QCkIs3MzFyllB6PxWJ6fo44FWGSUCqw
cMc7af/+/di7d6+Tk+6dOHFCKNLnn3/+pWEYQ9FoVPckxPEHUIpiBQIBvPrqqykiHTx4EHv27OHP
tQ2AVKQrV67MGIYxEo1G8+6kkg5xVmQivf322xmLdO3atd/quj76/PlzV3N72SJMEkqRYDCI1157
DYQQW/l7772HXbt22cZplNI2Sum9kZERoUhfffXVbzRNG1tYWDA8C3GMUhZp/fr1AOwR49ChQ9i5
c6dt7IT/O0km0s2bN39tGMbEwsJCXpxkc1AhRvqFgDmJP/fDhw9jx44dMAwjxUnDw8MykS4ahnEy
Eonk3EllkSQ4sWrVKrz++uu286SUoqurC9u3b3d0kkykW7duXdB1/Sfz8/OmqE42CGcS+DhdijCR
rI4xTRM9PT148803YRi2qNUG4P7w8PBa0fvduXPnPKX0g2fPnuVMJGGSwH2DSpaKigps2LAhRaTe
3l5s27YNuq5b26WVUnpvaGhIKNLt27fPUUqnnj17ZuY8xDEHWR/LYauoqEBtbS3f96C/vx9bt27l
ndQKQCrSnTt3PqaUTkUikRV/y8veQQyrSFYGBwfR3NwMTdNSnHT8+PF0In3w3XffrchJwjS7HPog
ntWrV2Pjxo28GBgaGsKmTZug67aJg1YAUpFu3759zjTNqcXFxay/7cKZhHJzEGP16tWoq6tDIpGw
tcfIyAjC4XA2In0MYOr58+dZNWhZzSS4Zc2aNQiFQkgkErbysbGxZDkf7gYHB4Ui3bx5MylSTpIE
BbB27VqEQiHE43Fbg01OTqKurg6aZlsWagUgFenGjRsfA/ggFotl1MgqSZBQWVmJUCiE5eVlW9uc
OnUKtbW1Tk66f+zYMZlI50zTnIrFYjTrJEGFOTtVVVWor69HLBZLESkUCvFOagGQTqSzAKbi8bir
BhYmCYoXVFdXo7GxEbFYLFlGCMHk5CTq6+v5xCGtSNevXz8LYCqRSKRt6JJfUc0V1dXVCIfDNicB
wPj4OOrr6/nUvIVSen9gYEAo0rVr185SSqcSiYQ03EkX7NRm36qqqtDY2Gjrk4D/ZXcNDQ2OTkon
EoApXddFzoiU7FU9+YKFu3g8bisfGxtDY2MjP3fXQim939/fLxTp6tWrZymlU4ZhODnpYdkt2OUC
Fu747O7EiRMIh8P8dFELADciHTJN84ml+F8AfqWyuCypqqpCOBxOGScNDw+jqamJH6akFenKlSt3
5+fnf0gp3U8pfd/n8/1gZmbmz6Svry+pRDQaxenTp2EYBqLRKN566608nmJpsLi4iCdPnoC/Kvjy
5cv45ptvbNfkAfgjgB9/8cUXUbfvrxy0QpiTuAQBg4OD2LRpE++kdwA8OHr0qNBJPMI+SOEeljjw
s+ADAwNobm7mv+xMpEo37y28aEQ5KDNY4sBlcTh69Ciam5v5tn2HUnq/r68vrUiODlICZQcTiZ/H
7Ovrw5YtW5yclFYk4UyCEig7qqur0dDQkBKNuru7kyLxTurt7RWKVDaX/nrJK6+8gsbGxpTrDLu6
urB161a++jsAhCIJszj+G6C2zGZcWLizvk4IwZEjR0QiPXASSU315ABR+7G5O75Njxw54hTu9lJK
H/T09NhEEq6oKoFyA0vBAbuQnZ2d2LJlC199LwCbSMpBHuAkEgB0dnYmx0m8k7q7uysBNZPgGUwk
6+VshBB0dXVh8+bNfPW9AM4Bae7yVuQWlt0BSOmTmpub+eoD3d3dPSrEeYxMpKamJl6DU9I0W5Ef
1q1bh3A4nBLuuru7+apNAT5zUw7yhnXr1oFSiqdPn8I0zeQPcHDt3qDS7AJSU1PjOE6yEAjwJcpF
3lJTU4Ovv/4aPp8v2ebW0KdCXJHBt7tKs4sAmTGEDlJZnHewJMFJJDWTUATIkjO1HlQEyH6fIiXE
OT1X5Be+a7FeqiW8JkHhHVb3ZJQklLJQxXSTtJMGjICoYqlTTOcq6/vVZGkRkFGSoLI477G2Nd+1
qHFQESBr84ASovDIkjPloCJA1gcJBVJ4h2yCQIW4IkB2PYhwwY49V+SfrJYblDjeYU0MpMsN1jUJ
hXeIxAHULZBFQUbjIOUg71HjoCJH1tYqzS4CnG6WY6iBahEgTbPT7aBYOZksDqadSVDC5J507el6
HKQoDK7TbDWTUBhcp9miqxsV+UXW1upfAxQBst9IEiYJykHekdVygxLJO1wv2ClhCoermQTV7xQG
mTHUVE+RwNqdN4kaBxUZrkOcCnfeYTWFclCRIrpYR6XZecZNJJLVsQmkpnpyj5t2zCjE8Tuqvii/
8MsMGa+oKiflDydxpA6y7mjdQTkp91jFsbat6wU7/iJG5aTcIQtrvBGkl13xOyuRVo6TONa/eaTL
Dfw9+yrcrQwncZzuBXY1DgKQ/E9ShBDbpkTKHFFYY23sykF8Bd5BDCVSZojEYQ6SdSVpB6qifkeJ
5A6ZOE7TbDzSJIHFR5U4ZIcbcfg+yPVMAiEkaT/mFieUk5xxI47MBAzpZKn1g5RI7nErjlO9tDMJ
TvZTIrknU3H4EJeRg6yqKpHSk61zrM9THCT7QOtlqEokOdmKw/dBPGlnEtgPnvKDVP55OQ9mV9Ln
ZDQO4ne2jnIZykl2ViIOAP7/fqcgDHGsgdlsghIplZWK43RfUEYXjRiGoUQSkCtx+Lk4aRbHrwGx
Psjn8ymRLORKHNa+MqQO0nU96SImDnu0JgRs30wTh5dRsFyLw9rXdZJgxTAMm8IiJwF2l7h1kqhe
seJWHKc6QKo4pmlC13VbfZ60d3nzFsy1SC8L+RDHKcXOeKpHiZRfcdg40/pZrkOcNQ0sV5Hy7RzZ
7Y9AmhVV1gexVdVyE8kLcZwGqhk5iIlSbiJ5IY51Kk1EyjjICp+nl4tIXoqT0YoqXzEYDCISicDn
88Hn88Hv9yefE0Lg9/uTZX6/P1lGCEnWYY/sOYDkexTjJV1ODc83plMfYp0ZYI8shFlnZFgZex4M
BlOSBCvSEPf48eO8nby1EZyeW8tkr1nLGOyErQNpUVk2r3uJ9PfiNE3z8FAK1wg8IuG9/GxGioP8
fj8qKio8OyDFC/x+f0oZ2b179z8BhLw6CFG4Svfotq4oJGXzWOjwBuDfAUrp7wGMePmpxRLKMqUA
IW/WB+BDAN96/cmKtHwL4KeEUopdu3bVAjgD4CCADYU9rrLnHwD+AODnjx49+s9/AexyD6bH7vMJ
AAAAAElFTkSuQmCC\
"""
def thumbnail():
icon = base64.decodestring(iconstr)
return icon
if __name__ == "__main__":
icon = thumbnail()
f = file("thumbnail.png","wb")
f.write(icon)
f.close()

View file

@ -1,180 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2009 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s): Michael Howitz, gocept gmbh & co. kg
#
# $Id: userfield.py 447 2008-07-10 20:01:30Z roug $
"""Class to show and manipulate user fields in odf documents."""
import sys
import zipfile
from odf.text import UserFieldDecl
from odf.namespaces import OFFICENS
from odf.opendocument import load
import io, sys
if sys.version_info[0]==3:
unicode=str
OUTENCODING = "utf-8"
# OpenDocument v.1.0 section 6.7.1
VALUE_TYPES = {
u'float': (OFFICENS, u'value'),
u'percentage': (OFFICENS, u'value'),
u'currency': (OFFICENS, u'value'),
u'date': (OFFICENS, u'date-value'),
u'time': (OFFICENS, u'time-value'),
u'boolean': (OFFICENS, u'boolean-value'),
u'string': (OFFICENS, u'string-value'),
}
class UserFields(object):
"""List, view and manipulate user fields."""
# these attributes can be a filename or a file like object
src_file = None
dest_file = None
def __init__(self, src=None, dest=None):
"""Constructor
@param src open file in binary mode: source document,
or filename as a unicode string, or None for stdin.
@param dest opendile in binary mode: destination document,
or filename as a unicode string, or None for stdout.
"""
assert(src==None or 'rb' in repr(src) or 'BufferedReader' in repr(src) or 'BytesIO' in repr(src) or type(src)==type(u""))
assert(dest==None or 'wb' in repr(dest) or 'BufferedWriter' in repr(dest) or 'BytesIO' in repr(dest) or type(dest)==type(u""))
self.src_file = src
self.dest_file = dest
self.document = None
def loaddoc(self):
if (sys.version_info[0]==3 and (isinstance(self.src_file, str) or (isinstance(self.src_file, io.IOBase)))) or (sys.version_info[0]==2 and isinstance(self.src_file, basestring)):
# src_file is a filename, check if it is a zip-file
if not zipfile.is_zipfile(self.src_file):
raise TypeError(u"%s is no odt file." % self.src_file)
elif self.src_file is None:
# use stdin if no file given
self.src_file = sys.stdin
self.document = load(self.src_file)
def savedoc(self):
# write output
if self.dest_file is None:
# use stdout if no filename given
self.document.save(u'-')
else:
self.document.save(self.dest_file)
def list_fields(self):
"""List (extract) all known user-fields.
@return list of user-field names as unicode strings.
"""
return [x[0] for x in self.list_fields_and_values()]
def list_fields_and_values(self, field_names=None):
"""List (extract) user-fields with type and value.
@param field_names list of field names as unicode strings
to show, or None for all.
@return list of tuples (<field name>, <field type>, <value>)
as type (unicode string, stringified type, unicode string).
"""
self.loaddoc()
found_fields = []
all_fields = self.document.getElementsByType(UserFieldDecl)
for f in all_fields:
value_type = f.getAttribute(u'valuetype')
if value_type == u'string':
value = f.getAttribute(u'stringvalue')
else:
value = f.getAttribute(u'value')
field_name = f.getAttribute(u'name')
if field_names is None or field_name in field_names:
found_fields.append((field_name,
value_type,
value))
return found_fields
def list_values(self, field_names):
"""Extract the contents of given field names from the file.
@param field_names list of field names as unicode strings
@return list of field values as unicode strings.
"""
return [x[2] for x in self.list_fields_and_values(field_names)]
def get(self, field_name):
"""Extract the contents of this field from the file.
@param field_name unicode string: name of a field
@return field value as a unicode string or None if field does not exist.
"""
assert(type(field_name)==type(u""))
values = self.list_values([field_name])
if not values:
return None
return values[0]
def get_type_and_value(self, field_name):
"""Extract the type and contents of this field from the file.
@param field_name unicode string: name of a field
@return tuple (<type>, <field-value>) as a pair of unicode strings
or None if field does not exist.
"""
assert(type(field_name)==type(u""))
fields = self.list_fields_and_values([field_name])
if not fields:
return None
field_name, value_type, value = fields[0]
return value_type, value
def update(self, data):
"""Set the value of user fields. The field types will be the same.
data ... dict, with field name as key, field value as value
Returns None
"""
self.loaddoc()
all_fields = self.document.getElementsByType(UserFieldDecl)
for f in all_fields:
field_name = f.getAttribute(u'name')
if field_name in data:
value_type = f.getAttribute(u'valuetype')
value = data.get(field_name)
if value_type == u'string':
f.setAttribute(u'stringvalue', value)
else:
f.setAttribute(u'value', value)
self.savedoc()

View file

@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from namespaces import XFORMSNS
from element import Element
# ODF 1.0 section 11.2
# XForms is designed to be embedded in another XML format.
# Autogenerated
def Model(**args):
return Element(qname = (XFORMSNS,'model'), **args)
def Instance(**args):
return Element(qname = (XFORMSNS,'instance'), **args)
def Bind(**args):
return Element(qname = (XFORMSNS,'bind'), **args)

File diff suppressed because it is too large Load diff

View file

@ -2,14 +2,9 @@
#--!-- coding: utf8 --!--
# As seen on http://stackoverflow.com/questions/36932/how-can-i-represent-an-enum-in-python
from enum import IntEnum
from enum import Enum
#def enum(**enums):
#return type(str('Enum'), (), enums)
class Character(Enum):
class Character(IntEnum):
name = 0
ID = 1
importance = 2
@ -22,7 +17,7 @@ class Character(Enum):
summaryFull = 9
notes = 10
class Plot(Enum):
class Plot(IntEnum):
name = 0
ID = 1
importance = 2
@ -32,20 +27,20 @@ class Plot(Enum):
steps = 6
summary = 7
class PlotStep(Enum):
class PlotStep(IntEnum):
name = 0
ID = 1
meta = 2
summary = 3
class World(Enum):
class World(IntEnum):
name = 0
ID = 1
description = 2
passion = 3
conflict = 4
class Outline(Enum):
class Outline(IntEnum):
title = 0
ID = 1
type = 2
@ -65,3 +60,8 @@ class Outline(Enum):
textFormat = 15
revisions = 16
customIcon = 17
class Abstract(IntEnum):
title = 0
ID = 1
type = 2

View file

@ -7,7 +7,7 @@ import subprocess
from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QWidget
from manuskript.models.outlineModel import outlineItem
from manuskript.models import outlineItem
from manuskript.functions import mainWindow

View file

@ -6,7 +6,7 @@ from PyQt5.QtWidgets import QPlainTextEdit, qApp, QFrame, QFileDialog
from manuskript.exporter.basic import basicFormat
from manuskript.functions import mainWindow
from manuskript.models.outlineModel import outlineItem
from manuskript.models import outlineItem
from manuskript.ui.exporters.manuskript.plainTextSettings import exporterSettings

View file

@ -172,7 +172,7 @@ def outlineItemColors(item):
# POV
colors["POV"] = QColor(Qt.transparent)
POV = item.data(Outline.POV.value)
POV = item.data(Outline.POV)
if POV == "":
col = QColor(Qt.transparent)
else:
@ -181,7 +181,7 @@ def outlineItemColors(item):
colors["POV"] = iconColor(mw.mdlCharacter.icon(i))
# Label
lbl = item.data(Outline.label.value)
lbl = item.data(Outline.label)
if lbl == "":
col = QColor(Qt.transparent)
else:
@ -192,7 +192,7 @@ def outlineItemColors(item):
colors["Label"] = col
# Progress
pg = item.data(Outline.goalPercentage.value)
pg = item.data(Outline.goalPercentage)
colors["Progress"] = colorFromProgress(pg)
# Compile
@ -353,10 +353,27 @@ def customIcons():
return sorted(r)
def statusMessage(message, duration=5000):
"""
Shows a message in MainWindow's status bar.
"""
mainWindow().statusBar().showMessage(message, duration)
def openURL(url):
"""
Opens url (string) in browser using desktop default application.
"""
QDesktopServices.openUrl(QUrl(url))
def inspect():
"""
Debugging tool. Call it to see a stack of calls up to that point.
"""
import inspect, os
print("-----------------------")
for s in inspect.stack()[1:]:
print(" * {}:{} // {}".format(
os.path.basename(s.filename),
s.lineno,
s.function))
print(" " + "".join(s.code_context))

View file

@ -3,7 +3,7 @@
import os
from manuskript.importer.abstractImporter import abstractImporter
from manuskript.models.outlineModel import outlineItem
from manuskript.models import outlineItem
from manuskript.enums import Outline
from PyQt5.QtWidgets import qApp

View file

@ -2,7 +2,7 @@
# --!-- coding: utf8 --!--
from manuskript.importer.abstractImporter import abstractImporter
from manuskript.models.outlineModel import outlineItem
from manuskript.models import outlineItem
from manuskript.enums import Outline
from PyQt5.QtWidgets import qApp
import re, os

View file

@ -2,7 +2,7 @@
# --!-- coding: utf8 --!--
from PyQt5.QtWidgets import qApp, QMessageBox
from manuskript.models.outlineModel import outlineItem
from manuskript.models import outlineItem
from manuskript.enums import Outline
from lxml import etree as ET
from manuskript.functions import mainWindow
@ -114,18 +114,18 @@ class mindMapImporter(abstractImporter):
# If there is one line, we use it as title.
# Otherwise we leave it to be inserted as a note.
if len(lines) == 1:
item.setData(Outline.title.value, "".join(lines))
item.setData(Outline.title, "".join(lines))
content = ""
if content:
# Set the note content as text value
content = HTML2MD(content)
item.setData(Outline.notes.value, content)
item.setData(Outline.notes, content)
if url:
# Set the url in notes
item.setData(Outline.notes.value,
item.data(Outline.notes.value) + "\n\n" + url)
item.setData(Outline.notes,
item.data(Outline.notes) + "\n\n" + url)
children = underElement.findall('node')
@ -137,10 +137,10 @@ class mindMapImporter(abstractImporter):
# Process if no children
elif self.getSetting("importTipAs").value() == "Text":
# Transform item to text
item.setData(Outline.type.value, 'md')
item.setData(Outline.type, 'md')
# Move notes to text
if item.data(Outline.notes.value):
item.setData(Outline.text.value, item.data(Outline.notes.value))
item.setData(Outline.notes.value, "")
if item.data(Outline.notes):
item.setData(Outline.text, item.data(Outline.notes))
item.setData(Outline.notes, "")
return items

View file

@ -2,7 +2,7 @@
# --!-- coding: utf8 --!--
from PyQt5.QtWidgets import qApp, QMessageBox
from manuskript.models.outlineModel import outlineItem
from manuskript.models import outlineItem
from manuskript.enums import Outline
from lxml import etree as ET
from manuskript.functions import mainWindow
@ -90,8 +90,8 @@ class opmlImporter(abstractImporter):
for el in children:
items.extend(cls.parseItems(el, card))
else:
card.setData(Outline.type.value, 'md')
card.setData(Outline.text.value, body)
card.setData(Outline.type, 'md')
card.setData(Outline.text, body)
return items

View file

@ -24,7 +24,7 @@ from lxml import etree as ET
from manuskript.load_save.version_0 import loadFilesFromZip
from manuskript.models.characterModel import CharacterInfo
from manuskript.models.outlineModel import outlineItem
from manuskript.models import outlineItem
try:
import zlib # Used with zipfile for compression
@ -473,7 +473,7 @@ def addPlotItem(root, mdl, parent=QModelIndex()):
outline.attrib[w.name] = val
# List characters as attrib
if y == Plot.characters.value:
if y == Plot.characters:
if mdl.hasChildren(index):
characters = []
for cX in range(mdl.rowCount(index)):
@ -486,7 +486,7 @@ def addPlotItem(root, mdl, parent=QModelIndex()):
outline.attrib.pop(Plot.characters.name)
# List resolution steps as sub items
elif y == Plot.steps.value:
elif y == Plot.steps:
if mdl.hasChildren(index):
for cX in range(mdl.rowCount(index)):
step = ET.SubElement(outline, "step")
@ -603,7 +603,7 @@ def outlineToMMD(item):
content += formatMetaData(attrib.name, str(val), 15)
content += "\n\n"
content += item.data(Outline.text.value)
content += item.data(Outline.text)
return content
@ -762,16 +762,16 @@ def loadProject(project, zip=None):
log("* Add plot: ", row[0].text())
# Characters
if row[Plot.characters.value].text():
IDs = row[Plot.characters.value].text().split(",")
if row[Plot.characters].text():
IDs = row[Plot.characters].text().split(",")
item = QStandardItem()
for ID in IDs:
item.appendRow(QStandardItem(ID.strip()))
row[Plot.characters.value] = item
row[Plot.characters] = item
# Subplots
for step in plot:
row[Plot.steps.value].appendRow(
row[Plot.steps].appendRow(
getStandardItemRowFromXMLEnum(step, PlotStep)
)
@ -929,18 +929,18 @@ def outlineFromMMD(text, parent):
# Store metadata
for k in md:
if k in Outline.__members__:
item.setData(Outline.__members__[k].value, str(md[k]))
item.setData(Outline.__members__[k], str(md[k]))
# Store body
item.setData(Outline.text.value, str(body))
item.setData(Outline.text, str(body))
# Set file format to "md"
# (Old version of manuskript had different file formats: text, t2t, html and md)
# If file format is html, convert to plain text:
if item.type() == "html":
item.setData(Outline.text.value, HTML2PlainText(body))
item.setData(Outline.text, HTML2PlainText(body))
if item.type() in ["txt", "t2t", "html"]:
item.setData(Outline.type.value, "md")
item.setData(Outline.type, "md")
return item

View file

@ -14,14 +14,13 @@ from manuskript.version import getVersion
faulthandler.enable()
def run():
def prepare(tests=False):
app = QApplication(sys.argv)
app.setOrganizationName("manuskript")
app.setOrganizationName("manuskript"+("_tests" if tests else ""))
app.setOrganizationDomain("www.theologeek.ch")
app.setApplicationName("manuskript")
app.setApplicationName("manuskript"+("_tests" if tests else ""))
app.setApplicationVersion(getVersion())
print("Running manuskript version {}.".format(getVersion()))
icon = QIcon()
for i in [16, 32, 64, 128, 256, 512]:
@ -57,25 +56,38 @@ def run():
QIcon.setThemeSearchPaths(QIcon.themeSearchPaths() + [appPath("icons")])
QIcon.setThemeName("NumixMsk")
# qApp.setWindowIcon(QIcon.fromTheme("im-aim"))
# Seperating launch to avoid segfault, so it seem.
# Cf. http://stackoverflow.com/questions/12433491/is-this-pyqt-4-python-bug-or-wrongly-behaving-code
launch()
# Main window
from manuskript.mainWindow import MainWindow
def launch():
from .mainWindow import MainWindow
main = MainWindow()
MW = MainWindow()
# We store the system default cursor flash time to be able to restore it
# later if necessary
main._defaultCursorFlashTime = qApp.cursorFlashTime()
main.show()
MW._defaultCursorFlashTime = qApp.cursorFlashTime()
return app, MW
def launch(MW = None):
if MW is None:
from manuskript.functions import mainWindow
MW = mainWindow()
MW.show()
qApp.exec_()
qApp.deleteLater()
def run():
"""
Run separates prepare and launch for two reasons:
1. I've read somewhere it helps with potential segfault (see comment below)
2. So that prepare can be used in tests, without running the whole thing
"""
# Need to return and keep `app` otherwise it gets deleted.
app, MW = prepare()
# Seperating launch to avoid segfault, so it seem.
# Cf. http://stackoverflow.com/questions/12433491/is-this-pyqt-4-python-bug-or-wrongly-behaving-code
launch(MW)
if __name__ == "__main__":
run()

View file

@ -10,11 +10,11 @@ from PyQt5.QtWidgets import QMainWindow, QHeaderView, qApp, QMenu, QActionGroup,
from manuskript import settings
from manuskript.enums import Character, PlotStep, Plot, World, Outline
from manuskript.functions import AUC, wordCount, appPath, findWidgetsOfClass
from manuskript.functions import wordCount, appPath, findWidgetsOfClass
import manuskript.functions as F
from manuskript import loadSave
from manuskript.models.characterModel import characterModel
from manuskript.models.outlineModel import outlineModel
from manuskript.models import outlineModel
from manuskript.models.plotModel import plotModel
from manuskript.models.worldModel import worldModel
from manuskript.settingsWindow import settingsWindow
@ -57,6 +57,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Var
self.currentProject = None
self._lastFocus = None
self._defaultCursorFlashTime = 1000 # Overriden at startup with system
# value. In manuskript.main.
self.readSettings()
@ -326,12 +328,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.txtPlotResult.setCurrentModelIndex(index)
self.sldPlotImportance.setCurrentModelIndex(index)
self.lstPlotPerso.setRootIndex(index.sibling(index.row(),
Plot.characters.value))
Plot.characters))
# Slider importance
self.updatePlotImportance(index.row())
subplotindex = index.sibling(index.row(), Plot.steps.value)
subplotindex = index.sibling(index.row(), Plot.steps)
self.lstSubPlots.setRootIndex(subplotindex)
if self.mdlPlots.rowCount(subplotindex):
self.updateSubPlotView()
@ -351,22 +353,22 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# So instead we set their width to 0.
#for i in range(self.mdlPlots.columnCount()):
#self.lstSubPlots.hideColumn(i)
#self.lstSubPlots.showColumn(PlotStep.name.value)
#self.lstSubPlots.showColumn(PlotStep.meta.value)
#self.lstSubPlots.showColumn(PlotStep.name)
#self.lstSubPlots.showColumn(PlotStep.meta)
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
PlotStep.ID.value, QHeaderView.Fixed)
PlotStep.ID, QHeaderView.Fixed)
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
PlotStep.summary.value, QHeaderView.Fixed)
PlotStep.summary, QHeaderView.Fixed)
self.lstSubPlots.horizontalHeader().resizeSection(
PlotStep.ID.value, 0)
PlotStep.ID, 0)
self.lstSubPlots.horizontalHeader().resizeSection(
PlotStep.summary.value, 0)
PlotStep.summary, 0)
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
PlotStep.name.value, QHeaderView.Stretch)
PlotStep.name, QHeaderView.Stretch)
self.lstSubPlots.horizontalHeader().setSectionResizeMode(
PlotStep.meta.value, QHeaderView.ResizeToContents)
PlotStep.meta, QHeaderView.ResizeToContents)
self.lstSubPlots.verticalHeader().hide()
def updatePlotImportance(self, ID):
@ -375,7 +377,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def changeCurrentSubPlot(self, index):
# Got segfaults when using textEditView model system, so ad hoc stuff.
index = index.sibling(index.row(), PlotStep.summary.value)
index = index.sibling(index.row(), PlotStep.summary)
item = self.mdlPlots.itemFromIndex(index)
if not item:
self.txtSubPlotSummary.setEnabled(False)
@ -393,7 +395,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
index = self.lstSubPlots.currentIndex()
if not index.isValid():
return
index = index.sibling(index.row(), PlotStep.summary.value)
index = index.sibling(index.row(), PlotStep.summary)
item = self.mdlPlots.itemFromIndex(index)
self._updatingSubPlot = True
@ -745,20 +747,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def makeUIConnections(self):
"Connections that have to be made once only, even when a new project is loaded."
self.lstCharacters.currentItemChanged.connect(self.changeCurrentCharacter, AUC)
self.lstCharacters.currentItemChanged.connect(self.changeCurrentCharacter, F.AUC)
self.txtPlotFilter.textChanged.connect(self.lstPlots.setFilter, AUC)
self.lstPlots.currentItemChanged.connect(self.changeCurrentPlot, AUC)
self.txtPlotFilter.textChanged.connect(self.lstPlots.setFilter, F.AUC)
self.lstPlots.currentItemChanged.connect(self.changeCurrentPlot, F.AUC)
self.txtSubPlotSummary.document().contentsChanged.connect(
self.updateSubPlotSummary, AUC)
self.lstSubPlots.clicked.connect(self.changeCurrentSubPlot, AUC)
self.updateSubPlotSummary, F.AUC)
self.lstSubPlots.clicked.connect(self.changeCurrentSubPlot, F.AUC)
self.btnRedacAddFolder.clicked.connect(self.treeRedacOutline.addFolder, AUC)
self.btnOutlineAddFolder.clicked.connect(self.treeOutlineOutline.addFolder, AUC)
self.btnRedacAddText.clicked.connect(self.treeRedacOutline.addText, AUC)
self.btnOutlineAddText.clicked.connect(self.treeOutlineOutline.addText, AUC)
self.btnRedacRemoveItem.clicked.connect(self.outlineRemoveItemsRedac, AUC)
self.btnOutlineRemoveItem.clicked.connect(self.outlineRemoveItemsOutline, AUC)
self.btnRedacAddFolder.clicked.connect(self.treeRedacOutline.addFolder, F.AUC)
self.btnOutlineAddFolder.clicked.connect(self.treeOutlineOutline.addFolder, F.AUC)
self.btnRedacAddText.clicked.connect(self.treeRedacOutline.addText, F.AUC)
self.btnOutlineAddText.clicked.connect(self.treeOutlineOutline.addText, F.AUC)
self.btnRedacRemoveItem.clicked.connect(self.outlineRemoveItemsRedac, F.AUC)
self.btnOutlineRemoveItem.clicked.connect(self.outlineRemoveItemsOutline, F.AUC)
self.tabMain.currentChanged.connect(self.toolbar.setCurrentGroup)
self.tabMain.currentChanged.connect(self.tabMainChanged)
@ -803,27 +805,27 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.lstCharacters.setCharactersModel(self.mdlCharacter)
self.tblPersoInfos.setModel(self.mdlCharacter)
self.btnAddPerso.clicked.connect(self.mdlCharacter.addCharacter, AUC)
self.btnAddPerso.clicked.connect(self.mdlCharacter.addCharacter, F.AUC)
try:
self.btnRmPerso.clicked.connect(self.lstCharacters.removeCharacter, AUC)
self.btnPersoColor.clicked.connect(self.lstCharacters.choseCharacterColor, AUC)
self.btnPersoAddInfo.clicked.connect(self.lstCharacters.addCharacterInfo, AUC)
self.btnPersoRmInfo.clicked.connect(self.lstCharacters.removeCharacterInfo, AUC)
self.btnRmPerso.clicked.connect(self.lstCharacters.removeCharacter, F.AUC)
self.btnPersoColor.clicked.connect(self.lstCharacters.choseCharacterColor, F.AUC)
self.btnPersoAddInfo.clicked.connect(self.lstCharacters.addCharacterInfo, F.AUC)
self.btnPersoRmInfo.clicked.connect(self.lstCharacters.removeCharacterInfo, F.AUC)
except TypeError:
# Connection has already been made
pass
for w, c in [
(self.txtPersoName, Character.name.value),
(self.sldPersoImportance, Character.importance.value),
(self.txtPersoMotivation, Character.motivation.value),
(self.txtPersoGoal, Character.goal.value),
(self.txtPersoConflict, Character.conflict.value),
(self.txtPersoEpiphany, Character.epiphany.value),
(self.txtPersoSummarySentence, Character.summarySentence.value),
(self.txtPersoSummaryPara, Character.summaryPara.value),
(self.txtPersoSummaryFull, Character.summaryFull.value),
(self.txtPersoNotes, Character.notes.value)
(self.txtPersoName, Character.name),
(self.sldPersoImportance, Character.importance),
(self.txtPersoMotivation, Character.motivation),
(self.txtPersoGoal, Character.goal),
(self.txtPersoConflict, Character.conflict),
(self.txtPersoEpiphany, Character.epiphany),
(self.txtPersoSummarySentence, Character.summarySentence),
(self.txtPersoSummaryPara, Character.summaryPara),
(self.txtPersoSummaryFull, Character.summaryFull),
(self.txtPersoNotes, Character.notes)
]:
w.setModel(self.mdlCharacter)
w.setColumn(c)
@ -834,21 +836,21 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.lstPlotPerso.setModel(self.mdlPlots)
self.lstPlots.setPlotModel(self.mdlPlots)
self._updatingSubPlot = False
self.btnAddPlot.clicked.connect(self.mdlPlots.addPlot, AUC)
self.btnAddPlot.clicked.connect(self.mdlPlots.addPlot, F.AUC)
self.btnRmPlot.clicked.connect(lambda:
self.mdlPlots.removePlot(self.lstPlots.currentPlotIndex()), AUC)
self.btnAddSubPlot.clicked.connect(self.mdlPlots.addSubPlot, AUC)
self.btnAddSubPlot.clicked.connect(self.updateSubPlotView, AUC)
self.btnRmSubPlot.clicked.connect(self.mdlPlots.removeSubPlot, AUC)
self.mdlPlots.removePlot(self.lstPlots.currentPlotIndex()), F.AUC)
self.btnAddSubPlot.clicked.connect(self.mdlPlots.addSubPlot, F.AUC)
self.btnAddSubPlot.clicked.connect(self.updateSubPlotView, F.AUC)
self.btnRmSubPlot.clicked.connect(self.mdlPlots.removeSubPlot, F.AUC)
self.lstPlotPerso.selectionModel().selectionChanged.connect(self.plotPersoSelectionChanged)
self.btnRmPlotPerso.clicked.connect(self.mdlPlots.removePlotPerso, AUC)
self.lstSubPlots.selectionModel().currentRowChanged.connect(self.changeCurrentSubPlot, AUC)
self.btnRmPlotPerso.clicked.connect(self.mdlPlots.removePlotPerso, F.AUC)
self.lstSubPlots.selectionModel().currentRowChanged.connect(self.changeCurrentSubPlot, F.AUC)
for w, c in [
(self.txtPlotName, Plot.name.value),
(self.txtPlotDescription, Plot.description.value),
(self.txtPlotResult, Plot.result.value),
(self.sldPlotImportance, Plot.importance.value),
(self.txtPlotName, Plot.name),
(self.txtPlotDescription, Plot.description),
(self.txtPlotResult, Plot.result),
(self.sldPlotImportance, Plot.importance),
]:
w.setModel(self.mdlPlots)
w.setColumn(c)
@ -861,7 +863,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.plotCharacterDelegate = outlineCharacterDelegate(self.mdlCharacter, self)
self.lstPlotPerso.setItemDelegate(self.plotCharacterDelegate)
self.plotDelegate = plotDelegate(self)
self.lstSubPlots.setItemDelegateForColumn(PlotStep.meta.value, self.plotDelegate)
self.lstSubPlots.setItemDelegateForColumn(PlotStep.meta, self.plotDelegate)
# World
self.treeWorld.setModel(self.mdlWorld)
@ -869,14 +871,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.treeWorld.hideColumn(i)
self.treeWorld.showColumn(0)
self.btnWorldEmptyData.setMenu(self.mdlWorld.emptyDataMenu())
self.treeWorld.selectionModel().selectionChanged.connect(self.changeCurrentWorld, AUC)
self.btnAddWorld.clicked.connect(self.mdlWorld.addItem, AUC)
self.btnRmWorld.clicked.connect(self.mdlWorld.removeItem, AUC)
self.treeWorld.selectionModel().selectionChanged.connect(self.changeCurrentWorld, F.AUC)
self.btnAddWorld.clicked.connect(self.mdlWorld.addItem, F.AUC)
self.btnRmWorld.clicked.connect(self.mdlWorld.removeItem, F.AUC)
for w, c in [
(self.txtWorldName, World.name.value),
(self.txtWorldDescription, World.description.value),
(self.txtWorldPassion, World.passion.value),
(self.txtWorldConflict, World.conflict.value),
(self.txtWorldName, World.name),
(self.txtWorldDescription, World.description),
(self.txtWorldPassion, World.passion),
(self.txtWorldConflict, World.conflict),
]:
w.setModel(self.mdlWorld)
w.setColumn(c)
@ -898,14 +900,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# self.redacEditor.setModel(self.mdlOutline)
self.storylineView.setModels(self.mdlOutline, self.mdlCharacter, self.mdlPlots)
self.treeOutlineOutline.selectionModel().selectionChanged.connect(self.outlineItemEditor.selectionChanged, AUC)
self.treeOutlineOutline.clicked.connect(self.outlineItemEditor.selectionChanged, AUC)
self.treeOutlineOutline.selectionModel().selectionChanged.connect(self.outlineItemEditor.selectionChanged, F.AUC)
self.treeOutlineOutline.clicked.connect(self.outlineItemEditor.selectionChanged, F.AUC)
# Sync selection
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.redacMetadata.selectionChanged, AUC)
self.treeRedacOutline.clicked.connect(self.redacMetadata.selectionChanged, AUC)
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.redacMetadata.selectionChanged, F.AUC)
self.treeRedacOutline.clicked.connect(self.redacMetadata.selectionChanged, F.AUC)
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.mainEditor.selectionChanged, AUC)
self.treeRedacOutline.selectionModel().selectionChanged.connect(self.mainEditor.selectionChanged, F.AUC)
# Cheat Sheet
self.cheatSheet.setModels()
@ -918,7 +920,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.tblDebugPersos.selectionModel().currentChanged.connect(
lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlCharacter.index(
self.tblDebugPersos.selectionModel().currentIndex().row(),
Character.name.value)), AUC)
Character.name)), F.AUC)
self.tblDebugPlots.setModel(self.mdlPlots)
self.tblDebugPlotsPersos.setModel(self.mdlPlots)
@ -926,11 +928,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.tblDebugPlots.selectionModel().currentChanged.connect(
lambda: self.tblDebugPlotsPersos.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
Plot.characters.value)), AUC)
Plot.characters)), F.AUC)
self.tblDebugPlots.selectionModel().currentChanged.connect(
lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
Plot.steps.value)), AUC)
Plot.steps)), F.AUC)
self.treeDebugWorld.setModel(self.mdlWorld)
self.treeDebugOutline.setModel(self.mdlOutline)
self.lstDebugLabels.setModel(self.mdlLabels)
@ -997,15 +999,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.disconnectAll(self.tblDebugPersos.selectionModel().currentChanged,
lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlCharacter.index(
self.tblDebugPersos.selectionModel().currentIndex().row(),
Character.name.value)))
Character.name)))
self.disconnectAll(self.tblDebugPlots.selectionModel().currentChanged,
lambda: self.tblDebugPlotsPersos.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
Plot.characters.value)))
Plot.characters)))
self.disconnectAll(self.tblDebugPlots.selectionModel().currentChanged,
lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index(
self.tblDebugPlots.selectionModel().currentIndex().row(),
Plot.steps.value)))
Plot.steps)))
###############################################################################
# HELP
@ -1173,7 +1175,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
for widget, text, pos in references:
label = helpLabel(text, self)
self.actShowHelp.toggled.connect(label.setVisible, AUC)
self.actShowHelp.toggled.connect(label.setVisible, F.AUC)
widget.layout().insertWidget(pos, label)
self.actShowHelp.setChecked(False)
@ -1185,17 +1187,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.updateMenuDict()
self.menuTools.addMenu(self.menuDict)
self.actSpellcheck.toggled.connect(self.toggleSpellcheck, AUC)
self.dictChanged.connect(self.mainEditor.setDict, AUC)
self.dictChanged.connect(self.redacMetadata.setDict, AUC)
self.dictChanged.connect(self.outlineItemEditor.setDict, AUC)
self.actSpellcheck.toggled.connect(self.toggleSpellcheck, F.AUC)
self.dictChanged.connect(self.mainEditor.setDict, F.AUC)
self.dictChanged.connect(self.redacMetadata.setDict, F.AUC)
self.dictChanged.connect(self.outlineItemEditor.setDict, F.AUC)
else:
# No Spell check support
self.actSpellcheck.setVisible(False)
a = QAction(self.tr("Install PyEnchant to use spellcheck"), self)
a.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxWarning))
a.triggered.connect(self.openPyEnchantWebPage, AUC)
a.triggered.connect(self.openPyEnchantWebPage, F.AUC)
self.menuTools.addAction(a)
###############################################################################
@ -1215,7 +1217,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
settings.dict = enchant.get_default_language()
if str(i[0]) == settings.dict:
a.setChecked(True)
a.triggered.connect(self.setDictionary, AUC)
a.triggered.connect(self.setDictionary, F.AUC)
self.menuDictGroup.addAction(a)
self.menuDict.addAction(a)
@ -1333,7 +1335,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
a.setData("{},{},{}".format(mnud, sd, vd))
if settings.viewSettings[mnud][sd] == vd:
a.setChecked(True)
a.triggered.connect(self.setViewSettingsAction, AUC)
a.triggered.connect(self.setViewSettingsAction, F.AUC)
agp.addAction(a)
m2.addAction(a)
m.addMenu(m2)
@ -1385,8 +1387,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
w.cmbPOV.setVisible(val)
# POV in outline view
if Outline.POV.value in settings.outlineViewColumns:
settings.outlineViewColumns.remove(Outline.POV.value)
if val is None and Outline.POV in settings.outlineViewColumns:
settings.outlineViewColumns.remove(Outline.POV)
from manuskript.ui.views.outlineView import outlineView
for w in findWidgetsOfClass(outlineView):
@ -1415,4 +1417,3 @@ class MainWindow(QMainWindow, Ui_MainWindow):
r = self.dialog.geometry()
r2 = self.geometry()
self.dialog.move(r2.center() - r.center())

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from manuskript.models.outlineItem import outlineItem
from manuskript.models.outlineModel import outlineModel

View file

@ -0,0 +1,306 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtCore import QAbstractItemModel, QMimeData
from PyQt5.QtCore import QModelIndex
from PyQt5.QtCore import QSize
from PyQt5.QtCore import QVariant
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QFont
from PyQt5.QtWidgets import QTextEdit, qApp
from lxml import etree as ET
from manuskript import enums
class abstractItem():
# Enum kept on the class for easier acces
enum = enums.Abstract
# Used for XML export
name = "abstractItem"
def __init__(self, model=None, title="", _type="abstract", xml=None, parent=None, ID=None):
self._data = {}
self.childItems = []
self._parent = None
self._model = model
self.IDs = ["0"] # used by root item to store unique IDs
self._lastPath = "" # used by loadSave version_1 to remember which files the items comes from,
# in case it is renamed / removed
self._data[self.enum.title] = title
self._data[self.enum.type] = _type
if xml is not None:
self.setFromXML(xml)
if ID:
self._data[self.enum.ID] = ID
if parent:
parent.appendChild(self)
#######################################################################
# Model
#######################################################################
def setModel(self, model):
self._model = model
for c in self.children():
c.setModel(model)
def index(self, column=0):
if self._model:
return self._model.indexFromItem(self, column)
else:
return QModelIndex()
def emitDataChanged(self, cols=None, recursive=False):
"""
Emits the dataChanged signal of the model, to signal views that data
have changed.
@param cols: an array of int (or None). The columns of the index that
have been changed.
@param recursive: boolean. If true, all children will also emit the
dataChanged signal.
"""
idx = self.index()
if idx and self._model:
if not cols:
# Emit data changed for the whole item (all columns)
self._model.dataChanged.emit(idx, self.index(len(self.enum)))
else:
# Emit only for the specified columns
for c in cols:
self._model.dataChanged.emit(self.index(c), self.index(c))
if recursive:
for c in self.children():
c.emitDataChanged(cols, recursive=True)
#######################################################################
# Properties
#######################################################################
def title(self):
return self._data.get(self.enum.title, "")
def ID(self):
return self._data.get(self.enum.ID)
def columnCount(self):
return len(self.enum)
def type(self):
return self._data[self.enum.type]
#######################################################################
# Parent / Children managment
#######################################################################
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def childCountRecursive(self):
n = self.childCount()
for c in self.children():
n += c.childCountRecursive()
return n
def children(self):
return self.childItems
def row(self):
if self.parent():
return self.parent().childItems.index(self)
def appendChild(self, child):
self.insertChild(self.childCount(), child)
def insertChild(self, row, child):
self.childItems.insert(row, child)
child._parent = self
child.setModel(self._model)
if not child.ID():
child.getUniqueID()
def removeChild(self, row):
"""
Removes child at position `row` and returns it.
@param row: index (int) of the child to remove.
@return: the removed abstractItem
"""
r = self.childItems.pop(row)
return r
def parent(self):
return self._parent
def path(self, sep=" > "):
"Returns path to item as string."
if self.parent().parent():
return "{parent}{sep}{title}".format(
parent=self.parent().path(),
sep=sep,
title=self.title())
else:
return self.title()
def pathID(self):
"Returns path to item as list of (ID, title)."
if self.parent() and self.parent().parent():
return self.parent().pathID() + [(self.ID(), self.title())]
else:
return [(self.ID(), self.title())]
def level(self):
"""Returns the level of the current item. Root item returns -1."""
if self.parent():
return self.parent().level() + 1
else:
return -1
def copy(self):
"""
Returns a copy of item, with no parent, and no ID.
"""
item = self.__class__(xml=self.toXML())
item.setData(self.enum.ID, None)
return item
###############################################################################
# IDS
###############################################################################
def getUniqueID(self, recursive=False):
self.setData(self.enum.ID, self._model.rootItem.findUniqueID())
if recursive:
for c in self.children():
c.getUniqueID(recursive)
def checkIDs(self):
"""This is called when a model is loaded.
Makes a list of all sub-items IDs, that is used to generate unique IDs afterwards.
"""
self.IDs = self.listAllIDs()
if max([self.IDs.count(i) for i in self.IDs if i]) != 1:
print("WARNING ! There are some items with same IDs:", [i for i in self.IDs if i and self.IDs.count(i) != 1])
def checkChildren(item):
for c in item.children():
_id = c.ID()
if not _id or _id == "0":
c.getUniqueID()
checkChildren(c)
checkChildren(self)
def listAllIDs(self):
IDs = [self.ID()]
for c in self.children():
IDs.extend(c.listAllIDs())
return IDs
def findUniqueID(self):
IDs = [int(i) for i in self.IDs]
k = 1
while k in IDs:
k += 1
self.IDs.append(str(k))
return str(k)
#######################################################################
# Data
#######################################################################
def data(self, column, role=Qt.DisplayRole):
# Return value in self._data
if role == Qt.DisplayRole or role == Qt.EditRole:
return self._data.get(column, "")
# Or return QVariant
return QVariant()
def setData(self, column, data, role=Qt.DisplayRole):
# Setting data
self._data[column] = data
# Emit signal
self.emitDataChanged(cols=[column]) # new in 0.5.0
###############################################################################
# XML
###############################################################################
# We don't want to write some datas (computed)
XMLExclude = []
# We want to force some data even if they're empty
XMLForce = []
def toXML(self):
"""
Returns a string containing the item (and children) in XML.
By default, saves all attributes from self.enum and lastPath.
You can define in XMLExclude and XMLForce what you want to be
excluded or forcibly included.
"""
item = ET.Element(self.name)
for attrib in self.enum:
if attrib in self.XMLExclude:
continue
val = self.data(attrib)
if val or attrib in self.XMLForce:
item.set(attrib.name, str(val))
# Saving lastPath
item.set("lastPath", self._lastPath)
# Additional stuff for subclasses
item = self.toXMLProcessItem(item)
for i in self.childItems:
item.append(ET.XML(i.toXML()))
return ET.tostring(item)
def toXMLProcessItem(self, item):
"""
Subclass this to change the behavior of `toXML`.
"""
return item
def setFromXML(self, xml):
root = ET.XML(xml)
for k in self.enum:
if k.name in root.attrib:
self.setData(k, str(root.attrib[k.name]))
if "lastPath" in root.attrib:
self._lastPath = root.attrib["lastPath"]
self.setFromXMLProcessMore(root)
for child in root:
if child.tag == self.name:
item = self.__class__(self._model, xml=ET.tostring(child), parent=self)
def setFromXMLProcessMore(self, root):
"""
Additional stuff that subclasses must do with the XML to restore
item.
"""
return

View file

@ -0,0 +1,547 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
import locale
from PyQt5.QtCore import QAbstractItemModel, QMimeData
from PyQt5.QtCore import QModelIndex
from PyQt5.QtCore import QSize
from PyQt5.QtCore import QVariant
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QFont
from PyQt5.QtWidgets import QTextEdit, qApp
from manuskript import settings
from lxml import etree as ET
from manuskript.enums import Outline
from manuskript.functions import mainWindow, toInt, wordCount
from manuskript.models import outlineItem
try:
locale.setlocale(locale.LC_ALL, '')
except:
# Invalid locale, but not really a big deal because it's used only for
# number formating
pass
import time, os
class abstractModel(QAbstractItemModel):
"""
Abstract model is the base class for all others models we use.
It's main responsibilities are:
- Interface with QModelIndex and stuff
- XML Import / Export
- Drag'n'drop
"""
def __init__(self, parent):
QAbstractItemModel.__init__(self, parent)
self.rootItem = outlineItem(self, title="Root", ID="0")
# Stores removed item, in order to remove them on disk when saving, depending on the file format.
self.removed = []
self._removingRows = False
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QModelIndex()
def indexFromItem(self, item, column=0):
if item == self.rootItem:
return QModelIndex()
parent = item.parent()
if not parent:
parent = self.rootItem
if len(parent.children()) == 0:
return None
# print(item.title(), [i.title() for i in parent.children()])
row = parent.children().index(item)
col = column
return self.createIndex(row, col, item)
def ID(self, index):
if index.isValid():
item = index.internalPointer()
return item.ID()
def findItemsContaining(self, text, columns, caseSensitive=False):
"""
Returns a list of IDs of all items containing `text`
in columns `columns` (being a list of int).
"""
return self.rootItem.findItemsContaining(text, columns, mainWindow(), caseSensitive)
def getItemByID(self, ID):
def search(item):
if item.ID() == ID:
return item
for c in item.children():
r = search(c)
if r:
return r
item = search(self.rootItem)
return item
def getIndexByID(self, ID, column=0):
"Returns the index of item whose ID is `ID`. If none, returns QModelIndex()."
item = self.getItemByID(ID)
if not item:
return QModelIndex()
else:
return self.indexFromItem(item, column)
def parent(self, index=QModelIndex()):
if not index.isValid():
return QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
if parentItem == self.rootItem:
return QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent=QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
def columnCount(self, parent=QModelIndex()):
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return QVariant()
item = index.internalPointer()
return item.data(index.column(), role)
def setData(self, index, value, role=Qt.EditRole):
item = index.internalPointer()
if item.data(index.column(), role) != value:
item.setData(index.column(), value, role)
# self.dataChanged.emit(index.sibling(index.row(), 0),
# index.sibling(index.row(), max([i.value for i in Outline])))
# print("Model emit", index.row(), index.column())
self.dataChanged.emit(index, index)
if index.column() == Outline.type:
# If type changed, then the icon of title changed.
# Some views might be glad to know it.
self.dataChanged.emit(index.sibling(index.row(), Outline.title),
index.sibling(index.row(), Outline.title))
return True
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role in [Qt.DisplayRole, Qt.ToolTipRole]:
if section == Outline.title:
return self.tr("Title")
elif section == Outline.POV:
return self.tr("POV")
elif section == Outline.label:
return self.tr("Label")
elif section == Outline.status:
return self.tr("Status")
elif section == Outline.compile:
return self.tr("Compile")
elif section == Outline.wordCount:
return self.tr("Word count")
elif section == Outline.goal:
return self.tr("Goal")
elif section == Outline.goalPercentage:
return "%"
else:
return [i.name for i in Outline][section]
elif role == Qt.SizeHintRole:
if section == Outline.compile:
return QSize(40, 30)
elif section == Outline.goalPercentage:
return QSize(100, 30)
else:
return QVariant()
else:
return QVariant()
return True
def maxLevel(self):
"""Returns the max depth of the model."""
def depth(item, d=-1):
d += 1
r = d
for c in item.children():
r = max(r, depth(c, d))
return r
d = depth(self.rootItem)
return d
#################### DRAG AND DROP ########################
# http://doc.qt.io/qt-5/model-view-programming.html#using-drag-and-drop-with-item-views
def flags(self, index):
# FIXME when dragging folders, sometimes flags is not called
flags = QAbstractItemModel.flags(self, index) | Qt.ItemIsEditable
if index.isValid() and index.internalPointer().isFolder() and index.column() == 0:
flags |= Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
elif index.isValid() and index.column() == 0:
flags |= Qt.ItemIsDragEnabled
elif not index.isValid():
flags |= Qt.ItemIsDropEnabled
if index.isValid() and index.column() == Outline.compile:
flags |= Qt.ItemIsUserCheckable
if index.column() in [i.value for i in [Outline.wordCount, Outline.goalPercentage]]:
flags &= ~ Qt.ItemIsEditable
return flags
def mimeTypes(self):
return ["application/xml"]
def mimeData(self, indexes):
mimeData = QMimeData()
encodedData = ""
root = ET.Element("outlineItems")
for index in indexes:
if index.isValid() and index.column() == 0:
item = ET.XML(index.internalPointer().toXML())
root.append(item)
encodedData = ET.tostring(root)
mimeData.setData("application/xml", encodedData)
return mimeData
def supportedDropActions(self):
return Qt.CopyAction | Qt.MoveAction
def canDropMimeData(self, data, action, row, column, parent):
"""Ensures that we are not droping an item into itself."""
if not data.hasFormat("application/xml"):
return False
if column > 0:
return False
# # Gets encoded mime data to retrieve the item
items = self.decodeMimeData(data)
if items is None:
return False
# We check if parent is not a child of one of the items
if self.isParentAChildOfItems(parent, items):
return False
return True
def isParentAChildOfItems(self, parent, items):
"""
Takes a parent index, and a list of outlineItems items. Check whether
parent is in a child of one of the items.
Return True in that case, False if not.
"""
# Get the parent item
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
for item in items:
# Get parentItem's parents IDs in a list
path = parentItem.pathID() # path to item in the form [(ID, title), ...]
path = [ID for ID, title in path]
# Is item in the path? It would mean that it tries to get dropped
# as a children of himself.
if item.ID() in path:
return True
return False
def decodeMimeData(self, data):
if not data.hasFormat("application/xml"):
return None
encodedData = bytes(data.data("application/xml")).decode()
root = ET.XML(encodedData)
if root is None:
return None
if root.tag != "outlineItems":
return None
items = []
for child in root:
if child.tag == "outlineItem":
item = outlineItem(xml=ET.tostring(child))
items.append(item)
# We remove every item whose parent is also in items, otherwise it gets
# duplicated. (https://github.com/olivierkes/manuskript/issues/169)
# For example if selecting:
# - Parent
# - Child
# And draging them, items encoded in mime data are: [Parent, Child],
# but Child is already contained in Parent, so if we do nothing we end
# up with:
# - Parent
# - Child
# - Child
newItems = items[:]
IDs = [i.ID() for i in items]
def checkIfChildIsPresent(item):
# Recursively check every children of item, to see if any is in
# the list of items to copy. If so, we remove it from the list.
for c in item.children():
# We check if children is in the selection
# and if it hasn't been removed yet
if c.ID() in IDs and c.ID() in [i.ID() for i in newItems]:
# Remove item by ID
newItems.remove([i for i in newItems if i.ID() == c.ID()][0])
checkIfChildIsPresent(c)
for i in items:
checkIfChildIsPresent(i)
items = newItems
return items
def dropMimeData(self, data, action, row, column, parent):
if action == Qt.IgnoreAction:
return True # What is that?
if action == Qt.MoveAction:
# Strangely, on some cases, we get a call to dropMimeData though
# self.canDropMimeData returned False.
# See https://github.com/olivierkes/manuskript/issues/169 to reproduce.
# So we double check for safety.
if not self.canDropMimeData(data, action, row, column, parent):
return False
items = self.decodeMimeData(data)
if items is None:
return False
if column > 0:
column = 0
if row != -1:
beginRow = row
elif parent.isValid():
beginRow = self.rowCount(parent) + 1
else:
beginRow = self.rowCount() + 1
if action == Qt.CopyAction:
# Behavior if parent is a text item
# For example, we select a text and do: CTRL+C CTRL+V
if parent.isValid() and not parent.internalPointer().isFolder():
# We insert copy in parent folder, just below
beginRow = parent.row() + 1
parent = parent.parent()
if parent.isValid() and parent.internalPointer().isFolder():
while self.isParentAChildOfItems(parent, items):
# We are copying a folder on itself. Assume duplicates.
# Copy not in, but next to
beginRow = parent.row() + 1
parent = parent.parent()
if not items:
return False
# In case of copy actions, items might be duplicates, so we need new IDs.
# But they might not be, if we cut, then paste. Paste is a Copy Action.
# The first paste would not need new IDs. But subsequent ones will.
if action == Qt.CopyAction:
IDs = self.rootItem.listAllIDs()
for item in items:
if item.ID() in IDs:
# Recursively remove ID. So will get a new one when inserted.
def stripID(item):
item.setData(Outline.ID, None)
for c in item.children():
stripID(c)
stripID(item)
r = self.insertItems(items, beginRow, parent)
return r
################# ADDING AND REMOVING #################
def insertItem(self, item, row, parent=QModelIndex()):
return self.insertItems([item], row, parent)
def insertItems(self, items, row, parent=QModelIndex()):
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
if parent.isValid() and parent.column() != 0:
parent = parentItem.index()
# Insert only if parent is folder
if parentItem.isFolder():
self.beginInsertRows(parent, row, row + len(items) - 1)
for i in items:
parentItem.insertChild(row + items.index(i), i)
self.endInsertRows()
return True
else:
return False
def appendItem(self, item, parent=QModelIndex()):
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
if parent.isValid() and parent.column() != 0:
parent = parentItem.index()
# If parent is folder, write into
if parentItem.isFolder():
self.insertItem(item, self.rowCount(parent), parent)
# If parent is not folder, write next to
else:
self.insertItem(item, parent.row() + 1, parent.parent())
def removeIndex(self, index):
item = index.internalPointer()
self.removeRow(item.row(), index.parent())
def removeIndexes(self, indexes):
levels = {}
for i in indexes:
item = i.internalPointer()
level = item.level()
if not level in levels:
levels[level] = []
levels[level].append([i.row(), i])
# Sort by level then by row
for l in reversed(sorted(levels.keys())):
rows = levels[l]
rows = list(reversed(sorted(rows, key=lambda x: x[0])))
for r in rows:
self.removeIndex(r[1])
def removeRow(self, row, parent=QModelIndex()):
return self.removeRows(row, 1, parent)
def removeRows(self, row, count, parent=QModelIndex()):
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
self._removingRows = True # Views that are updating can easily know
# if this is due to row removal.
self.beginRemoveRows(parent, row, row + count - 1)
for i in range(count):
item = parentItem.removeChild(row)
self.removed.append(item)
self._removingRows = False
self.endRemoveRows()
return True
# def insertRow(self, row, item, parent=QModelIndex()):
# self.beginInsertRows(parent, row, row)
# if not parent.isValid():
# parentItem = self.rootItem
# else:
# parentItem = parent.internalPointer()
# parentItem.insertChild(row, item)
# self.endInsertRows()
################# XML / saving / loading #################
def saveToXML(self, xml=None):
"If xml (filename) is given, saves the items to xml. Otherwise returns as string."
root = ET.XML(self.rootItem.toXML())
if xml:
ET.ElementTree(root).write(xml, encoding="UTF-8", xml_declaration=True, pretty_print=True)
else:
return ET.tostring(root, encoding="UTF-8", xml_declaration=True, pretty_print=True)
def loadFromXML(self, xml, fromString=False):
"Load from xml. Assume that xml is a filename. If fromString=True, xml is the content."
if not fromString:
root = ET.parse(xml)
else:
root = ET.fromstring(xml)
self.rootItem = outlineItem(model=self, xml=ET.tostring(root), ID="0")
self.rootItem.checkIDs()
def indexFromPath(self, path):
path = path.split(",")
item = self.rootItem
for p in path:
if p != "" and int(p) < item.childCount():
item = item.child(int(p))
return self.indexFromItem(item)

View file

@ -296,4 +296,4 @@ class CharacterInfo():
def __init__(self, character, description="", value=""):
self.description = description
self.value = value
self.character = character
self.character = character

View file

@ -0,0 +1,497 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
import time
import locale
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QIcon
from PyQt5.QtWidgets import qApp
from lxml import etree as ET
from manuskript.models.abstractItem import abstractItem
from manuskript import enums
from manuskript import functions as F
from manuskript import settings
from manuskript.converters import HTML2PlainText
try:
locale.setlocale(locale.LC_ALL, '')
except:
# Invalid locale, but not really a big deal because it's used only for
# number formating
pass
class outlineItem(abstractItem):
enum = enums.Outline
# Used for XML export
name = "outlineItem"
def __init__(self, model=None, title="", _type="folder", xml=None, parent=None, ID=None):
abstractItem.__init__(self, model, title, _type, xml, parent, ID)
self.defaultTextType = None
if not self._data.get(self.enum.compile):
self._data[self.enum.compile] = 2
#######################################################################
# Properties
#######################################################################
def isFolder(self):
return self._data[self.enum.type] == "folder"
def isText(self):
return self._data[self.enum.type] == "md"
def isMD(self):
return self._data[self.enum.type] == "md"
def isMMD(self):
return self._data[self.enum.type] == "md"
def text(self):
return self.data(self.enum.text)
def compile(self):
if self._data.get(self.enum.compile, 1) in ["0", 0]:
return False
elif self.parent():
return self.parent().compile()
else:
return True # rootItem always compile
def POV(self):
return self.data(self.enum.POV)
def status(self):
return self.data(self.enum.status)
def label(self):
return self.data(self.enum.label)
def customIcon(self):
return self.data(self.enum.customIcon)
def setCustomIcon(self, customIcon):
self.setData(self.enum.customIcon, customIcon)
def wordCount(self):
return self._data.get(self.enum.wordCount, 0)
#######################################################################
# Data
#######################################################################
def data(self, column, role=Qt.DisplayRole):
data = abstractItem.data(self, column, role)
E = self.enum
if role == Qt.DisplayRole or role == Qt.EditRole:
if data == "" and column == E.revisions:
return []
else:
return data
elif role == Qt.DecorationRole and column == E.title:
if self.customIcon():
return QIcon.fromTheme(self.data(E.customIcon))
if self.isFolder():
return QIcon.fromTheme("folder")
elif self.isText():
return QIcon.fromTheme("text-x-generic")
elif role == Qt.CheckStateRole and column == E.compile:
return Qt.Checked if self.compile() else Qt.Unchecked
elif role == Qt.FontRole:
f = QFont()
if column == E.wordCount and self.isFolder():
f.setItalic(True)
elif column == E.goal and self.isFolder() and not self.data(E.setGoal):
f.setItalic(True)
if self.isFolder():
f.setBold(True)
return f
def setData(self, column, data, role=Qt.DisplayRole):
E = self.enum
if column == E.text and self.isFolder():
# Folder have no text
return
if column == E.goal:
self._data[E.setGoal] = F.toInt(data) if F.toInt(data) > 0 else ""
# Checking if we will have to recount words
updateWordCount = False
if column in [E.wordCount, E.goal, E.setGoal]:
updateWordCount = not column in self._data or self._data[column] != data
# Stuff to do before
if column == E.text:
self.addRevision()
# Calling base class implementation
abstractItem.setData(self, column, data, role)
# Stuff to do afterwards
if column == E.text:
wc = F.wordCount(data)
self.setData(E.wordCount, wc)
if column == E.compile:
# Title changes when compile changes
self.emitDataChanged(cols=[E.title, E.compile],
recursive=True)
if column == E.customIcon:
# If custom icon changed, we tell views to update title (so that
# icons will be updated as well)
self.emitDataChanged(cols=[E.title])
if updateWordCount:
self.updateWordCount()
#######################################################################
# Wordcount
#######################################################################
def insertChild(self, row, child):
abstractItem.insertChild(self, row, child)
self.updateWordCount()
def removeChild(self, row):
r = abstractItem.removeChild(self, row)
# Might be causing segfault when updateWordCount emits dataChanged
self.updateWordCount(emit=False)
return r
def updateWordCount(self, emit=True):
"""Update word count for item and parents.
If emit is False, no signal is emitted (sometimes cause segfault)"""
if not self.isFolder():
setGoal = F.toInt(self.data(self.enum.setGoal))
goal = F.toInt(self.data(self.enum.goal))
if goal != setGoal:
self._data[self.enum.goal] = setGoal
if setGoal:
wc = F.toInt(self.data(self.enum.wordCount))
self.setData(self.enum.goalPercentage, wc / float(setGoal))
else:
wc = 0
for c in self.children():
wc += F.toInt(c.data(self.enum.wordCount))
self._data[self.enum.wordCount] = wc
setGoal = F.toInt(self.data(self.enum.setGoal))
goal = F.toInt(self.data(self.enum.goal))
if setGoal:
if goal != setGoal:
self._data[self.enum.goal] = setGoal
goal = setGoal
else:
goal = 0
for c in self.children():
goal += F.toInt(c.data(self.enum.goal))
self._data[self.enum.goal] = goal
if goal:
self.setData(self.enum.goalPercentage, wc / float(goal))
else:
self.setData(self.enum.goalPercentage, "")
if emit:
self.emitDataChanged([self.enum.goal, self.enum.setGoal,
self.enum.wordCount, self.enum.goalPercentage])
if self.parent():
self.parent().updateWordCount(emit)
def stats(self):
wc = self.data(enums.Outline.wordCount)
goal = self.data(enums.Outline.goal)
progress = self.data(enums.Outline.goalPercentage)
if not wc:
wc = 0
if goal:
return qApp.translate("outlineItem", "{} words / {} ({})").format(
locale.format("%d", wc, grouping=True),
locale.format("%d", goal, grouping=True),
"{}%".format(str(int(progress * 100))))
else:
return qApp.translate("outlineItem", "{} words").format(
locale.format("%d", wc, grouping=True))
#######################################################################
# Tools: split and merge
#######################################################################
def split(self, splitMark, recursive=True):
"""
Split scene at splitMark. If multiple splitMark, multiple splits.
If called on a folder and recursive is True, then it is recursively
applied to every children.
"""
if self.isFolder() and recursive:
for c in self.children():
c.split(splitMark)
else:
txt = self.text().split(splitMark)
if len(txt) == 1:
# Mark not found
return False
else:
# Stores the new text
self.setData(self.enum.text, txt[0])
k = 1
for subTxt in txt[1:]:
# Create a copy
item = self.copy()
# Change title adding _k
item.setData(self.enum.title,
"{}_{}".format(item.title(), k+1))
# Set text
item.setData(self.enum.text, subTxt)
# Inserting item
#self.parent().insertChild(self.row()+k, item)
self._model.insertItem(item, self.row()+k, self.parent().index())
k += 1
def splitAt(self, position, length=0):
"""
Splits note at position p.
If length is bigger than 0, it describes the length of the title, made
from the character following position.
"""
txt = self.text()
# Stores the new text
self.setData(self.enum.text, txt[:position])
# Create a copy
item = self.copy()
# Update title
if length > 0:
title = txt[position:position+length].replace("\n", "")
else:
title = "{}_{}".format(item.title(), 2)
item.setData(self.enum.title, title)
# Set text
item.setData(self.enum.text, txt[position+length:])
# Inserting item using the model to signal views
self._model.insertItem(item, self.row()+1, self.parent().index())
def mergeWith(self, items, sep="\n\n"):
"""
Merges item with several other items. Merge is basic, it merges only
the text.
@param items: list of `outlineItem`s.
@param sep: a text added between each item's text.
"""
# Merges the texts
text = [self.text()]
text.extend([i.text() for i in items])
self.setData(self.enum.text, sep.join(text))
# Removes other items
self._model.removeIndexes([i.index() for i in items])
#######################################################################
# Search
#######################################################################
def findItemsByPOV(self, POV):
"Returns a list of IDs of all subitems whose POV is ``POV``."
lst = []
if self.POV() == POV:
lst.append(self.ID())
for c in self.children():
lst.extend(c.findItemsByPOV(POV))
return lst
def findItemsContaining(self, text, columns, mainWindow=F.mainWindow(),
caseSensitive=False, recursive=True):
"""Returns a list if IDs of all subitems
containing ``text`` in columns ``columns``
(being a list of int).
"""
lst = self.itemContains(text, columns, mainWindow, caseSensitive)
if recursive:
for c in self.children():
lst.extend(c.findItemsContaining(text, columns, mainWindow, caseSensitive))
return lst
def itemContains(self, text, columns, mainWindow=F.mainWindow(),
caseSensitive=False):
lst = []
text = text.lower() if not caseSensitive else text
for c in columns:
if c == self.enum.POV and self.POV():
c = mainWindow.mdlCharacter.getCharacterByID(self.POV())
if c:
searchIn = c.name()
else:
searchIn = ""
print("Character POV not found:", self.POV())
elif c == self.enum.status:
searchIn = mainWindow.mdlStatus.item(F.toInt(self.status()), 0).text()
elif c == self.enum.label:
searchIn = mainWindow.mdlLabels.item(F.toInt(self.label()), 0).text()
else:
searchIn = self.data(c)
searchIn = searchIn.lower() if not caseSensitive else searchIn
if text in searchIn:
if not self.ID() in lst:
lst.append(self.ID())
return lst
###############################################################################
# REVISIONS
###############################################################################
def revisions(self):
return self.data(self.enum.revisions)
def appendRevision(self, ts, text):
if not self.enum.revisions in self._data:
self._data[self.enum.revisions] = []
self._data[self.enum.revisions].append((
int(ts),
text))
def addRevision(self):
if not settings.revisions["keep"]:
return
if not self.enum.text in self._data:
return
self.appendRevision(
time.time(),
self.text())
if settings.revisions["smartremove"]:
self.cleanRevisions()
self.emitDataChanged([self.enum.revisions])
def deleteRevision(self, ts):
self._data[self.enum.revisions] = [r for r in self._data[self.enum.revisions] if r[0] != ts]
self.emitDataChanged([self.enum.revisions])
def clearAllRevisions(self):
self._data[self.enum.revisions] = []
self.emitDataChanged([self.enum.revisions])
def cleanRevisions(self):
"Keep only one some the revisions."
rev = self.revisions()
rev2 = []
now = time.time()
rule = settings.revisions["rules"]
revs = {}
for i in rule:
revs[i] = []
for r in rev:
# Have to put the lambda key otherwise cannot order when one element is None
for span in sorted(rule, key=lambda x: x if x else 60 * 60 * 24 * 30 * 365):
if not span or now - r[0] < span:
revs[span].append(r)
break
for span in revs:
sortedRev = sorted(revs[span], key=lambda x: x[0])
last = None
for r in sortedRev:
if not last:
rev2.append(r)
last = r[0]
elif r[0] - last >= rule[span]:
rev2.append(r)
last = r[0]
if rev2 != rev:
self._data[self.enum.revisions] = rev2
self.emitDataChanged([self.enum.revisions])
#######################################################################
# XML
#######################################################################
# We don't want to write some datas (computed)
XMLExclude = [enums.Outline.wordCount,
enums.Outline.goal,
enums.Outline.goalPercentage,
enums.Outline.revisions]
# We want to force some data even if they're empty
XMLForce = [enums.Outline.compile]
def toXMLProcessItem(self, item):
# Saving revisions
rev = self.revisions()
for r in rev:
revItem = ET.Element("revision")
revItem.set("timestamp", str(r[0]))
revItem.set("text", r[1])
item.append(revItem)
return item
def setFromXMLProcessMore(self, root):
# If loading from an old file format, convert to md and
# remove html markup
if self.type() in ["txt", "t2t"]:
self.setData(Outline.type, "md")
elif self.type() == "html":
self.setData(Outline.type, "md")
self.setData(Outline.text, HTML2PlainText(self.data(Outline.text)))
self.setData(Outline.notes, HTML2PlainText(self.data(Outline.notes)))
# Revisions
for child in root:
if child.tag == "revision":
self.appendRevision(child.attrib["timestamp"], child.attrib["text"])

File diff suppressed because it is too large Load diff

View file

@ -28,8 +28,8 @@ class plotModel(QStandardItemModel):
def getPlotsByImportance(self):
plots = [[], [], []]
for i in range(self.rowCount()):
importance = self.item(i, Plot.importance.value).text()
ID = self.item(i, Plot.ID.value).text()
importance = self.item(i, Plot.importance).text()
ID = self.item(i, Plot.ID).text()
plots[2 - toInt(importance)].append(ID)
return plots
@ -37,23 +37,23 @@ class plotModel(QStandardItemModel):
index = self.getIndexFromID(ID)
if not index.isValid():
return
index = index.sibling(index.row(), Plot.steps.value)
index = index.sibling(index.row(), Plot.steps)
item = self.itemFromIndex(index)
lst = []
for i in range(item.rowCount()):
if item.child(i, PlotStep.ID.value):
_ID = item.child(i, PlotStep.ID.value).text()
if item.child(i, PlotStep.ID):
_ID = item.child(i, PlotStep.ID).text()
# Don't know why sometimes name is None (while drag'n'droping
# several items)
if item.child(i, PlotStep.name.value):
name = item.child(i, PlotStep.name.value).text()
if item.child(i, PlotStep.name):
name = item.child(i, PlotStep.name).text()
else:
name = ""
# Don't know why sometimes summary is None
if item.child(i, PlotStep.summary.value):
summary = item.child(i, PlotStep.summary.value).text()
if item.child(i, PlotStep.summary):
summary = item.child(i, PlotStep.summary).text()
else:
summary = ""
@ -62,17 +62,17 @@ class plotModel(QStandardItemModel):
def getPlotNameByID(self, ID):
for i in range(self.rowCount()):
_ID = self.item(i, Plot.ID.value).text()
_ID = self.item(i, Plot.ID).text()
if _ID == ID or toInt(_ID) == ID:
name = self.item(i, Plot.name.value).text()
name = self.item(i, Plot.name).text()
return name
return None
def getPlotImportanceByID(self, ID):
for i in range(self.rowCount()):
_ID = self.item(i, Plot.ID.value).text()
_ID = self.item(i, Plot.ID).text()
if _ID == ID or toInt(_ID) == ID:
importance = self.item(i, Plot.importance.value).text()
importance = self.item(i, Plot.importance).text()
return importance
return "0" # Default to "Minor"
@ -81,13 +81,13 @@ class plotModel(QStandardItemModel):
is ``subplotRaw``, of plot whose ID is ``plotID``.
"""
plotIndex = self.getIndexFromID(plotID)
name = plotIndex.child(subplotRaw, PlotStep.name.value).data()
summary = plotIndex.child(subplotRaw, PlotStep.summary.value).data()
name = plotIndex.child(subplotRaw, PlotStep.name).data()
summary = plotIndex.child(subplotRaw, PlotStep.summary).data()
return name, summary
def getIndexFromID(self, ID):
for i in range(self.rowCount()):
_ID = self.item(i, Plot.ID.value).text()
_ID = self.item(i, Plot.ID).text()
if _ID == ID or toInt(_ID) == ID:
return self.index(i, 0)
return QModelIndex()
@ -115,8 +115,8 @@ class plotModel(QStandardItemModel):
parentItem = self.itemFromIndex(parent)
vals = []
for i in range(self.rowCount(parent)):
index = self.index(i, Plot.ID.value, parent)
# item = self.item(i, Plot.ID.value)
index = self.index(i, Plot.ID, parent)
# item = self.item(i, Plot.ID)
if index.isValid() and index.data():
vals.append(int(index.data()))
@ -135,9 +135,9 @@ class plotModel(QStandardItemModel):
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
if section == PlotStep.name.value:
if section == PlotStep.name:
return self.tr("Name")
elif section == PlotStep.meta.value:
elif section == PlotStep.meta:
return self.tr("Meta")
else:
return ""
@ -148,8 +148,8 @@ class plotModel(QStandardItemModel):
def data(self, index, role=Qt.DisplayRole):
if index.parent().isValid() and \
index.parent().column() == Plot.steps.value and \
index.column() == PlotStep.meta.value:
index.parent().column() == Plot.steps and \
index.column() == PlotStep.meta:
if role == Qt.TextAlignmentRole:
return Qt.AlignRight | Qt.AlignVCenter
elif role == Qt.ForegroundRole:
@ -165,8 +165,8 @@ class plotModel(QStandardItemModel):
if not index.isValid():
return
parent = index.sibling(index.row(), Plot.steps.value)
parentItem = self.item(index.row(), Plot.steps.value)
parent = index.sibling(index.row(), Plot.steps)
parentItem = self.item(index.row(), Plot.steps)
if not parentItem:
return
@ -216,10 +216,10 @@ class plotModel(QStandardItemModel):
def addPlotPerso(self, v):
index = self.mw.lstPlots.currentPlotIndex()
if index.isValid():
if not self.item(index.row(), Plot.characters.value):
self.setItem(index.row(), Plot.characters.value, QStandardItem())
if not self.item(index.row(), Plot.characters):
self.setItem(index.row(), Plot.characters, QStandardItem())
item = self.item(index.row(), Plot.characters.value)
item = self.item(index.row(), Plot.characters)
# We check that the PersoID is not in the list yet
for i in range(item.rowCount()):

View file

@ -72,7 +72,7 @@ class plotsProxyModel(QSortFilterProxyModel):
self.mapModel()
def mapModelMaybe(self, topLeft, bottomRight):
if topLeft.column() <= Plot.importance.value <= bottomRight.column():
if topLeft.column() <= Plot.importance <= bottomRight.column():
self.mapModel()
def mapModel(self):
@ -85,7 +85,7 @@ class plotsProxyModel(QSortFilterProxyModel):
self._map.append(self._cats[i])
for p in range(src.rowCount()):
item = src.item(p, Plot.importance.value)
item = src.item(p, Plot.importance)
if item:
imp = int(item.text())

View file

@ -140,9 +140,9 @@ def infos(ref):
path = " > ".join(pathStr)
# Summaries and notes
ss = item.data(Outline.summarySentence.value)
ls = item.data(Outline.summaryFull.value)
notes = item.data(Outline.notes.value)
ss = item.data(Outline.summarySentence)
ls = item.data(Outline.summaryFull)
notes = item.data(Outline.notes)
text = """<h1>{title}</h1>
<p><b>{pathTitle}</b> {path}</p>
@ -187,6 +187,9 @@ def infos(ref):
elif _type == CharacterLetter:
m = mainWindow().mdlCharacter
c = m.getCharacterByID(int(_ref))
if c is None:
return qApp.translate("references", "Unknown reference: {}.").format(ref)
index = c.index()
name = c.name()
@ -237,7 +240,7 @@ def infos(ref):
idx = oM.getIndexByID(t)
listPOV += "<li><a href='{link}'>{text}</a></li>".format(
link=textReference(t),
text=oM.data(idx, Outline.title.value))
text=oM.data(idx, Outline.title))
text = """<h1>{name}</h1>
{goto}
@ -267,6 +270,9 @@ def infos(ref):
index = m.getIndexFromID(_ref)
name = m.getPlotNameByID(_ref)
if not index.isValid():
return qApp.translate("references", "Unknown reference: {}.").format(ref)
# Titles
descriptionTitle = qApp.translate("references", "Description")
resultTitle = qApp.translate("references", "Result")
@ -279,15 +285,15 @@ def infos(ref):
# Description
description = m.data(index.sibling(index.row(),
Plot.description.value))
Plot.description))
# Result
result = m.data(index.sibling(index.row(),
Plot.result.value))
Plot.result))
# Characters
pM = mainWindow().mdlCharacter
item = m.item(index.row(), Plot.characters.value)
item = m.item(index.row(), Plot.characters)
characters = ""
if item:
for r in range(item.rowCount()):
@ -298,12 +304,12 @@ def infos(ref):
# Resolution steps
steps = ""
item = m.item(index.row(), Plot.steps.value)
item = m.item(index.row(), Plot.steps)
if item:
for r in range(item.rowCount()):
title = item.child(r, PlotStep.name.value).text()
summary = item.child(r, PlotStep.summary.value).text()
meta = item.child(r, PlotStep.meta.value).text()
title = item.child(r, PlotStep.name).text()
summary = item.child(r, PlotStep.summary).text()
meta = item.child(r, PlotStep.meta).text()
if meta:
meta = " <span style='color:gray;'>({})</span>".format(meta)
steps += "<li><b>{title}</b>{summary}{meta}</li>".format(
@ -343,6 +349,9 @@ def infos(ref):
index = m.indexByID(_ref)
name = m.name(index)
if not index.isValid():
return qApp.translate("references", "Unknown reference: {}.").format(ref)
# Titles
descriptionTitle = qApp.translate("references", "Description")
passionTitle = qApp.translate("references", "Passion")
@ -532,7 +541,9 @@ def refToLink(ref):
elif _type == CharacterLetter:
m = mainWindow().mdlCharacter
text = m.getCharacterByID(int(_ref)).name()
c = m.getCharacterByID(int(_ref))
if c:
text = c.name()
elif _type == PlotLetter:
m = mainWindow().mdlPlots
@ -540,7 +551,9 @@ def refToLink(ref):
elif _type == WorldLetter:
m = mainWindow().mdlWorld
text = m.itemByID(_ref).text()
item = m.itemByID(_ref)
if item:
text = item.text()
if text:
return "<a href='{ref}'>{text}</a>".format(
@ -549,12 +562,10 @@ def refToLink(ref):
else:
return ref
def linkifyAllRefs(text):
"""Takes all the references in ``text`` and transform them into HMTL links."""
return re.sub(RegEx, lambda m: refToLink(m.group(0)), text)
def findReferencesTo(ref, parent=None, recursive=True):
"""List of text items containing references ref, and returns IDs.
Starts from item parent. If None, starts from root."""
@ -570,8 +581,8 @@ def findReferencesTo(ref, parent=None, recursive=True):
ref2 = ref[:-1] + "}"
# Since it's a simple search (no regex), we search for both.
lst = parent.findItemsContaining(ref, [Outline.notes.value], recursive=recursive)
lst += parent.findItemsContaining(ref2, [Outline.notes.value], recursive=recursive)
lst = parent.findItemsContaining(ref, [Outline.notes], recursive=recursive)
lst += parent.findItemsContaining(ref2, [Outline.notes], recursive=recursive)
return lst
@ -585,13 +596,12 @@ def listReferences(ref, title=qApp.translate("references", "Referenced in:")):
idx = oM.getIndexByID(t)
listRefs += "<li><a href='{link}'>{text}</a></li>".format(
link=textReference(t),
text=oM.data(idx, Outline.title.value))
text=oM.data(idx, Outline.title))
return "<h2>{title}</h2><ul>{ref}</ul>".format(
title=title,
ref=listRefs) if listRefs else ""
def basicFormat(text):
if not text:
return ""
@ -599,7 +609,6 @@ def basicFormat(text):
text = linkifyAllRefs(text)
return text
def open(ref):
"""Identify ``ref`` and open it."""
match = re.fullmatch(RegEx, ref)

View file

@ -46,24 +46,24 @@ class worldModel(QStandardItemModel):
def ID(self, index):
"""Returns the ID of the given index."""
index = index.sibling(index.row(), World.ID.value)
index = index.sibling(index.row(), World.ID)
return self.data(index)
def name(self, index):
"""Returns the name of the given index."""
index = index.sibling(index.row(), World.name.value)
index = index.sibling(index.row(), World.name)
return self.data(index)
def description(self, index):
index = index.sibling(index.row(), World.description.value)
index = index.sibling(index.row(), World.description)
return self.data(index)
def conflict(self, index):
index = index.sibling(index.row(), World.conflict.value)
index = index.sibling(index.row(), World.conflict)
return self.data(index)
def passion(self, index):
index = index.sibling(index.row(), World.passion.value)
index = index.sibling(index.row(), World.passion)
return self.data(index)
def itemID(self, item):

View file

@ -46,9 +46,9 @@ autoSaveDelay = 5
autoSaveNoChanges = True
autoSaveNoChangesDelay = 5
saveOnQuit = True
outlineViewColumns = [Outline.title.value, Outline.POV.value, Outline.status.value,
Outline.compile.value, Outline.wordCount.value, Outline.goal.value,
Outline.goalPercentage.value, Outline.label.value]
outlineViewColumns = [Outline.title, Outline.POV, Outline.status,
Outline.compile, Outline.wordCount, Outline.goal,
Outline.goalPercentage, Outline.label]
corkBackground = {
"color": "#926239",
"image": "writingdesk"

View file

@ -65,7 +65,9 @@ class settingsWindow(QWidget, Ui_Settings):
# General
self.cmbStyle.addItems(list(QStyleFactory.keys()))
self.cmbStyle.setCurrentIndex([i.lower() for i in list(QStyleFactory.keys())].index(qApp.style().objectName()))
self.cmbStyle.setCurrentIndex(
[i.lower() for i in list(QStyleFactory.keys())]
.index(qApp.style().objectName()))
self.cmbStyle.currentIndexChanged[str].connect(self.setStyle)
self.cmbTranslation.clear()
@ -75,13 +77,18 @@ class settingsWindow(QWidget, Ui_Settings):
tr["Español"] = "manuskript_es.qm"
tr["Deutsch"] = "manuskript_de.qm"
tr["Svenska"] = "manuskript_sv.qm"
self.translations = tr
for name in tr:
self.cmbTranslation.addItem(name, tr[name])
sttgs = QSettings(qApp.organizationName(), qApp.applicationName())
if sttgs.contains("applicationTranslation") and sttgs.value("applicationTranslation") in tr.values():
self.cmbTranslation.setCurrentText([i for i in tr if tr[i] == sttgs.value("applicationTranslation")][0])
if (sttgs.contains("applicationTranslation")
and sttgs.value("applicationTranslation") in tr.values()):
# Sets the correct translation
self.cmbTranslation.setCurrentText(
[i for i in tr
if tr[i] == sttgs.value("applicationTranslation")][0])
self.cmbTranslation.currentIndexChanged.connect(self.setTranslation)
@ -355,14 +362,14 @@ class settingsWindow(QWidget, Ui_Settings):
def outlineColumnsData(self):
return {
self.chkOutlineTitle: Outline.title.value,
self.chkOutlinePOV: Outline.POV.value,
self.chkOutlineLabel: Outline.label.value,
self.chkOutlineStatus: Outline.status.value,
self.chkOutlineCompile: Outline.compile.value,
self.chkOutlineWordCount: Outline.wordCount.value,
self.chkOutlineGoal: Outline.goal.value,
self.chkOutlinePercentage: Outline.goalPercentage.value,
self.chkOutlineTitle: Outline.title,
self.chkOutlinePOV: Outline.POV,
self.chkOutlineLabel: Outline.label,
self.chkOutlineStatus: Outline.status,
self.chkOutlineCompile: Outline.compile,
self.chkOutlineWordCount: Outline.wordCount,
self.chkOutlineGoal: Outline.goal,
self.chkOutlinePercentage: Outline.goalPercentage,
}
def outlineColumnsChanged(self):
@ -571,8 +578,8 @@ class settingsWindow(QWidget, Ui_Settings):
# px = QPixmap(64, 64)
# px.fill(iconColor(self.mw.mdlLabels.item(index.row()).icon()))
# self.btnLabelColor.setIcon(QIcon(px))
self.btnLabelColor.setStyleSheet(
"background:{};".format(iconColor(self.mw.mdlLabels.item(index.row()).icon()).name()))
self.btnLabelColor.setStyleSheet("background:{};".format(
iconColor(self.mw.mdlLabels.item(index.row()).icon()).name()))
self.btnLabelColor.setEnabled(True)
def addLabel(self):

View file

@ -0,0 +1,41 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests."""
# METHOD 1
# ========
# Don't know why, this causes seg fault on SemaphoreCI
# Seg fault in app = QApplication(...)
# Workaround: create and discard an app first...
from PyQt5.QtWidgets import QApplication
QApplication([])
# Create app and mainWindow
from manuskript import main
app, MW = main.prepare(tests=True)
# FIXME: Again, don't know why, but when closing a project and then reopening
# one, we get a `TypeError: connection is not unique` in MainWindow:
# self.btnAddSubPlot.clicked.connect(self.updateSubPlotView, F.AUC)
# Yet the disconnectAll() function has been called.
# Workaround: we remove the necessity for connection to be unique. This
# works for now, but could create issues later one when we want to tests
# those specific functionnality. Maybe it will be called several times.
# At that moment, we will need to catch the exception in the MainWindow,
# or better: understand why it happens at all, and only on some signals.
from manuskript import functions as F
from PyQt5.QtCore import Qt
F.AUC = Qt.AutoConnection
# METHOD 2
# ========
# We need a qApplication to be running, or all the calls to qApp
# will throw a seg fault.
# from PyQt5.QtWidgets import QApplication
# app = QApplication([])
# app.setOrganizationName("manuskript_tests")
# app.setApplicationName("manuskript_tests")
# from manuskript.mainWindow import MainWindow
# MW = MainWindow()

View file

@ -0,0 +1,73 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Fixtures."""
import pytest
@pytest.fixture
def MW():
"""
Returns the mainWindow
"""
from manuskript import functions as F
MW = F.mainWindow()
assert MW is not None
assert MW == F.MW
return MW
@pytest.fixture
def MWNoProject(MW):
"""
Take the MainWindow and close andy possibly open project.
"""
MW.closeProject()
assert MW.currentProject is None
return MW
@pytest.fixture
def MWEmptyProject(MW):
"""
Creates a MainWindow and load an empty project.
"""
import tempfile
tf = tempfile.NamedTemporaryFile(suffix=".msk")
MW.closeProject()
assert MW.currentProject is None
MW.welcome.createFile(tf.name, overwrite=True)
assert MW.currentProject is not None
return MW
# If using with: @pytest.fixture(scope='session', autouse=True)
# yield MW
# # Properly destructed after. Otherwise: seg fault.
# MW.deleteLater()
@pytest.fixture
def MWSampleProject(MW):
"""
Creates a MainWindow and load a copy of the Acts sample project.
"""
from manuskript import functions as F
import os
# Get the path of the first sample project. We assume it is here.
spDir = F.appPath("sample-projects")
lst = os.listdir(spDir)
# We assume it's saved in folder, so there is a `name.msk` file and a
# `name` folder.
src = [f for f in lst if f[-4:] == ".msk" and f[:-4] in lst][0]
src = os.path.join(spDir, src)
# Copy to a temp file
import tempfile
tf = tempfile.NamedTemporaryFile(suffix=".msk")
import shutil
shutil.copyfile(src, tf.name)
shutil.copytree(src[:-4], tf.name[:-4])
MW.closeProject()
MW.loadProject(tf.name)
assert MW.currentProject is not None
return MW

View file

@ -0,0 +1,4 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for models."""

View file

@ -0,0 +1,23 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Conf for models."""
import pytest
@pytest.fixture
def outlineModelBasic(MWEmptyProject):
"""Returns an outlineModel with a few items:
* Folder
* Text
* Text
"""
from manuskript.models import outlineItem
mdl = MWEmptyProject.mdlOutline
root = mdl.rootItem
f = outlineItem(title="Folder", parent=root)
t1 = outlineItem(title="Text", _type="md", parent=f)
t2 = outlineItem(title="Text", _type="md", parent=root)
return mdl

View file

@ -0,0 +1,161 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for outlineItem"""
import pytest
@pytest.fixture
def outlineItemFolder():
'''Returns a folder outlineItem title "Folder".'''
from manuskript.models import outlineItem
return outlineItem(title="Folder")
@pytest.fixture
def outlineItemText():
'''Returns a text outlineItem title "Text".'''
from manuskript.models import outlineItem
return outlineItem(title="Text", _type="md")
def test_outlineItemsProperties(outlineItemFolder, outlineItemText):
"""
Tests with simple items, without parent or models.
"""
from PyQt5.QtCore import Qt
# Simplification
folder = outlineItemFolder
text = outlineItemText
# getters
assert folder.isFolder() == True
assert text.isFolder() == False
assert text.isText() == True
assert text.isMD() == text.isMMD() == True
assert text.title() == "Text"
assert text.compile() == True
assert folder.POV() == ""
assert folder.status() == ""
assert folder.label() == ""
assert folder.customIcon() == ""
assert folder.data(42) == ""
assert folder.data(folder.enum.title, role=Qt.CheckStateRole) == None
# setData and other setters
assert text.data(text.enum.compile, role=Qt.CheckStateRole) == Qt.Checked
text.setData(text.enum.compile, 0)
assert text.compile() == False
assert text.data(text.enum.compile, role=Qt.CheckStateRole) == Qt.Unchecked
folder.setCustomIcon("custom")
assert folder.customIcon() == "custom"
folder.setData(folder.enum.text, "Some text")
assert folder.text() == "" # folders have no text
# wordCount
text.setData(text.enum.text, "Sample **text**.")
assert text.wordCount() == 2
text.setData(text.enum.goal, 4)
assert text.data(text.enum.goalPercentage) == .5
# revisions
assert text.data(text.enum.revisions) == []
def test_modelStuff(outlineModelBasic):
"""
Tests with children items.
"""
# Simplification
model = outlineModelBasic
# Child count
root = model.rootItem
assert len(root.children()) == 2
folder = root.child(0)
text1 = folder.child(0)
text2 = root.child(1)
# Compile
assert text1.compile() == True
folder.setData(folder.enum.compile, 0)
assert text1.compile() == False
# Word count
text1.setData(text1.enum.text, "Sample text.")
assert text1.wordCount() == 2
assert folder.wordCount() == 2
statsWithGoal = folder.stats()
assert statsWithGoal != ""
text1.setData(text1.enum.setGoal, 4)
assert folder.data(folder.enum.goal) == 4
folder.setData(folder.enum.setGoal, 3)
assert folder.data(folder.enum.goal) == 3
assert folder.stats() != statsWithGoal
# Split and merge
text1.setData(text1.enum.text, "Sample\n---\ntext.")
folder.split("invalid mark")
assert folder.childCount() == 1
folder.split("\n---\n")
assert folder.childCount() == 2
text1.mergeWith([folder.child(1)])
assert text1.text() == "Sample\n\ntext."
text1.setData(text1.enum.text, "Sample\nNewTitle\ntext.")
text1.splitAt(7, 8)
assert folder.child(1).title() == "NewTitle"
folder.child(1).splitAt(3)
assert folder.child(2).title() == "NewTitle_2"
folder.removeChild(2)
folder.removeChild(1)
folder.removeChild(0)
assert folder.childCount() == 0
# Search
folder.appendChild(text2)
text2.setData(text2.enum.POV, 1)
folder.setData(folder.enum.POV, 1)
assert len(folder.findItemsByPOV(1)) == 2
folder.setData(folder.enum.label, 1) # Idea
folder.setData(folder.enum.status, 4) # Final
text2.setData(text2.enum.text, "Some final value.")
from manuskript.functions import MW
cols = [folder.enum.text, folder.enum.POV,
folder.enum.label, folder.enum.status]
assert folder.findItemsContaining("VALUE", cols, MW, True) == []
assert folder.findItemsContaining("VALUE", cols, MW, False) == [text2.ID()]
# Revisions
text2.clearAllRevisions()
assert text2.revisions() == []
text2.setData(text2.enum.text, "Some value.")
assert len(text2.revisions()) == 1
text2.setData(text2.enum.text, "Some new value.")
assert len(text2.revisions()) == 1 # Auto clean
text2.deleteRevision(text2.revisions()[0][0])
assert len(text2.revisions()) == 0
# Model, count and copy
k = folder._model
folder.setModel(14)
assert text2._model == 14
folder.setModel(k)
assert folder.columnCount() == len(folder.enum)
text1 = text2.copy()
assert text1.ID() is None
folder.appendChild(text1)
assert text1.ID() is not None
assert folder.childCountRecursive() == 2
assert text1.path() == "Folder > Text"
assert len(text1.pathID()) == 2
# IDs
folder2 = folder.copy()
text3 = text1.copy()
text3.setData(text3.enum.ID, "0")
folder2.appendChild(text3)
folder.appendChild(folder2)
assert text3.ID() == "0"
root.checkIDs()
assert text3.ID() != "0"

View file

@ -0,0 +1,116 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for references.py"""
import pytest
def test_references(MWSampleProject):
"""
Tests references using sample project.
"""
from manuskript.models import references as Ref
MW = MWSampleProject
# References
ref1 = Ref.plotReference("42", searchable=True)
ref2 = Ref.plotReference("42")
assert ref1 in ref2
ref1 = Ref.characterReference("42", searchable=True)
ref2 = Ref.characterReference("42")
assert ref1 in ref2
ref1 = Ref.textReference("42", searchable=True)
ref2 = Ref.textReference("42")
assert ref1 in ref2
ref1 = Ref.worldReference("42", searchable=True)
ref2 = Ref.worldReference("42")
assert ref1 in ref2
# Plots
mdlPlots = MW.mdlPlots
plotsImp = mdlPlots.getPlotsByImportance()
plots = []
[plots.extend(i) for i in plotsImp]
assert len(plots) == 3
plotID = plots[0]
assert "\n" in Ref.infos(Ref.plotReference(plotID))
assert "Not a ref" in Ref.infos("<invalid>")
assert "Unknown" in Ref.infos(Ref.plotReference("999"))
assert Ref.shortInfos(Ref.plotReference(plotID)) is not None
assert Ref.shortInfos(Ref.plotReference("999")) == None
assert Ref.shortInfos("<invalidref>") == -1
# Character
mdlChar = MW.mdlCharacter
IDs = [mdlChar.ID(r) for r in range(mdlChar.rowCount())]
assert len(IDs) == 6 # Peter, Paul, Philip, Stephen, Barnabas, Herod
charID = IDs[0]
assert "\n" in Ref.infos(Ref.characterReference(charID))
assert "Unknown" in Ref.infos(Ref.characterReference("999"))
assert Ref.shortInfos(Ref.characterReference(charID)) is not None
assert Ref.shortInfos(Ref.characterReference("999")) == None
assert Ref.shortInfos("<invalidref>") == -1
# Texts
mdlOutline = MW.mdlOutline
assert mdlOutline.rowCount() == 3 # Jerusalem, Samaria, Extremities
root = mdlOutline.rootItem
textID = root.child(0).ID()
assert "\n" in Ref.infos(Ref.textReference(textID))
assert "Unknown" in Ref.infos(Ref.textReference("999"))
assert Ref.shortInfos(Ref.textReference(textID)) is not None
assert Ref.shortInfos(Ref.textReference("999")) == None
assert Ref.shortInfos("<invalidref>") == -1
# World
mdlWorld = MW.mdlWorld
assert mdlWorld.rowCount() == 3 # Places, Culture, Travel
worldID = mdlWorld.itemID(mdlWorld.item(2).child(1))
assert "\n" in Ref.infos(Ref.worldReference(worldID))
assert "Unknown" in Ref.infos(Ref.worldReference("999"))
assert Ref.shortInfos(Ref.worldReference(worldID)) is not None
assert Ref.shortInfos(Ref.worldReference("999")) == None
assert Ref.shortInfos("<invalidref>") == -1
refs = [Ref.plotReference(plotID),
Ref.characterReference(charID),
Ref.textReference(textID),
Ref.worldReference(worldID),]
# Titles
for ref in refs:
assert Ref.title(ref) is not None
assert Ref.title("<invalid>") is None
assert Ref.title(Ref.plotReference("999")) is None
# Other stuff
assert Ref.type(Ref.plotReference(plotID)) == Ref.PlotLetter
assert Ref.ID(Ref.textReference(textID)) == textID
assert "Unknown" in Ref.tooltip(Ref.worldReference("999"))
assert "Not a ref" in Ref.tooltip("<invalid>")
for ref in refs:
assert Ref.tooltip(ref) is not None
# Links
assert Ref.refToLink("<invalid>") is None
assert Ref.refToLink(Ref.plotReference("999")) == Ref.plotReference("999")
assert Ref.refToLink(Ref.characterReference("999")) == Ref.characterReference("999")
assert Ref.refToLink(Ref.textReference("999")) == Ref.textReference("999")
assert Ref.refToLink(Ref.worldReference("999")) == Ref.worldReference("999")
for ref in refs:
assert "<a href" in Ref.refToLink(ref)
# Open
assert Ref.open("<invalid>") is None
assert Ref.open(Ref.plotReference("999")) == False
assert Ref.open(Ref.characterReference("999")) == False
assert Ref.open(Ref.textReference("999")) == False
assert Ref.open(Ref.worldReference("999")) == False
for ref in refs:
assert Ref.open(ref) == True
assert Ref.open(Ref.EmptyRef.format("Z", 14, "")) == False

View file

@ -0,0 +1,96 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for functions"""
from manuskript import functions as F
def test_wordCount():
assert F.wordCount("In the beginning was the word.") == 6
assert F.wordCount("") == 0
def test_convert():
# toInt
assert F.toInt("9") == 9
assert F.toInt("a") == 0
assert F.toInt("") == 0
# toFloat
assert F.toFloat("9.4") == 9.4
assert F.toFloat("") == 0.
# toString
assert F.toString(None) == ""
assert F.toString("None") == ""
assert F.toString("Joy") == "Joy"
def test_several():
from PyQt5.QtGui import QPainter, QPixmap, QIcon, QColor
from PyQt5.QtCore import QRect
# drawProgress
px = QPixmap(10, 10)
F.drawProgress(QPainter(px), QRect(0, 0, 100, 100), 0.5)
# colorFromProgress
a = F.colorFromProgress(0.1)
b = F.colorFromProgress(0.5)
c = F.colorFromProgress(1.0)
d = F.colorFromProgress(1.5)
assert a != b != c != d
# iconColor & iconFromColor & iconFromColorString
icon = F.iconFromColorString("#ff0000")
assert F.iconColor(icon).name().lower() == "#ff0000"
# themeIcon
assert F.themeIcon("text") is not None
assert F.themeIcon("nonexistingname") is not None
# randomColor
c1 = F.randomColor()
c2 = F.randomColor(c1)
assert c1.name() != c2.name()
# mixColors
c1 = QColor("#FFF")
c2 = QColor("#000")
assert F.mixColors(c1, c2).name() == "#7f7f7f"
# colorifyPixmap
assert F.colorifyPixmap(px, c1) != None
def test_outlineItemColors():
from manuskript.models import outlineItem
item = outlineItem(title="Test")
r = F.outlineItemColors(item)
for i in ["POV", "Label", "Progress", "Compile"]:
assert i in r
from PyQt5.QtGui import QColor
assert r["Compile"].name(QColor.HexArgb) == "#00000000"
def test_paths():
assert F.appPath() is not None
assert F.writablePath is not None
assert len(F.allPaths("suffix")) == 2
assert F.tempFile("yop") is not None
f = F.findBackground("spacedreams.jpg")
assert "resources/backgrounds/spacedreams.jpg" in f
assert len(F.customIcons()) > 1
def test_mainWindow():
from PyQt5.QtWidgets import QWidget, QLCDNumber
assert F.mainWindow() is not None
assert F.MW is not None
F.statusMessage("Test")
F.printObjects()
assert len(F.findWidgetsOfClass(QWidget)) > 0
assert len(F.findWidgetsOfClass(QLCDNumber)) == 0

View file

@ -0,0 +1,164 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for settingsWindow"""
from manuskript import functions as F
def test_general(MWSampleProject):
MW = MWSampleProject
# Loading from mainWindow
MW.actSettings.triggered.emit()
assert MW.sw.isVisible()
MW.sw.close()
MW.actLabels.triggered.emit()
assert MW.sw.isVisible()
MW.sw.close()
MW.actStatus.triggered.emit()
assert MW.sw.isVisible()
MW.sw.hide()
MW.sw.setTab("General")
SW = MW.sw
# Imports
from PyQt5.QtWidgets import qApp, QStyleFactory
from PyQt5.QtCore import QSettings, Qt
qS = QSettings(qApp.organizationName(), qApp.applicationName())
from manuskript import settings as S
# Style
assert SW.cmbStyle.count() == len(list(QStyleFactory.keys()))
if qS.value("applicationStyle"):
assert SW.cmbStyle.currentText() == qS.value("applicationStyle")
## Seg fault when trying to set the style in tests:
# for s in styles:
# SW.cmbStyle.setCurrentText(s)
# assert S.value("applicationStyle") == s
# Translations
if qS.value("applicationTranslation"):
assert (SW.cmbTranslation.currentData()
== qS.value("applicationTranslation"))
for name in SW.translations:
SW.cmbTranslation.setCurrentText(name)
if qS.value("applicationTranslation"):
assert qS.value("applicationTranslation") == SW.translations[name]
def switchCheckBoxAndAssert(chk, settings):
"""
Asserts that the check state is that of settings, change checkstate
and asserts settings has been changed.
Settings is a function that returns the value.
"""
state = settings()
assert chk.isChecked() == state
chk.setChecked(not state)
assert chk.isChecked() is not state
# Loading and Saving
SW.txtAutoSave.setText("0")
SW.txtAutoSaveNoChanges.setText("0")
switchCheckBoxAndAssert(SW.chkAutoLoad,
lambda: qS.value("autoLoad", type=bool))
switchCheckBoxAndAssert(SW.chkAutoSave,
lambda: S.autoSave)
switchCheckBoxAndAssert(SW.chkAutoSaveNoChanges,
lambda: S.autoSaveNoChanges)
switchCheckBoxAndAssert(SW.chkSaveOnQuit,
lambda: S.saveOnQuit)
switchCheckBoxAndAssert(SW.chkSaveToZip,
lambda: S.saveToZip)
# Revisions
switchCheckBoxAndAssert(SW.chkRevisionsKeep,
lambda: S.revisions["keep"])
switchCheckBoxAndAssert(SW.chkRevisionRemove,
lambda: S.revisions["smartremove"])
# Views
# Simple way here, we just call the functions.
SW.cmbTreeIcon.currentIndexChanged.emit(0)
SW.cmbOutlineIcon.currentIndexChanged.emit(0)
SW.cmbCorkIcon.currentIndexChanged.emit(0)
SW.chkOutlineTitle.setChecked(Qt.Checked) #outlineColumnsChanged
SW.chkOutlineTitle.setChecked(Qt.Unchecked)
SW.chkOutlineTitle.setChecked(Qt.Checked)
# Can't test because of the dialog
# assert SW.setCorkColor() is None
SW.sldTreeIconSize.setValue(SW.sldTreeIconSize.value() + 1)
SW.rdoCorkNewStyle.toggled.emit(True)
SW.cmbCorkImage.currentIndexChanged.emit(0)
SW.cmbCorkImage.currentIndexChanged.emit(1)
# Test editor: same problem as above
# choseEditorFontColor
# choseEditorMisspelledColor
# choseEditorBackgroundColor
# Test editor
switchCheckBoxAndAssert(SW.chkEditorBackgroundTransparent,
lambda: S.textEditor["backgroundTransparent"])
assert SW.restoreEditorColors() is None
switchCheckBoxAndAssert(SW.chkEditorNoBlinking,
lambda: S.textEditor["cursorNotBlinking"])
# Twice on purpose: set and restore
switchCheckBoxAndAssert(SW.chkEditorNoBlinking,
lambda: S.textEditor["cursorNotBlinking"])
# Manually call updateAllWidgets, because other wise timer of 250ms
SW.updateAllWidgets()
# Labels
assert SW.updateLabelColor(MW.mdlLabels.item(1).index()) is None
rc = MW.mdlLabels.rowCount()
SW.addLabel()
SW.lstLabels.setCurrentIndex(
MW.mdlLabels.item(MW.mdlLabels.rowCount() - 1).index())
SW.removeLabel()
assert MW.mdlLabels.rowCount() == rc
# setLabelColor # Same problem as above
# Status
rc = MW.mdlStatus.rowCount()
SW.addStatus()
SW.lstStatus.setCurrentIndex(
MW.mdlStatus.item(MW.mdlStatus.rowCount() - 1).index())
SW.removeStatus()
assert MW.mdlStatus.rowCount() == rc
# Fullscreen
# self.lstThemes.currentItemChanged.connect(self.themeSelected)
item = SW.lstThemes.item(0)
SW.lstThemes.currentItemChanged.emit(item, None)
assert S.fullScreenTheme in item.data(Qt.UserRole)
SW.lstThemes.currentItemChanged.emit(None, None)
count = SW.lstThemes.count()
SW.newTheme()
assert SW.lstThemes.count() == count + 1
SW.newTheme() # theme with same name
item = SW.lstThemes.item(SW.lstThemes.count() - 1)
SW.lstThemes.setCurrentItem(item)
SW.removeTheme()
item = SW.lstThemes.item(count)
SW.lstThemes.setCurrentItem(item)
SW.editTheme()
switchCheckBoxAndAssert(SW.chkThemeIndent,
lambda: SW._themeData["Spacings/IndentFirstLine"])
SW.updateThemeFont(None)
SW.updateThemeBackground(0)
SW.updateThemeBackground(1)
SW.spnThemeLineSpacing.setValue(123)
for i in range(4):
SW.updateLineSpacing(i)
SW.updateUIFromTheme() # No time to wait on timer
assert SW._editingTheme is not None
SW.resize(SW.geometry().size()) # resizeEvent
#TODO: other edit test (see SW.loadTheme
SW.saveTheme()
item = SW.lstThemes.item(count)
SW.lstThemes.setCurrentItem(item)
SW.editTheme()
SW.cancelEdit()
item = SW.lstThemes.item(count)
SW.lstThemes.setCurrentItem(item)
SW.removeTheme()
assert SW.lstThemes.count() == count

View file

@ -0,0 +1,4 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for stuff in ui."""

View file

@ -0,0 +1,4 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for exporters, ui part."""

View file

@ -0,0 +1,27 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for settingsWindow"""
def test_loadExportWiget(MWSampleProject):
"""
Simply tests that export widget loads properly.
"""
MW = MWSampleProject
# Loading from mainWindow
MW.doCompile()
E = MW.dialog
assert E.isVisible()
E.hide()
# Load exporter manager
E.openManager()
EM = E.dialog
assert EM.isVisible()
EM.hide()
EM.close()
E.close()
#FIXME: test significant stuff

View file

@ -0,0 +1,4 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for importers, ui part."""

View file

@ -0,0 +1,19 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for settingsWindow"""
def test_loadImportWiget(MWSampleProject):
"""
Simply tests that import widget loads properly.
"""
MW = MWSampleProject
# Loading from mainWindow
MW.doImport()
I = MW.dialog
assert I.isVisible()
I.hide()
I.close()
#FIXME: test significant stuff

View file

@ -0,0 +1,22 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
"""Tests for the welcome widget."""
def test_autoLoad(MWNoProject):
"""
Tests for the welcome widget using MainWindow with no open project.
"""
MW = MWNoProject
from PyQt5.QtCore import QSettings
# Testing when no autoLoad
QSettings().remove("autoLoad")
autoLoad, path = MW.welcome.getAutoLoadValues()
assert type(autoLoad) == bool
assert autoLoad == False
for v in [True, False, 42, "42", None, True]:
MW.welcome.setAutoLoad(v)
autoLoad, path = MW.welcome.getAutoLoadValues()
assert type(autoLoad) == bool

View file

@ -105,9 +105,9 @@ class cheatSheet(QWidget, Ui_cheatSheet):
d = []
for r in range(self.plotModel.rowCount()):
name = self.plotModel.item(r, Plot.name.value).text()
ID = self.plotModel.item(r, Plot.ID.value).text()
imp = self.plotModel.item(r, Plot.importance.value).text()
name = self.plotModel.item(r, Plot.name).text()
ID = self.plotModel.item(r, Plot.ID).text()
imp = self.plotModel.item(r, Plot.importance).text()
imp = [self.tr("Minor"), self.tr("Secondary"), self.tr("Main")][int(imp)]
d.append((name, ID, imp))

View file

@ -273,9 +273,9 @@ class fullScreenEditor(QWidget):
if self._index:
item = self._index.internalPointer()
wc = item.data(Outline.wordCount.value)
goal = item.data(Outline.goal.value)
pg = item.data(Outline.goalPercentage.value)
wc = item.data(Outline.wordCount)
goal = item.data(Outline.goal)
pg = item.data(Outline.goalPercentage)
if goal:
rect = self.lblProgress.geometry()

View file

@ -301,9 +301,9 @@ class mainEditor(QWidget, Ui_mainEditor):
if not item:
item = self.mw.mdlOutline.rootItem
wc = item.data(Outline.wordCount.value)
goal = item.data(Outline.goal.value)
progress = item.data(Outline.goalPercentage.value)
wc = item.data(Outline.wordCount)
goal = item.data(Outline.goal)
progress = item.data(Outline.goalPercentage)
# mw = qApp.activeWindow()
if not wc:

View file

@ -4,7 +4,7 @@ from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QAction
from manuskript.enums import Outline
from manuskript.models.outlineModel import outlineModel
from manuskript.models import outlineModel
from manuskript.ui.editors.textFormat_ui import Ui_textFormat
@ -45,7 +45,7 @@ class textFormat(QWidget, Ui_textFormat):
self.setVisible(False)
return
if index.column() not in [Outline.text.value, Outline.notes.value]:
if index.column() not in [Outline.text, Outline.notes]:
self.setVisible(False)
return

View file

@ -25,7 +25,7 @@ class generalSettings(QWidget, Ui_generalSettings):
# TreeView to select parent
# We use a proxy to display only folders
proxy = QSortFilterProxyModel()
proxy.setFilterKeyColumn(Outline.type.value)
proxy.setFilterKeyColumn(Outline.type)
proxy.setFilterFixedString("folder")
proxy.setSourceModel(self.mw.mdlOutline)
self.treeGeneralParent.setModel(proxy)

View file

@ -12,7 +12,7 @@ from manuskript.ui.importers.importer_ui import Ui_importer
from manuskript.ui.importers.generalSettings import generalSettings
from manuskript.ui import style
from manuskript import importer
from manuskript.models.outlineModel import outlineModel, outlineItem
from manuskript.models import outlineModel, outlineItem
from manuskript.enums import Outline
from manuskript.exporter.pandoc import pandocExporter
@ -312,7 +312,7 @@ class importerDialog(QWidget, Ui_importer):
if self.settingsWidget.trimLongTitles():
for item in items:
if len(item.title()) > 32:
item.setData(Outline.title.value, item.title()[:32])
item.setData(Outline.title, item.title()[:32])
# Split at
if self.settingsWidget.splitScenes():

View file

@ -84,7 +84,7 @@ class revisions(QWidget, Ui_revisions):
def updateMaybe(self, topLeft, bottomRight):
if self._index and \
topLeft.column() <= Outline.revisions.value <= bottomRight.column() and \
topLeft.column() <= Outline.revisions <= bottomRight.column() and \
topLeft.row() <= self._index.row() <= bottomRight.row():
# self.update()
self.updateTimer.start()
@ -228,9 +228,9 @@ class revisions(QWidget, Ui_revisions):
ts = i.data(Qt.UserRole)
item = self._index.internalPointer()
textBefore = [r[1] for r in item.revisions() if r[0] == ts][0]
index = self._index.sibling(self._index.row(), Outline.text.value)
index = self._index.sibling(self._index.row(), Outline.text)
self._index.model().setData(index, textBefore)
# item.setData(Outline.text.value, textBefore)
# item.setData(Outline.text, textBefore)
def delete(self):
i = self.list.currentItem()

View file

@ -86,14 +86,14 @@ class search(QWidget, Ui_search):
# Chosing the right columns
lstColumns = [
("Title", Outline.title.value),
("Text", Outline.text.value),
("Summary", Outline.summarySentence.value),
("Summary", Outline.summaryFull.value),
("Notes", Outline.notes.value),
("POV", Outline.POV.value),
("Status", Outline.status.value),
("Label", Outline.label.value),
("Title", Outline.title),
("Text", Outline.text),
("Summary", Outline.summarySentence),
("Summary", Outline.summaryFull),
("Notes", Outline.notes),
("POV", Outline.POV),
("Status", Outline.status),
("Label", Outline.label),
]
columns = [c[1] for c in lstColumns if self.options[c[0]] or self.options["All"]]

View file

@ -10,9 +10,9 @@ class basicItemView(QWidget, Ui_basicItemView):
def __init__(self, parent=None):
QWidget.__init__(self)
self.setupUi(self)
self.txtSummarySentence.setColumn(Outline.summarySentence.value)
self.txtSummaryFull.setColumn(Outline.summaryFull.value)
self.txtGoal.setColumn(Outline.setGoal.value)
self.txtSummarySentence.setColumn(Outline.summarySentence)
self.txtSummaryFull.setColumn(Outline.summaryFull)
self.txtGoal.setColumn(Outline.setGoal)
def setModels(self, mdlOutline, mdlCharacter, mdlLabels, mdlStatus):
self.cmbPOV.setModels(mdlCharacter, mdlOutline)

View file

@ -44,11 +44,11 @@ class characterTreeView(QTreeWidget):
if topLeft.parent() != QModelIndex():
return
if topLeft.column() <= Character.name.value <= bottomRight.column():
if topLeft.column() <= Character.name <= bottomRight.column():
# Update name
self.updateNames()
elif topLeft.column() <= Character.importance.value <= bottomRight.column():
elif topLeft.column() <= Character.importance <= bottomRight.column():
# Importance changed
self.updateItems()

View file

@ -14,7 +14,7 @@ class chkOutlineCompile(QCheckBox):
def __init__(self, parent=None):
QCheckBox.__init__(self, parent)
self.stateChanged.connect(self.submit)
self._column = Outline.compile.value
self._column = Outline.compile
self._index = None
self._indexes = None
self._model = None
@ -43,13 +43,7 @@ class chkOutlineCompile(QCheckBox):
def getCheckedValue(self, index):
item = index.internalPointer()
c = item.data(Outline.compile)
if c:
c = int(c)
else:
c = Qt.Unchecked
return c
return Qt.Checked if item.compile() else Qt.Unchecked
def update(self, topLeft, bottomRight):

View file

@ -13,7 +13,7 @@ class cmbOutlineCharacterChoser(QComboBox):
def __init__(self, parent=None):
QComboBox.__init__(self, parent)
self.activated[int].connect(self.submit)
self._column = Outline.POV.value
self._column = Outline.POV
self._index = None
self._indexes = None
self._updating = False

View file

@ -11,7 +11,7 @@ class cmbOutlineLabelChoser(QComboBox):
def __init__(self, parent=None):
QComboBox.__init__(self, parent)
self.activated[int].connect(self.submit)
self._column = Outline.label.value
self._column = Outline.label
self._index = None
self._indexes = None
self._updating = False

View file

@ -11,7 +11,7 @@ class cmbOutlineStatusChoser(QComboBox):
def __init__(self, parent=None):
QComboBox.__init__(self, parent)
self.activated[int].connect(self.submit)
self._column = Outline.status.value
self._column = Outline.status
self._index = None
self._indexes = None
self._updating = False

View file

@ -114,7 +114,7 @@ class corkDelegate(QStyledItemDelegate):
if self.editing == Outline.summarySentence:
# One line summary
editor.setText(item.data(Outline.summarySentence.value))
editor.setText(item.data(Outline.summarySentence))
elif self.editing == Outline.title:
# Title
@ -122,21 +122,21 @@ class corkDelegate(QStyledItemDelegate):
elif self.editing == Outline.summaryFull:
# Summary
editor.setPlainText(item.data(Outline.summaryFull.value))
editor.setPlainText(item.data(Outline.summaryFull))
def setModelData(self, editor, model, index):
if self.editing == Outline.summarySentence:
# One line summary
model.setData(index.sibling(index.row(), Outline.summarySentence.value), editor.text())
model.setData(index.sibling(index.row(), Outline.summarySentence), editor.text())
elif self.editing == Outline.title:
# Title
model.setData(index, editor.text(), Outline.title.value)
model.setData(index, editor.text(), Outline.title)
elif self.editing == Outline.summaryFull:
# Summary
model.setData(index.sibling(index.row(), Outline.summaryFull.value), editor.toPlainText())
model.setData(index.sibling(index.row(), Outline.summaryFull), editor.toPlainText())
def updateRects(self, option, index):
if self.newStyle():
@ -173,7 +173,7 @@ class corkDelegate(QStyledItemDelegate):
self.mainRect.topRight() + QPoint(0, h))
self.mainTextRect = QRect(self.mainLineRect.bottomLeft() + QPoint(0, margin),
self.mainRect.bottomRight())
if not item.data(Outline.summarySentence.value):
if not item.data(Outline.summarySentence):
self.mainTextRect.setTopLeft(self.mainLineRect.topLeft())
def updateRects_v1(self, option, index):
@ -194,9 +194,9 @@ class corkDelegate(QStyledItemDelegate):
self.mainRect.topRight() + QPoint(0, iconSize))
self.mainTextRect = QRect(self.mainLineRect.bottomLeft() + QPoint(0, margin),
self.mainRect.bottomRight())
if not item.data(Outline.summarySentence.value):
if not item.data(Outline.summarySentence):
self.mainTextRect.setTopLeft(self.mainLineRect.topLeft())
if item.data(Outline.label.value) in ["", "0", 0]:
if item.data(Outline.label) in ["", "0", 0]:
self.titleRect.setBottomRight(self.labelRect.bottomRight() - QPoint(self.margin, self.margin))
def paint(self, p, option, index):
@ -340,8 +340,8 @@ class corkDelegate(QStyledItemDelegate):
p.restore()
# One line summary background
lineSummary = item.data(Outline.summarySentence.value)
fullSummary = item.data(Outline.summaryFull.value)
lineSummary = item.data(Outline.summarySentence)
fullSummary = item.data(Outline.summaryFull)
# Border
if settings.viewSettings["Cork"]["Border"] != "Nothing":
@ -359,7 +359,7 @@ class corkDelegate(QStyledItemDelegate):
p.restore()
# Draw status
status = item.data(Outline.status.value)
status = item.data(Outline.status)
if status:
it = mainWindow().mdlStatus.item(int(status), 0)
if it != None:
@ -476,8 +476,8 @@ class corkDelegate(QStyledItemDelegate):
p.drawLine(self.labelRect.topLeft(), self.labelRect.bottomLeft())
# One line summary background
lineSummary = item.data(Outline.summarySentence.value)
fullSummary = item.data(Outline.summaryFull.value)
lineSummary = item.data(Outline.summarySentence)
fullSummary = item.data(Outline.summaryFull)
if lineSummary or not fullSummary:
m = self.margin
r = self.mainLineRect.adjusted(-m, -m, m, m / 2)
@ -556,7 +556,7 @@ class corkDelegate(QStyledItemDelegate):
# Draw status
mainRect = self.mainRect
status = item.data(Outline.status.value)
status = item.data(Outline.status)
if status:
it = mainWindow().mdlStatus.item(int(status), 0)
if it != None:

View file

@ -9,7 +9,7 @@ from manuskript.functions import toString
class lineEditView(QLineEdit):
def __init__(self, parent=None):
QLineEdit.__init__(self, parent)
self._column = Outline.title.value
self._column = Outline.title
self._indexes = None
self._index = None
self._placeholderText = None

View file

@ -12,9 +12,9 @@ class metadataView(QWidget, Ui_metadataView):
QWidget.__init__(self, parent)
self.setupUi(self)
self._lastIndexes = None
self.txtSummarySentence.setColumn(Outline.summarySentence.value)
self.txtSummaryFull.setColumn(Outline.summaryFull.value)
self.txtNotes.setColumn(Outline.notes.value)
self.txtSummarySentence.setColumn(Outline.summarySentence)
self.txtSummaryFull.setColumn(Outline.summaryFull)
self.txtNotes.setColumn(Outline.notes)
self.revisions.setEnabled(False)
self.txtSummarySentence.setStyleSheet(style.lineEditSS())

View file

@ -10,7 +10,7 @@ from manuskript import settings
from manuskript.enums import Outline
from manuskript.functions import mainWindow, statusMessage
from manuskript.functions import toInt, customIcons
from manuskript.models.outlineModel import outlineItem
from manuskript.models import outlineItem
from manuskript.ui.tools.splitDialog import splitDialog
@ -31,8 +31,10 @@ class outlineBasics(QAbstractItemView):
if event.button() == Qt.RightButton:
self.menu = self.makePopupMenu()
self.menu.popup(event.globalPos())
else:
QAbstractItemView.mouseReleaseEvent(self, event)
# We don't call QAbstractItemView.mouseReleaseEvent because
# outlineBasics is never subclassed alone. So the others views
# (outlineView, corkView, treeView) that subclass outlineBasics
# call their respective mother class.
def makePopupMenu(self):
index = self.currentIndex()
@ -414,15 +416,15 @@ class outlineBasics(QAbstractItemView):
def setPOV(self, POV):
for i in self.getSelection():
self.model().setData(i.sibling(i.row(), Outline.POV.value), str(POV))
self.model().setData(i.sibling(i.row(), Outline.POV), str(POV))
def setStatus(self, status):
for i in self.getSelection():
self.model().setData(i.sibling(i.row(), Outline.status.value), str(status))
self.model().setData(i.sibling(i.row(), Outline.status), str(status))
def setLabel(self, label):
for i in self.getSelection():
self.model().setData(i.sibling(i.row(), Outline.label.value), str(label))
self.model().setData(i.sibling(i.row(), Outline.label), str(label))
def setCustomIcon(self, customIcon):
for i in self.getSelection():

View file

@ -114,7 +114,7 @@ class outlineCharacterDelegate(QStyledItemDelegate):
item = QModelIndex()
character = self.mdlCharacter.getCharacterByID(index.data())
if character:
item = character.index(Character.name.value)
item = character.index(Character.name)
opt = QStyleOptionViewItem(option)
self.initStyleOption(opt, item)
@ -172,14 +172,14 @@ class outlineCharacterDelegate(QStyledItemDelegate):
itemIndex = QModelIndex()
character = self.mdlCharacter.getCharacterByID(index.data())
if character:
itemIndex = character.index(Character.name.value)
itemIndex = character.index(Character.name)
opt = QStyleOptionViewItem(option)
self.initStyleOption(opt, itemIndex)
qApp.style().drawControl(QStyle.CE_ItemViewItem, opt, painter)
# if index.isValid() and index.internalPointer().data(Outline.POV.value) not in ["", None]:
# if index.isValid() and index.internalPointer().data(Outline.POV) not in ["", None]:
if itemIndex.isValid() and self.mdlCharacter.data(itemIndex) not in ["", None]:
opt = QStyleOptionComboBox()
opt.rect = option.rect
@ -195,6 +195,9 @@ class outlineCompileDelegate(QStyledItemDelegate):
def displayText(self, value, locale):
return ""
#def createEditor(self, parent, option, index):
#return None
class outlineGoalPercentageDelegate(QStyledItemDelegate):
def __init__(self, rootIndex=None, parent=None):
@ -215,12 +218,12 @@ class outlineGoalPercentageDelegate(QStyledItemDelegate):
item = index.internalPointer()
if not item.data(Outline.goal.value):
if not item.data(Outline.goal):
return
p = toFloat(item.data(Outline.goalPercentage.value))
p = toFloat(item.data(Outline.goalPercentage))
typ = item.data(Outline.type.value)
typ = item.data(Outline.type)
level = item.level()
if self.rootIndex and self.rootIndex.isValid():
@ -271,7 +274,7 @@ class outlineStatusDelegate(QStyledItemDelegate):
for i in range(self.mdlStatus.rowCount()):
editor.addItem(self.mdlStatus.item(i, 0).text())
val = index.internalPointer().data(Outline.status.value)
val = index.internalPointer().data(Outline.status)
if not val: val = 0
editor.setCurrentIndex(int(val))
editor.showPopup()
@ -289,7 +292,7 @@ class outlineStatusDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
QStyledItemDelegate.paint(self, painter, option, index)
if index.isValid() and index.internalPointer().data(Outline.status.value) not in ["", None, "0", 0]:
if index.isValid() and index.internalPointer().data(Outline.status) not in ["", None, "0", 0]:
opt = QStyleOptionComboBox()
opt.rect = option.rect
r = qApp.style().subControlRect(QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxArrow)
@ -329,7 +332,7 @@ class outlineLabelDelegate(QStyledItemDelegate):
editor.addItem(self.mdlLabels.item(i, 0).icon(),
self.mdlLabels.item(i, 0).text())
val = index.internalPointer().data(Outline.label.value)
val = index.internalPointer().data(Outline.label)
if not val: val = 0
editor.setCurrentIndex(int(val))
editor.showPopup()
@ -355,7 +358,7 @@ class outlineLabelDelegate(QStyledItemDelegate):
qApp.style().drawControl(QStyle.CE_ItemViewItem, opt, painter)
# Drop down indicator
if index.isValid() and index.internalPointer().data(Outline.label.value) not in ["", None, "0", 0]:
if index.isValid() and index.internalPointer().data(Outline.label) not in ["", None, "0", 0]:
opt = QStyleOptionComboBox()
opt.rect = option.rect
r = qApp.style().subControlRect(QStyle.CC_ComboBox, opt, QStyle.SC_ComboBoxArrow)

View file

@ -40,29 +40,29 @@ class outlineView(QTreeView, dndView, outlineBasics):
# Setting delegates
self.outlineTitleDelegate = outlineTitleDelegate(self)
# self.outlineTitleDelegate.setView(self)
self.setItemDelegateForColumn(Outline.title.value, self.outlineTitleDelegate)
self.setItemDelegateForColumn(Outline.title, self.outlineTitleDelegate)
self.outlineCharacterDelegate = outlineCharacterDelegate(self.modelCharacters)
self.setItemDelegateForColumn(Outline.POV.value, self.outlineCharacterDelegate)
self.setItemDelegateForColumn(Outline.POV, self.outlineCharacterDelegate)
self.outlineCompileDelegate = outlineCompileDelegate()
self.setItemDelegateForColumn(Outline.compile.value, self.outlineCompileDelegate)
self.setItemDelegateForColumn(Outline.compile, self.outlineCompileDelegate)
self.outlineStatusDelegate = outlineStatusDelegate(self.modelStatus)
self.setItemDelegateForColumn(Outline.status.value, self.outlineStatusDelegate)
self.setItemDelegateForColumn(Outline.status, self.outlineStatusDelegate)
self.outlineGoalPercentageDelegate = outlineGoalPercentageDelegate()
self.setItemDelegateForColumn(Outline.goalPercentage.value, self.outlineGoalPercentageDelegate)
self.setItemDelegateForColumn(Outline.goalPercentage, self.outlineGoalPercentageDelegate)
self.outlineLabelDelegate = outlineLabelDelegate(self.modelLabels)
self.setItemDelegateForColumn(Outline.label.value, self.outlineLabelDelegate)
self.setItemDelegateForColumn(Outline.label, self.outlineLabelDelegate)
# Hiding columns
self.hideColumns()
self.header().setSectionResizeMode(Outline.title.value, QHeaderView.Stretch)
self.header().setSectionResizeMode(Outline.POV.value, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.status.value, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.label.value, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.compile.value, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.wordCount.value, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.goal.value, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.goalPercentage.value, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.title, QHeaderView.Stretch)
self.header().setSectionResizeMode(Outline.POV, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.status, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.label, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.compile, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.wordCount, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.goal, QHeaderView.ResizeToContents)
self.header().setSectionResizeMode(Outline.goalPercentage, QHeaderView.ResizeToContents)
def hideColumns(self):
if not self.model():
@ -77,7 +77,7 @@ class outlineView(QTreeView, dndView, outlineBasics):
def setRootIndex(self, index):
QTreeView.setRootIndex(self, index)
self.outlineGoalPercentageDelegate = outlineGoalPercentageDelegate(index)
self.setItemDelegateForColumn(Outline.goalPercentage.value, self.outlineGoalPercentageDelegate)
self.setItemDelegateForColumn(Outline.goalPercentage, self.outlineGoalPercentageDelegate)
def dragMoveEvent(self, event):
dndView.dragMoveEvent(self, event)

View file

@ -78,7 +78,7 @@ class plotTreeView(QTreeWidget):
def updateMaybe(self, topLeft, bottomRight):
if topLeft.parent() != QModelIndex() and \
topLeft.column() <= PlotStep.name.value <= bottomRight.column() and \
topLeft.column() <= PlotStep.name <= bottomRight.column() and \
self._showSubPlot:
# Name's of Step has been updated, we update Items if showing
# subplots.
@ -86,11 +86,11 @@ class plotTreeView(QTreeWidget):
elif topLeft.parent() != QModelIndex():
return
if topLeft.column() <= Plot.name.value <= bottomRight.column():
if topLeft.column() <= Plot.name <= bottomRight.column():
# Update name
self.updateNames()
elif topLeft.column() <= Plot.importance.value <= bottomRight.column():
elif topLeft.column() <= Plot.importance <= bottomRight.column():
# Importance changed
self.updateItems()

View file

@ -11,7 +11,7 @@ class propertiesView(QWidget, Ui_propertiesView):
def __init__(self, parent=None):
QWidget.__init__(self)
self.setupUi(self)
self.txtGoal.setColumn(Outline.setGoal.value)
self.txtGoal.setColumn(Outline.setGoal)
def setModels(self, mdlOutline, mdlCharacter, mdlLabels, mdlStatus):
self.cmbPOV.setModels(mdlCharacter, mdlOutline)

View file

@ -61,7 +61,7 @@ class storylineView(QWidget, Ui_storylineView):
self._mdlCharacter.dataChanged.connect(self.reloadTimer.start)
def updateMaybe(self, topLeft, bottomRight):
if topLeft.column() <= Outline.notes.value <= bottomRight.column():
if topLeft.column() <= Outline.notes <= bottomRight.column():
self.reloadTimer.start
def plotReferences(self):

View file

@ -26,7 +26,7 @@ class textEditView(QTextEdit):
def __init__(self, parent=None, index=None, html=None, spellcheck=True, highlighting=False, dict="",
autoResize=False):
QTextEdit.__init__(self, parent)
self._column = Outline.text.value
self._column = Outline.text
self._index = None
self._indexes = None
self._model = None
@ -38,6 +38,10 @@ class textEditView(QTextEdit):
self.setAcceptRichText(False)
# When setting up a theme, this becomes true.
self._fromTheme = False
# Sometimes we need to update index because item has changed its
# position, so we only have it's ID as reference. We store it to
# update at the propper time.
self._updateIndexFromID = None
self.spellcheck = spellcheck
self.currentDict = dict if dict else settings.dict
@ -179,7 +183,7 @@ class textEditView(QTextEdit):
return
# what type of text are we editing?
if self._column not in [Outline.text.value, Outline.notes.value]:
if self._column not in [Outline.text, Outline.notes]:
self._textFormat = "text"
else:
@ -188,7 +192,7 @@ class textEditView(QTextEdit):
# Setting highlighter
if self._highlighting:
item = index.internalPointer()
if self._column in [Outline.text.value, Outline.notes.value]:
if self._column in [Outline.text, Outline.notes]:
self.highlighter = MMDHighlighter(self)
else:
self.highlighter = basicHighlighter(self)
@ -199,7 +203,7 @@ class textEditView(QTextEdit):
if self._fromTheme or \
not self._index or \
type(self._index.model()) != outlineModel or \
self._column != Outline.text.value:
self._column != Outline.text:
return
opt = settings.textEditor
@ -270,7 +274,15 @@ class textEditView(QTextEdit):
if self._updating:
return
elif self._index and self._index.isValid():
if self._updateIndexFromID:
# We have to update to a new index
self._index = self._index.model().getIndexByID(
self._updateIndexFromID,
self._column)
self._updateIndexFromID = None
if self._index and self._index.isValid():
if topLeft.parent() != self._index.parent():
return
@ -293,13 +305,34 @@ class textEditView(QTextEdit):
self.updateText()
def rowsAboutToBeRemoved(self, parent, first, last):
if self._index:
if self._index and self._index.isValid():
# Has my _index just been removed?
if self._index.parent() == parent and \
first <= self._index.row() <= last:
self._index = None
self.setEnabled(False)
return
# FIXME: self._indexes
# We check if item is a child of the row about to be removed
child = False
p = self._index.parent()
while p:
if p == parent:
child = True
p = None
elif p.isValid():
p = p.parent()
else:
p = None
if child:
# Item might have moved (so will not be valid any more)
ID = self._index.internalPointer().ID()
# We store ID, and we update it in self.update (after the
# rows have been removed).
self._updateIndexFromID = ID
def disconnectDocument(self):
try:
self.document().contentsChanged.disconnect(self.updateTimer.start)

View file

@ -94,7 +94,7 @@ class treeTitleDelegate(QStyledItemDelegate):
# If text color is Compile and item is selected, we have
# to change the color
if settings.viewSettings["Outline"]["Text"] == "Compile" and \
item.compile() in [0, "0"]:
not item.compile():
col = mixColors(textColor, QColor(S.window))
painter.setPen(col)
f = QFont(opt.font)
@ -109,27 +109,27 @@ class treeTitleDelegate(QStyledItemDelegate):
extraText = item.childCount()
extraText = " [{}]".format(extraText)
elif settings.viewSettings["Tree"]["InfoFolder"] == "WC":
extraText = item.data(Outline.wordCount.value)
extraText = item.wordCount()
extraText = " ({})".format(extraText)
elif settings.viewSettings["Tree"]["InfoFolder"] == "Progress":
extraText = int(toFloat(item.data(Outline.goalPercentage.value)) * 100)
extraText = int(toFloat(item.data(Outline.goalPercentage)) * 100)
if extraText:
extraText = " ({}%)".format(extraText)
elif settings.viewSettings["Tree"]["InfoFolder"] == "Summary":
extraText = item.data(Outline.summarySentence.value)
extraText = item.data(Outline.summarySentence)
if extraText:
extraText = " - {}".format(extraText)
if item.isText() and settings.viewSettings["Tree"]["InfoText"] != "Nothing":
if settings.viewSettings["Tree"]["InfoText"] == "WC":
extraText = item.data(Outline.wordCount.value)
extraText = item.wordCount()
extraText = " ({})".format(extraText)
elif settings.viewSettings["Tree"]["InfoText"] == "Progress":
extraText = int(toFloat(item.data(Outline.goalPercentage.value)) * 100)
extraText = int(toFloat(item.data(Outline.goalPercentage)) * 100)
if extraText:
extraText = " ({}%)".format(extraText)
elif settings.viewSettings["Tree"]["InfoText"] == "Summary":
extraText = item.data(Outline.summarySentence.value)
extraText = item.data(Outline.summarySentence)
if extraText:
extraText = " - {}".format(extraText)

View file

@ -22,12 +22,14 @@ class treeView(QTreeView, dndView, outlineBasics):
QTreeView.setModel(self, model)
# Hiding columns
for c in range(1, self.model().columnCount()):
for c in range(self.model().columnCount()):
self.hideColumn(c)
self.showColumn(Outline.title)
# Setting delegate
self.titleDelegate = treeTitleDelegate()
self.setItemDelegateForColumn(Outline.title.value, self.titleDelegate)
self.setItemDelegateForColumn(Outline.title, self.titleDelegate)
def makePopupMenu(self):
menu = outlineBasics.makePopupMenu(self)

Some files were not shown because too many files have changed in this diff Show more