Add a new GUI for the Build Tool (#1389)
@@ -30,9 +30,7 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Install Dependencies (pip)
|
- name: Install Dependencies (pip)
|
||||||
run: |
|
run: |
|
||||||
pip install --upgrade pip
|
pip install -U -r requirements.txt -r requirements-dev.txt
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install -r requirements-dev.txt
|
|
||||||
- name: Run Build Commands
|
- name: Run Build Commands
|
||||||
run: python setup.py qtlrelease sample
|
run: python setup.py qtlrelease sample
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
|
|||||||
@@ -19,17 +19,14 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
architecture: x64
|
architecture: x64
|
||||||
- name: Install Packages
|
- name: Install Packages (brew)
|
||||||
run: |
|
run: |
|
||||||
brew install enchant
|
brew install enchant
|
||||||
- name: Checkout Source
|
- name: Checkout Source
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies (pip)
|
||||||
run: |
|
run: |
|
||||||
pip install --upgrade pip
|
pip install -U pyobjc -r requirements.txt -r requirements-dev.txt
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install -r requirements-dev.txt
|
|
||||||
pip install pyobjc
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: |
|
run: |
|
||||||
export QT_QPA_PLATFORM=offscreen
|
export QT_QPA_PLATFORM=offscreen
|
||||||
|
|||||||
@@ -21,11 +21,9 @@ jobs:
|
|||||||
architecture: x64
|
architecture: x64
|
||||||
- name: Checkout Source
|
- name: Checkout Source
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies (pip)
|
||||||
run: |
|
run: |
|
||||||
pip install --upgrade pip
|
pip install -U -r requirements.txt -r requirements-dev.txt
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install -r requirements-dev.txt
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: |
|
run: |
|
||||||
pytest -v --cov=novelwriter --timeout=60
|
pytest -v --cov=novelwriter --timeout=60
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ __pycache__
|
|||||||
/novelwriter/assets/sample.zip
|
/novelwriter/assets/sample.zip
|
||||||
/sample/cache
|
/sample/cache
|
||||||
/sample/meta
|
/sample/meta
|
||||||
/sample/versions
|
|
||||||
*.bak
|
*.bak
|
||||||
*.lock
|
*.lock
|
||||||
ToC.txt
|
ToC.txt
|
||||||
@@ -44,9 +43,6 @@ ToC.txt
|
|||||||
/tests/temp
|
/tests/temp
|
||||||
/tests/lipsum/cache
|
/tests/lipsum/cache
|
||||||
/tests/lipsum/meta
|
/tests/lipsum/meta
|
||||||
/tests/minimal/cache
|
|
||||||
/tests/minimal/meta
|
|
||||||
/tests/oldproj/cache
|
|
||||||
/.pytest_cache
|
/.pytest_cache
|
||||||
/pytestdebug.log
|
/pytestdebug.log
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – Init File
|
novelWriter – Init File
|
||||||
=======================
|
=======================
|
||||||
Application initialisation
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2018-09-22 [0.0.1]
|
Created: 2018-09-22 [0.0.1]
|
||||||
@@ -241,8 +240,11 @@ def main(sysArgs=None):
|
|||||||
# Connect the exception handler before making the main GUI
|
# Connect the exception handler before making the main GUI
|
||||||
sys.excepthook = exceptionHandler
|
sys.excepthook = exceptionHandler
|
||||||
|
|
||||||
# Launch main GUI
|
# Run Config steps that require the QApplication
|
||||||
CONFIG.initLocalisation(nwApp)
|
CONFIG.initLocalisation(nwApp)
|
||||||
|
CONFIG.setTextFont(CONFIG.textFont, CONFIG.textSize) # Makes sure these are valid
|
||||||
|
|
||||||
|
# Launch main GUI
|
||||||
nwGUI = GuiMain()
|
nwGUI = GuiMain()
|
||||||
nwGUI.postLaunchTasks(cmdOpen)
|
nwGUI.postLaunchTasks(cmdOpen)
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
|||||||
add = typ_plus.svg
|
add = typ_plus.svg
|
||||||
backward = typ_chevron-left.svg
|
backward = typ_chevron-left.svg
|
||||||
bookmark = typ_bookmark.svg
|
bookmark = typ_bookmark.svg
|
||||||
|
browse = typ_folder-open.svg
|
||||||
|
build_excluded = typ_cancel.svg
|
||||||
|
build_filtered = typ_filter.svg
|
||||||
|
build_included = typ_pin.svg
|
||||||
bullet-off = typ_media-record-outline.svg
|
bullet-off = typ_media-record-outline.svg
|
||||||
bullet-on = typ_media-record.svg
|
bullet-on = typ_media-record.svg
|
||||||
checked = mixed_input-checked.svg
|
checked = mixed_input-checked.svg
|
||||||
@@ -37,6 +41,7 @@ cls_world = typ_location.svg
|
|||||||
cross = typ_times.svg
|
cross = typ_times.svg
|
||||||
down = typ_chevron-down.svg
|
down = typ_chevron-down.svg
|
||||||
edit = typ_pencil.svg
|
edit = typ_pencil.svg
|
||||||
|
export = typ_export.svg
|
||||||
forward = typ_chevron-right.svg
|
forward = typ_chevron-right.svg
|
||||||
maximise = typ_arrow-maximise.svg
|
maximise = typ_arrow-maximise.svg
|
||||||
menu = typ_th-menu.svg
|
menu = typ_th-menu.svg
|
||||||
@@ -54,6 +59,7 @@ proj_title = mixed_document-title.svg
|
|||||||
reference = typ_at.svg
|
reference = typ_at.svg
|
||||||
refresh = typ_refresh.svg
|
refresh = typ_refresh.svg
|
||||||
remove = typ_minus.svg
|
remove = typ_minus.svg
|
||||||
|
revert = typ_refresh-flipped.svg
|
||||||
search = typ_search.svg
|
search = typ_search.svg
|
||||||
search_cancel = typ_cancel-grey.svg
|
search_cancel = typ_cancel-grey.svg
|
||||||
search_case = nw_search-case.svg
|
search_case = nw_search-case.svg
|
||||||
@@ -73,7 +79,7 @@ sticky-off = typ_pin-outline.svg
|
|||||||
sticky-on = typ_pin.svg
|
sticky-on = typ_pin.svg
|
||||||
unchecked = mixed_input-unchecked.svg
|
unchecked = mixed_input-unchecked.svg
|
||||||
up = typ_chevron-up.svg
|
up = typ_chevron-up.svg
|
||||||
view_build = typ_export.svg
|
view_build = typ_export-grey.svg
|
||||||
view_editor = mixed_edit.svg
|
view_editor = mixed_edit.svg
|
||||||
view_novel = typ_book-grey.svg
|
view_novel = typ_book-grey.svg
|
||||||
view_outline = typ_puzzle-outline.svg
|
view_outline = typ_puzzle-outline.svg
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m6.8889 16.889v0.61111c2.0668-3.1509 4.4-4.8314 7.3333-4.8889v3.6667c0 0.67344 0.62456 1.2222 1.397 1.2222 0.44489 0 0.825-0.19311 1.0792-0.47789 2.3626-2.4799 7.3016-7.4666 7.3016-7.4666s-4.939-4.9891-7.3028-7.5008c-0.25422-0.25056-0.63311-0.44367-1.078-0.44367-0.77244 0-1.397 0.54633-1.397 1.2222v3.6667c-5.6956 0-7.3333 5.9534-7.3333 10.389zm-3.6667 5.5h17.111c0.67589 0 1.2222-0.54756 1.2222-1.2222v-7.3896c-0.81156 0.82622-1.6671 1.7026-2.4444 2.5019v3.6654h-14.667v-14.667h8.5556v-2.4444h-9.7778c-0.67589 0-1.2222 0.54756-1.2222 1.2222v17.111c0 0.67467 0.54633 1.2222 1.2222 1.2222z" fill="#aeaeae" stroke-width="1.2222"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 784 B |
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="m6.8889 16.889v0.61111c2.0668-3.1509 4.4-4.8314 7.3333-4.8889v3.6667c0 0.67344 0.62456 1.2222 1.397 1.2222 0.44489 0 0.825-0.19311 1.0792-0.47789 2.3626-2.4799 7.3016-7.4666 7.3016-7.4666s-4.939-4.9891-7.3028-7.5008c-0.25422-0.25056-0.63311-0.44367-1.078-0.44367-0.77244 0-1.397 0.54633-1.397 1.2222v3.6667c-5.6956 0-7.3333 5.9534-7.3333 10.389zm-3.6667 5.5h17.111c0.67589 0 1.2222-0.54756 1.2222-1.2222v-7.3896c-0.81156 0.82622-1.6671 1.7026-2.4444 2.5019v3.6654h-14.667v-14.667h8.5556v-2.4444h-9.7778c-0.67589 0-1.2222 0.54756-1.2222 1.2222v17.111c0 0.67467 0.54633 1.2222 1.2222 1.2222z" fill="#aeaeae" stroke-width="1.2222"/>
|
<path d="m6.4444 16.444v0.55556c1.8789-2.8644 4-4.3922 6.6667-4.4444v3.3333c0 0.61222 0.56778 1.1111 1.27 1.1111 0.40444 0 0.75-0.17556 0.98111-0.43444 2.1478-2.2544 6.6378-6.7878 6.6378-6.7878s-4.49-4.5356-6.6389-6.8189c-0.23111-0.22778-0.57556-0.40333-0.98-0.40333-0.70222 0-1.27 0.49667-1.27 1.1111v3.3333c-5.1778 0-6.6667 5.4122-6.6667 9.4444zm-3.3333 5h15.556c0.61444 0 1.1111-0.49778 1.1111-1.1111v-6.7178c-0.73778 0.75111-1.5156 1.5478-2.2222 2.2744v3.3322h-13.333v-13.333h7.7778v-2.2222h-8.8889c-0.61444 0-1.1111 0.49778-1.1111 1.1111v15.556c0 0.61333 0.49667 1.1111 1.1111 1.1111z" fill="#69c" stroke-width="1.1111"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 784 B After Width: | Height: | Size: 772 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m20.694 2h-17.387c-1.3661 0-1.7387 0.85714-0.74516 2l5.2161 6c0.99354 1.1429 1.7387 3.2857 1.7387 4.8571v7.1429l4.9677-2.8571v-5c0-1.1429 0.74516-3 1.7387-4.1429l5.2161-6c0.99354-1.1429 0.62096-2-0.74516-2z" fill="#f99157" stroke-width="1.332"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 398 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m21.316 8.1937h-2.2836c-0.38061-1.1418-1.4273-1.903-2.6643-1.903h-5.7091c0-1.0466-0.85636-1.903-1.903-1.903h-3.9012c-1.6175 0-2.8545 1.237-2.8545 2.8545v9.5154c0 1.6175 1.237 2.8545 2.8545 2.8545h11.418c1.6175 0 3.2352-1.237 3.6157-2.8545l2.0934-7.6121c0.09515-0.57091-0.1903-0.95154-0.66605-0.95154zm-17.413 0.95154v-1.903c0-0.57091 0.38061-0.95154 0.95154-0.95154h3.8061c0 1.0466 0.85636 1.903 1.903 1.903h5.7091c0.57091 0 0.95154 0.38061 0.95154 0.95154h-10.562c-0.57091 0-1.0466 0.38061-1.237 0.95154l-1.5225 5.9945zm14.177 7.1363c-0.1903 0.76121-1.0466 1.4273-1.8079 1.4273h-11.418s-0.38061-0.1903-0.1903-0.76121l1.8079-6.6605c0-0.09515 0.1903-0.1903 0.28545-0.1903h13.035z" fill="#aeaeae" stroke-width=".95154"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 873 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m10.837 12.256h-6.8373v-6.8347c0.010667-1.5131 0.89467-1.877 1.964-0.80652l1.5387 1.5437c1.3533-1.0598 3.0093-1.6397 4.7547-1.6397 2.0667 0 4.012 0.80518 5.472 2.2676 1.4653 1.4557 2.2707 3.4034 2.2707 5.4696 0 2.0703-0.80533 4.0153-2.268 5.475-1.4627 1.4624-3.4067 2.2689-5.4747 2.2689-2.0667 0-4.012-0.80652-5.4747-2.2702-0.39467-0.39593-0.744-0.82784-1.04-1.2864-0.46267-0.7212-0.25333-1.6797 0.46667-2.1396 0.71867-0.46125 1.6773-0.25195 2.1387 0.46658 0.17733 0.27595 0.38933 0.53323 0.624 0.77319 0.87867 0.87717 2.0453 1.3597 3.2853 1.3597s2.4067-0.48124 3.2853-1.3597c0.876-0.87717 1.36-2.0436 1.36-3.2861 0-1.2398-0.48267-2.4062-1.36-3.2807-0.87867-0.87717-2.0453-1.3611-3.2853-1.3611-0.91733 0-1.7947 0.26795-2.5453 0.76252l1.9307 1.9343c1.0707 1.0691 0.70667 1.9436-0.80533 1.9436z" fill="#9c9" stroke-width="1.3332"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 984 B |
@@ -19,6 +19,10 @@ licenseurl = https://creativecommons.org/licenses/by-sa/4.0/
|
|||||||
add = typ_plus.svg
|
add = typ_plus.svg
|
||||||
backward = typ_chevron-left.svg
|
backward = typ_chevron-left.svg
|
||||||
bookmark = typ_bookmark.svg
|
bookmark = typ_bookmark.svg
|
||||||
|
browse = typ_folder-open.svg
|
||||||
|
build_excluded = typ_cancel.svg
|
||||||
|
build_filtered = typ_filter.svg
|
||||||
|
build_included = typ_pin.svg
|
||||||
bullet-off = typ_media-record-outline.svg
|
bullet-off = typ_media-record-outline.svg
|
||||||
bullet-on = typ_media-record.svg
|
bullet-on = typ_media-record.svg
|
||||||
checked = mixed_input-checked.svg
|
checked = mixed_input-checked.svg
|
||||||
@@ -37,6 +41,7 @@ cls_world = typ_location.svg
|
|||||||
cross = typ_times.svg
|
cross = typ_times.svg
|
||||||
down = typ_chevron-down.svg
|
down = typ_chevron-down.svg
|
||||||
edit = typ_pencil.svg
|
edit = typ_pencil.svg
|
||||||
|
export = typ_export.svg
|
||||||
forward = typ_chevron-right.svg
|
forward = typ_chevron-right.svg
|
||||||
maximise = typ_arrow-maximise.svg
|
maximise = typ_arrow-maximise.svg
|
||||||
menu = typ_th-menu.svg
|
menu = typ_th-menu.svg
|
||||||
@@ -54,6 +59,7 @@ proj_title = mixed_document-title.svg
|
|||||||
reference = typ_at.svg
|
reference = typ_at.svg
|
||||||
refresh = typ_refresh.svg
|
refresh = typ_refresh.svg
|
||||||
remove = typ_minus.svg
|
remove = typ_minus.svg
|
||||||
|
revert = typ_refresh-flipped.svg
|
||||||
search = typ_search.svg
|
search = typ_search.svg
|
||||||
search_cancel = typ_cancel-grey.svg
|
search_cancel = typ_cancel-grey.svg
|
||||||
search_case = nw_search-case.svg
|
search_case = nw_search-case.svg
|
||||||
@@ -73,7 +79,7 @@ sticky-off = typ_pin-outline.svg
|
|||||||
sticky-on = typ_pin.svg
|
sticky-on = typ_pin.svg
|
||||||
unchecked = mixed_input-unchecked.svg
|
unchecked = mixed_input-unchecked.svg
|
||||||
up = typ_chevron-up.svg
|
up = typ_chevron-up.svg
|
||||||
view_build = typ_export.svg
|
view_build = typ_export-grey.svg
|
||||||
view_editor = mixed_edit.svg
|
view_editor = mixed_edit.svg
|
||||||
view_novel = typ_book-grey.svg
|
view_novel = typ_book-grey.svg
|
||||||
view_outline = typ_puzzle-outline.svg
|
view_outline = typ_puzzle-outline.svg
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m6.8889 16.889v0.61111c2.0668-3.1509 4.4-4.8314 7.3333-4.8889v3.6667c0 0.67344 0.62456 1.2222 1.397 1.2222 0.44489 0 0.825-0.19311 1.0792-0.47789 2.3626-2.4799 7.3016-7.4666 7.3016-7.4666s-4.939-4.9891-7.3028-7.5008c-0.25422-0.25056-0.63311-0.44367-1.078-0.44367-0.77244 0-1.397 0.54633-1.397 1.2222v3.6667c-5.6956 0-7.3333 5.9534-7.3333 10.389zm-3.6667 5.5h17.111c0.67589 0 1.2222-0.54756 1.2222-1.2222v-7.3896c-0.81156 0.82622-1.6671 1.7026-2.4444 2.5019v3.6654h-14.667v-14.667h8.5556v-2.4444h-9.7778c-0.67589 0-1.2222 0.54756-1.2222 1.2222v17.111c0 0.67467 0.54633 1.2222 1.2222 1.2222z" fill-opacity=".72157" stroke-width="1.2222"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 791 B |
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="m6.8889 16.889v0.61111c2.0668-3.1509 4.4-4.8314 7.3333-4.8889v3.6667c0 0.67344 0.62456 1.2222 1.397 1.2222 0.44489 0 0.825-0.19311 1.0792-0.47789 2.3626-2.4799 7.3016-7.4666 7.3016-7.4666s-4.939-4.9891-7.3028-7.5008c-0.25422-0.25056-0.63311-0.44367-1.078-0.44367-0.77244 0-1.397 0.54633-1.397 1.2222v3.6667c-5.6956 0-7.3333 5.9534-7.3333 10.389zm-3.6667 5.5h17.111c0.67589 0 1.2222-0.54756 1.2222-1.2222v-7.3896c-0.81156 0.82622-1.6671 1.7026-2.4444 2.5019v3.6654h-14.667v-14.667h8.5556v-2.4444h-9.7778c-0.67589 0-1.2222 0.54756-1.2222 1.2222v17.111c0 0.67467 0.54633 1.2222 1.2222 1.2222z" fill-opacity=".72157" stroke-width="1.2222"/>
|
<path d="m6.4444 16.444v0.55556c1.8789-2.8644 4-4.3922 6.6667-4.4444v3.3333c0 0.61222 0.56778 1.1111 1.27 1.1111 0.40444 0 0.75-0.17556 0.98111-0.43444 2.1478-2.2544 6.6378-6.7878 6.6378-6.7878s-4.49-4.5356-6.6389-6.8189c-0.23111-0.22778-0.57556-0.40333-0.98-0.40333-0.70222 0-1.27 0.49667-1.27 1.1111v3.3333c-5.1778 0-6.6667 5.4122-6.6667 9.4444zm-3.3333 5h15.556c0.61444 0 1.1111-0.49778 1.1111-1.1111v-6.7178c-0.73778 0.75111-1.5156 1.5478-2.2222 2.2744v3.3322h-13.333v-13.333h7.7778v-2.2222h-8.8889c-0.61444 0-1.1111 0.49778-1.1111 1.1111v15.556c0 0.61333 0.49667 1.1111 1.1111 1.1111z" fill="#4271ae" stroke-width="1.1111"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 791 B After Width: | Height: | Size: 775 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m20.694 2h-17.387c-1.3661 0-1.7387 0.85714-0.74516 2l5.2161 6c0.99354 1.1429 1.7387 3.2857 1.7387 4.8571v7.1429l4.9677-2.8571v-5c0-1.1429 0.74516-3 1.7387-4.1429l5.2161-6c0.99354-1.1429 0.62096-2-0.74516-2z" fill="#f5871f" stroke-width="1.332"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 398 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m21.316 8.1937h-2.2836c-0.38061-1.1418-1.4273-1.903-2.6643-1.903h-5.7091c0-1.0466-0.85636-1.903-1.903-1.903h-3.9012c-1.6175 0-2.8545 1.237-2.8545 2.8545v9.5154c0 1.6175 1.237 2.8545 2.8545 2.8545h11.418c1.6175 0 3.2352-1.237 3.6157-2.8545l2.0934-7.6121c0.09515-0.57091-0.1903-0.95154-0.66605-0.95154zm-17.413 0.95154v-1.903c0-0.57091 0.38061-0.95154 0.95154-0.95154h3.8061c0 1.0466 0.85636 1.903 1.903 1.903h5.7091c0.57091 0 0.95154 0.38061 0.95154 0.95154h-10.562c-0.57091 0-1.0466 0.38061-1.237 0.95154l-1.5225 5.9945zm14.177 7.1363c-0.1903 0.76121-1.0466 1.4273-1.8079 1.4273h-11.418s-0.38061-0.1903-0.1903-0.76121l1.8079-6.6605c0-0.09515 0.1903-0.1903 0.28545-0.1903h13.035z" fill-opacity=".72157" stroke-width="1.0467"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 880 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m10.837 12.256h-6.8373v-6.8347c0.010667-1.5131 0.89467-1.877 1.964-0.80652l1.5387 1.5437c1.3533-1.0598 3.0093-1.6397 4.7547-1.6397 2.0667 0 4.012 0.80518 5.472 2.2676 1.4653 1.4557 2.2707 3.4034 2.2707 5.4696 0 2.0703-0.80533 4.0153-2.268 5.475-1.4627 1.4624-3.4067 2.2689-5.4747 2.2689-2.0667 0-4.012-0.80652-5.4747-2.2702-0.39467-0.39593-0.744-0.82784-1.04-1.2864-0.46267-0.7212-0.25333-1.6797 0.46667-2.1396 0.71867-0.46125 1.6773-0.25195 2.1387 0.46658 0.17733 0.27595 0.38933 0.53323 0.624 0.77319 0.87867 0.87717 2.0453 1.3597 3.2853 1.3597s2.4067-0.48124 3.2853-1.3597c0.876-0.87717 1.36-2.0436 1.36-3.2861 0-1.2398-0.48267-2.4062-1.36-3.2807-0.87867-0.87717-2.0453-1.3611-3.2853-1.3611-0.91733 0-1.7947 0.26795-2.5453 0.76252l1.9307 1.9343c1.0707 1.0691 0.70667 1.9436-0.80533 1.9436z" fill="#718c00" stroke-width="1.3332"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 987 B |
@@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – Common Functions
|
novelWriter – Common Functions
|
||||||
==============================
|
==============================
|
||||||
Various common functions
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2019-05-12 [0.1]
|
Created: 2019-05-12 [0.1]
|
||||||
@@ -22,21 +21,22 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
|
import unicodedata
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
from typing import Any, Literal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
|
|
||||||
from PyQt5.QtCore import QCoreApplication
|
from PyQt5.QtCore import QCoreApplication
|
||||||
from PyQt5.QtWidgets import qApp
|
from PyQt5.QtWidgets import QWidget, qApp
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
@@ -49,9 +49,8 @@ logger = logging.getLogger(__name__)
|
|||||||
# Checker Functions
|
# Checker Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def checkStringNone(value, default):
|
def checkStringNone(value: Any, default: str | None) -> str | None:
|
||||||
"""Check if a variable is a string or a None.
|
"""Check if a variable is a string or a None."""
|
||||||
"""
|
|
||||||
if value is None or value == "None":
|
if value is None or value == "None":
|
||||||
return None
|
return None
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
@@ -59,35 +58,31 @@ def checkStringNone(value, default):
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkString(value, default):
|
def checkString(value: Any, default: str) -> str:
|
||||||
"""Check if a variable is a string.
|
"""Check if a variable is a string."""
|
||||||
"""
|
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return str(value)
|
return str(value)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkInt(value, default):
|
def checkInt(value: Any, default: int) -> int:
|
||||||
"""Check if a variable is an integer.
|
"""Check if a variable is an integer."""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
return int(value)
|
return int(value)
|
||||||
except Exception:
|
except Exception:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkFloat(value, default):
|
def checkFloat(value: Any, default: float) -> float:
|
||||||
"""Check if a variable is a float.
|
"""Check if a variable is a float."""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
return float(value)
|
return float(value)
|
||||||
except Exception:
|
except Exception:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkBool(value, default):
|
def checkBool(value: Any, default: bool) -> bool:
|
||||||
"""Check if a variable is a boolean.
|
"""Check if a variable is a boolean."""
|
||||||
"""
|
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
return value
|
return value
|
||||||
elif isinstance(value, str):
|
elif isinstance(value, str):
|
||||||
@@ -118,16 +113,15 @@ def checkHandle(value, default, allowNone=False):
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkUuid(value, default):
|
def checkUuid(value: Any, default: str) -> str:
|
||||||
"""Try to process a value as an uuid, or return a default.
|
"""Try to process a value as an UUID, or return a default."""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
return str(uuid.UUID(value))
|
return str(uuid.UUID(value))
|
||||||
except Exception:
|
except Exception:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def checkPath(value, default):
|
def checkPath(value: Any, default: Path) -> Path:
|
||||||
"""Check if a value is a valid path. Non-empty strings are accepted.
|
"""Check if a value is a valid path. Non-empty strings are accepted.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, Path):
|
if isinstance(value, Path):
|
||||||
@@ -142,7 +136,7 @@ def checkPath(value, default):
|
|||||||
# Validator Functions
|
# Validator Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def isHandle(value):
|
def isHandle(value: Any) -> bool:
|
||||||
"""Check if a string is a valid novelWriter handle.
|
"""Check if a string is a valid novelWriter handle.
|
||||||
Note: This is case sensitive. Must be lower case!
|
Note: This is case sensitive. Must be lower case!
|
||||||
"""
|
"""
|
||||||
@@ -156,9 +150,8 @@ def isHandle(value):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def isTitleTag(value):
|
def isTitleTag(value: Any) -> bool:
|
||||||
"""Check if a string is a valid title tag string.
|
"""Check if a string is a valid title tag string."""
|
||||||
"""
|
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
return False
|
return False
|
||||||
if len(value) != 5:
|
if len(value) != 5:
|
||||||
@@ -171,27 +164,23 @@ def isTitleTag(value):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def isItemClass(value):
|
def isItemClass(value: str) -> bool:
|
||||||
"""Check if a string is a valid nwItemClass identifier.
|
"""Check if a string is a valid nwItemClass identifier."""
|
||||||
"""
|
|
||||||
return value in nwItemClass.__members__
|
return value in nwItemClass.__members__
|
||||||
|
|
||||||
|
|
||||||
def isItemType(value):
|
def isItemType(value: str) -> bool:
|
||||||
"""Check if a string is a valid nwItemType identifier.
|
"""Check if a string is a valid nwItemType identifier."""
|
||||||
"""
|
|
||||||
return value in nwItemType.__members__
|
return value in nwItemType.__members__
|
||||||
|
|
||||||
|
|
||||||
def isItemLayout(value):
|
def isItemLayout(value: str) -> bool:
|
||||||
"""Check if a string is a valid nwItemLayout identifier.
|
"""Check if a string is a valid nwItemLayout identifier."""
|
||||||
"""
|
|
||||||
return value in nwItemLayout.__members__
|
return value in nwItemLayout.__members__
|
||||||
|
|
||||||
|
|
||||||
def hexToInt(value, default=0):
|
def hexToInt(value: Any, default: int = 0) -> int:
|
||||||
"""Convert a hex string to an integer.
|
"""Convert a hex string to an integer."""
|
||||||
"""
|
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
try:
|
try:
|
||||||
return int(value, 16)
|
return int(value, 16)
|
||||||
@@ -200,13 +189,13 @@ def hexToInt(value, default=0):
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def minmax(value, minVal, maxVal):
|
def minmax(value: int, minVal: int, maxVal: int) -> int:
|
||||||
"""Make sure an integer is between min and max value (inclusive).
|
"""Make sure an integer is between min and max value (inclusive).
|
||||||
"""
|
"""
|
||||||
return min(maxVal, max(minVal, value))
|
return min(maxVal, max(minVal, value))
|
||||||
|
|
||||||
|
|
||||||
def checkIntTuple(value, valid, default):
|
def checkIntTuple(value: int, valid: tuple | list | set, default: int) -> int:
|
||||||
"""Check that an int is an element of a tuple. If it isn't, return
|
"""Check that an int is an element of a tuple. If it isn't, return
|
||||||
the default value.
|
the default value.
|
||||||
"""
|
"""
|
||||||
@@ -220,7 +209,7 @@ def checkIntTuple(value, valid, default):
|
|||||||
# Formatting Functions
|
# Formatting Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def formatInt(value):
|
def formatInt(value: int) -> str:
|
||||||
"""Formats an integer with k, M, G etc.
|
"""Formats an integer with k, M, G etc.
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, int):
|
if not isinstance(value, int):
|
||||||
@@ -241,7 +230,7 @@ def formatInt(value):
|
|||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
def formatTimeStamp(value, fileSafe=False):
|
def formatTimeStamp(value: float, fileSafe: bool = False) -> str:
|
||||||
"""Take a number (on the format returned by time.time()) and convert
|
"""Take a number (on the format returned by time.time()) and convert
|
||||||
it to a timestamp string.
|
it to a timestamp string.
|
||||||
"""
|
"""
|
||||||
@@ -251,7 +240,7 @@ def formatTimeStamp(value, fileSafe=False):
|
|||||||
return datetime.fromtimestamp(value).strftime(nwConst.FMT_TSTAMP)
|
return datetime.fromtimestamp(value).strftime(nwConst.FMT_TSTAMP)
|
||||||
|
|
||||||
|
|
||||||
def formatTime(t):
|
def formatTime(t: int) -> str:
|
||||||
"""Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format
|
"""Format a time in seconds in HH:MM:SS format or d-HH:MM:SS format
|
||||||
if a full day or longer.
|
if a full day or longer.
|
||||||
"""
|
"""
|
||||||
@@ -267,20 +256,19 @@ def formatTime(t):
|
|||||||
# String Functions
|
# String Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def simplified(string):
|
def simplified(text: str) -> str:
|
||||||
"""Take a string an strip leading and trailing whitespaces, and
|
"""Take a string and strip leading and trailing whitespaces, and
|
||||||
replace all occurences of (multiple) whitespaces with a 0x20 space.
|
replace all occurences of (multiple) whitespaces with a 0x20 space.
|
||||||
"""
|
"""
|
||||||
return " ".join(str(string).strip().split())
|
return " ".join(str(text).strip().split())
|
||||||
|
|
||||||
|
|
||||||
def yesNo(value):
|
def yesNo(value: int | bool | None) -> Literal["yes", "no"]:
|
||||||
"""Convert a boolean evaluated variable to a yes or no.
|
"""Convert a boolean evaluated variable to a yes or no."""
|
||||||
"""
|
|
||||||
return "yes" if value else "no"
|
return "yes" if value else "no"
|
||||||
|
|
||||||
|
|
||||||
def transferCase(source, target):
|
def transferCase(source: str, target: str) -> str:
|
||||||
"""Transfers the case of the source word to the target word. This
|
"""Transfers the case of the source word to the target word. This
|
||||||
will consider all upper or lower, and first char capitalisation.
|
will consider all upper or lower, and first char capitalisation.
|
||||||
"""
|
"""
|
||||||
@@ -302,7 +290,7 @@ def transferCase(source, target):
|
|||||||
return theResult
|
return theResult
|
||||||
|
|
||||||
|
|
||||||
def fuzzyTime(seconds):
|
def fuzzyTime(seconds: int) -> str:
|
||||||
"""Converts a time difference in seconds into a fuzzy time string.
|
"""Converts a time difference in seconds into a fuzzy time string.
|
||||||
"""
|
"""
|
||||||
if seconds < 0:
|
if seconds < 0:
|
||||||
@@ -363,7 +351,7 @@ def fuzzyTime(seconds):
|
|||||||
).format(int(round(seconds/31557600)))
|
).format(int(round(seconds/31557600)))
|
||||||
|
|
||||||
|
|
||||||
def numberToRoman(value, toLower=False):
|
def numberToRoman(value: int, toLower: bool = False) -> str:
|
||||||
"""Convert an integer to a Roman number.
|
"""Convert an integer to a Roman number.
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, int):
|
if not isinstance(value, int):
|
||||||
@@ -391,7 +379,7 @@ def numberToRoman(value, toLower=False):
|
|||||||
# Encoder Functions
|
# Encoder Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def jsonEncode(data, n=0, nmax=0):
|
def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
|
||||||
"""Encode a dictionary, list or tuple as a json object or array, and
|
"""Encode a dictionary, list or tuple as a json object or array, and
|
||||||
indent from level n up to a max level nmax if nmax is larger than 0.
|
indent from level n up to a max level nmax if nmax is larger than 0.
|
||||||
"""
|
"""
|
||||||
@@ -481,9 +469,8 @@ def xmlIndent(tree: ET.Element | ET.ElementTree):
|
|||||||
# File and File System Functions
|
# File and File System Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def readTextFile(path):
|
def readTextFile(path: str | Path) -> str:
|
||||||
"""Read the content of a text file in a robust manner.
|
"""Read the content of a text file in a robust manner."""
|
||||||
"""
|
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
if not path.is_file():
|
if not path.is_file():
|
||||||
return ""
|
return ""
|
||||||
@@ -495,17 +482,16 @@ def readTextFile(path):
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def makeFileNameSafe(value):
|
def makeFileNameSafe(text: str) -> str:
|
||||||
"""Returns a filename safe string of the value.
|
"""Return a filename safe string.
|
||||||
|
See: https://unicode.org/reports/tr15/#Norm_Forms
|
||||||
"""
|
"""
|
||||||
clean = ""
|
text = unicodedata.normalize("NFKC", text).strip()
|
||||||
for c in str(value).strip():
|
allowed = (" ", ".", "-", "_")
|
||||||
if c.isalpha() or c.isdigit() or c == " ":
|
return "".join(c for c in text if c.isalnum() or c in allowed)
|
||||||
clean += c
|
|
||||||
return clean
|
|
||||||
|
|
||||||
|
|
||||||
def sha256sum(path):
|
def sha256sum(path: str | Path) -> str | None:
|
||||||
"""Make a shasum of a file using a buffer.
|
"""Make a shasum of a file using a buffer.
|
||||||
Based on: https://stackoverflow.com/a/44873382/5825851
|
Based on: https://stackoverflow.com/a/44873382/5825851
|
||||||
"""
|
"""
|
||||||
@@ -528,7 +514,7 @@ def sha256sum(path):
|
|||||||
# Other Functions
|
# Other Functions
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
def getGuiItem(objName):
|
def getGuiItem(objName: str) -> QWidget | None:
|
||||||
"""Returns a QtWidget based on its objectName.
|
"""Returns a QtWidget based on its objectName.
|
||||||
"""
|
"""
|
||||||
for qWidget in qApp.topLevelWidgets():
|
for qWidget in qApp.topLevelWidgets():
|
||||||
@@ -542,50 +528,49 @@ def getGuiItem(objName):
|
|||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
|
|
||||||
class NWConfigParser(ConfigParser):
|
class NWConfigParser(ConfigParser):
|
||||||
|
"""Common: Adapted Config Parser
|
||||||
|
|
||||||
|
This is a subclass of the standard config parser that adds type safe
|
||||||
|
helper functions, and support for lists.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def rdStr(self, section, option, default):
|
def rdStr(self, section: str, option: str, default: str) -> str:
|
||||||
"""Read string value.
|
"""Read string value."""
|
||||||
"""
|
|
||||||
return self.get(section, option, fallback=default)
|
return self.get(section, option, fallback=default)
|
||||||
|
|
||||||
def rdInt(self, section, option, default):
|
def rdInt(self, section: str, option: str, default: int) -> int:
|
||||||
"""Read integer value.
|
"""Read integer value."""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
return self.getint(section, option, fallback=default)
|
return self.getint(section, option, fallback=default)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.error("Could not read '%s':'%s' from config", section, option)
|
logger.error("Could not read '%s':'%s' from config", section, option)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def rdFlt(self, section, option, default):
|
def rdFlt(self, section: str, option: str, default: float) -> float:
|
||||||
"""Read float value.
|
"""Read float value."""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
return self.getfloat(section, option, fallback=default)
|
return self.getfloat(section, option, fallback=default)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.error("Could not read '%s':'%s' from config", section, option)
|
logger.error("Could not read '%s':'%s' from config", section, option)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def rdBool(self, section, option, default):
|
def rdBool(self, section: str, option: str, default: bool) -> bool:
|
||||||
"""Read boolean value.
|
"""Read boolean value."""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
return self.getboolean(section, option, fallback=default)
|
return self.getboolean(section, option, fallback=default)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.error("Could not read '%s':'%s' from config", section, option)
|
logger.error("Could not read '%s':'%s' from config", section, option)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def rdPath(self, section, option, default):
|
def rdPath(self, section: str, option: str, default: Path) -> Path:
|
||||||
"""Read a path value.
|
"""Read a Path value."""
|
||||||
"""
|
|
||||||
return checkPath(self.get(section, option, fallback=default), default)
|
return checkPath(self.get(section, option, fallback=default), default)
|
||||||
|
|
||||||
def rdStrList(self, section, option, default):
|
def rdStrList(self, section: str, option: str, default: list[str]) -> list[str]:
|
||||||
"""Read string list.
|
"""Read string list."""
|
||||||
"""
|
|
||||||
result = default.copy() if isinstance(default, list) else []
|
result = default.copy() if isinstance(default, list) else []
|
||||||
if self.has_option(section, option):
|
if self.has_option(section, option):
|
||||||
data = self.get(section, option, fallback="").split(",")
|
data = self.get(section, option, fallback="").split(",")
|
||||||
@@ -593,9 +578,8 @@ class NWConfigParser(ConfigParser):
|
|||||||
result[i] = data[i].strip()
|
result[i] = data[i].strip()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def rdIntList(self, section, option, default):
|
def rdIntList(self, section: str, option: str, default: list[int]) -> list[int]:
|
||||||
"""Read integer list.
|
"""Read integer list."""
|
||||||
"""
|
|
||||||
result = default.copy() if isinstance(default, list) else []
|
result = default.copy() if isinstance(default, list) else []
|
||||||
if self.has_option(section, option):
|
if self.has_option(section, option):
|
||||||
data = self.get(section, option, fallback="").split(",")
|
data = self.get(section, option, fallback="").split(",")
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@@ -30,6 +32,7 @@ import logging
|
|||||||
from time import time
|
from time import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QFontDatabase
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
QT_VERSION, QT_VERSION_STR, PYQT_VERSION, PYQT_VERSION_STR, QStandardPaths,
|
QT_VERSION, QT_VERSION_STR, PYQT_VERSION, PYQT_VERSION_STR, QStandardPaths,
|
||||||
QSysInfo, QLocale, QLibraryInfo, QTranslator
|
QSysInfo, QLocale, QLibraryInfo, QTranslator
|
||||||
@@ -120,7 +123,7 @@ class Config:
|
|||||||
self.askBeforeBackup = True # Flag for asking before running automatic backup
|
self.askBeforeBackup = True # Flag for asking before running automatic backup
|
||||||
|
|
||||||
# Text Editor Settings
|
# Text Editor Settings
|
||||||
self.textFont = None # Editor font
|
self.textFont = "" # Editor font
|
||||||
self.textSize = 12 # Editor font size
|
self.textSize = 12 # Editor font size
|
||||||
self.textWidth = 700 # Editor text width
|
self.textWidth = 700 # Editor text width
|
||||||
self.textMargin = 40 # Editor/viewer text margin
|
self.textMargin = 40 # Editor/viewer text margin
|
||||||
@@ -335,40 +338,59 @@ class Config:
|
|||||||
logger.debug("Last path updated: %s" % self._lastPath)
|
logger.debug("Last path updated: %s" % self._lastPath)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBackupPath(self, backupPath):
|
def setBackupPath(self, backupPath: Path | None):
|
||||||
"""Set the current backup path."""
|
"""Set the current backup path."""
|
||||||
self._backupPath = checkPath(backupPath, None)
|
self._backupPath = checkPath(backupPath, None)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def setTextFont(self, family: str | None, pointSize: int = 12):
|
||||||
|
"""Set the text font if it exists. If it doesn't, or is None,
|
||||||
|
set to default font.
|
||||||
|
"""
|
||||||
|
fontDB = QFontDatabase()
|
||||||
|
fontFam = fontDB.families()
|
||||||
|
self.textSize = pointSize
|
||||||
|
if family is None or family not in fontFam:
|
||||||
|
logger.warning("Unknown font '%s'", family)
|
||||||
|
if self.osWindows and "Arial" in fontFam:
|
||||||
|
self.textFont = "Arial"
|
||||||
|
elif self.osDarwin and "Courier" in fontFam:
|
||||||
|
self.textFont = "Courier"
|
||||||
|
else:
|
||||||
|
self.textFont = fontDB.systemFont(QFontDatabase.GeneralFont).family()
|
||||||
|
else:
|
||||||
|
self.textFont = family
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def pxInt(self, theSize):
|
def pxInt(self, value: int) -> int:
|
||||||
"""Used to scale fixed gui sizes by the screen scale factor.
|
"""Used to scale fixed gui sizes by the screen scale factor.
|
||||||
This function returns an int, which is always rounded down.
|
This function returns an int, which is always rounded down.
|
||||||
"""
|
"""
|
||||||
return int(theSize*self.guiScale)
|
return int(value*self.guiScale)
|
||||||
|
|
||||||
def rpxInt(self, theSize):
|
def rpxInt(self, value: int) -> int:
|
||||||
"""Used to un-scale fixed gui sizes by the screen scale factor.
|
"""Used to un-scale fixed gui sizes by the screen scale factor.
|
||||||
This function returns an int, which is always rounded down.
|
This function returns an int, which is always rounded down.
|
||||||
"""
|
"""
|
||||||
return int(theSize/self.guiScale)
|
return int(value/self.guiScale)
|
||||||
|
|
||||||
def dataPath(self, target=None):
|
def dataPath(self, target: str | None = None) -> Path:
|
||||||
"""Return a path in the data folder."""
|
"""Return a path in the data folder."""
|
||||||
if isinstance(target, str):
|
if isinstance(target, str):
|
||||||
return self._dataPath / target
|
return self._dataPath / target
|
||||||
return self._dataPath
|
return self._dataPath
|
||||||
|
|
||||||
def assetPath(self, target=None):
|
def assetPath(self, target: str | None = None) -> Path:
|
||||||
"""Return a path in the assets folder."""
|
"""Return a path in the assets folder."""
|
||||||
if isinstance(target, str):
|
if isinstance(target, str):
|
||||||
return self._appPath / "assets" / target
|
return self._appPath / "assets" / target
|
||||||
return self._appPath / "assets"
|
return self._appPath / "assets"
|
||||||
|
|
||||||
def lastPath(self):
|
def lastPath(self) -> Path:
|
||||||
"""Return the last path used by the user, but ensure it exists.
|
"""Return the last path used by the user, but ensure it exists.
|
||||||
"""
|
"""
|
||||||
if isinstance(self._lastPath, Path):
|
if isinstance(self._lastPath, Path):
|
||||||
@@ -376,14 +398,14 @@ class Config:
|
|||||||
return self._lastPath
|
return self._lastPath
|
||||||
return self._homePath
|
return self._homePath
|
||||||
|
|
||||||
def backupPath(self):
|
def backupPath(self) -> Path | None:
|
||||||
"""Return the backup path."""
|
"""Return the backup path."""
|
||||||
if isinstance(self._backupPath, Path):
|
if isinstance(self._backupPath, Path):
|
||||||
if self._backupPath.is_dir():
|
if self._backupPath.is_dir():
|
||||||
return self._backupPath
|
return self._backupPath
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def errorText(self):
|
def errorText(self) -> str:
|
||||||
"""Compile and return error messages from the initialisation of
|
"""Compile and return error messages from the initialisation of
|
||||||
the Config class, and clear the error buffer.
|
the Config class, and clear the error buffer.
|
||||||
"""
|
"""
|
||||||
@@ -392,7 +414,7 @@ class Config:
|
|||||||
self._errData = []
|
self._errData = []
|
||||||
return errMessage
|
return errMessage
|
||||||
|
|
||||||
def listLanguages(self, lngSet):
|
def listLanguages(self, lngSet: int) -> list[tuple[str, str]]:
|
||||||
"""List localisation files in the i18n folder. The default GUI
|
"""List localisation files in the i18n folder. The default GUI
|
||||||
language is British English (en_GB).
|
language is British English (en_GB).
|
||||||
"""
|
"""
|
||||||
@@ -423,7 +445,7 @@ class Config:
|
|||||||
# Config Actions
|
# Config Actions
|
||||||
##
|
##
|
||||||
|
|
||||||
def initConfig(self, confPath=None, dataPath=None):
|
def initConfig(self, confPath: str | Path | None = None, dataPath: str | Path | None = None):
|
||||||
"""Initialise the config class. The manual setting of confPath
|
"""Initialise the config class. The manual setting of confPath
|
||||||
and dataPath is mainly intended for the test suite.
|
and dataPath is mainly intended for the test suite.
|
||||||
"""
|
"""
|
||||||
@@ -449,9 +471,10 @@ class Config:
|
|||||||
|
|
||||||
# Also create the syntax, themes and icons folders if possible
|
# Also create the syntax, themes and icons folders if possible
|
||||||
if self._dataPath.is_dir():
|
if self._dataPath.is_dir():
|
||||||
|
(self._dataPath / "cache").mkdir(exist_ok=True)
|
||||||
|
(self._dataPath / "icons").mkdir(exist_ok=True)
|
||||||
(self._dataPath / "syntax").mkdir(exist_ok=True)
|
(self._dataPath / "syntax").mkdir(exist_ok=True)
|
||||||
(self._dataPath / "themes").mkdir(exist_ok=True)
|
(self._dataPath / "themes").mkdir(exist_ok=True)
|
||||||
(self._dataPath / "icons").mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
# Check if config file exists, and load it. If not, we save defaults
|
# Check if config file exists, and load it. If not, we save defaults
|
||||||
if (self._confPath / nwFiles.CONF_FILE).is_file():
|
if (self._confPath / nwFiles.CONF_FILE).is_file():
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
|
from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwOutline
|
from novelwriter.enum import nwBuildFmt, nwItemClass, nwItemLayout, nwOutline
|
||||||
|
|
||||||
|
|
||||||
def trConst(tString):
|
def trConst(tString):
|
||||||
@@ -75,16 +75,22 @@ class nwHeaders:
|
|||||||
|
|
||||||
class nwFiles:
|
class nwFiles:
|
||||||
|
|
||||||
|
# Config Files
|
||||||
CONF_FILE = "novelwriter.conf"
|
CONF_FILE = "novelwriter.conf"
|
||||||
|
RECENT_FILE = "recentProjects.json"
|
||||||
|
|
||||||
|
# Project Root Files
|
||||||
PROJ_FILE = "nwProject.nwx"
|
PROJ_FILE = "nwProject.nwx"
|
||||||
PROJ_DICT = "wordlist.txt"
|
PROJ_BACKUP = "nwProject.bak"
|
||||||
PROJ_LOCK = "nwProject.lock"
|
PROJ_LOCK = "nwProject.lock"
|
||||||
TOC_TXT = "ToC.txt"
|
TOC_TXT = "ToC.txt"
|
||||||
SESS_STATS = "sessionStats.log"
|
|
||||||
|
# Project Meta Files
|
||||||
|
BUILDS_FILE = "builds.json"
|
||||||
INDEX_FILE = "tagsIndex.json"
|
INDEX_FILE = "tagsIndex.json"
|
||||||
OPTS_FILE = "guiOptions.json"
|
OPTS_FILE = "guiOptions.json"
|
||||||
RECENT_FILE = "recentProjects.json"
|
PROJ_DICT = "wordlist.txt"
|
||||||
BUILD_CACHE = "prevBuild.json"
|
SESS_STATS = "sessionStats.log"
|
||||||
|
|
||||||
# END Class nwFiles
|
# END Class nwFiles
|
||||||
|
|
||||||
@@ -199,10 +205,45 @@ class nwLabels:
|
|||||||
nwOutline.CUSTOM: KEY_NAME[nwKeyWords.CUSTOM_KEY],
|
nwOutline.CUSTOM: KEY_NAME[nwKeyWords.CUSTOM_KEY],
|
||||||
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
|
nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"),
|
||||||
}
|
}
|
||||||
|
BUILD_FMT = {
|
||||||
|
nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"),
|
||||||
|
nwBuildFmt.FODT: QT_TRANSLATE_NOOP("Constant", "Flat Open Document (.fodt)"),
|
||||||
|
nwBuildFmt.HTML: QT_TRANSLATE_NOOP("Constant", "novelWriter HTML (.html)"),
|
||||||
|
nwBuildFmt.NWD: QT_TRANSLATE_NOOP("Constant", "novelWriter Markup (.txt)"),
|
||||||
|
nwBuildFmt.STD_MD: QT_TRANSLATE_NOOP("Constant", "Standard Markdown (.md)"),
|
||||||
|
nwBuildFmt.EXT_MD: QT_TRANSLATE_NOOP("Constant", "Extended Markdown (.md)"),
|
||||||
|
nwBuildFmt.J_HTML: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter HTML (.json)"),
|
||||||
|
nwBuildFmt.J_NWD: QT_TRANSLATE_NOOP("Constant", "JSON + novelWriter Markup (.json)"),
|
||||||
|
}
|
||||||
|
BUILD_EXT = {
|
||||||
|
nwBuildFmt.ODT: ".odt",
|
||||||
|
nwBuildFmt.FODT: ".fodt",
|
||||||
|
nwBuildFmt.HTML: ".html",
|
||||||
|
nwBuildFmt.NWD: ".txt",
|
||||||
|
nwBuildFmt.STD_MD: ".md",
|
||||||
|
nwBuildFmt.EXT_MD: ".md",
|
||||||
|
nwBuildFmt.J_HTML: ".json",
|
||||||
|
nwBuildFmt.J_NWD: ".json",
|
||||||
|
}
|
||||||
|
|
||||||
# END Class nwLabels
|
# END Class nwLabels
|
||||||
|
|
||||||
|
|
||||||
|
class nwHeadFmt:
|
||||||
|
|
||||||
|
TITLE = "{Title}"
|
||||||
|
CH_NUM = "{Chapter}"
|
||||||
|
CH_WORD = "{Chapter:Word}"
|
||||||
|
CH_ROMU = "{Chapter:URoman}"
|
||||||
|
CH_ROML = "{Chapter:LRoman}"
|
||||||
|
SC_NUM = "{Scene}"
|
||||||
|
SC_ABS = "{Scene:Abs}"
|
||||||
|
|
||||||
|
ALL = [TITLE, CH_NUM, CH_WORD, CH_ROMU, CH_ROML, SC_NUM, SC_ABS]
|
||||||
|
|
||||||
|
# END Class nwHeadFmt
|
||||||
|
|
||||||
|
|
||||||
class nwQuotes:
|
class nwQuotes:
|
||||||
"""Allowed quotation marks.
|
"""Allowed quotation marks.
|
||||||
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
Source: https://en.wikipedia.org/wiki/Quotation_mark
|
||||||
|
|||||||
@@ -0,0 +1,532 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Build Settings
|
||||||
|
============================
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2023-02-14 [2.1b1] BuildSettings
|
||||||
|
Created: 2023-05-22 [2.1b1] BuildCollection
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Iterable
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QT_TRANSLATE_NOOP
|
||||||
|
|
||||||
|
from novelwriter import CONFIG
|
||||||
|
from novelwriter.enum import nwBuildFmt
|
||||||
|
from novelwriter.error import logException
|
||||||
|
from novelwriter.common import checkUuid, isHandle, jsonEncode
|
||||||
|
from novelwriter.constants import nwFiles, nwHeadFmt
|
||||||
|
from novelwriter.core.item import NWItem
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# The Settings Template
|
||||||
|
# =====================
|
||||||
|
# Each entry contains a tuple on the form:
|
||||||
|
# (type, default, [min value, max value])
|
||||||
|
|
||||||
|
SETTINGS_TEMPLATE = {
|
||||||
|
"filter.includeNovel": (bool, True),
|
||||||
|
"filter.includeNotes": (bool, False),
|
||||||
|
"filter.includeInactive": (bool, False),
|
||||||
|
"headings.fmtTitle": (str, nwHeadFmt.TITLE),
|
||||||
|
"headings.fmtChapter": (str, nwHeadFmt.TITLE),
|
||||||
|
"headings.fmtUnnumbered": (str, nwHeadFmt.TITLE),
|
||||||
|
"headings.fmtScene": (str, "* * *"),
|
||||||
|
"headings.fmtSection": (str, ""),
|
||||||
|
"headings.hideScene": (bool, False),
|
||||||
|
"headings.hideSection": (bool, True),
|
||||||
|
"text.includeSynopsis": (bool, False),
|
||||||
|
"text.includeComments": (bool, False),
|
||||||
|
"text.includeKeywords": (bool, False),
|
||||||
|
"text.includeBodyText": (bool, True),
|
||||||
|
"text.addNoteHeadings": (bool, True),
|
||||||
|
"format.buildLang": (str, "en_GB"),
|
||||||
|
"format.textFont": (str, CONFIG.textFont),
|
||||||
|
"format.textSize": (int, 12),
|
||||||
|
"format.lineHeight": (float, 1.15, 0.75, 3.0),
|
||||||
|
"format.justifyText": (bool, False),
|
||||||
|
"format.stripUnicode": (bool, False),
|
||||||
|
"format.replaceTabs": (bool, False),
|
||||||
|
"odt.addColours": (bool, True),
|
||||||
|
"html.addStyles": (bool, True),
|
||||||
|
}
|
||||||
|
|
||||||
|
SETTINGS_LABELS = {
|
||||||
|
"filter": QT_TRANSLATE_NOOP("Builds", "Document Types"),
|
||||||
|
"filter.includeNovel": QT_TRANSLATE_NOOP("Builds", "Novel Documents"),
|
||||||
|
"filter.includeNotes": QT_TRANSLATE_NOOP("Builds", "Project Notes"),
|
||||||
|
"filter.includeInactive": QT_TRANSLATE_NOOP("Builds", "Inactive Documents"),
|
||||||
|
|
||||||
|
"headings": QT_TRANSLATE_NOOP("Builds", "Headings"),
|
||||||
|
"headings.fmtTitle": QT_TRANSLATE_NOOP("Builds", "Title Headings"),
|
||||||
|
"headings.fmtChapter": QT_TRANSLATE_NOOP("Builds", "Chapter Headings"),
|
||||||
|
"headings.fmtUnnumbered": QT_TRANSLATE_NOOP("Builds", "Unnumbered Headings"),
|
||||||
|
"headings.fmtScene": QT_TRANSLATE_NOOP("Builds", "Scene Headings"),
|
||||||
|
"headings.fmtSection": QT_TRANSLATE_NOOP("Builds", "Section Headings"),
|
||||||
|
"headings.hideScene": QT_TRANSLATE_NOOP("Builds", "Hide Scene Headings"),
|
||||||
|
"headings.hideSection": QT_TRANSLATE_NOOP("Builds", "Hide Section Headings"),
|
||||||
|
|
||||||
|
"text.grpContent": QT_TRANSLATE_NOOP("Builds", "Text Content"),
|
||||||
|
"text.includeSynopsis": QT_TRANSLATE_NOOP("Builds", "Include Synopsis"),
|
||||||
|
"text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"),
|
||||||
|
"text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"),
|
||||||
|
"text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"),
|
||||||
|
"text.grpInsert": QT_TRANSLATE_NOOP("Builds", "Insert Content"),
|
||||||
|
"text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"),
|
||||||
|
|
||||||
|
"format.grpFormat": QT_TRANSLATE_NOOP("Builds", "Text Format"),
|
||||||
|
"format.buildLang": QT_TRANSLATE_NOOP("Builds", "Document Language"),
|
||||||
|
"format.textFont": QT_TRANSLATE_NOOP("Builds", "Font Family"),
|
||||||
|
"format.textSize": QT_TRANSLATE_NOOP("Builds", "Font Size"),
|
||||||
|
"format.lineHeight": QT_TRANSLATE_NOOP("Builds", "Line Height"),
|
||||||
|
"format.grpOptions": QT_TRANSLATE_NOOP("Builds", "Text Options"),
|
||||||
|
"format.justifyText": QT_TRANSLATE_NOOP("Builds", "Justify Text Margins"),
|
||||||
|
"format.stripUnicode": QT_TRANSLATE_NOOP("Builds", "Replace Unicode Characters"),
|
||||||
|
"format.replaceTabs": QT_TRANSLATE_NOOP("Builds", "Replace Tabs with Spaces"),
|
||||||
|
|
||||||
|
"odt": QT_TRANSLATE_NOOP("Builds", "Open Document"),
|
||||||
|
"odt.addColours": QT_TRANSLATE_NOOP("Builds", "Add Highlight Colours"),
|
||||||
|
|
||||||
|
"html": QT_TRANSLATE_NOOP("Builds", "HTML"),
|
||||||
|
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FilterMode(Enum):
|
||||||
|
"""The decision reason for an item in a filtered project."""
|
||||||
|
|
||||||
|
UNKNOWN = 0
|
||||||
|
FILTERED = 1
|
||||||
|
INCLUDED = 2
|
||||||
|
EXCLUDED = 3
|
||||||
|
SKIPPED = 4
|
||||||
|
ROOT = 5
|
||||||
|
|
||||||
|
# END Enum FilterMode
|
||||||
|
|
||||||
|
|
||||||
|
class BuildSettings:
|
||||||
|
"""Core: Build Settings Class
|
||||||
|
|
||||||
|
This class manages the build settings for a Manuscript build job.
|
||||||
|
The settings can be packed/unpacked to/from a dictionary for JSON.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._name = ""
|
||||||
|
self._uuid = str(uuid.uuid4())
|
||||||
|
self._path = Path.home()
|
||||||
|
self._build = ""
|
||||||
|
self._format = nwBuildFmt.ODT
|
||||||
|
self._skipRoot = set()
|
||||||
|
self._excluded = set()
|
||||||
|
self._included = set()
|
||||||
|
self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()}
|
||||||
|
self._changed = False
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Properties
|
||||||
|
##
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
"""The build name."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def buildID(self) -> str:
|
||||||
|
"""The build ID as an UUID."""
|
||||||
|
return self._uuid
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lastPath(self) -> Path:
|
||||||
|
"""The last used build path."""
|
||||||
|
if self._path.is_dir():
|
||||||
|
return self._path
|
||||||
|
return Path.home()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lastBuildName(self) -> str:
|
||||||
|
"""The last used build name."""
|
||||||
|
return self._build
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lastFormat(self) -> nwBuildFmt:
|
||||||
|
"""The last used build format."""
|
||||||
|
return self._format
|
||||||
|
|
||||||
|
@property
|
||||||
|
def changed(self) -> bool:
|
||||||
|
"""The changed status of the build."""
|
||||||
|
return self._changed
|
||||||
|
|
||||||
|
##
|
||||||
|
# Getters
|
||||||
|
##
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getLabel(key: str) -> str:
|
||||||
|
"""Extract the GUI label for a specific setting."""
|
||||||
|
return SETTINGS_LABELS.get(key, "ERROR")
|
||||||
|
|
||||||
|
def getStr(self, key: str) -> str:
|
||||||
|
"""Type safe value access for strings."""
|
||||||
|
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1]))
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
def getBool(self, key: str) -> bool:
|
||||||
|
"""Type safe value access for bools."""
|
||||||
|
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1]))
|
||||||
|
return bool(value)
|
||||||
|
|
||||||
|
def getInt(self, key: str) -> int:
|
||||||
|
"""Type safe value access for integers."""
|
||||||
|
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1]))
|
||||||
|
if isinstance(value, int):
|
||||||
|
return value
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def getFloat(self, key: str) -> float:
|
||||||
|
"""Type safe value access for floats."""
|
||||||
|
value = self._settings.get(key, SETTINGS_TEMPLATE.get(key, (None, None)[1]))
|
||||||
|
if isinstance(value, float):
|
||||||
|
return value
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
##
|
||||||
|
# Setters
|
||||||
|
##
|
||||||
|
|
||||||
|
def setName(self, name: str):
|
||||||
|
"""Set the build setting display name."""
|
||||||
|
self._name = str(name)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setBuildID(self, value: str | uuid.UUID):
|
||||||
|
"""Set a UUID build ID."""
|
||||||
|
value = checkUuid(value, "")
|
||||||
|
if not value:
|
||||||
|
self._uuid = str(uuid.uuid4())
|
||||||
|
elif value != self._uuid:
|
||||||
|
self._uuid = value
|
||||||
|
return
|
||||||
|
|
||||||
|
def setLastPath(self, path: Path | str | None):
|
||||||
|
"""Set the last used build path."""
|
||||||
|
if isinstance(path, str):
|
||||||
|
path = Path(path)
|
||||||
|
if isinstance(path, Path) and path.is_dir():
|
||||||
|
self._path = path
|
||||||
|
else:
|
||||||
|
self._path = Path.home()
|
||||||
|
self._changed = True
|
||||||
|
return
|
||||||
|
|
||||||
|
def setLastBuildName(self, name: str):
|
||||||
|
"""Set the last used build name."""
|
||||||
|
self._build = str(name).strip()
|
||||||
|
self._changed = True
|
||||||
|
return
|
||||||
|
|
||||||
|
def setLastFormat(self, value: nwBuildFmt):
|
||||||
|
"""Set the last used build format."""
|
||||||
|
if isinstance(value, nwBuildFmt):
|
||||||
|
self._format = value
|
||||||
|
self._changed = True
|
||||||
|
return
|
||||||
|
|
||||||
|
def setFiltered(self, tHandle: str):
|
||||||
|
"""Set an item as filtered."""
|
||||||
|
self._excluded.discard(tHandle)
|
||||||
|
self._included.discard(tHandle)
|
||||||
|
self._changed = True
|
||||||
|
return
|
||||||
|
|
||||||
|
def setIncluded(self, tHandle: str):
|
||||||
|
"""Set an item as explicitly included."""
|
||||||
|
self._excluded.discard(tHandle)
|
||||||
|
self._included.add(tHandle)
|
||||||
|
self._changed = True
|
||||||
|
return
|
||||||
|
|
||||||
|
def setExcluded(self, tHandle: str):
|
||||||
|
"""Set an item as explicitly excluded."""
|
||||||
|
self._excluded.add(tHandle)
|
||||||
|
self._included.discard(tHandle)
|
||||||
|
self._changed = True
|
||||||
|
return
|
||||||
|
|
||||||
|
def setSkipRoot(self, tHandle: str, state: bool):
|
||||||
|
"""Set a specific root folder as skipped or not."""
|
||||||
|
if state is True:
|
||||||
|
self._skipRoot.discard(tHandle)
|
||||||
|
self._changed = True
|
||||||
|
elif state is False:
|
||||||
|
self._skipRoot.add(tHandle)
|
||||||
|
self._changed = True
|
||||||
|
return
|
||||||
|
|
||||||
|
def setValue(self, key: str, value: str | int | bool | float) -> bool:
|
||||||
|
"""Set a specific value for a build setting."""
|
||||||
|
if key not in SETTINGS_TEMPLATE:
|
||||||
|
return False
|
||||||
|
definition = SETTINGS_TEMPLATE[key]
|
||||||
|
if not isinstance(value, definition[0]):
|
||||||
|
return False
|
||||||
|
if len(definition) == 4:
|
||||||
|
value = min(max(value, definition[2]), definition[3])
|
||||||
|
self._changed = value != self._settings[key]
|
||||||
|
self._settings[key] = value
|
||||||
|
logger.debug(f"Build Setting '{key}' set to: {value}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def isRootAllowed(self, tHandle: str) -> bool:
|
||||||
|
"""Check if a root handle is allowed in the build."""
|
||||||
|
return tHandle not in self._skipRoot
|
||||||
|
|
||||||
|
def buildItemFilter(
|
||||||
|
self, project: NWProject, withRoots: bool = False
|
||||||
|
) -> dict[str, tuple[bool, FilterMode]]:
|
||||||
|
"""Return a dictionary of item handles with filter decissions
|
||||||
|
applied.
|
||||||
|
"""
|
||||||
|
result: dict[str, tuple[bool, FilterMode]] = {}
|
||||||
|
if not isinstance(project, NWProject):
|
||||||
|
return result
|
||||||
|
|
||||||
|
incNovel = bool(self.getBool("filter.includeNovel"))
|
||||||
|
incNotes = bool(self.getBool("filter.includeNotes"))
|
||||||
|
incInactive = bool(self.getBool("filter.includeInactive"))
|
||||||
|
|
||||||
|
for item in project.tree:
|
||||||
|
tHandle = item.itemHandle
|
||||||
|
if not tHandle:
|
||||||
|
continue
|
||||||
|
if not isinstance(item, NWItem):
|
||||||
|
result[tHandle] = (False, FilterMode.UNKNOWN)
|
||||||
|
continue
|
||||||
|
if item.isInactiveClass() or (item.itemRoot in self._skipRoot):
|
||||||
|
result[tHandle] = (False, FilterMode.SKIPPED)
|
||||||
|
continue
|
||||||
|
if withRoots and item.isRootType():
|
||||||
|
result[tHandle] = (True, FilterMode.ROOT)
|
||||||
|
continue
|
||||||
|
if not item.isFileType():
|
||||||
|
result[tHandle] = (False, FilterMode.SKIPPED)
|
||||||
|
continue
|
||||||
|
if tHandle in self._included:
|
||||||
|
result[tHandle] = (True, FilterMode.INCLUDED)
|
||||||
|
continue
|
||||||
|
if tHandle in self._excluded:
|
||||||
|
result[tHandle] = (False, FilterMode.EXCLUDED)
|
||||||
|
continue
|
||||||
|
|
||||||
|
isNote = item.isNoteLayout()
|
||||||
|
isNovel = item.isDocumentLayout()
|
||||||
|
isActive = item.isActive
|
||||||
|
|
||||||
|
byActive = isActive or (not isActive and incInactive)
|
||||||
|
byLayout = (isNote and incNotes) or (isNovel and incNovel)
|
||||||
|
|
||||||
|
isAllowed = byActive and byLayout
|
||||||
|
|
||||||
|
result[tHandle] = (isAllowed, FilterMode.FILTERED)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def resetChangedState(self):
|
||||||
|
"""Reset the changed status of the settings object. This must be
|
||||||
|
called when the changes have been safely saved or passed on.
|
||||||
|
"""
|
||||||
|
self._changed = False
|
||||||
|
return
|
||||||
|
|
||||||
|
def pack(self) -> dict:
|
||||||
|
"""Pack all content into a JSON compatible dictionary."""
|
||||||
|
logger.debug("Collecting build setting for '%s'", self._name)
|
||||||
|
return {
|
||||||
|
"name": self._name,
|
||||||
|
"uuid": self._uuid,
|
||||||
|
"path": str(self._path),
|
||||||
|
"build": self._build,
|
||||||
|
"format": self._format.name,
|
||||||
|
"settings": self._settings.copy(),
|
||||||
|
"content": {
|
||||||
|
"included": list(self._included),
|
||||||
|
"excluded": list(self._excluded),
|
||||||
|
"skipRoot": list(self._skipRoot),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def unpack(self, data: dict):
|
||||||
|
"""Unpack a dictionary and populate the class."""
|
||||||
|
settings = data.get("settings", {})
|
||||||
|
content = data.get("content", {})
|
||||||
|
included = content.get("included", [])
|
||||||
|
excluded = content.get("excluded", [])
|
||||||
|
skipRoot = content.get("skipRoot", [])
|
||||||
|
|
||||||
|
self.setName(data.get("name", ""))
|
||||||
|
self.setBuildID(data.get("uuid", ""))
|
||||||
|
self.setLastPath(data.get("path", None))
|
||||||
|
self.setLastBuildName(data.get("build", ""))
|
||||||
|
|
||||||
|
buildFmt = str(data.get("build", ""))
|
||||||
|
if buildFmt in nwBuildFmt.__members__:
|
||||||
|
self.setLastFormat(nwBuildFmt[buildFmt])
|
||||||
|
|
||||||
|
if isinstance(included, list):
|
||||||
|
self._included = set([h for h in included if isHandle(h)])
|
||||||
|
if isinstance(excluded, list):
|
||||||
|
self._excluded = set([h for h in excluded if isHandle(h)])
|
||||||
|
if isinstance(skipRoot, list):
|
||||||
|
self._skipRoot = set([h for h in skipRoot if isHandle(h)])
|
||||||
|
|
||||||
|
self._settings = {k: v[1] for k, v in SETTINGS_TEMPLATE.items()}
|
||||||
|
if isinstance(settings, dict):
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.setValue(key, value)
|
||||||
|
|
||||||
|
self._changed = False
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class BuildSettings
|
||||||
|
|
||||||
|
|
||||||
|
class BuildCollection:
|
||||||
|
"""Core: Build Collection Class
|
||||||
|
|
||||||
|
This object holds all the build setting objects defined by the given
|
||||||
|
project. The build settings are saved as a single JSON file in the
|
||||||
|
project folder.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, project: NWProject):
|
||||||
|
self._project = project
|
||||||
|
self._builds = {}
|
||||||
|
self._loadCollection()
|
||||||
|
return
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""Return the number of builds."""
|
||||||
|
return len(self._builds)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def getBuild(self, buildID: str) -> BuildSettings | None:
|
||||||
|
"""Get a specific build settings object."""
|
||||||
|
if buildID not in self._builds:
|
||||||
|
return None
|
||||||
|
build = BuildSettings()
|
||||||
|
build.unpack(self._builds[buildID])
|
||||||
|
return build
|
||||||
|
|
||||||
|
def setBuild(self, build: BuildSettings) -> bool:
|
||||||
|
"""Set build settings data in the collection."""
|
||||||
|
if not isinstance(build, BuildSettings):
|
||||||
|
return False
|
||||||
|
buildID = build.buildID
|
||||||
|
self._builds[buildID] = build.pack()
|
||||||
|
self._saveCollection()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def removeBuild(self, buildID: str):
|
||||||
|
"""Remove the a build from the collection."""
|
||||||
|
self._builds.pop(buildID, None)
|
||||||
|
self._saveCollection()
|
||||||
|
return
|
||||||
|
|
||||||
|
def builds(self) -> Iterable[tuple[str, str]]:
|
||||||
|
"""Iterate over all avaiable builds."""
|
||||||
|
for buildID in self._builds:
|
||||||
|
yield buildID, self._builds[buildID].get("name", "")
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _loadCollection(self) -> bool:
|
||||||
|
"""Load build collections file."""
|
||||||
|
buildsFile = self._project.storage.getMetaFile(nwFiles.BUILDS_FILE)
|
||||||
|
if not isinstance(buildsFile, Path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
if buildsFile.exists():
|
||||||
|
logger.debug("Loading builds file")
|
||||||
|
try:
|
||||||
|
with open(buildsFile, mode="r", encoding="utf-8") as inFile:
|
||||||
|
data = json.load(inFile)
|
||||||
|
except Exception:
|
||||||
|
logger.error("Failed to load builds file")
|
||||||
|
logException()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
logger.error("Builds file is not a JSON object")
|
||||||
|
return False
|
||||||
|
|
||||||
|
builds = data.get("novelWriter.builds", None)
|
||||||
|
if not isinstance(builds, dict):
|
||||||
|
logger.error("No novelWriter.builds in the builds file")
|
||||||
|
return False
|
||||||
|
|
||||||
|
for key, entry in builds.items():
|
||||||
|
if isinstance(entry, dict):
|
||||||
|
self._builds[key] = entry
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _saveCollection(self) -> bool:
|
||||||
|
"""Save build collections file."""
|
||||||
|
buildsFile = self._project.storage.getMetaFile(nwFiles.BUILDS_FILE)
|
||||||
|
if not isinstance(buildsFile, Path):
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.debug("Saving builds file")
|
||||||
|
try:
|
||||||
|
data = {"novelWriter.builds": self._builds}
|
||||||
|
with open(buildsFile, mode="w+", encoding="utf-8") as outFile:
|
||||||
|
outFile.write(jsonEncode(data, nmax=4))
|
||||||
|
except Exception:
|
||||||
|
logger.error("Failed to save builds file")
|
||||||
|
logException()
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# END Class BuildCollection
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Manuscript Document Builder
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2022-12-01 [2.1b1] NWBuildDocument
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from typing import Iterable
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QFont, QFontInfo
|
||||||
|
|
||||||
|
from novelwriter import CONFIG
|
||||||
|
from novelwriter.enum import nwBuildFmt
|
||||||
|
from novelwriter.error import formatException, logException
|
||||||
|
from novelwriter.core.tomd import ToMarkdown
|
||||||
|
from novelwriter.core.toodt import ToOdt
|
||||||
|
from novelwriter.core.tohtml import ToHtml
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
from novelwriter.core.tokenizer import Tokenizer
|
||||||
|
from novelwriter.core.buildsettings import BuildSettings
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NWBuildDocument:
|
||||||
|
"""Core: Manuscript Document Build Class
|
||||||
|
|
||||||
|
This is the core tool that assembles a project and outputs a
|
||||||
|
manuscript, based on a build definition object (BuildSettings).
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ("_project", "_build", "_queue", "_error", "_cache")
|
||||||
|
|
||||||
|
def __init__(self, project: NWProject, build: BuildSettings):
|
||||||
|
self._project = project
|
||||||
|
self._build = build
|
||||||
|
self._queue = []
|
||||||
|
self._error = None
|
||||||
|
self._cache = None
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Properties
|
||||||
|
##
|
||||||
|
|
||||||
|
@property
|
||||||
|
def error(self) -> str | None:
|
||||||
|
"""Return the last error, if any."""
|
||||||
|
return self._error
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lastBuild(self) -> Tokenizer | None:
|
||||||
|
"""Return the build object of the last build process, if any.
|
||||||
|
This is useful for accessing build details and data after the
|
||||||
|
build job is completed.
|
||||||
|
"""
|
||||||
|
return self._cache
|
||||||
|
|
||||||
|
##
|
||||||
|
# Special Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
"""Return the length of the build queue."""
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def addDocument(self, tHandle: str):
|
||||||
|
"""Add a document to the build queue manually."""
|
||||||
|
self._queue.append(tHandle)
|
||||||
|
return
|
||||||
|
|
||||||
|
def queueAll(self):
|
||||||
|
"""Queue all document as defined by the build settings."""
|
||||||
|
filtered = self._build.buildItemFilter(self._project)
|
||||||
|
noteTitles = self._build.getBool("text.addNoteHeadings")
|
||||||
|
for item in self._project.tree:
|
||||||
|
if not item.itemHandle:
|
||||||
|
continue
|
||||||
|
if filtered.get(item.itemHandle, False):
|
||||||
|
self._queue.append(item.itemHandle)
|
||||||
|
elif item.isRootType() and noteTitles:
|
||||||
|
self._queue.append(item.itemHandle)
|
||||||
|
return
|
||||||
|
|
||||||
|
def iterBuild(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]:
|
||||||
|
"""Wrapper for builders based on format."""
|
||||||
|
if bFormat in (nwBuildFmt.ODT, nwBuildFmt.FODT):
|
||||||
|
yield from self.iterBuildOpenDocument(path, bFormat == nwBuildFmt.FODT)
|
||||||
|
elif bFormat in (nwBuildFmt.HTML, nwBuildFmt.J_HTML):
|
||||||
|
yield from self.iterBuildHTML(path, asJson=bFormat == nwBuildFmt.J_HTML)
|
||||||
|
elif bFormat in (nwBuildFmt.STD_MD, nwBuildFmt.EXT_MD):
|
||||||
|
yield from self.iterBuildMarkdown(path, bFormat == nwBuildFmt.EXT_MD)
|
||||||
|
elif bFormat in (nwBuildFmt.NWD, nwBuildFmt.J_NWD):
|
||||||
|
yield from self.iterBuildNWD(path, asJson=bFormat == nwBuildFmt.J_NWD)
|
||||||
|
return
|
||||||
|
|
||||||
|
def iterBuildOpenDocument(self, path: Path, isFlat: bool) -> Iterable[tuple[int, bool]]:
|
||||||
|
"""Build an Open Document file."""
|
||||||
|
makeObj = ToOdt(self._project, isFlat=isFlat)
|
||||||
|
filtered = self._setupBuild(makeObj)
|
||||||
|
makeObj.initDocument()
|
||||||
|
|
||||||
|
for i, tHandle in enumerate(self._queue):
|
||||||
|
self._error = None
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
yield i, self._doBuild(makeObj, tHandle)
|
||||||
|
else:
|
||||||
|
yield i, False
|
||||||
|
|
||||||
|
makeObj.closeDocument()
|
||||||
|
|
||||||
|
self._error = None
|
||||||
|
self._cache = makeObj
|
||||||
|
|
||||||
|
try:
|
||||||
|
if isFlat:
|
||||||
|
makeObj.saveFlatXML(path)
|
||||||
|
else:
|
||||||
|
makeObj.saveOpenDocText(path)
|
||||||
|
except Exception as exc:
|
||||||
|
logException()
|
||||||
|
self._error = formatException(exc)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def iterBuildHTML(self, path: Path | None, asJson: bool = False) -> Iterable[tuple[int, bool]]:
|
||||||
|
"""Build an HTML file. If path is None, no file is saved. This
|
||||||
|
is used for generating build previews.
|
||||||
|
"""
|
||||||
|
makeObj = ToHtml(self._project)
|
||||||
|
filtered = self._setupBuild(makeObj)
|
||||||
|
|
||||||
|
if self._build.getBool("format.replaceTabs"):
|
||||||
|
makeObj.replaceTabs()
|
||||||
|
|
||||||
|
for i, tHandle in enumerate(self._queue):
|
||||||
|
self._error = None
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
yield i, self._doBuild(makeObj, tHandle)
|
||||||
|
else:
|
||||||
|
yield i, False
|
||||||
|
|
||||||
|
self._error = None
|
||||||
|
self._cache = makeObj
|
||||||
|
|
||||||
|
if isinstance(path, Path):
|
||||||
|
try:
|
||||||
|
if asJson:
|
||||||
|
makeObj.saveHtmlJson(path)
|
||||||
|
else:
|
||||||
|
makeObj.saveHtml5(path)
|
||||||
|
except Exception as exc:
|
||||||
|
logException()
|
||||||
|
self._error = formatException(exc)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def iterBuildMarkdown(self, path: Path, extendedMd: bool) -> Iterable[tuple[int, bool]]:
|
||||||
|
"""Build a Markdown file."""
|
||||||
|
makeObj = ToMarkdown(self._project)
|
||||||
|
filtered = self._setupBuild(makeObj)
|
||||||
|
|
||||||
|
if extendedMd:
|
||||||
|
makeObj.setGitHubMarkdown()
|
||||||
|
else:
|
||||||
|
makeObj.setStandardMarkdown()
|
||||||
|
|
||||||
|
if self._build.getBool("format.replaceTabs"):
|
||||||
|
makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
|
||||||
|
|
||||||
|
for i, tHandle in enumerate(self._queue):
|
||||||
|
self._error = None
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
yield i, self._doBuild(makeObj, tHandle)
|
||||||
|
else:
|
||||||
|
yield i, False
|
||||||
|
|
||||||
|
self._error = None
|
||||||
|
self._cache = makeObj
|
||||||
|
|
||||||
|
try:
|
||||||
|
makeObj.saveMarkdown(path)
|
||||||
|
except Exception as exc:
|
||||||
|
logException()
|
||||||
|
self._error = formatException(exc)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def iterBuildNWD(self, path: Path | None, asJson: bool = False) -> Iterable[tuple[int, bool]]:
|
||||||
|
"""Build a novelWriter Markdown file."""
|
||||||
|
makeObj = ToMarkdown(self._project)
|
||||||
|
filtered = self._setupBuild(makeObj)
|
||||||
|
|
||||||
|
makeObj.setKeepMarkdown(True)
|
||||||
|
if self._build.getBool("format.replaceTabs"):
|
||||||
|
makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
|
||||||
|
|
||||||
|
for i, tHandle in enumerate(self._queue):
|
||||||
|
self._error = None
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
yield i, self._doBuild(makeObj, tHandle, convert=False)
|
||||||
|
else:
|
||||||
|
yield i, False
|
||||||
|
|
||||||
|
self._error = None
|
||||||
|
self._cache = makeObj
|
||||||
|
|
||||||
|
if isinstance(path, Path):
|
||||||
|
try:
|
||||||
|
if asJson:
|
||||||
|
makeObj.saveRawMarkdownJSON(path)
|
||||||
|
else:
|
||||||
|
makeObj.saveRawMarkdown(path)
|
||||||
|
except Exception as exc:
|
||||||
|
logException()
|
||||||
|
self._error = formatException(exc)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _setupBuild(self, bldObj: Tokenizer) -> dict:
|
||||||
|
"""Configure the build object."""
|
||||||
|
# Get Settings
|
||||||
|
buildLang = self._build.getStr("format.buildLang")
|
||||||
|
textFont = self._build.getStr("format.textFont")
|
||||||
|
textSize = self._build.getInt("format.textSize")
|
||||||
|
|
||||||
|
# The language lookup dict is reloaded if needed
|
||||||
|
self._project.setProjectLang(buildLang)
|
||||||
|
|
||||||
|
fontFamily = textFont or CONFIG.textFont
|
||||||
|
bldFont = QFont(fontFamily, textSize)
|
||||||
|
fontInfo = QFontInfo(bldFont)
|
||||||
|
textFixed = fontInfo.fixedPitch()
|
||||||
|
|
||||||
|
bldObj.setTitleFormat(self._build.getStr("headings.fmtTitle"))
|
||||||
|
bldObj.setChapterFormat(self._build.getStr("headings.fmtChapter"))
|
||||||
|
bldObj.setUnNumberedFormat(self._build.getStr("headings.fmtUnnumbered"))
|
||||||
|
bldObj.setSceneFormat(
|
||||||
|
self._build.getStr("headings.fmtScene"),
|
||||||
|
self._build.getBool("headings.hideScene")
|
||||||
|
)
|
||||||
|
bldObj.setSectionFormat(
|
||||||
|
self._build.getStr("headings.fmtSection"),
|
||||||
|
self._build.getBool("headings.hideSection")
|
||||||
|
)
|
||||||
|
|
||||||
|
bldObj.setFont(fontFamily, textSize, textFixed)
|
||||||
|
bldObj.setJustify(self._build.getBool("format.justifyText"))
|
||||||
|
bldObj.setLineHeight(self._build.getFloat("format.lineHeight"))
|
||||||
|
|
||||||
|
bldObj.setSynopsis(self._build.getBool("text.includeSynopsis"))
|
||||||
|
bldObj.setComments(self._build.getBool("text.includeComments"))
|
||||||
|
bldObj.setKeywords(self._build.getBool("text.includeKeywords"))
|
||||||
|
bldObj.setBodyText(self._build.getBool("text.includeBodyText"))
|
||||||
|
|
||||||
|
if isinstance(bldObj, ToHtml):
|
||||||
|
bldObj.setStyles(self._build.getBool("html.addStyles"))
|
||||||
|
bldObj.setReplaceUnicode(self._build.getBool("format.stripUnicode"))
|
||||||
|
|
||||||
|
if isinstance(bldObj, ToOdt):
|
||||||
|
bldObj.setColourHeaders(self._build.getBool("odt.addColours"))
|
||||||
|
bldObj.setLanguage(buildLang)
|
||||||
|
|
||||||
|
filtered = self._build.buildItemFilter(
|
||||||
|
self._project, withRoots=self._build.getBool("text.addNoteHeadings")
|
||||||
|
)
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
|
||||||
|
def _doBuild(self, bldObj: Tokenizer, tHandle: str, convert: bool = True) -> bool:
|
||||||
|
"""Build a single document and add it to the build object."""
|
||||||
|
tItem = self._project.tree[tHandle]
|
||||||
|
if tItem is None:
|
||||||
|
self._error = f"Build: Unknown item '{tHandle}'"
|
||||||
|
logger.error(self._error)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
if tItem.isRootType() and not tItem.isNovelLike():
|
||||||
|
bldObj.addRootHeading(tItem.itemHandle)
|
||||||
|
if convert:
|
||||||
|
bldObj.doConvert()
|
||||||
|
elif tItem.isFileType():
|
||||||
|
bldObj.setText(tHandle)
|
||||||
|
bldObj.doPreProcessing()
|
||||||
|
bldObj.tokenizeText()
|
||||||
|
bldObj.doHeaders()
|
||||||
|
if convert:
|
||||||
|
bldObj.doConvert()
|
||||||
|
else:
|
||||||
|
logger.info(f"Build: Skipping '{tHandle}'")
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
self._error = f"Build: Failed to build '{tHandle}'"
|
||||||
|
logger.error(self._error)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# END Class NWBuildDocument
|
||||||
@@ -273,7 +273,7 @@ class NWIndex:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
logger.debug("Indexing item with handle '%s'", tHandle)
|
logger.debug("Indexing item with handle '%s'", tHandle)
|
||||||
if theItem.isInactive():
|
if theItem.isInactiveClass():
|
||||||
self._scanInactive(theItem, theText)
|
self._scanInactive(theItem, theText)
|
||||||
else:
|
else:
|
||||||
self._scanActive(tHandle, theItem, theText, itemTags)
|
self._scanActive(tHandle, theItem, theText, itemTags)
|
||||||
@@ -786,8 +786,7 @@ class ItemIndex:
|
|||||||
"""Iterate over all item headers of an item.
|
"""Iterate over all item headers of an item.
|
||||||
"""
|
"""
|
||||||
if tHandle in self._items:
|
if tHandle in self._items:
|
||||||
for sTitle, hItem in self._items[tHandle].items():
|
yield from self._items[tHandle].items()
|
||||||
yield sTitle, hItem
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def iterAllHeaders(self):
|
def iterAllHeaders(self):
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ class NWItem:
|
|||||||
"""
|
"""
|
||||||
return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE, nwItemClass.TRASH)
|
return self._class in (nwItemClass.NOVEL, nwItemClass.ARCHIVE, nwItemClass.TRASH)
|
||||||
|
|
||||||
def isInactive(self):
|
def isInactiveClass(self):
|
||||||
"""Returns true if the item is in an inactive class.
|
"""Returns true if the item is in an inactive class.
|
||||||
"""
|
"""
|
||||||
return self._class in (nwItemClass.NO_CLASS, nwItemClass.ARCHIVE, nwItemClass.TRASH)
|
return self._class in (nwItemClass.NO_CLASS, nwItemClass.ARCHIVE, nwItemClass.TRASH)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – Project Options Cache
|
novelWriter – Project Options Cache
|
||||||
===================================
|
===================================
|
||||||
Data class for user-defined GUI project options
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2019-10-21 [0.3.1]
|
Created: 2019-10-21 [0.3.1]
|
||||||
@@ -23,17 +22,22 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from novelwriter.error import logException
|
from novelwriter.error import logException
|
||||||
from novelwriter.common import checkBool, checkFloat, checkInt, checkString
|
from novelwriter.common import checkBool, checkFloat, checkInt, checkString
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
VALID_MAP = {
|
VALID_MAP = {
|
||||||
@@ -43,13 +47,6 @@ VALID_MAP = {
|
|||||||
"hideZeros", "hideNegative", "groupByDay", "showIdleTime", "histMax",
|
"hideZeros", "hideNegative", "groupByDay", "showIdleTime", "histMax",
|
||||||
},
|
},
|
||||||
"GuiDocSplit": {"spLevel", "intoFolder", "docHierarchy"},
|
"GuiDocSplit": {"spLevel", "intoFolder", "docHierarchy"},
|
||||||
"GuiBuildNovel": {
|
|
||||||
"winWidth", "winHeight", "boxWidth", "docWidth", "hideScene",
|
|
||||||
"hideSection", "addNovel", "addNotes", "ignoreFlag", "justifyText",
|
|
||||||
"excludeBody", "textFont", "textSize", "lineHeight", "noStyling",
|
|
||||||
"incSynopsis", "incComments", "incKeywords", "incBodyText",
|
|
||||||
"replaceTabs", "replaceUCode", "rootFilter",
|
|
||||||
},
|
|
||||||
"GuiOutline": {"headerOrder", "columnWidth", "columnHidden"},
|
"GuiOutline": {"headerOrder", "columnWidth", "columnHidden"},
|
||||||
"GuiProjectSettings": {
|
"GuiProjectSettings": {
|
||||||
"winWidth", "winHeight", "replaceColW", "statusColW", "importColW",
|
"winWidth", "winHeight", "replaceColW", "statusColW", "importColW",
|
||||||
@@ -60,13 +57,28 @@ VALID_MAP = {
|
|||||||
},
|
},
|
||||||
"GuiWordList": {"winWidth", "winHeight"},
|
"GuiWordList": {"winWidth", "winHeight"},
|
||||||
"GuiNovelView": {"lastCol", "lastColSize"},
|
"GuiNovelView": {"lastCol", "lastColSize"},
|
||||||
|
"GuiBuildSettings": {
|
||||||
|
"winWidth", "winHeight", "treeWidth", "filterWidth"
|
||||||
|
},
|
||||||
|
"GuiManuscript": {
|
||||||
|
"winWidth", "winHeight", "optsWidth", "viewWidth"
|
||||||
|
},
|
||||||
|
"GuiManuscriptBuild": {
|
||||||
|
"winWidth", "winHeight", "fmtWidth", "sumWidth"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class OptionState:
|
class OptionState:
|
||||||
|
"""Core: GUI Options Storage
|
||||||
|
|
||||||
def __init__(self, theProject):
|
A class for storing the state of the GUI. The data is stored per
|
||||||
self.theProject = theProject
|
project. Settings that should be project-independent are stored in
|
||||||
|
the Config instead.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, project: NWProject):
|
||||||
|
self._project = project
|
||||||
self._theState = {}
|
self._theState = {}
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -74,10 +86,10 @@ class OptionState:
|
|||||||
# Load and Save Cache
|
# Load and Save Cache
|
||||||
##
|
##
|
||||||
|
|
||||||
def loadSettings(self):
|
def loadSettings(self) -> bool:
|
||||||
"""Load the options dictionary from the project settings file.
|
"""Load the options dictionary from the project settings file.
|
||||||
"""
|
"""
|
||||||
stateFile = self.theProject.storage.getMetaFile(nwFiles.OPTS_FILE)
|
stateFile = self._project.storage.getMetaFile(nwFiles.OPTS_FILE)
|
||||||
if not isinstance(stateFile, Path):
|
if not isinstance(stateFile, Path):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -102,10 +114,9 @@ class OptionState:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveSettings(self):
|
def saveSettings(self) -> bool:
|
||||||
"""Save the options dictionary to the project settings file.
|
"""Save the options dictionary to the project settings file."""
|
||||||
"""
|
stateFile = self._project.storage.getMetaFile(nwFiles.OPTS_FILE)
|
||||||
stateFile = self.theProject.storage.getMetaFile(nwFiles.OPTS_FILE)
|
|
||||||
if not isinstance(stateFile, Path):
|
if not isinstance(stateFile, Path):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -124,9 +135,8 @@ class OptionState:
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setValue(self, group, name, value):
|
def setValue(self, group: str, name: str, value: Any) -> bool:
|
||||||
"""Save a value, with a given group and name.
|
"""Save a value, with a given group and name."""
|
||||||
"""
|
|
||||||
if group not in VALID_MAP:
|
if group not in VALID_MAP:
|
||||||
logger.error("Unknown option group '%s'", group)
|
logger.error("Unknown option group '%s'", group)
|
||||||
return False
|
return False
|
||||||
@@ -149,7 +159,7 @@ class OptionState:
|
|||||||
# Getters
|
# Getters
|
||||||
##
|
##
|
||||||
|
|
||||||
def getValue(self, group, name, default):
|
def getValue(self, group: str, name: str, default: Any) -> Any:
|
||||||
"""Return an arbitrary type value, if it exists. Otherwise,
|
"""Return an arbitrary type value, if it exists. Otherwise,
|
||||||
return the default value.
|
return the default value.
|
||||||
"""
|
"""
|
||||||
@@ -157,7 +167,7 @@ class OptionState:
|
|||||||
return self._theState[group].get(name, default)
|
return self._theState[group].get(name, default)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def getString(self, group, name, default):
|
def getString(self, group: str, name: str, default: str) -> str:
|
||||||
"""Return the value as a string, if it exists. Otherwise, return
|
"""Return the value as a string, if it exists. Otherwise, return
|
||||||
the default value.
|
the default value.
|
||||||
"""
|
"""
|
||||||
@@ -165,7 +175,7 @@ class OptionState:
|
|||||||
return checkString(self._theState[group].get(name, default), default)
|
return checkString(self._theState[group].get(name, default), default)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def getInt(self, group, name, default):
|
def getInt(self, group: str, name: str, default: int) -> int:
|
||||||
"""Return the value as an int, if it exists. Otherwise, return
|
"""Return the value as an int, if it exists. Otherwise, return
|
||||||
the default value.
|
the default value.
|
||||||
"""
|
"""
|
||||||
@@ -173,7 +183,7 @@ class OptionState:
|
|||||||
return checkInt(self._theState[group].get(name, default), default)
|
return checkInt(self._theState[group].get(name, default), default)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def getFloat(self, group, name, default):
|
def getFloat(self, group: str, name: str, default: float) -> float:
|
||||||
"""Return the value as a float, if it exists. Otherwise, return
|
"""Return the value as a float, if it exists. Otherwise, return
|
||||||
the default value.
|
the default value.
|
||||||
"""
|
"""
|
||||||
@@ -181,7 +191,7 @@ class OptionState:
|
|||||||
return checkFloat(self._theState[group].get(name, default), default)
|
return checkFloat(self._theState[group].get(name, default), default)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def getBool(self, group, name, default):
|
def getBool(self, group: str, name: str, default: bool) -> bool:
|
||||||
"""Return the value as a bool, if it exists. Otherwise, return
|
"""Return the value as a bool, if it exists. Otherwise, return
|
||||||
the default value.
|
the default value.
|
||||||
"""
|
"""
|
||||||
@@ -189,9 +199,9 @@ class OptionState:
|
|||||||
return checkBool(self._theState[group].get(name, default), default)
|
return checkBool(self._theState[group].get(name, default), default)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def getEnum(self, group, name, lookup, default):
|
def getEnum(self, group: str, name: str, lookup: type, default: Enum) -> Enum:
|
||||||
"""Return the value mapped to an enum. Otherwise return the
|
"""Return the value mapped to an enum. Otherwise return the
|
||||||
default value
|
default value.
|
||||||
"""
|
"""
|
||||||
if issubclass(lookup, Enum):
|
if issubclass(lookup, Enum):
|
||||||
if group in self._theState:
|
if group in self._theState:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – Project Data Class
|
novelWriter – Project Data Class
|
||||||
================================
|
================================
|
||||||
Data class for novelWriter projects
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2022-10-30 [2.0rc2]
|
Created: 2022-10-30 [2.0rc2]
|
||||||
@@ -22,12 +21,13 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from novelwriter.common import (
|
from novelwriter.common import (
|
||||||
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
|
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
|
||||||
)
|
)
|
||||||
@@ -37,6 +37,11 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class NWProjectData:
|
class NWProjectData:
|
||||||
|
"""Core: Project Data Class
|
||||||
|
|
||||||
|
The class holds all project data from the main XML file, aside from
|
||||||
|
the list of project items.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, theProject):
|
||||||
|
|
||||||
@@ -85,75 +90,94 @@ class NWProjectData:
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def uuid(self):
|
def uuid(self) -> str:
|
||||||
|
"""Return the project ID."""
|
||||||
return self._uuid
|
return self._uuid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self) -> str:
|
||||||
|
"""Return the project name."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self) -> str:
|
||||||
|
"""Return the project title."""
|
||||||
return self._title
|
return self._title
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def author(self):
|
def author(self) -> str:
|
||||||
|
"""Return the project author."""
|
||||||
return self._author
|
return self._author
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def saveCount(self):
|
def saveCount(self) -> int:
|
||||||
|
"""Return the count of project saves."""
|
||||||
return self._saveCount
|
return self._saveCount
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def autoCount(self):
|
def autoCount(self) -> int:
|
||||||
|
"""Return the count of project auto-saves."""
|
||||||
return self._autoCount
|
return self._autoCount
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def editTime(self):
|
def editTime(self) -> int:
|
||||||
|
"""Return the number of seconds the project has been edited."""
|
||||||
return self._editTime
|
return self._editTime
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def doBackup(self):
|
def doBackup(self) -> bool:
|
||||||
|
"""Return the backup setting."""
|
||||||
return self._doBackup
|
return self._doBackup
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def language(self):
|
def language(self) -> str | None:
|
||||||
|
"""Return the project language setting."""
|
||||||
return self._language
|
return self._language
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def spellCheck(self):
|
def spellCheck(self) -> bool:
|
||||||
|
"""Return the spell check enabled setting."""
|
||||||
return self._spellCheck
|
return self._spellCheck
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def spellLang(self):
|
def spellLang(self) -> str | None:
|
||||||
|
"""Return the spell check language."""
|
||||||
return self._spellLang
|
return self._spellLang
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def initCounts(self):
|
def initCounts(self) -> tuple[int, int]:
|
||||||
|
"""Return the initial count of words for novel and note
|
||||||
|
documents.
|
||||||
|
"""
|
||||||
return tuple(self._initCounts)
|
return tuple(self._initCounts)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def currCounts(self):
|
def currCounts(self) -> tuple[int, int]:
|
||||||
|
"""Return the current count of words for novel and note
|
||||||
|
documents.
|
||||||
|
"""
|
||||||
return tuple(self._currCounts)
|
return tuple(self._currCounts)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lastHandle(self):
|
def lastHandle(self) -> dict[str, str | None]:
|
||||||
|
"""Return the dictionary of last used handles for various
|
||||||
|
components of the GUI.
|
||||||
|
"""
|
||||||
return self._lastHandle
|
return self._lastHandle
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def autoReplace(self):
|
def autoReplace(self) -> dict[str, str]:
|
||||||
|
"""Return the autoreplace dictionary."""
|
||||||
return self._autoReplace
|
return self._autoReplace
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def titleFormat(self):
|
def itemStatus(self) -> NWStatus:
|
||||||
return self._titleFormat
|
"""Return the status settings object."""
|
||||||
|
|
||||||
@property
|
|
||||||
def itemStatus(self):
|
|
||||||
return self._status
|
return self._status
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def itemImport(self):
|
def itemImport(self) -> NWStatus:
|
||||||
|
"""Return the importance settings object."""
|
||||||
return self._import
|
return self._import
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -161,15 +185,13 @@ class NWProjectData:
|
|||||||
##
|
##
|
||||||
|
|
||||||
def incSaveCount(self):
|
def incSaveCount(self):
|
||||||
"""Increment the save count by one.
|
"""Increment the save count by one."""
|
||||||
"""
|
|
||||||
self._saveCount += 1
|
self._saveCount += 1
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def incAutoCount(self):
|
def incAutoCount(self):
|
||||||
"""Increment the auto save count by one.
|
"""Increment the auto save count by one."""
|
||||||
"""
|
|
||||||
self._autoCount += 1
|
self._autoCount += 1
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
@@ -178,23 +200,16 @@ class NWProjectData:
|
|||||||
# Getters
|
# Getters
|
||||||
##
|
##
|
||||||
|
|
||||||
def getLastHandle(self, component):
|
def getLastHandle(self, component: str) -> str | None:
|
||||||
"""Retrieve the last used handle for a given component.
|
"""Retrieve the last used handle for a given component."""
|
||||||
"""
|
|
||||||
return self._lastHandle.get(component, None)
|
return self._lastHandle.get(component, None)
|
||||||
|
|
||||||
def getTitleFormat(self, kind):
|
|
||||||
"""Retrieve the title format string for a given kind of header.
|
|
||||||
"""
|
|
||||||
return self._titleFormat.get(kind, "%title%")
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setUuid(self, value):
|
def setUuid(self, value: Any):
|
||||||
"""Set the project id.
|
"""Set the project id."""
|
||||||
"""
|
|
||||||
value = checkUuid(value, "")
|
value = checkUuid(value, "")
|
||||||
if not value:
|
if not value:
|
||||||
self._uuid = str(uuid.uuid4())
|
self._uuid = str(uuid.uuid4())
|
||||||
@@ -203,101 +218,95 @@ class NWProjectData:
|
|||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setName(self, value):
|
def setName(self, value: str | None):
|
||||||
"""Set a new project name.
|
"""Set a new project name."""
|
||||||
"""
|
|
||||||
if value != self._name:
|
if value != self._name:
|
||||||
self._name = simplified(str(value or ""))
|
self._name = simplified(str(value or ""))
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTitle(self, value):
|
def setTitle(self, value: str | None):
|
||||||
"""Set a new novel title.
|
"""Set a new novel title."""
|
||||||
"""
|
|
||||||
if value != self._title:
|
if value != self._title:
|
||||||
self._title = simplified(str(value or ""))
|
self._title = simplified(str(value or ""))
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setAuthor(self, value):
|
def setAuthor(self, value: str | None):
|
||||||
"""Set the author value.
|
"""Set the author value."""
|
||||||
"""
|
|
||||||
if value != self._title:
|
if value != self._title:
|
||||||
self._author = simplified(str(value or ""))
|
self._author = simplified(str(value or ""))
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSaveCount(self, value):
|
def setSaveCount(self, value: Any):
|
||||||
"""Set the save count from last session.
|
"""Set the save count from last session."""
|
||||||
"""
|
|
||||||
self._saveCount = checkInt(value, 0)
|
self._saveCount = checkInt(value, 0)
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setAutoCount(self, value):
|
def setAutoCount(self, value: Any):
|
||||||
"""Set the auto save count from last session.
|
"""Set the auto save count from last session."""
|
||||||
"""
|
|
||||||
self._autoCount = checkInt(value, 0)
|
self._autoCount = checkInt(value, 0)
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setEditTime(self, value):
|
def setEditTime(self, value: Any):
|
||||||
"""Set tyje edit time from last session.
|
"""Set tyje edit time from last session."""
|
||||||
"""
|
|
||||||
self._editTime = checkInt(value, 0)
|
self._editTime = checkInt(value, 0)
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setDoBackup(self, value):
|
def setDoBackup(self, value: Any):
|
||||||
"""Set the do write backup flag.
|
"""Set the do write backup flag."""
|
||||||
"""
|
|
||||||
if value != self._doBackup:
|
if value != self._doBackup:
|
||||||
self._doBackup = checkBool(value, False)
|
self._doBackup = checkBool(value, False)
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLanguage(self, value):
|
def setLanguage(self, value: str | None):
|
||||||
"""Set the project language.
|
"""Set the project language."""
|
||||||
"""
|
|
||||||
if value != self._language:
|
if value != self._language:
|
||||||
self._language = checkStringNone(value, None)
|
self._language = checkStringNone(value, None)
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSpellCheck(self, value):
|
def setSpellCheck(self, value: Any):
|
||||||
"""Set the spell check flag.
|
"""Set the spell check flag."""
|
||||||
"""
|
|
||||||
if value != self._spellCheck:
|
if value != self._spellCheck:
|
||||||
self._spellCheck = checkBool(value, False)
|
self._spellCheck = checkBool(value, False)
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSpellLang(self, value):
|
def setSpellLang(self, value: str | None):
|
||||||
"""Set the spell check language.
|
"""Set the spell check language."""
|
||||||
"""
|
|
||||||
if value != self._spellLang:
|
if value != self._spellLang:
|
||||||
self._spellLang = checkStringNone(value, None)
|
self._spellLang = checkStringNone(value, None)
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLastHandle(self, value, component=None):
|
def setLastHandle(self, value: str | None, component: str):
|
||||||
"""Set a last used handle into the handle registry. If component
|
"""Set a last used handle into the handle registry for a given
|
||||||
is None, the value is assumed to be the whole dictionary of
|
component.
|
||||||
values.
|
|
||||||
"""
|
"""
|
||||||
if isinstance(component, str):
|
if isinstance(component, str):
|
||||||
self._lastHandle[component] = checkStringNone(value, None)
|
self._lastHandle[component] = checkStringNone(value, None)
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
elif isinstance(value, dict):
|
return
|
||||||
|
|
||||||
|
def setLastHandles(self, value: dict):
|
||||||
|
"""Set the full last handles dictionary to a new set of values.
|
||||||
|
This is intended to be used at project load.
|
||||||
|
"""
|
||||||
|
if isinstance(value, dict):
|
||||||
for key, entry in value.items():
|
for key, entry in value.items():
|
||||||
if key in self._lastHandle:
|
if key in self._lastHandle:
|
||||||
self._lastHandle[key] = str(entry) if isHandle(entry) else None
|
self._lastHandle[key] = str(entry) if isHandle(entry) else None
|
||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setInitCounts(self, novel=None, notes=None):
|
def setInitCounts(self, novel: Any = None, notes: Any = None):
|
||||||
"""Set the worc count totals for novel and note files.
|
"""Set the word count totals for novel and note files."""
|
||||||
"""
|
|
||||||
if novel is not None:
|
if novel is not None:
|
||||||
self._initCounts[0] = checkInt(novel, 0)
|
self._initCounts[0] = checkInt(novel, 0)
|
||||||
self._currCounts[0] = checkInt(novel, 0)
|
self._currCounts[0] = checkInt(novel, 0)
|
||||||
@@ -306,18 +315,16 @@ class NWProjectData:
|
|||||||
self._currCounts[1] = checkInt(notes, 0)
|
self._currCounts[1] = checkInt(notes, 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setCurrCounts(self, novel=None, notes=None):
|
def setCurrCounts(self, novel: Any = None, notes: Any = None):
|
||||||
"""Set the worc count totals for novel and note files.
|
"""Set the word count totals for novel and note files."""
|
||||||
"""
|
|
||||||
if novel is not None:
|
if novel is not None:
|
||||||
self._currCounts[0] = checkInt(novel, 0)
|
self._currCounts[0] = checkInt(novel, 0)
|
||||||
if notes is not None:
|
if notes is not None:
|
||||||
self._currCounts[1] = checkInt(notes, 0)
|
self._currCounts[1] = checkInt(notes, 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setAutoReplace(self, value):
|
def setAutoReplace(self, value: dict):
|
||||||
"""Set the auto-replace dictionary.
|
"""Set the auto-replace dictionary."""
|
||||||
"""
|
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
self._autoReplace = {}
|
self._autoReplace = {}
|
||||||
for key, entry in value.items():
|
for key, entry in value.items():
|
||||||
@@ -326,14 +333,4 @@ class NWProjectData:
|
|||||||
self.theProject.setProjectChanged(True)
|
self.theProject.setProjectChanged(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTitleFormat(self, value):
|
|
||||||
"""Set the title formats.
|
|
||||||
"""
|
|
||||||
if isinstance(value, dict):
|
|
||||||
for key, entry in value.items():
|
|
||||||
if key in self._titleFormat and isinstance(entry, str):
|
|
||||||
self._titleFormat[key] = simplified(entry)
|
|
||||||
self.theProject.setProjectChanged(True)
|
|
||||||
return
|
|
||||||
|
|
||||||
# END Class NWProjectData
|
# END Class NWProjectData
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – Project XML Read/Write
|
novelWriter – Project XML Read/Write
|
||||||
====================================
|
====================================
|
||||||
Classes for reading and writing the project XML file
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2022-09-28 [2.0rc2] XMLReadState
|
Created: 2022-09-28 [2.0rc2] XMLReadState
|
||||||
@@ -24,12 +23,14 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from novelwriter import __version__, __hexversion__
|
from novelwriter import __version__, __hexversion__
|
||||||
@@ -39,9 +40,14 @@ from novelwriter.common import (
|
|||||||
)
|
)
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.core.status import NWStatus
|
||||||
|
from novelwriter.core.projectdata import NWProjectData
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
FILE_VERSION = "1.5" # The current project file format version
|
FILE_VERSION = "1.5" # The current project file format version
|
||||||
|
FILE_REVISION = "1" # The current project file format revision
|
||||||
HEX_VERSION = 0x0105
|
HEX_VERSION = 0x0105
|
||||||
|
|
||||||
NUM_VERSION = {
|
NUM_VERSION = {
|
||||||
@@ -55,6 +61,7 @@ NUM_VERSION = {
|
|||||||
|
|
||||||
|
|
||||||
class XMLReadState(Enum):
|
class XMLReadState(Enum):
|
||||||
|
"""The state of an XML read process."""
|
||||||
|
|
||||||
NO_ACTION = 0
|
NO_ACTION = 0
|
||||||
NO_ERROR = 1
|
NO_ERROR = 1
|
||||||
@@ -69,8 +76,10 @@ class XMLReadState(Enum):
|
|||||||
|
|
||||||
|
|
||||||
class ProjectXMLReader:
|
class ProjectXMLReader:
|
||||||
"""The main project XML file reader class. All data is read into a
|
"""Core: Project XML Reader
|
||||||
NWProjectData instance, which must be provided.
|
|
||||||
|
All data is read into a NWProjectData instance, which must be
|
||||||
|
provided.
|
||||||
|
|
||||||
File Format Version Change History
|
File Format Version Change History
|
||||||
==================================
|
==================================
|
||||||
@@ -97,19 +106,19 @@ class ProjectXMLReader:
|
|||||||
the content item meta entry. It also moves meta data related to
|
the content item meta entry. It also moves meta data related to
|
||||||
the project or the content into their respective section nodes
|
the project or the content into their respective section nodes
|
||||||
as attributes. The id attribute was also added to the project.
|
as attributes. The id attribute was also added to the project.
|
||||||
|
|
||||||
|
Rev 1: Drops the titleFormat section of settings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
|
|
||||||
self._path = Path(path)
|
self._path = Path(path)
|
||||||
self._state = XMLReadState.NO_ACTION
|
self._state = XMLReadState.NO_ACTION
|
||||||
|
|
||||||
self._root = ""
|
self._root = ""
|
||||||
self._version = 0x0
|
self._version = 0x0
|
||||||
|
self._revision = 0
|
||||||
self._appVersion = ""
|
self._appVersion = ""
|
||||||
self._hexVersion = 0x0
|
self._hexVersion = 0x0
|
||||||
self._timeStamp = ""
|
self._timeStamp = ""
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -117,55 +126,51 @@ class ProjectXMLReader:
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self) -> XMLReadState:
|
||||||
"""The state of the parsing as an XMLReadState enum value.
|
"""Return the parsing state."""
|
||||||
"""
|
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def xmlRoot(self):
|
def xmlRoot(self) -> str:
|
||||||
"""The root tag name of the XNL file,
|
"""Return the root tag name of the XNL file."""
|
||||||
"""
|
|
||||||
return self._root
|
return self._root
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def xmlVersion(self):
|
def xmlVersion(self) -> int:
|
||||||
"""The project XML version number.
|
"""Return the project XML version number."""
|
||||||
"""
|
|
||||||
return self._version
|
return self._version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def appVersion(self):
|
def xmlRevision(self) -> int:
|
||||||
"""The novelWriter version number who wrote the file.
|
"""Return the project XML revision number."""
|
||||||
"""
|
return self._revision
|
||||||
|
|
||||||
|
@property
|
||||||
|
def appVersion(self) -> str:
|
||||||
|
"""Return the version number who wrote the file."""
|
||||||
return self._appVersion
|
return self._appVersion
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hexVersion(self):
|
def hexVersion(self) -> int:
|
||||||
"""The novelWriter version number who wrote the file as hex.
|
"""Return the version number who wrote the file as hex."""
|
||||||
"""
|
|
||||||
return self._hexVersion
|
return self._hexVersion
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def timeStamp(self):
|
def timeStamp(self) -> str:
|
||||||
"""The date and time when the file was written.
|
"""Return the date and time when the file was written."""
|
||||||
"""
|
|
||||||
return self._timeStamp
|
return self._timeStamp
|
||||||
|
|
||||||
##
|
##
|
||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def read(self, projData, projContent):
|
def read(self, data: NWProjectData, content: list) -> bool:
|
||||||
"""Read and parse the project XML file.
|
"""Read and parse the project XML file."""
|
||||||
"""
|
|
||||||
tStart = time()
|
tStart = time()
|
||||||
logger.debug("Reading project XML")
|
logger.debug("Reading project XML")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xml = ET.parse(str(self._path))
|
xml = ET.parse(str(self._path))
|
||||||
self._state = XMLReadState.NO_ERROR
|
self._state = XMLReadState.NO_ERROR
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
# Trying to open backup file instead
|
# Trying to open backup file instead
|
||||||
logger.error("Failed to parse project XML", exc_info=exc)
|
logger.error("Failed to parse project XML", exc_info=exc)
|
||||||
@@ -200,20 +205,21 @@ class ProjectXMLReader:
|
|||||||
|
|
||||||
logger.debug("XML is '%s' version '%s'", self._root, fileVersion)
|
logger.debug("XML is '%s' version '%s'", self._root, fileVersion)
|
||||||
|
|
||||||
|
self._revision = checkInt(xRoot.attrib.get("fileRevision"), 0)
|
||||||
self._appVersion = str(xRoot.attrib.get("appVersion", ""))
|
self._appVersion = str(xRoot.attrib.get("appVersion", ""))
|
||||||
self._hexVersion = hexToInt(xRoot.attrib.get("hexVersion", ""))
|
self._hexVersion = hexToInt(xRoot.attrib.get("hexVersion", ""))
|
||||||
self._timeStamp = str(xRoot.attrib.get("timeStamp", ""))
|
self._timeStamp = str(xRoot.attrib.get("timeStamp", ""))
|
||||||
|
|
||||||
for xSection in xRoot:
|
for xSection in xRoot:
|
||||||
if xSection.tag == "project":
|
if xSection.tag == "project":
|
||||||
self._parseProjectMeta(xSection, projData)
|
self._parseProjectMeta(xSection, data)
|
||||||
elif xSection.tag == "settings":
|
elif xSection.tag == "settings":
|
||||||
self._parseProjectSettings(xSection, projData)
|
self._parseProjectSettings(xSection, data)
|
||||||
elif xSection.tag == "content":
|
elif xSection.tag == "content":
|
||||||
if self._version >= 0x0104:
|
if self._version >= 0x0104:
|
||||||
self._parseProjectContent(xSection, projData, projContent)
|
self._parseProjectContent(xSection, data, content)
|
||||||
else:
|
else:
|
||||||
self._parseProjectContentLegacy(xSection, projData, projContent)
|
self._parseProjectContentLegacy(xSection, data, content)
|
||||||
else:
|
else:
|
||||||
logger.warning("Ignored <root/%s> in XML", xSection.tag)
|
logger.warning("Ignored <root/%s> in XML", xSection.tag)
|
||||||
|
|
||||||
@@ -230,23 +236,22 @@ class ProjectXMLReader:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _parseProjectMeta(self, xSection, projData):
|
def _parseProjectMeta(self, xSection: ET.Element, data: NWProjectData):
|
||||||
"""Parse the project section of the XML file.
|
"""Parse the project section of the XML file."""
|
||||||
"""
|
|
||||||
logger.debug("Parsing <project> section")
|
logger.debug("Parsing <project> section")
|
||||||
|
|
||||||
projData.setUuid(xSection.attrib.get("id", None)) # Added in 1.5
|
data.setUuid(xSection.attrib.get("id", None)) # Added in 1.5
|
||||||
projData.setSaveCount(xSection.attrib.get("saveCount", 0)) # Moved in 1.5
|
data.setSaveCount(xSection.attrib.get("saveCount", 0)) # Moved in 1.5
|
||||||
projData.setAutoCount(xSection.attrib.get("autoCount", 0)) # Moved in 1.5
|
data.setAutoCount(xSection.attrib.get("autoCount", 0)) # Moved in 1.5
|
||||||
projData.setEditTime(xSection.attrib.get("editTime", 0)) # Moved in 1.5
|
data.setEditTime(xSection.attrib.get("editTime", 0)) # Moved in 1.5
|
||||||
|
|
||||||
for xItem in xSection:
|
for xItem in xSection:
|
||||||
if xItem.tag == "name":
|
if xItem.tag == "name":
|
||||||
projData.setName(xItem.text)
|
data.setName(xItem.text)
|
||||||
elif xItem.tag == "title":
|
elif xItem.tag == "title":
|
||||||
projData.setTitle(xItem.text)
|
data.setTitle(xItem.text)
|
||||||
elif xItem.tag == "author":
|
elif xItem.tag == "author":
|
||||||
projData.setAuthor(xItem.text)
|
data.setAuthor(xItem.text)
|
||||||
else:
|
else:
|
||||||
logger.warning("Ignored <root/project/%s> in XML", xItem.tag)
|
logger.warning("Ignored <root/project/%s> in XML", xItem.tag)
|
||||||
|
|
||||||
@@ -254,43 +259,37 @@ class ProjectXMLReader:
|
|||||||
if self._version < HEX_VERSION:
|
if self._version < HEX_VERSION:
|
||||||
for xItem in xSection:
|
for xItem in xSection:
|
||||||
if xItem.tag == "saveCount": # Moved to attribute in 1.5
|
if xItem.tag == "saveCount": # Moved to attribute in 1.5
|
||||||
projData.setSaveCount(xItem.text)
|
data.setSaveCount(xItem.text)
|
||||||
elif xItem.tag == "autoCount": # Moved to attribute in 1.5
|
elif xItem.tag == "autoCount": # Moved to attribute in 1.5
|
||||||
projData.setAutoCount(xItem.text)
|
data.setAutoCount(xItem.text)
|
||||||
elif xItem.tag == "editTime": # Moved to attribute in 1.5
|
elif xItem.tag == "editTime": # Moved to attribute in 1.5
|
||||||
projData.setEditTime(xItem.text)
|
data.setEditTime(xItem.text)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseProjectSettings(self, xSection, projData):
|
def _parseProjectSettings(self, xSection: ET.Element, data: NWProjectData):
|
||||||
"""Parse the settings section of the XML file.
|
"""Parse the settings section of the XML file."""
|
||||||
"""
|
|
||||||
logger.debug("Parsing <settings> section")
|
logger.debug("Parsing <settings> section")
|
||||||
|
|
||||||
for xItem in xSection:
|
for xItem in xSection:
|
||||||
if xItem.tag == "doBackup":
|
if xItem.tag == "doBackup":
|
||||||
projData.setDoBackup(xItem.text)
|
data.setDoBackup(xItem.text)
|
||||||
elif xItem.tag == "language":
|
elif xItem.tag == "language":
|
||||||
projData.setLanguage(xItem.text)
|
data.setLanguage(xItem.text)
|
||||||
elif xItem.tag == "spellChecking":
|
elif xItem.tag == "spellChecking":
|
||||||
projData.setSpellLang(xItem.text)
|
data.setSpellLang(xItem.text)
|
||||||
projData.setSpellCheck(xItem.attrib.get("auto", False))
|
data.setSpellCheck(xItem.attrib.get("auto", False))
|
||||||
elif xItem.tag == "status":
|
elif xItem.tag == "status":
|
||||||
self._parseStatusImport(xItem, projData.itemStatus)
|
self._parseStatusImport(xItem, data.itemStatus)
|
||||||
elif xItem.tag == "importance":
|
elif xItem.tag == "importance":
|
||||||
self._parseStatusImport(xItem, projData.itemImport)
|
self._parseStatusImport(xItem, data.itemImport)
|
||||||
elif xItem.tag == "lastHandle":
|
elif xItem.tag == "lastHandle":
|
||||||
projData.setLastHandle(self._parseDictKeyText(xItem))
|
data.setLastHandles(self._parseDictKeyText(xItem))
|
||||||
elif xItem.tag == "autoReplace":
|
elif xItem.tag == "autoReplace":
|
||||||
if self._version >= 0x0102:
|
if self._version >= 0x0102:
|
||||||
projData.setAutoReplace(self._parseDictKeyText(xItem))
|
data.setAutoReplace(self._parseDictKeyText(xItem))
|
||||||
else: # Pre 1.2 format
|
else: # Pre 1.2 format
|
||||||
projData.setAutoReplace(self._parseDictTagText(xItem))
|
data.setAutoReplace(self._parseDictTagText(xItem))
|
||||||
elif xItem.tag == "titleFormat":
|
|
||||||
if self._version >= 0x0105:
|
|
||||||
projData.setTitleFormat(self._parseDictKeyText(xItem))
|
|
||||||
else: # Pre 1.4 format
|
|
||||||
projData.setTitleFormat(self._parseDictTagText(xItem))
|
|
||||||
else:
|
else:
|
||||||
logger.warning("Ignored <root/settings/%s> in XML", xItem.tag)
|
logger.warning("Ignored <root/settings/%s> in XML", xItem.tag)
|
||||||
|
|
||||||
@@ -298,23 +297,22 @@ class ProjectXMLReader:
|
|||||||
if self._version < HEX_VERSION:
|
if self._version < HEX_VERSION:
|
||||||
for xItem in xSection:
|
for xItem in xSection:
|
||||||
if xItem.tag == "spellCheck": # Changed to spellChecking in 1.5
|
if xItem.tag == "spellCheck": # Changed to spellChecking in 1.5
|
||||||
projData.setSpellCheck(xItem.text)
|
data.setSpellCheck(xItem.text)
|
||||||
elif xItem.tag == "spellLang": # Changed to spellChecking in 1.5
|
elif xItem.tag == "spellLang": # Changed to spellChecking in 1.5
|
||||||
projData.setSpellLang(xItem.text)
|
data.setSpellLang(xItem.text)
|
||||||
elif xItem.tag == "novelWordCount": # Moved to content attribute in 1.5
|
elif xItem.tag == "novelWordCount": # Moved to content attribute in 1.5
|
||||||
projData.setInitCounts(novel=xItem.text)
|
data.setInitCounts(novel=xItem.text)
|
||||||
elif xItem.tag == "notesWordCount": # Moved to content attribute in 1.5
|
elif xItem.tag == "notesWordCount": # Moved to content attribute in 1.5
|
||||||
projData.setInitCounts(notes=xItem.text)
|
data.setInitCounts(notes=xItem.text)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseProjectContent(self, xSection, projData, projContent):
|
def _parseProjectContent(self, xSection: ET.Element, data: NWProjectData, content: list):
|
||||||
"""Parse the content section of the XML file.
|
"""Parse the content section of the XML file."""
|
||||||
"""
|
|
||||||
logger.debug("Parsing <content> section")
|
logger.debug("Parsing <content> section")
|
||||||
|
|
||||||
projData.setInitCounts(novel=xSection.attrib.get("novelWords", None)) # Moved in 1.5
|
data.setInitCounts(novel=xSection.attrib.get("novelWords", None)) # Moved in 1.5
|
||||||
projData.setInitCounts(notes=xSection.attrib.get("notesWords", None)) # Moved in 1.5
|
data.setInitCounts(notes=xSection.attrib.get("notesWords", None)) # Moved in 1.5
|
||||||
|
|
||||||
for xItem in xSection:
|
for xItem in xSection:
|
||||||
if xItem.tag != "item":
|
if xItem.tag != "item":
|
||||||
@@ -355,7 +353,7 @@ class ProjectXMLReader:
|
|||||||
if xVal.tag == "name" and "exported" in xVal.attrib:
|
if xVal.tag == "name" and "exported" in xVal.attrib:
|
||||||
name["active"] = checkBool(xVal.attrib.get("exported"), False)
|
name["active"] = checkBool(xVal.attrib.get("exported"), False)
|
||||||
|
|
||||||
projContent.append({
|
content.append({
|
||||||
"name": itemName,
|
"name": itemName,
|
||||||
"itemAttr": item,
|
"itemAttr": item,
|
||||||
"metaAttr": meta,
|
"metaAttr": meta,
|
||||||
@@ -364,14 +362,13 @@ class ProjectXMLReader:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseProjectContentLegacy(self, xSection, projData, projContent):
|
def _parseProjectContentLegacy(self, xSection: ET.Element, data: NWProjectData, content: list):
|
||||||
"""Parse the content section of the XML file for older versions.
|
"""Parse the content section of the XML file for older versions."""
|
||||||
"""
|
|
||||||
logger.debug("Parsing <content> section (legacy format)")
|
logger.debug("Parsing <content> section (legacy format)")
|
||||||
|
|
||||||
# Create maps to look up name -> key for status and importance
|
# Create maps to look up name -> key for status and importance
|
||||||
statusMap = {entry.get("name"): key for key, entry in projData.itemStatus.items()}
|
statusMap = {entry.get("name"): key for key, entry in data.itemStatus.items()}
|
||||||
importMap = {entry.get("name"): key for key, entry in projData.itemImport.items()}
|
importMap = {entry.get("name"): key for key, entry in data.itemImport.items()}
|
||||||
|
|
||||||
for xItem in xSection:
|
for xItem in xSection:
|
||||||
if xItem.tag != "item":
|
if xItem.tag != "item":
|
||||||
@@ -432,7 +429,7 @@ class ProjectXMLReader:
|
|||||||
if item.get("type", "") == "TRASH":
|
if item.get("type", "") == "TRASH":
|
||||||
item["type"] = "ROOT"
|
item["type"] = "ROOT"
|
||||||
|
|
||||||
projContent.append({
|
content.append({
|
||||||
"name": itemName,
|
"name": itemName,
|
||||||
"itemAttr": item,
|
"itemAttr": item,
|
||||||
"metaAttr": meta,
|
"metaAttr": meta,
|
||||||
@@ -441,9 +438,8 @@ class ProjectXMLReader:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseStatusImport(self, xItem, sObject):
|
def _parseStatusImport(self, xItem: ET.Element, sObject: NWStatus):
|
||||||
"""Parse a status or importance entry.
|
"""Parse a status or importance entry."""
|
||||||
"""
|
|
||||||
for xEntry in xItem:
|
for xEntry in xItem:
|
||||||
if xEntry.tag == "entry":
|
if xEntry.tag == "entry":
|
||||||
key = xEntry.attrib.get("key", None)
|
key = xEntry.attrib.get("key", None)
|
||||||
@@ -454,7 +450,7 @@ class ProjectXMLReader:
|
|||||||
sObject.write(key, xEntry.text, (red, green, blue), count)
|
sObject.write(key, xEntry.text, (red, green, blue), count)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _parseDictKeyText(self, xItem):
|
def _parseDictKeyText(self, xItem: ET.Element) -> dict:
|
||||||
"""Parse a dictionary stored with key as an attribute and the
|
"""Parse a dictionary stored with key as an attribute and the
|
||||||
value as the text porperty.
|
value as the text porperty.
|
||||||
"""
|
"""
|
||||||
@@ -474,12 +470,15 @@ class ProjectXMLReader:
|
|||||||
|
|
||||||
|
|
||||||
class ProjectXMLWriter:
|
class ProjectXMLWriter:
|
||||||
|
"""Core: Project XML Writer
|
||||||
|
|
||||||
|
The project writer class will only write a file according to the
|
||||||
|
very latest spec.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
|
|
||||||
self._path = Path(path)
|
self._path = Path(path)
|
||||||
self._error = None
|
self._error = None
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -487,68 +486,68 @@ class ProjectXMLWriter:
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def error(self):
|
def error(self) -> Exception | None:
|
||||||
|
"""Return the error status."""
|
||||||
return self._error
|
return self._error
|
||||||
|
|
||||||
##
|
##
|
||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def write(self, projData, projContent, saveTime, editTime):
|
def write(self, data: NWProjectData, content: list, saveTime: float, editTime: int) -> bool:
|
||||||
"""Write the project data and content to the XML files.
|
"""Write the project data and content to the XML files."""
|
||||||
"""
|
|
||||||
tStart = time()
|
tStart = time()
|
||||||
logger.debug("Writing project XML")
|
logger.debug("Writing project XML")
|
||||||
|
|
||||||
xRoot = ET.Element("novelWriterXML", attrib={
|
xRoot = ET.Element("novelWriterXML", attrib={
|
||||||
"appVersion": str(__version__),
|
"appVersion": str(__version__),
|
||||||
"hexVersion": str(__hexversion__),
|
"hexVersion": str(__hexversion__),
|
||||||
"fileVersion": FILE_VERSION,
|
"fileVersion": FILE_VERSION,
|
||||||
"timeStamp": formatTimeStamp(saveTime),
|
"fileRevision": FILE_REVISION,
|
||||||
|
"timeStamp": formatTimeStamp(saveTime),
|
||||||
})
|
})
|
||||||
|
|
||||||
# Save Project Meta
|
# Save Project Meta
|
||||||
projAttr = {
|
projAttr = {
|
||||||
"id": projData.uuid,
|
"id": data.uuid,
|
||||||
"saveCount": str(projData.saveCount),
|
"saveCount": str(data.saveCount),
|
||||||
"autoCount": str(projData.autoCount),
|
"autoCount": str(data.autoCount),
|
||||||
"editTime": str(editTime),
|
"editTime": str(editTime),
|
||||||
}
|
}
|
||||||
|
|
||||||
xProject = ET.SubElement(xRoot, "project", attrib=projAttr)
|
xProject = ET.SubElement(xRoot, "project", attrib=projAttr)
|
||||||
self._packSingleValue(xProject, "name", projData.name)
|
self._packSingleValue(xProject, "name", data.name)
|
||||||
self._packSingleValue(xProject, "title", projData.title)
|
self._packSingleValue(xProject, "title", data.title)
|
||||||
self._packSingleValue(xProject, "author", projData.author)
|
self._packSingleValue(xProject, "author", data.author)
|
||||||
|
|
||||||
# Save Project Settings
|
# Save Project Settings
|
||||||
xSettings = ET.SubElement(xRoot, "settings")
|
xSettings = ET.SubElement(xRoot, "settings")
|
||||||
self._packSingleValue(xSettings, "doBackup", yesNo(projData.doBackup))
|
self._packSingleValue(xSettings, "doBackup", yesNo(data.doBackup))
|
||||||
self._packSingleValue(xSettings, "language", projData.language)
|
self._packSingleValue(xSettings, "language", data.language)
|
||||||
self._packSingleValue(xSettings, "spellChecking", projData.spellLang, attrib={
|
self._packSingleValue(xSettings, "spellChecking", data.spellLang, attrib={
|
||||||
"auto": yesNo(projData.spellCheck)
|
"auto": yesNo(data.spellCheck)
|
||||||
})
|
})
|
||||||
self._packDictKeyValue(xSettings, "lastHandle", projData.lastHandle)
|
self._packDictKeyValue(xSettings, "lastHandle", data.lastHandle)
|
||||||
self._packDictKeyValue(xSettings, "autoReplace", projData.autoReplace)
|
self._packDictKeyValue(xSettings, "autoReplace", data.autoReplace)
|
||||||
self._packDictKeyValue(xSettings, "titleFormat", projData.titleFormat)
|
|
||||||
|
|
||||||
# Save Status/Importance
|
# Save Status/Importance
|
||||||
xStatus = ET.SubElement(xSettings, "status")
|
xStatus = ET.SubElement(xSettings, "status")
|
||||||
for label, attrib in projData.itemStatus.pack():
|
for label, attrib in data.itemStatus.pack():
|
||||||
self._packSingleValue(xStatus, "entry", label, attrib=attrib)
|
self._packSingleValue(xStatus, "entry", label, attrib=attrib)
|
||||||
|
|
||||||
xImport = ET.SubElement(xSettings, "importance")
|
xImport = ET.SubElement(xSettings, "importance")
|
||||||
for label, attrib in projData.itemImport.pack():
|
for label, attrib in data.itemImport.pack():
|
||||||
self._packSingleValue(xImport, "entry", label, attrib=attrib)
|
self._packSingleValue(xImport, "entry", label, attrib=attrib)
|
||||||
|
|
||||||
# Save Tree Content
|
# Save Tree Content
|
||||||
contAttr = {
|
contAttr = {
|
||||||
"items": str(len(projContent)),
|
"items": str(len(content)),
|
||||||
"novelWords": str(projData.currCounts[0]),
|
"novelWords": str(data.currCounts[0]),
|
||||||
"notesWords": str(projData.currCounts[1]),
|
"notesWords": str(data.currCounts[1]),
|
||||||
}
|
}
|
||||||
|
|
||||||
xContent = ET.SubElement(xRoot, "content", attrib=contAttr)
|
xContent = ET.SubElement(xRoot, "content", attrib=contAttr)
|
||||||
for item in projContent:
|
for item in content:
|
||||||
xItem = ET.SubElement(xContent, "item", attrib=item.get("itemAttr", {}))
|
xItem = ET.SubElement(xContent, "item", attrib=item.get("itemAttr", {}))
|
||||||
ET.SubElement(xItem, "meta", attrib=item.get("metaAttr", {}))
|
ET.SubElement(xItem, "meta", attrib=item.get("metaAttr", {}))
|
||||||
xName = ET.SubElement(xItem, "name", attrib=item.get("nameAttr", {}))
|
xName = ET.SubElement(xItem, "name", attrib=item.get("nameAttr", {}))
|
||||||
@@ -584,16 +583,16 @@ class ProjectXMLWriter:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _packSingleValue(self, xParent, name, value, attrib=None):
|
def _packSingleValue(
|
||||||
"""Pack a single value into an XML element.
|
self, xParent: ET.Element, name: str, value: str | None, attrib: dict | None = None
|
||||||
"""
|
):
|
||||||
|
"""Pack a single value into an XML element."""
|
||||||
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
xItem = ET.SubElement(xParent, name, attrib=attrib or {})
|
||||||
xItem.text = str(value) or ""
|
xItem.text = str(value) or ""
|
||||||
return
|
return
|
||||||
|
|
||||||
def _packDictKeyValue(self, xParent, name, data):
|
def _packDictKeyValue(self, xParent: ET.Element, name: str, data: dict):
|
||||||
"""Pack the entries of a dictionary into an XML element.
|
"""Pack the entries of a dictionary into an XML element."""
|
||||||
"""
|
|
||||||
xItem = ET.SubElement(xParent, name)
|
xItem = ET.SubElement(xParent, name)
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if len(key) > 0:
|
if len(key) > 0:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – Project Storage Class
|
novelWriter – Project Storage Class
|
||||||
===================================
|
===================================
|
||||||
The main class handling the project storage
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2022-11-01 [2.0rc2] NWStorage
|
Created: 2022-11-01 [2.0rc2] NWStorage
|
||||||
@@ -22,10 +21,12 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
|
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
|
||||||
|
|
||||||
@@ -36,29 +37,32 @@ from novelwriter.constants import nwFiles
|
|||||||
from novelwriter.core.document import NWDocument
|
from novelwriter.core.document import NWDocument
|
||||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
|
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NWStorage:
|
class NWStorage:
|
||||||
|
"""Core: Project Storage Class
|
||||||
|
|
||||||
|
The class that handles all paths related to the project storage.
|
||||||
|
"""
|
||||||
|
|
||||||
MODE_INACTIVE = 0
|
MODE_INACTIVE = 0
|
||||||
MODE_INPLACE = 1
|
MODE_INPLACE = 1
|
||||||
MODE_ARCHIVE = 2
|
MODE_ARCHIVE = 2
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, project: NWProject):
|
||||||
|
self._project = project
|
||||||
self.theProject = theProject
|
|
||||||
|
|
||||||
self._storagePath = None
|
self._storagePath = None
|
||||||
self._runtimePath = None
|
self._runtimePath = None
|
||||||
self._lockFilePath = None
|
self._lockFilePath = None
|
||||||
self._openMode = self.MODE_INACTIVE
|
self._openMode = self.MODE_INACTIVE
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""Reset internal variables.
|
"""Reset internal variables."""
|
||||||
"""
|
|
||||||
self._storagePath = None
|
self._storagePath = None
|
||||||
self._runtimePath = None
|
self._runtimePath = None
|
||||||
self._openMode = self.MODE_INACTIVE
|
self._openMode = self.MODE_INACTIVE
|
||||||
@@ -69,15 +73,17 @@ class NWStorage:
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def storagePath(self):
|
def storagePath(self) -> Path | None:
|
||||||
|
"""Get the path where the project is saved."""
|
||||||
return self._storagePath
|
return self._storagePath
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def runtimePath(self):
|
def runtimePath(self) -> Path | None:
|
||||||
|
"""Get the path where the project is saved at runtime."""
|
||||||
return self._runtimePath
|
return self._runtimePath
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def contentPath(self):
|
def contentPath(self) -> Path | None:
|
||||||
"""Return the path used for project content. The folder must
|
"""Return the path used for project content. The folder must
|
||||||
already exist, otherwise this property is None.
|
already exist, otherwise this property is None.
|
||||||
"""
|
"""
|
||||||
@@ -94,12 +100,11 @@ class NWStorage:
|
|||||||
# Core Methods
|
# Core Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def isOpen(self):
|
def isOpen(self) -> bool:
|
||||||
"""Check if the storage location is open.
|
"""Check if the storage location is open."""
|
||||||
"""
|
|
||||||
return self._runtimePath is not None
|
return self._runtimePath is not None
|
||||||
|
|
||||||
def openProjectInPlace(self, path, newProject=False):
|
def openProjectInPlace(self, path: str | Path, newProject: bool = False) -> bool:
|
||||||
"""Open a novelWriter project in-place. That is, it is opened
|
"""Open a novelWriter project in-place. That is, it is opened
|
||||||
directly from a project folder.
|
directly from a project folder.
|
||||||
"""
|
"""
|
||||||
@@ -124,13 +129,15 @@ class NWStorage:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def openProjectArchive(self, path): # pragma: no cover
|
def openProjectArchive(self, path: str | Path) -> bool: # pragma: no cover
|
||||||
"""Placeholder for later implementation. See #977.
|
"""Open the project from a single file.
|
||||||
|
Placeholder for later implementation. See #977.
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def runPostSaveTasks(self, autoSave=False): # pragma: no cover
|
def runPostSaveTasks(self, autoSave: bool = False) -> bool: # pragma: no cover
|
||||||
"""Run tasks after the project has been saved.
|
"""Run tasks after the project has been saved.
|
||||||
|
Placeholder for later implementation. See #977.
|
||||||
"""
|
"""
|
||||||
if self._openMode == self.MODE_INPLACE:
|
if self._openMode == self.MODE_INPLACE:
|
||||||
# Nothing to do, so we just return
|
# Nothing to do, so we just return
|
||||||
@@ -139,8 +146,7 @@ class NWStorage:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def closeSession(self):
|
def closeSession(self):
|
||||||
"""Run tasks related to closing the session.
|
"""Run tasks related to closing the session."""
|
||||||
"""
|
|
||||||
# Clear lockfile
|
# Clear lockfile
|
||||||
self.clear()
|
self.clear()
|
||||||
return
|
return
|
||||||
@@ -149,51 +155,35 @@ class NWStorage:
|
|||||||
# Content Access Methods
|
# Content Access Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def getXmlReader(self):
|
def getXmlReader(self) -> ProjectXMLReader | None:
|
||||||
"""Return a properly configured ProjectXMLReader instance.
|
"""Return a properly configured ProjectXMLReader instance."""
|
||||||
"""
|
|
||||||
if self._runtimePath is None:
|
if self._runtimePath is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
projFile = self._runtimePath / nwFiles.PROJ_FILE
|
projFile = self._runtimePath / nwFiles.PROJ_FILE
|
||||||
xmlReader = ProjectXMLReader(projFile)
|
xmlReader = ProjectXMLReader(projFile)
|
||||||
|
|
||||||
return xmlReader
|
return xmlReader
|
||||||
|
|
||||||
def getXmlWriter(self):
|
def getXmlWriter(self) -> ProjectXMLWriter | None:
|
||||||
"""Return a properly configured ProjectXMLWriter instance.
|
"""Return a properly configured ProjectXMLWriter instance."""
|
||||||
"""
|
|
||||||
if self._runtimePath is None:
|
if self._runtimePath is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
xmlWriter = ProjectXMLWriter(self._runtimePath)
|
xmlWriter = ProjectXMLWriter(self._runtimePath)
|
||||||
|
|
||||||
return xmlWriter
|
return xmlWriter
|
||||||
|
|
||||||
def getDocument(self, tHandle):
|
def getDocument(self, tHandle: str | None) -> NWDocument:
|
||||||
"""Return a document wrapper object.
|
"""Return a document wrapper object."""
|
||||||
"""
|
|
||||||
if self._runtimePath is not None:
|
if self._runtimePath is not None:
|
||||||
return NWDocument(self.theProject, tHandle)
|
return NWDocument(self._project, tHandle)
|
||||||
return NWDocument(self.theProject, None)
|
return NWDocument(self._project, None)
|
||||||
|
|
||||||
def getMetaFile(self, fileName):
|
def getMetaFile(self, fileName: str) -> Path | None:
|
||||||
"""Return the path to a file in the project meta folder.
|
"""Return the path to a file in the project meta folder."""
|
||||||
"""
|
|
||||||
if self._runtimePath is not None:
|
if self._runtimePath is not None:
|
||||||
return self._runtimePath / "meta" / fileName
|
return self._runtimePath / "meta" / fileName
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getCacheFile(self, fileName):
|
def readLockFile(self) -> list:
|
||||||
"""Return the path to a file in the project cache folder.
|
"""Read the project lock file."""
|
||||||
"""
|
|
||||||
if self._runtimePath is not None:
|
|
||||||
return self._runtimePath / "cache" / fileName
|
|
||||||
return None
|
|
||||||
|
|
||||||
def readLockFile(self):
|
|
||||||
"""Read the project lock file.
|
|
||||||
"""
|
|
||||||
if self._lockFilePath is None:
|
if self._lockFilePath is None:
|
||||||
return ["ERROR"]
|
return ["ERROR"]
|
||||||
|
|
||||||
@@ -212,9 +202,8 @@ class NWStorage:
|
|||||||
|
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def writeLockFile(self):
|
def writeLockFile(self) -> bool:
|
||||||
"""Write the project lock file.
|
"""Write the project lock file."""
|
||||||
"""
|
|
||||||
if self._lockFilePath is None:
|
if self._lockFilePath is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -231,9 +220,8 @@ class NWStorage:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def clearLockFile(self):
|
def clearLockFile(self) -> bool:
|
||||||
"""Remove the lock file, if it exists.
|
"""Remove the lock file, if it exists."""
|
||||||
"""
|
|
||||||
if self._lockFilePath is None:
|
if self._lockFilePath is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -247,7 +235,7 @@ class NWStorage:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def zipIt(self, target, compression=None):
|
def zipIt(self, target: str | Path, compression: int | None = None) -> bool:
|
||||||
"""Zip the content of the project at its runtime location into a
|
"""Zip the content of the project at its runtime location into a
|
||||||
zip file. This process will only grab files that are supposed to
|
zip file. This process will only grab files that are supposed to
|
||||||
be in the project. All non-project files will be left out.
|
be in the project. All non-project files will be left out.
|
||||||
@@ -260,11 +248,13 @@ class NWStorage:
|
|||||||
baseMeta = basePath / "meta"
|
baseMeta = basePath / "meta"
|
||||||
baseCont = basePath / "content"
|
baseCont = basePath / "content"
|
||||||
files = [
|
files = [
|
||||||
(basePath / nwFiles.PROJ_FILE, nwFiles.PROJ_FILE),
|
(basePath / nwFiles.PROJ_FILE, nwFiles.PROJ_FILE),
|
||||||
(baseMeta / nwFiles.OPTS_FILE, f"meta/{nwFiles.OPTS_FILE}"),
|
(basePath / nwFiles.PROJ_BACKUP, nwFiles.PROJ_BACKUP),
|
||||||
(baseMeta / nwFiles.SESS_STATS, f"meta/{nwFiles.SESS_STATS}"),
|
(baseMeta / nwFiles.BUILDS_FILE, f"meta/{nwFiles.BUILDS_FILE}"),
|
||||||
(baseMeta / nwFiles.INDEX_FILE, f"meta/{nwFiles.INDEX_FILE}"),
|
(baseMeta / nwFiles.INDEX_FILE, f"meta/{nwFiles.INDEX_FILE}"),
|
||||||
(baseMeta / nwFiles.PROJ_DICT, f"meta/{nwFiles.PROJ_DICT}"),
|
(baseMeta / nwFiles.OPTS_FILE, f"meta/{nwFiles.OPTS_FILE}"),
|
||||||
|
(baseMeta / nwFiles.PROJ_DICT, f"meta/{nwFiles.PROJ_DICT}"),
|
||||||
|
(baseMeta / nwFiles.SESS_STATS, f"meta/{nwFiles.SESS_STATS}"),
|
||||||
]
|
]
|
||||||
for contItem in baseCont.iterdir():
|
for contItem in baseCont.iterdir():
|
||||||
name = contItem.name
|
name = contItem.name
|
||||||
@@ -291,9 +281,8 @@ class NWStorage:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _prepareStorage(self, checkLegacy=True, newProject=False):
|
def _prepareStorage(self, checkLegacy: bool = True, newProject: bool = False) -> bool:
|
||||||
"""Prepare the storage area for the project.
|
"""Prepare the storage area for the project."""
|
||||||
"""
|
|
||||||
path = self._runtimePath
|
path = self._runtimePath
|
||||||
if not isinstance(path, Path):
|
if not isinstance(path, Path):
|
||||||
logger.error("No path set")
|
logger.error("No path set")
|
||||||
@@ -318,7 +307,6 @@ class NWStorage:
|
|||||||
try:
|
try:
|
||||||
path.mkdir(exist_ok=True)
|
path.mkdir(exist_ok=True)
|
||||||
(path / "content").mkdir(exist_ok=True)
|
(path / "content").mkdir(exist_ok=True)
|
||||||
(path / "cache").mkdir(exist_ok=True)
|
|
||||||
(path / "meta").mkdir(exist_ok=True)
|
(path / "meta").mkdir(exist_ok=True)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Failed to create required project folders", exc_info=exc)
|
logger.error("Failed to create required project folders", exc_info=exc)
|
||||||
@@ -385,8 +373,7 @@ class NWStorage:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _deleteDeprecatedFiles(self, path: Path):
|
def _deleteDeprecatedFiles(self, path: Path):
|
||||||
"""Delete files that are no longer used by novelWriter.
|
"""Delete files that are no longer used by novelWriter."""
|
||||||
"""
|
|
||||||
remove = [
|
remove = [
|
||||||
path / "meta" / "mainOptions.json", # Replaced in 0.5
|
path / "meta" / "mainOptions.json", # Replaced in 0.5
|
||||||
path / "meta" / "exportOptions.json", # Replaced in 0.5
|
path / "meta" / "exportOptions.json", # Replaced in 0.5
|
||||||
@@ -394,16 +381,20 @@ class NWStorage:
|
|||||||
path / "meta" / "timelineOptions.json", # Replaced in 0.5
|
path / "meta" / "timelineOptions.json", # Replaced in 0.5
|
||||||
path / "meta" / "docMergeOptions.json", # Replaced in 0.5
|
path / "meta" / "docMergeOptions.json", # Replaced in 0.5
|
||||||
path / "meta" / "sessionLogOptions.json", # Replaced in 0.5
|
path / "meta" / "sessionLogOptions.json", # Replaced in 0.5
|
||||||
|
path / "cache" / "prevBuild.json", # Dropped in 2.1 Beta 1
|
||||||
|
path / "cache", # Dropped in 2.1 Beta 1
|
||||||
path / "ToC.json", # Dropped in 1.0 RC 1
|
path / "ToC.json", # Dropped in 1.0 RC 1
|
||||||
]
|
]
|
||||||
for item in remove:
|
for item in remove:
|
||||||
if item.is_file():
|
if item.exists():
|
||||||
try:
|
try:
|
||||||
item.unlink()
|
if item.is_dir():
|
||||||
|
item.rmdir()
|
||||||
|
else:
|
||||||
|
item.unlink()
|
||||||
logger.info("Deleted: %s", item)
|
logger.info("Deleted: %s", item)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("Failed to delete: %s", item, exc_info=exc)
|
logger.warning("Failed to delete: %s", item, exc_info=exc)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class NWStorage
|
# END Class NWStorage
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – HTML Text Converter
|
novelWriter – HTML Text Converter
|
||||||
=================================
|
=================================
|
||||||
Extends the Tokenizer class to generate HTML output
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2019-05-07 [0.0.1]
|
Created: 2019-05-07 [0.0.1]
|
||||||
@@ -22,24 +21,36 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from time import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
|
from novelwriter.common import formatTimeStamp
|
||||||
from novelwriter.constants import nwKeyWords, nwLabels, nwHtmlUnicode
|
from novelwriter.constants import nwKeyWords, nwLabels, nwHtmlUnicode
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.tokenizer import Tokenizer, stripEscape
|
from novelwriter.core.tokenizer import Tokenizer, stripEscape
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ToHtml(Tokenizer):
|
class ToHtml(Tokenizer):
|
||||||
|
"""Core: HTML Document Writer
|
||||||
|
|
||||||
|
Extend the Tokenizer class to writer HTML output. This class is
|
||||||
|
also used by the Document Viewer, and Manuscript Build Preview.
|
||||||
|
"""
|
||||||
|
|
||||||
M_PREVIEW = 0 # Tweak output for the DocViewer
|
M_PREVIEW = 0 # Tweak output for the DocViewer
|
||||||
M_EXPORT = 1 # Tweak output for saving to HTML or printing
|
M_EXPORT = 1 # Tweak output for saving to HTML or printing
|
||||||
M_EBOOK = 2 # Tweak output for converting to epub
|
M_EBOOK = 2 # Tweak output for converting to epub
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, project: NWProject):
|
||||||
super().__init__(theProject)
|
super().__init__(project)
|
||||||
|
|
||||||
self._genMode = self.M_EXPORT
|
self._genMode = self.M_EXPORT
|
||||||
self._cssStyles = True
|
self._cssStyles = True
|
||||||
@@ -63,7 +74,7 @@ class ToHtml(Tokenizer):
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setPreview(self, doComments, doSynopsis):
|
def setPreview(self, doComments: bool, doSynopsis: bool):
|
||||||
"""If we're using this class to generate markdown preview, we
|
"""If we're using this class to generate markdown preview, we
|
||||||
need to make a few changes to formatting, which is managed by
|
need to make a few changes to formatting, which is managed by
|
||||||
these flags.
|
these flags.
|
||||||
@@ -74,14 +85,14 @@ class ToHtml(Tokenizer):
|
|||||||
self._doSynopsis = doSynopsis
|
self._doSynopsis = doSynopsis
|
||||||
return
|
return
|
||||||
|
|
||||||
def setStyles(self, cssStyles):
|
def setStyles(self, cssStyles: bool):
|
||||||
"""Enable/disable CSS styling. Some elements may still have
|
"""Enable or disable CSS styling. Some elements may still have
|
||||||
class tags.
|
class tags.
|
||||||
"""
|
"""
|
||||||
self._cssStyles = cssStyles
|
self._cssStyles = cssStyles
|
||||||
return
|
return
|
||||||
|
|
||||||
def setReplaceUnicode(self, doReplace):
|
def setReplaceUnicode(self, doReplace: bool):
|
||||||
"""Set the translation map to either minimal or full unicode for
|
"""Set the translation map to either minimal or full unicode for
|
||||||
html entities replacement.
|
html entities replacement.
|
||||||
"""
|
"""
|
||||||
@@ -92,16 +103,14 @@ class ToHtml(Tokenizer):
|
|||||||
if doReplace:
|
if doReplace:
|
||||||
# Extend to all relevant Unicode characters
|
# Extend to all relevant Unicode characters
|
||||||
self._trMap.update(str.maketrans(nwHtmlUnicode.U_TO_H))
|
self._trMap.update(str.maketrans(nwHtmlUnicode.U_TO_H))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def getFullResultSize(self):
|
def getFullResultSize(self) -> int:
|
||||||
"""Return the size of the full HTML result.
|
"""Return the size of the full HTML result."""
|
||||||
"""
|
|
||||||
return sum([len(x) for x in self._fullHTML])
|
return sum([len(x) for x in self._fullHTML])
|
||||||
|
|
||||||
def doPreProcessing(self):
|
def doPreProcessing(self):
|
||||||
@@ -109,12 +118,12 @@ class ToHtml(Tokenizer):
|
|||||||
characters into their respective HTML entities.
|
characters into their respective HTML entities.
|
||||||
"""
|
"""
|
||||||
super().doPreProcessing()
|
super().doPreProcessing()
|
||||||
self._theText = self._theText.translate(self._trMap)
|
self._text = self._text.translate(self._trMap)
|
||||||
return
|
return
|
||||||
|
|
||||||
def doConvert(self):
|
def doConvert(self):
|
||||||
"""Convert the list of text tokens into a HTML document saved
|
"""Convert the list of text tokens into a HTML document saved
|
||||||
to theResult.
|
to _result.
|
||||||
"""
|
"""
|
||||||
if self._genMode == self.M_PREVIEW:
|
if self._genMode == self.M_PREVIEW:
|
||||||
htmlTags = { # HTML4 + CSS2 (for Qt)
|
htmlTags = { # HTML4 + CSS2 (for Qt)
|
||||||
@@ -149,13 +158,13 @@ class ToHtml(Tokenizer):
|
|||||||
h3 = "h3"
|
h3 = "h3"
|
||||||
h4 = "h4"
|
h4 = "h4"
|
||||||
|
|
||||||
self._theResult = ""
|
self._result = ""
|
||||||
|
|
||||||
thisPar = []
|
thisPar = []
|
||||||
parStyle = None
|
parStyle = None
|
||||||
tmpResult = []
|
tmpResult = []
|
||||||
|
|
||||||
for tType, tLine, tText, tFormat, tStyle in self._theTokens:
|
for tType, tLine, tText, tFormat, tStyle in self._tokens:
|
||||||
|
|
||||||
# Replace < and > with HTML entities
|
# Replace < and > with HTML entities
|
||||||
if tFormat:
|
if tFormat:
|
||||||
@@ -282,51 +291,64 @@ class ToHtml(Tokenizer):
|
|||||||
tTemp = f"<p{hStyle}>{self._formatKeywords(tText)}</p>\n"
|
tTemp = f"<p{hStyle}>{self._formatKeywords(tText)}</p>\n"
|
||||||
tmpResult.append(tTemp)
|
tmpResult.append(tTemp)
|
||||||
|
|
||||||
self._theResult = "".join(tmpResult)
|
self._result = "".join(tmpResult)
|
||||||
tmpResult = []
|
tmpResult = []
|
||||||
|
|
||||||
if self._genMode != self.M_PREVIEW:
|
if self._genMode != self.M_PREVIEW:
|
||||||
self._fullHTML.append(self._theResult)
|
self._fullHTML.append(self._result)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveHTML5(self, savePath):
|
def saveHtml5(self, path: str | Path):
|
||||||
"""Save the data to an .html file.
|
"""Save the data to an HTML file."""
|
||||||
"""
|
with open(path, mode="w", encoding="utf-8") as fObj:
|
||||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
fObj.write((
|
||||||
theStyle = self.getStyleSheet()
|
|
||||||
theStyle.append("article {width: 800px; margin: 40px auto;}")
|
|
||||||
bodyText = "".join(self._fullHTML)
|
|
||||||
bodyText = bodyText.replace("\t", "	").rstrip()
|
|
||||||
|
|
||||||
theHtml = (
|
|
||||||
"<!DOCTYPE html>\n"
|
"<!DOCTYPE html>\n"
|
||||||
"<html>\n"
|
"<html>\n"
|
||||||
"<head>\n"
|
"<head>\n"
|
||||||
"<meta charset='utf-8'>\n"
|
"<meta charset='utf-8'>\n"
|
||||||
"<title>{projTitle:s}</title>\n"
|
"<title>{title:s}</title>\n"
|
||||||
"</head>\n"
|
"</head>\n"
|
||||||
"<style>\n"
|
"<style>\n"
|
||||||
"{htmlStyle:s}\n"
|
"{style:s}\n"
|
||||||
"</style>\n"
|
"</style>\n"
|
||||||
"<body>\n"
|
"<body>\n"
|
||||||
"<article>\n"
|
"<article>\n"
|
||||||
"{bodyText:s}\n"
|
"{body:s}\n"
|
||||||
"</article>\n"
|
"</article>\n"
|
||||||
"</body>\n"
|
"</body>\n"
|
||||||
"</html>\n"
|
"</html>\n"
|
||||||
).format(
|
).format(
|
||||||
projTitle=self.theProject.data.name,
|
title=self._project.data.name,
|
||||||
htmlStyle="\n".join(theStyle),
|
style="\n".join(self.getStyleSheet()),
|
||||||
bodyText=bodyText,
|
body=("".join(self._fullHTML)).replace("\t", "	").rstrip(),
|
||||||
)
|
))
|
||||||
outFile.write(theHtml)
|
logger.info("Wrote file: %s", path)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def replaceTabs(self, nSpaces=8, spaceChar=" "):
|
def saveHtmlJson(self, path: str | Path):
|
||||||
"""Replace tabs with spaces in the html.
|
"""Save the data to a JSON file."""
|
||||||
"""
|
timeStamp = time()
|
||||||
|
data = {
|
||||||
|
"meta": {
|
||||||
|
"projectName": self._project.data.name,
|
||||||
|
"novelTitle": self._project.data.title,
|
||||||
|
"novelAuthor": self._project.data.author,
|
||||||
|
"buildTime": int(timeStamp),
|
||||||
|
"buildTimeStr": formatTimeStamp(timeStamp),
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"css": self.getStyleSheet(),
|
||||||
|
"html": [page.rstrip("\n").split("\n") for page in self.fullHTML],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with open(path, mode="w", encoding="utf-8") as fObj:
|
||||||
|
json.dump(data, fObj, indent=2)
|
||||||
|
logger.info("Wrote file: %s", path)
|
||||||
|
return
|
||||||
|
|
||||||
|
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "):
|
||||||
|
"""Replace tabs with spaces in the html."""
|
||||||
htmlText = []
|
htmlText = []
|
||||||
tabSpace = spaceChar*nSpaces
|
tabSpace = spaceChar*nSpaces
|
||||||
for aLine in self._fullHTML:
|
for aLine in self._fullHTML:
|
||||||
@@ -335,20 +357,19 @@ class ToHtml(Tokenizer):
|
|||||||
self._fullHTML = htmlText
|
self._fullHTML = htmlText
|
||||||
return
|
return
|
||||||
|
|
||||||
def getStyleSheet(self):
|
def getStyleSheet(self) -> list:
|
||||||
"""Generate a stylesheet appropriate for the current settings.
|
"""Generate a stylesheet for the current settings."""
|
||||||
"""
|
styles = []
|
||||||
theStyles = []
|
|
||||||
if not self._cssStyles:
|
if not self._cssStyles:
|
||||||
return theStyles
|
return styles
|
||||||
|
|
||||||
mScale = self._lineHeight/1.15
|
mScale = self._lineHeight/1.15
|
||||||
textAlign = "justify" if self._doJustify else "left"
|
textAlign = "justify" if self._doJustify else "left"
|
||||||
|
|
||||||
theStyles.append("body {{font-family: '{0:s}'; font-size: {1:d}pt;}}".format(
|
styles.append("body {{font-family: '{0:s}'; font-size: {1:d}pt;}}".format(
|
||||||
self._textFont, self._textSize
|
self._textFont, self._textSize
|
||||||
))
|
))
|
||||||
theStyles.append((
|
styles.append((
|
||||||
"p {{"
|
"p {{"
|
||||||
"text-align: {0}; line-height: {1:d}%; "
|
"text-align: {0}; line-height: {1:d}%; "
|
||||||
"margin-top: {2:.2f}em; margin-bottom: {3:.2f}em;"
|
"margin-top: {2:.2f}em; margin-bottom: {3:.2f}em;"
|
||||||
@@ -359,7 +380,7 @@ class ToHtml(Tokenizer):
|
|||||||
mScale * self._marginText[0],
|
mScale * self._marginText[0],
|
||||||
mScale * self._marginText[1],
|
mScale * self._marginText[1],
|
||||||
))
|
))
|
||||||
theStyles.append((
|
styles.append((
|
||||||
"h1 {{"
|
"h1 {{"
|
||||||
"color: rgb(66, 113, 174); "
|
"color: rgb(66, 113, 174); "
|
||||||
"page-break-after: avoid; "
|
"page-break-after: avoid; "
|
||||||
@@ -369,7 +390,7 @@ class ToHtml(Tokenizer):
|
|||||||
).format(
|
).format(
|
||||||
mScale * self._marginHead1[0], mScale * self._marginHead1[1]
|
mScale * self._marginHead1[0], mScale * self._marginHead1[1]
|
||||||
))
|
))
|
||||||
theStyles.append((
|
styles.append((
|
||||||
"h2 {{"
|
"h2 {{"
|
||||||
"color: rgb(66, 113, 174); "
|
"color: rgb(66, 113, 174); "
|
||||||
"page-break-after: avoid; "
|
"page-break-after: avoid; "
|
||||||
@@ -379,7 +400,7 @@ class ToHtml(Tokenizer):
|
|||||||
).format(
|
).format(
|
||||||
mScale * self._marginHead2[0], mScale * self._marginHead2[1]
|
mScale * self._marginHead2[0], mScale * self._marginHead2[1]
|
||||||
))
|
))
|
||||||
theStyles.append((
|
styles.append((
|
||||||
"h3 {{"
|
"h3 {{"
|
||||||
"color: rgb(50, 50, 50); "
|
"color: rgb(50, 50, 50); "
|
||||||
"page-break-after: avoid; "
|
"page-break-after: avoid; "
|
||||||
@@ -389,7 +410,7 @@ class ToHtml(Tokenizer):
|
|||||||
).format(
|
).format(
|
||||||
mScale * self._marginHead3[0], mScale * self._marginHead3[1]
|
mScale * self._marginHead3[0], mScale * self._marginHead3[1]
|
||||||
))
|
))
|
||||||
theStyles.append((
|
styles.append((
|
||||||
"h4 {{"
|
"h4 {{"
|
||||||
"color: rgb(50, 50, 50); "
|
"color: rgb(50, 50, 50); "
|
||||||
"page-break-after: avoid; "
|
"page-break-after: avoid; "
|
||||||
@@ -399,7 +420,7 @@ class ToHtml(Tokenizer):
|
|||||||
).format(
|
).format(
|
||||||
mScale * self._marginHead4[0], mScale * self._marginHead4[1]
|
mScale * self._marginHead4[0], mScale * self._marginHead4[1]
|
||||||
))
|
))
|
||||||
theStyles.append((
|
styles.append((
|
||||||
".title {{"
|
".title {{"
|
||||||
"font-size: 2.5em; "
|
"font-size: 2.5em; "
|
||||||
"margin-top: {0:.2f}em; "
|
"margin-top: {0:.2f}em; "
|
||||||
@@ -408,7 +429,7 @@ class ToHtml(Tokenizer):
|
|||||||
).format(
|
).format(
|
||||||
mScale * self._marginTitle[0], mScale * self._marginTitle[1]
|
mScale * self._marginTitle[0], mScale * self._marginTitle[1]
|
||||||
))
|
))
|
||||||
theStyles.append((
|
styles.append((
|
||||||
".sep, .skip {{"
|
".sep, .skip {{"
|
||||||
"text-align: center; "
|
"text-align: center; "
|
||||||
"margin-top: {0:.2f}em; "
|
"margin-top: {0:.2f}em; "
|
||||||
@@ -418,61 +439,58 @@ class ToHtml(Tokenizer):
|
|||||||
mScale, mScale
|
mScale, mScale
|
||||||
))
|
))
|
||||||
|
|
||||||
theStyles.append("a {color: rgb(66, 113, 174);}")
|
styles.append("a {color: rgb(66, 113, 174);}")
|
||||||
theStyles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
styles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||||
theStyles.append(".break {text-align: left;}")
|
styles.append(".break {text-align: left;}")
|
||||||
theStyles.append(".synopsis {font-style: italic;}")
|
styles.append(".synopsis {font-style: italic;}")
|
||||||
theStyles.append(".comment {font-style: italic; color: rgb(100, 100, 100);}")
|
styles.append(".comment {font-style: italic; color: rgb(100, 100, 100);}")
|
||||||
|
|
||||||
return theStyles
|
return styles
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _formatSynopsis(self, tText):
|
def _formatSynopsis(self, text: str) -> str:
|
||||||
"""Apply HTML formatting to synopsis.
|
"""Apply HTML formatting to synopsis."""
|
||||||
"""
|
|
||||||
if self._genMode == self.M_PREVIEW:
|
if self._genMode == self.M_PREVIEW:
|
||||||
sSynop = self._trSynopsis
|
sSynop = self._trSynopsis
|
||||||
return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {tText}</p>\n"
|
return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {text}</p>\n"
|
||||||
else:
|
else:
|
||||||
sSynop = self._localLookup("Synopsis")
|
sSynop = self._localLookup("Synopsis")
|
||||||
return f"<p class='synopsis'><strong>{sSynop}:</strong> {tText}</p>\n"
|
return f"<p class='synopsis'><strong>{sSynop}:</strong> {text}</p>\n"
|
||||||
|
|
||||||
def _formatComments(self, tText):
|
def _formatComments(self, text: str) -> str:
|
||||||
"""Apply HTML formatting to comments.
|
"""Apply HTML formatting to comments."""
|
||||||
"""
|
|
||||||
if self._genMode == self.M_PREVIEW:
|
if self._genMode == self.M_PREVIEW:
|
||||||
return f"<p class='comment'>{tText}</p>\n"
|
return f"<p class='comment'>{text}</p>\n"
|
||||||
else:
|
else:
|
||||||
sComm = self._localLookup("Comment")
|
sComm = self._localLookup("Comment")
|
||||||
return f"<p class='comment'><strong>{sComm}:</strong> {tText}</p>\n"
|
return f"<p class='comment'><strong>{sComm}:</strong> {text}</p>\n"
|
||||||
|
|
||||||
def _formatKeywords(self, tText):
|
def _formatKeywords(self, text: str) -> str:
|
||||||
"""Apply HTML formatting to keywords.
|
"""Apply HTML formatting to keywords."""
|
||||||
"""
|
valid, bits, _ = self._project.index.scanThis("@"+text)
|
||||||
isValid, theBits, _ = self.theProject.index.scanThis("@"+tText)
|
if not valid or not bits:
|
||||||
if not isValid or not theBits:
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
retText = ""
|
result = ""
|
||||||
refTags = []
|
tags = []
|
||||||
if theBits[0] in nwLabels.KEY_NAME:
|
if bits[0] in nwLabels.KEY_NAME:
|
||||||
retText += f"<span class='tags'>{nwLabels.KEY_NAME[theBits[0]]}:</span> "
|
result += f"<span class='tags'>{nwLabels.KEY_NAME[bits[0]]}:</span> "
|
||||||
if len(theBits) > 1:
|
if len(bits) > 1:
|
||||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
if bits[0] == nwKeyWords.TAG_KEY:
|
||||||
retText += f"<a name='tag_{theBits[1]}'>{theBits[1]}</a>"
|
result += f"<a name='tag_{bits[1]}'>{bits[1]}</a>"
|
||||||
else:
|
else:
|
||||||
if self._genMode == self.M_PREVIEW:
|
if self._genMode == self.M_PREVIEW:
|
||||||
for tTag in theBits[1:]:
|
for tTag in bits[1:]:
|
||||||
refTags.append(f"<a href='#{theBits[0][1:]}={tTag}'>{tTag}</a>")
|
tags.append(f"<a href='#{bits[0][1:]}={tTag}'>{tTag}</a>")
|
||||||
retText += ", ".join(refTags)
|
result += ", ".join(tags)
|
||||||
else:
|
else:
|
||||||
for tTag in theBits[1:]:
|
for tTag in bits[1:]:
|
||||||
refTags.append(f"<a href='#tag_{tTag}'>{tTag}</a>")
|
tags.append(f"<a href='#tag_{tTag}'>{tTag}</a>")
|
||||||
retText += ", ".join(refTags)
|
result += ", ".join(tags)
|
||||||
|
|
||||||
return retText
|
return result
|
||||||
|
|
||||||
# END Class ToHtml
|
# END Class ToHtml
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – Text Tokenizer
|
novelWriter – Text Tokenizer
|
||||||
============================
|
============================
|
||||||
Split novelWriter plain text into its elements
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2019-05-05 [0.0.1]
|
Created: 2019-05-05 [0.0.1] Tokenizer
|
||||||
|
Created: 2023-05-23 [2.1b1] HeadingFormatter
|
||||||
|
|
||||||
This file is a part of novelWriter
|
This file is a part of novelWriter
|
||||||
Copyright 2018–2023, Veronica Berglyd Olsen
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
@@ -22,25 +22,30 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from time import time
|
||||||
|
from pathlib import Path
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from PyQt5.QtCore import QCoreApplication, QRegularExpression
|
from PyQt5.QtCore import QCoreApplication, QRegularExpression
|
||||||
|
|
||||||
from novelwriter.enum import nwItemLayout, nwItemType
|
from novelwriter.enum import nwItemLayout, nwItemType
|
||||||
from novelwriter.common import numberToRoman, checkInt
|
from novelwriter.common import formatTimeStamp, numberToRoman, checkInt
|
||||||
from novelwriter.constants import nwConst, nwRegEx, nwUnicode
|
from novelwriter.constants import nwConst, nwHeadFmt, nwRegEx, nwUnicode
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def stripEscape(text):
|
def stripEscape(text):
|
||||||
"""Helper function to strip escaped markdown characters from
|
"""Helper function to strip escaped Markdown characters from
|
||||||
paragraph text.
|
paragraph text.
|
||||||
"""
|
"""
|
||||||
if "\\" in text:
|
if "\\" in text:
|
||||||
@@ -51,6 +56,13 @@ def stripEscape(text):
|
|||||||
|
|
||||||
|
|
||||||
class Tokenizer(ABC):
|
class Tokenizer(ABC):
|
||||||
|
"""Core: Text Tokenizer Abstract Base Class
|
||||||
|
|
||||||
|
This is the base class for all document build classes. It parses the
|
||||||
|
novelWriter markup format and generates a registry of tokens and
|
||||||
|
text that can be further processed into other output formats by
|
||||||
|
subclasses.
|
||||||
|
"""
|
||||||
|
|
||||||
# In-Text Format
|
# In-Text Format
|
||||||
FMT_B_B = 1 # Begin bold
|
FMT_B_B = 1 # Begin bold
|
||||||
@@ -88,19 +100,18 @@ class Tokenizer(ABC):
|
|||||||
A_IND_L = 0x0100 # Left indentation
|
A_IND_L = 0x0100 # Left indentation
|
||||||
A_IND_R = 0x0200 # Right indentation
|
A_IND_R = 0x0200 # Right indentation
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, project: NWProject):
|
||||||
|
|
||||||
self.theProject = theProject
|
self._project = project
|
||||||
|
|
||||||
# Data Variables
|
# Data Variables
|
||||||
self._theText = "" # The raw text to be tokenized
|
self._text = "" # The raw text to be tokenized
|
||||||
self._theHandle = None # The handle associated with the text
|
self._nwItem = None # The NWItem associated with the handle
|
||||||
self._theItem = None # The NWItem associated with the handle
|
self._tokens = [] # The list of the processed tokens
|
||||||
self._theTokens = [] # The list of the processed tokens
|
self._result = "" # The result of the last document
|
||||||
self._theResult = "" # The result of the last document
|
|
||||||
|
|
||||||
self._keepMarkdown = False # Whether to keep the markdown text
|
self._keepMarkdown = False # Whether to keep the markdown text
|
||||||
self._theMarkdown = [] # The result novelWriter markdown of all documents
|
self._allMarkdown = [] # The result novelWriter markdown of all documents
|
||||||
|
|
||||||
# User Settings
|
# User Settings
|
||||||
self._textFont = "Serif" # Output text font
|
self._textFont = "Serif" # Output text font
|
||||||
@@ -124,11 +135,11 @@ class Tokenizer(ABC):
|
|||||||
self._marginMeta = (0.000, 0.584)
|
self._marginMeta = (0.000, 0.584)
|
||||||
|
|
||||||
# Title Formats
|
# Title Formats
|
||||||
self._fmtTitle = "%title%" # Formatting for titles
|
self._fmtTitle = nwHeadFmt.TITLE # Formatting for titles
|
||||||
self._fmtChapter = "%title%" # Formatting for numbered chapters
|
self._fmtChapter = nwHeadFmt.TITLE # Formatting for numbered chapters
|
||||||
self._fmtUnNum = "%title%" # Formatting for unnumbered chapters
|
self._fmtUnNum = nwHeadFmt.TITLE # Formatting for unnumbered chapters
|
||||||
self._fmtScene = "%title%" # Formatting for scenes
|
self._fmtScene = nwHeadFmt.TITLE # Formatting for scenes
|
||||||
self._fmtSection = "%title%" # Formatting for sections
|
self._fmtSection = nwHeadFmt.TITLE # Formatting for sections
|
||||||
|
|
||||||
self._hideScene = False # Do not include scene headers
|
self._hideScene = False # Do not include scene headers
|
||||||
self._hideSection = False # Do not include section headers
|
self._hideSection = False # Do not include section headers
|
||||||
@@ -136,10 +147,8 @@ class Tokenizer(ABC):
|
|||||||
self._linkHeaders = False # Add an anchor before headers
|
self._linkHeaders = False # Add an anchor before headers
|
||||||
|
|
||||||
# Instance Variables
|
# Instance Variables
|
||||||
self._numChapter = 0 # Counter for chapter numbers
|
self._hFormatter = HeadingFormatter(self._project)
|
||||||
self._numChScene = 0 # Counter for scene number within chapter
|
self._firstScene = False # Flag to indicate that the first scene of the chapter
|
||||||
self._numAbsScene = 0 # Counter for scene number within novel
|
|
||||||
self._firstScene = False # Flag to indicate that the first scene of the chapter
|
|
||||||
|
|
||||||
# This File
|
# This File
|
||||||
self._isNone = False # Document has unknown layout
|
self._isNone = False # Document has unknown layout
|
||||||
@@ -151,7 +160,7 @@ class Tokenizer(ABC):
|
|||||||
self._errData = []
|
self._errData = []
|
||||||
|
|
||||||
# Function Mapping
|
# Function Mapping
|
||||||
self._localLookup = self.theProject.localLookup
|
self._localLookup = self._project.localLookup
|
||||||
self.tr = partial(QCoreApplication.translate, "Tokenizer")
|
self.tr = partial(QCoreApplication.translate, "Tokenizer")
|
||||||
|
|
||||||
# Cached Translations
|
# Cached Translations
|
||||||
@@ -164,111 +173,136 @@ class Tokenizer(ABC):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def theResult(self):
|
def theResult(self) -> str:
|
||||||
return self._theResult
|
"""The result of the build process."""
|
||||||
|
return self._result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def theMarkdown(self):
|
def theMarkdown(self) -> list:
|
||||||
return self._theMarkdown
|
"""The combined novelWriter Markdown text."""
|
||||||
|
return self._allMarkdown
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def errData(self):
|
def errData(self) -> list:
|
||||||
|
"""The error data."""
|
||||||
return self._errData
|
return self._errData
|
||||||
|
|
||||||
##
|
##
|
||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setTitleFormat(self, fmtTitle):
|
def setTitleFormat(self, hFormat: str):
|
||||||
self._fmtTitle = fmtTitle.strip()
|
"""Set the title format pattern."""
|
||||||
|
self._fmtTitle = hFormat.strip()
|
||||||
return
|
return
|
||||||
|
|
||||||
def setChapterFormat(self, fmtChapter):
|
def setChapterFormat(self, hFormat: str):
|
||||||
self._fmtChapter = fmtChapter.strip()
|
"""Set the chapert format pattern."""
|
||||||
|
self._fmtChapter = hFormat.strip()
|
||||||
return
|
return
|
||||||
|
|
||||||
def setUnNumberedFormat(self, fmtUnNum):
|
def setUnNumberedFormat(self, hFormat: str):
|
||||||
self._fmtUnNum = fmtUnNum.strip()
|
"""Set the unnumbered format pattern."""
|
||||||
|
self._fmtUnNum = hFormat.strip()
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSceneFormat(self, fmtScene, hideScene):
|
def setSceneFormat(self, hFormat: str, hide: bool):
|
||||||
self._fmtScene = fmtScene.strip()
|
"""Set the scene format pattern and hidden status."""
|
||||||
self._hideScene = hideScene
|
self._fmtScene = hFormat.strip()
|
||||||
|
self._hideScene = hide
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSectionFormat(self, fmtSection, hideSection):
|
def setSectionFormat(self, hFormat: str, hide: bool):
|
||||||
self._fmtSection = fmtSection.strip()
|
"""Set the section format pattern and hidden status."""
|
||||||
self._hideSection = hideSection
|
self._fmtSection = hFormat.strip()
|
||||||
|
self._hideSection = hide
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFont(self, textFont, textSize, textFixed=False):
|
def setFont(self, family: str, size: int, isFixed: bool = False):
|
||||||
self._textFont = textFont
|
"""Set the build font."""
|
||||||
self._textSize = round(int(textSize))
|
self._textFont = family
|
||||||
self._textFixed = textFixed
|
self._textSize = round(int(size))
|
||||||
|
self._textFixed = isFixed
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLineHeight(self, lineHeight):
|
def setLineHeight(self, height: float):
|
||||||
self._lineHeight = min(max(float(lineHeight), 0.5), 5.0)
|
"""Set the line height between 0.5 and 5.0."""
|
||||||
|
self._lineHeight = min(max(float(height), 0.5), 5.0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBlockIndent(self, blockIndent):
|
def setBlockIndent(self, indent: float):
|
||||||
self._blockIndent = min(max(float(blockIndent), 0.0), 10.0)
|
"""Set the block indent between 0.0 and 10.0."""
|
||||||
|
self._blockIndent = min(max(float(indent), 0.0), 10.0)
|
||||||
return
|
return
|
||||||
|
|
||||||
def setJustify(self, doJustify):
|
def setJustify(self, state: bool):
|
||||||
self._doJustify = doJustify
|
"""Enable or disable text justification."""
|
||||||
|
self._doJustify = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTitleMargins(self, mUpper, mLower):
|
def setTitleMargins(self, upper: float, lower: float):
|
||||||
self._marginTitle = (float(mUpper), float(mLower))
|
"""Set the upper and lower title margin."""
|
||||||
|
self._marginTitle = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHead1Margins(self, mUpper, mLower):
|
def setHead1Margins(self, upper: float, lower: float):
|
||||||
self._marginHead1 = (float(mUpper), float(mLower))
|
"""Set the upper and lower header 1 margin."""
|
||||||
|
self._marginHead1 = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHead2Margins(self, mUpper, mLower):
|
def setHead2Margins(self, upper: float, lower: float):
|
||||||
self._marginHead2 = (float(mUpper), float(mLower))
|
"""Set the upper and lower header 2 margin."""
|
||||||
|
self._marginHead2 = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHead3Margins(self, mUpper, mLower):
|
def setHead3Margins(self, upper: float, lower: float):
|
||||||
self._marginHead3 = (float(mUpper), float(mLower))
|
"""Set the upper and lower header 3 margin."""
|
||||||
|
self._marginHead3 = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setHead4Margins(self, mUpper, mLower):
|
def setHead4Margins(self, upper: float, lower: float):
|
||||||
self._marginHead4 = (float(mUpper), float(mLower))
|
"""Set the upper and lower header 4 margin."""
|
||||||
|
self._marginHead4 = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTextMargins(self, mUpper, mLower):
|
def setTextMargins(self, upper: float, lower: float):
|
||||||
self._marginText = (float(mUpper), float(mLower))
|
"""Set the upper and lower text margin."""
|
||||||
|
self._marginText = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setMetaMargins(self, mUpper, mLower):
|
def setMetaMargins(self, upper: float, lower: float):
|
||||||
self._marginMeta = (float(mUpper), float(mLower))
|
"""Set the upper and lower meta text margin."""
|
||||||
|
self._marginMeta = (float(upper), float(lower))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLinkHeaders(self, linkHeaders):
|
def setLinkHeaders(self, state: bool):
|
||||||
self._linkHeaders = linkHeaders
|
"""Enable or disable adding an anchor before headers."""
|
||||||
|
self._linkHeaders = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBodyText(self, doBodyText):
|
def setBodyText(self, state: bool):
|
||||||
self._doBodyText = doBodyText
|
"""Include body text in build."""
|
||||||
|
self._doBodyText = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setSynopsis(self, doSynopsis):
|
def setSynopsis(self, state: bool):
|
||||||
self._doSynopsis = doSynopsis
|
"""Include synopsis comments in build."""
|
||||||
|
self._doSynopsis = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setComments(self, doComments):
|
def setComments(self, state: bool):
|
||||||
self._doComments = doComments
|
"""Include comments in build."""
|
||||||
|
self._doComments = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setKeywords(self, doKeywords):
|
def setKeywords(self, state: bool):
|
||||||
self._doKeywords = doKeywords
|
"""Include keywords in build."""
|
||||||
|
self._doKeywords = state
|
||||||
return
|
return
|
||||||
|
|
||||||
def setKeepMarkdown(self, keepMarkdown):
|
def setKeepMarkdown(self, state: bool):
|
||||||
self._keepMarkdown = keepMarkdown
|
"""Keep original markdown during build."""
|
||||||
|
self._keepMarkdown = state
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -279,10 +313,12 @@ class Tokenizer(ABC):
|
|||||||
def doConvert(self):
|
def doConvert(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def addRootHeading(self, theHandle):
|
def addRootHeading(self, tHandle: str) -> bool:
|
||||||
"""Add a heading at the start of a new root folder.
|
"""Add a heading at the start of a new root folder."""
|
||||||
"""
|
if not self._project.tree.checkType(tHandle, nwItemType.ROOT):
|
||||||
if not self.theProject.tree.checkType(theHandle, nwItemType.ROOT):
|
return False
|
||||||
|
theItem = self._project.tree[tHandle]
|
||||||
|
if not theItem:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self._isFirst:
|
if self._isFirst:
|
||||||
@@ -291,61 +327,58 @@ class Tokenizer(ABC):
|
|||||||
else:
|
else:
|
||||||
textAlign = self.A_PBB | self.A_CENTRE
|
textAlign = self.A_PBB | self.A_CENTRE
|
||||||
|
|
||||||
theItem = self.theProject.tree[theHandle]
|
|
||||||
locNotes = self._localLookup("Notes")
|
locNotes = self._localLookup("Notes")
|
||||||
theTitle = f"{locNotes}: {theItem.itemName}"
|
theTitle = f"{locNotes}: {theItem.itemName}"
|
||||||
self._theTokens = []
|
self._tokens = []
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_TITLE, 0, theTitle, None, textAlign
|
self.T_TITLE, 0, theTitle, None, textAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
self._theMarkdown.append(f"# {theTitle}\n\n")
|
self._allMarkdown.append(f"# {theTitle}\n\n")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def setText(self, theHandle, theText=None):
|
def setText(self, tHandle: str, text: str | None = None) -> bool:
|
||||||
"""Set the text for the tokenizer from a handle. If theText is
|
"""Set the text for the tokenizer from a handle. If theText is
|
||||||
not set, load it from the file.
|
not set, load it from the file.
|
||||||
"""
|
"""
|
||||||
self._theHandle = theHandle
|
self._nwItem = self._project.tree[tHandle]
|
||||||
self._theItem = self.theProject.tree[theHandle]
|
if self._nwItem is None:
|
||||||
if self._theItem is None:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if theText is None:
|
if text is None:
|
||||||
theText = self.theProject.storage.getDocument(theHandle).readDocument() or ""
|
text = self._project.storage.getDocument(tHandle).readDocument() or ""
|
||||||
|
|
||||||
self._theText = theText
|
self._text = text
|
||||||
|
|
||||||
docSize = len(self._theText)
|
docSize = len(self._text)
|
||||||
if docSize > nwConst.MAX_DOCSIZE:
|
if docSize > nwConst.MAX_DOCSIZE:
|
||||||
errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format(
|
errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format(
|
||||||
self._theItem.itemName, f"{docSize/1.0e6:.2f}"
|
self._nwItem.itemName, f"{docSize/1.0e6:.2f}"
|
||||||
)
|
)
|
||||||
self._theText = "# {0}\n\n{1}\n\n".format(self.tr("ERROR"), errVal)
|
self._text = "# {0}\n\n{1}\n\n".format(self.tr("ERROR"), errVal)
|
||||||
self._errData.append(errVal)
|
self._errData.append(errVal)
|
||||||
|
|
||||||
self._isNone = self._theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
self._isNone = self._nwItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||||
self._isNovel = self._theItem.itemLayout == nwItemLayout.DOCUMENT
|
self._isNovel = self._nwItem.itemLayout == nwItemLayout.DOCUMENT
|
||||||
self._isNote = self._theItem.itemLayout == nwItemLayout.NOTE
|
self._isNote = self._nwItem.itemLayout == nwItemLayout.NOTE
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def doPreProcessing(self):
|
def doPreProcessing(self):
|
||||||
"""Run trough the various replace doctionaries.
|
"""Run trough the various replace dictionaries."""
|
||||||
"""
|
|
||||||
# Process the user's auto-replace dictionary
|
# Process the user's auto-replace dictionary
|
||||||
autoReplace = self.theProject.data.autoReplace
|
autoReplace = self._project.data.autoReplace
|
||||||
if len(autoReplace) > 0:
|
if len(autoReplace) > 0:
|
||||||
repDict = {}
|
repDict = {}
|
||||||
for aKey, aVal in autoReplace.items():
|
for aKey, aVal in autoReplace.items():
|
||||||
repDict[f"<{aKey}>"] = aVal
|
repDict[f"<{aKey}>"] = aVal
|
||||||
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
|
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
|
||||||
self._theText = xRep.sub(lambda x: repDict[x.group(0)], self._theText)
|
self._text = xRep.sub(lambda x: repDict[x.group(0)], self._text)
|
||||||
|
|
||||||
# Process the character translation map
|
# Process the character translation map
|
||||||
trDict = {nwUnicode.U_MAPOSS: nwUnicode.U_RSQUO}
|
trDict = {nwUnicode.U_MAPOSS: nwUnicode.U_RSQUO}
|
||||||
self._theText = self._theText.translate(str.maketrans(trDict))
|
self._text = self._text.translate(str.maketrans(trDict))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -371,17 +404,17 @@ class Tokenizer(ABC):
|
|||||||
(QRegularExpression(nwRegEx.FMT_ST), [None, self.FMT_D_B, None, self.FMT_D_E]),
|
(QRegularExpression(nwRegEx.FMT_ST), [None, self.FMT_D_B, None, self.FMT_D_E]),
|
||||||
]
|
]
|
||||||
|
|
||||||
self._theTokens = []
|
self._tokens = []
|
||||||
tmpMarkdown = []
|
tmpMarkdown = []
|
||||||
nLine = 0
|
nLine = 0
|
||||||
breakNext = False
|
breakNext = False
|
||||||
for aLine in self._theText.splitlines():
|
for aLine in self._text.splitlines():
|
||||||
nLine += 1
|
nLine += 1
|
||||||
sLine = aLine.strip()
|
sLine = aLine.strip()
|
||||||
|
|
||||||
# Check for blank lines
|
# Check for blank lines
|
||||||
if len(sLine) == 0:
|
if len(sLine) == 0:
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_EMPTY, nLine, "", None, self.A_NONE
|
self.T_EMPTY, nLine, "", None, self.A_NONE
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
@@ -406,7 +439,7 @@ class Tokenizer(ABC):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
elif sLine == "[VSPACE]":
|
elif sLine == "[VSPACE]":
|
||||||
self._theTokens.append(
|
self._tokens.append(
|
||||||
(self.T_SKIP, nLine, "", None, sAlign)
|
(self.T_SKIP, nLine, "", None, sAlign)
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
@@ -414,11 +447,11 @@ class Tokenizer(ABC):
|
|||||||
elif sLine.startswith("[VSPACE:") and sLine.endswith("]"):
|
elif sLine.startswith("[VSPACE:") and sLine.endswith("]"):
|
||||||
nSkip = checkInt(sLine[8:-1], 0)
|
nSkip = checkInt(sLine[8:-1], 0)
|
||||||
if nSkip >= 1:
|
if nSkip >= 1:
|
||||||
self._theTokens.append(
|
self._tokens.append(
|
||||||
(self.T_SKIP, nLine, "", None, sAlign)
|
(self.T_SKIP, nLine, "", None, sAlign)
|
||||||
)
|
)
|
||||||
if nSkip > 1:
|
if nSkip > 1:
|
||||||
self._theTokens += (nSkip - 1) * [
|
self._tokens += (nSkip - 1) * [
|
||||||
(self.T_SKIP, nLine, "", None, self.A_NONE)
|
(self.T_SKIP, nLine, "", None, self.A_NONE)
|
||||||
]
|
]
|
||||||
continue
|
continue
|
||||||
@@ -427,20 +460,20 @@ class Tokenizer(ABC):
|
|||||||
cLine = aLine[1:].lstrip()
|
cLine = aLine[1:].lstrip()
|
||||||
synTag = cLine[:9].lower()
|
synTag = cLine[:9].lower()
|
||||||
if synTag == "synopsis:":
|
if synTag == "synopsis:":
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, sAlign
|
self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._doSynopsis and self._keepMarkdown:
|
if self._doSynopsis and self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
else:
|
else:
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_COMMENT, nLine, aLine[1:].strip(), None, sAlign
|
self.T_COMMENT, nLine, aLine[1:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._doComments and self._keepMarkdown:
|
if self._doComments and self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[0] == "@":
|
elif aLine[0] == "@":
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_KEYWORD, nLine, aLine[1:].strip(), None, sAlign
|
self.T_KEYWORD, nLine, aLine[1:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._doKeywords and self._keepMarkdown:
|
if self._doKeywords and self._keepMarkdown:
|
||||||
@@ -451,7 +484,7 @@ class Tokenizer(ABC):
|
|||||||
sAlign |= self.A_CENTRE
|
sAlign |= self.A_CENTRE
|
||||||
sAlign |= self.A_PBB
|
sAlign |= self.A_PBB
|
||||||
|
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_HEAD1, nLine, aLine[2:].strip(), None, sAlign
|
self.T_HEAD1, nLine, aLine[2:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
@@ -461,21 +494,21 @@ class Tokenizer(ABC):
|
|||||||
if self._isNovel:
|
if self._isNovel:
|
||||||
sAlign |= self.A_PBB
|
sAlign |= self.A_PBB
|
||||||
|
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_HEAD2, nLine, aLine[3:].strip(), None, sAlign
|
self.T_HEAD2, nLine, aLine[3:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:4] == "### ":
|
elif aLine[:4] == "### ":
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_HEAD3, nLine, aLine[4:].strip(), None, sAlign
|
self.T_HEAD3, nLine, aLine[4:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
elif aLine[:5] == "#### ":
|
elif aLine[:5] == "#### ":
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_HEAD4, nLine, aLine[5:].strip(), None, sAlign
|
self.T_HEAD4, nLine, aLine[5:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
@@ -487,7 +520,7 @@ class Tokenizer(ABC):
|
|||||||
else:
|
else:
|
||||||
tStyle = self.T_HEAD1
|
tStyle = self.T_HEAD1
|
||||||
|
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
tStyle, nLine, aLine[3:].strip(), None, sAlign | self.A_CENTRE
|
tStyle, nLine, aLine[3:].strip(), None, sAlign | self.A_CENTRE
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
@@ -500,7 +533,7 @@ class Tokenizer(ABC):
|
|||||||
else:
|
else:
|
||||||
tStyle = self.T_HEAD2
|
tStyle = self.T_HEAD2
|
||||||
|
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
tStyle, nLine, aLine[4:].strip(), None, sAlign
|
tStyle, nLine, aLine[4:].strip(), None, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
@@ -557,33 +590,33 @@ class Tokenizer(ABC):
|
|||||||
# Save the line as is, but append the array of formatting locations
|
# Save the line as is, but append the array of formatting locations
|
||||||
# sorted by position
|
# sorted by position
|
||||||
fmtPos = sorted(fmtPos, key=itemgetter(0))
|
fmtPos = sorted(fmtPos, key=itemgetter(0))
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_TEXT, nLine, aLine, fmtPos, sAlign
|
self.T_TEXT, nLine, aLine, fmtPos, sAlign
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("%s\n" % aLine)
|
tmpMarkdown.append("%s\n" % aLine)
|
||||||
|
|
||||||
# If we have content, turn off the first page flag
|
# If we have content, turn off the first page flag
|
||||||
if self._isFirst and self._theTokens:
|
if self._isFirst and self._tokens:
|
||||||
self._isFirst = False
|
self._isFirst = False
|
||||||
|
|
||||||
# Make sure the token array doesn't start with a page break
|
# Make sure the token array doesn't start with a page break
|
||||||
# on the very first page, adding a blank first page.
|
# on the very first page, adding a blank first page.
|
||||||
if self._theTokens[0][4] & self.A_PBB:
|
if self._tokens[0][4] & self.A_PBB:
|
||||||
tToken = self._theTokens[0]
|
tToken = self._tokens[0]
|
||||||
self._theTokens[0] = (
|
self._tokens[0] = (
|
||||||
tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] & ~self.A_PBB
|
tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] & ~self.A_PBB
|
||||||
)
|
)
|
||||||
|
|
||||||
# Always add an empty line at the end of the file
|
# Always add an empty line at the end of the file
|
||||||
self._theTokens.append((
|
self._tokens.append((
|
||||||
self.T_EMPTY, nLine, "", None, self.A_NONE
|
self.T_EMPTY, nLine, "", None, self.A_NONE
|
||||||
))
|
))
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
tmpMarkdown.append("\n")
|
tmpMarkdown.append("\n")
|
||||||
|
|
||||||
if self._keepMarkdown:
|
if self._keepMarkdown:
|
||||||
self._theMarkdown.append("".join(tmpMarkdown))
|
self._allMarkdown.append("".join(tmpMarkdown))
|
||||||
|
|
||||||
# Second Pass
|
# Second Pass
|
||||||
# ===========
|
# ===========
|
||||||
@@ -591,13 +624,13 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
pToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
|
pToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
|
||||||
nToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
|
nToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
|
||||||
tCount = len(self._theTokens)
|
tCount = len(self._tokens)
|
||||||
for n, tToken in enumerate(self._theTokens):
|
for n, tToken in enumerate(self._tokens):
|
||||||
|
|
||||||
if n > 0:
|
if n > 0:
|
||||||
pToken = self._theTokens[n-1]
|
pToken = self._tokens[n-1]
|
||||||
if n < tCount - 1:
|
if n < tCount - 1:
|
||||||
nToken = self._theTokens[n+1]
|
nToken = self._tokens[n+1]
|
||||||
|
|
||||||
if tToken[0] == self.T_KEYWORD:
|
if tToken[0] == self.T_KEYWORD:
|
||||||
aStyle = tToken[4]
|
aStyle = tToken[4]
|
||||||
@@ -605,20 +638,20 @@ class Tokenizer(ABC):
|
|||||||
aStyle |= self.A_Z_TOPMRG
|
aStyle |= self.A_Z_TOPMRG
|
||||||
if nToken[0] == self.T_KEYWORD:
|
if nToken[0] == self.T_KEYWORD:
|
||||||
aStyle |= self.A_Z_BTMMRG
|
aStyle |= self.A_Z_BTMMRG
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
tToken[0], tToken[1], tToken[2], tToken[3], aStyle
|
tToken[0], tToken[1], tToken[2], tToken[3], aStyle
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def doHeaders(self):
|
def doHeaders(self) -> bool:
|
||||||
"""Apply formatting to the text headers for novel files. This
|
"""Apply formatting to the text headers for novel files. This
|
||||||
also applies chapter and scene numbering.
|
also applies chapter and scene numbering.
|
||||||
"""
|
"""
|
||||||
if not self._isNovel:
|
if not self._isNovel:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for n, tToken in enumerate(self._theTokens):
|
for n, tToken in enumerate(self._tokens):
|
||||||
|
|
||||||
# In case we see text before a scene, we reset the flag
|
# In case we see text before a scene, we reset the flag
|
||||||
if tToken[0] == self.T_TEXT:
|
if tToken[0] == self.T_TEXT:
|
||||||
@@ -627,8 +660,8 @@ class Tokenizer(ABC):
|
|||||||
elif tToken[0] == self.T_HEAD1:
|
elif tToken[0] == self.T_HEAD1:
|
||||||
# Partition
|
# Partition
|
||||||
|
|
||||||
tTemp = self._formatHeading(self._fmtTitle, tToken[2])
|
tTemp = self._hFormatter.apply(self._fmtTitle, tToken[2])
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
tToken[0], tToken[1], tTemp, None, tToken[4]
|
tToken[0], tToken[1], tTemp, None, tToken[4]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -637,106 +670,148 @@ class Tokenizer(ABC):
|
|||||||
|
|
||||||
# Numbered or Unnumbered
|
# Numbered or Unnumbered
|
||||||
if tToken[0] == self.T_UNNUM:
|
if tToken[0] == self.T_UNNUM:
|
||||||
tTemp = self._formatHeading(self._fmtUnNum, tToken[2])
|
tTemp = self._hFormatter.apply(self._fmtUnNum, tToken[2])
|
||||||
else:
|
else:
|
||||||
self._numChapter += 1
|
self._hFormatter.incChapter()
|
||||||
tTemp = self._formatHeading(self._fmtChapter, tToken[2])
|
tTemp = self._hFormatter.apply(self._fmtChapter, tToken[2])
|
||||||
|
|
||||||
# Format the chapter header
|
# Format the chapter header
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
tToken[0], tToken[1], tTemp, None, tToken[4]
|
tToken[0], tToken[1], tTemp, None, tToken[4]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set scene variables
|
# Set scene variables
|
||||||
self._firstScene = True
|
self._firstScene = True
|
||||||
self._numChScene = 0
|
self._hFormatter.resetScene()
|
||||||
|
|
||||||
elif tToken[0] == self.T_HEAD3:
|
elif tToken[0] == self.T_HEAD3:
|
||||||
# Scene
|
# Scene
|
||||||
|
|
||||||
self._numChScene += 1
|
self._hFormatter.incScene()
|
||||||
self._numAbsScene += 1
|
|
||||||
|
|
||||||
tTemp = self._formatHeading(self._fmtScene, tToken[2])
|
tTemp = self._hFormatter.apply(self._fmtScene, tToken[2])
|
||||||
if tTemp == "" and self._hideScene:
|
if tTemp == "" and self._hideScene:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
elif tTemp == "" and not self._hideScene:
|
elif tTemp == "" and not self._hideScene:
|
||||||
if self._firstScene:
|
if self._firstScene:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
self.T_SKIP, tToken[1], "", None, tToken[4]
|
self.T_SKIP, tToken[1], "", None, tToken[4]
|
||||||
)
|
)
|
||||||
elif tTemp == self._fmtScene:
|
elif tTemp == self._fmtScene:
|
||||||
if self._firstScene:
|
if self._firstScene:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE
|
self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
tToken[0], tToken[1], tTemp, None, tToken[4]
|
tToken[0], tToken[1], tTemp, None, tToken[4]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Definitely no longer the first scene
|
|
||||||
self._firstScene = False
|
self._firstScene = False
|
||||||
|
|
||||||
elif tToken[0] == self.T_HEAD4:
|
elif tToken[0] == self.T_HEAD4:
|
||||||
# Section
|
# Section
|
||||||
|
|
||||||
tTemp = self._formatHeading(self._fmtSection, tToken[2])
|
tTemp = self._hFormatter.apply(self._fmtSection, tToken[2])
|
||||||
if tTemp == "" and self._hideSection:
|
if tTemp == "" and self._hideSection:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
self.T_EMPTY, tToken[1], "", None, self.A_NONE
|
||||||
)
|
)
|
||||||
elif tTemp == "" and not self._hideSection:
|
elif tTemp == "" and not self._hideSection:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
self.T_SKIP, tToken[1], "", None, tToken[4]
|
self.T_SKIP, tToken[1], "", None, tToken[4]
|
||||||
)
|
)
|
||||||
elif tTemp == self._fmtSection:
|
elif tTemp == self._fmtSection:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE
|
self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._theTokens[n] = (
|
self._tokens[n] = (
|
||||||
tToken[0], tToken[1], tTemp, None, tToken[4]
|
tToken[0], tToken[1], tTemp, None, tToken[4]
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveRawMarkdown(self, savePath):
|
def saveRawMarkdown(self, path: str | Path):
|
||||||
"""Save the data to a plain text file.
|
"""Save the raw text to a plain text file."""
|
||||||
"""
|
with open(path, mode="w", encoding="utf-8") as outFile:
|
||||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
for nwdPage in self._allMarkdown:
|
||||||
for nwdPage in self._theMarkdown:
|
|
||||||
outFile.write(nwdPage)
|
outFile.write(nwdPage)
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
def saveRawMarkdownJSON(self, path: str | Path):
|
||||||
# Internal Functions
|
"""Save the raw text to a JSON file."""
|
||||||
##
|
timeStamp = time()
|
||||||
|
data = {
|
||||||
def _formatHeading(self, theTitle, theText):
|
"meta": {
|
||||||
"""Replaces the %keyword% strings.
|
"projectName": self._project.data.name,
|
||||||
"""
|
"novelTitle": self._project.data.title,
|
||||||
theTitle = theTitle.replace(r"%title%", theText)
|
"novelAuthor": self._project.data.author,
|
||||||
theTitle = theTitle.replace(r"%ch%", str(self._numChapter))
|
"buildTime": int(timeStamp),
|
||||||
theTitle = theTitle.replace(r"%sc%", str(self._numChScene))
|
"buildTimeStr": formatTimeStamp(timeStamp),
|
||||||
theTitle = theTitle.replace(r"%sca%", str(self._numAbsScene))
|
},
|
||||||
if r"%chw%" in theTitle:
|
"text": {
|
||||||
theTitle = theTitle.replace(r"%chw%", self._localLookup(self._numChapter))
|
"nwd": [page.rstrip("\n").split("\n") for page in self._allMarkdown],
|
||||||
if r"%chi%" in theTitle:
|
}
|
||||||
theTitle = theTitle.replace(r"%chi%", numberToRoman(self._numChapter, True))
|
}
|
||||||
if r"%chI%" in theTitle:
|
with open(path, mode="w", encoding="utf-8") as fObj:
|
||||||
theTitle = theTitle.replace(r"%chI%", numberToRoman(self._numChapter, False))
|
json.dump(data, fObj, indent=2)
|
||||||
|
return
|
||||||
return theTitle[:1].upper() + theTitle[1:]
|
|
||||||
|
|
||||||
# END Class Tokenizer
|
# END Class Tokenizer
|
||||||
|
|
||||||
|
|
||||||
|
class HeadingFormatter:
|
||||||
|
|
||||||
|
def __init__(self, project: NWProject):
|
||||||
|
self._project = project
|
||||||
|
self._chCount = 0
|
||||||
|
self._scChCount = 0
|
||||||
|
self._scAbsCount = 0
|
||||||
|
return
|
||||||
|
|
||||||
|
def incChapter(self):
|
||||||
|
"""Increment the chapter counter."""
|
||||||
|
self._chCount += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
def incScene(self):
|
||||||
|
"""Increment the scene counters."""
|
||||||
|
self._scChCount += 1
|
||||||
|
self._scAbsCount += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
def resetScene(self):
|
||||||
|
"""Reset the chapter scene counter."""
|
||||||
|
self._scChCount = 0
|
||||||
|
return
|
||||||
|
|
||||||
|
def apply(self, hFormat: str, text: str):
|
||||||
|
"""Apply formatting to a specific heading."""
|
||||||
|
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
||||||
|
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
|
||||||
|
hFormat = hFormat.replace(nwHeadFmt.SC_NUM, str(self._scChCount))
|
||||||
|
hFormat = hFormat.replace(nwHeadFmt.SC_ABS, str(self._scAbsCount))
|
||||||
|
if nwHeadFmt.CH_WORD in hFormat:
|
||||||
|
chWord = self._project.localLookup(self._chCount)
|
||||||
|
hFormat = hFormat.replace(nwHeadFmt.CH_WORD, chWord)
|
||||||
|
if nwHeadFmt.CH_ROML in hFormat:
|
||||||
|
chRom = numberToRoman(self._chCount, toLower=True)
|
||||||
|
hFormat = hFormat.replace(nwHeadFmt.CH_ROML, chRom)
|
||||||
|
if nwHeadFmt.CH_ROMU in hFormat:
|
||||||
|
chRom = numberToRoman(self._chCount, toLower=False)
|
||||||
|
hFormat = hFormat.replace(nwHeadFmt.CH_ROMU, chRom)
|
||||||
|
|
||||||
|
return hFormat
|
||||||
|
|
||||||
|
# END Class HeadingFormatter
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – Markdown Text Converter
|
novelWriter – Markdown Text Converter
|
||||||
=====================================
|
=====================================
|
||||||
Extends the Tokenizer class to generate Makrdown output
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2021-02-06 [1.2b1]
|
Created: 2021-02-06 [1.2b1]
|
||||||
@@ -22,22 +21,32 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from novelwriter.constants import nwLabels
|
from novelwriter.constants import nwLabels
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.tokenizer import Tokenizer
|
from novelwriter.core.tokenizer import Tokenizer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ToMarkdown(Tokenizer):
|
class ToMarkdown(Tokenizer):
|
||||||
|
"""Core: Markdown Document Writer
|
||||||
|
|
||||||
|
Extend the Tokenizer class to writer Markdown output. It supports
|
||||||
|
both Standard Markdown and GitHub Flavour Markdown (Extended). The
|
||||||
|
class also supports concatenating novelWriter markup files.
|
||||||
|
"""
|
||||||
|
|
||||||
M_STD = 0 # Standard Markdown
|
M_STD = 0 # Standard Markdown
|
||||||
M_GH = 1 # GitHub Markdown
|
M_GH = 1 # GitHub Markdown
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, project: NWProject):
|
||||||
super().__init__(theProject)
|
super().__init__(project)
|
||||||
|
|
||||||
self._genMode = self.M_STD
|
self._genMode = self.M_STD
|
||||||
self._fullMD = []
|
self._fullMD = []
|
||||||
@@ -49,7 +58,8 @@ class ToMarkdown(Tokenizer):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fullMD(self):
|
def fullMD(self) -> list:
|
||||||
|
"""Return the markdown as a list."""
|
||||||
return self._fullMD
|
return self._fullMD
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -68,9 +78,8 @@ class ToMarkdown(Tokenizer):
|
|||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def getFullResultSize(self):
|
def getFullResultSize(self) -> int:
|
||||||
"""Return the size of the full Markdown result.
|
"""Return the size of the full Markdown result."""
|
||||||
"""
|
|
||||||
return sum([len(x) for x in self._fullMD])
|
return sum([len(x) for x in self._fullMD])
|
||||||
|
|
||||||
def doConvert(self):
|
def doConvert(self):
|
||||||
@@ -98,12 +107,12 @@ class ToMarkdown(Tokenizer):
|
|||||||
self.FMT_D_E: "~~",
|
self.FMT_D_E: "~~",
|
||||||
}
|
}
|
||||||
|
|
||||||
self._theResult = ""
|
self._result = ""
|
||||||
|
|
||||||
thisPar = []
|
thisPar = []
|
||||||
tmpResult = []
|
tmpResult = []
|
||||||
|
|
||||||
for tType, _, tText, tFormat, tStyle in self._theTokens:
|
for tType, _, tText, tFormat, tStyle in self._tokens:
|
||||||
|
|
||||||
# Process Text Type
|
# Process Text Type
|
||||||
if tType == self.T_EMPTY:
|
if tType == self.T_EMPTY:
|
||||||
@@ -159,56 +168,44 @@ class ToMarkdown(Tokenizer):
|
|||||||
elif tType == self.T_KEYWORD and self._doKeywords:
|
elif tType == self.T_KEYWORD and self._doKeywords:
|
||||||
tmpResult.append(self._formatKeywords(tText, tStyle))
|
tmpResult.append(self._formatKeywords(tText, tStyle))
|
||||||
|
|
||||||
self._theResult = "".join(tmpResult)
|
self._result = "".join(tmpResult)
|
||||||
tmpResult = []
|
tmpResult = []
|
||||||
|
|
||||||
self._fullMD.append(self._theResult)
|
self._fullMD.append(self._result)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveMarkdown(self, savePath):
|
def saveMarkdown(self, path: str | Path):
|
||||||
"""Save the data to a plain text file.
|
"""Save the data to a plain text file."""
|
||||||
"""
|
with open(path, mode="w", encoding="utf-8") as outFile:
|
||||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
outFile.write("".join(self._fullMD))
|
||||||
theText = "".join(self._fullMD)
|
logger.info("Wrote file: %s", path)
|
||||||
outFile.write(theText)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def replaceTabs(self, nSpaces=8, spaceChar=" "):
|
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "):
|
||||||
"""Replace tabs with spaces.
|
"""Replace tabs with spaces."""
|
||||||
"""
|
spaces = spaceChar*nSpaces
|
||||||
fullMD = []
|
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
|
||||||
eightSpace = spaceChar*nSpaces
|
|
||||||
for aPage in self._fullMD:
|
|
||||||
fullMD.append(aPage.replace("\t", eightSpace))
|
|
||||||
|
|
||||||
self._fullMD = fullMD
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _formatKeywords(self, tText, tStyle):
|
def _formatKeywords(self, text: str, style: int) -> str:
|
||||||
"""Apply Markdown formatting to keywords.
|
"""Apply Markdown formatting to keywords."""
|
||||||
"""
|
valid, bits, _ = self._project.index.scanThis("@"+text)
|
||||||
isValid, theBits, _ = self.theProject.index.scanThis("@"+tText)
|
if not valid or not bits:
|
||||||
if not isValid or not theBits:
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
retText = ""
|
result = ""
|
||||||
if theBits[0] in nwLabels.KEY_NAME:
|
if bits[0] in nwLabels.KEY_NAME:
|
||||||
retText += f"**{nwLabels.KEY_NAME[theBits[0]]}:** "
|
result += f"**{nwLabels.KEY_NAME[bits[0]]}:** "
|
||||||
|
if len(bits) > 1:
|
||||||
|
result += ", ".join(bits[1:])
|
||||||
|
|
||||||
if len(theBits) > 1:
|
result += " \n" if style & self.A_Z_BTMMRG else "\n\n"
|
||||||
retText += ", ".join(theBits[1:])
|
|
||||||
|
|
||||||
if tStyle & self.A_Z_BTMMRG:
|
return result
|
||||||
retText += " \n"
|
|
||||||
else:
|
|
||||||
retText += "\n\n"
|
|
||||||
|
|
||||||
return retText
|
|
||||||
|
|
||||||
# END Class ToMarkdown
|
# END Class ToMarkdown
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – ODT Text Converter
|
novelWriter – ODT Text Converter
|
||||||
================================
|
================================
|
||||||
Extends the Tokenizer class to generate ODT and FODT files
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2021-01-26 [1.2b1]
|
Created: 2021-01-26 [1.2b1] ToOdt
|
||||||
|
Created: 2021-01-27 [1.2b1] ODTParagraphStyle
|
||||||
|
Created: 2021-01-27 [1.2b1] ODTTextStyle
|
||||||
|
Created: 2021-08-14 [1.5b1] XMLParagraph
|
||||||
|
|
||||||
This file is a part of novelWriter
|
This file is a part of novelWriter
|
||||||
Copyright 2018–2023, Veronica Berglyd Olsen
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
@@ -22,8 +24,10 @@ General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
@@ -33,6 +37,7 @@ from datetime import datetime
|
|||||||
from novelwriter import __version__
|
from novelwriter import __version__
|
||||||
from novelwriter.common import xmlIndent
|
from novelwriter.common import xmlIndent
|
||||||
from novelwriter.constants import nwKeyWords, nwLabels
|
from novelwriter.constants import nwKeyWords, nwLabels
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.tokenizer import Tokenizer, stripEscape
|
from novelwriter.core.tokenizer import Tokenizer, stripEscape
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -52,9 +57,8 @@ for ns, uri in XML_NS.items():
|
|||||||
ET.register_namespace(ns, uri)
|
ET.register_namespace(ns, uri)
|
||||||
|
|
||||||
|
|
||||||
def _mkTag(ns, tag):
|
def _mkTag(ns: str, tag: str) -> str:
|
||||||
"""Assemble namespace and tag name.
|
"""Assemble namespace and tag name."""
|
||||||
"""
|
|
||||||
uri = XML_NS.get(ns, "")
|
uri = XML_NS.get(ns, "")
|
||||||
if uri:
|
if uri:
|
||||||
return f"{{{uri}}}{tag}"
|
return f"{{{uri}}}{tag}"
|
||||||
@@ -86,9 +90,16 @@ M_DEL = ~X_DEL
|
|||||||
|
|
||||||
|
|
||||||
class ToOdt(Tokenizer):
|
class ToOdt(Tokenizer):
|
||||||
|
"""Core: Open Document Writer
|
||||||
|
|
||||||
def __init__(self, theProject, isFlat):
|
Extend the Tokenizer class to writer Open Document files. The output
|
||||||
super().__init__(theProject)
|
should conform to the 1.3 Extended standard.
|
||||||
|
|
||||||
|
Test with: https://odfvalidator.org/
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, project: NWProject, isFlat: bool):
|
||||||
|
super().__init__(project)
|
||||||
|
|
||||||
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
|
self._isFlat = isFlat # Flat: .fodt, otherwise .odt
|
||||||
|
|
||||||
@@ -175,36 +186,26 @@ class ToOdt(Tokenizer):
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setLanguage(self, theLang):
|
def setLanguage(self, language: str):
|
||||||
"""Set language for the document.
|
"""Set language for the document."""
|
||||||
"""
|
if language:
|
||||||
if theLang is None:
|
langBits = language.split("_")
|
||||||
return False
|
self._dLanguage = langBits[0]
|
||||||
|
if len(langBits) > 1:
|
||||||
|
self._dCountry = langBits[1]
|
||||||
|
return
|
||||||
|
|
||||||
langBits = theLang.split("_")
|
def setColourHeaders(self, state: bool):
|
||||||
self._dLanguage = langBits[0]
|
"""Enable/disable coloured headings and comments."""
|
||||||
if len(langBits) > 1:
|
self._colourHead = state
|
||||||
self._dCountry = langBits[1]
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def setColourHeaders(self, doColour):
|
|
||||||
"""Enable/disable coloured headings and comments.
|
|
||||||
"""
|
|
||||||
self._colourHead = doColour
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def getErrors(self):
|
|
||||||
"""Return the list of errors."""
|
|
||||||
return self._errData
|
|
||||||
|
|
||||||
def initDocument(self):
|
def initDocument(self):
|
||||||
"""Initialises a new open document XML tree.
|
"""Initialises a new open document XML tree."""
|
||||||
"""
|
|
||||||
# Initialise Variables
|
# Initialise Variables
|
||||||
# ====================
|
# ====================
|
||||||
|
|
||||||
@@ -260,8 +261,8 @@ class ToOdt(Tokenizer):
|
|||||||
# ===============
|
# ===============
|
||||||
|
|
||||||
if self._headerText == "":
|
if self._headerText == "":
|
||||||
theTitle = self.theProject.data.title or self.theProject.data.name
|
theTitle = self._project.data.title or self._project.data.name
|
||||||
theAuth = self.theProject.data.author
|
theAuth = self._project.data.author
|
||||||
self._headerText = f"{theTitle} / {theAuth} /"
|
self._headerText = f"{theTitle} / {theAuth} /"
|
||||||
|
|
||||||
# Create Roots
|
# Create Roots
|
||||||
@@ -337,26 +338,26 @@ class ToOdt(Tokenizer):
|
|||||||
xMeta.text = f"novelWriter/{__version__}"
|
xMeta.text = f"novelWriter/{__version__}"
|
||||||
|
|
||||||
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "initial-creator"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "initial-creator"))
|
||||||
xMeta.text = self.theProject.data.author
|
xMeta.text = self._project.data.author
|
||||||
|
|
||||||
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "editing-cycles"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "editing-cycles"))
|
||||||
xMeta.text = str(self.theProject.data.saveCount)
|
xMeta.text = str(self._project.data.saveCount)
|
||||||
|
|
||||||
# Format is: PnYnMnDTnHnMnS
|
# Format is: PnYnMnDTnHnMnS
|
||||||
# https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#duration
|
# https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#duration
|
||||||
eT = self.theProject.data.editTime
|
eT = self._project.data.editTime
|
||||||
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "editing-duration"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("meta", "editing-duration"))
|
||||||
xMeta.text = f"P{eT//86400:d}DT{eT%86400//3600:d}H{eT%3600//60:d}M{eT%60:d}S"
|
xMeta.text = f"P{eT//86400:d}DT{eT%86400//3600:d}H{eT%3600//60:d}M{eT%60:d}S"
|
||||||
|
|
||||||
# Dublin Core Meta Data
|
# Dublin Core Meta Data
|
||||||
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "title"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "title"))
|
||||||
xMeta.text = self.theProject.data.title or self.theProject.data.name
|
xMeta.text = self._project.data.title or self._project.data.name
|
||||||
|
|
||||||
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "date"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "date"))
|
||||||
xMeta.text = timeStamp
|
xMeta.text = timeStamp
|
||||||
|
|
||||||
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "creator"))
|
xMeta = ET.SubElement(self._xMeta, _mkTag("dc", "creator"))
|
||||||
xMeta.text = self.theProject.data.author
|
xMeta.text = self._project.data.author
|
||||||
|
|
||||||
self._pageStyles()
|
self._pageStyles()
|
||||||
self._defaultStyles()
|
self._defaultStyles()
|
||||||
@@ -366,9 +367,8 @@ class ToOdt(Tokenizer):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def doConvert(self):
|
def doConvert(self):
|
||||||
"""Convert the list of text tokens into XML elements.
|
"""Convert the list of text tokens into XML elements."""
|
||||||
"""
|
self._result = "" # Not used, but cleared just in case
|
||||||
self._theResult = "" # Not used, but cleared just in case
|
|
||||||
|
|
||||||
odtTags = {
|
odtTags = {
|
||||||
self.FMT_B_B: "_B", # Bold open format
|
self.FMT_B_B: "_B", # Bold open format
|
||||||
@@ -382,7 +382,7 @@ class ToOdt(Tokenizer):
|
|||||||
thisPar = []
|
thisPar = []
|
||||||
thisFmt = []
|
thisFmt = []
|
||||||
parStyle = None
|
parStyle = None
|
||||||
for tType, _, tText, tFormat, tStyle in self._theTokens:
|
for tType, _, tText, tFormat, tStyle in self._tokens:
|
||||||
|
|
||||||
# Styles
|
# Styles
|
||||||
oStyle = ODTParagraphStyle()
|
oStyle = ODTParagraphStyle()
|
||||||
@@ -418,12 +418,12 @@ class ToOdt(Tokenizer):
|
|||||||
if self._doJustify:
|
if self._doJustify:
|
||||||
parStyle.setTextAlign("left")
|
parStyle.setTextAlign("left")
|
||||||
|
|
||||||
if len(thisPar) > 0:
|
if len(thisPar) > 0 and parStyle is not None:
|
||||||
tTemp = "\n".join(thisPar)
|
tTemp = "\n".join(thisPar)
|
||||||
fTemp = " ".join(thisFmt)
|
fTemp = " ".join(thisFmt)
|
||||||
tTxt = tTemp.rstrip()
|
tTxt = tTemp.rstrip()
|
||||||
tFmt = fTemp[:len(tTxt)]
|
tFmt = fTemp[:len(tTxt)]
|
||||||
self._addTextPar("Text_20_body", parStyle, tTxt, theFmt=tFmt)
|
self._addTextPar("Text_20_body", parStyle, tTxt, tFmt=tFmt)
|
||||||
|
|
||||||
thisPar = []
|
thisPar = []
|
||||||
thisFmt = []
|
thisFmt = []
|
||||||
@@ -474,41 +474,38 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
elif tType == self.T_SYNOPSIS and self._doSynopsis:
|
elif tType == self.T_SYNOPSIS and self._doSynopsis:
|
||||||
tTemp, fTemp = self._formatSynopsis(tText)
|
tTemp, fTemp = self._formatSynopsis(tText)
|
||||||
self._addTextPar("Text_20_Meta", oStyle, tTemp, theFmt=fTemp)
|
self._addTextPar("Text_20_Meta", oStyle, tTemp, tFmt=fTemp)
|
||||||
|
|
||||||
elif tType == self.T_COMMENT and self._doComments:
|
elif tType == self.T_COMMENT and self._doComments:
|
||||||
tTemp, fTemp = self._formatComments(tText)
|
tTemp, fTemp = self._formatComments(tText)
|
||||||
self._addTextPar("Text_20_Meta", oStyle, tTemp, theFmt=fTemp)
|
self._addTextPar("Text_20_Meta", oStyle, tTemp, tFmt=fTemp)
|
||||||
|
|
||||||
elif tType == self.T_KEYWORD and self._doKeywords:
|
elif tType == self.T_KEYWORD and self._doKeywords:
|
||||||
tTemp, fTemp = self._formatKeywords(tText)
|
tTemp, fTemp = self._formatKeywords(tText)
|
||||||
self._addTextPar("Text_20_Meta", oStyle, tTemp, theFmt=fTemp)
|
self._addTextPar("Text_20_Meta", oStyle, tTemp, tFmt=fTemp)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def closeDocument(self):
|
def closeDocument(self):
|
||||||
"""Return the serialised XML document
|
"""Return the serialised XML document"""
|
||||||
"""
|
|
||||||
# Build the auto-generated styles
|
# Build the auto-generated styles
|
||||||
for styleName, styleObj in self._autoPara.values():
|
for styleName, styleObj in self._autoPara.values():
|
||||||
styleObj.packXML(self._xAuto, styleName)
|
styleObj.packXML(self._xAuto, styleName)
|
||||||
for styleName, styleObj in self._autoText.values():
|
for styleName, styleObj in self._autoText.values():
|
||||||
styleObj.packXML(self._xAuto, styleName)
|
styleObj.packXML(self._xAuto, styleName)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveFlatXML(self, savePath):
|
def saveFlatXML(self, path: str | Path):
|
||||||
"""Save the data to an .fodt file.
|
"""Save the data to an .fodt file."""
|
||||||
"""
|
with open(path, mode="wb") as fObj:
|
||||||
with open(savePath, mode="wb") as outFile:
|
|
||||||
xml = ET.ElementTree(self._dFlat)
|
xml = ET.ElementTree(self._dFlat)
|
||||||
xmlIndent(xml)
|
xmlIndent(xml)
|
||||||
xml.write(outFile, encoding="utf-8", xml_declaration=True)
|
xml.write(fObj, encoding="utf-8", xml_declaration=True)
|
||||||
|
logger.info("Wrote file: %s", path)
|
||||||
return
|
return
|
||||||
|
|
||||||
def saveOpenDocText(self, savePath):
|
def saveOpenDocText(self, path: str | Path):
|
||||||
"""Save the data to an .odt file.
|
"""Save the data to an .odt file."""
|
||||||
"""
|
|
||||||
mMani = _mkTag("manifest", "manifest")
|
mMani = _mkTag("manifest", "manifest")
|
||||||
mVers = _mkTag("manifest", "version")
|
mVers = _mkTag("manifest", "version")
|
||||||
mPath = _mkTag("manifest", "full-path")
|
mPath = _mkTag("manifest", "full-path")
|
||||||
@@ -531,7 +528,7 @@ class ToOdt(Tokenizer):
|
|||||||
xml = ET.ElementTree(xObj)
|
xml = ET.ElementTree(xObj)
|
||||||
xml.write(fObj, encoding="utf-8", xml_declaration=True)
|
xml.write(fObj, encoding="utf-8", xml_declaration=True)
|
||||||
|
|
||||||
with ZipFile(savePath, mode="w") as outZip:
|
with ZipFile(path, mode="w") as outZip:
|
||||||
outZip.writestr("mimetype", X_MIME)
|
outZip.writestr("mimetype", X_MIME)
|
||||||
putInZip("META-INF/manifest.xml", xMani, outZip)
|
putInZip("META-INF/manifest.xml", xMani, outZip)
|
||||||
putInZip("settings.xml", xSett, outZip)
|
putInZip("settings.xml", xSett, outZip)
|
||||||
@@ -539,55 +536,56 @@ class ToOdt(Tokenizer):
|
|||||||
putInZip("meta.xml", self._dMeta, outZip)
|
putInZip("meta.xml", self._dMeta, outZip)
|
||||||
putInZip("styles.xml", self._dStyl, outZip)
|
putInZip("styles.xml", self._dStyl, outZip)
|
||||||
|
|
||||||
|
logger.info("Wrote file: %s", path)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _formatSynopsis(self, tText):
|
def _formatSynopsis(self, text: str) -> tuple[str, str]:
|
||||||
"""Apply formatting to synopsis lines.
|
"""Apply formatting to synopsis lines."""
|
||||||
"""
|
|
||||||
sSynop = self._localLookup("Synopsis")
|
sSynop = self._localLookup("Synopsis")
|
||||||
rTxt = "**{0}:** {1}".format(sSynop, tText)
|
rTxt = "**{0}:** {1}".format(sSynop, text)
|
||||||
rFmt = "_B{0} b_ {1}".format(" "*len(sSynop), " "*len(tText))
|
rFmt = "_B{0} b_ {1}".format(" "*len(sSynop), " "*len(text))
|
||||||
return rTxt, rFmt
|
return rTxt, rFmt
|
||||||
|
|
||||||
def _formatComments(self, tText):
|
def _formatComments(self, text: str) -> tuple[str, str]:
|
||||||
"""Apply formatting to comments.
|
"""Apply formatting to comments."""
|
||||||
"""
|
|
||||||
sComm = self._localLookup("Comment")
|
sComm = self._localLookup("Comment")
|
||||||
rTxt = "**{0}:** {1}".format(sComm, tText)
|
rTxt = "**{0}:** {1}".format(sComm, text)
|
||||||
rFmt = "_B{0} b_ {1}".format(" "*len(sComm), " "*len(tText))
|
rFmt = "_B{0} b_ {1}".format(" "*len(sComm), " "*len(text))
|
||||||
return rTxt, rFmt
|
return rTxt, rFmt
|
||||||
|
|
||||||
def _formatKeywords(self, tText):
|
def _formatKeywords(self, text: str) -> tuple[str, str]:
|
||||||
"""Apply formatting to keywords.
|
"""Apply formatting to keywords."""
|
||||||
"""
|
valid, bits, _ = self._project.index.scanThis("@"+text)
|
||||||
isValid, theBits, _ = self.theProject.index.scanThis("@"+tText)
|
if not valid or not bits:
|
||||||
if not isValid or not theBits:
|
return "", ""
|
||||||
return ""
|
|
||||||
|
|
||||||
rTxt = ""
|
rTxt = ""
|
||||||
rFmt = ""
|
rFmt = ""
|
||||||
if theBits[0] in nwLabels.KEY_NAME:
|
if bits[0] in nwLabels.KEY_NAME:
|
||||||
tText = nwLabels.KEY_NAME[theBits[0]]
|
text = nwLabels.KEY_NAME[bits[0]]
|
||||||
rTxt += "**{0}:** ".format(tText)
|
rTxt += "**{0}:** ".format(text)
|
||||||
rFmt += "_B{0} b_ ".format(" "*len(tText))
|
rFmt += "_B{0} b_ ".format(" "*len(text))
|
||||||
if len(theBits) > 1:
|
if len(bits) > 1:
|
||||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
if bits[0] == nwKeyWords.TAG_KEY:
|
||||||
rTxt += theBits[1]
|
rTxt += bits[1]
|
||||||
rFmt += " "*len(theBits[1])
|
rFmt += " "*len(bits[1])
|
||||||
else:
|
else:
|
||||||
tTags = ", ".join(theBits[1:])
|
tTags = ", ".join(bits[1:])
|
||||||
rTxt += tTags
|
rTxt += tTags
|
||||||
rFmt += (" "*len(tTags))
|
rFmt += (" "*len(tTags))
|
||||||
|
|
||||||
return rTxt, rFmt
|
return rTxt, rFmt
|
||||||
|
|
||||||
def _addTextPar(self, styleName, oStyle, theText, theFmt="", isHead=False, oLevel=None):
|
def _addTextPar(
|
||||||
"""Add a text paragraph to the text XML element.
|
self, styleName: str, oStyle: ODTParagraphStyle, tText: str, tFmt: str = "",
|
||||||
"""
|
isHead: bool = False, oLevel: str | None = None
|
||||||
|
):
|
||||||
|
"""Add a text paragraph to the text XML element."""
|
||||||
tAttr = {}
|
tAttr = {}
|
||||||
tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
|
tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
|
||||||
if oLevel is not None:
|
if oLevel is not None:
|
||||||
@@ -600,16 +598,16 @@ class ToOdt(Tokenizer):
|
|||||||
# xmlIndent will add a line break if the first subelement is a span.
|
# xmlIndent will add a line break if the first subelement is a span.
|
||||||
xElem.text = ""
|
xElem.text = ""
|
||||||
|
|
||||||
if not theText:
|
if not tText:
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Process Formatting
|
# Process Formatting
|
||||||
##
|
##
|
||||||
|
|
||||||
if len(theText) != len(theFmt):
|
if len(tText) != len(tFmt):
|
||||||
# Generate an empty format if there isn't any or it doesn't match
|
# Generate an empty format if there isn't any or it doesn't match
|
||||||
theFmt = " "*len(theText)
|
tFmt = " "*len(tText)
|
||||||
|
|
||||||
# The formatting loop
|
# The formatting loop
|
||||||
tTemp = ""
|
tTemp = ""
|
||||||
@@ -619,23 +617,23 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
parProc = XMLParagraph(xElem)
|
parProc = XMLParagraph(xElem)
|
||||||
|
|
||||||
for i, c in enumerate(theText):
|
for i, c in enumerate(tText):
|
||||||
|
|
||||||
if theFmt[i] == " ":
|
if tFmt[i] == " ":
|
||||||
tTemp += c
|
tTemp += c
|
||||||
elif theFmt[i] == "_":
|
elif tFmt[i] == "_":
|
||||||
continue
|
continue
|
||||||
elif theFmt[i] == "B":
|
elif tFmt[i] == "B":
|
||||||
xFmt |= X_BLD
|
xFmt |= X_BLD
|
||||||
elif theFmt[i] == "b":
|
elif tFmt[i] == "b":
|
||||||
xFmt &= M_BLD
|
xFmt &= M_BLD
|
||||||
elif theFmt[i] == "I":
|
elif tFmt[i] == "I":
|
||||||
xFmt |= X_ITA
|
xFmt |= X_ITA
|
||||||
elif theFmt[i] == "i":
|
elif tFmt[i] == "i":
|
||||||
xFmt &= M_ITA
|
xFmt &= M_ITA
|
||||||
elif theFmt[i] == "S":
|
elif tFmt[i] == "S":
|
||||||
xFmt |= X_DEL
|
xFmt |= X_DEL
|
||||||
elif theFmt[i] == "s":
|
elif tFmt[i] == "s":
|
||||||
xFmt &= M_DEL
|
xFmt &= M_DEL
|
||||||
else:
|
else:
|
||||||
pErr += 1
|
pErr += 1
|
||||||
@@ -666,9 +664,8 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _paraStyle(self, parName, oStyle):
|
def _paraStyle(self, parName: str, oStyle: ODTParagraphStyle) -> str:
|
||||||
"""Return a name for a style object.
|
"""Return a name for a style object."""
|
||||||
"""
|
|
||||||
refStyle = self._mainPara.get(parName, None)
|
refStyle = self._mainPara.get(parName, None)
|
||||||
if refStyle is None:
|
if refStyle is None:
|
||||||
logger.error("Unknown paragraph style '%s'", parName)
|
logger.error("Unknown paragraph style '%s'", parName)
|
||||||
@@ -687,29 +684,27 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
return newName
|
return newName
|
||||||
|
|
||||||
def _textStyle(self, tFmt):
|
def _textStyle(self, hFmt: int) -> str:
|
||||||
"""Return a text style for a given style code.
|
"""Return a text style for a given style code."""
|
||||||
"""
|
if hFmt in self._autoText:
|
||||||
if tFmt in self._autoText:
|
return self._autoText[hFmt][0]
|
||||||
return self._autoText[tFmt][0]
|
|
||||||
|
|
||||||
newName = "T%d" % (len(self._autoText) + 1)
|
newName = "T%d" % (len(self._autoText) + 1)
|
||||||
newStyle = ODTTextStyle()
|
newStyle = ODTTextStyle()
|
||||||
if tFmt & X_BLD:
|
if hFmt & X_BLD:
|
||||||
newStyle.setFontWeight("bold")
|
newStyle.setFontWeight("bold")
|
||||||
if tFmt & X_ITA:
|
if hFmt & X_ITA:
|
||||||
newStyle.setFontStyle("italic")
|
newStyle.setFontStyle("italic")
|
||||||
if tFmt & X_DEL:
|
if hFmt & X_DEL:
|
||||||
newStyle.setStrikeStyle("solid")
|
newStyle.setStrikeStyle("solid")
|
||||||
newStyle.setStrikeType("single")
|
newStyle.setStrikeType("single")
|
||||||
|
|
||||||
self._autoText[tFmt] = (newName, newStyle)
|
self._autoText[hFmt] = (newName, newStyle)
|
||||||
|
|
||||||
return newName
|
return newName
|
||||||
|
|
||||||
def _emToCm(self, emVal):
|
def _emToCm(self, emVal: float) -> str:
|
||||||
"""Converts an em value to centimetres.
|
"""Converts an em value to centimetres."""
|
||||||
"""
|
|
||||||
return f"{emVal*2.54/72*self._textSize:.3f}cm"
|
return f"{emVal*2.54/72*self._textSize:.3f}cm"
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -717,110 +712,107 @@ class ToOdt(Tokenizer):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def _pageStyles(self):
|
def _pageStyles(self):
|
||||||
"""Set the default page style.
|
"""Set the default page style."""
|
||||||
"""
|
tAttr = {}
|
||||||
theAttr = {}
|
tAttr[_mkTag("style", "name")] = "PM1"
|
||||||
theAttr[_mkTag("style", "name")] = "PM1"
|
|
||||||
if self._isFlat:
|
if self._isFlat:
|
||||||
xPage = ET.SubElement(self._xAuto, _mkTag("style", "page-layout"), attrib=theAttr)
|
xPage = ET.SubElement(self._xAuto, _mkTag("style", "page-layout"), attrib=tAttr)
|
||||||
else:
|
else:
|
||||||
xPage = ET.SubElement(self._xAut2, _mkTag("style", "page-layout"), attrib=theAttr)
|
xPage = ET.SubElement(self._xAut2, _mkTag("style", "page-layout"), attrib=tAttr)
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("fo", "margin-top")] = self._mDocTop
|
tAttr[_mkTag("fo", "margin-top")] = self._mDocTop
|
||||||
theAttr[_mkTag("fo", "margin-bottom")] = self._mDocBtm
|
tAttr[_mkTag("fo", "margin-bottom")] = self._mDocBtm
|
||||||
theAttr[_mkTag("fo", "margin-left")] = self._mDocLeft
|
tAttr[_mkTag("fo", "margin-left")] = self._mDocLeft
|
||||||
theAttr[_mkTag("fo", "margin-right")] = self._mDocRight
|
tAttr[_mkTag("fo", "margin-right")] = self._mDocRight
|
||||||
ET.SubElement(xPage, _mkTag("style", "page-layout-properties"), attrib=theAttr)
|
ET.SubElement(xPage, _mkTag("style", "page-layout-properties"), attrib=tAttr)
|
||||||
|
|
||||||
xHead = ET.SubElement(xPage, _mkTag("style", "header-style"))
|
xHead = ET.SubElement(xPage, _mkTag("style", "header-style"))
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("fo", "min-height")] = "0.600cm"
|
tAttr[_mkTag("fo", "min-height")] = "0.600cm"
|
||||||
theAttr[_mkTag("fo", "margin-left")] = "0.000cm"
|
tAttr[_mkTag("fo", "margin-left")] = "0.000cm"
|
||||||
theAttr[_mkTag("fo", "margin-right")] = "0.000cm"
|
tAttr[_mkTag("fo", "margin-right")] = "0.000cm"
|
||||||
theAttr[_mkTag("fo", "margin-bottom")] = "0.500cm"
|
tAttr[_mkTag("fo", "margin-bottom")] = "0.500cm"
|
||||||
ET.SubElement(xHead, _mkTag("style", "header-footer-properties"), attrib=theAttr)
|
ET.SubElement(xHead, _mkTag("style", "header-footer-properties"), attrib=tAttr)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _defaultStyles(self):
|
def _defaultStyles(self):
|
||||||
"""Set the default styles.
|
"""Set the default styles."""
|
||||||
"""
|
|
||||||
# Add Paragraph Family Style
|
# Add Paragraph Family Style
|
||||||
# ==========================
|
# ==========================
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
tAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "default-style"), attrib=theAttr)
|
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "default-style"), attrib=tAttr)
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("style", "line-break")] = "strict"
|
tAttr[_mkTag("style", "line-break")] = "strict"
|
||||||
theAttr[_mkTag("style", "tab-stop-distance")] = "1.251cm"
|
tAttr[_mkTag("style", "tab-stop-distance")] = "1.251cm"
|
||||||
theAttr[_mkTag("style", "writing-mode")] = "page"
|
tAttr[_mkTag("style", "writing-mode")] = "page"
|
||||||
ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=tAttr)
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("style", "font-name")] = self._textFont
|
tAttr[_mkTag("style", "font-name")] = self._textFont
|
||||||
theAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
tAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
||||||
theAttr[_mkTag("fo", "font-size")] = self._fSizeText
|
tAttr[_mkTag("fo", "font-size")] = self._fSizeText
|
||||||
theAttr[_mkTag("fo", "language")] = self._dLanguage
|
tAttr[_mkTag("fo", "language")] = self._dLanguage
|
||||||
theAttr[_mkTag("fo", "country")] = self._dCountry
|
tAttr[_mkTag("fo", "country")] = self._dCountry
|
||||||
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=tAttr)
|
||||||
|
|
||||||
# Add Standard Paragraph Style
|
# Add Standard Paragraph Style
|
||||||
# ============================
|
# ============================
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = "Standard"
|
tAttr[_mkTag("style", "name")] = "Standard"
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
tAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
tAttr[_mkTag("style", "class")] = "text"
|
||||||
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=tAttr)
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("style", "font-name")] = self._textFont
|
tAttr[_mkTag("style", "font-name")] = self._textFont
|
||||||
theAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
tAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
||||||
theAttr[_mkTag("fo", "font-size")] = self._fSizeText
|
tAttr[_mkTag("fo", "font-size")] = self._fSizeText
|
||||||
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=tAttr)
|
||||||
|
|
||||||
# Add Default Heading Style
|
# Add Default Heading Style
|
||||||
# =========================
|
# =========================
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = "Heading"
|
tAttr[_mkTag("style", "name")] = "Heading"
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
tAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
theAttr[_mkTag("style", "parent-style-name")] = "Standard"
|
tAttr[_mkTag("style", "parent-style-name")] = "Standard"
|
||||||
theAttr[_mkTag("style", "next-style-name")] = "Text_20_body"
|
tAttr[_mkTag("style", "next-style-name")] = "Text_20_body"
|
||||||
theAttr[_mkTag("style", "class")] = "text"
|
tAttr[_mkTag("style", "class")] = "text"
|
||||||
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=tAttr)
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("fo", "margin-top")] = self._mTopHead
|
tAttr[_mkTag("fo", "margin-top")] = self._mTopHead
|
||||||
theAttr[_mkTag("fo", "margin-bottom")] = self._mBotHead
|
tAttr[_mkTag("fo", "margin-bottom")] = self._mBotHead
|
||||||
theAttr[_mkTag("fo", "keep-with-next")] = "always"
|
tAttr[_mkTag("fo", "keep-with-next")] = "always"
|
||||||
ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=tAttr)
|
||||||
|
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("style", "font-name")] = self._textFont
|
tAttr[_mkTag("style", "font-name")] = self._textFont
|
||||||
theAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
tAttr[_mkTag("fo", "font-family")] = self._fontFamily
|
||||||
theAttr[_mkTag("fo", "font-size")] = self._fSizeHead
|
tAttr[_mkTag("fo", "font-size")] = self._fSizeHead
|
||||||
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
|
ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=tAttr)
|
||||||
|
|
||||||
# Add Header and Footer Styles
|
# Add Header and Footer Styles
|
||||||
# ============================
|
# ============================
|
||||||
theAttr = {}
|
tAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = "Header_20_and_20_Footer"
|
tAttr[_mkTag("style", "name")] = "Header_20_and_20_Footer"
|
||||||
theAttr[_mkTag("style", "display-name")] = "Header and Footer"
|
tAttr[_mkTag("style", "display-name")] = "Header and Footer"
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
tAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
theAttr[_mkTag("style", "parent-style-name")] = "Standard"
|
tAttr[_mkTag("style", "parent-style-name")] = "Standard"
|
||||||
theAttr[_mkTag("style", "class")] = "extra"
|
tAttr[_mkTag("style", "class")] = "extra"
|
||||||
ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
|
ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=tAttr)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _useableStyles(self):
|
def _useableStyles(self):
|
||||||
"""Set the usable styles.
|
"""Set the usable styles."""
|
||||||
"""
|
|
||||||
# Add Text Body Style
|
# Add Text Body Style
|
||||||
# ===================
|
# ===================
|
||||||
|
|
||||||
@@ -974,12 +966,11 @@ class ToOdt(Tokenizer):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _writeHeader(self):
|
def _writeHeader(self):
|
||||||
"""Write the header elements.
|
"""Write the header elements."""
|
||||||
"""
|
tAttr = {}
|
||||||
theAttr = {}
|
tAttr[_mkTag("style", "name")] = "Standard"
|
||||||
theAttr[_mkTag("style", "name")] = "Standard"
|
tAttr[_mkTag("style", "page-layout-name")] = "PM1"
|
||||||
theAttr[_mkTag("style", "page-layout-name")] = "PM1"
|
xPage = ET.SubElement(self._xMast, _mkTag("style", "master-page"), attrib=tAttr)
|
||||||
xPage = ET.SubElement(self._xMast, _mkTag("style", "master-page"), attrib=theAttr)
|
|
||||||
|
|
||||||
# Standard Page Header
|
# Standard Page Header
|
||||||
xHead = ET.SubElement(xPage, _mkTag("style", "header"))
|
xHead = ET.SubElement(xPage, _mkTag("style", "header"))
|
||||||
@@ -1059,28 +1050,28 @@ class ODTParagraphStyle:
|
|||||||
# Attribute Setters
|
# Attribute Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setDisplayName(self, theValue):
|
def setDisplayName(self, value: str | None):
|
||||||
self._mAttr["display-name"][1] = str(theValue)
|
self._mAttr["display-name"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setParentStyleName(self, theValue):
|
def setParentStyleName(self, value: str | None):
|
||||||
self._mAttr["parent-style-name"][1] = str(theValue)
|
self._mAttr["parent-style-name"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setNextStyleName(self, theValue):
|
def setNextStyleName(self, value: str | None):
|
||||||
self._mAttr["next-style-name"][1] = str(theValue)
|
self._mAttr["next-style-name"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setOutlineLevel(self, theValue):
|
def setOutlineLevel(self, value: str | None):
|
||||||
if theValue in self.VALID_LEVEL:
|
if value in self.VALID_LEVEL:
|
||||||
self._mAttr["default-outline-level"][1] = str(theValue)
|
self._mAttr["default-outline-level"][1] = value
|
||||||
else:
|
else:
|
||||||
self._mAttr["default-outline-level"][1] = None
|
self._mAttr["default-outline-level"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setClass(self, theValue):
|
def setClass(self, value: str | None):
|
||||||
if theValue in self.VALID_CLASS:
|
if value in self.VALID_CLASS:
|
||||||
self._mAttr["class"][1] = str(theValue)
|
self._mAttr["class"][1] = value
|
||||||
else:
|
else:
|
||||||
self._mAttr["class"][1] = None
|
self._mAttr["class"][1] = None
|
||||||
return
|
return
|
||||||
@@ -1089,43 +1080,43 @@ class ODTParagraphStyle:
|
|||||||
# Paragraph Setters
|
# Paragraph Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setMarginTop(self, theValue):
|
def setMarginTop(self, value: str | None):
|
||||||
self._pAttr["margin-top"][1] = str(theValue)
|
self._pAttr["margin-top"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setMarginBottom(self, theValue):
|
def setMarginBottom(self, value: str | None):
|
||||||
self._pAttr["margin-bottom"][1] = str(theValue)
|
self._pAttr["margin-bottom"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setMarginLeft(self, theValue):
|
def setMarginLeft(self, value: str | None):
|
||||||
self._pAttr["margin-left"][1] = str(theValue)
|
self._pAttr["margin-left"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setMarginRight(self, theValue):
|
def setMarginRight(self, value: str | None):
|
||||||
self._pAttr["margin-right"][1] = str(theValue)
|
self._pAttr["margin-right"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setLineHeight(self, theValue):
|
def setLineHeight(self, value: str | None):
|
||||||
self._pAttr["line-height"][1] = str(theValue)
|
self._pAttr["line-height"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setTextAlign(self, theValue):
|
def setTextAlign(self, value: str | None):
|
||||||
if theValue in self.VALID_ALIGN:
|
if value in self.VALID_ALIGN:
|
||||||
self._pAttr["text-align"][1] = str(theValue)
|
self._pAttr["text-align"][1] = value
|
||||||
else:
|
else:
|
||||||
self._pAttr["text-align"][1] = None
|
self._pAttr["text-align"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBreakBefore(self, theValue):
|
def setBreakBefore(self, value: str | None):
|
||||||
if theValue in self.VALID_BREAK:
|
if value in self.VALID_BREAK:
|
||||||
self._pAttr["break-before"][1] = str(theValue)
|
self._pAttr["break-before"][1] = value
|
||||||
else:
|
else:
|
||||||
self._pAttr["break-before"][1] = None
|
self._pAttr["break-before"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setBreakAfter(self, theValue):
|
def setBreakAfter(self, value: str | None):
|
||||||
if theValue in self.VALID_BREAK:
|
if value in self.VALID_BREAK:
|
||||||
self._pAttr["break-after"][1] = str(theValue)
|
self._pAttr["break-after"][1] = value
|
||||||
else:
|
else:
|
||||||
self._pAttr["break-after"][1] = None
|
self._pAttr["break-after"][1] = None
|
||||||
return
|
return
|
||||||
@@ -1134,38 +1125,38 @@ class ODTParagraphStyle:
|
|||||||
# Text Setters
|
# Text Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setFontName(self, theValue):
|
def setFontName(self, value: str | None):
|
||||||
self._tAttr["font-name"][1] = str(theValue)
|
self._tAttr["font-name"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFontFamily(self, theValue):
|
def setFontFamily(self, value: str | None):
|
||||||
self._tAttr["font-family"][1] = str(theValue)
|
self._tAttr["font-family"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFontSize(self, theValue):
|
def setFontSize(self, value: str | None):
|
||||||
self._tAttr["font-size"][1] = str(theValue)
|
self._tAttr["font-size"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFontWeight(self, theValue):
|
def setFontWeight(self, value: str | None):
|
||||||
if theValue in self.VALID_WEIGHT:
|
if value in self.VALID_WEIGHT:
|
||||||
self._tAttr["font-weight"][1] = str(theValue)
|
self._tAttr["font-weight"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["font-weight"][1] = None
|
self._tAttr["font-weight"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setColor(self, theValue):
|
def setColor(self, value: str | None):
|
||||||
self._tAttr["color"][1] = str(theValue)
|
self._tAttr["color"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
def setOpacity(self, theValue):
|
def setOpacity(self, value: str | None):
|
||||||
self._tAttr["opacity"][1] = str(theValue)
|
self._tAttr["opacity"][1] = value
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def checkNew(self, refStyle):
|
def checkNew(self, refStyle: ODTParagraphStyle):
|
||||||
"""Check if there are new settings in refStyle that differ from
|
"""Check if there are new settings in refStyle that differ from
|
||||||
those in the current object.
|
those in the current object.
|
||||||
"""
|
"""
|
||||||
@@ -1180,9 +1171,8 @@ class ODTParagraphStyle:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getID(self):
|
def getID(self) -> str:
|
||||||
"""Generate a unique ID from the settings.
|
"""Generate a unique ID from the settings."""
|
||||||
"""
|
|
||||||
theString = (
|
theString = (
|
||||||
f"Paragraph:Main:{str(self._mAttr)}:"
|
f"Paragraph:Main:{str(self._mAttr)}:"
|
||||||
f"Paragraph:Para:{str(self._pAttr)}:"
|
f"Paragraph:Para:{str(self._pAttr)}:"
|
||||||
@@ -1190,11 +1180,10 @@ class ODTParagraphStyle:
|
|||||||
)
|
)
|
||||||
return sha256(theString.encode()).hexdigest()
|
return sha256(theString.encode()).hexdigest()
|
||||||
|
|
||||||
def packXML(self, xParent, xName):
|
def packXML(self, xParent: ET.Element, name: str):
|
||||||
"""Pack the content into an xml element.
|
"""Pack the content into an xml element."""
|
||||||
"""
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = xName
|
theAttr[_mkTag("style", "name")] = name
|
||||||
theAttr[_mkTag("style", "family")] = "paragraph"
|
theAttr[_mkTag("style", "family")] = "paragraph"
|
||||||
for aName, (aNm, aVal) in self._mAttr.items():
|
for aName, (aNm, aVal) in self._mAttr.items():
|
||||||
if aVal is not None:
|
if aVal is not None:
|
||||||
@@ -1249,30 +1238,30 @@ class ODTTextStyle:
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setFontWeight(self, theValue):
|
def setFontWeight(self, value: str | None):
|
||||||
if theValue in self.VALID_WEIGHT:
|
if value in self.VALID_WEIGHT:
|
||||||
self._tAttr["font-weight"][1] = str(theValue)
|
self._tAttr["font-weight"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["font-weight"][1] = None
|
self._tAttr["font-weight"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setFontStyle(self, theValue):
|
def setFontStyle(self, value: str | None):
|
||||||
if theValue in self.VALID_STYLE:
|
if value in self.VALID_STYLE:
|
||||||
self._tAttr["font-style"][1] = str(theValue)
|
self._tAttr["font-style"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["font-style"][1] = None
|
self._tAttr["font-style"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setStrikeStyle(self, theValue):
|
def setStrikeStyle(self, value: str | None):
|
||||||
if theValue in self.VALID_LSTYLE:
|
if value in self.VALID_LSTYLE:
|
||||||
self._tAttr["text-line-through-style"][1] = str(theValue)
|
self._tAttr["text-line-through-style"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["text-line-through-style"][1] = None
|
self._tAttr["text-line-through-style"][1] = None
|
||||||
return
|
return
|
||||||
|
|
||||||
def setStrikeType(self, theValue):
|
def setStrikeType(self, value: str | None):
|
||||||
if theValue in self.VALID_LTYPE:
|
if value in self.VALID_LTYPE:
|
||||||
self._tAttr["text-line-through-type"][1] = str(theValue)
|
self._tAttr["text-line-through-type"][1] = value
|
||||||
else:
|
else:
|
||||||
self._tAttr["text-line-through-type"][1] = None
|
self._tAttr["text-line-through-type"][1] = None
|
||||||
return
|
return
|
||||||
@@ -1281,11 +1270,10 @@ class ODTTextStyle:
|
|||||||
# Methods
|
# Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def packXML(self, xParent, xName):
|
def packXML(self, xParent: ET.Element, name: str):
|
||||||
"""Pack the content into an xml element.
|
"""Pack the content into an xml element."""
|
||||||
"""
|
|
||||||
theAttr = {}
|
theAttr = {}
|
||||||
theAttr[_mkTag("style", "name")] = xName
|
theAttr[_mkTag("style", "name")] = name
|
||||||
theAttr[_mkTag("style", "family")] = "text"
|
theAttr[_mkTag("style", "family")] = "text"
|
||||||
xEntry = ET.SubElement(xParent, _mkTag("style", "style"), attrib=theAttr)
|
xEntry = ET.SubElement(xParent, _mkTag("style", "style"), attrib=theAttr)
|
||||||
|
|
||||||
@@ -1332,7 +1320,7 @@ class XMLParagraph:
|
|||||||
object and attribute is written to,
|
object and attribute is written to,
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, xRoot):
|
def __init__(self, xRoot: ET.Element):
|
||||||
|
|
||||||
self._xRoot = xRoot
|
self._xRoot = xRoot
|
||||||
self._xTail = ET.Element("")
|
self._xTail = ET.Element("")
|
||||||
@@ -1345,17 +1333,17 @@ class XMLParagraph:
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def appendText(self, text):
|
def appendText(self, tText: str):
|
||||||
"""Append text to the XML element. We do this one character at
|
"""Append text to the XML element. We do this one character at
|
||||||
the time in order to be able to process line breaks, tabs and
|
the time in order to be able to process line breaks, tabs and
|
||||||
spaces separately. Multiple spaces above one are concatenated
|
spaces separately. Multiple spaces are concatenated into a
|
||||||
into a single tag, and must therefore be processed separately.
|
single tag, and must therefore be processed separately.
|
||||||
"""
|
"""
|
||||||
text = stripEscape(text)
|
tText = stripEscape(tText)
|
||||||
nSpaces = 0
|
nSpaces = 0
|
||||||
self._rawTxt += text
|
self._rawTxt += tText
|
||||||
|
|
||||||
for c in text:
|
for c in tText:
|
||||||
if c == " ":
|
if c == " ":
|
||||||
nSpaces += 1
|
nSpaces += 1
|
||||||
continue
|
continue
|
||||||
@@ -1392,43 +1380,42 @@ class XMLParagraph:
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
if self._nState == X_ROOT_TEXT:
|
if self._nState == X_ROOT_TEXT:
|
||||||
self._xRoot.text += c
|
self._xRoot.text = (self._xRoot.text or "") + c
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
elif self._nState == X_ROOT_TAIL:
|
elif self._nState == X_ROOT_TAIL:
|
||||||
self._xTail.tail += c
|
self._xTail.tail = (self._xTail.tail or "") + c
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
elif self._nState == X_SPAN_TEXT:
|
elif self._nState == X_SPAN_TEXT:
|
||||||
self._xTail.text += c
|
self._xTail.text = (self._xTail.text or "") + c
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
elif self._nState == X_SPAN_SING:
|
elif self._nState == X_SPAN_SING:
|
||||||
self._xSing.tail += c
|
self._xSing.tail = (self._xSing.tail or "") + c
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
|
|
||||||
if nSpaces > 0:
|
if nSpaces > 0:
|
||||||
|
# Handle trailing spaces
|
||||||
self._processSpaces(nSpaces)
|
self._processSpaces(nSpaces)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def appendSpan(self, tText, tFmt):
|
def appendSpan(self, tText: str, tFmt: str):
|
||||||
"""Append a text span to the XML element. The span is always
|
"""Append a text span to the XML element. The span is always
|
||||||
closed since we do not allow nested spans (like Libre Office).
|
closed since we do not allow nested spans (like Libre Office).
|
||||||
Therefore we return to the root element level when we're done
|
Therefore we return to the root element level when we're done
|
||||||
processing the text of the span.
|
processing the text of the span.
|
||||||
"""
|
"""
|
||||||
self._xTail = ET.SubElement(self._xRoot, TAG_SPAN, attrib={
|
self._xTail = ET.SubElement(self._xRoot, TAG_SPAN, attrib={TAG_STNM: tFmt})
|
||||||
TAG_STNM: tFmt
|
|
||||||
})
|
|
||||||
self._xTail.text = "" # Defaults to None
|
self._xTail.text = "" # Defaults to None
|
||||||
self._xTail.tail = "" # Defaults to None
|
self._xTail.tail = "" # Defaults to None
|
||||||
self._nState = X_SPAN_TEXT
|
self._nState = X_SPAN_TEXT
|
||||||
self.appendText(tText)
|
self.appendText(tText)
|
||||||
self._nState = X_ROOT_TAIL
|
self._nState = X_ROOT_TAIL
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def checkError(self):
|
def checkError(self):
|
||||||
"""Check that the number of characters written matches the
|
"""Check that the number of characters written matches the
|
||||||
number of characters received."""
|
number of characters received.
|
||||||
|
"""
|
||||||
errMsg = ""
|
errMsg = ""
|
||||||
nMissed = len(self._rawTxt) - self._chrPos
|
nMissed = len(self._rawTxt) - self._chrPos
|
||||||
if nMissed != 0:
|
if nMissed != 0:
|
||||||
@@ -1439,7 +1426,7 @@ class XMLParagraph:
|
|||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _processSpaces(self, nSpaces):
|
def _processSpaces(self, nSpaces: int):
|
||||||
"""Add spaces to paragraph. The first space is always written
|
"""Add spaces to paragraph. The first space is always written
|
||||||
as-is (unless it's the first character of the paragraph). The
|
as-is (unless it's the first character of the paragraph). The
|
||||||
second space uses the dedicated tag for spaces, and from the
|
second space uses the dedicated tag for spaces, and from the
|
||||||
@@ -1451,16 +1438,16 @@ class XMLParagraph:
|
|||||||
if nSpaces > 0:
|
if nSpaces > 0:
|
||||||
if self._chrPos > 0:
|
if self._chrPos > 0:
|
||||||
if self._nState == X_ROOT_TEXT:
|
if self._nState == X_ROOT_TEXT:
|
||||||
self._xRoot.text += " "
|
self._xRoot.text = (self._xRoot.text or "") + " "
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
elif self._nState == X_ROOT_TAIL:
|
elif self._nState == X_ROOT_TAIL:
|
||||||
self._xTail.tail += " "
|
self._xTail.tail = (self._xTail.tail or "") + " "
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
elif self._nState == X_SPAN_TEXT:
|
elif self._nState == X_SPAN_TEXT:
|
||||||
self._xTail.text += " "
|
self._xTail.text = (self._xTail.text or "") + " "
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
elif self._nState == X_SPAN_SING:
|
elif self._nState == X_SPAN_SING:
|
||||||
self._xSing.tail += " "
|
self._xSing.tail = (self._xSing.tail or "") + " "
|
||||||
self._chrPos += 1
|
self._chrPos += 1
|
||||||
else:
|
else:
|
||||||
nSpaces += 1
|
nSpaces += 1
|
||||||
|
|||||||
@@ -1,475 +0,0 @@
|
|||||||
"""
|
|
||||||
novelWriter – Custom Widgets and Layouts
|
|
||||||
========================================
|
|
||||||
Various custom widget and layout classes
|
|
||||||
|
|
||||||
File History:
|
|
||||||
Created: 2020-05-03 [0.4.5] QConfigLayout
|
|
||||||
Created: 2020-05-03 [0.4.5] QSwitch
|
|
||||||
Created: 2020-05-17 [0.5.1] PagedDialog
|
|
||||||
|
|
||||||
This file is a part of novelWriter
|
|
||||||
Copyright 2018–2023, Veronica Berglyd Olsen
|
|
||||||
|
|
||||||
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 3 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, see <https://www.gnu.org/licenses/>.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from PyQt5.QtGui import QColor, QPalette, QPainter
|
|
||||||
from PyQt5.QtCore import (
|
|
||||||
Qt, QRect, QPoint, QRectF, QPropertyAnimation, pyqtProperty
|
|
||||||
)
|
|
||||||
from PyQt5.QtWidgets import (
|
|
||||||
QGridLayout, QLabel, QWidget, QVBoxLayout, QHBoxLayout, QSizePolicy,
|
|
||||||
QAbstractButton, QDialog, QTabWidget, QTabBar, QStyle, QStylePainter,
|
|
||||||
QStyleOptionTab, QLineEdit
|
|
||||||
)
|
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
|
||||||
from novelwriter.constants import nwUnicode
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
|
||||||
# Config Form Layout
|
|
||||||
# =============================================================================================== #
|
|
||||||
|
|
||||||
class QConfigLayout(QGridLayout):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self._nextRow = 0
|
|
||||||
self._helpCol = QColor(0, 0, 0)
|
|
||||||
self._fontScale = 0.9
|
|
||||||
|
|
||||||
self._itemMap = {}
|
|
||||||
|
|
||||||
wSp = CONFIG.pxInt(8)
|
|
||||||
self.setHorizontalSpacing(wSp)
|
|
||||||
self.setVerticalSpacing(wSp)
|
|
||||||
self.setColumnStretch(0, 1)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
|
||||||
# Getters and Setters
|
|
||||||
##
|
|
||||||
|
|
||||||
def setHelpTextStyle(self, helpCol, fontScale=0.9):
|
|
||||||
"""Set the text color for the help text.
|
|
||||||
"""
|
|
||||||
if isinstance(helpCol, QColor):
|
|
||||||
self._helpCol = helpCol
|
|
||||||
else:
|
|
||||||
self._helpCol = QColor(*helpCol)
|
|
||||||
self._fontScale = fontScale
|
|
||||||
return
|
|
||||||
|
|
||||||
def setHelpText(self, intRow, theText):
|
|
||||||
"""Set the text for the help label.
|
|
||||||
"""
|
|
||||||
if intRow in self._itemMap:
|
|
||||||
self._itemMap[intRow]["help"].setText(theText)
|
|
||||||
return
|
|
||||||
|
|
||||||
def setLabelText(self, intRow, theText):
|
|
||||||
"""Set the text for the main label.
|
|
||||||
"""
|
|
||||||
if intRow in self._itemMap:
|
|
||||||
self._itemMap[intRow]["label"].setText(theText)
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
|
||||||
# Class Methods
|
|
||||||
##
|
|
||||||
|
|
||||||
def addGroupLabel(self, theLabel):
|
|
||||||
"""Adds a text label to separate groups of settings.
|
|
||||||
"""
|
|
||||||
if isinstance(theLabel, QLabel):
|
|
||||||
qLabel = theLabel
|
|
||||||
elif isinstance(theLabel, str):
|
|
||||||
qLabel = QLabel("<b>%s</b>" % theLabel)
|
|
||||||
else:
|
|
||||||
qLabel = None
|
|
||||||
raise ValueError("theLabel must be a QLabel")
|
|
||||||
|
|
||||||
hM = CONFIG.pxInt(4)
|
|
||||||
qLabel.setContentsMargins(0, hM, 0, hM)
|
|
||||||
self.addWidget(qLabel, self._nextRow, 0, 1, 2, Qt.AlignLeft)
|
|
||||||
|
|
||||||
self.setRowStretch(self._nextRow, 0)
|
|
||||||
self.setRowStretch(self._nextRow + 1, 1)
|
|
||||||
|
|
||||||
self._nextRow += 1
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def addRow(self, theLabel, theWidget, helpText=None, theUnit=None, theButton=None):
|
|
||||||
"""Add a label and a widget as a new row of the grid.
|
|
||||||
"""
|
|
||||||
thisEntry = {
|
|
||||||
"label": None,
|
|
||||||
"help": None,
|
|
||||||
"widget": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
if isinstance(theLabel, QLabel):
|
|
||||||
qLabel = theLabel
|
|
||||||
elif isinstance(theLabel, str):
|
|
||||||
qLabel = QLabel(theLabel)
|
|
||||||
else:
|
|
||||||
qLabel = None
|
|
||||||
raise ValueError("theLabel must be a QLabel")
|
|
||||||
|
|
||||||
if isinstance(theWidget, QWidget):
|
|
||||||
qWidget = theWidget
|
|
||||||
else:
|
|
||||||
qWidget = None
|
|
||||||
raise ValueError("theWidget must be a QWidget")
|
|
||||||
|
|
||||||
wSp = CONFIG.pxInt(8)
|
|
||||||
qLabel.setIndent(wSp)
|
|
||||||
if helpText is not None:
|
|
||||||
qHelp = QHelpLabel(str(helpText), self._helpCol, self._fontScale)
|
|
||||||
qHelp.setIndent(wSp)
|
|
||||||
|
|
||||||
labelBox = QVBoxLayout()
|
|
||||||
labelBox.addWidget(qLabel)
|
|
||||||
labelBox.addWidget(qHelp)
|
|
||||||
labelBox.setSpacing(0)
|
|
||||||
labelBox.addStretch(1)
|
|
||||||
|
|
||||||
thisEntry["help"] = qHelp
|
|
||||||
self.addLayout(labelBox, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.addWidget(qLabel, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
|
||||||
|
|
||||||
if theUnit is not None:
|
|
||||||
controlBox = QHBoxLayout()
|
|
||||||
controlBox.addWidget(qWidget, 0, Qt.AlignVCenter)
|
|
||||||
controlBox.addWidget(QLabel(theUnit), 0, Qt.AlignVCenter)
|
|
||||||
controlBox.setSpacing(wSp)
|
|
||||||
self.addLayout(controlBox, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
|
||||||
|
|
||||||
elif theButton is not None:
|
|
||||||
controlBox = QHBoxLayout()
|
|
||||||
controlBox.addWidget(qWidget, 0, Qt.AlignVCenter)
|
|
||||||
controlBox.addWidget(theButton, 0, Qt.AlignVCenter)
|
|
||||||
controlBox.setSpacing(wSp)
|
|
||||||
self.addLayout(controlBox, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
|
||||||
|
|
||||||
else:
|
|
||||||
if isinstance(theWidget, QLineEdit):
|
|
||||||
qLayout = QHBoxLayout()
|
|
||||||
qLayout.addWidget(theWidget)
|
|
||||||
self.addLayout(qLayout, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
|
||||||
else:
|
|
||||||
self.addWidget(qWidget, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
|
||||||
|
|
||||||
qLabel.setBuddy(qWidget)
|
|
||||||
|
|
||||||
self.setRowStretch(self._nextRow, 0)
|
|
||||||
self.setRowStretch(self._nextRow+1, 1)
|
|
||||||
|
|
||||||
thisEntry["label"] = qLabel
|
|
||||||
thisEntry["widget"] = qWidget
|
|
||||||
|
|
||||||
self._itemMap[self._nextRow] = thisEntry
|
|
||||||
|
|
||||||
self._nextRow += 1
|
|
||||||
|
|
||||||
return self._nextRow - 1
|
|
||||||
|
|
||||||
# END Class QConfigLayout
|
|
||||||
|
|
||||||
|
|
||||||
class QHelpLabel(QLabel):
|
|
||||||
|
|
||||||
def __init__(self, theText, textCol, fontSize=0.9):
|
|
||||||
super().__init__(theText)
|
|
||||||
|
|
||||||
if isinstance(textCol, QColor):
|
|
||||||
qCol = textCol
|
|
||||||
else:
|
|
||||||
qCol = QColor(*textCol)
|
|
||||||
|
|
||||||
lblCol = self.palette()
|
|
||||||
lblCol.setColor(QPalette.WindowText, qCol)
|
|
||||||
self.setPalette(lblCol)
|
|
||||||
|
|
||||||
lblFont = self.font()
|
|
||||||
lblFont.setPointSizeF(fontSize*lblFont.pointSizeF())
|
|
||||||
self.setFont(lblFont)
|
|
||||||
|
|
||||||
self.setWordWrap(True)
|
|
||||||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
# END Class QHelpLabel
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
|
||||||
# Switch Widget
|
|
||||||
# =============================================================================================== #
|
|
||||||
|
|
||||||
class QSwitch(QAbstractButton):
|
|
||||||
|
|
||||||
def __init__(self, parent=None, width=None, height=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
|
|
||||||
if width is None:
|
|
||||||
self._xW = CONFIG.pxInt(40)
|
|
||||||
else:
|
|
||||||
self._xW = width
|
|
||||||
|
|
||||||
if height is None:
|
|
||||||
self._xH = CONFIG.pxInt(20)
|
|
||||||
else:
|
|
||||||
self._xH = height
|
|
||||||
|
|
||||||
self._xR = int(self._xH*0.5)
|
|
||||||
self._xT = int(self._xH*0.6)
|
|
||||||
self._rB = int(CONFIG.guiScale*2)
|
|
||||||
self._rH = self._xH - 2*self._rB
|
|
||||||
self._rR = self._xR - self._rB
|
|
||||||
|
|
||||||
self.setCheckable(True)
|
|
||||||
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
|
||||||
self.setFixedWidth(self._xW)
|
|
||||||
self.setFixedHeight(self._xH)
|
|
||||||
self._offset = self._xR
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
|
||||||
# Properties
|
|
||||||
##
|
|
||||||
|
|
||||||
@pyqtProperty(int)
|
|
||||||
def offset(self):
|
|
||||||
return self._offset
|
|
||||||
|
|
||||||
@offset.setter
|
|
||||||
def offset(self, offset):
|
|
||||||
self._offset = offset
|
|
||||||
self.update()
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
|
||||||
# Getters and Setters
|
|
||||||
##
|
|
||||||
|
|
||||||
def setChecked(self, checked):
|
|
||||||
"""Overload setChecked to also alter the offset.
|
|
||||||
"""
|
|
||||||
super().setChecked(checked)
|
|
||||||
if checked:
|
|
||||||
self.offset = self._xW - self._xR
|
|
||||||
else:
|
|
||||||
self.offset = self._xR
|
|
||||||
return
|
|
||||||
|
|
||||||
##
|
|
||||||
# Events
|
|
||||||
##
|
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
|
||||||
"""Overload resize to ensure correct offset.
|
|
||||||
"""
|
|
||||||
super().resizeEvent(event)
|
|
||||||
if self.isChecked():
|
|
||||||
self.offset = self._xW - self._xR
|
|
||||||
else:
|
|
||||||
self.offset = self._xR
|
|
||||||
return
|
|
||||||
|
|
||||||
def paintEvent(self, event):
|
|
||||||
"""Drawing the switch itself.
|
|
||||||
"""
|
|
||||||
qPaint = QPainter(self)
|
|
||||||
qPaint.setRenderHint(QPainter.Antialiasing, True)
|
|
||||||
qPaint.setPen(Qt.NoPen)
|
|
||||||
|
|
||||||
qPalette = self.palette()
|
|
||||||
if self.isChecked():
|
|
||||||
trackBrush = qPalette.highlight()
|
|
||||||
thumbBrush = qPalette.highlightedText()
|
|
||||||
textColor = qPalette.highlight().color()
|
|
||||||
thumbText = nwUnicode.U_CHECK
|
|
||||||
else:
|
|
||||||
trackBrush = qPalette.dark()
|
|
||||||
thumbBrush = qPalette.light()
|
|
||||||
textColor = qPalette.dark().color()
|
|
||||||
thumbText = nwUnicode.U_CROSS
|
|
||||||
|
|
||||||
if self.isEnabled():
|
|
||||||
trackOpacity = 1.0
|
|
||||||
else:
|
|
||||||
trackOpacity = 0.6
|
|
||||||
trackBrush = qPalette.shadow()
|
|
||||||
thumbBrush = qPalette.mid()
|
|
||||||
textColor = qPalette.shadow().color()
|
|
||||||
|
|
||||||
qPaint.setBrush(trackBrush)
|
|
||||||
qPaint.setOpacity(trackOpacity)
|
|
||||||
qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
|
|
||||||
|
|
||||||
qPaint.setBrush(thumbBrush)
|
|
||||||
qPaint.drawEllipse(self.offset - self._rR, self._rB, self._rH, self._rH)
|
|
||||||
|
|
||||||
theFont = qPaint.font()
|
|
||||||
theFont.setPixelSize(self._xT)
|
|
||||||
qPaint.setPen(textColor)
|
|
||||||
qPaint.setFont(theFont)
|
|
||||||
qPaint.drawText(
|
|
||||||
QRectF(self.offset - self._rR, self._rB, self._rH, self._rH),
|
|
||||||
Qt.AlignCenter, thumbText
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
|
||||||
"""Animate the switch on mouse release.
|
|
||||||
"""
|
|
||||||
super().mouseReleaseEvent(event)
|
|
||||||
if event.button() == Qt.LeftButton:
|
|
||||||
doAnim = QPropertyAnimation(self, b"offset", self)
|
|
||||||
doAnim.setDuration(120)
|
|
||||||
doAnim.setStartValue(self.offset)
|
|
||||||
if self.isChecked():
|
|
||||||
doAnim.setEndValue(self._xW - self._xR)
|
|
||||||
else:
|
|
||||||
doAnim.setEndValue(self._xR)
|
|
||||||
doAnim.start()
|
|
||||||
return
|
|
||||||
|
|
||||||
def enterEvent(self, event):
|
|
||||||
"""Change the cursor when hovering the button.
|
|
||||||
"""
|
|
||||||
self.setCursor(Qt.PointingHandCursor)
|
|
||||||
super().enterEvent(event)
|
|
||||||
return
|
|
||||||
|
|
||||||
# END Class QSwitch
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
|
||||||
# Paged Dialog w/Custom TabWidget
|
|
||||||
# =============================================================================================== #
|
|
||||||
|
|
||||||
class PagedDialog(QDialog):
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
|
|
||||||
self._tabBar = VerticalTabBar(self)
|
|
||||||
self._tabBar.setExpanding(False)
|
|
||||||
|
|
||||||
self._tabBox = QTabWidget()
|
|
||||||
self._tabBox.setTabBar(self._tabBar)
|
|
||||||
self._tabBox.setTabPosition(QTabWidget.West)
|
|
||||||
|
|
||||||
self._buttonBox = QHBoxLayout()
|
|
||||||
|
|
||||||
self._outerBox = QVBoxLayout()
|
|
||||||
self._outerBox.addWidget(self._tabBox)
|
|
||||||
self._outerBox.addLayout(self._buttonBox)
|
|
||||||
|
|
||||||
# Default Margins
|
|
||||||
thisStyle = self.style()
|
|
||||||
mL = thisStyle.pixelMetric(QStyle.PM_LayoutLeftMargin)
|
|
||||||
mR = thisStyle.pixelMetric(QStyle.PM_LayoutRightMargin)
|
|
||||||
mT = thisStyle.pixelMetric(QStyle.PM_LayoutLeftMargin)
|
|
||||||
mB = thisStyle.pixelMetric(QStyle.PM_LayoutBottomMargin)
|
|
||||||
|
|
||||||
# Set Margins
|
|
||||||
self.setContentsMargins(0, 0, 0, 0)
|
|
||||||
self._outerBox.setContentsMargins(0, 0, 0, mB)
|
|
||||||
self._buttonBox.setContentsMargins(mL, 0, mR, 0)
|
|
||||||
self._outerBox.setSpacing(mT)
|
|
||||||
|
|
||||||
self.setLayout(self._outerBox)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def addTab(self, widget, label):
|
|
||||||
"""Forward the adding of tabs to the QTabWidget.
|
|
||||||
"""
|
|
||||||
self._tabBox.addTab(widget, label)
|
|
||||||
return
|
|
||||||
|
|
||||||
def addControls(self, buttonBar):
|
|
||||||
"""Add a button bar to the dialog.
|
|
||||||
"""
|
|
||||||
self._buttonBox.addWidget(buttonBar)
|
|
||||||
return
|
|
||||||
|
|
||||||
def setCurrentWidget(self, widget):
|
|
||||||
"""Forward the changing of tab to the QTabWidget.
|
|
||||||
"""
|
|
||||||
self._tabBox.setCurrentWidget(widget)
|
|
||||||
return
|
|
||||||
|
|
||||||
# END Class PagedDialog
|
|
||||||
|
|
||||||
|
|
||||||
class VerticalTabBar(QTabBar):
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self._mW = CONFIG.pxInt(150)
|
|
||||||
return
|
|
||||||
|
|
||||||
def tabSizeHint(self, index):
|
|
||||||
"""Return a transposed size hint for the rotated bar.
|
|
||||||
"""
|
|
||||||
tSize = super().tabSizeHint(index)
|
|
||||||
tSize.transpose()
|
|
||||||
tSize.setWidth(min(tSize.width(), self._mW))
|
|
||||||
return tSize
|
|
||||||
|
|
||||||
def paintEvent(self, event):
|
|
||||||
"""Custom implementation of the label painter that rotates the
|
|
||||||
label 90 degrees.
|
|
||||||
"""
|
|
||||||
pObj = QStylePainter(self)
|
|
||||||
oObj = QStyleOptionTab()
|
|
||||||
|
|
||||||
for i in range(self.count()):
|
|
||||||
self.initStyleOption(oObj, i)
|
|
||||||
pObj.drawControl(QStyle.CE_TabBarTabShape, oObj)
|
|
||||||
pObj.save()
|
|
||||||
|
|
||||||
oSize = oObj.rect.size()
|
|
||||||
oSize.transpose()
|
|
||||||
oRect = QRect(QPoint(), oSize)
|
|
||||||
oRect.moveCenter(oObj.rect.center())
|
|
||||||
oObj.rect = oRect
|
|
||||||
|
|
||||||
oCenter = self.tabRect(i).center()
|
|
||||||
pObj.translate(oCenter)
|
|
||||||
pObj.rotate(90)
|
|
||||||
pObj.translate(-oCenter)
|
|
||||||
pObj.drawControl(QStyle.CE_TabBarTabLabel, oObj)
|
|
||||||
pObj.restore()
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
# END Class VerticalTabBar
|
|
||||||
@@ -47,7 +47,7 @@ class GuiAbout(QDialog):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiAbout ...")
|
logger.debug("Create: GuiAbout")
|
||||||
self.setObjectName("GuiAbout")
|
self.setObjectName("GuiAbout")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -110,10 +110,14 @@ class GuiAbout(QDialog):
|
|||||||
self.outerBox.addWidget(self.buttonBox)
|
self.outerBox.addWidget(self.buttonBox)
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
logger.debug("GuiAbout initialisation complete")
|
logger.debug("Ready: GuiAbout")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiAbout")
|
||||||
|
return
|
||||||
|
|
||||||
def populateGUI(self):
|
def populateGUI(self):
|
||||||
"""Populate tabs with text.
|
"""Populate tabs with text.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -33,17 +33,20 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.custom import QHelpLabel, QSwitch
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
from novelwriter.extensions.configlayout import NHelpLabel
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GuiDocMerge(QDialog):
|
class GuiDocMerge(QDialog):
|
||||||
|
|
||||||
|
D_HANDLE = Qt.ItemDataRole.UserRole
|
||||||
|
|
||||||
def __init__(self, mainGui, sHandle, itemList):
|
def __init__(self, mainGui, sHandle, itemList):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocMerge ...")
|
logger.debug("Create: GuiDocMerge")
|
||||||
self.setObjectName("GuiDocMerge")
|
self.setObjectName("GuiDocMerge")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -55,7 +58,7 @@ class GuiDocMerge(QDialog):
|
|||||||
self.setWindowTitle(self.tr("Merge Documents"))
|
self.setWindowTitle(self.tr("Merge Documents"))
|
||||||
|
|
||||||
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
|
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Documents to Merge")))
|
||||||
self.helpLabel = QHelpLabel(self.tr(
|
self.helpLabel = NHelpLabel(self.tr(
|
||||||
"Drag and drop items to change the order, or uncheck to exclude."
|
"Drag and drop items to change the order, or uncheck to exclude."
|
||||||
), self.mainTheme.helpText)
|
), self.mainTheme.helpText)
|
||||||
|
|
||||||
@@ -74,7 +77,7 @@ class GuiDocMerge(QDialog):
|
|||||||
|
|
||||||
# Merge Options
|
# Merge Options
|
||||||
self.trashLabel = QLabel(self.tr("Move merged items to Trash"))
|
self.trashLabel = QLabel(self.tr("Move merged items to Trash"))
|
||||||
self.trashSwitch = QSwitch(width=2*iPx, height=iPx)
|
self.trashSwitch = NSwitch(width=2*iPx, height=iPx)
|
||||||
|
|
||||||
self.optBox = QGridLayout()
|
self.optBox = QGridLayout()
|
||||||
self.optBox.addWidget(self.trashLabel, 0, 0)
|
self.optBox.addWidget(self.trashLabel, 0, 0)
|
||||||
@@ -106,10 +109,14 @@ class GuiDocMerge(QDialog):
|
|||||||
# Load Content
|
# Load Content
|
||||||
self._loadContent(sHandle, itemList)
|
self._loadContent(sHandle, itemList)
|
||||||
|
|
||||||
logger.debug("GuiDocMerge initialisation complete")
|
logger.debug("Ready: GuiDocMerge")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiDocMerge")
|
||||||
|
return
|
||||||
|
|
||||||
def getData(self):
|
def getData(self):
|
||||||
"""Return the user's choices.
|
"""Return the user's choices.
|
||||||
"""
|
"""
|
||||||
@@ -117,7 +124,7 @@ class GuiDocMerge(QDialog):
|
|||||||
for i in range(self.listBox.count()):
|
for i in range(self.listBox.count()):
|
||||||
item = self.listBox.item(i)
|
item = self.listBox.item(i)
|
||||||
if item is not None and item.checkState() == Qt.Checked:
|
if item is not None and item.checkState() == Qt.Checked:
|
||||||
finalItems.append(item.data(Qt.UserRole))
|
finalItems.append(item.data(self.D_HANDLE))
|
||||||
|
|
||||||
self._data["moveToTrash"] = self.trashSwitch.isChecked()
|
self._data["moveToTrash"] = self.trashSwitch.isChecked()
|
||||||
self._data["finalItems"] = finalItems
|
self._data["finalItems"] = finalItems
|
||||||
@@ -161,7 +168,7 @@ class GuiDocMerge(QDialog):
|
|||||||
newItem = QListWidgetItem()
|
newItem = QListWidgetItem()
|
||||||
newItem.setIcon(itemIcon)
|
newItem.setIcon(itemIcon)
|
||||||
newItem.setText(nwItem.itemName)
|
newItem.setText(nwItem.itemName)
|
||||||
newItem.setData(Qt.UserRole, tHandle)
|
newItem.setData(self.D_HANDLE, tHandle)
|
||||||
newItem.setCheckState(Qt.Checked)
|
newItem.setCheckState(Qt.Checked)
|
||||||
|
|
||||||
self.listBox.addItem(newItem)
|
self.listBox.addItem(newItem)
|
||||||
|
|||||||
@@ -33,21 +33,22 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.custom import QHelpLabel, QSwitch
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
from novelwriter.extensions.configlayout import NHelpLabel
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GuiDocSplit(QDialog):
|
class GuiDocSplit(QDialog):
|
||||||
|
|
||||||
LINE_ROLE = Qt.UserRole
|
LINE_ROLE = Qt.ItemDataRole.UserRole
|
||||||
LEVEL_ROLE = Qt.UserRole + 1
|
LEVEL_ROLE = Qt.ItemDataRole.UserRole + 1
|
||||||
LABEL_ROLE = Qt.UserRole + 2
|
LABEL_ROLE = Qt.ItemDataRole.UserRole + 2
|
||||||
|
|
||||||
def __init__(self, mainGui, sHandle):
|
def __init__(self, mainGui, sHandle):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocSplit ...")
|
logger.debug("Create: GuiDocSplit")
|
||||||
self.setObjectName("GuiDocSplit")
|
self.setObjectName("GuiDocSplit")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -60,7 +61,7 @@ class GuiDocSplit(QDialog):
|
|||||||
self.setWindowTitle(self.tr("Split Document"))
|
self.setWindowTitle(self.tr("Split Document"))
|
||||||
|
|
||||||
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
|
self.headLabel = QLabel("<b>{0}</b>".format(self.tr("Document Headers")))
|
||||||
self.helpLabel = QHelpLabel(
|
self.helpLabel = NHelpLabel(
|
||||||
self.tr("Select the maximum level to split into files."),
|
self.tr("Select the maximum level to split into files."),
|
||||||
self.mainGui.mainTheme.helpText
|
self.mainGui.mainTheme.helpText
|
||||||
)
|
)
|
||||||
@@ -94,15 +95,15 @@ class GuiDocSplit(QDialog):
|
|||||||
|
|
||||||
# Split Options
|
# Split Options
|
||||||
self.folderLabel = QLabel(self.tr("Split into a new folder"))
|
self.folderLabel = QLabel(self.tr("Split into a new folder"))
|
||||||
self.folderSwitch = QSwitch(width=2*iPx, height=iPx)
|
self.folderSwitch = NSwitch(width=2*iPx, height=iPx)
|
||||||
self.folderSwitch.setChecked(intoFolder)
|
self.folderSwitch.setChecked(intoFolder)
|
||||||
|
|
||||||
self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"))
|
self.hierarchyLabel = QLabel(self.tr("Create document hierarchy"))
|
||||||
self.hierarchySwitch = QSwitch(width=2*iPx, height=iPx)
|
self.hierarchySwitch = NSwitch(width=2*iPx, height=iPx)
|
||||||
self.hierarchySwitch.setChecked(docHierarchy)
|
self.hierarchySwitch.setChecked(docHierarchy)
|
||||||
|
|
||||||
self.trashLabel = QLabel(self.tr("Move split document to Trash"))
|
self.trashLabel = QLabel(self.tr("Move split document to Trash"))
|
||||||
self.trashSwitch = QSwitch(width=2*iPx, height=iPx)
|
self.trashSwitch = NSwitch(width=2*iPx, height=iPx)
|
||||||
|
|
||||||
self.optBox = QGridLayout()
|
self.optBox = QGridLayout()
|
||||||
self.optBox.addWidget(self.folderLabel, 0, 0)
|
self.optBox.addWidget(self.folderLabel, 0, 0)
|
||||||
@@ -137,10 +138,14 @@ class GuiDocSplit(QDialog):
|
|||||||
# Load Content
|
# Load Content
|
||||||
self._loadContent(sHandle)
|
self._loadContent(sHandle)
|
||||||
|
|
||||||
logger.debug("GuiDocSplit initialisation complete")
|
logger.debug("Ready: GuiDocSplit")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiDocSplit")
|
||||||
|
return
|
||||||
|
|
||||||
def getData(self):
|
def getData(self):
|
||||||
"""Return the user's choices. Also save the users options for
|
"""Return the user's choices. Also save the users options for
|
||||||
the next time the dialog is used.
|
the next time the dialog is used.
|
||||||
|
|||||||
@@ -33,18 +33,20 @@ from PyQt5.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.custom import QSwitch, QConfigLayout, PagedDialog
|
|
||||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||||
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
from novelwriter.extensions.pageddialog import NPagedDialog
|
||||||
|
from novelwriter.extensions.configlayout import NConfigLayout
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GuiPreferences(PagedDialog):
|
class GuiPreferences(NPagedDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiPreferences ...")
|
logger.debug("Create: GuiPreferences")
|
||||||
self.setObjectName("GuiPreferences")
|
self.setObjectName("GuiPreferences")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -81,10 +83,14 @@ class GuiPreferences(PagedDialog):
|
|||||||
self._needsRestart = False
|
self._needsRestart = False
|
||||||
self._refreshTree = False
|
self._refreshTree = False
|
||||||
|
|
||||||
logger.debug("GuiPreferences initialisation complete")
|
logger.debug("Ready: GuiPreferences")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiPreferences")
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Properties
|
# Properties
|
||||||
##
|
##
|
||||||
@@ -159,7 +165,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.mainTheme = prefsGui.mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = NConfigLayout()
|
||||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||||
self.setLayout(self.mainForm)
|
self.setLayout(self.mainForm)
|
||||||
|
|
||||||
@@ -228,7 +234,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.tr("Font family"),
|
self.tr("Font family"),
|
||||||
self.guiFont,
|
self.guiFont,
|
||||||
self.tr("Requires restart to take effect."),
|
self.tr("Requires restart to take effect."),
|
||||||
theButton=self.fontButton
|
button=self.fontButton
|
||||||
)
|
)
|
||||||
|
|
||||||
# Font Size
|
# Font Size
|
||||||
@@ -241,14 +247,14 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.tr("Font size"),
|
self.tr("Font size"),
|
||||||
self.guiFontSize,
|
self.guiFontSize,
|
||||||
self.tr("Requires restart to take effect."),
|
self.tr("Requires restart to take effect."),
|
||||||
theUnit=self.tr("pt")
|
unit=self.tr("pt")
|
||||||
)
|
)
|
||||||
|
|
||||||
# GUI Settings
|
# GUI Settings
|
||||||
# ============
|
# ============
|
||||||
self.mainForm.addGroupLabel(self.tr("GUI Settings"))
|
self.mainForm.addGroupLabel(self.tr("GUI Settings"))
|
||||||
|
|
||||||
self.emphLabels = QSwitch()
|
self.emphLabels = NSwitch()
|
||||||
self.emphLabels.setChecked(CONFIG.emphLabels)
|
self.emphLabels.setChecked(CONFIG.emphLabels)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Emphasise partition and chapter labels"),
|
self.tr("Emphasise partition and chapter labels"),
|
||||||
@@ -256,7 +262,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.tr("Makes them stand out in the project tree."),
|
self.tr("Makes them stand out in the project tree."),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.showFullPath = QSwitch()
|
self.showFullPath = NSwitch()
|
||||||
self.showFullPath.setChecked(CONFIG.showFullPath)
|
self.showFullPath.setChecked(CONFIG.showFullPath)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Show full path in document header"),
|
self.tr("Show full path in document header"),
|
||||||
@@ -264,7 +270,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.tr("Add the parent folder names to the header.")
|
self.tr("Add the parent folder names to the header.")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.hideVScroll = QSwitch()
|
self.hideVScroll = NSwitch()
|
||||||
self.hideVScroll.setChecked(CONFIG.hideVScroll)
|
self.hideVScroll.setChecked(CONFIG.hideVScroll)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Hide vertical scroll bars in main windows"),
|
self.tr("Hide vertical scroll bars in main windows"),
|
||||||
@@ -272,7 +278,7 @@ class GuiPreferencesGeneral(QWidget):
|
|||||||
self.tr("Scrolling available with mouse wheel and keys only.")
|
self.tr("Scrolling available with mouse wheel and keys only.")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.hideHScroll = QSwitch()
|
self.hideHScroll = NSwitch()
|
||||||
self.hideHScroll.setChecked(CONFIG.hideHScroll)
|
self.hideHScroll.setChecked(CONFIG.hideHScroll)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Hide horizontal scroll bars in main windows"),
|
self.tr("Hide horizontal scroll bars in main windows"),
|
||||||
@@ -340,7 +346,7 @@ class GuiPreferencesProjects(QWidget):
|
|||||||
self.mainTheme = prefsGui.mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = NConfigLayout()
|
||||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||||
self.setLayout(self.mainForm)
|
self.setLayout(self.mainForm)
|
||||||
|
|
||||||
@@ -358,7 +364,7 @@ class GuiPreferencesProjects(QWidget):
|
|||||||
self.tr("Save document interval"),
|
self.tr("Save document interval"),
|
||||||
self.autoSaveDoc,
|
self.autoSaveDoc,
|
||||||
self.tr("How often the document is automatically saved."),
|
self.tr("How often the document is automatically saved."),
|
||||||
theUnit=self.tr("seconds")
|
unit=self.tr("seconds")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Project Save Timer
|
# Project Save Timer
|
||||||
@@ -371,7 +377,7 @@ class GuiPreferencesProjects(QWidget):
|
|||||||
self.tr("Save project interval"),
|
self.tr("Save project interval"),
|
||||||
self.autoSaveProj,
|
self.autoSaveProj,
|
||||||
self.tr("How often the project is automatically saved."),
|
self.tr("How often the project is automatically saved."),
|
||||||
theUnit=self.tr("seconds")
|
unit=self.tr("seconds")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Project Backup
|
# Project Backup
|
||||||
@@ -389,7 +395,7 @@ class GuiPreferencesProjects(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Run when closing
|
# Run when closing
|
||||||
self.backupOnClose = QSwitch()
|
self.backupOnClose = NSwitch()
|
||||||
self.backupOnClose.setChecked(CONFIG.backupOnClose)
|
self.backupOnClose.setChecked(CONFIG.backupOnClose)
|
||||||
self.backupOnClose.toggled.connect(self._toggledBackupOnClose)
|
self.backupOnClose.toggled.connect(self._toggledBackupOnClose)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -400,7 +406,7 @@ class GuiPreferencesProjects(QWidget):
|
|||||||
|
|
||||||
# Ask before backup
|
# Ask before backup
|
||||||
# Only enabled when "Run when closing" is checked
|
# Only enabled when "Run when closing" is checked
|
||||||
self.askBeforeBackup = QSwitch()
|
self.askBeforeBackup = NSwitch()
|
||||||
self.askBeforeBackup.setChecked(CONFIG.askBeforeBackup)
|
self.askBeforeBackup.setChecked(CONFIG.askBeforeBackup)
|
||||||
self.askBeforeBackup.setEnabled(CONFIG.backupOnClose)
|
self.askBeforeBackup.setEnabled(CONFIG.backupOnClose)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -414,7 +420,7 @@ class GuiPreferencesProjects(QWidget):
|
|||||||
self.mainForm.addGroupLabel(self.tr("Session Timer"))
|
self.mainForm.addGroupLabel(self.tr("Session Timer"))
|
||||||
|
|
||||||
# Pause when idle
|
# Pause when idle
|
||||||
self.stopWhenIdle = QSwitch()
|
self.stopWhenIdle = NSwitch()
|
||||||
self.stopWhenIdle.setChecked(CONFIG.stopWhenIdle)
|
self.stopWhenIdle.setChecked(CONFIG.stopWhenIdle)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Pause the session timer when not writing"),
|
self.tr("Pause the session timer when not writing"),
|
||||||
@@ -433,7 +439,7 @@ class GuiPreferencesProjects(QWidget):
|
|||||||
self.tr("Editor inactive time before pausing timer"),
|
self.tr("Editor inactive time before pausing timer"),
|
||||||
self.userIdleTime,
|
self.userIdleTime,
|
||||||
self.tr("User activity includes typing and changing the content."),
|
self.tr("User activity includes typing and changing the content."),
|
||||||
theUnit=self.tr("minutes")
|
unit=self.tr("minutes")
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -495,7 +501,7 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
self.mainTheme = prefsGui.mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = NConfigLayout()
|
||||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||||
self.setLayout(self.mainForm)
|
self.setLayout(self.mainForm)
|
||||||
|
|
||||||
@@ -515,7 +521,7 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
self.tr("Font family"),
|
self.tr("Font family"),
|
||||||
self.textFont,
|
self.textFont,
|
||||||
self.tr("Applies to both document editor and viewer."),
|
self.tr("Applies to both document editor and viewer."),
|
||||||
theButton=self.fontButton
|
button=self.fontButton
|
||||||
)
|
)
|
||||||
|
|
||||||
# Font Size
|
# Font Size
|
||||||
@@ -528,7 +534,7 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
self.tr("Font size"),
|
self.tr("Font size"),
|
||||||
self.textSize,
|
self.textSize,
|
||||||
self.tr("Applies to both document editor and viewer."),
|
self.tr("Applies to both document editor and viewer."),
|
||||||
theUnit=self.tr("pt")
|
unit=self.tr("pt")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Text Flow
|
# Text Flow
|
||||||
@@ -545,7 +551,7 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
self.tr("Maximum text width in \"Normal Mode\""),
|
self.tr("Maximum text width in \"Normal Mode\""),
|
||||||
self.textWidth,
|
self.textWidth,
|
||||||
self.tr("Set to 0 to disable this feature."),
|
self.tr("Set to 0 to disable this feature."),
|
||||||
theUnit=self.tr("px")
|
unit=self.tr("px")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Max Text Width in Focus Mode
|
# Max Text Width in Focus Mode
|
||||||
@@ -558,11 +564,11 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
self.tr("Maximum text width in \"Focus Mode\""),
|
self.tr("Maximum text width in \"Focus Mode\""),
|
||||||
self.focusWidth,
|
self.focusWidth,
|
||||||
self.tr("The maximum width cannot be disabled."),
|
self.tr("The maximum width cannot be disabled."),
|
||||||
theUnit=self.tr("px")
|
unit=self.tr("px")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Focus Mode Footer
|
# Focus Mode Footer
|
||||||
self.hideFocusFooter = QSwitch()
|
self.hideFocusFooter = NSwitch()
|
||||||
self.hideFocusFooter.setChecked(CONFIG.hideFocusFooter)
|
self.hideFocusFooter.setChecked(CONFIG.hideFocusFooter)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Hide document footer in \"Focus Mode\""),
|
self.tr("Hide document footer in \"Focus Mode\""),
|
||||||
@@ -571,7 +577,7 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Justify Text
|
# Justify Text
|
||||||
self.doJustify = QSwitch()
|
self.doJustify = NSwitch()
|
||||||
self.doJustify.setChecked(CONFIG.doJustify)
|
self.doJustify.setChecked(CONFIG.doJustify)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Justify the text margins"),
|
self.tr("Justify the text margins"),
|
||||||
@@ -589,7 +595,7 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
self.tr("Minimum text margin"),
|
self.tr("Minimum text margin"),
|
||||||
self.textMargin,
|
self.textMargin,
|
||||||
self.tr("Applies to both document editor and viewer."),
|
self.tr("Applies to both document editor and viewer."),
|
||||||
theUnit=self.tr("px")
|
unit=self.tr("px")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Tab Width
|
# Tab Width
|
||||||
@@ -602,7 +608,7 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
self.tr("Tab width"),
|
self.tr("Tab width"),
|
||||||
self.tabWidth,
|
self.tabWidth,
|
||||||
self.tr("The width of a tab key press in the editor and viewer."),
|
self.tr("The width of a tab key press in the editor and viewer."),
|
||||||
theUnit=self.tr("px")
|
unit=self.tr("px")
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -611,8 +617,7 @@ class GuiPreferencesDocuments(QWidget):
|
|||||||
"""Save the values set for this tab.
|
"""Save the values set for this tab.
|
||||||
"""
|
"""
|
||||||
# Text Style
|
# Text Style
|
||||||
CONFIG.textFont = self.textFont.text()
|
CONFIG.setTextFont(self.textFont.text(), self.textSize.value())
|
||||||
CONFIG.textSize = self.textSize.value()
|
|
||||||
|
|
||||||
# Text Flow
|
# Text Flow
|
||||||
CONFIG.textWidth = self.textWidth.value()
|
CONFIG.textWidth = self.textWidth.value()
|
||||||
@@ -653,7 +658,7 @@ class GuiPreferencesEditor(QWidget):
|
|||||||
self.mainTheme = prefsGui.mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = NConfigLayout()
|
||||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||||
self.setLayout(self.mainForm)
|
self.setLayout(self.mainForm)
|
||||||
|
|
||||||
@@ -701,7 +706,7 @@ class GuiPreferencesEditor(QWidget):
|
|||||||
self.tr("Big document limit"),
|
self.tr("Big document limit"),
|
||||||
self.bigDocLimit,
|
self.bigDocLimit,
|
||||||
self.tr("Full spell checking is disabled above this limit."),
|
self.tr("Full spell checking is disabled above this limit."),
|
||||||
theUnit=self.tr("kB")
|
unit=self.tr("kB")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Word Count
|
# Word Count
|
||||||
@@ -718,11 +723,11 @@ class GuiPreferencesEditor(QWidget):
|
|||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Word count interval"),
|
self.tr("Word count interval"),
|
||||||
self.wordCountTimer,
|
self.wordCountTimer,
|
||||||
theUnit=self.tr("seconds")
|
unit=self.tr("seconds")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Include Notes in Word Count
|
# Include Notes in Word Count
|
||||||
self.incNotesWCount = QSwitch()
|
self.incNotesWCount = NSwitch()
|
||||||
self.incNotesWCount.setChecked(CONFIG.incNotesWCount)
|
self.incNotesWCount.setChecked(CONFIG.incNotesWCount)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Include project notes in status bar word count"),
|
self.tr("Include project notes in status bar word count"),
|
||||||
@@ -734,7 +739,7 @@ class GuiPreferencesEditor(QWidget):
|
|||||||
self.mainForm.addGroupLabel(self.tr("Writing Guides"))
|
self.mainForm.addGroupLabel(self.tr("Writing Guides"))
|
||||||
|
|
||||||
# Show Tabs and Spaces
|
# Show Tabs and Spaces
|
||||||
self.showTabsNSpaces = QSwitch()
|
self.showTabsNSpaces = NSwitch()
|
||||||
self.showTabsNSpaces.setChecked(CONFIG.showTabsNSpaces)
|
self.showTabsNSpaces.setChecked(CONFIG.showTabsNSpaces)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Show tabs and spaces"),
|
self.tr("Show tabs and spaces"),
|
||||||
@@ -742,7 +747,7 @@ class GuiPreferencesEditor(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Show Line Endings
|
# Show Line Endings
|
||||||
self.showLineEndings = QSwitch()
|
self.showLineEndings = NSwitch()
|
||||||
self.showLineEndings.setChecked(CONFIG.showLineEndings)
|
self.showLineEndings.setChecked(CONFIG.showLineEndings)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Show line endings"),
|
self.tr("Show line endings"),
|
||||||
@@ -763,11 +768,11 @@ class GuiPreferencesEditor(QWidget):
|
|||||||
self.tr("Scroll past end of the document"),
|
self.tr("Scroll past end of the document"),
|
||||||
self.scrollPastEnd,
|
self.scrollPastEnd,
|
||||||
self.tr("Set to 0 to disable this feature."),
|
self.tr("Set to 0 to disable this feature."),
|
||||||
theUnit=self.tr("lines")
|
unit=self.tr("lines")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Typewriter Scrolling
|
# Typewriter Scrolling
|
||||||
self.autoScroll = QSwitch()
|
self.autoScroll = NSwitch()
|
||||||
self.autoScroll.setChecked(CONFIG.autoScroll)
|
self.autoScroll.setChecked(CONFIG.autoScroll)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Typewriter style scrolling when you type"),
|
self.tr("Typewriter style scrolling when you type"),
|
||||||
@@ -785,7 +790,7 @@ class GuiPreferencesEditor(QWidget):
|
|||||||
self.tr("Minimum position for Typewriter scrolling"),
|
self.tr("Minimum position for Typewriter scrolling"),
|
||||||
self.autoScrollPos,
|
self.autoScrollPos,
|
||||||
self.tr("Percentage of the editor height from the top."),
|
self.tr("Percentage of the editor height from the top."),
|
||||||
theUnit="%"
|
unit="%"
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -825,7 +830,7 @@ class GuiPreferencesSyntax(QWidget):
|
|||||||
self.mainTheme = prefsGui.mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = NConfigLayout()
|
||||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||||
self.setLayout(self.mainForm)
|
self.setLayout(self.mainForm)
|
||||||
|
|
||||||
@@ -833,7 +838,7 @@ class GuiPreferencesSyntax(QWidget):
|
|||||||
# =================
|
# =================
|
||||||
self.mainForm.addGroupLabel(self.tr("Quotes & Dialogue"))
|
self.mainForm.addGroupLabel(self.tr("Quotes & Dialogue"))
|
||||||
|
|
||||||
self.highlightQuotes = QSwitch()
|
self.highlightQuotes = NSwitch()
|
||||||
self.highlightQuotes.setChecked(CONFIG.highlightQuotes)
|
self.highlightQuotes.setChecked(CONFIG.highlightQuotes)
|
||||||
self.highlightQuotes.toggled.connect(self._toggleHighlightQuotes)
|
self.highlightQuotes.toggled.connect(self._toggleHighlightQuotes)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -842,7 +847,7 @@ class GuiPreferencesSyntax(QWidget):
|
|||||||
self.tr("Applies to the document editor only.")
|
self.tr("Applies to the document editor only.")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.allowOpenSQuote = QSwitch()
|
self.allowOpenSQuote = NSwitch()
|
||||||
self.allowOpenSQuote.setChecked(CONFIG.allowOpenSQuote)
|
self.allowOpenSQuote.setChecked(CONFIG.allowOpenSQuote)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Allow open-ended single quotes"),
|
self.tr("Allow open-ended single quotes"),
|
||||||
@@ -850,7 +855,7 @@ class GuiPreferencesSyntax(QWidget):
|
|||||||
self.tr("Highlight single-quoted line with no closing quote.")
|
self.tr("Highlight single-quoted line with no closing quote.")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.allowOpenDQuote = QSwitch()
|
self.allowOpenDQuote = NSwitch()
|
||||||
self.allowOpenDQuote.setChecked(CONFIG.allowOpenDQuote)
|
self.allowOpenDQuote.setChecked(CONFIG.allowOpenDQuote)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Allow open-ended double quotes"),
|
self.tr("Allow open-ended double quotes"),
|
||||||
@@ -862,7 +867,7 @@ class GuiPreferencesSyntax(QWidget):
|
|||||||
# =============
|
# =============
|
||||||
self.mainForm.addGroupLabel(self.tr("Text Emphasis"))
|
self.mainForm.addGroupLabel(self.tr("Text Emphasis"))
|
||||||
|
|
||||||
self.highlightEmph = QSwitch()
|
self.highlightEmph = NSwitch()
|
||||||
self.highlightEmph.setChecked(CONFIG.highlightEmph)
|
self.highlightEmph.setChecked(CONFIG.highlightEmph)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Add highlight colour to emphasised text"),
|
self.tr("Add highlight colour to emphasised text"),
|
||||||
@@ -875,7 +880,7 @@ class GuiPreferencesSyntax(QWidget):
|
|||||||
|
|
||||||
self.mainForm.addGroupLabel(self.tr("Text Errors"))
|
self.mainForm.addGroupLabel(self.tr("Text Errors"))
|
||||||
|
|
||||||
self.showMultiSpaces = QSwitch()
|
self.showMultiSpaces = NSwitch()
|
||||||
self.showMultiSpaces.setChecked(CONFIG.showMultiSpaces)
|
self.showMultiSpaces.setChecked(CONFIG.showMultiSpaces)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Highlight multiple or trailing spaces"),
|
self.tr("Highlight multiple or trailing spaces"),
|
||||||
@@ -930,7 +935,7 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
self.mainTheme = prefsGui.mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = NConfigLayout()
|
||||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||||
self.setLayout(self.mainForm)
|
self.setLayout(self.mainForm)
|
||||||
|
|
||||||
@@ -939,7 +944,7 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
self.mainForm.addGroupLabel(self.tr("Automatic Features"))
|
self.mainForm.addGroupLabel(self.tr("Automatic Features"))
|
||||||
|
|
||||||
# Auto-Select Word Under Cursor
|
# Auto-Select Word Under Cursor
|
||||||
self.autoSelect = QSwitch()
|
self.autoSelect = NSwitch()
|
||||||
self.autoSelect.setChecked(CONFIG.autoSelect)
|
self.autoSelect.setChecked(CONFIG.autoSelect)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("Auto-select word under cursor"),
|
self.tr("Auto-select word under cursor"),
|
||||||
@@ -948,7 +953,7 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Auto-Replace as You Type Main Switch
|
# Auto-Replace as You Type Main Switch
|
||||||
self.doReplace = QSwitch()
|
self.doReplace = NSwitch()
|
||||||
self.doReplace.setChecked(CONFIG.doReplace)
|
self.doReplace.setChecked(CONFIG.doReplace)
|
||||||
self.doReplace.toggled.connect(self._toggleAutoReplaceMain)
|
self.doReplace.toggled.connect(self._toggleAutoReplaceMain)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -962,7 +967,7 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
self.mainForm.addGroupLabel(self.tr("Replace as You Type"))
|
self.mainForm.addGroupLabel(self.tr("Replace as You Type"))
|
||||||
|
|
||||||
# Auto-Replace Single Quotes
|
# Auto-Replace Single Quotes
|
||||||
self.doReplaceSQuote = QSwitch()
|
self.doReplaceSQuote = NSwitch()
|
||||||
self.doReplaceSQuote.setChecked(CONFIG.doReplaceSQuote)
|
self.doReplaceSQuote.setChecked(CONFIG.doReplaceSQuote)
|
||||||
self.doReplaceSQuote.setEnabled(CONFIG.doReplace)
|
self.doReplaceSQuote.setEnabled(CONFIG.doReplace)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -972,7 +977,7 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Auto-Replace Double Quotes
|
# Auto-Replace Double Quotes
|
||||||
self.doReplaceDQuote = QSwitch()
|
self.doReplaceDQuote = NSwitch()
|
||||||
self.doReplaceDQuote.setChecked(CONFIG.doReplaceDQuote)
|
self.doReplaceDQuote.setChecked(CONFIG.doReplaceDQuote)
|
||||||
self.doReplaceDQuote.setEnabled(CONFIG.doReplace)
|
self.doReplaceDQuote.setEnabled(CONFIG.doReplace)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -982,7 +987,7 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Auto-Replace Hyphens
|
# Auto-Replace Hyphens
|
||||||
self.doReplaceDash = QSwitch()
|
self.doReplaceDash = NSwitch()
|
||||||
self.doReplaceDash.setChecked(CONFIG.doReplaceDash)
|
self.doReplaceDash.setChecked(CONFIG.doReplaceDash)
|
||||||
self.doReplaceDash.setEnabled(CONFIG.doReplace)
|
self.doReplaceDash.setEnabled(CONFIG.doReplace)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -992,7 +997,7 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Auto-Replace Dots
|
# Auto-Replace Dots
|
||||||
self.doReplaceDots = QSwitch()
|
self.doReplaceDots = NSwitch()
|
||||||
self.doReplaceDots.setChecked(CONFIG.doReplaceDots)
|
self.doReplaceDots.setChecked(CONFIG.doReplaceDots)
|
||||||
self.doReplaceDots.setEnabled(CONFIG.doReplace)
|
self.doReplaceDots.setEnabled(CONFIG.doReplace)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -1026,7 +1031,7 @@ class GuiPreferencesAutomation(QWidget):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Use Thin Space
|
# Use Thin Space
|
||||||
self.fmtPadThin = QSwitch()
|
self.fmtPadThin = NSwitch()
|
||||||
self.fmtPadThin.setChecked(CONFIG.fmtPadThin)
|
self.fmtPadThin.setChecked(CONFIG.fmtPadThin)
|
||||||
self.fmtPadThin.setEnabled(CONFIG.doReplace)
|
self.fmtPadThin.setEnabled(CONFIG.doReplace)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
@@ -1084,7 +1089,7 @@ class GuiPreferencesQuotes(QWidget):
|
|||||||
self.mainTheme = prefsGui.mainGui.mainTheme
|
self.mainTheme = prefsGui.mainGui.mainTheme
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = NConfigLayout()
|
||||||
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
self.mainForm.setHelpTextStyle(self.mainTheme.helpText)
|
||||||
self.setLayout(self.mainForm)
|
self.setLayout(self.mainForm)
|
||||||
|
|
||||||
@@ -1110,7 +1115,7 @@ class GuiPreferencesQuotes(QWidget):
|
|||||||
self.tr("Single quote open style"),
|
self.tr("Single quote open style"),
|
||||||
self.quoteSym["SO"],
|
self.quoteSym["SO"],
|
||||||
self.tr("The symbol to use for a leading single quote."),
|
self.tr("The symbol to use for a leading single quote."),
|
||||||
theButton=self.btnSingleStyleO
|
button=self.btnSingleStyleO
|
||||||
)
|
)
|
||||||
|
|
||||||
self.quoteSym["SC"] = QLineEdit()
|
self.quoteSym["SC"] = QLineEdit()
|
||||||
@@ -1126,7 +1131,7 @@ class GuiPreferencesQuotes(QWidget):
|
|||||||
self.tr("Single quote close style"),
|
self.tr("Single quote close style"),
|
||||||
self.quoteSym["SC"],
|
self.quoteSym["SC"],
|
||||||
self.tr("The symbol to use for a trailing single quote."),
|
self.tr("The symbol to use for a trailing single quote."),
|
||||||
theButton=self.btnSingleStyleC
|
button=self.btnSingleStyleC
|
||||||
)
|
)
|
||||||
|
|
||||||
# Double Quote Style
|
# Double Quote Style
|
||||||
@@ -1143,7 +1148,7 @@ class GuiPreferencesQuotes(QWidget):
|
|||||||
self.tr("Double quote open style"),
|
self.tr("Double quote open style"),
|
||||||
self.quoteSym["DO"],
|
self.quoteSym["DO"],
|
||||||
self.tr("The symbol to use for a leading double quote."),
|
self.tr("The symbol to use for a leading double quote."),
|
||||||
theButton=self.btnDoubleStyleO
|
button=self.btnDoubleStyleO
|
||||||
)
|
)
|
||||||
|
|
||||||
self.quoteSym["DC"] = QLineEdit()
|
self.quoteSym["DC"] = QLineEdit()
|
||||||
@@ -1159,7 +1164,7 @@ class GuiPreferencesQuotes(QWidget):
|
|||||||
self.tr("Double quote close style"),
|
self.tr("Double quote close style"),
|
||||||
self.quoteSym["DC"],
|
self.quoteSym["DC"],
|
||||||
self.tr("The symbol to use for a trailing double quote."),
|
self.tr("The symbol to use for a trailing double quote."),
|
||||||
theButton=self.btnDoubleStyleC
|
button=self.btnDoubleStyleC
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -35,19 +35,20 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.common import formatTime, numberToRoman
|
from novelwriter.common import formatTime, numberToRoman
|
||||||
from novelwriter.custom import PagedDialog, QSwitch
|
|
||||||
from novelwriter.constants import nwUnicode
|
from novelwriter.constants import nwUnicode
|
||||||
from novelwriter.gui.components import NovelSelector
|
from novelwriter.gui.components import NovelSelector
|
||||||
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
from novelwriter.extensions.pageddialog import NPagedDialog
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GuiProjectDetails(PagedDialog):
|
class GuiProjectDetails(NPagedDialog):
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectDetails ...")
|
logger.debug("Create: GuiProjectDetails")
|
||||||
self.setObjectName("GuiProjectDetails")
|
self.setObjectName("GuiProjectDetails")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -77,10 +78,14 @@ class GuiProjectDetails(PagedDialog):
|
|||||||
self.buttonBox.rejected.connect(self._doClose)
|
self.buttonBox.rejected.connect(self._doClose)
|
||||||
self.addControls(self.buttonBox)
|
self.addControls(self.buttonBox)
|
||||||
|
|
||||||
logger.debug("GuiProjectDetails initialisation complete")
|
logger.debug("Ready: GuiProjectDetails")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiProjectDetails")
|
||||||
|
return
|
||||||
|
|
||||||
def updateValues(self):
|
def updateValues(self):
|
||||||
"""Set all the values of the pages.
|
"""Set all the values of the pages.
|
||||||
"""
|
"""
|
||||||
@@ -383,7 +388,7 @@ class GuiProjectDetailsContents(QWidget):
|
|||||||
self.dblLabel = QLabel(self.tr("Clear double pages"))
|
self.dblLabel = QLabel(self.tr("Clear double pages"))
|
||||||
self.dblLabel.setToolTip(dblHelp)
|
self.dblLabel.setToolTip(dblHelp)
|
||||||
|
|
||||||
self.dblValue = QSwitch(self, 2*iPx, iPx)
|
self.dblValue = NSwitch(self, 2*iPx, iPx)
|
||||||
self.dblValue.setChecked(clearDouble)
|
self.dblValue.setChecked(clearDouble)
|
||||||
self.dblValue.setToolTip(dblHelp)
|
self.dblValue.setToolTip(dblHelp)
|
||||||
self.dblValue.clicked.connect(self._populateTree)
|
self.dblValue.clicked.connect(self._populateTree)
|
||||||
|
|||||||
@@ -53,10 +53,12 @@ class GuiProjectLoad(QDialog):
|
|||||||
C_COUNT = 1
|
C_COUNT = 1
|
||||||
C_TIME = 2
|
C_TIME = 2
|
||||||
|
|
||||||
|
D_PATH = Qt.ItemDataRole.UserRole
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectLoad ...")
|
logger.debug("Create: GuiProjectLoad")
|
||||||
self.setObjectName("GuiProjectLoad")
|
self.setObjectName("GuiProjectLoad")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -145,10 +147,14 @@ class GuiProjectLoad(QDialog):
|
|||||||
keyDelete.setKey(QKeySequence(Qt.Key_Delete))
|
keyDelete.setKey(QKeySequence(Qt.Key_Delete))
|
||||||
keyDelete.activated.connect(self._doDeleteRecent)
|
keyDelete.activated.connect(self._doDeleteRecent)
|
||||||
|
|
||||||
logger.debug("GuiProjectLoad initialisation complete")
|
logger.debug("Ready: GuiProjectLoad")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiProjectLoad")
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Buttons
|
# Buttons
|
||||||
##
|
##
|
||||||
@@ -163,7 +169,7 @@ class GuiProjectLoad(QDialog):
|
|||||||
|
|
||||||
selItems = self.listBox.selectedItems()
|
selItems = self.listBox.selectedItems()
|
||||||
if selItems:
|
if selItems:
|
||||||
self.openPath = selItems[0].data(self.C_NAME, Qt.UserRole)
|
self.openPath = selItems[0].data(self.C_NAME, self.D_PATH)
|
||||||
self.openState = self.OPEN_STATE
|
self.openState = self.OPEN_STATE
|
||||||
self.accept()
|
self.accept()
|
||||||
|
|
||||||
@@ -174,7 +180,7 @@ class GuiProjectLoad(QDialog):
|
|||||||
"""
|
"""
|
||||||
selList = self.listBox.selectedItems()
|
selList = self.listBox.selectedItems()
|
||||||
if selList:
|
if selList:
|
||||||
self.selPath.setText(selList[0].data(self.C_NAME, Qt.UserRole))
|
self.selPath.setText(selList[0].data(self.C_NAME, self.D_PATH))
|
||||||
return
|
return
|
||||||
|
|
||||||
def _doBrowse(self):
|
def _doBrowse(self):
|
||||||
@@ -228,7 +234,7 @@ class GuiProjectLoad(QDialog):
|
|||||||
)
|
)
|
||||||
if msgYes:
|
if msgYes:
|
||||||
CONFIG.recentProjects.remove(
|
CONFIG.recentProjects.remove(
|
||||||
selList[0].data(self.C_NAME, Qt.UserRole)
|
selList[0].data(self.C_NAME, self.D_PATH)
|
||||||
)
|
)
|
||||||
self._populateList()
|
self._populateList()
|
||||||
|
|
||||||
@@ -270,12 +276,12 @@ class GuiProjectLoad(QDialog):
|
|||||||
newItem = QTreeWidgetItem([""]*4)
|
newItem = QTreeWidgetItem([""]*4)
|
||||||
newItem.setIcon(self.C_NAME, nwxIcon)
|
newItem.setIcon(self.C_NAME, nwxIcon)
|
||||||
newItem.setText(self.C_NAME, title)
|
newItem.setText(self.C_NAME, title)
|
||||||
newItem.setData(self.C_NAME, Qt.UserRole, path)
|
newItem.setData(self.C_NAME, self.D_PATH, path)
|
||||||
newItem.setText(self.C_COUNT, formatInt(words))
|
newItem.setText(self.C_COUNT, formatInt(words))
|
||||||
newItem.setText(self.C_TIME, datetime.fromtimestamp(time).strftime("%x %X"))
|
newItem.setText(self.C_TIME, datetime.fromtimestamp(time).strftime("%x %X"))
|
||||||
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
|
newItem.setTextAlignment(self.C_NAME, Qt.AlignLeft | Qt.AlignVCenter)
|
||||||
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
|
newItem.setTextAlignment(self.C_COUNT, Qt.AlignRight | Qt.AlignVCenter)
|
||||||
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
|
newItem.setTextAlignment(self.C_TIME, Qt.AlignRight | Qt.AlignVCenter)
|
||||||
newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed)
|
newItem.setFont(self.C_TIME, self.mainTheme.guiFontFixed)
|
||||||
self.listBox.addTopLevelItem(newItem)
|
self.listBox.addTopLevelItem(newItem)
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,14 @@ from PyQt5.QtWidgets import (
|
|||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.enum import nwAlert
|
from novelwriter.enum import nwAlert
|
||||||
from novelwriter.common import simplified
|
from novelwriter.common import simplified
|
||||||
from novelwriter.custom import QSwitch, PagedDialog, QConfigLayout
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
from novelwriter.extensions.pageddialog import NPagedDialog
|
||||||
|
from novelwriter.extensions.configlayout import NConfigLayout
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GuiProjectSettings(PagedDialog):
|
class GuiProjectSettings(NPagedDialog):
|
||||||
|
|
||||||
TAB_MAIN = 0
|
TAB_MAIN = 0
|
||||||
TAB_STATUS = 1
|
TAB_STATUS = 1
|
||||||
@@ -50,7 +52,7 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
def __init__(self, mainGui, focusTab=TAB_MAIN):
|
def __init__(self, mainGui, focusTab=TAB_MAIN):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectSettings ...")
|
logger.debug("Create: GuiProjectSettings")
|
||||||
self.setObjectName("GuiProjectSettings")
|
self.setObjectName("GuiProjectSettings")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -91,10 +93,14 @@ class GuiProjectSettings(PagedDialog):
|
|||||||
# Focus Tab
|
# Focus Tab
|
||||||
self._focusTab(focusTab)
|
self._focusTab(focusTab)
|
||||||
|
|
||||||
logger.debug("GuiProjectSettings initialisation complete")
|
logger.debug("Ready: GuiProjectSettings")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiProjectSettings")
|
||||||
|
return
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def spellChanged(self):
|
def spellChanged(self):
|
||||||
return self._spellChanged
|
return self._spellChanged
|
||||||
@@ -194,7 +200,7 @@ class GuiProjectEditMain(QWidget):
|
|||||||
self.theProject = projGui.theProject
|
self.theProject = projGui.theProject
|
||||||
|
|
||||||
# The Form
|
# The Form
|
||||||
self.mainForm = QConfigLayout()
|
self.mainForm = NConfigLayout()
|
||||||
self.mainForm.setHelpTextStyle(self.mainGui.mainTheme.helpText)
|
self.mainForm.setHelpTextStyle(self.mainGui.mainTheme.helpText)
|
||||||
self.setLayout(self.mainForm)
|
self.setLayout(self.mainForm)
|
||||||
|
|
||||||
@@ -253,7 +259,7 @@ class GuiProjectEditMain(QWidget):
|
|||||||
if spellIdx != -1:
|
if spellIdx != -1:
|
||||||
self.spellLang.setCurrentIndex(spellIdx)
|
self.spellLang.setCurrentIndex(spellIdx)
|
||||||
|
|
||||||
self.doBackup = QSwitch(self)
|
self.doBackup = NSwitch(self)
|
||||||
self.doBackup.setChecked(not self.theProject.data.doBackup)
|
self.doBackup.setChecked(not self.theProject.data.doBackup)
|
||||||
self.mainForm.addRow(
|
self.mainForm.addRow(
|
||||||
self.tr("No backup on close"),
|
self.tr("No backup on close"),
|
||||||
@@ -271,9 +277,9 @@ class GuiProjectEditStatus(QWidget):
|
|||||||
COL_LABEL = 0
|
COL_LABEL = 0
|
||||||
COL_USAGE = 1
|
COL_USAGE = 1
|
||||||
|
|
||||||
KEY_ROLE = Qt.UserRole
|
KEY_ROLE = Qt.ItemDataRole.UserRole
|
||||||
COL_ROLE = Qt.UserRole + 1
|
COL_ROLE = Qt.ItemDataRole.UserRole + 1
|
||||||
NUM_ROLE = Qt.UserRole + 2
|
NUM_ROLE = Qt.ItemDataRole.UserRole + 2
|
||||||
|
|
||||||
def __init__(self, projGui, isStatus):
|
def __init__(self, projGui, isStatus):
|
||||||
super().__init__(parent=projGui)
|
super().__init__(parent=projGui)
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class GuiQuoteSelect(QDialog):
|
|||||||
|
|
||||||
selectedQuote = ""
|
selectedQuote = ""
|
||||||
|
|
||||||
|
D_KEY = Qt.ItemDataRole.UserRole
|
||||||
|
|
||||||
def __init__(self, parent=None, currentQuote='"'):
|
def __init__(self, parent=None, currentQuote='"'):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
@@ -75,7 +77,7 @@ class GuiQuoteSelect(QDialog):
|
|||||||
theText = "[ %s ] %s" % (sKey, trConst(sLabel))
|
theText = "[ %s ] %s" % (sKey, trConst(sLabel))
|
||||||
minSize = max(minSize, qMetrics.boundingRect(theText).width())
|
minSize = max(minSize, qMetrics.boundingRect(theText).width())
|
||||||
qtItem = QListWidgetItem(theText)
|
qtItem = QListWidgetItem(theText)
|
||||||
qtItem.setData(Qt.UserRole, sKey)
|
qtItem.setData(self.D_KEY, sKey)
|
||||||
self.listBox.addItem(qtItem)
|
self.listBox.addItem(qtItem)
|
||||||
if sKey == currentQuote:
|
if sKey == currentQuote:
|
||||||
self.listBox.setCurrentItem(qtItem)
|
self.listBox.setCurrentItem(qtItem)
|
||||||
@@ -111,7 +113,7 @@ class GuiQuoteSelect(QDialog):
|
|||||||
"""
|
"""
|
||||||
selItems = self.listBox.selectedItems()
|
selItems = self.listBox.selectedItems()
|
||||||
if selItems:
|
if selItems:
|
||||||
theSymbol = selItems[0].data(Qt.UserRole)
|
theSymbol = selItems[0].data(self.D_KEY)
|
||||||
self.previewLabel.setText(theSymbol)
|
self.previewLabel.setText(theSymbol)
|
||||||
self.selectedQuote = theSymbol
|
self.selectedQuote = theSymbol
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class GuiUpdates(QDialog):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiUpdates ...")
|
logger.debug("Create: GuiUpdates")
|
||||||
self.setObjectName("GuiUpdates")
|
self.setObjectName("GuiUpdates")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -113,10 +113,14 @@ class GuiUpdates(QDialog):
|
|||||||
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
logger.debug("GuiUpdates initialisation complete")
|
logger.debug("Ready: GuiUpdates")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiUpdates")
|
||||||
|
return
|
||||||
|
|
||||||
def checkLatest(self):
|
def checkLatest(self):
|
||||||
"""Check for latest release.
|
"""Check for latest release.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class GuiWordList(QDialog):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiWordList ...")
|
logger.debug("Create: GuiWordList")
|
||||||
self.setObjectName("GuiWordList")
|
self.setObjectName("GuiWordList")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -108,10 +108,14 @@ class GuiWordList(QDialog):
|
|||||||
|
|
||||||
self._loadWordList()
|
self._loadWordList()
|
||||||
|
|
||||||
logger.debug("GuiWordList initialisation complete")
|
logger.debug("Ready: GuiWordList")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiWordList")
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -171,3 +171,17 @@ class nwOutline(Enum):
|
|||||||
SYNOP = 16
|
SYNOP = 16
|
||||||
|
|
||||||
# END Enum nwOutline
|
# END Enum nwOutline
|
||||||
|
|
||||||
|
|
||||||
|
class nwBuildFmt(Enum):
|
||||||
|
|
||||||
|
ODT = 0
|
||||||
|
FODT = 1
|
||||||
|
HTML = 2
|
||||||
|
NWD = 3
|
||||||
|
STD_MD = 4
|
||||||
|
EXT_MD = 5
|
||||||
|
J_HTML = 6
|
||||||
|
J_NWD = 7
|
||||||
|
|
||||||
|
# END Enum nwBuildFormat
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Custom Layouts and Widgets
|
||||||
|
"""
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Custom Widget: Progress Circle
|
||||||
|
============================================
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2023-06-07 [2.1b1]
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QBrush, QColor, QPaintEvent, QPainter, QPen
|
||||||
|
from PyQt5.QtCore import QRect, Qt
|
||||||
|
from PyQt5.QtWidgets import QProgressBar, QSizePolicy, QWidget
|
||||||
|
|
||||||
|
|
||||||
|
class NProgressCircle(QProgressBar):
|
||||||
|
"""Extension: Circular Progress Widget
|
||||||
|
|
||||||
|
A custom widget that paints a circular progress indicator instead of
|
||||||
|
a straight bar. It is also possible to set custom text for iṫ.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
"_text", "_point", "_dRect", "_cRect", "_dPen", "_dBrush",
|
||||||
|
"_cPen", "_bPen", "_tColor"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget, size: int, point: int):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
self._text = None
|
||||||
|
self._point = point
|
||||||
|
self._dRect = QRect(0, 0, size, size)
|
||||||
|
self._cRect = QRect(point, point, size - 2*point, size - 2*point)
|
||||||
|
self._dPen = QPen(Qt.transparent)
|
||||||
|
self._dBrush = QBrush(Qt.transparent)
|
||||||
|
self.setColours(
|
||||||
|
track=self.palette().alternateBase().color(),
|
||||||
|
bar=self.palette().highlight().color(),
|
||||||
|
text=self.palette().text().color()
|
||||||
|
)
|
||||||
|
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||||
|
self.setFixedWidth(size)
|
||||||
|
self.setFixedHeight(size)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setColours(
|
||||||
|
self, back: QColor | None = None, track: QColor | None = None,
|
||||||
|
bar: QColor | None = None, text: QColor | None = None
|
||||||
|
):
|
||||||
|
"""Set the colours of the widget."""
|
||||||
|
if isinstance(back, QColor):
|
||||||
|
self._dPen = QPen(back)
|
||||||
|
self._dBrush = QBrush(back)
|
||||||
|
if isinstance(bar, QColor):
|
||||||
|
self._cPen = QPen(QBrush(bar), self._point, Qt.SolidLine, Qt.RoundCap)
|
||||||
|
if isinstance(track, QColor):
|
||||||
|
self._bPen = QPen(QBrush(track), self._point, Qt.SolidLine, Qt.RoundCap)
|
||||||
|
if isinstance(text, QColor):
|
||||||
|
self._tColor = text
|
||||||
|
return
|
||||||
|
|
||||||
|
def setCentreText(self, text: str | None):
|
||||||
|
"""Replace the progress text with a custom string."""
|
||||||
|
self._text = text
|
||||||
|
self.setValue(self.value()) # Triggers a redraw
|
||||||
|
return
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent):
|
||||||
|
"""Custom painter for the progress bar."""
|
||||||
|
progress = 100.0*self.value()/self.maximum()
|
||||||
|
angle = ceil(16*3.6*progress)
|
||||||
|
qPaint = QPainter(self)
|
||||||
|
qPaint.setRenderHint(QPainter.Antialiasing, True)
|
||||||
|
qPaint.setPen(self._dPen)
|
||||||
|
qPaint.setBrush(self._dBrush)
|
||||||
|
qPaint.drawEllipse(self._dRect)
|
||||||
|
qPaint.setPen(self._bPen)
|
||||||
|
qPaint.drawArc(self._cRect, 0, 360*16)
|
||||||
|
qPaint.setPen(self._cPen)
|
||||||
|
qPaint.drawArc(self._cRect, 90*16, -angle)
|
||||||
|
qPaint.setPen(self._tColor)
|
||||||
|
qPaint.drawText(self._cRect, Qt.AlignCenter, self._text or f"{progress:.1f} %")
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NProgressCircle
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Custom Widget: Config Layout
|
||||||
|
==========================================
|
||||||
|
A custom grid layout for config pages
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2020-05-03 [0.4.5]
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QColor, QPalette
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QAbstractButton, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QSizePolicy,
|
||||||
|
QVBoxLayout, QWidget
|
||||||
|
)
|
||||||
|
|
||||||
|
from novelwriter import CONFIG
|
||||||
|
|
||||||
|
FONT_SCALE = 0.9
|
||||||
|
|
||||||
|
|
||||||
|
class NConfigLayout(QGridLayout):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._nextRow = 0
|
||||||
|
self._helpCol = QColor(0, 0, 0)
|
||||||
|
self._fontScale = FONT_SCALE
|
||||||
|
self._itemMap = {}
|
||||||
|
|
||||||
|
wSp = CONFIG.pxInt(8)
|
||||||
|
self.setHorizontalSpacing(wSp)
|
||||||
|
self.setVerticalSpacing(wSp)
|
||||||
|
self.setColumnStretch(0, 1)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Getters and Setters
|
||||||
|
##
|
||||||
|
|
||||||
|
def setHelpTextStyle(self, color: QColor | list | tuple, fontScale: float = FONT_SCALE):
|
||||||
|
"""Set the text color for the help text.
|
||||||
|
"""
|
||||||
|
if isinstance(color, QColor):
|
||||||
|
self._helpCol = color
|
||||||
|
else:
|
||||||
|
self._helpCol = QColor(*color)
|
||||||
|
self._fontScale = fontScale
|
||||||
|
return
|
||||||
|
|
||||||
|
def setHelpText(self, row: int, text: str):
|
||||||
|
"""Set the text for the help label.
|
||||||
|
"""
|
||||||
|
if row in self._itemMap:
|
||||||
|
qHelp = self._itemMap[row][1]
|
||||||
|
if isinstance(qHelp, NHelpLabel):
|
||||||
|
qHelp.setText(text)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setLabelText(self, row: int, text: str):
|
||||||
|
"""Set the text for the main label.
|
||||||
|
"""
|
||||||
|
if row in self._itemMap:
|
||||||
|
self._itemMap[row](0).setText(text)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Class Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def addGroupLabel(self, label: str):
|
||||||
|
"""Adds a text label to separate groups of settings.
|
||||||
|
"""
|
||||||
|
hM = CONFIG.pxInt(4)
|
||||||
|
qLabel = QLabel("<b>%s</b>" % label)
|
||||||
|
qLabel.setContentsMargins(0, hM, 0, hM)
|
||||||
|
self.addWidget(qLabel, self._nextRow, 0, 1, 2, Qt.AlignLeft)
|
||||||
|
self.setRowStretch(self._nextRow, 0)
|
||||||
|
self.setRowStretch(self._nextRow + 1, 1)
|
||||||
|
self._nextRow += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
def addRow(
|
||||||
|
self, label: str, widget: QWidget, helpText: str | None = None,
|
||||||
|
unit: str | None = None, button: QWidget | None = None
|
||||||
|
) -> int:
|
||||||
|
"""Add a label and a widget as a new row of the grid.
|
||||||
|
"""
|
||||||
|
if isinstance(widget, QWidget):
|
||||||
|
qWidget = widget
|
||||||
|
else:
|
||||||
|
qWidget = None
|
||||||
|
raise ValueError("The widget must be a QWidget")
|
||||||
|
|
||||||
|
wSp = CONFIG.pxInt(8)
|
||||||
|
qLabel = QLabel(label)
|
||||||
|
qLabel.setIndent(wSp)
|
||||||
|
|
||||||
|
qHelp = None
|
||||||
|
if helpText is not None:
|
||||||
|
qHelp = NHelpLabel(str(helpText), self._helpCol, self._fontScale)
|
||||||
|
qHelp.setIndent(wSp)
|
||||||
|
|
||||||
|
labelBox = QVBoxLayout()
|
||||||
|
labelBox.addWidget(qLabel)
|
||||||
|
labelBox.addWidget(qHelp)
|
||||||
|
labelBox.setSpacing(0)
|
||||||
|
labelBox.addStretch(1)
|
||||||
|
|
||||||
|
self.addLayout(labelBox, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.addWidget(qLabel, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
||||||
|
|
||||||
|
if isinstance(unit, str):
|
||||||
|
controlBox = QHBoxLayout()
|
||||||
|
controlBox.addWidget(qWidget, 0, Qt.AlignVCenter)
|
||||||
|
controlBox.addWidget(QLabel(unit), 0, Qt.AlignVCenter)
|
||||||
|
controlBox.setSpacing(wSp)
|
||||||
|
self.addLayout(controlBox, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||||
|
|
||||||
|
elif isinstance(button, QAbstractButton):
|
||||||
|
controlBox = QHBoxLayout()
|
||||||
|
controlBox.addWidget(qWidget, 0, Qt.AlignVCenter)
|
||||||
|
controlBox.addWidget(button, 0, Qt.AlignVCenter)
|
||||||
|
controlBox.setSpacing(wSp)
|
||||||
|
self.addLayout(controlBox, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if isinstance(widget, QLineEdit):
|
||||||
|
qLayout = QHBoxLayout()
|
||||||
|
qLayout.addWidget(widget)
|
||||||
|
self.addLayout(qLayout, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||||
|
else:
|
||||||
|
self.addWidget(qWidget, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||||
|
|
||||||
|
qLabel.setBuddy(qWidget)
|
||||||
|
|
||||||
|
self.setRowStretch(self._nextRow, 0)
|
||||||
|
self.setRowStretch(self._nextRow+1, 1)
|
||||||
|
|
||||||
|
self._itemMap[self._nextRow] = (qLabel, qHelp, qWidget)
|
||||||
|
self._nextRow += 1
|
||||||
|
|
||||||
|
return self._nextRow - 1
|
||||||
|
|
||||||
|
# END Class NConfigLayout
|
||||||
|
|
||||||
|
|
||||||
|
class NSimpleLayout(QGridLayout):
|
||||||
|
"""Similar to NConfigLayout, but only has a label + widget two
|
||||||
|
column layout.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self._nextRow = 0
|
||||||
|
|
||||||
|
wSp = CONFIG.pxInt(8)
|
||||||
|
self.setHorizontalSpacing(wSp)
|
||||||
|
self.setVerticalSpacing(wSp)
|
||||||
|
self.setColumnStretch(0, 1)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def addGroupLabel(self, label: str):
|
||||||
|
"""Adds a text label to separate groups of settings.
|
||||||
|
"""
|
||||||
|
hM = CONFIG.pxInt(4)
|
||||||
|
qLabel = QLabel("<b>%s</b>" % label)
|
||||||
|
qLabel.setContentsMargins(0, hM, 0, hM)
|
||||||
|
self.addWidget(qLabel, self._nextRow, 0, 1, 2, Qt.AlignLeft)
|
||||||
|
self.setRowStretch(self._nextRow, 0)
|
||||||
|
self.setRowStretch(self._nextRow + 1, 1)
|
||||||
|
self._nextRow += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
def addRow(self, label: str, widget: QWidget):
|
||||||
|
"""Add a label and a widget as a new row of the grid.
|
||||||
|
"""
|
||||||
|
if isinstance(widget, QWidget):
|
||||||
|
qWidget = widget
|
||||||
|
else:
|
||||||
|
qWidget = None
|
||||||
|
raise ValueError("The widget must be a QWidget")
|
||||||
|
|
||||||
|
wSp = CONFIG.pxInt(8)
|
||||||
|
qLabel = QLabel(label)
|
||||||
|
qLabel.setIndent(wSp)
|
||||||
|
self.addWidget(qLabel, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
||||||
|
|
||||||
|
if isinstance(widget, QLineEdit):
|
||||||
|
qLayout = QHBoxLayout()
|
||||||
|
qLayout.addWidget(widget)
|
||||||
|
self.addLayout(qLayout, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||||
|
else:
|
||||||
|
self.addWidget(qWidget, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||||
|
|
||||||
|
qLabel.setBuddy(qWidget)
|
||||||
|
|
||||||
|
self.setRowStretch(self._nextRow, 0)
|
||||||
|
self.setRowStretch(self._nextRow+1, 1)
|
||||||
|
self._nextRow += 1
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NSimpleLayout
|
||||||
|
|
||||||
|
|
||||||
|
class NHelpLabel(QLabel):
|
||||||
|
|
||||||
|
def __init__(self, text: str, color: QColor | list | tuple, fontSize: float = FONT_SCALE):
|
||||||
|
super().__init__(text)
|
||||||
|
|
||||||
|
if isinstance(color, QColor):
|
||||||
|
qCol = color
|
||||||
|
else:
|
||||||
|
qCol = QColor(*color)
|
||||||
|
|
||||||
|
lblCol = self.palette()
|
||||||
|
lblCol.setColor(QPalette.WindowText, qCol)
|
||||||
|
self.setPalette(lblCol)
|
||||||
|
|
||||||
|
lblFont = self.font()
|
||||||
|
lblFont.setPointSizeF(fontSize*lblFont.pointSizeF())
|
||||||
|
self.setFont(lblFont)
|
||||||
|
|
||||||
|
self.setWordWrap(True)
|
||||||
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NHelpLabel
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Custom Widget: Paged Dialog
|
||||||
|
=========================================
|
||||||
|
A custom dialog with tabs and a vertical tab bar
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2020-05-17 [0.5.1]
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QRect, QPoint
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar,
|
||||||
|
QTabWidget, QVBoxLayout
|
||||||
|
)
|
||||||
|
|
||||||
|
from novelwriter import CONFIG
|
||||||
|
|
||||||
|
|
||||||
|
class NPagedDialog(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
self._tabBar = NVerticalTabBar(self)
|
||||||
|
self._tabBar.setExpanding(False)
|
||||||
|
|
||||||
|
self._tabBox = QTabWidget(self)
|
||||||
|
self._tabBox.setTabBar(self._tabBar)
|
||||||
|
self._tabBox.setTabPosition(QTabWidget.West)
|
||||||
|
|
||||||
|
self._buttonBox = QHBoxLayout()
|
||||||
|
|
||||||
|
self._outerBox = QVBoxLayout()
|
||||||
|
self._outerBox.addWidget(self._tabBox)
|
||||||
|
self._outerBox.addLayout(self._buttonBox)
|
||||||
|
|
||||||
|
# Default Margins
|
||||||
|
thisStyle = self.style()
|
||||||
|
mL = thisStyle.pixelMetric(QStyle.PM_LayoutLeftMargin)
|
||||||
|
mR = thisStyle.pixelMetric(QStyle.PM_LayoutRightMargin)
|
||||||
|
mT = thisStyle.pixelMetric(QStyle.PM_LayoutLeftMargin)
|
||||||
|
mB = thisStyle.pixelMetric(QStyle.PM_LayoutBottomMargin)
|
||||||
|
|
||||||
|
# Set Margins
|
||||||
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self._outerBox.setContentsMargins(0, 0, 0, mB)
|
||||||
|
self._buttonBox.setContentsMargins(mL, 0, mR, 0)
|
||||||
|
self._outerBox.setSpacing(mT)
|
||||||
|
|
||||||
|
self.setLayout(self._outerBox)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def addTab(self, widget, label):
|
||||||
|
"""Forward the adding of tabs to the QTabWidget.
|
||||||
|
"""
|
||||||
|
self._tabBox.addTab(widget, label)
|
||||||
|
return
|
||||||
|
|
||||||
|
def addControls(self, buttonBar):
|
||||||
|
"""Add a button bar to the dialog.
|
||||||
|
"""
|
||||||
|
self._buttonBox.addWidget(buttonBar)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setCurrentWidget(self, widget):
|
||||||
|
"""Forward the changing of tab to the QTabWidget.
|
||||||
|
"""
|
||||||
|
self._tabBox.setCurrentWidget(widget)
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NPagedDialog
|
||||||
|
|
||||||
|
|
||||||
|
class NVerticalTabBar(QTabBar):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
self._mW = CONFIG.pxInt(150)
|
||||||
|
return
|
||||||
|
|
||||||
|
def tabSizeHint(self, index):
|
||||||
|
"""Return a transposed size hint for the rotated bar.
|
||||||
|
"""
|
||||||
|
tSize = super().tabSizeHint(index)
|
||||||
|
tSize.transpose()
|
||||||
|
tSize.setWidth(min(tSize.width(), self._mW))
|
||||||
|
return tSize
|
||||||
|
|
||||||
|
def paintEvent(self, event):
|
||||||
|
"""Custom implementation of the label painter that rotates the
|
||||||
|
label 90 degrees.
|
||||||
|
"""
|
||||||
|
pObj = QStylePainter(self)
|
||||||
|
oObj = QStyleOptionTab()
|
||||||
|
|
||||||
|
for i in range(self.count()):
|
||||||
|
self.initStyleOption(oObj, i)
|
||||||
|
pObj.drawControl(QStyle.CE_TabBarTabShape, oObj)
|
||||||
|
pObj.save()
|
||||||
|
|
||||||
|
oSize = oObj.rect.size()
|
||||||
|
oSize.transpose()
|
||||||
|
oRect = QRect(QPoint(), oSize)
|
||||||
|
oRect.moveCenter(oObj.rect.center())
|
||||||
|
oObj.rect = oRect
|
||||||
|
|
||||||
|
oCenter = self.tabRect(i).center()
|
||||||
|
pObj.translate(oCenter)
|
||||||
|
pObj.rotate(90)
|
||||||
|
pObj.translate(-oCenter)
|
||||||
|
pObj.drawControl(QStyle.CE_TabBarTabLabel, oObj)
|
||||||
|
pObj.restore()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NVerticalTabBar
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Custom Widget: Paged SideBar
|
||||||
|
==========================================
|
||||||
|
A custom widget for making a sidebar for flipping through pages
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2023-02-21 [2.1b1] NPagedSideBar
|
||||||
|
Created: 2023-02-21 [2.1b1] NPagedToolButton
|
||||||
|
Created: 2023-02-21 [2.1b1] NPagedToolLabel
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QColor, QPainter
|
||||||
|
from PyQt5.QtCore import QRectF, Qt, pyqtSignal, pyqtSlot
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QButtonGroup, QLabel, QSizePolicy, QStyle, QStyleOptionToolButton, QToolBar,
|
||||||
|
QToolButton, QWidget
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NPagedSideBar(QToolBar):
|
||||||
|
|
||||||
|
buttonClicked = pyqtSignal(int)
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
self._buttons = []
|
||||||
|
self._actions = []
|
||||||
|
self._labelCol = None
|
||||||
|
self._spacerHeight = self.fontMetrics().height() // 2
|
||||||
|
|
||||||
|
self._group = QButtonGroup(self)
|
||||||
|
self._group.setExclusive(True)
|
||||||
|
self._group.buttonClicked.connect(self._buttonClicked)
|
||||||
|
|
||||||
|
self.setMovable(False)
|
||||||
|
self.setOrientation(Qt.Vertical)
|
||||||
|
|
||||||
|
stretch = QWidget(self)
|
||||||
|
stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
self._stretchAction = self.addWidget(stretch)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def setLabelColor(self, color):
|
||||||
|
"""Set the text color for the labels.
|
||||||
|
"""
|
||||||
|
if isinstance(color, list):
|
||||||
|
self._labelCol = QColor(*color)
|
||||||
|
elif isinstance(color, QColor):
|
||||||
|
self._labelCol = color
|
||||||
|
return
|
||||||
|
|
||||||
|
def addSeparator(self):
|
||||||
|
"""Add a spacer widget.
|
||||||
|
"""
|
||||||
|
spacer = QWidget(self)
|
||||||
|
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||||
|
spacer.setFixedHeight(self._spacerHeight)
|
||||||
|
self.insertWidget(self._stretchAction, spacer)
|
||||||
|
return
|
||||||
|
|
||||||
|
def addLabel(self, text):
|
||||||
|
"""Add a new label to the toolbar.
|
||||||
|
"""
|
||||||
|
label = NPagedToolLabel(self, self._labelCol)
|
||||||
|
label.setText(text)
|
||||||
|
self.insertWidget(self._stretchAction, label)
|
||||||
|
return
|
||||||
|
|
||||||
|
def addButton(self, text, buttonId=-1):
|
||||||
|
"""Add a new button to the toolbar.
|
||||||
|
"""
|
||||||
|
button = NPagedToolButton(self)
|
||||||
|
button.setText(text)
|
||||||
|
|
||||||
|
action = self.insertWidget(self._stretchAction, button)
|
||||||
|
self._group.addButton(button, id=buttonId)
|
||||||
|
|
||||||
|
self._buttons.append(button)
|
||||||
|
self._actions.append(action)
|
||||||
|
|
||||||
|
return action
|
||||||
|
|
||||||
|
def setSelected(self, buttonId):
|
||||||
|
"""Set the selected button.
|
||||||
|
"""
|
||||||
|
self._group.button(buttonId).setChecked(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Private Slots
|
||||||
|
##
|
||||||
|
|
||||||
|
@pyqtSlot("QAbstractButton*")
|
||||||
|
def _buttonClicked(self, button):
|
||||||
|
"""A button was clicked in the group, emit its id.
|
||||||
|
"""
|
||||||
|
buttonId = self._group.id(button)
|
||||||
|
if buttonId != -1:
|
||||||
|
self.buttonClicked.emit(buttonId)
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NPagedSideBar
|
||||||
|
|
||||||
|
|
||||||
|
class NPagedToolButton(QToolButton):
|
||||||
|
|
||||||
|
__slots__ = ("_bH", "_tM", "_lM", "_cR")
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||||
|
self.setCheckable(True)
|
||||||
|
|
||||||
|
fH = self.fontMetrics().height()
|
||||||
|
self._bH = round(fH * 1.7)
|
||||||
|
self._tM = (self._bH - fH) // 2
|
||||||
|
self._lM = self.style().pixelMetric(QStyle.PM_ButtonMargin)
|
||||||
|
self._cR = self._lM // 2
|
||||||
|
self.setFixedHeight(self._bH)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def paintEvent(self, event):
|
||||||
|
"""Overload the paint event to draw a simple, left aligned text
|
||||||
|
label, with a highlight when selected and alternative base
|
||||||
|
colour when hovered.
|
||||||
|
"""
|
||||||
|
opt = QStyleOptionToolButton()
|
||||||
|
opt.initFrom(self)
|
||||||
|
|
||||||
|
paint = QPainter(self)
|
||||||
|
paint.setRenderHint(QPainter.Antialiasing, True)
|
||||||
|
paint.setPen(Qt.NoPen)
|
||||||
|
|
||||||
|
width = self.width()
|
||||||
|
height = self.height()
|
||||||
|
palette = self.palette()
|
||||||
|
|
||||||
|
if opt.state & QStyle.State_MouseOver:
|
||||||
|
backCol = palette.alternateBase()
|
||||||
|
paint.setBrush(backCol)
|
||||||
|
paint.setOpacity(0.5)
|
||||||
|
paint.drawRoundedRect(0, 0, width, height, self._cR, self._cR)
|
||||||
|
|
||||||
|
if self.isChecked():
|
||||||
|
backCol = palette.highlight()
|
||||||
|
paint.setBrush(backCol)
|
||||||
|
paint.setOpacity(0.5)
|
||||||
|
paint.drawRoundedRect(0, 0, width, height, self._cR, self._cR)
|
||||||
|
textCol = palette.highlightedText().color()
|
||||||
|
else:
|
||||||
|
textCol = palette.text().color()
|
||||||
|
|
||||||
|
tW = width - 2*self._lM
|
||||||
|
tH = height - 2*self._tM
|
||||||
|
|
||||||
|
paint.setPen(textCol)
|
||||||
|
paint.setOpacity(1.0)
|
||||||
|
paint.drawText(QRectF(self._lM, self._tM, tW, tH), Qt.AlignLeft, self.text())
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NPagedToolButton
|
||||||
|
|
||||||
|
|
||||||
|
class NPagedToolLabel(QLabel):
|
||||||
|
|
||||||
|
__slots__ = ("_bH", "_tM", "_lM", "_textCol")
|
||||||
|
|
||||||
|
def __init__(self, parent, textColor=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||||
|
|
||||||
|
fH = self.fontMetrics().height()
|
||||||
|
self._bH = round(fH * 1.7)
|
||||||
|
self._tM = (self._bH - fH) // 2
|
||||||
|
self._lM = self.style().pixelMetric(QStyle.PM_ButtonMargin)
|
||||||
|
self.setFixedHeight(self._bH)
|
||||||
|
|
||||||
|
self._textCol = textColor or self.palette().text().color()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def paintEvent(self, event):
|
||||||
|
"""Overload the paint event to draw a simple, left aligned text
|
||||||
|
label that matches the button style.
|
||||||
|
"""
|
||||||
|
paint = QPainter(self)
|
||||||
|
paint.setRenderHint(QPainter.Antialiasing, True)
|
||||||
|
paint.setPen(Qt.NoPen)
|
||||||
|
|
||||||
|
width = self.width()
|
||||||
|
height = self.height()
|
||||||
|
|
||||||
|
tW = width - 2*self._lM
|
||||||
|
tH = height - 2*self._tM
|
||||||
|
|
||||||
|
paint.setPen(self._textCol)
|
||||||
|
paint.setOpacity(1.0)
|
||||||
|
paint.drawText(QRectF(self._lM, self._tM, tW, tH), Qt.AlignLeft, self.text())
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NPagedToolLabel
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Custom Widget: Progress Simple
|
||||||
|
============================================
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2023-06-09 [2.1b1]
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QPaintEvent, QPainter
|
||||||
|
from PyQt5.QtWidgets import QProgressBar, QWidget
|
||||||
|
|
||||||
|
|
||||||
|
class NProgressSimple(QProgressBar):
|
||||||
|
"""Extension: Simple Progress Widget
|
||||||
|
|
||||||
|
A custom widget that paints a plain bar with no other styling.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
return
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent):
|
||||||
|
"""Custom painter for the progress bar."""
|
||||||
|
if self.value() == 0:
|
||||||
|
return
|
||||||
|
progress = ceil(self.width()*float(self.value())/self.maximum())
|
||||||
|
qPaint = QPainter(self)
|
||||||
|
qPaint.setRenderHint(QPainter.Antialiasing, True)
|
||||||
|
qPaint.setPen(self.palette().highlight().color())
|
||||||
|
qPaint.setBrush(self.palette().highlight())
|
||||||
|
qPaint.drawRect(0, 0, progress, self.height())
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NProgressSimple
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Custom Widget: Switch
|
||||||
|
===================================
|
||||||
|
A custom switch widget
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2020-05-03 [0.4.5]
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QPainter
|
||||||
|
from PyQt5.QtCore import Qt, QRectF, QPropertyAnimation, pyqtProperty
|
||||||
|
from PyQt5.QtWidgets import QSizePolicy, QAbstractButton
|
||||||
|
|
||||||
|
from novelwriter import CONFIG
|
||||||
|
from novelwriter.constants import nwUnicode
|
||||||
|
|
||||||
|
|
||||||
|
class NSwitch(QAbstractButton):
|
||||||
|
|
||||||
|
def __init__(self, parent=None, width=None, height=None):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
if width is None:
|
||||||
|
self._xW = CONFIG.pxInt(40)
|
||||||
|
else:
|
||||||
|
self._xW = width
|
||||||
|
|
||||||
|
if height is None:
|
||||||
|
self._xH = CONFIG.pxInt(20)
|
||||||
|
else:
|
||||||
|
self._xH = height
|
||||||
|
|
||||||
|
self._xR = int(self._xH*0.5)
|
||||||
|
self._xT = int(self._xH*0.6)
|
||||||
|
self._rB = int(CONFIG.guiScale*2)
|
||||||
|
self._rH = self._xH - 2*self._rB
|
||||||
|
self._rR = self._xR - self._rB
|
||||||
|
|
||||||
|
self.setCheckable(True)
|
||||||
|
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||||
|
self.setFixedWidth(self._xW)
|
||||||
|
self.setFixedHeight(self._xH)
|
||||||
|
self._offset = self._xR
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Properties
|
||||||
|
##
|
||||||
|
|
||||||
|
@pyqtProperty(int)
|
||||||
|
def offset(self):
|
||||||
|
return self._offset
|
||||||
|
|
||||||
|
@offset.setter
|
||||||
|
def offset(self, offset):
|
||||||
|
self._offset = offset
|
||||||
|
self.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Getters and Setters
|
||||||
|
##
|
||||||
|
|
||||||
|
def setChecked(self, checked):
|
||||||
|
"""Overload setChecked to also alter the offset.
|
||||||
|
"""
|
||||||
|
super().setChecked(checked)
|
||||||
|
if checked:
|
||||||
|
self.offset = self._xW - self._xR
|
||||||
|
else:
|
||||||
|
self.offset = self._xR
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Events
|
||||||
|
##
|
||||||
|
|
||||||
|
def resizeEvent(self, event):
|
||||||
|
"""Overload resize to ensure correct offset.
|
||||||
|
"""
|
||||||
|
super().resizeEvent(event)
|
||||||
|
if self.isChecked():
|
||||||
|
self.offset = self._xW - self._xR
|
||||||
|
else:
|
||||||
|
self.offset = self._xR
|
||||||
|
return
|
||||||
|
|
||||||
|
def paintEvent(self, event):
|
||||||
|
"""Drawing the switch itself.
|
||||||
|
"""
|
||||||
|
qPaint = QPainter(self)
|
||||||
|
qPaint.setRenderHint(QPainter.Antialiasing, True)
|
||||||
|
qPaint.setPen(Qt.NoPen)
|
||||||
|
|
||||||
|
qPalette = self.palette()
|
||||||
|
if self.isChecked():
|
||||||
|
trackBrush = qPalette.highlight()
|
||||||
|
thumbBrush = qPalette.highlightedText()
|
||||||
|
textColor = qPalette.highlight().color()
|
||||||
|
thumbText = nwUnicode.U_CHECK
|
||||||
|
else:
|
||||||
|
trackBrush = qPalette.dark()
|
||||||
|
thumbBrush = qPalette.light()
|
||||||
|
textColor = qPalette.dark().color()
|
||||||
|
thumbText = nwUnicode.U_CROSS
|
||||||
|
|
||||||
|
if self.isEnabled():
|
||||||
|
trackOpacity = 1.0
|
||||||
|
else:
|
||||||
|
trackOpacity = 0.6
|
||||||
|
trackBrush = qPalette.shadow()
|
||||||
|
thumbBrush = qPalette.mid()
|
||||||
|
textColor = qPalette.shadow().color()
|
||||||
|
|
||||||
|
qPaint.setBrush(trackBrush)
|
||||||
|
qPaint.setOpacity(trackOpacity)
|
||||||
|
qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
|
||||||
|
|
||||||
|
qPaint.setBrush(thumbBrush)
|
||||||
|
qPaint.drawEllipse(self.offset - self._rR, self._rB, self._rH, self._rH)
|
||||||
|
|
||||||
|
theFont = qPaint.font()
|
||||||
|
theFont.setPixelSize(self._xT)
|
||||||
|
qPaint.setPen(textColor)
|
||||||
|
qPaint.setFont(theFont)
|
||||||
|
qPaint.drawText(
|
||||||
|
QRectF(self.offset - self._rR, self._rB, self._rH, self._rH),
|
||||||
|
Qt.AlignCenter, thumbText
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, event):
|
||||||
|
"""Animate the switch on mouse release.
|
||||||
|
"""
|
||||||
|
super().mouseReleaseEvent(event)
|
||||||
|
if event.button() == Qt.LeftButton:
|
||||||
|
doAnim = QPropertyAnimation(self, b"offset", self)
|
||||||
|
doAnim.setDuration(120)
|
||||||
|
doAnim.setStartValue(self.offset)
|
||||||
|
if self.isChecked():
|
||||||
|
doAnim.setEndValue(self._xW - self._xR)
|
||||||
|
else:
|
||||||
|
doAnim.setEndValue(self._xR)
|
||||||
|
doAnim.start()
|
||||||
|
return
|
||||||
|
|
||||||
|
def enterEvent(self, event):
|
||||||
|
"""Change the cursor when hovering the button.
|
||||||
|
"""
|
||||||
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
|
super().enterEvent(event)
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NSwitch
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – Custom Widget: Switch Box
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2023-04-16 [2.1b1]
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from PyQt5.QtCore import Qt, pyqtSignal
|
||||||
|
from PyQt5.QtGui import QIcon
|
||||||
|
from PyQt5.QtWidgets import QGridLayout, QLabel, QScrollArea, QSizePolicy, QWidget
|
||||||
|
|
||||||
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
|
||||||
|
|
||||||
|
class NSwitchBox(QScrollArea):
|
||||||
|
"""Extension: Switch Box Widget
|
||||||
|
|
||||||
|
A widget that can hold a list of switches with labels and optional
|
||||||
|
icons. The switch toggles emits a common signal with a switch key.
|
||||||
|
"""
|
||||||
|
|
||||||
|
switchToggled = pyqtSignal(str, bool)
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget, baseSize: int):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
self._index = 0
|
||||||
|
self._hSwitch = baseSize
|
||||||
|
self._wSwitch = 2*self._hSwitch
|
||||||
|
self._sIcon = baseSize
|
||||||
|
self.clear()
|
||||||
|
return
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""Rebuild the content of the core widget."""
|
||||||
|
self._content = QGridLayout()
|
||||||
|
self._content.setColumnStretch(1, 1)
|
||||||
|
|
||||||
|
self._widget = QWidget()
|
||||||
|
self._widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
|
||||||
|
self._widget.setLayout(self._content)
|
||||||
|
|
||||||
|
self.setWidgetResizable(True)
|
||||||
|
self.setWidget(self._widget)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def addLabel(self, text: str):
|
||||||
|
"""Add a header label to the content box."""
|
||||||
|
label = QLabel(text)
|
||||||
|
font = label.font()
|
||||||
|
font.setBold(True)
|
||||||
|
label.setFont(font)
|
||||||
|
self._content.addWidget(label, self._index, 0, 1, 3, Qt.AlignLeft)
|
||||||
|
self._bumpIndex()
|
||||||
|
return
|
||||||
|
|
||||||
|
def addItem(self, qIcon: QIcon, text: str, identifier: str, default: bool = False):
|
||||||
|
"""Add an item to the content box."""
|
||||||
|
icon = QLabel("")
|
||||||
|
icon.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||||
|
icon.setPixmap(qIcon.pixmap(self._sIcon, self._sIcon))
|
||||||
|
self._content.addWidget(icon, self._index, 0, Qt.AlignLeft)
|
||||||
|
|
||||||
|
label = QLabel(text)
|
||||||
|
self._content.addWidget(label, self._index, 1, Qt.AlignLeft)
|
||||||
|
|
||||||
|
switch = NSwitch(width=self._wSwitch, height=self._hSwitch)
|
||||||
|
switch.setChecked(default)
|
||||||
|
switch.toggled.connect(lambda state: self._emitSwitchSignal(identifier, state))
|
||||||
|
self._content.addWidget(switch, self._index, 2, Qt.AlignRight)
|
||||||
|
|
||||||
|
self._bumpIndex()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def addSeparator(self):
|
||||||
|
"""Add a blank entry in the content box.
|
||||||
|
"""
|
||||||
|
spacer = QWidget()
|
||||||
|
spacer.setFixedHeight(int(0.5*self._sIcon))
|
||||||
|
self._content.addWidget(spacer, self._index, 0, 1, 3, Qt.AlignLeft)
|
||||||
|
self._bumpIndex()
|
||||||
|
return
|
||||||
|
|
||||||
|
def setInnerContentsMargins(self, left: int, top: int, right: int, bottom: int):
|
||||||
|
"""Set the contents margins of the inner layout."""
|
||||||
|
self._content.setContentsMargins(left, top, right, bottom)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _emitSwitchSignal(self, identifier: str, state: bool):
|
||||||
|
"""Emit a signal for a switch toggle."""
|
||||||
|
self.switchToggled.emit(identifier, state)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _bumpIndex(self):
|
||||||
|
"""Increase the index counter and make sure only the last
|
||||||
|
columns is stretcing.
|
||||||
|
"""
|
||||||
|
self._content.setRowStretch(self._index, 0)
|
||||||
|
self._content.setRowStretch(self._index + 1, 1)
|
||||||
|
self._index += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class NSwitchBox
|
||||||
@@ -78,7 +78,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocEditor ...")
|
logger.debug("Create: GuiDocEditor")
|
||||||
|
|
||||||
# Class Variables
|
# Class Variables
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -183,7 +183,7 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.updateSyntaxColours()
|
self.updateSyntaxColours()
|
||||||
self.initEditor()
|
self.initEditor()
|
||||||
|
|
||||||
logger.debug("GuiDocEditor initialisation complete")
|
logger.debug("Ready: GuiDocEditor")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -276,31 +276,16 @@ class GuiDocEditor(QTextEdit):
|
|||||||
self.setDictionaries()
|
self.setDictionaries()
|
||||||
|
|
||||||
# Set font
|
# Set font
|
||||||
theFont = QFont()
|
textFont = QFont()
|
||||||
qDoc = self.document()
|
textFont.setFamily(CONFIG.textFont)
|
||||||
if CONFIG.textFont is None:
|
textFont.setPointSize(CONFIG.textSize)
|
||||||
# If none is defined, set a default font
|
self.setFont(textFont)
|
||||||
theFont = QFont()
|
|
||||||
if CONFIG.osWindows and "Arial" in self.mainTheme.guiFontDB.families():
|
|
||||||
theFont.setFamily("Arial")
|
|
||||||
theFont.setPointSize(12)
|
|
||||||
elif CONFIG.osDarwin and "Courier" in self.mainTheme.guiFontDB.families():
|
|
||||||
theFont.setFamily("Courier")
|
|
||||||
theFont.setPointSize(12)
|
|
||||||
else:
|
|
||||||
theFont = qDoc.defaultFont()
|
|
||||||
|
|
||||||
CONFIG.textFont = theFont.family()
|
|
||||||
CONFIG.textSize = theFont.pointSize()
|
|
||||||
|
|
||||||
theFont.setFamily(CONFIG.textFont)
|
|
||||||
theFont.setPointSize(CONFIG.textSize)
|
|
||||||
self.setFont(theFont)
|
|
||||||
|
|
||||||
# Set default text margins
|
# Set default text margins
|
||||||
# Due to cursor visibility, a part of the margin must be
|
# Due to cursor visibility, a part of the margin must be
|
||||||
# allocated to the document itself. See issue #1112.
|
# allocated to the document itself. See issue #1112.
|
||||||
cW = self.cursorWidth()
|
cW = self.cursorWidth()
|
||||||
|
qDoc = self.document()
|
||||||
qDoc.setDocumentMargin(cW)
|
qDoc.setDocumentMargin(cW)
|
||||||
self._vpMargin = max(CONFIG.getTextMargin() - cW, 0)
|
self._vpMargin = max(CONFIG.getTextMargin() - cW, 0)
|
||||||
self.setViewportMargins(self._vpMargin, self._vpMargin, self._vpMargin, self._vpMargin)
|
self.setViewportMargins(self._vpMargin, self._vpMargin, self._vpMargin, self._vpMargin)
|
||||||
@@ -2242,7 +2227,7 @@ class GuiDocEditSearch(QFrame):
|
|||||||
def __init__(self, docEditor):
|
def __init__(self, docEditor):
|
||||||
super().__init__(parent=docEditor)
|
super().__init__(parent=docEditor)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocEditSearch ...")
|
logger.debug("Create: GuiDocEditSearch")
|
||||||
|
|
||||||
self.docEditor = docEditor
|
self.docEditor = docEditor
|
||||||
self.mainGui = docEditor.mainGui
|
self.mainGui = docEditor.mainGui
|
||||||
@@ -2386,7 +2371,7 @@ class GuiDocEditSearch(QFrame):
|
|||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiDocEditSearch initialisation complete")
|
logger.debug("Ready: GuiDocEditSearch")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -2657,7 +2642,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
def __init__(self, docEditor):
|
def __init__(self, docEditor):
|
||||||
super().__init__(parent=docEditor)
|
super().__init__(parent=docEditor)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocEditHeader ...")
|
logger.debug("Create: GuiDocEditHeader")
|
||||||
|
|
||||||
self.docEditor = docEditor
|
self.docEditor = docEditor
|
||||||
self.mainGui = docEditor.mainGui
|
self.mainGui = docEditor.mainGui
|
||||||
@@ -2742,7 +2727,7 @@ class GuiDocEditHeader(QWidget):
|
|||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiDocEditHeader initialisation complete")
|
logger.debug("Ready: GuiDocEditHeader")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -2892,7 +2877,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
def __init__(self, docEditor):
|
def __init__(self, docEditor):
|
||||||
super().__init__(parent=docEditor)
|
super().__init__(parent=docEditor)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocEditFooter ...")
|
logger.debug("Create: GuiDocEditFooter")
|
||||||
|
|
||||||
self.docEditor = docEditor
|
self.docEditor = docEditor
|
||||||
self.mainGui = docEditor.mainGui
|
self.mainGui = docEditor.mainGui
|
||||||
@@ -2986,7 +2971,7 @@ class GuiDocEditFooter(QWidget):
|
|||||||
self.updateLineCount()
|
self.updateLineCount()
|
||||||
self.updateCounts()
|
self.updateCounts()
|
||||||
|
|
||||||
logger.debug("GuiDocEditFooter initialisation complete")
|
logger.debug("Ready: GuiDocEditFooter")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|||||||
def __init__(self, theDoc, mainGui, spEnchant):
|
def __init__(self, theDoc, mainGui, spEnchant):
|
||||||
super().__init__(theDoc)
|
super().__init__(theDoc)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocHighlighter ...")
|
logger.debug("Create: GuiDocHighlighter")
|
||||||
|
|
||||||
self.theDoc = theDoc
|
self.theDoc = theDoc
|
||||||
self.spEnchant = spEnchant
|
self.spEnchant = spEnchant
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -76,7 +77,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|||||||
|
|
||||||
self.initHighlighter()
|
self.initHighlighter()
|
||||||
|
|
||||||
logger.debug("GuiDocHighlighter initialisation complete")
|
logger.debug("Ready: GuiDocHighlighter")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocViewer ...")
|
logger.debug("Create: GuiDocViewer")
|
||||||
|
|
||||||
# Class Variables
|
# Class Variables
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -88,7 +88,7 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
|
|
||||||
self.initViewer()
|
self.initViewer()
|
||||||
|
|
||||||
logger.debug("GuiDocViewer initialisation complete")
|
logger.debug("Ready: GuiDocViewer")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -114,13 +114,10 @@ class GuiDocViewer(QTextBrowser):
|
|||||||
self._makeStyleSheet()
|
self._makeStyleSheet()
|
||||||
|
|
||||||
# Set Font
|
# Set Font
|
||||||
theFont = QFont()
|
textFont = QFont()
|
||||||
if CONFIG.textFont is None:
|
textFont.setFamily(CONFIG.textFont)
|
||||||
# If none is defined, set the default back to config
|
textFont.setPointSize(CONFIG.textSize)
|
||||||
CONFIG.textFont = self.document().defaultFont().family()
|
self.setFont(textFont)
|
||||||
theFont.setFamily(CONFIG.textFont)
|
|
||||||
theFont.setPointSize(CONFIG.textSize)
|
|
||||||
self.setFont(theFont)
|
|
||||||
|
|
||||||
# Set the widget colours to match syntax theme
|
# Set the widget colours to match syntax theme
|
||||||
mainPalette = self.palette()
|
mainPalette = self.palette()
|
||||||
@@ -682,7 +679,7 @@ class GuiDocViewHeader(QWidget):
|
|||||||
def __init__(self, docViewer):
|
def __init__(self, docViewer):
|
||||||
super().__init__(parent=docViewer)
|
super().__init__(parent=docViewer)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocViewHeader ...")
|
logger.debug("Create: GuiDocViewHeader")
|
||||||
|
|
||||||
self.docViewer = docViewer
|
self.docViewer = docViewer
|
||||||
self.mainGui = docViewer.mainGui
|
self.mainGui = docViewer.mainGui
|
||||||
@@ -769,7 +766,7 @@ class GuiDocViewHeader(QWidget):
|
|||||||
# Fix the Colours
|
# Fix the Colours
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiDocViewHeader initialisation complete")
|
logger.debug("Ready: GuiDocViewHeader")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -899,7 +896,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
def __init__(self, docViewer):
|
def __init__(self, docViewer):
|
||||||
super().__init__(parent=docViewer)
|
super().__init__(parent=docViewer)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocViewFooter ...")
|
logger.debug("Create: GuiDocViewFooter")
|
||||||
|
|
||||||
self.docViewer = docViewer
|
self.docViewer = docViewer
|
||||||
self.mainGui = docViewer.mainGui
|
self.mainGui = docViewer.mainGui
|
||||||
@@ -1026,7 +1023,7 @@ class GuiDocViewFooter(QWidget):
|
|||||||
# Fix the Colours
|
# Fix the Colours
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiDocViewFooter initialisation complete")
|
logger.debug("Ready: GuiDocViewFooter")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1142,7 +1139,8 @@ class GuiDocViewDetails(QScrollArea):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiDocViewDetails ...")
|
logger.debug("Create: GuiDocViewDetails")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
self.theProject = mainGui.theProject
|
self.theProject = mainGui.theProject
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = mainGui.mainTheme
|
||||||
@@ -1171,7 +1169,7 @@ class GuiDocViewDetails(QScrollArea):
|
|||||||
self.setMinimumHeight(CONFIG.pxInt(50))
|
self.setMinimumHeight(CONFIG.pxInt(50))
|
||||||
self.setFrameStyle(QFrame.NoFrame)
|
self.setFrameStyle(QFrame.NoFrame)
|
||||||
|
|
||||||
logger.debug("GuiDocViewDetails initialisation complete")
|
logger.debug("Ready: GuiDocViewDetails")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ class GuiItemDetails(QWidget):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiItemDetails ...")
|
logger.debug("Create: GuiItemDetails")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
self.theProject = mainGui.theProject
|
self.theProject = mainGui.theProject
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = mainGui.mainTheme
|
||||||
@@ -182,7 +183,7 @@ class GuiItemDetails(QWidget):
|
|||||||
self.mainBox.setColumnMinimumWidth(1, flagWidth)
|
self.mainBox.setColumnMinimumWidth(1, flagWidth)
|
||||||
self.mainBox.setColumnMinimumWidth(4, countWidth)
|
self.mainBox.setColumnMinimumWidth(4, countWidth)
|
||||||
|
|
||||||
logger.debug("GuiItemDetails initialisation complete")
|
logger.debug("Ready: GuiItemDetails")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ class GuiMainMenu(QMenuBar):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiMainMenu ...")
|
logger.debug("Create: GuiMainMenu")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
self.theProject = mainGui.theProject
|
self.theProject = mainGui.theProject
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
self._docInsert = self.mainGui.docEditor.insertText
|
self._docInsert = self.mainGui.docEditor.insertText
|
||||||
self._insertKeyWord = self.mainGui.docEditor.insertKeyWord
|
self._insertKeyWord = self.mainGui.docEditor.insertKeyWord
|
||||||
|
|
||||||
logger.debug("GuiMainMenu initialisation complete")
|
logger.debug("Ready: GuiMainMenu")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -814,7 +815,7 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Tools > Separator
|
# Tools > Separator
|
||||||
self.toolsMenu.addSeparator()
|
self.toolsMenu.addSeparator()
|
||||||
|
|
||||||
# Tools > Rebuild Indices
|
# Tools > Rebuild Index
|
||||||
self.aRebuildIndex = QAction(self.tr("Rebuild Index"), self)
|
self.aRebuildIndex = QAction(self.tr("Rebuild Index"), self)
|
||||||
self.aRebuildIndex.setShortcut("F9")
|
self.aRebuildIndex.setShortcut("F9")
|
||||||
self.aRebuildIndex.triggered.connect(lambda: self.mainGui.rebuildIndex())
|
self.aRebuildIndex.triggered.connect(lambda: self.mainGui.rebuildIndex())
|
||||||
@@ -823,24 +824,24 @@ class GuiMainMenu(QMenuBar):
|
|||||||
# Tools > Separator
|
# Tools > Separator
|
||||||
self.toolsMenu.addSeparator()
|
self.toolsMenu.addSeparator()
|
||||||
|
|
||||||
# Tools > Backup
|
# Tools > Backup Project
|
||||||
self.aBackupProject = QAction(self.tr("Backup Project"), self)
|
self.aBackupProject = QAction(self.tr("Backup Project"), self)
|
||||||
self.aBackupProject.triggered.connect(lambda: self.theProject.backupProject(True))
|
self.aBackupProject.triggered.connect(lambda: self.theProject.backupProject(True))
|
||||||
self.toolsMenu.addAction(self.aBackupProject)
|
self.toolsMenu.addAction(self.aBackupProject)
|
||||||
|
|
||||||
# Tools > Export Project
|
# Tools > Build Manuscript
|
||||||
self.aBuildProject = QAction(self.tr("Build Novel Project"), self)
|
self.aBuildManuscript = QAction(self.tr("Build Manuscript"), self)
|
||||||
self.aBuildProject.setShortcut("F5")
|
self.aBuildManuscript.setShortcut("F5")
|
||||||
self.aBuildProject.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
|
self.aBuildManuscript.triggered.connect(self.mainGui.showBuildManuscriptDialog)
|
||||||
self.toolsMenu.addAction(self.aBuildProject)
|
self.toolsMenu.addAction(self.aBuildManuscript)
|
||||||
|
|
||||||
# Tools > Writing Stats
|
# Tools > Writing Statistics
|
||||||
self.aWritingStats = QAction(self.tr("Writing Statistics"), self)
|
self.aWritingStats = QAction(self.tr("Writing Statistics"), self)
|
||||||
self.aWritingStats.setShortcut("F6")
|
self.aWritingStats.setShortcut("F6")
|
||||||
self.aWritingStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
|
self.aWritingStats.triggered.connect(lambda: self.mainGui.showWritingStatsDialog())
|
||||||
self.toolsMenu.addAction(self.aWritingStats)
|
self.toolsMenu.addAction(self.aWritingStats)
|
||||||
|
|
||||||
# Tools > Settings
|
# Tools > Preferences
|
||||||
self.aPreferences = QAction(self.tr("Preferences"), self)
|
self.aPreferences = QAction(self.tr("Preferences"), self)
|
||||||
self.aPreferences.setShortcut("Ctrl+,")
|
self.aPreferences.setShortcut("Ctrl+,")
|
||||||
self.aPreferences.setMenuRole(QAction.PreferencesRole)
|
self.aPreferences.setMenuRole(QAction.PreferencesRole)
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
def __init__(self, novelView):
|
def __init__(self, novelView):
|
||||||
super().__init__(parent=novelView)
|
super().__init__(parent=novelView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiNovelToolBar ...")
|
logger.debug("Create: GuiNovelToolBar")
|
||||||
|
|
||||||
self.novelView = novelView
|
self.novelView = novelView
|
||||||
self.mainGui = novelView.mainGui
|
self.mainGui = novelView.mainGui
|
||||||
@@ -264,7 +264,7 @@ class GuiNovelToolBar(QWidget):
|
|||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiNovelToolBar initialisation complete")
|
logger.debug("Ready: GuiNovelToolBar")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -383,19 +383,21 @@ class GuiNovelToolBar(QWidget):
|
|||||||
|
|
||||||
class GuiNovelTree(QTreeWidget):
|
class GuiNovelTree(QTreeWidget):
|
||||||
|
|
||||||
|
C_DATA = 0
|
||||||
C_TITLE = 0
|
C_TITLE = 0
|
||||||
C_WORDS = 1
|
C_WORDS = 1
|
||||||
C_EXTRA = 2
|
C_EXTRA = 2
|
||||||
C_MORE = 3
|
C_MORE = 3
|
||||||
|
|
||||||
D_HANDLE = Qt.UserRole
|
D_HANDLE = Qt.ItemDataRole.UserRole
|
||||||
D_TITLE = Qt.UserRole + 1
|
D_TITLE = Qt.ItemDataRole.UserRole + 1
|
||||||
D_KEY = Qt.UserRole + 2
|
D_KEY = Qt.ItemDataRole.UserRole + 2
|
||||||
|
D_EXTRA = Qt.ItemDataRole.UserRole + 3
|
||||||
|
|
||||||
def __init__(self, novelView):
|
def __init__(self, novelView):
|
||||||
super().__init__(parent=novelView)
|
super().__init__(parent=novelView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiNovelTree ...")
|
logger.debug("Create: GuiNovelTree")
|
||||||
|
|
||||||
self.novelView = novelView
|
self.novelView = novelView
|
||||||
self.mainGui = novelView.mainGui
|
self.mainGui = novelView.mainGui
|
||||||
@@ -460,7 +462,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
self.initSettings()
|
self.initSettings()
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiNovelTree initialisation complete")
|
logger.debug("Ready: GuiNovelTree")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -527,7 +529,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
selItem = self.selectedItems()
|
selItem = self.selectedItems()
|
||||||
titleKey = None
|
titleKey = None
|
||||||
if selItem:
|
if selItem:
|
||||||
titleKey = selItem[0].data(self.C_TITLE, self.D_KEY)
|
titleKey = selItem[0].data(self.C_DATA, self.D_KEY)
|
||||||
|
|
||||||
self._populateTree(rootHandle)
|
self._populateTree(rootHandle)
|
||||||
self.theProject.data.setLastHandle(rootHandle, "novelTree")
|
self.theProject.data.setLastHandle(rootHandle, "novelTree")
|
||||||
@@ -564,8 +566,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
selList = self.selectedItems()
|
selList = self.selectedItems()
|
||||||
trItem = selList[0] if selList else self.currentItem()
|
trItem = selList[0] if selList else self.currentItem()
|
||||||
if isinstance(trItem, QTreeWidgetItem):
|
if isinstance(trItem, QTreeWidgetItem):
|
||||||
tHandle = trItem.data(self.C_TITLE, self.D_HANDLE)
|
tHandle = trItem.data(self.C_DATA, self.D_HANDLE)
|
||||||
sTitle = trItem.data(self.C_TITLE, self.D_TITLE)
|
sTitle = trItem.data(self.C_DATA, self.D_TITLE)
|
||||||
return tHandle, sTitle
|
return tHandle, sTitle
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
@@ -596,7 +598,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
for i in range(self.topLevelItemCount()):
|
for i in range(self.topLevelItemCount()):
|
||||||
tItem = self.topLevelItem(i)
|
tItem = self.topLevelItem(i)
|
||||||
if tItem is not None:
|
if tItem is not None:
|
||||||
if tItem.data(self.C_TITLE, self.D_HANDLE) == tHandle:
|
if tItem.data(self.C_DATA, self.D_HANDLE) == tHandle:
|
||||||
tItem.setBackground(self.C_TITLE, self.palette().alternateBase())
|
tItem.setBackground(self.C_TITLE, self.palette().alternateBase())
|
||||||
tItem.setBackground(self.C_WORDS, self.palette().alternateBase())
|
tItem.setBackground(self.C_WORDS, self.palette().alternateBase())
|
||||||
tItem.setBackground(self.C_EXTRA, self.palette().alternateBase())
|
tItem.setBackground(self.C_EXTRA, self.palette().alternateBase())
|
||||||
@@ -659,7 +661,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
for i in range(self.topLevelItemCount()):
|
for i in range(self.topLevelItemCount()):
|
||||||
trItem = self.topLevelItem(i)
|
trItem = self.topLevelItem(i)
|
||||||
if isinstance(trItem, QTreeWidgetItem):
|
if isinstance(trItem, QTreeWidgetItem):
|
||||||
lastText = trItem.data(self.C_EXTRA, Qt.UserRole)
|
lastText = trItem.data(self.C_DATA, self.D_EXTRA)
|
||||||
trItem.setText(self.C_EXTRA, fMetric.elidedText(lastText, Qt.ElideRight, eliW))
|
trItem.setText(self.C_EXTRA, fMetric.elidedText(lastText, Qt.ElideRight, eliW))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -672,8 +674,8 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
"""The user clicked on an item in the tree.
|
"""The user clicked on an item in the tree.
|
||||||
"""
|
"""
|
||||||
if mIndex.column() == self.C_MORE:
|
if mIndex.column() == self.C_MORE:
|
||||||
tHandle = mIndex.siblingAtColumn(self.C_TITLE).data(self.D_HANDLE)
|
tHandle = mIndex.siblingAtColumn(self.C_DATA).data(self.D_HANDLE)
|
||||||
sTitle = mIndex.siblingAtColumn(self.C_TITLE).data(self.D_TITLE)
|
sTitle = mIndex.siblingAtColumn(self.C_DATA).data(self.D_TITLE)
|
||||||
tipPos = self.mapToGlobal(self.visualRect(mIndex).topRight())
|
tipPos = self.mapToGlobal(self.visualRect(mIndex).topRight())
|
||||||
self._popMetaBox(tipPos, tHandle, sTitle)
|
self._popMetaBox(tipPos, tHandle, sTitle)
|
||||||
return
|
return
|
||||||
@@ -715,9 +717,9 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
newItem = QTreeWidgetItem()
|
newItem = QTreeWidgetItem()
|
||||||
newItem.setData(self.C_TITLE, self.D_HANDLE, tHandle)
|
newItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
|
||||||
newItem.setData(self.C_TITLE, self.D_TITLE, sTitle)
|
newItem.setData(self.C_DATA, self.D_TITLE, sTitle)
|
||||||
newItem.setData(self.C_TITLE, self.D_KEY, tKey)
|
newItem.setData(self.C_DATA, self.D_KEY, tKey)
|
||||||
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
|
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
|
||||||
|
|
||||||
self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle)
|
self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle)
|
||||||
@@ -748,7 +750,7 @@ class GuiNovelTree(QTreeWidget):
|
|||||||
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
|
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
|
||||||
elideText = self.fontMetrics().elidedText(lastText, Qt.ElideRight, mW)
|
elideText = self.fontMetrics().elidedText(lastText, Qt.ElideRight, mW)
|
||||||
trItem.setText(self.C_EXTRA, elideText)
|
trItem.setText(self.C_EXTRA, elideText)
|
||||||
trItem.setData(self.C_EXTRA, Qt.UserRole, lastText)
|
trItem.setData(self.C_DATA, self.D_EXTRA, lastText)
|
||||||
trItem.setToolTip(self.C_EXTRA, toolTip)
|
trItem.setToolTip(self.C_EXTRA, toolTip)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
def __init__(self, theOutline):
|
def __init__(self, theOutline):
|
||||||
super().__init__(parent=theOutline)
|
super().__init__(parent=theOutline)
|
||||||
|
|
||||||
logger.debug("Initialising GuiOutlineToolBar ...")
|
logger.debug("Create: GuiOutlineToolBar")
|
||||||
|
|
||||||
self.mainGui = theOutline.mainGui
|
self.mainGui = theOutline.mainGui
|
||||||
self.theProject = theOutline.mainGui.theProject
|
self.theProject = theOutline.mainGui.theProject
|
||||||
@@ -260,7 +260,7 @@ class GuiOutlineToolBar(QToolBar):
|
|||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiOutlineToolBar initialisation complete")
|
logger.debug("Ready: GuiOutlineToolBar")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -360,8 +360,8 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
nwOutline.SYNOP: False,
|
nwOutline.SYNOP: False,
|
||||||
}
|
}
|
||||||
|
|
||||||
D_HANDLE = Qt.UserRole
|
D_HANDLE = Qt.ItemDataRole.UserRole
|
||||||
D_TITLE = Qt.UserRole + 1
|
D_TITLE = Qt.ItemDataRole.UserRole + 1
|
||||||
|
|
||||||
hiddenStateChanged = pyqtSignal()
|
hiddenStateChanged = pyqtSignal()
|
||||||
activeItemChanged = pyqtSignal(str, str)
|
activeItemChanged = pyqtSignal(str, str)
|
||||||
@@ -369,7 +369,7 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
def __init__(self, outlineView):
|
def __init__(self, outlineView):
|
||||||
super().__init__(parent=outlineView)
|
super().__init__(parent=outlineView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiOutlineTree ...")
|
logger.debug("Create: GuiOutlineTree")
|
||||||
|
|
||||||
self.outlineView = outlineView
|
self.outlineView = outlineView
|
||||||
self.mainGui = outlineView.mainGui
|
self.mainGui = outlineView.mainGui
|
||||||
@@ -423,7 +423,7 @@ class GuiOutlineTree(QTreeWidget):
|
|||||||
|
|
||||||
self.hiddenStateChanged.emit()
|
self.hiddenStateChanged.emit()
|
||||||
|
|
||||||
logger.debug("GuiOutlineTree initialisation complete")
|
logger.debug("Ready: GuiOutlineTree")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -796,7 +796,7 @@ class GuiOutlineDetails(QScrollArea):
|
|||||||
def __init__(self, theOutline):
|
def __init__(self, theOutline):
|
||||||
super().__init__(parent=theOutline)
|
super().__init__(parent=theOutline)
|
||||||
|
|
||||||
logger.debug("Initialising GuiOutlineDetails ...")
|
logger.debug("Create: GuiOutlineDetails")
|
||||||
|
|
||||||
self.theOutline = theOutline
|
self.theOutline = theOutline
|
||||||
self.mainGui = theOutline.mainGui
|
self.mainGui = theOutline.mainGui
|
||||||
@@ -982,7 +982,7 @@ class GuiOutlineDetails(QScrollArea):
|
|||||||
|
|
||||||
self.initSettings()
|
self.initSettings()
|
||||||
|
|
||||||
logger.debug("GuiOutlineDetails initialisation complete")
|
logger.debug("Ready: GuiOutlineDetails")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ class GuiProjectToolBar(QWidget):
|
|||||||
def __init__(self, projView):
|
def __init__(self, projView):
|
||||||
super().__init__(parent=projView)
|
super().__init__(parent=projView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectToolBar ...")
|
logger.debug("Create: GuiProjectToolBar")
|
||||||
|
|
||||||
self.projView = projView
|
self.projView = projView
|
||||||
self.projTree = projView.projTree
|
self.projTree = projView.projTree
|
||||||
@@ -328,7 +328,7 @@ class GuiProjectToolBar(QWidget):
|
|||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiProjectToolBar initialisation complete")
|
logger.debug("Ready: GuiProjectToolBar")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -449,13 +449,13 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
C_ACTIVE = 2
|
C_ACTIVE = 2
|
||||||
C_STATUS = 3
|
C_STATUS = 3
|
||||||
|
|
||||||
D_HANDLE = Qt.UserRole
|
D_HANDLE = Qt.ItemDataRole.UserRole
|
||||||
D_WORDS = Qt.UserRole + 1
|
D_WORDS = Qt.ItemDataRole.UserRole + 1
|
||||||
|
|
||||||
def __init__(self, projView):
|
def __init__(self, projView):
|
||||||
super().__init__(parent=projView)
|
super().__init__(parent=projView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectTree ...")
|
logger.debug("Create: GuiProjectTree")
|
||||||
|
|
||||||
self.projView = projView
|
self.projView = projView
|
||||||
self.mainGui = projView.mainGui
|
self.mainGui = projView.mainGui
|
||||||
@@ -522,7 +522,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
# Set custom settings
|
# Set custom settings
|
||||||
self.initSettings()
|
self.initSettings()
|
||||||
|
|
||||||
logger.debug("GuiProjectTree initialisation complete")
|
logger.debug("Ready: GuiProjectTree")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1423,7 +1423,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
self.theProject.tree.updateItemData(mHandle)
|
self.theProject.tree.updateItemData(mHandle)
|
||||||
|
|
||||||
# Update the index
|
# Update the index
|
||||||
if nwItemS.isInactive():
|
if nwItemS.isInactiveClass():
|
||||||
self.theProject.index.deleteHandle(mHandle)
|
self.theProject.index.deleteHandle(mHandle)
|
||||||
else:
|
else:
|
||||||
self.theProject.index.reIndexHandle(mHandle)
|
self.theProject.index.reIndexHandle(mHandle)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – GUI Main Window SideBar
|
novelWriter – GUI Main Window SideBar
|
||||||
===========================================
|
=====================================
|
||||||
GUI class for the main window side bar
|
GUI class for the main window side bar
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
@@ -43,7 +43,7 @@ class GuiSideBar(QToolBar):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiSideBar ...")
|
logger.debug("Create: GuiSideBar")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = mainGui.mainTheme
|
||||||
@@ -82,8 +82,8 @@ class GuiSideBar(QToolBar):
|
|||||||
|
|
||||||
self.aBuild = QAction(self.tr("Build"), self)
|
self.aBuild = QAction(self.tr("Build"), self)
|
||||||
self.aBuild.setFont(lblFont)
|
self.aBuild.setFont(lblFont)
|
||||||
self.aBuild.setToolTip(self.tr("Build Novel Project"))
|
self.aBuild.setToolTip(self.tr("Build Manuscript"))
|
||||||
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildProjectDialog())
|
self.aBuild.triggered.connect(lambda: self.mainGui.showBuildManuscriptDialog())
|
||||||
|
|
||||||
self.aDetails = QAction(self.tr("Details"), self)
|
self.aDetails = QAction(self.tr("Details"), self)
|
||||||
self.aDetails.setFont(lblFont)
|
self.aDetails.setFont(lblFont)
|
||||||
@@ -122,7 +122,7 @@ class GuiSideBar(QToolBar):
|
|||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
|
|
||||||
logger.debug("GuiSideBar initialisation complete")
|
logger.debug("Ready: GuiSideBar")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class GuiMainStatus(QStatusBar):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiMainStatus ...")
|
logger.debug("Create: GuiMainStatus")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
self.mainTheme = mainGui.mainTheme
|
self.mainTheme = mainGui.mainTheme
|
||||||
@@ -108,7 +108,7 @@ class GuiMainStatus(QStatusBar):
|
|||||||
# Other Settings
|
# Other Settings
|
||||||
self.setSizeGripEnabled(True)
|
self.setSizeGripEnabled(True)
|
||||||
|
|
||||||
logger.debug("GuiMainStatus initialisation complete")
|
logger.debug("Ready: GuiMainStatus")
|
||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
self.clearStatus()
|
self.clearStatus()
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ class GuiTheme:
|
|||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
# Init GUI Font
|
# Init GUI Font
|
||||||
self.guiFontDB = QFontDatabase()
|
|
||||||
self._setGuiFont()
|
self._setGuiFont()
|
||||||
|
|
||||||
# Load Themes
|
# Load Themes
|
||||||
@@ -366,13 +365,14 @@ class GuiTheme:
|
|||||||
"""Update the GUI's font style from settings.
|
"""Update the GUI's font style from settings.
|
||||||
"""
|
"""
|
||||||
theFont = QFont()
|
theFont = QFont()
|
||||||
if CONFIG.guiFont not in self.guiFontDB.families():
|
fontDB = QFontDatabase()
|
||||||
if CONFIG.osWindows and "Arial" in self.guiFontDB.families():
|
if CONFIG.guiFont not in fontDB.families():
|
||||||
|
if CONFIG.osWindows and "Arial" in fontDB.families():
|
||||||
# On Windows we default to Arial if possible
|
# On Windows we default to Arial if possible
|
||||||
theFont.setFamily("Arial")
|
theFont.setFamily("Arial")
|
||||||
theFont.setPointSize(10)
|
theFont.setPointSize(10)
|
||||||
else:
|
else:
|
||||||
theFont = self.guiFontDB.systemFont(QFontDatabase.GeneralFont)
|
theFont = fontDB.systemFont(QFontDatabase.GeneralFont)
|
||||||
CONFIG.guiFont = theFont.family()
|
CONFIG.guiFont = theFont.family()
|
||||||
CONFIG.guiFontSize = theFont.pointSize()
|
CONFIG.guiFontSize = theFont.pointSize()
|
||||||
else:
|
else:
|
||||||
@@ -443,19 +443,20 @@ class GuiIcons:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
ICON_KEYS = {
|
ICON_KEYS = {
|
||||||
# Project and GUI icons
|
# Project and GUI Icons
|
||||||
"novelwriter", "cls_archive", "cls_character", "cls_custom", "cls_entity", "cls_none",
|
"novelwriter", "build_excluded", "build_filtered", "build_included", "cls_archive",
|
||||||
"cls_novel", "cls_object", "cls_plot", "cls_timeline", "cls_trash", "cls_world",
|
"cls_character", "cls_custom", "cls_entity", "cls_none", "cls_novel", "cls_object",
|
||||||
"proj_chapter", "proj_details", "proj_document", "proj_folder", "proj_note", "proj_nwx",
|
"cls_plot", "cls_timeline", "cls_trash", "cls_world", "proj_chapter", "proj_details",
|
||||||
"proj_section", "proj_scene", "proj_stats", "proj_title", "search_cancel", "search_case",
|
"proj_document", "proj_folder", "proj_note", "proj_nwx", "proj_section", "proj_scene",
|
||||||
"search_loop", "search_preserve", "search_project", "search_regex", "search_word",
|
"proj_stats", "proj_title", "search_cancel", "search_case", "search_loop",
|
||||||
"status_idle", "status_lang", "status_lines", "status_stats", "status_time", "view_build",
|
"search_preserve", "search_project", "search_regex", "search_word", "status_idle",
|
||||||
"view_editor", "view_novel", "view_outline",
|
"status_lang", "status_lines", "status_stats", "status_time", "view_build", "view_editor",
|
||||||
|
"view_novel", "view_outline",
|
||||||
|
|
||||||
# General Button Icons
|
# General Button Icons
|
||||||
"add", "backward", "bookmark", "checked", "close", "cross", "down", "edit", "forward",
|
"add", "backward", "bookmark", "browse", "checked", "close", "cross", "down", "edit",
|
||||||
"maximise", "menu", "minimise", "noncheckable", "reference", "refresh", "remove",
|
"export", "forward", "maximise", "menu", "minimise", "noncheckable", "reference",
|
||||||
"search_replace", "search", "settings", "unchecked", "up",
|
"refresh", "remove", "revert", "search_replace", "search", "settings", "unchecked", "up",
|
||||||
|
|
||||||
# Switches
|
# Switches
|
||||||
"sticky-on", "sticky-off",
|
"sticky-on", "sticky-off",
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
"""
|
"""
|
||||||
novelWriter – GUI Main Window
|
novelWriter – GUI Main Window
|
||||||
=============================
|
=============================
|
||||||
The main application window
|
|
||||||
|
|
||||||
File History:
|
File History:
|
||||||
Created: 2018-09-22 [0.0.1]
|
Created: 2018-09-22 [0.0.1] GuiMain
|
||||||
|
|
||||||
This file is a part of novelWriter
|
This file is a part of novelWriter
|
||||||
Copyright 2018–2023, Veronica Berglyd Olsen
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
@@ -56,8 +55,8 @@ from novelwriter.dialogs.wordlist import GuiWordList
|
|||||||
from novelwriter.dialogs.preferences import GuiPreferences
|
from novelwriter.dialogs.preferences import GuiPreferences
|
||||||
from novelwriter.dialogs.projdetails import GuiProjectDetails
|
from novelwriter.dialogs.projdetails import GuiProjectDetails
|
||||||
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
||||||
from novelwriter.tools.build import GuiBuildNovel
|
|
||||||
from novelwriter.tools.lipsum import GuiLipsum
|
from novelwriter.tools.lipsum import GuiLipsum
|
||||||
|
from novelwriter.tools.manuscript import GuiManuscript
|
||||||
from novelwriter.tools.projwizard import GuiProjectWizard
|
from novelwriter.tools.projwizard import GuiProjectWizard
|
||||||
from novelwriter.tools.writingstats import GuiWritingStats
|
from novelwriter.tools.writingstats import GuiWritingStats
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
@@ -73,11 +72,29 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class GuiMain(QMainWindow):
|
class GuiMain(QMainWindow):
|
||||||
|
"""Main GUI Window
|
||||||
|
|
||||||
|
The Main GUI window class. It is the entry point of the
|
||||||
|
application, and holds all runtime objects aside from the main
|
||||||
|
Config instance, which is created before the Main GUI.
|
||||||
|
|
||||||
|
The Main GUI is split up into GUI components, assembled in the init
|
||||||
|
function. Also, the project instance and theme instance are created
|
||||||
|
here. These should be passed around to all other objects who need
|
||||||
|
them and new instances of them should generally not be created.
|
||||||
|
|
||||||
|
* All other GUI classes that depend on any components from the
|
||||||
|
main GUI should be passed a reference to the instance of this
|
||||||
|
class.
|
||||||
|
* All non-GUI classes can be passed a reference to the NWProject
|
||||||
|
instance if the Main GUI is not needed (which it generally
|
||||||
|
shouldn't need).
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
logger.debug("Initialising GUI ...")
|
logger.debug("Create: GUI")
|
||||||
self.setObjectName("GuiMain")
|
self.setObjectName("GuiMain")
|
||||||
self.threadPool = QThreadPool()
|
self.threadPool = QThreadPool()
|
||||||
|
|
||||||
@@ -301,7 +318,7 @@ class GuiMain(QMainWindow):
|
|||||||
if CONFIG.isFullScreen:
|
if CONFIG.isFullScreen:
|
||||||
self.toggleFullScreenMode()
|
self.toggleFullScreenMode()
|
||||||
|
|
||||||
logger.debug("GUI initialisation complete")
|
logger.debug("Ready: GUI")
|
||||||
|
|
||||||
if __hexversion__[-2] == "a" and logger.getEffectiveLevel() > logging.DEBUG:
|
if __hexversion__[-2] == "a" and logger.getEffectiveLevel() > logging.DEBUG:
|
||||||
self.makeAlert(self.tr(
|
self.makeAlert(self.tr(
|
||||||
@@ -992,23 +1009,25 @@ class GuiMain(QMainWindow):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def showBuildProjectDialog(self):
|
@pyqtSlot()
|
||||||
"""Open the build project dialog.
|
def showBuildManuscriptDialog(self):
|
||||||
|
"""Open the build manuscript dialog.
|
||||||
"""
|
"""
|
||||||
if not self.hasProject:
|
if not self.hasProject:
|
||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
dlgBuild = getGuiItem("GuiBuildNovel")
|
dlgBuild = getGuiItem("GuiManuscript")
|
||||||
if dlgBuild is None:
|
if dlgBuild is None:
|
||||||
dlgBuild = GuiBuildNovel(self)
|
dlgBuild = GuiManuscript(self)
|
||||||
assert isinstance(dlgBuild, GuiBuildNovel)
|
assert isinstance(dlgBuild, GuiManuscript)
|
||||||
|
|
||||||
dlgBuild.setModal(False)
|
dlgBuild.setModal(False)
|
||||||
dlgBuild.show()
|
dlgBuild.show()
|
||||||
dlgBuild.raise_()
|
dlgBuild.raise_()
|
||||||
qApp.processEvents()
|
qApp.processEvents()
|
||||||
dlgBuild.viewCachedDoc()
|
|
||||||
|
dlgBuild.loadContent()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.common import readTextFile
|
from novelwriter.common import readTextFile
|
||||||
from novelwriter.custom import QSwitch
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ class GuiLipsum(QDialog):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiLipsum ...")
|
logger.debug("Create: GuiLipsum")
|
||||||
self.setObjectName("GuiLipsum")
|
self.setObjectName("GuiLipsum")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -77,7 +77,7 @@ class GuiLipsum(QDialog):
|
|||||||
self.paraCount.setValue(5)
|
self.paraCount.setValue(5)
|
||||||
|
|
||||||
self.randLabel = QLabel(self.tr("Randomise order"))
|
self.randLabel = QLabel(self.tr("Randomise order"))
|
||||||
self.randSwitch = QSwitch()
|
self.randSwitch = NSwitch()
|
||||||
|
|
||||||
self.formBox = QGridLayout()
|
self.formBox = QGridLayout()
|
||||||
self.formBox.addWidget(self.headLabel, 0, 0, 1, 2, Qt.AlignLeft)
|
self.formBox.addWidget(self.headLabel, 0, 0, 1, 2, Qt.AlignLeft)
|
||||||
@@ -107,10 +107,14 @@ class GuiLipsum(QDialog):
|
|||||||
self.outerBox.setSpacing(CONFIG.pxInt(16))
|
self.outerBox.setSpacing(CONFIG.pxInt(16))
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
logger.debug("GuiLipsum initialisation complete")
|
logger.debug("Ready: GuiLipsum")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiLipsum")
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Slots
|
# Slots
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -0,0 +1,410 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – GUI Build Manuscript
|
||||||
|
==================================
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2023-05-24 [2.1b1] GuiManuscriptBuild
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QSize, QTimer, Qt, pyqtSlot
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QAbstractButton, QAbstractItemView, QDialog, QDialogButtonBox, QFileDialog,
|
||||||
|
QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem,
|
||||||
|
QPushButton, QSplitter, QVBoxLayout, QWidget
|
||||||
|
)
|
||||||
|
|
||||||
|
from novelwriter import CONFIG
|
||||||
|
from novelwriter.enum import nwAlert, nwBuildFmt
|
||||||
|
from novelwriter.common import makeFileNameSafe
|
||||||
|
from novelwriter.constants import nwLabels
|
||||||
|
from novelwriter.core.item import NWItem
|
||||||
|
from novelwriter.core.docbuild import NWBuildDocument
|
||||||
|
from novelwriter.core.buildsettings import BuildSettings
|
||||||
|
from novelwriter.extensions.simpleprogress import NProgressSimple
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class GuiManuscriptBuild(QDialog):
|
||||||
|
"""GUI Tools: Manuscript Build Dialog
|
||||||
|
|
||||||
|
This is the tool for running the build itself. It can be accessed
|
||||||
|
independently of the Manuscript Build Tool.
|
||||||
|
"""
|
||||||
|
|
||||||
|
D_KEY = Qt.ItemDataRole.UserRole
|
||||||
|
|
||||||
|
def __init__(self, parent: QWidget, mainGui: GuiMain, build: BuildSettings):
|
||||||
|
super().__init__(parent=parent)
|
||||||
|
|
||||||
|
logger.debug("Create: GuiManuscriptBuild")
|
||||||
|
self.setObjectName("GuiManuscriptBuild")
|
||||||
|
|
||||||
|
self.mainGui = mainGui
|
||||||
|
self.mainTheme = mainGui.mainTheme
|
||||||
|
self.theProject = mainGui.theProject
|
||||||
|
|
||||||
|
self._parent = parent
|
||||||
|
self._build = build
|
||||||
|
|
||||||
|
self.setWindowTitle(self.tr("Build Manuscript"))
|
||||||
|
self.setMinimumWidth(CONFIG.pxInt(500))
|
||||||
|
self.setMinimumHeight(CONFIG.pxInt(300))
|
||||||
|
|
||||||
|
iPx = self.mainTheme.baseIconSize
|
||||||
|
sp4 = CONFIG.pxInt(4)
|
||||||
|
sp8 = CONFIG.pxInt(8)
|
||||||
|
sp16 = CONFIG.pxInt(16)
|
||||||
|
wWin = CONFIG.pxInt(620)
|
||||||
|
hWin = CONFIG.pxInt(360)
|
||||||
|
|
||||||
|
pOptions = self.theProject.options
|
||||||
|
self.resize(
|
||||||
|
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winWidth", wWin)),
|
||||||
|
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "winHeight", hWin))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Output Format
|
||||||
|
# =============
|
||||||
|
|
||||||
|
self.lblFormat = QLabel(self.tr("Output Format"))
|
||||||
|
self.listFormats = QListWidget()
|
||||||
|
self.listFormats.setIconSize(QSize(iPx, iPx))
|
||||||
|
current = None
|
||||||
|
for key in nwBuildFmt:
|
||||||
|
item = QListWidgetItem()
|
||||||
|
item.setText(nwLabels.BUILD_FMT[key])
|
||||||
|
item.setData(self.D_KEY, key)
|
||||||
|
self.listFormats.addItem(item)
|
||||||
|
if key == self._build.lastFormat:
|
||||||
|
current = item
|
||||||
|
if current:
|
||||||
|
self.listFormats.setCurrentItem(current)
|
||||||
|
|
||||||
|
self.formatBox = QVBoxLayout()
|
||||||
|
self.formatBox.addWidget(self.lblFormat, 0)
|
||||||
|
self.formatBox.addWidget(self.listFormats, 1)
|
||||||
|
self.formatBox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
self.formatWidget = QWidget()
|
||||||
|
self.formatWidget.setLayout(self.formatBox)
|
||||||
|
self.formatWidget.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
# =================
|
||||||
|
|
||||||
|
self.lblContent = QLabel(self.tr("Table of Contents"))
|
||||||
|
|
||||||
|
self.listContent = QListWidget(self)
|
||||||
|
self.listContent.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.listContent.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
|
||||||
|
|
||||||
|
self.contentBox = QVBoxLayout()
|
||||||
|
self.contentBox.addWidget(self.lblContent, 0)
|
||||||
|
self.contentBox.addWidget(self.listContent, 0)
|
||||||
|
self.contentBox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
self.contentWidget = QWidget()
|
||||||
|
self.contentWidget.setLayout(self.contentBox)
|
||||||
|
self.contentWidget.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# Dialog Controls
|
||||||
|
# ===============
|
||||||
|
|
||||||
|
font = self.font()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setUnderline(True)
|
||||||
|
font.setPointSizeF(1.5*font.pointSizeF())
|
||||||
|
|
||||||
|
self.lblMain = QLabel(self._build.name)
|
||||||
|
self.lblMain.setWordWrap(True)
|
||||||
|
self.lblMain.setFont(font)
|
||||||
|
|
||||||
|
# Build Path
|
||||||
|
self.lblPath = QLabel(self.tr("Path"))
|
||||||
|
self.buildPath = QLineEdit(self)
|
||||||
|
self.btnBrowse = QPushButton(self.mainTheme.getIcon("browse"), "")
|
||||||
|
|
||||||
|
self.pathBox = QHBoxLayout()
|
||||||
|
self.pathBox.addWidget(self.buildPath)
|
||||||
|
self.pathBox.addWidget(self.btnBrowse)
|
||||||
|
self.pathBox.setSpacing(sp8)
|
||||||
|
|
||||||
|
# Build Name
|
||||||
|
self.lblName = QLabel(self.tr("File Name"))
|
||||||
|
self.buildName = QLineEdit(self)
|
||||||
|
self.btnReset = QPushButton(self.mainTheme.getIcon("revert"), "")
|
||||||
|
self.btnReset.setToolTip(self.tr("Reset file name to default"))
|
||||||
|
|
||||||
|
self.nameBox = QHBoxLayout()
|
||||||
|
self.nameBox.addWidget(self.buildName)
|
||||||
|
self.nameBox.addWidget(self.btnReset)
|
||||||
|
self.nameBox.setSpacing(sp8)
|
||||||
|
|
||||||
|
# Build Progress
|
||||||
|
self.buildProgress = NProgressSimple(self)
|
||||||
|
self.buildProgress.setMinimum(0)
|
||||||
|
self.buildProgress.setValue(0)
|
||||||
|
self.buildProgress.setTextVisible(False)
|
||||||
|
self.buildProgress.setFixedHeight(sp8)
|
||||||
|
|
||||||
|
# Build Box
|
||||||
|
self.buildBox = QGridLayout()
|
||||||
|
self.buildBox.addWidget(self.lblPath, 0, 0)
|
||||||
|
self.buildBox.addLayout(self.pathBox, 0, 1)
|
||||||
|
self.buildBox.addWidget(self.lblName, 1, 0)
|
||||||
|
self.buildBox.addLayout(self.nameBox, 1, 1)
|
||||||
|
self.buildBox.setHorizontalSpacing(sp8)
|
||||||
|
self.buildBox.setVerticalSpacing(sp4)
|
||||||
|
|
||||||
|
# Dialog Buttons
|
||||||
|
self.btnBuild = QPushButton(self.mainTheme.getIcon("export"), self.tr("&Build"))
|
||||||
|
self.dlgButtons = QDialogButtonBox(QDialogButtonBox.Close)
|
||||||
|
self.dlgButtons.addButton(self.btnBuild, QDialogButtonBox.ActionRole)
|
||||||
|
|
||||||
|
# Assemble GUI
|
||||||
|
# ============
|
||||||
|
|
||||||
|
self.mainSplit = QSplitter()
|
||||||
|
self.mainSplit.addWidget(self.formatWidget)
|
||||||
|
self.mainSplit.addWidget(self.contentWidget)
|
||||||
|
self.mainSplit.setHandleWidth(sp16)
|
||||||
|
self.mainSplit.setCollapsible(0, False)
|
||||||
|
self.mainSplit.setCollapsible(1, False)
|
||||||
|
self.mainSplit.setSizes([
|
||||||
|
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "fmtWidth", wWin//2)),
|
||||||
|
CONFIG.pxInt(pOptions.getInt("GuiManuscriptBuild", "sumWidth", wWin//2)),
|
||||||
|
])
|
||||||
|
|
||||||
|
self.outerBox = QVBoxLayout()
|
||||||
|
self.outerBox.addWidget(self.lblMain, 0, Qt.AlignCenter)
|
||||||
|
self.outerBox.addSpacing(sp16)
|
||||||
|
self.outerBox.addWidget(self.mainSplit, 1)
|
||||||
|
self.outerBox.addSpacing(sp4)
|
||||||
|
self.outerBox.addWidget(self.buildProgress, 0)
|
||||||
|
self.outerBox.addSpacing(sp4)
|
||||||
|
self.outerBox.addLayout(self.buildBox, 0)
|
||||||
|
self.outerBox.addSpacing(sp16)
|
||||||
|
self.outerBox.addWidget(self.dlgButtons, 0)
|
||||||
|
self.outerBox.setSpacing(0)
|
||||||
|
|
||||||
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
|
self.btnBuild.setFocus()
|
||||||
|
self._populateContentList()
|
||||||
|
self.buildPath.setText(str(self._build.lastPath))
|
||||||
|
if self._build.lastBuildName:
|
||||||
|
self.buildName.setText(self._build.lastBuildName)
|
||||||
|
else:
|
||||||
|
self._doResetBuildName()
|
||||||
|
|
||||||
|
# Signals
|
||||||
|
self.btnReset.clicked.connect(self._doResetBuildName)
|
||||||
|
self.btnBrowse.clicked.connect(self._doSelectPath)
|
||||||
|
self.dlgButtons.clicked.connect(self._dialogButtonClicked)
|
||||||
|
self.listFormats.itemSelectionChanged.connect(self._formatSelectionChanged)
|
||||||
|
|
||||||
|
logger.debug("Ready: GuiManuscriptBuild")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""For debug use only."""
|
||||||
|
logger.debug("Delete: GuiManuscriptBuild")
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Events
|
||||||
|
##
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""Capture the user closing the window so we can save GUI
|
||||||
|
settings.
|
||||||
|
"""
|
||||||
|
self._saveSettings()
|
||||||
|
event.accept()
|
||||||
|
self.deleteLater()
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Private Slots
|
||||||
|
##
|
||||||
|
|
||||||
|
@pyqtSlot(str, bool)
|
||||||
|
def _applyBuildOptions(self, key: str, state: bool):
|
||||||
|
"""Set the build options for the build."""
|
||||||
|
self._build.setValue(key, state)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot("QAbstractButton*")
|
||||||
|
def _dialogButtonClicked(self, button: QAbstractButton):
|
||||||
|
"""Handle button clicks from the dialog button box."""
|
||||||
|
role = self.dlgButtons.buttonRole(button)
|
||||||
|
if role == QDialogButtonBox.ActionRole:
|
||||||
|
self._runBuild()
|
||||||
|
elif role == QDialogButtonBox.RejectRole:
|
||||||
|
self.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _doSelectPath(self):
|
||||||
|
"""Select a folder for output."""
|
||||||
|
bPath = Path(self.buildPath.text())
|
||||||
|
bPath = bPath if bPath.is_dir() else self._build.lastPath
|
||||||
|
savePath = QFileDialog.getExistingDirectory(
|
||||||
|
self, self.tr("Select Folder"), str(bPath)
|
||||||
|
)
|
||||||
|
if savePath:
|
||||||
|
self.buildPath.setText(savePath)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _doResetBuildName(self):
|
||||||
|
"""Generate a default build name."""
|
||||||
|
bName = f"{self.theProject.data.name} - {self._build.name}"
|
||||||
|
self.buildName.setText(bName)
|
||||||
|
self._build.setLastBuildName(bName)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _formatSelectionChanged(self):
|
||||||
|
"""The user selected a different format, so we reset the
|
||||||
|
progress bar.
|
||||||
|
"""
|
||||||
|
self.buildProgress.setValue(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _resetProgress(self):
|
||||||
|
"""Set the progress bar back to 0."""
|
||||||
|
self.buildProgress.setValue(0)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _runBuild(self) -> bool:
|
||||||
|
"""Run the currently selected build."""
|
||||||
|
bFormat = self._getSelectedFormat()
|
||||||
|
if not isinstance(bFormat, nwBuildFmt):
|
||||||
|
return False
|
||||||
|
|
||||||
|
bName = self.buildName.text().strip()
|
||||||
|
if not bName:
|
||||||
|
self._doResetBuildName()
|
||||||
|
bName = self.buildName.text().strip()
|
||||||
|
|
||||||
|
self.buildProgress.setValue(0)
|
||||||
|
bPath = Path(self.buildPath.text())
|
||||||
|
if not bPath.is_dir():
|
||||||
|
self.mainGui.makeAlert(self.tr("Output folder does not exist."), nwAlert.ERROR)
|
||||||
|
return False
|
||||||
|
|
||||||
|
bExt = nwLabels.BUILD_EXT[bFormat]
|
||||||
|
buildPath = (bPath / makeFileNameSafe(bName)).with_suffix(bExt)
|
||||||
|
|
||||||
|
if buildPath.exists():
|
||||||
|
if not self.mainGui.askQuestion(
|
||||||
|
self.tr("File Exists"),
|
||||||
|
self.tr("The file already exists. Do you want to overwrite it?")
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
docBuild = NWBuildDocument(self.theProject, self._build)
|
||||||
|
docBuild.queueAll()
|
||||||
|
|
||||||
|
self.buildProgress.setMaximum(len(docBuild))
|
||||||
|
for i, _ in docBuild.iterBuild(buildPath, bFormat):
|
||||||
|
self.buildProgress.setValue(i+1)
|
||||||
|
|
||||||
|
self._build.setLastPath(bPath)
|
||||||
|
self._build.setLastBuildName(bName)
|
||||||
|
self._build.setLastFormat(bFormat)
|
||||||
|
|
||||||
|
QTimer.singleShot(1000, self._resetProgress)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _getSelectedFormat(self) -> nwBuildFmt | None:
|
||||||
|
"""Get the currently selected format."""
|
||||||
|
items = self.listFormats.selectedItems()
|
||||||
|
if items and isinstance(items[0], QListWidgetItem):
|
||||||
|
return items[0].data(self.D_KEY)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _saveSettings(self):
|
||||||
|
"""Save the user GUI settings."""
|
||||||
|
logger.debug("Saving GuiManuscriptBuild settings")
|
||||||
|
|
||||||
|
winWidth = CONFIG.rpxInt(self.width())
|
||||||
|
winHeight = CONFIG.rpxInt(self.height())
|
||||||
|
|
||||||
|
mainSplit = self.mainSplit.sizes()
|
||||||
|
fmtWidth = CONFIG.rpxInt(mainSplit[0])
|
||||||
|
sumWidth = CONFIG.rpxInt(mainSplit[1])
|
||||||
|
|
||||||
|
pOptions = self.theProject.options
|
||||||
|
pOptions.setValue("GuiManuscriptBuild", "winWidth", winWidth)
|
||||||
|
pOptions.setValue("GuiManuscriptBuild", "winHeight", winHeight)
|
||||||
|
pOptions.setValue("GuiManuscriptBuild", "fmtWidth", fmtWidth)
|
||||||
|
pOptions.setValue("GuiManuscriptBuild", "sumWidth", sumWidth)
|
||||||
|
pOptions.saveSettings()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _populateContentList(self):
|
||||||
|
"""Build the content list."""
|
||||||
|
rootMap = {}
|
||||||
|
filtered = self._build.buildItemFilter(self.theProject)
|
||||||
|
self.listContent.clear()
|
||||||
|
for nwItem in self.theProject.tree:
|
||||||
|
tHandle = nwItem.itemHandle
|
||||||
|
rHandle = nwItem.itemRoot
|
||||||
|
|
||||||
|
if tHandle is None or rHandle is None or not nwItem.isFileType():
|
||||||
|
continue
|
||||||
|
|
||||||
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
|
if rHandle not in rootMap:
|
||||||
|
rItem = self.theProject.tree[rHandle]
|
||||||
|
if isinstance(rItem, NWItem):
|
||||||
|
rootMap[rHandle] = rItem.itemName
|
||||||
|
|
||||||
|
itemIcon = self.mainTheme.getItemIcon(
|
||||||
|
nwItem.itemType, nwItem.itemClass,
|
||||||
|
nwItem.itemLayout, nwItem.mainHeading
|
||||||
|
)
|
||||||
|
rootName = rootMap.get(rHandle, "??????")
|
||||||
|
item = QListWidgetItem(f"{rootName}: {nwItem.itemName}")
|
||||||
|
item.setIcon(itemIcon)
|
||||||
|
self.listContent.addItem(item)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class GuiManuscriptBuild
|
||||||
@@ -0,0 +1,665 @@
|
|||||||
|
"""
|
||||||
|
novelWriter – GUI Manuscript Tool
|
||||||
|
=================================
|
||||||
|
|
||||||
|
File History:
|
||||||
|
Created: 2023-05-13 [2.1b1] GuiManuscript
|
||||||
|
|
||||||
|
This file is a part of novelWriter
|
||||||
|
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||||
|
|
||||||
|
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 3 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from PyQt5.QtGui import QColor, QCursor, QFont, QPalette, QResizeEvent
|
||||||
|
from PyQt5.QtCore import QSize, QTimer, Qt, pyqtSlot
|
||||||
|
from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
|
||||||
|
from PyQt5.QtWidgets import (
|
||||||
|
QDialog, QGridLayout, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton,
|
||||||
|
QSplitter, QTextBrowser, QToolButton, QVBoxLayout, QWidget, qApp
|
||||||
|
)
|
||||||
|
|
||||||
|
from novelwriter import CONFIG
|
||||||
|
from novelwriter.error import logException
|
||||||
|
from novelwriter.common import checkInt, fuzzyTime
|
||||||
|
from novelwriter.core.tohtml import ToHtml
|
||||||
|
from novelwriter.core.docbuild import NWBuildDocument
|
||||||
|
from novelwriter.core.buildsettings import BuildCollection, BuildSettings
|
||||||
|
from novelwriter.tools.manusbuild import GuiManuscriptBuild
|
||||||
|
from novelwriter.tools.manussettings import GuiBuildSettings
|
||||||
|
from novelwriter.extensions.circularprogress import NProgressCircle
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.guimain import GuiMain
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class GuiManuscript(QDialog):
|
||||||
|
"""GUI Tools: Manuscript Tool
|
||||||
|
|
||||||
|
The dialog displays all the users build definitions, a preview panel
|
||||||
|
for the manuscript, and can trigger the actual build dialog to build
|
||||||
|
a document directly to disk.
|
||||||
|
"""
|
||||||
|
|
||||||
|
D_KEY = Qt.ItemDataRole.UserRole
|
||||||
|
|
||||||
|
def __init__(self, mainGui: GuiMain):
|
||||||
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
|
logger.debug("Create: GuiManuscript")
|
||||||
|
self.setObjectName("GuiManuscript")
|
||||||
|
|
||||||
|
self.mainGui = mainGui
|
||||||
|
self.mainTheme = mainGui.mainTheme
|
||||||
|
self.theProject = mainGui.theProject
|
||||||
|
|
||||||
|
self._builds = BuildCollection(self.theProject)
|
||||||
|
self._buildMap: dict[str, QListWidgetItem] = {}
|
||||||
|
|
||||||
|
self.setWindowTitle(self.tr("Build Manuscript"))
|
||||||
|
self.setMinimumWidth(CONFIG.pxInt(600))
|
||||||
|
self.setMinimumHeight(CONFIG.pxInt(500))
|
||||||
|
|
||||||
|
iPx = self.mainTheme.baseIconSize
|
||||||
|
wWin = CONFIG.pxInt(900)
|
||||||
|
hWin = CONFIG.pxInt(600)
|
||||||
|
|
||||||
|
pOptions = self.theProject.options
|
||||||
|
self.resize(
|
||||||
|
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winWidth", wWin)),
|
||||||
|
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "winHeight", hWin))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build Controls
|
||||||
|
# ==============
|
||||||
|
|
||||||
|
qPalette = self.palette()
|
||||||
|
qPalette.setBrush(QPalette.Window, qPalette.base())
|
||||||
|
self.setPalette(qPalette)
|
||||||
|
|
||||||
|
fadeCol = qPalette.text().color()
|
||||||
|
buttonStyle = (
|
||||||
|
"QToolButton {{padding: {0}px; border: none; background: transparent;}} "
|
||||||
|
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
|
||||||
|
).format(CONFIG.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
||||||
|
|
||||||
|
self.tbAdd = QToolButton(self)
|
||||||
|
self.tbAdd.setIcon(self.mainTheme.getIcon("add"))
|
||||||
|
self.tbAdd.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.tbAdd.setToolTip(self.tr("Add New Build"))
|
||||||
|
self.tbAdd.setStyleSheet(buttonStyle)
|
||||||
|
self.tbAdd.clicked.connect(self._createNewBuild)
|
||||||
|
|
||||||
|
self.tbDel = QToolButton(self)
|
||||||
|
self.tbDel.setIcon(self.mainTheme.getIcon("remove"))
|
||||||
|
self.tbDel.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.tbDel.setToolTip(self.tr("Delete Selected Build"))
|
||||||
|
self.tbDel.setStyleSheet(buttonStyle)
|
||||||
|
self.tbDel.clicked.connect(self._deleteSelectedBuild)
|
||||||
|
|
||||||
|
self.tbEdit = QToolButton(self)
|
||||||
|
self.tbEdit.setIcon(self.mainTheme.getIcon("edit"))
|
||||||
|
self.tbEdit.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.tbEdit.setToolTip(self.tr("Edit Selected Build"))
|
||||||
|
self.tbEdit.setStyleSheet(buttonStyle)
|
||||||
|
self.tbEdit.clicked.connect(self._editSelectedBuild)
|
||||||
|
|
||||||
|
self.lblBuilds = QLabel("<b>{0}</b>".format(self.tr("Builds")))
|
||||||
|
|
||||||
|
self.buildList = QListWidget()
|
||||||
|
self.buildList.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.buildList.doubleClicked.connect(self._editSelectedBuild)
|
||||||
|
|
||||||
|
self.listToolBox = QHBoxLayout()
|
||||||
|
self.listToolBox.addWidget(self.lblBuilds)
|
||||||
|
self.listToolBox.addStretch(1)
|
||||||
|
self.listToolBox.addWidget(self.tbAdd)
|
||||||
|
self.listToolBox.addWidget(self.tbDel)
|
||||||
|
self.listToolBox.addWidget(self.tbEdit)
|
||||||
|
self.listToolBox.setSpacing(0)
|
||||||
|
|
||||||
|
# Process Controls
|
||||||
|
# ================
|
||||||
|
|
||||||
|
self.btnPreview = QPushButton(self.tr("Preview"))
|
||||||
|
self.btnPreview.clicked.connect(self._generatePreview)
|
||||||
|
|
||||||
|
self.btnPrint = QPushButton(self.tr("Print"))
|
||||||
|
self.btnPrint.clicked.connect(self._printDocument)
|
||||||
|
|
||||||
|
self.btnBuild = QPushButton(self.tr("Build"))
|
||||||
|
self.btnBuild.clicked.connect(self._buildManuscript)
|
||||||
|
|
||||||
|
self.btnClose = QPushButton(self.tr("Close"))
|
||||||
|
self.btnClose.clicked.connect(self._doClose)
|
||||||
|
|
||||||
|
self.processBox = QGridLayout()
|
||||||
|
self.processBox.addWidget(self.btnPreview, 0, 0)
|
||||||
|
self.processBox.addWidget(self.btnPrint, 0, 1)
|
||||||
|
self.processBox.addWidget(self.btnBuild, 1, 0)
|
||||||
|
self.processBox.addWidget(self.btnClose, 1, 1)
|
||||||
|
|
||||||
|
# Assemble GUI
|
||||||
|
# ============
|
||||||
|
|
||||||
|
self.docPreview = _PreviewWidget(self.mainGui)
|
||||||
|
|
||||||
|
self.controlBox = QVBoxLayout()
|
||||||
|
self.controlBox.addLayout(self.listToolBox, 0)
|
||||||
|
self.controlBox.addWidget(self.buildList, 1)
|
||||||
|
self.controlBox.addLayout(self.processBox, 0)
|
||||||
|
self.controlBox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
self.optsWidget = QWidget()
|
||||||
|
self.optsWidget.setLayout(self.controlBox)
|
||||||
|
|
||||||
|
self.mainSplit = QSplitter()
|
||||||
|
self.mainSplit.addWidget(self.optsWidget)
|
||||||
|
self.mainSplit.addWidget(self.docPreview)
|
||||||
|
self.mainSplit.setStretchFactor(0, 0)
|
||||||
|
self.mainSplit.setStretchFactor(1, 1)
|
||||||
|
self.mainSplit.setSizes([
|
||||||
|
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "optsWidth", wWin//4)),
|
||||||
|
CONFIG.pxInt(pOptions.getInt("GuiManuscript", "viewWidth", 3*wWin//4)),
|
||||||
|
])
|
||||||
|
|
||||||
|
self.outerBox = QVBoxLayout()
|
||||||
|
self.outerBox.addWidget(self.mainSplit)
|
||||||
|
|
||||||
|
self.setLayout(self.outerBox)
|
||||||
|
self.setSizeGripEnabled(True)
|
||||||
|
|
||||||
|
logger.debug("Ready: GuiManuscript")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""For debug use only."""
|
||||||
|
logger.debug("Delete: GuiManuscript")
|
||||||
|
return
|
||||||
|
|
||||||
|
def loadContent(self):
|
||||||
|
"""Load dialog content from project data."""
|
||||||
|
if len(self._builds) == 0:
|
||||||
|
build = BuildSettings()
|
||||||
|
build.setName(self.tr("My Manuscript"))
|
||||||
|
self._builds.setBuild(build)
|
||||||
|
|
||||||
|
self._updateBuildsList()
|
||||||
|
|
||||||
|
logger.debug("Loading build cache")
|
||||||
|
cache = CONFIG.dataPath("cache") / f"build_{self.theProject.data.uuid}.json"
|
||||||
|
if cache.is_file():
|
||||||
|
try:
|
||||||
|
with open(cache, mode="r", encoding="utf-8") as fObj:
|
||||||
|
data = json.load(fObj)
|
||||||
|
build = self._builds.getBuild(data.get("uuid", ""))
|
||||||
|
if isinstance(build, BuildSettings):
|
||||||
|
self._updatePreview(data, build)
|
||||||
|
except Exception:
|
||||||
|
logger.error("Failed to save build cache")
|
||||||
|
logException()
|
||||||
|
return
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Events
|
||||||
|
##
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""Capture the user closing the window so we can save GUI
|
||||||
|
settings. We also check that we don't have a build settings
|
||||||
|
diralog open.
|
||||||
|
"""
|
||||||
|
self._saveSettings()
|
||||||
|
for obj in self.children():
|
||||||
|
# Make sure we don't have any settings windows open
|
||||||
|
if isinstance(obj, GuiBuildSettings) and obj.isVisible():
|
||||||
|
obj.close()
|
||||||
|
event.accept()
|
||||||
|
self.deleteLater()
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Private Slots
|
||||||
|
##
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _createNewBuild(self):
|
||||||
|
"""Open the build settings dialog for a new build."""
|
||||||
|
build = BuildSettings()
|
||||||
|
build.setName(self.tr("My Manuscript"))
|
||||||
|
self._openSettingsDialog(build)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _editSelectedBuild(self):
|
||||||
|
"""Edit the currently selected build settings entry."""
|
||||||
|
build = self._getSelectedBuild()
|
||||||
|
if build is not None:
|
||||||
|
self._openSettingsDialog(build)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _deleteSelectedBuild(self):
|
||||||
|
"""Delete the currently selected build settings entry."""
|
||||||
|
build = self._getSelectedBuild()
|
||||||
|
if build is not None:
|
||||||
|
proceed = self.mainGui.askQuestion(
|
||||||
|
self.tr("Delete Build"),
|
||||||
|
self.tr("Delete build '{0}'?".format(build.name))
|
||||||
|
)
|
||||||
|
if proceed:
|
||||||
|
self._builds.removeBuild(build.buildID)
|
||||||
|
self._updateBuildsList()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot(BuildSettings)
|
||||||
|
def _processNewSettings(self, build: BuildSettings):
|
||||||
|
"""Process new build settings from the settings dialog."""
|
||||||
|
self._builds.setBuild(build)
|
||||||
|
self._updateBuildItem(build)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _generatePreview(self):
|
||||||
|
"""Run the document builder on the current build settings for
|
||||||
|
the preview widget.
|
||||||
|
"""
|
||||||
|
build = self._getSelectedBuild()
|
||||||
|
if build is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
docBuild = NWBuildDocument(self.theProject, build)
|
||||||
|
docBuild.queueAll()
|
||||||
|
|
||||||
|
self.docPreview.beginNewBuild(len(docBuild))
|
||||||
|
for step, _ in docBuild.iterBuildHTML(None):
|
||||||
|
self.docPreview.buildStep(step + 1)
|
||||||
|
qApp.processEvents()
|
||||||
|
qApp.thread().msleep(5)
|
||||||
|
|
||||||
|
buildObj = docBuild.lastBuild
|
||||||
|
assert isinstance(buildObj, ToHtml)
|
||||||
|
result = {
|
||||||
|
"uuid": build.buildID,
|
||||||
|
"time": int(time()),
|
||||||
|
"styles": buildObj.getStyleSheet(),
|
||||||
|
"html": buildObj.fullHTML,
|
||||||
|
}
|
||||||
|
|
||||||
|
self._updatePreview(result, build)
|
||||||
|
|
||||||
|
logger.debug("Saving build cache")
|
||||||
|
cache = CONFIG.dataPath("cache") / f"build_{self.theProject.data.uuid}.json"
|
||||||
|
try:
|
||||||
|
with open(cache, mode="w+", encoding="utf-8") as outFile:
|
||||||
|
outFile.write(json.dumps(result, indent=2))
|
||||||
|
except Exception:
|
||||||
|
logger.error("Failed to save build cache")
|
||||||
|
logException()
|
||||||
|
return
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _buildManuscript(self):
|
||||||
|
"""Open the build dialog and build the manuscript."""
|
||||||
|
build = self._getSelectedBuild()
|
||||||
|
if build is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
dlgBuild = GuiManuscriptBuild(self, self.mainGui, build)
|
||||||
|
dlgBuild.exec_()
|
||||||
|
|
||||||
|
# After the build is done, save build settings changes
|
||||||
|
if build.changed:
|
||||||
|
self._builds.setBuild(build)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _printDocument(self):
|
||||||
|
"""Open the print preview dialog."""
|
||||||
|
thePreview = QPrintPreviewDialog(self)
|
||||||
|
thePreview.paintRequested.connect(self.docPreview.printPreview)
|
||||||
|
thePreview.exec_()
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _doClose(self):
|
||||||
|
"""Forward the close button to the default close method."""
|
||||||
|
self.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _updatePreview(self, data: dict, build: BuildSettings):
|
||||||
|
"""Update the preview widget and set relevant values."""
|
||||||
|
self.docPreview.setContent(data)
|
||||||
|
self.docPreview.setBuildName(build.name)
|
||||||
|
self.docPreview.setTextFont(
|
||||||
|
build.getStr("format.textFont"),
|
||||||
|
build.getInt("format.textSize")
|
||||||
|
)
|
||||||
|
self.docPreview.setJustify(
|
||||||
|
build.getBool("format.justifyText")
|
||||||
|
)
|
||||||
|
if build.buildID and build.buildID in self._buildMap:
|
||||||
|
self._buildMap[build.buildID].setSelected(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _getSelectedBuild(self) -> BuildSettings | None:
|
||||||
|
"""Get the currently selected build."""
|
||||||
|
bItems = self.buildList.selectedItems()
|
||||||
|
if bItems:
|
||||||
|
build = self._builds.getBuild(bItems[0].data(self.D_KEY))
|
||||||
|
if isinstance(build, BuildSettings):
|
||||||
|
return build
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _saveSettings(self):
|
||||||
|
"""Save the user GUI settings."""
|
||||||
|
logger.debug("Saving GuiManuscript settings")
|
||||||
|
|
||||||
|
winWidth = CONFIG.rpxInt(self.width())
|
||||||
|
winHeight = CONFIG.rpxInt(self.height())
|
||||||
|
|
||||||
|
mainSplit = self.mainSplit.sizes()
|
||||||
|
optsWidth = CONFIG.rpxInt(mainSplit[0])
|
||||||
|
viewWidth = CONFIG.rpxInt(mainSplit[1])
|
||||||
|
|
||||||
|
pOptions = self.theProject.options
|
||||||
|
pOptions.setValue("GuiManuscript", "winWidth", winWidth)
|
||||||
|
pOptions.setValue("GuiManuscript", "winHeight", winHeight)
|
||||||
|
pOptions.setValue("GuiManuscript", "optsWidth", optsWidth)
|
||||||
|
pOptions.setValue("GuiManuscript", "viewWidth", viewWidth)
|
||||||
|
pOptions.saveSettings()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _openSettingsDialog(self, build: BuildSettings):
|
||||||
|
"""Open the build settings dialog."""
|
||||||
|
dlgSettings = GuiBuildSettings(self, self.mainGui, build)
|
||||||
|
dlgSettings.setModal(False)
|
||||||
|
dlgSettings.show()
|
||||||
|
dlgSettings.raise_()
|
||||||
|
qApp.processEvents()
|
||||||
|
dlgSettings.loadContent()
|
||||||
|
dlgSettings.newSettingsReady.connect(self._processNewSettings)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _updateBuildsList(self):
|
||||||
|
"""Update the list of available builds."""
|
||||||
|
self.buildList.clear()
|
||||||
|
for key, name in self._builds.builds():
|
||||||
|
bItem = QListWidgetItem()
|
||||||
|
bItem.setText(name)
|
||||||
|
bItem.setIcon(self.mainTheme.getIcon("export"))
|
||||||
|
bItem.setData(self.D_KEY, key)
|
||||||
|
self.buildList.addItem(bItem)
|
||||||
|
self._buildMap[key] = bItem
|
||||||
|
return
|
||||||
|
|
||||||
|
def _updateBuildItem(self, build: BuildSettings):
|
||||||
|
"""Update the entry of a specific build item."""
|
||||||
|
bItem = self._buildMap.get(build.buildID, None)
|
||||||
|
if isinstance(bItem, QListWidgetItem):
|
||||||
|
bItem.setText(build.name)
|
||||||
|
else: # Propbably a new item
|
||||||
|
self._updateBuildsList()
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class GuiManuscript
|
||||||
|
|
||||||
|
|
||||||
|
class _PreviewWidget(QTextBrowser):
|
||||||
|
|
||||||
|
def __init__(self, mainGui: GuiMain):
|
||||||
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
|
self.mainGui = mainGui
|
||||||
|
self.mainTheme = mainGui.mainTheme
|
||||||
|
self.theProject = mainGui.theProject
|
||||||
|
|
||||||
|
self._docTime = 0
|
||||||
|
self._buildName = ""
|
||||||
|
|
||||||
|
# Document Setup
|
||||||
|
dPalette = self.palette()
|
||||||
|
dPalette.setColor(QPalette.Base, QColor(255, 255, 255))
|
||||||
|
dPalette.setColor(QPalette.Text, QColor(0, 0, 0))
|
||||||
|
self.setPalette(dPalette)
|
||||||
|
|
||||||
|
self.setMinimumWidth(40*self.mainGui.mainTheme.textNWidth)
|
||||||
|
self.setTextFont(CONFIG.textFont, CONFIG.textSize)
|
||||||
|
self.setTabStopDistance(CONFIG.getTabWidth())
|
||||||
|
self.setOpenExternalLinks(False)
|
||||||
|
|
||||||
|
self.document().setDocumentMargin(CONFIG.getTextMargin())
|
||||||
|
self.setPlaceholderText(self.tr(
|
||||||
|
"Press the \"Build Preview\" button to generate ..."
|
||||||
|
))
|
||||||
|
|
||||||
|
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
|
||||||
|
|
||||||
|
# Document Age
|
||||||
|
aPalette = self.palette()
|
||||||
|
aPalette.setColor(QPalette.Background, aPalette.toolTipBase().color())
|
||||||
|
aPalette.setColor(QPalette.Foreground, aPalette.toolTipText().color())
|
||||||
|
|
||||||
|
aFont = self.font()
|
||||||
|
aFont.setPointSizeF(0.9*self.mainTheme.fontPointSize)
|
||||||
|
|
||||||
|
self.ageLabel = QLabel("", self)
|
||||||
|
self.ageLabel.setIndent(0)
|
||||||
|
self.ageLabel.setFont(aFont)
|
||||||
|
self.ageLabel.setPalette(aPalette)
|
||||||
|
self.ageLabel.setAutoFillBackground(True)
|
||||||
|
self.ageLabel.setAlignment(Qt.AlignCenter)
|
||||||
|
self.ageLabel.setFixedHeight(int(2.1*self.mainTheme.fontPixelSize))
|
||||||
|
|
||||||
|
# Progress
|
||||||
|
self.buildProgress = NProgressCircle(self, CONFIG.pxInt(160), CONFIG.pxInt(16))
|
||||||
|
self.buildProgress.setVisible(False)
|
||||||
|
self.buildProgress.setMaximum(1)
|
||||||
|
self.buildProgress.setValue(0)
|
||||||
|
self.buildProgress.setColours(
|
||||||
|
back=QColor(255, 255, 255, 224),
|
||||||
|
track=QColor(196, 196, 196, 128),
|
||||||
|
text=QColor(0, 0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._updateDocMargins()
|
||||||
|
self._updateBuildAge()
|
||||||
|
|
||||||
|
# Age Timer
|
||||||
|
self.ageTimer = QTimer()
|
||||||
|
self.ageTimer.setInterval(10)
|
||||||
|
self.ageTimer.timeout.connect(self._updateBuildAge)
|
||||||
|
self.ageTimer.start()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Setters
|
||||||
|
##
|
||||||
|
|
||||||
|
def setBuildName(self, name: str):
|
||||||
|
"""Set the build name for the document label."""
|
||||||
|
self._buildName = name
|
||||||
|
self._updateBuildAge()
|
||||||
|
return
|
||||||
|
|
||||||
|
def setJustify(self, state: bool):
|
||||||
|
"""Enable/disable the justify text option."""
|
||||||
|
options = self.document().defaultTextOption()
|
||||||
|
if state:
|
||||||
|
options.setAlignment(Qt.AlignJustify)
|
||||||
|
else:
|
||||||
|
options.setAlignment(Qt.AlignAbsolute)
|
||||||
|
self.document().setDefaultTextOption(options)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setTextFont(self, family: str, size: int):
|
||||||
|
"""Set the text font properties."""
|
||||||
|
font = QFont()
|
||||||
|
font.setFamily(family)
|
||||||
|
font.setPointSize(size)
|
||||||
|
self.setFont(font)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def beginNewBuild(self, length: int):
|
||||||
|
"""Clear the document and show the progress bar."""
|
||||||
|
self.buildProgress.setMaximum(length)
|
||||||
|
self.buildProgress.setValue(0)
|
||||||
|
self.buildProgress.setCentreText(None)
|
||||||
|
self.buildProgress.setVisible(True)
|
||||||
|
self.setPlaceholderText("")
|
||||||
|
self.clear()
|
||||||
|
return
|
||||||
|
|
||||||
|
def buildStep(self, value: int):
|
||||||
|
"""Update the progress bar value."""
|
||||||
|
self.buildProgress.setValue(value)
|
||||||
|
qApp.processEvents()
|
||||||
|
return
|
||||||
|
|
||||||
|
def setContent(self, data: dict):
|
||||||
|
"""Set the content of the preview widget."""
|
||||||
|
sPos = self.verticalScrollBar().value()
|
||||||
|
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||||
|
|
||||||
|
self.buildProgress.setCentreText(self.tr("Processing ..."))
|
||||||
|
qApp.processEvents()
|
||||||
|
|
||||||
|
styles = "\n".join(data.get("styles", [
|
||||||
|
"h1, h2 {color: rgb(66, 113, 174);}",
|
||||||
|
"h3, h4 {color: rgb(50, 50, 50);}",
|
||||||
|
"a {color: rgb(66, 113, 174);}",
|
||||||
|
".tags {color: rgb(245, 135, 31); font-weight: bold;}",
|
||||||
|
]))
|
||||||
|
self.document().setDefaultStyleSheet(styles)
|
||||||
|
|
||||||
|
html = "".join(data.get("html", []))
|
||||||
|
html = html.replace("\t", "!!tab!!")
|
||||||
|
html = html.replace("<del>", "<span style='text-decoration: line-through;'>")
|
||||||
|
html = html.replace("</del>", "</span>")
|
||||||
|
self.setHtml(html)
|
||||||
|
qApp.processEvents()
|
||||||
|
while self.find("!!tab!!"):
|
||||||
|
theCursor = self.textCursor()
|
||||||
|
theCursor.insertText("\t")
|
||||||
|
|
||||||
|
self.verticalScrollBar().setValue(sPos)
|
||||||
|
self._docTime = checkInt(data.get("time"), 0)
|
||||||
|
self._updateBuildAge()
|
||||||
|
|
||||||
|
# Since we change the content while it may still be rendering, we mark
|
||||||
|
# the document as dirty again to make sure it's re-rendered properly.
|
||||||
|
self.document().markContentsDirty(0, self.document().characterCount())
|
||||||
|
|
||||||
|
self.buildProgress.setCentreText(self.tr("Done"))
|
||||||
|
qApp.restoreOverrideCursor()
|
||||||
|
qApp.processEvents()
|
||||||
|
QTimer.singleShot(300, self._hideProgress)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Events
|
||||||
|
##
|
||||||
|
|
||||||
|
def resizeEvent(self, event: QResizeEvent):
|
||||||
|
"""Capture resize and update the document margins."""
|
||||||
|
super().resizeEvent(event)
|
||||||
|
self._updateDocMargins()
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Public Slots
|
||||||
|
##
|
||||||
|
|
||||||
|
@pyqtSlot("QPrinter*")
|
||||||
|
def printPreview(self, printer: QPrinter):
|
||||||
|
"""Connect the print preview painter to the document viewer."""
|
||||||
|
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||||
|
printer.setOrientation(QPrinter.Portrait)
|
||||||
|
self.document().print(printer)
|
||||||
|
qApp.restoreOverrideCursor()
|
||||||
|
return
|
||||||
|
##
|
||||||
|
# Private Slots
|
||||||
|
##
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _updateBuildAge(self):
|
||||||
|
"""Update the build time and the fuzzy age."""
|
||||||
|
if self._docTime > 0:
|
||||||
|
strBuildTime = "%s (%s)" % (
|
||||||
|
datetime.fromtimestamp(self._docTime).strftime("%x %X"),
|
||||||
|
fuzzyTime(int(time()) - self._docTime)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
strBuildTime = self.tr("Unknown")
|
||||||
|
text = "{0} {1}".format(self.tr("Built"), strBuildTime)
|
||||||
|
if self._buildName:
|
||||||
|
text = "<b>{0}</b><br>{1}".format(self._buildName, text)
|
||||||
|
self.ageLabel.setText(text)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _hideProgress(self):
|
||||||
|
"""Clean up the build progress bar."""
|
||||||
|
self.buildProgress.setVisible(False)
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _updateDocMargins(self):
|
||||||
|
"""Automatically adjust the header to fill the top of the
|
||||||
|
document within the viewport.
|
||||||
|
"""
|
||||||
|
vBar = self.verticalScrollBar()
|
||||||
|
sW = vBar.width() if vBar.isVisible() else 0
|
||||||
|
tB = self.frameWidth()
|
||||||
|
vW = self.width() - 2*tB - sW
|
||||||
|
vH = self.height() - 2*tB
|
||||||
|
tH = self.ageLabel.height()
|
||||||
|
pS = self.buildProgress.width()
|
||||||
|
self.ageLabel.setGeometry(tB, tB, vW, tH)
|
||||||
|
self.setViewportMargins(0, tH, 0, 0)
|
||||||
|
self.buildProgress.move((vW-pS)//2, (vH-pS)//2)
|
||||||
|
return
|
||||||
|
|
||||||
|
# END Class _PreviewWidget
|
||||||
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
|
|||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
from novelwriter.common import makeFileNameSafe
|
from novelwriter.common import makeFileNameSafe
|
||||||
from novelwriter.custom import QSwitch
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class GuiProjectWizard(QWizard):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectWizard ...")
|
logger.debug("Create: GuiProjectWizard")
|
||||||
self.setObjectName("GuiProjectWizard")
|
self.setObjectName("GuiProjectWizard")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -76,10 +76,14 @@ class GuiProjectWizard(QWizard):
|
|||||||
|
|
||||||
self.setOption(QWizard.NoBackButtonOnStartPage, True)
|
self.setOption(QWizard.NoBackButtonOnStartPage, True)
|
||||||
|
|
||||||
logger.debug("GuiProjectWizard initialisation complete")
|
logger.debug("Ready: GuiProjectWizard")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiProjectWizard")
|
||||||
|
return
|
||||||
|
|
||||||
# END Class GuiProjectWizard
|
# END Class GuiProjectWizard
|
||||||
|
|
||||||
|
|
||||||
@@ -323,19 +327,19 @@ class ProjWizardCustomPage(QWizardPage):
|
|||||||
fS = CONFIG.pxInt(4)
|
fS = CONFIG.pxInt(4)
|
||||||
|
|
||||||
# Root Folders
|
# Root Folders
|
||||||
self.addPlot = QSwitch()
|
self.addPlot = NSwitch()
|
||||||
self.addPlot.setChecked(True)
|
self.addPlot.setChecked(True)
|
||||||
self.addPlot.clicked.connect(self._syncSwitches)
|
self.addPlot.clicked.connect(self._syncSwitches)
|
||||||
|
|
||||||
self.addChar = QSwitch()
|
self.addChar = NSwitch()
|
||||||
self.addChar.setChecked(True)
|
self.addChar.setChecked(True)
|
||||||
self.addChar.clicked.connect(self._syncSwitches)
|
self.addChar.clicked.connect(self._syncSwitches)
|
||||||
|
|
||||||
self.addWorld = QSwitch()
|
self.addWorld = NSwitch()
|
||||||
self.addWorld.setChecked(False)
|
self.addWorld.setChecked(False)
|
||||||
self.addWorld.clicked.connect(self._syncSwitches)
|
self.addWorld.clicked.connect(self._syncSwitches)
|
||||||
|
|
||||||
self.addNotes = QSwitch()
|
self.addNotes = NSwitch()
|
||||||
self.addNotes.setChecked(False)
|
self.addNotes.setChecked(False)
|
||||||
|
|
||||||
# Generate Content
|
# Generate Content
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ from novelwriter import CONFIG
|
|||||||
from novelwriter.enum import nwAlert
|
from novelwriter.enum import nwAlert
|
||||||
from novelwriter.error import formatException
|
from novelwriter.error import formatException
|
||||||
from novelwriter.common import formatTime, checkInt, checkIntTuple, minmax
|
from novelwriter.common import formatTime, checkInt, checkIntTuple, minmax
|
||||||
from novelwriter.custom import QSwitch
|
|
||||||
from novelwriter.constants import nwConst, nwFiles
|
from novelwriter.constants import nwConst, nwFiles
|
||||||
|
from novelwriter.extensions.switch import NSwitch
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ class GuiWritingStats(QDialog):
|
|||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui):
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
logger.debug("Initialising GuiWritingStats ...")
|
logger.debug("Create: GuiWritingStats")
|
||||||
self.setObjectName("GuiWritingStats")
|
self.setObjectName("GuiWritingStats")
|
||||||
|
|
||||||
self.mainGui = mainGui
|
self.mainGui = mainGui
|
||||||
@@ -189,37 +189,37 @@ class GuiWritingStats(QDialog):
|
|||||||
self.filterForm = QGridLayout(self)
|
self.filterForm = QGridLayout(self)
|
||||||
self.filterBox.setLayout(self.filterForm)
|
self.filterBox.setLayout(self.filterForm)
|
||||||
|
|
||||||
self.incNovel = QSwitch(width=2*sPx, height=sPx)
|
self.incNovel = NSwitch(width=2*sPx, height=sPx)
|
||||||
self.incNovel.setChecked(
|
self.incNovel.setChecked(
|
||||||
pOptions.getBool("GuiWritingStats", "incNovel", True)
|
pOptions.getBool("GuiWritingStats", "incNovel", True)
|
||||||
)
|
)
|
||||||
self.incNovel.clicked.connect(self._updateListBox)
|
self.incNovel.clicked.connect(self._updateListBox)
|
||||||
|
|
||||||
self.incNotes = QSwitch(width=2*sPx, height=sPx)
|
self.incNotes = NSwitch(width=2*sPx, height=sPx)
|
||||||
self.incNotes.setChecked(
|
self.incNotes.setChecked(
|
||||||
pOptions.getBool("GuiWritingStats", "incNotes", True)
|
pOptions.getBool("GuiWritingStats", "incNotes", True)
|
||||||
)
|
)
|
||||||
self.incNotes.clicked.connect(self._updateListBox)
|
self.incNotes.clicked.connect(self._updateListBox)
|
||||||
|
|
||||||
self.hideZeros = QSwitch(width=2*sPx, height=sPx)
|
self.hideZeros = NSwitch(width=2*sPx, height=sPx)
|
||||||
self.hideZeros.setChecked(
|
self.hideZeros.setChecked(
|
||||||
pOptions.getBool("GuiWritingStats", "hideZeros", True)
|
pOptions.getBool("GuiWritingStats", "hideZeros", True)
|
||||||
)
|
)
|
||||||
self.hideZeros.clicked.connect(self._updateListBox)
|
self.hideZeros.clicked.connect(self._updateListBox)
|
||||||
|
|
||||||
self.hideNegative = QSwitch(width=2*sPx, height=sPx)
|
self.hideNegative = NSwitch(width=2*sPx, height=sPx)
|
||||||
self.hideNegative.setChecked(
|
self.hideNegative.setChecked(
|
||||||
pOptions.getBool("GuiWritingStats", "hideNegative", False)
|
pOptions.getBool("GuiWritingStats", "hideNegative", False)
|
||||||
)
|
)
|
||||||
self.hideNegative.clicked.connect(self._updateListBox)
|
self.hideNegative.clicked.connect(self._updateListBox)
|
||||||
|
|
||||||
self.groupByDay = QSwitch(width=2*sPx, height=sPx)
|
self.groupByDay = NSwitch(width=2*sPx, height=sPx)
|
||||||
self.groupByDay.setChecked(
|
self.groupByDay.setChecked(
|
||||||
pOptions.getBool("GuiWritingStats", "groupByDay", False)
|
pOptions.getBool("GuiWritingStats", "groupByDay", False)
|
||||||
)
|
)
|
||||||
self.groupByDay.clicked.connect(self._updateListBox)
|
self.groupByDay.clicked.connect(self._updateListBox)
|
||||||
|
|
||||||
self.showIdleTime = QSwitch(width=2*sPx, height=sPx)
|
self.showIdleTime = NSwitch(width=2*sPx, height=sPx)
|
||||||
self.showIdleTime.setChecked(
|
self.showIdleTime.setChecked(
|
||||||
pOptions.getBool("GuiWritingStats", "showIdleTime", False)
|
pOptions.getBool("GuiWritingStats", "showIdleTime", False)
|
||||||
)
|
)
|
||||||
@@ -286,10 +286,14 @@ class GuiWritingStats(QDialog):
|
|||||||
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
logger.debug("GuiWritingStats initialisation complete")
|
logger.debug("Ready: GuiWritingStats")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
logger.debug("Delete: GuiWritingStats")
|
||||||
|
return
|
||||||
|
|
||||||
def populateGUI(self):
|
def populateGUI(self):
|
||||||
"""Populate list box with data from the log file.
|
"""Populate list box with data from the log file.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
pyqt5>=5.10
|
pyqt5>=5.10
|
||||||
lxml>=4.2.0
|
|
||||||
pyenchant>=3.0.0
|
pyenchant>=3.0.0
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 21:29:35">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" fileRevision="1" timeStamp="2023-06-03 18:18:20">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1505" autoCount="237" editTime="74902">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1509" autoCount="237" editTime="75095">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
@@ -20,13 +20,6 @@
|
|||||||
<entry key="B">E</entry>
|
<entry key="B">E</entry>
|
||||||
<entry key="C">D</entry>
|
<entry key="C">D</entry>
|
||||||
</autoReplace>
|
</autoReplace>
|
||||||
<titleFormat>
|
|
||||||
<entry key="title">%title%</entry>
|
|
||||||
<entry key="chapter">Chapter %chw%: %title%</entry>
|
|
||||||
<entry key="unnumbered">%title%</entry>
|
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
|
||||||
<entry key="section" />
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
<status>
|
||||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
||||||
@@ -65,7 +58,7 @@
|
|||||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="1369" />
|
<meta expanded="no" heading="H3" charCount="2687" wordCount="479" paraCount="14" cursorPos="748" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
|||||||
@@ -50,6 +50,6 @@ gui_scripts =
|
|||||||
universal = 0
|
universal = 0
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
ignore = D107,D205,D400,E133,E221,E226,E228,E241,W503
|
ignore = E133,E221,E226,E228,E241,W503
|
||||||
max-line-length = 99
|
max-line-length = 99
|
||||||
exclude = docs/*
|
exclude = docs/*
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import shutil
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from mock import MockGuiMain
|
from mocked import MockGuiMain
|
||||||
from tools import cleanProject
|
from tools import cleanProject
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
@@ -49,6 +49,7 @@ def resetConfigVars():
|
|||||||
"""
|
"""
|
||||||
CONFIG.setLastPath(_TMP_ROOT)
|
CONFIG.setLastPath(_TMP_ROOT)
|
||||||
CONFIG.setBackupPath(_TMP_ROOT)
|
CONFIG.setBackupPath(_TMP_ROOT)
|
||||||
|
CONFIG.setTextFont(None)
|
||||||
CONFIG._homePath = _TMP_ROOT
|
CONFIG._homePath = _TMP_ROOT
|
||||||
CONFIG.guiLocale = "en_GB"
|
CONFIG.guiLocale = "en_GB"
|
||||||
return
|
return
|
||||||
@@ -199,27 +200,6 @@ def mockRnd(monkeypatch):
|
|||||||
# Temp Project Folders
|
# Temp Project Folders
|
||||||
##
|
##
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
|
||||||
def nwLipsum():
|
|
||||||
"""A medium sized novelWriter example project with a lot of Lorem
|
|
||||||
Ipsum text.
|
|
||||||
"""
|
|
||||||
srcDir = _TST_ROOT / "lipsum"
|
|
||||||
dstDir = _TMP_ROOT / "lipsum"
|
|
||||||
if dstDir.exists():
|
|
||||||
shutil.rmtree(dstDir)
|
|
||||||
|
|
||||||
shutil.copytree(srcDir, dstDir)
|
|
||||||
cleanProject(dstDir)
|
|
||||||
|
|
||||||
yield str(dstDir)
|
|
||||||
|
|
||||||
if dstDir.exists():
|
|
||||||
shutil.rmtree(dstDir)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def prjLipsum():
|
def prjLipsum():
|
||||||
"""A medium sized novelWriter example project with a lot of Lorem
|
"""A medium sized novelWriter example project with a lot of Lorem
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" timeStamp="2022-11-07 13:00:48">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.5" fileRevision="1" timeStamp="2022-11-07 13:00:48">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="5" autoCount="10" editTime="1000">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="5" autoCount="10" editTime="1000">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<title>Sample Project</title>
|
<title>Sample Project</title>
|
||||||
@@ -20,13 +20,6 @@
|
|||||||
<entry key="B">E</entry>
|
<entry key="B">E</entry>
|
||||||
<entry key="C">D</entry>
|
<entry key="C">D</entry>
|
||||||
</autoReplace>
|
</autoReplace>
|
||||||
<titleFormat>
|
|
||||||
<entry key="title">%title%</entry>
|
|
||||||
<entry key="chapter">Chapter %chw%: %title%</entry>
|
|
||||||
<entry key="unnumbered">%title%</entry>
|
|
||||||
<entry key="scene">Scene %ch%.%sc%: %title%</entry>
|
|
||||||
<entry key="section" />
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
<status>
|
||||||
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
<entry key="sf12341" count="4" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
<entry key="sf24ce6" count="2" red="200" green="50" blue="0">Notes</entry>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:10:19">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" fileRevision="1" timeStamp="2023-06-03 18:18:14">
|
||||||
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="40" autoCount="24" editTime="1905">
|
<project id="5ac0df12-8b8b-476b-9905-21d33685687c" saveCount="42" autoCount="24" editTime="1913">
|
||||||
<name>Lorem Ipsum</name>
|
<name>Lorem Ipsum</name>
|
||||||
<title>Lorem Ipsum</title>
|
<title>Lorem Ipsum</title>
|
||||||
<author>lipsum.com</author>
|
<author>lipsum.com</author>
|
||||||
@@ -19,13 +19,6 @@
|
|||||||
<entry key="Rep1">Replace Text 1</entry>
|
<entry key="Rep1">Replace Text 1</entry>
|
||||||
<entry key="Rep2">Replace Text 2</entry>
|
<entry key="Rep2">Replace Text 2</entry>
|
||||||
</autoReplace>
|
</autoReplace>
|
||||||
<titleFormat>
|
|
||||||
<entry key="title">%title%</entry>
|
|
||||||
<entry key="chapter">Chapter %ch%: %title%</entry>
|
|
||||||
<entry key="unnumbered">%title%</entry>
|
|
||||||
<entry key="scene">* * *</entry>
|
|
||||||
<entry key="section" />
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
<status>
|
||||||
<entry key="sbaa94f" count="3" red="100" green="100" blue="100">New</entry>
|
<entry key="sbaa94f" count="3" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s27bf7c" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s27bf7c" count="0" red="200" green="50" blue="0">Note</entry>
|
||||||
@@ -45,7 +38,7 @@
|
|||||||
<name status="sbaa94f" import="i613591">Novel</name>
|
<name status="sbaa94f" import="i613591">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="7a992350f3eb6" parent="b3643d0f92e32" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="7a992350f3eb6" parent="b3643d0f92e32" root="b3643d0f92e32" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H1" charCount="230" wordCount="40" paraCount="3" cursorPos="148" />
|
<meta expanded="no" heading="H1" charCount="230" wordCount="40" paraCount="3" cursorPos="14" />
|
||||||
<name status="sedd043" import="i613591" active="yes">Lorem Ipsum</name>
|
<name status="sedd043" import="i613591" active="yes">Lorem Ipsum</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="8c58a65414c23" parent="b3643d0f92e32" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="8c58a65414c23" parent="b3643d0f92e32" root="b3643d0f92e32" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[Meta]
|
[Meta]
|
||||||
timestamp = 2022-11-11 12:48:18
|
timestamp = 2023-05-31 10:52:21
|
||||||
|
|
||||||
[Main]
|
[Main]
|
||||||
theme = default
|
theme = default
|
||||||
@@ -29,7 +29,7 @@ backuponclose = False
|
|||||||
askbeforebackup = True
|
askbeforebackup = True
|
||||||
|
|
||||||
[Editor]
|
[Editor]
|
||||||
textfont = None
|
textfont =
|
||||||
textsize = 12
|
textsize = 12
|
||||||
width = 700
|
width = 700
|
||||||
margin = 40
|
margin = 40
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:13:44">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" fileRevision="1" timeStamp="2023-06-03 18:18:23">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -16,13 +16,6 @@
|
|||||||
<entry key="outline">None</entry>
|
<entry key="outline">None</entry>
|
||||||
</lastHandle>
|
</lastHandle>
|
||||||
<autoReplace />
|
<autoReplace />
|
||||||
<titleFormat>
|
|
||||||
<entry key="title">%title%</entry>
|
|
||||||
<entry key="chapter">%title%</entry>
|
|
||||||
<entry key="unnumbered">%title%</entry>
|
|
||||||
<entry key="scene">* * *</entry>
|
|
||||||
<entry key="section" />
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="7" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:13:44">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" fileRevision="1" timeStamp="2023-06-03 18:18:23">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="1" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Novel</title>
|
<title>New Novel</title>
|
||||||
@@ -16,13 +16,6 @@
|
|||||||
<entry key="outline">None</entry>
|
<entry key="outline">None</entry>
|
||||||
</lastHandle>
|
</lastHandle>
|
||||||
<autoReplace />
|
<autoReplace />
|
||||||
<titleFormat>
|
|
||||||
<entry key="title">%title%</entry>
|
|
||||||
<entry key="chapter">%title%</entry>
|
|
||||||
<entry key="unnumbered">%title%</entry>
|
|
||||||
<entry key="scene">* * *</entry>
|
|
||||||
<entry key="section" />
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="6" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="6" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<office:document-styles xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3">
|
<office:document-styles xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3">
|
||||||
<office:font-face-decls>
|
<office:font-face-decls>
|
||||||
<style:font-face style:name="Liberation Serif" style:font-pitch="variable" />
|
<style:font-face style:name="Liberation Serif" style:font-pitch="variable" />
|
||||||
</office:font-face-decls>
|
</office:font-face-decls>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="Text_20_Meta" style:family="paragraph" style:display-name="Text Meta" style:parent-style-name="Standard" style:class="text">
|
<style:style style:name="Text_20_Meta" style:family="paragraph" style:display-name="Text Meta" style:parent-style-name="Standard" style:class="text">
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.247cm" fo:line-height="115%" />
|
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.247cm" fo:line-height="115%" />
|
||||||
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="12pt" fo:color="None" loext:opacity="None" />
|
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="12pt" />
|
||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="Title" style:family="paragraph" style:display-name="Title" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
<style:style style:name="Title" style:family="paragraph" style:display-name="Title" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
||||||
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" fo:text-align="center" />
|
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" fo:text-align="center" />
|
||||||
@@ -30,19 +30,19 @@
|
|||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="Heading_20_1" style:family="paragraph" style:display-name="Heading 1" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text">
|
<style:style style:name="Heading_20_1" style:family="paragraph" style:display-name="Heading 1" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text">
|
||||||
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" />
|
<style:paragraph-properties fo:margin-top="0.423cm" fo:margin-bottom="0.212cm" />
|
||||||
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="24pt" fo:font-weight="bold" fo:color="None" loext:opacity="None" />
|
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="24pt" fo:font-weight="bold" />
|
||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="Heading_20_2" style:family="paragraph" style:display-name="Heading 2" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="2" style:class="text">
|
<style:style style:name="Heading_20_2" style:family="paragraph" style:display-name="Heading 2" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="2" style:class="text">
|
||||||
<style:paragraph-properties fo:margin-top="0.353cm" fo:margin-bottom="0.212cm" />
|
<style:paragraph-properties fo:margin-top="0.353cm" fo:margin-bottom="0.212cm" />
|
||||||
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="19pt" fo:font-weight="bold" fo:color="None" loext:opacity="None" />
|
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="19pt" fo:font-weight="bold" />
|
||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="Heading_20_3" style:family="paragraph" style:display-name="Heading 3" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="3" style:class="text">
|
<style:style style:name="Heading_20_3" style:family="paragraph" style:display-name="Heading 3" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="3" style:class="text">
|
||||||
<style:paragraph-properties fo:margin-top="0.247cm" fo:margin-bottom="0.212cm" />
|
<style:paragraph-properties fo:margin-top="0.247cm" fo:margin-bottom="0.212cm" />
|
||||||
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="16pt" fo:font-weight="bold" fo:color="None" loext:opacity="None" />
|
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="16pt" fo:font-weight="bold" />
|
||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="Heading_20_4" style:family="paragraph" style:display-name="Heading 4" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="4" style:class="text">
|
<style:style style:name="Heading_20_4" style:family="paragraph" style:display-name="Heading 4" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="4" style:class="text">
|
||||||
<style:paragraph-properties fo:margin-top="0.247cm" fo:margin-bottom="0.212cm" />
|
<style:paragraph-properties fo:margin-top="0.247cm" fo:margin-bottom="0.212cm" />
|
||||||
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="14pt" fo:font-weight="bold" fo:color="None" loext:opacity="None" />
|
<style:text-properties style:font-name="Liberation Serif" fo:font-family="'Liberation Serif'" fo:font-size="14pt" fo:font-weight="bold" />
|
||||||
</style:style>
|
</style:style>
|
||||||
<style:style style:name="Header" style:family="paragraph" style:display-name="Header" style:parent-style-name="Header_20_and_20_Footer">
|
<style:style style:name="Header" style:family="paragraph" style:display-name="Header" style:parent-style-name="Header_20_and_20_Footer">
|
||||||
<style:paragraph-properties fo:text-align="right" />
|
<style:paragraph-properties fo:text-align="right" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:01:04">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" fileRevision="1" timeStamp="2023-06-03 18:18:22">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
@@ -16,13 +16,6 @@
|
|||||||
<entry key="outline">None</entry>
|
<entry key="outline">None</entry>
|
||||||
</lastHandle>
|
</lastHandle>
|
||||||
<autoReplace />
|
<autoReplace />
|
||||||
<titleFormat>
|
|
||||||
<entry key="title">%title%</entry>
|
|
||||||
<entry key="chapter">%title%</entry>
|
|
||||||
<entry key="unnumbered">%title%</entry>
|
|
||||||
<entry key="scene">* * *</entry>
|
|
||||||
<entry key="section" />
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="15" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="15" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:01:04">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" fileRevision="1" timeStamp="2023-06-03 18:18:22">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
@@ -16,13 +16,6 @@
|
|||||||
<entry key="outline">None</entry>
|
<entry key="outline">None</entry>
|
||||||
</lastHandle>
|
</lastHandle>
|
||||||
<autoReplace />
|
<autoReplace />
|
||||||
<titleFormat>
|
|
||||||
<entry key="title">%title%</entry>
|
|
||||||
<entry key="chapter">%title%</entry>
|
|
||||||
<entry key="unnumbered">%title%</entry>
|
|
||||||
<entry key="scene">* * *</entry>
|
|
||||||
<entry key="section" />
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="9" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="9" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" timeStamp="2023-05-29 17:01:04">
|
<novelWriterXML appVersion="2.0.7" hexVersion="0x020007f0" fileVersion="1.5" fileRevision="1" timeStamp="2023-06-03 18:18:22">
|
||||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="1" autoCount="0" editTime="0">
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>New Project</title>
|
<title>New Project</title>
|
||||||
@@ -16,13 +16,6 @@
|
|||||||
<entry key="outline">None</entry>
|
<entry key="outline">None</entry>
|
||||||
</lastHandle>
|
</lastHandle>
|
||||||
<autoReplace />
|
<autoReplace />
|
||||||
<titleFormat>
|
|
||||||
<entry key="title">%title%</entry>
|
|
||||||
<entry key="chapter">%title%</entry>
|
|
||||||
<entry key="unnumbered">%title%</entry>
|
|
||||||
<entry key="scene">* * *</entry>
|
|
||||||
<entry key="section" />
|
|
||||||
</titleFormat>
|
|
||||||
<status>
|
<status>
|
||||||
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
|
||||||
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
# Lorem Ipsum
|
|
||||||
|
|
||||||
**By lipsum.com**
|
|
||||||
|
|
||||||
“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”
|
|
||||||
|
|
||||||
“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”
|
|
||||||
|
|
||||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.
|
|
||||||
|
|
||||||
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.
|
|
||||||
|
|
||||||
## Prologue
|
|
||||||
|
|
||||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
|
||||||
|
|
||||||
# Act One
|
|
||||||
|
|
||||||
“Fusce maximus felis libero”
|
|
||||||
|
|
||||||
## Chapter 1: Chapter One
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.
|
|
||||||
|
|
||||||
Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.
|
|
||||||
|
|
||||||
Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.
|
|
||||||
|
|
||||||
Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.
|
|
||||||
|
|
||||||
Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.
|
|
||||||
|
|
||||||
Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.
|
|
||||||
|
|
||||||
Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.
|
|
||||||
|
|
||||||
Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.
|
|
||||||
|
|
||||||
Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.
|
|
||||||
|
|
||||||
## Chapter 2: Chapter Two
|
|
||||||
|
|
||||||
Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.
|
|
||||||
|
|
||||||
Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.
|
|
||||||
|
|
||||||
Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.
|
|
||||||
|
|
||||||
Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.
|
|
||||||
|
|
||||||
Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.
|
|
||||||
|
|
||||||
Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.
|
|
||||||
|
|
||||||
Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.
|
|
||||||
|
|
||||||
Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.
|
|
||||||
|
|
||||||
Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.
|
|
||||||
|
|
||||||
In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.
|
|
||||||
|
|
||||||
Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.
|
|
||||||
|
|
||||||
Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.
|
|
||||||
|
|
||||||
Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
|
|
||||||
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
|
||||||
<office:meta>
|
|
||||||
<meta:creation-date>2023-05-29T22:10:29</meta:creation-date>
|
|
||||||
<meta:generator>novelWriter/2.0.7</meta:generator>
|
|
||||||
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
|
||||||
<meta:editing-cycles>40</meta:editing-cycles>
|
|
||||||
<meta:editing-duration>P0DT0H31M45S</meta:editing-duration>
|
|
||||||
<dc:title>Lorem Ipsum</dc:title>
|
|
||||||
<dc:date>2023-05-29T22:10:29</dc:date>
|
|
||||||
<dc:creator>lipsum.com</dc:creator>
|
|
||||||
</office:meta>
|
|
||||||
<office:font-face-decls>
|
|
||||||
<style:font-face style:name="DejaVu Sans" style:font-pitch="variable" />
|
|
||||||
</office:font-face-decls>
|
|
||||||
<office:styles>
|
|
||||||
<style:default-style style:family="paragraph">
|
|
||||||
<style:paragraph-properties style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" fo:language="en" fo:country="GB" />
|
|
||||||
</style:default-style>
|
|
||||||
<style:style style:name="Standard" style:family="paragraph" style:class="text">
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" fo:keep-with-next="always" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="13pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra" />
|
|
||||||
<style:style style:name="Text_20_body" style:family="paragraph" style:display-name="Text body" style:parent-style-name="Standard" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.227cm" fo:line-height="115%" fo:text-align="left" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Text_20_Meta" style:family="paragraph" style:display-name="Text Meta" style:parent-style-name="Standard" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.227cm" fo:line-height="115%" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" fo:color="#813709" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Title" style:family="paragraph" style:display-name="Title" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.388cm" fo:margin-bottom="0.194cm" fo:text-align="center" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="28pt" fo:font-weight="bold" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_1" style:family="paragraph" style:display-name="Heading 1" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.388cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="22pt" fo:font-weight="bold" fo:color="#2a6099" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_2" style:family="paragraph" style:display-name="Heading 2" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="2" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.324cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="18pt" fo:font-weight="bold" fo:color="#2a6099" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_3" style:family="paragraph" style:display-name="Heading 3" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="3" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="14pt" fo:font-weight="bold" fo:color="#444444" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_4" style:family="paragraph" style:display-name="Heading 4" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="4" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="13pt" fo:font-weight="bold" fo:color="#444444" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Header" style:family="paragraph" style:display-name="Header" style:parent-style-name="Header_20_and_20_Footer">
|
|
||||||
<style:paragraph-properties fo:text-align="right" />
|
|
||||||
</style:style>
|
|
||||||
</office:styles>
|
|
||||||
<office:automatic-styles>
|
|
||||||
<style:page-layout style:name="PM1">
|
|
||||||
<style:page-layout-properties fo:margin-top="2.000cm" fo:margin-bottom="2.000cm" fo:margin-left="2.000cm" fo:margin-right="2.000cm" />
|
|
||||||
<style:header-style>
|
|
||||||
<style:header-footer-properties fo:min-height="0.600cm" fo:margin-left="0.000cm" fo:margin-right="0.000cm" fo:margin-bottom="0.500cm" />
|
|
||||||
</style:header-style>
|
|
||||||
</style:page-layout>
|
|
||||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Text_20_body">
|
|
||||||
<style:paragraph-properties fo:text-align="center" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
|
||||||
<style:paragraph-properties fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Heading_20_1">
|
|
||||||
<style:paragraph-properties fo:text-align="center" fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="T1" style:family="text">
|
|
||||||
<style:text-properties fo:font-weight="bold" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="T2" style:family="text">
|
|
||||||
<style:text-properties fo:font-style="italic" />
|
|
||||||
</style:style>
|
|
||||||
</office:automatic-styles>
|
|
||||||
<office:master-styles>
|
|
||||||
<style:master-page style:name="Standard" style:page-layout-name="PM1">
|
|
||||||
<style:header>
|
|
||||||
<text:p text:style-name="Header">Lorem Ipsum / lipsum.com / <text:page-number text:select-page="current">2</text:page-number></text:p>
|
|
||||||
</style:header>
|
|
||||||
<style:header-first>
|
|
||||||
<text:p text:style-name="Header" />
|
|
||||||
</style:header-first>
|
|
||||||
</style:master-page>
|
|
||||||
</office:master-styles>
|
|
||||||
<office:body>
|
|
||||||
<office:text>
|
|
||||||
<text:p text:style-name="Title">Lorem Ipsum</text:p>
|
|
||||||
<text:p text:style-name="P1"><text:span text:style-name="T1">By lipsum.com</text:span></text:p>
|
|
||||||
<text:p text:style-name="P1">“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”</text:p>
|
|
||||||
<text:p text:style-name="P1">“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</text:p>
|
|
||||||
<text:h text:style-name="P2" text:outline-level="2">Prologue</text:h>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:span text:style-name="T2">Lorem Ipsum</text:span> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="1">Act One</text:h>
|
|
||||||
<text:p text:style-name="P1">“Fusce maximus felis libero”</text:p>
|
|
||||||
<text:h text:style-name="P2" text:outline-level="2">Chapter 1: Chapter One</text:h>
|
|
||||||
<text:p text:style-name="Text_20_body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.</text:p>
|
|
||||||
<text:p text:style-name="P1">* * *</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.</text:p>
|
|
||||||
<text:p text:style-name="P1">* * *</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.</text:p>
|
|
||||||
<text:h text:style-name="P2" text:outline-level="2">Chapter 2: Chapter Two</text:h>
|
|
||||||
<text:p text:style-name="Text_20_body">Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.</text:p>
|
|
||||||
<text:p text:style-name="P1">* * *</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.</text:p>
|
|
||||||
<text:p text:style-name="P1">* * *</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.</text:p>
|
|
||||||
<text:p text:style-name="P1">* * *</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.</text:p>
|
|
||||||
</office:text>
|
|
||||||
</office:body>
|
|
||||||
</office:document>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset='utf-8'>
|
|
||||||
<title>Lorem Ipsum</title>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
body {font-family: 'DejaVu Sans'; font-size: 11pt;}
|
|
||||||
p {text-align: left; line-height: 115%; margin-top: 0.00em; margin-bottom: 0.58em;}
|
|
||||||
h1 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 1.00em; margin-bottom: 0.50em;}
|
|
||||||
h2 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 0.83em; margin-bottom: 0.50em;}
|
|
||||||
h3 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
|
|
||||||
h4 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
|
|
||||||
.title {font-size: 2.5em; margin-top: 1.00em; margin-bottom: 0.50em;}
|
|
||||||
.sep, .skip {text-align: center; margin-top: 1.00em; margin-bottom: 1.00em;}
|
|
||||||
a {color: rgb(66, 113, 174);}
|
|
||||||
.tags {color: rgb(245, 135, 31); font-weight: bold;}
|
|
||||||
.break {text-align: left;}
|
|
||||||
.synopsis {font-style: italic;}
|
|
||||||
.comment {font-style: italic; color: rgb(100, 100, 100);}
|
|
||||||
article {width: 800px; margin: 40px auto;}
|
|
||||||
</style>
|
|
||||||
<body>
|
|
||||||
<article>
|
|
||||||
<h1 class='title' style='text-align: center;'>Lorem Ipsum</h1>
|
|
||||||
<p style='text-align: center;'><strong>By lipsum.com</strong></p>
|
|
||||||
<p style='text-align: center;'>“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”</p>
|
|
||||||
<p style='text-align: center;'>“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”</p>
|
|
||||||
<p>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</p>
|
|
||||||
<p>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
|
|
||||||
<h1 style='page-break-before: always;'>Prologue</h1>
|
|
||||||
<p><em>Lorem Ipsum</em> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Act One</h1>
|
|
||||||
<p style='text-align: center;'>“Fusce maximus felis libero”</p>
|
|
||||||
<h1 style='page-break-before: always;'>Chapter 1: Chapter One</h1>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.</p>
|
|
||||||
<p class='sep' style='text-align: center;'>* * *</p>
|
|
||||||
<p>Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.</p>
|
|
||||||
<p>Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.</p>
|
|
||||||
<p>Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.</p>
|
|
||||||
<p>Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.</p>
|
|
||||||
<p class='sep' style='text-align: center;'>* * *</p>
|
|
||||||
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.</p>
|
|
||||||
<p>Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.</p>
|
|
||||||
<p>Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.</p>
|
|
||||||
<p>Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.</p>
|
|
||||||
<p>Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.</p>
|
|
||||||
<p>Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.</p>
|
|
||||||
<h1 style='page-break-before: always;'>Chapter 2: Chapter Two</h1>
|
|
||||||
<p>Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.</p>
|
|
||||||
<p class='sep' style='text-align: center;'>* * *</p>
|
|
||||||
<p>Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.</p>
|
|
||||||
<p>Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.</p>
|
|
||||||
<p>Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.</p>
|
|
||||||
<p>Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.</p>
|
|
||||||
<p class='sep' style='text-align: center;'>* * *</p>
|
|
||||||
<p>Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.</p>
|
|
||||||
<p>Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.</p>
|
|
||||||
<p>Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.</p>
|
|
||||||
<p>Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.</p>
|
|
||||||
<p>Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.</p>
|
|
||||||
<p>Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.</p>
|
|
||||||
<p class='sep' style='text-align: center;'>* * *</p>
|
|
||||||
<p>Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.</p>
|
|
||||||
<p>In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.</p>
|
|
||||||
<p>Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.</p>
|
|
||||||
<p>Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.</p>
|
|
||||||
<p>Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.</p>
|
|
||||||
</article>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
# Lorem Ipsum
|
|
||||||
|
|
||||||
**By lipsum.com**
|
|
||||||
|
|
||||||
“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”
|
|
||||||
|
|
||||||
“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”
|
|
||||||
|
|
||||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.
|
|
||||||
|
|
||||||
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.
|
|
||||||
|
|
||||||
## Prologue
|
|
||||||
|
|
||||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
|
||||||
|
|
||||||
# Act One
|
|
||||||
|
|
||||||
“Fusce maximus felis libero”
|
|
||||||
|
|
||||||
## Chapter 1: Chapter One
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.
|
|
||||||
|
|
||||||
Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.
|
|
||||||
|
|
||||||
Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.
|
|
||||||
|
|
||||||
Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.
|
|
||||||
|
|
||||||
Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.
|
|
||||||
|
|
||||||
Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.
|
|
||||||
|
|
||||||
Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.
|
|
||||||
|
|
||||||
Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.
|
|
||||||
|
|
||||||
Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.
|
|
||||||
|
|
||||||
## Chapter 2: Chapter Two
|
|
||||||
|
|
||||||
Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.
|
|
||||||
|
|
||||||
Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.
|
|
||||||
|
|
||||||
Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.
|
|
||||||
|
|
||||||
Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.
|
|
||||||
|
|
||||||
Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.
|
|
||||||
|
|
||||||
Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.
|
|
||||||
|
|
||||||
Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.
|
|
||||||
|
|
||||||
Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.
|
|
||||||
|
|
||||||
Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.
|
|
||||||
|
|
||||||
In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.
|
|
||||||
|
|
||||||
Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.
|
|
||||||
|
|
||||||
Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.
|
|
||||||
|
|
||||||
Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
|
|
||||||
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#! Lorem Ipsum
|
|
||||||
|
|
||||||
**By lipsum.com**
|
|
||||||
|
|
||||||
“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”
|
|
||||||
|
|
||||||
“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.
|
|
||||||
|
|
||||||
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.
|
|
||||||
|
|
||||||
##! Prologue
|
|
||||||
|
|
||||||
|
|
||||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
|
||||||
|
|
||||||
# Act One
|
|
||||||
|
|
||||||
“Fusce maximus felis libero”
|
|
||||||
|
|
||||||
## Chapter One
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.
|
|
||||||
|
|
||||||
### Scene One
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.
|
|
||||||
|
|
||||||
Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.
|
|
||||||
|
|
||||||
#### Scene One, Section Two
|
|
||||||
|
|
||||||
Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.
|
|
||||||
|
|
||||||
Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.
|
|
||||||
|
|
||||||
### Scene Two
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.
|
|
||||||
|
|
||||||
Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.
|
|
||||||
|
|
||||||
Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.
|
|
||||||
|
|
||||||
#### Scene Two, Section Two
|
|
||||||
|
|
||||||
Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.
|
|
||||||
|
|
||||||
Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.
|
|
||||||
|
|
||||||
Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.
|
|
||||||
|
|
||||||
## Chapter Two
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.
|
|
||||||
|
|
||||||
### Scene Three
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.
|
|
||||||
|
|
||||||
Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.
|
|
||||||
|
|
||||||
Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.
|
|
||||||
|
|
||||||
Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.
|
|
||||||
|
|
||||||
### Scene Four
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.
|
|
||||||
|
|
||||||
Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.
|
|
||||||
|
|
||||||
Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.
|
|
||||||
|
|
||||||
Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.
|
|
||||||
|
|
||||||
Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.
|
|
||||||
|
|
||||||
Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.
|
|
||||||
|
|
||||||
### Scene Five
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.
|
|
||||||
|
|
||||||
In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.
|
|
||||||
|
|
||||||
Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.
|
|
||||||
|
|
||||||
Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.
|
|
||||||
|
|
||||||
Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
|
|
||||||
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
|
||||||
<office:meta>
|
|
||||||
<meta:creation-date>2023-05-29T22:11:22</meta:creation-date>
|
|
||||||
<meta:generator>novelWriter/2.0.7</meta:generator>
|
|
||||||
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
|
||||||
<meta:editing-cycles>40</meta:editing-cycles>
|
|
||||||
<meta:editing-duration>P0DT0H31M45S</meta:editing-duration>
|
|
||||||
<dc:title>Lorem Ipsum</dc:title>
|
|
||||||
<dc:date>2023-05-29T22:11:22</dc:date>
|
|
||||||
<dc:creator>lipsum.com</dc:creator>
|
|
||||||
</office:meta>
|
|
||||||
<office:font-face-decls>
|
|
||||||
<style:font-face style:name="DejaVu Sans" style:font-pitch="variable" />
|
|
||||||
</office:font-face-decls>
|
|
||||||
<office:styles>
|
|
||||||
<style:default-style style:family="paragraph">
|
|
||||||
<style:paragraph-properties style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" fo:language="en" fo:country="GB" />
|
|
||||||
</style:default-style>
|
|
||||||
<style:style style:name="Standard" style:family="paragraph" style:class="text">
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" fo:keep-with-next="always" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="13pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra" />
|
|
||||||
<style:style style:name="Text_20_body" style:family="paragraph" style:display-name="Text body" style:parent-style-name="Standard" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.227cm" fo:line-height="115%" fo:text-align="justify" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Text_20_Meta" style:family="paragraph" style:display-name="Text Meta" style:parent-style-name="Standard" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.227cm" fo:line-height="115%" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" fo:color="#813709" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Title" style:family="paragraph" style:display-name="Title" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.388cm" fo:margin-bottom="0.194cm" fo:text-align="center" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="28pt" fo:font-weight="bold" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_1" style:family="paragraph" style:display-name="Heading 1" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.388cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="22pt" fo:font-weight="bold" fo:color="#2a6099" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_2" style:family="paragraph" style:display-name="Heading 2" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="2" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.324cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="18pt" fo:font-weight="bold" fo:color="#2a6099" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_3" style:family="paragraph" style:display-name="Heading 3" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="3" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="14pt" fo:font-weight="bold" fo:color="#444444" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_4" style:family="paragraph" style:display-name="Heading 4" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="4" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="13pt" fo:font-weight="bold" fo:color="#444444" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Header" style:family="paragraph" style:display-name="Header" style:parent-style-name="Header_20_and_20_Footer">
|
|
||||||
<style:paragraph-properties fo:text-align="right" />
|
|
||||||
</style:style>
|
|
||||||
</office:styles>
|
|
||||||
<office:automatic-styles>
|
|
||||||
<style:page-layout style:name="PM1">
|
|
||||||
<style:page-layout-properties fo:margin-top="2.000cm" fo:margin-bottom="2.000cm" fo:margin-left="2.000cm" fo:margin-right="2.000cm" />
|
|
||||||
<style:header-style>
|
|
||||||
<style:header-footer-properties fo:min-height="0.600cm" fo:margin-left="0.000cm" fo:margin-right="0.000cm" fo:margin-bottom="0.500cm" />
|
|
||||||
</style:header-style>
|
|
||||||
</style:page-layout>
|
|
||||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Text_20_body">
|
|
||||||
<style:paragraph-properties fo:text-align="center" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Text_20_Meta">
|
|
||||||
<style:paragraph-properties fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
|
||||||
<style:paragraph-properties fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Heading_20_1">
|
|
||||||
<style:paragraph-properties fo:text-align="center" fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Text_20_Meta">
|
|
||||||
<style:paragraph-properties fo:margin-bottom="0.000cm" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Text_20_Meta">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.000cm" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Title">
|
|
||||||
<style:paragraph-properties fo:text-align="center" fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="T1" style:family="text">
|
|
||||||
<style:text-properties fo:font-weight="bold" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="T2" style:family="text">
|
|
||||||
<style:text-properties fo:font-style="italic" />
|
|
||||||
</style:style>
|
|
||||||
</office:automatic-styles>
|
|
||||||
<office:master-styles>
|
|
||||||
<style:master-page style:name="Standard" style:page-layout-name="PM1">
|
|
||||||
<style:header>
|
|
||||||
<text:p text:style-name="Header">Lorem Ipsum / lipsum.com / <text:page-number text:select-page="current">2</text:page-number></text:p>
|
|
||||||
</style:header>
|
|
||||||
<style:header-first>
|
|
||||||
<text:p text:style-name="Header" />
|
|
||||||
</style:header-first>
|
|
||||||
</style:master-page>
|
|
||||||
</office:master-styles>
|
|
||||||
<office:body>
|
|
||||||
<office:text>
|
|
||||||
<text:p text:style-name="Title">Lorem Ipsum</text:p>
|
|
||||||
<text:p text:style-name="P1"><text:span text:style-name="T1">By lipsum.com</text:span></text:p>
|
|
||||||
<text:p text:style-name="P1">“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”</text:p>
|
|
||||||
<text:p text:style-name="P1">“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”</text:p>
|
|
||||||
<text:p text:style-name="P2"><text:span text:style-name="T1">Comment:</text:span> Exctracted from the lipsum.com website.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="2">Prologue</text:h>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Explanation from the lipsum.com website.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:span text:style-name="T2">Lorem Ipsum</text:span> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</text:p>
|
|
||||||
<text:h text:style-name="P4" text:outline-level="1">Act One</text:h>
|
|
||||||
<text:p text:style-name="P1">“Fusce maximus felis libero”</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="2">Chapter One: Chapter One</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 1.1: Scene One</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_4" text:outline-level="4">1.1.1: Scene One, Section Two</text:h>
|
|
||||||
<text:p text:style-name="Text_20_body">Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 1.2: Scene Two</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_4" text:outline-level="4">1.2.1: Scene Two, Section Two</text:h>
|
|
||||||
<text:p text:style-name="Text_20_body">Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="2">Why do we use it?</text:h>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Comment:</text:span> Exctracted from the lipsum.com website.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:tab />It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:tab />The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:tab />Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="2">Chapter Two: Chapter Two</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 2.1: Scene Three</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 2.2: Scene Four</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 2.3: Scene Five</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.</text:p>
|
|
||||||
<text:p text:style-name="P7">Notes: Characters</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_1" text:outline-level="1">Nobody Owens</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Tag:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.</text:p>
|
|
||||||
<text:p text:style-name="P7">Notes: Plot</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_1" text:outline-level="1">Main Plot</text:h>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Tag:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.</text:p>
|
|
||||||
<text:p text:style-name="P7">Notes: World</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_1" text:outline-level="1">Ancient Europe</text:h>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Tag:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.</text:p>
|
|
||||||
</office:text>
|
|
||||||
</office:body>
|
|
||||||
</office:document>
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset='utf-8'>
|
|
||||||
<title>Lorem Ipsum</title>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
body {font-family: 'DejaVu Sans'; font-size: 11pt;}
|
|
||||||
p {text-align: justify; line-height: 115%; margin-top: 0.00em; margin-bottom: 0.58em;}
|
|
||||||
h1 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 1.00em; margin-bottom: 0.50em;}
|
|
||||||
h2 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 0.83em; margin-bottom: 0.50em;}
|
|
||||||
h3 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
|
|
||||||
h4 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
|
|
||||||
.title {font-size: 2.5em; margin-top: 1.00em; margin-bottom: 0.50em;}
|
|
||||||
.sep, .skip {text-align: center; margin-top: 1.00em; margin-bottom: 1.00em;}
|
|
||||||
a {color: rgb(66, 113, 174);}
|
|
||||||
.tags {color: rgb(245, 135, 31); font-weight: bold;}
|
|
||||||
.break {text-align: left;}
|
|
||||||
.synopsis {font-style: italic;}
|
|
||||||
.comment {font-style: italic; color: rgb(100, 100, 100);}
|
|
||||||
article {width: 800px; margin: 40px auto;}
|
|
||||||
</style>
|
|
||||||
<body>
|
|
||||||
<article>
|
|
||||||
<h1 class='title' style='text-align: center;'>Lorem Ipsum</h1>
|
|
||||||
<p style='text-align: center;'><strong>By lipsum.com</strong></p>
|
|
||||||
<p style='text-align: center;'>“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”</p>
|
|
||||||
<p style='text-align: center;'>“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”</p>
|
|
||||||
<p class='comment'><strong>Comment:</strong> Exctracted from the lipsum.com website.</p>
|
|
||||||
<p>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</p>
|
|
||||||
<p>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
|
|
||||||
<h1 style='page-break-before: always;'>Prologue</h1>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Explanation from the lipsum.com website.</p>
|
|
||||||
<p><em>Lorem Ipsum</em> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Act One</h1>
|
|
||||||
<p style='text-align: center;'>“Fusce maximus felis libero”</p>
|
|
||||||
<h1 style='page-break-before: always;'>Chapter One: Chapter One</h1>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam.</p>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.</p>
|
|
||||||
<h2>Scene 1.1: Scene One</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur.</p>
|
|
||||||
<p>Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.</p>
|
|
||||||
<p>Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.</p>
|
|
||||||
<h3>1.1.1: Scene One, Section Two</h3>
|
|
||||||
<p>Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.</p>
|
|
||||||
<p>Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.</p>
|
|
||||||
<h2>Scene 1.2: Scene Two</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci.</p>
|
|
||||||
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.</p>
|
|
||||||
<p>Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.</p>
|
|
||||||
<p>Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.</p>
|
|
||||||
<h3>1.2.1: Scene Two, Section Two</h3>
|
|
||||||
<p>Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.</p>
|
|
||||||
<p>Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.</p>
|
|
||||||
<p>Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.</p>
|
|
||||||
<h1 style='page-break-before: always;'>Why do we use it?</h1>
|
|
||||||
<p class='comment'><strong>Comment:</strong> Exctracted from the lipsum.com website.</p>
|
|
||||||
<p>	It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.</p>
|
|
||||||
<p>	The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.</p>
|
|
||||||
<p>	Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
|
|
||||||
<h1 style='page-break-before: always;'>Chapter Two: Chapter Two</h1>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue.</p>
|
|
||||||
<p>Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.</p>
|
|
||||||
<h2>Scene 2.1: Scene Three</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</p>
|
|
||||||
<p>Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.</p>
|
|
||||||
<p>Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.</p>
|
|
||||||
<p>Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.</p>
|
|
||||||
<p>Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.</p>
|
|
||||||
<h2>Scene 2.2: Scene Four</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo.</p>
|
|
||||||
<p>Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.</p>
|
|
||||||
<p>Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.</p>
|
|
||||||
<p>Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.</p>
|
|
||||||
<p>Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.</p>
|
|
||||||
<p>Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.</p>
|
|
||||||
<p>Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.</p>
|
|
||||||
<h2>Scene 2.3: Scene Five</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</p>
|
|
||||||
<p>Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.</p>
|
|
||||||
<p>In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.</p>
|
|
||||||
<p>Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.</p>
|
|
||||||
<p>Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.</p>
|
|
||||||
<p>Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Notes: Characters</h1>
|
|
||||||
<h1>Nobody Owens</h1>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Tag:</span> <a name='tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p>Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.</p>
|
|
||||||
<p>Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.</p>
|
|
||||||
<p>Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Notes: Plot</h1>
|
|
||||||
<h1>Main Plot</h1>
|
|
||||||
<p><span class='tags'>Tag:</span> <a name='tag_Main'>Main</a></p>
|
|
||||||
<p>Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet.</p>
|
|
||||||
<p>Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Notes: World</h1>
|
|
||||||
<h1>Ancient Europe</h1>
|
|
||||||
<p><span class='tags'>Tag:</span> <a name='tag_Europe'>Europe</a></p>
|
|
||||||
<p>Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.</p>
|
|
||||||
<p>Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.</p>
|
|
||||||
<p>Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.</p>
|
|
||||||
</article>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
# Lorem Ipsum
|
|
||||||
|
|
||||||
**By lipsum.com**
|
|
||||||
|
|
||||||
“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”
|
|
||||||
|
|
||||||
“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”
|
|
||||||
|
|
||||||
**Comment:** Exctracted from the lipsum.com website.
|
|
||||||
|
|
||||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.
|
|
||||||
|
|
||||||
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.
|
|
||||||
|
|
||||||
## Prologue
|
|
||||||
|
|
||||||
**Synopsis:** Explanation from the lipsum.com website.
|
|
||||||
|
|
||||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
|
||||||
|
|
||||||
# Act One
|
|
||||||
|
|
||||||
“Fusce maximus felis libero”
|
|
||||||
|
|
||||||
## Chapter One: Chapter One
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam.
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.
|
|
||||||
|
|
||||||
### Scene 1.1: Scene One
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur.
|
|
||||||
|
|
||||||
Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.
|
|
||||||
|
|
||||||
Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.
|
|
||||||
|
|
||||||
#### 1.1.1: Scene One, Section Two
|
|
||||||
|
|
||||||
Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.
|
|
||||||
|
|
||||||
Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.
|
|
||||||
|
|
||||||
### Scene 1.2: Scene Two
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci.
|
|
||||||
|
|
||||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.
|
|
||||||
|
|
||||||
Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.
|
|
||||||
|
|
||||||
Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.
|
|
||||||
|
|
||||||
#### 1.2.1: Scene Two, Section Two
|
|
||||||
|
|
||||||
Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.
|
|
||||||
|
|
||||||
Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.
|
|
||||||
|
|
||||||
Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.
|
|
||||||
|
|
||||||
## Why do we use it?
|
|
||||||
|
|
||||||
**Comment:** Exctracted from the lipsum.com website.
|
|
||||||
|
|
||||||
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.
|
|
||||||
|
|
||||||
The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.
|
|
||||||
|
|
||||||
Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
|
|
||||||
|
|
||||||
## Chapter Two: Chapter Two
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue.
|
|
||||||
|
|
||||||
Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.
|
|
||||||
|
|
||||||
### Scene 2.1: Scene Three
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
|
||||||
|
|
||||||
Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.
|
|
||||||
|
|
||||||
Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.
|
|
||||||
|
|
||||||
Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.
|
|
||||||
|
|
||||||
Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.
|
|
||||||
|
|
||||||
### Scene 2.2: Scene Four
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo.
|
|
||||||
|
|
||||||
Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.
|
|
||||||
|
|
||||||
Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.
|
|
||||||
|
|
||||||
Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.
|
|
||||||
|
|
||||||
Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.
|
|
||||||
|
|
||||||
Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.
|
|
||||||
|
|
||||||
Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.
|
|
||||||
|
|
||||||
### Scene 2.3: Scene Five
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
|
||||||
|
|
||||||
Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.
|
|
||||||
|
|
||||||
In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.
|
|
||||||
|
|
||||||
Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.
|
|
||||||
|
|
||||||
Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.
|
|
||||||
|
|
||||||
Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
|
|
||||||
|
|
||||||
# Notes: Characters
|
|
||||||
|
|
||||||
# Nobody Owens
|
|
||||||
|
|
||||||
**Tag:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
|
|
||||||
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
|
|
||||||
|
|
||||||
Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.
|
|
||||||
|
|
||||||
Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.
|
|
||||||
|
|
||||||
# Notes: Plot
|
|
||||||
|
|
||||||
# Main Plot
|
|
||||||
|
|
||||||
**Tag:** Main
|
|
||||||
|
|
||||||
Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet.
|
|
||||||
|
|
||||||
Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.
|
|
||||||
|
|
||||||
# Notes: World
|
|
||||||
|
|
||||||
# Ancient Europe
|
|
||||||
|
|
||||||
**Tag:** Europe
|
|
||||||
|
|
||||||
Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.
|
|
||||||
|
|
||||||
Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.
|
|
||||||
|
|
||||||
Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.
|
|
||||||
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
#! Lorem Ipsum
|
|
||||||
|
|
||||||
**By lipsum.com**
|
|
||||||
|
|
||||||
“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”
|
|
||||||
|
|
||||||
“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”
|
|
||||||
|
|
||||||
|
|
||||||
% Exctracted from the lipsum.com website.
|
|
||||||
|
|
||||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.
|
|
||||||
|
|
||||||
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.
|
|
||||||
|
|
||||||
##! Prologue
|
|
||||||
|
|
||||||
% Synopsis:Explanation from the lipsum.com website.
|
|
||||||
|
|
||||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
|
||||||
|
|
||||||
# Act One
|
|
||||||
|
|
||||||
“Fusce maximus felis libero”
|
|
||||||
|
|
||||||
## Chapter One
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam.
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.
|
|
||||||
|
|
||||||
### Scene One
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur.
|
|
||||||
|
|
||||||
Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.
|
|
||||||
|
|
||||||
Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.
|
|
||||||
|
|
||||||
#### Scene One, Section Two
|
|
||||||
|
|
||||||
Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.
|
|
||||||
|
|
||||||
Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.
|
|
||||||
|
|
||||||
### Scene Two
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci.
|
|
||||||
|
|
||||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.
|
|
||||||
|
|
||||||
Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.
|
|
||||||
|
|
||||||
Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.
|
|
||||||
|
|
||||||
#### Scene Two, Section Two
|
|
||||||
|
|
||||||
Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.
|
|
||||||
|
|
||||||
Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.
|
|
||||||
|
|
||||||
Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.
|
|
||||||
|
|
||||||
##! Why do we use it?
|
|
||||||
|
|
||||||
% Exctracted from the lipsum.com website.
|
|
||||||
|
|
||||||
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.
|
|
||||||
|
|
||||||
The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.
|
|
||||||
|
|
||||||
Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
|
|
||||||
|
|
||||||
## Chapter Two
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue.
|
|
||||||
|
|
||||||
Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.
|
|
||||||
|
|
||||||
### Scene Three
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
|
||||||
|
|
||||||
Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.
|
|
||||||
|
|
||||||
Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.
|
|
||||||
|
|
||||||
Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.
|
|
||||||
|
|
||||||
Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.
|
|
||||||
|
|
||||||
### Scene Four
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo.
|
|
||||||
|
|
||||||
Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.
|
|
||||||
|
|
||||||
Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.
|
|
||||||
|
|
||||||
Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.
|
|
||||||
|
|
||||||
Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.
|
|
||||||
|
|
||||||
Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.
|
|
||||||
|
|
||||||
Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.
|
|
||||||
|
|
||||||
### Scene Five
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
|
||||||
|
|
||||||
Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.
|
|
||||||
|
|
||||||
In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.
|
|
||||||
|
|
||||||
Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.
|
|
||||||
|
|
||||||
Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.
|
|
||||||
|
|
||||||
Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
|
|
||||||
|
|
||||||
# Notes: Characters
|
|
||||||
|
|
||||||
# Nobody Owens
|
|
||||||
|
|
||||||
@tag: Bod
|
|
||||||
@plot: Main
|
|
||||||
|
|
||||||
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
|
|
||||||
|
|
||||||
Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.
|
|
||||||
|
|
||||||
Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.
|
|
||||||
|
|
||||||
# Notes: Plot
|
|
||||||
|
|
||||||
# Main Plot
|
|
||||||
|
|
||||||
@tag: Main
|
|
||||||
|
|
||||||
Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet.
|
|
||||||
|
|
||||||
Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.
|
|
||||||
|
|
||||||
# Notes: World
|
|
||||||
|
|
||||||
# Ancient Europe
|
|
||||||
|
|
||||||
@tag: Europe
|
|
||||||
|
|
||||||
Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.
|
|
||||||
|
|
||||||
Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.
|
|
||||||
|
|
||||||
Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.
|
|
||||||
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
|
||||||
<office:meta>
|
|
||||||
<meta:creation-date>2023-05-29T22:11:58</meta:creation-date>
|
|
||||||
<meta:generator>novelWriter/2.0.7</meta:generator>
|
|
||||||
<meta:initial-creator>lipsum.com</meta:initial-creator>
|
|
||||||
<meta:editing-cycles>40</meta:editing-cycles>
|
|
||||||
<meta:editing-duration>P0DT0H31M45S</meta:editing-duration>
|
|
||||||
<dc:title>Lorem Ipsum</dc:title>
|
|
||||||
<dc:date>2023-05-29T22:11:58</dc:date>
|
|
||||||
<dc:creator>lipsum.com</dc:creator>
|
|
||||||
</office:meta>
|
|
||||||
<office:font-face-decls>
|
|
||||||
<style:font-face style:name="DejaVu Sans" style:font-pitch="variable" />
|
|
||||||
</office:font-face-decls>
|
|
||||||
<office:styles>
|
|
||||||
<style:default-style style:family="paragraph">
|
|
||||||
<style:paragraph-properties style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" fo:language="en" fo:country="GB" />
|
|
||||||
</style:default-style>
|
|
||||||
<style:style style:name="Standard" style:family="paragraph" style:class="text">
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard" style:next-style-name="Text_20_body" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" fo:keep-with-next="always" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="13pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Header_20_and_20_Footer" style:display-name="Header and Footer" style:family="paragraph" style:parent-style-name="Standard" style:class="extra" />
|
|
||||||
<style:style style:name="Text_20_body" style:family="paragraph" style:display-name="Text body" style:parent-style-name="Standard" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.227cm" fo:line-height="115%" fo:text-align="justify" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Text_20_Meta" style:family="paragraph" style:display-name="Text Meta" style:parent-style-name="Standard" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.227cm" fo:line-height="115%" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="11pt" fo:color="#813709" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Title" style:family="paragraph" style:display-name="Title" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:class="chapter">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.388cm" fo:margin-bottom="0.194cm" fo:text-align="center" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="28pt" fo:font-weight="bold" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_1" style:family="paragraph" style:display-name="Heading 1" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.388cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="22pt" fo:font-weight="bold" fo:color="#2a6099" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_2" style:family="paragraph" style:display-name="Heading 2" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="2" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.324cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="18pt" fo:font-weight="bold" fo:color="#2a6099" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_3" style:family="paragraph" style:display-name="Heading 3" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="3" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="14pt" fo:font-weight="bold" fo:color="#444444" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Heading_20_4" style:family="paragraph" style:display-name="Heading 4" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="4" style:class="text">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.227cm" fo:margin-bottom="0.194cm" />
|
|
||||||
<style:text-properties style:font-name="DejaVu Sans" fo:font-family="'DejaVu Sans'" fo:font-size="13pt" fo:font-weight="bold" fo:color="#444444" loext:opacity="100%" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="Header" style:family="paragraph" style:display-name="Header" style:parent-style-name="Header_20_and_20_Footer">
|
|
||||||
<style:paragraph-properties fo:text-align="right" />
|
|
||||||
</style:style>
|
|
||||||
</office:styles>
|
|
||||||
<office:automatic-styles>
|
|
||||||
<style:page-layout style:name="PM1">
|
|
||||||
<style:page-layout-properties fo:margin-top="2.000cm" fo:margin-bottom="2.000cm" fo:margin-left="2.000cm" fo:margin-right="2.000cm" />
|
|
||||||
<style:header-style>
|
|
||||||
<style:header-footer-properties fo:min-height="0.600cm" fo:margin-left="0.000cm" fo:margin-right="0.000cm" fo:margin-bottom="0.500cm" />
|
|
||||||
</style:header-style>
|
|
||||||
</style:page-layout>
|
|
||||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Text_20_body">
|
|
||||||
<style:paragraph-properties fo:text-align="center" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P2" style:family="paragraph" style:parent-style-name="Text_20_Meta">
|
|
||||||
<style:paragraph-properties fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P3" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
|
||||||
<style:paragraph-properties fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P4" style:family="paragraph" style:parent-style-name="Heading_20_1">
|
|
||||||
<style:paragraph-properties fo:text-align="center" fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P5" style:family="paragraph" style:parent-style-name="Text_20_Meta">
|
|
||||||
<style:paragraph-properties fo:margin-bottom="0.000cm" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P6" style:family="paragraph" style:parent-style-name="Text_20_Meta">
|
|
||||||
<style:paragraph-properties fo:margin-top="0.000cm" fo:margin-bottom="0.000cm" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="P7" style:family="paragraph" style:parent-style-name="Title">
|
|
||||||
<style:paragraph-properties fo:text-align="center" fo:break-before="page" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="T1" style:family="text">
|
|
||||||
<style:text-properties fo:font-weight="bold" />
|
|
||||||
</style:style>
|
|
||||||
<style:style style:name="T2" style:family="text">
|
|
||||||
<style:text-properties fo:font-style="italic" />
|
|
||||||
</style:style>
|
|
||||||
</office:automatic-styles>
|
|
||||||
<office:master-styles>
|
|
||||||
<style:master-page style:name="Standard" style:page-layout-name="PM1">
|
|
||||||
<style:header>
|
|
||||||
<text:p text:style-name="Header">Lorem Ipsum / lipsum.com / <text:page-number text:select-page="current">2</text:page-number></text:p>
|
|
||||||
</style:header>
|
|
||||||
<style:header-first>
|
|
||||||
<text:p text:style-name="Header" />
|
|
||||||
</style:header-first>
|
|
||||||
</style:master-page>
|
|
||||||
</office:master-styles>
|
|
||||||
<office:body>
|
|
||||||
<office:text>
|
|
||||||
<text:p text:style-name="Title">Lorem Ipsum</text:p>
|
|
||||||
<text:p text:style-name="P1"><text:span text:style-name="T1">By lipsum.com</text:span></text:p>
|
|
||||||
<text:p text:style-name="P1">“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”</text:p>
|
|
||||||
<text:p text:style-name="P1">“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”</text:p>
|
|
||||||
<text:p text:style-name="P2"><text:span text:style-name="T1">Comment:</text:span> Exctracted from the lipsum.com website.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="2">Prologue</text:h>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Explanation from the lipsum.com website.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:span text:style-name="T2">Lorem Ipsum</text:span> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</text:p>
|
|
||||||
<text:h text:style-name="P4" text:outline-level="1">Act One</text:h>
|
|
||||||
<text:p text:style-name="P1">“Fusce maximus felis libero”</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="2">Chapter One: Chapter One</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 1.1: Scene One</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_4" text:outline-level="4">1.1.1: Scene One, Section Two</text:h>
|
|
||||||
<text:p text:style-name="Text_20_body">Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 1.2: Scene Two</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_4" text:outline-level="4">1.2.1: Scene Two, Section Two</text:h>
|
|
||||||
<text:p text:style-name="Text_20_body">Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="2">Why do we use it?</text:h>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Comment:</text:span> Exctracted from the lipsum.com website.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:tab />It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:tab />The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body"><text:tab />Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</text:p>
|
|
||||||
<text:h text:style-name="P3" text:outline-level="2">Chapter Two: Chapter Two</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 2.1: Scene Three</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 2.2: Scene Four</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 2.3: Scene Five</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Point of View:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="P6"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Locations:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Synopsis:</text:span> Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.</text:p>
|
|
||||||
<text:p text:style-name="P7">Notes: Characters</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_1" text:outline-level="1">Nobody Owens</text:h>
|
|
||||||
<text:p text:style-name="P5"><text:span text:style-name="T1">Tag:</text:span> Bod</text:p>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Plot:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.</text:p>
|
|
||||||
<text:p text:style-name="P7">Notes: Plot</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_1" text:outline-level="1">Main Plot</text:h>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Tag:</text:span> Main</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.</text:p>
|
|
||||||
<text:p text:style-name="P7">Notes: World</text:p>
|
|
||||||
<text:h text:style-name="Heading_20_1" text:outline-level="1">Ancient Europe</text:h>
|
|
||||||
<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T1">Tag:</text:span> Europe</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.</text:p>
|
|
||||||
<text:p text:style-name="Text_20_body">Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.</text:p>
|
|
||||||
</office:text>
|
|
||||||
</office:body>
|
|
||||||
</office:document>
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset='utf-8'>
|
|
||||||
<title>Lorem Ipsum</title>
|
|
||||||
</head>
|
|
||||||
<style>
|
|
||||||
body {font-family: 'DejaVu Sans'; font-size: 11pt;}
|
|
||||||
p {text-align: justify; line-height: 115%; margin-top: 0.00em; margin-bottom: 0.58em;}
|
|
||||||
h1 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 1.00em; margin-bottom: 0.50em;}
|
|
||||||
h2 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 0.83em; margin-bottom: 0.50em;}
|
|
||||||
h3 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
|
|
||||||
h4 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
|
|
||||||
.title {font-size: 2.5em; margin-top: 1.00em; margin-bottom: 0.50em;}
|
|
||||||
.sep, .skip {text-align: center; margin-top: 1.00em; margin-bottom: 1.00em;}
|
|
||||||
a {color: rgb(66, 113, 174);}
|
|
||||||
.tags {color: rgb(245, 135, 31); font-weight: bold;}
|
|
||||||
.break {text-align: left;}
|
|
||||||
.synopsis {font-style: italic;}
|
|
||||||
.comment {font-style: italic; color: rgb(100, 100, 100);}
|
|
||||||
article {width: 800px; margin: 40px auto;}
|
|
||||||
</style>
|
|
||||||
<body>
|
|
||||||
<article>
|
|
||||||
<h1 class='title' style='text-align: center;'>Lorem Ipsum</h1>
|
|
||||||
<p style='text-align: center;'><strong>By lipsum.com</strong></p>
|
|
||||||
<p style='text-align: center;'>“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”</p>
|
|
||||||
<p style='text-align: center;'>“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”</p>
|
|
||||||
<p class='comment'><strong>Comment:</strong> Exctracted from the lipsum.com website.</p>
|
|
||||||
<p>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</p>
|
|
||||||
<p>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
|
|
||||||
<h1 style='page-break-before: always;'>Prologue</h1>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Explanation from the lipsum.com website.</p>
|
|
||||||
<p><em>Lorem Ipsum</em> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Act One</h1>
|
|
||||||
<p style='text-align: center;'>“Fusce maximus felis libero”</p>
|
|
||||||
<h1 style='page-break-before: always;'>Chapter One: Chapter One</h1>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam.</p>
|
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.</p>
|
|
||||||
<h2>Scene 1.1: Scene One</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur.</p>
|
|
||||||
<p>Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.</p>
|
|
||||||
<p>Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.</p>
|
|
||||||
<h3>1.1.1: Scene One, Section Two</h3>
|
|
||||||
<p>Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.</p>
|
|
||||||
<p>Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.</p>
|
|
||||||
<h2>Scene 1.2: Scene Two</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci.</p>
|
|
||||||
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.</p>
|
|
||||||
<p>Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.</p>
|
|
||||||
<p>Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.</p>
|
|
||||||
<h3>1.2.1: Scene Two, Section Two</h3>
|
|
||||||
<p>Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.</p>
|
|
||||||
<p>Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.</p>
|
|
||||||
<p>Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.</p>
|
|
||||||
<h1 style='page-break-before: always;'>Why do we use it?</h1>
|
|
||||||
<p class='comment'><strong>Comment:</strong> Exctracted from the lipsum.com website.</p>
|
|
||||||
<p> It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.</p>
|
|
||||||
<p> The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.</p>
|
|
||||||
<p> Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
|
|
||||||
<h1 style='page-break-before: always;'>Chapter Two: Chapter Two</h1>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue.</p>
|
|
||||||
<p>Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.</p>
|
|
||||||
<h2>Scene 2.1: Scene Three</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</p>
|
|
||||||
<p>Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.</p>
|
|
||||||
<p>Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.</p>
|
|
||||||
<p>Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.</p>
|
|
||||||
<p>Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.</p>
|
|
||||||
<h2>Scene 2.2: Scene Four</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo.</p>
|
|
||||||
<p>Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.</p>
|
|
||||||
<p>Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.</p>
|
|
||||||
<p>Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.</p>
|
|
||||||
<p>Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.</p>
|
|
||||||
<p>Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.</p>
|
|
||||||
<p>Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.</p>
|
|
||||||
<h2>Scene 2.3: Scene Five</h2>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Locations:</span> <a href='#tag_Europe'>Europe</a></p>
|
|
||||||
<p class='synopsis'><strong>Synopsis:</strong> Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</p>
|
|
||||||
<p>Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.</p>
|
|
||||||
<p>In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.</p>
|
|
||||||
<p>Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.</p>
|
|
||||||
<p>Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.</p>
|
|
||||||
<p>Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Notes: Characters</h1>
|
|
||||||
<h1>Nobody Owens</h1>
|
|
||||||
<p style='margin-bottom: 0;'><span class='tags'>Tag:</span> <a name='tag_Bod'>Bod</a></p>
|
|
||||||
<p style='margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
|
|
||||||
<p>Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.</p>
|
|
||||||
<p>Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.</p>
|
|
||||||
<p>Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Notes: Plot</h1>
|
|
||||||
<h1>Main Plot</h1>
|
|
||||||
<p><span class='tags'>Tag:</span> <a name='tag_Main'>Main</a></p>
|
|
||||||
<p>Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet.</p>
|
|
||||||
<p>Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.</p>
|
|
||||||
<h1 class='title' style='text-align: center; page-break-before: always;'>Notes: World</h1>
|
|
||||||
<h1>Ancient Europe</h1>
|
|
||||||
<p><span class='tags'>Tag:</span> <a name='tag_Europe'>Europe</a></p>
|
|
||||||
<p>Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.</p>
|
|
||||||
<p>Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.</p>
|
|
||||||
<p>Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.</p>
|
|
||||||
</article>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
# Lorem Ipsum
|
|
||||||
|
|
||||||
**By lipsum.com**
|
|
||||||
|
|
||||||
“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”
|
|
||||||
|
|
||||||
“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”
|
|
||||||
|
|
||||||
**Comment:** Exctracted from the lipsum.com website.
|
|
||||||
|
|
||||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.
|
|
||||||
|
|
||||||
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.
|
|
||||||
|
|
||||||
## Prologue
|
|
||||||
|
|
||||||
**Synopsis:** Explanation from the lipsum.com website.
|
|
||||||
|
|
||||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
|
||||||
|
|
||||||
# Act One
|
|
||||||
|
|
||||||
“Fusce maximus felis libero”
|
|
||||||
|
|
||||||
## Chapter One: Chapter One
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam.
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.
|
|
||||||
|
|
||||||
### Scene 1.1: Scene One
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur.
|
|
||||||
|
|
||||||
Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.
|
|
||||||
|
|
||||||
Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.
|
|
||||||
|
|
||||||
#### 1.1.1: Scene One, Section Two
|
|
||||||
|
|
||||||
Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.
|
|
||||||
|
|
||||||
Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.
|
|
||||||
|
|
||||||
### Scene 1.2: Scene Two
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci.
|
|
||||||
|
|
||||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.
|
|
||||||
|
|
||||||
Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.
|
|
||||||
|
|
||||||
Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.
|
|
||||||
|
|
||||||
#### 1.2.1: Scene Two, Section Two
|
|
||||||
|
|
||||||
Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.
|
|
||||||
|
|
||||||
Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.
|
|
||||||
|
|
||||||
Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.
|
|
||||||
|
|
||||||
## Why do we use it?
|
|
||||||
|
|
||||||
**Comment:** Exctracted from the lipsum.com website.
|
|
||||||
|
|
||||||
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.
|
|
||||||
|
|
||||||
The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.
|
|
||||||
|
|
||||||
Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
|
|
||||||
|
|
||||||
## Chapter Two: Chapter Two
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue.
|
|
||||||
|
|
||||||
Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.
|
|
||||||
|
|
||||||
### Scene 2.1: Scene Three
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
|
||||||
|
|
||||||
Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.
|
|
||||||
|
|
||||||
Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.
|
|
||||||
|
|
||||||
Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.
|
|
||||||
|
|
||||||
Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.
|
|
||||||
|
|
||||||
### Scene 2.2: Scene Four
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo.
|
|
||||||
|
|
||||||
Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.
|
|
||||||
|
|
||||||
Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.
|
|
||||||
|
|
||||||
Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.
|
|
||||||
|
|
||||||
Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.
|
|
||||||
|
|
||||||
Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.
|
|
||||||
|
|
||||||
Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.
|
|
||||||
|
|
||||||
### Scene 2.3: Scene Five
|
|
||||||
|
|
||||||
**Point of View:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
**Locations:** Europe
|
|
||||||
|
|
||||||
**Synopsis:** Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
|
||||||
|
|
||||||
Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.
|
|
||||||
|
|
||||||
In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.
|
|
||||||
|
|
||||||
Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.
|
|
||||||
|
|
||||||
Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.
|
|
||||||
|
|
||||||
Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
|
|
||||||
|
|
||||||
# Notes: Characters
|
|
||||||
|
|
||||||
# Nobody Owens
|
|
||||||
|
|
||||||
**Tag:** Bod
|
|
||||||
**Plot:** Main
|
|
||||||
|
|
||||||
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
|
|
||||||
|
|
||||||
Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.
|
|
||||||
|
|
||||||
Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.
|
|
||||||
|
|
||||||
# Notes: Plot
|
|
||||||
|
|
||||||
# Main Plot
|
|
||||||
|
|
||||||
**Tag:** Main
|
|
||||||
|
|
||||||
Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet.
|
|
||||||
|
|
||||||
Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.
|
|
||||||
|
|
||||||
# Notes: World
|
|
||||||
|
|
||||||
# Ancient Europe
|
|
||||||
|
|
||||||
**Tag:** Europe
|
|
||||||
|
|
||||||
Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.
|
|
||||||
|
|
||||||
Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.
|
|
||||||
|
|
||||||
Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.
|
|
||||||
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
#! Lorem Ipsum
|
|
||||||
|
|
||||||
**By lipsum.com**
|
|
||||||
|
|
||||||
“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”
|
|
||||||
|
|
||||||
“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”
|
|
||||||
|
|
||||||
|
|
||||||
% Exctracted from the lipsum.com website.
|
|
||||||
|
|
||||||
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.
|
|
||||||
|
|
||||||
The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.
|
|
||||||
|
|
||||||
##! Prologue
|
|
||||||
|
|
||||||
% Synopsis:Explanation from the lipsum.com website.
|
|
||||||
|
|
||||||
_Lorem Ipsum_ is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
|
||||||
|
|
||||||
# Act One
|
|
||||||
|
|
||||||
“Fusce maximus felis libero”
|
|
||||||
|
|
||||||
## Chapter One
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam.
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam. Praesent magna nunc, lacinia sit amet quam eget, aliquet ultrices justo. Morbi ornare enim et lorem rutrum finibus ut eu dolor. Aliquam a orci odio. Ut ultrices sem quis massa placerat, eget mollis nisl cursus. Cras vel sagittis justo. Ut non ultricies leo. Maecenas rutrum velit in est varius, et egestas massa pulvinar.
|
|
||||||
|
|
||||||
### Scene One
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur.
|
|
||||||
|
|
||||||
Aenean ut placerat velit. Etiam laoreet ullamcorper risus, eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. Etiam finibus nisi vel mi molestie consectetur. Donec quis ante nunc. Mauris ut leo ipsum. Vestibulum est neque, hendrerit nec neque a, ullamcorper lobortis tellus. Fusce sollicitudin purus quis congue bibendum. Aliquam condimentum ipsum tristique blandit tristique. Donec pulvinar neque ac suscipit malesuada.
|
|
||||||
|
|
||||||
Aliquam ut nisl arcu. Ut ultricies, lorem dignissim rutrum convallis, risus orci tempus lectus, congue feugiat sem lectus vitae odio. Duis sit amet justo finibus, hendrerit nulla at, ullamcorper enim. Praesent vel tellus sit amet tellus vulputate bibendum. Morbi eleifend sagittis sem, ac volutpat ante congue non. In hac habitasse platea dictumst. Morbi lobortis fermentum elit, dignissim sagittis ligula volutpat lacinia. Vestibulum eu interdum odio. Integer ac purus commodo metus congue tempor non at urna. Sed eget tortor vel quam viverra egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec non convallis mauris, ac feugiat ex.
|
|
||||||
|
|
||||||
#### Scene One, Section Two
|
|
||||||
|
|
||||||
Integer vel libero ipsum. Donec varius aliquam libero, sit amet commodo urna hendrerit non. Nullam quis erat mollis nunc viverra volutpat tincidunt in odio. Nam vitae quam sem. Aliquam suscipit nulla non lorem pharetra semper. Ut suscipit erat eu ligula accumsan ultrices. Phasellus nisl tellus, placerat sed laoreet id, consectetur nec dolor. Sed fringilla ipsum id dapibus posuere. Aenean finibus pharetra tincidunt. Ut molestie malesuada nulla, id posuere lorem tincidunt eu. Aliquam tempor eros a est vulputate, scelerisque pulvinar ipsum fermentum. In hac habitasse platea dictumst.
|
|
||||||
|
|
||||||
Curabitur congue, justo quis interdum fermentum, tellus nulla imperdiet sapien, eu interdum enim tellus condimentum metus. Vivamus nunc velit, dignissim ut ultrices sit amet, ultricies quis enim. Donec ut vestibulum neque. Vivamus semper neque id ex ullamcorper varius. Fusce mattis nibh viverra lorem sagittis, et tempor arcu congue. Suspendisse sit amet felis sed urna facilisis mattis eget vitae arcu. Proin eu magna hendrerit, tristique sem maximus, placerat diam. Nulla tristique sed velit sit amet varius. Etiam vel ornare magna, in vulputate arcu. Cras velit orci, tincidunt sed volutpat cursus, bibendum vel sem. Nunc vulputate pharetra tortor, ac consectetur neque tincidunt sit amet. Nulla ornare mi sed mi dignissim ultricies. Ut tincidunt bibendum mauris, sed elementum ex vulputate vel. Mauris fermentum, felis nec vehicula congue, felis lorem facilisis erat, a dictum dolor augue vitae quam. Maecenas rutrum tortor nec consequat eleifend.
|
|
||||||
|
|
||||||
### Scene Two
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci.
|
|
||||||
|
|
||||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer sapien nulla, dictum at lacus a, dignissim consectetur dolor. Nunc vel eleifend lacus, eu dapibus orci. Vestibulum facilisis bibendum aliquam. Aliquam posuere, turpis ac bibendum varius, sem tellus venenatis risus, in elementum massa enim ac lorem. Integer in sem ac diam blandit ultricies ut in nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet erat est. Curabitur vitae cursus justo, sit amet placerat dolor. Vivamus eu felis hendrerit, tincidunt massa rutrum, maximus arcu. Pellentesque commodo justo odio, vel rutrum nulla tincidunt eu. Integer non neque condimentum, convallis diam non, varius ligula. Aliquam eget sapien mauris. Aenean pharetra nunc nisi, vel maximus ante tristique sit amet. Aliquam risus metus, interdum non odio eu, consectetur lacinia sapien.
|
|
||||||
|
|
||||||
Proin vitae gravida nisl. Integer viverra orci turpis, sit amet pretium ligula facilisis consequat. Nulla interdum commodo metus, mollis consequat dui tincidunt et. Proin consequat bibendum justo id commodo. Fusce fermentum nunc turpis, eu vestibulum risus feugiat ut. Sed scelerisque vel ligula ut interdum. Suspendisse ac blandit ligula, sagittis fringilla dolor. In tincidunt convallis diam et ornare. Aenean id dignissim est, ut rhoncus quam. Donec vitae nisl velit. In convallis nibh ut augue dignissim, eu elementum quam cursus. Phasellus in lectus lorem. Curabitur in pellentesque nisi, at gravida sapien. Sed cursus justo volutpat lacus placerat, sit amet dignissim turpis commodo. Aliquam vitae orci eget nulla posuere condimentum in ut felis.
|
|
||||||
|
|
||||||
Nulla accumsan ante in pulvinar efficitur. Nulla non velit quis urna hendrerit bibendum. Suspendisse ultrices ante eu justo malesuada, sed fermentum enim rutrum. Nunc fermentum pharetra felis, vitae sollicitudin quam rutrum porta. Aliquam fringilla velit a mi laoreet, et luctus est rutrum. In gravida non ipsum sit amet tempus. Curabitur et eleifend purus. Nulla facilisi.
|
|
||||||
|
|
||||||
#### Scene Two, Section Two
|
|
||||||
|
|
||||||
Suspendisse potenti. Fusce tempus lorem nec laoreet suscipit. Fusce vulputate nisl ac diam tincidunt, nec malesuada quam pellentesque. Maecenas congue, tellus quis commodo rutrum, magna leo egestas arcu, quis suscipit ex risus id ligula. Suspendisse potenti. Morbi blandit lacus vitae laoreet vulputate. Donec vitae tellus eleifend, lobortis eros eu, tincidunt enim. Nullam et ullamcorper nisi. Vivamus tellus ex, lobortis quis rutrum ut, dapibus sit amet turpis. Phasellus pellentesque metus diam, commodo tristique ante commodo ac. Ut mollis ipsum nec diam blandit sollicitudin. Duis bibendum lacus nec commodo dapibus. Sed condimentum luctus ante, id ultricies urna varius nec. Nam convallis magna nec bibendum ultrices. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed auctor pharetra quam, vitae porta ex bibendum eu.
|
|
||||||
|
|
||||||
Vivamus ut venenatis lectus. Phasellus nec elit id sem dictum ornare. Quisque feugiat, diam eget sagittis ultricies, orci turpis efficitur nisi, et fringilla justo odio nec nibh. In hac habitasse platea dictumst. Sed tempus bibendum feugiat. Etiam luctus mauris arcu, non interdum ipsum ultrices id. Vivamus blandit urna sit amet scelerisque vulputate. Quisque in metus eget massa rutrum dictum sit amet sed nulla. Vivamus vel efficitur dolor.
|
|
||||||
|
|
||||||
Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, vestibulum vitae augue. Praesent id ante sit amet odio venenatis placerat a at erat. Sed sed metus sed nisi dictum varius. Integer tincidunt fermentum purus ac porta. Fusce porttitor non risus eget tristique. Donec augue nunc, maximus at fermentum vel, varius et neque. Ut sed consectetur mauris. Quisque ipsum enim, porttitor vitae imperdiet sit amet, tempor et mauris. Aliquam malesuada tincidunt lectus quis blandit. Sed commodo orci felis, quis ultrices tellus facilisis sed. Nunc vel varius est. Duis ullamcorper eu metus in pulvinar. Morbi at sapien dictum, rutrum mauris eget, interdum tellus.
|
|
||||||
|
|
||||||
##! Why do we use it?
|
|
||||||
|
|
||||||
% Exctracted from the lipsum.com website.
|
|
||||||
|
|
||||||
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.
|
|
||||||
|
|
||||||
The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.
|
|
||||||
|
|
||||||
Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
|
|
||||||
|
|
||||||
## Chapter Two
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue.
|
|
||||||
|
|
||||||
Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue. Vestibulum pharetra fermentum leo, sed faucibus eros placerat quis. In hac habitasse platea dictumst. Donec metus massa, rutrum quis consequat et, tincidunt ac felis. Duis mollis metus ac nunc tincidunt blandit. Ut aliquet velit eu odio pharetra condimentum. Integer rutrum lacus orci, id venenatis libero accumsan at.
|
|
||||||
|
|
||||||
### Scene Three
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
|
||||||
|
|
||||||
Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean tincidunt lacus vitae nibh elementum eleifend. Sed rutrum condimentum sem quis blandit. Duis imperdiet libero metus, quis convallis quam faucibus a. Nulla ligula est, semper quis sollicitudin et, pretium id justo. Curabitur pharetra risus eget consectetur commodo. Duis mattis arcu non est condimentum, id venenatis risus volutpat. Pellentesque aliquet mauris non mauris porttitor ultrices. Phasellus ut vestibulum mi. Suspendisse malesuada metus lorem, a malesuada orci rhoncus a. Praesent euismod convallis ante, lacinia tincidunt ex egestas id. Praesent sit amet efficitur sapien. Morbi tincidunt volutpat nunc sed dictum. Aliquam ultrices metus id fermentum lobortis.
|
|
||||||
|
|
||||||
Pellentesque id sagittis dui. Praesent ut nisi sit amet libero euismod ornare. Vestibulum vehicula, lorem eget aliquet imperdiet, eros nulla iaculis mi, vel bibendum est dui sed orci. Nullam vitae lorem rutrum, euismod lacus id, ullamcorper lectus. Duis nec commodo mi, a fringilla diam. Vestibulum molestie nibh tristique, viverra augue non, aliquet metus. Phasellus a tellus ac nisl tempor aliquet. Nulla vitae sapien rutrum augue ornare ultrices a quis nisi. Sed pulvinar tincidunt ex. Fusce vel sem vitae ante pellentesque lobortis.
|
|
||||||
|
|
||||||
Maecenas ullamcorper lacus nec turpis finibus aliquet eget rutrum augue. Integer lorem erat, faucibus non lacus lacinia, pulvinar egestas felis. Proin rutrum nunc eget nulla varius, id blandit mauris tincidunt. Donec sit amet ullamcorper nisi, ut efficitur mi. Aliquam aliquet, nulla eget rhoncus tristique, justo lorem consectetur dui, id ornare leo odio sed tellus. Curabitur interdum velit a turpis condimentum venenatis. Nunc rhoncus sem ac augue auctor, nec malesuada ex fringilla. Vestibulum egestas diam sed leo consectetur vulputate quis eget enim. Nam tincidunt metus sit amet maximus ullamcorper. Sed placerat velit vitae massa efficitur viverra. Etiam eleifend dignissim ante, sed luctus nisl tristique a. In vestibulum pharetra dolor in molestie. Vivamus auctor massa ac magna imperdiet, sit amet iaculis turpis finibus.
|
|
||||||
|
|
||||||
Aenean dapibus vulputate purus, sit amet tempor nunc suscipit consequat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris auctor congue eros, non pellentesque neque dapibus ac. Vestibulum non leo nec urna lacinia eleifend quis et diam. Praesent eu nisi magna. Nulla at magna massa. Suspendisse porta varius scelerisque. Duis at auctor dolor, non dapibus urna. Nunc venenatis feugiat magna non molestie. Aliquam non ornare ex. Quisque eu ultrices velit, quis pellentesque eros. Phasellus eleifend, elit id imperdiet aliquam, nulla quam molestie turpis, at egestas odio ante et tortor. Suspendisse fringilla condimentum justo, at aliquet odio aliquam ac.
|
|
||||||
|
|
||||||
### Scene Four
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo.
|
|
||||||
|
|
||||||
Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo. Nullam viverra dui et auctor pretium. Ut ullamcorper velit urna, sed imperdiet massa convallis a. Suspendisse efficitur, ipsum nec cursus pulvinar, eros urna posuere diam, nec elementum mi felis vitae sapien.
|
|
||||||
|
|
||||||
Duis efficitur metus pulvinar, molestie magna eget, feugiat dui. Fusce convallis vehicula ipsum convallis blandit. Duis eros risus, malesuada eu imperdiet in, hendrerit ac metus. Vestibulum id justo gravida, dignissim nibh non, iaculis diam. Fusce accumsan est ut massa porta ultricies. Nulla vitae justo in tortor laoreet mollis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu libero justo. Vivamus aliquet placerat est, et auctor eros posuere venenatis. Nunc quam diam, tincidunt ac aliquet in, fermentum sit amet lectus. Proin commodo tincidunt blandit. Quisque erat arcu, semper nec dui non, consectetur gravida ipsum. Nullam pretium consectetur elit at condimentum.
|
|
||||||
|
|
||||||
Etiam sagittis, erat vitae accumsan tempor, neque augue scelerisque nulla, ut ultrices justo urna sit amet augue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean at pulvinar tortor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras vel porta quam. Nullam eu mauris mollis, vehicula justo vel, placerat sapien. Phasellus viverra elit et vestibulum pharetra. Vestibulum commodo fermentum leo, eu porta nisi aliquam eget. Nulla tempus porttitor nisi nec mollis. Nam non mollis turpis. Nam finibus leo a bibendum tincidunt. Donec commodo velit magna, ac semper sapien mattis id. Proin sem velit, lobortis quis ultricies id, pharetra et lectus. Vestibulum condimentum neque vitae mi dapibus mollis. Mauris luctus vel sapien vitae hendrerit.
|
|
||||||
|
|
||||||
Aenean vestibulum magna placerat fermentum tempus. Nam auctor condimentum nunc, in elementum quam ornare a. Etiam in ipsum elit. Proin pharetra, dolor sollicitudin pellentesque congue, lorem dolor ultricies magna, non iaculis risus nisl dictum diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Vivamus vel euismod nibh, et lobortis dolor. Maecenas dui odio, gravida nec molestie ut, feugiat ut arcu. Pellentesque risus sapien, gravida a convallis quis, ullamcorper porttitor sapien.
|
|
||||||
|
|
||||||
Donec ipsum eros, vestibulum sit amet cursus eget, iaculis quis dolor. Pellentesque magna augue, tristique dapibus mi vitae, molestie venenatis enim. Nam malesuada, turpis volutpat rhoncus ullamcorper, justo est eleifend orci, ut luctus risus ex rutrum arcu. Sed mi elit, feugiat rhoncus ornare sed, porta id leo. Pellentesque feugiat nulla tincidunt erat suscipit, eu congue lacus hendrerit. Morbi pulvinar enim sed consequat auctor. Ut eleifend enim sem, vitae euismod ex ultricies sit amet. Curabitur eu efficitur nisi, suscipit finibus sapien. In sodales blandit erat, vestibulum pulvinar ante volutpat nec. Vivamus dictum non libero at molestie. Donec sit amet neque in ante convallis pretium. Nunc vel iaculis dui.
|
|
||||||
|
|
||||||
Phasellus eu nunc ut nunc faucibus laoreet. Aliquam at magna risus. Praesent lobortis, risus finibus semper varius, magna purus vestibulum eros, at pulvinar sapien enim a ex. In scelerisque malesuada ex, sit amet egestas neque condimentum sed. Praesent vulputate efficitur massa. Cras at accumsan ligula. In elementum lectus eget blandit dictum. Nam vitae libero ut justo eleifend rutrum ac nec arcu. Aliquam sodales in quam congue vestibulum. Aliquam in accumsan sapien. Quisque lobortis nisl nisi, vitae bibendum turpis efficitur sed. Vestibulum tempor nulla eget nisi convallis, blandit sagittis ipsum convallis. Donec odio nibh, ultrices quis odio in, mollis euismod libero.
|
|
||||||
|
|
||||||
### Scene Five
|
|
||||||
|
|
||||||
@pov: Bod
|
|
||||||
@plot: Main
|
|
||||||
@location: Europe
|
|
||||||
|
|
||||||
% Synopsis: Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
|
||||||
|
|
||||||
Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In sed felis auctor, rhoncus dui ac, consequat dolor. Integer volutpat libero sed nisl aliquet varius. Suspendisse et lorem sapien. Proin id ultrices nibh, ac suscipit diam. Suspendisse placerat varius porttitor. Curabitur elementum sed enim ultrices imperdiet.
|
|
||||||
|
|
||||||
In ut lobortis lacus, nec luctus arcu. Vivamus condimentum sapien a ipsum malesuada sodales. Donec et vestibulum risus. Integer dictum euismod eros id tincidunt. Aliquam sagittis leo vitae consequat fermentum. Donec maximus ex eu ex iaculis porta. Praesent pharetra lacinia risus, et eleifend diam commodo non. Sed feugiat ipsum ut orci sagittis, quis faucibus lectus blandit. Sed tellus quam, gravida vitae laoreet quis, tempus lobortis dui. Vivamus semper accumsan ullamcorper. Praesent tempus pretium eros, non elementum risus. Pellentesque odio quam, auctor quis ex non, vulputate egestas dolor. Nunc luctus enim ut justo sodales consectetur. Sed aliquet a mauris vel posuere.
|
|
||||||
|
|
||||||
Donec luctus lectus efficitur, blandit nisi vitae, dignissim tellus. Pellentesque euismod pharetra augue gravida hendrerit. Quisque nisi mi, mattis ac nisi non, maximus malesuada ante. Nulla lobortis, diam eu ornare ornare, tellus enim feugiat arcu, non vestibulum tortor nunc eu justo. Integer blandit felis justo, eu semper est scelerisque vel. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam ultricies, nisi vel elementum commodo, nisl dolor tincidunt magna, sed varius est nunc at lectus. Aliquam dolor tortor, sodales placerat ultricies quis, sodales quis sapien. Duis ullamcorper sollicitudin risus at mattis. Integer consequat et nunc at condimentum. Pellentesque cursus congue augue, non suscipit lectus sodales ut. Nam a mi bibendum, blandit nisl eu, accumsan nunc. Aliquam a ex mauris. Sed nec sem quis arcu dignissim tempus eget et turpis. Ut sed ex nec ipsum ultrices lobortis.
|
|
||||||
|
|
||||||
Pellentesque rhoncus pharetra eros, non mollis nisi pretium non. Mauris accumsan quis odio quis euismod. Maecenas ultrices, augue et aliquam tincidunt, erat tellus ornare ligula, quis ultrices turpis nibh vel justo. Fusce gravida odio tellus. In a congue diam. Mauris consequat ex id leo lacinia dictum. Fusce id sem sodales, ultrices sapien ac, convallis orci. Donec gravida nunc sit amet nisi hendrerit, sed porta enim aliquam. In hac habitasse platea dictumst. Cras a orci felis. Curabitur non felis nec urna maximus auctor ut ut nisi. Curabitur at turpis eleifend, blandit eros at, molestie odio. Phasellus euismod neque augue.
|
|
||||||
|
|
||||||
Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
|
|
||||||
|
|
||||||
# Notes: Characters
|
|
||||||
|
|
||||||
# Nobody Owens
|
|
||||||
|
|
||||||
@tag: Bod
|
|
||||||
@plot: Main
|
|
||||||
|
|
||||||
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
|
|
||||||
|
|
||||||
Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.
|
|
||||||
|
|
||||||
Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.
|
|
||||||
|
|
||||||
# Notes: Plot
|
|
||||||
|
|
||||||
# Main Plot
|
|
||||||
|
|
||||||
@tag: Main
|
|
||||||
|
|
||||||
Suspendisse vulputate malesuada pellentesque. Aenean sollicitudin cursus mi, vitae ultricies felis ullamcorper eu. Duis luctus risus mi, in accumsan velit cursus ut. Vestibulum eleifend leo in magna eleifend fermentum. Proin nec ornare elit. Phasellus nec interdum risus. In a volutpat augue, quis egestas justo. Morbi porta mauris mattis bibendum imperdiet.
|
|
||||||
|
|
||||||
Mauris ut erat eu lorem malesuada egestas vel vel urna. Maecenas ac semper quam. Maecenas aliquet metus non interdum mattis. Proin consectetur molestie ligula. Aliquam sollicitudin pulvinar urna a pellentesque. Suspendisse ultrices, est mattis scelerisque porta, nisi nisi laoreet nisl, non condimentum quam ante a velit. Proin scelerisque justo augue, nec laoreet ligula egestas at. Etiam enim quam, ultrices non accumsan hendrerit, elementum vel ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam efficitur odio libero, in vestibulum arcu aliquam at. Cras non vehicula augue. Integer lobortis, est vitae aliquam facilisis, metus ligula aliquet eros, at porttitor sem tortor eget massa. Aliquam varius scelerisque neque sed gravida. Aenean eleifend lorem id ante elementum sollicitudin. Proin commodo massa a quam volutpat, mollis fermentum turpis efficitur.
|
|
||||||
|
|
||||||
# Notes: World
|
|
||||||
|
|
||||||
# Ancient Europe
|
|
||||||
|
|
||||||
@tag: Europe
|
|
||||||
|
|
||||||
Vivamus sodales risus ac accumsan posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nunc vel enim felis. Vestibulum dignissim massa nunc, a auctor magna eleifend et. Proin dignissim sodales erat vitae convallis. Aliquam id tellus dui. Curabitur sollicitudin scelerisque ex sit amet posuere. Nam rutrum felis id rhoncus feugiat. Duis sagittis quam quis purus efficitur, quis rutrum odio iaculis. Maecenas semper ante turpis, at vulputate mi consectetur non. Sed rutrum nibh turpis, quis rhoncus purus ornare quis. Vestibulum at rutrum mauris. Integer dolor nisi, tincidunt eget vehicula ac, ultricies at ligula.
|
|
||||||
|
|
||||||
Aenean semper turpis quis varius rhoncus. Vivamus ac mi eget felis euismod vulputate. Nam eu tempus velit. Etiam ut est porta, finibus erat sit amet, consectetur felis. Nullam consequat felis ut lacus pharetra, in lobortis urna mollis. Nulla varius eros nec lorem rhoncus, sed venenatis risus ultrices. Phasellus pellentesque laoreet neque, ut ultricies lacus vulputate quis. In malesuada dui sit amet est interdum, eget consectetur mi gravida. Cras vel bibendum purus. Quisque commodo tempor arcu, non lacinia sem blandit eleifend. Quisque at neque gravida, porttitor metus a, suscipit diam. Quisque convallis sodales lacus et condimentum. Donec a suscipit diam. Pellentesque eget cursus neque.
|
|
||||||
|
|
||||||
Nunc ullamcorper magna quis elit condimentum rhoncus. Aenean dictum pulvinar dolor suscipit interdum. Aliquam elit massa, elementum nec cursus eu, maximus nec ipsum. Donec ullamcorper iaculis dolor eu commodo. Nunc eget tortor quis turpis consectetur varius. Vestibulum nec justo vel tellus venenatis condimentum. Duis auctor iaculis massa. Nunc risus magna, rutrum vitae eros non, tristique mollis enim.
|
|
||||||
|
|
||||||