Merge branch 'develop' into session-target

This commit is contained in:
nephlm 2022-11-21 01:13:28 -05:00 committed by GitHub
commit 0a81ff4960
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
221 changed files with 107982 additions and 25216 deletions

46
.github/workflows/linux.yml vendored Normal file
View file

@ -0,0 +1,46 @@
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the develop branch
push:
branches: [ develop ]
pull_request:
branches: [ develop ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
test:
name: Test on node ${{ matrix.python_version }} and ${{ matrix.os }}
# The type of runner that the job will run on
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.6]
# python-version: [3.6, 3.7, 3.8, 3.9]
os: [ubuntu-16.04]
# os: [ubuntu-16.04, ubuntu-latest, windows-latest, macos-10.15]
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pyqt5==5.9 lxml pytest pytest-faulthandler
sudo apt-get -qq update
sudo apt-get -qq install python3-pip python3-dev build-essential qt5-default libxml2-dev libxslt1-dev mesa-utils libgl1-mesa-glx libgl1-mesa-dev libxcb-xinerama0-dev
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Test with pytest
run: |
xvfb-run -s '-screen 0 640x480x24 +extension GLX' pytest -vs

5
.gitignore vendored
View file

@ -11,12 +11,17 @@
.idea
.project
.pydevproject
.python-version
.settings/org.eclipse.core.resources.prefs
.vscode
.vimrc
ExportTest
Notes.t2t
dist
build
icons/Numix
manuskript/pycallgraph.txt
manuskript.log
snowflake*
test-projects
main.pyproject.user

View file

@ -2,7 +2,7 @@ language: generic
os:
- osx
- linux
osx_image: xcode8.3
osx_image: xcode13.4
sudo: required
install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then package/prepare_osx.sh; fi

View file

@ -1,4 +1,358 @@
# Change Log
# Changelog
## [0.14.0](https://github.com/olivierkes/manuskript/tree/0.14.0) (2022-06-08)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.13.1...0.14.0)
**Implemented enhancements:**
- \[Feature Request\] Full Compile Export [\#1034](https://github.com/olivierkes/manuskript/issues/1034)
- Cleaning up the flatpak [\#1029](https://github.com/olivierkes/manuskript/issues/1029)
**Fixed bugs:**
- Crashing when I'm trying to create the 1st project [\#1035](https://github.com/olivierkes/manuskript/issues/1035)
- crash on the "Outline" section [\#1032](https://github.com/olivierkes/manuskript/issues/1032)
- Manuskript crashing while booting [\#1021](https://github.com/olivierkes/manuskript/issues/1021)
- \[Bug\] double click causes crash in outline or plots view [\#1014](https://github.com/olivierkes/manuskript/issues/1014)
- Re-ordering items in the outline causes data loss/text overwritting [\#1001](https://github.com/olivierkes/manuskript/issues/1001)
- An unhandled exception has occurred! on cheatsheet [\#994](https://github.com/olivierkes/manuskript/issues/994)
- Crash when changing index card style [\#992](https://github.com/olivierkes/manuskript/issues/992)
- Regex search causes crash [\#989](https://github.com/olivierkes/manuskript/issues/989)
- Crash when project file is open in another application [\#950](https://github.com/olivierkes/manuskript/issues/950)
- outliner folder gets renamed and contents replaced when dragged over other folder, only in upward direction [\#719](https://github.com/olivierkes/manuskript/issues/719)
**Closed issues:**
- Creating empty language file hr-HR [\#1023](https://github.com/olivierkes/manuskript/issues/1023)
- Creating project from template dialog has fixed size [\#996](https://github.com/olivierkes/manuskript/issues/996)
- Russian translation [\#990](https://github.com/olivierkes/manuskript/issues/990)
- \[Windows Version\] Two program windows pop up [\#327](https://github.com/olivierkes/manuskript/issues/327)
**Merged pull requests:**
- catch AttributeError in plotTreeView double click event \(fixes \#1014\) [\#1015](https://github.com/olivierkes/manuskript/pull/1015) ([amconners](https://github.com/amconners))
- Change welcome page spacer size policy to allow welcome to expand to fill most of screen \(fixes olivierkes\#996\) [\#1007](https://github.com/olivierkes/manuskript/pull/1007) ([jdanielp](https://github.com/jdanielp))
- Fix crash when files are locked for writing \(Fixes olivierkes\#950\) [\#1004](https://github.com/olivierkes/manuskript/pull/1004) ([jdanielp](https://github.com/jdanielp))
- Fix crash when regex is not valid \(Fixes olivierkes\#989\) [\#1003](https://github.com/olivierkes/manuskript/pull/1003) ([jdanielp](https://github.com/jdanielp))
- Potential fix for \#719, \#1001: data loss when reordering items in upward direction [\#1002](https://github.com/olivierkes/manuskript/pull/1002) ([olivierkes](https://github.com/olivierkes))
- hard\_line\_breaks support [\#1000](https://github.com/olivierkes/manuskript/pull/1000) ([Mte90](https://github.com/Mte90))
- Fix for \#992 and \#998 [\#999](https://github.com/olivierkes/manuskript/pull/999) ([amconners](https://github.com/amconners))
- Write a test for ParseMMDFile function. [\#831](https://github.com/olivierkes/manuskript/pull/831) ([zeth](https://github.com/zeth))
## [0.13.1](https://github.com/olivierkes/manuskript/tree/0.13.1) (2021-12-13)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.12.0...0.13.1)
**Implemented enhancements:**
- Stripping down the install for low resource machines [\#956](https://github.com/olivierkes/manuskript/issues/956)
- Add filename/titles to warning dialog box when deleting files or folders [\#931](https://github.com/olivierkes/manuskript/issues/931)
- \[Request\] Additional Package formats: AppImage \(also note Flatpak, Snap\) [\#71](https://github.com/olivierkes/manuskript/issues/71)
**Fixed bugs:**
- Character Detailed Info Crash [\#971](https://github.com/olivierkes/manuskript/issues/971)
- Fail to launch [\#967](https://github.com/olivierkes/manuskript/issues/967)
- Critical error when running on Fedora 35 \(manuskript won't start\) [\#957](https://github.com/olivierkes/manuskript/issues/957)
- Can't open Manuskript executable "manuskript-0.12.0-win32" and "manuskript-0.12.0-fix-7e05b72-win32" on windows 7 professional. [\#951](https://github.com/olivierkes/manuskript/issues/951)
- Software crashes when I try to add a new character detail. [\#945](https://github.com/olivierkes/manuskript/issues/945)
- It crashes when I double click characters name's frame [\#937](https://github.com/olivierkes/manuskript/issues/937)
- calling pandoc 2.5 is broken with the later versions [\#922](https://github.com/olivierkes/manuskript/issues/922)
- Entry Point Error, how can I solve this ? [\#911](https://github.com/olivierkes/manuskript/issues/911)
- Issues pertaining to the wrong table being referenced in the "Detailed info" tab on the "Characters" pane [\#910](https://github.com/olivierkes/manuskript/issues/910)
- Upgraded from 0.11 to 0.12, open from command line and dolphin broken [\#898](https://github.com/olivierkes/manuskript/issues/898)
- IndexError in characterModel.py [\#896](https://github.com/olivierkes/manuskript/issues/896)
- Error on export return code: 43 [\#886](https://github.com/olivierkes/manuskript/issues/886)
- Issue installing v12 on Win7 64bit [\#872](https://github.com/olivierkes/manuskript/issues/872)
- well, now it don't even open... [\#870](https://github.com/olivierkes/manuskript/issues/870)
- Overlapping IDs repeating [\#865](https://github.com/olivierkes/manuskript/issues/865)
- crash on character details frame [\#843](https://github.com/olivierkes/manuskript/issues/843)
- POV selection changes if character is deleted and a new one is added [\#808](https://github.com/olivierkes/manuskript/issues/808)
- Crashhhh [\#791](https://github.com/olivierkes/manuskript/issues/791)
- Manuskript runs amok, with Project \(Project is added\) [\#729](https://github.com/olivierkes/manuskript/issues/729)
**Security fixes:**
- Possible security issue [\#891](https://github.com/olivierkes/manuskript/issues/891)
**Closed issues:**
- Ubuntu snap on edge channel not updated with v12 [\#955](https://github.com/olivierkes/manuskript/issues/955)
- Installed on chromebook \(linux\), no issues, opened it up and it has no words? [\#925](https://github.com/olivierkes/manuskript/issues/925)
- App crashing when adding detailed info to character. [\#920](https://github.com/olivierkes/manuskript/issues/920)
- Ubuntu 21.04, Manuskrip not installing or working with .deb snap or repository. [\#900](https://github.com/olivierkes/manuskript/issues/900)
- \(Windows, 0.12.0\) "GetDaylightFlag" not found in "kernel32.dll" [\#889](https://github.com/olivierkes/manuskript/issues/889)
- Text pasted in Summary or Notes/References disappears [\#873](https://github.com/olivierkes/manuskript/issues/873)
- Linux missing dependency? [\#847](https://github.com/olivierkes/manuskript/issues/847)
- Flatpak package [\#425](https://github.com/olivierkes/manuskript/issues/425)
**Merged pull requests:**
- Fixes for a number of reported bugs [\#901](https://github.com/olivierkes/manuskript/pull/901) ([worstje](https://github.com/worstje))
- Kill old insecure pickle setting files \(untested\) [\#895](https://github.com/olivierkes/manuskript/pull/895) ([zeth](https://github.com/zeth))
- Deal with race condition. [\#894](https://github.com/olivierkes/manuskript/pull/894) ([zeth](https://github.com/zeth))
- Someone was in a JavaScript mood? [\#893](https://github.com/olivierkes/manuskript/pull/893) ([zeth](https://github.com/zeth))
## [0.12.0](https://github.com/olivierkes/manuskript/tree/0.12.0) (2021-04-30)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.11.0...0.12.0)
**Implemented enhancements:**
- \[Feature Request\] Word counter in full screen mode [\#723](https://github.com/olivierkes/manuskript/issues/723)
- Improving user-friendliness of log files [\#852](https://github.com/olivierkes/manuskript/issues/852)
- \[Feature Request\] Add spell check for Chinese. [\#822](https://github.com/olivierkes/manuskript/issues/822)
- Number of characters instead of number of words? [\#774](https://github.com/olivierkes/manuskript/issues/774)
- Italian spellchecker [\#730](https://github.com/olivierkes/manuskript/issues/730)
- Word count statistics incorrect when using Chinese characters [\#607](https://github.com/olivierkes/manuskript/issues/607)
- select which monitor in full screen mode [\#456](https://github.com/olivierkes/manuskript/issues/456)
- \[feature request\] Scene Search [\#376](https://github.com/olivierkes/manuskript/issues/376)
- \[Feature request\] References in characters' bios [\#347](https://github.com/olivierkes/manuskript/issues/347)
- add languagetool [\#142](https://github.com/olivierkes/manuskript/issues/142)
**Fixed bugs:**
- Can't open any file [\#758](https://github.com/olivierkes/manuskript/issues/758)
- Main major and minor character are not functional [\#698](https://github.com/olivierkes/manuskript/issues/698)
- Using a world building template is broken [\#866](https://github.com/olivierkes/manuskript/issues/866)
- Slow startup when using language\_tool\_python in the test python setup [\#862](https://github.com/olivierkes/manuskript/issues/862)
- When using the + on editor or outline the application crashes [\#855](https://github.com/olivierkes/manuskript/issues/855)
- Fullscreen and Exit -\> Fullscreen survives [\#846](https://github.com/olivierkes/manuskript/issues/846)
- Loading another project leads to crash [\#833](https://github.com/olivierkes/manuskript/issues/833)
- Crashes with using language-check [\#832](https://github.com/olivierkes/manuskript/issues/832)
- Crash on showing settingsWindow.py [\#817](https://github.com/olivierkes/manuskript/issues/817)
- Python syntax warning upon installation [\#792](https://github.com/olivierkes/manuskript/issues/792)
- Right click → Insert Reference buggy when search term is followed by punctuation [\#781](https://github.com/olivierkes/manuskript/issues/781)
- Error on export when using pandoc [\#736](https://github.com/olivierkes/manuskript/issues/736)
- Snap Package: All types of export with pandoc fail with error 97 [\#709](https://github.com/olivierkes/manuskript/issues/709)
- export error \(pandoc\) [\#590](https://github.com/olivierkes/manuskript/issues/590)
**Closed issues:**
- Help! Can't open MSK.file [\#759](https://github.com/olivierkes/manuskript/issues/759)
- Can't run Manuskript [\#742](https://github.com/olivierkes/manuskript/issues/742)
- Manuskript Crashes on Project Open [\#741](https://github.com/olivierkes/manuskript/issues/741)
- Program crashes randomly and then never opens again even after reinstall [\#665](https://github.com/olivierkes/manuskript/issues/665)
- PLOT Character Error Message [\#519](https://github.com/olivierkes/manuskript/issues/519)
- Enable/Disable POV-Option for Characters [\#335](https://github.com/olivierkes/manuskript/issues/335)
- Show character count progress indicator [\#334](https://github.com/olivierkes/manuskript/issues/334)
- Small bug with LanguageTool [\#860](https://github.com/olivierkes/manuskript/issues/860)
**Merged pull requests:**
- Hyphens no longer counted in word count [\#816](https://github.com/olivierkes/manuskript/pull/816) ([James-Joe](https://github.com/James-Joe))
- - adding characters count. Implementing \#334 [\#339](https://github.com/olivierkes/manuskript/pull/339) ([lechbaczynski](https://github.com/lechbaczynski))
- Fix missing root when using world building template [\#867](https://github.com/olivierkes/manuskript/pull/867) ([belug23](https://github.com/belug23))
- Add configuration for github actions to test linux on pull requests [\#864](https://github.com/olivierkes/manuskript/pull/864) ([belug23](https://github.com/belug23))
- Fix errors when language tool isn't installed [\#863](https://github.com/olivierkes/manuskript/pull/863) ([belug23](https://github.com/belug23))
- Fix 860 languagetool get locale language [\#861](https://github.com/olivierkes/manuskript/pull/861) ([belug23](https://github.com/belug23))
- Friendly logging for end users [\#859](https://github.com/olivierkes/manuskript/pull/859) ([worstje](https://github.com/worstje))
- Fixing the tests for travis-CI [\#858](https://github.com/olivierkes/manuskript/pull/858) ([belug23](https://github.com/belug23))
- Fix \#855 - Avoid a crash when there's no model [\#856](https://github.com/olivierkes/manuskript/pull/856) ([belug23](https://github.com/belug23))
- Fix \#846 close Fullscreen when exiting main editor [\#854](https://github.com/olivierkes/manuskript/pull/854) ([belug23](https://github.com/belug23))
- Fix \#456 - Force the distraction free window on the display of the main window [\#851](https://github.com/olivierkes/manuskript/pull/851) ([belug23](https://github.com/belug23))
- Fixed project not opening with missing background [\#850](https://github.com/olivierkes/manuskript/pull/850) ([rbb8403](https://github.com/rbb8403))
- setup signal handler to avoid accident data loss [\#835](https://github.com/olivierkes/manuskript/pull/835) ([lingsamuel](https://github.com/lingsamuel))
- Properly disconnect add person connection. [\#834](https://github.com/olivierkes/manuskript/pull/834) ([BentleyJOakes](https://github.com/BentleyJOakes))
- Change outlineItem ID assignment process for major optimization [\#827](https://github.com/olivierkes/manuskript/pull/827) ([emgineering](https://github.com/emgineering))
- Fix for TypeErrors when using certain app styles [\#793](https://github.com/olivierkes/manuskript/pull/793) ([FrancoisDuchene](https://github.com/FrancoisDuchene))
- Fixed pandoc command arguments [\#790](https://github.com/olivierkes/manuskript/pull/790) ([DarkRedman](https://github.com/DarkRedman))
- Update manuskript\_fr.ts [\#789](https://github.com/olivierkes/manuskript/pull/789) ([DarkRedman](https://github.com/DarkRedman))
- Update abstractModel.py [\#777](https://github.com/olivierkes/manuskript/pull/777) ([siliconserf](https://github.com/siliconserf))
- Clones importance setting when adding new characters. [\#775](https://github.com/olivierkes/manuskript/pull/775) ([BentleyJOakes](https://github.com/BentleyJOakes))
- typofixing here and there [\#768](https://github.com/olivierkes/manuskript/pull/768) ([goofy-mdn](https://github.com/goofy-mdn))
- Fix Python 3.8 SyntaxWarning: "is not" with a literal [\#762](https://github.com/olivierkes/manuskript/pull/762) ([gedakc](https://github.com/gedakc))
- Set minimum of xcode11 for macOS X in Travis CI build [\#760](https://github.com/olivierkes/manuskript/pull/760) ([gedakc](https://github.com/gedakc))
- Enabling/Disabling POV for a specific character [\#748](https://github.com/olivierkes/manuskript/pull/748) ([TheJackiMonster](https://github.com/TheJackiMonster))
- Added basic support for LanguageTool via 'language\_check' as advanced spellchecker [\#747](https://github.com/olivierkes/manuskript/pull/747) ([TheJackiMonster](https://github.com/TheJackiMonster))
- Added char-count with settings to enable/disable it. [\#746](https://github.com/olivierkes/manuskript/pull/746) ([TheJackiMonster](https://github.com/TheJackiMonster))
- Add snap layout for pandoc templates directory [\#737](https://github.com/olivierkes/manuskript/pull/737) ([tomwardill](https://github.com/tomwardill))
- Select newly added world items, opening branches as necessary [\#735](https://github.com/olivierkes/manuskript/pull/735) ([johnbintz](https://github.com/johnbintz))
- Add global search [\#717](https://github.com/olivierkes/manuskript/pull/717) ([moisesjbc](https://github.com/moisesjbc))
- added 3 buttons to the textEditView that allow quickly adding new items [\#690](https://github.com/olivierkes/manuskript/pull/690) ([nagolinc](https://github.com/nagolinc))
- Logging and command-line arguments [\#667](https://github.com/olivierkes/manuskript/pull/667) ([worstje](https://github.com/worstje))
## [0.11.0](https://github.com/olivierkes/manuskript/tree/0.11.0) (2020-01-18)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.10.0...0.11.0)
**Implemented enhancements:**
- \[Feature Request\] Automatically calculate folder word count goal [\#664](https://github.com/olivierkes/manuskript/issues/664)
**Fixed bugs:**
- Ctrl+Space destroys and Ctrl+Z can't recover [\#703](https://github.com/olivierkes/manuskript/issues/703)
- References Mysteriously Delete in Summary and Reference/Notes Fields [\#688](https://github.com/olivierkes/manuskript/issues/688)
- After use of ctrl-z or ctrl-v Manuskript goes in inactive State [\#672](https://github.com/olivierkes/manuskript/issues/672)
- Cut and Paste - Comment Field - First shows, then disappiers [\#670](https://github.com/olivierkes/manuskript/issues/670)
- 0.10.0 crash using Windows dark mode [\#659](https://github.com/olivierkes/manuskript/issues/659)
**Closed issues:**
- Can't get the program to open [\#686](https://github.com/olivierkes/manuskript/issues/686)
- Default for separator between folders should be pagebreak [\#680](https://github.com/olivierkes/manuskript/issues/680)
- Program crashes on copy and paste [\#441](https://github.com/olivierkes/manuskript/issues/441)
**Merged pull requests:**
- Change wording of import warning for PyQt/Qt versions 5.11 and 5.12 [\#715](https://github.com/olivierkes/manuskript/pull/715) ([gedakc](https://github.com/gedakc))
- Remove support for macOS X Sierra \(10.12\) in Travis CI build [\#713](https://github.com/olivierkes/manuskript/pull/713) ([gedakc](https://github.com/gedakc))
- Fixed bugs caused by parallel access during multithreading [\#706](https://github.com/olivierkes/manuskript/pull/706) ([TheJackiMonster](https://github.com/TheJackiMonster))
- More german translations [\#701](https://github.com/olivierkes/manuskript/pull/701) ([fabianbeil](https://github.com/fabianbeil))
- Fixed translation mistake. Trilogy translates to Trilogie in German [\#700](https://github.com/olivierkes/manuskript/pull/700) ([fabianbeil](https://github.com/fabianbeil))
- Fix for Windows 10 Dark Theme on older Qt versions [\#660](https://github.com/olivierkes/manuskript/pull/660) ([worstje](https://github.com/worstje))
## [0.10.0](https://github.com/olivierkes/manuskript/tree/0.10.0) (2019-09-30)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.9.0...0.10.0)
**Implemented enhancements:**
- Add non-enchant spellcheck support [\#505](https://github.com/olivierkes/manuskript/issues/505)
- Basic dark theme support \(Windows 10\) [\#630](https://github.com/olivierkes/manuskript/pull/630) ([worstje](https://github.com/worstje))
- Refactor spellchecker code and add other spellcheck library support [\#507](https://github.com/olivierkes/manuskript/pull/507) ([kakaroto](https://github.com/kakaroto))
**Fixed bugs:**
- Impossible to change UI language to english if your system locale isn't set to an anglophonic country [\#619](https://github.com/olivierkes/manuskript/issues/619)
- All Imports are crashing [\#611](https://github.com/olivierkes/manuskript/issues/611)
- When compiling, it overwrite files without asking [\#608](https://github.com/olivierkes/manuskript/issues/608)
- Crash when exporting with pandoc as custom path only [\#563](https://github.com/olivierkes/manuskript/issues/563)
- Crash on insertion of new page character [\#562](https://github.com/olivierkes/manuskript/issues/562)
- Crash on adding word goal in outline [\#561](https://github.com/olivierkes/manuskript/issues/561)
- Crash in Windows 10 when drag and drop [\#559](https://github.com/olivierkes/manuskript/issues/559)
- Image crash: When using tooltip on an incomplete image filename [\#549](https://github.com/olivierkes/manuskript/issues/549)
- pandoc export crashes if project title is empty [\#535](https://github.com/olivierkes/manuskript/issues/535)
- editor/cork options wrong after deleting a text [\#516](https://github.com/olivierkes/manuskript/issues/516)
- crash on import directory [\#500](https://github.com/olivierkes/manuskript/issues/500)
- Inconsistent and/or undesirable window placements [\#481](https://github.com/olivierkes/manuskript/issues/481)
- Crash when deleting folder with files in tree view [\#479](https://github.com/olivierkes/manuskript/issues/479)
- New level 'unit' is reset [\#468](https://github.com/olivierkes/manuskript/issues/468)
- Lowering number of saved revisions below 1 crashes program [\#381](https://github.com/olivierkes/manuskript/issues/381)
- ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters [\#31](https://github.com/olivierkes/manuskript/issues/31)
- OS X: cannot leave fullscreen mode [\#24](https://github.com/olivierkes/manuskript/issues/24)
**Closed issues:**
- Fullscreen mode causes spike in CPU [\#643](https://github.com/olivierkes/manuskript/issues/643)
- Italian dictionary [\#638](https://github.com/olivierkes/manuskript/issues/638)
- Manuskript 9.0 crashes when creating new project or opening existing project [\#631](https://github.com/olivierkes/manuskript/issues/631)
- Spell Check Not working [\#625](https://github.com/olivierkes/manuskript/issues/625)
- Manuskript me fastidio un documento de word [\#616](https://github.com/olivierkes/manuskript/issues/616)
- Feature request: Option to vertically center text input line on screen in fullscreen mode [\#602](https://github.com/olivierkes/manuskript/issues/602)
- File Randomly won't open [\#597](https://github.com/olivierkes/manuskript/issues/597)
- utf-8' codec can't decode byte 0xff in position 0 [\#591](https://github.com/olivierkes/manuskript/issues/591)
- Issue with saving as directory [\#589](https://github.com/olivierkes/manuskript/issues/589)
- Headings h4 not translated from Markdown to ODF [\#580](https://github.com/olivierkes/manuskript/issues/580)
- \[BUG\] Shim error [\#579](https://github.com/olivierkes/manuskript/issues/579)
- Crash when edit text [\#555](https://github.com/olivierkes/manuskript/issues/555)
- Unusual environment failure [\#547](https://github.com/olivierkes/manuskript/issues/547)
- Won't run \(Arch Linux\) [\#546](https://github.com/olivierkes/manuskript/issues/546)
- Rendre extensible les modèles d'intrigue [\#329](https://github.com/olivierkes/manuskript/issues/329)
- Word count goal progress bar broken in develop. [\#652](https://github.com/olivierkes/manuskript/issues/652)
- story line feature crashing [\#620](https://github.com/olivierkes/manuskript/issues/620)
- Italian translation not applied, application still english. [\#599](https://github.com/olivierkes/manuskript/issues/599)
- Adding Persian\(Farsi\) in Weblate [\#596](https://github.com/olivierkes/manuskript/issues/596)
- Importing images into Manuskript [\#593](https://github.com/olivierkes/manuskript/issues/593)
- British English translation [\#592](https://github.com/olivierkes/manuskript/issues/592)
- Crashes in outliner [\#582](https://github.com/olivierkes/manuskript/issues/582)
**Merged pull requests:**
- Changes to Revisions UI [\#655](https://github.com/olivierkes/manuskript/pull/655) ([worstje](https://github.com/worstje))
- Restore progress bar functionality [\#654](https://github.com/olivierkes/manuskript/pull/654) ([worstje](https://github.com/worstje))
- Default keep revisions to disabled, and remove tests for revisions [\#653](https://github.com/olivierkes/manuskript/pull/653) ([gedakc](https://github.com/gedakc))
- Fix word recognition for spell checker, ignore active partial words [\#651](https://github.com/olivierkes/manuskript/pull/651) ([gedakc](https://github.com/gedakc))
- Fix typo missed in previous commit [\#648](https://github.com/olivierkes/manuskript/pull/648) ([luzpaz](https://github.com/luzpaz))
- Fix source typo [\#645](https://github.com/olivierkes/manuskript/pull/645) ([luzpaz](https://github.com/luzpaz))
- Move Qt 5.11 / 5.12 version warning to Import invocation [\#642](https://github.com/olivierkes/manuskript/pull/642) ([gedakc](https://github.com/gedakc))
- Add DISABLE\_WAYLAND to snapcraft.yaml [\#637](https://github.com/olivierkes/manuskript/pull/637) ([gedakc](https://github.com/gedakc))
- Further refinement of image tooltips \(issue \#593\) [\#629](https://github.com/olivierkes/manuskript/pull/629) ([worstje](https://github.com/worstje))
- Reworking of the translation loading process \(issue \#619\) [\#627](https://github.com/olivierkes/manuskript/pull/627) ([worstje](https://github.com/worstje))
- change markdown to "Markdown" in.... [\#624](https://github.com/olivierkes/manuskript/pull/624) ([leela52452](https://github.com/leela52452))
- Fix tab key order, and default window tab for character & plot panes [\#623](https://github.com/olivierkes/manuskript/pull/623) ([gedakc](https://github.com/gedakc))
- Add British English Translation updates [\#621](https://github.com/olivierkes/manuskript/pull/621) ([gedakc](https://github.com/gedakc))
- Do not prompt "Save project?" when \_Save on quit\_ setting enabled [\#615](https://github.com/olivierkes/manuskript/pull/615) ([gedakc](https://github.com/gedakc))
- Fix exports silently overwriting files \(fixes \#608\) & small fix to dialog logic [\#613](https://github.com/olivierkes/manuskript/pull/613) ([worstje](https://github.com/worstje))
- Working Pandoc import \(fixes \#611\) & small dialog UI update. [\#612](https://github.com/olivierkes/manuskript/pull/612) ([worstje](https://github.com/worstje))
- Fix Linux Travis CI build error - pyenv: version `3.6.3' not installed [\#610](https://github.com/olivierkes/manuskript/pull/610) ([gedakc](https://github.com/gedakc))
- Fix crash when setting word Goal on new Text \(scene\) in Outline pane [\#609](https://github.com/olivierkes/manuskript/pull/609) ([gedakc](https://github.com/gedakc))
- Spelling: Manuscript, could not, process, … No content [\#588](https://github.com/olivierkes/manuskript/pull/588) ([comradekingu](https://github.com/comradekingu))
- fix issue \#468 'unit' is reset [\#587](https://github.com/olivierkes/manuskript/pull/587) ([NocturnalFred](https://github.com/NocturnalFred))
- Fix pandoc export crashes is project title is empty [\#585](https://github.com/olivierkes/manuskript/pull/585) ([gedakc](https://github.com/gedakc))
- Track dirty state and have the UI behave accordingly [\#583](https://github.com/olivierkes/manuskript/pull/583) ([worstje](https://github.com/worstje))
- Fix crash if invalid character is inserted into the text. [\#578](https://github.com/olivierkes/manuskript/pull/578) ([kakaroto](https://github.com/kakaroto))
- Fix crash if using a custom pandoc installation [\#577](https://github.com/olivierkes/manuskript/pull/577) ([kakaroto](https://github.com/kakaroto))
- Fix dialog windows being created outside the desktop area [\#576](https://github.com/olivierkes/manuskript/pull/576) ([kakaroto](https://github.com/kakaroto))
- Fix occasional crashes when \(re\)moving items [\#571](https://github.com/olivierkes/manuskript/pull/571) ([worstje](https://github.com/worstje))
- trying to resolve full screen exit issues on macOS [\#569](https://github.com/olivierkes/manuskript/pull/569) ([dschanoeh](https://github.com/dschanoeh))
- Fix typos in translation format placeholders that lead to crash [\#566](https://github.com/olivierkes/manuskript/pull/566) ([RaphaelWimmer](https://github.com/RaphaelWimmer))
- Fixed \#549 and refactored the image tooltip code [\#558](https://github.com/olivierkes/manuskript/pull/558) ([worstje](https://github.com/worstje))
- Fix typo [\#548](https://github.com/olivierkes/manuskript/pull/548) ([vulpivia](https://github.com/vulpivia))
- Fix misc. typos [\#489](https://github.com/olivierkes/manuskript/pull/489) ([luzpaz](https://github.com/luzpaz))
## [0.9.0](https://github.com/olivierkes/manuskript/tree/0.9.0) (2019-04-04)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.8.0...0.9.0)
**Implemented enhancements:**
- Fullscreen editor suggestions [\#527](https://github.com/olivierkes/manuskript/issues/527)
- \[Feature Request\] Keyboard shortcuts in Full-Screen mode [\#444](https://github.com/olivierkes/manuskript/issues/444)
- \[Feature Request\] Add Ability to Add Image When Creating Fullscreen Theme [\#399](https://github.com/olivierkes/manuskript/issues/399)
- Making Fullscreen Mode Great Again [\#234](https://github.com/olivierkes/manuskript/issues/234)
**Fixed bugs:**
- Crash when previewing malformed regular expression when compiling [\#488](https://github.com/olivierkes/manuskript/issues/488)
- Spellcheck On/Off setting ignored / Manuskript unresponsive [\#474](https://github.com/olivierkes/manuskript/issues/474)
- Wrong codepage for import causes crash [\#470](https://github.com/olivierkes/manuskript/issues/470)
- Full-screen mode right-click menu black text on black background [\#440](https://github.com/olivierkes/manuskript/issues/440)
- Application language still the same after changing it in the settings. [\#411](https://github.com/olivierkes/manuskript/issues/411)
**Closed issues:**
- Python issues? lxml [\#541](https://github.com/olivierkes/manuskript/issues/541)
- Cannot open a project. [\#529](https://github.com/olivierkes/manuskript/issues/529)
- Corrupted Project File Crashes When Opening. [\#522](https://github.com/olivierkes/manuskript/issues/522)
- Specific document suddenly won't open [\#502](https://github.com/olivierkes/manuskript/issues/502)
- trying to get pandoc to work manuskript 0.8.0 Win10 64 [\#475](https://github.com/olivierkes/manuskript/issues/475)
- Editor does not show text [\#472](https://github.com/olivierkes/manuskript/issues/472)
- Application crashes when trying to save "…" [\#461](https://github.com/olivierkes/manuskript/issues/461)
- Feature Request: script writing interface for manuskript [\#435](https://github.com/olivierkes/manuskript/issues/435)
- suggestion: Use sudo for your Fedora install instructions, not su -c [\#573](https://github.com/olivierkes/manuskript/issues/573)
- Chinese translation filename suffix [\#428](https://github.com/olivierkes/manuskript/issues/428)
**Merged pull requests:**
- Fix color scheme of fullscreen editor [\#539](https://github.com/olivierkes/manuskript/pull/539) ([kakaroto](https://github.com/kakaroto))
- Directory entries in ZIP break loading code [\#531](https://github.com/olivierkes/manuskript/pull/531) ([worstje](https://github.com/worstje))
- Providing a suitable icon for consumption by Windows operating systems [\#530](https://github.com/olivierkes/manuskript/pull/530) ([worstje](https://github.com/worstje))
- Ensure text file open methods use utf-8 encoding [\#515](https://github.com/olivierkes/manuskript/pull/515) ([gedakc](https://github.com/gedakc))
- Fix crash when right-clicking twice on fullscreen panel in Windows 10 [\#514](https://github.com/olivierkes/manuskript/pull/514) ([kakaroto](https://github.com/kakaroto))
- Add support for IPython Jupyter QT Console as a debugging aid [\#513](https://github.com/olivierkes/manuskript/pull/513) ([kakaroto](https://github.com/kakaroto))
- Fix background of popup menus that were transparent \(black\) [\#512](https://github.com/olivierkes/manuskript/pull/512) ([kakaroto](https://github.com/kakaroto))
- Add snap build and package [\#511](https://github.com/olivierkes/manuskript/pull/511) ([tomwardill](https://github.com/tomwardill))
- Add ability to add new background images through UI. [\#510](https://github.com/olivierkes/manuskript/pull/510) ([kakaroto](https://github.com/kakaroto))
- Fullscreen panels improvements [\#509](https://github.com/olivierkes/manuskript/pull/509) ([kakaroto](https://github.com/kakaroto))
- Fix corkView background image on Windows [\#508](https://github.com/olivierkes/manuskript/pull/508) ([kakaroto](https://github.com/kakaroto))
- Do not default spellcheck to True for new editor views [\#506](https://github.com/olivierkes/manuskript/pull/506) ([kakaroto](https://github.com/kakaroto))
- Set editor theme stylesheet to QTextEdit only. [\#504](https://github.com/olivierkes/manuskript/pull/504) ([kakaroto](https://github.com/kakaroto))
- Fix fullscreen editor's myScrollBar delayed destruction causing a crash [\#503](https://github.com/olivierkes/manuskript/pull/503) ([kakaroto](https://github.com/kakaroto))
- 2nd try to fix macOS X blank screen when leaving fullscreen editor mode [\#495](https://github.com/olivierkes/manuskript/pull/495) ([gedakc](https://github.com/gedakc))
- Fix crash when right clicking a word in editor and enchant is not installed [\#492](https://github.com/olivierkes/manuskript/pull/492) ([kakaroto](https://github.com/kakaroto))
- Don't crash if a typo is made in the exporter's regular expression. [\#486](https://github.com/olivierkes/manuskript/pull/486) ([kakaroto](https://github.com/kakaroto))
- Fix crash when previewing pandoc HTML with QTextEdit as web renderer… [\#485](https://github.com/olivierkes/manuskript/pull/485) ([kakaroto](https://github.com/kakaroto))
- Fix crash when 7 pound signs are written alone on a line. [\#484](https://github.com/olivierkes/manuskript/pull/484) ([kakaroto](https://github.com/kakaroto))
- Try to fix macOS X blank screen when leaving editor fullscreen mode [\#482](https://github.com/olivierkes/manuskript/pull/482) ([gedakc](https://github.com/gedakc))
- Fix wrong codepage crash on import with Windows 10 [\#478](https://github.com/olivierkes/manuskript/pull/478) ([gedakc](https://github.com/gedakc))
- Spelling: Manuscript, may have to be restarted [\#454](https://github.com/olivierkes/manuskript/pull/454) ([comradekingu](https://github.com/comradekingu))
- Chinese translation [\#434](https://github.com/olivierkes/manuskript/pull/434) ([lingsamuel](https://github.com/lingsamuel))
- fix translator [\#433](https://github.com/olivierkes/manuskript/pull/433) ([lingsamuel](https://github.com/lingsamuel))
- Remember last accessed directory [\#431](https://github.com/olivierkes/manuskript/pull/431) ([lingsamuel](https://github.com/lingsamuel))
- translation suffix, change translation load order [\#430](https://github.com/olivierkes/manuskript/pull/430) ([lingsamuel](https://github.com/lingsamuel))
## [0.8.0](https://github.com/olivierkes/manuskript/tree/0.8.0) (2018-12-05)
@ -18,10 +372,11 @@
**Closed issues:**
- pt\_PT translation and Weblate [\#408](https://github.com/olivierkes/manuskript/issues/408)
- Italian translation [\#395](https://github.com/olivierkes/manuskript/issues/395)
- Problems with running from 0.7.0 pyinstaller package on mac os x 10.13 [\#386](https://github.com/olivierkes/manuskript/issues/386)
- Old bugs in current version 0.6.0 \(with crosslinks and details\) [\#371](https://github.com/olivierkes/manuskript/issues/371)
- pt\_PT translation and Weblate [\#408](https://github.com/olivierkes/manuskript/issues/408)
- Italian translation [\#395](https://github.com/olivierkes/manuskript/issues/395)
- Snowflake view mode always disabled [\#45](https://github.com/olivierkes/manuskript/issues/45)
**Merged pull requests:**
@ -39,6 +394,7 @@
- Moved incorrectly placed parameter to correct place. Closes \#377. [\#389](https://github.com/olivierkes/manuskript/pull/389) ([RiderExMachina](https://github.com/RiderExMachina))
## [0.7.0](https://github.com/olivierkes/manuskript/tree/0.7.0) (2018-08-15)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.6.0...0.7.0)
**Implemented enhancements:**
@ -97,6 +453,7 @@
- Build MacOS release with XCode 7.3 image [\#287](https://github.com/olivierkes/manuskript/pull/287) ([katafrakt](https://github.com/katafrakt))
## [0.6.0](https://github.com/olivierkes/manuskript/tree/0.6.0) (2017-11-29)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.5.0...0.6.0)
**Implemented enhancements:**
@ -141,6 +498,7 @@
- Adds: Import OPML [\#192](https://github.com/olivierkes/manuskript/pull/192) ([camstevenson](https://github.com/camstevenson))
## [0.5.0](https://github.com/olivierkes/manuskript/tree/0.5.0) (2017-10-31)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.4.0...0.5.0)
**Implemented enhancements:**
@ -219,6 +577,7 @@
- Fixes: incorrect reference to 32px icon [\#97](https://github.com/olivierkes/manuskript/pull/97) ([gedakc](https://github.com/gedakc))
## [0.4.0](https://github.com/olivierkes/manuskript/tree/0.4.0) (2017-05-25)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.3.0...0.4.0)
**Implemented enhancements:**
@ -272,7 +631,6 @@
- Is This An Active Project [\#56](https://github.com/olivierkes/manuskript/issues/56)
- Qt WebKit is deprecated [\#54](https://github.com/olivierkes/manuskript/issues/54)
- Unable to run application [\#47](https://github.com/olivierkes/manuskript/issues/47)
- Snowflake view mode always disabled [\#45](https://github.com/olivierkes/manuskript/issues/45)
- \[Windows\] Compile Dialog does not have a title [\#39](https://github.com/olivierkes/manuskript/issues/39)
- Creating manuskript binay for Android and IOS [\#21](https://github.com/olivierkes/manuskript/issues/21)
- Compiling Manuskript in windows [\#19](https://github.com/olivierkes/manuskript/issues/19)
@ -292,6 +650,7 @@
- Added spanish translation \(and changed "chuleta" for "guía rápida"\). [\#66](https://github.com/olivierkes/manuskript/pull/66) ([jmgaguilera](https://github.com/jmgaguilera))
## [0.3.0](https://github.com/olivierkes/manuskript/tree/0.3.0) (2016-03-31)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.2.0...0.3.0)
**Fixed bugs:**
@ -306,6 +665,7 @@
- Windows installation issue [\#16](https://github.com/olivierkes/manuskript/issues/16)
## [0.2.0](https://github.com/olivierkes/manuskript/tree/0.2.0) (2016-02-28)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.1.1...0.2.0)
**Fixed bugs:**
@ -314,6 +674,7 @@
- Save file doesn't automatically add .msk [\#2](https://github.com/olivierkes/manuskript/issues/2)
## [0.1.1](https://github.com/olivierkes/manuskript/tree/0.1.1) (2016-02-08)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/0.1.0...0.1.1)
**Fixed bugs:**
@ -322,5 +683,8 @@
## [0.1.0](https://github.com/olivierkes/manuskript/tree/0.1.0) (2016-02-06)
[Full Changelog](https://github.com/olivierkes/manuskript/compare/5df82d5e2de7cadd75b013c48ce4575688dd804a...0.1.0)
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*

View file

@ -1,11 +1,13 @@
# Manuskript
[Manuskript](http://www.theologeek.ch/manuskript) is an open-source
[Manuskript](https://www.theologeek.ch/manuskript) is an open-source
tool for writers.
[![manuskript](https://snapcraft.io/manuskript/badge.svg)](https://snapcraft.io/manuskript)
Manuskript runs on GNU/Linux, Mac OS X, and Windows.
![Main view](http://www.theologeek.ch/manuskript/wp-content/uploads/2017/11/manuskript-0.5.0-main-view.jpg)
![Main view](https://www.theologeek.ch/manuskript/wp-content/uploads/2017/11/manuskript-0.5.0-main-view.jpg)
## Features
@ -18,34 +20,34 @@ With Manuskript you can:
* Create characters
* Conceive plots
* Construct outlines
([Outline mode](http://www.theologeek.ch/manuskript/2016/02/05/outliner/)
([Outline mode](https://www.theologeek.ch/manuskript/2016/02/05/outliner/)
and/or
[Index cards](http://www.theologeek.ch/manuskript/2016/02/05/index-cards/))
[Index cards](https://www.theologeek.ch/manuskript/2016/02/05/index-cards/))
* Write with focus
([Distraction free mode](https://github.com/olivierkes/manuskript/wiki/Full-screen-mode))
* Build worlds
* [Track items](https://github.com/olivierkes/manuskript/wiki/How-to-keep-track-of-important-items)
* Edit and re-organize chapters and scenes
* View [Story line](http://www.theologeek.ch/manuskript/2016/02/28/story-line/)
* View [Story line](https://www.theologeek.ch/manuskript/2016/02/28/story-line/)
* Compose with
[fiction or non-fiction templates and writing modes](http://www.theologeek.ch/manuskript/2016/03/31/writing-modes-simple-fiction/)
* Export to HTML, ePub, OpenDocument, DocX, PDF, and
[fiction or non-fiction templates and writing modes](https://www.theologeek.ch/manuskript/2016/03/31/writing-modes-simple-fiction/)
* Import and export document formats such as HTML, ePub, OpenDocument, DocX, and
[more](https://github.com/olivierkes/manuskript/wiki/Import-and-Export-capabilities)
Additionally Manuskript can help in many more ways with a spell
checker, markdown highlighter,
[frequency analyzer](http://www.theologeek.ch/manuskript/2016/02/08/frequency-analyzer/),
[frequency analyzer](https://www.theologeek.ch/manuskript/2016/02/08/frequency-analyzer/),
and automatic save in
[open and plain text file format](http://www.theologeek.ch/manuskript/2016/03/31/open-plain-text-file-format/).
[open and plain text file format](https://www.theologeek.ch/manuskript/2016/03/31/open-plain-text-file-format/).
## Download
[Download](http://www.theologeek.ch/manuskript/download) and install Manuskript today.
[Download](https://www.theologeek.ch/manuskript/download) and install Manuskript today.
## HowTo's
See the [Wiki](http://github.com/olivierkes/manuskript/wiki) for more
See the [Wiki](https://github.com/olivierkes/manuskript/wiki) for more
detailed instructions on how to install and use Manuskript.
## Contribute
@ -56,7 +58,7 @@ You can help improve Manuskript by reporting
and
[translating to other languages](https://github.com/olivierkes/manuskript/wiki/Translate-Manuskript).
See also
[Manuskript Contribute page](http://www.theologeek.ch/manuskript/contribute/).
[Manuskript Contribute page](https://www.theologeek.ch/manuskript/contribute/).
Manuskript is written in Python3 and PyQt5.

View file

@ -89,17 +89,26 @@ SOURCES += ../manuskript/ui/views/outlineBasics.py
SOURCES += ../manuskript/ui/views/outlineDelegates.py
SOURCES += ../manuskript/ui/collapsibleDockWidgets.py
TRANSLATIONS += manuskript_ar_SA.ts
TRANSLATIONS += manuskript_de.ts
TRANSLATIONS += manuskript_en_GB.ts
TRANSLATIONS += manuskript_es.ts
TRANSLATIONS += manuskript_fa.ts
TRANSLATIONS += manuskript_fr.ts
TRANSLATIONS += manuskript_hu.ts
TRANSLATIONS += manuskript_id.ts
TRANSLATIONS += manuskript_it.ts
TRANSLATIONS += manuskript_ja.ts
TRANSLATIONS += manuskript_ko.ts
TRANSLATIONS += manuskript_nb_NO.ts
TRANSLATIONS += manuskript_nl.ts
TRANSLATIONS += manuskript_pl.ts
TRANSLATIONS += manuskript_pt_BR.ts
TRANSLATIONS += manuskript_pt_PT.ts
TRANSLATIONS += manuskript_ro.ts
TRANSLATIONS += manuskript_ru.ts
TRANSLATIONS += manuskript_sv.ts
TRANSLATIONS += manuskript_tr.ts
TRANSLATIONS += manuskript_uk.ts
TRANSLATIONS += manuskript_zh_CN.ts
TRANSLATIONS += manuskript_zh_HANT.ts

BIN
i18n/manuskript_ar_SA.qm Normal file

Binary file not shown.

4007
i18n/manuskript_ar_SA.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_ca.qm Normal file

Binary file not shown.

4539
i18n/manuskript_ca.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_da.qm Normal file

Binary file not shown.

4652
i18n/manuskript_da.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_el.qm Normal file

Binary file not shown.

4539
i18n/manuskript_el.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_en_GB.qm Normal file

Binary file not shown.

4010
i18n/manuskript_en_GB.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_eo.qm Normal file

Binary file not shown.

4582
i18n/manuskript_eo.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_fa.qm Normal file

Binary file not shown.

4005
i18n/manuskript_fa.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_fi.qm Normal file

Binary file not shown.

4638
i18n/manuskript_fi.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_he.qm Normal file

Binary file not shown.

4633
i18n/manuskript_he.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_hr.qm Normal file

Binary file not shown.

4684
i18n/manuskript_hr.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_ja.qm Normal file

Binary file not shown.

4008
i18n/manuskript_ja.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_ko.qm Normal file

Binary file not shown.

4008
i18n/manuskript_ko.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_mr.qm Normal file

Binary file not shown.

4638
i18n/manuskript_mr.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_pt.qm Normal file

Binary file not shown.

4582
i18n/manuskript_pt.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_ro.qm Normal file

Binary file not shown.

4005
i18n/manuskript_ro.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_ta.qm Normal file

Binary file not shown.

4633
i18n/manuskript_ta.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_tr.qm Normal file

Binary file not shown.

4005
i18n/manuskript_tr.ts Normal file

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_uk.qm Normal file

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

BIN
i18n/manuskript_zh_HANT.qm Normal file

Binary file not shown.

4005
i18n/manuskript_zh_HANT.ts Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

3
main.pyproject Normal file

File diff suppressed because one or more lines are too long

View file

@ -20,32 +20,35 @@ profile:
compile:
cd manuskript && python3 setup.py build_ext --inplace
callgraph:
cd manuskript; pycallgraph myoutput -- main.py
translation:
pylupdate5 -noobsolete i18n/manuskript.pro
linguist:
linguist i18n/manuskript_fr.ts
lrelease i18n/manuskript_fr.ts
i18n: $(QMs)
pyinstaller:
python3 /usr/local/bin/pyinstaller manuskript.spec
snappkg:
snapcraft snap
stats:
python3 libs/gh-release-stats.py olivierkes manuskript -d
%_rc.py : %.qrc
pyrcc5 "$<" -o "$@"
pyrcc5 "$<" -o "$@"
%.py : %.ui
# pyuic4 "$<" > "$@"
pyuic5 "$<" > "$@"
# pyuic4 "$<" > "$@"
pyuic5 "$<" > "$@"
%.qm: %.ts
lrelease "$<"

View file

@ -2,7 +2,6 @@
block_cipher = None
a = Analysis(['bin/manuskript'],
pathex=['.'],
binaries=None,
@ -20,8 +19,10 @@ a = Analysis(['bin/manuskript'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
@ -29,11 +30,52 @@ exe = EXE(pyz,
debug=False,
strip=False,
upx=True,
console=True )
console=True,
icon=os.path.join(SPECPATH, 'icons/Manuskript/manuskript.ico') )
wexe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='manuskriptw',
debug=False,
strip=False,
upx=True,
console=False,
icon=os.path.join(SPECPATH, 'icons/Manuskript/manuskript.ico') )
coll = COLLECT(exe,
wexe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='manuskript')
version=os.environ['manuskript_version']
app = BUNDLE(coll,
name='manuskript.app',
icon=os.path.join(SPECPATH, 'icons/Manuskript/Manuskript.icns'),
bundle_identifier='ch.theologeek.manuskript',
version=version,
info_plist={
'NSPrincipalClass': 'NSApplication',
'NSAppleScriptEnabled': False,
'NSHighResolutionCapable': True,
'CFBundleURLTypes': [{
'CFBundleURLName': 'MSK',
'CFBundleTypeRole': 'Editor',
'CFBundleURLSchemes': ['msk'],
}],
'CFBundleDocumentTypes': [
{
'CFBundleTypeName': 'MSK',
'CFBundleTypeIconFile': 'icons/Manuscript/manuskript',
'CFBundleTypeExtensions': ['msk'],
'CFBundleTypeRole': 'Editor',
'LSHandlerRank': 'Owner'
}
]
},
)

View file

@ -11,6 +11,9 @@ from PyQt5.QtGui import QCursor
from manuskript.converters import abstractConverter
from manuskript.functions import mainWindow
import logging
LOGGER = logging.getLogger(__name__)
try:
import markdown as MD
except ImportError:
@ -26,12 +29,12 @@ class markdownConverter(abstractConverter):
@classmethod
def isValid(self):
return MD is not None
return MD != None
@classmethod
def convert(self, markdown):
if not self.isValid:
print("ERROR: markdownConverter is called but not valid.")
LOGGER.error("markdownConverter is called but not valid.")
return ""
html = MD.markdown(markdown)

View file

@ -9,8 +9,10 @@ from PyQt5.QtWidgets import qApp, QMessageBox
from PyQt5.QtGui import QCursor
from manuskript.converters import abstractConverter
from manuskript.functions import mainWindow
from manuskript.functions import mainWindow, safeTranslate
import logging
LOGGER = logging.getLogger(__name__)
class pandocConverter(abstractConverter):
@ -38,7 +40,7 @@ class pandocConverter(abstractConverter):
@classmethod
def convert(self, src, _from="markdown", to="html", args=None, outputfile=None):
if not self.isValid:
print("ERROR: pandocConverter is called but not valid.")
LOGGER.error("pandocConverter is called but not valid.")
return ""
cmd = [self.runCmd()]
@ -70,9 +72,9 @@ class pandocConverter(abstractConverter):
if stderr:
err = stderr.decode("utf-8")
print(err)
LOGGER.error(err)
QMessageBox.critical(mainWindow().dialog,
qApp.translate("Export", "Error"), err)
safeTranslate(qApp, "Export", "Error"), err)
return None
return stdout.decode("utf-8")

View file

@ -1,5 +1,5 @@
#!/usr/bin/env python
#--!-- coding: utf8 --!--
# --!-- coding: utf8 --!--
from enum import IntEnum
@ -16,6 +16,8 @@ class Character(IntEnum):
summaryPara = 8
summaryFull = 9
notes = 10
pov = 11
infos = 12
class Plot(IntEnum):
name = 0
@ -60,8 +62,24 @@ class Outline(IntEnum):
textFormat = 15
revisions = 16
customIcon = 17
charCount = 18
class Abstract(IntEnum):
title = 0
ID = 1
type = 2
class FlatData(IntEnum):
summarySituation = 0,
summarySentence = 1,
summaryPara = 2,
summaryPage = 3,
summaryFull = 4
class Model(IntEnum):
Character = 0
Plot = 1
PlotStep = 2
World = 3
Outline = 4
FlatData = 5

View file

@ -10,6 +10,8 @@ from PyQt5.QtWidgets import QWidget
from manuskript.models import outlineItem
from manuskript.functions import mainWindow
import logging
LOGGER = logging.getLogger(__name__)
class basicExporter:
@ -58,8 +60,8 @@ class basicExporter:
elif self.isValid() == 1:
run = self.customPath
else:
print("Error: no command for", self.name)
return
LOGGER.error("No command for %s.", self.name)
return None
r = subprocess.check_output([run] + args) # timeout=.2
return r.decode("utf-8")
@ -71,7 +73,7 @@ class basicExporter:
# try:
# output = subprocess.check_output(cmdl, stdin=cmd.stdout, stderr=subprocess.STDOUT) # , cwd="/tmp"
# except subprocess.CalledProcessError as e:
# print("Error!")
# LOGGER.error("Failed to read from process output.")
# return text
# cmd.wait()
#

View file

@ -6,6 +6,8 @@ from PyQt5.QtWidgets import QPlainTextEdit, qApp, QTabWidget, QFrame, QTextEdit
from manuskript.exporter.manuskript.markdown import markdown, markdownSettings
from manuskript.ui.views.webView import webView
from manuskript.ui.exporters.manuskript.plainTextSettings import exporterSettings
from manuskript.functions import safeTranslate
import os
try:
@ -15,15 +17,16 @@ except ImportError:
class HTML(markdown):
name = "HTML"
description = qApp.translate("Export", "Basic HTML output using the Python module 'markdown'.")
InvalidBecause = qApp.translate("Export", "Python module 'markdown'.")
description = safeTranslate(qApp, "Export", "Basic HTML output using the Python module 'markdown'.")
InvalidBecause = safeTranslate(qApp, "Export", "Python module 'markdown'.")
icon = "text-html"
exportVarName = "lastManuskriptHTML"
exportFilter = "HTML files (*.html);; Any files (*)"
exportDefaultSuffix = ".html"
def isValid(self):
return MD is not None
return MD != None
def settingsWidget(self):
w = markdownSettings(self)
@ -50,12 +53,12 @@ class HTML(markdown):
w1 = QPlainTextEdit()
w1.setFrameShape(QFrame.NoFrame)
w1.setReadOnly(True)
t.addTab(w0, qApp.translate("Export", "Markdown source"))
t.addTab(w1, qApp.translate("Export", "HTML Source"))
t.addTab(w0, safeTranslate(qApp, "Export", "Markdown source"))
t.addTab(w1, safeTranslate(qApp, "Export", "HTML Source"))
if webView:
w2 = webView()
t.addTab(w2, qApp.translate("Export", "HTML Output"))
t.addTab(w2, safeTranslate(qApp, "Export", "HTML Output"))
t.setCurrentIndex(2)
return t

View file

@ -6,13 +6,13 @@ from manuskript.exporter.basic import basicExporter, basicFormat
from manuskript.exporter.manuskript.HTML import HTML
from manuskript.exporter.manuskript.markdown import markdown
from manuskript.exporter.manuskript.plainText import plainText
from manuskript.functions import appPath
from manuskript.functions import appPath, safeTranslate
class manuskriptExporter(basicExporter):
name = "Manuskript"
description = qApp.translate("Export", "Default exporter, provides basic formats used by other exporters.")
description = safeTranslate(qApp, "Export", "Default exporter, provides basic formats used by other exporters.")
exportTo = [
plainText(),
markdown(),

View file

@ -4,18 +4,19 @@ from PyQt5.QtGui import QTextCharFormat, QFont
from PyQt5.QtWidgets import QPlainTextEdit, QGroupBox, qApp, QVBoxLayout, QCheckBox
from manuskript.exporter.manuskript.plainText import plainText
from manuskript.functions import mainWindow
from manuskript.functions import mainWindow, safeTranslate
from manuskript.ui.highlighters import MMDHighlighter
from manuskript.ui.exporters.manuskript.plainTextSettings import exporterSettings
class markdown(plainText):
name = "Markdown"
description = qApp.translate("Export", """Just like plain text, excepts adds markdown titles.
description = safeTranslate(qApp, "Export", """Just like plain text, excepts adds markdown titles.
Presupposes that texts are formatted in markdown.""")
exportVarName = "lastManuskriptMarkdown"
exportFilter = "Markdown files (*.md);; Any files (*)"
exportDefaultSuffix = ".md"
icon = "text-x-markdown"
def settingsWidget(self):
@ -56,7 +57,7 @@ class markdownSettings(exporterSettings):
w = self.toolBox.widget(self.toolBox.count() - 1)
self.grpMarkdown = QGroupBox(self.tr("Markdown"))
self.grpMarkdown.setLayout(QVBoxLayout())
self.chkMarkdownHighlighter = QCheckBox(qApp.translate("Export", "Preview with highlighter."))
self.chkMarkdownHighlighter = QCheckBox(safeTranslate(qApp, "Export", "Preview with highlighter."))
self.grpMarkdown.layout().addWidget(self.chkMarkdownHighlighter)
w.layout().insertWidget(w.layout().count() - 1, self.grpMarkdown)

View file

@ -2,18 +2,21 @@
# --!-- coding: utf8 --!--
import re
from PyQt5.QtGui import QFont, QTextCharFormat
from PyQt5.QtWidgets import QPlainTextEdit, qApp, QFrame, QFileDialog
from PyQt5.QtWidgets import QPlainTextEdit, qApp, QFrame, QFileDialog, QMessageBox
from manuskript.exporter.basic import basicFormat
from manuskript.functions import mainWindow
from manuskript.functions import mainWindow, getSaveFileNameWithSuffix, safeTranslate
from manuskript.models import outlineItem
from manuskript.ui.exporters.manuskript.plainTextSettings import exporterSettings
import codecs
import logging
LOGGER = logging.getLogger(__name__)
class plainText(basicFormat):
name = qApp.translate("Export", "Plain text")
description = qApp.translate("Export", """Simplest export to plain text. Allows you to use your own markup not understood
by manuskript, for example <a href='www.fountain.io'>Fountain</a>.""")
name = safeTranslate(qApp, "Export", "Plain text")
description = safeTranslate(qApp, "Export", """Simplest export to plain text. Allows you to use your own markup not understood
by Manuskript, for example <a href='www.fountain.io'>Fountain</a>.""")
implemented = True
requires = {
"Settings": True,
@ -24,6 +27,7 @@ class plainText(basicFormat):
# Default settings used in self.getExportFilename. For easy subclassing when exporting plaintext.
exportVarName = "lastPlainText"
exportFilter = "Text files (*.txt);; Any files (*)"
exportDefaultSuffix = ".txt" # qt ignores the period, but it is clearer in our code to have it
def __init__(self):
pass
@ -41,14 +45,19 @@ class plainText(basicFormat):
def output(self, settingsWidget):
settings = settingsWidget.getSettings()
return self.concatenate(mainWindow().mdlOutline.rootItem, settings)
try:
return self.concatenate(mainWindow().mdlOutline.rootItem, settings)
except re.error as e:
QMessageBox.warning(mainWindow().dialog, safeTranslate(qApp, "Export", "Error"),
safeTranslate(qApp, "Export", "Could not process regular expression: \n{}").format(str(e)))
return ""
def getExportFilename(self, settingsWidget, varName=None, filter=None):
if varName is None:
if varName == None:
varName = self.exportVarName
if filter is None:
if filter == None:
filter = self.exportFilter
settings = settingsWidget.getSettings()
@ -59,30 +68,18 @@ class plainText(basicFormat):
else:
filename = ""
filename, filter = QFileDialog.getSaveFileName(settingsWidget.parent(),
caption=qApp.translate("Export", "Chose output file..."),
filter=filter,
directory=filename)
filename, filter = getSaveFileNameWithSuffix(settingsWidget.parent(),
caption=safeTranslate(qApp, "Export", "Choose output file…"),
filter=filter,
directory=filename,
defaultSuffix=self.exportDefaultSuffix)
if filename:
s[varName] = filename
settingsWidget.settings["Output"] = s
# Auto adds extension if necessary
try:
# Extract the extension from "Some name (*.ext)"
ext = filter.split("(")[1].split(")")[0]
ext = ext.split(".")[1]
if " " in ext: # In case there are multiple extensions: "Images (*.png *.jpg)"
ext = ext.split(" ")[0]
except:
ext = ""
if ext and filename[-len(ext)-1:] != ".{}".format(ext):
filename += "." + ext
# Save settings
settingsWidget.writeSettings()
# Save settings
settingsWidget.writeSettings()
return filename
@ -90,14 +87,15 @@ class plainText(basicFormat):
settings = settingsWidget.getSettings()
filename = self.getExportFilename(settingsWidget)
settingsWidget.writeSettings()
content = self.output(settingsWidget)
if not content:
print("Error: content is empty. Nothing saved.")
return
if filename:
settingsWidget.writeSettings()
content = self.output(settingsWidget)
if not content:
LOGGER.error("No content. Nothing saved.")
return
with open(filename, "w", encoding='utf8') as f:
f.write(content)

View file

@ -1,21 +1,24 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtWidgets import qApp
from PyQt5.QtWidgets import qApp, QTextEdit
from PyQt5.QtCore import QUrl
from manuskript.exporter.manuskript import HTML as MskHTML
from manuskript.exporter.pandoc.abstractPlainText import abstractPlainText
from manuskript.functions import safeTranslate
import os
class HTML(abstractPlainText):
name = "HTML"
description = qApp.translate("Export", """A little known format modestly used. You know, web sites for example.""")
description = safeTranslate(qApp, "Export", """A little known format modestly used. You know, web sites for example.""")
icon = "text-html"
exportVarName = "lastPandocHTML"
toFormat = "html"
exportFilter = "HTML files (*.html);; Any files (*)"
exportDefaultSuffix = ".html"
requires = {
"Settings": True,
"Preview": True,
@ -40,4 +43,8 @@ class HTML(abstractPlainText):
previewWidget.widget(0).setPlainText(src)
self.preparesTextEditView(previewWidget.widget(1), settings["Preview"]["PreviewFont"])
previewWidget.widget(1).setPlainText(html)
previewWidget.widget(2).setHtml(html, QUrl.fromLocalFile(path))
w2 = previewWidget.widget(2)
if isinstance(w2, QTextEdit):
w2.setHtml(html)
else:
w2.setHtml(html, QUrl.fromLocalFile(path))

View file

@ -7,7 +7,7 @@ from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import qApp
from manuskript.exporter.pandoc.abstractOutput import abstractOutput
from manuskript.functions import tempFile
from manuskript.functions import tempFile, safeTranslate
from manuskript.ui.views.PDFViewer import PDFViewer
@ -15,14 +15,15 @@ class PDF(abstractOutput):
"""PDF Viewer using PDF.js. Cf. https://github.com/mozilla/pdf.js/wiki/Setup-PDF.js-in-a-website"""
name = "PDF"
description = qApp.translate("Export", "Needs LaTeX to be installed.")
InvalidBecause = qApp.translate("Export", """a valid LaTeX installation. Pandoc recommendations can be found on:
description = safeTranslate(qApp, "Export", "Needs LaTeX to be installed.")
InvalidBecause = safeTranslate(qApp, "Export", """a valid LaTeX installation. Pandoc recommendations can be found on:
<a href="https://pandoc.org/installing.html">pandoc.org/installing.html</a>. If you want Unicode support, you need XeLaTeX.""")
icon = "application-pdf"
exportVarName = "lastPandocPDF"
toFormat = "pdf"
exportFilter = "PDF files (*.pdf);; Any files (*)"
exportDefaultSuffix = ".pdf"
requires = {
"Settings": True,
"Preview": True,
@ -30,7 +31,7 @@ class PDF(abstractOutput):
def isValid(self):
path = shutil.which("pdflatex") or shutil.which("xelatex")
return path is not None
return path != None
def output(self, settingsWidget, outputfile=None):
args = settingsWidget.runnableSettings()

View file

@ -11,13 +11,15 @@ from manuskript.exporter.pandoc.HTML import HTML
from manuskript.exporter.pandoc.PDF import PDF
from manuskript.exporter.pandoc.outputFormats import ePub, OpenDocument, DocX
from manuskript.exporter.pandoc.plainText import reST, markdown, latex, OPML
from manuskript.functions import mainWindow
from manuskript.functions import mainWindow, safeTranslate
import logging
LOGGER = logging.getLogger(__name__)
class pandocExporter(basicExporter):
name = "Pandoc"
description = qApp.translate("Export", """<p>A universal document converter. Can be used to convert markdown to a wide range of other
description = safeTranslate(qApp, "Export", """<p>A universal document converter. Can be used to convert Markdown to a wide range of other
formats.</p>
<p>Website: <a href="http://www.pandoc.org">http://pandoc.org/</a></p>
""")
@ -48,7 +50,14 @@ class pandocExporter(basicExporter):
return ""
def convert(self, src, args, outputfile=None):
args = [self.cmd] + args
if self.isValid() == 2:
run = self.cmd
elif self.isValid() == 1:
run = self.customPath
else:
LOGGER.error("No command for pandoc.")
return None
args = [run] + args
if outputfile:
args.append("--output={}".format(outputfile))
@ -67,8 +76,11 @@ class pandocExporter(basicExporter):
if var and item and item.text().strip():
args.append("--variable={}:{}".format(var, item.text().strip()))
# Add title metatadata required for pandoc >= 2.x
args.append("--metadata=title:{}".format(mainWindow().mdlFlatData.item(0, 0).text().strip()))
# Add title metadata required for pandoc >= 2.x
title = "Untitled"
if mainWindow().mdlFlatData.item(0, 0):
title = mainWindow().mdlFlatData.item(0, 0).text().strip()
args.append("--metadata=title:{}".format(title))
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
@ -87,12 +99,16 @@ class pandocExporter(basicExporter):
qApp.restoreOverrideCursor()
if stderr or p.returncode != 0:
err = "ERROR on export" + "\n" \
+ "Return code" + ": %d\n" % (p.returncode) \
+ "Command and parameters" + ":\n%s\n" % (p.args) \
+ "Stderr content" + ":\n" + stderr.decode("utf-8")
print(err)
QMessageBox.critical(mainWindow().dialog, qApp.translate("Export", "Error"), err)
err_type = "ERROR" if p.returncode != 0 else "WARNING"
err = "%s on export\n" % err_type \
+ "Return code: %d\n" % p.returncode \
+ "Command and parameters:\n%s\n" % p.args \
+ "Stderr content:\n" + stderr.decode("utf-8")
if p.returncode != 0:
LOGGER.error(err)
QMessageBox.critical(mainWindow().dialog, safeTranslate(qApp, "Export", "Error"), err)
else:
LOGGER.warning(err)
return None
return stdout.decode("utf-8")

View file

@ -10,6 +10,7 @@ class abstractOutput(abstractPlainText):
toFormat = "SUBCLASSME"
icon = "SUBCLASSME"
exportFilter = "SUBCLASSME"
exportDefaultSuffix = ".SUBCLASSME"
requires = {
"Settings": True,
"Preview": False,

View file

@ -7,6 +7,10 @@ from PyQt5.QtWidgets import qApp, QVBoxLayout, QCheckBox, QWidget, QHBoxLayout,
from manuskript.exporter.manuskript.markdown import markdown, markdownSettings
from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
from manuskript.functions import safeTranslate
import logging
LOGGER = logging.getLogger(__name__)
class abstractPlainText(markdown):
@ -16,19 +20,22 @@ class abstractPlainText(markdown):
toFormat = "SUBCLASSME"
icon = "SUBCLASSME"
exportFilter = "SUBCLASSME"
exportDefaultSuffix = ".SUBCLASSME"
def __init__(self, exporter):
self.exporter = exporter
def settingsWidget(self):
# Get pandoc major version to determine valid command line options
p = re.compile(r'pandoc (\d+)\..*')
p = re.compile(r'pandoc (\d+)\.(\d+).*')
m = p.match(self.exporter.version())
if m:
majorVersion = m.group(1)
minorVersion = m.group(2)
else:
majorVersion = ""
w = pandocSettings(self, majorVersion, toFormat=self.toFormat)
minorVersion = ""
w = pandocSettings(self, majorVersion, minorVersion, toFormat=self.toFormat)
w.loadSettings()
return w
@ -55,8 +62,35 @@ class abstractPlainText(markdown):
previewWidget.setPlainText(r)
def versionAsInt(version):
if version is None:
return 0
try:
return int(version)
except ValueError:
return 0
def versionToIntArray(version):
if version is None:
return [0, 0]
p = re.compile(r'(\d+)\.(\d+).*')
m = p.match(version)
if m:
majorVersion = m.group(1)
minorVersion = m.group(2)
else:
majorVersion = ""
minorVersion = ""
return [ versionAsInt(majorVersion), versionAsInt(minorVersion) ]
class pandocSetting:
def __init__(self, arg, type, format, label, widget=None, default=None, min=None, max=None, vals=None, suffix=""):
def __init__(self, arg, type, format, label, widget=None, default=None, min=None, max=None, vals=None, suffix="",
minVersion=None, maxVersion=None, specific=False, toc=False):
self.arg = arg # start with EXT for extensions
self.type = type
self.label = label
@ -69,12 +103,16 @@ class pandocSetting:
self.max = max
self.vals = vals.split("|") if vals else []
self.suffix = suffix
self.minVersion = versionToIntArray(minVersion)
self.maxVersion = versionToIntArray(maxVersion)
self.specific = specific
self.toc = toc
def isValid(self, format):
"""Return whether the specific setting is active with the given format."""
# Empty formats means all
if self.formats is "":
if self.formats == "":
return True
# "html" in "html markdown latex"
@ -87,120 +125,143 @@ class pandocSetting:
return False
def isCompatible(self, majorVersion, minorVersion):
majorNumber = versionAsInt(majorVersion)
minorNumber = versionAsInt(minorVersion)
if (majorNumber < self.minVersion[0]) or ((majorNumber == self.minVersion[0]) and
(minorNumber < self.minVersion[1])):
return False
if (self.maxVersion[0] == 0) and (self.maxVersion[1] == 0):
return True
return (majorNumber < self.maxVersion[0]) or ((majorNumber == self.maxVersion[0]) and
(minorNumber <= self.maxVersion[1]))
def isSpecific(self):
return self.specific
def isTOC(self):
return self.toc
class pandocSettings(markdownSettings):
settingsList = {
# General
"standalone": pandocSetting("--standalone", "checkbox", "",
qApp.translate("Export", "Standalone document (not just a fragment)"),
safeTranslate(qApp, "Export", "Standalone document (not just a fragment)"),
default=True),
"TOC": pandocSetting("--toc", "checkbox", "",
qApp.translate("Export", "Include a table of contents.")),
safeTranslate(qApp, "Export", "Include a table of contents."), toc=True),
"TOC-depth": pandocSetting("--toc-depth=", "number", "",
qApp.translate("Export", "Number of sections level to include in TOC: "),
default=3, min=1, max=6),
safeTranslate(qApp, "Export", "Number of sections level to include in TOC: "),
default=3, min=1, max=6, toc=True, minVersion="1.10"),
# pandoc v1 only
"smart": pandocSetting("--smart", "checkbox", "",
qApp.translate("Export", "Typographically correct output")),
safeTranslate(qApp, "Export", "Typographically correct output"),
maxVersion="1.19.2.4"),
# pandoc v1 only
"normalize": pandocSetting("--normalize", "checkbox", "",
qApp.translate("Export", "Normalize the document (cleaner)")),
"base-header": pandocSetting("--base-header-level=", "number", "",
qApp.translate("Export", "Specify the base level for headers: "),
default=1, min=1),
safeTranslate(qApp, "Export", "Normalize the document (cleaner)"),
minVersion="1.8", maxVersion="1.19.2.4"),
# pandoc v1.5 to 2.7.3
"base-header": pandocSetting("--base-header-level=", "number", "",
safeTranslate(qApp, "Export", "Specify the base level for headers: "),
default=1, min=1, minVersion="1.5", maxVersion="2.7.3"),
# pandoc v2.8+
"shift-heading": pandocSetting("--shift-heading-level-by=", "number", "",
safeTranslate(qApp, "Export", "Specify the base level for headers: "),
default=0, min=0, minVersion="2.8"),
"disable-YAML": pandocSetting("EXT-yaml_metadata_block", "checkbox", "",
qApp.translate("Export", "Disable YAML metadata block.\nUse that if you get YAML related error.")),
safeTranslate(qApp, "Export", "Disable YAML metadata block.\nUse that if you get YAML related error."),
minVersion="1.12"),
"hard-line-breaks": pandocSetting("EXT-hard_line_block", "checkbox", "",
safeTranslate(qApp, "Export", "Enable the support on markdown for line break on new line."),
minVersion="1.16"),
# Specific
"ref-link": pandocSetting("--reference-links", "checkbox", "markdown rst",
qApp.translate("Export", "Use reference-style links instead of inline links")),
safeTranslate(qApp, "Export", "Use reference-style links instead of inline links"),
specific=True),
# pandoc v1.9 to v2.11.1
"atx": pandocSetting("--atx-headers", "checkbox", "markdown asciidoc",
qApp.translate("Export", "Use ATX-style headers")),
safeTranslate(qApp, "Export", "Use ATX-style headers"), specific=True,
minVersion="1.9", maxVersion="2.11.1"),
# pandoc v2.11.2+
"atx-heading": pandocSetting("--markdown-headings=atx|setext", "checkbox", "markdown asciidoc",
safeTranslate(qApp, "Export", "Use ATX-style headers"), specific=True,
minVersion="2.11.2"),
"self-contained": pandocSetting("--self-contained", "checkbox", "html",
qApp.translate("Export", "Self-contained HTML files, with no dependencies")),
safeTranslate(qApp, "Export", "Self-contained HTML files, with no dependencies"),
specific=True, minVersion="1.9"),
"q-tags": pandocSetting("--html-q-tags", "checkbox", "html",
qApp.translate("Export", "Use <q> tags for quotes in HTML")),
safeTranslate(qApp, "Export", "Use <q> tags for quotes in HTML"), specific=True,
minVersion="1.10"),
# pandoc v1 only
"latex-engine": pandocSetting("--latex-engine=", "combo", "pdf",
qApp.translate("Export", "LaTeX engine used to produce the PDF."),
vals="pdflatex|lualatex|xelatex"),
safeTranslate(qApp, "Export", "LaTeX engine used to produce the PDF."),
vals="pdflatex|lualatex|xelatex", specific=True,
minVersion="1.9", maxVersion="1.19.2.4"),
# pandoc v2
"pdf-engine": pandocSetting("--pdf-engine=", "combo", "pdf",
qApp.translate("Export", "LaTeX engine used to produce the PDF."),
vals="pdflatex|lualatex|xelatex"),
safeTranslate(qApp, "Export", "LaTeX engine used to produce the PDF."),
vals="pdflatex|lualatex|xelatex", minVersion="2.0", specific=True),
"epub3": pandocSetting("EXTepub3", "checkbox", "epub",
qApp.translate("Export", "Convert to ePUB3")),
}
pdfSettings = {
safeTranslate(qApp, "Export", "Convert to ePUB3"), specific=True,
minVersion="1.10"),
# PDF
"latex-ps": pandocSetting("--variable=papersize:", "combo", "pdf latex", # FIXME: does not work with default template
qApp.translate("Export", "Paper size:"),
vals="letter|A4|A5"),
safeTranslate(qApp, "Export", "Paper size:"),
vals="letter|A4|A5", specific=True, minVersion="1.4"),
"latex-fs": pandocSetting("--variable=fontsize:", "number", "pdf latex", # FIXME: does not work with default template
qApp.translate("Export", "Font size:"),
min=8, max=88, default=12, suffix="pt"),
safeTranslate(qApp, "Export", "Font size:"),
min=8, max=88, default=12, suffix="pt", specific=True, minVersion="1.4"),
"latex-class": pandocSetting("--variable=documentclass:", "combo", "pdf latex",
qApp.translate("Export", "Class:"),
vals="article|report|book|memoir"),
safeTranslate(qApp, "Export", "Class:"),
vals="article|report|book|memoir", specific=True, minVersion="1.4"),
"latex-ls": pandocSetting("--variable=linestretch:", "combo", "pdf latex",
qApp.translate("Export", "Line spacing:"),
vals="1|1.25|1.5|2"),
safeTranslate(qApp, "Export", "Line spacing:"),
vals="1|1.25|1.5|2", specific=True, minVersion="1.4"),
# FIXME: complete with http://pandoc.org/README.html#variables-for-latex
}
def __init__(self, _format, majorVersion="", toFormat=None, parent=None):
def __init__(self, _format, majorVersion="", minorVersion="", toFormat=None, parent=None):
markdownSettings.__init__(self, _format, parent)
self.format = toFormat
self.majorVersion = majorVersion
self.minorVersion = minorVersion
dropSettings = []
for key, setting in self.settingsList.items():
if not setting.isCompatible(self.majorVersion, self.minorVersion):
dropSettings.append(key)
LOGGER.info(f'Using pandoc settings: {self.majorVersion}.{self.minorVersion}, dropping: {dropSettings}')
for key in dropSettings:
self.settingsList.pop(key, None)
w = QWidget(self)
w.setLayout(QVBoxLayout())
self.grpPandocGeneral = self.collapsibleGroupBox(self.tr("General"), w)
if majorVersion == "1":
# pandoc v1 only
self.addSettingsWidget("smart", self.grpPandocGeneral)
self.addSettingsWidget("normalize", self.grpPandocGeneral)
else:
# pandoc v2
self.settingsList.pop("smart", None)
self.settingsList.pop("normalize", None)
self.addSettingsWidget("base-header", self.grpPandocGeneral)
self.addSettingsWidget("standalone", self.grpPandocGeneral)
self.addSettingsWidget("disable-YAML", self.grpPandocGeneral)
self.grpPandocSpecific = self.collapsibleGroupBox(self.tr("Custom settings for {}").format(self.format), w)
self.grpPandocTOC = self.collapsibleGroupBox(self.tr("Table of Content"), w)
self.addSettingsWidget("TOC", self.grpPandocTOC)
self.addSettingsWidget("TOC-depth", self.grpPandocTOC)
self.grpPandocSpecific = self.collapsibleGroupBox(self.tr("Custom settings for {}").format(self.format), w)
self.addSettingsWidget("ref-link", self.grpPandocSpecific)
self.addSettingsWidget("atx", self.grpPandocSpecific)
self.addSettingsWidget("self-contained", self.grpPandocSpecific)
self.addSettingsWidget("q-tags", self.grpPandocSpecific)
if majorVersion == "1":
# pandoc v1 only
self.addSettingsWidget("latex-engine", self.grpPandocSpecific)
self.settingsList.pop("pdf-engine", None)
else:
# pandoc v2
self.settingsList.pop("latex-engine", None)
self.addSettingsWidget("pdf-engine", self.grpPandocSpecific)
self.addSettingsWidget("epub3", self.grpPandocSpecific)
# PDF settings
self.settingsList.update(self.pdfSettings)
for i in self.pdfSettings:
self.addSettingsWidget(i, self.grpPandocSpecific)
for key, setting in self.settingsList.items():
if setting.isTOC():
self.addSettingsWidget(key, self.grpPandocTOC)
elif setting.isSpecific():
self.addSettingsWidget(key, self.grpPandocSpecific)
else:
self.addSettingsWidget(key, self.grpPandocGeneral)
self.toolBox.insertItem(self.toolBox.count() - 1, w, "Pandoc")
self.toolBox.layout().setSpacing(0) # Not sure why this is needed, but hey...
@ -310,6 +371,8 @@ class pandocSettings(markdownSettings):
extensions += "-yaml_metadata_block"
if name == "epub3" and s.widget.isChecked():
toFormat = "epub3"
if name == "hard-line-breaks" and s.widget.isChecked():
extensions += "+hard_line_breaks"
r = ["--from=markdown" + extensions,
"--to={}".format(toFormat)]

View file

@ -3,34 +3,38 @@
from PyQt5.QtWidgets import qApp
from manuskript.exporter.pandoc.abstractOutput import abstractOutput
from manuskript.functions import safeTranslate
class ePub(abstractOutput):
name = "ePub"
description = qApp.translate("Export", """Books that don't kill trees.""")
description = safeTranslate(qApp, "Export", """Books that don't kill trees.""")
icon = "application-epub+zip"
exportVarName = "lastPandocePub"
toFormat = "epub"
exportFilter = "ePub files (*.epub);; Any files (*)"
exportDefaultSuffix = ".epub"
class OpenDocument(abstractOutput):
name = "OpenDocument"
description = qApp.translate("Export", "OpenDocument format. Used by LibreOffice for example.")
description = safeTranslate(qApp, "Export", "OpenDocument format. Used by LibreOffice for example.")
exportVarName = "lastPandocODT"
toFormat = "odt"
icon = "application-vnd.oasis.opendocument.text"
exportFilter = "OpenDocument files (*.odt);; Any files (*)"
exportDefaultSuffix = ".odt"
class DocX(abstractOutput):
name = "DocX"
description = qApp.translate("Export", "Microsoft Office (.docx) document.")
description = safeTranslate(qApp, "Export", "Microsoft Office (.docx) document.")
exportVarName = "lastPandocDocX"
toFormat = "docx"
icon = "application-vnd.openxmlformats-officedocument.wordprocessingml.document"
exportFilter = "DocX files (*.docx);; Any files (*)"
exportDefaultSuffix = ".docx"

View file

@ -3,43 +3,47 @@
from PyQt5.QtWidgets import qApp
from manuskript.exporter.pandoc.abstractPlainText import abstractPlainText
from manuskript.functions import safeTranslate
class markdown(abstractPlainText):
name = "Markdown"
description = qApp.translate("Export", """Export to markdown, using pandoc. Allows more formatting options
description = safeTranslate(qApp, "Export", """Export to markdown, using pandoc. Allows more formatting options
than the basic manuskript exporter.""")
icon = "text-x-markdown"
exportVarName = "lastPandocMarkdown"
toFormat = "markdown"
exportFilter = "Markdown files (*.md);; Any files (*)"
exportDefaultSuffix = ".md"
class reST(abstractPlainText):
name = "reST"
description = qApp.translate("Export", """reStructuredText is a lightweight markup language.""")
description = safeTranslate(qApp, "Export", """reStructuredText is a lightweight markup language.""")
exportVarName = "lastPandocreST"
toFormat = "rst"
icon = "text-plain"
exportFilter = "reST files (*.rst);; Any files (*)"
exportDefaultSuffix = ".rst"
class latex(abstractPlainText):
name = "LaTeX"
description = qApp.translate("Export", """LaTeX is a word processor and document markup language used to create
description = safeTranslate(qApp, "Export", """LaTeX is a word processor and document markup language used to create
beautiful documents.""")
exportVarName = "lastPandocLatex"
toFormat = "latex"
icon = "text-x-tex"
exportFilter = "Tex files (*.tex);; Any files (*)"
exportDefaultSuffix = ".tex"
class OPML(abstractPlainText):
name = "OPML"
description = qApp.translate("Export", """The purpose of this format is to provide a way to exchange information
description = safeTranslate(qApp, "Export", """The purpose of this format is to provide a way to exchange information
between outliners and Internet services that can be browsed or controlled
through an outliner.""")
@ -47,5 +51,5 @@ class OPML(abstractPlainText):
toFormat = "opml"
icon = "text-x-opml+xml"
exportFilter = "OPML files (*.opml);; Any files (*)"
exportDefaultSuffix = ".opml"

View file

@ -3,25 +3,62 @@
import os
import re
import sys
import pathlib
from random import *
from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp, QDir
from PyQt5.QtCore import QUrl, QTimer
from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QProcess, QRegExp
from PyQt5.QtCore import QDir, QUrl, QTimer
from PyQt5.QtGui import QBrush, QIcon, QPainter, QColor, QImage, QPixmap
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import qApp, QTextEdit
from PyQt5.QtWidgets import qApp, QFileDialog
from manuskript.enums import Outline
import logging
LOGGER = logging.getLogger(__name__)
# Used to detect multiple connections
AUC = Qt.AutoConnection | Qt.UniqueConnection
MW = None
def safeTranslate(qApp, group, text):
try:
return qApp.translate(group, text)
except:
return text
def wordCount(text):
t = text.strip().replace(" ", "\n").split("\n")
t = [l for l in t if l]
return len(t)
return len(re.findall(r"\S+", text))
def charCount(text, use_spaces = True):
if use_spaces:
return len(re.findall(r"[\S ]", text))
else:
return len(re.findall(r"\S", text))
validate_ok = lambda *args, **kwargs: True
def uiParse(input, default, converter, validator=validate_ok):
"""
uiParse is a utility function that intends to make it easy to convert
user input to data that falls in the range of expected values the
program is expecting to handle.
It swallows all exceptions that happen during conversion.
The validator should return True to permit the converted value.
"""
result = default
try:
result = converter(input)
except:
pass # failed to convert
# Whitelist default value in case default type differs from converter output.
if (result != default) and not validator(result):
result = default
return result
def toInt(text):
@ -50,6 +87,7 @@ def toString(text):
def drawProgress(painter, rect, progress, radius=0):
from manuskript.ui import style as S
progress = toFloat(progress) # handle invalid input (issue #561)
painter.setPen(Qt.NoPen)
painter.setBrush(QColor(S.base)) # "#dddddd"
painter.drawRoundedRect(rect, radius, radius)
@ -57,7 +95,7 @@ def drawProgress(painter, rect, progress, radius=0):
painter.setBrush(QBrush(colorFromProgress(progress)))
r2 = QRect(rect)
r2.setWidth(r2.width() * min(progress, 1))
r2.setWidth(int(r2.width() * min(progress, 1)))
painter.drawRoundedRect(r2, radius, radius)
@ -140,24 +178,25 @@ def randomColor(mix=None):
b = randint(0, 255)
if mix:
r = (r + mix.red()) / 2
g = (g + mix.green()) / 2
b = (b + mix.blue()) / 2
r = int((r + mix.red()) / 2)
g = int((g + mix.green()) / 2)
b = int((b + mix.blue()) / 2)
return QColor(r, g, b)
def mixColors(col1, col2, f=.5):
def mixColors(col1, col2, f=0.5):
fromString = False
if type(col1) == str:
fromString = True
col1 = QColor(col1)
if type(col2) == str:
col2 = QColor(col2)
f2 = 1-f
r = col1.red() * f + col2.red() * f2
g = col1.green() * f + col2.green() * f2
b = col1.blue() * f + col2.blue() * f2
f2 = 1.0 - f
r = int(col1.red() * f + col2.red() * f2)
g = int(col1.green() * f + col2.green() * f2)
b = int(col1.blue() * f + col2.blue() * f2)
return QColor(r, g, b) if not fromString else QColor(r, g, b).name()
@ -372,7 +411,7 @@ def statusMessage(message, duration=5000, importance=1):
MW.statusLabel.adjustSize()
g = MW.statusLabel.geometry()
# g.moveCenter(MW.mapFromGlobal(MW.geometry().center()))
s = MW.layout().spacing() / 2
s = int(MW.layout().spacing() / 2)
g.setLeft(s)
g.moveBottom(MW.mapFromGlobal(MW.geometry().bottomLeft()).y() - s)
MW.statusLabel.setGeometry(g)
@ -385,6 +424,28 @@ def openURL(url):
"""
QDesktopServices.openUrl(QUrl(url))
def getSaveFileNameWithSuffix(parent, caption, directory, filter, options=None, selectedFilter=None, defaultSuffix=None):
"""
A reimplemented version of QFileDialog.getSaveFileName() because we would like to make use
of the QFileDialog.defaultSuffix property that getSaveFileName() does not let us adjust.
Note: knowing the selected filter is not an invitation to change the chosen filename later.
"""
dialog = QFileDialog(parent=parent, caption=caption, directory=directory, filter=filter)
if options:
dialog.setOptions(options)
if defaultSuffix:
dialog.setDefaultSuffix(defaultSuffix)
dialog.setFileMode(QFileDialog.AnyFile)
if hasattr(dialog, 'setSupportedSchemes'): # Pre-Qt5.6 lacks this.
dialog.setSupportedSchemes(("file",))
dialog.setAcceptMode(QFileDialog.AcceptSave)
if selectedFilter:
dialog.selectNameFilter(selectedFilter)
if (dialog.exec() == QFileDialog.Accepted):
return dialog.selectedFiles()[0], dialog.selectedNameFilter()
return None, None
def inspect():
"""
Debugging tool. Call it to see a stack of calls up to that point.
@ -397,3 +458,127 @@ def inspect():
s.lineno,
s.function))
print(" " + "".join(s.code_context))
def search(searchRegex, text):
"""
Search all occurrences of a regex in a text.
:param searchRegex: a regex object with the search to perform
:param text: text to search on
:return: list of tuples (startPos, endPos)
"""
if text is not None:
return [(m.start(), m.end(), getSearchResultContext(text, m.start(), m.end())) for m in searchRegex.finditer(text)]
else:
return []
def getSearchResultContext(text, startPos, endPos):
matchSize = endPos - startPos
maxContextSize = max(matchSize, 600)
extraContextSize = int((maxContextSize - matchSize) / 2)
separator = "[...]"
context = ""
i = startPos - 1
while i > 0 and (startPos - i) < extraContextSize and text[i] != '\n':
i -= 1
contextStartPos = i
if i > 0:
context += separator + " "
context += text[contextStartPos:startPos].replace('\n', '')
context += '<b>' + text[startPos:endPos].replace('\n', '') + '</b>'
i = endPos
while i < len(text) and (i - endPos) < extraContextSize and text[i] != '\n':
i += 1
contextEndPos = i
context += text[endPos:contextEndPos].replace('\n', '')
if i < len(text):
context += " " + separator
return context
# Based on answer by jfs at:
# https://stackoverflow.com/questions/3718657/how-to-properly-determine-current-script-directory
def getManuskriptPath(follow_symlinks=True):
"""Used to obtain the path Manuskript is located at."""
if getattr(sys, 'frozen', False): # py2exe, PyInstaller, cx_Freeze
path = os.path.abspath(sys.executable)
else:
import inspect
path = inspect.getabsfile(getManuskriptPath) + "/../.."
if follow_symlinks:
path = os.path.realpath(path)
return os.path.dirname(path)
# Based on answer by kagronik at:
# https://stackoverflow.com/questions/14989858/get-the-current-git-hash-in-a-python-script
def getGitRevision(base_path):
"""Get git revision without relying on external processes or libraries."""
git_dir = pathlib.Path(base_path) / '.git'
if not git_dir.exists():
return None
with (git_dir / 'HEAD').open('r') as head:
ref = head.readline().split(' ')[-1].strip()
with (git_dir / ref).open('r') as git_hash:
return git_hash.readline().strip()
def getGitRevisionAsString(base_path, short=False):
"""Catches errors and presents a nice string."""
try:
rev = getGitRevision(base_path)
if rev is not None:
if short:
rev = rev[:7]
return "#" + rev
else:
return "" # not a git repository
except Exception as e:
LOGGER.warning("Failed to obtain Git revision: %s", e)
return "#ERROR"
def showInFolder(path, open_file_as_fallback=False):
'''
Show a file or folder in explorer/finder, highlighting it where possible.
Source: https://stackoverflow.com/a/46019091/3388962
'''
path = os.path.abspath(path)
dirPath = path if os.path.isdir(path) else os.path.dirname(path)
if sys.platform == 'win32':
args = []
args.append('/select,')
args.append(QDir.toNativeSeparators(path))
if QProcess.startDetached('explorer', args):
return True
elif sys.platform == 'darwin':
args = []
args.append('-e')
args.append('tell application "Finder"')
args.append('-e')
args.append('activate')
args.append('-e')
args.append('select POSIX file "%s"' % path)
args.append('-e')
args.append('end tell')
args.append('-e')
args.append('return')
if not QProcess.execute('/usr/bin/osascript', args):
return True
#if not QtCore.QProcess.execute('/usr/bin/open', [dirPath]):
# return
# TODO: Linux is not implemented. It has many file managers (nautilus, xdg-open, etc.)
# each of which needs special ways to highlight a file in a file manager window.
# Fallback.
return QDesktopServices.openUrl(QUrl(path if open_file_as_fallback else dirPath))
# Spellchecker loads writablePath from this file, so we need to load it after they get defined
from manuskript.functions.spellchecker import Spellchecker

View file

@ -0,0 +1,677 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
import os, gzip, json, glob, re
from PyQt5.QtCore import QLocale
from collections import OrderedDict
from manuskript.functions import writablePath
try:
import enchant
except ImportError:
enchant = None
try:
import spellchecker as pyspellchecker
except ImportError:
pyspellchecker = None
SYMSPELLPY_MIN_VERSION = "6.3.8"
try:
import symspellpy
import distutils.version
if distutils.version.LooseVersion(symspellpy.__version__) < SYMSPELLPY_MIN_VERSION:
symspellpy = None
except ImportError:
symspellpy = None
use_language_check = False
try:
try:
import language_tool_python as languagetool
except:
import language_check as languagetool
use_language_check = True
except:
languagetool = None
class Spellchecker:
dictionaries = {}
# In order of priority
implementations = []
def __init__(self):
pass
@staticmethod
def registerImplementation(impl):
Spellchecker.implementations.append(impl)
@staticmethod
def isInstalled():
for impl in Spellchecker.implementations:
if impl.isInstalled():
return True
return False
@staticmethod
def supportedLibraries():
libs = OrderedDict()
for impl in Spellchecker.implementations:
libs[impl.getLibraryName()] = impl.getLibraryRequirement()
return libs
@staticmethod
def availableLibraries():
ret = []
for impl in Spellchecker.implementations:
if impl.isInstalled():
ret.append(impl.getLibraryName())
return ret
@staticmethod
def availableDictionaries():
dictionaries = OrderedDict()
for impl in Spellchecker.implementations:
if impl.isInstalled():
dictionaries[impl.getLibraryName()] = impl.availableDictionaries()
return dictionaries
@staticmethod
def normalizeDictName(lib, dictionary):
return "{}:{}".format(lib, dictionary)
@staticmethod
def getDefaultDictionary():
for impl in Spellchecker.implementations:
default = impl.getDefaultDictionary()
if default:
return Spellchecker.normalizeDictName(impl.getLibraryName(), default)
return None
@staticmethod
def getLibraryURL(lib=None):
urls = {}
for impl in Spellchecker.implementations:
urls[impl.getLibraryName()] = impl.getLibraryURL()
if lib:
return urls.get(lib, None)
return urls
@staticmethod
def getDictionary(dictionary):
if not dictionary:
dictionary = Spellchecker.getDefaultDictionary()
if not dictionary:
return None
values = dictionary.split(":", 1)
if len(values) == 1:
(lib, name) = (Spellchecker.implementations[0].getLibraryName(), dictionary)
dictionary = Spellchecker.normalizeDictName(lib, name)
else:
(lib, name) = values
try:
d = Spellchecker.dictionaries.get(dictionary, None)
if d == None:
for impl in Spellchecker.implementations:
if impl.isInstalled() and lib == impl.getLibraryName():
d = impl(name)
Spellchecker.dictionaries[dictionary] = d
break
return d
except Exception as e:
pass
return None
class BasicMatch:
def __init__(self, startIndex, endIndex):
self.start = startIndex
self.end = endIndex
self.locqualityissuetype = 'misspelling'
self.replacements = []
self.msg = ''
def getWord(self, text):
return text[self.start:self.end]
class BasicDictionary:
def __init__(self, name):
self._lang = name
if not self._lang:
self._lang = self.getDefaultDictionary()
self._customDict = set()
customPath = self.getCustomDictionaryPath()
try:
with gzip.open(customPath, "rt", encoding='utf-8') as f:
self._customDict = set(json.loads(f.read()))
for word in self._customDict:
self._dict.create_dictionary_entry(word, self.CUSTOM_COUNT)
except:
# If error loading the file, overwrite with empty dictionary
self._saveCustomDict()
@property
def name(self):
return self._lang
@staticmethod
def getLibraryName():
raise NotImplemented
@staticmethod
def getLibraryRequirement():
return None
@staticmethod
def getLibraryURL():
raise NotImplemented
@staticmethod
def isInstalled():
raise NotImplemented
@staticmethod
def getDefaultDictionary():
raise NotImplemented
@staticmethod
def availableDictionaries():
raise NotImplemented
def checkText(self, text):
# Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
WORDS = r'(?iu)((?:[^_\W]|\')+)[^A-Za-z0-9\']'
# (?iu) means case insensitive and Unicode
# ((?:[^_\W]|\')+) means words exclude underscores but include apostrophes
# [^A-Za-z0-9\'] used with above hack to prevent spellcheck while typing word
#
# See also https://stackoverflow.com/questions/2062169/regex-w-in-utf-8
matches = []
for word_object in re.finditer(WORDS, text):
word = word_object.group(1)
if (self.isMisspelled(word) and not self.isCustomWord(word)):
matches.append(BasicMatch(
word_object.start(1), word_object.end(1)
))
return matches
def isMisspelled(self, word):
raise NotImplemented
def getSuggestions(self, word):
raise NotImplemented
def findSuggestions(self, text, start, end):
if start < end:
word = text[start:end]
if (self.isMisspelled(word) and not self.isCustomWord(word)):
match = BasicMatch(start, end)
match.replacements = self.getSuggestions(word)
return [ match ]
return []
def isCustomWord(self, word):
return word.lower() in self._customDict
def addWord(self, word):
word = word.lower()
if not word in self._customDict:
self._customDict.add(word)
self._saveCustomDict()
def removeWord(self, word):
word = word.lower()
if word in self._customDict:
self._customDict.remove(word)
self._saveCustomDict()
@classmethod
def getResourcesPath(cls):
path = os.path.join(writablePath(), "resources", "dictionaries", cls.getLibraryName())
if not os.path.exists(path):
os.makedirs(path)
return path
def getCustomDictionaryPath(self):
return os.path.join(self.getResourcesPath(), "{}.json.gz".format(self._lang))
def _saveCustomDict(self):
customPath = self.getCustomDictionaryPath()
with gzip.open(customPath, "wt") as f:
f.write(json.dumps(list(self._customDict)))
class EnchantDictionary(BasicDictionary):
def __init__(self, name):
self._lang = name
if not (self._lang and enchant.dict_exists(self._lang)):
self._lang = self.getDefaultDictionary()
self._dict = enchant.DictWithPWL(self._lang, self.getCustomDictionaryPath())
@staticmethod
def getLibraryName():
return "PyEnchant"
@staticmethod
def getLibraryURL():
return "https://pypi.org/project/pyenchant/"
@staticmethod
def isInstalled():
return enchant != None
@staticmethod
def availableDictionaries():
if EnchantDictionary.isInstalled():
return list(map(lambda i: str(i[0]), enchant.list_dicts()))
return []
@staticmethod
def getDefaultDictionary():
if not EnchantDictionary.isInstalled():
return None
default_locale = enchant.get_default_language()
if default_locale and not enchant.dict_exists(default_locale):
default_locale = None
if default_locale == None:
default_locale = QLocale.system().name()
if default_locale == None:
default_locale = self.availableDictionaries()[0]
return default_locale
def isMisspelled(self, word):
return not self._dict.check(word)
def getSuggestions(self, word):
return self._dict.suggest(word)
def isCustomWord(self, word):
return self._dict.is_added(word)
def addWord(self, word):
self._dict.add(word)
def removeWord(self, word):
self._dict.remove(word)
def getCustomDictionaryPath(self):
return os.path.join(self.getResourcesPath(), "{}.txt".format(self.name))
class PySpellcheckerDictionary(BasicDictionary):
def __init__(self, name):
BasicDictionary.__init__(self, name)
self._dict = pyspellchecker.SpellChecker(self.name)
self._dict.word_frequency.load_words(self._customDict)
@staticmethod
def getLibraryName():
return "pyspellchecker"
@staticmethod
def getLibraryURL():
return "https://pyspellchecker.readthedocs.io/en/latest/"
@staticmethod
def isInstalled():
return pyspellchecker != None
@staticmethod
def availableDictionaries():
if PySpellcheckerDictionary.isInstalled():
dictionaries = []
files = glob.glob(os.path.join(pyspellchecker.__path__[0], "resources", "*.json.gz"))
for file in files:
dictionaries.append(os.path.basename(file)[:-8])
return dictionaries
return []
@staticmethod
def getDefaultDictionary():
if not PySpellcheckerDictionary.isInstalled():
return None
default_locale = QLocale.system().name()
if default_locale:
default_locale = default_locale[0:2]
if default_locale == None:
default_locale = "en"
return default_locale
def isMisspelled(self, word):
return len(self._dict.unknown([word])) > 0
def getSuggestions(self, word):
candidates = self._dict.candidates(word)
if word in candidates:
candidates.remove(word)
return candidates
def addWord(self, word):
BasicDictionary.addWord(self, word)
self._dict.word_frequency.add(word.lower())
def removeWord(self, word):
BasicDictionary.removeWord(self, word)
self._dict.word_frequency.remove(word.lower())
class SymSpellDictionary(BasicDictionary):
CUSTOM_COUNT = 1
DISTANCE = 2
def __init__(self, name):
BasicDictionary.__init__(self, name)
self._dict = symspellpy.SymSpell(self.DISTANCE)
cachePath = self.getCachedDictionaryPath()
try:
if not self._dict.load_pickle(cachePath, False):
raise Exception("Can't load cached dictionary. " +
"File might be corrupted or incompatible with installed symspellpy version")
except:
if pyspellchecker:
path = os.path.join(pyspellchecker.__path__[0], "resources", "{}.json.gz".format(self.name))
if os.path.exists(path):
with gzip.open(path, "rt", encoding='utf-8') as f:
data = json.loads(f.read())
for key in data:
self._dict.create_dictionary_entry(key, data[key])
self._dict.save_pickle(cachePath, False)
for word in self._customDict:
self._dict.create_dictionary_entry(word, self.CUSTOM_COUNT)
def getCachedDictionaryPath(self):
return os.path.join(self.getResourcesPath(), "{}.sym".format(self.name))
@staticmethod
def getLibraryName():
return "symspellpy"
@staticmethod
def getLibraryRequirement():
return ">= " + SYMSPELLPY_MIN_VERSION
@staticmethod
def getLibraryURL():
return "https://github.com/mammothb/symspellpy"
@staticmethod
def isInstalled():
return symspellpy != None
@classmethod
def availableDictionaries(cls):
if SymSpellDictionary.isInstalled():
files = glob.glob(os.path.join(cls.getResourcesPath(), "*.sym"))
dictionaries = []
for file in files:
dictionaries.append(os.path.basename(file)[:-4])
for sp_dict in PySpellcheckerDictionary.availableDictionaries():
if not sp_dict in dictionaries:
dictionaries.append(sp_dict)
return dictionaries
return []
@staticmethod
def getDefaultDictionary():
if not SymSpellDictionary.isInstalled():
return None
return PySpellcheckerDictionary.getDefaultDictionary()
def isMisspelled(self, word):
suggestions = self._dict.lookup(word.lower(), symspellpy.Verbosity.TOP)
if len(suggestions) > 0 and suggestions[0].distance == 0:
return False
# Try the word as is, since a dictionary might have uppercase letter as part
# of it's spelling ("I'm" or "January" for example)
suggestions = self._dict.lookup(word, symspellpy.Verbosity.TOP)
if len(suggestions) > 0 and suggestions[0].distance == 0:
return False
return True
def getSuggestions(self, word):
upper = word.isupper()
upper1 = word[0].isupper()
suggestions = self._dict.lookup_compound(word, 2)
suggestions.extend(self._dict.lookup(word, symspellpy.Verbosity.CLOSEST))
candidates = []
for sug in suggestions:
if upper:
term = sug.term.upper()
elif upper1:
term = sug.term[0].upper() + sug.term[1:]
else:
term = sug.term
if sug.distance > 0 and not term in candidates:
candidates.append(term)
return candidates
def addWord(self, word):
BasicDictionary.addWord(self, word)
self._dict.create_dictionary_entry(word.lower(), self.CUSTOM_COUNT)
def removeWord(self, word):
BasicDictionary.removeWord(self, word)
# Since 6.3.8
self._dict.delete_dictionary_entry(word)
def get_languagetool_match_errorLength(match):
if use_language_check:
return match.errorlength
else:
return match.errorLength
def get_languagetool_match_ruleIssueType(match):
if use_language_check:
return match.locqualityissuetype
else:
return match.ruleIssueType
def get_languagetool_match_message(match):
if use_language_check:
return match.msg
else:
return match.message
class LanguageToolCache:
def __init__(self, tool, text):
self._length = len(text)
self._matches = self._buildMatches(tool, text)
def getMatches(self):
return self._matches
def _buildMatches(self, tool, text):
matches = []
for match in tool.check(text):
start = match.offset
end = start + get_languagetool_match_errorLength(match)
basic_match = BasicMatch(start, end)
basic_match.locqualityissuetype = get_languagetool_match_ruleIssueType(match)
basic_match.replacements = match.replacements
basic_match.msg = get_languagetool_match_message(match)
matches.append(basic_match)
return matches
def update(self, tool, text):
if len(text) != self._length:
self._matches = self._buildMatches(tool, text)
def get_languagetool_languages(tool):
if use_language_check:
return languagetool.get_languages()
else:
return tool._get_languages()
def get_languagetool_locale_language():
if use_language_check:
return languagetool.get_locale_language()
else:
return languagetool.utils.get_locale_language()
class LanguageToolDictionary(BasicDictionary):
_tool = None
def __init__(self, name):
BasicDictionary.__init__(self, name)
if not (self._lang and self._lang in get_languagetool_languages(self.getTool())):
self._lang = self.getDefaultDictionary()
self.tool = languagetool.LanguageTool(self._lang)
self._cache = {}
@staticmethod
def getTool():
if LanguageToolDictionary._tool == None:
try:
LanguageToolDictionary._tool = languagetool.LanguageTool()
except:
return None
return LanguageToolDictionary._tool
@staticmethod
def getLibraryName():
return "LanguageTool"
@staticmethod
def getLibraryURL():
if use_language_check:
return "https://pypi.org/project/language-check/"
else:
return "https://pypi.org/project/language-tool-python/"
@staticmethod
def isInstalled():
if (languagetool != None) and (LanguageToolDictionary.getTool() != None):
# This check, if Java is installed, is necessary to
# make sure LanguageTool can be run without problems.
#
return (os.system('java -version') == 0)
return False
@staticmethod
def availableDictionaries():
if LanguageToolDictionary.isInstalled():
tool = LanguageToolDictionary.getTool()
languages = list(get_languagetool_languages(tool))
languages.sort()
return languages
return []
@staticmethod
def getDefaultDictionary():
if not LanguageToolDictionary.isInstalled():
return None
default_locale = get_languagetool_locale_language()
tool = LanguageToolDictionary.getTool()
if default_locale and not default_locale in get_languagetool_languages(tool):
default_locale = None
if default_locale == None:
default_locale = QLocale.system().name()
if default_locale == None:
default_locale = self.availableDictionaries()[0]
return default_locale
def checkText(self, text):
matches = []
if len(text) == 0:
return matches
textId = hash(text)
cacheEntry = None
if not textId in self._cache:
cacheEntry = LanguageToolCache(self.tool, text)
self._cache[textId] = cacheEntry
else:
cacheEntry = self._cache[textId]
cacheEntry.update(self.tool, text)
for match in cacheEntry.getMatches():
word = match.getWord(text)
if not (match.locqualityissuetype == 'misspelling' and self.isCustomWord(word)):
matches.append(match)
return matches
def isMisspelled(self, word):
if self.isCustomWord(word):
return False
for match in self.checkText(word):
if match.locqualityissuetype == 'misspelling':
return True
return False
def getSuggestions(self, word):
suggestions = []
for match in self.checkText(word):
suggestions += match.replacements
return suggestions
def findSuggestions(self, text, start, end):
matches = []
checked = self.checkText(text)
if start == end:
# Check for containing area:
for match in checked:
if (start >= match.start and start <= match.end):
matches.append(match)
else:
# Check for overlapping area:
for match in checked:
if (match.end > start and match.start < end):
matches.append(match)
return matches
# Register the implementations in order of priority
Spellchecker.registerImplementation(EnchantDictionary)
Spellchecker.registerImplementation(SymSpellDictionary)
Spellchecker.registerImplementation(PySpellcheckerDictionary)
Spellchecker.registerImplementation(LanguageToolDictionary)

View file

@ -5,6 +5,8 @@ import os
from manuskript.importer.abstractImporter import abstractImporter
from manuskript.models import outlineItem
from manuskript.enums import Outline
from manuskript.functions import safeTranslate
from PyQt5.QtWidgets import qApp
@ -44,7 +46,7 @@ class folderImporter(abstractImporter):
fName, fExt = os.path.splitext(f)
if fExt.lower() in ext:
try:
with open(os.path.join(dirpath, f), "r") as fr:
with open(os.path.join(dirpath, f), "r", encoding="utf-8") as fr:
content = fr.read()
child = outlineItem(title=fName, _type="md", parent=item)
child._data[Outline.text] = content
@ -94,27 +96,27 @@ class folderImporter(abstractImporter):
# Add group
group = self.addGroup(widget.toolBox.widget(0),
qApp.translate("Import", "Folder import"))
safeTranslate(qApp, "Import", "Folder import"))
#group = cls.addPage(widget, "Folder import")
self.addSetting("info", "label",
qApp.translate("Import", """<p><b>Info:</b> Imports a whole
safeTranslate(qApp, "Import", """<p><b>Info:</b> Imports a whole
directory structure. Folders are added as folders, and
plaintext documents within (you chose which ones by extension)
are added as scene.</p>
<p>Only text files are supported (not images, binary or others).</p>"""))
self.addSetting("ext", "text",
qApp.translate("Import", "Include only those extensions:"),
safeTranslate(qApp, "Import", "Include only those extensions:"),
default="*.txt, *.md",
tooltip=qApp.translate("Import", "Comma separated values")),
tooltip=safeTranslate(qApp, "Import", "Comma separated values")),
self.addSetting("sortItems", "checkbox",
qApp.translate("Import", "Sort items by name"),
safeTranslate(qApp, "Import", "Sort items by name"),
default=True),
self.addSetting("separateFolderFiles", "checkbox",
qApp.translate("Import", "Import folder then files"),
safeTranslate(qApp, "Import", "Import folder then files"),
default=True),
self.addSettingsTo(group)

View file

@ -4,6 +4,8 @@
from manuskript.importer.abstractImporter import abstractImporter
from manuskript.models import outlineItem
from manuskript.enums import Outline
from manuskript.functions import safeTranslate
from PyQt5.QtWidgets import qApp
import re, os
@ -63,7 +65,7 @@ class markdownImporter(abstractImporter):
if not fromString:
# Read file
with open(filePath, "r") as f:
with open(filePath, "r", encoding="utf-8") as f:
txt = f.read()
else:
txt = fromString
@ -173,11 +175,11 @@ class markdownImporter(abstractImporter):
# Add group
group = self.addGroup(widget.toolBox.widget(0),
qApp.translate("Import", "Markdown import"))
safeTranslate(qApp, "Import", "Markdown import"))
#group = cls.addPage(widget, "Folder import")
self.addSetting("info", "label",
qApp.translate("Import", """<b>Info:</b> A very simple
safeTranslate(qApp, "Import", """<b>Info:</b> A very simple
parser that will go through a markdown document and
create items for each titles.<br/>&nbsp;"""))

View file

@ -5,10 +5,11 @@ from PyQt5.QtWidgets import qApp, QMessageBox
from manuskript.models import outlineItem
from manuskript.enums import Outline
from lxml import etree as ET
from manuskript.functions import mainWindow
from manuskript.functions import mainWindow, safeTranslate
from manuskript.importer.abstractImporter import abstractImporter
from manuskript.converters import HTML2MD, HTML2PlainText
class mindMapImporter(abstractImporter):
name = "Mind Map"
@ -47,15 +48,15 @@ class mindMapImporter(abstractImporter):
node = root.find("node")
items = []
if node is not None:
if node != None:
items.extend(self.parseItems(node, parentItem))
ret = True
if not ret:
QMessageBox.critical(
settingsWidget,
qApp.translate("Import", "Mind Map Import"),
qApp.translate("Import", "This does not appear to be a valid Mind Map file."))
safeTranslate(qApp, "Import", "Mind Map Import"),
safeTranslate(qApp, "Import", "This does not appear to be a valid Mind Map file."))
return None
@ -68,10 +69,10 @@ class mindMapImporter(abstractImporter):
# Add group
group = self.addGroup(widget.toolBox.widget(0),
qApp.translate("Import", "Mind Map import"))
safeTranslate(qApp, "Import", "Mind Map import"))
self.addSetting("importTipAs", "combo",
qApp.translate("Import", "Import tip as:"),
safeTranslate(qApp, "Import", "Import tip as:"),
vals="Text|Folder",
)
@ -86,7 +87,7 @@ class mindMapImporter(abstractImporter):
# Title
title = underElement.get('TEXT', "").replace("\n", " ")
if not title:
title = qApp.translate("Import", "Untitled")
title = safeTranslate(qApp, "Import", "Untitled")
item = outlineItem(parent=parentItem, title=title)
items.append(item)
@ -97,7 +98,7 @@ class mindMapImporter(abstractImporter):
# Rich text content
content = ""
content = underElement.find("richcontent")
if content is not None:
if content != None:
# In Freemind, can be note or node
# Note: it's a note
# Node: it's the title of the node, in rich text
@ -130,7 +131,7 @@ class mindMapImporter(abstractImporter):
children = underElement.findall('node')
# Process children
if children is not None and len(children) > 0:
if children != None and len(children) > 0:
for c in children:
items.extend(self.parseItems(c, item))

View file

@ -5,9 +5,10 @@ from PyQt5.QtWidgets import qApp, QMessageBox
from manuskript.models import outlineItem
from manuskript.enums import Outline
from lxml import etree as ET
from manuskript.functions import mainWindow
from manuskript.functions import mainWindow, safeTranslate
from manuskript.importer.abstractImporter import abstractImporter
class opmlImporter(abstractImporter):
name = "OPML"
@ -34,8 +35,8 @@ class opmlImporter(abstractImporter):
opmlContent = opmlFile.read()
except:
QMessageBox.critical(settingsWidget,
qApp.translate("Import", "OPML Import"),
qApp.translate("Import", "File open failed."))
safeTranslate(qApp, "Import", "OPML Import"),
safeTranslate(qApp, "Import", "File open failed."))
return None
elif fromString == "":
@ -52,10 +53,10 @@ class opmlImporter(abstractImporter):
bodyNode = opmlNode.find("body")
items = []
if bodyNode is not None:
if bodyNode != None:
outlineEls = bodyNode.findall("outline")
if outlineEls is not None:
if outlineEls != None:
for element in outlineEls:
items.extend(cls.parseItems(element, parentItem))
ret = True
@ -63,8 +64,8 @@ class opmlImporter(abstractImporter):
if not ret:
QMessageBox.critical(
settingsWidget,
qApp.translate("Import", "OPML Import"),
qApp.translate("Import", "This does not appear to be a valid OPML file."))
safeTranslate(qApp, "Import", "OPML Import"),
safeTranslate(qApp, "Import", "This does not appear to be a valid OPML file."))
return None
@ -74,19 +75,20 @@ class opmlImporter(abstractImporter):
def parseItems(cls, underElement, parentItem=None):
items = []
title = underElement.get('text')
if title is not None:
if title != None:
card = outlineItem(parent=parentItem, title=title)
items.append(card)
body = ""
note = underElement.get('_note')
if note is not None and not cls.isWhitespaceOnly(note):
if note != None and not cls.isWhitespaceOnly(note):
#body = cls.restoreNewLines(note)
body = note
children = underElement.findall('outline')
if children is not None and len(children) > 0:
if children != None and len(children) > 0:
for el in children:
items.extend(cls.parseItems(el, card))
else:
@ -121,4 +123,4 @@ class opmlImporter(abstractImporter):
s = cls.restoreNewLines(inString)
s = ''.join(s.split())
return len(s) is 0
return len(s) == 0

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