mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-17 11:22:28 +12:00
Adds: unit tests and starts refactoring models
This commit is contained in:
commit
64994cde3a
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -11,4 +11,5 @@ icons/Numix
|
|||
.idea
|
||||
dist
|
||||
build
|
||||
test-projects
|
||||
test-projects
|
||||
.cache
|
||||
|
|
|
@ -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.
|
|
@ -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):
|
||||
#
|
||||
|
|
@ -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
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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)
|
||||
|
188
libs/odf/draw.py
188
libs/odf/draw.py
|
@ -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)
|
||||
|
|
@ -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 :
|
|
@ -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("&", "&")
|
||||
data = data.replace("<", "<")
|
||||
data = data.replace(">", ">")
|
||||
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']=' '
|
||||
entities['\r']=''
|
||||
data = _escape(data, entities)
|
||||
if '"' in data:
|
||||
if "'" in data:
|
||||
data = '"%s"' % data.replace('"', """)
|
||||
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
|
||||
|
||||
|
|
@ -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')
|
||||
)
|
115
libs/odf/form.py
115
libs/odf/form.py
|
@ -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)
|
||||
|
9194
libs/odf/grammar.py
9194
libs/odf/grammar.py
File diff suppressed because it is too large
Load diff
115
libs/odf/load.py
115
libs/odf/load.py
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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',
|
||||
}
|
|
@ -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)
|
||||
|
|
@ -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
|
@ -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']))
|
||||
|
|
@ -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
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
@ -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)
|
573
libs/odf/text.py
573
libs/odf/text.py
|
@ -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)
|
||||
|
|
@ -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()
|
|
@ -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()
|
||||
|
|
@ -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)
|
5987
libs/txt2tags
5987
libs/txt2tags
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
from manuskript.models.outlineItem import outlineItem
|
||||
from manuskript.models.outlineModel import outlineModel
|
306
manuskript/models/abstractItem.py
Normal file
306
manuskript/models/abstractItem.py
Normal 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
|
547
manuskript/models/abstractModel.py
Normal file
547
manuskript/models/abstractModel.py
Normal 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)
|
|
@ -296,4 +296,4 @@ class CharacterInfo():
|
|||
def __init__(self, character, description="", value=""):
|
||||
self.description = description
|
||||
self.value = value
|
||||
self.character = character
|
||||
self.character = character
|
||||
|
|
497
manuskript/models/outlineItem.py
Normal file
497
manuskript/models/outlineItem.py
Normal 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
|
@ -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()):
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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):
|
||||
|
|
41
manuskript/tests/__init__.py
Normal file
41
manuskript/tests/__init__.py
Normal 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()
|
73
manuskript/tests/conftest.py
Normal file
73
manuskript/tests/conftest.py
Normal 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
|
4
manuskript/tests/models/__init__.py
Normal file
4
manuskript/tests/models/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
"""Tests for models."""
|
23
manuskript/tests/models/conftest.py
Normal file
23
manuskript/tests/models/conftest.py
Normal 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
|
161
manuskript/tests/models/test_outlineItem.py
Normal file
161
manuskript/tests/models/test_outlineItem.py
Normal 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"
|
116
manuskript/tests/models/test_references.py
Normal file
116
manuskript/tests/models/test_references.py
Normal 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
|
96
manuskript/tests/test_functions.py
Normal file
96
manuskript/tests/test_functions.py
Normal 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
|
164
manuskript/tests/test_settingsWindow.py
Normal file
164
manuskript/tests/test_settingsWindow.py
Normal 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
|
4
manuskript/tests/ui/__init__.py
Normal file
4
manuskript/tests/ui/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
"""Tests for stuff in ui."""
|
4
manuskript/tests/ui/exporters/__init__.py
Normal file
4
manuskript/tests/ui/exporters/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
"""Tests for exporters, ui part."""
|
27
manuskript/tests/ui/exporters/test_exporters.py
Normal file
27
manuskript/tests/ui/exporters/test_exporters.py
Normal 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
|
4
manuskript/tests/ui/importers/__init__.py
Normal file
4
manuskript/tests/ui/importers/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
"""Tests for importers, ui part."""
|
19
manuskript/tests/ui/importers/test_importers.py
Normal file
19
manuskript/tests/ui/importers/test_importers.py
Normal 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
|
22
manuskript/tests/ui/test_welcome.py
Normal file
22
manuskript/tests/ui/test_welcome.py
Normal 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
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"]]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue