mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-14 18:02:23 +12:00
Merge branch 'develop' into session-target
This commit is contained in:
commit
0a81ff4960
46
.github/workflows/linux.yml
vendored
Normal file
46
.github/workflows/linux.yml
vendored
Normal 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
5
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
374
CHANGELOG.md
374
CHANGELOG.md
|
@ -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)*
|
||||
|
|
26
README.md
26
README.md
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
BIN
i18n/manuskript_ar_SA.qm
Normal file
Binary file not shown.
4007
i18n/manuskript_ar_SA.ts
Normal file
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
BIN
i18n/manuskript_ca.qm
Normal file
Binary file not shown.
4539
i18n/manuskript_ca.ts
Normal file
4539
i18n/manuskript_ca.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
i18n/manuskript_ca@valencia.qm
Normal file
BIN
i18n/manuskript_ca@valencia.qm
Normal file
Binary file not shown.
4537
i18n/manuskript_ca@valencia.ts
Normal file
4537
i18n/manuskript_ca@valencia.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
i18n/manuskript_da.qm
Normal file
BIN
i18n/manuskript_da.qm
Normal file
Binary file not shown.
4652
i18n/manuskript_da.ts
Normal file
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
BIN
i18n/manuskript_el.qm
Normal file
Binary file not shown.
4539
i18n/manuskript_el.ts
Normal file
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
BIN
i18n/manuskript_en_GB.qm
Normal file
Binary file not shown.
4010
i18n/manuskript_en_GB.ts
Normal file
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
BIN
i18n/manuskript_eo.qm
Normal file
Binary file not shown.
4582
i18n/manuskript_eo.ts
Normal file
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
BIN
i18n/manuskript_fa.qm
Normal file
Binary file not shown.
4005
i18n/manuskript_fa.ts
Normal file
4005
i18n/manuskript_fa.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
i18n/manuskript_fi.qm
Normal file
BIN
i18n/manuskript_fi.qm
Normal file
Binary file not shown.
4638
i18n/manuskript_fi.ts
Normal file
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
BIN
i18n/manuskript_he.qm
Normal file
Binary file not shown.
4633
i18n/manuskript_he.ts
Normal file
4633
i18n/manuskript_he.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
i18n/manuskript_hr.qm
Normal file
BIN
i18n/manuskript_hr.qm
Normal file
Binary file not shown.
4684
i18n/manuskript_hr.ts
Normal file
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
BIN
i18n/manuskript_ja.qm
Normal file
Binary file not shown.
4008
i18n/manuskript_ja.ts
Normal file
4008
i18n/manuskript_ja.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
i18n/manuskript_ko.qm
Normal file
BIN
i18n/manuskript_ko.qm
Normal file
Binary file not shown.
4008
i18n/manuskript_ko.ts
Normal file
4008
i18n/manuskript_ko.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
i18n/manuskript_mr.qm
Normal file
BIN
i18n/manuskript_mr.qm
Normal file
Binary file not shown.
4638
i18n/manuskript_mr.ts
Normal file
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
BIN
i18n/manuskript_pt.qm
Normal file
Binary file not shown.
4582
i18n/manuskript_pt.ts
Normal file
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
BIN
i18n/manuskript_ro.qm
Normal file
Binary file not shown.
4005
i18n/manuskript_ro.ts
Normal file
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
BIN
i18n/manuskript_ta.qm
Normal file
Binary file not shown.
4633
i18n/manuskript_ta.ts
Normal file
4633
i18n/manuskript_ta.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
i18n/manuskript_tr.qm
Normal file
BIN
i18n/manuskript_tr.qm
Normal file
Binary file not shown.
4005
i18n/manuskript_tr.ts
Normal file
4005
i18n/manuskript_tr.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
i18n/manuskript_uk.qm
Normal file
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
BIN
i18n/manuskript_zh_HANT.qm
Normal file
Binary file not shown.
4005
i18n/manuskript_zh_HANT.ts
Normal file
4005
i18n/manuskript_zh_HANT.ts
Normal file
File diff suppressed because it is too large
Load diff
BIN
icons/Manuskript/Manuskript.icns
Normal file
BIN
icons/Manuskript/Manuskript.icns
Normal file
Binary file not shown.
BIN
icons/Manuskript/manuskript.ico
Normal file
BIN
icons/Manuskript/manuskript.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
3
main.pyproject
Normal file
3
main.pyproject
Normal file
File diff suppressed because one or more lines are too long
17
makefile
17
makefile
|
@ -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 "$<"
|
||||
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
|
@ -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()
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -10,6 +10,7 @@ class abstractOutput(abstractPlainText):
|
|||
toFormat = "SUBCLASSME"
|
||||
icon = "SUBCLASSME"
|
||||
exportFilter = "SUBCLASSME"
|
||||
exportDefaultSuffix = ".SUBCLASSME"
|
||||
requires = {
|
||||
"Settings": True,
|
||||
"Preview": False,
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
677
manuskript/functions/spellchecker.py
Normal file
677
manuskript/functions/spellchecker.py
Normal 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)
|
|
@ -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)
|
||||
|
|
|
@ -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/> """))
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue